diff --git a/AUTHORS b/AUTHORS
index d4ec525b..fd4fd13 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -669,6 +669,7 @@
 Paul Sapunaru <paul.sapunaru@intel.com>
 Paul Wicks <pwicks86@gmail.com>
 Pavan Kumar Emani <pavan.e@samsung.com>
+Pavel Golikov <paullo612@ya.ru>
 Pavel Ivanov <paivanof@gmail.com>
 Pawel Forysiuk <p.forysiuk@samsung.com>
 Paweł Hajdan jr <phajdan.jr@gmail.com>
diff --git a/DEPS b/DEPS
index 85a87fb..9725d6e 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': '8f37ce5b9f2c139e4bad8e14ed6b884c1510dffd',
+  'skia_revision': '191e64b6c6c2e791ff4d7dae0209a78eed0376f0',
   # 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': 'abd39667a5d0a9605b16b053afe18859af116045',
+  'v8_revision': 'be1288c9cdfde38689bacccdf804a429d9e9f9c3',
   # 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.
@@ -133,7 +133,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '04ea03e4e1e1575ee576dafe1793f15fa41e5cc4',
+  'angle_revision': 'bbdeee914aeb1fa5f6a2ea65b31c879c8af10b30',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -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': 'e2809cef2bf967cf572527e0944307fd2a9277d0',
+  'feed_revision': '84617ef81ded68396acf495e8392266950972223',
   # 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.
@@ -229,7 +229,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': '64f2750e5dc553baa2922b780f15049023689ef9',
+  'spv_tools_revision': '9f36c8bb721d4bd8e5ee311a2677b68829c9832a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -1036,7 +1036,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '8a5b9a8ad6fa8d06ac3f63a091303eead859cfd0',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'a6ba1795b7bcccd80ee68449bcec172c6cea7b6a',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1199,7 +1199,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'a2b35635aaef3e9301d69f77f9a0a3fd99291b08',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'bde71044cd4a011486073ba68ad84e606d25aa2d',
+    Var('webrtc_git') + '/src.git' + '@' + 'bba675db3eb4eaedd110427b7cf8afdb7409e2e1',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -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@96eb27a4c496f74f67ea65f132efc081f878fd7b',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@bbd8fc1f9fa45d234881fd681cd867de003c07ff',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_feature_list_creator.cc b/android_webview/browser/aw_feature_list_creator.cc
index 6af0f30..cf71dbd9 100644
--- a/android_webview/browser/aw_feature_list_creator.cc
+++ b/android_webview/browser/aw_feature_list_creator.cc
@@ -164,6 +164,11 @@
       std::vector<std::string>(), /*low_entropy_provider=*/nullptr,
       std::make_unique<base::FeatureList>(), aw_field_trials_.get(),
       &ignored_safe_seed_manager);
+
+  // Activate a study which exercises permanent-consistency, to test the launch
+  // of permanent-consistency support in WebView.
+  // TODO(crbug/917537): Remove this after m73.
+  base::FieldTrialList::FindFullName("AndroidWebViewConsistencyTest");
 }
 
 void AwFeatureListCreator::CreateFeatureListAndFieldTrials() {
diff --git a/android_webview/java/src/org/chromium/android_webview/AwProxyController.java b/android_webview/java/src/org/chromium/android_webview/AwProxyController.java
index 419fc7f..bb2df95 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwProxyController.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwProxyController.java
@@ -16,7 +16,7 @@
 public class AwProxyController {
     public AwProxyController() {}
 
-    public void setProxyOverride(
+    public String setProxyOverride(
             String[][] proxyRules, String[] bypassRules, Runnable listener, Executor executor) {
         int length = (proxyRules == null ? 0 : proxyRules.length);
         String[] urlSchemes = new String[length];
@@ -31,31 +31,29 @@
             // proxy URLs
             proxyUrls[i] = proxyRules[i][1];
             if (proxyUrls[i] == null) {
-                throw new NullPointerException("Proxy rule " + i + " has a null url");
+                return "Proxy rule " + i + " has a null url";
             }
         }
         length = (bypassRules == null ? 0 : bypassRules.length);
         for (int i = 0; i < length; i++) {
             if (bypassRules[i] == null) {
-                throw new NullPointerException("Bypass rule " + i + " is null");
+                return "Bypass rule " + i + " is null";
             }
         }
         if (executor == null) {
-            throw new NullPointerException("Executor must not be null");
+            return "Executor must not be null";
         }
 
-        String result =
-                nativeSetProxyOverride(urlSchemes, proxyUrls, bypassRules, listener, executor);
-        if (!result.isEmpty()) {
-            throw new IllegalArgumentException(result);
-        }
+        return nativeSetProxyOverride(urlSchemes, proxyUrls, bypassRules, listener, executor);
     }
 
-    public void clearProxyOverride(Runnable listener, Executor executor) {
+    public String clearProxyOverride(Runnable listener, Executor executor) {
         if (executor == null) {
-            throw new NullPointerException("Executor must not be null");
+            return "Executor must not be null";
         }
+
         nativeClearProxyOverride(listener, executor);
+        return "";
     }
 
     @CalledByNativeUnchecked
@@ -67,4 +65,4 @@
     private native String nativeSetProxyOverride(String[] urlSchemes, String[] proxyUrls,
             String[] bypassRules, Runnable listener, Executor executor);
     private native void nativeClearProxyOverride(Runnable listener, Executor executor);
-}
\ No newline at end of file
+}
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/HttpCacheTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/HttpCacheTest.java
index 3df5276..6a165062 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/HttpCacheTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/HttpCacheTest.java
@@ -13,6 +13,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.android_webview.AwContents;
+import org.chromium.base.FileUtils;
 import org.chromium.base.test.util.Feature;
 import org.chromium.net.test.util.TestWebServer;
 
@@ -40,7 +41,7 @@
                                                 .getCacheDir()
                                                 .getPath(),
                 "org.chromium.android_webview");
-        deleteDirectory(webViewCacheDir);
+        FileUtils.recursivelyDeleteFile(webViewCacheDir);
 
         mActivityTestRule.startBrowserProcess();
         final TestAwContentsClient contentClient = new TestAwContentsClient();
@@ -66,12 +67,4 @@
         Assert.assertTrue(webViewCacheDir.isDirectory());
         Assert.assertTrue(webViewCacheDir.list().length > 0);
     }
-
-    private void deleteDirectory(File dir) throws Exception {
-        if (!dir.exists()) return;
-        Assert.assertTrue(dir.isDirectory());
-        Process rmrf = Runtime.getRuntime().exec("rm -rf " + dir.getAbsolutePath());
-        rmrf.waitFor();
-        Assert.assertFalse(dir.exists());
-    }
 }
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibProxyControllerAdapter.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibProxyControllerAdapter.java
index 1c3b24b..ebe0f6fc 100644
--- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibProxyControllerAdapter.java
+++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibProxyControllerAdapter.java
@@ -5,6 +5,8 @@
 package org.chromium.support_lib_glue;
 
 import org.chromium.android_webview.AwProxyController;
+import org.chromium.android_webview.WebViewChromiumRunQueue;
+import org.chromium.base.ThreadUtils;
 import org.chromium.support_lib_boundary.ProxyControllerBoundaryInterface;
 
 import java.util.concurrent.Executor;
@@ -13,20 +15,47 @@
  * Adapter between AwProxyController and ProxyControllerBoundaryInterface.
  */
 public class SupportLibProxyControllerAdapter implements ProxyControllerBoundaryInterface {
+    private final WebViewChromiumRunQueue mRunQueue;
     private final AwProxyController mProxyController;
 
-    public SupportLibProxyControllerAdapter(AwProxyController proxyController) {
+    public SupportLibProxyControllerAdapter(
+            WebViewChromiumRunQueue runQueue, AwProxyController proxyController) {
+        mRunQueue = runQueue;
         mProxyController = proxyController;
     }
 
     @Override
     public void setProxyOverride(
             String[][] proxyRules, String[] bypassRules, Runnable listener, Executor executor) {
-        mProxyController.setProxyOverride(proxyRules, bypassRules, listener, executor);
+        String result;
+        if (checkNeedsPost()) {
+            result = mRunQueue.runOnUiThreadBlocking(() -> {
+                return mProxyController.setProxyOverride(
+                        proxyRules, bypassRules, listener, executor);
+            });
+        } else {
+            result = mProxyController.setProxyOverride(proxyRules, bypassRules, listener, executor);
+        }
+        if (!result.isEmpty()) {
+            throw new IllegalArgumentException(result);
+        }
     }
 
     @Override
     public void clearProxyOverride(Runnable listener, Executor executor) {
-        mProxyController.clearProxyOverride(listener, executor);
+        String result;
+        if (checkNeedsPost()) {
+            result = mRunQueue.runOnUiThreadBlocking(
+                    () -> { return mProxyController.clearProxyOverride(listener, executor); });
+        } else {
+            result = mProxyController.clearProxyOverride(listener, executor);
+        }
+        if (!result.isEmpty()) {
+            throw new IllegalArgumentException(result);
+        }
     }
-}
\ No newline at end of file
+
+    private static boolean checkNeedsPost() {
+        return !ThreadUtils.runningOnUiThread();
+    }
+}
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java
index 7db638e0..afc132c 100644
--- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java
+++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java
@@ -167,7 +167,8 @@
         synchronized (mAwInit.getLock()) {
             if (mProxyController == null) {
                 mProxyController = BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(
-                        new SupportLibProxyControllerAdapter(mAwInit.getAwProxyController()));
+                        new SupportLibProxyControllerAdapter(
+                                mAwInit.getRunQueue(), mAwInit.getAwProxyController()));
             }
         }
         return mProxyController;
diff --git a/ash/components/tap_visualizer/BUILD.gn b/ash/components/tap_visualizer/BUILD.gn
index d9b25db..137d8de 100644
--- a/ash/components/tap_visualizer/BUILD.gn
+++ b/ash/components/tap_visualizer/BUILD.gn
@@ -13,6 +13,7 @@
   ]
 
   deps = [
+    "//ash/components/tap_visualizer/public/mojom",
     "//ash/public/cpp",
     "//base",
     "//cc/paint",
diff --git a/ash/components/tap_visualizer/manifest.json b/ash/components/tap_visualizer/manifest.json
index 68ed3f22..ccc2753c 100644
--- a/ash/components/tap_visualizer/manifest.json
+++ b/ash/components/tap_visualizer/manifest.json
@@ -4,6 +4,9 @@
   "sandbox_type": "none",
   "interface_provider_specs": {
     "service_manager:connector": {
+      "provides": {
+        "tap_visualizer": [ "tap_visualizer.mojom.TapVisualizer" ]
+      },
       "requires": {
         "*": [ "app" ],
         "service_manager": [ "service_manager:service_manager" ]
diff --git a/ash/components/tap_visualizer/public/mojom/BUILD.gn b/ash/components/tap_visualizer/public/mojom/BUILD.gn
index eaeb9d8..c734e71 100644
--- a/ash/components/tap_visualizer/public/mojom/BUILD.gn
+++ b/ash/components/tap_visualizer/public/mojom/BUILD.gn
@@ -4,14 +4,8 @@
 
 import("//mojo/public/tools/bindings/mojom.gni")
 
-group("mojom") {
-  public_deps = [
-    ":constants",
-  ]
-}
-
-mojom("constants") {
+mojom("mojom") {
   sources = [
-    "constants.mojom",
+    "tap_visualizer.mojom",
   ]
 }
diff --git a/ash/components/tap_visualizer/public/mojom/constants.mojom b/ash/components/tap_visualizer/public/mojom/tap_visualizer.mojom
similarity index 80%
rename from ash/components/tap_visualizer/public/mojom/constants.mojom
rename to ash/components/tap_visualizer/public/mojom/tap_visualizer.mojom
index b7a33bd6..ecbc4c3 100644
--- a/ash/components/tap_visualizer/public/mojom/constants.mojom
+++ b/ash/components/tap_visualizer/public/mojom/tap_visualizer.mojom
@@ -5,3 +5,8 @@
 module tap_visualizer.mojom;
 
 const string kServiceName = "tap_visualizer_app";
+
+interface TapVisualizer {
+  // Show the UI.
+  Show();
+};
diff --git a/ash/components/tap_visualizer/tap_visualizer_app.cc b/ash/components/tap_visualizer/tap_visualizer_app.cc
index 9b18560..ed6d888 100644
--- a/ash/components/tap_visualizer/tap_visualizer_app.cc
+++ b/ash/components/tap_visualizer/tap_visualizer_app.cc
@@ -25,26 +25,16 @@
 
 TapVisualizerApp::TapVisualizerApp(
     service_manager::mojom::ServiceRequest request)
-    : service_binding_(this, std::move(request)) {}
+    : service_binding_(this, std::move(request)) {
+  registry_.AddInterface<mojom::TapVisualizer>(base::BindRepeating(
+      &TapVisualizerApp::AddBinding, base::Unretained(this)));
+}
 
 TapVisualizerApp::~TapVisualizerApp() {
   display::Screen::GetScreen()->RemoveObserver(this);
   aura::Env::GetInstance()->RemoveEventObserver(this);
 }
 
-void TapVisualizerApp::Start() {
-  // Watches moves so the user can drag around a touch point.
-  aura::Env* env = aura::Env::GetInstance();
-  std::set<ui::EventType> types = {ui::ET_TOUCH_PRESSED, ui::ET_TOUCH_RELEASED,
-                                   ui::ET_TOUCH_MOVED, ui::ET_TOUCH_CANCELLED};
-  env->AddEventObserver(this, env, types);
-  display::Screen::GetScreen()->AddObserver(this);
-  for (const display::Display& display :
-       display::Screen::GetScreen()->GetAllDisplays()) {
-    CreateWidgetForDisplay(display.id());
-  }
-}
-
 void TapVisualizerApp::OnStart() {
   views::AuraInit::InitParams params;
   params.connector = service_binding_.GetConnector();
@@ -55,7 +45,31 @@
     Terminate();
     return;
   }
-  Start();
+}
+
+void TapVisualizerApp::OnBindInterface(
+    const service_manager::BindSourceInfo& remote_info,
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle interface_pipe) {
+  registry_.BindInterface(interface_name, std::move(interface_pipe));
+}
+
+void TapVisualizerApp::Show() {
+  // Only show the first time.
+  if (is_showing_)
+    return;
+  is_showing_ = true;
+
+  // Watches moves so the user can drag around a touch point.
+  aura::Env* env = aura::Env::GetInstance();
+  std::set<ui::EventType> types = {ui::ET_TOUCH_PRESSED, ui::ET_TOUCH_RELEASED,
+                                   ui::ET_TOUCH_MOVED, ui::ET_TOUCH_CANCELLED};
+  env->AddEventObserver(this, env, types);
+  display::Screen::GetScreen()->AddObserver(this);
+  for (const display::Display& display :
+       display::Screen::GetScreen()->GetAllDisplays()) {
+    CreateWidgetForDisplay(display.id());
+  }
 }
 
 void TapVisualizerApp::OnEvent(const ui::Event& event) {
@@ -106,4 +120,9 @@
       std::make_unique<TapRenderer>(std::move(widget));
 }
 
+void TapVisualizerApp::AddBinding(mojom::TapVisualizerRequest request) {
+  tap_visualizer_binding_.Close();
+  tap_visualizer_binding_.Bind(std::move(request));
+}
+
 }  // namespace tap_visualizer
diff --git a/ash/components/tap_visualizer/tap_visualizer_app.h b/ash/components/tap_visualizer/tap_visualizer_app.h
index c8577c2..3763ae2 100644
--- a/ash/components/tap_visualizer/tap_visualizer_app.h
+++ b/ash/components/tap_visualizer/tap_visualizer_app.h
@@ -10,7 +10,9 @@
 #include <map>
 #include <memory>
 
+#include "ash/components/tap_visualizer/public/mojom/tap_visualizer.mojom.h"
 #include "base/macros.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/public/cpp/service.h"
 #include "services/service_manager/public/cpp/service_binding.h"
 #include "services/service_manager/public/mojom/service.mojom.h"
@@ -28,6 +30,7 @@
 // Application that paints touch tap points as circles. Creates a fullscreen
 // transparent widget on each display to draw the taps.
 class TapVisualizerApp : public service_manager::Service,
+                         public tap_visualizer::mojom::TapVisualizer,
                          public ui::EventObserver,
                          public display::DisplayObserver {
  public:
@@ -38,11 +41,14 @@
  private:
   friend class TapVisualizerAppTestApi;
 
-  // Starts showing touches on all displays.
-  void Start();
-
   // service_manager::Service:
   void OnStart() override;
+  void OnBindInterface(const service_manager::BindSourceInfo& remote_info,
+                       const std::string& interface_name,
+                       mojo::ScopedMessagePipeHandle interface_pipe) override;
+
+  // mojom::TapVisualizer:
+  void Show() override;
 
   // ui::EventObserver:
   void OnEvent(const ui::Event& event) override;
@@ -54,12 +60,19 @@
   // Creates the touch HUD widget for a display.
   void CreateWidgetForDisplay(int64_t display_id);
 
+  void AddBinding(mojom::TapVisualizerRequest request);
+
   service_manager::ServiceBinding service_binding_;
+  service_manager::BinderRegistry registry_;
+  mojo::Binding<mojom::TapVisualizer> tap_visualizer_binding_{this};
 
   // Must be released after |display_id_to_renderer_| which indirectly depends
   // on aura.
   std::unique_ptr<views::AuraInit> aura_init_;
 
+  // True after the first Show().
+  bool is_showing_ = false;
+
   // Maps display::Display::id() to the renderer for that display.
   std::map<int64_t, std::unique_ptr<TapRenderer>> display_id_to_renderer_;
 
diff --git a/ash/components/tap_visualizer/tap_visualizer_app_unittest.cc b/ash/components/tap_visualizer/tap_visualizer_app_unittest.cc
index f8b43eb9..66c9331 100644
--- a/ash/components/tap_visualizer/tap_visualizer_app_unittest.cc
+++ b/ash/components/tap_visualizer/tap_visualizer_app_unittest.cc
@@ -26,7 +26,7 @@
   explicit TapVisualizerAppTestApi(TapVisualizerApp* app) : app_(app) {}
   ~TapVisualizerAppTestApi() = default;
 
-  void Start() { app_->Start(); }
+  void Show() { app_->Show(); }
 
   bool HasRendererForDisplay(int64_t display_id) {
     return base::ContainsKey(app_->display_id_to_renderer_, display_id);
@@ -83,7 +83,7 @@
   // Simulate the service starting.
   TapVisualizerApp app(nullptr);
   TapVisualizerAppTestApi test_api(&app);
-  test_api.Start();
+  test_api.Show();
 
   // A fullscreen widget is created.
   views::Widget* widget = test_api.GetWidgetForDisplay(kFirstDisplayId);
@@ -113,7 +113,7 @@
   // Simulate the service starting.
   TapVisualizerApp app(nullptr);
   TapVisualizerAppTestApi test_api(&app);
-  test_api.Start();
+  test_api.Show();
 
   // Two renderers are created.
   EXPECT_TRUE(test_api.HasRendererForDisplay(kFirstDisplayId));
diff --git a/ash/login/login_screen_controller.cc b/ash/login/login_screen_controller.cc
index 7733074..f1a1bbd 100644
--- a/ash/login/login_screen_controller.cc
+++ b/ash/login/login_screen_controller.cc
@@ -505,6 +505,13 @@
       ->SetShowGuestButtonInOobe(show);
 }
 
+void LoginScreenController::SetShowParentAccess(bool show) {
+  Shelf::ForWindow(Shell::Get()->GetPrimaryRootWindow())
+      ->shelf_widget()
+      ->login_shelf_view()
+      ->SetShowParentAccess(show);
+}
+
 void LoginScreenController::FocusLoginShelf(bool reverse) {
   Shelf* shelf = Shelf::ForWindow(Shell::Get()->GetPrimaryRootWindow());
   // Tell the focus direction to the status area or the shelf so they can focus
diff --git a/ash/login/login_screen_controller.h b/ash/login/login_screen_controller.h
index 07af915..b78ca78 100644
--- a/ash/login/login_screen_controller.h
+++ b/ash/login/login_screen_controller.h
@@ -157,6 +157,7 @@
   void SetShutdownButtonEnabled(bool enable) override;
   void SetAllowLoginAsGuest(bool allow_guest) override;
   void SetShowGuestButtonInOobe(bool show) override;
+  void SetShowParentAccess(bool show) override;
   void FocusLoginShelf(bool reverse) override;
 
   // Flushes the mojo pipes - to be used in tests.
diff --git a/ash/login/login_screen_test_api.h b/ash/login/login_screen_test_api.h
index 4b70664..ed24dee 100644
--- a/ash/login/login_screen_test_api.h
+++ b/ash/login/login_screen_test_api.h
@@ -5,7 +5,7 @@
 #ifndef ASH_LOGIN_LOGIN_SCREEN_TEST_API_H_
 #define ASH_LOGIN_LOGIN_SCREEN_TEST_API_H_
 
-#include "ash/public/interfaces/login_screen_test_api.mojom.h"
+#include "ash/public/interfaces/login_screen_test_api.test-mojom.h"
 #include "base/macros.h"
 #include "components/account_id/account_id.h"
 
diff --git a/ash/main.cc b/ash/main.cc
index d9e398f..8aedc9f 100644
--- a/ash/main.cc
+++ b/ash/main.cc
@@ -45,12 +45,17 @@
   if (!enabled_features.empty())
     enabled_features += ",";
   enabled_features += features::kMash.name;
+  // Disable SingleProcessMash, even if it's on by default in the test suite.
+  if (!disabled_features.empty())
+    disabled_features += ",";
+  disabled_features += features::kSingleProcessMash.name;
   // This code path is really only for testing (production code uses the utility
   // process to launch AshService), so it's ok to use a for-testing function.
   base::FeatureList::ClearInstanceForTesting();
   CHECK(base::FeatureList::InitializeInstance(enabled_features,
                                               disabled_features));
   CHECK(base::FeatureList::IsEnabled(features::kMash));
+  CHECK(!base::FeatureList::IsEnabled(features::kSingleProcessMash));
 
   ui::MaterialDesignController::Initialize();
 
diff --git a/ash/media/media_notification_item.cc b/ash/media/media_notification_item.cc
index ac44a05..f6b36c5 100644
--- a/ash/media/media_notification_item.cc
+++ b/ash/media/media_notification_item.cc
@@ -69,6 +69,15 @@
     view_->UpdateWithMediaMetadata(session_metadata_);
 }
 
+void MediaNotificationItem::MediaSessionActionsChanged(
+    const std::vector<media_session::mojom::MediaSessionAction>& actions) {
+  session_actions_ = std::set<media_session::mojom::MediaSessionAction>(
+      actions.begin(), actions.end());
+
+  if (view_)
+    view_->UpdateWithMediaActions(session_actions_);
+}
+
 void MediaNotificationItem::SetView(MediaNotificationView* view) {
   DCHECK(view_ || view);
 
@@ -78,6 +87,7 @@
     DCHECK(!session_info_.is_null());
     view_->UpdateWithMediaSessionInfo(session_info_);
     view_->UpdateWithMediaMetadata(session_metadata_);
+    view_->UpdateWithMediaActions(session_actions_);
   }
 }
 
diff --git a/ash/media/media_notification_item.h b/ash/media/media_notification_item.h
index ba06190..6b5b2781 100644
--- a/ash/media/media_notification_item.h
+++ b/ash/media/media_notification_item.h
@@ -5,6 +5,7 @@
 #ifndef ASH_MEDIA_MEDIA_NOTIFICATION_ITEM_H_
 #define ASH_MEDIA_MEDIA_NOTIFICATION_ITEM_H_
 
+#include <set>
 #include <string>
 
 #include "ash/ash_export.h"
@@ -34,7 +35,7 @@
       const base::Optional<media_session::MediaMetadata>& metadata) override;
   void MediaSessionActionsChanged(
       const std::vector<media_session::mojom::MediaSessionAction>& actions)
-      override {}
+      override;
 
   void SetView(MediaNotificationView* view);
 
@@ -66,6 +67,8 @@
 
   media_session::MediaMetadata session_metadata_;
 
+  std::set<media_session::mojom::MediaSessionAction> session_actions_;
+
   mojo::Binding<media_session::mojom::MediaSessionObserver> observer_binding_{
       this};
 
diff --git a/ash/media/media_notification_view.cc b/ash/media/media_notification_view.cc
index 3f308a5..b4f4a29 100644
--- a/ash/media/media_notification_view.cc
+++ b/ash/media/media_notification_view.cc
@@ -7,6 +7,7 @@
 #include "ash/media/media_notification_constants.h"
 #include "ash/media/media_notification_controller.h"
 #include "ash/shell.h"
+#include "base/stl_util.h"
 #include "components/vector_icons/vector_icons.h"
 #include "services/media_session/public/mojom/media_session.mojom.h"
 #include "ui/gfx/font.h"
@@ -207,9 +208,12 @@
   bool playing = session_info->playback_state ==
                  media_session::mojom::MediaPlaybackState::kPlaying;
   play_pause_button_->SetToggled(playing);
-  play_pause_button_->set_tag(
-      playing ? static_cast<int>(MediaSessionAction::kPause)
-              : static_cast<int>(MediaSessionAction::kPlay));
+
+  MediaSessionAction action =
+      playing ? MediaSessionAction::kPause : MediaSessionAction::kPlay;
+  play_pause_button_->set_tag(static_cast<int>(action));
+  play_pause_button_->SetVisible(IsActionButtonVisible(action));
+  PreferredSizeChanged();
 }
 
 void MediaNotificationView::UpdateWithMediaMetadata(
@@ -228,6 +232,13 @@
   artist_label_->SetVisible(!metadata.artist.empty());
 }
 
+void MediaNotificationView::UpdateWithMediaActions(
+    const std::set<media_session::mojom::MediaSessionAction>& actions) {
+  enabled_actions_ = actions;
+  UpdateActionButtonsVisibility();
+  PreferredSizeChanged();
+}
+
 void MediaNotificationView::UpdateControlButtonsVisibilityWithNotification(
     const message_center::Notification& notification) {
   // Media notifications do not use the settings and snooze buttons.
@@ -238,18 +249,31 @@
   UpdateControlButtonsVisibility();
 }
 
-void MediaNotificationView::UpdateViewForExpandedState() {
+bool MediaNotificationView::IsActionButtonVisible(
+    MediaSessionAction action) const {
+  // Not all media sessions support the same actions.
+  bool visible = base::ContainsKey(enabled_actions_, action);
+
   // We should reduce the number of action buttons we show when we are
   // collapsed.
+  // TODO(beccahughes): Use priority based ranking here
+  if (!expanded_ && visible)
+    visible = ShouldShowActionWhenCollapsed(action);
+
+  return visible;
+}
+
+void MediaNotificationView::UpdateActionButtonsVisibility() {
   for (int i = 0; i < button_row_->child_count(); ++i) {
     views::Button* action_button =
         views::Button::AsButton(button_row_->child_at(i));
 
-    action_button->SetVisible(expanded_ || ShouldShowActionWhenCollapsed(
-                                               static_cast<MediaSessionAction>(
-                                                   action_button->tag())));
+    action_button->SetVisible(IsActionButtonVisible(
+        static_cast<MediaSessionAction>(action_button->tag())));
   }
+}
 
+void MediaNotificationView::UpdateViewForExpandedState() {
   // Adjust the layout of the |main_row_| based on the expanded state. If the
   // notification is expanded then the buttons should be below the title/artist
   // information. If it is collapsed then the buttons will be to the right.
@@ -272,6 +296,8 @@
   }
 
   header_row_->SetExpanded(expanded_);
+
+  UpdateActionButtonsVisibility();
 }
 
 void MediaNotificationView::CreateMediaButton(const gfx::VectorIcon& icon,
diff --git a/ash/media/media_notification_view.h b/ash/media/media_notification_view.h
index 67e392a8..097d138 100644
--- a/ash/media/media_notification_view.h
+++ b/ash/media/media_notification_view.h
@@ -58,6 +58,8 @@
   void UpdateWithMediaSessionInfo(
       const media_session::mojom::MediaSessionInfoPtr& session_info);
   void UpdateWithMediaMetadata(const media_session::MediaMetadata& metadata);
+  void UpdateWithMediaActions(
+      const std::set<media_session::mojom::MediaSessionAction>& actions);
 
  private:
   friend class MediaNotificationViewTest;
@@ -70,6 +72,10 @@
   void CreateMediaButton(const gfx::VectorIcon& icon,
                          media_session::mojom::MediaSessionAction action);
 
+  bool IsActionButtonVisible(
+      media_session::mojom::MediaSessionAction action) const;
+
+  void UpdateActionButtonsVisibility();
   void UpdateViewForExpandedState();
 
   // View containing close and settings buttons.
@@ -79,6 +85,9 @@
   // Whether this notification is expanded or not.
   bool expanded_ = false;
 
+  // Set of enabled actions.
+  std::set<media_session::mojom::MediaSessionAction> enabled_actions_;
+
   // Container views directly attached to this view.
   message_center::NotificationHeaderView* header_row_ = nullptr;
   views::View* button_row_ = nullptr;
diff --git a/ash/media/media_notification_view_unittest.cc b/ash/media/media_notification_view_unittest.cc
index 298d38b..5b05946 100644
--- a/ash/media/media_notification_view_unittest.cc
+++ b/ash/media/media_notification_view_unittest.cc
@@ -117,6 +117,8 @@
   void TearDown() override {
     view_ = nullptr;
 
+    actions_.clear();
+
     message_center::MessageViewFactory::
         ClearCustomNotificationViewFactoryForTest(
             kMediaSessionNotificationCustomViewType);
@@ -147,6 +149,28 @@
     EXPECT_EQ(metadata.artist, artist_label()->text());
   }
 
+  void EnableAllActions() {
+    actions_.insert(MediaSessionAction::kPlay);
+    actions_.insert(MediaSessionAction::kPause);
+    actions_.insert(MediaSessionAction::kPreviousTrack);
+    actions_.insert(MediaSessionAction::kNextTrack);
+    actions_.insert(MediaSessionAction::kSeekBackward);
+    actions_.insert(MediaSessionAction::kSeekForward);
+    actions_.insert(MediaSessionAction::kStop);
+
+    NotifyUpdatedActions();
+  }
+
+  void EnableAction(MediaSessionAction action) {
+    actions_.insert(action);
+    NotifyUpdatedActions();
+  }
+
+  void DisableAction(MediaSessionAction action) {
+    actions_.erase(action);
+    NotifyUpdatedActions();
+  }
+
   MediaNotificationView* view() const { return view_; }
 
   TestMediaController* media_controller() const {
@@ -173,6 +197,10 @@
     return nullptr;
   }
 
+  bool IsActionButtonVisible(MediaSessionAction action) const {
+    return GetButtonForAction(action)->visible();
+  }
+
   MediaNotificationItem* GetItem() const {
     return Shell::Get()->media_notification_controller()->GetItem(
         request_id_.ToString());
@@ -190,10 +218,17 @@
     return view;
   }
 
+  void NotifyUpdatedActions() {
+    GetItem()->MediaSessionActionsChanged(
+        std::vector<MediaSessionAction>(actions_.begin(), actions_.end()));
+  }
+
   base::UnguessableToken request_id_;
 
   base::test::ScopedFeatureList scoped_feature_list_;
 
+  std::set<MediaSessionAction> actions_;
+
   std::unique_ptr<TestMediaController> media_controller_;
   std::unique_ptr<views::Widget> widget_;
   MediaNotificationView* view_ = nullptr;
@@ -224,6 +259,8 @@
 }
 
 TEST_F(MediaNotificationViewTest, ButtonsSanityCheck) {
+  EnableAllActions();
+
   EXPECT_TRUE(button_row()->visible());
   EXPECT_GT(button_row()->width(), 0);
   EXPECT_GT(button_row()->height(), 0);
@@ -251,6 +288,8 @@
 }
 
 TEST_F(MediaNotificationViewTest, NextTrackButtonClick) {
+  EnableAction(MediaSessionAction::kNextTrack);
+
   EXPECT_EQ(0, media_controller()->next_track_count());
 
   gfx::Point cursor_location(1, 1);
@@ -264,6 +303,8 @@
 }
 
 TEST_F(MediaNotificationViewTest, PlayButtonClick) {
+  EnableAction(MediaSessionAction::kPlay);
+
   EXPECT_EQ(0, media_controller()->resume_count());
 
   gfx::Point cursor_location(1, 1);
@@ -277,6 +318,8 @@
 }
 
 TEST_F(MediaNotificationViewTest, PauseButtonClick) {
+  EnableAction(MediaSessionAction::kPause);
+
   EXPECT_EQ(0, media_controller()->suspend_count());
 
   media_session::mojom::MediaSessionInfoPtr session_info(
@@ -297,6 +340,8 @@
 }
 
 TEST_F(MediaNotificationViewTest, PreviousTrackButtonClick) {
+  EnableAction(MediaSessionAction::kPreviousTrack);
+
   EXPECT_EQ(0, media_controller()->previous_track_count());
 
   gfx::Point cursor_location(1, 1);
@@ -310,6 +355,8 @@
 }
 
 TEST_F(MediaNotificationViewTest, SeekBackwardButtonClick) {
+  EnableAction(MediaSessionAction::kSeekBackward);
+
   EXPECT_EQ(0, media_controller()->seek_backward_count());
 
   gfx::Point cursor_location(1, 1);
@@ -323,6 +370,8 @@
 }
 
 TEST_F(MediaNotificationViewTest, SeekForwardButtonClick) {
+  EnableAction(MediaSessionAction::kSeekForward);
+
   EXPECT_EQ(0, media_controller()->seek_forward_count());
 
   gfx::Point cursor_location(1, 1);
@@ -384,34 +433,61 @@
 }
 
 TEST_F(MediaNotificationViewTest, PlayToggle_FromObserver_Empty) {
-  views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
-      GetButtonForAction(MediaSessionAction::kPlay));
-  ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
-  EXPECT_FALSE(button->toggled_for_testing());
+  EnableAction(MediaSessionAction::kPlay);
 
-  GetItem()->MediaSessionInfoChanged(
+  {
+    views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
+        GetButtonForAction(MediaSessionAction::kPlay));
+    ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
+    EXPECT_FALSE(button->toggled_for_testing());
+  }
+
+  view()->UpdateWithMediaSessionInfo(
       media_session::mojom::MediaSessionInfo::New());
-  EXPECT_FALSE(button->toggled_for_testing());
+
+  {
+    views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
+        GetButtonForAction(MediaSessionAction::kPlay));
+    ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
+    EXPECT_FALSE(button->toggled_for_testing());
+  }
 }
 
 TEST_F(MediaNotificationViewTest, PlayToggle_FromObserver_PlaybackState) {
-  views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
-      GetButtonForAction(MediaSessionAction::kPlay));
-  ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
-  EXPECT_FALSE(button->toggled_for_testing());
+  EnableAction(MediaSessionAction::kPlay);
+  EnableAction(MediaSessionAction::kPause);
+
+  {
+    views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
+        GetButtonForAction(MediaSessionAction::kPlay));
+    ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
+    EXPECT_FALSE(button->toggled_for_testing());
+  }
 
   media_session::mojom::MediaSessionInfoPtr session_info(
       media_session::mojom::MediaSessionInfo::New());
 
   session_info->playback_state =
       media_session::mojom::MediaPlaybackState::kPlaying;
-  GetItem()->MediaSessionInfoChanged(session_info.Clone());
-  EXPECT_TRUE(button->toggled_for_testing());
+  view()->UpdateWithMediaSessionInfo(session_info.Clone());
+
+  {
+    views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
+        GetButtonForAction(MediaSessionAction::kPause));
+    ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
+    EXPECT_TRUE(button->toggled_for_testing());
+  }
 
   session_info->playback_state =
       media_session::mojom::MediaPlaybackState::kPaused;
-  GetItem()->MediaSessionInfoChanged(session_info.Clone());
-  EXPECT_FALSE(button->toggled_for_testing());
+  view()->UpdateWithMediaSessionInfo(session_info.Clone());
+
+  {
+    views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
+        GetButtonForAction(MediaSessionAction::kPlay));
+    ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
+    EXPECT_FALSE(button->toggled_for_testing());
+  }
 }
 
 TEST_F(MediaNotificationViewTest, UpdateMetadata_FromObserver) {
@@ -469,6 +545,8 @@
 }
 
 TEST_F(MediaNotificationViewTest, Buttons_WhenCollapsed) {
+  EnableAllActions();
+
   media_session::MediaMetadata metadata;
   metadata.artist = base::ASCIIToUTF16("artist");
 
@@ -478,16 +556,28 @@
 
   EXPECT_FALSE(is_expanded());
 
-  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay)->visible());
-  EXPECT_TRUE(
-      GetButtonForAction(MediaSessionAction::kPreviousTrack)->visible());
-  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kNextTrack)->visible());
-  EXPECT_FALSE(
-      GetButtonForAction(MediaSessionAction::kSeekBackward)->visible());
-  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kSeekForward)->visible());
+  EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kPlay));
+  EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kPreviousTrack));
+  EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
+  EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekBackward));
+  EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekForward));
+
+  DisableAction(MediaSessionAction::kPreviousTrack);
+  EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kPreviousTrack));
+
+  EnableAction(MediaSessionAction::kPreviousTrack);
+  EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kPreviousTrack));
+
+  DisableAction(MediaSessionAction::kSeekForward);
+  EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekForward));
+
+  EnableAction(MediaSessionAction::kSeekForward);
+  EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekForward));
 }
 
 TEST_F(MediaNotificationViewTest, Buttons_WhenExpanded) {
+  EnableAllActions();
+
   media_session::MediaMetadata metadata;
   metadata.artist = base::ASCIIToUTF16("artist");
 
@@ -497,12 +587,11 @@
 
   EXPECT_TRUE(is_expanded());
 
-  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay)->visible());
-  EXPECT_TRUE(
-      GetButtonForAction(MediaSessionAction::kPreviousTrack)->visible());
-  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kNextTrack)->visible());
-  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekBackward)->visible());
-  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekForward)->visible());
+  EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kPlay));
+  EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kPreviousTrack));
+  EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
+  EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kSeekBackward));
+  EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kSeekForward));
 }
 
 TEST_F(MediaNotificationViewTest, ClickHeader_ToggleExpand) {
@@ -527,4 +616,24 @@
   EXPECT_TRUE(is_expanded());
 }
 
+TEST_F(MediaNotificationViewTest, ActionButtonsHiddenByDefault) {
+  EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kPlay));
+  EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
+  EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kPreviousTrack));
+  EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekForward));
+  EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekBackward));
+}
+
+TEST_F(MediaNotificationViewTest, ActionButtonsToggleVisbility) {
+  EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
+
+  EnableAction(MediaSessionAction::kNextTrack);
+
+  EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
+
+  DisableAction(MediaSessionAction::kNextTrack);
+
+  EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
+}
+
 }  // namespace ash
diff --git a/ash/metrics/time_to_first_present_recorder_test_api.h b/ash/metrics/time_to_first_present_recorder_test_api.h
index 1d39c01..0b1240d8 100644
--- a/ash/metrics/time_to_first_present_recorder_test_api.h
+++ b/ash/metrics/time_to_first_present_recorder_test_api.h
@@ -5,7 +5,7 @@
 #ifndef ASH_METRICS_TIME_TO_FIRST_PRESENT_RECORDER_TEST_API_H_
 #define ASH_METRICS_TIME_TO_FIRST_PRESENT_RECORDER_TEST_API_H_
 
-#include "ash/public/interfaces/time_to_first_present_recorder_test_api.mojom.h"
+#include "ash/public/interfaces/time_to_first_present_recorder_test_api.test-mojom.h"
 #include "base/macros.h"
 
 namespace ash {
diff --git a/ash/mojo_test_interface_factory.cc b/ash/mojo_test_interface_factory.cc
index 1d8f737..026e60d35 100644
--- a/ash/mojo_test_interface_factory.cc
+++ b/ash/mojo_test_interface_factory.cc
@@ -8,12 +8,12 @@
 
 #include "ash/login/login_screen_test_api.h"
 #include "ash/metrics/time_to_first_present_recorder_test_api.h"
-#include "ash/public/interfaces/login_screen_test_api.mojom.h"
-#include "ash/public/interfaces/shelf_test_api.mojom.h"
-#include "ash/public/interfaces/shell_test_api.mojom.h"
-#include "ash/public/interfaces/status_area_widget_test_api.mojom.h"
-#include "ash/public/interfaces/system_tray_test_api.mojom.h"
-#include "ash/public/interfaces/time_to_first_present_recorder_test_api.mojom.h"
+#include "ash/public/interfaces/login_screen_test_api.test-mojom.h"
+#include "ash/public/interfaces/shelf_test_api.test-mojom.h"
+#include "ash/public/interfaces/shell_test_api.test-mojom.h"
+#include "ash/public/interfaces/status_area_widget_test_api.test-mojom.h"
+#include "ash/public/interfaces/system_tray_test_api.test-mojom.h"
+#include "ash/public/interfaces/time_to_first_present_recorder_test_api.test-mojom.h"
 #include "ash/shelf/shelf_test_api.h"
 #include "ash/shell_test_api.h"
 #include "ash/system/status_area_widget_test_api.h"
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn
index f5984197..a4682ed 100644
--- a/ash/public/interfaces/BUILD.gn
+++ b/ash/public/interfaces/BUILD.gn
@@ -100,12 +100,12 @@
   testonly = true
   disable_variants = true
   sources = [
-    "login_screen_test_api.mojom",
-    "shelf_test_api.mojom",
-    "shell_test_api.mojom",
-    "status_area_widget_test_api.mojom",
-    "system_tray_test_api.mojom",
-    "time_to_first_present_recorder_test_api.mojom",
+    "login_screen_test_api.test-mojom",
+    "shelf_test_api.test-mojom",
+    "shell_test_api.test-mojom",
+    "status_area_widget_test_api.test-mojom",
+    "system_tray_test_api.test-mojom",
+    "time_to_first_present_recorder_test_api.test-mojom",
   ]
   deps = [
     "//components/account_id/interfaces",
diff --git a/ash/public/interfaces/login_screen.mojom b/ash/public/interfaces/login_screen.mojom
index ee8df7d..6e7f203 100644
--- a/ash/public/interfaces/login_screen.mojom
+++ b/ash/public/interfaces/login_screen.mojom
@@ -218,6 +218,9 @@
   // Sets if the guest button on the login shelf can be shown during OOBE.
   SetShowGuestButtonInOobe(bool show);
 
+  // Sets whether parent access button can be shown on the login shelf.
+  SetShowParentAccess(bool show);
+
   // Transitions focus to the shelf area. If |reverse|, focuses the status area.
   FocusLoginShelf(bool reverse);
 };
diff --git a/ash/public/interfaces/login_screen_test_api.mojom b/ash/public/interfaces/login_screen_test_api.test-mojom
similarity index 100%
rename from ash/public/interfaces/login_screen_test_api.mojom
rename to ash/public/interfaces/login_screen_test_api.test-mojom
diff --git a/ash/public/interfaces/shelf_test_api.mojom b/ash/public/interfaces/shelf_test_api.test-mojom
similarity index 100%
rename from ash/public/interfaces/shelf_test_api.mojom
rename to ash/public/interfaces/shelf_test_api.test-mojom
diff --git a/ash/public/interfaces/shell_test_api.mojom b/ash/public/interfaces/shell_test_api.test-mojom
similarity index 100%
rename from ash/public/interfaces/shell_test_api.mojom
rename to ash/public/interfaces/shell_test_api.test-mojom
diff --git a/ash/public/interfaces/status_area_widget_test_api.mojom b/ash/public/interfaces/status_area_widget_test_api.test-mojom
similarity index 100%
rename from ash/public/interfaces/status_area_widget_test_api.mojom
rename to ash/public/interfaces/status_area_widget_test_api.test-mojom
diff --git a/ash/public/interfaces/system_tray_test_api.mojom b/ash/public/interfaces/system_tray_test_api.test-mojom
similarity index 100%
rename from ash/public/interfaces/system_tray_test_api.mojom
rename to ash/public/interfaces/system_tray_test_api.test-mojom
diff --git a/ash/public/interfaces/time_to_first_present_recorder_test_api.mojom b/ash/public/interfaces/time_to_first_present_recorder_test_api.test-mojom
similarity index 100%
rename from ash/public/interfaces/time_to_first_present_recorder_test_api.mojom
rename to ash/public/interfaces/time_to_first_present_recorder_test_api.test-mojom
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn
index d156267..205a5d4 100644
--- a/ash/resources/vector_icons/BUILD.gn
+++ b/ash/resources/vector_icons/BUILD.gn
@@ -99,6 +99,7 @@
     "shelf_overflow.icon",
     "shelf_overflow_horizontal_dots.icon",
     "shelf_overview.icon",
+    "shelf_parent_access_button.icon",
     "shelf_position.icon",
     "shelf_shutdown_button.icon",
     "shelf_sign_out_button.icon",
diff --git a/ash/resources/vector_icons/shelf_parent_access_button.icon b/ash/resources/vector_icons/shelf_parent_access_button.icon
new file mode 100644
index 0000000..f87fa00
--- /dev/null
+++ b/ash/resources/vector_icons/shelf_parent_access_button.icon
@@ -0,0 +1,71 @@
+CANVAS_DIMENSIONS, 40,
+MOVE_TO, 30, 13.33f,
+R_H_LINE_TO, -1.67f,
+V_LINE_TO, 10,
+R_CUBIC_TO, 0, -4.6f, -3.73f, -8.33f, -8.33f, -8.33f,
+ARC_TO, 8.34f, 8.34f, 0, 0, 0, 11.67f, 10,
+R_V_LINE_TO, 3.33f,
+H_LINE_TO, 10,
+R_ARC_TO, 3.34f, 3.34f, 0, 0, 0, -3.33f, 3.33f,
+R_V_LINE_TO, 16.67f,
+R_CUBIC_TO, 0, 1.83f, 1.5f, 3.33f, 3.33f, 3.33f,
+R_H_LINE_TO, 20,
+R_CUBIC_TO, 1.83f, 0, 3.33f, -1.5f, 3.33f, -3.33f,
+V_LINE_TO, 16.67f,
+R_CUBIC_TO, 0, -1.83f, -1.5f, -3.33f, -3.33f, -3.33f,
+CLOSE,
+MOVE_TO, 15, 10,
+R_CUBIC_TO, 0, -2.77f, 2.23f, -5, 5, -5,
+R_CUBIC_TO, 2.77f, 0, 5, 2.23f, 5, 5,
+R_V_LINE_TO, 3.33f,
+H_LINE_TO, 15,
+V_LINE_TO, 10,
+CLOSE,
+R_MOVE_TO, 15, 23.33f,
+H_LINE_TO, 10,
+V_LINE_TO, 16.67f,
+R_H_LINE_TO, 20,
+R_V_LINE_TO, 16.67f,
+CLOSE,
+R_MOVE_TO, -10, -5,
+R_CUBIC_TO, 1.83f, 0, 3.33f, -1.5f, 3.33f, -3.33f,
+R_CUBIC_TO, 0, -1.83f, -1.5f, -3.33f, -3.33f, -3.33f,
+ARC_TO, 3.34f, 3.34f, 0, 0, 0, 16.67f, 25,
+R_CUBIC_TO, 0, 1.83f, 1.5f, 3.33f, 3.33f, 3.33f,
+CLOSE
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 15, 6.67f,
+R_H_LINE_TO, -0.83f,
+V_LINE_TO, 5,
+CUBIC_TO, 14.17f, 2.7f, 12.3f, 0.83f, 10, 0.83f,
+ARC_TO, 4.17f, 4.17f, 0, 0, 0, 5.83f, 5,
+R_V_LINE_TO, 1.67f,
+H_LINE_TO, 5,
+R_CUBIC_TO, -0.92f, 0, -1.67f, 0.75f, -1.67f, 1.67f,
+R_V_LINE_TO, 8.33f,
+R_CUBIC_TO, 0, 0.92f, 0.75f, 1.67f, 1.67f, 1.67f,
+R_H_LINE_TO, 10,
+R_CUBIC_TO, 0.92f, 0, 1.67f, -0.75f, 1.67f, -1.67f,
+V_LINE_TO, 8.33f,
+R_CUBIC_TO, 0, -0.92f, -0.75f, -1.67f, -1.67f, -1.67f,
+CLOSE,
+MOVE_TO, 7.5f, 5,
+R_CUBIC_TO, 0, -1.38f, 1.12f, -2.5f, 2.5f, -2.5f,
+R_CUBIC_TO, 1.38f, 0, 2.5f, 1.12f, 2.5f, 2.5f,
+R_V_LINE_TO, 1.67f,
+R_H_LINE_TO, -5,
+V_LINE_TO, 5,
+CLOSE,
+MOVE_TO, 15, 16.67f,
+H_LINE_TO, 5,
+V_LINE_TO, 8.33f,
+R_H_LINE_TO, 10,
+R_V_LINE_TO, 8.33f,
+CLOSE,
+R_MOVE_TO, -5, -2.5f,
+R_CUBIC_TO, 0.92f, 0, 1.67f, -0.75f, 1.67f, -1.67f,
+R_CUBIC_TO, 0, -0.92f, -0.75f, -1.67f, -1.67f, -1.67f,
+R_CUBIC_TO, -0.92f, 0, -1.67f, 0.75f, -1.67f, 1.67f,
+R_CUBIC_TO, 0, 0.92f, 0.75f, 1.67f, 1.67f, 1.67f,
+CLOSE
diff --git a/ash/shelf/login_shelf_view.cc b/ash/shelf/login_shelf_view.cc
index eac4157..12e6575 100644
--- a/ash/shelf/login_shelf_view.cc
+++ b/ash/shelf/login_shelf_view.cc
@@ -386,6 +386,8 @@
   add_button(kBrowseAsGuest, IDS_ASH_BROWSE_AS_GUEST_BUTTON,
              kShelfBrowseAsGuestButtonIcon);
   add_button(kAddUser, IDS_ASH_ADD_USER_BUTTON, kShelfAddPersonButtonIcon);
+  add_button(kParentAccess, IDS_ASH_PARENT_ACCESS_BUTTON,
+             kShelfParentAccessButtonIcon);
 
   // Adds observers for states that affect the visiblity of different buttons.
   tray_action_observer_.Add(Shell::Get()->tray_action());
@@ -506,6 +508,11 @@
   UpdateUi();
 }
 
+void LoginShelfView::SetShowParentAccess(bool show) {
+  show_parent_access_ = show;
+  UpdateUi();
+}
+
 void LoginShelfView::SetShowGuestButtonInOobe(bool show) {
   allow_guest_in_oobe_ = show;
   UpdateUi();
@@ -563,6 +570,7 @@
   bool show_reboot = Shell::Get()->shutdown_controller()->reboot_on_shutdown();
   mojom::TrayActionState tray_action_state =
       Shell::Get()->tray_action()->GetLockScreenNoteState();
+  bool is_locked = (session_state == SessionState::LOCKED);
   bool is_lock_screen_note_in_foreground =
       (tray_action_state == mojom::TrayActionState::kActive ||
        tray_action_state == mojom::TrayActionState::kLaunching) &&
@@ -573,13 +581,13 @@
                                      !is_lock_screen_note_in_foreground);
   GetViewByID(kRestart)->SetVisible(show_reboot &&
                                     !is_lock_screen_note_in_foreground);
-  GetViewByID(kSignOut)->SetVisible(session_state == SessionState::LOCKED &&
+  GetViewByID(kSignOut)->SetVisible(is_locked &&
                                     !is_lock_screen_note_in_foreground);
   GetViewByID(kCloseNote)
-      ->SetVisible(session_state == SessionState::LOCKED &&
-                   is_lock_screen_note_in_foreground);
+      ->SetVisible(is_locked && is_lock_screen_note_in_foreground);
   GetViewByID(kCancel)->SetVisible(session_state ==
                                    SessionState::LOGIN_SECONDARY);
+  GetViewByID(kParentAccess)->SetVisible(is_locked && show_parent_access_);
 
   bool is_login_primary = (session_state == SessionState::LOGIN_PRIMARY);
   bool dialog_visible = dialog_state_ != mojom::OobeDialogState::HIDDEN;
diff --git a/ash/shelf/login_shelf_view.h b/ash/shelf/login_shelf_view.h
index 1dd6e459..95e6cc32 100644
--- a/ash/shelf/login_shelf_view.h
+++ b/ash/shelf/login_shelf_view.h
@@ -50,14 +50,15 @@
                                   public LoginDataDispatcher::Observer {
  public:
   enum ButtonId {
-    kShutdown = 1,    // Shut down the device.
-    kRestart,         // Restart the device.
-    kSignOut,         // Sign out the active user session.
-    kCloseNote,       // Close the lock screen note.
-    kCancel,          // Cancel multiple user sign-in.
-    kBrowseAsGuest,   // Use in guest mode.
-    kAddUser,         // Add a new user.
-    kApps,            // Show list of available kiosk apps.
+    kShutdown = 1,   // Shut down the device.
+    kRestart,        // Restart the device.
+    kSignOut,        // Sign out the active user session.
+    kCloseNote,      // Close the lock screen note.
+    kCancel,         // Cancel multiple user sign-in.
+    kBrowseAsGuest,  // Use in guest mode.
+    kAddUser,        // Add a new user.
+    kApps,           // Show list of available kiosk apps.
+    kParentAccess    // Unlock child device with Parent Access Code.
   };
 
   explicit LoginShelfView(
@@ -78,6 +79,9 @@
   // true the button may still not be visible.
   void SetAllowLoginAsGuest(bool allow_guest);
 
+  // Sets whether parent access button can be shown on the login shelf.
+  void SetShowParentAccess(bool show);
+
   // Sets if the guest button on the login shelf can be shown during gaia
   // signin screen.
   void SetShowGuestButtonInOobe(bool show);
@@ -125,7 +129,7 @@
   mojom::OobeDialogState dialog_state_ = mojom::OobeDialogState::HIDDEN;
   bool allow_guest_ = true;
   bool allow_guest_in_oobe_ = false;
-
+  bool show_parent_access_ = false;
   // When the Gaia screen is active during Login, the guest-login button should
   // appear if there are no user views.
   bool login_screen_has_users_ = false;
diff --git a/ash/shelf/login_shelf_view_unittest.cc b/ash/shelf/login_shelf_view_unittest.cc
index 71b269b..6b7ad1f 100644
--- a/ash/shelf/login_shelf_view_unittest.cc
+++ b/ash/shelf/login_shelf_view_unittest.cc
@@ -10,6 +10,7 @@
 #include "ash/focus_cycler.h"
 #include "ash/lock_screen_action/lock_screen_action_background_controller.h"
 #include "ash/lock_screen_action/test_lock_screen_action_background_controller.h"
+#include "ash/login/login_screen_controller.h"
 #include "ash/login/mock_login_screen_client.h"
 #include "ash/login/ui/login_test_base.h"
 #include "ash/login/ui/views_utils.h"
@@ -519,5 +520,48 @@
   EXPECT_TRUE(IsButtonEnabled(LoginShelfView::kShutdown));
 }
 
+TEST_F(LoginShelfViewTest, ParentAccessButtonVisibility) {
+  // Parent access button should only be visible on lock screen.
+  Shell::Get()->login_screen_controller()->SetShowParentAccess(true);
+
+  NotifySessionStateChanged(SessionState::LOGIN_PRIMARY);
+  EXPECT_TRUE(ShowsShelfButtons({LoginShelfView::kShutdown,
+                                 LoginShelfView::kBrowseAsGuest,
+                                 LoginShelfView::kAddUser}));
+
+  NotifySessionStateChanged(SessionState::LOGGED_IN_NOT_ACTIVE);
+  EXPECT_TRUE(ShowsShelfButtons({LoginShelfView::kShutdown}));
+
+  NotifySessionStateChanged(SessionState::ACTIVE);
+  EXPECT_TRUE(ShowsShelfButtons({}));
+
+  NotifySessionStateChanged(SessionState::LOGIN_SECONDARY);
+  EXPECT_TRUE(
+      ShowsShelfButtons({LoginShelfView::kShutdown, LoginShelfView::kCancel}));
+
+  NotifySessionStateChanged(SessionState::ACTIVE);
+  EXPECT_TRUE(ShowsShelfButtons({}));
+
+  NotifySessionStateChanged(SessionState::LOCKED);
+  EXPECT_TRUE(
+      ShowsShelfButtons({LoginShelfView::kShutdown, LoginShelfView::kSignOut,
+                         LoginShelfView::kParentAccess}));
+}
+
+TEST_F(LoginShelfViewTest, ParentAccessButtonVisibilityChangeOnLockScreen) {
+  NotifySessionStateChanged(SessionState::LOCKED);
+  EXPECT_TRUE(
+      ShowsShelfButtons({LoginShelfView::kShutdown, LoginShelfView::kSignOut}));
+
+  Shell::Get()->login_screen_controller()->SetShowParentAccess(true);
+  EXPECT_TRUE(
+      ShowsShelfButtons({LoginShelfView::kShutdown, LoginShelfView::kSignOut,
+                         LoginShelfView::kParentAccess}));
+
+  Shell::Get()->login_screen_controller()->SetShowParentAccess(false);
+  EXPECT_TRUE(
+      ShowsShelfButtons({LoginShelfView::kShutdown, LoginShelfView::kSignOut}));
+}
+
 }  // namespace
 }  // namespace ash
diff --git a/ash/shelf/shelf_test_api.h b/ash/shelf/shelf_test_api.h
index 6f01ef4..7c5d702e 100644
--- a/ash/shelf/shelf_test_api.h
+++ b/ash/shelf/shelf_test_api.h
@@ -5,7 +5,7 @@
 #ifndef ASH_SHELF_SHELF_TEST_API_H_
 #define ASH_SHELF_SHELF_TEST_API_H_
 
-#include "ash/public/interfaces/shelf_test_api.mojom.h"
+#include "ash/public/interfaces/shelf_test_api.test-mojom.h"
 #include "base/macros.h"
 
 namespace ash {
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index aee4d3a..8d86563 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -483,16 +483,26 @@
   return nullptr;
 }
 
-bool ShelfView::ShouldHideTooltip(const gfx::Point& cursor_location) const {
-  gfx::Rect tooltip_bounds;
-  for (int i = 0; i < child_count(); ++i) {
-    const views::View* child = child_at(i);
+void ShelfView::UpdateVisibleShelfItemBoundsUnion() {
+  visible_shelf_item_bounds_union_.SetRect(0, 0, 0, 0);
+  for (int i = first_visible_index_; i <= last_visible_index_; ++i) {
+    const views::View* child = view_model_->view_at(i);
     if (!IsTabletModeEnabled() && child == GetBackButton())
       continue;
-    if (child != overflow_button_ && ShouldShowTooltipForView(child))
-      tooltip_bounds.Union(child->GetMirroredBounds());
+    if (ShouldShowTooltipForView(child))
+      visible_shelf_item_bounds_union_.Union(child->GetMirroredBounds());
   }
-  return !tooltip_bounds.Contains(cursor_location);
+}
+
+bool ShelfView::ShouldHideTooltip(const gfx::Point& cursor_location) const {
+  // Hide the tooltip if this is the app list button and the list is showing.
+  const AppListButton* app_list_button = GetAppListButton();
+  if (app_list_button &&
+      app_list_button->GetMirroredBounds().Contains(cursor_location) &&
+      app_list_button->is_showing_app_list()) {
+    return true;
+  }
+  return !visible_shelf_item_bounds_union_.Contains(cursor_location);
 }
 
 bool ShelfView::ShouldShowTooltipForView(const views::View* view) const {
@@ -928,6 +938,7 @@
   overflow_button_->SetBoundsRect(overflow_bounds);
   UpdateBackButton();
   LayoutAppListAndBackButtonHighlight();
+  UpdateVisibleShelfItemBoundsUnion();
 }
 
 bool ShelfView::IsItemPinned(const ShelfItem& item) const {
@@ -1187,6 +1198,7 @@
   }
   overflow_button_->SetBoundsRect(overflow_bounds);
   LayoutAppListAndBackButtonHighlight();
+  UpdateVisibleShelfItemBoundsUnion();
 }
 
 views::View* ShelfView::CreateViewForItem(const ShelfItem& item) {
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index 87eb8f0..355df80 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -151,6 +151,11 @@
   AppListButton* GetAppListButton() const;
   BackButton* GetBackButton() const;
 
+  // Updates the union of all the shelf item bounds shown by this shelf view.
+  // This is used to determine the common area where the mouse can hover
+  // for showing tooltips without stuttering over gaps.
+  void UpdateVisibleShelfItemBoundsUnion();
+
   // Returns true if the mouse cursor exits the area for launcher tooltip.
   // There are thin gaps between launcher buttons but the tooltip shouldn't hide
   // in the gaps, but the tooltip should hide if the mouse moved totally outside
@@ -597,6 +602,10 @@
   // items.
   views::Separator* separator_ = nullptr;
 
+  // The union of all visible shelf item bounds. Used for showing tooltips in
+  // a continuous manner.
+  gfx::Rect visible_shelf_item_bounds_union_;
+
   // A view to draw a background behind the app list and back buttons.
   // Owned by the view hierarchy.
   views::View* back_and_app_list_background_ = nullptr;
diff --git a/ash/shell.cc b/ash/shell.cc
index 81f97594..4be8d81a 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -22,7 +22,7 @@
 #include "ash/assistant/assistant_controller.h"
 #include "ash/autoclick/autoclick_controller.h"
 #include "ash/cast_config_controller.h"
-#include "ash/components/tap_visualizer/public/mojom/constants.mojom.h"
+#include "ash/components/tap_visualizer/public/mojom/tap_visualizer.mojom.h"
 #include "ash/contained_shell/contained_shell_controller.h"
 #include "ash/dbus/ash_dbus_services.h"
 #include "ash/detachable_base/detachable_base_handler.h"
@@ -1262,9 +1262,11 @@
       base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kShowTaps)) {
     // The show taps feature is a separate service.
     // TODO(jamescook): Make this work in ash_shell_with_content.
-    // TODO(https://crbug.com/904148): This should not use |WarmService()|.
-    connector_->WarmService(service_manager::ServiceFilter::ByName(
-        tap_visualizer::mojom::kServiceName));
+    tap_visualizer::mojom::TapVisualizerPtr tap_visualizer_ptr;
+    connector_->BindInterface(service_manager::ServiceFilter::ByName(
+                                  tap_visualizer::mojom::kServiceName),
+                              mojo::MakeRequest(&tap_visualizer_ptr));
+    tap_visualizer_ptr->Show();
   }
 
   if (!::features::IsMultiProcessMash()) {
diff --git a/ash/shell/ash_content_browser_manifest_overlay.json b/ash/shell/ash_content_browser_manifest_overlay.json
index 52d105d..9f0f4d0 100644
--- a/ash/shell/ash_content_browser_manifest_overlay.json
+++ b/ash/shell/ash_content_browser_manifest_overlay.json
@@ -4,8 +4,9 @@
   "interface_provider_specs": {
     "service_manager:connector": {
       "requires": {
+        "device": [ "device:fingerprint" ],
         "shortcut_viewer_app": [ "shortcut_viewer" ],
-        "device": [ "device:fingerprint" ]
+        "tap_visualizer_app": [ "tap_visualizer" ]
       }
     }
   }
diff --git a/ash/shell/content/client/shell_browser_main_parts.cc b/ash/shell/content/client/shell_browser_main_parts.cc
index 246ac97b..da86a59e 100644
--- a/ash/shell/content/client/shell_browser_main_parts.cc
+++ b/ash/shell/content/client/shell_browser_main_parts.cc
@@ -9,7 +9,7 @@
 
 #include "ash/components/quick_launch/public/mojom/constants.mojom.h"
 #include "ash/components/shortcut_viewer/public/mojom/shortcut_viewer.mojom.h"
-#include "ash/components/tap_visualizer/public/mojom/constants.mojom.h"
+#include "ash/components/tap_visualizer/public/mojom/tap_visualizer.mojom.h"
 #include "ash/keyboard/test_keyboard_ui.h"
 #include "ash/login_status.h"
 #include "ash/shell.h"
diff --git a/ash/shell/content/client/shell_content_browser_client.cc b/ash/shell/content/client/shell_content_browser_client.cc
index 1e393c3a..d37d07b 100644
--- a/ash/shell/content/client/shell_content_browser_client.cc
+++ b/ash/shell/content/client/shell_content_browser_client.cc
@@ -9,7 +9,7 @@
 #include "ash/ash_service.h"
 #include "ash/components/quick_launch/public/mojom/constants.mojom.h"
 #include "ash/components/shortcut_viewer/public/mojom/shortcut_viewer.mojom.h"
-#include "ash/components/tap_visualizer/public/mojom/constants.mojom.h"
+#include "ash/components/tap_visualizer/public/mojom/tap_visualizer.mojom.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/public/interfaces/constants.mojom.h"
 #include "ash/shell.h"
diff --git a/ash/shell/content/client/shell_main_delegate.cc b/ash/shell/content/client/shell_main_delegate.cc
index 0328236..87d7874 100644
--- a/ash/shell/content/client/shell_main_delegate.cc
+++ b/ash/shell/content/client/shell_main_delegate.cc
@@ -8,7 +8,7 @@
 #include "ash/components/quick_launch/quick_launch_application.h"
 #include "ash/components/shortcut_viewer/public/mojom/shortcut_viewer.mojom.h"
 #include "ash/components/shortcut_viewer/shortcut_viewer_application.h"
-#include "ash/components/tap_visualizer/public/mojom/constants.mojom.h"
+#include "ash/components/tap_visualizer/public/mojom/tap_visualizer.mojom.h"
 #include "ash/components/tap_visualizer/tap_visualizer_app.h"
 #include "ash/shell/content/client/shell_content_browser_client.h"
 #include "base/command_line.h"
diff --git a/ash/shell_test_api.h b/ash/shell_test_api.h
index 0b4117c..1dcfb91b 100644
--- a/ash/shell_test_api.h
+++ b/ash/shell_test_api.h
@@ -7,7 +7,7 @@
 
 #include <memory>
 
-#include "ash/public/interfaces/shell_test_api.mojom.h"
+#include "ash/public/interfaces/shell_test_api.test-mojom.h"
 #include "base/macros.h"
 #include "services/ws/common/types.h"
 
diff --git a/ash/system/network/network_icon_unittest.cc b/ash/system/network/network_icon_unittest.cc
index 060200f..36aba2a 100644
--- a/ash/system/network/network_icon_unittest.cc
+++ b/ash/system/network/network_icon_unittest.cc
@@ -26,6 +26,14 @@
 
 namespace network_icon {
 
+namespace {
+
+const char kShillManagerClientStubWifiDevice[] = "/device/stub_wifi_device1";
+const char kShillManagerClientStubCellularDevice[] =
+    "/device/stub_cellular_device1";
+
+}  // namespace
+
 class NetworkIconTest : public chromeos::NetworkStateTest {
  public:
   NetworkIconTest() = default;
@@ -33,43 +41,72 @@
 
   void SetUp() override {
     chromeos::DBusThreadManager::Initialize();
-    chromeos::NetworkStateTest::SetUp();
+    NetworkStateTest::SetUp();
+    SetUpDefaultNetworkState();
 
     chromeos::NetworkHandler::Initialize();
-    handler_ = chromeos::NetworkHandler::Get()->network_state_handler();
-
-    tether_network_ =
-        std::make_unique<chromeos::NetworkState>("tetherNetworkPath");
-    tether_network_->set_type(chromeos::kTypeTether);
-
-    wifi_network_ = std::make_unique<chromeos::NetworkState>("wifiServicePath");
-    wifi_network_->set_type(shill::kTypeWifi);
-
-    cellular_network_ =
-        std::make_unique<chromeos::NetworkState>("cellularServicePath");
-    cellular_network_->set_type(shill::kTypeCellular);
-
-    wifi_tether_network_ =
-        std::make_unique<chromeos::NetworkState>("wifiTetherServicePath");
-    wifi_tether_network_->set_type(shill::kTypeWifi);
-    wifi_tether_network_.get()->set_tether_guid("tetherNetworkGuid");
-
-    ethernet_network_ =
-        std::make_unique<chromeos::NetworkState>("ethernetNetworkPath");
-    ethernet_network_->set_type(shill::kTypeEthernet);
+    base::RunLoop().RunUntilIdle();
   }
 
   void TearDown() override {
     PurgeNetworkIconCache();
-
     chromeos::NetworkHandler::Shutdown();
 
     ShutdownNetworkState();
     chromeos::NetworkStateTest::TearDown();
+
     chromeos::DBusThreadManager::Shutdown();
   }
 
-  gfx::Image ImageForNetwork(chromeos::NetworkState* network) {
+  void SetUpDefaultNetworkState() {
+    base::RunLoop().RunUntilIdle();  // Process any pending updates
+    device_test_ = chromeos::DBusThreadManager::Get()
+                       ->GetShillDeviceClient()
+                       ->GetTestInterface();
+    ASSERT_TRUE(device_test_);
+    device_test_->ClearDevices();
+    device_test_->AddDevice(kShillManagerClientStubWifiDevice, shill::kTypeWifi,
+                            "stub_wifi_device1");
+    device_test_->AddDevice(kShillManagerClientStubCellularDevice,
+                            shill::kTypeCellular, "stub_cellular_device1");
+
+    ClearDefaultServices();
+
+    wifi1_path_ = ConfigureService(
+        R"({"GUID": "wifi1_guid", "Type": "wifi", "State": "idle"})");
+    wifi2_path_ = ConfigureService(
+        R"({"GUID": "wifi2_guid", "Type": "wifi", "State": "idle"})");
+    cellular_path_ = ConfigureService(
+        R"({"GUID": "cellular_guid", "Type": "cellular", "Technology": "LTE",
+            "State": "idle"})");
+  }
+
+  std::unique_ptr<chromeos::NetworkState> CreateStandaloneNetworkState(
+      const std::string& id,
+      const std::string& type,
+      const std::string& connection_state,
+      int signal_strength) {
+    auto network = std::make_unique<chromeos::NetworkState>(id);
+    network->set_type(type);
+    network->set_visible(true);
+    network->set_connection_state(connection_state);
+    network->set_signal_strength(signal_strength);
+    return network;
+  }
+
+  std::unique_ptr<chromeos::NetworkState>
+  CreateStandaloneWifiTetherNetworkState(const std::string& id,
+                                         const std::string& tether_guid,
+                                         const std::string& connection_state,
+                                         int signal_strength) {
+    std::unique_ptr<chromeos::NetworkState> network =
+        CreateStandaloneNetworkState(id, shill::kTypeWifi, connection_state,
+                                     signal_strength);
+    network->set_tether_guid(tether_guid);
+    return network;
+  }
+
+  gfx::Image ImageForNetwork(const chromeos::NetworkState* network) {
     gfx::ImageSkia image_skia = GetImageForNetwork(network, icon_type_);
     return gfx::Image(image_skia);
   }
@@ -79,11 +116,23 @@
   // Wi-Fi network. The icon for a cellular network should be different from one
   // for a Wi-Fi network. The icon for a Tether network should be the same as
   // one for a Wi-Fi network with an associated Tether guid.
-  void GetAndCompareImagesByNetworkType() {
-    gfx::Image tether_image = ImageForNetwork(tether_network_.get());
-    gfx::Image wifi_image = ImageForNetwork(wifi_network_.get());
-    gfx::Image cellular_image = ImageForNetwork(cellular_network_.get());
-    gfx::Image wifi_tether_image = ImageForNetwork(wifi_tether_network_.get());
+  void GetAndCompareImagesByNetworkType(
+      const chromeos::NetworkState* wifi_network,
+      const chromeos::NetworkState* cellular_network,
+      const chromeos::NetworkState* tether_network,
+      const chromeos::NetworkState* wifi_tether_network) {
+    ASSERT_EQ(wifi_network->type(), shill::kTypeWifi);
+    gfx::Image wifi_image = ImageForNetwork(wifi_network);
+
+    ASSERT_EQ(cellular_network->type(), shill::kTypeCellular);
+    gfx::Image cellular_image = ImageForNetwork(cellular_network);
+
+    ASSERT_EQ(tether_network->type(), chromeos::kTypeTether);
+    gfx::Image tether_image = ImageForNetwork(tether_network);
+
+    ASSERT_EQ(wifi_tether_network->type(), shill::kTypeWifi);
+    ASSERT_FALSE(wifi_tether_network->tether_guid().empty());
+    gfx::Image wifi_tether_image = ImageForNetwork(wifi_tether_network);
 
     EXPECT_FALSE(gfx::test::AreImagesEqual(tether_image, wifi_image));
     EXPECT_FALSE(gfx::test::AreImagesEqual(cellular_image, wifi_image));
@@ -99,7 +148,8 @@
 
     ASSERT_EQ(
         chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
-        handler_->GetTechnologyState(chromeos::NetworkTypePattern::Cellular()));
+        network_state_handler()->GetTechnologyState(
+            chromeos::NetworkTypePattern::Cellular()));
   }
 
   void SetCellularUninitialized() {
@@ -110,27 +160,28 @@
 
     base::RunLoop().RunUntilIdle();
 
-    ASSERT_EQ(
-        chromeos::NetworkStateHandler::TechnologyState::
-            TECHNOLOGY_UNINITIALIZED,
-        handler_->GetTechnologyState(chromeos::NetworkTypePattern::Cellular()));
+    ASSERT_EQ(chromeos::NetworkStateHandler::TechnologyState::
+                  TECHNOLOGY_UNINITIALIZED,
+              network_state_handler()->GetTechnologyState(
+                  chromeos::NetworkTypePattern::Cellular()));
   }
 
-  const base::MessageLoop message_loop_;
+  const std::string& wifi1_path() const { return wifi1_path_; }
+  const std::string& wifi2_path() const { return wifi2_path_; }
+  const std::string& cellular_path() const { return cellular_path_; }
 
   IconType icon_type_ = ICON_TYPE_TRAY_REGULAR;
 
-  chromeos::NetworkStateHandler* handler_;
-
-  std::unique_ptr<chromeos::NetworkState> tether_network_;
-  std::unique_ptr<chromeos::NetworkState> wifi_network_;
-  std::unique_ptr<chromeos::NetworkState> cellular_network_;
-  // A network whose type is shill::kTypeWifi, but which is associated with
-  // a Tether network via its Tether network ID.
-  std::unique_ptr<chromeos::NetworkState> wifi_tether_network_;
-  std::unique_ptr<chromeos::NetworkState> ethernet_network_;
+  chromeos::ShillDeviceClient::TestInterface* device_test_;
 
  private:
+  const base::MessageLoop message_loop_;
+
+  // Preconfigured service paths:
+  std::string wifi1_path_;
+  std::string wifi2_path_;
+  std::string cellular_path_;
+
   DISALLOW_COPY_AND_ASSIGN(NetworkIconTest);
 };
 
@@ -141,39 +192,76 @@
 // verifies that the Tether network and Wi-Fi network with associated Tether
 // guid are treated the same for purposes of icon display
 TEST_F(NetworkIconTest, CompareImagesByNetworkType_NotVisible) {
-  GetAndCompareImagesByNetworkType();
+  std::unique_ptr<chromeos::NetworkState> wifi_network =
+      CreateStandaloneNetworkState("wifi", shill::kTypeWifi, shill::kStateIdle,
+                                   50);
+
+  std::unique_ptr<chromeos::NetworkState> cellular_network =
+      CreateStandaloneNetworkState("cellular", shill::kTypeCellular,
+                                   shill::kStateIdle, 50);
+
+  std::unique_ptr<chromeos::NetworkState> wimax_network =
+      CreateStandaloneNetworkState("wimax", shill::kTypeWimax,
+                                   shill::kStateIdle, 50);
+
+  EXPECT_TRUE(gfx::test::AreImagesEqual(ImageForNetwork(cellular_network.get()),
+                                        ImageForNetwork(wimax_network.get())));
+
+  std::unique_ptr<chromeos::NetworkState> tether_network =
+      CreateStandaloneNetworkState("tether", chromeos::kTypeTether,
+                                   shill::kStateIdle, 50);
+
+  std::unique_ptr<chromeos::NetworkState> wifi_tether_network =
+      CreateStandaloneWifiTetherNetworkState("wifi_tether", "tether",
+                                             shill::kStateIdle, 50);
+
+  GetAndCompareImagesByNetworkType(wifi_network.get(), cellular_network.get(),
+                                   tether_network.get(),
+                                   wifi_tether_network.get());
 }
 
 TEST_F(NetworkIconTest, CompareImagesByNetworkType_Connecting) {
-  tether_network_->set_visible(true);
-  tether_network_->set_connection_state(shill::kStateAssociation);
+  std::unique_ptr<chromeos::NetworkState> wifi_network =
+      CreateStandaloneNetworkState("wifi", shill::kTypeWifi,
+                                   shill::kStateAssociation, 50);
 
-  wifi_network_->set_visible(true);
-  wifi_network_->set_connection_state(shill::kStateAssociation);
+  std::unique_ptr<chromeos::NetworkState> cellular_network =
+      CreateStandaloneNetworkState("cellular", shill::kTypeCellular,
+                                   shill::kStateAssociation, 50);
 
-  cellular_network_->set_visible(true);
-  cellular_network_->set_connection_state(shill::kStateAssociation);
+  std::unique_ptr<chromeos::NetworkState> tether_network =
+      CreateStandaloneNetworkState("tether", chromeos::kTypeTether,
+                                   shill::kStateAssociation, 50);
 
-  wifi_tether_network_->set_visible(true);
-  wifi_tether_network_->set_connection_state(shill::kStateAssociation);
+  std::unique_ptr<chromeos::NetworkState> wifi_tether_network =
+      CreateStandaloneWifiTetherNetworkState("wifi_tether", "tether",
+                                             shill::kStateAssociation, 50);
 
-  GetAndCompareImagesByNetworkType();
+  GetAndCompareImagesByNetworkType(wifi_network.get(), cellular_network.get(),
+                                   tether_network.get(),
+                                   wifi_tether_network.get());
 }
 
 TEST_F(NetworkIconTest, CompareImagesByNetworkType_Connected) {
-  tether_network_->set_visible(true);
-  tether_network_->set_connection_state(shill::kStateOnline);
+  std::unique_ptr<chromeos::NetworkState> wifi_network =
+      CreateStandaloneNetworkState("wifi", shill::kTypeWifi,
+                                   shill::kStateOnline, 50);
 
-  wifi_network_->set_visible(true);
-  wifi_network_->set_connection_state(shill::kStateOnline);
+  std::unique_ptr<chromeos::NetworkState> cellular_network =
+      CreateStandaloneNetworkState("cellular", shill::kTypeCellular,
+                                   shill::kStateOnline, 50);
 
-  cellular_network_->set_visible(true);
-  cellular_network_->set_connection_state(shill::kStateOnline);
+  std::unique_ptr<chromeos::NetworkState> tether_network =
+      CreateStandaloneNetworkState("tether", chromeos::kTypeTether,
+                                   shill::kStateOnline, 50);
 
-  wifi_tether_network_->set_visible(true);
-  wifi_tether_network_->set_connection_state(shill::kStateOnline);
+  std::unique_ptr<chromeos::NetworkState> wifi_tether_network =
+      CreateStandaloneWifiTetherNetworkState("wifi_tether", "tether",
+                                             shill::kStateOnline, 50);
 
-  GetAndCompareImagesByNetworkType();
+  GetAndCompareImagesByNetworkType(wifi_network.get(), cellular_network.get(),
+                                   tether_network.get(),
+                                   wifi_tether_network.get());
 }
 
 TEST_F(NetworkIconTest,
@@ -193,16 +281,12 @@
 
   test_manager_client()->AddTechnology(shill::kTypeCellular, true);
 
-  chromeos::DBusThreadManager* dbus_manager =
-      chromeos::DBusThreadManager::Get();
-  chromeos::ShillDeviceClient::TestInterface* device_test =
-      dbus_manager->GetShillDeviceClient()->GetTestInterface();
-
-  device_test->SetDeviceProperty("/device/cellular1", shill::kScanningProperty,
-                                 base::Value(true), /*notify_changed=*/true);
+  device_test_->SetDeviceProperty(kShillManagerClientStubCellularDevice,
+                                  shill::kScanningProperty, base::Value(true),
+                                  /*notify_changed=*/true);
   base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(
-      handler_->GetScanningByType(chromeos::NetworkTypePattern::Cellular()));
+  ASSERT_TRUE(network_state_handler()->GetScanningByType(
+      chromeos::NetworkTypePattern::Cellular()));
 
   EXPECT_EQ(IDS_ASH_STATUS_TRAY_MOBILE_SCANNING, GetCellularUninitializedMsg());
 }
@@ -210,30 +294,420 @@
 TEST_F(NetworkIconTest, NetworkSignalStrength) {
   using ss = SignalStrength;
 
+  std::unique_ptr<chromeos::NetworkState> ethernet_network =
+      CreateStandaloneNetworkState("eth", shill::kTypeEthernet,
+                                   shill::kStateOnline, 50);
+
+  std::unique_ptr<chromeos::NetworkState> wifi_network =
+      CreateStandaloneNetworkState("wifi", shill::kTypeWifi,
+                                   shill::kStateOnline, 50);
+
   // Verify non-wirless network types return SignalStrength::NOT_WIRELESS, and
   // wireless network types return something other than
   // SignalStrength::NOT_WIRELESS.
   EXPECT_EQ(ss::NOT_WIRELESS,
-            GetSignalStrengthForNetwork(ethernet_network_.get()));
-  EXPECT_NE(ss::NOT_WIRELESS, GetSignalStrengthForNetwork(wifi_network_.get()));
+            GetSignalStrengthForNetwork(ethernet_network.get()));
+  EXPECT_NE(ss::NOT_WIRELESS, GetSignalStrengthForNetwork(wifi_network.get()));
 
   // Signal strength is divided into four categories: none, weak, medium and
   // strong. They are meant to match the number of sections in the wifi icon.
   // The wifi icon currently has four levels; signals [0, 100] are mapped to [1,
   // 4]. There are only three signal strengths so icons that were mapped to 2
   // are also considered weak.
-  wifi_network_->set_signal_strength(0);
-  EXPECT_EQ(ss::NONE, GetSignalStrengthForNetwork(wifi_network_.get()));
-  wifi_network_->set_signal_strength(50);
-  EXPECT_EQ(ss::WEAK, GetSignalStrengthForNetwork(wifi_network_.get()));
-  wifi_network_->set_signal_strength(51);
-  EXPECT_EQ(ss::MEDIUM, GetSignalStrengthForNetwork(wifi_network_.get()));
-  wifi_network_->set_signal_strength(75);
-  EXPECT_EQ(ss::MEDIUM, GetSignalStrengthForNetwork(wifi_network_.get()));
-  wifi_network_->set_signal_strength(76);
-  EXPECT_EQ(ss::STRONG, GetSignalStrengthForNetwork(wifi_network_.get()));
-  wifi_network_->set_signal_strength(100);
-  EXPECT_EQ(ss::STRONG, GetSignalStrengthForNetwork(wifi_network_.get()));
+  wifi_network->set_signal_strength(0);
+  EXPECT_EQ(ss::NONE, GetSignalStrengthForNetwork(wifi_network.get()));
+  wifi_network->set_signal_strength(50);
+  EXPECT_EQ(ss::WEAK, GetSignalStrengthForNetwork(wifi_network.get()));
+  wifi_network->set_signal_strength(51);
+  EXPECT_EQ(ss::MEDIUM, GetSignalStrengthForNetwork(wifi_network.get()));
+  wifi_network->set_signal_strength(75);
+  EXPECT_EQ(ss::MEDIUM, GetSignalStrengthForNetwork(wifi_network.get()));
+  wifi_network->set_signal_strength(76);
+  EXPECT_EQ(ss::STRONG, GetSignalStrengthForNetwork(wifi_network.get()));
+  wifi_network->set_signal_strength(100);
+  EXPECT_EQ(ss::STRONG, GetSignalStrengthForNetwork(wifi_network.get()));
+}
+
+TEST_F(NetworkIconTest, DefaultImageAndLabelWifiConnected) {
+  // Set the Wifi service as connected.
+  SetServiceProperty(wifi1_path(), shill::kSignalStrengthProperty,
+                     base::Value(45));
+  SetServiceProperty(wifi1_path(), shill::kStateProperty,
+                     base::Value(shill::kStateOnline));
+
+  gfx::ImageSkia default_image;
+  base::string16 label;
+  bool animating = false;
+  ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image,
+                                                    &label, &animating);
+  ASSERT_FALSE(default_image.isNull());
+  EXPECT_FALSE(animating);
+
+  std::unique_ptr<chromeos::NetworkState> reference_network =
+      CreateStandaloneNetworkState("reference", shill::kTypeWifi,
+                                   shill::kStateOnline, 45);
+  EXPECT_TRUE(gfx::test::AreImagesEqual(
+      gfx::Image(default_image), ImageForNetwork(reference_network.get())));
+}
+
+TEST_F(NetworkIconTest, DefaultImageAndLabelWifiConnecting) {
+  // Set the Wifi service as connected.
+  SetServiceProperty(wifi1_path(), shill::kSignalStrengthProperty,
+                     base::Value(45));
+  SetServiceProperty(wifi1_path(), shill::kStateProperty,
+                     base::Value(shill::kStateAssociation));
+
+  gfx::ImageSkia default_image;
+  base::string16 label;
+  bool animating = false;
+  ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image,
+                                                    &label, &animating);
+  ASSERT_FALSE(default_image.isNull());
+  EXPECT_TRUE(animating);
+
+  std::unique_ptr<chromeos::NetworkState> reference_network =
+      CreateStandaloneNetworkState("reference", shill::kTypeWifi,
+                                   shill::kStateAssociation, 45);
+  EXPECT_TRUE(gfx::test::AreImagesEqual(
+      gfx::Image(default_image), ImageForNetwork(reference_network.get())));
+}
+
+// Tests that the default network image is a cellular network icon when cellular
+// network is the default network, even if a wifi network is connected.
+// Generally, shill will prefer wifi over cellular networks when both are
+// connected, but that is not always the case. For example, if the connected
+// wifi service has no Internet connectivity, cellular service will be selected
+// as default.
+TEST_F(NetworkIconTest, DefaultImageAndLabelCellularDefaultWithWifiConnected) {
+  // Set both wifi and cellular networks in a connected state, but with wifi not
+  // online - this should prompt fake shill manager implementation to prefer
+  // cellular network over wifi.
+  SetServiceProperty(wifi1_path(), shill::kSignalStrengthProperty,
+                     base::Value(45));
+  SetServiceProperty(wifi1_path(), shill::kStateProperty,
+                     base::Value(shill::kStateReady));
+
+  SetServiceProperty(cellular_path(), shill::kSignalStrengthProperty,
+                     base::Value(65));
+  SetServiceProperty(cellular_path(), shill::kStateProperty,
+                     base::Value(shill::kStateOnline));
+
+  gfx::ImageSkia default_image;
+  base::string16 label;
+  bool animating = false;
+  ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image,
+                                                    &label, &animating);
+  ASSERT_FALSE(default_image.isNull());
+  EXPECT_FALSE(animating);
+
+  std::unique_ptr<chromeos::NetworkState> reference_network =
+      CreateStandaloneNetworkState("reference", shill::kTypeCellular,
+                                   shill::kStateOnline, 65);
+
+  EXPECT_TRUE(gfx::test::AreImagesEqual(
+      gfx::Image(default_image), ImageForNetwork(reference_network.get())));
+}
+
+// Tests the use case where the default network starts reconnecting while
+// another network is connected.
+TEST_F(NetworkIconTest, DefaultImageReconnectingWifiWithCellularConnected) {
+  // First connect both wifi and cellular network (with wifi as default).
+  SetServiceProperty(wifi1_path(), shill::kSignalStrengthProperty,
+                     base::Value(45));
+  SetServiceProperty(wifi1_path(), shill::kStateProperty,
+                     base::Value(shill::kStateOnline));
+
+  SetServiceProperty(cellular_path(), shill::kSignalStrengthProperty,
+                     base::Value(65));
+  SetServiceProperty(cellular_path(), shill::kStateProperty,
+                     base::Value(shill::kStateOnline));
+
+  // Start reconnecting wifi network.
+  SetServiceProperty(wifi1_path(), shill::kStateProperty,
+                     base::Value(shill::kStateAssociation));
+
+  gfx::ImageSkia default_image;
+  base::string16 label;
+  bool animating = false;
+  // Verify that the default network is connecting icon for the initial default
+  // network (even though the default network as reported by shill actually
+  // changed).
+  ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image,
+                                                    &label, &animating);
+  ASSERT_FALSE(default_image.isNull());
+  EXPECT_TRUE(animating);
+
+  std::unique_ptr<chromeos::NetworkState> reference_network_1 =
+      CreateStandaloneNetworkState("reference1", shill::kTypeWifi,
+                                   shill::kStateAssociation, 45);
+  EXPECT_TRUE(gfx::test::AreImagesEqual(
+      gfx::Image(default_image), ImageForNetwork(reference_network_1.get())));
+
+  SetServiceProperty(wifi1_path(), shill::kStateProperty,
+                     base::Value(shill::kStateReady));
+
+  // At this point the wifi network is not connecting, but is not yet default -
+  // the default network icon should have image for the cellular network (which
+  // is the default one at this point).
+  // TODO(tbarzic): This creates a unoptimal behavior in the UI where cellular
+  //     icon could flash between wifi connecting and connected icons - this
+  //     should be fixed, for example by not showing connecting icon when
+  //     reconnecting a network if another network is connected.
+  std::unique_ptr<chromeos::NetworkState> reference_network_2 =
+      CreateStandaloneNetworkState("reference2", shill::kTypeCellular,
+                                   shill::kStateOnline, 65);
+  ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image,
+                                                    &label, &animating);
+  ASSERT_FALSE(default_image.isNull());
+  EXPECT_FALSE(animating);
+
+  EXPECT_TRUE(gfx::test::AreImagesEqual(
+      gfx::Image(default_image), ImageForNetwork(reference_network_2.get())));
+
+  SetServiceProperty(wifi1_path(), shill::kStateProperty,
+                     base::Value(shill::kStateOnline));
+
+  // The wifi network is online, and thus default - the default network icon
+  // should display the wifi network's associated image again.
+  std::unique_ptr<chromeos::NetworkState> reference_network_3 =
+      CreateStandaloneNetworkState("reference3", shill::kTypeWifi,
+                                   shill::kStateOnline, 45);
+  ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image,
+                                                    &label, &animating);
+  ASSERT_FALSE(default_image.isNull());
+  EXPECT_FALSE(animating);
+
+  EXPECT_TRUE(gfx::test::AreImagesEqual(
+      gfx::Image(default_image), ImageForNetwork(reference_network_3.get())));
+}
+
+TEST_F(NetworkIconTest, DefaultImageDisconnectWifiWithCellularConnected) {
+  // First connect both wifi and cellular network (with wifi as default).
+  SetServiceProperty(wifi1_path(), shill::kSignalStrengthProperty,
+                     base::Value(45));
+  SetServiceProperty(wifi1_path(), shill::kStateProperty,
+                     base::Value(shill::kStateOnline));
+
+  SetServiceProperty(cellular_path(), shill::kSignalStrengthProperty,
+                     base::Value(65));
+  SetServiceProperty(cellular_path(), shill::kStateProperty,
+                     base::Value(shill::kStateOnline));
+
+  // Disconnect wifi network, and verify the default icon changes to cellular.
+  SetServiceProperty(wifi1_path(), shill::kStateProperty,
+                     base::Value(shill::kStateIdle));
+
+  gfx::ImageSkia default_image;
+  base::string16 label;
+  bool animating = false;
+  ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image,
+                                                    &label, &animating);
+  ASSERT_FALSE(default_image.isNull());
+  EXPECT_FALSE(animating);
+
+  std::unique_ptr<chromeos::NetworkState> reference_network =
+      CreateStandaloneNetworkState("reference", shill::kTypeCellular,
+                                   shill::kStateOnline, 65);
+  EXPECT_TRUE(gfx::test::AreImagesEqual(
+      gfx::Image(default_image), ImageForNetwork(reference_network.get())));
+}
+
+// Tests that the default network image remains the same if non-default network
+// reconnects.
+TEST_F(NetworkIconTest, DefaultImageWhileNonDefaultNetworkReconnecting) {
+  // First connect both wifi and cellular network (with wifi as default).
+  SetServiceProperty(wifi1_path(), shill::kSignalStrengthProperty,
+                     base::Value(45));
+  SetServiceProperty(wifi1_path(), shill::kStateProperty,
+                     base::Value(shill::kStateOnline));
+
+  SetServiceProperty(cellular_path(), shill::kSignalStrengthProperty,
+                     base::Value(65));
+  SetServiceProperty(cellular_path(), shill::kStateProperty,
+                     base::Value(shill::kStateOnline));
+
+  // Start reconnecting the non-default, cellular network.
+  SetServiceProperty(cellular_path(), shill::kStateProperty,
+                     base::Value(shill::kStateAssociation));
+
+  gfx::ImageSkia default_image;
+  base::string16 label;
+  bool animating = false;
+  // Currently, a connecting icon is used as default network icon even if
+  // another network connected and used as default.
+  // TODO(tbarzic): Consider changing network icon logic to use a connected
+  //     network icon if a network is connected while a network is reconnecting.
+  ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image,
+                                                    &label, &animating);
+  ASSERT_FALSE(default_image.isNull());
+  EXPECT_TRUE(animating);
+
+  std::unique_ptr<chromeos::NetworkState> reference_network_1 =
+      CreateStandaloneNetworkState("reference1", shill::kTypeCellular,
+                                   shill::kStateAssociation, 65);
+  EXPECT_TRUE(gfx::test::AreImagesEqual(
+      gfx::Image(default_image), ImageForNetwork(reference_network_1.get())));
+
+  // Move the cellular network to connected, but not yet online state - the
+  // default network image changes back to the default network.
+  SetServiceProperty(cellular_path(), shill::kStateProperty,
+                     base::Value(shill::kStateReady));
+
+  ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image,
+                                                    &label, &animating);
+  ASSERT_FALSE(default_image.isNull());
+  EXPECT_FALSE(animating);
+  std::unique_ptr<chromeos::NetworkState> reference_network_2 =
+      CreateStandaloneNetworkState("reference2", shill::kTypeWifi,
+                                   shill::kStateOnline, 45);
+  EXPECT_TRUE(gfx::test::AreImagesEqual(
+      gfx::Image(default_image), ImageForNetwork(reference_network_2.get())));
+
+  // Move the cellular network to online state - the default network image
+  // should remain the same.
+  SetServiceProperty(cellular_path(), shill::kStateProperty,
+                     base::Value(shill::kStateOnline));
+
+  ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image,
+                                                    &label, &animating);
+  ASSERT_FALSE(default_image.isNull());
+  EXPECT_FALSE(animating);
+  EXPECT_TRUE(gfx::test::AreImagesEqual(
+      gfx::Image(default_image), ImageForNetwork(reference_network_2.get())));
+}
+
+// Tests that the default network image shows a cellular network icon if
+// cellular network is connected while wifi is connecting.
+TEST_F(NetworkIconTest, DefaultImageConnectingToWifiWileCellularConnected) {
+  // Connect cellular network, and set the wifi as connecting.
+  SetServiceProperty(wifi1_path(), shill::kSignalStrengthProperty,
+                     base::Value(45));
+  SetServiceProperty(wifi1_path(), shill::kStateProperty,
+                     base::Value(shill::kStateAssociation));
+
+  SetServiceProperty(cellular_path(), shill::kSignalStrengthProperty,
+                     base::Value(65));
+  SetServiceProperty(cellular_path(), shill::kStateProperty,
+                     base::Value(shill::kStateOnline));
+
+  gfx::ImageSkia default_image;
+  base::string16 label;
+  bool animating = false;
+  ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image,
+                                                    &label, &animating);
+  ASSERT_FALSE(default_image.isNull());
+  EXPECT_FALSE(animating);
+
+  std::unique_ptr<chromeos::NetworkState> reference_network =
+      CreateStandaloneNetworkState("reference", shill::kTypeCellular,
+                                   shill::kStateOnline, 65);
+  EXPECT_TRUE(gfx::test::AreImagesEqual(
+      gfx::Image(default_image), ImageForNetwork(reference_network.get())));
+}
+
+// Test that a cellular icon is displayed when activating cellular
+// network (if other networks are not connected).
+TEST_F(NetworkIconTest, DefaultNetworkImageActivatingCellularNetwork) {
+  SetServiceProperty(cellular_path(), shill::kSignalStrengthProperty,
+                     base::Value(65));
+  SetServiceProperty(cellular_path(), shill::kActivationStateProperty,
+                     base::Value(shill::kActivationStateActivating));
+
+  gfx::ImageSkia default_image;
+  base::string16 label;
+  bool animating = false;
+  ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image,
+                                                    &label, &animating);
+  ASSERT_FALSE(default_image.isNull());
+  EXPECT_FALSE(animating);
+
+  std::unique_ptr<chromeos::NetworkState> reference_network =
+      CreateStandaloneNetworkState("reference", shill::kTypeCellular,
+                                   shill::kStateIdle, 65);
+  EXPECT_TRUE(gfx::test::AreImagesEqual(
+      gfx::Image(default_image), ImageForNetwork(reference_network.get())));
+}
+
+// Tests that default network image is a wifi network image if wifi network is
+// connected during cellular network activation.
+TEST_F(NetworkIconTest,
+       DefaultNetworkImageActivatingCellularNetworkWithConnectedWifi) {
+  SetServiceProperty(wifi1_path(), shill::kSignalStrengthProperty,
+                     base::Value(45));
+  SetServiceProperty(wifi1_path(), shill::kStateProperty,
+                     base::Value(shill::kStateOnline));
+
+  SetServiceProperty(cellular_path(), shill::kSignalStrengthProperty,
+                     base::Value(65));
+  SetServiceProperty(cellular_path(), shill::kActivationStateProperty,
+                     base::Value(shill::kActivationStateActivating));
+
+  gfx::ImageSkia default_image;
+  base::string16 label;
+  bool animating = false;
+  ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image,
+                                                    &label, &animating);
+  ASSERT_FALSE(default_image.isNull());
+  EXPECT_FALSE(animating);
+
+  std::unique_ptr<chromeos::NetworkState> reference_network =
+      CreateStandaloneNetworkState("reference", shill::kTypeWifi,
+                                   shill::kStateIdle, 45);
+  EXPECT_TRUE(gfx::test::AreImagesEqual(
+      gfx::Image(default_image), ImageForNetwork(reference_network.get())));
+}
+
+// Tests that wifi image is shown when connecting to wifi network with vpn.
+TEST_F(NetworkIconTest, DefaultNetworkImageVpnAndWifi) {
+  SetServiceProperty(wifi1_path(), shill::kSignalStrengthProperty,
+                     base::Value(65));
+  SetServiceProperty(wifi1_path(), shill::kStateProperty,
+                     base::Value(shill::kStateAssociation));
+
+  std::string vpn_path = ConfigureService(
+      R"({"GUID": "vpn_guid", "Type": "vpn", "State": "online"})");
+  ASSERT_FALSE(vpn_path.empty());
+
+  gfx::ImageSkia default_image;
+  base::string16 label;
+  bool animating = false;
+  ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image,
+                                                    &label, &animating);
+  ASSERT_FALSE(default_image.isNull());
+  EXPECT_TRUE(animating);
+
+  std::unique_ptr<chromeos::NetworkState> reference_network =
+      CreateStandaloneNetworkState("reference", shill::kTypeWifi,
+                                   shill::kStateAssociation, 65);
+  EXPECT_TRUE(gfx::test::AreImagesEqual(
+      gfx::Image(default_image), ImageForNetwork(reference_network.get())));
+}
+
+// Tests that cellular image is shown when connecting to cellular network with
+// VPN.
+TEST_F(NetworkIconTest, DefaultNetworkImageVpnAndCellular) {
+  SetServiceProperty(cellular_path(), shill::kSignalStrengthProperty,
+                     base::Value(65));
+  SetServiceProperty(cellular_path(), shill::kStateProperty,
+                     base::Value(shill::kStateAssociation));
+
+  std::string vpn_path = ConfigureService(
+      R"({"GUID": "vpn_guid", "Type": "vpn", "State": "online"})");
+  ASSERT_FALSE(vpn_path.empty());
+
+  gfx::ImageSkia default_image;
+  base::string16 label;
+  bool animating = false;
+  ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image,
+                                                    &label, &animating);
+  ASSERT_FALSE(default_image.isNull());
+  EXPECT_TRUE(animating);
+
+  std::unique_ptr<chromeos::NetworkState> reference_network =
+      CreateStandaloneNetworkState("reference", shill::kTypeCellular,
+                                   shill::kStateAssociation, 65);
+  EXPECT_TRUE(gfx::test::AreImagesEqual(
+      gfx::Image(default_image), ImageForNetwork(reference_network.get())));
 }
 
 }  // namespace network_icon
diff --git a/ash/system/status_area_widget_test_api.h b/ash/system/status_area_widget_test_api.h
index c83987e2..10af7197 100644
--- a/ash/system/status_area_widget_test_api.h
+++ b/ash/system/status_area_widget_test_api.h
@@ -7,7 +7,7 @@
 
 #include <memory>
 
-#include "ash/public/interfaces/status_area_widget_test_api.mojom.h"
+#include "ash/public/interfaces/status_area_widget_test_api.test-mojom.h"
 #include "ash/system/status_area_widget.h"
 #include "base/macros.h"
 
diff --git a/ash/system/unified/unified_system_tray_test_api.h b/ash/system/unified/unified_system_tray_test_api.h
index aa9285f..6323aacd 100644
--- a/ash/system/unified/unified_system_tray_test_api.h
+++ b/ash/system/unified/unified_system_tray_test_api.h
@@ -7,7 +7,7 @@
 
 #include <memory>
 
-#include "ash/public/interfaces/system_tray_test_api.mojom.h"
+#include "ash/public/interfaces/system_tray_test_api.test-mojom.h"
 #include "base/macros.h"
 
 namespace ui {
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager.cc b/ash/wm/tablet_mode/tablet_mode_window_manager.cc
index 54d8d0a0..90ba5eaf 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager.cc
@@ -51,7 +51,7 @@
   Shell::Get()->split_view_controller()->RemoveObserver(this);
   EnableBackdropBehindTopWindowOnEachDisplay(false);
   RemoveWindowCreationObservers();
-  RestoreAllWindows();
+  ArrangeWindowsForDesktopMode();
 }
 
 int TabletModeWindowManager::GetNumberOfManagedWindows() {
@@ -269,7 +269,7 @@
   // guarantee the proper order, it will be turned off from here.
   CancelOverview();
 
-  MaximizeAllWindows();
+  ArrangeWindowsForTabletMode();
   AddWindowCreationObservers();
   EnableBackdropBehindTopWindowOnEachDisplay(true);
   display::Screen::GetScreen()->AddObserver(this);
@@ -278,18 +278,77 @@
   event_handler_ = std::make_unique<wm::TabletModeEventHandler>();
 }
 
-void TabletModeWindowManager::MaximizeAllWindows() {
-  // For maximizing and tracking windows, we want the build mru list to ignore
-  // the fact that the windows are on the lock screen.
+void TabletModeWindowManager::ArrangeWindowsForTabletMode() {
+  // We want the build mru list to include windows on the lock screen.
   ScopedSkipUserSessionBlockedCheck scoped_skip_user_session_blocked_check;
+
   MruWindowTracker::WindowList windows =
       Shell::Get()->mru_window_tracker()->BuildWindowListIgnoreModal();
-  // Add all existing MRU windows.
+
+  // Specifically check for the case of no windows, so that subsequent logic can
+  // refer to the active window and assume it exists.
+  if (windows.empty())
+    return;
+
+  const mojom::WindowStateType active_window_state_type =
+      wm::GetWindowState(windows[0])->GetStateType();
+
+  // If the active window is not snapped, then just maximize all windows.
+  if (active_window_state_type != mojom::WindowStateType::LEFT_SNAPPED &&
+      active_window_state_type != mojom::WindowStateType::RIGHT_SNAPPED) {
+    for (auto* window : windows)
+      MaximizeAndTrackWindow(window);
+    return;
+  }
+
+  // The snapped active window will be represented by split view, which will be
+  // activated after maximizing all windows. The split view layout is decided
+  // here by examining window states before all those states become maximized.
+  SplitViewController::SnapPosition curr_win_snap_pos =
+      SplitViewController::NONE;
+  SplitViewController::SnapPosition prev_win_snap_pos =
+      SplitViewController::NONE;
+  if (active_window_state_type == mojom::WindowStateType::LEFT_SNAPPED) {
+    // The active window snapped on the left shall go there in split view.
+    curr_win_snap_pos = SplitViewController::LEFT;
+
+    if (windows.size() > 1u && wm::GetWindowState(windows[1])->GetStateType() ==
+                                   mojom::WindowStateType::RIGHT_SNAPPED) {
+      // The previous window snapped on the right shall go there in split view.
+      prev_win_snap_pos = SplitViewController::RIGHT;
+    }
+  } else {
+    DCHECK_EQ(mojom::WindowStateType::RIGHT_SNAPPED, active_window_state_type);
+
+    // The active window snapped on the right shall go there in split view.
+    curr_win_snap_pos = SplitViewController::RIGHT;
+
+    if (windows.size() > 1u && wm::GetWindowState(windows[1])->GetStateType() ==
+                                   mojom::WindowStateType::LEFT_SNAPPED) {
+      // The previous window snapped on the left shall go there in split view.
+      prev_win_snap_pos = SplitViewController::LEFT;
+    }
+  }
+
+  // Use |defer_bounds_update| to suppress the maximizing animation which would
+  // look weird here, especially when overview appears beside the active window.
   for (auto* window : windows)
-    MaximizeAndTrackWindow(window);
+    MaximizeAndTrackWindow(window, /*defer_bounds_update=*/true);
+
+  // Implement the previously decided split view layout.
+  SplitViewController* split_view_controller =
+      Shell::Get()->split_view_controller();
+  split_view_controller->SnapWindow(windows[0], curr_win_snap_pos);
+  if (prev_win_snap_pos == SplitViewController::NONE)
+    Shell::Get()->window_selector_controller()->ToggleOverview();
+  else
+    split_view_controller->SnapWindow(windows[1], prev_win_snap_pos);
+
+  for (auto* window : windows)
+    SetDeferBoundsUpdates(window, false);
 }
 
-void TabletModeWindowManager::RestoreAllWindows() {
+void TabletModeWindowManager::ArrangeWindowsForDesktopMode() {
   while (window_state_map_.size())
     ForgetWindow(window_state_map_.begin()->first, false /* destroyed */);
 }
@@ -301,7 +360,9 @@
     iter->second->SetDeferBoundsUpdates(defer_bounds_updates);
 }
 
-void TabletModeWindowManager::MaximizeAndTrackWindow(aura::Window* window) {
+void TabletModeWindowManager::MaximizeAndTrackWindow(
+    aura::Window* window,
+    bool defer_bounds_updates) {
   if (!ShouldHandleWindow(window))
     return;
 
@@ -310,7 +371,8 @@
 
   // We create and remember a tablet mode state which will attach itself to
   // the provided state object.
-  window_state_map_[window] = new TabletModeWindowState(window, this);
+  window_state_map_[window] =
+      new TabletModeWindowState(window, this, defer_bounds_updates);
 }
 
 void TabletModeWindowManager::ForgetWindow(aura::Window* window,
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager.h b/ash/wm/tablet_mode/tablet_mode_window_manager.h
index bf4bdb9..cf573b6 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager.h
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager.h
@@ -95,11 +95,13 @@
  private:
   using WindowToState = std::map<aura::Window*, TabletModeWindowState*>;
 
-  // Maximize all windows and restore their current state.
-  void MaximizeAllWindows();
+  // Maximize all windows, except that a snapped active window shall become
+  // represented in split view, along with the previously active window if it is
+  // snapped to the opposite side.
+  void ArrangeWindowsForTabletMode();
 
-  // Restore all windows to their previous state.
-  void RestoreAllWindows();
+  // Revert all windows to how they were arranged before tablet mode.
+  void ArrangeWindowsForDesktopMode();
 
   // Set whether to defer bounds updates for |window|. When set to false bounds
   // will be updated as they may be stale.
@@ -110,7 +112,8 @@
   // state).
   // Note: If the given window cannot be handled by us the function will return
   // immediately.
-  void MaximizeAndTrackWindow(aura::Window* window);
+  void MaximizeAndTrackWindow(aura::Window* window,
+                              bool defer_bounds_updates = false);
 
   // Remove a window from our tracking list. If the window is going to be
   // destroyed, do not restore its old previous window state object as it will
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc b/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
index 8e6d7ae..117b42db 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
@@ -43,6 +43,7 @@
 #include "ui/events/test/event_generator.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/transient_window_manager.h"
+#include "ui/wm/core/window_util.h"
 
 namespace ash {
 
@@ -169,6 +170,22 @@
     return window;
   }
 
+  // Creates a test window snapped on the left in desktop mode.
+  std::unique_ptr<aura::Window> CreateDesktopWindowSnappedLeft() {
+    std::unique_ptr<aura::Window> window = CreateTestWindow();
+    wm::WMEvent snap_to_left(wm::WMEventType::WM_EVENT_CYCLE_SNAP_LEFT);
+    wm::GetWindowState(window.get())->OnWMEvent(&snap_to_left);
+    return window;
+  }
+
+  // Creates a test window snapped on the right in desktop mode.
+  std::unique_ptr<aura::Window> CreateDesktopWindowSnappedRight() {
+    std::unique_ptr<aura::Window> window = CreateTestWindow();
+    wm::WMEvent snap_to_right(wm::WMEventType::WM_EVENT_CYCLE_SNAP_RIGHT);
+    wm::GetWindowState(window.get())->OnWMEvent(&snap_to_right);
+    return window;
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(TabletModeWindowManagerTest);
 };
@@ -1830,4 +1847,76 @@
   EXPECT_EQ(rect.size(), child->bounds().size());
 }
 
+// Test that if the active window is not snapped before tablet mode, then
+// neither split view nor overview is activated.
+TEST_F(TabletModeWindowManagerTest, ActiveNoSnap) {
+  SplitViewController* split_view_controller =
+      Shell::Get()->split_view_controller();
+  std::unique_ptr<aura::Window> window = CreateTestWindow();
+  ::wm::ActivateWindow(window.get());
+  ASSERT_TRUE(CreateTabletModeWindowManager());
+  EXPECT_EQ(SplitViewController::NO_SNAP, split_view_controller->state());
+  EXPECT_FALSE(Shell::Get()->window_selector_controller()->IsSelecting());
+}
+
+// Test that if the active window is snapped on the left before tablet mode,
+// then split view is activated with the active window on the left.
+TEST_F(TabletModeWindowManagerTest, ActiveLeftSnap) {
+  SplitViewController* split_view_controller =
+      Shell::Get()->split_view_controller();
+  std::unique_ptr<aura::Window> window = CreateDesktopWindowSnappedLeft();
+  ::wm::ActivateWindow(window.get());
+  ASSERT_TRUE(CreateTabletModeWindowManager());
+  EXPECT_EQ(SplitViewController::LEFT_SNAPPED, split_view_controller->state());
+  EXPECT_EQ(window.get(), split_view_controller->left_window());
+  EXPECT_TRUE(Shell::Get()->window_selector_controller()->IsSelecting());
+}
+
+// Test that if the active window is snapped on the right before tablet mode,
+// then split view is activated with the active window on the right.
+TEST_F(TabletModeWindowManagerTest, ActiveRightSnap) {
+  SplitViewController* split_view_controller =
+      Shell::Get()->split_view_controller();
+  std::unique_ptr<aura::Window> window = CreateDesktopWindowSnappedRight();
+  ::wm::ActivateWindow(window.get());
+  ASSERT_TRUE(CreateTabletModeWindowManager());
+  EXPECT_EQ(SplitViewController::RIGHT_SNAPPED, split_view_controller->state());
+  EXPECT_EQ(window.get(), split_view_controller->right_window());
+  EXPECT_TRUE(Shell::Get()->window_selector_controller()->IsSelecting());
+}
+
+// Test that if before tablet mode, the active window is snapped on the left and
+// the previous window is snapped on the right, then split view is activated
+// with the active window on the left and the previous window on the right.
+TEST_F(TabletModeWindowManagerTest, ActiveLeftSnapPreviousRightSnap) {
+  SplitViewController* split_view_controller =
+      Shell::Get()->split_view_controller();
+  std::unique_ptr<aura::Window> left_window = CreateDesktopWindowSnappedLeft();
+  std::unique_ptr<aura::Window> right_window =
+      CreateDesktopWindowSnappedRight();
+  ::wm::ActivateWindow(left_window.get());
+  ASSERT_TRUE(CreateTabletModeWindowManager());
+  EXPECT_EQ(SplitViewController::BOTH_SNAPPED, split_view_controller->state());
+  EXPECT_EQ(left_window.get(), split_view_controller->left_window());
+  EXPECT_EQ(right_window.get(), split_view_controller->right_window());
+  EXPECT_FALSE(Shell::Get()->window_selector_controller()->IsSelecting());
+}
+
+// Test that if before tablet mode, the active window is snapped on the right
+// and the previous window is snapped on the left, then split view is activated
+// with the active window on the right and the previous window on the left.
+TEST_F(TabletModeWindowManagerTest, ActiveRightSnapPreviousLeftSnap) {
+  SplitViewController* split_view_controller =
+      Shell::Get()->split_view_controller();
+  std::unique_ptr<aura::Window> left_window = CreateDesktopWindowSnappedLeft();
+  std::unique_ptr<aura::Window> right_window =
+      CreateDesktopWindowSnappedRight();
+  ::wm::ActivateWindow(right_window.get());
+  ASSERT_TRUE(CreateTabletModeWindowManager());
+  EXPECT_EQ(SplitViewController::BOTH_SNAPPED, split_view_controller->state());
+  EXPECT_EQ(left_window.get(), split_view_controller->left_window());
+  EXPECT_EQ(right_window.get(), split_view_controller->right_window());
+  EXPECT_FALSE(Shell::Get()->window_selector_controller()->IsSelecting());
+}
+
 }  // namespace ash
diff --git a/ash/wm/tablet_mode/tablet_mode_window_state.cc b/ash/wm/tablet_mode/tablet_mode_window_state.cc
index 92b5287..f11d0979 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_state.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_state.cc
@@ -167,10 +167,12 @@
 }
 
 TabletModeWindowState::TabletModeWindowState(aura::Window* window,
-                                             TabletModeWindowManager* creator)
+                                             TabletModeWindowManager* creator,
+                                             bool defer_bounds_updates)
     : window_(window),
       creator_(creator),
-      current_state_type_(wm::GetWindowState(window)->GetStateType()) {
+      current_state_type_(wm::GetWindowState(window)->GetStateType()),
+      defer_bounds_updates_(defer_bounds_updates) {
   old_state_.reset(wm::GetWindowState(window)
                        ->SetStateObject(std::unique_ptr<State>(this))
                        .release());
diff --git a/ash/wm/tablet_mode/tablet_mode_window_state.h b/ash/wm/tablet_mode/tablet_mode_window_state.h
index 88b0e38b..2d5af86 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_state.h
+++ b/ash/wm/tablet_mode/tablet_mode_window_state.h
@@ -26,7 +26,9 @@
   // state handler. Upon destruction it will restore the previous state handler
   // and call |creator::WindowStateDestroyed()| to inform that the window mode
   // was reverted to the old window manager.
-  TabletModeWindowState(aura::Window* window, TabletModeWindowManager* creator);
+  TabletModeWindowState(aura::Window* window,
+                        TabletModeWindowManager* creator,
+                        bool defer_bounds_updates);
   ~TabletModeWindowState() override;
 
   void set_ignore_wm_events(bool ignore) { ignore_wm_events_ = ignore; }
diff --git a/base/message_loop/message_loop_impl.cc b/base/message_loop/message_loop_impl.cc
index 52526b4..a7e30b2 100644
--- a/base/message_loop/message_loop_impl.cc
+++ b/base/message_loop/message_loop_impl.cc
@@ -11,7 +11,6 @@
 #include "base/callback_helpers.h"
 #include "base/compiler_specific.h"
 #include "base/debug/task_annotator.h"
-#include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop_task_runner.h"
@@ -19,9 +18,7 @@
 #include "base/message_loop/message_pump_for_io.h"
 #include "base/message_loop/message_pump_for_ui.h"
 #include "base/message_loop/sequenced_task_source.h"
-#include "base/optional.h"
 #include "base/run_loop.h"
-#include "base/synchronization/lock.h"
 #include "base/task/common/operations_controller.h"
 #include "base/threading/thread_id_name_manager.h"
 #include "base/threading/thread_restrictions.h"
@@ -34,13 +31,6 @@
 #include "base/message_loop/message_pump_android.h"
 #endif
 
-namespace {
-
-constexpr base::Feature kLockFreeScheduleWork{"LockFreeScheduleWork",
-                                              base::FEATURE_ENABLED_BY_DEFAULT};
-
-}  // namespace
-
 namespace base {
 
 class MessageLoopImpl::Controller : public SequencedTaskSource::Observer {
@@ -73,12 +63,6 @@
  private:
   internal::OperationsController operations_controller_;
 
-  // An optional lock, only instantiated and used when kLockFreeScheduleWork is
-  // experimentally disabled. This lock is undesired, we only run a retroactive
-  // experiment to reintroduce it to confirm that we see the impact of its
-  // removal across our new swath of jank metrics.
-  Optional<Lock> undesired_operations_lock_;
-
   // A TaskAnnotator which is owned by this Controller to be able to use it
   // without locking |message_loop_lock_|. It cannot be owned by MessageLoop
   // because this Controller cannot access |message_loop_| safely without the
@@ -95,8 +79,6 @@
 MessageLoopImpl::Controller::Controller(MessageLoopImpl* message_loop)
     : message_loop_(message_loop) {
   DCHECK(message_loop_);
-  if (!FeatureList::IsEnabled(kLockFreeScheduleWork))
-    undesired_operations_lock_.emplace();
 }
 
 void MessageLoopImpl::Controller::WillQueueTask(PendingTask* task) {
@@ -111,12 +93,6 @@
   if (!operation_token)
     return;
 
-  // This is a retroactive experiment to determine the performance improvement
-  // caused by the removal of this lock in an earlier CL.
-  Optional<AutoLock> undesired_schedule_work_lock;
-  if (undesired_operations_lock_)
-    undesired_schedule_work_lock.emplace(*undesired_operations_lock_);
-
   // Some scenarios can result in getting to this point on multiple threads at
   // once, e.g.:
   //
diff --git a/build/android/gyp/dex.py b/build/android/gyp/dex.py
index cba8c7f..effc6a1 100755
--- a/build/android/gyp/dex.py
+++ b/build/android/gyp/dex.py
@@ -37,6 +37,10 @@
   parser = optparse.OptionParser()
   build_utils.AddDepfileOption(parser)
 
+  parser.add_option('--classpath', help='Classpaths necessary for desugaring.')
+  parser.add_option(
+      '--sdk-jars',
+      help='Path(s) to android sdk jar, necessary for desugaring.')
   parser.add_option('--output-directory',
                     default=os.getcwd(),
                     help='Path to the output build directory.')
@@ -318,7 +322,13 @@
   if options.multi_dex:
     input_paths.append(options.main_dex_list_path)
 
-  dex_cmd = ['java', '-jar', options.d8_jar_path, '--no-desugaring']
+  dex_cmd = ['java', '-jar', options.d8_jar_path]
+  options.sdk_jars = build_utils.ParseGnList(options.sdk_jars)
+  options.classpath = build_utils.ParseGnList(options.classpath)
+  for path in options.classpath:
+    dex_cmd += ['--classpath', path]
+  for path in options.sdk_jars:
+    dex_cmd += ['--lib', path]
   if options.multi_dex:
     dex_cmd += ['--main-dex-list', options.main_dex_list_path]
   if options.release:
diff --git a/build/android/gyp/proguard.py b/build/android/gyp/proguard.py
index 84fbd02..4e6ec5f7 100755
--- a/build/android/gyp/proguard.py
+++ b/build/android/gyp/proguard.py
@@ -14,19 +14,6 @@
 from util import proguard_util
 
 
-_DANGEROUS_OPTIMIZATIONS = [
-    # See crbug.com/825995 (can cause VerifyErrors)
-    "class/merging/vertical",
-    "class/unboxing/enum",
-    # See crbug.com/625992
-    "code/allocation/variable",
-    # See crbug.com/625994
-    "field/propagation/value",
-    "method/propagation/parameter",
-    "method/propagation/returnvalue",
-]
-
-
 # Example:
 # android.arch.core.internal.SafeIterableMap$Entry -> b:
 #     1:1:java.lang.Object getKey():353:353 -> getKey
@@ -56,13 +43,12 @@
                     help='GN list of paths to proguard configuration files '
                          'included by --proguard-configs, but that should '
                          'not actually be included.')
-  parser.add_option('--mapping', help='Path to proguard mapping to apply.')
+  parser.add_option(
+      '--apply-mapping', help='Path to proguard mapping to apply.')
   parser.add_option('--mapping-output',
                     help='Path for proguard to output mapping file to.')
   parser.add_option('--classpath', action='append',
                     help='Classpath for proguard.')
-  parser.add_option('--enable-dangerous-optimizations', action='store_true',
-                    help='Enable optimizations which are known to have issues.')
   parser.add_option('--main-dex-rules-path', action='append',
                     help='Paths to main dex rules for multidex'
                          '- only works with R8.')
@@ -95,6 +81,10 @@
   if not options.mapping_output:
     options.mapping_output = options.output_path + ".mapping"
 
+  if options.apply_mapping:
+    options.apply_mapping = os.path.abspath(options.apply_mapping)
+
+
   return options
 
 
@@ -117,26 +107,26 @@
   shutil.move(tmp_dex_path, dex_path)
 
 
-def _CreateR8Command(options, map_output_path, output_dir):
-  # TODO: R8 needs -applymapping equivalent.
+def _CreateR8Command(options, map_output_path, output_dir, tmp_proguard_config,
+                     libraries):
   cmd = [
     'java', '-jar', options.r8_path,
-    '--no-desugaring',
     '--no-data-resources',
     '--output', output_dir,
     '--pg-map-output', map_output_path,
   ]
 
-  classpath = [
-      p for p in set(options.classpath) if p not in options.input_paths
-  ]
-
-  for lib in classpath:
+  for lib in libraries:
     cmd += ['--lib', lib]
 
   for config_file in options.proguard_configs:
     cmd += ['--pg-conf', config_file]
 
+  if options.apply_mapping:
+    tmp_proguard_config.write('-applymapping ' + options.apply_mapping)
+    tmp_proguard_config.flush()
+    cmd += ['--pg-conf', tmp_proguard_config.name]
+
   if options.min_api:
     cmd += ['--min-api', options.min_api]
 
@@ -152,33 +142,27 @@
   args = build_utils.ExpandFileArgs(args)
   options = _ParseOptions(args)
 
-  proguard = proguard_util.ProguardCmdBuilder(options.proguard_path)
-  proguard.injars(options.input_paths)
-  proguard.configs(options.proguard_configs)
-  proguard.config_exclusions(options.proguard_config_exclusions)
-  proguard.outjar(options.output_path)
-  proguard.mapping_output(options.mapping_output)
-
-  # If a jar is part of input no need to include it as library jar.
-  classpath = [
-      p for p in set(options.classpath) if p not in options.input_paths
-  ]
-  proguard.libraryjars(classpath)
-  proguard.verbose(options.verbose)
-  if not options.enable_dangerous_optimizations:
-    proguard.disable_optimizations(_DANGEROUS_OPTIMIZATIONS)
+  libraries = []
+  for p in options.classpath:
+    # If a jar is part of input no need to include it as library jar.
+    if p not in libraries and p not in options.input_paths:
+      libraries.append(p)
 
   # TODO(agrieve): Remove proguard usages.
   if options.r8_path:
     with tempfile.NamedTemporaryFile() as mapping_temp:
-      if options.output_path.endswith('.dex'):
-        with build_utils.TempDir() as tmp_dex_dir:
-          cmd = _CreateR8Command(options, mapping_temp.name, tmp_dex_dir)
+      with tempfile.NamedTemporaryFile() as tmp_proguard_config:
+        if options.output_path.endswith('.dex'):
+          with build_utils.TempDir() as tmp_dex_dir:
+            cmd = _CreateR8Command(options, mapping_temp.name, tmp_dex_dir,
+                                   tmp_proguard_config, libraries)
+            build_utils.CheckOutput(cmd)
+            _MoveTempDexFile(tmp_dex_dir, options.output_path)
+        else:
+          cmd = _CreateR8Command(options, mapping_temp.name,
+                                 options.output_path, tmp_proguard_config,
+                                 libraries)
           build_utils.CheckOutput(cmd)
-          _MoveTempDexFile(tmp_dex_dir, options.output_path)
-      else:
-        cmd = _CreateR8Command(options, mapping_temp.name, options.output_path)
-        build_utils.CheckOutput(cmd)
 
       # Copy the mapping file back to where it should be.
       map_path = options.mapping_output
@@ -188,20 +172,35 @@
         mapping_temp.seek(0)
         mapping.writelines(l for l in mapping_temp if not l.startswith("#"))
 
-    build_utils.WriteDepfile(options.depfile, options.output_path,
-                             inputs=proguard.GetDepfileDeps(),
-                             add_pydeps=False)
+    other_inputs = []
+    if options.apply_mapping:
+      other_inputs += options.apply_mapping
+
+    build_utils.WriteDepfile(
+        options.depfile,
+        options.output_path,
+        inputs=options.proguard_configs + options.input_paths + libraries +
+        other_inputs,
+        add_pydeps=False)
   else:
+    proguard = proguard_util.ProguardCmdBuilder(options.proguard_path)
+    proguard.injars(options.input_paths)
+    proguard.configs(options.proguard_configs)
+    proguard.config_exclusions(options.proguard_config_exclusions)
+    proguard.outjar(options.output_path)
+    proguard.mapping_output(options.mapping_output)
+    proguard.libraryjars(libraries)
+    proguard.verbose(options.verbose)
     # Do not consider the temp file as an input since its name is random.
     input_paths = proguard.GetInputs()
 
     with tempfile.NamedTemporaryFile() as f:
-      if options.mapping:
-        input_paths.append(options.mapping)
+      if options.apply_mapping:
+        input_paths.append(options.apply_mapping)
         # Maintain only class name mappings in the .mapping file in order to
         # work around what appears to be a ProGuard bug in -applymapping:
         #     method 'int close()' is not being kept as 'a', but remapped to 'c'
-        _RemoveMethodMappings(options.mapping, f)
+        _RemoveMethodMappings(options.apply_mapping, f)
         proguard.mapping(f.name)
 
       input_strings = proguard.build()
diff --git a/build/config/android/config.gni b/build/config/android/config.gni
index 7a02ba5..2fb5743 100644
--- a/build/config/android/config.gni
+++ b/build/config/android/config.gni
@@ -221,9 +221,10 @@
     android_sdk_tools_bundle_aapt2 =
         "//third_party/android_build_tools/aapt2/aapt2"
 
-    # Use r8 for Java optimization rather than ProGuard.
-    # This will evenutally be the default. https://crbug.com/872904
-    experimental_use_r8 = false
+    # Use R8 for Java optimization rather than ProGuard for all targets. R8 is
+    # already used as the default for public targets. This will evenutally be
+    # the default. https://crbug.com/908988
+    use_r8 = false
   }
 
   # We need a second declare_args block to make sure we are using the overridden
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index f70eb63..9bbcaca 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -951,13 +951,7 @@
       pool = "//build/toolchain:link_pool($default_toolchain)"
 
       _output_path = invoker.output_path
-      _proguard_jar_path = _default_proguard_jar_path
-      if (defined(invoker.proguard_jar_path)) {
-        _proguard_jar_path = invoker.proguard_jar_path
-      }
-
       inputs = [
-        _proguard_jar_path,
         invoker.build_config,
       ]
       if (defined(invoker.inputs)) {
@@ -976,8 +970,6 @@
       args = [
         "--depfile",
         rebase_path(depfile, root_build_dir),
-        "--proguard-path",
-        rebase_path(_proguard_jar_path, root_build_dir),
         "--output-path",
         rebase_path(_output_path, root_build_dir),
         "--mapping-output",
@@ -987,20 +979,25 @@
         "--classpath",
         "@FileArg($_rebased_build_config:android:sdk_jars)",
       ]
-      if (experimental_use_r8) {
+
+      if (!defined(invoker.proguard_jar_path) || use_r8) {
         args += [
           "--r8-path",
           rebase_path(_r8_path, root_build_dir),
         ]
+        inputs += [ _r8_path ]
+      } else {
+        _proguard_jar_path = invoker.proguard_jar_path
+        args += [
+          "--proguard-path",
+          rebase_path(_proguard_jar_path, root_build_dir),
+        ]
+        inputs += [ _proguard_jar_path ]
       }
+
       if (defined(invoker.args)) {
         args += invoker.args
       }
-      if (defined(invoker.proguard_jar_path)) {
-        # We assume that if we are using a different ProGuard, this new version
-        # can handle the 'dangerous' optimizaions.
-        args += [ "--enable-dangerous-optimizations" ]
-      }
     }
   }
 
@@ -1074,11 +1071,18 @@
 
     _proguard_enabled =
         defined(invoker.proguard_enabled) && invoker.proguard_enabled
-    _proguarding_with_r8 = _proguard_enabled && experimental_use_r8
+    _proguarding_with_r8 =
+        _proguard_enabled && !defined(invoker.proguard_jar_path)
+    _enable_multidex =
+        defined(invoker.enable_multidex) && invoker.enable_multidex
 
     assert(!(defined(invoker.input_jars) && _proguard_enabled),
            "input_jars can't be specified when proguarding a dex.")
 
+    if (_enable_multidex) {
+      _main_dex_rules = "//build/android/main_dex_classes.flags"
+    }
+
     if (!_proguarding_with_r8) {
       _dexing_jars = []
       if (defined(invoker.input_jars)) {
@@ -1088,10 +1092,6 @@
 
     if (_proguard_enabled) {
       if (defined(invoker.enable_multidex)) {
-        assert(invoker.enable_multidex || !invoker.enable_multidex)
-        if (defined(invoker.extra_main_dex_proguard_config)) {
-          assert(invoker.extra_main_dex_proguard_config != [])
-        }
         if (defined(invoker.negative_main_dex_globs)) {
           assert(invoker.negative_main_dex_globs != [])
         }
@@ -1145,13 +1145,27 @@
           ]
         }
 
+        if (_enable_multidex && _proguarding_with_r8) {
+          if (defined(invoker.extra_main_dex_proguard_config)) {
+            args += [
+              "--main-dex-rules-path",
+              rebase_path(invoker.extra_main_dex_proguard_config,
+                          root_build_dir),
+            ]
+            inputs += [ invoker.extra_main_dex_proguard_config ]
+          }
+          args += [
+            "--main-dex-rules-path",
+            rebase_path(_main_dex_rules, root_build_dir),
+          ]
+          inputs += [ _main_dex_rules ]
+        }
+
         output_path = _proguard_output_path
       }
     }
 
     if (!_proguarding_with_r8) {
-      _enable_multidex =
-          defined(invoker.enable_multidex) && invoker.enable_multidex
       if (_enable_multidex) {
         _main_dex_list_path = invoker.output + ".main_dex_list"
         _main_dex_list_target_name = "${target_name}__main_dex_list"
@@ -1168,8 +1182,6 @@
           # http://crbug.com/725224. Fix for bots running out of memory.
           pool = "//build/toolchain:link_pool($default_toolchain)"
 
-          main_dex_rules = "//build/android/main_dex_classes.flags"
-
           if (defined(invoker.proguard_jar_path)) {
             _proguard_jar_path = invoker.proguard_jar_path
           } else {
@@ -1179,7 +1191,7 @@
           _shrinked_android = "$android_sdk_build_tools/lib/shrinkedAndroid.jar"
           _dx = "$android_sdk_build_tools/lib/dx.jar"
           inputs = [
-            main_dex_rules,
+            _main_dex_rules,
             _dx,
             _proguard_jar_path,
             _shrinked_android,
@@ -1199,7 +1211,7 @@
             "--main-dex-list-path",
             rebase_path(_main_dex_list_path, root_build_dir),
             "--main-dex-rules-path",
-            rebase_path(main_dex_rules, root_build_dir),
+            rebase_path(_main_dex_rules, root_build_dir),
             "--proguard-path",
             rebase_path(_proguard_jar_path, root_build_dir),
           ]
@@ -1243,22 +1255,31 @@
                                ])
         script = "//build/android/gyp/dex.py"
         depfile = "$target_gen_dir/$target_name.d"
-        inputs = []
+        inputs = [
+          invoker.build_config,
+        ]
         outputs = [
           invoker.output,
         ]
 
         _rebased_output = rebase_path(invoker.output, root_build_dir)
-
+        _rebased_build_config =
+            rebase_path(invoker.build_config, root_build_dir)
         args = [
           "--depfile",
           rebase_path(depfile, root_build_dir),
           "--dex-path",
           _rebased_output,
+          "--sdk-jars=@FileArg($_rebased_build_config:android:sdk_jars)",
         ]
 
+        # Some targets use java_runtime_classpath and others use
+        # javac_full_interface_classpath. Either of them will work for desugar.
         if (_proguard_enabled) {
           deps += [ ":${_proguard_target_name}" ]
+          args += [ "--classpath=@FileArg($_rebased_build_config:deps_info:java_runtime_classpath)" ]
+        } else {
+          args += [ "--classpath=@FileArg($_rebased_build_config:deps_info:javac_full_interface_classpath)" ]
         }
 
         if (_enable_multidex) {
@@ -1425,7 +1446,7 @@
     # Turned off because of existing code which fails the assertion
     _enable_thread_annotations = false
 
-    _desugar = defined(invoker.supports_android) && invoker.supports_android
+    _desugar = defined(invoker.desugar) && invoker.desugar
     _emma_instrument = invoker.emma_instrument
     _enable_bytecode_rewriter =
         _enable_assert || _enable_custom_resources || _enable_thread_annotations
@@ -3357,6 +3378,11 @@
         _accumulated_public_deps += [ ":$_copy_system_library_target_name" ]
       } else {
         _process_prebuilt_target_name = "${target_name}__process_prebuilt"
+
+        # Skip desugaring for debug builds using r8 to increase incremental
+        # build speed. Release builds still need desugar.jar for binary size.
+        _using_r8 = !defined(invoker.proguard_jar_path) || use_r8
+        _desugar = !(_using_r8 && is_java_debug) && _supports_android
         process_java_prebuilt(_process_prebuilt_target_name) {
           forward_variables_from(invoker,
                                  [
@@ -3366,7 +3392,7 @@
                                    "jar_included_patterns",
                                  ])
           is_prebuilt = _is_prebuilt
-          supports_android = _supports_android
+          desugar = _desugar
           enable_build_hooks = _enable_build_hooks
           enable_build_hooks_android = _enable_build_hooks_android
           build_config = _build_config
@@ -3389,7 +3415,9 @@
           dex("${target_name}__dex") {
             input_jars = [ _final_jar_path ]
             output = _dex_path
+            build_config = _build_config
             deps = [
+              ":$_build_config_target_name",
               ":$_process_prebuilt_target_name",
             ]
           }
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index bcb3d717..add2ce2 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -2578,10 +2578,6 @@
           forward_variables_from(invoker, [ "proguard_jar_path" ])
           deps += _deps + [ ":$_compile_resources_target" ]
           proguard_configs = [ _jar_path ]
-          if (defined(invoker.apk_under_test)) {
-            proguard_args = [ "--mapping=@FileArg($_rebased_build_config:deps_info:proguard_under_test_mapping)" ]
-            deps += [ "${invoker.apk_under_test}__final_dex" ]
-          }
           proguard_mapping_path = _proguard_mapping_path
         } else {
           if (_enable_multidex) {
diff --git a/build/config/posix/BUILD.gn b/build/config/posix/BUILD.gn
index af09c96..a143421 100644
--- a/build/config/posix/BUILD.gn
+++ b/build/config/posix/BUILD.gn
@@ -35,7 +35,13 @@
   libs = []
 
   if (use_custom_libcxx) {
-    if (!is_component_build) {
+    if (is_component_build) {
+      # In component builds, symbols from libc++.so are exported for all DSOs to
+      # use.  If the system libc++ gets loaded (indirectly through a system
+      # library), then it will conflict with our libc++.so.  Add a custom ABI
+      # version to avoid conflicts.
+      defines += [ "_LIBCPP_ABI_VERSION=Cr" ]
+    } else {
       # Don't leak any symbols on a static build.
       defines += [ "_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS" ]
       if (!export_libcxxabi_from_executables) {
diff --git a/build/config/win/BUILD.gn b/build/config/win/BUILD.gn
index 9e6ead0..805818ea0 100644
--- a/build/config/win/BUILD.gn
+++ b/build/config/win/BUILD.gn
@@ -264,9 +264,12 @@
     cflags_cc +=
         [ "-I" + rebase_path("$libcxx_prefix/include", root_build_dir) ]
     if (!is_component_build) {
+      # Don't leak any symbols on a static build.
       defines += [ "_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS" ]
     }
 
+    # Windows doesn't need to set _LIBCPP_ABI_VERSION since there's no system
+    # C++ library we could conflict with.
     defines += [
       "CR_LIBCXX_REVISION=$libcxx_svn_revision",
       "CR_LIBCXXABI_REVISION=$libcxxabi_svn_revision",
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 9eaf90f..3f2bf53 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-3697400dc0b66d69e8bb1544147bf387ae4e5a81
\ No newline at end of file
+4cb6cfe20aa2144b34d051ee31306969be7582c7
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index d13ca12..60095c4 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-fa7ae7dc8664d4089fd890b79358d1db5cfb7fa1
\ No newline at end of file
+ece0fa0dc069c87a2ce7636fc4b170de33642d05
\ No newline at end of file
diff --git a/cc/animation/keyframe_model.cc b/cc/animation/keyframe_model.cc
index 51449a9..06c9a54 100644
--- a/cc/animation/keyframe_model.cc
+++ b/cc/animation/keyframe_model.cc
@@ -143,14 +143,10 @@
 }
 
 void KeyframeModel::Pause(base::TimeDelta pause_offset) {
-  // Convert pause offset to monotonic time.
-
-  // TODO(crbug.com/840364): This conversion is incorrect. pause_offset is
-  // actually a local time so to convert it to monotonic time we should include
-  // total_paused_duration_ but exclude time_offset. The current calculation is
-  // is incorrect for animations that have start-delay or are paused and
-  // unpaused multiple times.
-  base::TimeTicks monotonic_time = pause_offset + start_time_ + time_offset_;
+  // Convert pause offset which is in local time to monotonic time.
+  // TODO(yigu): This should be scaled by playbackrate. http://crbug.com/912407
+  base::TimeTicks monotonic_time =
+      pause_offset + start_time_ + total_paused_duration_;
   SetRunState(PAUSED, monotonic_time);
 }
 
@@ -242,8 +238,7 @@
     return base::TimeDelta();
 
   // If we're paused, time is 'stuck' at the pause time.
-  base::TimeTicks time =
-      (run_state_ == PAUSED) ? pause_time_ - time_offset_ : monotonic_time;
+  base::TimeTicks time = (run_state_ == PAUSED) ? pause_time_ : monotonic_time;
   return time - start_time_ - total_paused_duration_;
 }
 
diff --git a/cc/animation/keyframe_model_unittest.cc b/cc/animation/keyframe_model_unittest.cc
index e205231..a04fe6d 100644
--- a/cc/animation/keyframe_model_unittest.cc
+++ b/cc/animation/keyframe_model_unittest.cc
@@ -438,16 +438,67 @@
                 .InSecondsF());
 }
 
+TEST(KeyframeModelTest, TrimTimePauseBasic) {
+  std::unique_ptr<KeyframeModel> keyframe_model(CreateKeyframeModel(1));
+  keyframe_model->set_fill_mode(KeyframeModel::FillMode::NONE);
+
+  keyframe_model->Pause(base::TimeDelta::FromSecondsD(0.5));
+  // When paused, the time returned is always the pause time
+  EXPECT_EQ(0.5,
+            keyframe_model->TrimTimeToCurrentIteration(TicksFromSecondsF(-1))
+                .InSecondsF());
+  EXPECT_EQ(0.5,
+            keyframe_model->TrimTimeToCurrentIteration(TicksFromSecondsF(0.2))
+                .InSecondsF());
+  EXPECT_EQ(0.5,
+            keyframe_model->TrimTimeToCurrentIteration(TicksFromSecondsF(2))
+                .InSecondsF());
+}
+
+TEST(KeyframeModelTest, TrimTimePauseAffectedByDelay) {
+  std::unique_ptr<KeyframeModel> keyframe_model(CreateKeyframeModel(1));
+  keyframe_model->set_fill_mode(KeyframeModel::FillMode::NONE);
+  // Pause time is in local time so delay should apply on top of it.
+  keyframe_model->set_time_offset(TimeDelta::FromSecondsD(-0.2));
+  keyframe_model->Pause(base::TimeDelta::FromSecondsD(0.5));
+  EXPECT_EQ(0.3,
+            keyframe_model->TrimTimeToCurrentIteration(TicksFromSecondsF(0.1))
+                .InSecondsF());
+
+  keyframe_model->set_time_offset(TimeDelta::FromSecondsD(0.2));
+  keyframe_model->Pause(base::TimeDelta::FromSecondsD(0.5));
+  EXPECT_EQ(0.7,
+            keyframe_model->TrimTimeToCurrentIteration(TicksFromSecondsF(0.1))
+                .InSecondsF());
+}
+
+TEST(KeyframeModelTest, TrimTimePauseNotAffectedByStartTime) {
+  std::unique_ptr<KeyframeModel> keyframe_model(CreateKeyframeModel(1));
+  keyframe_model->set_fill_mode(KeyframeModel::FillMode::NONE);
+  // Pause time is in local time so start time should not affect it.
+  keyframe_model->set_start_time(TicksFromSecondsF(0.2));
+  keyframe_model->Pause(base::TimeDelta::FromSecondsD(0.5));
+  EXPECT_EQ(0.5,
+            keyframe_model->TrimTimeToCurrentIteration(TicksFromSecondsF(0.1))
+                .InSecondsF());
+
+  keyframe_model->set_start_time(TicksFromSecondsF(0.4));
+  keyframe_model->Pause(base::TimeDelta::FromSecondsD(0.5));
+  EXPECT_EQ(0.5,
+            keyframe_model->TrimTimeToCurrentIteration(TicksFromSecondsF(0.1))
+                .InSecondsF());
+}
+
 TEST(KeyframeModelTest, TrimTimePauseResume) {
   std::unique_ptr<KeyframeModel> keyframe_model(CreateKeyframeModel(1));
   keyframe_model->SetRunState(KeyframeModel::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_EQ(0,
             keyframe_model->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                 .InSecondsF());
-  EXPECT_EQ(0.5,
-            keyframe_model->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))
+  EXPECT_EQ(0.4,
+            keyframe_model->TrimTimeToCurrentIteration(TicksFromSecondsF(0.4))
                 .InSecondsF());
-  keyframe_model->SetRunState(KeyframeModel::PAUSED, TicksFromSecondsF(0.5));
+  keyframe_model->Pause(base::TimeDelta::FromSecondsD(0.5));
   EXPECT_EQ(
       0.5, keyframe_model->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0))
                .InSecondsF());
@@ -459,6 +510,15 @@
   EXPECT_EQ(
       1, keyframe_model->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.5))
              .InSecondsF());
+  keyframe_model->Pause(base::TimeDelta::FromSecondsD(0.6));
+  EXPECT_EQ(
+      0.6, keyframe_model->TrimTimeToCurrentIteration(TicksFromSecondsF(2000.0))
+               .InSecondsF());
+  keyframe_model->SetRunState(KeyframeModel::RUNNING,
+                              TicksFromSecondsF(2000.0));
+  EXPECT_EQ(
+      0.7, keyframe_model->TrimTimeToCurrentIteration(TicksFromSecondsF(2000.1))
+               .InSecondsF());
 }
 
 TEST(KeyframeModelTest, TrimTimePauseResumeReverse) {
@@ -471,7 +531,7 @@
   EXPECT_EQ(0.5,
             keyframe_model->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))
                 .InSecondsF());
-  keyframe_model->SetRunState(KeyframeModel::PAUSED, TicksFromSecondsF(0.25));
+  keyframe_model->Pause(base::TimeDelta::FromSecondsD(0.25));
   EXPECT_EQ(0.75, keyframe_model
                       ->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0))
                       .InSecondsF());
diff --git a/cc/paint/BUILD.gn b/cc/paint/BUILD.gn
index a12b09a..74d2b6d 100644
--- a/cc/paint/BUILD.gn
+++ b/cc/paint/BUILD.gn
@@ -31,7 +31,6 @@
     "image_provider.h",
     "image_transfer_cache_entry.cc",
     "image_transfer_cache_entry.h",
-    "layer_tree_painter.h",
     "paint_cache.cc",
     "paint_cache.h",
     "paint_canvas.h",
@@ -61,6 +60,7 @@
     "paint_shader.cc",
     "paint_shader.h",
     "paint_worklet_input.h",
+    "paint_worklet_layer_painter.h",
     "raw_memory_transfer_cache_entry.cc",
     "raw_memory_transfer_cache_entry.h",
     "record_paint_canvas.cc",
diff --git a/cc/paint/layer_tree_painter.h b/cc/paint/layer_tree_painter.h
deleted file mode 100644
index 9536252..0000000
--- a/cc/paint/layer_tree_painter.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CC_PAINT_LAYER_TREE_PAINTER_H_
-#define CC_PAINT_LAYER_TREE_PAINTER_H_
-
-#include "cc/cc_export.h"
-
-namespace cc {
-
-class CC_EXPORT LayerTreePainter {
- public:
-  virtual ~LayerTreePainter() {}
-
-  // TODO(xidachen) add a PaintWorkletPaint function.
-};
-
-}  // namespace cc
-
-#endif  // CC_PAINT_LAYER_TREE_PAINTER_H_
diff --git a/cc/paint/paint_worklet_layer_painter.h b/cc/paint/paint_worklet_layer_painter.h
new file mode 100644
index 0000000..586bd3b7
--- /dev/null
+++ b/cc/paint/paint_worklet_layer_painter.h
@@ -0,0 +1,21 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_PAINT_PAINT_WORKLET_LAYER_PAINTER_H_
+#define CC_PAINT_PAINT_WORKLET_LAYER_PAINTER_H_
+
+#include "cc/cc_export.h"
+
+namespace cc {
+
+class CC_EXPORT PaintWorkletLayerPainter {
+ public:
+  virtual ~PaintWorkletLayerPainter() {}
+
+  // TODO(xidachen) add a PaintWorkletPaint function.
+};
+
+}  // namespace cc
+
+#endif  // CC_PAINT_PAINT_WORKLET_LAYER_PAINTER_H_
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 4a1c252..75d238e 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1457,9 +1457,6 @@
     load_library_from_apk = _is_modern && chromium_linker_supported
 
     version_name = chrome_version_name
-    if (!experimental_use_r8) {
-      enable_multidex = true
-    }
   }
 }
 
@@ -1558,9 +1555,6 @@
         (!defined(use_trichrome_library) || !use_trichrome_library)
 
     version_name = chrome_version_name
-    if (!experimental_use_r8) {
-      enable_multidex = true
-    }
   }
 }
 
@@ -1823,9 +1817,6 @@
   base_module_target = ":chrome_public_base_module"
   if (!is_java_debug) {
     proguard_enabled = true
-    if (!experimental_use_r8) {
-      enable_multidex = true
-    }
   }
 
   # Signing is very slow, only do that for official builds.
@@ -1839,9 +1830,6 @@
   base_module_target = ":chrome_modern_public_base_module"
   if (!is_java_debug) {
     proguard_enabled = true
-    if (!experimental_use_r8) {
-      enable_multidex = true
-    }
   }
   enable_language_splits = enable_chrome_language_splits
   if (modularize_vr) {
@@ -1859,9 +1847,6 @@
   base_module_target = ":monochrome_public_base_module"
   if (!is_java_debug) {
     proguard_enabled = true
-    if (!experimental_use_r8) {
-      enable_multidex = true
-    }
     proguard_android_sdk_dep = webview_framework_dep
   }
   enable_language_splits = enable_chrome_language_splits
diff --git a/chrome/android/java/res/layout-sw600dp/location_status_icon.xml b/chrome/android/java/res/layout-sw600dp/location_status_icon.xml
new file mode 100644
index 0000000..2a5653c
--- /dev/null
+++ b/chrome/android/java/res/layout-sw600dp/location_status_icon.xml
@@ -0,0 +1,17 @@
+<?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. -->
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <org.chromium.ui.widget.ChromeImageButton
+        android:id="@+id/location_bar_status_icon"
+        style="@style/LocationBarButton"
+        android:layout_width="@dimen/location_bar_icon_width"
+        android:layout_height="match_parent"
+        android:layout_gravity="center"
+        android:contentDescription="@string/accessibility_toolbar_btn_site_info"
+        android:scaleType="center" />
+</merge>
diff --git a/chrome/android/java/res/layout/autofill_name_fixflow.xml b/chrome/android/java/res/layout/autofill_name_fixflow.xml
index c091f65..96ebfcec 100644
--- a/chrome/android/java/res/layout/autofill_name_fixflow.xml
+++ b/chrome/android/java/res/layout/autofill_name_fixflow.xml
@@ -11,6 +11,7 @@
     style="@style/AlertDialogContent"
     android:minHeight="36dp"
     android:layout_marginBottom="32dp"
+    android:paddingBottom="16dp"
     android:layout_marginTop="6dp"
     android:orientation="horizontal"
     android:gravity="center_vertical">
@@ -30,7 +31,7 @@
             android:hint="@string/autofill_card_holder_name"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:imeOptions="flagNoExtractUi"
+            android:imeOptions="actionDone"
             android:inputType="textCapWords"/>
 
     </org.chromium.chrome.browser.widget.CompatibilityTextInputLayout>
diff --git a/chrome/android/java/res/layout/location_status.xml b/chrome/android/java/res/layout/location_status.xml
index ea736de2..a993528 100644
--- a/chrome/android/java/res/layout/location_status.xml
+++ b/chrome/android/java/res/layout/location_status.xml
@@ -11,14 +11,7 @@
         android:layout_width="wrap_content"
         android:layout_height="match_parent">
 
-        <org.chromium.ui.widget.ChromeImageButton
-            android:id="@+id/location_bar_status_icon"
-            style="@style/LocationBarButton"
-            android:layout_width="@dimen/location_bar_start_icon_width"
-            android:layout_height="match_parent"
-            android:layout_gravity="center"
-            android:contentDescription="@string/accessibility_toolbar_btn_site_info"
-            android:scaleType="center" />
+        <include layout="@layout/location_status_icon" />
 
         <TextView android:id="@+id/location_bar_verbose_status"
             android:layout_width="wrap_content"
diff --git a/chrome/android/java/res/layout/location_status_icon.xml b/chrome/android/java/res/layout/location_status_icon.xml
new file mode 100644
index 0000000..722f8a2
--- /dev/null
+++ b/chrome/android/java/res/layout/location_status_icon.xml
@@ -0,0 +1,18 @@
+<?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. -->
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <org.chromium.ui.widget.ChromeImageButton
+        android:id="@+id/location_bar_status_icon"
+        style="@style/LocationBarButton"
+        android:layout_width="@dimen/location_bar_start_icon_width"
+        android:layout_height="match_parent"
+        android:layout_gravity="center"
+        android:paddingEnd="4dp"
+        android:contentDescription="@string/accessibility_toolbar_btn_site_info"
+        android:scaleType="center" />
+</merge>
diff --git a/chrome/android/java/res/layout/preference_text_scale.xml b/chrome/android/java/res/layout/preference_text_scale.xml
index 6281107c..6645594 100644
--- a/chrome/android/java/res/layout/preference_text_scale.xml
+++ b/chrome/android/java/res/layout/preference_text_scale.xml
@@ -32,12 +32,13 @@
     <TextView
         android:id="@+id/preview"
         android:layout_width="match_parent"
-        android:layout_height="180dp"
+        android:layout_height="wrap_content"
+        android:minHeight="180dp"
         android:layout_marginTop="10dp"
         android:background="#fafafa"
         android:padding="16dp"
         android:text="@string/font_size_preview_text"
-        android:textColor="?android:attr/textColorPrimary"
+        android:textAppearance="@style/TextAppearance.AccessibilityTextPreference"
         android:lineSpacingExtra="6dp" />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml
index b635b01..1e93ba6 100644
--- a/chrome/android/java/res/values-v17/styles.xml
+++ b/chrome/android/java/res/values-v17/styles.xml
@@ -40,6 +40,8 @@
         <item name="android:windowExitTransition" tools:targetApi="21">@null</item>
         <item name="android:windowSharedElementEnterTransition" tools:targetApi="21">@transition/move_image</item>
         <item name="android:windowSharedElementExitTransition" tools:targetApi="21">@transition/move_image</item>
+        <item name="android:popupMenuStyle">@style/PopupMenuStyle</item>
+        <item name="android:contextPopupMenuStyle" tools:targetApi="24">@style/PopupMenuStyle</item>
     </style>
     <style name="MainTheme" parent="MainThemeBase" />
     <style name="TranslucentMainTheme" parent="MainThemeBase">
@@ -74,6 +76,8 @@
         <item name="colorControlHighlight">@color/control_highlight_color</item>
         <item name="spinnerStyle">@style/SpinnerStyle</item>
         <item name="windowNoTitle">true</item>
+        <item name="android:popupMenuStyle">@style/PopupMenuStyle</item>
+        <item name="android:contextPopupMenuStyle" tools:targetApi="24">@style/PopupMenuStyle</item>
     </style>
     <style name="FullscreenWhite" parent="FullscreenWhiteBase" />
 
@@ -144,6 +148,8 @@
         <item name="floatLabelPaddingTop">@dimen/pref_autofill_field_top_margin</item>
         <item name="actionBarStyle">@style/PreferenceActionBarModern</item>
         <item name="android:windowContentOverlay">@null</item>
+        <item name="android:popupMenuStyle">@style/PopupMenuStyle</item>
+        <item name="android:contextPopupMenuStyle" tools:targetApi="24">@style/PopupMenuStyle</item>
     </style>
     <style name="PreferenceActionBarModern" parent="@style/Widget.AppCompat.Light.ActionBar.Solid">
       <item name="titleTextStyle">@style/BlackHeadline</item>
@@ -201,6 +207,9 @@
         <item name="android:layout_marginTop">2dp</item>
         <item name="android:background">@color/modern_grey_600</item>
     </style>
+    <style name="TextAppearance.AccessibilityTextPreference">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
 
     <style name="ThemeWithActionBarBase" parent="Theme.AppCompat.Light">
         <item name="android:windowBackground">@drawable/action_bar_activity_bg</item>
@@ -212,6 +221,8 @@
         <item name="colorAccent">@color/pref_accent_color</item>
         <item name="colorControlHighlight">@color/control_highlight_color</item>
         <item name="spinnerStyle">@style/SpinnerStyle</item>
+        <item name="android:popupMenuStyle">@style/PopupMenuStyle</item>
+        <item name="android:contextPopupMenuStyle" tools:targetApi="24">@style/PopupMenuStyle</item>
     </style>
     <style name="ThemeWithActionBar" parent="ThemeWithActionBarBase" />
 
@@ -337,6 +348,13 @@
         <item name="android:popupElevation" tools:targetApi="21">0dp</item>
     </style>
 
+    <!-- Popup and long-press context popup menu style -->
+    <style name="PopupMenuStyle" parent="Widget.AppCompat.Light.PopupMenu">
+        <item name="android:popupBackground">@drawable/popup_bg</item>
+        <item name="android:overlapAnchor" tools:targetApi="21">true</item>
+        <item name="android:popupElevation" tools:targetApi="21">0dp</item>
+    </style>
+
     <!-- Bubble styles -->
     <style name="TextBubbleAnimation">
         <item name="android:windowEnterAnimation">@anim/textbubble_in</item>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUndoController.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUndoController.java
index 6f4cfa9..fad6de9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUndoController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUndoController.java
@@ -82,10 +82,11 @@
         if (!isUndoable) return;
 
         if (titles.length == 1) {
-            mSnackbarManager.showSnackbar(Snackbar
-                    .make(titles[0], this, Snackbar.TYPE_ACTION, Snackbar.UMA_BOOKMARK_DELETE_UNDO)
-                    .setTemplateText(mContext.getString(R.string.undo_bar_delete_message))
-                    .setAction(mContext.getString(R.string.undo), null));
+            mSnackbarManager.showSnackbar(
+                    Snackbar.make(titles[0], this, Snackbar.TYPE_ACTION,
+                                    Snackbar.UMA_BOOKMARK_DELETE_UNDO)
+                            .setTemplateText(mContext.getString(R.string.delete_message))
+                            .setAction(mContext.getString(R.string.undo), null));
         } else {
             mSnackbarManager.showSnackbar(
                     Snackbar.make(String.format(Locale.getDefault(), "%d", titles.length), this,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
index 14ab7b6..e113352 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
@@ -89,6 +89,7 @@
     private CompositorView mCompositorView;
 
     private boolean mContentOverlayVisiblity = true;
+    private boolean mCanBeFocusable;
 
     private int mPendingFrameCount;
 
@@ -817,9 +818,10 @@
     }
 
     @Override
-    public void setContentOverlayVisibility(boolean show) {
-        if (show != mContentOverlayVisiblity) {
+    public void setContentOverlayVisibility(boolean show, boolean canBeFocusable) {
+        if (show != mContentOverlayVisiblity || canBeFocusable != mCanBeFocusable) {
             mContentOverlayVisiblity = show;
+            mCanBeFocusable = canBeFocusable;
             updateContentOverlayVisibility(mContentOverlayVisiblity);
         }
     }
@@ -1019,8 +1021,8 @@
             }
         } else {
             if (mView.getParent() == this) {
-                setFocusable(true);
-                setFocusableInTouchMode(true);
+                setFocusable(mCanBeFocusable);
+                setFocusableInTouchMode(mCanBeFocusable);
 
                 if (webContents != null && !webContents.isDestroyed()) {
                     getContentView().setVisibility(View.INVISIBLE);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java
index 6486adc..ea485d6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java
@@ -764,6 +764,14 @@
     }
 
     /**
+     * @return True if the host container can set itself as focusable e.g. for accessibility.
+     *         Subclasses can override e.g. to provide a different default focused view.
+     */
+    public boolean canHostBeFocusable() {
+        return true;
+    }
+
+    /**
      * @param e                 The {@link MotionEvent} to consider.
      * @param offsets           The current touch offsets that should be applied to the
      *                          {@link EventFilter}s.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
index d40da26a..0766793 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
@@ -809,7 +809,8 @@
 
         onViewportChanged();
         getActiveLayout().show(time(), animate);
-        mHost.setContentOverlayVisibility(getActiveLayout().shouldDisplayContentOverlay());
+        mHost.setContentOverlayVisibility(getActiveLayout().shouldDisplayContentOverlay(),
+                getActiveLayout().canHostBeFocusable());
         mHost.requestRender();
 
         // Notify observers about the new scene.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerHost.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerHost.java
index 73b543a..44fa199 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerHost.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerHost.java
@@ -86,8 +86,9 @@
     /**
      * Sets the visibility of the content overlays.
      * @param show True if the content overlays should be shown.
+     * @param canBeFocusable Whether the host view can make itself focusable e.g. for accessibility.
      */
-    void setContentOverlayVisibility(boolean show);
+    void setContentOverlayVisibility(boolean show, boolean canBeFocusable);
 
     /**
      * @return The {@link TitleCache} to use to store title bitmaps.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsBridge.java
index bfb5734..fabe543 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsBridge.java
@@ -4,8 +4,6 @@
 
 package org.chromium.chrome.browser.contextual_suggestions;
 
-import android.graphics.Bitmap;
-
 import org.chromium.base.Callback;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
@@ -88,18 +86,27 @@
         nativeFetchSuggestions(mNativeContextualSuggestionsBridge, url, callback);
     }
 
-    /** Fetches a thumbnail for the suggestion. */
-    void fetchSuggestionImage(SnippetArticle suggestion, Callback<Bitmap> callback) {
+    /**
+     * Get the url of the image used for the given suggestion.
+     *
+     * @param suggestion The suggestion that the image url is for.
+     * @return The url for the image or null if one can't be found.
+     */
+    String getImageUrl(SnippetArticle suggestion) {
         assert mNativeContextualSuggestionsBridge != 0;
-        nativeFetchSuggestionImage(
-                mNativeContextualSuggestionsBridge, suggestion.mIdWithinCategory, callback);
+        return nativeGetImageUrl(mNativeContextualSuggestionsBridge, suggestion.mIdWithinCategory);
     }
 
-    /** Fetches a favicon for the suggestion. */
-    void fetchSuggestionFavicon(SnippetArticle suggestion, Callback<Bitmap> callback) {
+    /**
+     * Get the url of the favicon used for the given suggestion.
+     *
+     * @param suggestion The suggestion that the favicon url is for.
+     * @return The url for the image or null if one can't be found.
+     */
+    String getFaviconUrl(SnippetArticle suggestion) {
         assert mNativeContextualSuggestionsBridge != 0;
-        nativeFetchSuggestionFavicon(
-                mNativeContextualSuggestionsBridge, suggestion.mIdWithinCategory, callback);
+        return nativeGetFaviconUrl(
+                mNativeContextualSuggestionsBridge, suggestion.mIdWithinCategory);
     }
 
     /** Requests the backend to clear state related to this bridge. */
@@ -157,10 +164,10 @@
     private native void nativeDestroy(long nativeContextualSuggestionsBridge);
     private native void nativeFetchSuggestions(long nativeContextualSuggestionsBridge, String url,
             Callback<ContextualSuggestionsResult> callback);
-    private native void nativeFetchSuggestionImage(
-            long nativeContextualSuggestionsBridge, String suggestionId, Callback<Bitmap> callback);
-    private native void nativeFetchSuggestionFavicon(
-            long nativeContextualSuggestionsBridge, String suggestionId, Callback<Bitmap> callback);
+    private native String nativeGetImageUrl(
+            long nativeContextualSuggestionsBridge, String suggestionId);
+    private native String nativeGetFaviconUrl(
+            long nativeContextualSuggestionsBridge, String suggestionId);
     private native void nativeClearState(long nativeContextualSuggestionsBridge);
     private native void nativeReportEvent(
             long nativeContextualSuggestionsBridge, WebContents webContents, int eventId);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsSourceImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsSourceImpl.java
index f345ab3e..febd0af8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsSourceImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsSourceImpl.java
@@ -7,6 +7,10 @@
 import android.graphics.Bitmap;
 
 import org.chromium.base.Callback;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.task.PostTask;
+import org.chromium.base.task.TaskTraits;
+import org.chromium.chrome.browser.cached_image_fetcher.CachedImageFetcher;
 import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsBridge.ContextualSuggestionsResult;
 import org.chromium.chrome.browser.ntp.snippets.EmptySuggestionsSource;
 import org.chromium.chrome.browser.ntp.snippets.SnippetArticle;
@@ -19,6 +23,7 @@
 class ContextualSuggestionsSourceImpl
         extends EmptySuggestionsSource implements ContextualSuggestionsSource {
     private ContextualSuggestionsBridge mBridge;
+    private CachedImageFetcher mCachedImageFetcher;
 
     /**
      * Creates a ContextualSuggestionsSource for getting contextual suggestions for the current
@@ -28,6 +33,7 @@
      */
     public ContextualSuggestionsSourceImpl(Profile profile) {
         mBridge = new ContextualSuggestionsBridge(profile);
+        mCachedImageFetcher = CachedImageFetcher.getInstance();
     }
 
     @Override
@@ -37,19 +43,32 @@
 
     @Override
     public void fetchSuggestionImage(SnippetArticle suggestion, Callback<Bitmap> callback) {
-        mBridge.fetchSuggestionImage(suggestion, callback);
+        String url = mBridge.getImageUrl(suggestion);
+        mCachedImageFetcher.fetchImage(url, callback);
     }
 
     @Override
     public void fetchContextualSuggestionImage(
             SnippetArticle suggestion, Callback<Bitmap> callback) {
-        mBridge.fetchSuggestionImage(suggestion, callback);
+        String url = mBridge.getImageUrl(suggestion);
+        if (url == null) {
+            PostTask.postTask(new TaskTraits(), () -> callback.onResult(null));
+            return;
+        }
+
+        mCachedImageFetcher.fetchImage(url, callback);
     }
 
     @Override
     public void fetchSuggestionFavicon(SnippetArticle suggestion, int minimumSizePx,
             int desiredSizePx, Callback<Bitmap> callback) {
-        mBridge.fetchSuggestionFavicon(suggestion, callback);
+        String url = mBridge.getFaviconUrl(suggestion);
+        if (url == null) {
+            PostTask.postTask(new TaskTraits(), () -> callback.onResult(null));
+            return;
+        }
+
+        mCachedImageFetcher.fetchImage(url, desiredSizePx, desiredSizePx, callback);
     }
 
     @Override
@@ -66,4 +85,11 @@
     public void clearState() {
         mBridge.clearState();
     }
+
+    @VisibleForTesting
+    ContextualSuggestionsSourceImpl(
+            ContextualSuggestionsBridge bridge, CachedImageFetcher cachedImageFethcer) {
+        mBridge = bridge;
+        mCachedImageFetcher = cachedImageFethcer;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java
index 5e6a59c..e0c7821 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java
@@ -34,22 +34,17 @@
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.base.library_loader.ProcessInitException;
 import org.chromium.chrome.browser.ChromeApplication;
-import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.download.DownloadNotificationUmaHelper.UmaDownloadResumption;
 import org.chromium.chrome.browser.download.items.OfflineContentAggregatorNotificationBridgeUiFactory;
 import org.chromium.chrome.browser.init.BrowserParts;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.init.EmptyBrowserParts;
-import org.chromium.chrome.browser.init.ServiceManagerStartupUtils;
 import org.chromium.chrome.browser.util.IntentUtils;
 import org.chromium.components.offline_items_collection.ContentId;
 import org.chromium.components.offline_items_collection.LegacyHelpers;
 import org.chromium.components.offline_items_collection.PendingState;
 import org.chromium.content_public.browser.BrowserStartupController;
 
-import java.util.HashSet;
-import java.util.Set;
-
 /**
  * Class that spins up native when an interaction with a notification happens and passes the
  * relevant information on to native.
@@ -202,10 +197,7 @@
             @Override
             public boolean startServiceManagerOnly() {
                 if (!LegacyHelpers.isLegacyDownload(id)) return false;
-                Set<String> features = new HashSet<String>();
-                features.add(ChromeFeatureList.SERVICE_MANAGER_FOR_DOWNLOAD);
-                features.add(ChromeFeatureList.NETWORK_SERVICE);
-                return ServiceManagerStartupUtils.canStartServiceManager(features)
+                return DownloadUtils.shouldStartServiceManagerOnly()
                         && !ACTION_DOWNLOAD_OPEN.equals(intent.getAction());
             }
         };
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
index 265beeb..e3e5d66 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
@@ -42,6 +42,7 @@
 import org.chromium.chrome.browser.download.ui.DownloadHistoryItemWrapper.OfflineItemWrapper;
 import org.chromium.chrome.browser.feature_engagement.ScreenshotTabObserver;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
+import org.chromium.chrome.browser.init.ServiceManagerStartupUtils;
 import org.chromium.chrome.browser.media.MediaViewerUtils;
 import org.chromium.chrome.browser.offlinepages.DownloadUiActionFlags;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
@@ -81,9 +82,11 @@
 import java.util.Calendar;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -999,6 +1002,14 @@
                 && entry.isAutoResumable;
     }
 
+    /** @return Whether we should start service manager only, based off the features enabled. */
+    public static boolean shouldStartServiceManagerOnly() {
+        Set<String> features = new HashSet<String>();
+        features.add(ChromeFeatureList.SERVICE_MANAGER_FOR_DOWNLOAD);
+        features.add(ChromeFeatureList.NETWORK_SERVICE);
+        return ServiceManagerStartupUtils.canStartServiceManager(features);
+    }
+
     /**
      * Format the number of bytes into KB, or MB, or GB and return the corresponding string
      * resource. Uses default download-related set of strings.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/snackbars/UndoUiUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/snackbars/UndoUiUtils.java
index 2e49528..0e9bd09 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/snackbars/UndoUiUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/snackbars/UndoUiUtils.java
@@ -27,7 +27,7 @@
     public static String getTemplateTextFor(Collection<OfflineItem> items) {
         Context context = ContextUtils.getApplicationContext();
         return items.size() == 1
-                ? context.getString(R.string.undo_bar_delete_message)
+                ? context.getString(R.string.delete_message)
                 : context.getString(R.string.undo_bar_multiple_downloads_delete_message);
     }
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/service/DownloadBackgroundTask.java b/chrome/android/java/src/org/chromium/chrome/browser/download/service/DownloadBackgroundTask.java
index 80a55b62e..a21f939 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/service/DownloadBackgroundTask.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/service/DownloadBackgroundTask.java
@@ -10,6 +10,7 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.chrome.browser.background_task_scheduler.NativeBackgroundTask;
 import org.chromium.chrome.browser.background_task_scheduler.NativeBackgroundTask.StartBeforeNativeResult;
+import org.chromium.chrome.browser.download.DownloadUtils;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.background_task_scheduler.TaskParameters;
 import org.chromium.components.download.DownloadTaskType;
@@ -22,6 +23,8 @@
  * Entry point for the download service to perform desired action when the task is fired by the
  * scheduler. The scheduled task is executed for the regular profile and also for incognito profile
  * if an incognito profile exists.
+ * TODO(shaktisahu): Since we probably don't need to run tasks for incognito profile, cleanup this
+ * class and remove any reference to profiles.
  */
 @JNINamespace("download::android")
 public class DownloadBackgroundTask extends NativeBackgroundTask {
@@ -37,6 +40,9 @@
     // Keeps track of in progress tasks which haven't invoked their {@link TaskFinishedCallback}s.
     private Map<Integer, PendingTaskCounter> mPendingTaskCounters = new HashMap<>();
 
+    @DownloadTaskType
+    private int mCurrentTaskType;
+
     @Override
     protected @StartBeforeNativeResult int onStartTaskBeforeNativeLoaded(
             Context context, TaskParameters taskParameters, TaskFinishedCallback callback) {
@@ -59,34 +65,42 @@
         // In case of future upgrades, we would need to build an intent for the old version and
         // validate that this code still works. This would require decoupling this immediate class
         // from native as well.
-        @DownloadTaskType
-        final int taskType =
-                taskParameters.getExtras().getInt(DownloadTaskScheduler.EXTRA_TASK_TYPE);
+        mCurrentTaskType = taskParameters.getExtras().getInt(DownloadTaskScheduler.EXTRA_TASK_TYPE);
 
         Callback<Boolean> wrappedCallback = new Callback<Boolean>() {
             @Override
             public void onResult(Boolean needsReschedule) {
-                if (mPendingTaskCounters.get(taskType) == null) return;
+                if (mPendingTaskCounters.get(mCurrentTaskType) == null) return;
 
                 boolean noPendingCallbacks =
-                        decrementPendingCallbackCount(taskType, needsReschedule);
+                        decrementPendingCallbackCount(mCurrentTaskType, needsReschedule);
                 if (noPendingCallbacks) {
-                    callback.taskFinished(mPendingTaskCounters.get(taskType).needsReschedule);
-                    mPendingTaskCounters.remove(taskType);
+                    callback.taskFinished(
+                            mPendingTaskCounters.get(mCurrentTaskType).needsReschedule);
+                    mPendingTaskCounters.remove(mCurrentTaskType);
                 }
             }
         };
 
-        Profile profile = Profile.getLastUsedProfile().getOriginalProfile();
-        incrementPendingCallbackCount(taskType);
-        nativeStartBackgroundTask(profile, taskType, wrappedCallback);
+        Profile profile = supportsServiceManagerOnly()
+                ? null
+                : Profile.getLastUsedProfile().getOriginalProfile();
+        incrementPendingCallbackCount(mCurrentTaskType);
+        nativeStartBackgroundTask(profile, mCurrentTaskType, wrappedCallback);
 
-        if (profile.hasOffTheRecordProfile()) {
-            incrementPendingCallbackCount(taskType);
-            nativeStartBackgroundTask(profile.getOffTheRecordProfile(), taskType, wrappedCallback);
+        if (profile != null && profile.hasOffTheRecordProfile()) {
+            incrementPendingCallbackCount(mCurrentTaskType);
+            nativeStartBackgroundTask(
+                    profile.getOffTheRecordProfile(), mCurrentTaskType, wrappedCallback);
         }
     }
 
+    @Override
+    protected boolean supportsServiceManagerOnly() {
+        return mCurrentTaskType == DownloadTaskType.DOWNLOAD_AUTO_RESUMPTION_TASK
+                && DownloadUtils.shouldStartServiceManagerOnly();
+    }
+
     private void incrementPendingCallbackCount(@DownloadTaskType int taskType) {
         PendingTaskCounter taskCounter = mPendingTaskCounters.containsKey(taskType)
                 ? mPendingTaskCounters.get(taskType)
@@ -117,10 +131,12 @@
         int taskType = taskParameters.getExtras().getInt(DownloadTaskScheduler.EXTRA_TASK_TYPE);
         mPendingTaskCounters.remove(taskType);
 
-        Profile profile = Profile.getLastUsedProfile().getOriginalProfile();
+        Profile profile = supportsServiceManagerOnly()
+                ? null
+                : Profile.getLastUsedProfile().getOriginalProfile();
         boolean needsReschedule = nativeStopBackgroundTask(profile, taskType);
 
-        if (profile.hasOffTheRecordProfile()) {
+        if (profile != null && profile.hasOffTheRecordProfile()) {
             needsReschedule |= nativeStopBackgroundTask(profile.getOffTheRecordProfile(), taskType);
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/service/DownloadTaskScheduler.java b/chrome/android/java/src/org/chromium/chrome/browser/download/service/DownloadTaskScheduler.java
index e54d9e3..3b71fa1f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/service/DownloadTaskScheduler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/service/DownloadTaskScheduler.java
@@ -29,6 +29,7 @@
     public static final String EXTRA_TASK_TYPE = "extra_task_type";
     static final long TWELVE_HOURS_IN_SECONDS = TimeUnit.HOURS.toSeconds(12);
     static final long FIVE_MINUTES_IN_SECONDS = TimeUnit.MINUTES.toSeconds(5);
+    static final long ONE_DAY_IN_SECONDS = TimeUnit.DAYS.toSeconds(1);
 
     @CalledByNative
     private static void scheduleTask(@DownloadTaskType int taskType,
@@ -69,6 +70,8 @@
                 2 * FIVE_MINUTES_IN_SECONDS);
         scheduleTask(DownloadTaskType.CLEANUP_TASK, false, false, 0, TWELVE_HOURS_IN_SECONDS,
                 2 * TWELVE_HOURS_IN_SECONDS);
+        scheduleTask(DownloadTaskType.DOWNLOAD_AUTO_RESUMPTION_TASK, false, false, 0,
+                FIVE_MINUTES_IN_SECONDS, ONE_DAY_IN_SECONDS);
     }
 
     private static int getTaskId(@DownloadTaskType int taskType) {
@@ -77,6 +80,8 @@
                 return TaskIds.DOWNLOAD_SERVICE_JOB_ID;
             case DownloadTaskType.CLEANUP_TASK:
                 return TaskIds.DOWNLOAD_CLEANUP_JOB_ID;
+            case DownloadTaskType.DOWNLOAD_AUTO_RESUMPTION_TASK:
+                return TaskIds.DOWNLOAD_AUTO_RESUMPTION_JOB_ID;
             default:
                 assert false;
                 return -1;
@@ -85,9 +90,16 @@
 
     private static int getRequiredNetworkType(
             @DownloadTaskType int taskType, boolean requiresUnmeteredNetwork) {
-        if (taskType != DownloadTaskType.DOWNLOAD_TASK) return TaskInfo.NETWORK_TYPE_NONE;
-
-        return requiresUnmeteredNetwork ? TaskInfo.NETWORK_TYPE_UNMETERED
-                                        : TaskInfo.NETWORK_TYPE_ANY;
+        switch (taskType) {
+            case DownloadTaskType.CLEANUP_TASK:
+                return TaskInfo.NETWORK_TYPE_NONE;
+            case DownloadTaskType.DOWNLOAD_TASK: // intentional fall-through
+            case DownloadTaskType.DOWNLOAD_AUTO_RESUMPTION_TASK:
+                return requiresUnmeteredNetwork ? TaskInfo.NETWORK_TYPE_UNMETERED
+                                                : TaskInfo.NETWORK_TYPE_ANY;
+            default:
+                assert false;
+                return TaskInfo.NETWORK_TYPE_ANY;
+        }
     }
 }
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 a574193..c5461e8d 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
@@ -471,7 +471,8 @@
         String snackbarText = singleItemDeleted
                 ? items.get(0).getDisplayFileName()
                 : String.format(Locale.getDefault(), "%d", items.size());
-        int snackbarTemplateId = singleItemDeleted ? R.string.undo_bar_delete_message
+        int snackbarTemplateId = singleItemDeleted
+                ? R.string.delete_message
                 : R.string.undo_bar_multiple_downloads_delete_message;
 
         Snackbar snackbar = Snackbar.make(snackbarText, mUndoDeletionSnackbarController,
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 e4bb173..02e33c3 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
@@ -222,11 +222,25 @@
             recordSelectionCountHistorgram("Remove");
             recordUserActionWithOptionalSearch("RemoveSelected");
 
+            int numItemsRemoved = 0;
+            HistoryItem lastItemRemoved = null;
             for (HistoryItem historyItem : mSelectionDelegate.getSelectedItems()) {
                 mHistoryAdapter.markItemForRemoval(historyItem);
+                numItemsRemoved++;
+                lastItemRemoved = historyItem;
             }
+
             mHistoryAdapter.removeItems();
             mSelectionDelegate.clearSelection();
+
+            if (numItemsRemoved == 1) {
+                assert lastItemRemoved != null;
+                announceItemRemoved(lastItemRemoved);
+            } else if (numItemsRemoved > 1) {
+                mRecyclerView.announceForAccessibility(mRecyclerView.getContext().getString(
+                        R.string.multiple_history_items_deleted, numItemsRemoved));
+            }
+
             return true;
         } else if (item.getItemId() == R.id.search_menu_id) {
             mHistoryAdapter.removeHeader();
@@ -285,6 +299,12 @@
         }
         mHistoryAdapter.markItemForRemoval(item);
         mHistoryAdapter.removeItems();
+        announceItemRemoved(item);
+    }
+
+    private void announceItemRemoved(HistoryItem item) {
+        mRecyclerView.announceForAccessibility(
+                mRecyclerView.getContext().getString(R.string.delete_message, item.getTitle()));
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
index 59e6dd41..a489f1f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
@@ -23,7 +23,6 @@
  * Container that holds the {@link UrlBar} and SSL state related with the current {@link Tab}.
  */
 public interface LocationBar extends UrlBarDelegate {
-
     /**
      * Handles native dependent initialization for this class.
      */
@@ -134,7 +133,7 @@
     /**
      * Updates the security icon displayed in the LocationBar.
      */
-    void updateSecurityIcon();
+    void updateStatusIcon();
 
     /**
      * @return The {@link ViewGroup} that this container holds.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index e8044731..5bae736 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -247,8 +247,7 @@
     }
 
     @Override
-    public void initializeControls(WindowDelegate windowDelegate,
-            WindowAndroid windowAndroid) {
+    public void initializeControls(WindowDelegate windowDelegate, WindowAndroid windowAndroid) {
         mWindowDelegate = windowDelegate;
         mWindowAndroid = windowAndroid;
 
@@ -331,8 +330,7 @@
         // This will only be called once at least one tab exists, and the tab model is told to
         // update its state. During Chrome initialization the tab model update happens after the
         // call to onNativeLibraryReady, so this assert will not fire.
-        assert mNativeInitialized
-                : "Setting Autocomplete Profile before native side initialized";
+        assert mNativeInitialized : "Setting Autocomplete Profile before native side initialized";
         mAutocompleteCoordinator.setAutocompleteProfile(profile);
         mOmniboxPrerender.initializeForProfile(profile);
     }
@@ -581,8 +579,8 @@
      * Updates the security icon displayed in the LocationBar.
      */
     @Override
-    public void updateSecurityIcon() {
-        mStatusViewCoordinator.updateSecurityIcon();
+    public void updateStatusIcon() {
+        mStatusViewCoordinator.updateStatusIcon();
         // Update the URL in case the scheme change triggers a URL emphasis change.
         setUrlToPageUrl();
     }
@@ -622,21 +620,21 @@
                 int widthMeasureSpec;
                 int heightMeasureSpec;
                 if (childLayoutParams.width == LayoutParams.WRAP_CONTENT) {
-                    widthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                            getMeasuredWidth(), MeasureSpec.AT_MOST);
+                    widthMeasureSpec =
+                            MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST);
                 } else if (childLayoutParams.width == LayoutParams.MATCH_PARENT) {
-                    widthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                            getMeasuredWidth(), MeasureSpec.EXACTLY);
+                    widthMeasureSpec =
+                            MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY);
                 } else {
                     widthMeasureSpec = MeasureSpec.makeMeasureSpec(
                             childLayoutParams.width, MeasureSpec.EXACTLY);
                 }
                 if (childLayoutParams.height == LayoutParams.WRAP_CONTENT) {
-                    heightMeasureSpec = MeasureSpec.makeMeasureSpec(
-                            getMeasuredHeight(), MeasureSpec.AT_MOST);
+                    heightMeasureSpec =
+                            MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST);
                 } else if (childLayoutParams.height == LayoutParams.MATCH_PARENT) {
-                    heightMeasureSpec = MeasureSpec.makeMeasureSpec(
-                            getMeasuredHeight(), MeasureSpec.EXACTLY);
+                    heightMeasureSpec =
+                            MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY);
                 } else {
                     heightMeasureSpec = MeasureSpec.makeMeasureSpec(
                             childLayoutParams.height, MeasureSpec.EXACTLY);
@@ -782,7 +780,6 @@
         });
     }
 
-
     @Override
     public void onClick(View v) {
         if (v == mDeleteButton) {
@@ -947,7 +944,7 @@
     public void updateLoadingState(boolean updateUrl) {
         if (updateUrl) setUrlToPageUrl();
         updateNavigationButton();
-        mStatusViewCoordinator.updateSecurityIcon();
+        mStatusViewCoordinator.updateStatusIcon();
     }
 
     /** @return The current active {@link Tab}. */
@@ -1070,10 +1067,10 @@
     }
 
     @Override
-    public void setTitleToPageTitle() { }
+    public void setTitleToPageTitle() {}
 
     @Override
-    public void setShowTitle(boolean showTitle) { }
+    public void setShowTitle(boolean showTitle) {}
 
     @Override
     public WindowAndroid getWindowAndroid() {
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 978e64e..bfae132 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
@@ -23,7 +23,6 @@
     private boolean mVerboseStatusSpaceAvailable;
     private boolean mPageIsPreview;
     private boolean mPageIsOffline;
-    private boolean mTabletMode;
 
     private int mUrlMinWidth;
     private int mSeparatorMinWidth;
@@ -110,13 +109,6 @@
     }
 
     /**
-     * Toggle tablet mode.
-     */
-    void setTabletMode(boolean isTablet) {
-        mTabletMode = isTablet;
-    }
-
-    /**
      * Specify object to receive status click events.
      *
      * @param listener Specifies target object to receive events.
@@ -265,7 +257,7 @@
      *     - not shown if URL is focused.
      */
     private void updateLocationBarIcon() {
-        if (mUrlHasFocus && mTabletMode) {
+        if (mUrlHasFocus) {
             mModel.set(StatusProperties.STATUS_ICON_RES, mNavigationIconRes);
             mModel.set(StatusProperties.STATUS_ICON_TINT_RES, mNavigationIconTintRes);
             mModel.set(StatusProperties.STATUS_ICON_DESCRIPTION_RES,
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 8e4c04b..dcecd81 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
@@ -73,9 +73,6 @@
 
         mMediator.setVerboseStatusTextMinWidth(
                 res.getDimensionPixelSize(R.dimen.location_bar_min_verbose_status_text_width));
-
-        mMediator.setTabletMode(mIsTablet);
-        mMediator.setNavigationButtonType(NavigationButtonType.PAGE);
     }
 
     /**
@@ -110,13 +107,13 @@
 
         // TODO(ender): remove this once icon selection has complete set of
         // corresponding properties (for tinting etc).
-        updateSecurityIcon();
+        updateStatusIcon();
     }
 
     /**
      * Updates the security icon displayed in the LocationBar.
      */
-    public void updateSecurityIcon() {
+    public void updateStatusIcon() {
         mMediator.setSecurityIconResource(mToolbarDataProvider.getSecurityIconResource(mIsTablet));
         mMediator.setSecurityIconTint(mToolbarDataProvider.getSecurityIconColorStateList());
         mMediator.setSecurityIconDescription(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index cae5c8c..7ff42bb4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -127,7 +127,6 @@
      * Handle UI updates of menu icons. Only applicable for phones.
      */
     public interface MenuDelegatePhone {
-
         /**
          * Called when current tab's loading status changes.
          *
@@ -408,13 +407,13 @@
                 if (mLocationBarModel.getTab() == null) return;
 
                 assert tab == mLocationBarModel.getTab();
-                mLocationBar.updateSecurityIcon();
+                mLocationBar.updateStatusIcon();
                 mLocationBar.setUrlToPageUrl();
             }
 
             @Override
             public void didReloadLoFiImages(Tab tab) {
-                mLocationBar.updateSecurityIcon();
+                mLocationBar.updateStatusIcon();
             }
 
             @Override
@@ -459,7 +458,7 @@
                     // finishes loading. If this is a preview, update the security icon which will
                     // also update the verbose status view to make sure the "Lite" badge is
                     // displayed.
-                    mLocationBar.updateSecurityIcon();
+                    mLocationBar.updateStatusIcon();
                     PreviewsUma.recordLitePageAtLoadFinish(
                             PreviewsAndroidBridge.getInstance().getPreviewsType(
                                     tab.getWebContents()));
@@ -603,7 +602,7 @@
                     // Some previews are not fully decided until the page commits. If this
                     // is a preview, update the security icon which will also update the verbose
                     // status view to make sure the "Lite" badge is displayed.
-                    mLocationBar.updateSecurityIcon();
+                    mLocationBar.updateStatusIcon();
                     PreviewsUma.recordLitePageAtCommit(
                             PreviewsAndroidBridge.getInstance().getPreviewsType(
                                     tab.getWebContents()),
@@ -1402,8 +1401,9 @@
 
         if (mControlsVisibilityDelegate == null) return;
         if (hasFocus) {
-            mFullscreenFocusToken = mControlsVisibilityDelegate
-                    .showControlsPersistentAndClearOldToken(mFullscreenFocusToken);
+            mFullscreenFocusToken =
+                    mControlsVisibilityDelegate.showControlsPersistentAndClearOldToken(
+                            mFullscreenFocusToken);
         } else {
             mControlsVisibilityDelegate.releasePersistentShowingToken(mFullscreenFocusToken);
         }
@@ -1544,8 +1544,7 @@
      * @param activityCreationTimeMs The time of creation for the activity this toolbar belongs to.
      * @param activityName Simple class name for the activity this toolbar belongs to.
      */
-    public void onDeferredStartup(final long activityCreationTimeMs,
-            final String activityName) {
+    public void onDeferredStartup(final long activityCreationTimeMs, final String activityName) {
         // Record startup performance statistics
         long elapsedTime = SystemClock.elapsedRealtime() - activityCreationTimeMs;
         if (elapsedTime < RECORD_UMA_PERFORMANCE_METRICS_DELAY_MS) {
@@ -1598,8 +1597,8 @@
     private void updateBookmarkButtonStatus() {
         assert mToolbarInflationComplete;
         Tab currentTab = mLocationBarModel.getTab();
-        boolean isBookmarked = currentTab != null
-                && currentTab.getBookmarkId() != Tab.INVALID_BOOKMARK_ID;
+        boolean isBookmarked =
+                currentTab != null && currentTab.getBookmarkId() != Tab.INVALID_BOOKMARK_ID;
         boolean editingAllowed = currentTab == null || mBookmarkBridge == null
                 || mBookmarkBridge.isEditBookmarksEnabled();
         mToolbar.updateBookmarkButton(isBookmarked, editingAllowed);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
index 5582c02b..f4795047 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
@@ -334,7 +334,7 @@
             mTitleBar.setLayoutParams(lp);
             mTitleBar.setTextSize(TypedValue.COMPLEX_UNIT_PX,
                     getResources().getDimension(R.dimen.custom_tabs_title_text_size));
-            updateSecurityIcon();
+            updateStatusIcon();
         } else {
             assert false : "Unreached state";
         }
@@ -387,7 +387,7 @@
                 setUrlBarHidden(false);
             }
         }
-        updateSecurityIcon();
+        updateStatusIcon();
     }
 
     @VisibleForTesting
@@ -460,7 +460,7 @@
     @Override
     public void updateLoadingState(boolean updateUrl) {
         if (updateUrl) setUrlToPageUrl();
-        updateSecurityIcon();
+        updateStatusIcon();
     }
 
     @Override
@@ -476,7 +476,7 @@
     @Override
     public void updateVisualsForState() {
         Resources resources = getResources();
-        updateSecurityIcon();
+        updateStatusIcon();
         updateButtonsTint();
         if (mUrlCoordinator.setUseDarkTextColors(mUseDarkColors)) {
             setUrlToPageUrl();
@@ -485,7 +485,7 @@
         int titleTextColor = mUseDarkColors
                 ? ApiCompatibilityUtils.getColor(resources, R.color.url_emphasis_default_text)
                 : ApiCompatibilityUtils.getColor(
-                          resources, R.color.url_emphasis_light_default_text);
+                        resources, R.color.url_emphasis_light_default_text);
         mTitleBar.setTextColor(titleTextColor);
 
         if (getProgressBar() != null) {
@@ -537,7 +537,7 @@
     public void initializeControls(WindowDelegate windowDelegate, WindowAndroid windowAndroid) {}
 
     @Override
-    public void updateSecurityIcon() {
+    public void updateStatusIcon() {
         if (mState == STATE_TITLE_ONLY) return;
 
         int securityIconResource = getToolbarDataProvider().getSecurityIconResource(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index 69c89632..b57f70de 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -153,6 +153,7 @@
     private boolean mForceTextureCapture;
     private boolean mUseLightDrawablesForTextureCapture;
     private boolean mLightDrawablesUsedForLastTextureCapture;
+    private int mTabCountForLastTextureCapture;
 
     @ViewDebug.ExportedProperty(category = "chrome")
     private boolean mAnimateNormalToolbar;
@@ -654,7 +655,7 @@
     private int getViewBoundsLeftOfLocationBar(@VisualState int visualState) {
         // Uses getMeasuredWidth()s instead of getLeft() because this is called in onMeasure
         // and the layout values have not yet been set.
-        if (visualState == VisualState.NEW_TAB_NORMAL) {
+        if (visualState == VisualState.NEW_TAB_NORMAL && mTabSwitcherState == STATIC_TAB) {
             return mToolbarSidePadding;
         } else if (ApiCompatibilityUtils.isLayoutRtl(this)) {
             return getBoundsAfterAccountingForRightButtons();
@@ -682,7 +683,7 @@
     private int getViewBoundsRightOfLocationBar(@VisualState int visualState) {
         // Uses getMeasuredWidth()s instead of getRight() because this is called in onMeasure
         // and the layout values have not yet been set.
-        if (visualState == VisualState.NEW_TAB_NORMAL) {
+        if (visualState == VisualState.NEW_TAB_NORMAL && mTabSwitcherState == STATIC_TAB) {
             return getMeasuredWidth() - mToolbarSidePadding;
         } else if (ApiCompatibilityUtils.isLayoutRtl(this)) {
             return getMeasuredWidth() - getBoundsAfterAccountingForLeftButton();
@@ -1297,6 +1298,10 @@
 
         mLightDrawablesUsedForLastTextureCapture = mUseLightDrawablesForTextureCapture;
 
+        if (mTabSwitcherAnimationTabStackDrawable != null && mToggleTabStackButton != null) {
+            mTabCountForLastTextureCapture = mTabSwitcherAnimationTabStackDrawable.getTabCount();
+        }
+
         canvas.restore();
     }
 
@@ -1420,6 +1425,7 @@
                 int rightDelta = getViewBoundsRightOfLocationBar(mVisualState)
                         - mUnfocusedLocationBarLayoutLeft - mUnfocusedLocationBarLayoutWidth;
                 float inversePercent = 1f - mUrlExpansionPercent;
+
                 locationBarClipLeft += leftDelta * inversePercent;
                 locationBarClipRight -= rightDelta * inversePercent;
 
@@ -1524,9 +1530,17 @@
     public boolean setForceTextureCapture(boolean forceTextureCapture) {
         if (forceTextureCapture) {
             setUseLightDrawablesForTextureCapture();
-            // Only force a texture capture if the tint for the toolbar drawables is changing.
+            // Only force a texture capture if the tint for the toolbar drawables is changing or
+            // if the tab count has changed since the last texture capture.
             mForceTextureCapture =
                     mLightDrawablesUsedForLastTextureCapture != mUseLightDrawablesForTextureCapture;
+
+            if (mTabSwitcherAnimationTabStackDrawable != null && mToggleTabStackButton != null) {
+                mForceTextureCapture = mForceTextureCapture
+                        || mTabCountForLastTextureCapture
+                                != mTabSwitcherAnimationTabStackDrawable.getTabCount();
+            }
+
             return mForceTextureCapture;
         }
 
@@ -1620,7 +1634,7 @@
         exitAnimation.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
-                updateViewsForTabSwitcherMode();
+                onExitTabSwitcherAnimationEnd();
             }
         });
 
@@ -1646,19 +1660,28 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 mDelayedTabSwitcherModeAnimation = null;
-                updateViewsForTabSwitcherMode();
+                onExitTabSwitcherAnimationEnd();
             }
         });
 
         return exitAnimation;
     }
 
+    private void onExitTabSwitcherAnimationEnd() {
+        updateViewsForTabSwitcherMode();
+
+        // Request a texture update to ensure a texture is captured before the user
+        // re-enters the tab switcher.
+        mLayoutUpdateHost.requestUpdate();
+    }
+
     @Override
     public void setTextureCaptureMode(boolean textureMode) {
         assert mTextureCaptureMode != textureMode;
         mTextureCaptureMode = textureMode;
         if (mTextureCaptureMode) {
-            if (!hideShadowForIncognitoNtp() && !hideShadowForInterstitial()) {
+            if (!hideShadowForIncognitoNtp() && !hideShadowForInterstitial()
+                    && !hideShadowForRegularNtpTextureCapture()) {
                 mToolbarShadow.setVisibility(VISIBLE);
             }
             mPreTextureCaptureAlpha = getAlpha();
@@ -1673,6 +1696,11 @@
         }
     }
 
+    private boolean hideShadowForRegularNtpTextureCapture() {
+        return !isIncognito() && NewTabPage.isNTPUrl(getToolbarDataProvider().getCurrentUrl())
+                && mNtpSearchBoxScrollPercent < 1.f;
+    }
+
     // TODO(dtrainor): This is always true when in the tab switcher (crbug.com/710750).
     private boolean isTabSwitcherAnimationRunning() {
         return mTabSwitcherState == ENTERING_TAB_SWITCHER
@@ -2267,7 +2295,6 @@
         // These are used to skip setting state unnecessarily while in the tab switcher.
         boolean inOrEnteringStaticTab =
                 mTabSwitcherState == STATIC_TAB || mTabSwitcherState == EXITING_TAB_SWITCHER;
-        boolean inOrEnteringTabSwitcher = !inOrEnteringStaticTab;
 
         @VisualState
         int newVisualState = computeVisualState();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/OverviewListLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/OverviewListLayout.java
index 2e9a4ab..50bfd31 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/OverviewListLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/OverviewListLayout.java
@@ -160,6 +160,13 @@
     }
 
     @Override
+    public boolean canHostBeFocusable() {
+        // TODO(https://crbug.com/918171): Consider fine-tuning accessibility support for the
+        // overview list layout.
+        return false;
+    }
+
+    @Override
     public void setTabModelSelector(
             TabModelSelector tabModelSelector, TabContentManager tabContentManager) {
         super.setTabModelSelector(tabModelSelector, tabContentManager);
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 884a3e3..0474fe5d 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -814,6 +814,9 @@
       <message name="IDS_ANDROID_HISTORY_BLOCKED_SITE" desc="The text displayed in the history page indicating that a visit to a web site was blocked due to an administrator policy.">
         Blocked site
       </message>
+      <message name="IDS_MULTIPLE_HISTORY_ITEMS_DELETED" desc="Accessibility announcement when multiple browsing history items have been deleted.">
+        <ph name="NUMBER_OF_ITEMS">%1$s<ex>3</ex></ph> items deleted
+      </message>
 
       <message name="IDS_USAGE_AND_CRASH_REPORTS_TITLE_LEGACY" desc="Title for 'Usage and crash reports' preference">
         Usage and crash reports
@@ -3408,8 +3411,8 @@
       <message name="IDS_UNDO_BAR_CLOSE_ALL_MESSAGE" desc="Message shown when you can undo a close all tabs action.">
         <ph name="TAB_COUNT">%1$s<ex>3</ex></ph> tabs closed
       </message>
-      <message name="IDS_UNDO_BAR_DELETE_MESSAGE" desc="Message shown when you can undo a delete action.">
-        Deleted <ph name="BOOKMARK_TITLE">%1$s<ex>YouTube</ex></ph>
+      <message name="IDS_DELETE_MESSAGE" desc="Message shown or announced when an item has been deleted.">
+        Deleted <ph name="ITEM_TITLE">%1$s<ex>YouTube</ex></ph>
       </message>
       <message name="IDS_UNDO_BAR_MULTIPLE_DELETE_MESSAGE" desc="Message shown when you can undo several bookmark delete actions.">
         <ph name="NUMBER_OF_BOOKMARKS">%1$s<ex>3</ex></ph> bookmarks deleted
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 99909c3..90a1aad5 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -2385,6 +2385,7 @@
   "junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityDisclosureControllerTest.java",
   "junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityOpenTimeRecorderTest.java",
   "junit/src/org/chromium/chrome/browser/cached_image_fetcher/CachedImageFetcherImplTest.java",
+  "junit/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsSourceImplTest.java",
   "junit/src/org/chromium/chrome/browser/cached_image_fetcher/InMemoryCachedImageFetcherTest.java",
   "junit/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManagerImplTest.java",
   "junit/src/org/chromium/chrome/browser/compositor/EventOffsetHandlerTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/MockLayoutHost.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/MockLayoutHost.java
index e5280d8..e7afc5b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/MockLayoutHost.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/MockLayoutHost.java
@@ -32,10 +32,10 @@
         }
 
         @Override
-        public void remove(int tabId) { }
+        public void remove(int tabId) {}
 
         @Override
-        public void clearExcept(int tabId) { }
+        public void clearExcept(int tabId) {}
     }
 
     private final MockTitleCache mMockTitleCache = new MockTitleCache();
@@ -49,16 +49,16 @@
     }
 
     @Override
-    public void requestRender() { }
+    public void requestRender() {}
 
     @Override
-    public void onCompositorLayout() { }
+    public void onCompositorLayout() {}
 
     @Override
     public void didSwapFrame(int pendingFrameCount) {}
 
     @Override
-    public void onSurfaceCreated() { }
+    public void onSurfaceCreated() {}
 
     @Override
     public void onSurfaceResized(int width, int height) {}
@@ -118,13 +118,13 @@
     }
 
     @Override
-    public void pushDebugRect(Rect rect, int color) { }
+    public void pushDebugRect(Rect rect, int color) {}
 
     @Override
-    public void loadPersitentTextureDataIfNeeded() { }
+    public void loadPersitentTextureDataIfNeeded() {}
 
     @Override
-    public void setContentOverlayVisibility(boolean visible) { }
+    public void setContentOverlayVisibility(boolean visible, boolean canBeFocusable) {}
 
     @Override
     public TitleCache getTitleCache() {
@@ -142,10 +142,10 @@
     }
 
     @Override
-    public void invalidateAccessibilityProvider() { }
+    public void invalidateAccessibilityProvider() {}
 
     @Override
-    public void onContentChanged() { }
+    public void onContentChanged() {}
 
     @Override
     public int getBrowserControlsBackgroundColor() {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsSourceImplTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsSourceImplTest.java
new file mode 100644
index 0000000..6643969
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsSourceImplTest.java
@@ -0,0 +1,119 @@
+// 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.contextual_suggestions;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.graphics.Bitmap;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowLooper;
+
+import org.chromium.base.Callback;
+import org.chromium.base.task.test.BackgroundShadowAsyncTask;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.cached_image_fetcher.CachedImageFetcher;
+import org.chromium.chrome.browser.contextmenu.ChromeContextMenuPopulatorTest.ShadowUrlUtilities;
+import org.chromium.chrome.browser.ntp.snippets.SnippetArticle;
+
+/**
+ * Unit tests for CachedImageFetcherImpl.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE,
+        shadows = {ShadowUrlUtilities.class, BackgroundShadowAsyncTask.class})
+public class ContextualSuggestionsSourceImplTest {
+    private static final String IMAGE_URL = "http://foo.com/bar.png";
+    private static final int FAVICON_DIMENSION = 100;
+
+    ContextualSuggestionsSourceImpl mContextualSuggestionsSourceImpl;
+
+    @Mock
+    ContextualSuggestionsBridge mBridge;
+
+    @Mock
+    CachedImageFetcher mCachedImageFetcher;
+
+    @Mock
+    Bitmap mBitmap;
+
+    @Captor
+    ArgumentCaptor<Callback<Bitmap>> mCallbackCaptor;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContextualSuggestionsSourceImpl =
+                new ContextualSuggestionsSourceImpl(mBridge, mCachedImageFetcher);
+        // Answer with or without width/height.
+        doAnswer((InvocationOnMock invocation) -> {
+            mCallbackCaptor.getValue().onResult(mBitmap);
+            return null;
+        })
+                .when(mCachedImageFetcher)
+                .fetchImage(any(), mCallbackCaptor.capture());
+    }
+
+    @Test
+    @SmallTest
+    public void testFetchContextualSuggestionImage() throws Exception {
+        doReturn(IMAGE_URL).when(mBridge).getImageUrl(any());
+
+        SnippetArticle article = mock(SnippetArticle.class);
+        mContextualSuggestionsSourceImpl.fetchContextualSuggestionImage(
+                article, (Bitmap bitmap) -> { assertEquals(bitmap, mBitmap); });
+        BackgroundShadowAsyncTask.runBackgroundTasks();
+        ShadowLooper.runUiThreadTasks();
+    }
+
+    @Test
+    @SmallTest
+    public void testFetchContextualSuggestionImageWhenNullUrlIsReturned() throws Exception {
+        doReturn(null).when(mBridge).getImageUrl(any());
+
+        SnippetArticle article = mock(SnippetArticle.class);
+        mContextualSuggestionsSourceImpl.fetchContextualSuggestionImage(
+                article, (Bitmap bitmap) -> { assertEquals(bitmap, null); });
+        BackgroundShadowAsyncTask.runBackgroundTasks();
+        ShadowLooper.runUiThreadTasks();
+    }
+
+    @Test
+    @SmallTest
+    public void testFetchContextualSuggestionFavicon() throws Exception {
+        doReturn(IMAGE_URL).when(mBridge).getFaviconUrl(any());
+
+        SnippetArticle article = mock(SnippetArticle.class);
+        mContextualSuggestionsSourceImpl.fetchSuggestionFavicon(article, FAVICON_DIMENSION,
+                FAVICON_DIMENSION, (Bitmap bitmap) -> { assertEquals(bitmap, mBitmap); });
+        BackgroundShadowAsyncTask.runBackgroundTasks();
+        ShadowLooper.runUiThreadTasks();
+    }
+
+    @Test
+    @SmallTest
+    public void testFetchContextualSuggestionFaviconNullUrlIsReturned() throws Exception {
+        doReturn(null).when(mBridge).getFaviconUrl(any());
+
+        SnippetArticle article = mock(SnippetArticle.class);
+        mContextualSuggestionsSourceImpl.fetchSuggestionFavicon(article, FAVICON_DIMENSION,
+                FAVICON_DIMENSION, (Bitmap bitmap) -> { assertEquals(bitmap, null); });
+        BackgroundShadowAsyncTask.runBackgroundTasks();
+        ShadowLooper.runUiThreadTasks();
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/trichrome.gni b/chrome/android/trichrome.gni
index fe21a31b..f94d61e6 100644
--- a/chrome/android/trichrome.gni
+++ b/chrome/android/trichrome.gni
@@ -8,9 +8,20 @@
 import("//build/config/android/rules.gni")
 import("//chrome/android/chrome_public_apk_tmpl.gni")
 
-trichrome_library_package = "org.chromium.trichromelibrary"
-trichrome_certdigest =
-    "1975b2f17177bc89a5dff31f9e64a6cae281a53dc1d1d59b1d147fe1c82afa00"
+if (!defined(default_trichrome_certdigest)) {
+  default_trichrome_certdigest =
+      "32a2fc74d731105859e5a85df16d95f102d85b22099b8064c5d8915c61dad1e0"
+}
+
+declare_args() {
+  # The package name for the Trichrome static shared library on Android.
+  trichrome_library_package = "org.chromium.trichromelibrary"
+
+  # The SHA256 certificate digest for the Trichrome static shared library on
+  # Android. You can use "apksigner verify --print-certs" on the signed APK to
+  # calculate the correct digest.
+  trichrome_certdigest = default_trichrome_certdigest
+}
 
 trichrome_jinja_variables = [
   "min_sdk_version=28",
diff --git a/chrome/app/theme/default_100_percent/legacy/favicon_laptop.png b/chrome/app/theme/default_100_percent/legacy/favicon_laptop.png
deleted file mode 100644
index 698ea8b..0000000
--- a/chrome/app/theme/default_100_percent/legacy/favicon_laptop.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_100_percent/legacy/favicon_phone.png b/chrome/app/theme/default_100_percent/legacy/favicon_phone.png
deleted file mode 100644
index ab57626..0000000
--- a/chrome/app/theme/default_100_percent/legacy/favicon_phone.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_100_percent/legacy/favicon_tablet.png b/chrome/app/theme/default_100_percent/legacy/favicon_tablet.png
deleted file mode 100644
index c1458f1..0000000
--- a/chrome/app/theme/default_100_percent/legacy/favicon_tablet.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/legacy/favicon_laptop.png b/chrome/app/theme/default_200_percent/legacy/favicon_laptop.png
deleted file mode 100644
index 5ac0165c..0000000
--- a/chrome/app/theme/default_200_percent/legacy/favicon_laptop.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/legacy/favicon_phone.png b/chrome/app/theme/default_200_percent/legacy/favicon_phone.png
deleted file mode 100644
index 7a83337..0000000
--- a/chrome/app/theme/default_200_percent/legacy/favicon_phone.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/legacy/favicon_tablet.png b/chrome/app/theme/default_200_percent/legacy/favicon_tablet.png
deleted file mode 100644
index ac453a6..0000000
--- a/chrome/app/theme/default_200_percent/legacy/favicon_tablet.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index 2335092..5b66242e 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -164,11 +164,6 @@
       <structure type="chrome_scaled_image" name="IDR_INFOBAR_3D_BLOCKED" file="common/infobar_3d_blocked.png" />
       <structure type="chrome_scaled_image" name="IDR_INPUT_ALERT" file="common/input_alert.png" />
       <structure type="chrome_scaled_image" name="IDR_INPUT_ALERT_MENU" file="common/input_alert_menu.png" />
-      <if expr="is_macosx">
-        <structure type="chrome_scaled_image" name="IDR_LAPTOP_FAVICON" file="legacy/favicon_laptop.png" />
-        <structure type="chrome_scaled_image" name="IDR_PHONE_FAVICON" file="legacy/favicon_phone.png" />
-        <structure type="chrome_scaled_image" name="IDR_TABLET_FAVICON" file="legacy/favicon_tablet.png" />
-      </if>
       <if expr="enable_service_discovery">
         <structure type="chrome_scaled_image" name="IDR_LOCAL_DISCOVERY_CLOUDPRINT_ICON" file="common/cloudprint.png" />
       </if>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 47c0c5b4..5bd63ba 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -5096,6 +5096,7 @@
   if (is_android) {
     deps += [
       "//chrome/browser/ui/webui/eoc_internals:mojo_bindings_js",
+      "//chrome/browser/ui/webui/feed_internals:mojo_bindings_js",
       "//chrome/browser/ui/webui/snippets_internals:mojo_bindings_js",
     ]
   } else {
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index decb45e1..eed68bc 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2611,6 +2611,10 @@
      flag_descriptions::kExperimentalAccessibilityFeaturesName,
      flag_descriptions::kExperimentalAccessibilityFeaturesDescription, kOsCrOS,
      SINGLE_VALUE_TYPE(::switches::kEnableExperimentalAccessibilityFeatures)},
+    {"enable-experimental-accessibility-labels",
+     flag_descriptions::kExperimentalAccessibilityLabelsName,
+     flag_descriptions::kExperimentalAccessibilityLabelsDescription, kOsAll,
+     SINGLE_VALUE_TYPE(::switches::kEnableExperimentalAccessibilityLabels)},
 #if defined(OS_CHROMEOS)
     {"opt-in-ime-menu", flag_descriptions::kEnableImeMenuName,
      flag_descriptions::kEnableImeMenuDescription, kOsCrOS,
diff --git a/chrome/browser/android/contextual_suggestions/contextual_suggestions_bridge.cc b/chrome/browser/android/contextual_suggestions/contextual_suggestions_bridge.cc
index 37342f7..3738ef1 100644
--- a/chrome/browser/android/contextual_suggestions/contextual_suggestions_bridge.cc
+++ b/chrome/browser/android/contextual_suggestions/contextual_suggestions_bridge.cc
@@ -114,30 +114,27 @@
                           ScopedJavaGlobalRef<jobject>(j_callback)));
 }
 
-void ContextualSuggestionsBridge::FetchSuggestionImage(
+base::android::ScopedJavaLocalRef<jstring>
+ContextualSuggestionsBridge::GetImageUrl(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
-    const JavaParamRef<jstring>& j_suggestion_id,
-    const JavaParamRef<jobject>& j_callback) {
+    const JavaParamRef<jstring>& j_suggestion_id) {
   std::string suggestion_id(ConvertJavaStringToUTF8(env, j_suggestion_id));
-  service_proxy_->FetchContextualSuggestionImage(
-      suggestion_id,
-      base::BindOnce(&ContextualSuggestionsBridge::OnImageFetched,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     ScopedJavaGlobalRef<jobject>(j_callback)));
+  std::string image_url =
+      service_proxy_->GetContextualSuggestionImageUrl(suggestion_id);
+  return image_url.empty() ? nullptr : ConvertUTF8ToJavaString(env, image_url);
 }
 
-void ContextualSuggestionsBridge::FetchSuggestionFavicon(
+base::android::ScopedJavaLocalRef<jstring>
+ContextualSuggestionsBridge::GetFaviconUrl(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
-    const JavaParamRef<jstring>& j_suggestion_id,
-    const JavaParamRef<jobject>& j_callback) {
+    const JavaParamRef<jstring>& j_suggestion_id) {
   std::string suggestion_id(ConvertJavaStringToUTF8(env, j_suggestion_id));
-  service_proxy_->FetchContextualSuggestionFavicon(
-      suggestion_id,
-      base::BindOnce(&ContextualSuggestionsBridge::OnImageFetched,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     ScopedJavaGlobalRef<jobject>(j_callback)));
+  std::string favicon_url =
+      service_proxy_->GetContextualSuggestionFaviconUrl(suggestion_id);
+  return favicon_url.empty() ? nullptr
+                             : ConvertUTF8ToJavaString(env, favicon_url);
 }
 
 void ContextualSuggestionsBridge::ClearState(JNIEnv* env,
diff --git a/chrome/browser/android/contextual_suggestions/contextual_suggestions_bridge.h b/chrome/browser/android/contextual_suggestions/contextual_suggestions_bridge.h
index 27249fa..a9ce466 100644
--- a/chrome/browser/android/contextual_suggestions/contextual_suggestions_bridge.h
+++ b/chrome/browser/android/contextual_suggestions/contextual_suggestions_bridge.h
@@ -44,13 +44,17 @@
       const base::android::JavaParamRef<jstring>& j_suggestion_id,
       const base::android::JavaParamRef<jobject>& j_callback);
 
-  // Fetches a favicon corresponding to suggestion with |j_suggestion_id| and
-  // passes results to Java side using |j_callback|.
-  void FetchSuggestionFavicon(
+  // Gets the image URL for the given |suggestion_id|.
+  base::android::ScopedJavaLocalRef<jstring> GetImageUrl(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jstring>& j_suggestion_id,
-      const base::android::JavaParamRef<jobject>& j_callback);
+      const base::android::JavaParamRef<jstring>& j_suggestion_id);
+
+  // Gets the favicon URL for the given |suggestion_id|.
+  base::android::ScopedJavaLocalRef<jstring> GetFaviconUrl(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj,
+      const base::android::JavaParamRef<jstring>& j_suggestion_id);
 
   // Requests the backend to clear state related to this bridge.
   void ClearState(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
diff --git a/chrome/browser/android/download/download_controller.cc b/chrome/browser/android/download/download_controller.cc
index 166981e1..f569dcd 100644
--- a/chrome/browser/android/download/download_controller.cc
+++ b/chrome/browser/android/download/download_controller.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/android/chrome_feature_list.h"
 #include "chrome/browser/android/download/dangerous_download_infobar_delegate.h"
 #include "chrome/browser/android/download/download_manager_service.h"
+#include "chrome/browser/android/download/download_utils.h"
 #include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/download/download_stats.h"
 #include "chrome/browser/infobars/infobar_service.h"
@@ -28,6 +29,7 @@
 #include "chrome/browser/ui/android/view_android_helper.h"
 #include "chrome/browser/vr/vr_tab_helper.h"
 #include "chrome/grit/chromium_strings.h"
+#include "components/download/public/common/auto_resumption_handler.h"
 #include "components/download/public/common/download_url_parameters.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -59,11 +61,6 @@
 // Guards download_controller_
 base::LazyInstance<base::Lock>::DestructorAtExit g_download_controller_lock_;
 
-// If received bytes is more than the size limit and resumption will restart
-// from the beginning, throttle it.
-int kDefaultAutoResumptionSizeLimit = 10 * 1024 * 1024;  // 10 MB
-const char kAutoResumptionSizeLimitParamName[] = "AutoResumptionSizeLimit";
-
 void CreateContextMenuDownload(
     const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
     const content::ContextMenuParams& params,
@@ -116,16 +113,6 @@
   dlm->DownloadUrl(std::move(dl_params));
 }
 
-int GetAutoResumptionSizeLimit() {
-  std::string value = base::GetFieldTrialParamValueByFeature(
-      chrome::android::kDownloadAutoResumptionThrottling,
-      kAutoResumptionSizeLimitParamName);
-  int size_limit;
-  return base::StringToInt(value, &size_limit)
-             ? size_limit
-             : kDefaultAutoResumptionSizeLimit;
-}
-
 // Helper class for retrieving a DownloadManager.
 class DownloadManagerGetter : public DownloadManager::Observer {
  public:
@@ -395,6 +382,8 @@
   download_item->RemoveObserver(this);
   download_item->AddObserver(this);
 
+  download::AutoResumptionHandler::Get()->OnDownloadStarted(download_item);
+
   OnDownloadUpdated(download_item);
 }
 
@@ -480,7 +469,7 @@
   if (!download_item->GetURL().SchemeIsHTTPOrHTTPS())
     return false;
 
-  static int size_limit = GetAutoResumptionSizeLimit();
+  static int size_limit = DownloadUtils::GetAutoResumptionSizeLimit();
   bool exceeds_size_limit = download_item->GetReceivedBytes() > size_limit;
   std::string etag = download_item->GetETag();
   std::string last_modified = download_item->GetLastModifiedTime();
diff --git a/chrome/browser/android/download/download_manager_service.cc b/chrome/browser/android/download/download_manager_service.cc
index aadb99c..1ff1531 100644
--- a/chrome/browser/android/download/download_manager_service.cc
+++ b/chrome/browser/android/download/download_manager_service.cc
@@ -16,6 +16,8 @@
 #include "base/time/time.h"
 #include "chrome/browser/android/chrome_feature_list.h"
 #include "chrome/browser/android/download/download_controller.h"
+#include "chrome/browser/android/download/download_utils.h"
+#include "chrome/browser/android/download/service/download_task_scheduler.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/download/download_core_service.h"
 #include "chrome/browser/download/download_core_service_factory.h"
@@ -23,6 +25,8 @@
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/chrome_constants.h"
+#include "components/download/network/android/network_status_listener_android.h"
+#include "components/download/public/common/auto_resumption_handler.h"
 #include "components/download/public/common/download_item.h"
 #include "components/download/public/common/download_item_impl.h"
 #include "components/download/public/common/download_url_loader_factory_getter_impl.h"
@@ -51,6 +55,20 @@
 int kDefaultAutoResumptionLimit = 5;
 const char kAutoResumptionLimitParamName[] = "AutoResumptionLimit";
 
+void CreateAutoResumptionHandler() {
+  auto network_listener =
+      std::make_unique<download::NetworkStatusListenerAndroid>();
+  auto task_scheduler =
+      std::make_unique<download::android::DownloadTaskScheduler>();
+  auto task_manager =
+      std::make_unique<download::TaskManager>(std::move(task_scheduler));
+  auto config = std::make_unique<download::AutoResumptionHandler::Config>();
+  config->auto_resumption_size_limit =
+      DownloadUtils::GetAutoResumptionSizeLimit();
+  download::AutoResumptionHandler::Create(
+      std::move(network_listener), std::move(task_manager), std::move(config));
+}
+
 bool ShouldShowDownloadItem(download::DownloadItem* item) {
   return !item->IsTemporary() && !item->IsTransient();
 }
@@ -649,6 +667,22 @@
   if (!in_progress_manager_ && !is_history_query_complete_)
     return;
   is_pending_downloads_loaded_ = true;
+
+  // Kick-off the auto-resumption handler.
+  content::DownloadManager::DownloadVector all_items;
+  if (in_progress_manager_) {
+    in_progress_manager_->GetAllDownloads(&all_items);
+  } else {
+    content::DownloadManager* manager = GetDownloadManager(false);
+    if (manager)
+      manager->GetAllDownloads(&all_items);
+  }
+
+  if (!download::AutoResumptionHandler::Get())
+    CreateAutoResumptionHandler();
+
+  download::AutoResumptionHandler::Get()->SetResumableDownloads(all_items);
+
   for (auto iter = pending_actions_.begin(); iter != pending_actions_.end();
        ++iter) {
     DownloadActionParams params = iter->second;
diff --git a/chrome/browser/android/download/download_utils.cc b/chrome/browser/android/download/download_utils.cc
index c413f75..27670e0 100644
--- a/chrome/browser/android/download/download_utils.cc
+++ b/chrome/browser/android/download/download_utils.cc
@@ -5,6 +5,9 @@
 #include "chrome/browser/android/download/download_utils.h"
 
 #include "base/android/jni_string.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/strings/string_number_conversions.h"
+#include "chrome/browser/android/chrome_feature_list.h"
 #include "chrome/browser/download/offline_item_utils.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/download/public/common/download_utils.h"
@@ -15,6 +18,14 @@
 using base::android::JavaParamRef;
 using base::android::ScopedJavaLocalRef;
 
+namespace {
+// If received bytes is more than the size limit and resumption will restart
+// from the beginning, throttle it.
+int kDefaultAutoResumptionSizeLimit = 10 * 1024 * 1024;  // 10 MB
+const char kAutoResumptionSizeLimitParamName[] = "AutoResumptionSizeLimit";
+
+}  // namespace
+
 static ScopedJavaLocalRef<jstring> JNI_DownloadUtils_GetFailStateMessage(
     JNIEnv* env,
     jint fail_state) {
@@ -46,3 +57,14 @@
   return base::FilePath(
       base::android::ConvertJavaStringToUTF8(env, uri_jstring));
 }
+
+// static
+int DownloadUtils::GetAutoResumptionSizeLimit() {
+  std::string value = base::GetFieldTrialParamValueByFeature(
+      chrome::android::kDownloadAutoResumptionThrottling,
+      kAutoResumptionSizeLimitParamName);
+  int size_limit;
+  return base::StringToInt(value, &size_limit)
+             ? size_limit
+             : kDefaultAutoResumptionSizeLimit;
+}
diff --git a/chrome/browser/android/download/download_utils.h b/chrome/browser/android/download/download_utils.h
index b507ffa..73c0b27a 100644
--- a/chrome/browser/android/download/download_utils.h
+++ b/chrome/browser/android/download/download_utils.h
@@ -11,6 +11,7 @@
 class DownloadUtils {
  public:
   static base::FilePath GetUriStringForPath(const base::FilePath& file_path);
+  static int GetAutoResumptionSizeLimit();
 };
 
 #endif  // CHROME_BROWSER_ANDROID_DOWNLOAD_DOWNLOAD_UTILS_H_
diff --git a/chrome/browser/android/download/service/download_background_task.cc b/chrome/browser/android/download/service/download_background_task.cc
index 86c394a..b7cc72db 100644
--- a/chrome/browser/android/download/service/download_background_task.cc
+++ b/chrome/browser/android/download/service/download_background_task.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
 #include "components/download/public/background_service/download_service.h"
+#include "components/download/public/common/auto_resumption_handler.h"
 #include "content/public/browser/browser_context.h"
 #include "jni/DownloadBackgroundTask_jni.h"
 
@@ -16,6 +17,13 @@
 namespace download {
 namespace android {
 
+DownloadService* GetDownloadService(
+    const base::android::JavaParamRef<jobject>& jprofile) {
+  Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
+  DCHECK(profile);
+  return DownloadServiceFactory::GetForBrowserContext(profile);
+}
+
 // static
 void JNI_DownloadBackgroundTask_StartBackgroundTask(
     JNIEnv* env,
@@ -23,17 +31,23 @@
     const base::android::JavaParamRef<jobject>& jprofile,
     jint task_type,
     const base::android::JavaParamRef<jobject>& jcallback) {
-  Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
-  DCHECK(profile);
-
   TaskFinishedCallback finish_callback =
       base::BindOnce(&base::android::RunBooleanCallbackAndroid,
                      base::android::ScopedJavaGlobalRef<jobject>(jcallback));
 
-  DownloadService* download_service =
-      DownloadServiceFactory::GetForBrowserContext(profile);
-  download_service->OnStartScheduledTask(
-      static_cast<DownloadTaskType>(task_type), std::move(finish_callback));
+  switch (static_cast<DownloadTaskType>(task_type)) {
+    case download::DownloadTaskType::DOWNLOAD_AUTO_RESUMPTION_TASK: {
+      download::AutoResumptionHandler::Get()->OnStartScheduledTask(
+          std::move(finish_callback));
+      break;
+    }
+    case download::DownloadTaskType::DOWNLOAD_TASK:
+      FALLTHROUGH;
+    case download::DownloadTaskType::CLEANUP_TASK:
+      GetDownloadService(jprofile)->OnStartScheduledTask(
+          static_cast<DownloadTaskType>(task_type), std::move(finish_callback));
+      break;
+  }
 }
 
 // static
@@ -42,13 +56,18 @@
     const base::android::JavaParamRef<jobject>& jcaller,
     const base::android::JavaParamRef<jobject>& jprofile,
     jint task_type) {
-  Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
-  DCHECK(profile);
-
-  DownloadService* download_service =
-      DownloadServiceFactory::GetForBrowserContext(profile);
-  return download_service->OnStopScheduledTask(
-      static_cast<DownloadTaskType>(task_type));
+  switch (static_cast<DownloadTaskType>(task_type)) {
+    case download::DownloadTaskType::DOWNLOAD_AUTO_RESUMPTION_TASK: {
+      download::AutoResumptionHandler::Get()->OnStopScheduledTask();
+      break;
+    }
+    case download::DownloadTaskType::DOWNLOAD_TASK:
+      FALLTHROUGH;
+    case download::DownloadTaskType::CLEANUP_TASK:
+      return GetDownloadService(jprofile)->OnStopScheduledTask(
+          static_cast<DownloadTaskType>(task_type));
+  }
+  return false;
 }
 
 }  // namespace android
diff --git a/chrome/browser/apps/platform_apps/app_window_interactive_uitest.cc b/chrome/browser/apps/platform_apps/app_window_interactive_uitest.cc
index c97d0f3..15fb04b 100644
--- a/chrome/browser/apps/platform_apps/app_window_interactive_uitest.cc
+++ b/chrome/browser/apps/platform_apps/app_window_interactive_uitest.cc
@@ -18,7 +18,6 @@
 
 #if defined(OS_MACOSX)
 #include "base/mac/mac_util.h"
-#include "ui/base/test/scoped_fake_nswindow_fullscreen.h"
 #endif
 
 #if defined(OS_WIN)
diff --git a/chrome/browser/ash_service_registry.cc b/chrome/browser/ash_service_registry.cc
index 7b0fd72a..feb2ebe 100644
--- a/chrome/browser/ash_service_registry.cc
+++ b/chrome/browser/ash_service_registry.cc
@@ -7,7 +7,7 @@
 #include "ash/ash_service.h"
 #include "ash/components/quick_launch/public/mojom/constants.mojom.h"
 #include "ash/components/shortcut_viewer/public/mojom/shortcut_viewer.mojom.h"
-#include "ash/components/tap_visualizer/public/mojom/constants.mojom.h"
+#include "ash/components/tap_visualizer/public/mojom/tap_visualizer.mojom.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/public/interfaces/constants.mojom.h"
 #include "ash/public/interfaces/window_properties.mojom.h"
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
index 1170ff2..373ca03 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
@@ -320,8 +320,9 @@
   DCHECK(image_service);
 
   // TODO(jdonnelly, rhalavati): Create a helper function with Callback to
-  // create annotation and pass it to image_service, merging this annotation and
-  // chrome/browser/ui/omnibox/chrome_omnibox_client.cc
+  // create annotation and pass it to image_service, merging the annotations
+  // in omnibox_page_handler.cc, chrome_omnibox_client.cc,
+  // and chrome_autocomplete_provider_client.cc.
   net::NetworkTrafficAnnotationTag traffic_annotation =
       net::DefineNetworkTrafficAnnotation("omnibox_prefetch_image", R"(
         semantics {
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index a9e45ef..6d22976a 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -200,6 +200,9 @@
         <include name="IDR_EOC_INTERNALS_JS" file="resources\eoc_internals\eoc_internals.js" compress="gzip" type="BINDATA" />
         <include name="IDR_EOC_INTERNALS_MOJO_JS" file="${root_gen_dir}\chrome\browser\ui\webui\eoc_internals\eoc_internals.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" />
         <include name="IDR_EXPLORE_SITES_INTERNALS_HTML" file="resources\explore_sites_internals\explore_sites_internals.html" allowexternalscript="true" compress="gzip" type="BINDATA" />
+        <include name="IDR_FEED_INTERNALS_HTML" file="resources\feed_internals\feed_internals.html" allowexternalscript="true" compress="gzip" type="BINDATA" />
+        <include name="IDR_FEED_INTERNALS_JS" file="resources\feed_internals\feed_internals.js" compress="gzip" type="BINDATA" />
+        <include name="IDR_FEED_INTERNALS_MOJO_JS" file="${root_gen_dir}\chrome\browser\ui\webui\feed_internals\feed_internals.mojom-lite.js" use_base_dir="false" type="BINDATA" compress="gzip" />
         <include name="IDR_OFFLINE_INTERNALS_HTML" file="resources\offline_pages\offline_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
         <include name="IDR_OFFLINE_INTERNALS_CSS" file="resources\offline_pages\offline_internals.css" type="BINDATA" compress="gzip" />
         <include name="IDR_OFFLINE_INTERNALS_JS" file="resources\offline_pages\offline_internals.js" type="BINDATA" compress="gzip" />
@@ -559,9 +562,6 @@
         <include name="IDR_MD_USER_MANAGER_TUTORIAL_HTML" file="resources\md_user_manager\user_manager_tutorial.html" type="BINDATA" />
         <include name="IDR_MD_USER_MANAGER_TUTORIAL_JS" file="resources\md_user_manager\user_manager_tutorial.js" type="BINDATA" />
       </if>
-      <if expr="is_macosx">
-        <include name="IDR_RECENTLY_CLOSED_WINDOW" file="resources\ntp4\images\closed_window.png" type="BINDATA" />
-      </if>
       <if expr="not is_android">
         <include name="IDR_IDENTITY_INTERNALS_HTML" file="resources\identity_internals.html" type="BINDATA" />
         <include name="IDR_IDENTITY_INTERNALS_CSS" file="resources\identity_internals.css" type="BINDATA" />
diff --git a/chrome/browser/chrome_content_browser_manifest_overlay.json b/chrome/browser/chrome_content_browser_manifest_overlay.json
index 808d55570..0b868ae8 100644
--- a/chrome/browser/chrome_content_browser_manifest_overlay.json
+++ b/chrome/browser/chrome_content_browser_manifest_overlay.json
@@ -66,6 +66,7 @@
         "removable_storage_writer": [ "removable_storage_writer" ],
         "secure_channel": [ "secure_channel" ],
         "shortcut_viewer_app": [ "shortcut_viewer" ],
+        "tap_visualizer_app": [ "tap_visualizer" ],
         "ui": [
           "ime_registrar",
           "input_device_controller",
@@ -108,6 +109,7 @@
           // TODO(beng): These should be moved to a separate capability.
           "app_management.mojom.PageHandlerFactory",
           "eoc_internals.mojom.PageHandler",
+          "feed_internals.mojom.PageHandler",
           "media.mojom.MediaEngagementScoreDetailsProvider",
           "md_downloads.mojom.PageHandlerFactory",
           "mojom.BluetoothInternalsHandler",
diff --git a/chrome/browser/chromeos/accessibility/select_to_speak_browsertest.cc b/chrome/browser/chromeos/accessibility/select_to_speak_browsertest.cc
index 0f3ad72..82ea907 100644
--- a/chrome/browser/chromeos/accessibility/select_to_speak_browsertest.cc
+++ b/chrome/browser/chromeos/accessibility/select_to_speak_browsertest.cc
@@ -9,7 +9,7 @@
 #include "ash/accessibility/accessibility_focus_ring_layer.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/status_area_widget_test_api.mojom.h"
+#include "ash/public/interfaces/status_area_widget_test_api.test-mojom.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
 #include "ash/system/status_area_widget.h"
diff --git a/chrome/browser/chromeos/child_accounts/screen_time_controller.cc b/chrome/browser/chromeos/child_accounts/screen_time_controller.cc
index ba64beb..2a0dded1 100644
--- a/chrome/browser/chromeos/child_accounts/screen_time_controller.cc
+++ b/chrome/browser/chromeos/child_accounts/screen_time_controller.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/chromeos/child_accounts/screen_time_controller.h"
 
+#include "ash/public/interfaces/login_screen.mojom.h"
+#include "base/feature_list.h"
 #include "base/optional.h"
 #include "base/time/clock.h"
 #include "base/time/default_clock.h"
@@ -13,6 +15,8 @@
 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login_screen_client.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/session_manager_client.h"
@@ -177,6 +181,8 @@
   ScreenLocker::default_screen_locker()->SetAuthEnabledForUser(
       account_id, !visible,
       visible ? next_unlock_time : base::Optional<base::Time>());
+  if (base::FeatureList::IsEnabled(features::kParentAccessCode))
+    LoginScreenClient::Get()->login_screen()->SetShowParentAccess(visible);
 }
 
 void ScreenTimeController::OnPolicyChanged() {
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
index 00f47991c..39feac5 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -10,7 +10,7 @@
 #include <utility>
 
 #include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/shell_test_api.mojom.h"
+#include "ash/public/interfaces/shell_test_api.test-mojom.h"
 #include "base/containers/circular_deque.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_value_converter.h"
diff --git a/chrome/browser/chromeos/first_run/chromeos_first_run_browsertest.cc b/chrome/browser/chromeos/first_run/chromeos_first_run_browsertest.cc
index 4c835c44..4d92980a 100644
--- a/chrome/browser/chromeos/first_run/chromeos_first_run_browsertest.cc
+++ b/chrome/browser/chromeos/first_run/chromeos_first_run_browsertest.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/system_tray_test_api.mojom.h"
+#include "ash/public/interfaces/system_tray_test_api.test-mojom.h"
 #include "chrome/browser/chromeos/first_run/first_run.h"
 #include "chrome/browser/chromeos/first_run/first_run_controller.h"
 #include "chrome/browser/chromeos/first_run/step_names.h"
diff --git a/chrome/browser/chromeos/login/lock/screen_locker_tester.cc b/chrome/browser/chromeos/login/lock/screen_locker_tester.cc
index 4e66315..727367a8 100644
--- a/chrome/browser/chromeos/login/lock/screen_locker_tester.cc
+++ b/chrome/browser/chromeos/login/lock/screen_locker_tester.cc
@@ -8,7 +8,7 @@
 
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/login_screen_test_api.mojom.h"
+#include "ash/public/interfaces/login_screen_test_api.test-mojom.h"
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
diff --git a/chrome/browser/chromeos/login/login_shelf_test_helper.h b/chrome/browser/chromeos/login/login_shelf_test_helper.h
index 1607a28..dba4530 100644
--- a/chrome/browser/chromeos/login/login_shelf_test_helper.h
+++ b/chrome/browser/chromeos/login/login_shelf_test_helper.h
@@ -4,7 +4,7 @@
 
 #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_SHELF_TEST_HELPER_H_
 #define CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_SHELF_TEST_HELPER_H_
-#include "ash/public/interfaces/login_screen_test_api.mojom.h"
+#include "ash/public/interfaces/login_screen_test_api.test-mojom.h"
 
 namespace chromeos {
 
diff --git a/chrome/browser/chromeos/policy/device_system_use_24hour_clock_browsertest.cc b/chrome/browser/chromeos/policy/device_system_use_24hour_clock_browsertest.cc
index 7a5f10ab..cd9b6f5 100644
--- a/chrome/browser/chromeos/policy/device_system_use_24hour_clock_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_system_use_24hour_clock_browsertest.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/system_tray_test_api.mojom.h"
+#include "ash/public/interfaces/system_tray_test_api.test-mojom.h"
 #include "base/command_line.h"
 #include "base/location.h"
 #include "base/macros.h"
diff --git a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager_unittest.cc b/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager_unittest.cc
index 40ff96e..02f655ac 100644
--- a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager_unittest.cc
+++ b/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager_unittest.cc
@@ -217,6 +217,8 @@
   const GURL kUrl3 = GURL("https://example3.com/");
 
  private:
+  // TODO(crbug.com/917580): Remove this; thread_bundle() should be used
+  // instead.
   const scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
   chromeos::FakeChromeUserManager fake_user_manager_;
 
diff --git a/chrome/browser/chromeos/power/ml/fake_boot_clock.cc b/chrome/browser/chromeos/power/ml/fake_boot_clock.cc
index 730688a..ac61ed6 100644
--- a/chrome/browser/chromeos/power/ml/fake_boot_clock.cc
+++ b/chrome/browser/chromeos/power/ml/fake_boot_clock.cc
@@ -12,15 +12,28 @@
     scoped_refptr<const base::TestMockTimeTaskRunner> task_runner,
     base::TimeDelta initial_time_since_boot)
     : task_runner_(task_runner),
+      env_(nullptr),
       initial_time_since_boot_(initial_time_since_boot) {
   DCHECK_GE(initial_time_since_boot, base::TimeDelta());
   initial_time_ticks_ = task_runner_->NowTicks();
 }
 
+FakeBootClock::FakeBootClock(base::test::ScopedTaskEnvironment* env,
+                             base::TimeDelta initial_time_since_boot)
+    : env_(env), initial_time_since_boot_(initial_time_since_boot) {
+  DCHECK_GE(initial_time_since_boot, base::TimeDelta());
+  initial_time_ticks_ = env_->NowTicks();
+}
+
 FakeBootClock::~FakeBootClock() = default;
 
 base::TimeDelta FakeBootClock::GetTimeSinceBoot() {
-  base::TimeTicks now = task_runner_->NowTicks();
+  base::TimeTicks now;
+  if (env_) {
+    now = env_->NowTicks();
+  } else {
+    now = task_runner_->NowTicks();
+  }
   return (now - initial_time_ticks_) + initial_time_since_boot_;
 }
 
diff --git a/chrome/browser/chromeos/power/ml/fake_boot_clock.h b/chrome/browser/chromeos/power/ml/fake_boot_clock.h
index 0c9784c0..070b3dd8 100644
--- a/chrome/browser/chromeos/power/ml/fake_boot_clock.h
+++ b/chrome/browser/chromeos/power/ml/fake_boot_clock.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/test/test_mock_time_task_runner.h"
 
 namespace chromeos {
@@ -19,13 +20,18 @@
  public:
   FakeBootClock(scoped_refptr<const base::TestMockTimeTaskRunner> task_runner,
                 base::TimeDelta initial_time_since_boot);
+  FakeBootClock(base::test::ScopedTaskEnvironment* env,
+                base::TimeDelta initial_time_since_boot);
   ~FakeBootClock() override;
 
   // BootClock:
   base::TimeDelta GetTimeSinceBoot() override;
 
  private:
+  // TODO(crbug.com/917580): This is no longer needed and should be removed.
+  // |env_| should give sufficient access to fake clock values.
   scoped_refptr<const base::TestMockTimeTaskRunner> task_runner_;
+  base::test::ScopedTaskEnvironment* env_;
   base::TimeDelta initial_time_since_boot_;
   base::TimeTicks initial_time_ticks_;
 
diff --git a/chrome/browser/chromeos/power/ml/user_activity_manager_unittest.cc b/chrome/browser/chromeos/power/ml/user_activity_manager_unittest.cc
index f7eb0e9..1d5c98c 100644
--- a/chrome/browser/chromeos/power/ml/user_activity_manager_unittest.cc
+++ b/chrome/browser/chromeos/power/ml/user_activity_manager_unittest.cc
@@ -9,8 +9,10 @@
 #include <string>
 #include <vector>
 
+#include "base/cancelable_callback.h"
+#include "base/sequenced_task_runner.h"
 #include "base/test/scoped_feature_list.h"
-#include "base/test/test_mock_time_task_runner.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/time/clock.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
@@ -36,6 +38,8 @@
 #include "components/ukm/content/source_url_recorder.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_utils.h"
 #include "content/public/test/web_contents_tester.h"
 #include "services/metrics/public/cpp/ukm_source.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -98,11 +102,10 @@
 };
 
 // Testing smart dim model.
-// TODO(crbug.com/914640): This is single-threaded, unlike the production
-// implementation. Convert it to multi-threaded.
 class FakeSmartDimModel : public SmartDimModel {
  public:
-  FakeSmartDimModel() = default;
+  FakeSmartDimModel(const scoped_refptr<base::SequencedTaskRunner> runner)
+      : task_runner_(runner) {}
   ~FakeSmartDimModel() override = default;
 
   void set_inactivity_score(const int inactivity_score) {
@@ -113,9 +116,8 @@
     decision_threshold_ = decision_threshold;
   }
 
-  // SmartDimModel overrides:
-  void RequestDimDecision(const UserActivityEvent::Features& features,
-                          DimDecisionCallback dim_callback) override {
+  UserActivityEvent::ModelPrediction ShouldDim(
+      const UserActivityEvent::Features& input_features) {
     UserActivityEvent::ModelPrediction model_prediction;
     // If either of these two values are set outside of the legal range [0,100],
     // return an error code.
@@ -135,14 +137,30 @@
         model_prediction.set_response(UserActivityEvent::ModelPrediction::DIM);
       }
     }
-    std::move(dim_callback).Run(model_prediction);
+    return model_prediction;
   }
 
-  void CancelPreviousRequest() override{};
+  // SmartDimModel overrides:
+  void RequestDimDecision(const UserActivityEvent::Features& features,
+                          DimDecisionCallback dim_callback) override {
+    // Cancel previously assigned callbacks and set it to the new callback.
+    cancelable_callback_.Reset(std::move(dim_callback));
+    base::PostTaskAndReplyWithResult(
+        task_runner_.get(), FROM_HERE,
+        base::BindOnce(&FakeSmartDimModel::ShouldDim, base::Unretained(this),
+                       features),
+        base::BindOnce(cancelable_callback_.callback()));
+  }
+
+  // TODO(crbug.com/893425): Add unit tests that test this API.
+  void CancelPreviousRequest() override { cancelable_callback_.Cancel(); };
 
  private:
   int inactivity_score_ = -1;
   int decision_threshold_ = -1;
+  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
+  base::CancelableOnceCallback<void(UserActivityEvent::ModelPrediction)>
+      cancelable_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeSmartDimModel);
 };
@@ -150,7 +168,11 @@
 class UserActivityManagerTest : public ChromeRenderViewHostTestHarness {
  public:
   UserActivityManagerTest()
-      : task_runner_(base::MakeRefCounted<base::TestMockTimeTaskRunner>()) {
+      : ChromeRenderViewHostTestHarness(
+            base::test::ScopedTaskEnvironment::MainThreadType::UI_MOCK_TIME,
+            base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED,
+            content::TestBrowserThreadBundle::Options::DEFAULT),
+        model_(thread_bundle()->GetMainThreadTaskRunner()) {
     fake_power_manager_client_.Init(nullptr);
     viz::mojom::VideoDetectorObserverPtr observer;
     idle_event_notifier_ = std::make_unique<IdleEventNotifier>(
@@ -161,8 +183,9 @@
         &fake_power_manager_client_, &session_manager_,
         mojo::MakeRequest(&observer), &fake_user_manager_, &model_);
     activity_logger_->SetTaskRunnerForTesting(
-        task_runner_, std::make_unique<FakeBootClock>(
-                          task_runner_, base::TimeDelta::FromSeconds(10)));
+        thread_bundle()->GetMainThreadTaskRunner(),
+        std::make_unique<FakeBootClock>(thread_bundle(),
+                                        base::TimeDelta::FromSeconds(10)));
   }
 
   ~UserActivityManagerTest() override = default;
@@ -210,7 +233,7 @@
   void ReportSuspend(power_manager::SuspendImminent::Reason reason,
                      base::TimeDelta sleep_duration) {
     fake_power_manager_client_.SendSuspendImminent(reason);
-    GetTaskRunner()->FastForwardBy(sleep_duration);
+    thread_bundle()->FastForwardBy(sleep_duration);
     fake_power_manager_client_.SendSuspendDone(sleep_duration);
   }
 
@@ -285,10 +308,6 @@
     return ukm::GetSourceIdForWebContentsDocument(contents);
   }
 
-  const scoped_refptr<base::TestMockTimeTaskRunner>& GetTaskRunner() {
-    return task_runner_;
-  }
-
   TestingUserActivityUkmLogger delegate_;
   FakeSmartDimModel model_;
   chromeos::FakeChromeUserManager fake_user_manager_;
@@ -302,8 +321,6 @@
   const GURL url4_ = GURL("https://example4.com/");
 
  private:
-  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
-
   ui::UserActivityDetector user_activity_detector_;
   std::unique_ptr<IdleEventNotifier> idle_event_notifier_;
   chromeos::FakePowerManagerClient fake_power_manager_client_;
@@ -319,7 +336,7 @@
   // Trigger an idle event.
   const IdleEventNotifier::ActivityData data;
   ReportIdleEvent(data);
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(2));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(2));
   ReportUserActivity(nullptr);
 
   const std::vector<UserActivityEvent>& events = delegate_.events();
@@ -385,18 +402,18 @@
   // Trigger the 2nd idle event.
   ReportIdleEvent(data);
   // Second user event.
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(2));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(2));
   ReportUserActivity(nullptr);
 
   // Trigger the 3rd idle event.
   ReportIdleEvent(data);
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(3));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(3));
   ReportSuspend(power_manager::SuspendImminent_Reason_IDLE,
                 base::TimeDelta::FromSeconds(10));
 
   // Trigger the 4th idle event.
   ReportIdleEvent(data);
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(4));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(4));
   ReportSuspend(power_manager::SuspendImminent_Reason_IDLE,
                 base::TimeDelta::FromSeconds(10));
 
@@ -463,7 +480,7 @@
   const IdleEventNotifier::ActivityData data;
   ReportIdleEvent(data);
 
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(2));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(2));
   ReportLidEvent(chromeos::PowerManagerClient::LidState::CLOSED);
   const std::vector<UserActivityEvent>& events = delegate_.events();
   EXPECT_TRUE(events.empty());
@@ -517,9 +534,9 @@
   // Trigger an idle event.
   const IdleEventNotifier::ActivityData data;
   ReportIdleEvent(data);
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(20));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(20));
   ReportScreenIdleState(true /* screen_dim */, false /* screen_off */);
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(30));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(30));
   ReportScreenIdleState(true /* screen_dim */, true /* screen_off */);
   ReportSuspend(power_manager::SuspendImminent_Reason_IDLE,
                 base::TimeDelta::FromSeconds(10));
@@ -543,11 +560,11 @@
   // Trigger an idle event.
   const IdleEventNotifier::ActivityData data;
   ReportIdleEvent(data);
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(20));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(20));
   ReportScreenIdleState(true /* screen_dim */, false /* screen_off */);
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(30));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(30));
   ReportScreenIdleState(true /* screen_dim */, true /* screen_off */);
-  GetTaskRunner()->FastForwardUntilNoTasksRemain();
+  thread_bundle()->RunUntilIdle();
 
   const std::vector<UserActivityEvent>& events = delegate_.events();
   ASSERT_EQ(0U, events.size());
@@ -560,14 +577,14 @@
   const IdleEventNotifier::ActivityData data;
   ReportIdleEvent(data);
 
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(20));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(20));
   ReportScreenIdleState(true /* screen_dim */, false /* screen_off */);
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(30));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(30));
   ReportScreenIdleState(true /* screen_dim */, true /* screen_off */);
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(1));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(1));
 
   ReportUserActivity(nullptr);
-  GetTaskRunner()->FastForwardUntilNoTasksRemain();
+  thread_bundle()->RunUntilIdle();
 
   const std::vector<UserActivityEvent>& events = delegate_.events();
   ASSERT_EQ(1U, events.size());
@@ -621,7 +638,7 @@
   const IdleEventNotifier::ActivityData data;
   ReportIdleEvent(data);
 
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(20));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(20));
   ReportSuspend(power_manager::SuspendImminent_Reason_IDLE,
                 base::TimeDelta::FromSeconds(1));
   const std::vector<UserActivityEvent>& events = delegate_.events();
@@ -797,7 +814,7 @@
 
   ReportScreenIdleState(false /* screen_dim */, true /* screen_off */);
 
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(7));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(7));
   ReportUserActivity(nullptr);
 
   const std::vector<UserActivityEvent>& events = delegate_.events();
@@ -825,7 +842,7 @@
   ReportIdleEvent(data);
 
   ReportScreenIdleState(false /* screen_dim */, false /* screen_off */);
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(7));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(7));
   ReportScreenIdleState(true /* screen_dim */, true /* screen_off */);
 
   ReportUserActivity(nullptr);
@@ -854,7 +871,7 @@
 
   ReportScreenIdleState(true /* screen_dim */, false /* screen_off */);
   ReportScreenIdleState(true /* screen_dim */, true /* screen_off */);
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(7));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(7));
   ReportScreenIdleState(false /* screen_dim */, false /* screen_off */);
   ReportUserActivity(nullptr);
 
@@ -886,6 +903,7 @@
 
   const IdleEventNotifier::ActivityData data;
   ReportIdleEvent(data);
+  thread_bundle()->RunUntilIdle();
   ReportUserActivity(nullptr);
   EXPECT_EQ(1, GetNumberOfDeferredDims());
 
@@ -921,6 +939,7 @@
 
   const IdleEventNotifier::ActivityData data;
   ReportIdleEvent(data);
+  thread_bundle()->RunUntilIdle();
   EXPECT_EQ(1, GetNumberOfDeferredDims());
 
   const std::vector<UserActivityEvent>& events = delegate_.events();
@@ -939,6 +958,7 @@
 
   const IdleEventNotifier::ActivityData data;
   ReportIdleEvent(data);
+  thread_bundle()->RunUntilIdle();
   ReportUserActivity(nullptr);
   EXPECT_EQ(0, GetNumberOfDeferredDims());
 
@@ -967,20 +987,21 @@
 
   const IdleEventNotifier::ActivityData data;
   ReportIdleEvent(data);
+  thread_bundle()->RunUntilIdle();
   EXPECT_EQ(1, GetNumberOfDeferredDims());
 
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(6));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(6));
   ReportSuspend(power_manager::SuspendImminent_Reason_IDLE,
                 base::TimeDelta::FromSeconds(3));
 
   // 2nd ScreenDimImminent is not deferred despite model score says so.
   model_.set_inactivity_score(20);
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(10));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(10));
   ReportIdleEvent(data);
   EXPECT_EQ(1, GetNumberOfDeferredDims());
 
   // Log when a SuspendImminent is received
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(20));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(20));
   ReportSuspend(power_manager::SuspendImminent_Reason_IDLE,
                 base::TimeDelta::FromSeconds(3));
 
@@ -1035,16 +1056,18 @@
   model_.set_inactivity_score(40);
   const IdleEventNotifier::ActivityData data;
   ReportIdleEvent(data);
+  thread_bundle()->RunUntilIdle();
   EXPECT_EQ(1, GetNumberOfDeferredDims());
 
   // 2nd ScreenDimImminent is not deferred despite model score says so.
   model_.set_inactivity_score(20);
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(10));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(10));
   ReportIdleEvent(data);
+  thread_bundle()->RunUntilIdle();
   EXPECT_EQ(1, GetNumberOfDeferredDims());
 
   // Log when a SuspendImminent is received
-  GetTaskRunner()->FastForwardBy(base::TimeDelta::FromSeconds(20));
+  thread_bundle()->FastForwardBy(base::TimeDelta::FromSeconds(20));
   ReportSuspend(power_manager::SuspendImminent_Reason_IDLE,
                 base::TimeDelta::FromSeconds(3));
 
@@ -1095,6 +1118,7 @@
 
   const IdleEventNotifier::ActivityData data;
   ReportIdleEvent(data);
+  thread_bundle()->RunUntilIdle();
   ReportUserActivity(nullptr);
   EXPECT_EQ(0, GetNumberOfDeferredDims());
 
diff --git a/chrome/browser/chromeos/shutdown_policy_browsertest.cc b/chrome/browser/chromeos/shutdown_policy_browsertest.cc
index d8625c30..c2e2ab1 100644
--- a/chrome/browser/chromeos/shutdown_policy_browsertest.cc
+++ b/chrome/browser/chromeos/shutdown_policy_browsertest.cc
@@ -9,7 +9,7 @@
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/ash_view_ids.h"
 #include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/system_tray_test_api.mojom.h"
+#include "ash/public/interfaces/system_tray_test_api.test-mojom.h"
 #include "base/command_line.h"
 #include "base/location.h"
 #include "base/macros.h"
diff --git a/chrome/browser/chromeos/system/tray_accessibility_browsertest.cc b/chrome/browser/chromeos/system/tray_accessibility_browsertest.cc
index a33c722..6048586 100644
--- a/chrome/browser/chromeos/system/tray_accessibility_browsertest.cc
+++ b/chrome/browser/chromeos/system/tray_accessibility_browsertest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/ash_view_ids.h"
-#include "ash/public/interfaces/system_tray_test_api.mojom.h"
+#include "ash/public/interfaces/system_tray_test_api.test-mojom.h"
 #include "base/callback.h"
 #include "base/command_line.h"
 #include "base/run_loop.h"
diff --git a/chrome/browser/devtools/devtools_sanity_interactive_browsertest.cc b/chrome/browser/devtools/devtools_sanity_interactive_browsertest.cc
index af4e61d..007abf8bdcb 100644
--- a/chrome/browser/devtools/devtools_sanity_interactive_browsertest.cc
+++ b/chrome/browser/devtools/devtools_sanity_interactive_browsertest.cc
@@ -147,14 +147,7 @@
   CheckIsMinimized(true);
 }
 
-#if defined(OS_MACOSX)
-// https://crbug.com/828031
-#define MAYBE_NormalToFullscreenWindow DISABLED_NormalToFullscreenWindow
-#else
-#define MAYBE_NormalToFullscreenWindow NormalToFullscreenWindow
-#endif
-IN_PROC_BROWSER_TEST_F(DevToolsManagerDelegateTest,
-                       MAYBE_NormalToFullscreenWindow) {
+IN_PROC_BROWSER_TEST_F(DevToolsManagerDelegateTest, NormalToFullscreenWindow) {
 #if defined(OS_MACOSX)
   ui::test::ScopedFakeNSWindowFullscreen faker;
 #endif
@@ -183,7 +176,7 @@
 }
 
 #if defined(OS_MACOSX)
-// https://crbug.com/828031
+// MacViews does not yet implement maximized windows: https://crbug.com/836327
 #define MAYBE_MaximizedToFullscreenWindow DISABLED_MaximizedToFullscreenWindow
 #else
 #define MAYBE_MaximizedToFullscreenWindow MaximizedToFullscreenWindow
@@ -193,14 +186,8 @@
   browser()->window()->Maximize();
   CheckIsMaximized(true);
 
-#if defined(OS_MACOSX)
-  ui::test::ScopedFakeNSWindowFullscreen faker;
-#endif
   CheckIsFullscreen(false);
   SendCommand("fullscreen");
-#if defined(OS_MACOSX)
-  faker.FinishTransition();
-#endif
   CheckIsFullscreen(true);
 }
 
@@ -225,14 +212,7 @@
   CheckIsMaximized(false);
 }
 
-#if defined(OS_MACOSX)
-// https://crbug.com/828031
-#define MAYBE_ExitFullscreenWindow DISABLED_ExitFullscreenWindow
-#else
-#define MAYBE_ExitFullscreenWindow ExitFullscreenWindow
-#endif
-IN_PROC_BROWSER_TEST_F(DevToolsManagerDelegateTest,
-                       MAYBE_ExitFullscreenWindow) {
+IN_PROC_BROWSER_TEST_F(DevToolsManagerDelegateTest, ExitFullscreenWindow) {
 #if defined(OS_MACOSX)
   ui::test::ScopedFakeNSWindowFullscreen faker;
 #endif
diff --git a/chrome/browser/download/download_task_scheduler_impl.cc b/chrome/browser/download/download_task_scheduler_impl.cc
index 325ae9b..d12556d 100644
--- a/chrome/browser/download/download_task_scheduler_impl.cc
+++ b/chrome/browser/download/download_task_scheduler_impl.cc
@@ -49,6 +49,11 @@
 
 void DownloadTaskSchedulerImpl::RunScheduledTask(
     download::DownloadTaskType task_type) {
+  if (task_type == download::DownloadTaskType::DOWNLOAD_AUTO_RESUMPTION_TASK) {
+    NOTREACHED();
+    return;
+  }
+
   download::DownloadService* download_service =
       DownloadServiceFactory::GetForBrowserContext(context_);
   download_service->OnStartScheduledTask(
diff --git a/chrome/browser/extensions/api/tabs/tabs_test.cc b/chrome/browser/extensions/api/tabs/tabs_test.cc
index 7f5bd8b..84d6446 100644
--- a/chrome/browser/extensions/api/tabs/tabs_test.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_test.cc
@@ -1035,6 +1035,7 @@
 #if defined(OS_MACOSX)
   if (base::mac::IsOS10_10())
     return;  // Fails when swarmed. http://crbug.com/660582
+  ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen;
 #endif
 
   scoped_refptr<WindowsCreateFunction> function(new WindowsCreateFunction());
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index f68b35a..c515ca8 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -675,7 +675,7 @@
   },
   {
     "name": "document-passive-event-listeners",
-    "owners": ["input-dev" ],
+    "owners": ["nzolghadr, input-dev" ],
     "expiry_milestone": 76
   },
   {
@@ -1174,6 +1174,11 @@
     "expiry_milestone": 78
   },
   {
+    "name": "enable-experimental-accessibility-labels",
+    "owners": [ "katie", "dmazzoni", "dtseng" ],
+    "expiry_milestone": 78
+  },
+  {
     "name": "enable-experimental-crostini-ui",
     "owners": [ "nverne", "benwells" ],
     "expiry_milestone": 76
@@ -1936,8 +1941,8 @@
   },
   {
     "name": "enable-touch-drag-drop",
-    "owners": ["input-dev" ],
-    "expiry_milestone": -1
+    "owners": ["eirage", "input-dev" ],
+    "expiry_milestone": 78
   },
   {
     "name": "enable-touchable-app-context-menu",
@@ -2762,13 +2767,13 @@
   },
   {
     "name": "passive-event-listeners-due-to-fling",
-    "owners": ["input-dev" ],
+    "owners": ["sahel", "input-dev" ],
     "expiry_milestone": 76
   },
   {
     "name": "passive-listener-default",
-    "owners": ["input-dev" ],
-    "expiry_milestone": 76
+    "owners": ["nzolghadr", "input-dev" ],
+    "expiry_milestone": 78
   },
   {
     "name": "passwords-keyboard-accessory",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index b44def0..3ada28ad 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -692,6 +692,11 @@
 const char kExperimentalAccessibilityAutoclickDescription[] =
     "Enable additional features for automatic clicks.";
 
+const char kExperimentalAccessibilityLabelsName[] =
+    "Experimental accessibility image label features";
+const char kExperimentalAccessibilityLabelsDescription[] =
+    "Enable additional features for image labels for accessibility.";
+
 const char kExperimentalAccessibilitySwitchAccessName[] =
     "Experimental feature Switch Access";
 const char kExperimentalAccessibilitySwitchAccessDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 2040a504..e658eb0 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -166,6 +166,9 @@
 extern const char kExperimentalAccessibilityAutoclickName[];
 extern const char kExperimentalAccessibilityAutoclickDescription[];
 
+extern const char kExperimentalAccessibilityLabelsName[];
+extern const char kExperimentalAccessibilityLabelsDescription[];
+
 extern const char kExperimentalAccessibilitySwitchAccessName[];
 extern const char kExperimentalAccessibilitySwitchAccessDescription[];
 
diff --git a/chrome/browser/notifications/notification_interactive_uitest.cc b/chrome/browser/notifications/notification_interactive_uitest.cc
index 952f260..1dc55cdc 100644
--- a/chrome/browser/notifications/notification_interactive_uitest.cc
+++ b/chrome/browser/notifications/notification_interactive_uitest.cc
@@ -580,6 +580,9 @@
 }
 
 IN_PROC_BROWSER_TEST_F(NotificationsTest, TestShouldDisplayFullscreen) {
+#if defined(OS_MACOSX)
+  ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen;
+#endif
   ASSERT_TRUE(embedded_test_server()->Start());
 
   AllowAllOrigins();
@@ -662,6 +665,9 @@
 // Verify that a notification is actually displayed when the webpage that
 // creates it is fullscreen with the fullscreen notification flag turned on.
 IN_PROC_BROWSER_TEST_F(NotificationsTest, TestShouldDisplayPopupNotification) {
+#if defined(OS_MACOSX)
+  ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen;
+#endif
   ASSERT_TRUE(embedded_test_server()->Start());
 
   // Creates a simple notification.
diff --git a/chrome/browser/ntp_snippets/contextual_content_suggestions_service_factory.cc b/chrome/browser/ntp_snippets/contextual_content_suggestions_service_factory.cc
index 373a206d..5622d2c 100644
--- a/chrome/browser/ntp_snippets/contextual_content_suggestions_service_factory.cc
+++ b/chrome/browser/ntp_snippets/contextual_content_suggestions_service_factory.cc
@@ -12,20 +12,13 @@
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/search/suggestions/image_decoder_impl.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
-#include "components/image_fetcher/core/image_decoder.h"
-#include "components/image_fetcher/core/image_fetcher.h"
-#include "components/image_fetcher/core/image_fetcher_impl.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/ntp_snippets/contextual/contextual_content_suggestions_service.h"
 #include "components/ntp_snippets/contextual/contextual_suggestions_features.h"
 #include "components/ntp_snippets/contextual/contextual_suggestions_fetcher_impl.h"
 #include "components/ntp_snippets/contextual/reporting/contextual_suggestions_debugging_reporter.h"
 #include "components/ntp_snippets/contextual/reporting/contextual_suggestions_reporter.h"
-#include "components/ntp_snippets/remote/cached_image_fetcher.h"
-#include "components/ntp_snippets/remote/remote_suggestions_database.h"
-#include "components/prefs/pref_service.h"
 #include "components/unified_consent/feature.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
@@ -39,9 +32,6 @@
 using contextual_suggestions::ContextualSuggestionsFetcherImpl;
 using contextual_suggestions::ContextualContentSuggestionsService;
 
-using ntp_snippets::CachedImageFetcher;
-using ntp_snippets::RemoteSuggestionsDatabase;
-
 namespace {
 
 bool AreContextualContentSuggestionsEnabled() {
@@ -96,7 +86,6 @@
     return nullptr;
   }
 
-  PrefService* pref_service = profile->GetPrefs();
   content::StoragePartition* storage_partition =
       content::BrowserContext::GetDefaultStoragePartition(context);
   std::unique_ptr<unified_consent::UrlKeyedDataCollectionConsentHelper>
@@ -112,26 +101,12 @@
       std::make_unique<ContextualSuggestionsFetcherImpl>(
           storage_partition->GetURLLoaderFactoryForBrowserProcess(),
           std::move(consent_helper), g_browser_process->GetApplicationLocale());
-  const base::FilePath::CharType kDatabaseFolder[] =
-      FILE_PATH_LITERAL("contextualSuggestionsDatabase");
-  base::FilePath database_dir(profile->GetPath().Append(kDatabaseFolder));
-  auto contextual_suggestions_database =
-      std::make_unique<RemoteSuggestionsDatabase>(database_dir);
-  auto cached_image_fetcher =
-      std::make_unique<ntp_snippets::CachedImageFetcher>(
-          std::make_unique<image_fetcher::ImageFetcherImpl>(
-              std::make_unique<suggestions::ImageDecoderImpl>(),
-              content::BrowserContext::GetDefaultStoragePartition(profile)
-                  ->GetURLLoaderFactoryForBrowserProcess()),
-          pref_service, contextual_suggestions_database.get());
   auto reporter_provider = std::make_unique<
       contextual_suggestions::ContextualSuggestionsReporterProvider>(
       std::make_unique<
           contextual_suggestions::ContextualSuggestionsDebuggingReporter>());
   auto* service = new ContextualContentSuggestionsService(
-      std::move(contextual_suggestions_fetcher),
-      std::move(cached_image_fetcher),
-      std::move(contextual_suggestions_database), std::move(reporter_provider));
+      std::move(contextual_suggestions_fetcher), std::move(reporter_provider));
 
   return service;
 }
diff --git a/chrome/browser/previews/previews_lite_page_browsertest.cc b/chrome/browser/previews/previews_lite_page_browsertest.cc
index 4b546b7..4a2a165 100644
--- a/chrome/browser/previews/previews_lite_page_browsertest.cc
+++ b/chrome/browser/previews/previews_lite_page_browsertest.cc
@@ -974,7 +974,8 @@
     VerifyPreviewNotLoaded();
     histogram_tester.ExpectBucketCount(
         "Previews.ServerLitePage.BlacklistReasons",
-        PreviewsLitePageNavigationThrottle::BlacklistReason::kHostBlacklisted,
+        PreviewsLitePageNavigationThrottle::BlacklistReason::
+            kHostBypassBlacklisted,
         1);
     ClearDeciderState();
 
@@ -1121,10 +1122,8 @@
   ClearDeciderState();
 }
 
-// This was previously marked DISABLE_ON_WIN_MAC(), but the test is also failing
-// flakily on Linux, so now it is completely disabled. https://crbug.com/915775
 IN_PROC_BROWSER_TEST_F(PreviewsLitePageServerBrowserTest,
-                       DISABLED_LitePagePreviewsReportSavings) {
+                       DISABLE_ON_WIN_MAC(LitePagePreviewsReportSavings)) {
   PrefService* prefs = browser()->profile()->GetPrefs();
   prefs->SetBoolean(data_reduction_proxy::prefs::kDataUsageReportingEnabled,
                     true);
@@ -1136,6 +1135,10 @@
   ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
   VerifyPreviewLoaded();
 
+  // Navigate to an untracked (no preview) page before checking reported savings
+  // to reduce flakiness.
+  ui_test_utils::NavigateToURL(browser(), GURL("http://www.google.com"));
+
   EXPECT_EQ(GetTotalOriginalContentLength() - GetTotalDataUsage(), 40U);
   EXPECT_EQ(GetDataUsage(), 20U);
 }
diff --git a/chrome/browser/previews/previews_lite_page_decider.cc b/chrome/browser/previews/previews_lite_page_decider.cc
index 89c0322..afd9a2b 100644
--- a/chrome/browser/previews/previews_lite_page_decider.cc
+++ b/chrome/browser/previews/previews_lite_page_decider.cc
@@ -135,7 +135,7 @@
       page_id_(base::RandUint64()),
       drp_settings_(nullptr),
       pref_service_(nullptr),
-      host_blacklist_(std::make_unique<base::DictionaryValue>()) {
+      host_bypass_blacklist_(std::make_unique<base::DictionaryValue>()) {
   if (!browser_context)
     return;
 
@@ -148,15 +148,15 @@
   DCHECK(!browser_context->IsOffTheRecord());
 
   pref_service_ = Profile::FromBrowserContext(browser_context)->GetPrefs();
-  host_blacklist_ =
+  host_bypass_blacklist_ =
       pref_service_->GetDictionary(kHostBlacklist)->CreateDeepCopy();
 
   // Note: This switch has no effect if |drp_settings| was null since
-  // |host_blacklist_| would be empty anyways.
+  // |host_bypass_blacklist_| would be empty anyways.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           previews::switches::kClearLitePageRedirectLocalBlacklist)) {
-    host_blacklist_->Clear();
-    pref_service_->Set(kHostBlacklist, *host_blacklist_);
+    host_bypass_blacklist_->Clear();
+    pref_service_->Set(kHostBlacklist, *host_bypass_blacklist_);
   }
 
   // Add |this| as an observer to DRP, but if DRP is already initialized, check
@@ -253,14 +253,14 @@
 
 void PreviewsLitePageDecider::ClearBlacklist() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  host_blacklist_->Clear();
+  host_bypass_blacklist_->Clear();
   if (pref_service_)
-    pref_service_->Set(kHostBlacklist, *host_blacklist_);
+    pref_service_->Set(kHostBlacklist, *host_bypass_blacklist_);
 }
 
 void PreviewsLitePageDecider::ClearStateForTesting() {
   single_bypass_.clear();
-  host_blacklist_->Clear();
+  host_bypass_blacklist_->Clear();
 }
 
 void PreviewsLitePageDecider::SetUserHasSeenUINotification() {
@@ -369,21 +369,22 @@
                      base::Unretained(this)));
 }
 
-void PreviewsLitePageDecider::BlacklistHost(const std::string& host,
-                                            base::TimeDelta duration) {
+void PreviewsLitePageDecider::BlacklistBypassedHost(const std::string& host,
+                                                    base::TimeDelta duration) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // If there is an existing entry, intentionally update it.
-  host_blacklist_->SetKey(
+  host_bypass_blacklist_->SetKey(
       host, base::Value((base::Time::Now() + duration).ToDoubleT()));
 
-  RemoveStaleEntries(host_blacklist_.get());
+  RemoveStaleEntries(host_bypass_blacklist_.get());
   if (pref_service_)
-    pref_service_->Set(kHostBlacklist, *host_blacklist_);
+    pref_service_->Set(kHostBlacklist, *host_bypass_blacklist_);
 }
 
-bool PreviewsLitePageDecider::HostBlacklisted(const std::string& host) {
+bool PreviewsLitePageDecider::HostBlacklistedFromBypass(
+    const std::string& host) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  base::Value* value = host_blacklist_->FindKey(host);
+  base::Value* value = host_bypass_blacklist_->FindKey(host);
   if (!value)
     return false;
 
diff --git a/chrome/browser/previews/previews_lite_page_decider.h b/chrome/browser/previews/previews_lite_page_decider.h
index 8f908f0..e6d9cfda 100644
--- a/chrome/browser/previews/previews_lite_page_decider.h
+++ b/chrome/browser/previews/previews_lite_page_decider.h
@@ -85,9 +85,9 @@
                          const std::string& host) override;
   bool NeedsToNotifyUser() override;
   void NotifyUser(content::WebContents* web_contents) override;
-  void BlacklistHost(const std::string& host,
-                     base::TimeDelta duration) override;
-  bool HostBlacklisted(const std::string& host) override;
+  void BlacklistBypassedHost(const std::string& host,
+                             base::TimeDelta duration) override;
+  bool HostBlacklistedFromBypass(const std::string& host) override;
 
   // data_reduction_proxy::DataReductionProxySettingsObserver:
   void OnProxyRequestHeadersChanged(
@@ -122,7 +122,7 @@
   // A dictionary of host string to base::Time. If a hostname is a member of
   // this dictionary, that host should be blacklisted from this preview until
   // after the time value. This is stored persistently in prefs.
-  std::unique_ptr<base::DictionaryValue> host_blacklist_;
+  std::unique_ptr<base::DictionaryValue> host_bypass_blacklist_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/chrome/browser/previews/previews_lite_page_decider_unittest.cc b/chrome/browser/previews/previews_lite_page_decider_unittest.cc
index 71be1a7c..0bde54a 100644
--- a/chrome/browser/previews/previews_lite_page_decider_unittest.cc
+++ b/chrome/browser/previews/previews_lite_page_decider_unittest.cc
@@ -39,7 +39,7 @@
   base::test::ScopedTaskEnvironment scoped_task_environment_;
 };
 
-TEST_F(PreviewsLitePageDeciderTest, TestHostBlacklist) {
+TEST_F(PreviewsLitePageDeciderTest, TestHostBypassBlacklist) {
   const int kBlacklistDurationDays = 30;
   const std::string kHost = "google.com";
   const std::string kOtherHost = "chromium.org";
@@ -51,49 +51,49 @@
   PreviewsLitePageNavigationThrottleManager* manager = decider.get();
 
   // Simple happy case.
-  manager->BlacklistHost(kHost, kOneDay);
-  EXPECT_TRUE(manager->HostBlacklisted(kHost));
+  manager->BlacklistBypassedHost(kHost, kOneDay);
+  EXPECT_TRUE(manager->HostBlacklistedFromBypass(kHost));
   decider->ClearStateForTesting();
 
   // Old entries are deleted.
-  manager->BlacklistHost(kHost, kYesterday);
-  EXPECT_FALSE(manager->HostBlacklisted(kHost));
+  manager->BlacklistBypassedHost(kHost, kYesterday);
+  EXPECT_FALSE(manager->HostBlacklistedFromBypass(kHost));
   decider->ClearStateForTesting();
 
   // Oldest entry is thrown out.
-  manager->BlacklistHost(kHost, kOneDay);
-  EXPECT_TRUE(manager->HostBlacklisted(kHost));
+  manager->BlacklistBypassedHost(kHost, kOneDay);
+  EXPECT_TRUE(manager->HostBlacklistedFromBypass(kHost));
   for (int i = 1; i <= kBlacklistDurationDays; i++) {
-    manager->BlacklistHost(kHost + base::IntToString(i),
-                           kOneDay + base::TimeDelta::FromSeconds(i));
+    manager->BlacklistBypassedHost(kHost + base::IntToString(i),
+                                   kOneDay + base::TimeDelta::FromSeconds(i));
   }
-  EXPECT_FALSE(manager->HostBlacklisted(kHost));
+  EXPECT_FALSE(manager->HostBlacklistedFromBypass(kHost));
   decider->ClearStateForTesting();
 
   // Oldest entry is not thrown out if there was a stale entry to remove.
-  manager->BlacklistHost(kHost, kOneDay);
-  EXPECT_TRUE(manager->HostBlacklisted(kHost));
+  manager->BlacklistBypassedHost(kHost, kOneDay);
+  EXPECT_TRUE(manager->HostBlacklistedFromBypass(kHost));
   for (int i = 1; i <= kBlacklistDurationDays - 1; i++) {
-    manager->BlacklistHost(kHost + base::IntToString(i),
-                           kOneDay + base::TimeDelta::FromSeconds(i));
+    manager->BlacklistBypassedHost(kHost + base::IntToString(i),
+                                   kOneDay + base::TimeDelta::FromSeconds(i));
   }
-  manager->BlacklistHost(kOtherHost, kYesterday);
-  EXPECT_TRUE(manager->HostBlacklisted(kHost));
+  manager->BlacklistBypassedHost(kOtherHost, kYesterday);
+  EXPECT_TRUE(manager->HostBlacklistedFromBypass(kHost));
   decider->ClearStateForTesting();
 }
 
-TEST_F(PreviewsLitePageDeciderTest, TestClearBlacklist) {
+TEST_F(PreviewsLitePageDeciderTest, TestClearHostBypassBlacklist) {
   const std::string kHost = "1.chromium.org";
 
   std::unique_ptr<PreviewsLitePageDecider> decider =
       std::make_unique<PreviewsLitePageDecider>(nullptr);
   PreviewsLitePageNavigationThrottleManager* manager = decider.get();
 
-  manager->BlacklistHost(kHost, base::TimeDelta::FromMinutes(1));
-  EXPECT_TRUE(manager->HostBlacklisted(kHost));
+  manager->BlacklistBypassedHost(kHost, base::TimeDelta::FromMinutes(1));
+  EXPECT_TRUE(manager->HostBlacklistedFromBypass(kHost));
 
   decider->ClearBlacklist();
-  EXPECT_FALSE(manager->HostBlacklisted(kHost));
+  EXPECT_FALSE(manager->HostBlacklistedFromBypass(kHost));
 }
 
 TEST_F(PreviewsLitePageDeciderTest, TestServerUnavailable) {
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
index 172e4f8..9e5fe4b 100644
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
@@ -306,8 +306,8 @@
     }
   }
 
-  if (manager_->HostBlacklisted(url.host()))
-    blacklist_reasons.push_back(BlacklistReason::kHostBlacklisted);
+  if (manager_->HostBlacklistedFromBypass(url.host()))
+    blacklist_reasons.push_back(BlacklistReason::kHostBypassBlacklisted);
 
   // Record UMA
   for (BlacklistReason reason : blacklist_reasons) {
@@ -562,7 +562,8 @@
           chrome_proxy_header.find("host-blacklisted") != std::string::npos;
 
       if (blacklist_host)
-        manager_->BlacklistHost(GURL(original_url).host(), kBlacklistDuration);
+        manager_->BlacklistBypassedHost(GURL(original_url).host(),
+                                        kBlacklistDuration);
 
       UMA_HISTOGRAM_BOOLEAN("Previews.ServerLitePage.HostBlacklistedOnBypass",
                             blacklist_host);
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle.h b/chrome/browser/previews/previews_lite_page_navigation_throttle.h
index e531a84..a0d073ef 100644
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle.h
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle.h
@@ -44,8 +44,8 @@
     kPathSuffixBlacklisted = 0,
     kNavigationToPreviewsDomain = 1,
     kNavigationToPrivateDomain = 2,
-    kHostBlacklisted = 3,
-    kMaxValue = kHostBlacklisted,
+    kHostBypassBlacklisted = 3,
+    kMaxValue = kHostBypassBlacklisted,
   };
 
   // Reasons that a navigation is not eligible for this preview. This enum must
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h b/chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h
index 81b9a26..57f3493 100644
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h
@@ -53,12 +53,14 @@
   // Prompts |this| to display the required UI notifications to the user.
   virtual void NotifyUser(content::WebContents* web_contents) = 0;
 
-  // Blacklists the given |host| for the given |duration|.
-  virtual void BlacklistHost(const std::string& host,
-                             base::TimeDelta duration) = 0;
+  // Blacklists the given |host| for the given |duration| in the server
+  // bypass blacklist for LitePageRedirects.
+  virtual void BlacklistBypassedHost(const std::string& host,
+                                     base::TimeDelta duration) = 0;
 
-  // Returns true if the given |host| is blacklisted.
-  virtual bool HostBlacklisted(const std::string& host) = 0;
+  // Returns true if the given |host| is blacklisted in the server bypass
+  // blacklist.
+  virtual bool HostBlacklistedFromBypass(const std::string& host) = 0;
 };
 
 #endif  // CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_NAVIGATION_THROTTLE_MANAGER_H_
diff --git a/chrome/browser/resources/chromeos/login/arc_terms_of_service.html b/chrome/browser/resources/chromeos/login/arc_terms_of_service.html
index 758591d..7193b7b2 100644
--- a/chrome/browser/resources/chromeos/login/arc_terms_of_service.html
+++ b/chrome/browser/resources/chromeos/login/arc_terms_of_service.html
@@ -78,7 +78,8 @@
         <oobe-text-button id="arc-tos-accept-button"
             inverse on-tap="onAccept_"
             disabled="[[arcTosButtonsDisabled]]">
-          <div i18n-content="arcTermsOfServiceAcceptButton"></div>
+          <div id="arc-tos-accept-button-content"
+              i18n-content="arcTermsOfServiceAcceptButton"></div>
         </oobe-text-button>
       </div>
     </oobe-dialog>
diff --git a/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.js b/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.js
index 2653942..3e285ee 100644
--- a/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.js
+++ b/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.js
@@ -616,7 +616,7 @@
         this.setMetricsMode(
             loadTimeData.getString('arcTextMetricsManagedEnabled'), true);
       }
-      this.getElement_('arc-tos-accept-button').textContent =
+      this.getElement_('arc-tos-accept-button-content').textContent =
           loadTimeData.getString(
               isDemoModeSetup ? 'arcTermsOfServiceAcceptAndContinueButton' :
                                 'arcTermsOfServiceAcceptButton');
diff --git a/chrome/browser/resources/feed_internals/BUILD.gn b/chrome/browser/resources/feed_internals/BUILD.gn
new file mode 100644
index 0000000..2b2288c
--- /dev/null
+++ b/chrome/browser/resources/feed_internals/BUILD.gn
@@ -0,0 +1,19 @@
+# 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("//third_party/closure_compiler/compile_js.gni")
+
+js_type_check("closure_compile") {
+  deps = [
+    ":feed_internals",
+  ]
+}
+
+js_library("feed_internals") {
+  extra_deps = [ "//chrome/browser/ui/webui/feed_internals:mojo_bindings_js" ]
+  externs_list = [
+    "$root_gen_dir/chrome/browser/ui/webui/feed_internals/feed_internals.mojom-lite.externs.js",
+    "$externs_path/mojo.js",
+  ]
+}
diff --git a/chrome/browser/resources/feed_internals/OWNERS b/chrome/browser/resources/feed_internals/OWNERS
new file mode 100644
index 0000000..4a7fc50
--- /dev/null
+++ b/chrome/browser/resources/feed_internals/OWNERS
@@ -0,0 +1 @@
+file://components/feed/OWNERS
diff --git a/chrome/browser/resources/feed_internals/feed_internals.html b/chrome/browser/resources/feed_internals/feed_internals.html
new file mode 100644
index 0000000..df5acf4
--- /dev/null
+++ b/chrome/browser/resources/feed_internals/feed_internals.html
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+
+<!doctype html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Feed Internals</title>
+  <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
+
+  <script src="chrome://resources/js/mojo_bindings_lite.js"></script>
+  <script src="feed_internals.mojom-lite.js"></script>
+
+  <script src="feed_internals.js"></script>
+</head>
+
+<body>
+  TODO(chouinard): Implement this.
+</body>
+</html>
diff --git a/chrome/browser/resources/feed_internals/feed_internals.js b/chrome/browser/resources/feed_internals/feed_internals.js
new file mode 100644
index 0000000..57f55000
--- /dev/null
+++ b/chrome/browser/resources/feed_internals/feed_internals.js
@@ -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.
+
+'use strict';
+
+// Reference to the backend.
+let pageHandler = null;
+
+document.addEventListener('DOMContentLoaded', function() {
+  // Setup backend mojo.
+  pageHandler = feedInternals.mojom.PageHandler.getProxy();
+});
diff --git a/chrome/browser/resources/ntp4/images/2x/closed_window.png b/chrome/browser/resources/ntp4/images/2x/closed_window.png
deleted file mode 100644
index ca1fdd8..0000000
--- a/chrome/browser/resources/ntp4/images/2x/closed_window.png
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/ntp4/images/closed_window.png b/chrome/browser/resources/ntp4/images/closed_window.png
deleted file mode 100644
index d92c9dda..0000000
--- a/chrome/browser/resources/ntp4/images/closed_window.png
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/omnibox/omnibox.css b/chrome/browser/resources/omnibox/omnibox.css
index a7eedde..1314695 100644
--- a/chrome/browser/resources/omnibox/omnibox.css
+++ b/chrome/browser/resources/omnibox/omnibox.css
@@ -154,6 +154,10 @@
   font-size: 0;
 }
 
+.pair-container {
+  align-items: center;
+}
+
 .cell-contents-and-description .pair-container,
 .cell-fill-and-inline .pair-container {
   display: flex;
@@ -168,12 +172,20 @@
   margin-right: 15px;
 }
 
-.cell-contents-and-description .pair-item:first-child {
+.cell-contents-and-description img.pair-item {
+  border-radius: 3px;
+  height: 28px;
+}
+
+.cell-contents-and-description .pair-item.contents {
   color: blue;
 }
 
+.cell-contents-and-description .pair-item.answer {
+  color: orange;
+}
+
 .cell-fill-and-inline .pair-container {
-  align-items: center;
   margin-right: -1px;
 }
 
@@ -181,7 +193,7 @@
   margin-right: 1px;
 }
 
-.cell-fill-and-inline .pair-item:nth-child(2):not(:empty) {
+.cell-fill-and-inline .pair-item.inline:not(:empty) {
   border: 1px solid;
   font-weight: bold;
 }
@@ -190,6 +202,11 @@
   color: red;
 }
 
+.cell-destination-url img {
+  margin-right: 5px;
+  vertical-align: middle;
+}
+
 .accesskey {
   text-decoration: underline;
   text-transform: capitalize;
@@ -207,6 +224,11 @@
   border: 1px solid;
 }
 
+img:not([src]),
+.pair-item:empty:not(img) {
+  display: none;
+}
+
 omnibox-input {
   --action-color: rgb(66, 133, 244);
   --hover-color: #f2f2f2;
diff --git a/chrome/browser/resources/omnibox/omnibox.js b/chrome/browser/resources/omnibox/omnibox.js
index b1b18ea..2190eab2 100644
--- a/chrome/browser/resources/omnibox/omnibox.js
+++ b/chrome/browser/resources/omnibox/omnibox.js
@@ -19,7 +19,8 @@
 
 (function () {
   class BrowserProxy {
-    constructor() {
+    /** @param {!omnibox_output.OmniboxOutput} omniboxOutput */
+    constructor(omniboxOutput) {
       /** @private {!mojom.OmniboxPageCallbackRouter} */
       this.callbackRouter_ = new mojom.OmniboxPageCallbackRouter;
 
@@ -28,7 +29,9 @@
       // match. Response refers to the data returned from the C++
       // AutocompleteController.
       this.callbackRouter_.handleNewAutocompleteResult.addListener(
-          result => omniboxOutput.addAutocompleteResponse(result));
+          omniboxOutput.addAutocompleteResponse.bind(omniboxOutput));
+      this.callbackRouter_.handleAnswerImageData.addListener(
+          omniboxOutput.updateAnswerImage.bind(omniboxOutput));
 
       /** @private {!mojom.OmniboxPageHandlerProxy} */
       this.handler_ = mojom.OmniboxPageHandler.getProxy();
@@ -43,7 +46,7 @@
   }
 
   /** @type {!BrowserProxy} */
-  const browserProxy = new BrowserProxy();
+  let browserProxy;
   /** @type {!OmniboxInput} */
   let omniboxInput;
   /** @type {!omnibox_output.OmniboxOutput} */
@@ -53,6 +56,7 @@
     omniboxInput = /** @type {!OmniboxInput} */ ($('omnibox-input'));
     omniboxOutput =
         /** @type {!omnibox_output.OmniboxOutput} */ ($('omnibox-output'));
+    browserProxy = new BrowserProxy(omniboxOutput);
 
     omniboxInput.addEventListener('query-inputs-changed', event => {
       omniboxOutput.clearAutocompleteResponses();
diff --git a/chrome/browser/resources/omnibox/omnibox_output.js b/chrome/browser/resources/omnibox/omnibox_output.js
index c599c85..1083f300 100644
--- a/chrome/browser/resources/omnibox/omnibox_output.js
+++ b/chrome/browser/resources/omnibox/omnibox_output.js
@@ -65,6 +65,14 @@
     }
 
     /**
+     * @param {string} url
+     * @param {string} data
+     */
+    updateAnswerImage(url, data) {
+      this.matches.forEach(match => match.updateAnswerImage(url, data));
+    }
+
+    /**
      * Show or hide various output elements depending on display inputs.
      * 1) Show non-last result groups only if showIncompleteResults is true.
      * 2) Show the details section above each table if showDetails or
@@ -102,7 +110,7 @@
     get visibleTableText() {
       return this.resultsGroups_
           .flatMap(resultsGroup => resultsGroup.visibleText)
-          .reduce((prev, cur) => `${prev}${cur}\n`, '');
+          .join('\n');
     }
   }
 
@@ -326,8 +334,10 @@
 
     /** @param {!mojom.AutocompleteMatch} match */
     set match(match) {
-      /** @type {!Object} */
+      /** @type {!Object<string, !OutputProperty>} */
       this.properties = {};
+      /** @type {!OutputProperty} */
+      this.properties.contentsAndDescription;
       /** @type {?string} */
       this.providerName = match.providerName || null;
 
@@ -367,6 +377,15 @@
         this.appendChild(this.additionalProperties);
     }
 
+    /**
+     * @param {string} url
+     * @param {string} data
+     */
+    updateAnswerImage(url, data) {
+      if (this.properties.contentsAndDescription.value === url)
+        this.properties.contentsAndDescription.setAnswerImageData(data);
+    }
+
     /** @param {boolean} showDetails */
     updateVisibility(showDetails) {
       // Show certain columns only if they showDetails is true.
@@ -398,7 +417,9 @@
      * needs to be displayed for this match.
      */
     get hasAdditionalProperties() {
-      return Object.keys(this.additionalProperties.value).length > 0;
+      return Object
+                 .keys(/** @type {!Object} */ (this.additionalProperties.value))
+                 .length > 0;
     }
 
     /** @private @return {!Array<!OutputProperty>} */
@@ -446,7 +467,7 @@
   class OutputProperty extends HTMLTableCellElement {
     /**
      * @param {Column} column
-     * @param {!Array<!Object>} values
+     * @param {!Array<*>} values
      * @return {!OutputProperty}
      */
     static create(column, values) {
@@ -457,16 +478,11 @@
       return outputProperty;
     }
 
-    /** @return {!Object} */
-    get value() {
-      return this.value_;
-    }
-
-    /** @param {!Array<!Object>} values */
+    /** @param {!Array<*>} values */
     set values(values) {
-      /** @private {!Object} */
-      this.value_ = values[0];
-      /** @private {!Array<!Object>} */
+      /** @type {*} */
+      this.value = values[0];
+      /** @private {!Array<*>} */
       this.values_ = values;
       /** @override */
       this.render_();
@@ -477,7 +493,7 @@
 
     /** @return {string} */
     get text() {
-      return this.value_ + '';
+      return this.value + '';
     }
   }
 
@@ -538,6 +554,54 @@
     }
   }
 
+  class OutputAnswerProperty extends OutputProperty {
+    constructor() {
+      super();
+
+      /** @private {!Element} */
+      this.container_ = document.createElement('div');
+      this.container_.classList.add('pair-container');
+      this.appendChild(this.container_);
+
+      /** @type {!Element} */
+      this.image_ = document.createElement('img');
+      this.image_.classList.add('pair-item', 'image');
+      this.container_.appendChild(this.image_);
+
+      /** @type {!Element} */
+      this.contents_ = document.createElement('div');
+      this.contents_.classList.add('pair-item', 'contents');
+      this.container_.appendChild(this.contents_);
+
+      /** @type {!Element} */
+      this.description_ = document.createElement('div');
+      this.description_.classList.add('pair-item', 'description');
+      this.container_.appendChild(this.description_);
+
+      /** @type {!Element} */
+      this.answer_ = document.createElement('div');
+      this.answer_.classList.add('pair-item', 'answer');
+      this.container_.appendChild(this.answer_);
+    }
+
+    /** @param {string} imageData */
+    setAnswerImageData(imageData) {
+      this.image_.src = imageData;
+    }
+
+    /** @private @override */
+    render_() {
+      this.contents_.textContent = this.values_[1];
+      this.description_.textContent = this.values_[2];
+      this.answer_.textContent = this.values_[3];
+    }
+
+    /** @override @return {string} */
+    get text() {
+      return this.values_.join('.');
+    }
+  }
+
   class OutputBooleanProperty extends OutputProperty {
     constructor() {
       super();
@@ -554,7 +618,7 @@
     }
 
     get text() {
-      return (this.value_ ? 'is: ' : 'not: ') + this.name;
+      return (this.value ? 'is: ' : 'not: ') + this.name;
     }
   }
 
@@ -579,7 +643,7 @@
 
     /** @override @return {string} */
     get text() {
-      return JSON.stringify(this.value_, null, 2);
+      return JSON.stringify(this.value, null, 2);
     }
 
     /**
@@ -627,23 +691,37 @@
 
     /** @override @return {string} */
     get text() {
-      return this.value_.reduce(
+      return this.value.reduce(
           (prev, {key, value}) => `${prev}${key}: ${value}\n`, '');
     }
   }
 
-  class OutputLinkProperty extends OutputProperty {
+  class OutputUrlProperty extends OutputProperty {
     constructor() {
       super();
+
+      /** @private {!Element} */
+      this.container_ = document.createElement('div');
+      this.container_.classList.add('pair-container');
+      this.appendChild(this.container_);
+
+      /** @private {!Element} */
+      this.icon_ = document.createElement('img');
+      this.container_.appendChild(this.icon_);
+
       /** @private {!Element} */
       this.link_ = document.createElement('a');
-      this.appendChild(this.link_);
+      this.container_.appendChild(this.link_);
     }
 
     /** @private @override */
     render_() {
-      this.link_.textContent = this.value_;
-      this.link_.href = this.value_;
+      if (this.values_[1])
+        this.icon_.removeAttribute('src');
+      else
+        this.icon_.src = `chrome://favicon/${this.value}`;
+      this.link_.textContent = this.value;
+      this.link_.href = this.value;
     }
   }
 
@@ -657,7 +735,7 @@
 
     /** @private @override */
     render_() {
-      this.div_.textContent = this.value_;
+      this.div_.textContent = this.value;
     }
   }
 
@@ -786,10 +864,11 @@
         'The result score. Higher is more relevant.', ['relevance'],
         OutputTextProperty),
     new Column(
-        ['Contents', 'Description'], '', 'contentsAndDescription', true,
+        ['Contents', 'Description', 'Answer'], '', 'contentsAndDescription',
+        true,
         'The text that is presented identifying the result. / The page title ' +
             'of the result.',
-        ['contents', 'description'], OutputPairProperty),
+        ['image', 'contents', 'description', 'answer'], OutputAnswerProperty),
     new Column(
         ['D'], '', 'allowedToBeDefaultMatch', true,
         'Can Be Default\nA green checkmark indicates that the result can be ' +
@@ -808,7 +887,7 @@
         ['hasTabMatch'], OutputBooleanProperty),
     new Column(
         ['URL'], '', 'destinationUrl', true, 'The URL for the result.',
-        ['destinationUrl'], OutputLinkProperty),
+        ['destinationUrl', 'isSearchType'], OutputUrlProperty),
     new Column(
         ['Fill', 'Inline'], '', 'fillAndInline', false,
         'The text shown in the omnibox when the result is selected. / The ' +
@@ -876,6 +955,8 @@
       'output-overlapping-pair-property', OutputOverlappingPairProperty,
       {extends: 'td'});
   customElements.define(
+      'output-answer-property', OutputAnswerProperty, {extends: 'td'});
+  customElements.define(
       'output-boolean-property', OutputBooleanProperty, {extends: 'td'});
   customElements.define(
       'output-json-property', OutputJsonProperty, {extends: 'td'});
@@ -883,7 +964,7 @@
       'output-key-value-tuple-property', OutputKeyValueTuplesProperty,
       {extends: 'td'});
   customElements.define(
-      'output-link-property', OutputLinkProperty, {extends: 'td'});
+      'output-url-property', OutputUrlProperty, {extends: 'td'});
   customElements.define(
       'output-text-property', OutputTextProperty, {extends: 'td'});
 
diff --git a/chrome/browser/resources/print_preview/data/BUILD.gn b/chrome/browser/resources/print_preview/data/BUILD.gn
index a93e13c..7909d63 100644
--- a/chrome/browser/resources/print_preview/data/BUILD.gn
+++ b/chrome/browser/resources/print_preview/data/BUILD.gn
@@ -34,7 +34,6 @@
     "..:native_layer",
     "//ui/webui/resources/js:cr",
     "//ui/webui/resources/js:event_tracker",
-    "//ui/webui/resources/js:webui_listener_tracker",
     "//ui/webui/resources/js/cr:event_target",
   ]
 }
diff --git a/chrome/browser/resources/print_preview/data/destination_store.js b/chrome/browser/resources/print_preview/data/destination_store.js
index aaf4a73..39bb6471 100644
--- a/chrome/browser/resources/print_preview/data/destination_store.js
+++ b/chrome/browser/resources/print_preview/data/destination_store.js
@@ -126,10 +126,10 @@
      * A data store that stores destinations and dispatches events when the
      * data store changes.
      * @param {!print_preview.UserInfo} userInfo User information repository.
-     * @param {!WebUIListenerTracker} listenerTracker Tracker for WebUI
-     *     listeners added in DestinationStore constructor.
+     * @param {function(string, !Function):void} addListenerCallback Function
+     *     to call to add Web UI listeners in DestinationStore constructor.
      */
-    constructor(userInfo, listenerTracker) {
+    constructor(userInfo, addListenerCallback) {
       super();
 
       /**
@@ -279,7 +279,9 @@
 
       this.reset_();
 
-      this.addWebUIEventListeners_(listenerTracker);
+      addListenerCallback('printers-added', this.onPrintersAdded_.bind(this));
+      addListenerCallback(
+          'reload-printer-list', this.onDestinationsReload.bind(this));
     }
 
     /**
@@ -343,19 +345,6 @@
     }
 
     /**
-     * Starts listening for relevant WebUI events and adds the listeners to
-     * |listenerTracker|. |listenerTracker| is responsible for removing the
-     * listeners when necessary.
-     * @param {!WebUIListenerTracker} listenerTracker
-     * @private
-     */
-    addWebUIEventListeners_(listenerTracker) {
-      listenerTracker.add('printers-added', this.onPrintersAdded_.bind(this));
-      listenerTracker.add(
-          'reload-printer-list', this.onDestinationsReload.bind(this));
-    }
-
-    /**
      * @param {(?print_preview.Destination |
      *          ?print_preview.RecentDestination)} destination
      * @return {boolean} Whether the destination is valid.
diff --git a/chrome/browser/resources/print_preview/new/BUILD.gn b/chrome/browser/resources/print_preview/new/BUILD.gn
index f0ded25..8dc6030 100644
--- a/chrome/browser/resources/print_preview/new/BUILD.gn
+++ b/chrome/browser/resources/print_preview/new/BUILD.gn
@@ -76,7 +76,7 @@
     "//ui/webui/resources/cr_elements:cr_container_shadow_behavior",
     "//ui/webui/resources/js:event_tracker",
     "//ui/webui/resources/js:util",
-    "//ui/webui/resources/js:webui_listener_tracker",
+    "//ui/webui/resources/js:web_ui_listener_behavior",
     "//ui/webui/resources/js/cr/ui:focus_outline_manager",
   ]
 }
diff --git a/chrome/browser/resources/print_preview/new/app.html b/chrome/browser/resources/print_preview/new/app.html
index 1141f96..9e47084 100644
--- a/chrome/browser/resources/print_preview/new/app.html
+++ b/chrome/browser/resources/print_preview/new/app.html
@@ -8,7 +8,7 @@
 <link rel="import" href="chrome://resources/html/cr/ui/focus_outline_manager.html">
 <link rel="import" href="chrome://resources/html/event_tracker.html">
 <link rel="import" href="chrome://resources/html/util.html">
-<link rel="import" href="chrome://resources/html/webui_listener_tracker.html">
+<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html">
 <link rel="import" href="../cloud_print_interface.html">
 <link rel="import" href="../cloud_print_interface_manager.html">
diff --git a/chrome/browser/resources/print_preview/new/app.js b/chrome/browser/resources/print_preview/new/app.js
index abe88463..0ee80c6 100644
--- a/chrome/browser/resources/print_preview/new/app.js
+++ b/chrome/browser/resources/print_preview/new/app.js
@@ -14,7 +14,11 @@
 Polymer({
   is: 'print-preview-app',
 
-  behaviors: [SettingsBehavior, CrContainerShadowBehavior],
+  behaviors: [
+    SettingsBehavior,
+    CrContainerShadowBehavior,
+    WebUIListenerBehavior,
+  ],
 
   properties: {
     /**
@@ -142,9 +146,6 @@
     'close': 'onCrDialogClose_',
   },
 
-  /** @private {?WebUIListenerTracker} */
-  listenerTracker_: null,
-
   /** @private {?print_preview.NativeLayer} */
   nativeLayer_: null,
 
@@ -179,14 +180,13 @@
     this.nativeLayer_ = print_preview.NativeLayer.getInstance();
     this.documentInfo_ = new print_preview.DocumentInfo();
     this.userInfo_ = new print_preview.UserInfo();
-    this.listenerTracker_ = new WebUIListenerTracker();
-    this.listenerTracker_.add(
+    this.addWebUIListener(
         'use-cloud-print', this.onCloudPrintEnable_.bind(this));
-    this.listenerTracker_.add('print-failed', this.onPrintFailed_.bind(this));
-    this.listenerTracker_.add(
+    this.addWebUIListener('print-failed', this.onPrintFailed_.bind(this));
+    this.addWebUIListener(
         'print-preset-options', this.onPrintPresetOptions_.bind(this));
     this.destinationStore_ = new print_preview.DestinationStore(
-        this.userInfo_, this.listenerTracker_);
+        this.userInfo_, this.addWebUIListener.bind(this));
     this.invitationStore_ = new print_preview.InvitationStore(this.userInfo_);
     this.tracker_.add(window, 'keydown', this.onKeyDown_.bind(this));
     this.$.previewArea.setPluginKeyEventCallback(this.onKeyDown_.bind(this));
@@ -216,7 +216,6 @@
 
   /** @override */
   detached: function() {
-    this.listenerTracker_.removeAll();
     this.tracker_.removeAll();
   },
 
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.html b/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
index b2894017..9768d00 100644
--- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
+++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
@@ -1,5 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
@@ -14,440 +15,351 @@
 <dom-module id="settings-site-settings-page">
   <template>
     <style include="settings-shared">
-      .settings-box iron-icon + .middle {
-        padding-inline-start: 20px;
+      cr-link-row {
+        --cr-icon-button-margin-start: 20px;
+      }
+
+      /* Add min-height to prevent shifting as sublabels load. */
+      .two-line {
+        --cr-section-min-height: var(--cr-section-two-line-min-height);
       }
     </style>
     <template is="dom-if" if="[[enableSiteSettings_]]">
       <div class="settings-box first line-only">
         <h2 class="first">$i18n{siteSettingsAllSites}</h2>
       </div>
-      <div class="settings-box first line-only" category$="[[ALL_SITES]]"
-          data-route="SITE_SETTINGS_ALL" on-click="onTapNavigate_" actionable>
-        <div class="start">$i18n{siteSettingsAllSitesDescription}</div>
-        <paper-icon-button-light class="subpage-arrow">
-          <button aria-label="$i18n{siteSettingsAllSitesDescription}"></button>
-        </paper-icon-button-light>
-      </div>
+      <cr-link-row
+          data-route="SITE_SETTINGS_ALL"
+          icon-class="subpage-arrow"
+          id="all-sites"
+          label="$i18n{siteSettingsAllSitesDescription}"
+          on-click="onTapNavigate_">
+      </cr-link-row>
       <div class="settings-box first line-only">
         <h2>$i18n{siteSettingsPermissions}</h2>
       </div>
     </template>
-    <div id="cookies" class="settings-box two-line first"
-        category$="[[ContentSettingsTypes.COOKIES]]"
-        data-route="SITE_SETTINGS_COOKIES" on-click="onTapNavigate_" actionable>
-      <iron-icon icon="settings:cookie"></iron-icon>
-      <div class="middle">
-        $i18n{siteSettingsCookies}
-        <div class="secondary" id="cookiesSecondary">
-          [[defaultSettingLabel_(
-              default_.cookies,
-              '$i18nPolymer{siteSettingsCookiesAllowed}',
-              '$i18nPolymer{siteSettingsBlocked}',
-              '$i18nPolymer{deleteDataPostSession}')]]
-        </div>
-      </div>
-      <paper-icon-button-light class="subpage-arrow">
-        <button aria-label="$i18n{siteSettingsCookies}"
-            aria-describedby="cookiesSecondary"></button>
-      </paper-icon-button-light>
-    </div>
-    <div id="location" class="settings-box two-line"
-        category$="[[ContentSettingsTypes.GEOLOCATION]]"
-        data-route="SITE_SETTINGS_LOCATION" on-click="onTapNavigate_"
-        actionable>
-      <iron-icon icon="settings:location-on"></iron-icon>
-      <div class="middle">
-        $i18n{siteSettingsLocation}
-        <div class="secondary" id="locationSecondary">
-          [[defaultSettingLabel_(
-              default_.location,
-              '$i18nPolymer{siteSettingsAskBeforeAccessing}',
-              '$i18nPolymer{siteSettingsBlocked}')]]
-        </div>
-      </div>
-      <paper-icon-button-light class="subpage-arrow">
-        <button aria-label="$i18n{siteSettingsLocation}"
-            aria-describedby="locationSecondary"></button>
-      </paper-icon-button-light>
-    </div>
-    <div id="camera" class="settings-box two-line"
-        category$="[[ContentSettingsTypes.CAMERA]]"
+
+    <cr-link-row
+        class="two-line"
+        data-route="SITE_SETTINGS_COOKIES"
+        icon-class="subpage-arrow"
+        id="cookies"
+        label="$i18n{siteSettingsCookies}"
+        on-click="onTapNavigate_"
+        start-icon="settings:cookie"
+        sub-label="[[defaultSettingLabel_(
+            default_.cookies,
+            '$i18nPolymer{siteSettingsCookiesAllowed}',
+            '$i18nPolymer{siteSettingsBlocked}',
+            '$i18nPolymer{deleteDataPostSession}')]]">
+    </cr-link-row>
+
+    <cr-link-row
+        class="hr two-line"
+        data-route="SITE_SETTINGS_LOCATION"
+        icon-class="subpage-arrow"
+        id="location"
+        label="$i18n{siteSettingsLocation}"
+        on-click="onTapNavigate_"
+        start-icon="settings:location-on"
+        sub-label="[[defaultSettingLabel_(
+            default_.location,
+            '$i18nPolymer{siteSettingsAskBeforeAccessing}',
+            '$i18nPolymer{siteSettingsBlocked}')]]">
+    </cr-link-row>
+
+    <cr-link-row
+        class="hr two-line"
         data-route="SITE_SETTINGS_CAMERA"
-        on-click="onTapNavigate_" actionable>
-      <iron-icon icon="settings:videocam"></iron-icon>
-      <div class="middle">
-        $i18n{siteSettingsCamera}
-        <div class="secondary" id="cameraSecondary">
-          [[defaultSettingLabel_(
-              default_.mediaStreamCamera,
-              '$i18nPolymer{siteSettingsAskBeforeAccessing}',
-              '$i18nPolymer{siteSettingsBlocked}')]]
-        </div>
-      </div>
-      <paper-icon-button-light class="subpage-arrow">
-        <button aria-label="$i18n{siteSettingsCamera}"
-            aria-describedby="cameraSecondary"></button>
-      </paper-icon-button-light>
-    </div>
-    <div id="microphone" class="settings-box two-line"
-        category$="[[ContentSettingsTypes.MIC]]"
-        data-route="SITE_SETTINGS_MICROPHONE" on-click="onTapNavigate_"
-        actionable>
-      <iron-icon icon="settings:mic"></iron-icon>
-      <div class="middle">
-        $i18n{siteSettingsMic}
-        <div class="secondary" id="micSecondary">
-          [[defaultSettingLabel_(
-              default_.mediaStreamMic,
-              '$i18nPolymer{siteSettingsAskBeforeAccessing}',
-              '$i18nPolymer{siteSettingsBlocked}')]]
-        </div>
-      </div>
-      <paper-icon-button-light class="subpage-arrow">
-        <button aria-label="$i18n{siteSettingsMic}"
-            aria-describedby="micSecondary"></button>
-      </paper-icon-button-light>
-    </div>
+        icon-class="subpage-arrow"
+        id="camera"
+        label="$i18n{siteSettingsCamera}"
+        on-click="onTapNavigate_"
+        start-icon="settings:videocam"
+        sub-label="[[defaultSettingLabel_(
+            default_.mediaStreamCamera,
+            '$i18nPolymer{siteSettingsAskBeforeAccessing}',
+            '$i18nPolymer{siteSettingsBlocked}')]]">
+    </cr-link-row>
+
+    <cr-link-row
+        class="hr two-line"
+        data-route="SITE_SETTINGS_MICROPHONE"
+        icon-class="subpage-arrow"
+        id="microphone"
+        label="$i18n{siteSettingsMic}"
+        on-click="onTapNavigate_"
+        start-icon="settings:mic"
+        sub-label="[[defaultSettingLabel_(
+            default_.mediaStreamMic,
+            '$i18nPolymer{siteSettingsAskBeforeAccessing}',
+            '$i18nPolymer{siteSettingsBlocked}')]]">
+    </cr-link-row>
+
     <template is="dom-if" if="[[enableSensorsContentSetting_]]">
-      <div id="sensors" class="settings-box two-line"
-          category$="[[ContentSettingsTypes.SENSORS]]"
-          data-route="SITE_SETTINGS_SENSORS" on-click="onTapNavigate_"
-          actionable>
-        <iron-icon icon="settings:sensors"></iron-icon>
-        <div class="middle">
-          $i18n{siteSettingsSensors}
-          <div class="secondary" id="sensorsSecondary">
-            [[defaultSettingLabel_(
-                default_.sensors,
-                '$i18nPolymer{siteSettingsSensorsAllow}',
-                '$i18nPolymer{siteSettingsSensorsBlock}')]]
-          </div>
-        </div>
-        <button class="subpage-arrow" is="paper-icon-button-light"
-            aria-label="$i18n{siteSettingsSensors}"
-            aria-describedby="sensorsSecondary"></button>
-      </div>
+      <cr-link-row
+          class="hr two-line"
+          data-route="SITE_SETTINGS_SENSORS"
+          icon-class="subpage-arrow"
+          id="sensors"
+          label="$i18n{siteSettingsSensors}"
+          on-click="onTapNavigate_"
+          start-icon="settings:sensors"
+          sub-label="[[defaultSettingLabel_(
+              default_.sensors,
+              '$i18nPolymer{siteSettingsSensorsAllow}',
+              '$i18nPolymer{siteSettingsSensorsBlock}')]]">
+      </cr-link-row>
     </template>
-    <div id="notifications" class="settings-box two-line"
-        category$="[[ContentSettingsTypes.NOTIFICATIONS]]"
-        data-route="SITE_SETTINGS_NOTIFICATIONS" on-click="onTapNavigate_"
-        actionable>
-      <iron-icon icon="settings:notifications"></iron-icon>
-      <div class="middle">
-        $i18n{siteSettingsNotifications}
-        <div class="secondary" id="notificationsSecondary">
-          [[defaultSettingLabel_(
-              default_.notifications,
-              '$i18nPolymer{siteSettingsAskBeforeSending}',
-              '$i18nPolymer{siteSettingsBlocked}')]]
-        </div>
-      </div>
-      <paper-icon-button-light class="subpage-arrow">
-        <button aria-label="$i18n{siteSettingsNotifications}"
-            aria-describedby="notificationsSecondary"></button>
-      </paper-icon-button-light>
-    </div>
-    <div id="javascript" class="settings-box two-line"
-        category$="[[ContentSettingsTypes.JAVASCRIPT]]"
-        data-route="SITE_SETTINGS_JAVASCRIPT" on-click="onTapNavigate_"
-        actionable>
-      <iron-icon icon="settings:code"></iron-icon>
-      <div class="middle">
-        $i18n{siteSettingsJavascript}
-        <div class="secondary" id="javascriptSecondary">
-          [[defaultSettingLabel_(
-              default_.javascript,
-              '$i18nPolymer{siteSettingsAllowed}',
-              '$i18nPolymer{siteSettingsBlocked}')]]
-        </div>
-      </div>
-      <paper-icon-button-light class="subpage-arrow">
-        <button aria-label="$i18n{siteSettingsJavascript}"
-            aria-describedby="javascriptSecondary"></button>
-      </paper-icon-button-light>
-    </div>
-    <div id="flash" class="settings-box two-line"
-        category$="[[ContentSettingsTypes.PLUGINS]]"
-        data-route="SITE_SETTINGS_FLASH" on-click="onTapNavigate_" actionable>
-      <iron-icon icon="cr:extension"></iron-icon>
-      <div class="middle">
-        $i18n{siteSettingsFlash}
-        <div class="secondary" id="flashSecondary">
-          [[defaultSettingLabel_(
-              default_.plugins,
-              '$i18nPolymer{siteSettingsFlashAskFirst}',
-              '$i18nPolymer{siteSettingsFlashBlock}')]]
-        </div>
-      </div>
-      <paper-icon-button-light class="subpage-arrow">
-        <button aria-label="$i18n{siteSettingsFlash}"
-            aria-describedby="flashSecondary"></button>
-      </paper-icon-button-light>
-    </div>
-    <div id="images" class="settings-box two-line"
-        category$="[[ContentSettingsTypes.IMAGES]]"
-        data-route="SITE_SETTINGS_IMAGES" on-click="onTapNavigate_" actionable>
-      <iron-icon icon="settings:photo"></iron-icon>
-      <div class="middle">
-        $i18n{siteSettingsImages}
-        <div class="secondary" id="imagesSecondary">
-          [[defaultSettingLabel_(
-              default_.images,
-              '$i18nPolymer{siteSettingsShowAll}',
-              '$i18nPolymer{siteSettingsDontShowImages}')]]
-        </div>
-      </div>
-      <paper-icon-button-light class="subpage-arrow">
-        <button aria-label="$i18n{siteSettingsImages}"
-            aria-describedby="imagesSecondary"></button>
-      </paper-icon-button-light>
-    </div>
-    <div id="popups" category$="[[ContentSettingsTypes.POPUPS]]"
-        class="settings-box two-line" data-route="SITE_SETTINGS_POPUPS"
-        on-click="onTapNavigate_" actionable>
-      <iron-icon icon="cr:open-in-new"></iron-icon>
-      <div class="middle">
-        $i18n{siteSettingsPopups}
-        <div class="secondary" id="popupsSecondary">
-          [[defaultSettingLabel_(
-              default_.popups,
-              '$i18nPolymer{siteSettingsAllowed}',
-              '$i18nPolymer{siteSettingsBlocked}')]]
-        </div>
-      </div>
-      <paper-icon-button-light class="subpage-arrow">
-        <button aria-label="$i18n{siteSettingsPopups}"
-            aria-describedby="popupsSecondary"></button>
-      </paper-icon-button-light>
-    </div>
+
+    <cr-link-row
+        class="hr two-line"
+        data-route="SITE_SETTINGS_NOTIFICATIONS"
+        icon-class="subpage-arrow"
+        id="notifications"
+        label="$i18n{siteSettingsNotifications}"
+        on-click="onTapNavigate_"
+        start-icon="settings:notifications"
+        sub-label="[[defaultSettingLabel_(
+            default_.notifications,
+            '$i18nPolymer{siteSettingsAskBeforeSending}',
+            '$i18nPolymer{siteSettingsBlocked}')]]">
+    </cr-link-row>
+
+    <cr-link-row
+        class="hr two-line"
+        data-route="SITE_SETTINGS_JAVASCRIPT"
+        icon-class="subpage-arrow"
+        id="javascript"
+        label="$i18n{siteSettingsJavascript}"
+        on-click="onTapNavigate_"
+        start-icon="settings:code"
+        sub-label="[[defaultSettingLabel_(
+            default_.javascript,
+            '$i18nPolymer{siteSettingsAllowed}',
+            '$i18nPolymer{siteSettingsBlocked}')]]">
+    </cr-link-row>
+
+    <cr-link-row
+        class="hr two-line"
+        data-route="SITE_SETTINGS_FLASH"
+        icon-class="subpage-arrow"
+        id="flash"
+        label="$i18n{siteSettingsFlash}"
+        on-click="onTapNavigate_"
+        start-icon="cr:extension"
+        sub-label="[[defaultSettingLabel_(
+            default_.plugins,
+            '$i18nPolymer{siteSettingsFlashAskFirst}',
+            '$i18nPolymer{siteSettingsFlashBlock}')]]">
+    </cr-link-row>
+
+    <cr-link-row
+        class="hr two-line"
+        data-route="SITE_SETTINGS_IMAGES"
+        icon-class="subpage-arrow"
+        id="images"
+        label="$i18n{siteSettingsImages}"
+        on-click="onTapNavigate_"
+        start-icon="settings:photo"
+        sub-label="[[defaultSettingLabel_(
+            default_.images,
+            '$i18nPolymer{siteSettingsShowAll}',
+            '$i18nPolymer{siteSettingsDontShowImages}')]]">
+    </cr-link-row>
+
+    <cr-link-row
+        class="hr two-line"
+        data-route="SITE_SETTINGS_POPUPS"
+        icon-class="subpage-arrow"
+        id="popups"
+        label="$i18n{siteSettingsPopups}"
+        on-click="onTapNavigate_"
+        start-icon="cr:open-in-new"
+        sub-label="[[defaultSettingLabel_(
+            default_.popups,
+            '$i18nPolymer{siteSettingsAllowed}',
+            '$i18nPolymer{siteSettingsBlocked}')]]">
+    </cr-link-row>
+
     <template is="dom-if" if="[[enableSafeBrowsingSubresourceFilter_]]">
-      <div id="ads" class="settings-box two-line"
-          category$="[[ContentSettingsTypes.ADS]]"
-          data-route="SITE_SETTINGS_ADS" on-click="onTapNavigate_"
-          actionable>
-        <iron-icon icon="settings:ads"></iron-icon>
-        <div class="middle">
-          $i18n{siteSettingsAds}
-          <div class="secondary" id="adsSecondary">
-            [[defaultSettingLabel_(
-                default_.ads,
-                '$i18nPolymer{siteSettingsAllowed}',
-                '$i18nPolymer{siteSettingsAdsBlock}')]]
-          </div>
-        </div>
-        <paper-icon-button-light class="subpage-arrow">
-          <button aria-label="$i18n{siteSettingsAds}"
-              aria-describedby="adsSecondary"></button>
-        </paper-icon-button-light>
-      </div>
+      <cr-link-row
+          class="hr two-line"
+          data-route="SITE_SETTINGS_ADS"
+          icon-class="subpage-arrow"
+          id="ads"
+          label="$i18n{siteSettingsAds}"
+          on-click="onTapNavigate_"
+          start-icon="settings:ads"
+          sub-label="[[defaultSettingLabel_(
+              default_.ads,
+              '$i18nPolymer{siteSettingsAllowed}',
+              '$i18nPolymer{siteSettingsAdsBlock}')]]">
+      </cr-link-row>
     </template>
-    <div id="background-sync" class="settings-box two-line"
-        category$="[[ContentSettingsTypes.BACKGROUND_SYNC]]"
-        data-route="SITE_SETTINGS_BACKGROUND_SYNC" on-click="onTapNavigate_"
-        actionable>
-      <iron-icon icon="cr:sync"></iron-icon>
-      <div class="middle">
-        $i18n{siteSettingsBackgroundSync}
-        <div class="secondary" id="backgroundSyncSecondary">
-          [[defaultSettingLabel_(
-              default_.backgroundSync,
-              '$i18nPolymer{siteSettingsAllowRecentlyClosedSites}',
-              '$i18nPolymer{siteSettingsBackgroundSyncBlocked}')]]
-        </div>
-      </div>
-      <paper-icon-button-light class="subpage-arrow">
-        <button aria-label="$i18n{siteSettingsBackgroundSync}"
-            aria-describedby="backgroundSyncSecondary"></button>
-      </paper-icon-button-light>
-    </div>
+
+    <cr-link-row
+        class="hr two-line"
+        data-route="SITE_SETTINGS_BACKGROUND_SYNC"
+        icon-class="subpage-arrow"
+        id="background-sync"
+        label="$i18n{siteSettingsBackgroundSync}"
+        on-click="onTapNavigate_"
+        start-icon="cr:sync"
+        sub-label="[[defaultSettingLabel_(
+            default_.backgroundSync,
+            '$i18nPolymer{siteSettingsAllowRecentlyClosedSites}',
+            '$i18nPolymer{siteSettingsBackgroundSyncBlocked}')]]">
+    </cr-link-row>
+
     <template is="dom-if" if="[[enableSoundContentSetting_]]">
-      <div id="sound" class="settings-box two-line"
-          category$="[[ContentSettingsTypes.SOUND]]"
-          data-route="SITE_SETTINGS_SOUND" on-click="onTapNavigate_"
-          actionable>
-          <iron-icon icon="settings:volume-up"></iron-icon>
-        <div class="middle">
-          $i18n{siteSettingsSound}
-          <div class="secondary" id="soundSecondary">
-            [[defaultSettingLabel_(
-                default_.sound,
-                '$i18nPolymer{siteSettingsSoundAllow}',
-                '$i18nPolymer{siteSettingsSoundBlock}')]]
-          </div>
-        </div>
-        <paper-icon-button-light class="subpage-arrow">
-          <button aria-label="$i18n{siteSettingsSound}"
-              aria-describedby="soundSecondary"></button>
-        </paper-icon-button-light>
-      </div>
+      <cr-link-row
+          class="hr two-line"
+          data-route="SITE_SETTINGS_SOUND"
+          icon-class="subpage-arrow"
+          id="sound"
+          label="$i18n{siteSettingsSound}"
+          on-click="onTapNavigate_"
+          start-icon="settings:volume-up"
+          sub-label="[[defaultSettingLabel_(
+              default_.sound,
+              '$i18nPolymer{siteSettingsSoundAllow}',
+              '$i18nPolymer{siteSettingsSoundBlock}')]]">
+      </cr-link-row>
     </template>
-    <div id="automatic-downloads" class="settings-box two-line"
-        category$="[[ContentSettingsTypes.AUTOMATIC_DOWNLOADS]]"
+
+    <cr-link-row
+        class="hr two-line"
         data-route="SITE_SETTINGS_AUTOMATIC_DOWNLOADS"
-        on-click="onTapNavigate_" actionable>
-      <iron-icon icon="cr:file-download"></iron-icon>
-      <div class="middle">
-        $i18n{siteSettingsAutomaticDownloads}
-        <div class="secondary" id="automaticDownloadsSecondary">
-          [[defaultSettingLabel_(
-              default_.multipleAutomaticDownloads,
-              '$i18nPolymer{siteSettingsAutoDownloadAsk}',
-              '$i18nPolymer{siteSettingsAutoDownloadBlock}')]]
-        </div>
-      </div>
-      <paper-icon-button-light class="subpage-arrow">
-        <button aria-label="$i18n{siteSettingsAutomaticDownloads}"
-            aria-describedby="automaticDownloadsSecondary"></button>
-      </paper-icon-button-light>
-    </div>
-    <div id="unsandboxed-plugins" class="settings-box two-line"
-        category$="[[ContentSettingsTypes.UNSANDBOXED_PLUGINS]]"
+        icon-class="subpage-arrow"
+        id="automatic-downloads"
+        label="$i18n{siteSettingsAutomaticDownloads}"
+        on-click="onTapNavigate_"
+        start-icon="cr:file-download"
+        sub-label="[[defaultSettingLabel_(
+            default_.multipleAutomaticDownloads,
+            '$i18nPolymer{siteSettingsAutoDownloadAsk}',
+            '$i18nPolymer{siteSettingsAutoDownloadBlock}')]]">
+    </cr-link-row>
+
+    <cr-link-row
+        class="hr two-line"
         data-route="SITE_SETTINGS_UNSANDBOXED_PLUGINS"
-        on-click="onTapNavigate_" actionable>
-      <iron-icon icon="cr:extension"></iron-icon>
-      <div class="middle">
-        $i18n{siteSettingsUnsandboxedPlugins}
-        <div class="secondary" id="unsandboxedPluginsSecondary">
-          [[defaultSettingLabel_(
-              default_.ppapiBroker,
-              '$i18nPolymer{siteSettingsUnsandboxedPluginsAsk}',
-              '$i18nPolymer{siteSettingsUnsandboxedPluginsBlock}')]]
-        </div>
-      </div>
-      <paper-icon-button-light class="subpage-arrow">
-        <button aria-label="$i18n{siteSettingsUnsandboxedPlugins}"
-            aria-describedby="unsandboxedPluginsSecondary"></button>
-      </paper-icon-button-light>
-    </div>
+        icon-class="subpage-arrow"
+        id="unsandboxed-plugins"
+        label="$i18n{siteSettingsUnsandboxedPlugins}"
+        on-click="onTapNavigate_"
+        start-icon="cr:extension"
+        sub-label="[[defaultSettingLabel_(
+            default_.ppapiBroker,
+            '$i18nPolymer{siteSettingsUnsandboxedPluginsAsk}',
+            '$i18nPolymer{siteSettingsUnsandboxedPluginsBlock}')]]">
+    </cr-link-row>
+
     <template is="dom-if" if="[[!isGuest_]]">
-      <div id="protocol-handlers" class="settings-box two-line"
-          category$="[[ContentSettingsTypes.PROTOCOL_HANDLERS]]"
+      <cr-link-row
+          class="hr two-line"
           data-route="SITE_SETTINGS_HANDLERS"
-          on-click="onTapNavigate_" actionable>
-        <iron-icon icon="settings:protocol-handler"></iron-icon>
-        <div class="middle">
-          $i18n{siteSettingsHandlers}
-          <div class="secondary" id="handlersSecondary">
-            [[defaultSettingLabel_(
-                default_.registerProtocolHandler,
-                '$i18nPolymer{siteSettingsHandlersAsk}',
-                '$i18nPolymer{siteSettingsHandlersBlocked}')]]
-          </div>
-        </div>
-        <paper-icon-button-light class="subpage-arrow">
-          <button aria-label="$i18n{siteSettingsHandlers}"
-              aria-describedby="handlersSecondary"></button>
-        </paper-icon-button-light>
-      </div>
+          icon-class="subpage-arrow"
+          id="protocol-handlers"
+          label="$i18n{siteSettingsHandlers}"
+          on-click="onTapNavigate_"
+          start-icon="settings:protocol-handler"
+          sub-label="[[defaultSettingLabel_(
+              default_.registerProtocolHandler,
+              '$i18nPolymer{siteSettingsHandlersAsk}',
+              '$i18nPolymer{siteSettingsHandlersBlocked}')]]">
+      </cr-link-row>
     </template>
-    <div id="midi-devices" class="settings-box two-line"
-        category$="[[ContentSettingsTypes.MIDI_DEVICES]]"
+
+    <cr-link-row
+        class="hr two-line"
         data-route="SITE_SETTINGS_MIDI_DEVICES"
-        on-click="onTapNavigate_" actionable>
-      <iron-icon icon="settings:midi"></iron-icon>
-      <div class="middle">
-        $i18n{siteSettingsMidiDevices}
-        <div class="secondary" id="midiDevicesSecondary">
-          [[defaultSettingLabel_(
-              default_.midiSysex,
-              '$i18nPolymer{siteSettingsMidiDevicesAsk}',
-              '$i18nPolymer{siteSettingsMidiDevicesBlock}')]]
-        </div>
-      </div>
-      <paper-icon-button-light class="subpage-arrow">
-        <button aria-label="$i18n{siteSettingsMidiDevices}"
-            aria-describedby="midiDevicesSecondary"></button>
-      </paper-icon-button-light>
-    </div>
-    <div id="zoom-levels" class="settings-box"
-        category$="[[ContentSettingsTypes.ZOOM_LEVELS]]"
-        data-route="SITE_SETTINGS_ZOOM_LEVELS" on-click="onTapNavigate_"
-        actionable>
-      <iron-icon icon="settings:zoom-in"></iron-icon>
-      <div class="middle">$i18n{siteSettingsZoomLevels}</div>
-      <paper-icon-button-light class="subpage-arrow">
-        <button aria-label="$i18n{siteSettingsZoomLevels}"></button>
-      </paper-icon-button-light>
-    </div>
-    <div id="usb-devices" class="settings-box two-line"
-        category$="[[ContentSettingsTypes.USB_DEVICES]]"
-        data-route="SITE_SETTINGS_USB_DEVICES" on-click="onTapNavigate_"
-        actionable>
-      <iron-icon icon="settings:usb"></iron-icon>
-      <div class="middle">
-        $i18n{siteSettingsUsbDevices}
-        <div class="secondary" id="usbDevicesSecondary">
-          [[defaultSettingLabel_(
-              default_.usbDevices,
-              '$i18nPolymer{siteSettingsUsbDevicesAsk}',
-              '$i18nPolymer{siteSettingsUsbDevicesBlock}')]]
-        </div>
-      </div>
-      <paper-icon-button-light class="subpage-arrow">
-        <button aria-label="$i18n{siteSettingsUsbDevices}"
-            area-describedby="usbDevicesSecondary"></button>
-      </paper-icon-button-light>
-    </div>
-    <div id="pdf-documents" class="settings-box"
-        data-route="SITE_SETTINGS_PDF_DOCUMENTS" on-click="onTapNavigate_"
-        actionable>
-      <iron-icon icon="settings:pdf"></iron-icon>
-      <div class="middle">$i18n{siteSettingsPdfDocuments}</div>
-      <paper-icon-button-light class="subpage-arrow">
-        <button aria-label="$i18n{siteSettingsPdfDocuments}"></button>
-      </paper-icon-button-light>
-    </div>
-    <div id="protected-content" class="settings-box"
-        data-route="SITE_SETTINGS_PROTECTED_CONTENT" on-click="onTapNavigate_"
-        actionable>
-      <iron-icon icon="settings:protected-content"></iron-icon>
-      <div class="middle">$i18n{siteSettingsProtectedContent}</div>
-      <paper-icon-button-light class="subpage-arrow">
-        <button aria-label="$i18n{siteSettingsProtectedContent}"></button>
-      </paper-icon-button-light>
-    </div>
+        icon-class="subpage-arrow"
+        id="midi-devices"
+        label="$i18n{siteSettingsMidiDevices}"
+        on-click="onTapNavigate_"
+        start-icon="settings:midi"
+        sub-label="[[defaultSettingLabel_(
+            default_.midiSysex,
+            '$i18nPolymer{siteSettingsMidiDevicesAsk}',
+            '$i18nPolymer{siteSettingsMidiDevicesBlock}')]]">
+    </cr-link-row>
+
+    <cr-link-row
+        class="hr"
+        data-route="SITE_SETTINGS_ZOOM_LEVELS"
+        icon-class="subpage-arrow"
+        id="zoom-levels"
+        label="$i18n{siteSettingsZoomLevels}"
+        on-click="onTapNavigate_"
+        start-icon="settings:zoom-in">
+    </cr-link-row>
+
+    <cr-link-row
+        class="hr two-line"
+        data-route="SITE_SETTINGS_USB_DEVICES"
+        icon-class="subpage-arrow"
+        id="usb-devices"
+        label="$i18n{siteSettingsUsbDevices}"
+        on-click="onTapNavigate_"
+        start-icon="settings:usb"
+        sub-label="[[defaultSettingLabel_(
+            default_.usbDevices,
+            '$i18nPolymer{siteSettingsUsbDevicesAsk}',
+            '$i18nPolymer{siteSettingsUsbDevicesBlock}')]]">
+    </cr-link-row>
+
+    <cr-link-row
+        class="hr"
+        data-route="SITE_SETTINGS_PDF_DOCUMENTS"
+        icon-class="subpage-arrow"
+        id="pdf-documents"
+        label="$i18n{siteSettingsPdfDocuments}"
+        on-click="onTapNavigate_"
+        start-icon="settings:pdf">
+    </cr-link-row>
+
+    <cr-link-row
+        class="hr"
+        data-route="SITE_SETTINGS_PROTECTED_CONTENT"
+        icon-class="subpage-arrow"
+        id="protected-content"
+        label="$i18n{siteSettingsProtectedContent}"
+        on-click="onTapNavigate_"
+        start-icon="settings:protected-content">
+    </cr-link-row>
+
     <template is="dom-if" if="[[enableClipboardContentSetting_]]">
-      <div id="clipboard" class="settings-box two-line"
-          category$="[[ContentSettingsTypes.CLIPBOARD]]"
-          data-route="SITE_SETTINGS_CLIPBOARD" on-click="onTapNavigate_"
-          actionable>
-        <iron-icon icon="settings:clipboard"></iron-icon>
-        <div class="middle">
-          $i18n{siteSettingsClipboard}
-          <div class="secondary" id="clipboardSecondary">
-            [[defaultSettingLabel_(
-                default_.clipboard,
-                '$i18nPolymer{siteSettingsAskBeforeAccessing}',
-                '$i18nPolymer{siteSettingsBlocked}')]]
-          </div>
-        </div>
-        <paper-icon-button-light class="subpage-arrow">
-          <button aria-label="$i18n{siteSettingsClipboard}"
-              aria-describedby="clipboardSecondary"></button>
-        </paper-icon-button-light>
-      </div>
+      <cr-link-row
+          class="hr two-line"
+          data-route="SITE_SETTINGS_CLIPBOARD"
+          icon-class="subpage-arrow"
+          id="clipboard"
+          label="$i18n{siteSettingsClipboard}"
+          on-click="onTapNavigate_"
+          start-icon="settings:clipboard"
+          sub-label="[[defaultSettingLabel_(
+              default_.clipboard,
+              '$i18nPolymer{siteSettingsAskBeforeAccessing}',
+              '$i18nPolymer{siteSettingsBlocked}')]]">
+      </cr-link-row>
     </template>
     <template is="dom-if" if="[[enablePaymentHandlerContentSetting_]]">
-      <div id="paymentHandler" class="settings-box two-line"
-           category$="[[ContentSettingsTypes.PAYMENT_HANDLER]]"
-           data-route="SITE_SETTINGS_PAYMENT_HANDLER" on-click="onTapNavigate_"
-           actionable>
-        <iron-icon icon="settings:payment-handler"></iron-icon>
-        <div class="middle">
-          $i18n{siteSettingsPaymentHandler}
-          <div class="secondary" id="paymentHandlerSecondary">
-            [[defaultSettingLabel_(
-                default_.paymentHandler,
-                '$i18nPolymer{siteSettingsPaymentHandlerAllow}',
-                '$i18nPolymer{siteSettingsPaymentHandlerBlock}')]]
-          </div>
-        </div>
-        <paper-icon-button-light class="subpage-arrow">
-          <button aria-label="$i18n{siteSettingsPaymentHandler}"
-              aria-describedby="paymentHandlerSecondary"></button>
-        </paper-icon-button-light>
-      </div>
+      <cr-link-row
+          class="hr two-line"
+          data-route="SITE_SETTINGS_PAYMENT_HANDLER"
+          icon-class="subpage-arrow"
+          id="paymentHandler"
+          label="$i18n{siteSettingsClipboard}"
+          on-click="onTapNavigate_"
+          start-icon="settings:payment-handler"
+          sub-label="[[defaultSettingLabel_(
+              default_.paymentHandler,
+              '$i18nPolymer{siteSettingsPaymentHandlerAllow}',
+              '$i18nPolymer{siteSettingsPaymentHandlerBlock}')]]">
+      </cr-link-row>
     </template>
   </template>
   <script src="site_settings_page.js"></script>
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.js b/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
index c6213ed..c589b22 100644
--- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
+++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
@@ -137,7 +137,7 @@
       const route = pair[0];
       const id = pair[1];
       this.focusConfig.set(route.path, () => this.async(() => {
-        cr.ui.focusWithoutInk(assert(this.$$(`#${id} .subpage-arrow button`)));
+        cr.ui.focusWithoutInk(assert(this.$$(`#${id}`)));
       }));
     });
   },
diff --git a/chrome/browser/signin/force_signin_verifier.cc b/chrome/browser/signin/force_signin_verifier.cc
index 6ad8616..5cc5185 100644
--- a/chrome/browser/signin/force_signin_verifier.cc
+++ b/chrome/browser/signin/force_signin_verifier.cc
@@ -9,11 +9,12 @@
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
-#include "components/signin/core/browser/signin_manager.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "content/public/browser/network_service_instance.h"
 #include "google_apis/gaia/gaia_constants.h"
+#include "services/identity/public/cpp/identity_manager.h"
+#include "services/identity/public/cpp/primary_account_access_token_fetcher.h"
+#include "services/identity/public/cpp/primary_account_mutator.h"
 
 namespace {
 const net::BackoffEntry::Policy kForceSigninVerifierBackoffPolicy = {
@@ -37,13 +38,10 @@
     "Signin.ForceSigninVerificationTime.Failure";
 
 ForceSigninVerifier::ForceSigninVerifier(Profile* profile)
-    : OAuth2TokenService::Consumer("force_signin_verifier"),
-      has_token_verified_(false),
+    : has_token_verified_(false),
       backoff_entry_(&kForceSigninVerifierBackoffPolicy),
       creation_time_(base::TimeTicks::Now()),
-      oauth2_token_service_(
-          ProfileOAuth2TokenServiceFactory::GetForProfile(profile)),
-      signin_manager_(SigninManagerFactory::GetForProfile(profile)) {
+      identity_manager_(IdentityManagerFactory::GetForProfile(profile)) {
   content::GetNetworkConnectionTracker()->AddNetworkConnectionObserver(this);
   UMA_HISTOGRAM_BOOLEAN(kForceSigninVerificationMetricsName,
                         ShouldSendRequest());
@@ -54,9 +52,29 @@
   Cancel();
 }
 
-void ForceSigninVerifier::OnGetTokenSuccess(
-    const OAuth2TokenService::Request* request,
-    const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
+void ForceSigninVerifier::OnAccessTokenFetchComplete(
+    GoogleServiceAuthError error,
+    identity::AccessTokenInfo token_info) {
+  if (error.state() != GoogleServiceAuthError::NONE) {
+    if (error.IsPersistentError()) {
+      UMA_HISTOGRAM_MEDIUM_TIMES(kForceSigninVerificationFailureTimeMetricsName,
+                                 base::TimeTicks::Now() - creation_time_);
+      has_token_verified_ = true;
+      CloseAllBrowserWindows();
+      content::GetNetworkConnectionTracker()->RemoveNetworkConnectionObserver(
+          this);
+      Cancel();
+    } else {
+      backoff_entry_.InformOfRequest(false);
+      backoff_request_timer_.Start(
+          FROM_HERE, backoff_entry_.GetTimeUntilRelease(),
+          base::BindOnce(&ForceSigninVerifier::SendRequest,
+                         base::Unretained(this)));
+      access_token_fetcher_.reset();
+    }
+    return;
+  }
+
   UMA_HISTOGRAM_MEDIUM_TIMES(kForceSigninVerificationSuccessTimeMetricsName,
                              base::TimeTicks::Now() - creation_time_);
   has_token_verified_ = true;
@@ -64,26 +82,6 @@
   Cancel();
 }
 
-void ForceSigninVerifier::OnGetTokenFailure(
-    const OAuth2TokenService::Request* request,
-    const GoogleServiceAuthError& error) {
-  if (error.IsPersistentError()) {
-    UMA_HISTOGRAM_MEDIUM_TIMES(kForceSigninVerificationFailureTimeMetricsName,
-                               base::TimeTicks::Now() - creation_time_);
-    has_token_verified_ = true;
-    CloseAllBrowserWindows();
-    content::GetNetworkConnectionTracker()->RemoveNetworkConnectionObserver(
-        this);
-    Cancel();
-  } else {
-    backoff_entry_.InformOfRequest(false);
-    backoff_request_timer_.Start(
-        FROM_HERE, backoff_entry_.GetTimeUntilRelease(),
-        base::Bind(&ForceSigninVerifier::SendRequest, base::Unretained(this)));
-    access_token_request_.reset();
-  }
-}
-
 void ForceSigninVerifier::OnConnectionChanged(
     network::mojom::ConnectionType type) {
   // Try again immediately once the network is back and cancel any pending
@@ -98,7 +96,7 @@
 void ForceSigninVerifier::Cancel() {
   backoff_entry_.Reset();
   backoff_request_timer_.Stop();
-  access_token_request_.reset();
+  access_token_fetcher_.reset();
   content::GetNetworkConnectionTracker()->RemoveNetworkConnectionObserver(this);
 }
 
@@ -123,30 +121,39 @@
     return;
   }
 
-  std::string account_id = signin_manager_->GetAuthenticatedAccountId();
-  OAuth2TokenService::ScopeSet oauth2_scopes;
+  identity::ScopeSet oauth2_scopes;
   oauth2_scopes.insert(GaiaConstants::kChromeSyncOAuth2Scope);
-  access_token_request_ =
-      oauth2_token_service_->StartRequest(account_id, oauth2_scopes, this);
+  // It is safe to use Unretained(this) here given that the callback
+  // will not be invoked if this object is deleted.
+  access_token_fetcher_ =
+      std::make_unique<identity::PrimaryAccountAccessTokenFetcher>(
+          "force_signin_verifier", identity_manager_, oauth2_scopes,
+          base::BindOnce(&ForceSigninVerifier::OnAccessTokenFetchComplete,
+                         base::Unretained(this)),
+          identity::PrimaryAccountAccessTokenFetcher::Mode::kImmediate);
 }
 
 bool ForceSigninVerifier::ShouldSendRequest() {
-  return !has_token_verified_ && access_token_request_.get() == nullptr &&
-         signin_manager_->IsAuthenticated();
+  return !has_token_verified_ && access_token_fetcher_.get() == nullptr &&
+         identity_manager_->HasPrimaryAccount();
 }
 
 void ForceSigninVerifier::CloseAllBrowserWindows() {
   // Do not close window if there is ongoing reauth. If it fails later, the
   // signin process should take care of the signout.
-  if (signin_manager_->AuthInProgress())
+  auto* primary_account_mutator = identity_manager_->GetPrimaryAccountMutator();
+  if (!primary_account_mutator ||
+      primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress())
     return;
-  signin_manager_->SignOutAndRemoveAllAccounts(
+  primary_account_mutator->ClearPrimaryAccount(
+      identity::PrimaryAccountMutator::ClearAccountsAction::kRemoveAll,
       signin_metrics::AUTHENTICATION_FAILED_WITH_FORCE_SIGNIN,
       signin_metrics::SignoutDelete::IGNORE_METRIC);
 }
 
-OAuth2TokenService::Request* ForceSigninVerifier::GetRequestForTesting() {
-  return access_token_request_.get();
+identity::PrimaryAccountAccessTokenFetcher*
+ForceSigninVerifier::GetAccessTokenFetcherForTesting() {
+  return access_token_fetcher_.get();
 }
 
 net::BackoffEntry* ForceSigninVerifier::GetBackoffEntryForTesting() {
diff --git a/chrome/browser/signin/force_signin_verifier.h b/chrome/browser/signin/force_signin_verifier.h
index 1df2c5e1..74633ebc 100644
--- a/chrome/browser/signin/force_signin_verifier.h
+++ b/chrome/browser/signin/force_signin_verifier.h
@@ -11,12 +11,17 @@
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
-#include "google_apis/gaia/oauth2_token_service.h"
+#include "google_apis/gaia/google_service_auth_error.h"
 #include "net/base/backoff_entry.h"
 #include "services/network/public/cpp/network_connection_tracker.h"
 
 class Profile;
-class SigninManager;
+
+namespace identity {
+class IdentityManager;
+class PrimaryAccountAccessTokenFetcher;
+struct AccessTokenInfo;
+}  // namespace identity
 
 extern const char kForceSigninVerificationMetricsName[];
 extern const char kForceSigninVerificationSuccessTimeMetricsName[];
@@ -26,18 +31,13 @@
 // into memory by the first time via gaia server. It will retry on any transient
 // error.
 class ForceSigninVerifier
-    : public OAuth2TokenService::Consumer,
-      public network::NetworkConnectionTracker::NetworkConnectionObserver {
+    : public network::NetworkConnectionTracker::NetworkConnectionObserver {
  public:
   explicit ForceSigninVerifier(Profile* profile);
   ~ForceSigninVerifier() override;
 
-  // override OAuth2TokenService::Consumer
-  void OnGetTokenSuccess(
-      const OAuth2TokenService::Request* request,
-      const OAuth2AccessTokenConsumer::TokenResponse& token_response) override;
-  void OnGetTokenFailure(const OAuth2TokenService::Request* request,
-                         const GoogleServiceAuthError& error) override;
+  void OnAccessTokenFetchComplete(GoogleServiceAuthError error,
+                                  identity::AccessTokenInfo token_info);
 
   // override network::NetworkConnectionTracker::NetworkConnectionObserver
   void OnConnectionChanged(network::mojom::ConnectionType type) override;
@@ -66,12 +66,13 @@
 
   virtual void CloseAllBrowserWindows();
 
-  OAuth2TokenService::Request* GetRequestForTesting();
+  identity::PrimaryAccountAccessTokenFetcher* GetAccessTokenFetcherForTesting();
   net::BackoffEntry* GetBackoffEntryForTesting();
   base::OneShotTimer* GetOneShotTimerForTesting();
 
  private:
-  std::unique_ptr<OAuth2TokenService::Request> access_token_request_;
+  std::unique_ptr<identity::PrimaryAccountAccessTokenFetcher>
+      access_token_fetcher_;
 
   // Indicates whether the verification is finished successfully or with a
   // persistent error.
@@ -80,8 +81,7 @@
   base::OneShotTimer backoff_request_timer_;
   base::TimeTicks creation_time_;
 
-  OAuth2TokenService* oauth2_token_service_;
-  SigninManager* signin_manager_;
+  identity::IdentityManager* identity_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(ForceSigninVerifier);
 };
diff --git a/chrome/browser/signin/force_signin_verifier_unittest.cc b/chrome/browser/signin/force_signin_verifier_unittest.cc
index a74e8610..26c249e9 100644
--- a/chrome/browser/signin/force_signin_verifier_unittest.cc
+++ b/chrome/browser/signin/force_signin_verifier_unittest.cc
@@ -7,14 +7,11 @@
 #include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h"
-#include "chrome/browser/signin/fake_signin_manager_builder.h"
-#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
 #include "chrome/test/base/testing_profile.h"
-#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "services/identity/public/cpp/identity_test_environment.h"
 #include "services/network/test/test_network_connection_tracker.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -28,7 +25,9 @@
 
   int FailureCount() { return GetBackoffEntryForTesting()->failure_count(); }
 
-  OAuth2TokenService::Request* request() { return GetRequestForTesting(); }
+  identity::PrimaryAccountAccessTokenFetcher* access_token_fetcher() {
+    return GetAccessTokenFetcherForTesting();
+  }
 
   MOCK_METHOD0(CloseAllBrowserWindows, void(void));
 };
@@ -38,20 +37,12 @@
       public network::NetworkConnectionTracker::NetworkConnectionObserver {
  public:
   void SetUp() override {
-    TestingProfile::Builder builder;
-    builder.AddTestingFactory(
-        SigninManagerFactory::GetInstance(),
-        base::BindRepeating(&BuildFakeSigninManagerForTesting));
-    builder.AddTestingFactory(
-        ProfileOAuth2TokenServiceFactory::GetInstance(),
-        base::BindRepeating(&BuildFakeProfileOAuth2TokenService));
-    profile_ = builder.Build();
-    FakeSigninManager* signin_manager_ = static_cast<FakeSigninManager*>(
-        SigninManagerFactory::GetForProfile(profile_.get()));
-    signin_manager_->SignIn("fake_id", "fake_username", "fake_password");
-
-    oauth2_token_service()->GetDelegate()->UpdateCredentials("fake_id",
-                                                             "fake_token");
+    profile_ = IdentityTestEnvironmentProfileAdaptor::
+        CreateProfileForIdentityTestEnvironment();
+    identity_test_env_profile_adaptor_ =
+        std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile_.get());
+    account_info_ =
+        identity_test_env()->MakePrimaryAccountAvailable("email@test.com");
   }
 
   void TearDown() override { verifier_.reset(); }
@@ -60,14 +51,17 @@
     wait_for_network_type_change_.QuitWhenIdle();
   }
 
-  FakeProfileOAuth2TokenService* oauth2_token_service() {
-    return static_cast<FakeProfileOAuth2TokenService*>(
-        ProfileOAuth2TokenServiceFactory::GetForProfile(profile_.get()));
+  identity::IdentityTestEnvironment* identity_test_env() {
+    return identity_test_env_profile_adaptor_->identity_test_env();
   }
 
   std::unique_ptr<MockForceSigninVerifier> verifier_;
   content::TestBrowserThreadBundle bundle_;
   std::unique_ptr<TestingProfile> profile_;
+  std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
+      identity_test_env_profile_adaptor_;
+
+  AccountInfo account_info_;
 
   base::RunLoop wait_for_network_type_change_;
   base::RunLoop wait_for_network_type_async_return_;
@@ -83,17 +77,15 @@
 TEST_F(ForceSigninVerifierTest, OnGetTokenSuccess) {
   verifier_ = std::make_unique<MockForceSigninVerifier>(profile_.get());
 
-  ASSERT_NE(nullptr, verifier_->request());
+  ASSERT_NE(nullptr, verifier_->access_token_fetcher());
   ASSERT_FALSE(verifier_->HasTokenBeenVerified());
   ASSERT_FALSE(verifier_->IsDelayTaskPosted());
   EXPECT_CALL(*verifier_.get(), CloseAllBrowserWindows()).Times(0);
 
-  FakeProfileOAuth2TokenService* service = oauth2_token_service();
-  ASSERT_EQ(1u, service->GetPendingRequests().size());
-  service->IssueAllTokensForAccount("fake_id",
-                                    OAuth2AccessTokenConsumer::TokenResponse());
+  identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+      account_info_.account_id, /*token=*/"", base::Time());
 
-  ASSERT_EQ(nullptr, verifier_->request());
+  ASSERT_EQ(nullptr, verifier_->access_token_fetcher());
   ASSERT_TRUE(verifier_->HasTokenBeenVerified());
   ASSERT_FALSE(verifier_->IsDelayTaskPosted());
   ASSERT_EQ(0, verifier_->FailureCount());
@@ -108,16 +100,15 @@
 TEST_F(ForceSigninVerifierTest, OnGetTokenPersistentFailure) {
   verifier_ = std::make_unique<MockForceSigninVerifier>(profile_.get());
 
-  ASSERT_NE(nullptr, verifier_->request());
+  ASSERT_NE(nullptr, verifier_->access_token_fetcher());
   ASSERT_FALSE(verifier_->HasTokenBeenVerified());
   ASSERT_FALSE(verifier_->IsDelayTaskPosted());
   EXPECT_CALL(*verifier_.get(), CloseAllBrowserWindows()).Times(1);
 
-  FakeProfileOAuth2TokenService* service = oauth2_token_service();
-  ASSERT_EQ(1u, service->GetPendingRequests().size());
-  service->IssueErrorForAllPendingRequests(persistent_error_);
+  identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
+      persistent_error_);
 
-  ASSERT_EQ(nullptr, verifier_->request());
+  ASSERT_EQ(nullptr, verifier_->access_token_fetcher());
   ASSERT_TRUE(verifier_->HasTokenBeenVerified());
   ASSERT_FALSE(verifier_->IsDelayTaskPosted());
   ASSERT_EQ(0, verifier_->FailureCount());
@@ -132,16 +123,15 @@
 TEST_F(ForceSigninVerifierTest, OnGetTokenTransientFailure) {
   verifier_ = std::make_unique<MockForceSigninVerifier>(profile_.get());
 
-  ASSERT_NE(nullptr, verifier_->request());
+  ASSERT_NE(nullptr, verifier_->access_token_fetcher());
   ASSERT_FALSE(verifier_->HasTokenBeenVerified());
   ASSERT_FALSE(verifier_->IsDelayTaskPosted());
   EXPECT_CALL(*verifier_.get(), CloseAllBrowserWindows()).Times(0);
 
-  FakeProfileOAuth2TokenService* service = oauth2_token_service();
-  ASSERT_EQ(1u, service->GetPendingRequests().size());
-  service->IssueErrorForAllPendingRequests(transient_error_);
+  identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
+      transient_error_);
 
-  ASSERT_EQ(nullptr, verifier_->request());
+  ASSERT_EQ(nullptr, verifier_->access_token_fetcher());
   ASSERT_FALSE(verifier_->HasTokenBeenVerified());
   ASSERT_TRUE(verifier_->IsDelayTaskPosted());
   ASSERT_EQ(1, verifier_->FailureCount());
@@ -158,12 +148,11 @@
 
   content::GetNetworkConnectionTracker()->AddNetworkConnectionObserver(this);
 
-  FakeProfileOAuth2TokenService* service = oauth2_token_service();
-  ASSERT_EQ(1u, service->GetPendingRequests().size());
-  service->IssueErrorForAllPendingRequests(transient_error_);
+  identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
+      transient_error_);
 
   ASSERT_EQ(1, verifier_->FailureCount());
-  ASSERT_EQ(nullptr, verifier_->request());
+  ASSERT_EQ(nullptr, verifier_->access_token_fetcher());
   ASSERT_TRUE(verifier_->IsDelayTaskPosted());
 
   network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
@@ -172,7 +161,7 @@
   content::GetNetworkConnectionTracker()->RemoveNetworkConnectionObserver(this);
 
   ASSERT_EQ(0, verifier_->FailureCount());
-  ASSERT_EQ(nullptr, verifier_->request());
+  ASSERT_EQ(nullptr, verifier_->access_token_fetcher());
   ASSERT_FALSE(verifier_->IsDelayTaskPosted());
 }
 
@@ -181,12 +170,11 @@
 
   content::GetNetworkConnectionTracker()->AddNetworkConnectionObserver(this);
 
-  FakeProfileOAuth2TokenService* service = oauth2_token_service();
-  ASSERT_EQ(1u, service->GetPendingRequests().size());
-  service->IssueErrorForAllPendingRequests(transient_error_);
+  identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
+      transient_error_);
 
   ASSERT_EQ(1, verifier_->FailureCount());
-  ASSERT_EQ(nullptr, verifier_->request());
+  ASSERT_EQ(nullptr, verifier_->access_token_fetcher());
   ASSERT_TRUE(verifier_->IsDelayTaskPosted());
 
   network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
@@ -195,7 +183,7 @@
   content::GetNetworkConnectionTracker()->RemoveNetworkConnectionObserver(this);
 
   ASSERT_EQ(0, verifier_->FailureCount());
-  ASSERT_NE(nullptr, verifier_->request());
+  ASSERT_NE(nullptr, verifier_->access_token_fetcher());
   ASSERT_FALSE(verifier_->IsDelayTaskPosted());
 }
 
@@ -206,7 +194,7 @@
   verifier_ = std::make_unique<MockForceSigninVerifier>(profile_.get());
 
   // There is no network type at first.
-  ASSERT_EQ(nullptr, verifier_->request());
+  ASSERT_EQ(nullptr, verifier_->access_token_fetcher());
 
   // Waiting for the network type returns.
   base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -214,7 +202,7 @@
   wait_for_network_type_async_return_.Run();
 
   // Get the type and send the request.
-  ASSERT_NE(nullptr, verifier_->request());
+  ASSERT_NE(nullptr, verifier_->access_token_fetcher());
 }
 
 TEST_F(ForceSigninVerifierTest, LaunchVerifierWithoutNetwork) {
@@ -226,7 +214,7 @@
   verifier_ = std::make_unique<MockForceSigninVerifier>(profile_.get());
 
   // There is no network type.
-  ASSERT_EQ(nullptr, verifier_->request());
+  ASSERT_EQ(nullptr, verifier_->access_token_fetcher());
 
   // Waiting for the network type returns.
   base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -234,7 +222,7 @@
   wait_for_network_type_async_return_.Run();
 
   // Get the type, there is no network connection, don't send the request.
-  ASSERT_EQ(nullptr, verifier_->request());
+  ASSERT_EQ(nullptr, verifier_->access_token_fetcher());
 
   // Network is resumed.
   content::GetNetworkConnectionTracker()->AddNetworkConnectionObserver(this);
@@ -244,7 +232,7 @@
   content::GetNetworkConnectionTracker()->RemoveNetworkConnectionObserver(this);
 
   // Send the request.
-  ASSERT_NE(nullptr, verifier_->request());
+  ASSERT_NE(nullptr, verifier_->access_token_fetcher());
 }
 
 TEST_F(ForceSigninVerifierTest, ChangeNetworkFromWIFITo4GWithOnGoingRequest) {
@@ -254,9 +242,8 @@
       false);
 
   verifier_ = std::make_unique<MockForceSigninVerifier>(profile_.get());
-  FakeProfileOAuth2TokenService* service = oauth2_token_service();
 
-  EXPECT_EQ(nullptr, verifier_->request());
+  EXPECT_EQ(nullptr, verifier_->access_token_fetcher());
 
   // Waiting for the network type returns.
   base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -264,9 +251,8 @@
   wait_for_network_type_async_return_.Run();
 
   // The network type if wifi, send the request.
-  auto* first_request = verifier_->request();
+  auto* first_request = verifier_->access_token_fetcher();
   EXPECT_NE(nullptr, first_request);
-  EXPECT_EQ(1u, service->GetPendingRequests().size());
 
   // Network is changed to 4G.
   content::GetNetworkConnectionTracker()->AddNetworkConnectionObserver(this);
@@ -276,8 +262,9 @@
   content::GetNetworkConnectionTracker()->RemoveNetworkConnectionObserver(this);
 
   // There is still one on-going request.
-  EXPECT_EQ(first_request, verifier_->request());
-  EXPECT_EQ(1u, service->GetPendingRequests().size());
+  EXPECT_EQ(first_request, verifier_->access_token_fetcher());
+  identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+      account_info_.account_id, /*token=*/"", base::Time());
 }
 
 TEST_F(ForceSigninVerifierTest, ChangeNetworkFromWIFITo4GWithFinishedRequest) {
@@ -287,9 +274,8 @@
       false);
 
   verifier_ = std::make_unique<MockForceSigninVerifier>(profile_.get());
-  FakeProfileOAuth2TokenService* service = oauth2_token_service();
 
-  EXPECT_EQ(nullptr, verifier_->request());
+  EXPECT_EQ(nullptr, verifier_->access_token_fetcher());
 
   // Waiting for the network type returns.
   base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -297,12 +283,12 @@
   wait_for_network_type_async_return_.Run();
 
   // The network type if wifi, send the request.
-  EXPECT_NE(nullptr, verifier_->request());
+  EXPECT_NE(nullptr, verifier_->access_token_fetcher());
 
   // Finishes the request.
-  service->IssueAllTokensForAccount("fake_id",
-                                    OAuth2AccessTokenConsumer::TokenResponse());
-  EXPECT_EQ(nullptr, verifier_->request());
+  identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+      account_info_.account_id, /*token=*/"", base::Time());
+  EXPECT_EQ(nullptr, verifier_->access_token_fetcher());
 
   // Network is changed to 4G.
   content::GetNetworkConnectionTracker()->AddNetworkConnectionObserver(this);
@@ -312,5 +298,5 @@
   content::GetNetworkConnectionTracker()->RemoveNetworkConnectionObserver(this);
 
   // No more request because it's verfied already.
-  EXPECT_EQ(nullptr, verifier_->request());
+  EXPECT_EQ(nullptr, verifier_->access_token_fetcher());
 }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index b1723f8d..98b2856 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -564,6 +564,7 @@
     deps += [
       "//chrome/browser/ui/webui/eoc_internals:mojo_bindings",
       "//chrome/browser/ui/webui/explore_sites_internals:mojo_bindings",
+      "//chrome/browser/ui/webui/feed_internals:mojo_bindings",
       "//chrome/browser/ui/webui/snippets_internals:mojo_bindings",
     ]
   }
@@ -719,6 +720,10 @@
       "webui/explore_sites_internals/explore_sites_internals_page_handler.h",
       "webui/explore_sites_internals/explore_sites_internals_ui.cc",
       "webui/explore_sites_internals/explore_sites_internals_ui.h",
+      "webui/feed_internals/feed_internals_page_handler.cc",
+      "webui/feed_internals/feed_internals_page_handler.h",
+      "webui/feed_internals/feed_internals_ui.cc",
+      "webui/feed_internals/feed_internals_ui.h",
       "webui/offline/offline_internals_ui.cc",
       "webui/offline/offline_internals_ui.h",
       "webui/offline/offline_internals_ui_message_handler.cc",
@@ -2775,6 +2780,8 @@
       "views/tabs/tab_controller.h",
       "views/tabs/tab_drag_controller.cc",
       "views/tabs/tab_drag_controller.h",
+      "views/tabs/tab_hover_card_bubble_view.cc",
+      "views/tabs/tab_hover_card_bubble_view.h",
       "views/tabs/tab_icon.cc",
       "views/tabs/tab_icon.h",
       "views/tabs/tab_strip.cc",
diff --git a/chrome/browser/ui/ash/accelerator_commands_browsertest.cc b/chrome/browser/ui/ash/accelerator_commands_browsertest.cc
index e508ca1c..061b016 100644
--- a/chrome/browser/ui/ash/accelerator_commands_browsertest.cc
+++ b/chrome/browser/ui/ash/accelerator_commands_browsertest.cc
@@ -6,7 +6,7 @@
 
 #include "ash/public/cpp/window_properties.h"
 #include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/shell_test_api.mojom.h"
+#include "ash/public/interfaces/shell_test_api.test-mojom.h"
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "build/build_config.h"
diff --git a/chrome/browser/ui/ash/network/networking_config_chromeos_browsertest.cc b/chrome/browser/ui/ash/network/networking_config_chromeos_browsertest.cc
index 5cd935a7..2f28227 100644
--- a/chrome/browser/ui/ash/network/networking_config_chromeos_browsertest.cc
+++ b/chrome/browser/ui/ash/network/networking_config_chromeos_browsertest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/public/cpp/ash_view_ids.h"
 #include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/system_tray_test_api.mojom.h"
+#include "ash/public/interfaces/system_tray_test_api.test-mojom.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/chrome/browser/ui/ash/shelf_browsertest.cc b/chrome/browser/ui/ash/shelf_browsertest.cc
index 09df5f1d..c9bdcb08 100644
--- a/chrome/browser/ui/ash/shelf_browsertest.cc
+++ b/chrome/browser/ui/ash/shelf_browsertest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/public/cpp/shelf_prefs.h"
 #include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/shelf_test_api.mojom.h"
+#include "ash/public/interfaces/shelf_test_api.test-mojom.h"
 #include "base/command_line.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/browser.h"
diff --git a/chrome/browser/ui/ash/system_tray_client_browsertest.cc b/chrome/browser/ui/ash/system_tray_client_browsertest.cc
index d56244c..2a45095 100644
--- a/chrome/browser/ui/ash/system_tray_client_browsertest.cc
+++ b/chrome/browser/ui/ash/system_tray_client_browsertest.cc
@@ -6,7 +6,7 @@
 
 #include "ash/public/cpp/ash_view_ids.h"
 #include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/system_tray_test_api.mojom.h"
+#include "ash/public/interfaces/system_tray_test_api.test-mojom.h"
 #include "base/i18n/time_formatting.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/chromeos/login/login_manager_test.h"
diff --git a/chrome/browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc b/chrome/browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc
index 8e934eb..0fb85bba 100644
--- a/chrome/browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc
+++ b/chrome/browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc
@@ -8,7 +8,7 @@
 #include "ash/public/cpp/ash_view_ids.h"
 #include "ash/public/interfaces/ash_message_center_controller.mojom.h"
 #include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/system_tray_test_api.mojom.h"
+#include "ash/public/interfaces/system_tray_test_api.test-mojom.h"
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/media/router/media_routes_observer.h"
diff --git a/chrome/browser/ui/ash/tablet_mode_client_test_util.cc b/chrome/browser/ui/ash/tablet_mode_client_test_util.cc
index 6f48cd03..19131ed1 100644
--- a/chrome/browser/ui/ash/tablet_mode_client_test_util.cc
+++ b/chrome/browser/ui/ash/tablet_mode_client_test_util.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/shell_test_api.mojom.h"
+#include "ash/public/interfaces/shell_test_api.test-mojom.h"
 #include "base/run_loop.h"
 #include "chrome/browser/ui/ash/tablet_mode_client.h"
 #include "chrome/browser/ui/ash/tablet_mode_client_observer.h"
diff --git a/chrome/browser/ui/ash/time_to_first_present_recorder_browsertest.cc b/chrome/browser/ui/ash/time_to_first_present_recorder_browsertest.cc
index 18059148..53b8af8 100644
--- a/chrome/browser/ui/ash/time_to_first_present_recorder_browsertest.cc
+++ b/chrome/browser/ui/ash/time_to_first_present_recorder_browsertest.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/time_to_first_present_recorder_test_api.mojom.h"
+#include "ash/public/interfaces/time_to_first_present_recorder_test_api.test-mojom.h"
 #include "base/command_line.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "content/public/common/service_manager_connection.h"
diff --git a/chrome/browser/ui/cocoa/history_menu_bridge_unittest.mm b/chrome/browser/ui/cocoa/history_menu_bridge_unittest.mm
index 3ff33b4..337c3e2 100644
--- a/chrome/browser/ui/cocoa/history_menu_bridge_unittest.mm
+++ b/chrome/browser/ui/cocoa/history_menu_bridge_unittest.mm
@@ -140,6 +140,10 @@
     bridge_->GotFaviconData(item, image_result);
   }
 
+  void CancelFaviconRequest(HistoryMenuBridge::HistoryItem* item) {
+    bridge_->CancelFaviconRequest(item);
+  }
+
   std::unique_ptr<MockBridge> bridge_;
 };
 
@@ -347,6 +351,9 @@
   // Make sure the item was modified properly.
   EXPECT_TRUE(item.icon_requested);
   EXPECT_NE(base::CancelableTaskTracker::kBadTaskId, item.icon_task_id);
+
+  // Cancel the request.
+  CancelFaviconRequest(&item);
 }
 
 TEST_F(HistoryMenuBridgeTest, GotFaviconData) {
@@ -360,6 +367,9 @@
   item.menu_item.reset([[NSMenuItem alloc] init]);
   GetFaviconForHistoryItem(&item);
 
+  // Cancel the request so there will be no race.
+  CancelFaviconRequest(&item);
+
   // Pretend to be called back.
   favicon_base::FaviconImageResult image_result;
   image_result.image = gfx::Image::CreateFrom1xBitmap(bitmap);
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller_test.h b/chrome/browser/ui/exclusive_access/fullscreen_controller_test.h
index cef93c0..abe629b9 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller_test.h
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller_test.h
@@ -105,6 +105,13 @@
   void ToggleTabFullscreen_Internal(bool enter_fullscreen,
                                     bool retry_until_success);
 
+#if defined(OS_MACOSX)
+  // On Mac, entering into the system fullscreen mode can tickle crashes in
+  // the WindowServer (c.f. https://crbug.com/828031), so provide a fake for
+  // testing.
+  ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen_window_;
+#endif
+
   base::test::ScopedFeatureList scoped_feature_list_;
 
   base::WeakPtrFactory<FullscreenControllerTest> weak_ptr_factory_;
diff --git a/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc b/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc
index 742f3f3e..8a0f983 100644
--- a/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc
+++ b/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc
@@ -394,13 +394,8 @@
                                       kFwd, kIgnoreCase, NULL, NULL));
 }
 
-#if defined(OS_WIN)
-#define MAYBE_LargePage DISABLED_LargePage
-#else
-#define MAYBE_LargePage LargePage
-#endif
 // Find in a very large page.
-IN_PROC_BROWSER_TEST_F(FindInPageControllerTest, MAYBE_LargePage) {
+IN_PROC_BROWSER_TEST_F(FindInPageControllerTest, LargePage) {
   WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   ui_test_utils::NavigateToURL(browser(), GetURL("largepage.html"));
@@ -409,15 +404,8 @@
                                  kFwd, kIgnoreCase, NULL));
 }
 
-// https://crbug.com/825341: Flaky timeout on Win7 Tests (dbg)(1)
-#if defined(OS_WIN) && !defined(NDEBUG)
-#define MAYBE_FindLongString DISABLED_FindLongString
-#else
-#define MAYBE_FindLongString FindLongString
-#endif
-
 // Find a very long string in a large page.
-IN_PROC_BROWSER_TEST_F(FindInPageControllerTest, MAYBE_FindLongString) {
+IN_PROC_BROWSER_TEST_F(FindInPageControllerTest, FindLongString) {
   WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   ui_test_utils::NavigateToURL(browser(), GetURL("largepage.html"));
@@ -445,9 +433,8 @@
                                kFwd, kIgnoreCase, NULL));
 }
 
-// http://crbug.com/369169, http://crbug.com/733286
 // Search Back and Forward on a single occurrence.
-IN_PROC_BROWSER_TEST_F(FindInPageControllerTest, DISABLED_SingleOccurrence) {
+IN_PROC_BROWSER_TEST_F(FindInPageControllerTest, SingleOccurrence) {
   WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   ui_test_utils::NavigateToURL(browser(), GetURL("FindRandomTests.html"));
@@ -789,9 +776,7 @@
 
 // Make sure we don't get into an infinite loop when text box contains very
 // large amount of text.
-// Disable the test as it started being flaky, see http://crbug/367701.
-IN_PROC_BROWSER_TEST_F(FindInPageControllerTest,
-                       DISABLED_FindRestarts_Issue70505) {
+IN_PROC_BROWSER_TEST_F(FindInPageControllerTest, FindRestarts_Issue70505) {
   // First we navigate to our page.
   GURL url = GetURL(kLongTextareaPage);
   ui_test_utils::NavigateToURL(browser(), url);
@@ -807,9 +792,7 @@
 }
 
 // This tests bug 11761: FindInPage terminates search prematurely.
-// This test is not expected to pass until bug 11761 is fixed.
-IN_PROC_BROWSER_TEST_F(FindInPageControllerTest,
-                       DISABLED_FindInPagePrematureEnd) {
+IN_PROC_BROWSER_TEST_F(FindInPageControllerTest, FindInPagePrematureEnd) {
   // First we navigate to our special focus tracking page.
   GURL url = GetURL(kPrematureEnd);
   ui_test_utils::NavigateToURL(browser(), url);
@@ -891,18 +874,10 @@
   EXPECT_TRUE(fully_visible);
 }
 
-// FindDisappearOnNewTabAndHistory is flaky, at least on Mac.
-// See http://crbug.com/43072
-#if defined(OS_MACOSX)
-#define MAYBE_FindDisappearOnNewTabAndHistory DISABLED_FindDisappearOnNewTabAndHistory
-#else
-#define MAYBE_FindDisappearOnNewTabAndHistory FindDisappearOnNewTabAndHistory
-#endif
-
 // Make sure Find box disappears when History/Downloads page is opened, and
 // when a New Tab is opened.
 IN_PROC_BROWSER_TEST_F(FindInPageControllerTest,
-                       MAYBE_FindDisappearOnNewTabAndHistory) {
+                       FindDisappearOnNewTabAndHistory) {
   // First we navigate to our special focus tracking page.
   GURL url = GetURL(kSimple);
   ui_test_utils::NavigateToURL(browser(), url);
@@ -992,18 +967,9 @@
   EXPECT_EQ(position.x(), start_position.x());
 }
 
-// FindNextInNewTabUsesPrepopulate times-out on Mac and Aura.
-// See http://crbug.com/43070
-#if defined(OS_MACOSX) || defined(USE_AURA)
-#define MAYBE_FindNextInNewTabUsesPrepopulate \
-    DISABLED_FindNextInNewTabUsesPrepopulate
-#else
-#define MAYBE_FindNextInNewTabUsesPrepopulate FindNextInNewTabUsesPrepopulate
-#endif
-
 // Make sure F3 in a new tab works if Find has previous string to search for.
 IN_PROC_BROWSER_TEST_F(FindInPageControllerTest,
-                       MAYBE_FindNextInNewTabUsesPrepopulate) {
+                       FindNextInNewTabUsesPrepopulate) {
   // First we navigate to any page.
   GURL url = GetURL(kSimple);
   ui_test_utils::NavigateToURL(browser(), url);
@@ -1279,17 +1245,9 @@
   EXPECT_EQ(ASCIIToUTF16("page"), GetFindBarText());
 }
 
-// TODO(rohitrao): Searching in incognito tabs does not work in browser tests in
-// Linux views.  Investigate and fix.  http://crbug.com/40948
-#if defined(OS_LINUX) && defined(TOOLKIT_VIEWS)
-#define MAYBE_NoIncognitoPrepopulate DISABLED_NoIncognitoPrepopulate
-#else
-#define MAYBE_NoIncognitoPrepopulate NoIncognitoPrepopulate
-#endif
-
 // This tests that search terms entered into an incognito find bar are not used
 // as prepopulate terms for non-incognito windows.
-IN_PROC_BROWSER_TEST_F(FindInPageControllerTest, MAYBE_NoIncognitoPrepopulate) {
+IN_PROC_BROWSER_TEST_F(FindInPageControllerTest, NoIncognitoPrepopulate) {
   FindBar* find_bar = browser()->GetFindBarController()->find_bar();
   if (find_bar->HasGlobalFindPasteboard())
     return;
diff --git a/chrome/browser/ui/find_bar/find_bar_host_interactive_uitest.cc b/chrome/browser/ui/find_bar/find_bar_host_interactive_uitest.cc
index 1c08e1d..404669c1 100644
--- a/chrome/browser/ui/find_bar/find_bar_host_interactive_uitest.cc
+++ b/chrome/browser/ui/find_bar/find_bar_host_interactive_uitest.cc
@@ -60,13 +60,7 @@
 // This tests the FindInPage end-state, in other words: what is focused when you
 // close the Find box (ie. if you find within a link the link should be
 // focused).
-// Flaky on CrOS.
-#if defined(OS_CHROMEOS)
-#define MAYBE_FindInPageEndState DISABLED_FindInPageEndState
-#else
-#define MAYBE_FindInPageEndState FindInPageEndState
-#endif
-IN_PROC_BROWSER_TEST_F(FindInPageInteractiveTest, MAYBE_FindInPageEndState) {
+IN_PROC_BROWSER_TEST_F(FindInPageInteractiveTest, FindInPageEndState) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   // Make sure Chrome is in the foreground, otherwise sending input
diff --git a/chrome/browser/ui/keyboard_lock_interactive_browsertest.cc b/chrome/browser/ui/keyboard_lock_interactive_browsertest.cc
index e114037..7b79a7bb 100644
--- a/chrome/browser/ui/keyboard_lock_interactive_browsertest.cc
+++ b/chrome/browser/ui/keyboard_lock_interactive_browsertest.cc
@@ -18,6 +18,10 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "ui/base/ui_base_features.h"
 
+#if defined(OS_MACOSX)
+#include "ui/base/test/scoped_fake_nswindow_fullscreen.h"
+#endif
+
 namespace {
 
 // Javascript snippet used to verify the keyboard lock API exists.
@@ -103,6 +107,10 @@
   base::test::ScopedFeatureList scoped_feature_list_;
   net::EmbeddedTestServer https_test_server_;
 
+#if defined(OS_MACOSX)
+  ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen_;
+#endif
+
   DISALLOW_COPY_AND_ASSIGN(KeyboardLockInteractiveBrowserTest);
 };
 
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
index 5d7db03..684c64b 100644
--- a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
+++ b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
@@ -334,9 +334,9 @@
       continue;
     }
     // TODO(jdonnelly, rhalavati): Create a helper function with Callback to
-    // create annotation and pass it to image_service, merging this annotation
-    // and the one in
-    // chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
+    // create annotation and pass it to image_service, merging the annotations
+    // in omnibox_page_handler.cc, chrome_omnibox_client.cc,
+    // and chrome_autocomplete_provider_client.cc.
     constexpr net::NetworkTrafficAnnotationTag traffic_annotation =
         net::DefineNetworkTrafficAnnotation("omnibox_result_change", R"(
           semantics {
diff --git a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
index 616a072..60cc1a0d 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
+++ b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
@@ -15,6 +15,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
+#include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/search.h"
@@ -28,9 +29,7 @@
 #include "chrome/browser/ui/in_product_help/reopen_tab_in_product_help_factory.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/toolbar/app_menu_model.h"
-#include "chrome/grit/browser_resources.h"
 #include "chrome/grit/generated_resources.h"
-#include "chrome/grit/theme_resources.h"
 #include "components/favicon_base/favicon_types.h"
 #include "components/feature_engagement/buildflags.h"
 #include "components/prefs/scoped_user_pref_update.h"
@@ -42,13 +41,9 @@
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
-#include "ui/resources/grit/ui_resources.h"
-
-#if !defined(OS_MACOSX)
-#include "chrome/app/vector_icons/vector_icons.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/paint_vector_icon.h"
-#endif
+#include "ui/resources/grit/ui_resources.h"
 
 namespace {
 
@@ -80,10 +75,6 @@
 // shown in the menu.
 const int kMaxLocalEntries = 8;
 
-// Index of the separator that follows the history menu item. Used as a
-// reference position for inserting local entries.
-const int kHistorySeparatorIndex = 1;
-
 // Comparator function for use with std::sort that will sort sessions by
 // descending modified_time (i.e., most recent first).
 bool SortSessionsByRecency(const sync_sessions::SyncedSession* s1,
@@ -131,11 +122,9 @@
   return command_id - kFirstLocalWindowCommandId;
 }
 
-#if !defined(OS_MACOSX)
 gfx::Image CreateFavicon(const gfx::VectorIcon& icon) {
   return gfx::Image(gfx::CreateVectorIcon(icon, 16, gfx::kChromeIconGrey));
 }
-#endif
 
 }  // namespace
 
@@ -172,9 +161,6 @@
   GURL url;
 };
 
-const int RecentTabsSubMenuModel::kRecentlyClosedHeaderCommandId = 1120;
-const int RecentTabsSubMenuModel::kDisabledRecentlyClosedHeaderCommandId = 1121;
-
 RecentTabsSubMenuModel::RecentTabsSubMenuModel(
     ui::AcceleratorProvider* accelerator_provider,
     Browser* browser)
@@ -183,14 +169,9 @@
       session_sync_service_(
           SessionSyncServiceFactory::GetInstance()->GetForProfile(
               browser->profile())),
-      last_local_model_index_(kHistorySeparatorIndex),
       default_favicon_(
           ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
-              IDR_DEFAULT_FAVICON)),
-#if !defined(OS_MACOSX)
-      tab_restore_service_observer_(this),
-#endif  // !defined(OS_MACOSX)
-      weak_ptr_factory_(this) {
+              IDR_DEFAULT_FAVICON)) {
   // Invoke asynchronous call to load tabs from local last session, which does
   // nothing if the tabs have already been loaded or they shouldn't be loaded.
   // TabRestoreServiceChanged() will be called after the tabs are loaded.
@@ -198,15 +179,9 @@
       TabRestoreServiceFactory::GetForProfile(browser_->profile());
   if (service) {
     service->LoadTabsFromLastSession();
-
-// Mac doesn't support the dynamic menu.
-#if !defined(OS_MACOSX)
     tab_restore_service_observer_.Add(service);
-#endif
   }
 
-// Mac doesn't support the dynamic menu.
-#if !defined(OS_MACOSX)
   if (session_sync_service_) {
     // Using a weak pointer below for simplicity although, strictly speaking,
     // it's not needed because the subscription itself should take care.
@@ -216,7 +191,6 @@
                 &RecentTabsSubMenuModel::OnForeignSessionUpdated,
                 weak_ptr_factory_.GetWeakPtr()));
   }
-#endif  // !defined(OS_MACOSX)
 
   Build();
 
@@ -235,13 +209,10 @@
 }
 
 bool RecentTabsSubMenuModel::IsCommandIdEnabled(int command_id) const {
-  if (command_id == kRecentlyClosedHeaderCommandId ||
-      command_id == kDisabledRecentlyClosedHeaderCommandId ||
-      command_id == IDC_RECENT_TABS_NO_DEVICE_TABS ||
-      IsDeviceNameCommandId(command_id)) {
-    return false;
-  }
-  return true;
+  return command_id != kRecentlyClosedHeaderCommandId &&
+         command_id != kDisabledRecentlyClosedHeaderCommandId &&
+         command_id != IDC_RECENT_TABS_NO_DEVICE_TABS &&
+         !IsDeviceNameCommandId(command_id);
 }
 
 bool RecentTabsSubMenuModel::GetAcceleratorForCommandId(
@@ -433,13 +404,7 @@
     InsertItemWithStringIdAt(++last_local_model_index_,
                              kRecentlyClosedHeaderCommandId,
                              IDS_RECENTLY_CLOSED);
-#if defined(OS_MACOSX)
-    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-    SetIcon(last_local_model_index_,
-            rb.GetNativeImageNamed(IDR_RECENTLY_CLOSED_WINDOW));
-#else
     SetIcon(last_local_model_index_, CreateFavicon(kTabIcon));
-#endif
 
     int added_count = 0;
     for (const auto& entry : service->entries()) {
@@ -549,12 +514,7 @@
   // See comments in BuildLocalEntries() about usage of InsertItem*At().
   InsertItemAt(curr_model_index, command_id, l10n_util::GetPluralStringFUTF16(
       IDS_RECENTLY_CLOSED_WINDOW, num_tabs));
-#if defined(OS_MACOSX)
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  SetIcon(curr_model_index, rb.GetNativeImageNamed(IDR_RECENTLY_CLOSED_WINDOW));
-#else
   SetIcon(curr_model_index, CreateFavicon(kTabIcon));
-#endif
   local_window_items_.push_back(window_id);
 }
 
@@ -580,30 +540,6 @@
 void RecentTabsSubMenuModel::AddDeviceFavicon(
     int index_in_menu,
     sync_pb::SyncEnums::DeviceType device_type) {
-#if defined(OS_MACOSX)
-  int favicon_id = -1;
-  switch (device_type) {
-    case sync_pb::SyncEnums::TYPE_PHONE:
-      favicon_id = IDR_PHONE_FAVICON;
-      break;
-
-    case sync_pb::SyncEnums::TYPE_TABLET:
-      favicon_id = IDR_TABLET_FAVICON;
-      break;
-
-    case sync_pb::SyncEnums::TYPE_CROS:
-    case sync_pb::SyncEnums::TYPE_WIN:
-    case sync_pb::SyncEnums::TYPE_MAC:
-    case sync_pb::SyncEnums::TYPE_LINUX:
-    case sync_pb::SyncEnums::TYPE_OTHER:
-    case sync_pb::SyncEnums::TYPE_UNSET:
-      favicon_id = IDR_LAPTOP_FAVICON;
-      break;
-  }
-
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  SetIcon(index_in_menu, rb.GetNativeImageNamed(favicon_id));
-#else
   const gfx::VectorIcon* favicon = nullptr;
   switch (device_type) {
     case sync_pb::SyncEnums::TYPE_PHONE:
@@ -625,7 +561,6 @@
   }
 
   SetIcon(index_in_menu, CreateFavicon(*favicon));
-#endif
 }
 
 void RecentTabsSubMenuModel::AddTabFavicon(int command_id, const GURL& url) {
diff --git a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h
index 87b2a6dc..e36446cf 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h
+++ b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h
@@ -56,8 +56,8 @@
  public:
   // Command Id for recently closed items header or disabled item to which the
   // accelerator string will be appended.
-  static const int kRecentlyClosedHeaderCommandId;
-  static const int kDisabledRecentlyClosedHeaderCommandId;
+  static constexpr int kRecentlyClosedHeaderCommandId = 1120;
+  static constexpr int kDisabledRecentlyClosedHeaderCommandId = 1121;
 
   // Exposed for tests only: return the Command Id for the first entry in the
   // recently closed window items list.
@@ -82,9 +82,12 @@
 
  private:
   struct TabNavigationItem;
-  typedef std::vector<TabNavigationItem> TabNavigationItems;
+  using TabNavigationItems = std::vector<TabNavigationItem>;
+  using WindowItems = std::vector<SessionID>;
 
-  typedef std::vector<SessionID> WindowItems;
+  // Index of the separator that follows the history menu item. Used as a
+  // reference position for inserting local entries.
+  static constexpr int kHistorySeparatorIndex = 1;
 
   // Build the menu items by populating the menumodel.
   void Build();
@@ -174,7 +177,7 @@
 
   // Index of the last local entry (recently closed tab or window) in the
   // menumodel.
-  int last_local_model_index_;
+  int last_local_model_index_ = kHistorySeparatorIndex;
 
   gfx::Image default_favicon_;
 
@@ -184,16 +187,13 @@
   // Time the menu is open for until a recent tab is selected.
   base::ElapsedTimer menu_opened_timer_;
 
-// Mac doesn't support the dynamic menu.
-#if !defined(OS_MACOSX)
   ScopedObserver<sessions::TabRestoreService, RecentTabsSubMenuModel>
-      tab_restore_service_observer_;
-#endif
+      tab_restore_service_observer_{this};
 
   std::unique_ptr<base::CallbackList<void()>::Subscription>
       foreign_session_updated_subscription_;
 
-  base::WeakPtrFactory<RecentTabsSubMenuModel> weak_ptr_factory_;
+  base::WeakPtrFactory<RecentTabsSubMenuModel> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(RecentTabsSubMenuModel);
 };
diff --git a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc
index 0ef7a74..5b457ce7 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc
+++ b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc
@@ -271,17 +271,8 @@
   EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(6, &url, &title));
 }
 
-// TODO(sail): enable this test when dynamic model is enabled in
-// RecentTabsSubMenuModel.
-#if defined(OS_MACOSX)
-#define MAYBE_RecentlyClosedTabsAndWindowsFromLastSession \
-    DISABLED_RecentlyClosedTabsAndWindowsFromLastSession
-#else
-#define MAYBE_RecentlyClosedTabsAndWindowsFromLastSession \
-    RecentlyClosedTabsAndWindowsFromLastSession
-#endif
 TEST_F(RecentTabsSubMenuModelTest,
-       MAYBE_RecentlyClosedTabsAndWindowsFromLastSession) {
+       RecentlyClosedTabsAndWindowsFromLastSession) {
   DisableSync();
 
   TabRestoreServiceFactory::GetInstance()->SetTestingFactory(
@@ -508,13 +499,7 @@
   EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(12, &url, &title));
 }
 
-// Mac doesn't support the dynamic menu.
-#if defined(OS_MACOSX)
-#define MAYBE_OtherDevicesDynamicUpdate DISABLED_OtherDevicesDynamicUpdate
-#else
-#define MAYBE_OtherDevicesDynamicUpdate OtherDevicesDynamicUpdate
-#endif
-TEST_F(RecentTabsSubMenuModelTest, MAYBE_OtherDevicesDynamicUpdate) {
+TEST_F(RecentTabsSubMenuModelTest, OtherDevicesDynamicUpdate) {
   // Create menu with disabled synchronization.
   DisableSync();
 
diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_container.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_container.cc
index 97f9584..cf373a5 100644
--- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_container.cc
+++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_container.cc
@@ -32,6 +32,7 @@
 #if BUILDFLAG(ENABLE_APP_LIST)
 #include "ash/public/cpp/app_list/app_list_constants.h"
 #include "third_party/skia/include/core/SkPaint.h"
+#include "ui/native_theme/native_theme.h"
 #include "ui/views/background.h"
 #endif
 
@@ -135,7 +136,8 @@
   explicit AppListDialogContainer(views::View* dialog_body)
       : BaseDialogContainer(dialog_body, base::RepeatingClosure()) {
     SetBackground(std::make_unique<AppListOverlayBackground>());
-    close_button_ = views::BubbleFrameView::CreateCloseButton(this);
+    close_button_ = views::BubbleFrameView::CreateCloseButton(
+        this, GetNativeTheme()->SystemDarkModeEnabled());
     AddChildView(close_button_);
   }
   ~AppListDialogContainer() override {}
diff --git a/chrome/browser/ui/views/frame/browser_frame_mac.h b/chrome/browser/ui/views/frame/browser_frame_mac.h
index 26dccf4..0e3b7ae 100644
--- a/chrome/browser/ui/views/frame/browser_frame_mac.h
+++ b/chrome/browser/ui/views/frame/browser_frame_mac.h
@@ -29,7 +29,7 @@
   BrowserWindowTouchBarController* GetTouchBarController() const;
 
   // Overridden from views::NativeWidgetMac:
-  int SheetPositionY() override;
+  int32_t SheetOffsetY() override;
   void GetWindowFrameTitlebarHeight(bool* override_titlebar_height,
                                     float* titlebar_height) override;
   void OnFocusWindowToolbar() override;
diff --git a/chrome/browser/ui/views/frame/browser_frame_mac.mm b/chrome/browser/ui/views/frame/browser_frame_mac.mm
index 9fc237d..4c59e51 100644
--- a/chrome/browser/ui/views/frame/browser_frame_mac.mm
+++ b/chrome/browser/ui/views/frame/browser_frame_mac.mm
@@ -118,15 +118,12 @@
 ////////////////////////////////////////////////////////////////////////////////
 // BrowserFrameMac, views::NativeWidgetMac implementation:
 
-int BrowserFrameMac::SheetPositionY() {
+int32_t BrowserFrameMac::SheetOffsetY() {
+  // ModalDialogHost::GetDialogPosition() is relative to the host view. In
+  // practice, this ends up being the widget's content view.
   web_modal::WebContentsModalDialogHost* dialog_host =
       browser_view_->GetWebContentsModalDialogHost();
-  NSView* view = dialog_host->GetHostView().GetNativeNSView();
-  // Get the position of the host view relative to the window since
-  // ModalDialogHost::GetDialogPosition() is relative to the host view.
-  int host_view_y =
-      [view convertPoint:NSMakePoint(0, NSHeight([view frame])) toView:nil].y;
-  return host_view_y - dialog_host->GetDialogPosition(gfx::Size()).y();
+  return dialog_host->GetDialogPosition(gfx::Size()).y();
 }
 
 void BrowserFrameMac::GetWindowFrameTitlebarHeight(
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
index 20c5474e..123fb73 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -15,8 +15,8 @@
 #include "ash/public/cpp/vector_icons/vector_icons.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/shelf_test_api.mojom.h"
-#include "ash/public/interfaces/shell_test_api.mojom.h"
+#include "ash/public/interfaces/shelf_test_api.test-mojom.h"
+#include "ash/public/interfaces/shell_test_api.test-mojom.h"
 #include "ash/public/interfaces/window_pin_type.mojom.h"
 #include "ash/shell.h"                                   // mash-ok
 #include "ash/wm/overview/window_selector_controller.h"  // mash-ok
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc
index d3d963d5..adf434d 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc
@@ -7,7 +7,7 @@
 #include "ash/public/cpp/caption_buttons/frame_caption_button_container_view.h"
 #include "ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h"
 #include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/shell_test_api.mojom.h"
+#include "ash/public/interfaces/shell_test_api.test-mojom.h"
 #include "base/macros.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
diff --git a/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc b/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
index f8ebb69..72275a1 100644
--- a/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
@@ -25,6 +25,10 @@
 #include "ui/aura/test/env_test_helper.h"
 #endif
 
+#if defined(OS_MACOSX)
+#include "ui/base/test/scoped_fake_nswindow_fullscreen.h"
+#endif
+
 using ZoomBubbleBrowserTest = InProcessBrowserTest;
 
 namespace {
@@ -42,6 +46,10 @@
 // Test whether the zoom bubble is anchored and whether it is visible when in
 // non-immersive fullscreen.
 IN_PROC_BROWSER_TEST_F(ZoomBubbleBrowserTest, NonImmersiveFullscreen) {
+#if defined(OS_MACOSX)
+  ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen;
+#endif
+
   BrowserView* browser_view = static_cast<BrowserView*>(browser()->window());
   content::WebContents* web_contents = browser_view->GetActiveWebContents();
 
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_view_unittest.cc b/chrome/browser/ui/views/media_router/cast_dialog_view_unittest.cc
index 820b6f5..c85d61e1 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_view_unittest.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_view_unittest.cc
@@ -145,13 +145,7 @@
   CastDialogView* dialog_ = nullptr;
 };
 
-// Flaky on Mac. https://crbug.com/843599
-#if defined(OS_MACOSX)
-#define MAYBE_ShowAndHideDialog DISABLED_ShowAndHideDialog
-#else
-#define MAYBE_ShowAndHideDialog ShowAndHideDialog
-#endif
-TEST_F(CastDialogViewTest, MAYBE_ShowAndHideDialog) {
+TEST_F(CastDialogViewTest, ShowAndHideDialog) {
   EXPECT_FALSE(CastDialogView::IsShowing());
   EXPECT_EQ(nullptr, CastDialogView::GetCurrentDialogWidget());
 
diff --git a/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc b/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
index f2a9da5..5d30d90 100644
--- a/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
+++ b/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
@@ -270,15 +270,7 @@
   TestOpenDialogFromContextMenu();
 }
 
-// Disabled on macOS due to many timeouts. Seems fine on all other platforms.
-// crbug.com/849146
-#if defined(OS_MACOSX)
-#define MAYBE_OpenDialogFromAppMenu DISABLED_OpenDialogFromAppMenu
-#else
-#define MAYBE_OpenDialogFromAppMenu OpenDialogFromAppMenu
-#endif
-IN_PROC_BROWSER_TEST_F(MediaRouterWebUIBrowserTest,
-                       MAYBE_OpenDialogFromAppMenu) {
+IN_PROC_BROWSER_TEST_F(MediaRouterWebUIBrowserTest, OpenDialogFromAppMenu) {
   TestOpenDialogFromAppMenu();
 }
 
@@ -287,12 +279,8 @@
   TestEphemeralToolbarIconForDialog();
 }
 
-// Flaky on chromeos, linux, win: https://crbug.com/658005
-// Flaky on MacViews: https://crbug.com/817408
-// TODO(https://crbug.com/678472): Replace this test case with a Views version.
-// Close out the bugs above when doing so.
 IN_PROC_BROWSER_TEST_F(MediaRouterWebUIBrowserTest,
-                       DISABLED_OpenDialogWithMediaRouterAction) {
+                       OpenDialogWithMediaRouterAction) {
   // We start off at about:blank page.
   // Make sure there is 1 tab and media router is enabled.
   ASSERT_EQ(1, browser()->tab_strip_model()->count());
@@ -322,15 +310,7 @@
   SetAlwaysShowActionPref(false);
 }
 
-#if defined(MEMORY_SANITIZER)
-// Flaky crashes. crbug.com/863945
-#define MAYBE_OpenDialogsInMultipleTabs DISABLED_OpenDialogsInMultipleTabs
-#else
-#define MAYBE_OpenDialogsInMultipleTabs OpenDialogsInMultipleTabs
-#endif
-
-IN_PROC_BROWSER_TEST_F(MediaRouterWebUIBrowserTest,
-                       MAYBE_OpenDialogsInMultipleTabs) {
+IN_PROC_BROWSER_TEST_F(MediaRouterWebUIBrowserTest, OpenDialogsInMultipleTabs) {
   // Start with two tabs.
   chrome::NewTab(browser());
   ASSERT_EQ(2, browser()->tab_strip_model()->count());
diff --git a/chrome/browser/ui/views/media_router/presentation_receiver_window_view_browsertest.cc b/chrome/browser/ui/views/media_router/presentation_receiver_window_view_browsertest.cc
index e89d18f..6ddd485 100644
--- a/chrome/browser/ui/views/media_router/presentation_receiver_window_view_browsertest.cc
+++ b/chrome/browser/ui/views/media_router/presentation_receiver_window_view_browsertest.cc
@@ -190,15 +190,8 @@
 }
 #endif
 
-#if defined(OS_MACOSX)
-// https://crbug.com/828031
-#define MAYBE_LocationBarViewShown DISABLED_LocationBarViewShown
-#else
-#define MAYBE_LocationBarViewShown LocationBarViewShown
-#endif
-
 IN_PROC_BROWSER_TEST_F(PresentationReceiverWindowViewBrowserTest,
-                       MAYBE_LocationBarViewShown) {
+                       LocationBarViewShown) {
   receiver_view_->ShowInactiveFullscreen();
   receiver_view_->ExitFullscreen();
   ASSERT_FALSE(receiver_view_->IsFullscreen());
@@ -211,15 +204,8 @@
   EXPECT_LT(0, location_bar_view->height());
 }
 
-#if defined(OS_MACOSX)
-// https://crbug.com/828031
-#define MAYBE_ShowPageInfoDialog DISABLED_ShowPageInfoDialog
-#else
-#define MAYBE_ShowPageInfoDialog ShowPageInfoDialog
-#endif
-
 IN_PROC_BROWSER_TEST_F(PresentationReceiverWindowViewBrowserTest,
-                       MAYBE_ShowPageInfoDialog) {
+                       ShowPageInfoDialog) {
   content::NavigationController::LoadURLParams load_params(GURL("about:blank"));
   fake_delegate_->web_contents()->GetController().LoadURLWithParams(
       load_params);
diff --git a/chrome/browser/ui/views/select_file_dialog_extension_browsertest.cc b/chrome/browser/ui/views/select_file_dialog_extension_browsertest.cc
index 2d68226..0771154 100644
--- a/chrome/browser/ui/views/select_file_dialog_extension_browsertest.cc
+++ b/chrome/browser/ui/views/select_file_dialog_extension_browsertest.cc
@@ -7,7 +7,7 @@
 #include <memory>
 
 #include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/shell_test_api.mojom.h"
+#include "ash/public/interfaces/shell_test_api.test-mojom.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/logging.h"
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc
index 1fe42b4..0a987432 100644
--- a/chrome/browser/ui/views/tabs/tab.cc
+++ b/chrome/browser/ui/views/tabs/tab.cc
@@ -35,6 +35,7 @@
 #include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h"
 #include "chrome/browser/ui/views/tabs/tab_close_button.h"
 #include "chrome/browser/ui/views/tabs/tab_controller.h"
+#include "chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h"
 #include "chrome/browser/ui/views/tabs/tab_icon.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "chrome/browser/ui/views/tabs/tab_style.h"
@@ -176,8 +177,7 @@
   focus_ring_ = views::FocusRing::Install(this);
 }
 
-Tab::~Tab() {
-}
+Tab::~Tab() = default;
 
 void Tab::AnimationEnded(const gfx::Animation* animation) {
   if (animation == &title_animation_)
@@ -396,6 +396,7 @@
 }
 
 bool Tab::OnKeyPressed(const ui::KeyEvent& event) {
+  controller_->UpdateHoverCard(this, false);
   if (event.key_code() == ui::VKEY_SPACE && !IsSelected()) {
     controller_->SelectTab(this);
     return true;
@@ -415,6 +416,7 @@
 }  // namespace
 
 bool Tab::OnMousePressed(const ui::MouseEvent& event) {
+  controller_->UpdateHoverCard(this, false);
   controller_->OnMouseEventInTab(this, event);
 
   // Allow a right click from touch to drag, which corresponds to a long click.
@@ -511,6 +513,7 @@
   hover_controller_.Show(GlowHoverController::SUBTLE);
   UpdateForegroundColors();
   Layout();
+  controller_->UpdateHoverCard(this, true);
 }
 
 void Tab::OnMouseExited(const ui::MouseEvent& event) {
@@ -521,6 +524,7 @@
 }
 
 void Tab::OnGestureEvent(ui::GestureEvent* event) {
+  controller_->UpdateHoverCard(this, false);
   switch (event->type()) {
     case ui::ET_GESTURE_TAP_DOWN: {
       // TAP_DOWN is only dispatched for the first touch point.
@@ -720,8 +724,10 @@
   if (old.pinned != data_.pinned)
     showing_alert_indicator_ = false;
 
-  if (data_.alert_state != old.alert_state || data_.title != old.title)
+  if (data_.alert_state != old.alert_state || data_.title != old.title) {
     TooltipTextChanged();
+    controller_->UpdateHoverCard(this, mouse_hovered_);
+  }
 
   Layout();
   SchedulePaint();
@@ -916,8 +922,6 @@
     if (showing_icon_)
       available_width -= favicon_width;
 
-    // Show the close button if it's allowed to show on hover, even if it's
-    // forced to be hidden normally.
     showing_close_button_ &= large_enough_for_close_button;
     if (showing_close_button_)
       available_width -= close_button_width;
diff --git a/chrome/browser/ui/views/tabs/tab_controller.h b/chrome/browser/ui/views/tabs/tab_controller.h
index ba529af..ccf70fd3 100644
--- a/chrome/browser/ui/views/tabs/tab_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_controller.h
@@ -108,6 +108,10 @@
   virtual void OnMouseEventInTab(views::View* source,
                                  const ui::MouseEvent& event) = 0;
 
+  // Updates hover-card content, anchoring and visibility based on what tab is
+  // hovered and whether the card should be shown.
+  virtual void UpdateHoverCard(Tab* tab, bool should_show) = 0;
+
   // Returns whether |tab| needs to be painted. When this returns true, |clip|
   // is set to the path which should be clipped out of the current tab's region
   // (for hit testing or painting), if any.  |clip| is only non-empty when
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
new file mode 100644
index 0000000..877afbaa
--- /dev/null
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
@@ -0,0 +1,79 @@
+// 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.
+
+#include "chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/views/tabs/tab_renderer_data.h"
+#include "components/url_formatter/url_formatter.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/layout_provider.h"
+#include "ui/views/widget/widget.h"
+
+TabHoverCardBubbleView::TabHoverCardBubbleView(views::View* anchor_view,
+                                               TabRendererData data)
+    : BubbleDialogDelegateView(anchor_view, views::BubbleBorder::TOP_LEFT) {
+  SetLayoutManager(
+      std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical));
+  // Set so that when hovering over a tab in a inactive window that window will
+  // not become active. Setting this to false creates the need to explicitly
+  // hide the hovercard on press, touch, and keyboard events.
+  set_can_activate(false);
+
+  title_label_ = new views::Label();
+  domain_label_ = new views::Label();
+  UpdateCardContent(data);
+
+  title_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  title_label_->SetMultiLine(false);
+  title_label_->SetFontList(views::Label::GetDefaultFontList().Derive(
+      1, gfx::Font::FontStyle::NORMAL, gfx::Font::Weight::MEDIUM));
+  title_label_->SetEnabledColor(gfx::kGoogleGrey800);
+  domain_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  domain_label_->SetMultiLine(false);
+  domain_label_->SetEnabledColor(gfx::kGoogleGrey500);
+
+  AddChildView(title_label_);
+  AddChildView(domain_label_);
+  widget_ = views::BubbleDialogDelegateView::CreateBubble(this);
+}
+
+TabHoverCardBubbleView::~TabHoverCardBubbleView() = default;
+
+void TabHoverCardBubbleView::Show() {
+  widget_->Show();
+}
+
+void TabHoverCardBubbleView::Hide() {
+  widget_->Hide();
+}
+
+void TabHoverCardBubbleView::UpdateCardContent(TabRendererData data) {
+  title_label_->SetText(data.title);
+
+  base::string16 domain = url_formatter::FormatUrl(
+      data.url,
+      url_formatter::kFormatUrlOmitUsernamePassword |
+          url_formatter::kFormatUrlOmitHTTPS |
+          url_formatter::kFormatUrlOmitHTTP |
+          url_formatter::kFormatUrlOmitTrailingSlashOnBareHostname |
+          url_formatter::kFormatUrlOmitTrivialSubdomains |
+          url_formatter::kFormatUrlTrimAfterHost,
+      net::UnescapeRule::NORMAL, nullptr, nullptr, nullptr);
+  domain_label_->SetText(domain);
+}
+
+void TabHoverCardBubbleView::UpdateCardAnchor(View* tab) {
+  views::BubbleDialogDelegateView::SetAnchorView(tab);
+}
+
+int TabHoverCardBubbleView::GetDialogButtons() const {
+  return ui::DIALOG_BUTTON_NONE;
+}
+
+gfx::Size TabHoverCardBubbleView::CalculatePreferredSize() const {
+  const gfx::Size size = BubbleDialogDelegateView::CalculatePreferredSize();
+  return gfx::Size(240, size.height());
+}
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h
new file mode 100644
index 0000000..c828a38
--- /dev/null
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h
@@ -0,0 +1,47 @@
+// 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 CHROME_BROWSER_UI_VIEWS_TABS_TAB_HOVER_CARD_BUBBLE_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_TABS_TAB_HOVER_CARD_BUBBLE_VIEW_H_
+
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
+
+namespace views {
+class Label;
+class Widget;
+}  // namespace views
+struct TabRendererData;
+
+// Dialog that displays an informational hover card containing page information.
+class TabHoverCardBubbleView : public views::BubbleDialogDelegateView {
+ public:
+  TabHoverCardBubbleView(views::View* anchor_view, TabRendererData data);
+
+  ~TabHoverCardBubbleView() override;
+
+  // Creates (if not already created) and shows the widget.
+  void Show();
+
+  void Hide();
+
+  // Updates and formats title and domain with given data.
+  void UpdateCardContent(TabRendererData data);
+
+  // Updates where the card should be anchored.
+  void UpdateCardAnchor(View* tab);
+
+  // BubbleDialogDelegateView:
+  int GetDialogButtons() const override;
+
+ private:
+  friend class TabHoverCardBubbleViewBrowserTest;
+
+  views::Widget* widget_ = nullptr;
+  views::Label* title_label_;
+  views::Label* domain_label_;
+
+  gfx::Size CalculatePreferredSize() const override;
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_TABS_TAB_HOVER_CARD_BUBBLE_VIEW_H_
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_browsertest.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_browsertest.cc
new file mode 100644
index 0000000..9cf3ca5
--- /dev/null
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_browsertest.cc
@@ -0,0 +1,169 @@
+// 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 "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/browser_features.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/test/test_browser_dialog.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/tabs/tab.h"
+#include "chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h"
+#include "chrome/browser/ui/views/tabs/tab_strip.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/widget/widget.h"
+
+using views::Widget;
+
+class TabHoverCardBubbleViewBrowserTest : public DialogBrowserTest {
+ public:
+  TabHoverCardBubbleViewBrowserTest() = default;
+  ~TabHoverCardBubbleViewBrowserTest() override = default;
+
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(features::kTabHoverCards);
+    DialogBrowserTest::SetUp();
+  }
+
+  static TabHoverCardBubbleView* GetHoverCard(const TabStrip* tabstrip) {
+    return tabstrip->hover_card_;
+  }
+
+  static Widget* GetHoverCardWidget(const TabHoverCardBubbleView* hover_card) {
+    return hover_card->widget_;
+  }
+
+  const base::string16& GetHoverCardTitle(
+      const TabHoverCardBubbleView* hover_card) {
+    return hover_card->title_label_->text();
+  }
+
+  const base::string16& GetHoverCardDomain(
+      const TabHoverCardBubbleView* hover_card) {
+    return hover_card->domain_label_->text();
+  }
+
+  void MouseExitTabStrip() {
+    TabStrip* tab_strip =
+        BrowserView::GetBrowserViewForBrowser(browser())->tabstrip();
+    ui::MouseEvent stop_hover_event(ui::ET_MOUSE_EXITED, gfx::Point(),
+                                    gfx::Point(), base::TimeTicks(),
+                                    ui::EF_NONE, 0);
+    tab_strip->OnMouseExited(stop_hover_event);
+  }
+
+  void ClickMouseOnTab() {
+    TabStrip* tab_strip =
+        BrowserView::GetBrowserViewForBrowser(browser())->tabstrip();
+    Tab* tab = tab_strip->tab_at(0);
+    ui::MouseEvent click_event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                               base::TimeTicks(), ui::EF_NONE, 0);
+    tab->OnMousePressed(click_event);
+  }
+
+  void HoverMouseOverTabAt(int index) {
+    TabStrip* tab_strip =
+        BrowserView::GetBrowserViewForBrowser(browser())->tabstrip();
+    Tab* tab = tab_strip->tab_at(index);
+    ui::MouseEvent hover_event(ui::ET_MOUSE_ENTERED, gfx::Point(), gfx::Point(),
+                               base::TimeTicks(), ui::EF_NONE, 0);
+    tab->OnMouseEntered(hover_event);
+  }
+
+  // DialogBrowserTest:
+  void ShowUi(const std::string& name) override {
+    TabStrip* tab_strip =
+        BrowserView::GetBrowserViewForBrowser(browser())->tabstrip();
+    Tab* tab = tab_strip->tab_at(0);
+    ui::MouseEvent hover_event(ui::ET_MOUSE_ENTERED, gfx::Point(), gfx::Point(),
+                               base::TimeTicks(), ui::EF_NONE, 0);
+    tab->OnMouseEntered(hover_event);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TabHoverCardBubbleViewBrowserTest);
+
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewBrowserTest,
+                       InvokeUi_tab_hover_card) {
+  ShowAndVerifyUi();
+}
+
+// Verify hover card is visible while hovering and not visible outside of the
+// tabstrip.
+IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewBrowserTest,
+                       WidgetVisibleOnHover) {
+  ShowUi("default");
+  TabStrip* tab_strip =
+      BrowserView::GetBrowserViewForBrowser(browser())->tabstrip();
+  TabHoverCardBubbleView* hover_card = GetHoverCard(tab_strip);
+  Widget* widget = GetHoverCardWidget(hover_card);
+
+  EXPECT_TRUE(widget != nullptr);
+  EXPECT_TRUE(widget->IsVisible());
+  MouseExitTabStrip();
+  EXPECT_FALSE(widget->IsVisible());
+}
+
+// Verify hover card is not visible after clicking on a tab.
+IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewBrowserTest,
+                       WidgetNotVisibleOnClick) {
+  ShowUi("default");
+  TabStrip* tab_strip =
+      BrowserView::GetBrowserViewForBrowser(browser())->tabstrip();
+  TabHoverCardBubbleView* hover_card = GetHoverCard(tab_strip);
+  Widget* widget = GetHoverCardWidget(hover_card);
+
+  EXPECT_TRUE(widget != nullptr);
+  EXPECT_TRUE(widget->IsVisible());
+  ClickMouseOnTab();
+  EXPECT_FALSE(widget->IsVisible());
+}
+
+// Verify title, domain, and anchor are correctly updated when moving hover
+// from one tab to another.
+IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewBrowserTest, WidgetDataUpdate) {
+  TabStrip* tab_strip =
+      BrowserView::GetBrowserViewForBrowser(browser())->tabstrip();
+  TabRendererData newTabData = TabRendererData();
+  newTabData.title = base::UTF8ToUTF16("Test Tab 2");
+  newTabData.url = GURL("http://example.com/this/should/not/be/seen");
+  tab_strip->AddTabAt(1, newTabData, false);
+
+  ShowUi("default");
+  TabHoverCardBubbleView* hover_card = GetHoverCard(tab_strip);
+  Widget* widget = GetHoverCardWidget(hover_card);
+
+  EXPECT_TRUE(widget != nullptr);
+  EXPECT_TRUE(widget->IsVisible());
+  HoverMouseOverTabAt(1);
+  EXPECT_TRUE(widget != nullptr);
+  EXPECT_TRUE(widget->IsVisible());
+  Tab* tab = tab_strip->tab_at(1);
+  EXPECT_EQ(GetHoverCardTitle(hover_card), base::UTF8ToUTF16("Test Tab 2"));
+  EXPECT_EQ(GetHoverCardDomain(hover_card), base::UTF8ToUTF16("example.com"));
+  EXPECT_EQ(hover_card->GetAnchorView(), static_cast<views::View*>(tab));
+}
+
+// Verify inactive window remains inactive when showing a hover card for a tab
+// in the inactive window.
+IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewBrowserTest,
+                       InactiveWindowStaysInactiveOnHover) {
+  ShowUi("default");
+  BrowserView::GetBrowserViewForBrowser(browser())->Deactivate();
+  ASSERT_FALSE(BrowserView::GetBrowserViewForBrowser(browser())->IsActive());
+  TabStrip* tab_strip =
+      BrowserView::GetBrowserViewForBrowser(browser())->tabstrip();
+  Tab* tab = tab_strip->tab_at(0);
+  ui::MouseEvent hover_event(ui::ET_MOUSE_ENTERED, gfx::Point(), gfx::Point(),
+                             base::TimeTicks(), ui::EF_NONE, 0);
+  tab->OnMouseEntered(hover_event);
+  ASSERT_FALSE(BrowserView::GetBrowserViewForBrowser(browser())->IsActive());
+}
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 57e10c7f..54ea15c 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -15,6 +15,7 @@
 #include "base/compiler_specific.h"
 #include "base/containers/adapters.h"
 #include "base/containers/flat_map.h"
+#include "base/feature_list.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
@@ -23,6 +24,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "cc/paint/paint_flags.h"
+#include "chrome/browser/browser_features.h"
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/layout_constants.h"
@@ -33,6 +35,7 @@
 #include "chrome/browser/ui/views/tabs/stacked_tab_strip_layout.h"
 #include "chrome/browser/ui/views/tabs/tab.h"
 #include "chrome/browser/ui/views/tabs/tab_drag_controller.h"
+#include "chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h"
 #include "chrome/browser/ui/views/tabs/tab_strip_controller.h"
 #include "chrome/browser/ui/views/tabs/tab_strip_layout.h"
 #include "chrome/browser/ui/views/tabs/tab_strip_observer.h"
@@ -1158,6 +1161,29 @@
   UpdateStackedLayoutFromMouseEvent(source, event);
 }
 
+void TabStrip::UpdateHoverCard(Tab* tab, bool should_show) {
+  if (!base::FeatureList::IsEnabled(features::kTabHoverCards))
+    return;
+
+  if (!hover_card_ && !should_show)
+    return;
+
+  // The view tracker is used to prevent us from accessing the hover card after
+  // it has been deleted by the owning widget.
+  if (!hover_card_ || !hover_card_view_tracker_.view()) {
+    hover_card_ = new TabHoverCardBubbleView(tab, tab->data());
+    hover_card_view_tracker_.SetView(hover_card_);
+  }
+
+  if (should_show) {
+    hover_card_->UpdateCardContent(tab->data());
+    hover_card_->UpdateCardAnchor(tab);
+    hover_card_->Show();
+  } else {
+    hover_card_->Hide();
+  }
+}
+
 bool TabStrip::ShouldPaintTab(const Tab* tab, float scale, gfx::Path* clip) {
   if (!MaySetClip())
     return true;
@@ -2781,6 +2807,10 @@
   SetResetToShrinkOnExit(true);
 }
 
+void TabStrip::OnMouseExited(const ui::MouseEvent& event) {
+  UpdateHoverCard(nullptr, false);
+}
+
 void TabStrip::OnGestureEvent(ui::GestureEvent* event) {
   SetResetToShrinkOnExit(false);
   switch (event->type()) {
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index cfcd87a2..b335e94 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -34,11 +34,13 @@
 #include "ui/views/view.h"
 #include "ui/views/view_model.h"
 #include "ui/views/view_targeter_delegate.h"
+#include "ui/views/view_tracker.h"
 
 class NewTabButton;
 class StackedTabStripLayout;
 class Tab;
 class TabDragController;
+class TabHoverCardBubbleView;
 class TabStripController;
 class TabStripObserver;
 
@@ -276,6 +278,7 @@
   const Tab* GetAdjacentTab(const Tab* tab, int offset) override;
   void OnMouseEventInTab(views::View* source,
                          const ui::MouseEvent& event) override;
+  void UpdateHoverCard(Tab* tab, bool should_show) override;
   bool ShouldPaintTab(const Tab* tab, float scale, gfx::Path* clip) override;
   int GetStrokeThickness() const override;
   bool CanPaintThrobberToLayer() const override;
@@ -329,6 +332,7 @@
 
   friend class TabDragController;
   friend class TabDragControllerTest;
+  friend class TabHoverCardBubbleViewBrowserTest;
   friend class TabStripTest;
 
   // Used during a drop session of a url. Tracks the position of the drop as
@@ -623,6 +627,7 @@
   void OnMouseCaptureLost() override;
   void OnMouseMoved(const ui::MouseEvent& event) override;
   void OnMouseEntered(const ui::MouseEvent& event) override;
+  void OnMouseExited(const ui::MouseEvent& event) override;
 
   // ui::EventHandler:
   void OnGestureEvent(ui::GestureEvent* event) override;
@@ -647,6 +652,11 @@
   views::ViewModelT<Tab> tabs_;
   TabsClosingMap tabs_closing_map_;
 
+  // The view tracker is used to keep track of if the hover card has been
+  // destroyed by its widget.
+  TabHoverCardBubbleView* hover_card_ = nullptr;
+  views::ViewTracker hover_card_view_tracker_;
+
   std::unique_ptr<TabStripController> controller_;
 
   // The "New Tab" button.
diff --git a/chrome/browser/ui/views/tabs/tab_unittest.cc b/chrome/browser/ui/views/tabs/tab_unittest.cc
index e0e1abca..b213e97 100644
--- a/chrome/browser/ui/views/tabs/tab_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_unittest.cc
@@ -85,6 +85,7 @@
   }
   void OnMouseEventInTab(views::View* source,
                          const ui::MouseEvent& event) override {}
+  void UpdateHoverCard(Tab* tab, bool hovered) override {}
   bool ShouldPaintTab(const Tab* tab, float scale, gfx::Path* clip) override {
     return true;
   }
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index f62f7be..294caa8 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -119,6 +119,7 @@
 #if defined(OS_ANDROID)
 #include "chrome/browser/ui/webui/eoc_internals/eoc_internals_ui.h"
 #include "chrome/browser/ui/webui/explore_sites_internals/explore_sites_internals_ui.h"
+#include "chrome/browser/ui/webui/feed_internals/feed_internals_ui.h"
 #include "chrome/browser/ui/webui/offline/offline_internals_ui.h"
 #include "chrome/browser/ui/webui/snippets_internals/snippets_internals_ui.h"
 #include "chrome/browser/ui/webui/webapks_ui.h"
@@ -534,9 +535,12 @@
   if (url.host_piece() == chrome::kChromeUIOfflineInternalsHost)
     return &NewWebUI<OfflineInternalsUI>;
   if (url.host_piece() == chrome::kChromeUISnippetsInternalsHost &&
-      !profile->IsOffTheRecord() &&
-      !base::FeatureList::IsEnabled(feed::kInterestFeedContentSuggestions))
-    return &NewWebUI<SnippetsInternalsUI>;
+      !profile->IsOffTheRecord()) {
+    if (base::FeatureList::IsEnabled(feed::kInterestFeedContentSuggestions))
+      return &NewWebUI<FeedInternalsUI>;
+    else
+      return &NewWebUI<SnippetsInternalsUI>;
+  }
   if (url.host_piece() == chrome::kChromeUIWebApksHost)
     return &NewWebUI<WebApksUI>;
 #else   // !defined(OS_ANDROID)
diff --git a/chrome/browser/ui/webui/chromeos/system_web_dialog_browsertest.cc b/chrome/browser/ui/webui/chromeos/system_web_dialog_browsertest.cc
index dbb5da7..ab90e90 100644
--- a/chrome/browser/ui/webui/chromeos/system_web_dialog_browsertest.cc
+++ b/chrome/browser/ui/webui/chromeos/system_web_dialog_browsertest.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.h"
 
 #include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/shell_test_api.mojom.h"
+#include "ash/public/interfaces/shell_test_api.test-mojom.h"
 #include "chrome/browser/chromeos/login/login_manager_test.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
 #include "chrome/common/webui_url_constants.h"
diff --git a/chrome/browser/ui/webui/feed_internals/BUILD.gn b/chrome/browser/ui/webui/feed_internals/BUILD.gn
new file mode 100644
index 0000000..a5a9762
--- /dev/null
+++ b/chrome/browser/ui/webui/feed_internals/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("mojo_bindings") {
+  sources = [
+    "feed_internals.mojom",
+  ]
+}
diff --git a/chrome/browser/ui/webui/feed_internals/OWNERS b/chrome/browser/ui/webui/feed_internals/OWNERS
new file mode 100644
index 0000000..ee60b75
--- /dev/null
+++ b/chrome/browser/ui/webui/feed_internals/OWNERS
@@ -0,0 +1,4 @@
+file://components/feed/OWNERS
+
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chrome/browser/ui/webui/feed_internals/feed_internals.mojom b/chrome/browser/ui/webui/feed_internals/feed_internals.mojom
new file mode 100644
index 0000000..30142d68
--- /dev/null
+++ b/chrome/browser/ui/webui/feed_internals/feed_internals.mojom
@@ -0,0 +1,11 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module feed_internals.mojom;
+
+// Browser interface for the page. Consists of calls for data and hooks for
+// interactivity.
+interface PageHandler {
+  // TODO(chouinard): Implement this.
+};
diff --git a/chrome/browser/ui/webui/feed_internals/feed_internals_page_handler.cc b/chrome/browser/ui/webui/feed_internals/feed_internals_page_handler.cc
new file mode 100644
index 0000000..8732a01
--- /dev/null
+++ b/chrome/browser/ui/webui/feed_internals/feed_internals_page_handler.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/ui/webui/feed_internals/feed_internals_page_handler.h"
+
+#include <utility>
+
+FeedInternalsPageHandler::FeedInternalsPageHandler(
+    feed_internals::mojom::PageHandlerRequest request)
+    : binding_(this, std::move(request)) {}
+
+FeedInternalsPageHandler::~FeedInternalsPageHandler() = default;
diff --git a/chrome/browser/ui/webui/feed_internals/feed_internals_page_handler.h b/chrome/browser/ui/webui/feed_internals/feed_internals_page_handler.h
new file mode 100644
index 0000000..9709dfe
--- /dev/null
+++ b/chrome/browser/ui/webui/feed_internals/feed_internals_page_handler.h
@@ -0,0 +1,25 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_FEED_INTERNALS_FEED_INTERNALS_PAGE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_FEED_INTERNALS_FEED_INTERNALS_PAGE_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/feed_internals/feed_internals.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+// Concrete implementation of feed_internals::mojom::PageHandler.
+class FeedInternalsPageHandler : public feed_internals::mojom::PageHandler {
+ public:
+  explicit FeedInternalsPageHandler(
+      feed_internals::mojom::PageHandlerRequest request);
+  ~FeedInternalsPageHandler() override;
+
+ private:
+  mojo::Binding<feed_internals::mojom::PageHandler> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(FeedInternalsPageHandler);
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_FEED_INTERNALS_FEED_INTERNALS_PAGE_HANDLER_H_
diff --git a/chrome/browser/ui/webui/feed_internals/feed_internals_ui.cc b/chrome/browser/ui/webui/feed_internals/feed_internals_ui.cc
new file mode 100644
index 0000000..2dc5355
--- /dev/null
+++ b/chrome/browser/ui/webui/feed_internals/feed_internals_ui.cc
@@ -0,0 +1,40 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/feed_internals/feed_internals_ui.h"
+
+#include <utility>
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/feed_internals/feed_internals_page_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+FeedInternalsUI::FeedInternalsUI(content::WebUI* web_ui)
+    : ui::MojoWebUIController(web_ui) {
+  content::WebUIDataSource* source =
+      content::WebUIDataSource::Create(chrome::kChromeUISnippetsInternalsHost);
+
+  source->AddResourcePath("feed_internals.js", IDR_FEED_INTERNALS_JS);
+  source->AddResourcePath("feed_internals.mojom-lite.js",
+                          IDR_FEED_INTERNALS_MOJO_JS);
+  source->SetDefaultResource(IDR_FEED_INTERNALS_HTML);
+  source->UseGzip();
+
+  Profile* profile = Profile::FromWebUI(web_ui);
+  content::WebUIDataSource::Add(profile, source);
+  // This class is the caller of the callback when an observer interface is
+  // triggered. So this base::Unretained is safe.
+  AddHandlerToRegistry(base::BindRepeating(
+      &FeedInternalsUI::BindFeedInternalsPageHandler, base::Unretained(this)));
+}
+
+FeedInternalsUI::~FeedInternalsUI() = default;
+
+void FeedInternalsUI::BindFeedInternalsPageHandler(
+    feed_internals::mojom::PageHandlerRequest request) {
+  page_handler_ =
+      std::make_unique<FeedInternalsPageHandler>(std::move(request));
+}
diff --git a/chrome/browser/ui/webui/feed_internals/feed_internals_ui.h b/chrome/browser/ui/webui/feed_internals/feed_internals_ui.h
new file mode 100644
index 0000000..7b6bed2
--- /dev/null
+++ b/chrome/browser/ui/webui/feed_internals/feed_internals_ui.h
@@ -0,0 +1,32 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_FEED_INTERNALS_FEED_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_FEED_INTERNALS_FEED_INTERNALS_UI_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/feed_internals/feed_internals.mojom.h"
+#include "chrome/browser/ui/webui/feed_internals/feed_internals_page_handler.h"
+#include "ui/webui/mojo_web_ui_controller.h"
+
+// UI controller for chrome://feed-internals, hooks up a concrete implementation
+// of feed_internals::mojom::PageHandler to requests for that page handler
+// that will come from the frontend.
+class FeedInternalsUI : public ui::MojoWebUIController {
+ public:
+  explicit FeedInternalsUI(content::WebUI* web_ui);
+  ~FeedInternalsUI() override;
+
+ private:
+  void BindFeedInternalsPageHandler(
+      feed_internals::mojom::PageHandlerRequest request);
+
+  std::unique_ptr<FeedInternalsPageHandler> page_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(FeedInternalsUI);
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_FEED_INTERNALS_FEED_INTERNALS_UI_H_
diff --git a/chrome/browser/ui/webui/omnibox/omnibox.mojom b/chrome/browser/ui/webui/omnibox/omnibox.mojom
index 2a1f4bc..6ea786b 100644
--- a/chrome/browser/ui/webui/omnibox/omnibox.mojom
+++ b/chrome/browser/ui/webui/omnibox/omnibox.mojom
@@ -20,11 +20,14 @@
   string fill_into_edit;
   string inline_autocompletion;
   string destination_url;
+  string image;
   string contents;
   string description;
+  string answer;
   string transition;
   bool allowed_to_be_default_match;
   string type;
+  bool is_search_type;
   bool has_tab_match;
   string? associated_keyword;
   string keyword;
@@ -66,4 +69,5 @@
 
 interface OmniboxPage {
   HandleNewAutocompleteResult(OmniboxResult result);
+  HandleAnswerImageData(string image_url, string image_data);
 };
diff --git a/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc b/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc
index f5414e3..c4af0cd9 100644
--- a/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc
+++ b/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc
@@ -10,6 +10,7 @@
 #include <utility>
 
 #include "base/auto_reset.h"
+#include "base/base64.h"
 #include "base/bind.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
@@ -17,6 +18,8 @@
 #include "base/values.h"
 #include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h"
 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
+#include "chrome/browser/bitmap_fetcher/bitmap_fetcher_service.h"
+#include "chrome/browser/bitmap_fetcher/bitmap_fetcher_service_factory.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -32,8 +35,84 @@
 #include "components/search_engines/template_url.h"
 #include "content/public/browser/web_ui.h"
 #include "third_party/metrics_proto/omnibox_event.pb.h"
+#include "ui/gfx/image/image.h"
 
 using bookmarks::BookmarkModel;
+using OmniboxPageImageCallback =
+    base::RepeatingCallback<void(const SkBitmap& bitmap)>;
+
+namespace {
+
+using ImageReciever = std::function<void(std::string)>;
+
+class OmniboxPageImageObserver : public BitmapFetcherService::Observer {
+ public:
+  explicit OmniboxPageImageObserver(ImageReciever image_reciever)
+      : image_reciever_(image_reciever) {}
+
+  void OnImageChanged(BitmapFetcherService::RequestId request_id,
+                      const SkBitmap& bitmap) override {
+    auto data = gfx::Image::CreateFrom1xBitmap(bitmap).As1xPNGBytes();
+    std::string base_64;
+    base::Base64Encode(base::StringPiece(data->front_as<char>(), data->size()),
+                       &base_64);
+    const char kDataUrlPrefix[] = "data:image/png;base64,";
+    std::string data_url = GURL(kDataUrlPrefix + base_64).spec();
+    image_reciever_(data_url);
+  }
+
+ private:
+  const ImageReciever image_reciever_;
+
+  DISALLOW_COPY_AND_ASSIGN(OmniboxPageImageObserver);
+};
+
+std::string SuggestionAnswerTypeToString(int answer_type) {
+  switch (answer_type) {
+    case SuggestionAnswer::ANSWER_TYPE_INVALID:
+      return "invalid";
+    case SuggestionAnswer::ANSWER_TYPE_DICTIONARY:
+      return "dictionary";
+    case SuggestionAnswer::ANSWER_TYPE_FINANCE:
+      return "finance";
+    case SuggestionAnswer::ANSWER_TYPE_KNOWLEDGE_GRAPH:
+      return "knowledge graph";
+    case SuggestionAnswer::ANSWER_TYPE_LOCAL:
+      return "local";
+    case SuggestionAnswer::ANSWER_TYPE_SPORTS:
+      return "sports";
+    case SuggestionAnswer::ANSWER_TYPE_SUNRISE:
+      return "sunrise";
+    case SuggestionAnswer::ANSWER_TYPE_TRANSLATION:
+      return "translation";
+    case SuggestionAnswer::ANSWER_TYPE_WEATHER:
+      return "weather";
+    case SuggestionAnswer::ANSWER_TYPE_WHEN_IS:
+      return "when is";
+    case SuggestionAnswer::ANSWER_TYPE_CURRENCY:
+      return "currency";
+    case SuggestionAnswer::ANSWER_TYPE_LOCAL_TIME:
+      return "local time";
+    case SuggestionAnswer::ANSWER_TYPE_PLAY_INSTALL:
+      return "play install";
+    default:
+      return std::to_string(answer_type);
+  }
+}
+
+std::string SuggestionAnswerImageLineToString(
+    const SuggestionAnswer::ImageLine& image_line) {
+  std::string string;
+  for (auto text_field : image_line.text_fields())
+    string += base::UTF16ToUTF8(text_field.text());
+  if (image_line.additional_text())
+    string += " " + base::UTF16ToUTF8(image_line.additional_text()->text());
+  if (image_line.status_text())
+    string += " " + base::UTF16ToUTF8(image_line.status_text()->text());
+  return string;
+}
+
+}  // namespace
 
 namespace mojo {
 
@@ -69,6 +148,7 @@
     result->inline_autocompletion =
         base::UTF16ToUTF8(input.inline_autocompletion);
     result->destination_url = input.destination_url.spec();
+    result->image = input.ImageUrl().spec().c_str();
     result->contents = base::UTF16ToUTF8(input.contents);
     // At this time, we're not bothering to send along the long vector that
     // represent contents classification.  i.e., for each character, what
@@ -77,10 +157,18 @@
     // At this time, we're not bothering to send along the long vector that
     // represents description classification.  i.e., for each character, what
     // type of text it is.
+    if (input.answer) {
+      result->answer =
+          SuggestionAnswerImageLineToString(input.answer->first_line()) +
+          " / " +
+          SuggestionAnswerImageLineToString(input.answer->second_line()) +
+          " / " + SuggestionAnswerTypeToString(input.answer->type());
+    }
     result->transition =
         ui::PageTransitionGetCoreTransitionString(input.transition);
     result->allowed_to_be_default_match = input.allowed_to_be_default_match;
     result->type = AutocompleteMatchType::ToString(input.type);
+    result->is_search_type = AutocompleteMatch::IsSearchType(input.type);
     result->has_tab_match = input.has_tab_match;
     if (input.associated_keyword.get() != NULL) {
       result->associated_keyword =
@@ -166,7 +254,72 @@
     }
   }
 
+  // Obtain a vector of all image urls required.
+  std::vector<std::string> image_urls;
+  for (size_t i = 0; i < result->combined_results.size(); ++i)
+    image_urls.push_back(result->combined_results[i]->image);
+  for (size_t i = 0; i < result->results_by_provider.size(); ++i) {
+    const mojom::AutocompleteResultsForProvider& result_by_provider =
+        *result->results_by_provider[i];
+    for (size_t j = 0; j < result_by_provider.results.size(); ++j)
+      image_urls.push_back(result_by_provider.results[j]->image);
+  }
+
   page_->HandleNewAutocompleteResult(std::move(result));
+
+  // Fill in image data
+  BitmapFetcherService* image_service =
+      BitmapFetcherServiceFactory::GetForBrowserContext(profile_);
+  for (std::string image_url : image_urls) {
+    const ImageReciever handleAnswerImageData = [this,
+                                                 image_url](std::string data) {
+      page_->HandleAnswerImageData(image_url, data);
+    };
+    if (!image_url.empty()) {
+      // TODO(jdonnelly, rhalavati, manukh): Create a helper function with
+      // Callback to create annotation and pass it to image_service, merging
+      // the annotations in omnibox_page_handler.cc, chrome_omnibox_client.cc,
+      // and chrome_autocomplete_provider_client.cc.
+      auto traffic_annotation = net::DefineNetworkTrafficAnnotation(
+          "omnibox_debug_results_change", R"(
+          semantics {
+            sender: "Omnibox"
+            description:
+              "Chromium provides answers in the suggestion list for "
+              "certain queries that user types in the omnibox. This request "
+              "retrieves a small image (for example, an icon illustrating "
+              "the current weather conditions) when this can add information "
+              "to an answer."
+            trigger:
+              "Change of results for the query typed by the user in the "
+              "omnibox."
+            data:
+              "The only data sent is the path to an image. No user data is "
+              "included, although some might be inferrable (e.g. whether the "
+              "weather is sunny or rainy in the user's current location) "
+              "from the name of the image in the path."
+            destination: WEBSITE
+          }
+          policy {
+            cookies_allowed: YES
+            cookies_store: "user"
+            setting:
+              "You can enable or disable this feature via 'Use a prediction "
+              "service to help complete searches and URLs typed in the "
+              "address bar.' in Chromium's settings under Advanced. The "
+              "feature is enabled by default."
+            chrome_policy {
+              SearchSuggestEnabled {
+                  policy_options {mode: MANDATORY}
+                  SearchSuggestEnabled: false
+              }
+            }
+          })");
+      image_service->RequestImage(
+          GURL(image_url), new OmniboxPageImageObserver(handleAnswerImageData),
+          traffic_annotation);
+    }
+  }
 }
 
 bool OmniboxPageHandler::LookupIsTypedHost(const base::string16& host,
diff --git a/chrome/browser/vr/BUILD.gn b/chrome/browser/vr/BUILD.gn
index ca41231..82b0a80 100644
--- a/chrome/browser/vr/BUILD.gn
+++ b/chrome/browser/vr/BUILD.gn
@@ -197,10 +197,7 @@
     "//skia",
   ]
 
-  defines = [
-    "VR_IMPLEMENTATION",
-    "VR_UI_IMPLEMENTATION",
-  ]
+  defines = [ "VR_UI_IMPLEMENTATION" ]
 
   if (use_command_buffer) {
     sources += [
@@ -258,6 +255,7 @@
     "sounds_manager_audio_delegate.h",
     "ui_factory.cc",
     "ui_factory.h",
+    "vr_export.h",
     "vr_web_contents_observer.cc",
     "vr_web_contents_observer.h",
   ]
@@ -317,10 +315,7 @@
     deps += [ "//services/ws/public/cpp/gpu" ]
   }
 
-  defines = [
-    "VR_IMPLEMENTATION",
-    "VR_UI_IMPLEMENTATION",
-  ]
+  defines = [ "VR_IMPLEMENTATION" ]
 
   if (!use_command_buffer) {
     sources += [
@@ -333,7 +328,7 @@
 
 # vr_base contains common dependencies of vr_common and vr_ui. It exists because
 # vr_ui must not depend on vr_common.
-source_set("vr_base") {
+component("vr_base") {
   sources = [
     "assets_component_update_status.h",
     "assets_load_status.h",
@@ -392,7 +387,7 @@
     "ui_support.h",
     "ui_test_input.h",
     "ui_unsupported_mode.h",
-    "vr_export.h",
+    "vr_base_export.h",
     "vr_geometry_util.cc",
     "vr_geometry_util.h",
     "vr_gl_util.cc",
@@ -404,10 +399,7 @@
     "keyboard_delegate_for_testing.h",
   ]
 
-  defines = [
-    "VR_IMPLEMENTATION",
-    "VR_UI_IMPLEMENTATION",
-  ]
+  defines = [ "VR_BASE_IMPLEMENTATION" ]
 
   public_deps = [
     ":vr_build_features",
diff --git a/chrome/browser/vr/assets_loader.h b/chrome/browser/vr/assets_loader.h
index fc02813..cddcf5d8 100644
--- a/chrome/browser/vr/assets_loader.h
+++ b/chrome/browser/vr/assets_loader.h
@@ -13,7 +13,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/version.h"
 #include "chrome/browser/vr/assets_load_status.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 
 namespace base {
 class DictionaryValue;
@@ -40,7 +40,7 @@
 // component will be made available on a different thread than the asset load
 // request. Internally, the function calls will be posted on the main thread.
 // The asset load may be performed on a worker thread.
-class VR_EXPORT AssetsLoader {
+class VR_BASE_EXPORT AssetsLoader {
  public:
   typedef base::OnceCallback<void(AssetsLoadStatus status,
                                   std::unique_ptr<Assets> assets,
diff --git a/chrome/browser/vr/browser_ui_interface.h b/chrome/browser/vr/browser_ui_interface.h
index db96570..67dd786 100644
--- a/chrome/browser/vr/browser_ui_interface.h
+++ b/chrome/browser/vr/browser_ui_interface.h
@@ -11,7 +11,7 @@
 #include "chrome/browser/vr/model/capturing_state_model.h"
 #include "chrome/browser/vr/model/web_vr_model.h"
 #include "chrome/browser/vr/ui_unsupported_mode.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 #include "components/security_state/core/security_state.h"
 
 namespace base {
@@ -28,7 +28,7 @@
 // The browser communicates state changes to the VR UI via this interface.
 // A GL thread would also implement this interface to provide a convenient way
 // to call these methods from the main thread.
-class VR_EXPORT BrowserUiInterface {
+class VR_BASE_EXPORT BrowserUiInterface {
  public:
   virtual ~BrowserUiInterface() {}
 
diff --git a/chrome/browser/vr/fov_rectangle.h b/chrome/browser/vr/fov_rectangle.h
index 39f2a5ab..aff74c6 100644
--- a/chrome/browser/vr/fov_rectangle.h
+++ b/chrome/browser/vr/fov_rectangle.h
@@ -8,7 +8,7 @@
 #include <utility>
 
 #include "chrome/browser/vr/gl_texture_location.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 
 namespace vr {
 
diff --git a/chrome/browser/vr/input_event.h b/chrome/browser/vr/input_event.h
index cac699c9..87698bb3 100644
--- a/chrome/browser/vr/input_event.h
+++ b/chrome/browser/vr/input_event.h
@@ -6,12 +6,12 @@
 #define CHROME_BROWSER_VR_INPUT_EVENT_H_
 
 #include "base/time/time.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 #include "ui/gfx/geometry/point_f.h"
 
 namespace vr {
 
-class VR_EXPORT InputEvent {
+class VR_BASE_EXPORT InputEvent {
  public:
   enum Type {
     kTypeUndefined = -1,
diff --git a/chrome/browser/vr/keyboard_delegate.h b/chrome/browser/vr/keyboard_delegate.h
index 6de0daa..8c8dabc5 100644
--- a/chrome/browser/vr/keyboard_delegate.h
+++ b/chrome/browser/vr/keyboard_delegate.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_VR_KEYBOARD_DELEGATE_H_
 
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 
 namespace gfx {
 class Point3F;
@@ -20,7 +20,7 @@
 struct CameraModel;
 struct TextInputInfo;
 
-class VR_EXPORT KeyboardDelegate {
+class VR_BASE_EXPORT KeyboardDelegate {
  public:
   virtual ~KeyboardDelegate() {}
 
diff --git a/chrome/browser/vr/keyboard_delegate_for_testing.h b/chrome/browser/vr/keyboard_delegate_for_testing.h
index 5bf9dbc..e78485f 100644
--- a/chrome/browser/vr/keyboard_delegate_for_testing.h
+++ b/chrome/browser/vr/keyboard_delegate_for_testing.h
@@ -11,6 +11,7 @@
 #include "chrome/browser/vr/keyboard_delegate.h"
 #include "chrome/browser/vr/model/text_input_info.h"
 #include "chrome/browser/vr/ui_test_input.h"
+#include "chrome/browser/vr/vr_base_export.h"
 
 namespace gfx {
 class Point3F;
@@ -22,7 +23,7 @@
 class KeyboardUiInterface;
 struct CameraModel;
 
-class KeyboardDelegateForTesting : public KeyboardDelegate {
+class VR_BASE_EXPORT KeyboardDelegateForTesting : public KeyboardDelegate {
  public:
   KeyboardDelegateForTesting();
   ~KeyboardDelegateForTesting() override;
diff --git a/chrome/browser/vr/keyboard_ui_interface.h b/chrome/browser/vr/keyboard_ui_interface.h
index f88e730..2837f4d 100644
--- a/chrome/browser/vr/keyboard_ui_interface.h
+++ b/chrome/browser/vr/keyboard_ui_interface.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_VR_KEYBOARD_UI_INTERFACE_H_
 
 #include "chrome/browser/vr/model/text_input_info.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 
 namespace vr {
 
@@ -14,7 +14,7 @@
 // that we have this interface to restrict the UI API to keyboard-specific
 // callback functions because the keyboard delegate doesn't need access to all
 // of the UI.
-class VR_EXPORT KeyboardUiInterface {
+class VR_BASE_EXPORT KeyboardUiInterface {
  public:
   virtual ~KeyboardUiInterface() {}
   virtual void OnInputEdited(const EditedText& info) = 0;
diff --git a/chrome/browser/vr/metrics/metrics_helper.h b/chrome/browser/vr/metrics/metrics_helper.h
index 59bb6e8..d1ade64 100644
--- a/chrome/browser/vr/metrics/metrics_helper.h
+++ b/chrome/browser/vr/metrics/metrics_helper.h
@@ -13,7 +13,7 @@
 #include "chrome/browser/vr/assets_component_update_status.h"
 #include "chrome/browser/vr/assets_load_status.h"
 #include "chrome/browser/vr/mode.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 
 namespace base {
 class Version;
@@ -24,7 +24,7 @@
 // Helper to collect VR UMA metrics.
 //
 // For thread-safety, all functions must be called in sequence.
-class VR_EXPORT MetricsHelper {
+class VR_BASE_EXPORT MetricsHelper {
  public:
   MetricsHelper();
   ~MetricsHelper();
diff --git a/chrome/browser/vr/metrics/session_metrics_helper.h b/chrome/browser/vr/metrics/session_metrics_helper.h
index 3e0c907..eb8f0d4d 100644
--- a/chrome/browser/vr/metrics/session_metrics_helper.h
+++ b/chrome/browser/vr/metrics/session_metrics_helper.h
@@ -10,7 +10,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/vr/mode.h"
 #include "chrome/browser/vr/ui_browser_interface.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
@@ -147,7 +147,8 @@
 // metrics that require state monitoring, such as durations, but also tracks
 // data we want attached to that, such as number of videos watched and how the
 // session was started.
-class VR_EXPORT SessionMetricsHelper : public content::WebContentsObserver {
+class VR_BASE_EXPORT SessionMetricsHelper
+    : public content::WebContentsObserver {
  public:
   // Returns the SessionMetricsHelper singleton if it has been created for the
   // WebContents.
diff --git a/chrome/browser/vr/model/assets.h b/chrome/browser/vr/model/assets.h
index 291b131..47c146a 100644
--- a/chrome/browser/vr/model/assets.h
+++ b/chrome/browser/vr/model/assets.h
@@ -8,13 +8,13 @@
 #include <memory>
 
 #include "base/version.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 
 class SkBitmap;
 
 namespace vr {
 
-struct VR_EXPORT Assets {
+struct VR_BASE_EXPORT Assets {
   Assets();
   ~Assets();
 
diff --git a/chrome/browser/vr/model/camera_model.h b/chrome/browser/vr/model/camera_model.h
index fd2c29f..b6e5298 100644
--- a/chrome/browser/vr/model/camera_model.h
+++ b/chrome/browser/vr/model/camera_model.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_VR_MODEL_CAMERA_MODEL_H_
 #define CHROME_BROWSER_VR_MODEL_CAMERA_MODEL_H_
 
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 #include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/transform.h"
 
@@ -17,7 +17,7 @@
   kRightEye,
 };
 
-struct VR_EXPORT CameraModel {
+struct VR_BASE_EXPORT CameraModel {
   EyeType eye_type;
   gfx::Rect viewport;
   gfx::Transform view_matrix;
diff --git a/chrome/browser/vr/model/capturing_state_model.h b/chrome/browser/vr/model/capturing_state_model.h
index 8058c7d..367c960 100644
--- a/chrome/browser/vr/model/capturing_state_model.h
+++ b/chrome/browser/vr/model/capturing_state_model.h
@@ -5,11 +5,11 @@
 #ifndef CHROME_BROWSER_VR_MODEL_CAPTURING_STATE_MODEL_H_
 #define CHROME_BROWSER_VR_MODEL_CAPTURING_STATE_MODEL_H_
 
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 
 namespace vr {
 
-struct VR_EXPORT CapturingStateModel {
+struct VR_BASE_EXPORT CapturingStateModel {
   bool audio_capture_enabled = false;
   bool video_capture_enabled = false;
   bool screen_capture_enabled = false;
diff --git a/chrome/browser/vr/model/controller_model.h b/chrome/browser/vr/model/controller_model.h
index c01b285..d1832f5 100644
--- a/chrome/browser/vr/model/controller_model.h
+++ b/chrome/browser/vr/model/controller_model.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_VR_MODEL_CONTROLLER_MODEL_H_
 
 #include "base/time/time.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 #include "ui/gfx/geometry/point3_f.h"
 #include "ui/gfx/transform.h"
 
@@ -16,7 +16,7 @@
 // platform-specific VR subsystem (e.g., GVR). It is used by both the
 // UiInputManager (for generating gestures), and by the UI for rendering the
 // controller.
-struct VR_EXPORT ControllerModel {
+struct VR_BASE_EXPORT ControllerModel {
   enum ButtonState {
     kUp,
     kDown,
diff --git a/chrome/browser/vr/model/hosted_platform_ui.h b/chrome/browser/vr/model/hosted_platform_ui.h
index 2a0dfe5..b6a5be6e 100644
--- a/chrome/browser/vr/model/hosted_platform_ui.h
+++ b/chrome/browser/vr/model/hosted_platform_ui.h
@@ -6,12 +6,12 @@
 #define CHROME_BROWSER_VR_MODEL_HOSTED_PLATFORM_UI_H_
 
 #include "chrome/browser/vr/platform_ui_input_delegate.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 #include "ui/gfx/geometry/rect_f.h"
 
 namespace vr {
 typedef PlatformUiInputDelegate* PlatformUiInputDelegatePtr;
-struct VR_EXPORT HostedPlatformUi {
+struct VR_BASE_EXPORT HostedPlatformUi {
   bool hosted_ui_enabled = false;
   PlatformUiInputDelegatePtr delegate = nullptr;
   unsigned int texture_id = 0;
diff --git a/chrome/browser/vr/model/location_bar_state.h b/chrome/browser/vr/model/location_bar_state.h
index b8109886..a07ec4f 100644
--- a/chrome/browser/vr/model/location_bar_state.h
+++ b/chrome/browser/vr/model/location_bar_state.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_VR_MODEL_LOCATION_BAR_STATE_H_
 #define CHROME_BROWSER_VR_MODEL_LOCATION_BAR_STATE_H_
 
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 #include "components/security_state/core/security_state.h"
 #include "url/gurl.h"
 
@@ -16,7 +16,7 @@
 namespace vr {
 
 // Passes information obtained from LocationBarModel to the VR UI framework.
-struct VR_EXPORT LocationBarState {
+struct VR_BASE_EXPORT LocationBarState {
  public:
   LocationBarState();
   LocationBarState(const GURL& url,
diff --git a/chrome/browser/vr/model/omnibox_suggestions.h b/chrome/browser/vr/model/omnibox_suggestions.h
index 3f96980..75dd82f 100644
--- a/chrome/browser/vr/model/omnibox_suggestions.h
+++ b/chrome/browser/vr/model/omnibox_suggestions.h
@@ -6,13 +6,13 @@
 #define CHROME_BROWSER_VR_MODEL_OMNIBOX_SUGGESTIONS_H_
 
 #include "base/strings/string16.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "url/gurl.h"
 
 namespace vr {
 
-struct VR_EXPORT Autocompletion {
+struct VR_BASE_EXPORT Autocompletion {
   Autocompletion();
   Autocompletion(const base::string16& new_input,
                  const base::string16& new_suffix);
@@ -28,7 +28,7 @@
   base::string16 suffix;
 };
 
-struct VR_EXPORT OmniboxSuggestion {
+struct VR_BASE_EXPORT OmniboxSuggestion {
   OmniboxSuggestion();
 
   OmniboxSuggestion(const base::string16& new_contents,
@@ -53,7 +53,7 @@
   Autocompletion autocompletion;
 };
 
-struct VR_EXPORT OmniboxSuggestions {
+struct VR_BASE_EXPORT OmniboxSuggestions {
   OmniboxSuggestions();
   ~OmniboxSuggestions();
 
@@ -62,7 +62,7 @@
 
 // This struct contains the minimal set of information required to construct an
 // AutocompleteInput on VR's behalf.
-struct VR_EXPORT AutocompleteRequest {
+struct VR_BASE_EXPORT AutocompleteRequest {
   base::string16 text;
   size_t cursor_position = 0;
   bool prevent_inline_autocomplete = false;
@@ -77,7 +77,7 @@
 };
 
 // This struct represents the current request to the AutocompleteController.
-struct VR_EXPORT AutocompleteStatus {
+struct VR_BASE_EXPORT AutocompleteStatus {
   bool active = false;
   base::string16 input;
 
diff --git a/chrome/browser/vr/model/reticle_model.h b/chrome/browser/vr/model/reticle_model.h
index 4a0c754..46ec333 100644
--- a/chrome/browser/vr/model/reticle_model.h
+++ b/chrome/browser/vr/model/reticle_model.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_VR_MODEL_RETICLE_MODEL_H_
 #define CHROME_BROWSER_VR_MODEL_RETICLE_MODEL_H_
 
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 #include "ui/gfx/geometry/point3_f.h"
 
 namespace vr {
@@ -19,7 +19,7 @@
 // controller's laser. It is computed by the UiInputManager and is used by the
 // input manager in the production of gestures as well as by the Reticle element
 // in the scene.
-struct VR_EXPORT ReticleModel {
+struct VR_BASE_EXPORT ReticleModel {
   gfx::Point3F target_point;
   gfx::PointF target_local_point;
   int target_element_id = 0;
diff --git a/chrome/browser/vr/model/speech_recognition_model.h b/chrome/browser/vr/model/speech_recognition_model.h
index bb77e5f..88b86b9 100644
--- a/chrome/browser/vr/model/speech_recognition_model.h
+++ b/chrome/browser/vr/model/speech_recognition_model.h
@@ -6,11 +6,11 @@
 #define CHROME_BROWSER_VR_MODEL_SPEECH_RECOGNITION_MODEL_H_
 
 #include "base/strings/string16.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 
 namespace vr {
 
-struct VR_EXPORT SpeechRecognitionModel {
+struct VR_BASE_EXPORT SpeechRecognitionModel {
   int speech_recognition_state = 0;
   bool has_or_can_request_audio_permission = true;
   base::string16 recognition_result;
diff --git a/chrome/browser/vr/model/text_input_info.h b/chrome/browser/vr/model/text_input_info.h
index cf47e8e..868863f 100644
--- a/chrome/browser/vr/model/text_input_info.h
+++ b/chrome/browser/vr/model/text_input_info.h
@@ -11,12 +11,12 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/vr/text_edit_action.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 
 namespace vr {
 
 // Represents the state of an editable text field.
-struct VR_EXPORT TextInputInfo {
+struct VR_BASE_EXPORT TextInputInfo {
  public:
   TextInputInfo();
   explicit TextInputInfo(base::string16 t);
@@ -65,7 +65,7 @@
 // A superset of TextInputInfo, consisting of a current and previous text field
 // state.  A keyboard can return this structure, allowing clients to derive
 // deltas in keyboard state.
-struct VR_EXPORT EditedText {
+struct VR_BASE_EXPORT EditedText {
  public:
   EditedText();
   EditedText(const EditedText& other);
diff --git a/chrome/browser/vr/model/web_vr_model.h b/chrome/browser/vr/model/web_vr_model.h
index 476ee70d..43dae76 100644
--- a/chrome/browser/vr/model/web_vr_model.h
+++ b/chrome/browser/vr/model/web_vr_model.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_VR_MODEL_WEB_VR_MODEL_H_
 #define CHROME_BROWSER_VR_MODEL_WEB_VR_MODEL_H_
 
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 
 namespace vr {
 
@@ -35,7 +35,7 @@
   kPromptBluetooth,
 };
 
-struct VR_EXPORT WebVrModel {
+struct VR_BASE_EXPORT WebVrModel {
   WebVrState state = kWebVrNoTimeoutPending;
   bool has_received_permissions = false;
   bool showing_hosted_ui = false;
diff --git a/chrome/browser/vr/platform_ui_input_delegate.h b/chrome/browser/vr/platform_ui_input_delegate.h
index 77e5d395..5daf9ea4 100644
--- a/chrome/browser/vr/platform_ui_input_delegate.h
+++ b/chrome/browser/vr/platform_ui_input_delegate.h
@@ -15,7 +15,7 @@
 #include "chrome/browser/vr/macros.h"
 #include "chrome/browser/vr/model/text_input_info.h"
 #include "chrome/browser/vr/text_edit_action.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace base {
@@ -32,7 +32,7 @@
 
 // This class is responsible for processing all events and gestures for
 // PlatformUiElement.
-class VR_EXPORT PlatformUiInputDelegate {
+class VR_BASE_EXPORT PlatformUiInputDelegate {
  public:
   PlatformUiInputDelegate();
   explicit PlatformUiInputDelegate(PlatformInputHandler* input_handler);
diff --git a/chrome/browser/vr/pose_util.h b/chrome/browser/vr/pose_util.h
index 859f84e..cbf553f 100644
--- a/chrome/browser/vr/pose_util.h
+++ b/chrome/browser/vr/pose_util.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_VR_POSE_UTIL_H_
 #define CHROME_BROWSER_VR_POSE_UTIL_H_
 
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 #include "ui/gfx/geometry/vector3d_f.h"
 
 namespace gfx {
@@ -15,10 +15,10 @@
 namespace vr {
 
 // Provides the direction the head is looking towards as a 3x1 unit vector.
-VR_EXPORT gfx::Vector3dF GetForwardVector(const gfx::Transform& head_pose);
+VR_BASE_EXPORT gfx::Vector3dF GetForwardVector(const gfx::Transform& head_pose);
 
 // Returns a vector heading upward from the viewer's head.
-VR_EXPORT gfx::Vector3dF GetUpVector(const gfx::Transform& head_pose);
+VR_BASE_EXPORT gfx::Vector3dF GetUpVector(const gfx::Transform& head_pose);
 
 }  // namespace vr
 
diff --git a/chrome/browser/vr/render_info.h b/chrome/browser/vr/render_info.h
index 087f9f76..6dac178 100644
--- a/chrome/browser/vr/render_info.h
+++ b/chrome/browser/vr/render_info.h
@@ -6,14 +6,14 @@
 #define CHROME_BROWSER_VR_RENDER_INFO_H_
 
 #include "chrome/browser/vr/model/camera_model.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 #include "ui/gfx/transform.h"
 
 namespace vr {
 
 // Provides information for rendering such as the viewport and view/projection
 // matrix.
-struct VR_EXPORT RenderInfo {
+struct VR_BASE_EXPORT RenderInfo {
   gfx::Transform head_pose;
   CameraModel left_eye_model;
   CameraModel right_eye_model;
diff --git a/chrome/browser/vr/scheduler_ui_interface.h b/chrome/browser/vr/scheduler_ui_interface.h
index c8407f6..d13610d 100644
--- a/chrome/browser/vr/scheduler_ui_interface.h
+++ b/chrome/browser/vr/scheduler_ui_interface.h
@@ -8,11 +8,11 @@
 #include <utility>
 
 #include "chrome/browser/vr/gl_texture_location.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 
 namespace vr {
 
-class VR_EXPORT SchedulerUiInterface {
+class VR_BASE_EXPORT SchedulerUiInterface {
  public:
   virtual ~SchedulerUiInterface() {}
 
diff --git a/chrome/browser/vr/speech_recognizer.h b/chrome/browser/vr/speech_recognizer.h
index e209a5f..3ce7ca7 100644
--- a/chrome/browser/vr/speech_recognizer.h
+++ b/chrome/browser/vr/speech_recognizer.h
@@ -11,7 +11,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 
 namespace content {
 class SpeechRecognitionManager;
@@ -90,7 +90,7 @@
 // SpeechRecognizer is a wrapper around the speech recognition engine that
 // simplifies its use from the UI thread. This class handles all setup/shutdown,
 // collection of results, error cases, and threading.
-class VR_EXPORT SpeechRecognizer : public IOBrowserUIInterface {
+class VR_BASE_EXPORT SpeechRecognizer : public IOBrowserUIInterface {
  public:
   // |shared_url_loader_factory_info| must be for a creating a
   // SharedURLLoaderFactory that can be used on the IO Thread.
diff --git a/chrome/browser/vr/text_edit_action.h b/chrome/browser/vr/text_edit_action.h
index 833373cc..ffa025f 100644
--- a/chrome/browser/vr/text_edit_action.h
+++ b/chrome/browser/vr/text_edit_action.h
@@ -8,7 +8,7 @@
 #include <vector>
 
 #include "base/strings/string16.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 
 namespace vr {
 
@@ -20,7 +20,7 @@
   DELETE_TEXT,
 };
 
-class VR_EXPORT TextEditAction {
+class VR_BASE_EXPORT TextEditAction {
  public:
   explicit TextEditAction(TextEditActionType type);
   TextEditAction(TextEditActionType type,
diff --git a/chrome/browser/vr/text_input_delegate.h b/chrome/browser/vr/text_input_delegate.h
index e9ae347..834a4f5f 100644
--- a/chrome/browser/vr/text_input_delegate.h
+++ b/chrome/browser/vr/text_input_delegate.h
@@ -7,13 +7,13 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 
 namespace vr {
 
 struct TextInputInfo;
 
-class VR_EXPORT TextInputDelegate {
+class VR_BASE_EXPORT TextInputDelegate {
  public:
   TextInputDelegate();
   virtual ~TextInputDelegate();
diff --git a/chrome/browser/vr/ui_browser_interface.h b/chrome/browser/vr/ui_browser_interface.h
index 9635da9..757ec3a 100644
--- a/chrome/browser/vr/ui_browser_interface.h
+++ b/chrome/browser/vr/ui_browser_interface.h
@@ -8,7 +8,7 @@
 #include "chrome/browser/vr/exit_vr_prompt_choice.h"
 #include "chrome/browser/vr/model/omnibox_suggestions.h"
 #include "chrome/browser/vr/ui_unsupported_mode.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 #include "ui/gfx/geometry/size_f.h"
 #include "url/gurl.h"
 
@@ -23,7 +23,7 @@
 
 // An interface for the VR UI to communicate with VrShell. Many of the functions
 // in this interface are proxies to methods on VrShell.
-class VR_EXPORT UiBrowserInterface {
+class VR_BASE_EXPORT UiBrowserInterface {
  public:
   virtual ~UiBrowserInterface() = default;
 
diff --git a/chrome/browser/vr/ui_initial_state.h b/chrome/browser/vr/ui_initial_state.h
index c8be12c..2f64f894 100644
--- a/chrome/browser/vr/ui_initial_state.h
+++ b/chrome/browser/vr/ui_initial_state.h
@@ -5,13 +5,13 @@
 #ifndef CHROME_BROWSER_VR_UI_INITIAL_STATE_H_
 #define CHROME_BROWSER_VR_UI_INITIAL_STATE_H_
 
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 
 namespace vr {
 
 // This class describes the initial state of a UI, and may be used by a UI
 // instances owner to specify a custom state on startup.
-struct VR_EXPORT UiInitialState {
+struct VR_BASE_EXPORT UiInitialState {
   UiInitialState();
   UiInitialState(const UiInitialState& other);
   bool in_web_vr = false;
diff --git a/chrome/browser/vr/ui_support.h b/chrome/browser/vr/ui_support.h
index c9183c61..e1e819c 100644
--- a/chrome/browser/vr/ui_support.h
+++ b/chrome/browser/vr/ui_support.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_VR_UI_SUPPORT_H_
 #define CHROME_BROWSER_VR_UI_SUPPORT_H_
 
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 #include "components/url_formatter/url_formatter.h"
 #include "third_party/icu/source/common/unicode/uchar.h"
 #include "third_party/icu/source/common/unicode/uscript.h"
@@ -20,10 +20,10 @@
 // must supply wrappers for use by the module.  For libraries that require only
 // one or two methods, it's more efficient to make wrappers.
 
-VR_EXPORT UScriptCode UScriptGetScript(UChar32 codepoint, UErrorCode* err);
+VR_BASE_EXPORT UScriptCode UScriptGetScript(UChar32 codepoint, UErrorCode* err);
 
-VR_EXPORT base::string16 FormatUrlForVr(const GURL& gurl,
-                                        url::Parsed* new_parsed);
+VR_BASE_EXPORT base::string16 FormatUrlForVr(const GURL& gurl,
+                                             url::Parsed* new_parsed);
 
 }  // namespace vr
 
diff --git a/chrome/browser/vr/vr_base_export.h b/chrome/browser/vr/vr_base_export.h
new file mode 100644
index 0000000..8fb9b51
--- /dev/null
+++ b/chrome/browser/vr/vr_base_export.h
@@ -0,0 +1,29 @@
+// 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 CHROME_BROWSER_VR_VR_BASE_EXPORT_H_
+#define CHROME_BROWSER_VR_VR_BASE_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(VR_BASE_IMPLEMENTATION)
+#define VR_BASE_EXPORT __declspec(dllexport)
+#else
+#define VR_BASE_EXPORT __declspec(dllimport)
+#endif  // defined(VR_BASE_IMPLEMENTATION)
+
+#else  // defined(WIN32)
+#if defined(VR_BASE_IMPLEMENTATION)
+#define VR_BASE_EXPORT __attribute__((visibility("default")))
+#else
+#define VR_BASE_EXPORT
+#endif  // defined(VR_BASE_IMPLEMENTATION)
+#endif
+
+#else  // defined(COMPONENT_BUILD)
+#define VR_BASE_EXPORT
+#endif
+
+#endif  // CHROME_BROWSER_VR_VR_BASE_EXPORT_H_
diff --git a/chrome/browser/vr/vr_geometry_util.h b/chrome/browser/vr/vr_geometry_util.h
index 71dd25f2..0a8674f 100644
--- a/chrome/browser/vr/vr_geometry_util.h
+++ b/chrome/browser/vr/vr_geometry_util.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_VR_VR_GEOMETRY_UTIL_H_
 #define CHROME_BROWSER_VR_VR_GEOMETRY_UTIL_H_
 
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace gfx {
@@ -17,14 +17,15 @@
 
 namespace vr {
 
-VR_EXPORT gfx::Rect CalculatePixelSpaceRect(const gfx::Size& texture_size,
-                                            const gfx::RectF& texture_rect);
+VR_BASE_EXPORT gfx::Rect CalculatePixelSpaceRect(
+    const gfx::Size& texture_size,
+    const gfx::RectF& texture_rect);
 
 // Returns the normalized size of the element projected into screen space.
 // If (1, 1) the element fills the entire buffer.
-VR_EXPORT gfx::SizeF CalculateScreenSize(const gfx::Transform& proj_matrix,
-                                         float distance,
-                                         const gfx::SizeF& size);
+VR_BASE_EXPORT gfx::SizeF CalculateScreenSize(const gfx::Transform& proj_matrix,
+                                              float distance,
+                                              const gfx::SizeF& size);
 
 }  // namespace vr
 
diff --git a/chrome/browser/vr/vr_gl_util.h b/chrome/browser/vr/vr_gl_util.h
index 85ee60d..c9e90582 100644
--- a/chrome/browser/vr/vr_gl_util.h
+++ b/chrome/browser/vr/vr_gl_util.h
@@ -9,7 +9,7 @@
 #include <string>
 
 #include "chrome/browser/vr/gl_bindings.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_base_export.h"
 #include "third_party/skia/include/core/SkColor.h"
 
 #define SHADER(Src) "#version 100\n" #Src
@@ -23,27 +23,28 @@
 
 namespace vr {
 
-VR_EXPORT std::array<float, 16> MatrixToGLArray(const gfx::Transform& matrix);
+VR_BASE_EXPORT std::array<float, 16> MatrixToGLArray(
+    const gfx::Transform& matrix);
 
 // Compile a shader.
-VR_EXPORT GLuint CompileShader(GLenum shader_type,
-                               const GLchar* shader_source,
-                               std::string& error);
+VR_BASE_EXPORT GLuint CompileShader(GLenum shader_type,
+                                    const GLchar* shader_source,
+                                    std::string& error);
 
 // Compile and link a program.
-VR_EXPORT GLuint CreateAndLinkProgram(GLuint vertex_shader_handle,
-                                      GLuint fragment_shader_handle,
-                                      std::string& error);
+VR_BASE_EXPORT GLuint CreateAndLinkProgram(GLuint vertex_shader_handle,
+                                           GLuint fragment_shader_handle,
+                                           std::string& error);
 
 // Sets default texture parameters given a texture type.
-VR_EXPORT void SetTexParameters(GLenum texture_type);
+VR_BASE_EXPORT void SetTexParameters(GLenum texture_type);
 
 // Sets color uniforms given an SkColor.
-VR_EXPORT void SetColorUniform(GLuint handle, SkColor c);
+VR_BASE_EXPORT void SetColorUniform(GLuint handle, SkColor c);
 
 // Sets color uniforms (but not alpha) given an SkColor. The alpha is assumed to
 // be 1.0 in this case.
-VR_EXPORT void SetOpaqueColorUniform(GLuint handle, SkColor c);
+VR_BASE_EXPORT void SetOpaqueColorUniform(GLuint handle, SkColor c);
 
 }  // namespace vr
 
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index d5c530d..7fb2b3d 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -432,6 +432,13 @@
 const base::Feature kUseNewAcceptLanguageHeader{
     "UseNewAcceptLanguageHeader", base::FEATURE_ENABLED_BY_DEFAULT};
 
+#if defined(OS_CHROMEOS)
+// Enables usage of Parent Access Code to authorize certain actions on child
+// user device.
+const base::Feature kParentAccessCode{"ParentAccessCode",
+                                      base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+
 // Delegate permissions to cross-origin iframes when the feature has been
 // allowed by feature policy.
 const base::Feature kPermissionDelegation{"PermissionDelegation",
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 125efbf99..323b99a 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -289,6 +289,11 @@
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kUseNewAcceptLanguageHeader;
 
+#if defined(OS_CHROMEOS)
+COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::Feature kParentAccessCode;
+#endif
+
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kPermissionDelegation;
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index afd7fc3..a054566 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -806,6 +806,7 @@
       "../browser/ui/omnibox/lookalike_url_navigation_observer_browsertest.cc",
       "../browser/ui/passwords/google_password_manager_navigation_throttle_browsertest.cc",
       "../browser/ui/tabs/pinned_tab_service_browsertest.cc",
+      "../browser/ui/views/tabs/tab_hover_card_bubble_view_browsertest.cc",
 
       # If this list is used on Android in the future, these browser / speech/*
       # files will probably not be applicable.
diff --git a/chrome/test/base/chrome_render_view_host_test_harness.cc b/chrome/test/base/chrome_render_view_host_test_harness.cc
index 0e1fcd5..89ed72a2 100644
--- a/chrome/test/base/chrome_render_view_host_test_harness.cc
+++ b/chrome/test/base/chrome_render_view_host_test_harness.cc
@@ -61,6 +61,14 @@
     int thread_bundle_options)
     : content::RenderViewHostTestHarness(thread_bundle_options) {}
 
+ChromeRenderViewHostTestHarness::ChromeRenderViewHostTestHarness(
+    base::test::ScopedTaskEnvironment::MainThreadType main_thread_type,
+    base::test::ScopedTaskEnvironment::ExecutionMode execution_control_mode,
+    int thread_bundle_options)
+    : content::RenderViewHostTestHarness(main_thread_type,
+                                         execution_control_mode,
+                                         thread_bundle_options) {}
+
 ChromeRenderViewHostTestHarness::~ChromeRenderViewHostTestHarness() = default;
 
 TestingProfile* ChromeRenderViewHostTestHarness::profile() {
diff --git a/chrome/test/base/chrome_render_view_host_test_harness.h b/chrome/test/base/chrome_render_view_host_test_harness.h
index 7581537..d510965 100644
--- a/chrome/test/base/chrome_render_view_host_test_harness.h
+++ b/chrome/test/base/chrome_render_view_host_test_harness.h
@@ -16,6 +16,10 @@
     : public content::RenderViewHostTestHarness {
  public:
   explicit ChromeRenderViewHostTestHarness(int thread_bundle_options = 0);
+  ChromeRenderViewHostTestHarness(
+      base::test::ScopedTaskEnvironment::MainThreadType main_thread_type,
+      base::test::ScopedTaskEnvironment::ExecutionMode execution_control_mode,
+      int thread_bundle_options = 0);
   ~ChromeRenderViewHostTestHarness() override;
 
   TestingProfile* profile();
diff --git a/chrome/test/base/in_process_browser_test.h b/chrome/test/base/in_process_browser_test.h
index bb17390..86d4414 100644
--- a/chrome/test/base/in_process_browser_test.h
+++ b/chrome/test/base/in_process_browser_test.h
@@ -22,7 +22,6 @@
 
 #if defined(OS_MACOSX)
 #include "ui/base/test/scoped_fake_full_keyboard_access.h"
-#include "ui/base/test/scoped_fake_nswindow_fullscreen.h"
 #endif
 
 namespace base {
@@ -284,12 +283,6 @@
   // more consistent with other platforms, where most views are focusable by
   // default.
   ui::test::ScopedFakeFullKeyboardAccess faked_full_keyboard_access_;
-
-  // Don't allow browser tests to enter real fullscreen - it might trigger races
-  // in WindowServer in test configurations. This is a temporary debugging hack.
-  // TODO(ellyjones): Remove this or make it permanent -
-  // https://crbug.com/828031.
-  ui::test::ScopedFakeNSWindowFullscreen faked_fullscreen_;
 #endif  // OS_MACOSX
 
 #if defined(OS_WIN)
diff --git a/chrome/test/chromedriver/chrome/ui_events.cc b/chrome/test/chromedriver/chrome/ui_events.cc
index 6e1f485a..321e9bbeb 100644
--- a/chrome/test/chromedriver/chrome/ui_events.cc
+++ b/chrome/test/chromedriver/chrome/ui_events.cc
@@ -22,18 +22,16 @@
       y(y),
       modifiers(modifiers),
       buttons(buttons),
-      click_count(click_count) {}
+      click_count(click_count),
+      element_id(std::string()),
+      pointer_type(kMouse) {}
 
 MouseEvent::MouseEvent(const MouseEvent& other) = default;
 
 MouseEvent::~MouseEvent() {}
 
-TouchEvent::TouchEvent(TouchEventType type,
-                       int x,
-                       int y)
-    : type(type),
-      x(x),
-      y(y) {}
+TouchEvent::TouchEvent(TouchEventType type, int x, int y)
+    : type(type), x(x), y(y), element_id(std::string()) {}
 
 TouchEvent::TouchEvent(const TouchEvent& other) = default;
 
@@ -45,7 +43,10 @@
       modified_text(std::string()),
       unmodified_text(std::string()),
       key(std::string()),
-      key_code(ui::VKEY_UNKNOWN) {}
+      key_code(ui::VKEY_UNKNOWN),
+      location(0),
+      code(),
+      is_from_action(false) {}
 
 KeyEvent::KeyEvent(const KeyEvent& that)
     : type(that.type),
@@ -53,7 +54,10 @@
       modified_text(that.modified_text),
       unmodified_text(that.unmodified_text),
       key(that.key),
-      key_code(that.key_code) {}
+      key_code(that.key_code),
+      location(that.location),
+      code(that.code),
+      is_from_action(that.is_from_action) {}
 
 KeyEvent::~KeyEvent() {}
 
@@ -91,6 +95,27 @@
   return this;
 }
 
+KeyEventBuilder* KeyEventBuilder::SetLocation(int location) {
+  key_event_.location = location;
+  return this;
+}
+
+KeyEventBuilder* KeyEventBuilder::SetDefaultKey(const std::string& key) {
+  if (key_event_.key.size() == 0)
+    key_event_.key = key;
+  return this;
+}
+
+KeyEventBuilder* KeyEventBuilder::SetCode(const std::string& code) {
+  key_event_.code = code;
+  return this;
+}
+
+KeyEventBuilder* KeyEventBuilder::SetIsFromAction() {
+  key_event_.is_from_action = true;
+  return this;
+}
+
 KeyEvent KeyEventBuilder::Build() {
   DCHECK(key_event_.type != kInvalidEventType);
   return key_event_;
diff --git a/chrome/test/chromedriver/chrome/ui_events.h b/chrome/test/chromedriver/chrome/ui_events.h
index bf327195..8b9930d 100644
--- a/chrome/test/chromedriver/chrome/ui_events.h
+++ b/chrome/test/chromedriver/chrome/ui_events.h
@@ -29,6 +29,9 @@
   kNoneMouseButton
 };
 
+// Specifies the event's pointer type.
+enum PointerType { kMouse = 0, kPen };
+
 struct MouseEvent {
   MouseEvent(MouseEventType type,
              MouseButton button,
@@ -49,6 +52,7 @@
   // |click_count| should not be negative.
   int click_count;
   std::string element_id;
+  PointerType pointer_type;
 };
 
 // Specifies the type of the touch event.
@@ -100,6 +104,9 @@
   std::string unmodified_text;
   std::string key;
   ui::KeyboardCode key_code;
+  int location;
+  std::string code;
+  bool is_from_action;
 };
 
 class KeyEventBuilder {
@@ -113,6 +120,10 @@
   KeyEventBuilder* SetText(const std::string& unmodified_text,
                            const std::string& modified_text);
   KeyEventBuilder* SetKeyCode(ui::KeyboardCode key_code);
+  KeyEventBuilder* SetLocation(int location);
+  KeyEventBuilder* SetDefaultKey(const std::string& key);
+  KeyEventBuilder* SetCode(const std::string& key);
+  KeyEventBuilder* SetIsFromAction();
   KeyEvent Build();
   void Generate(std::list<KeyEvent>* key_events);
 
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.cc b/chrome/test/chromedriver/chrome/web_view_impl.cc
index ad5b2eb..f3132e92 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.cc
+++ b/chrome/test/chromedriver/chrome/web_view_impl.cc
@@ -122,6 +122,18 @@
   }
 }
 
+const char* GetAsString(PointerType type) {
+  switch (type) {
+    case kMouse:
+      return "mouse";
+    case kPen:
+      return "pen";
+    default:
+      NOTREACHED();
+      return "";
+  }
+}
+
 }  // namespace
 
 WebViewImpl::WebViewImpl(const std::string& id,
@@ -467,6 +479,7 @@
     params.SetString("button", GetAsString(it->button));
     params.SetInteger("buttons", it->buttons);
     params.SetInteger("clickCount", it->click_count);
+    params.SetString("pointerType", GetAsString(it->pointer_type));
     Status status = client_->SendCommand("Input.dispatchMouseEvent", params);
     if (status.IsError())
       return status;
@@ -506,12 +519,28 @@
     params.SetString("text", it->modified_text);
     params.SetString("unmodifiedText", it->unmodified_text);
     params.SetInteger("windowsVirtualKeyCode", it->key_code);
-    ui::DomCode dom_code = ui::UsLayoutKeyboardCodeToDomCode(it->key_code);
-    std::string code = ui::KeycodeConverter::DomCodeToCodeString(dom_code);
+    std::string code;
+    if (it->is_from_action) {
+      code = it->code;
+    } else {
+      ui::DomCode dom_code = ui::UsLayoutKeyboardCodeToDomCode(it->key_code);
+      code = ui::KeycodeConverter::DomCodeToCodeString(dom_code);
+    }
     if (!code.empty())
       params.SetString("code", code);
     if (!it->key.empty())
       params.SetString("key", it->key);
+    else if (it->is_from_action)
+      params.SetString("key", it->modified_text);
+    if (it->location != 0) {
+      // The |location| parameter in DevTools protocol only accepts 1 (left
+      // modifiers) and 2 (right modifiers). For location 3 (numeric keypad),
+      // it is necessary to set the |isKeypad| parameter.
+      if (it->location == 3)
+        params.SetBoolean("isKeypad", true);
+      else
+        params.SetInteger("location", it->location);
+    }
     Status status = client_->SendCommand("Input.dispatchKeyEvent", params);
     if (status.IsError())
       return status;
diff --git a/chrome/test/chromedriver/key_converter.cc b/chrome/test/chromedriver/key_converter.cc
index 612c9cc..e0d0eec 100644
--- a/chrome/test/chromedriver/key_converter.cc
+++ b/chrome/test/chromedriver/key_converter.cc
@@ -9,6 +9,7 @@
 #include "base/format_macros.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversion_utils.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/test/chromedriver/chrome/status.h"
 #include "chrome/test/chromedriver/chrome/ui_events.h"
@@ -27,12 +28,13 @@
   { kAltKeyModifierMask, ui::VKEY_MENU }
 };
 
-// TODO(kkania): Use this in KeyMap.
 // Ordered list of all the key codes corresponding to special WebDriver keys.
-// These WebDriver keys are defined in the Unicode Private Use Area.
-// http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/value
+// These keys are "special" in the sense that their code points are defined by
+// the W3C spec (https://w3c.github.io/webdriver/#dfn-normalised-key-value),
+// and are in the Unicode Private Use Area. All other keys have their code
+// points defined by the Unicode standard.
 const ui::KeyboardCode kSpecialWebDriverKeys[] = {
-    ui::VKEY_UNKNOWN,
+    ui::VKEY_UNKNOWN,   // \uE000
     ui::VKEY_UNKNOWN,
     ui::VKEY_HELP,
     ui::VKEY_BACK,
@@ -48,7 +50,7 @@
     ui::VKEY_SPACE,
     ui::VKEY_PRIOR,    // page up
     ui::VKEY_NEXT,     // page down
-    ui::VKEY_END,
+    ui::VKEY_END,      // \uE010
     ui::VKEY_HOME,
     ui::VKEY_LEFT,
     ui::VKEY_UP,
@@ -64,7 +66,7 @@
     ui::VKEY_NUMPAD3,
     ui::VKEY_NUMPAD4,
     ui::VKEY_NUMPAD5,
-    ui::VKEY_NUMPAD6,
+    ui::VKEY_NUMPAD6,   // \uE020
     ui::VKEY_NUMPAD7,
     ui::VKEY_NUMPAD8,
     ui::VKEY_NUMPAD9,
@@ -80,7 +82,7 @@
     ui::VKEY_UNKNOWN,
     ui::VKEY_UNKNOWN,
     ui::VKEY_UNKNOWN,
-    ui::VKEY_UNKNOWN,
+    ui::VKEY_UNKNOWN,   // \uE030
     ui::VKEY_F1,
     ui::VKEY_F2,
     ui::VKEY_F3,
@@ -92,13 +94,51 @@
     ui::VKEY_F9,
     ui::VKEY_F10,
     ui::VKEY_F11,
-    ui::VKEY_F12};
+    ui::VKEY_F12,
+    ui::VKEY_LWIN,      // meta
+    ui::VKEY_UNKNOWN,
+    ui::VKEY_UNKNOWN,
+    ui::VKEY_DBE_DBCSCHAR,  // \uE040 ZenkakuHankaku
+    ui::VKEY_UNKNOWN,
+    ui::VKEY_UNKNOWN,
+    ui::VKEY_UNKNOWN,
+    ui::VKEY_UNKNOWN,
+    ui::VKEY_UNKNOWN,
+    ui::VKEY_UNKNOWN,
+    ui::VKEY_UNKNOWN,
+    ui::VKEY_UNKNOWN,
+    ui::VKEY_UNKNOWN,
+    ui::VKEY_UNKNOWN,
+    ui::VKEY_UNKNOWN,
+    ui::VKEY_UNKNOWN,
+    ui::VKEY_UNKNOWN,
+    ui::VKEY_UNKNOWN,
+    ui::VKEY_UNKNOWN,
+    ui::VKEY_RSHIFT,    // \uE050
+    ui::VKEY_RCONTROL,
+    ui::VKEY_RMENU,
+    ui::VKEY_RWIN,      // meta
+    ui::VKEY_PRIOR,     // page up
+    ui::VKEY_NEXT,      // page down
+    ui::VKEY_END,
+    ui::VKEY_HOME,
+    ui::VKEY_LEFT,
+    ui::VKEY_UP,
+    ui::VKEY_RIGHT,
+    ui::VKEY_DOWN,
+    ui::VKEY_INSERT,
+    ui::VKEY_DELETE,
+};
 
 const base::char16 kWebDriverNullKey = 0xE000U;
 const base::char16 kWebDriverShiftKey = 0xE008U;
 const base::char16 kWebDriverControlKey = 0xE009U;
 const base::char16 kWebDriverAltKey = 0xE00AU;
 const base::char16 kWebDriverCommandKey = 0xE03DU;
+const base::char16 kWebDriverRightShiftKey = 0xE050U;
+const base::char16 kWebDriverRightControlKey = 0xE051U;
+const base::char16 kWebDriverRightAltKey = 0xE052U;
+const base::char16 kWebDriverRightCommandKey = 0xE053U;
 
 // Returns whether the given key code has a corresponding printable char.
 // Notice: The given key code should be a special WebDriver key code.
@@ -116,6 +156,10 @@
     case kWebDriverControlKey:
     case kWebDriverAltKey:
     case kWebDriverCommandKey:
+    case kWebDriverRightShiftKey:
+    case kWebDriverRightControlKey:
+    case kWebDriverRightAltKey:
+    case kWebDriverRightCommandKey:
       return true;
     default:
       return false;
@@ -168,6 +212,240 @@
   return true;
 }
 
+// The "normalised key value" table from W3C spec
+// (https://w3c.github.io/webdriver/#dfn-normalised-key-value).
+// The code point starts at \uE000 and must increase by 1 with each row,
+// with placeholders (empty strings) used for unassigned code points.
+const int kNormalisedKeyValueBase = 0xE000;
+const char* const kNormalisedKeyValue[] = {
+    "Unidentified",  // \uE000
+    "Cancel",        // \uE001
+    "Help",          // \uE002
+    "Backspace",     // \uE003
+    "Tab",           // \uE004
+    "Clear",         // \uE005
+    "Return",        // \uE006
+    "Enter",         // \uE007
+    "Shift",         // \uE008
+    "Control",       // \uE009
+    "Alt",           // \uE00A
+    "Pause",         // \uE00B
+    "Escape",        // \uE00C
+    " ",             // \uE00D
+    "PageUp",        // \uE00E
+    "PageDown",      // \uE00F
+    "End",           // \uE010
+    "Home",          // \uE011
+    "ArrowLeft",     // \uE012
+    "ArrowUp",       // \uE013
+    "ArrowRight",    // \uE014
+    "ArrowDown",     // \uE015
+    "Insert",        // \uE016
+    "Delete",        // \uE017
+    ";",             // \uE018
+    "=",             // \uE019
+    "0",             // \uE01A
+    "1",             // \uE01B
+    "2",             // \uE01C
+    "3",             // \uE01D
+    "4",             // \uE01E
+    "5",             // \uE01F
+    "6",             // \uE020
+    "7",             // \uE021
+    "8",             // \uE022
+    "9",             // \uE023
+    "*",             // \uE024
+    "+",             // \uE025
+    ",",             // \uE026
+    "-",             // \uE027
+    ".",             // \uE028
+    "/",             // \uE029
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "F1",            // \uE031
+    "F2",            // \uE032
+    "F3",            // \uE033
+    "F4",            // \uE034
+    "F5",            // \uE035
+    "F6",            // \uE036
+    "F7",            // \uE037
+    "F8",            // \uE038
+    "F9",            // \uE039
+    "F10",           // \uE03A
+    "F11",           // \uE03B
+    "F12",           // \uE03C
+    "Meta",          // \uE03D
+    "",
+    "",
+    "ZenkakuHankaku", // \uE040
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Shift",         // \uE050
+    "Control",       // \uE051
+    "Alt",           // \uE052
+    "Meta",          // \uE053
+    "PageUp",        // \uE054
+    "PageDown",      // \uE055
+    "End",           // \uE056
+    "Home",          // \uE057
+    "ArrowLeft",     // \uE058
+    "ArrowUp",       // \uE059
+    "ArrowRight",    // \uE05A
+    "ArrowDown",     // \uE05B
+    "Insert",        // \uE05C
+    "Delete",        // \uE05D
+};
+
+// The "code for key" table (https://w3c.github.io/webdriver/#dfn-code),
+// with the following modifications:
+// * Fixed some inconsistencies in the original table.
+//   See https://github.com/w3c/webdriver/pull/1384.
+// * Replaced "OSLeft" and "OSRight" with "MetaLeft" and "MetaRight", to be
+//   compatible with Chrome.
+//   TODO(johnchen@chromium.org): Find a better way to handle this.
+const struct {
+  base::char16 key;
+  base::char16 alternate_key;
+  std::string code;
+} kCodeForKey[] = {
+    {'`',    '~',    "Backquote"},
+    {'\\',   '|',    "Backslash"},
+    {0xE003, 0,      "Backspace"},
+    {'[',    '{',    "BracketLeft"},
+    {']',    '}',    "BracketRight"},
+    {',',    '<',    "Comma"},
+    {'0',    ')',    "Digit0"},
+    {'1',    '!',    "Digit1"},
+    {'2',    '@',    "Digit2"},
+    {'3',    '#',    "Digit3"},
+    {'4',    '$',    "Digit4"},
+    {'5',    '%',    "Digit5"},
+    {'6',    '^',    "Digit6"},
+    {'7',    '&',    "Digit7"},
+    {'8',    '*',    "Digit8"},
+    {'9',    '(',    "Digit9"},
+    {'=',    '+',    "Equal"},
+    {'<',    '>',    "IntlBackslash"},
+    {'a',    'A',    "KeyA"},
+    {'b',    'B',    "KeyB"},
+    {'c',    'C',    "KeyC"},
+    {'d',    'D',    "KeyD"},
+    {'e',    'E',    "KeyE"},
+    {'f',    'F',    "KeyF"},
+    {'g',    'G',    "KeyG"},
+    {'h',    'H',    "KeyH"},
+    {'i',    'I',    "KeyI"},
+    {'j',    'J',    "KeyJ"},
+    {'k',    'K',    "KeyK"},
+    {'l',    'L',    "KeyL"},
+    {'m',    'M',    "KeyM"},
+    {'n',    'N',    "KeyN"},
+    {'o',    'O',    "KeyO"},
+    {'p',    'P',    "KeyP"},
+    {'q',    'Q',    "KeyQ"},
+    {'r',    'R',    "KeyR"},
+    {'s',    'S',    "KeyS"},
+    {'t',    'T',    "KeyT"},
+    {'u',    'U',    "KeyU"},
+    {'v',    'V',    "KeyV"},
+    {'w',    'W',    "KeyW"},
+    {'x',    'X',    "KeyX"},
+    {'y',    'Y',    "KeyY"},
+    {'z',    'Z',    "KeyZ"},
+    {'-',    '_',    "Minus"},
+    {'.',    '>',    "Period"},
+    {'\'',   '"',    "Quote"},
+    {';',    ':',    "Semicolon"},
+    {'/',    '?',    "Slash"},
+    {0xE00A, 0,      "AltLeft"},
+    {0xE052, 0,      "AltRight"},
+    {0xE009, 0,      "ControlLeft"},
+    {0xE051, 0,      "ControlRight"},
+    {0xE006, 0,      "Enter"},
+    {0xE03D, 0,      "MetaLeft"},
+    {0xE053, 0,      "MetaRight"},
+    {0xE008, 0,      "ShiftLeft"},
+    {0xE050, 0,      "ShiftRight"},
+    {' ',    0xE00D, "Space"},
+    {0xE004, 0,      "Tab"},
+    {0xE017, 0,      "Delete"},
+    {0xE010, 0,      "End"},
+    {0xE002, 0,      "Help"},
+    {0xE011, 0,      "Home"},
+    {0xE016, 0,      "Insert"},
+    {0xE00F, 0,      "PageDown"},
+    {0xE00E, 0,      "PageUp"},
+    {0xE015, 0,      "ArrowDown"},
+    {0xE012, 0,      "ArrowLeft"},
+    {0xE014, 0,      "ArrowRight"},
+    {0xE013, 0,      "ArrowUp"},
+    {0xE00C, 0,      "Escape"},
+    {0xE031, 0,      "F1"},
+    {0xE032, 0,      "F2"},
+    {0xE033, 0,      "F3"},
+    {0xE034, 0,      "F4"},
+    {0xE035, 0,      "F5"},
+    {0xE036, 0,      "F6"},
+    {0xE037, 0,      "F7"},
+    {0xE038, 0,      "F8"},
+    {0xE039, 0,      "F9"},
+    {0xE03A, 0,      "F10"},
+    {0xE03B, 0,      "F11"},
+    {0xE03C, 0,      "F12"},
+    {0xE01A, 0xE05C, "Numpad0"},
+    {0xE01B, 0xE056, "Numpad1"},
+    {0xE01C, 0xE05B, "Numpad2"},
+    {0xE01D, 0xE055, "Numpad3"},
+    {0xE01E, 0xE058, "Numpad4"},
+    {0xE01F, 0,      "Numpad5"},
+    {0xE020, 0xE05A, "Numpad6"},
+    {0xE021, 0xE057, "Numpad7"},
+    {0xE022, 0xE059, "Numpad8"},
+    {0xE023, 0xE054, "Numpad9"},
+    {0xE025, 0,      "NumpadAdd"},
+    {0xE026, 0,      "NumpadComma"},
+    {0xE028, 0xE05D, "NumpadDecimal"},
+    {0xE029, 0,      "NumpadDivide"},
+    {0xE007, 0,      "NumpadEnter"},
+    {0xE024, 0,      "NumpadMultiply"},
+    {0xE027, 0,      "NumpadSubtract"},
+};
+
+// The "key location for key" table from W3C spec
+// (https://w3c.github.io/webdriver/#dfn-key-location). For simplicity, it is
+// implemented as a few 'if' statements, instead of as a true table.
+int GetKeyLocation(uint32_t code_point) {
+  if (code_point >= 0xe007 && code_point <= 0xe00a)
+    return 1;
+  if (code_point >= 0xe01a && code_point <= 0xe029)
+    return 3;
+  if (code_point == 0xe03d)
+    return 1;
+  if (code_point >= 0xe050 && code_point <= 0xe053)
+    return 2;
+  if (code_point >= 0xe054 && code_point <= 0xe05d)
+    return 3;
+  return 0;
+}
+
 }  // namespace
 
 Status ConvertKeysToKeyEvents(const base::string16& client_keys,
@@ -205,19 +483,21 @@
       // Press or release the modifier, and adjust |sticky_modifiers|.
       bool modifier_down = false;
       ui::KeyboardCode key_code = ui::VKEY_UNKNOWN;
-      if (key == kWebDriverShiftKey) {
+      if (key == kWebDriverShiftKey || key == kWebDriverRightShiftKey) {
         sticky_modifiers ^= kShiftKeyModifierMask;
         modifier_down = (sticky_modifiers & kShiftKeyModifierMask) != 0;
         key_code = ui::VKEY_SHIFT;
-      } else if (key == kWebDriverControlKey) {
+      } else if (key == kWebDriverControlKey ||
+                 key == kWebDriverRightControlKey) {
         sticky_modifiers ^= kControlKeyModifierMask;
         modifier_down = (sticky_modifiers & kControlKeyModifierMask) != 0;
         key_code = ui::VKEY_CONTROL;
-      } else if (key == kWebDriverAltKey) {
+      } else if (key == kWebDriverAltKey || key == kWebDriverRightAltKey) {
         sticky_modifiers ^= kAltKeyModifierMask;
         modifier_down = (sticky_modifiers & kAltKeyModifierMask) != 0;
         key_code = ui::VKEY_MENU;
-      } else if (key == kWebDriverCommandKey) {
+      } else if (key == kWebDriverCommandKey ||
+                 key == kWebDriverRightCommandKey) {
         sticky_modifiers ^= kMetaKeyModifierMask;
         modifier_down = (sticky_modifiers & kMetaKeyModifierMask) != 0;
         key_code = ui::VKEY_COMMAND;
@@ -333,3 +613,164 @@
   *modifiers = sticky_modifiers;
   return Status(kOk);
 }
+
+Status ConvertKeyActionToKeyEvent(const base::DictionaryValue* action_object,
+                                  base::DictionaryValue* input_state,
+                                  bool is_key_down,
+                                  std::vector<KeyEvent>* key_events) {
+  std::string raw_key;
+  if (!action_object->GetString("value", &raw_key))
+    return Status(kUnknownError, "missing 'value'");
+
+  int32_t char_index = 0;
+  uint32_t code_point;
+  base::ReadUnicodeCharacter(raw_key.c_str(), raw_key.size(), &char_index,
+                             &code_point);
+
+  std::string key;
+  if (code_point >= kNormalisedKeyValueBase &&
+      code_point < kNormalisedKeyValueBase + base::size(kNormalisedKeyValue)) {
+    key = kNormalisedKeyValue[code_point - kNormalisedKeyValueBase];
+  }
+  if (key.size() == 0)
+    key = raw_key;
+
+  base::DictionaryValue* pressed;
+  if (!input_state->GetDictionary("pressed", &pressed))
+    return Status(kUnknownError, "missing 'pressed'");
+  bool already_pressed = pressed->HasKey(key);
+  if (!is_key_down && !already_pressed)
+    return Status(kOk);
+
+  std::string code;
+  if (code_point != 0) {
+    for (auto& mapping : kCodeForKey) {
+      if (mapping.key == code_point || mapping.alternate_key == code_point) {
+        code = mapping.code;
+        break;
+      }
+    }
+  }
+
+  int modifiers;
+  if (!input_state->GetInteger("modifiers", &modifiers))
+    return Status(kUnknownError, "missing 'modifiers'");
+
+  bool is_modifier_key = false;
+  bool is_special_key = false;
+  bool should_skip = false;
+  std::string unmodified_text, modified_text;
+  ui::KeyboardCode key_code = ui::VKEY_UNKNOWN;
+  std::string error_msg;
+
+  is_modifier_key = IsModifierKey(code_point);
+  if (!is_modifier_key)
+    is_special_key = KeyCodeFromSpecialWebDriverKey(code_point, &key_code);
+
+  if (is_modifier_key) {
+    int updated_modifier;
+    if (code_point == kWebDriverShiftKey) {
+      updated_modifier = kShiftKeyModifierMask;
+      key_code = ui::VKEY_SHIFT;
+    } else if (code_point == kWebDriverRightShiftKey) {
+      updated_modifier = kShiftKeyModifierMask;
+      key_code = ui::VKEY_RSHIFT;
+    } else if (code_point == kWebDriverControlKey) {
+      updated_modifier = kControlKeyModifierMask;
+      key_code = ui::VKEY_CONTROL;
+    } else if (code_point == kWebDriverRightControlKey) {
+      updated_modifier = kControlKeyModifierMask;
+      key_code = ui::VKEY_RCONTROL;
+    } else if (code_point == kWebDriverAltKey) {
+      updated_modifier = kAltKeyModifierMask;
+      key_code = ui::VKEY_MENU;
+    } else if (code_point == kWebDriverRightAltKey) {
+      updated_modifier = kAltKeyModifierMask;
+      key_code = ui::VKEY_RMENU;
+    } else if (code_point == kWebDriverCommandKey) {
+      updated_modifier = kMetaKeyModifierMask;
+      key_code = ui::VKEY_COMMAND;
+    } else if (code_point == kWebDriverRightCommandKey) {
+      updated_modifier = kMetaKeyModifierMask;
+      key_code = ui::VKEY_RWIN;
+    } else {
+      return Status(kUnknownError, "unknown modifier key");
+    }
+
+    if (is_key_down)
+      modifiers |= updated_modifier;
+    else
+      modifiers &= ~updated_modifier;
+
+    input_state->SetInteger("modifiers", modifiers);
+  } else if (is_special_key ||
+             KeyCodeFromShorthandKey(code_point, &key_code, &should_skip)) {
+    if (should_skip)
+      return Status(kOk);
+    if (key_code == ui::VKEY_RETURN) {
+      // For some reason Chrome expects a carriage return for the return key.
+      modified_text = unmodified_text = "\r";
+    } else if (is_special_key && !IsSpecialKeyPrintable(key_code)) {
+      // To prevent char event for special keys like DELETE.
+      modified_text = unmodified_text = std::string();
+    } else {
+      // WebDriver assumes a numpad key should translate to the number,
+      // which requires NumLock to be on with some platforms. This isn't
+      // formally in the spec, but is expected by their tests.
+      int webdriver_modifiers = 0;
+      if (key_code >= ui::VKEY_NUMPAD0 && key_code <= ui::VKEY_NUMPAD9)
+        webdriver_modifiers = kNumLockKeyModifierMask;
+      if (!ConvertKeyCodeToText(key_code, webdriver_modifiers, &unmodified_text,
+                                &error_msg))
+        return Status(kUnknownError, error_msg);
+      if (!ConvertKeyCodeToText(key_code, modifiers | webdriver_modifiers,
+                                &modified_text, &error_msg))
+        return Status(kUnknownError, error_msg);
+    }
+  } else {
+    int necessary_modifiers = 0;
+    ConvertCharToKeyCode(code_point, &key_code, &necessary_modifiers,
+                         &error_msg);
+    if (!error_msg.empty())
+      return Status(kUnknownError, error_msg);
+    if (key_code != ui::VKEY_UNKNOWN) {
+      modifiers |= necessary_modifiers;
+      if (!ConvertKeyCodeToText(key_code, 0, &unmodified_text, &error_msg))
+        return Status(kUnknownError, error_msg);
+      if (!ConvertKeyCodeToText(key_code, modifiers, &modified_text,
+                                &error_msg))
+        return Status(kUnknownError, error_msg);
+      if (unmodified_text.empty() || modified_text.empty()) {
+        // To prevent char event for special cases like CTRL + x (cut).
+        unmodified_text.clear();
+        modified_text.clear();
+      }
+    } else {
+      // Do a best effort and use the raw key we were given.
+      unmodified_text = raw_key;
+      modified_text = raw_key;
+    }
+  }
+
+  if (is_key_down)
+    pressed->SetBoolean(key, true);
+  else
+    pressed->Remove(key, nullptr);
+
+  KeyEventBuilder builder;
+  builder.SetKeyCode(key_code)
+      ->SetModifiers(modifiers)
+      ->SetLocation(GetKeyLocation(code_point))
+      ->SetDefaultKey(key)
+      ->SetCode(code)
+      ->SetIsFromAction();
+  if (!is_modifier_key)
+    builder.SetText(unmodified_text, modified_text);
+  if (is_key_down) {
+    key_events->push_back(builder.SetType(kKeyDownEventType)->Build());
+  } else {
+    key_events->push_back(builder.SetType(kKeyUpEventType)->Build());
+  }
+
+  return Status(kOk);
+}
diff --git a/chrome/test/chromedriver/key_converter.h b/chrome/test/chromedriver/key_converter.h
index ebed6b5..bc912bb9a 100644
--- a/chrome/test/chromedriver/key_converter.h
+++ b/chrome/test/chromedriver/key_converter.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/strings/string16.h"
+#include "base/values.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 
 struct KeyEvent;
@@ -24,4 +25,9 @@
                               int* modifiers,
                               std::list<KeyEvent>* key_events);
 
+Status ConvertKeyActionToKeyEvent(const base::DictionaryValue* action_object,
+                                  base::DictionaryValue* input_state,
+                                  bool is_key_down,
+                                  std::vector<KeyEvent>* client_key_events);
+
 #endif  // CHROME_TEST_CHROMEDRIVER_KEY_CONVERTER_H_
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 5548e4d..81b0a79 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -777,6 +777,74 @@
     self._driver.PerformActions(actions)
     self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
 
+  def testActionsPause(self):
+    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/empty.html'))
+    self._driver.ExecuteScript(
+        '''
+        document.body.innerHTML
+          = "<input type='text' autofocus style='width:100px; height:100px'>";
+        window.events = [];
+        const input = document.getElementsByTagName("input")[0];
+        const listener
+          = e => window.events.push({type: e.type, time: e.timeStamp});
+        input.addEventListener("keydown", listener);
+        input.addEventListener("keyup", listener);
+        input.addEventListener("mousedown", listener);
+        ''')
+
+    # Actions on 3 devices, across 5 ticks, with 200 ms pause at each tick.
+    # Tick   "key" device   "pointer" device  "none" device
+    #    0   "a" key down   move for 200 ms   pause 100 ms
+    #    1   pause 200 ms   pointer down
+    #    2   "a" key up     pointer up        pause 200 ms
+    #    3   "b" key down   pause 200 ms
+    #    4   "b" key up
+    actions = {'actions': [
+        {
+            'type': 'key',
+            'id': 'key',
+            'actions': [
+                {'type': 'keyDown',  'value': 'a'},
+                {'type': 'pause',    'duration': 200},
+                {'type': 'keyUp',    'value': 'a'},
+                {'type': 'keyDown',  'value': 'b'},
+                {'type': 'keyUp',    'value': 'b'},
+            ]
+        },
+        {
+            'type': 'pointer',
+            'id': 'mouse',
+            'actions': [
+                {'type': 'pointerMove',  'duration': 200,  'x': 10,  'y': 10},
+                {'type': 'pointerDown',  'button': 1},
+                {'type': 'pointerUp',    'button': 1},
+                {'type': 'pause',        'duration': 200},
+            ]
+        },
+        {
+            'type': 'none',
+            'id': 'none',
+            'actions': [
+                {'type': 'pause',  'duration': 100},
+                {'type': 'pause'},
+                {'type': 'pause',  'duration': 200},
+            ]
+        }
+    ]}
+
+    self._driver.PerformActions(actions)
+    events = self._driver.ExecuteScript('return window.events')
+    expected_events = ['keydown', 'mousedown', 'keyup', 'keydown', 'keyup']
+    self.assertEquals(len(expected_events), len(events))
+    for i in range(len(events)):
+      self.assertEqual(expected_events[i], events[i]['type'])
+      if i > 0:
+        elapsed_time = events[i]['time'] - events[i-1]['time']
+        self.assertGreaterEqual(elapsed_time, 200)
+        # Elapsed time can be anything above 200 ms. Arbitrarily select 300 ms
+        # as the upper limit.
+        self.assertLessEqual(elapsed_time, 300)
+
   def testPageLoadStrategyIsNormalByDefault(self):
     self.assertEquals('normal',
                       self._driver.capabilities['pageLoadStrategy'])
diff --git a/chrome/test/chromedriver/window_commands.cc b/chrome/test/chromedriver/window_commands.cc
index 329646a..fc359ad9 100644
--- a/chrome/test/chromedriver/window_commands.cc
+++ b/chrome/test/chromedriver/window_commands.cc
@@ -13,6 +13,7 @@
 #include "base/callback.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversion_utils.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
@@ -171,6 +172,14 @@
   }
 }
 
+PointerType StringToPointerType(std::string pointer_type) {
+  CHECK(pointer_type == "pen" || pointer_type == "mouse");
+  if (pointer_type == "pen")
+    return kPen;
+  else
+    return kMouse;
+}
+
 struct Cookie {
   Cookie(const std::string& name,
          const std::string& value,
@@ -346,6 +355,39 @@
   return Status(kOk);
 }
 
+// Implements "compute the tick duration" algorithm from W3C spec
+// (https://w3c.github.io/webdriver/#dfn-computing-the-tick-duration).
+// For convenience, this function computes durations of all ticks, while the
+// original algorithm computes duration of one tick.
+void ComputeTickDurations(std::vector<int>* tick_durations,
+                          const base::ListValue& actions_list) {
+  for (size_t i = 0; i < actions_list.GetSize(); i++) {
+    const base::DictionaryValue* action_sequence = nullptr;
+    actions_list.GetDictionary(i, &action_sequence);
+    const base::ListValue* actions = nullptr;
+    action_sequence->GetList("actions", &actions);
+    std::string type;
+    action_sequence->GetString("sourceType", &type);
+
+    for (size_t j = 0; j < actions->GetSize(); j++) {
+      const base::DictionaryValue* action = nullptr;
+      actions->GetDictionary(j, &action);
+      std::string subtype;
+      action->GetString("subtype", &subtype);
+
+      if (subtype == "pause" ||
+          (type == "pointer" && subtype == "pointerMove")) {
+        if (j >= tick_durations->size())
+          tick_durations->resize(j + 1);
+        int duration = 0;
+        GetOptionalInt(action, "duration", &duration);
+        if (duration > (*tick_durations)[j])
+          (*tick_durations)[j] = duration;
+      }
+    }
+  }
+}
+
 Status ElementInViewCenter(Session* session,
                            WebView* web_view,
                            std::string element_id,
@@ -995,17 +1037,14 @@
     base::DictionaryValue tmp_state;
     tmp_state.SetString("id", id);
     if (type == "key") {
-      std::unique_ptr<base::ListValue> pressed(new base::ListValue);
-      bool alt = false;
-      bool shift = false;
-      bool ctrl = false;
-      bool meta = false;
-
-      tmp_state.SetList("pressed", std::move(pressed));
-      tmp_state.SetBoolean("alt", alt);
-      tmp_state.SetBoolean("shift", shift);
-      tmp_state.SetBoolean("ctrl", ctrl);
-      tmp_state.SetBoolean("meta", meta);
+      // Initialize a key input state object
+      // (https://w3c.github.io/webdriver/#dfn-key-input-state).
+      tmp_state.SetDictionary("pressed",
+                              std::make_unique<base::DictionaryValue>());
+      // For convenience, we use one integer property to encode four Boolean
+      // properties (alt, shift, ctrl, meta) from the spec, using values from
+      // enum KeyModifierMask.
+      tmp_state.SetInteger("modifiers", 0);
     } else if (type == "pointer") {
       std::unique_ptr<base::ListValue> pressed(new base::ListValue);
       int x = 0;
@@ -1065,11 +1104,19 @@
           return status;
       } else {
         std::string key;
-        // TODO: check if key is a single unicode code point
-        if (!action_item->GetString("value", &key)) {
-          return Status(kInvalidArgument,
-                        "'value' must be a single unicode point");
+        bool valid = action_item->GetString("value", &key);
+        if (valid) {
+          // check if key is a single unicode code point
+          int32_t char_index = 0;
+          uint32_t code_point;
+          valid =
+              base::ReadUnicodeCharacter(key.c_str(), key.size(), &char_index,
+                                         &code_point) &&
+              static_cast<std::string::size_type>(char_index + 1) == key.size();
         }
+        if (!valid)
+          return Status(kInvalidArgument,
+                        "'value' must be a single Unicode code point");
         action->SetString("value", key);
       }
     } else if (type == "pointer") {
@@ -1085,7 +1132,7 @@
       action->SetString("subtype", subtype);
 
       if (subtype == "pointerDown" || subtype == "pointerUp") {
-        if (pointer_type == "mouse") {
+        if (pointer_type == "mouse" || pointer_type == "pen") {
           int button;
           if (!action_item->GetInteger("button", &button) || button < 0 ||
               button > 4) {
@@ -1211,36 +1258,19 @@
         action->GetString("subtype", &subtype);
         std::string id;
         action->GetString("id", &id);
+
+        base::DictionaryValue* input_state;
+        if (!session->input_state_table.GetDictionary(id, &input_state))
+          return Status(kUnknownError, "missing input state");
+
         if (subtype == "pause") {
           key_events.push_back(builder.SetType(kPauseEventType)->Build());
         } else {
-          base::DictionaryValue dispatch_params;
-          base::string16 raw_key;
-          action->GetString("value", &raw_key);
-          base::char16 key = raw_key[0];
-          // TODO: understand necessary_modifiers
-          int necessary_modifiers = 0;
-          ui::KeyboardCode key_code = ui::VKEY_UNKNOWN;
-          std::string error_msg;
-          ConvertCharToKeyCode(key, &key_code, &necessary_modifiers,
-                               &error_msg);
-          if (!error_msg.empty())
-            return Status(kUnknownError, error_msg);
+          Status status = ConvertKeyActionToKeyEvent(
+              action, input_state, subtype == "keyDown", &key_events);
 
-          if (subtype == "keyDown")
-            key_events.push_back(builder.SetType(kKeyDownEventType)
-                                     ->SetText(base::UTF16ToUTF8(raw_key),
-                                               base::UTF16ToUTF8(raw_key))
-                                     ->SetKeyCode(key_code)
-                                     ->SetModifiers(0)
-                                     ->Build());
-          else if (subtype == "keyUp")
-            key_events.push_back(builder.SetType(kKeyUpEventType)
-                                     ->SetText(base::UTF16ToUTF8(raw_key),
-                                               base::UTF16ToUTF8(raw_key))
-                                     ->SetKeyCode(key_code)
-                                     ->SetModifiers(0)
-                                     ->Build());
+          if (status.IsError())
+            return status;
         }
       }
       longest_key_list_size =
@@ -1288,7 +1318,7 @@
           }
         }
 
-        if (pointer_type == "mouse") {
+        if (pointer_type == "mouse" || pointer_type == "pen") {
           int click_count = 0;
           if (action_type == "pointerDown" || action_type == "pointerUp") {
             pointer_action->GetString("button", &button_type);
@@ -1298,6 +1328,7 @@
                            StringToMouseButton(button_type), x, y, 0, buttons,
                            click_count);
           event.element_id = element_id;
+          event.pointer_type = StringToPointerType(pointer_type);
           mouse_events.push_back(event);
           if (action_type == "pointerDown")
             buttons |= StringToModifierMouseButton(button_type);
@@ -1317,7 +1348,7 @@
         }
       }
 
-      if (pointer_type == "mouse") {
+      if (pointer_type == "mouse" || pointer_type == "pen") {
         longest_mouse_list_size =
             std::max(mouse_events.size(), longest_mouse_list_size);
         mouse_events_list.push_back(mouse_events);
@@ -1329,9 +1360,12 @@
     }
   }
 
+  std::vector<int> tick_durations;
+  ComputeTickDurations(&tick_durations, actions_list);
+
   size_t max_list_length =
-      std::max(std::max(longest_mouse_list_size, longest_touch_list_size),
-               longest_key_list_size);
+      std::max({longest_mouse_list_size, longest_touch_list_size,
+                longest_key_list_size, tick_durations.size()});
   std::map<std::string, gfx::Point> element_center_point;
   for (size_t i = 0; i < max_list_length; i++) {
     std::list<MouseEvent> dispatch_mouse_events;
@@ -1401,6 +1435,10 @@
       if (status.IsError())
         return status;
     }
+    if (i < tick_durations.size() && tick_durations[i] > 0) {
+      base::PlatformThread::Sleep(
+          base::TimeDelta::FromMilliseconds(tick_durations[i]));
+    }
   }
   return Status(kOk);
 }
diff --git a/chrome/test/data/prerender/prerender_loader_with_unload.html b/chrome/test/data/prerender/prerender_loader_with_unload.html
index 53e3398..cb3a5a2 100644
--- a/chrome/test/data/prerender/prerender_loader_with_unload.html
+++ b/chrome/test/data/prerender/prerender_loader_with_unload.html
@@ -6,10 +6,8 @@
 <script>
   function UnloadHandler() {
     // Signal to the browser the unload happened by hitting a URL. Must
-    // be synchronous so the request isn't cancelled.
-    var xhr = new XMLHttpRequest();
-    xhr.open('GET', '/unload-url', false);
-    xhr.send();
+    // not be cancelled.
+    fetch('/unload-url', {method: 'GET', keepalive: true});
   }
 
   addEventListener('unload', UnloadHandler, false);
diff --git a/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.js b/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.js
index d0d7e78a..8057ee7 100644
--- a/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.js
+++ b/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.js
@@ -22,13 +22,18 @@
     let nativeLayer = null;
 
     /** @override */
+    suiteSetup(function() {
+      print_preview_test_utils.setupTestListenerElement();
+    });
+
+    /** @override */
     setup(function() {
       // Create destinations.
       nativeLayer = new print_preview.NativeLayerStub();
       print_preview.NativeLayer.setInstance(nativeLayer);
       const userInfo = new print_preview.UserInfo();
-      destinationStore = new print_preview.DestinationStore(
-          userInfo, new WebUIListenerTracker());
+      destinationStore =
+          print_preview_test_utils.createDestinationStore(userInfo);
       const localDestinations = [];
       const destinations = print_preview_test_utils.getDestinations(
           nativeLayer, localDestinations);
diff --git a/chrome/test/data/webui/print_preview/destination_dialog_test.js b/chrome/test/data/webui/print_preview/destination_dialog_test.js
index 1f96e5aa..81493ada 100644
--- a/chrome/test/data/webui/print_preview/destination_dialog_test.js
+++ b/chrome/test/data/webui/print_preview/destination_dialog_test.js
@@ -34,13 +34,18 @@
     let recentDestinations = [];
 
     /** @override */
+    suiteSetup(function() {
+      print_preview_test_utils.setupTestListenerElement();
+    });
+
+    /** @override */
     setup(function() {
       // Create data classes
       nativeLayer = new print_preview.NativeLayerStub();
       print_preview.NativeLayer.setInstance(nativeLayer);
       userInfo = new print_preview.UserInfo();
-      destinationStore = new print_preview.DestinationStore(
-          userInfo, new WebUIListenerTracker());
+      destinationStore =
+          print_preview_test_utils.createDestinationStore(userInfo);
       destinations = print_preview_test_utils.getDestinations(
           nativeLayer, localDestinations);
       recentDestinations =
diff --git a/chrome/test/data/webui/print_preview/destination_search_test.js b/chrome/test/data/webui/print_preview/destination_search_test.js
index 39736c9..114ec68 100644
--- a/chrome/test/data/webui/print_preview/destination_search_test.js
+++ b/chrome/test/data/webui/print_preview/destination_search_test.js
@@ -29,13 +29,18 @@
     let nativeLayer = null;
 
     /** @override */
+    suiteSetup(function() {
+      print_preview_test_utils.setupTestListenerElement();
+    });
+
+    /** @override */
     setup(function() {
       // Create data classes
       nativeLayer = new print_preview.NativeLayerStub();
       print_preview.NativeLayer.setInstance(nativeLayer);
       userInfo = new print_preview.UserInfo();
-      destinationStore = new print_preview.DestinationStore(
-          userInfo, new WebUIListenerTracker());
+      destinationStore =
+          print_preview_test_utils.createDestinationStore(userInfo);
       nativeLayer.setLocalDestinationCapabilities(
           print_preview_test_utils.getCddTemplate('FooDevice', 'FooName'));
       destinationStore.init(
diff --git a/chrome/test/data/webui/print_preview/destination_settings_test.js b/chrome/test/data/webui/print_preview/destination_settings_test.js
index b94cb19..67ece56 100644
--- a/chrome/test/data/webui/print_preview/destination_settings_test.js
+++ b/chrome/test/data/webui/print_preview/destination_settings_test.js
@@ -14,6 +14,11 @@
     let destinationSettings = null;
 
     /** @override */
+    suiteSetup(function() {
+      print_preview_test_utils.setupTestListenerElement();
+    });
+
+    /** @override */
     setup(function() {
       PolymerTest.clearBody();
       const nativeLayer = new print_preview.NativeLayerStub();
@@ -37,8 +42,8 @@
       // Set up the destination store, but no destination yet. Button is
       // disabled.
       const userInfo = new print_preview.UserInfo();
-      const destinationStore = new print_preview.DestinationStore(
-          userInfo, new WebUIListenerTracker());
+      const destinationStore =
+          print_preview_test_utils.createDestinationStore(userInfo);
       destinationStore.init(
           false /* isInAppKioskMode */, 'FooDevice' /* printerName */,
           '' /* serializedDefaultDestinationSelectionRulesStr */,
diff --git a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
index 571a4a9..25a7528 100644
--- a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
+++ b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
@@ -685,7 +685,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      ROOT_PATH + 'ui/webui/resources/js/webui_listener_tracker.js',
+      ROOT_PATH + 'ui/webui/resources/js/web_ui_listener_behavior.js',
       ROOT_PATH + 'ui/webui/resources/js/cr/event_target.js',
       '../settings/test_util.js',
       '../test_browser_proxy.js',
@@ -846,7 +846,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      ROOT_PATH + 'ui/webui/resources/js/webui_listener_tracker.js',
+      ROOT_PATH + 'ui/webui/resources/js/web_ui_listener_behavior.js',
       '../settings/test_util.js',
       '../test_browser_proxy.js',
       'native_layer_stub.js',
@@ -1150,9 +1150,10 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      ROOT_PATH + 'ui/webui/resources/js/webui_listener_tracker.js',
+      ROOT_PATH + 'ui/webui/resources/js/web_ui_listener_behavior.js',
       '../test_browser_proxy.js',
       'native_layer_stub.js',
+      'print_preview_test_utils.js',
       'destination_settings_test.js',
     ]);
   }
diff --git a/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js b/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js
index da1f677f..d648b97 100644
--- a/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js
+++ b/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js
@@ -73,7 +73,7 @@
   get extraLibraries() {
     return super.extraLibraries.concat([
       ROOT_PATH + 'chrome/test/data/webui/settings/test_util.js',
-      ROOT_PATH + 'ui/webui/resources/js/webui_listener_tracker.js',
+      ROOT_PATH + 'ui/webui/resources/js/web_ui_listener_behavior.js',
       '../test_browser_proxy.js',
       'native_layer_stub.js',
       'print_preview_test_utils.js',
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 84f7bf1..11286e8 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
@@ -291,18 +291,43 @@
         new CustomEvent('input', {composed: true, bubbles: true}));
   }
 
+  function setupTestListenerElement() {
+    const domModule = document.createElement('dom-module');
+    domModule.setAttribute('id', 'test-listener-element');
+    domModule.appendChild(document.createElement('template'));
+    document.body.appendChild(domModule);
+    Polymer({
+      is: 'test-listener-element',
+      behaviors: [WebUIListenerBehavior],
+    });
+  }
+
+  /**
+   * @param {!print_preview.UserInfo} userInfo
+   * @return {!print_preview.DestinationStore}
+   */
+  function createDestinationStore(userInfo) {
+    const testListenerElement = document.createElement('test-listener-element');
+    document.body.appendChild(testListenerElement);
+    return new print_preview.DestinationStore(
+        userInfo,
+        testListenerElement.addWebUIListener.bind(testListenerElement));
+  }
+
   return {
-    getDefaultInitialSettings: getDefaultInitialSettings,
-    getCddTemplate: getCddTemplate,
-    getCddTemplateWithAdvancedSettings: getCddTemplateWithAdvancedSettings,
-    getDefaultMediaSize: getDefaultMediaSize,
-    getDefaultOrientation: getDefaultOrientation,
+    createDestinationStore: createDestinationStore,
     createDestinationWithCertificateStatus:
         createDestinationWithCertificateStatus,
+    getCddTemplate: getCddTemplate,
+    getCddTemplateWithAdvancedSettings: getCddTemplateWithAdvancedSettings,
+    getDefaultInitialSettings: getDefaultInitialSettings,
+    getDefaultMediaSize: getDefaultMediaSize,
+    getDefaultOrientation: getDefaultOrientation,
     getDestinations: getDestinations,
     getMediaSizeCapabilityWithCustomNames:
         getMediaSizeCapabilityWithCustomNames,
     getPdfPrinter: getPdfPrinter,
+    setupTestListenerElement: setupTestListenerElement,
     triggerInputEvent: triggerInputEvent,
   };
 });
diff --git a/chrome/utility/mash_service_factory.cc b/chrome/utility/mash_service_factory.cc
index fa980b87..da86094 100644
--- a/chrome/utility/mash_service_factory.cc
+++ b/chrome/utility/mash_service_factory.cc
@@ -11,7 +11,7 @@
 #include "ash/components/quick_launch/quick_launch_application.h"
 #include "ash/components/shortcut_viewer/public/mojom/shortcut_viewer.mojom.h"
 #include "ash/components/shortcut_viewer/shortcut_viewer_application.h"
-#include "ash/components/tap_visualizer/public/mojom/constants.mojom.h"
+#include "ash/components/tap_visualizer/public/mojom/tap_visualizer.mojom.h"
 #include "ash/components/tap_visualizer/tap_visualizer_app.h"
 #include "ash/public/interfaces/constants.mojom.h"
 #include "base/bind.h"
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 21a6e66..616026a6 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -166,6 +166,9 @@
       <message name="IDS_ASH_ADD_USER_BUTTON" desc="Text shown on an add user button on login/locker screen">
         Add Person
       </message>
+      <message name="IDS_ASH_PARENT_ACCESS_BUTTON" desc="Text shown on an parent access button on login/lock screen">
+        Parent access
+      </message>
       <message name="IDS_ASH_SHELF_APPS_BUTTON" desc="Text shown on apps button on login screen.">
         Apps
       </message>
diff --git a/chromeos/chromeos_strings_grd/IDS_ASH_PARENT_ACCESS_BUTTON.png.sha1 b/chromeos/chromeos_strings_grd/IDS_ASH_PARENT_ACCESS_BUTTON.png.sha1
new file mode 100644
index 0000000..84b93a1
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_ASH_PARENT_ACCESS_BUTTON.png.sha1
@@ -0,0 +1 @@
+0c66f67616eaa0fdc226f46282ff1e235ffa3f83
\ No newline at end of file
diff --git a/chromeos/network/network_state_test.cc b/chromeos/network/network_state_test.cc
index a65cd93..7ca514a7 100644
--- a/chromeos/network/network_state_test.cc
+++ b/chromeos/network/network_state_test.cc
@@ -57,6 +57,14 @@
   network_state_handler_->Shutdown();
 }
 
+void NetworkStateTest::ClearDefaultServices() {
+  DBusThreadManager::Get()
+      ->GetShillServiceClient()
+      ->GetTestInterface()
+      ->ClearServices();
+  base::RunLoop().RunUntilIdle();
+}
+
 std::string NetworkStateTest::ConfigureService(
     const std::string& shill_json_string) {
   last_created_service_path_ = "";
diff --git a/chromeos/network/network_state_test.h b/chromeos/network/network_state_test.h
index cad8250d..e12044f 100644
--- a/chromeos/network/network_state_test.h
+++ b/chromeos/network/network_state_test.h
@@ -28,6 +28,9 @@
   // Call this before TearDown() to shut down NetworkStateHandler.
   void ShutdownNetworkState();
 
+  // Clears services that are set by fake shill service manager by default.
+  void ClearDefaultServices();
+
   // Configures a new service using Shill properties from |shill_json_string|
   // which must include a GUID and Type. Returns the service path, or "" if the
   // service could not be configured.
diff --git a/components/autofill/content/common/autofill_types.mojom b/components/autofill/content/common/autofill_types.mojom
index ea443f0..b1ab281 100644
--- a/components/autofill/content/common/autofill_types.mojom
+++ b/components/autofill/content/common/autofill_types.mojom
@@ -108,7 +108,10 @@
   BUTTON_ELEMENT_SUBMIT_TYPE,
   BUTTON_ELEMENT_BUTTON_TYPE,
   INPUT_ELEMENT_SUBMIT_TYPE,
-  INPUT_ELEMENT_BUTTON_TYPE
+  INPUT_ELEMENT_BUTTON_TYPE,
+  HYPERLINK,
+  DIV,
+  SPAN
 };
 
 // autofill::FormFieldData
diff --git a/components/autofill/content/common/autofill_types_struct_traits.cc b/components/autofill/content/common/autofill_types_struct_traits.cc
index 9ea94763d..0a2bb00e 100644
--- a/components/autofill/content/common/autofill_types_struct_traits.cc
+++ b/components/autofill/content/common/autofill_types_struct_traits.cc
@@ -541,6 +541,12 @@
       return autofill::mojom::ButtonTitleType::INPUT_ELEMENT_SUBMIT_TYPE;
     case autofill::ButtonTitleType::INPUT_ELEMENT_BUTTON_TYPE:
       return autofill::mojom::ButtonTitleType::INPUT_ELEMENT_BUTTON_TYPE;
+    case autofill::ButtonTitleType::HYPERLINK:
+      return autofill::mojom::ButtonTitleType::HYPERLINK;
+    case autofill::ButtonTitleType::DIV:
+      return autofill::mojom::ButtonTitleType::DIV;
+    case autofill::ButtonTitleType::SPAN:
+      return autofill::mojom::ButtonTitleType::SPAN;
   }
   NOTREACHED();
   return autofill::mojom::ButtonTitleType::NONE;
@@ -566,6 +572,15 @@
     case autofill::mojom::ButtonTitleType::INPUT_ELEMENT_BUTTON_TYPE:
       *output = autofill::ButtonTitleType::INPUT_ELEMENT_BUTTON_TYPE;
       return true;
+    case autofill::mojom::ButtonTitleType::HYPERLINK:
+      *output = autofill::ButtonTitleType::HYPERLINK;
+      return true;
+    case autofill::mojom::ButtonTitleType::DIV:
+      *output = autofill::ButtonTitleType::DIV;
+      return true;
+    case autofill::mojom::ButtonTitleType::SPAN:
+      *output = autofill::ButtonTitleType::SPAN;
+      return true;
   }
   NOTREACHED();
   return false;
diff --git a/components/autofill/content/renderer/form_autofill_util.cc b/components/autofill/content/renderer/form_autofill_util.cc
index 971072d2..bdc47a09 100644
--- a/components/autofill/content/renderer/form_autofill_util.cc
+++ b/components/autofill/content/renderer/form_autofill_util.cc
@@ -9,6 +9,7 @@
 #include <map>
 #include <memory>
 #include <set>
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -75,6 +76,13 @@
 // Maximal length of all button titles.
 const int kMaxLengthForAllButtonTitles = 200;
 
+// Text features to detect form submission buttons. Features are selected based
+// on analysis of real forms and their buttons.
+// TODO(crbug.com/910546): Consider to add more features (e.g. non-English
+// features).
+const char* const kButtonFeatures[] = {"button", "btn", "submit",
+                                       "boton" /* "button" in Spanish */};
+
 // A bit field mask for FillForm functions to not fill some fields.
 enum FieldFilterMask {
   FILTER_NONE                      = 0,
@@ -366,15 +374,15 @@
 // Helper function to add a button's |title| to the |list| and updates the
 // |total_length| of stored titles. Returns true iff the list still have free
 // capacity.
-bool AddButtonTitleToList(base::string16&& title,
+bool AddButtonTitleToList(base::string16 title,
                           ButtonTitleType button_type,
                           ButtonTitleList* list,
                           int* total_length) {
   if (*total_length >= kMaxLengthForAllButtonTitles)
     return false;
+  title = base::CollapseWhitespace(std::move(title), false);
   if (title.empty())
     return true;
-  title = base::CollapseWhitespace(std::move(title), false);
   TruncateString(&title,
                  std::min(kMaxLengthForSingleButtonTitle,
                           kMaxLengthForAllButtonTitles - *total_length));
@@ -383,6 +391,59 @@
   return *total_length < kMaxLengthForAllButtonTitles;
 }
 
+// Returns true iff |attribute| contains one of |kButtonFeatures|.
+bool AttributeHasButtonFeature(const WebString& attribute) {
+  if (attribute.IsNull())
+    return false;
+  std::string value = attribute.Utf8();
+  std::transform(value.begin(), value.end(), value.begin(), ::tolower);
+  for (const char* const button_feature : kButtonFeatures) {
+    if (value.find(button_feature, 0) != std::string::npos)
+      return true;
+  }
+  return false;
+}
+
+// Returns true if |element|'s id, name or css class contain |kButtonFeatures|.
+bool ElementAttributesHasButtonFeature(const WebElement& element) {
+  return AttributeHasButtonFeature(element.GetAttribute("id")) ||
+         AttributeHasButtonFeature(element.GetAttribute("name")) ||
+         AttributeHasButtonFeature(element.GetAttribute("class"));
+}
+
+// Finds elements from |elements| that contains |kButtonFeatures|, adds them to
+// |list| and updates the |total_length| of the |list|'s items.
+// If |extract_value_attribute|, the "value" attribute is extracted as a button
+// title. Otherwise, |WebElement::TextContent| (aka innerText in Javascript) is
+// extracted as a title.
+void FindElementsWithButtonFeatures(const WebElementCollection& elements,
+                                    ButtonTitleType button_type,
+                                    bool extract_value_attribute,
+                                    ButtonTitleList* list,
+                                    int* total_length) {
+  static base::NoDestructor<WebString> kValue("value");
+  if (*total_length >= kMaxLengthForAllButtonTitles)
+    return;
+  for (WebElement item = elements.FirstItem();
+       !item.IsNull() && *total_length < kMaxLengthForAllButtonTitles;
+       item = elements.NextItem()) {
+    if (!ElementAttributesHasButtonFeature(item))
+      continue;
+
+    base::string16 title =
+        extract_value_attribute
+            ? (item.HasAttribute(*kValue) ? item.GetAttribute(*kValue).Utf16()
+                                          : base::string16())
+            : item.TextContent().Utf16();
+    if (extract_value_attribute && title.empty())
+      title = item.TextContent().Utf16();
+    if (!AddButtonTitleToList(std::move(title), button_type, list,
+                              total_length)) {
+      break;
+    }
+  }
+}
+
 // Helper for |InferLabelForElement()| that infers a label, if possible, from
 // a previous sibling of |element|,
 // e.g. Some Text <input ...>
@@ -809,6 +870,9 @@
 ButtonTitleList InferButtonTitlesForForm(const WebFormElement& form_element) {
   static base::NoDestructor<WebString> kSubmit("submit");
   static base::NoDestructor<WebString> kButton("button");
+  static base::NoDestructor<WebString> kA("a");
+  static base::NoDestructor<WebString> kDiv("div");
+  static base::NoDestructor<WebString> kSpan("span");
 
   ButtonTitleList result;
   WebVector<WebFormControlElement> control_elements;
@@ -821,7 +885,7 @@
         control_element.FormControlTypeForAutofill() == *kButton;
     if (!is_submit_input && !is_button_input)
       continue;
-    base::string16&& title = control_element.Value().Utf16();
+    base::string16 title = control_element.Value().Utf16();
     if (!AddButtonTitleToList(std::move(title),
                               is_submit_input
                                   ? ButtonTitleType::INPUT_ELEMENT_SUBMIT_TYPE
@@ -842,7 +906,7 @@
       continue;
     }
     bool is_submit_type = type_attribute.IsNull() || type_attribute == *kSubmit;
-    base::string16&& title = item.TextContent().Utf16();
+    base::string16 title = item.TextContent().Utf16();
     if (!AddButtonTitleToList(std::move(title),
                               is_submit_type
                                   ? ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE
@@ -851,6 +915,15 @@
       break;
     }
   }
+  FindElementsWithButtonFeatures(
+      form_element.GetElementsByHTMLTagName(*kA), ButtonTitleType::HYPERLINK,
+      true /* extract_value_attribute */, &result, &total_length);
+  FindElementsWithButtonFeatures(
+      form_element.GetElementsByHTMLTagName(*kDiv), ButtonTitleType::DIV,
+      false /* extract_value_attribute */, &result, &total_length);
+  FindElementsWithButtonFeatures(
+      form_element.GetElementsByHTMLTagName(*kSpan), ButtonTitleType::SPAN,
+      false /* extract_value_attribute */, &result, &total_length);
   return result;
 }
 
diff --git a/components/autofill/content/renderer/form_autofill_util_browsertest.cc b/components/autofill/content/renderer/form_autofill_util_browsertest.cc
index a05e245..a43be23 100644
--- a/components/autofill/content/renderer/form_autofill_util_browsertest.cc
+++ b/components/autofill/content/renderer/form_autofill_util_browsertest.cc
@@ -273,6 +273,10 @@
       "  <input type='button' value='\n Show\t password '>"
       "  <button>Sign Up</button>"
       "  <button type='button'>Register</button>"
+      "  <a id='Submit' value='Create account'>"
+      "  <div name='BTN'> Join </div>"
+      "  <span class='button'> Start </span>"
+      "  <a class='empty button' value='   \t   \n'>"
       "</form>";
 
   LoadHTML(kHtml);
@@ -291,7 +295,11 @@
       {base::UTF8ToUTF16("Sign Up"),
        autofill::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE},
       {base::UTF8ToUTF16("Register"),
-       autofill::ButtonTitleType::BUTTON_ELEMENT_BUTTON_TYPE}};
+       autofill::ButtonTitleType::BUTTON_ELEMENT_BUTTON_TYPE},
+      {base::UTF8ToUTF16("Create account"),
+       autofill::ButtonTitleType::HYPERLINK},
+      {base::UTF8ToUTF16("Join"), autofill::ButtonTitleType::DIV},
+      {base::UTF8ToUTF16("Start"), autofill::ButtonTitleType::SPAN}};
   EXPECT_EQ(expected, actual);
 }
 
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index 7d159ac..b646acb8 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -288,6 +288,32 @@
   return HTML_TYPE_UNRECOGNIZED;
 }
 
+// Helper function for explicit conversion between |ButtonTitleType| defined in
+// "button_title_type.h" and "server.proto".
+AutofillUploadContents_ButtonTitle_ButtonTitleType ToServerButtonTitleType(
+    autofill::ButtonTitleType input) {
+  switch (input) {
+    case ButtonTitleType::NONE:
+      return AutofillUploadContents::ButtonTitle::NONE;
+    case ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE:
+      return AutofillUploadContents::ButtonTitle::BUTTON_ELEMENT_SUBMIT_TYPE;
+    case ButtonTitleType::BUTTON_ELEMENT_BUTTON_TYPE:
+      return AutofillUploadContents::ButtonTitle::BUTTON_ELEMENT_BUTTON_TYPE;
+    case ButtonTitleType::INPUT_ELEMENT_SUBMIT_TYPE:
+      return AutofillUploadContents::ButtonTitle::INPUT_ELEMENT_SUBMIT_TYPE;
+    case ButtonTitleType::INPUT_ELEMENT_BUTTON_TYPE:
+      return AutofillUploadContents::ButtonTitle::INPUT_ELEMENT_BUTTON_TYPE;
+    case ButtonTitleType::HYPERLINK:
+      return AutofillUploadContents::ButtonTitle::HYPERLINK;
+    case ButtonTitleType::DIV:
+      return AutofillUploadContents::ButtonTitle::DIV;
+    case ButtonTitleType::SPAN:
+      return AutofillUploadContents::ButtonTitle::SPAN;
+  }
+  NOTREACHED();
+  return AutofillUploadContents::ButtonTitle::NONE;
+}
+
 std::ostream& operator<<(
     std::ostream& out,
     const autofill::AutofillQueryResponseContents& response) {
@@ -575,9 +601,7 @@
     for (const ButtonTitleInfo& e : button_titles_) {
       auto* button_title = upload->add_button_title();
       button_title->set_title(base::UTF16ToUTF8(e.first));
-      button_title->set_type(
-          static_cast<AutofillUploadContents_ButtonTitle_ButtonTitleType>(
-              e.second));
+      button_title->set_type(ToServerButtonTitleType(e.second));
     }
   }
 
diff --git a/components/autofill/core/browser/proto/server.proto b/components/autofill/core/browser/proto/server.proto
index 74472b38..35326fe 100644
--- a/components/autofill/core/browser/proto/server.proto
+++ b/components/autofill/core/browser/proto/server.proto
@@ -384,6 +384,9 @@
       BUTTON_ELEMENT_BUTTON_TYPE = 2;  // <button type='button'>
       INPUT_ELEMENT_SUBMIT_TYPE = 3;   // <input type='submit'>
       INPUT_ELEMENT_BUTTON_TYPE = 4;   // <input type='button'>
+      HYPERLINK = 5;                   // e.g. <a class='button'>
+      DIV = 6;                         // e.g. <div id='submit'>
+      SPAN = 7;                        // e.g. <span name='btn'>
     }
     optional ButtonTitleType type = 2;
   }
diff --git a/components/autofill/core/common/button_title_type.h b/components/autofill/core/common/button_title_type.h
index 7fd97142..bf3696d 100644
--- a/components/autofill/core/common/button_title_type.h
+++ b/components/autofill/core/common/button_title_type.h
@@ -15,7 +15,10 @@
   BUTTON_ELEMENT_SUBMIT_TYPE = 1,  // <button type='submit'>
   BUTTON_ELEMENT_BUTTON_TYPE = 2,  // <button type='button'>
   INPUT_ELEMENT_SUBMIT_TYPE = 3,   // <input type='submit'>
-  INPUT_ELEMENT_BUTTON_TYPE = 4    // <input type='button'>
+  INPUT_ELEMENT_BUTTON_TYPE = 4,   // <input type='button'>
+  HYPERLINK = 5,                   // e.g. <a class='button'>
+  DIV = 6,                         // e.g. <div id='submit'>
+  SPAN = 7                         // e.g. <span name='btn'>
 };
 
 }  // namespace autofill
diff --git a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java
index 95b34c0..d1ca29e 100644
--- a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java
+++ b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java
@@ -35,8 +35,9 @@
     static final int BACKGROUND_TASK_COMPONENT_UPDATE = 15;
     static final int BACKGROUND_TASK_DEPRECATED_EXPLORE_SITES_REFRESH = 16;
     static final int BACKGROUND_TASK_EXPLORE_SITES_REFRESH = 17;
+    static final int BACKGROUND_TASK_DOWNLOAD_AUTO_RESUMPTION = 18;
     // Keep this one at the end and increment appropriately when adding new tasks.
-    static final int BACKGROUND_TASK_COUNT = 18;
+    static final int BACKGROUND_TASK_COUNT = 19;
 
     static final String KEY_CACHED_UMA = "bts_cached_uma";
 
@@ -243,6 +244,8 @@
                 return BACKGROUND_TASK_DOWNLOAD_SERVICE;
             case TaskIds.DOWNLOAD_CLEANUP_JOB_ID:
                 return BACKGROUND_TASK_DOWNLOAD_CLEANUP;
+            case TaskIds.DOWNLOAD_AUTO_RESUMPTION_JOB_ID:
+                return BACKGROUND_TASK_DOWNLOAD_AUTO_RESUMPTION;
             case TaskIds.WEBVIEW_VARIATIONS_SEED_FETCH_JOB_ID:
                 return BACKGROUND_TASK_WEBVIEW_VARIATIONS;
             case TaskIds.OFFLINE_PAGES_PREFETCH_NOTIFICATION_JOB_ID:
diff --git a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java
index a885f1c..959377ad 100644
--- a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java
+++ b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java
@@ -26,6 +26,7 @@
     public static final int WEBVIEW_VARIATIONS_SEED_FETCH_JOB_ID = 83;
     public static final int WEBAPK_UPDATE_JOB_ID = 91;
     public static final int DOWNLOAD_RESUMPTION_JOB_ID = 55;
+    public static final int DOWNLOAD_AUTO_RESUMPTION_JOB_ID = 56;
     public static final int FEED_REFRESH_JOB_ID = 22;
     public static final int COMPONENT_UPDATE_JOB_ID = 2;
     public static final int DEPRECATED_EXPLORE_SITES_REFRESH_JOB_ID = 100;
diff --git a/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUmaTest.java b/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUmaTest.java
index 8a9dc167..905c63d 100644
--- a/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUmaTest.java
+++ b/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUmaTest.java
@@ -71,6 +71,9 @@
         assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_DOWNLOAD_CLEANUP,
                 BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId(
                         TaskIds.DOWNLOAD_CLEANUP_JOB_ID));
+        assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_DOWNLOAD_AUTO_RESUMPTION,
+                BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId(
+                        TaskIds.DOWNLOAD_AUTO_RESUMPTION_JOB_ID));
         assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_WEBVIEW_VARIATIONS,
                 BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId(
                         TaskIds.WEBVIEW_VARIATIONS_SEED_FETCH_JOB_ID));
@@ -93,7 +96,7 @@
         assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_DEPRECATED_EXPLORE_SITES_REFRESH,
                 BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId(
                         TaskIds.DEPRECATED_EXPLORE_SITES_REFRESH_JOB_ID));
-        assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_COUNT, 18);
+        assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_COUNT, 19);
     }
 
     @Test
diff --git a/components/cronet/native/cronet.idl b/components/cronet/native/cronet.idl
index 4186fa1..77b1eed3 100644
--- a/components/cronet/native/cronet.idl
+++ b/components/cronet/native/cronet.idl
@@ -1131,8 +1131,12 @@
 };
 
 /**
+ * This value indicates an invalid time.
+ */
+const int64 kInvalidTime = -1;
+
+/**
  * Information about a finished request.
- * TODO(mef): Define this to include metrics.
  */
 struct RequestFinishedInfo {
   /**
@@ -1152,4 +1156,158 @@
      */
     CANCELED = 2,
   };
+
+  // TODO(caraitto): Move metrics fields to their own class once C generator
+  // supports struct within a struct.
+
+  /*
+   * The below fields represent metrics collected for a single request. Most of
+   * these metrics are timestamps for events during the lifetime of the request,
+   * which can be used to build a detailed timeline for investigating
+   * performance.
+   *
+   * Events happen in this order:
+   * <ol>
+   * <li>{@link #request_start request start}</li>
+   * <li>{@link #dns_start DNS start}</li>
+   * <li>{@link #dns_end DNS end}</li>
+   * <li>{@link #connect_start connect start}</li>
+   * <li>{@link #ssl_start SSL start}</li>
+   * <li>{@link #ssl_end SSL end}</li>
+   * <li>{@link #connect_end connect end}</li>
+   * <li>{@link #sending_start sending start}</li>
+   * <li>{@link #sending_end sending end}</li>
+   * <li>{@link #response_start response start}</li>
+   * <li>{@link #request_end request end}</li>
+   * </ol>
+   *
+   * Start times are reported as the time when a request started blocking on
+   * the event, not when the event actually occurred, with the exception of
+   * push start and end. If a metric is not meaningful or not available,
+   * including cases when a request finished before reaching that stage, start
+   * and end times will be kInvalidTime. If no time was spent blocking on an
+   * event, start and end will be the same time.
+   *
+   * Timestamps are recorded using a clock that is guaranteed not to run
+   * backwards. All timestamps are correct relative to the system clock at the
+   * time of request start, and taking the difference between two timestamps
+   * will give the correct difference between the events. In order to preserve
+   * this property, timestamps for events other than request start are not
+   * guaranteed to match the system clock at the times they represent.
+   *
+   * Most timing metrics are taken from
+   * <a
+   * href="https://cs.chromium.org/chromium/src/net/base/load_timing_info.h">LoadTimingInfo</a>,
+   * which holds the information for <a href="http://w3c.github.io/navigation-timing/"></a> and
+   * <a href="https://www.w3.org/TR/resource-timing/"></a>.
+   *
+   * All time fields are expressed as the number of milleseconds since the UNIX
+   * epoch, or kInvalidTime.
+   */
+
+  /**
+   * Time when the request started, which corresponds to calling
+   * Cronet_UrlRequest_Start(). This timestamp will match the system clock at
+   * the time it represents.
+   */
+  int64 request_start = kInvalidTime;
+
+  /**
+   * Time when DNS lookup started. This and {@link #dns_end} will be set to
+   * non-kInvalidTime regardless of whether the result came from a DNS server
+   * or the local cache. Will equal kInvalidTime if the socket was reused (see
+   * {@link #socket_reused}).
+   */
+  int64 dns_start = kInvalidTime;
+
+  /**
+   * Time when DNS lookup finished. This and {@link dns_start} will return
+   * non-kInvalidTime regardless of whether the result came from a DNS server
+   * or the local cache. Will equal kInvalidTime if the socket was reused (see
+   * {@link #socket_reused}).
+   */
+  int64 dns_end = kInvalidTime;
+
+  /**
+   * Time when connection establishment started, typically when DNS resolution
+   * finishes. Will equal kInvalidTime if the socket was reused (see {@link
+   * #socket_reused}).
+   */
+  int64 connect_start = kInvalidTime;
+
+  /**
+   * Time when connection establishment finished, after TCP connection is
+   * established and, if using HTTPS, SSL handshake is completed. For QUIC
+   * 0-RTT, this represents the time of handshake confirmation and might happen
+   * later than {@link #sending_start}. Will equal kInvalidTime if the socket
+   * was reused (see {@link #socket_reused}).
+   */
+  int64 connect_end = kInvalidTime;
+
+  /**
+   * Time when SSL handshake started. For QUIC, this will be the same time as
+   * {@link #connect_start}. Will equal kInvalidTime if SSL is not used or if
+   * the socket was reused (see {@link #socket_reused}).
+   */
+  int64 ssl_start = kInvalidTime;
+
+  /**
+   * Time when SSL handshake finished. For QUIC, this will be the same time as
+   * {@link #connect_end}. Will equal kInvalidTime if SSL is not used or if the
+   * socket was reused (see {@link #socket_reused}).
+   */
+  int64 ssl_end = kInvalidTime;
+
+  /**
+   * Time when sending HTTP request headers started.
+   */
+  int64 sending_start = kInvalidTime;
+
+  /**
+   * Time when sending HTTP request body finished. (Sending request body
+   * happens after sending request headers.)
+   */
+  int64 sending_end = kInvalidTime;
+
+  /**
+   * Time when first byte of HTTP/2 server push was received.  Will equal
+   * kInvalidTime if server push is not used.
+   */
+  int64 push_start = kInvalidTime;
+
+  /**
+   * Time when last byte of HTTP/2 server push was received.  Will equal
+   * kInvalidTime if server push is not used.
+   */
+  int64 push_end = kInvalidTime;
+
+  /**
+   * Time when the end of the response headers was received.
+   */
+  int64 response_start = kInvalidTime;
+
+  /**
+   * Time when the request finished.
+   */
+  int64 request_end = kInvalidTime;
+
+  /**
+   * True if the socket was reused from a previous request, false otherwise.
+   * In HTTP/2 or QUIC, if streams are multiplexed in a single connection,
+   * this will be {@code true} for all streams after the first.  When {@code
+   * true}, DNS, connection, and SSL times will be kInvalidTime.
+   */
+  bool socket_reused = false;
+
+  /**
+   * Returns total bytes sent over the network transport layer, or -1 if not
+   * collected.
+   */
+  int64 sent_byte_count = -1;
+
+  /**
+   * Total bytes received over the network transport layer, or -1 if not
+   * collected. Number of bytes does not include any previous redirects.
+   */
+  int64 received_byte_count = -1;
 };
diff --git a/components/cronet/native/generated/cronet.idl_c.h b/components/cronet/native/generated/cronet.idl_c.h
index b67abff4..07bf485e 100644
--- a/components/cronet/native/generated/cronet.idl_c.h
+++ b/components/cronet/native/generated/cronet.idl_c.h
@@ -148,6 +148,9 @@
   Cronet_UrlRequestStatusListener_Status_READING_RESPONSE = 14,
 } Cronet_UrlRequestStatusListener_Status;
 
+// Declare constants
+const int64_t Cronet_kInvalidTime = -1;
+
 ///////////////////////
 // Concrete interface Cronet_Buffer.
 
@@ -970,7 +973,116 @@
 CRONET_EXPORT void Cronet_RequestFinishedInfo_Destroy(
     Cronet_RequestFinishedInfoPtr self);
 // Cronet_RequestFinishedInfo setters.
+CRONET_EXPORT
+void Cronet_RequestFinishedInfo_request_start_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t request_start);
+CRONET_EXPORT
+void Cronet_RequestFinishedInfo_dns_start_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t dns_start);
+CRONET_EXPORT
+void Cronet_RequestFinishedInfo_dns_end_set(Cronet_RequestFinishedInfoPtr self,
+                                            int64_t dns_end);
+CRONET_EXPORT
+void Cronet_RequestFinishedInfo_connect_start_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t connect_start);
+CRONET_EXPORT
+void Cronet_RequestFinishedInfo_connect_end_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t connect_end);
+CRONET_EXPORT
+void Cronet_RequestFinishedInfo_ssl_start_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t ssl_start);
+CRONET_EXPORT
+void Cronet_RequestFinishedInfo_ssl_end_set(Cronet_RequestFinishedInfoPtr self,
+                                            int64_t ssl_end);
+CRONET_EXPORT
+void Cronet_RequestFinishedInfo_sending_start_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t sending_start);
+CRONET_EXPORT
+void Cronet_RequestFinishedInfo_sending_end_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t sending_end);
+CRONET_EXPORT
+void Cronet_RequestFinishedInfo_push_start_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t push_start);
+CRONET_EXPORT
+void Cronet_RequestFinishedInfo_push_end_set(Cronet_RequestFinishedInfoPtr self,
+                                             int64_t push_end);
+CRONET_EXPORT
+void Cronet_RequestFinishedInfo_response_start_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t response_start);
+CRONET_EXPORT
+void Cronet_RequestFinishedInfo_request_end_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t request_end);
+CRONET_EXPORT
+void Cronet_RequestFinishedInfo_socket_reused_set(
+    Cronet_RequestFinishedInfoPtr self,
+    bool socket_reused);
+CRONET_EXPORT
+void Cronet_RequestFinishedInfo_sent_byte_count_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t sent_byte_count);
+CRONET_EXPORT
+void Cronet_RequestFinishedInfo_received_byte_count_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t received_byte_count);
 // Cronet_RequestFinishedInfo getters.
+CRONET_EXPORT
+int64_t Cronet_RequestFinishedInfo_request_start_get(
+    Cronet_RequestFinishedInfoPtr self);
+CRONET_EXPORT
+int64_t Cronet_RequestFinishedInfo_dns_start_get(
+    Cronet_RequestFinishedInfoPtr self);
+CRONET_EXPORT
+int64_t Cronet_RequestFinishedInfo_dns_end_get(
+    Cronet_RequestFinishedInfoPtr self);
+CRONET_EXPORT
+int64_t Cronet_RequestFinishedInfo_connect_start_get(
+    Cronet_RequestFinishedInfoPtr self);
+CRONET_EXPORT
+int64_t Cronet_RequestFinishedInfo_connect_end_get(
+    Cronet_RequestFinishedInfoPtr self);
+CRONET_EXPORT
+int64_t Cronet_RequestFinishedInfo_ssl_start_get(
+    Cronet_RequestFinishedInfoPtr self);
+CRONET_EXPORT
+int64_t Cronet_RequestFinishedInfo_ssl_end_get(
+    Cronet_RequestFinishedInfoPtr self);
+CRONET_EXPORT
+int64_t Cronet_RequestFinishedInfo_sending_start_get(
+    Cronet_RequestFinishedInfoPtr self);
+CRONET_EXPORT
+int64_t Cronet_RequestFinishedInfo_sending_end_get(
+    Cronet_RequestFinishedInfoPtr self);
+CRONET_EXPORT
+int64_t Cronet_RequestFinishedInfo_push_start_get(
+    Cronet_RequestFinishedInfoPtr self);
+CRONET_EXPORT
+int64_t Cronet_RequestFinishedInfo_push_end_get(
+    Cronet_RequestFinishedInfoPtr self);
+CRONET_EXPORT
+int64_t Cronet_RequestFinishedInfo_response_start_get(
+    Cronet_RequestFinishedInfoPtr self);
+CRONET_EXPORT
+int64_t Cronet_RequestFinishedInfo_request_end_get(
+    Cronet_RequestFinishedInfoPtr self);
+CRONET_EXPORT
+bool Cronet_RequestFinishedInfo_socket_reused_get(
+    Cronet_RequestFinishedInfoPtr self);
+CRONET_EXPORT
+int64_t Cronet_RequestFinishedInfo_sent_byte_count_get(
+    Cronet_RequestFinishedInfoPtr self);
+CRONET_EXPORT
+int64_t Cronet_RequestFinishedInfo_received_byte_count_get(
+    Cronet_RequestFinishedInfoPtr self);
 
 #ifdef __cplusplus
 }
diff --git a/components/cronet/native/generated/cronet.idl_impl_struct.cc b/components/cronet/native/generated/cronet.idl_impl_struct.cc
index ae1cbb8..6ab68f7 100644
--- a/components/cronet/native/generated/cronet.idl_impl_struct.cc
+++ b/components/cronet/native/generated/cronet.idl_impl_struct.cc
@@ -775,5 +775,208 @@
 }
 
 // Struct Cronet_RequestFinishedInfo setters.
+void Cronet_RequestFinishedInfo_request_start_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t request_start) {
+  DCHECK(self);
+  self->request_start = request_start;
+}
+
+void Cronet_RequestFinishedInfo_dns_start_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t dns_start) {
+  DCHECK(self);
+  self->dns_start = dns_start;
+}
+
+void Cronet_RequestFinishedInfo_dns_end_set(Cronet_RequestFinishedInfoPtr self,
+                                            int64_t dns_end) {
+  DCHECK(self);
+  self->dns_end = dns_end;
+}
+
+void Cronet_RequestFinishedInfo_connect_start_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t connect_start) {
+  DCHECK(self);
+  self->connect_start = connect_start;
+}
+
+void Cronet_RequestFinishedInfo_connect_end_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t connect_end) {
+  DCHECK(self);
+  self->connect_end = connect_end;
+}
+
+void Cronet_RequestFinishedInfo_ssl_start_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t ssl_start) {
+  DCHECK(self);
+  self->ssl_start = ssl_start;
+}
+
+void Cronet_RequestFinishedInfo_ssl_end_set(Cronet_RequestFinishedInfoPtr self,
+                                            int64_t ssl_end) {
+  DCHECK(self);
+  self->ssl_end = ssl_end;
+}
+
+void Cronet_RequestFinishedInfo_sending_start_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t sending_start) {
+  DCHECK(self);
+  self->sending_start = sending_start;
+}
+
+void Cronet_RequestFinishedInfo_sending_end_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t sending_end) {
+  DCHECK(self);
+  self->sending_end = sending_end;
+}
+
+void Cronet_RequestFinishedInfo_push_start_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t push_start) {
+  DCHECK(self);
+  self->push_start = push_start;
+}
+
+void Cronet_RequestFinishedInfo_push_end_set(Cronet_RequestFinishedInfoPtr self,
+                                             int64_t push_end) {
+  DCHECK(self);
+  self->push_end = push_end;
+}
+
+void Cronet_RequestFinishedInfo_response_start_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t response_start) {
+  DCHECK(self);
+  self->response_start = response_start;
+}
+
+void Cronet_RequestFinishedInfo_request_end_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t request_end) {
+  DCHECK(self);
+  self->request_end = request_end;
+}
+
+void Cronet_RequestFinishedInfo_socket_reused_set(
+    Cronet_RequestFinishedInfoPtr self,
+    bool socket_reused) {
+  DCHECK(self);
+  self->socket_reused = socket_reused;
+}
+
+void Cronet_RequestFinishedInfo_sent_byte_count_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t sent_byte_count) {
+  DCHECK(self);
+  self->sent_byte_count = sent_byte_count;
+}
+
+void Cronet_RequestFinishedInfo_received_byte_count_set(
+    Cronet_RequestFinishedInfoPtr self,
+    int64_t received_byte_count) {
+  DCHECK(self);
+  self->received_byte_count = received_byte_count;
+}
 
 // Struct Cronet_RequestFinishedInfo getters.
+int64_t Cronet_RequestFinishedInfo_request_start_get(
+    Cronet_RequestFinishedInfoPtr self) {
+  DCHECK(self);
+  return self->request_start;
+}
+
+int64_t Cronet_RequestFinishedInfo_dns_start_get(
+    Cronet_RequestFinishedInfoPtr self) {
+  DCHECK(self);
+  return self->dns_start;
+}
+
+int64_t Cronet_RequestFinishedInfo_dns_end_get(
+    Cronet_RequestFinishedInfoPtr self) {
+  DCHECK(self);
+  return self->dns_end;
+}
+
+int64_t Cronet_RequestFinishedInfo_connect_start_get(
+    Cronet_RequestFinishedInfoPtr self) {
+  DCHECK(self);
+  return self->connect_start;
+}
+
+int64_t Cronet_RequestFinishedInfo_connect_end_get(
+    Cronet_RequestFinishedInfoPtr self) {
+  DCHECK(self);
+  return self->connect_end;
+}
+
+int64_t Cronet_RequestFinishedInfo_ssl_start_get(
+    Cronet_RequestFinishedInfoPtr self) {
+  DCHECK(self);
+  return self->ssl_start;
+}
+
+int64_t Cronet_RequestFinishedInfo_ssl_end_get(
+    Cronet_RequestFinishedInfoPtr self) {
+  DCHECK(self);
+  return self->ssl_end;
+}
+
+int64_t Cronet_RequestFinishedInfo_sending_start_get(
+    Cronet_RequestFinishedInfoPtr self) {
+  DCHECK(self);
+  return self->sending_start;
+}
+
+int64_t Cronet_RequestFinishedInfo_sending_end_get(
+    Cronet_RequestFinishedInfoPtr self) {
+  DCHECK(self);
+  return self->sending_end;
+}
+
+int64_t Cronet_RequestFinishedInfo_push_start_get(
+    Cronet_RequestFinishedInfoPtr self) {
+  DCHECK(self);
+  return self->push_start;
+}
+
+int64_t Cronet_RequestFinishedInfo_push_end_get(
+    Cronet_RequestFinishedInfoPtr self) {
+  DCHECK(self);
+  return self->push_end;
+}
+
+int64_t Cronet_RequestFinishedInfo_response_start_get(
+    Cronet_RequestFinishedInfoPtr self) {
+  DCHECK(self);
+  return self->response_start;
+}
+
+int64_t Cronet_RequestFinishedInfo_request_end_get(
+    Cronet_RequestFinishedInfoPtr self) {
+  DCHECK(self);
+  return self->request_end;
+}
+
+bool Cronet_RequestFinishedInfo_socket_reused_get(
+    Cronet_RequestFinishedInfoPtr self) {
+  DCHECK(self);
+  return self->socket_reused;
+}
+
+int64_t Cronet_RequestFinishedInfo_sent_byte_count_get(
+    Cronet_RequestFinishedInfoPtr self) {
+  DCHECK(self);
+  return self->sent_byte_count;
+}
+
+int64_t Cronet_RequestFinishedInfo_received_byte_count_get(
+    Cronet_RequestFinishedInfoPtr self) {
+  DCHECK(self);
+  return self->received_byte_count;
+}
diff --git a/components/cronet/native/generated/cronet.idl_impl_struct.h b/components/cronet/native/generated/cronet.idl_impl_struct.h
index 0a47ffe..298f8071 100644
--- a/components/cronet/native/generated/cronet.idl_impl_struct.h
+++ b/components/cronet/native/generated/cronet.idl_impl_struct.h
@@ -161,6 +161,23 @@
   explicit Cronet_RequestFinishedInfo(Cronet_RequestFinishedInfo&& from);
   ~Cronet_RequestFinishedInfo();
 
+  int64_t request_start = Cronet_kInvalidTime;
+  int64_t dns_start = Cronet_kInvalidTime;
+  int64_t dns_end = Cronet_kInvalidTime;
+  int64_t connect_start = Cronet_kInvalidTime;
+  int64_t connect_end = Cronet_kInvalidTime;
+  int64_t ssl_start = Cronet_kInvalidTime;
+  int64_t ssl_end = Cronet_kInvalidTime;
+  int64_t sending_start = Cronet_kInvalidTime;
+  int64_t sending_end = Cronet_kInvalidTime;
+  int64_t push_start = Cronet_kInvalidTime;
+  int64_t push_end = Cronet_kInvalidTime;
+  int64_t response_start = Cronet_kInvalidTime;
+  int64_t request_end = Cronet_kInvalidTime;
+  bool socket_reused = false;
+  int64_t sent_byte_count = -1;
+  int64_t received_byte_count = -1;
+
  private:
   DISALLOW_ASSIGN(Cronet_RequestFinishedInfo);
 };
diff --git a/components/cronet/native/generated/cronet.idl_impl_struct_unittest.cc b/components/cronet/native/generated/cronet.idl_impl_struct_unittest.cc
index 6a3b243..ba6106a 100644
--- a/components/cronet/native/generated/cronet.idl_impl_struct_unittest.cc
+++ b/components/cronet/native/generated/cronet.idl_impl_struct_unittest.cc
@@ -259,6 +259,70 @@
   Cronet_RequestFinishedInfoPtr second = Cronet_RequestFinishedInfo_Create();
 
   // Copy values from |first| to |second|.
+  Cronet_RequestFinishedInfo_request_start_set(
+      second, Cronet_RequestFinishedInfo_request_start_get(first));
+  EXPECT_EQ(Cronet_RequestFinishedInfo_request_start_get(first),
+            Cronet_RequestFinishedInfo_request_start_get(second));
+  Cronet_RequestFinishedInfo_dns_start_set(
+      second, Cronet_RequestFinishedInfo_dns_start_get(first));
+  EXPECT_EQ(Cronet_RequestFinishedInfo_dns_start_get(first),
+            Cronet_RequestFinishedInfo_dns_start_get(second));
+  Cronet_RequestFinishedInfo_dns_end_set(
+      second, Cronet_RequestFinishedInfo_dns_end_get(first));
+  EXPECT_EQ(Cronet_RequestFinishedInfo_dns_end_get(first),
+            Cronet_RequestFinishedInfo_dns_end_get(second));
+  Cronet_RequestFinishedInfo_connect_start_set(
+      second, Cronet_RequestFinishedInfo_connect_start_get(first));
+  EXPECT_EQ(Cronet_RequestFinishedInfo_connect_start_get(first),
+            Cronet_RequestFinishedInfo_connect_start_get(second));
+  Cronet_RequestFinishedInfo_connect_end_set(
+      second, Cronet_RequestFinishedInfo_connect_end_get(first));
+  EXPECT_EQ(Cronet_RequestFinishedInfo_connect_end_get(first),
+            Cronet_RequestFinishedInfo_connect_end_get(second));
+  Cronet_RequestFinishedInfo_ssl_start_set(
+      second, Cronet_RequestFinishedInfo_ssl_start_get(first));
+  EXPECT_EQ(Cronet_RequestFinishedInfo_ssl_start_get(first),
+            Cronet_RequestFinishedInfo_ssl_start_get(second));
+  Cronet_RequestFinishedInfo_ssl_end_set(
+      second, Cronet_RequestFinishedInfo_ssl_end_get(first));
+  EXPECT_EQ(Cronet_RequestFinishedInfo_ssl_end_get(first),
+            Cronet_RequestFinishedInfo_ssl_end_get(second));
+  Cronet_RequestFinishedInfo_sending_start_set(
+      second, Cronet_RequestFinishedInfo_sending_start_get(first));
+  EXPECT_EQ(Cronet_RequestFinishedInfo_sending_start_get(first),
+            Cronet_RequestFinishedInfo_sending_start_get(second));
+  Cronet_RequestFinishedInfo_sending_end_set(
+      second, Cronet_RequestFinishedInfo_sending_end_get(first));
+  EXPECT_EQ(Cronet_RequestFinishedInfo_sending_end_get(first),
+            Cronet_RequestFinishedInfo_sending_end_get(second));
+  Cronet_RequestFinishedInfo_push_start_set(
+      second, Cronet_RequestFinishedInfo_push_start_get(first));
+  EXPECT_EQ(Cronet_RequestFinishedInfo_push_start_get(first),
+            Cronet_RequestFinishedInfo_push_start_get(second));
+  Cronet_RequestFinishedInfo_push_end_set(
+      second, Cronet_RequestFinishedInfo_push_end_get(first));
+  EXPECT_EQ(Cronet_RequestFinishedInfo_push_end_get(first),
+            Cronet_RequestFinishedInfo_push_end_get(second));
+  Cronet_RequestFinishedInfo_response_start_set(
+      second, Cronet_RequestFinishedInfo_response_start_get(first));
+  EXPECT_EQ(Cronet_RequestFinishedInfo_response_start_get(first),
+            Cronet_RequestFinishedInfo_response_start_get(second));
+  Cronet_RequestFinishedInfo_request_end_set(
+      second, Cronet_RequestFinishedInfo_request_end_get(first));
+  EXPECT_EQ(Cronet_RequestFinishedInfo_request_end_get(first),
+            Cronet_RequestFinishedInfo_request_end_get(second));
+  Cronet_RequestFinishedInfo_socket_reused_set(
+      second, Cronet_RequestFinishedInfo_socket_reused_get(first));
+  EXPECT_EQ(Cronet_RequestFinishedInfo_socket_reused_get(first),
+            Cronet_RequestFinishedInfo_socket_reused_get(second));
+  Cronet_RequestFinishedInfo_sent_byte_count_set(
+      second, Cronet_RequestFinishedInfo_sent_byte_count_get(first));
+  EXPECT_EQ(Cronet_RequestFinishedInfo_sent_byte_count_get(first),
+            Cronet_RequestFinishedInfo_sent_byte_count_get(second));
+  Cronet_RequestFinishedInfo_received_byte_count_set(
+      second, Cronet_RequestFinishedInfo_received_byte_count_get(first));
+  EXPECT_EQ(Cronet_RequestFinishedInfo_received_byte_count_get(first),
+            Cronet_RequestFinishedInfo_received_byte_count_get(second));
   Cronet_RequestFinishedInfo_Destroy(first);
   Cronet_RequestFinishedInfo_Destroy(second);
 }
diff --git a/components/cronet/tools/generators/c_templates/module_c.h.tmpl b/components/cronet/tools/generators/c_templates/module_c.h.tmpl
index ef38db1..d7e87df0 100644
--- a/components/cronet/tools/generators/c_templates/module_c.h.tmpl
+++ b/components/cronet/tools/generators/c_templates/module_c.h.tmpl
@@ -62,6 +62,11 @@
 
 {%   endfor %}
 
+// Declare constants
+{%- for constant in module.constants %}
+{{constant|format_constant_declaration}};
+{%- endfor %}
+
 {#--- Interface Stubs -#}
 {%  for interface in interfaces %}
 {%- set interface_name = interface|get_name_for_kind %}
diff --git a/components/cronet/tools/generators/cronet_c_generator.py b/components/cronet/tools/generators/cronet_c_generator.py
index 721539b..01b827a 100644
--- a/components/cronet/tools/generators/cronet_c_generator.py
+++ b/components/cronet/tools/generators/cronet_c_generator.py
@@ -537,8 +537,8 @@
       return "%sextern const char %s[]" % \
           ((self.export_attribute + " ") if self.export_attribute else "",
            constant.name)
-    return "constexpr %s %s = %s" % (
-        GetCppPodType(constant.kind), constant.name,
+    return "const %s %s_%s = %s" % (
+        GetCppPodType(constant.kind), self.module.namespace, constant.name,
         self._ConstantValue(constant))
 
   def _GetCppWrapperType(self, kind, add_same_module_namespaces=False):
diff --git a/components/download/internal/background_service/controller_impl.cc b/components/download/internal/background_service/controller_impl.cc
index 717d390..eae2dc6 100644
--- a/components/download/internal/background_service/controller_impl.cc
+++ b/components/download/internal/background_service/controller_impl.cc
@@ -401,6 +401,8 @@
     case DownloadTaskType::CLEANUP_TASK:
       ScheduleCleanupTask();
       break;
+    case DownloadTaskType::DOWNLOAD_AUTO_RESUMPTION_TASK:
+      NOTREACHED();
   }
 }
 
diff --git a/components/download/internal/background_service/stats.cc b/components/download/internal/background_service/stats.cc
index ef8cf015..88aebf4a 100644
--- a/components/download/internal/background_service/stats.cc
+++ b/components/download/internal/background_service/stats.cc
@@ -48,6 +48,8 @@
       return "DownloadTask";
     case DownloadTaskType::CLEANUP_TASK:
       return "CleanUpTask";
+    case DownloadTaskType::DOWNLOAD_AUTO_RESUMPTION_TASK:
+      return "DownloadAutoResumptionTask";
   }
   NOTREACHED();
   return std::string();
diff --git a/components/download/internal/common/download_item_impl.cc b/components/download/internal/common/download_item_impl.cc
index a39e308..4b536f5 100644
--- a/components/download/internal/common/download_item_impl.cc
+++ b/components/download/internal/common/download_item_impl.cc
@@ -588,10 +588,12 @@
 }
 
 void DownloadItemImpl::UpdateResumptionInfo(bool user_resume) {
-  if (user_resume)
+  if (user_resume) {
     allow_metered_ |= delegate_->IsActiveNetworkMetered();
+    bytes_wasted_ = 0;
+  }
 
-  auto_resume_count_ = user_resume ? 0 : auto_resume_count_++;
+  auto_resume_count_ = user_resume ? 0 : ++auto_resume_count_;
 }
 
 void DownloadItemImpl::Cancel(bool user_cancel) {
diff --git a/components/download/internal/common/download_item_impl_delegate.cc b/components/download/internal/common/download_item_impl_delegate.cc
index 973248a..cc3fb6af5d 100644
--- a/components/download/internal/common/download_item_impl_delegate.cc
+++ b/components/download/internal/common/download_item_impl_delegate.cc
@@ -5,7 +5,9 @@
 #include "components/download/public/common/download_item_impl_delegate.h"
 
 #include "base/logging.h"
+#include "build/build_config.h"
 #include "components/download/database/in_progress/download_entry.h"
+#include "components/download/public/common/auto_resumption_handler.h"
 #include "components/download/public/common/download_danger_type.h"
 #include "components/download/public/common/download_item_impl.h"
 
@@ -94,7 +96,9 @@
 }
 
 bool DownloadItemImplDelegate::IsActiveNetworkMetered() const {
-  return false;
+  return download::AutoResumptionHandler::Get()
+             ? download::AutoResumptionHandler::Get()->IsActiveNetworkMetered()
+             : false;
 }
 
 void DownloadItemImplDelegate::ReportBytesWasted(DownloadItemImpl* download) {}
diff --git a/components/download/internal/common/in_progress_download_manager.cc b/components/download/internal/common/in_progress_download_manager.cc
index 276d856..abecb58 100644
--- a/components/download/internal/common/in_progress_download_manager.cc
+++ b/components/download/internal/common/in_progress_download_manager.cc
@@ -409,6 +409,12 @@
   on_initialized_callbacks_.clear();
 }
 
+void InProgressDownloadManager::GetAllDownloads(
+    std::vector<download::DownloadItem*>* downloads) const {
+  for (auto& item : in_progress_downloads_)
+    downloads->push_back(item.get());
+}
+
 DownloadItemImpl* InProgressDownloadManager::GetInProgressDownload(
     const std::string& guid) {
   for (auto& item : in_progress_downloads_) {
diff --git a/components/download/public/background_service/download_task_types.h b/components/download/public/background_service/download_task_types.h
index 48fcfb35..5140f520 100644
--- a/components/download/public/background_service/download_task_types.h
+++ b/components/download/public/background_service/download_task_types.h
@@ -15,6 +15,9 @@
 
   // Task to remove unnecessary files from the system.
   CLEANUP_TASK = 1,
+
+  // Task to invoke the download auto-resumption handler.
+  DOWNLOAD_AUTO_RESUMPTION_TASK = 2,
 };
 
 }  // namespace download
diff --git a/components/download/public/common/BUILD.gn b/components/download/public/common/BUILD.gn
index 8de5574..e14fe35 100644
--- a/components/download/public/common/BUILD.gn
+++ b/components/download/public/common/BUILD.gn
@@ -15,6 +15,8 @@
 
 component("public") {
   sources = [
+    "auto_resumption_handler.cc",
+    "auto_resumption_handler.h",
     "base_file.h",
     "download_content.h",
     "download_create_info.h",
@@ -65,6 +67,9 @@
 
   public_deps = [
     ":interfaces",
+    "//components/download/network",
+    "//components/download/public/background_service:public",
+    "//services/network/public/cpp",
   ]
 
   deps = [
diff --git a/components/download/public/common/auto_resumption_handler.cc b/components/download/public/common/auto_resumption_handler.cc
new file mode 100644
index 0000000..3e6dfa5
--- /dev/null
+++ b/components/download/public/common/auto_resumption_handler.cc
@@ -0,0 +1,295 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/download/public/common/auto_resumption_handler.h"
+
+#include <vector>
+
+#include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/download/public/background_service/task_scheduler.h"
+#include "url/gurl.h"
+
+namespace {
+
+static download::AutoResumptionHandler* g_auto_resumption_handler = nullptr;
+
+// The delay to wait for after a chrome restart before resuming all pending
+// downloads so that tab loading doesn't get impacted.
+const base::TimeDelta kAutoResumeStartupDelay =
+    base::TimeDelta::FromSeconds(10);
+
+// The interval at which various download updates are grouped together for
+// computing the params for the task scheduler.
+const base::TimeDelta kBatchDownloadUpdatesInterval =
+    base::TimeDelta::FromSeconds(1);
+
+// The delay to wait for before immediately retrying a download after it got
+// interrupted due to network reasons.
+const base::TimeDelta kDownloadImmediateRetryDelay =
+    base::TimeDelta::FromSeconds(1);
+
+// The task type to use for scheduling a task.
+const download::DownloadTaskType kResumptionTaskType =
+    download::DownloadTaskType::DOWNLOAD_AUTO_RESUMPTION_TASK;
+
+// The window start time after which the system should fire the task.
+const int64_t kWindowStartTimeSeconds = 0;
+
+// The window end time before which the system should fire the task.
+const int64_t kWindowEndTimeSeconds = 24 * 60 * 60;
+
+bool IsMetered(network::mojom::ConnectionType type) {
+  switch (type) {
+    case network::mojom::ConnectionType::CONNECTION_2G:
+    case network::mojom::ConnectionType::CONNECTION_3G:
+    case network::mojom::ConnectionType::CONNECTION_4G:
+      return true;
+    case network::mojom::ConnectionType::CONNECTION_ETHERNET:
+    case network::mojom::ConnectionType::CONNECTION_WIFI:
+    case network::mojom::ConnectionType::CONNECTION_UNKNOWN:
+    case network::mojom::ConnectionType::CONNECTION_NONE:
+    case network::mojom::ConnectionType::CONNECTION_BLUETOOTH:
+      return false;
+  }
+  NOTREACHED();
+  return false;
+}
+
+bool IsConnected(network::mojom::ConnectionType type) {
+  switch (type) {
+    case network::mojom::ConnectionType::CONNECTION_UNKNOWN:
+    case network::mojom::ConnectionType::CONNECTION_NONE:
+    case network::mojom::ConnectionType::CONNECTION_BLUETOOTH:
+      return false;
+    default:
+      return true;
+  }
+}
+
+bool IsInterruptedDownloadAutoResumable(download::DownloadItem* download_item,
+                                        int auto_resumption_size_limit) {
+  if (!download_item->GetURL().SchemeIsHTTPOrHTTPS())
+    return false;
+
+  if (download_item->GetBytesWasted() > auto_resumption_size_limit)
+    return false;
+
+  int interrupt_reason = download_item->GetLastReason();
+  DCHECK_NE(interrupt_reason, download::DOWNLOAD_INTERRUPT_REASON_NONE);
+  return interrupt_reason ==
+             download::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT ||
+         interrupt_reason ==
+             download::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED ||
+         interrupt_reason ==
+             download::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED ||
+         interrupt_reason == download::DOWNLOAD_INTERRUPT_REASON_CRASH;
+}
+
+}  // namespace
+
+namespace download {
+
+AutoResumptionHandler::Config::Config() : auto_resumption_size_limit(0) {}
+
+// static
+void AutoResumptionHandler::Create(
+    std::unique_ptr<download::NetworkStatusListener> network_listener,
+    std::unique_ptr<download::TaskManager> task_manager,
+    std::unique_ptr<Config> config) {
+  DCHECK(!g_auto_resumption_handler);
+  g_auto_resumption_handler = new AutoResumptionHandler(
+      std::move(network_listener), std::move(task_manager), std::move(config));
+}
+
+// static
+AutoResumptionHandler* AutoResumptionHandler::Get() {
+  return g_auto_resumption_handler;
+}
+
+AutoResumptionHandler::AutoResumptionHandler(
+    std::unique_ptr<download::NetworkStatusListener> network_listener,
+    std::unique_ptr<download::TaskManager> task_manager,
+    std::unique_ptr<Config> config)
+    : network_listener_(std::move(network_listener)),
+      task_manager_(std::move(task_manager)),
+      config_(std::move(config)),
+      weak_factory_(this) {
+  network_listener_->Start(this);
+}
+
+AutoResumptionHandler::~AutoResumptionHandler() {
+  network_listener_->Stop();
+}
+
+void AutoResumptionHandler::SetResumableDownloads(
+    const std::vector<download::DownloadItem*>& downloads) {
+  resumable_downloads_.clear();
+  for (auto* download : downloads) {
+    if (!IsAutoResumableDownload(download))
+      continue;
+    resumable_downloads_.insert(std::make_pair(download->GetGuid(), download));
+    download->RemoveObserver(this);
+    download->AddObserver(this);
+  }
+
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&AutoResumptionHandler::ResumePendingDownloads,
+                     weak_factory_.GetWeakPtr()),
+      kAutoResumeStartupDelay);
+}
+
+bool AutoResumptionHandler::IsActiveNetworkMetered() const {
+  return IsMetered(network_listener_->GetConnectionType());
+}
+
+void AutoResumptionHandler::OnNetworkChanged(
+    network::mojom::ConnectionType type) {
+  if (!IsConnected(type))
+    return;
+
+  ResumePendingDownloads();
+}
+
+void AutoResumptionHandler::OnDownloadStarted(download::DownloadItem* item) {
+  item->RemoveObserver(this);
+  item->AddObserver(this);
+
+  OnDownloadUpdated(item);
+}
+
+void AutoResumptionHandler::OnDownloadUpdated(download::DownloadItem* item) {
+  if (IsAutoResumableDownload(item))
+    resumable_downloads_[item->GetGuid()] = item;
+  else
+    resumable_downloads_.erase(item->GetGuid());
+
+  if (item->GetState() == download::DownloadItem::INTERRUPTED &&
+      IsAutoResumableDownload(item) && SatisfiesNetworkRequirements(item)) {
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&AutoResumptionHandler::ResumeDownload,
+                       weak_factory_.GetWeakPtr(), item),
+        kDownloadImmediateRetryDelay);
+    return;
+  }
+  RecomputeTaskParams();
+}
+
+void AutoResumptionHandler::OnDownloadRemoved(download::DownloadItem* item) {
+  resumable_downloads_.erase(item->GetGuid());
+  RecomputeTaskParams();
+}
+
+void AutoResumptionHandler::ResumeDownload(download::DownloadItem* download) {
+  if (SatisfiesNetworkRequirements(download))
+    download->Resume(false);
+  else
+    RecomputeTaskParams();
+}
+
+void AutoResumptionHandler::OnStartScheduledTask(
+    download::TaskFinishedCallback callback) {
+  task_manager_->OnStartScheduledTask(kResumptionTaskType, std::move(callback));
+  ResumePendingDownloads();
+}
+
+bool AutoResumptionHandler::OnStopScheduledTask() {
+  task_manager_->OnStopScheduledTask(kResumptionTaskType);
+  RescheduleTaskIfNecessary();
+  return false;
+}
+
+void AutoResumptionHandler::RecomputeTaskParams() {
+  if (recompute_task_params_scheduled_)
+    return;
+
+  recompute_task_params_scheduled_ = true;
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&AutoResumptionHandler::RescheduleTaskIfNecessary,
+                     weak_factory_.GetWeakPtr()),
+      kBatchDownloadUpdatesInterval);
+}
+
+void AutoResumptionHandler::RescheduleTaskIfNecessary() {
+  recompute_task_params_scheduled_ = false;
+
+  bool has_resumable_downloads = false;
+  bool has_actionable_downloads = false;
+  bool can_download_on_metered = false;
+  for (auto iter = resumable_downloads_.begin();
+       iter != resumable_downloads_.end(); ++iter) {
+    download::DownloadItem* download = iter->second;
+    if (!IsAutoResumableDownload(download))
+      continue;
+
+    has_resumable_downloads = true;
+    has_actionable_downloads |= SatisfiesNetworkRequirements(download);
+    can_download_on_metered |= download->AllowMetered();
+    if (can_download_on_metered)
+      break;
+  }
+
+  if (!has_actionable_downloads)
+    task_manager_->NotifyTaskFinished(kResumptionTaskType, false);
+
+  if (!has_resumable_downloads) {
+    task_manager_->UnscheduleTask(kResumptionTaskType);
+    return;
+  }
+
+  download::TaskManager::TaskParams task_params;
+  task_params.require_unmetered_network = !can_download_on_metered;
+  task_params.window_start_time_seconds = kWindowStartTimeSeconds;
+  task_params.window_end_time_seconds = kWindowEndTimeSeconds;
+  task_manager_->ScheduleTask(kResumptionTaskType, task_params);
+}
+
+void AutoResumptionHandler::ResumePendingDownloads() {
+  for (auto iter = resumable_downloads_.begin();
+       iter != resumable_downloads_.end(); ++iter) {
+    download::DownloadItem* download = iter->second;
+    if (!IsAutoResumableDownload(download))
+      continue;
+
+    if (SatisfiesNetworkRequirements(download))
+      download->Resume(false);
+  }
+}
+
+bool AutoResumptionHandler::SatisfiesNetworkRequirements(
+    download::DownloadItem* download) {
+  if (!IsConnected(network_listener_->GetConnectionType()))
+    return false;
+
+  return download->AllowMetered() || !IsActiveNetworkMetered();
+}
+
+bool AutoResumptionHandler::IsAutoResumableDownload(
+    download::DownloadItem* item) {
+  if (item->IsDangerous())
+    return false;
+
+  switch (item->GetState()) {
+    case download::DownloadItem::IN_PROGRESS:
+      return !item->IsPaused();
+    case download::DownloadItem::COMPLETE:
+    case download::DownloadItem::CANCELLED:
+      return false;
+    case download::DownloadItem::INTERRUPTED:
+      return !item->IsPaused() &&
+             IsInterruptedDownloadAutoResumable(
+                 item, config_->auto_resumption_size_limit);
+    case download::DownloadItem::MAX_DOWNLOAD_STATE:
+      NOTREACHED();
+  }
+
+  return false;
+}
+
+}  // namespace download
diff --git a/components/download/public/common/auto_resumption_handler.h b/components/download/public/common/auto_resumption_handler.h
new file mode 100644
index 0000000..c0bb504
--- /dev/null
+++ b/components/download/public/common/auto_resumption_handler.h
@@ -0,0 +1,90 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_DOWNLOAD_PUBLIC_COMMON_AUTO_RESUMPTION_HANDLER_H_
+#define COMPONENTS_DOWNLOAD_PUBLIC_COMMON_AUTO_RESUMPTION_HANDLER_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/download/network/network_status_listener.h"
+#include "components/download/public/background_service/task_manager.h"
+#include "components/download/public/common/download_export.h"
+#include "components/download/public/common/download_item.h"
+
+namespace download {
+
+// Handles auto-resumptions for downloads. Listens to network changes and
+// schecules task to resume downloads accordingly.
+class COMPONENTS_DOWNLOAD_EXPORT AutoResumptionHandler
+    : public download::NetworkStatusListener::Observer,
+      public download::DownloadItem::Observer {
+ public:
+  struct COMPONENTS_DOWNLOAD_EXPORT Config {
+    Config();
+    ~Config() = default;
+
+    int auto_resumption_size_limit;
+  };
+
+  // Creates the singleton instance of AutoResumptionHandler.
+  static void Create(
+      std::unique_ptr<download::NetworkStatusListener> network_listener,
+      std::unique_ptr<download::TaskManager> task_manager,
+      std::unique_ptr<Config> config);
+
+  // Returns the singleton instance of the AutoResumptionHandler.
+  static AutoResumptionHandler* Get();
+
+  AutoResumptionHandler(
+      std::unique_ptr<download::NetworkStatusListener> network_listener,
+      std::unique_ptr<download::TaskManager> task_manager,
+      std::unique_ptr<Config> config);
+  ~AutoResumptionHandler() override;
+
+  void SetResumableDownloads(
+      const std::vector<download::DownloadItem*>& downloads);
+  bool IsActiveNetworkMetered() const;
+  void OnStartScheduledTask(download::TaskFinishedCallback callback);
+  bool OnStopScheduledTask();
+
+  void OnDownloadStarted(download::DownloadItem* item);
+
+  // DownloadItem::Observer overrides.
+  void OnDownloadUpdated(download::DownloadItem* item) override;
+  void OnDownloadRemoved(download::DownloadItem* item) override;
+
+ private:
+  // NetworkStatusListener::Observer implementation.
+  void OnNetworkChanged(network::mojom::ConnectionType type) override;
+
+  void ResumePendingDownloads();
+  void RecomputeTaskParams();
+  void RescheduleTaskIfNecessary();
+  void ResumeDownload(download::DownloadItem* download);
+  bool SatisfiesNetworkRequirements(download::DownloadItem* download);
+  bool IsAutoResumableDownload(download::DownloadItem* item);
+
+  std::unique_ptr<download::NetworkStatusListener> network_listener_;
+
+  std::unique_ptr<download::TaskManager> task_manager_;
+
+  std::unique_ptr<Config> config_;
+
+  std::map<std::string, download::DownloadItem*> resumable_downloads_;
+
+  bool recompute_task_params_scheduled_ = false;
+
+  base::WeakPtrFactory<AutoResumptionHandler> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(AutoResumptionHandler);
+};
+
+}  // namespace download
+
+#endif  // COMPONENTS_DOWNLOAD_PUBLIC_COMMON_AUTO_RESUMPTION_HANDLER_H_
diff --git a/components/download/public/common/in_progress_download_manager.h b/components/download/public/common/in_progress_download_manager.h
index d76534f..563d97d 100644
--- a/components/download/public/common/in_progress_download_manager.h
+++ b/components/download/public/common/in_progress_download_manager.h
@@ -122,6 +122,9 @@
   // Called to remove an in-progress download.
   void RemoveInProgressDownload(const std::string& guid);
 
+  // Called to get all in-progress downloads.
+  void GetAllDownloads(std::vector<download::DownloadItem*>* downloads) const;
+
   // Called to retrieve an in-progress download.
   DownloadItemImpl* GetInProgressDownload(const std::string& guid);
 
diff --git a/components/leveldb_proto/shared_proto_database.cc b/components/leveldb_proto/shared_proto_database.cc
index d306534..6f349388 100644
--- a/components/leveldb_proto/shared_proto_database.cc
+++ b/components/leveldb_proto/shared_proto_database.cc
@@ -30,7 +30,8 @@
       db_dir_(db_dir),
       db_wrapper_(std::make_unique<ProtoLevelDBWrapper>(task_runner_)),
       db_(std::make_unique<LevelDB>(client_name.c_str())),
-      weak_factory_(this) {
+      weak_factory_(
+          std::make_unique<base::WeakPtrFactory<SharedProtoDatabase>>(this)) {
   DETACH_FROM_SEQUENCE(on_task_runner_);
 }
 
@@ -45,7 +46,7 @@
   task_runner_->PostTaskAndReply(
       FROM_HERE, base::DoNothing(),
       base::BindOnce(&SharedProtoDatabase::RunInitCallback,
-                     weak_factory_.GetWeakPtr(), std::move(callback)));
+                     weak_factory_->GetWeakPtr(), std::move(callback)));
 }
 
 void SharedProtoDatabase::RunInitCallback(
@@ -89,7 +90,7 @@
   db_wrapper_->InitWithDatabase(
       db_.get(), db_dir_, options, false /* destroy_on_corruption */,
       base::BindOnce(&SharedProtoDatabase::OnDatabaseInit,
-                     weak_factory_.GetWeakPtr(), std::move(callback),
+                     weak_factory_->GetWeakPtr(), std::move(callback),
                      std::move(callback_task_runner)));
 }
 
@@ -117,7 +118,9 @@
                                  base::BindOnce(std::move(callback), status));
 }
 
-SharedProtoDatabase::~SharedProtoDatabase() = default;
+SharedProtoDatabase::~SharedProtoDatabase() {
+  task_runner_->DeleteSoon(FROM_HERE, std::move(weak_factory_));
+}
 
 LevelDB* SharedProtoDatabase::GetLevelDBForTesting() const {
   return db_.get();
diff --git a/components/leveldb_proto/shared_proto_database.h b/components/leveldb_proto/shared_proto_database.h
index 8ab5f5b..18b8750 100644
--- a/components/leveldb_proto/shared_proto_database.h
+++ b/components/leveldb_proto/shared_proto_database.h
@@ -116,7 +116,7 @@
                        scoped_refptr<base::SequencedTaskRunner>>>
       outstanding_init_requests_;
 
-  base::WeakPtrFactory<SharedProtoDatabase> weak_factory_;
+  std::unique_ptr<base::WeakPtrFactory<SharedProtoDatabase>> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SharedProtoDatabase);
 };
@@ -150,7 +150,7 @@
   auto current_task_runner = base::SequencedTaskRunnerHandle::Get();
   task_runner_->PostTask(
       FROM_HERE,
-      base::BindOnce(&SharedProtoDatabase::Init, weak_factory_.GetWeakPtr(),
+      base::BindOnce(&SharedProtoDatabase::Init, weak_factory_->GetWeakPtr(),
                      create_if_missing,
                      base::BindOnce(&GetClientInitCallback<T>,
                                     std::move(callback), std::move(client)),
@@ -167,7 +167,7 @@
   auto current_task_runner = base::SequencedTaskRunnerHandle::Get();
   task_runner_->PostTask(
       FROM_HERE,
-      base::BindOnce(&SharedProtoDatabase::Init, weak_factory_.GetWeakPtr(),
+      base::BindOnce(&SharedProtoDatabase::Init, weak_factory_->GetWeakPtr(),
                      create_if_missing, std::move(callback),
                      std::move(current_task_runner)));
   return GetClientInternal<T>(client_namespace, type_prefix);
diff --git a/components/ntp_snippets/contextual/contextual_content_suggestions_service.cc b/components/ntp_snippets/contextual/contextual_content_suggestions_service.cc
index 0fd2a63a2..3a88cdca 100644
--- a/components/ntp_snippets/contextual/contextual_content_suggestions_service.cc
+++ b/components/ntp_snippets/contextual/contextual_content_suggestions_service.cc
@@ -14,17 +14,11 @@
 #include "components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy.h"
 #include "components/ntp_snippets/contextual/contextual_suggestions_features.h"
 #include "components/ntp_snippets/contextual/contextual_suggestions_result.h"
-#include "components/ntp_snippets/remote/cached_image_fetcher.h"
-#include "components/ntp_snippets/remote/remote_suggestions_database.h"
 #include "components/ntp_snippets/remote/remote_suggestions_provider_impl.h"
-#include "ui/gfx/image/image.h"
 
 namespace contextual_suggestions {
 
 using ntp_snippets::ContentSuggestion;
-using ntp_snippets::ImageDataFetchedCallback;
-using ntp_snippets::ImageFetchedCallback;
-using ntp_snippets::CachedImageFetcher;
 using ntp_snippets::RemoteSuggestionsDatabase;
 
 namespace {
@@ -37,15 +31,10 @@
 ContextualContentSuggestionsService::ContextualContentSuggestionsService(
     std::unique_ptr<ContextualSuggestionsFetcher>
         contextual_suggestions_fetcher,
-    std::unique_ptr<CachedImageFetcher> image_fetcher,
-    std::unique_ptr<RemoteSuggestionsDatabase> contextual_suggestions_database,
     std::unique_ptr<ContextualSuggestionsReporterProvider> reporter_provider)
-    : contextual_suggestions_database_(
-          std::move(contextual_suggestions_database)),
-      fetch_cache_(kFetchCacheCapacity),
+    : fetch_cache_(kFetchCacheCapacity),
       contextual_suggestions_fetcher_(
           std::move(contextual_suggestions_fetcher)),
-      image_fetcher_(std::move(image_fetcher)),
       reporter_provider_(std::move(reporter_provider)) {}
 
 ContextualContentSuggestionsService::~ContextualContentSuggestionsService() =
@@ -70,15 +59,6 @@
   }
 }
 
-void ContextualContentSuggestionsService::FetchContextualSuggestionImage(
-    const ContentSuggestion::ID& suggestion_id,
-    const GURL& image_url,
-    ImageFetchedCallback callback) {
-  image_fetcher_->FetchSuggestionImage(suggestion_id, image_url,
-                                       ImageDataFetchedCallback(),
-                                       std::move(callback));
-}
-
 void ContextualContentSuggestionsService::FetchDone(
     const GURL& url,
     FetchClustersCallback callback,
diff --git a/components/ntp_snippets/contextual/contextual_content_suggestions_service.h b/components/ntp_snippets/contextual/contextual_content_suggestions_service.h
index d2f4b7a..de7dcde 100644
--- a/components/ntp_snippets/contextual/contextual_content_suggestions_service.h
+++ b/components/ntp_snippets/contextual/contextual_content_suggestions_service.h
@@ -12,7 +12,6 @@
 
 #include "base/callback.h"
 #include "base/optional.h"
-#include "components/image_fetcher/core/image_fetcher.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/ntp_snippets/callbacks.h"
 #include "components/ntp_snippets/content_suggestion.h"
@@ -22,27 +21,18 @@
 #include "components/ntp_snippets/contextual/reporting/contextual_suggestions_reporter.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 
-namespace ntp_snippets {
-class CachedImageFetcher;
-class RemoteSuggestionsDatabase;
-}  // namespace ntp_snippets
-
 namespace contextual_suggestions {
 
 static constexpr int kFetchCacheCapacity = 10;
 
 class ContextualContentSuggestionsServiceProxy;
 
-// Retrieves contextual suggestions for a given URL and fetches images
-// for contextual suggestion, using caching.
+// Retrieves contextual suggestions for a given URL using caching.
 class ContextualContentSuggestionsService : public KeyedService {
  public:
   ContextualContentSuggestionsService(
       std::unique_ptr<ContextualSuggestionsFetcher>
           contextual_suggestions_fetcher,
-      std::unique_ptr<ntp_snippets::CachedImageFetcher> image_fetcher,
-      std::unique_ptr<ntp_snippets::RemoteSuggestionsDatabase>
-          contextual_suggestions_database,
       std::unique_ptr<
           contextual_suggestions::ContextualSuggestionsReporterProvider>
           reporter_provider);
@@ -54,13 +44,6 @@
       FetchClustersCallback callback,
       ReportFetchMetricsCallback metrics_callback);
 
-  // Fetches an image pointed to by |url| and internally caches it using
-  // |suggestion_id|. Asynchronous if cache or network is queried.
-  virtual void FetchContextualSuggestionImage(
-      const ntp_snippets::ContentSuggestion::ID& suggestion_id,
-      const GURL& url,
-      ntp_snippets::ImageFetchedCallback callback);
-
   void FetchDone(const GURL& url,
                  FetchClustersCallback callback,
                  ReportFetchMetricsCallback metrics_callback,
@@ -82,9 +65,6 @@
   void BelowConfidenceThresholdFetchDone(
       FetchClustersCallback callback,
       ReportFetchMetricsCallback metrics_callback);
-  // Cache for images of contextual suggestions, needed by CachedImageFetcher.
-  std::unique_ptr<ntp_snippets::RemoteSuggestionsDatabase>
-      contextual_suggestions_database_;
 
   // Cache of contextual suggestions fetch results, keyed by the context url.
   ContextualSuggestionsCache fetch_cache_;
@@ -92,15 +72,9 @@
   // Performs actual network request to fetch contextual suggestions.
   std::unique_ptr<ContextualSuggestionsFetcher> contextual_suggestions_fetcher_;
 
-  std::unique_ptr<ntp_snippets::CachedImageFetcher> image_fetcher_;
-
   std::unique_ptr<contextual_suggestions::ContextualSuggestionsReporterProvider>
       reporter_provider_;
 
-  // Look up by ContentSuggestion::ID::id_within_category() aka std::string to
-  // get image URL.
-  std::map<std::string, GURL> image_url_by_id_;
-
   DISALLOW_COPY_AND_ASSIGN(ContextualContentSuggestionsService);
 };
 
diff --git a/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy.cc b/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy.cc
index 2cd0e8d..1329c359 100644
--- a/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy.cc
+++ b/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy.cc
@@ -49,36 +49,29 @@
           weak_ptr_factory_.GetWeakPtr(), last_ukm_source_id_, url.spec()));
 }
 
-void ContextualContentSuggestionsServiceProxy::FetchContextualSuggestionImage(
-    const std::string& suggestion_id,
-    ntp_snippets::ImageFetchedCallback callback) {
+std::string
+ContextualContentSuggestionsServiceProxy::GetContextualSuggestionImageUrl(
+    const std::string& suggestion_id) {
   auto suggestion_iter = suggestions_.find(suggestion_id);
   if (suggestion_iter == suggestions_.end()) {
     DVLOG(1) << "Unkown suggestion ID: " << suggestion_id;
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), gfx::Image()));
-    return;
+    return "";
   }
 
   std::string& image_id = suggestion_iter->second.image_id;
-  GURL image_url = ImageUrlFromId(image_id);
-
-  FetchImageImpl(image_url, image_id, std::move(callback));
+  return ImageUrlFromId(image_id).spec();
 }
 
-void ContextualContentSuggestionsServiceProxy::FetchContextualSuggestionFavicon(
-    const std::string& suggestion_id,
-    ntp_snippets::ImageFetchedCallback callback) {
+std::string
+ContextualContentSuggestionsServiceProxy::GetContextualSuggestionFaviconUrl(
+    const std::string& suggestion_id) {
   auto suggestion_iter = suggestions_.find(suggestion_id);
   if (suggestion_iter == suggestions_.end()) {
     DVLOG(1) << "Unkown suggestion ID: " << suggestion_id;
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), gfx::Image()));
-    return;
+    return "";
   }
 
-  FetchImageImpl(GURL(suggestion_iter->second.favicon_image_url),
-                 suggestion_iter->second.favicon_image_id, std::move(callback));
+  return suggestion_iter->second.favicon_image_url;
 }
 
 void ContextualContentSuggestionsServiceProxy::ClearState() {
@@ -115,19 +108,6 @@
   last_ukm_source_id_ = ukm::kInvalidSourceId;
 }
 
-void ContextualContentSuggestionsServiceProxy::FetchImageImpl(
-    const GURL& image_url,
-    const std::string& image_id,
-    ntp_snippets::ImageFetchedCallback callback) {
-  ntp_snippets::ContentSuggestion::ID synthetic_cache_id(
-      ntp_snippets::Category::FromKnownCategory(
-          ntp_snippets::KnownCategories::CONTEXTUAL),
-      image_id);
-
-  service_->FetchContextualSuggestionImage(synthetic_cache_id, image_url,
-                                           std::move(callback));
-}
-
 void ContextualContentSuggestionsServiceProxy::CacheSuggestions(
     FetchClustersCallback callback,
     ContextualSuggestionsResult result) {
diff --git a/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy.h b/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy.h
index 64c82075..19828d79 100644
--- a/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy.h
+++ b/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy.h
@@ -34,17 +34,12 @@
   void FetchContextualSuggestions(const GURL& url,
                                   FetchClustersCallback callback);
 
-  // Fetches an image for a contextual suggestion with specified
-  // |suggestion_id|.
-  void FetchContextualSuggestionImage(
-      const std::string& suggestion_id,
-      ntp_snippets::ImageFetchedCallback callback);
+  // Get the URL for the given suggestion id.
+  std::string GetContextualSuggestionImageUrl(const std::string& suggestion_id);
 
-  // Fetches a favicon for a contextual suggestion with specified
-  // |suggestion_id|.
-  void FetchContextualSuggestionFavicon(
-      const std::string& suggestion_id,
-      ntp_snippets::ImageFetchedCallback callback);
+  // Get the URL for the given suggestion id.
+  std::string GetContextualSuggestionFaviconUrl(
+      const std::string& suggestion_id);
 
   // Clears the state of the proxy.
   void ClearState();
@@ -58,10 +53,6 @@
   void FlushMetrics();
 
  private:
-  void FetchImageImpl(const GURL& image_url,
-                      const std::string& image_id,
-                      ntp_snippets::ImageFetchedCallback callback);
-
   void CacheSuggestions(FetchClustersCallback callback,
                         ContextualSuggestionsResult result);
   // Pointer to the service.
diff --git a/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy_unittest.cc b/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy_unittest.cc
index 674543a..ca36b2e 100644
--- a/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy_unittest.cc
+++ b/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy_unittest.cc
@@ -12,7 +12,6 @@
 #include "components/ntp_snippets/contextual/contextual_content_suggestions_service.h"
 #include "components/ntp_snippets/contextual/contextual_suggestions_test_utils.h"
 #include "components/ntp_snippets/contextual/reporting/contextual_suggestions_metrics_reporter.h"
-#include "components/ntp_snippets/remote/cached_image_fetcher.h"
 #include "components/ntp_snippets/remote/remote_suggestions_database.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -27,6 +26,11 @@
 static constexpr char kTestPeekText[] = "Test peek test";
 static constexpr char kValidFromUrl[] = "http://some.url";
 
+static constexpr char kImageId[] = "id";
+// Keep the string after tbn: in sync with the above constant for image id.
+static constexpr char kImageUrl[] = "http://www.google.com/images?q=tbn:id";
+static constexpr char kFaviconUrl[] = "http://google.com/cats.fav";
+
 class FakeContextualContentSuggestionsService
     : public ContextualContentSuggestionsService {
  public:
@@ -50,7 +54,7 @@
 
 FakeContextualContentSuggestionsService::
     FakeContextualContentSuggestionsService()
-    : ContextualContentSuggestionsService(nullptr, nullptr, nullptr, nullptr) {}
+    : ContextualContentSuggestionsService(nullptr, nullptr) {}
 
 FakeContextualContentSuggestionsService::
     ~FakeContextualContentSuggestionsService() {}
@@ -90,6 +94,30 @@
   EXPECT_TRUE(mock_cluster_callback.has_run);
 }
 
+TEST_F(ContextualContentSuggestionsServiceProxyTest, GetImageUrl) {
+  auto suggestion = SuggestionBuilder(GURL(kValidFromUrl))
+                        .ImageId(kImageId)
+                        .FaviconImageUrl(kFaviconUrl)
+                        .Build();
+  auto cluster =
+      ClusterBuilder(kValidFromUrl).AddSuggestion(suggestion).Build();
+  MockClustersCallback mock_cluster_callback;
+
+  proxy()->FetchContextualSuggestions(GURL(kValidFromUrl),
+                                      mock_cluster_callback.ToOnceCallback());
+  service()->RunClustersCallback(ContextualSuggestionsResult(
+      kTestPeekText, std::vector<Cluster>({cluster}), PeekConditions(),
+      ServerExperimentInfos()));
+
+  EXPECT_EQ(kImageUrl, proxy()->GetContextualSuggestionImageUrl(suggestion.id));
+  EXPECT_EQ(kFaviconUrl,
+            proxy()->GetContextualSuggestionFaviconUrl(suggestion.id));
+
+  // Id not found.
+  EXPECT_EQ("", proxy()->GetContextualSuggestionImageUrl(""));
+  EXPECT_EQ("", proxy()->GetContextualSuggestionFaviconUrl(""));
+}
+
 // TODO(fgorski): More tests will be added, once we have the suggestions
 // restructured.
 
diff --git a/components/ntp_snippets/contextual/contextual_content_suggestions_service_unittest.cc b/components/ntp_snippets/contextual/contextual_content_suggestions_service_unittest.cc
index c732147..ae4ea73 100644
--- a/components/ntp_snippets/contextual/contextual_content_suggestions_service_unittest.cc
+++ b/components/ntp_snippets/contextual/contextual_content_suggestions_service_unittest.cc
@@ -15,7 +15,6 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_task_environment.h"
-#include "components/image_fetcher/core/image_fetcher_impl.h"
 #include "components/ntp_snippets/category_info.h"
 #include "components/ntp_snippets/content_suggestion.h"
 #include "components/ntp_snippets/contextual/contextual_suggestion.h"
@@ -23,23 +22,16 @@
 #include "components/ntp_snippets/contextual/contextual_suggestions_test_utils.h"
 #include "components/ntp_snippets/contextual/reporting/contextual_suggestions_debugging_reporter.h"
 #include "components/ntp_snippets/contextual/reporting/contextual_suggestions_reporter.h"
-#include "components/ntp_snippets/remote/cached_image_fetcher.h"
 #include "components/ntp_snippets/remote/json_to_categories.h"
-#include "components/ntp_snippets/remote/remote_suggestions_database.h"
+#include "components/ntp_snippets/remote/request_throttler.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/image/image.h"
-#include "ui/gfx/image/image_unittest_util.h"
 
-using ntp_snippets::CachedImageFetcher;
 using ntp_snippets::Category;
 using ntp_snippets::ContentSuggestion;
 using ntp_snippets::KnownCategories;
-using ntp_snippets::ImageFetchedCallback;
-using ntp_snippets::ImageDataFetchedCallback;
-using ntp_snippets::RemoteSuggestionsDatabase;
 using ntp_snippets::RequestThrottler;
 
 using testing::_;
@@ -80,26 +72,6 @@
   PeekConditions peek_conditions_;
 };
 
-// Always fetches a fake image if the given URL is valid.
-class FakeCachedImageFetcher : public CachedImageFetcher {
- public:
-  explicit FakeCachedImageFetcher(PrefService* pref_service)
-      : CachedImageFetcher(std::unique_ptr<image_fetcher::ImageFetcher>(),
-                           pref_service,
-                           nullptr) {}
-
-  void FetchSuggestionImage(const ContentSuggestion::ID&,
-                            const GURL& image_url,
-                            ImageDataFetchedCallback image_data_callback,
-                            ImageFetchedCallback callback) override {
-    gfx::Image image;
-    if (image_url.is_valid()) {
-      image = gfx::test::CreateImage();
-    }
-    std::move(callback).Run(image);
-  }
-};
-
 }  // namespace
 
 class ContextualContentSuggestionsServiceTest : public testing::Test {
@@ -116,8 +88,6 @@
         std::move(debugging_reporter));
     source_ = std::make_unique<ContextualContentSuggestionsService>(
         std::move(fetcher),
-        std::make_unique<FakeCachedImageFetcher>(&pref_service_),
-        std::unique_ptr<RemoteSuggestionsDatabase>(),
         std::move(reporter_provider));
   }
 
diff --git a/components/omnibox/browser/suggestion_answer.cc b/components/omnibox/browser/suggestion_answer.cc
index 0adf61d..de02c03 100644
--- a/components/omnibox/browser/suggestion_answer.cc
+++ b/components/omnibox/browser/suggestion_answer.cc
@@ -292,7 +292,7 @@
          second_line_.Equals(answer.second_line_);
 }
 
-void SuggestionAnswer::AddImageURLsTo(std::vector<GURL>* urls) const {
+void SuggestionAnswer::AddImageURLsTo(URLs* urls) const {
   // Note: first_line_.image_url() is not used in practice (so it's ignored).
   if (image_url_.is_valid())
     urls->push_back(image_url_);
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index ea1777e..d572d9a 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -27,6 +27,7 @@
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/shell/browser/shell.h"
 #include "content/test/accessibility_browser_test_utils.h"
+#include "ui/base/ui_base_features.h"
 
 #if defined(OS_MACOSX)
 #include "base/mac/mac_util.h"
@@ -1882,6 +1883,12 @@
   RunHtmlTest(FILE_PATH_LITERAL("video.html"));
 }
 
+// TODO(crbug.com/916003): Fix race condition.
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+                       DISABLED_AccessibilityVideoControls) {
+  RunHtmlTest(FILE_PATH_LITERAL("video-controls.html"));
+}
+
 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityWbr) {
   RunHtmlTest(FILE_PATH_LITERAL("wbr.html"));
 }
diff --git a/content/browser/devtools/protocol/input_handler.cc b/content/browser/devtools/protocol/input_handler.cc
index 09b41c2d..86ceb74 100644
--- a/content/browser/devtools/protocol/input_handler.cc
+++ b/content/browser/devtools/protocol/input_handler.cc
@@ -176,6 +176,15 @@
   return blink::WebInputEvent::kUndefined;
 }
 
+blink::WebPointerProperties::PointerType GetPointerType(
+    const std::string& type) {
+  if (type == Input::DispatchMouseEvent::PointerTypeEnum::Mouse)
+    return blink::WebPointerProperties::PointerType::kMouse;
+  if (type == Input::DispatchMouseEvent::PointerTypeEnum::Pen)
+    return blink::WebPointerProperties::PointerType::kPen;
+  return blink::WebPointerProperties::PointerType::kMouse;
+}
+
 bool GenerateTouchPoints(
     blink::WebTouchEvent* event,
     blink::WebInputEvent::Type type,
@@ -598,6 +607,7 @@
     Maybe<int> click_count,
     Maybe<double> delta_x,
     Maybe<double> delta_y,
+    Maybe<std::string> pointer_type,
     std::unique_ptr<DispatchMouseEventCallback> callback) {
   blink::WebInputEvent::Type type = GetMouseEventType(event_type);
   if (type == blink::WebInputEvent::kUndefined) {
@@ -642,7 +652,7 @@
 
   mouse_event->button = button;
   mouse_event->click_count = click_count.fromMaybe(0);
-  mouse_event->pointer_type = blink::WebPointerProperties::PointerType::kMouse;
+  mouse_event->pointer_type = GetPointerType(pointer_type.fromMaybe(""));
 
   gfx::PointF point;
   RenderWidgetHostImpl* widget_host =
diff --git a/content/browser/devtools/protocol/input_handler.h b/content/browser/devtools/protocol/input_handler.h
index 62cc8405..0d9f46d 100644
--- a/content/browser/devtools/protocol/input_handler.h
+++ b/content/browser/devtools/protocol/input_handler.h
@@ -70,6 +70,7 @@
       Maybe<int> click_count,
       Maybe<double> delta_x,
       Maybe<double> delta_y,
+      Maybe<std::string> pointer_type,
       std::unique_ptr<DispatchMouseEventCallback> callback) override;
 
   void DispatchTouchEvent(
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index 70fc8ad..6cdb9cd 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -2146,27 +2146,27 @@
 
   parameters.injected_errors.pop();
   TestDownloadHttpResponse::StartServing(parameters, server_url2);
-  download->Resume(false);
+  download->Resume(true);
   WaitForInterrupt(download);
 
   parameters.injected_errors.pop();
   TestDownloadHttpResponse::StartServing(parameters, server_url2);
-  download->Resume(false);
+  download->Resume(true);
   WaitForInterrupt(download);
 
   parameters.injected_errors.pop();
   TestDownloadHttpResponse::StartServing(parameters, server_url2);
-  download->Resume(false);
+  download->Resume(true);
   WaitForInterrupt(download);
 
   parameters.injected_errors.pop();
   TestDownloadHttpResponse::StartServing(parameters, server_url2);
-  download->Resume(false);
+  download->Resume(true);
   WaitForInterrupt(download);
 
   parameters.injected_errors.pop();
   TestDownloadHttpResponse::StartServing(parameters, server_url2);
-  download->Resume(false);
+  download->Resume(true);
   WaitForCompletion(download);
 
   EXPECT_EQ(expected_hash, download->GetHash());
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index 644ddcb9..163932d 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -774,11 +774,6 @@
   return browser_context_->IsOffTheRecord();
 }
 
-bool DownloadManagerImpl::IsActiveNetworkMetered() const {
-  // TODO(shaktisahu): Call ChromeDownloadManagerDelegate to get this.
-  return false;
-}
-
 void DownloadManagerImpl::ReportBytesWasted(
     download::DownloadItemImpl* download) {
   in_progress_manager_->ReportBytesWasted(download);
diff --git a/content/browser/download/download_manager_impl.h b/content/browser/download/download_manager_impl.h
index d596137..b94e85c 100644
--- a/content/browser/download/download_manager_impl.h
+++ b/content/browser/download/download_manager_impl.h
@@ -257,7 +257,6 @@
   base::Optional<download::DownloadEntry> GetInProgressEntry(
       download::DownloadItemImpl* download) override;
   bool IsOffTheRecord() const override;
-  bool IsActiveNetworkMetered() const override;
   void ReportBytesWasted(download::DownloadItemImpl* download) override;
 
   // Drops a download before it is created.
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 1897079..93938ee 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -2269,15 +2269,12 @@
   associated_registry->AddInterface(base::Bind(
       &RenderProcessHostImpl::CreateRendererHost, base::Unretained(this)));
 
-  // TODO(lukasza): https://crbug.com/891872: Stop vending out non-origin-bound
-  // URLLoaderFactories to the renderer process.
   if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
-    const base::Optional<url::Origin> kNoOrigin = base::nullopt;
     AddUIThreadInterface(
         registry.get(),
-        base::BindRepeating(&RenderProcessHostImpl::CreateURLLoaderFactory,
-                            base::Unretained(this), kNoOrigin,
-                            nullptr /* header_client */));
+        base::BindRepeating(
+            &RenderProcessHostImpl::CreateURLLoaderFactoryForRendererProcess,
+            base::Unretained(this)));
   }
 
   registry->AddInterface(
@@ -2536,10 +2533,28 @@
   return &process_resource_coordinator_;
 }
 
+void RenderProcessHostImpl::CreateURLLoaderFactoryForRendererProcess(
+    network::mojom::URLLoaderFactoryRequest request) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  base::Optional<url::Origin> request_initiator_site_lock;
+  GURL process_lock =
+      ChildProcessSecurityPolicyImpl::GetInstance()->GetOriginLock(GetID());
+  if (process_lock.is_valid()) {
+    request_initiator_site_lock = SiteInstanceImpl::GetRequestInitiatorSiteLock(
+        GetBrowserContext(), process_lock);
+  }
+
+  CreateURLLoaderFactory(request_initiator_site_lock,
+                         nullptr /* header_client */, std::move(request));
+}
+
 void RenderProcessHostImpl::CreateURLLoaderFactory(
     const base::Optional<url::Origin>& origin,
     network::mojom::TrustedURLLoaderHeaderClientPtrInfo header_client,
     network::mojom::URLLoaderFactoryRequest request) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
   // "chrome-guest://..." is never used as a |request_initiator|.  Therefore
   // it doesn't make sense to associate a URLLoaderFactory with a
   // chrome-guest-based |origin|.
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index f17fb77..efaf56d 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -661,6 +661,15 @@
   // execute.
   void CancelProcessShutdownDelayForUnload();
 
+  // Creates a URLLoaderFactory that can be used by the renderer process,
+  // without binding it to a specific frame or an origin.
+  //
+  // TODO(kinuko, lukasza): https://crbug.com/891872: Remove, once all
+  // URLLoaderFactories are associated with a specific origin and an execution
+  // context (e.g. a frame, a service worker or any other kind of worker).
+  void CreateURLLoaderFactoryForRendererProcess(
+      network::mojom::URLLoaderFactoryRequest request);
+
   mojo::OutgoingInvitation mojo_invitation_;
 
   std::unique_ptr<ChildConnection> child_connection_;
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 1536a11..dfe596f 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -164,7 +164,7 @@
 }  // namespace
 
 // static
-const int64_t RenderViewHostImpl::kUnloadTimeoutMS = 1000;
+const int64_t RenderViewHostImpl::kUnloadTimeoutMS = 500;
 
 ///////////////////////////////////////////////////////////////////////////////
 // RenderViewHost, public:
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 02c5cd1..1da0c85d 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -12910,9 +12910,18 @@
   cc::TouchAction expected_touch_action = cc::kTouchActionPan;
   GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
                           effective_touch_action, whitelisted_touch_action);
-  EXPECT_EQ(expected_touch_action, effective_touch_action.has_value()
-                                       ? effective_touch_action.value()
-                                       : cc::kTouchActionAuto);
+  cc::TouchAction effective_touch_action_result =
+      effective_touch_action.has_value() ? effective_touch_action.value()
+                                         : cc::kTouchActionAuto;
+  // TouchAction might have not been propagated to child frames yet, loop until
+  // we get the expected touch action value.
+  while (expected_touch_action != effective_touch_action_result) {
+    GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
+                            effective_touch_action, whitelisted_touch_action);
+    effective_touch_action_result = effective_touch_action.has_value()
+                                        ? effective_touch_action.value()
+                                        : cc::kTouchActionAuto;
+  }
   if (whitelisted_touch_action.has_value())
     EXPECT_EQ(expected_touch_action, whitelisted_touch_action.value());
 
@@ -12926,9 +12935,16 @@
                             child_thread_observer.get());
   GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
                           effective_touch_action, whitelisted_touch_action);
-  EXPECT_EQ(expected_touch_action, effective_touch_action.has_value()
-                                       ? effective_touch_action.value()
-                                       : cc::kTouchActionAuto);
+  effective_touch_action_result = effective_touch_action.has_value()
+                                      ? effective_touch_action.value()
+                                      : cc::kTouchActionAuto;
+  while (expected_touch_action != effective_touch_action_result) {
+    GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
+                            effective_touch_action, whitelisted_touch_action);
+    effective_touch_action_result = effective_touch_action.has_value()
+                                        ? effective_touch_action.value()
+                                        : cc::kTouchActionAuto;
+  }
   if (whitelisted_touch_action.has_value())
     EXPECT_EQ(expected_touch_action, whitelisted_touch_action.value());
 
@@ -12941,9 +12957,16 @@
   expected_touch_action = cc::kTouchActionAuto;
   GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
                           effective_touch_action, whitelisted_touch_action);
-  EXPECT_EQ(expected_touch_action, effective_touch_action.has_value()
-                                       ? effective_touch_action.value()
-                                       : cc::kTouchActionAuto);
+  effective_touch_action_result = effective_touch_action.has_value()
+                                      ? effective_touch_action.value()
+                                      : cc::kTouchActionAuto;
+  while (expected_touch_action != effective_touch_action_result) {
+    GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
+                            effective_touch_action, whitelisted_touch_action);
+    effective_touch_action_result = effective_touch_action.has_value()
+                                        ? effective_touch_action.value()
+                                        : cc::kTouchActionAuto;
+  }
   if (whitelisted_touch_action.has_value())
     EXPECT_EQ(expected_touch_action, whitelisted_touch_action.value());
 }
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 1904759..b3b1de9 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -477,6 +477,11 @@
   if (base::FeatureList::IsEnabled(blink::features::kWritableFilesAPI))
     WebRuntimeFeatures::EnableFeatureFromString("WritableFiles", true);
 
+  if (base::FeatureList::IsEnabled(
+          blink::features::kForbidSyncXHRInPageDismissal)) {
+    WebRuntimeFeatures::EnableForbidSyncXHRInPageDismissal(true);
+  }
+
   // End individual features.
   // Do not add individual features below this line.
 
diff --git a/content/public/test/test_renderer_host.cc b/content/public/test/test_renderer_host.cc
index 6750a68..941d58b 100644
--- a/content/public/test/test_renderer_host.cc
+++ b/content/public/test/test_renderer_host.cc
@@ -187,6 +187,15 @@
     : thread_bundle_(
           std::make_unique<TestBrowserThreadBundle>(thread_bundle_options)) {}
 
+RenderViewHostTestHarness::RenderViewHostTestHarness(
+    base::test::ScopedTaskEnvironment::MainThreadType main_thread_type,
+    base::test::ScopedTaskEnvironment::ExecutionMode execution_control_mode,
+    int thread_bundle_options)
+    : thread_bundle_(
+          std::make_unique<TestBrowserThreadBundle>(main_thread_type,
+                                                    execution_control_mode,
+                                                    thread_bundle_options)) {}
+
 RenderViewHostTestHarness::~RenderViewHostTestHarness() {
 }
 
diff --git a/content/public/test/test_renderer_host.h b/content/public/test/test_renderer_host.h
index a95b908..9c7cbf7 100644
--- a/content/public/test/test_renderer_host.h
+++ b/content/public/test/test_renderer_host.h
@@ -192,6 +192,10 @@
   // Constructs a RenderViewHostTestHarness which uses |thread_bundle_options|
   // to initialize its TestBrowserThreadBundle.
   explicit RenderViewHostTestHarness(int thread_bundle_options = 0);
+  RenderViewHostTestHarness(
+      base::test::ScopedTaskEnvironment::MainThreadType main_thread_type,
+      base::test::ScopedTaskEnvironment::ExecutionMode execution_control_mode,
+      int thread_bundle_options = 0);
   ~RenderViewHostTestHarness() override;
 
   NavigationController& controller();
diff --git a/content/test/data/accessibility/html/video-controls-expected-auralinux.txt b/content/test/data/accessibility/html/video-controls-expected-auralinux.txt
new file mode 100644
index 0000000..9ed3131
--- /dev/null
+++ b/content/test/data/accessibility/html/video-controls-expected-auralinux.txt
@@ -0,0 +1,16 @@
+[document web]
+++[section]
+++++[video]
+++++++[section]
+++++++++[tool bar] name='video' description='video' horizontal
+++++++++[tool bar] name='video' description='video' horizontal
+++++++++++[tool bar] name='video' description='video' horizontal
+++++++++++++[tool bar] name='video' description='video' horizontal
+++++++++++++++[push button] name='play'
+++++++++++++++[text] name='0:00'
+++++++++++++++[section]
+++++++++++++++++[section]
+++++++++++++++++[push button] name='mute'
+++++++++++++++[push button] name='enter full screen'
+++++++++++++++[push button] name='show more media controls' description='more options'
+++++++++++++[slider] description='movie time scrubber' horizontal
diff --git a/content/test/data/accessibility/html/video-controls-expected-blink.txt b/content/test/data/accessibility/html/video-controls-expected-blink.txt
new file mode 100644
index 0000000..ae9e018
--- /dev/null
+++ b/content/test/data/accessibility/html/video-controls-expected-blink.txt
@@ -0,0 +1,18 @@
+rootWebArea
+++genericContainer
+++++video
+++++++genericContainer
+++++++++toolbar horizontal description='video' name='video' descriptionFrom=uninitialized
+++++++++toolbar horizontal description='video' name='video' descriptionFrom=uninitialized
+++++++++++toolbar horizontal description='video' name='video' descriptionFrom=uninitialized
+++++++++++++toolbar horizontal description='video' name='video' descriptionFrom=uninitialized
+++++++++++++++button name='play' restriction=disabled
+++++++++++++++staticText name='0:00'
+++++++++++++++++inlineTextBox name='0:00'
+++++++++++++++genericContainer
+++++++++++++++++genericContainer
+++++++++++++++++button name='mute' restriction=disabled
+++++++++++++++button name='enter full screen' restriction=disabled
+++++++++++++++button description='more options' name='show more media controls' descriptionFrom=uninitialized restriction=disabled
+++++++++++++slider horizontal description='movie time scrubber' value='0:00' descriptionFrom=uninitialized valueForRange=0.00 minValueForRange=0.00 maxValueForRange=0.00
+++++++++++++++sliderThumb
diff --git a/content/test/data/accessibility/html/video-controls.html b/content/test/data/accessibility/html/video-controls.html
new file mode 100644
index 0000000..c69705a
--- /dev/null
+++ b/content/test/data/accessibility/html/video-controls.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <video controls></video>
+  </body>
+</html>
diff --git a/content/test/fuzzer/BUILD.gn b/content/test/fuzzer/BUILD.gn
index cadbe39..b6ea88c3 100644
--- a/content/test/fuzzer/BUILD.gn
+++ b/content/test/fuzzer/BUILD.gn
@@ -68,7 +68,6 @@
   deps = [
     ":fuzzer_support",
   ]
-  additional_configs = [ "//testing/libfuzzer:no_clusterfuzz" ]
 }
 
 fuzzer_test("clear_site_data_fuzzer") {
@@ -90,7 +89,6 @@
     ":html_tree_proto",
     "//third_party/libprotobuf-mutator",
   ]
-  additional_configs = [ "//testing/libfuzzer:no_clusterfuzz" ]
 }
 
 proto_library("html_tree_proto") {
diff --git a/content/test/gpu/gpu_tests/pixel_expectations.py b/content/test/gpu/gpu_tests/pixel_expectations.py
index 54ac8fa5..09da641c 100644
--- a/content/test/gpu/gpu_tests/pixel_expectations.py
+++ b/content/test/gpu/gpu_tests/pixel_expectations.py
@@ -49,16 +49,11 @@
     # TODO(vmiura) check / generate reference images for Android devices
     self.Fail('Pixel_SolidColorBackground', ['mac', 'android'], bug=624256)
 
-    # TODO(michaelludwig): restore after Skia roll (see below)
-    #self.Fail('Pixel_CSSFilterEffects', ['mac', ('nvidia', 0xfe9)], bug=690277)
+    self.Fail('Pixel_CSSFilterEffects', ['mac', ('nvidia', 0xfe9)], bug=690277)
 
     # Became flaky on 10.13.6. When it flakes, it flakes 3 times, so
     # mark failing, unfortunately.
-    # TODO(michaelludwig): restore after Skia roll (see below)
-    #self.Fail('Pixel_CSSFilterEffects', ['highsierra', 'amd'], bug=872423)
-
-    # TODO(michaelludwig): remove after Skia roll
-    self.Fail('Pixel_CSSFilterEffects', bug=915735)
+    self.Fail('Pixel_CSSFilterEffects', ['highsierra', 'amd'], bug=872423)
 
     # TODO(kbr): flakily timing out on this configuration.
     self.Flaky('*', ['linux', 'intel', 'debug'], bug=648369)
diff --git a/crypto/BUILD.gn b/crypto/BUILD.gn
index b7e91c73..c27c7cf 100644
--- a/crypto/BUILD.gn
+++ b/crypto/BUILD.gn
@@ -69,9 +69,6 @@
     "symmetric_key.h",
   ]
 
-  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
-
   deps = [
     ":platform",
     "//base",
@@ -154,8 +151,6 @@
     ]
   }
 
-  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
-
   deps = [
     ":crypto",
     ":platform",
diff --git a/ios/chrome/browser/ui/settings/autofill_credit_card_edit_table_view_controller.mm b/ios/chrome/browser/ui/settings/autofill_credit_card_edit_table_view_controller.mm
index f19bc5a..53bb1bf 100644
--- a/ios/chrome/browser/ui/settings/autofill_credit_card_edit_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill_credit_card_edit_table_view_controller.mm
@@ -142,7 +142,7 @@
 
 - (void)loadModel {
   [super loadModel];
-  TableViewModel* model = self.tableViewModel;
+  TableViewModel<TableViewItem*>* model = self.tableViewModel;
 
   BOOL isEditing = self.tableView.editing;
 
diff --git a/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.h b/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.h
index 1fd4e3c0..dce72b7 100644
--- a/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.h
+++ b/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.h
@@ -7,18 +7,15 @@
 
 #import <UIKit/UIKit.h>
 
-#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
-#import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
-
-@class MDCButton;
+#import "ios/chrome/browser/ui/table_view/cells/table_view_item.h"
 
 // Item that configures a CopiedToChromeCell.
-@interface CopiedToChromeItem : CollectionViewItem
+@interface CopiedToChromeItem : TableViewItem
 @end
 
 // A cell indicating that the credit card has been copied to Chrome. Includes a
 // button to clear the copy.
-@interface CopiedToChromeCell : MDCCollectionViewCell
+@interface CopiedToChromeCell : UITableViewCell
 
 // Text label displaying the item's text.
 @property(nonatomic, readonly, strong) UILabel* textLabel;
diff --git a/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.mm b/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.mm
index 45ac0f2..1185dc40 100644
--- a/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.mm
+++ b/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.mm
@@ -5,28 +5,16 @@
 #import "ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.h"
 
 #include "components/strings/grit/components_strings.h"
-#include "ios/chrome/browser/ui/collection_view/cells/collection_view_cell_constants.h"
-#import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 #include "ios/chrome/grit/ios_chromium_strings.h"
-#import "ios/third_party/material_components_ios/src/components/Buttons/src/MaterialButtons.h"
-#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
-#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-namespace {
-// Padding used on the leading and trailing edges of the cell.
-const CGFloat kHorizontalPadding = 16;
-
-// Padding used on the top and bottom edges of the cell.
-const CGFloat kVerticalPadding = 16;
-}  // namespace
-
 @implementation CopiedToChromeItem
 
 - (instancetype)initWithType:(NSInteger)type {
@@ -42,10 +30,10 @@
 @implementation CopiedToChromeCell
 
 @synthesize textLabel = _textLabel;
-@synthesize button = _button;
 
-- (instancetype)initWithFrame:(CGRect)frame {
-  self = [super initWithFrame:frame];
+- (instancetype)initWithStyle:(UITableViewCellStyle)style
+              reuseIdentifier:(NSString*)reuseIdentifier {
+  self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
   if (self) {
     UIView* contentView = self.contentView;
 
@@ -53,8 +41,9 @@
     _textLabel.translatesAutoresizingMaskIntoConstraints = NO;
     _textLabel.text =
         l10n_util::GetNSString(IDS_IOS_AUTOFILL_DESCRIBE_LOCAL_COPY);
-    _textLabel.font = [UIFont systemFontOfSize:kUIKitMainFontSize];
-    _textLabel.textColor = UIColorFromRGB(kUIKitMainTextColor);
+    _textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
+    _textLabel.adjustsFontForContentSizeCategory = YES;
+    _textLabel.textColor = UIColor.blackColor;
     [_textLabel
         setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
                                         forAxis:
@@ -62,7 +51,7 @@
     [contentView addSubview:_textLabel];
 
     _button = [UIButton buttonWithType:UIButtonTypeCustom];
-    [_button setTitleColor:UIColorFromRGB(kUIKitFooterLinkColor)
+    [_button setTitleColor:UIColorFromRGB(kTableViewTextLabelColorBlue)
                   forState:UIControlStateNormal];
 
     _button.translatesAutoresizingMaskIntoConstraints = NO;
@@ -75,18 +64,20 @@
     [NSLayoutConstraint activateConstraints:@[
       [_textLabel.leadingAnchor
           constraintEqualToAnchor:contentView.leadingAnchor
-                         constant:kHorizontalPadding],
+                         constant:kTableViewHorizontalSpacing],
       [_textLabel.trailingAnchor
           constraintLessThanOrEqualToAnchor:_button.leadingAnchor
-                                   constant:-kHorizontalPadding],
+                                   constant:-kTableViewHorizontalSpacing],
       [_textLabel.centerYAnchor
           constraintEqualToAnchor:contentView.centerYAnchor],
-      [_button.trailingAnchor constraintEqualToAnchor:contentView.trailingAnchor
-                                             constant:-kHorizontalPadding],
+      [_button.trailingAnchor
+          constraintEqualToAnchor:contentView.trailingAnchor
+                         constant:-kTableViewHorizontalSpacing],
       [_button.firstBaselineAnchor
           constraintEqualToAnchor:_textLabel.firstBaselineAnchor],
     ]];
-    AddOptionalVerticalPadding(contentView, _textLabel, kVerticalPadding);
+    AddOptionalVerticalPadding(contentView, _textLabel,
+                               kTableViewLargeVerticalSpacing);
   }
   return self;
 }
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 1b889ae..866ecff 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
@@ -11,6 +11,7 @@
 #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/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"
 #import "ios/chrome/browser/ui/settings/cells/settings_detail_item.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_image_detail_text_item.h"
@@ -282,6 +283,11 @@
   [model addItem:autofillItemWithAllTexts
       toSectionWithIdentifier:SectionIdentifierAutofill];
 
+  CopiedToChromeItem* copiedToChrome =
+      [[CopiedToChromeItem alloc] initWithType:ItemTypeAutofillData];
+  [model addItem:copiedToChrome
+      toSectionWithIdentifier:SectionIdentifierAutofill];
+
   // SectionIdentifierAccount.
   TableViewSigninPromoItem* signinPromo =
       [[TableViewSigninPromoItem alloc] initWithType:ItemTypeAccount];
diff --git a/media/learning/common/learning_task.h b/media/learning/common/learning_task.h
index 9e1d630..8319d208 100644
--- a/media/learning/common/learning_task.h
+++ b/media/learning/common/learning_task.h
@@ -28,6 +28,7 @@
   // combination of orderings and types.
   enum class Model {
     kRandomForest,
+    kExtraTrees,
   };
 
   enum class Ordering {
diff --git a/media/learning/common/training_example.cc b/media/learning/common/training_example.cc
index 5055f25..a0b03eaa 100644
--- a/media/learning/common/training_example.cc
+++ b/media/learning/common/training_example.cc
@@ -22,10 +22,14 @@
 TrainingExample::~TrainingExample() = default;
 
 std::ostream& operator<<(std::ostream& out, const TrainingExample& example) {
-  for (const auto& feature : example.features)
-    out << " " << feature;
+  out << example.features << " => " << example.target_value;
 
-  out << " => " << example.target_value;
+  return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const FeatureVector& features) {
+  for (const auto& feature : features)
+    out << " " << feature;
 
   return out;
 }
diff --git a/media/learning/common/training_example.h b/media/learning/common/training_example.h
index 8252c8b..0252a58 100644
--- a/media/learning/common/training_example.h
+++ b/media/learning/common/training_example.h
@@ -107,6 +107,9 @@
 COMPONENT_EXPORT(LEARNING_COMMON)
 std::ostream& operator<<(std::ostream& out, const TrainingExample& example);
 
+COMPONENT_EXPORT(LEARNING_COMMON)
+std::ostream& operator<<(std::ostream& out, const FeatureVector& features);
+
 }  // namespace learning
 }  // namespace media
 
diff --git a/media/learning/impl/BUILD.gn b/media/learning/impl/BUILD.gn
index 777da95..0a2c150 100644
--- a/media/learning/impl/BUILD.gn
+++ b/media/learning/impl/BUILD.gn
@@ -7,6 +7,8 @@
   visibility = [ "//media/learning/impl:unit_tests" ]
 
   sources = [
+    "extra_trees_trainer.cc",
+    "extra_trees_trainer.h",
     "learning_session_impl.cc",
     "learning_session_impl.h",
     "learning_task_controller.h",
@@ -15,8 +17,6 @@
     "model.h",
     "one_hot.cc",
     "one_hot.h",
-    "random_forest.cc",
-    "random_forest.h",
     "random_forest_trainer.cc",
     "random_forest_trainer.h",
     "random_number_generator.cc",
@@ -26,6 +26,8 @@
     "target_distribution.cc",
     "target_distribution.h",
     "training_algorithm.h",
+    "voting_ensemble.cc",
+    "voting_ensemble.h",
   ]
 
   defines = [ "IS_LEARNING_IMPL_IMPL" ]
@@ -43,6 +45,7 @@
   testonly = true
 
   sources = [
+    "extra_trees_trainer_unittest.cc",
     "fisher_iris_dataset.cc",
     "fisher_iris_dataset.h",
     "learning_session_impl_unittest.cc",
diff --git a/media/learning/impl/extra_trees_trainer.cc b/media/learning/impl/extra_trees_trainer.cc
new file mode 100644
index 0000000..27e0346d
--- /dev/null
+++ b/media/learning/impl/extra_trees_trainer.cc
@@ -0,0 +1,49 @@
+// 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/learning/impl/extra_trees_trainer.h"
+
+#include <set>
+
+#include "base/logging.h"
+#include "media/learning/impl/one_hot.h"
+#include "media/learning/impl/random_tree_trainer.h"
+#include "media/learning/impl/voting_ensemble.h"
+
+namespace media {
+namespace learning {
+
+ExtraTreesTrainer::ExtraTreesTrainer() = default;
+
+ExtraTreesTrainer::~ExtraTreesTrainer() = default;
+
+std::unique_ptr<Model> ExtraTreesTrainer::Train(
+    const LearningTask& task,
+    const TrainingData& training_data) {
+  int n_trees = task.rf_number_of_trees;
+
+  RandomTreeTrainer tree_trainer(rng());
+  std::vector<std::unique_ptr<Model>> trees;
+  trees.reserve(n_trees);
+
+  // RandomTree requires one-hot vectors to properly choose split points the way
+  // that ExtraTrees require.
+  std::unique_ptr<OneHotConverter> converter =
+      std::make_unique<OneHotConverter>(task, training_data);
+  TrainingData converted_training_data = converter->Convert(training_data);
+
+  for (int i = 0; i < n_trees; i++) {
+    // Train the tree.
+    std::unique_ptr<Model> tree = tree_trainer.Train(
+        converter->converted_task(), converted_training_data);
+
+    trees.push_back(std::move(tree));
+  }
+
+  return std::make_unique<ConvertingModel>(
+      std::move(converter), std::make_unique<VotingEnsemble>(std::move(trees)));
+}
+
+}  // namespace learning
+}  // namespace media
diff --git a/media/learning/impl/extra_trees_trainer.h b/media/learning/impl/extra_trees_trainer.h
new file mode 100644
index 0000000..34a99187
--- /dev/null
+++ b/media/learning/impl/extra_trees_trainer.h
@@ -0,0 +1,44 @@
+// 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_LEARNING_IMPL_EXTRA_TREES_TRAINER_H_
+#define MEDIA_LEARNING_IMPL_EXTRA_TREES_TRAINER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/component_export.h"
+#include "base/macros.h"
+#include "media/learning/common/learning_task.h"
+#include "media/learning/impl/random_number_generator.h"
+#include "media/learning/impl/training_algorithm.h"
+
+namespace media {
+namespace learning {
+
+// Bagged forest of extremely randomized trees.
+//
+// These are an ensemble of trees.  Each tree is constructed from the full
+// training set.  The trees are constructed by selecting a random subset of
+// features at each node.  For each feature, a uniformly random split point is
+// chosen.  The feature with the best randomly chosen split point is used.
+//
+// These will automatically convert nominal values to one-hot vectors.
+class COMPONENT_EXPORT(LEARNING_IMPL) ExtraTreesTrainer
+    : public HasRandomNumberGenerator {
+ public:
+  ExtraTreesTrainer();
+  ~ExtraTreesTrainer();
+
+  std::unique_ptr<Model> Train(const LearningTask& task,
+                               const TrainingData& training_data);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ExtraTreesTrainer);
+};
+
+}  // namespace learning
+}  // namespace media
+
+#endif  // MEDIA_LEARNING_IMPL_EXTRA_TREES_TRAINER_H_
diff --git a/media/learning/impl/extra_trees_trainer_unittest.cc b/media/learning/impl/extra_trees_trainer_unittest.cc
new file mode 100644
index 0000000..c830c523
--- /dev/null
+++ b/media/learning/impl/extra_trees_trainer_unittest.cc
@@ -0,0 +1,103 @@
+// 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/learning/impl/extra_trees_trainer.h"
+
+#include "base/memory/ref_counted.h"
+#include "media/learning/impl/fisher_iris_dataset.h"
+#include "media/learning/impl/test_random_number_generator.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+namespace learning {
+
+class ExtraTreesTest : public testing::TestWithParam<LearningTask::Ordering> {
+ public:
+  ExtraTreesTest() : rng_(0), ordering_(GetParam()) {
+    trainer_.SetRandomNumberGeneratorForTesting(&rng_);
+  }
+
+  // Set up |task_| to have |n| features with the given ordering.
+  void SetupFeatures(size_t n) {
+    for (size_t i = 0; i < n; i++) {
+      LearningTask::ValueDescription desc;
+      desc.ordering = ordering_;
+      task_.feature_descriptions.push_back(desc);
+    }
+  }
+
+  TestRandomNumberGenerator rng_;
+  ExtraTreesTrainer trainer_;
+  LearningTask task_;
+  // Feature ordering.
+  LearningTask::Ordering ordering_;
+};
+
+TEST_P(ExtraTreesTest, EmptyTrainingDataWorks) {
+  TrainingData empty;
+  auto model = trainer_.Train(task_, empty);
+  EXPECT_NE(model.get(), nullptr);
+  EXPECT_EQ(model->PredictDistribution(FeatureVector()), TargetDistribution());
+}
+
+TEST_P(ExtraTreesTest, FisherIrisDataset) {
+  SetupFeatures(4);
+  FisherIrisDataset iris;
+  TrainingData training_data = iris.GetTrainingData();
+  auto model = trainer_.Train(task_, training_data);
+
+  // Verify predictions on the training set, just for sanity.
+  size_t num_correct = 0;
+  for (const TrainingExample& example : training_data) {
+    TargetDistribution distribution =
+        model->PredictDistribution(example.features);
+    TargetValue predicted_value;
+    if (distribution.FindSingularMax(&predicted_value) &&
+        predicted_value == example.target_value) {
+      num_correct += example.weight;
+    }
+  }
+
+  // Expect very high accuracy.  We should get ~100%.
+  // We get about 96% for one-hot features, and 100% for numeric.  Since the
+  // data really is numeric, that seems reasonable.
+  double train_accuracy = ((double)num_correct) / training_data.total_weight();
+  EXPECT_GT(train_accuracy, 0.95);
+}
+
+TEST_P(ExtraTreesTest, WeightedTrainingSetIsSupported) {
+  // Create a training set with unseparable data, but give one of them a large
+  // weight.  See if that one wins.
+  SetupFeatures(1);
+  TrainingExample example_1({FeatureValue(123)}, TargetValue(1));
+  TrainingExample example_2({FeatureValue(123)}, TargetValue(2));
+  const size_t weight = 100;
+  TrainingData training_data;
+  example_1.weight = weight;
+  training_data.push_back(example_1);
+  // Push many |example_2|'s, which will win without the weights.
+  training_data.push_back(example_2);
+  training_data.push_back(example_2);
+  training_data.push_back(example_2);
+  training_data.push_back(example_2);
+
+  // Create a weighed set with |weight| for each example's weight.
+  EXPECT_FALSE(training_data.is_unweighted());
+  auto model = trainer_.Train(task_, training_data);
+
+  // The singular max should be example_1.
+  TargetDistribution distribution =
+      model->PredictDistribution(example_1.features);
+  TargetValue predicted_value;
+  EXPECT_TRUE(distribution.FindSingularMax(&predicted_value));
+  EXPECT_EQ(predicted_value, example_1.target_value);
+}
+
+INSTANTIATE_TEST_CASE_P(ExtraTreesTest,
+                        ExtraTreesTest,
+                        testing::ValuesIn({LearningTask::Ordering::kUnordered,
+                                           LearningTask::Ordering::kNumeric}));
+
+}  // namespace learning
+}  // namespace media
diff --git a/media/learning/impl/learning_task_controller_impl.cc b/media/learning/impl/learning_task_controller_impl.cc
index 50d38c0..0c8f9c3 100644
--- a/media/learning/impl/learning_task_controller_impl.cc
+++ b/media/learning/impl/learning_task_controller_impl.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/bind.h"
+#include "media/learning/impl/extra_trees_trainer.h"
 #include "media/learning/impl/random_tree_trainer.h"
 
 namespace media {
@@ -15,6 +16,15 @@
 LearningTaskControllerImpl::LearningTaskControllerImpl(const LearningTask& task)
     : task_(task), training_data_(std::make_unique<TrainingData>()) {
   switch (task_.model) {
+    case LearningTask::Model::kExtraTrees:
+      training_cb_ = base::BindRepeating(
+          [](const LearningTask& task, TrainingData training_data,
+             TrainedModelCB model_cb) {
+            ExtraTreesTrainer trainer;
+            std::move(model_cb).Run(trainer.Train(task, training_data));
+          },
+          task_);
+      break;
     case LearningTask::Model::kRandomForest:
       // TODO(liberato): forest!
       training_cb_ = RandomTreeTrainer::GetTrainingAlgorithmCB(task_);
diff --git a/media/learning/impl/random_forest.cc b/media/learning/impl/random_forest.cc
deleted file mode 100644
index d533536..0000000
--- a/media/learning/impl/random_forest.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/learning/impl/random_forest.h"
-
-namespace media {
-namespace learning {
-
-RandomForest::RandomForest(std::vector<std::unique_ptr<Model>> trees)
-    : trees_(std::move(trees)) {}
-
-RandomForest::~RandomForest() = default;
-
-TargetDistribution RandomForest::PredictDistribution(
-    const FeatureVector& instance) {
-  TargetDistribution forest_distribution;
-
-  for (auto iter = trees_.begin(); iter != trees_.end(); iter++)
-    forest_distribution += (*iter)->PredictDistribution(instance);
-
-  return forest_distribution;
-}
-
-}  // namespace learning
-}  // namespace media
diff --git a/media/learning/impl/random_forest.h b/media/learning/impl/random_forest.h
deleted file mode 100644
index 84b80587..0000000
--- a/media/learning/impl/random_forest.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_LEARNING_IMPL_RANDOM_FOREST_H_
-#define MEDIA_LEARNING_IMPL_RANDOM_FOREST_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/component_export.h"
-#include "base/macros.h"
-#include "media/learning/impl/model.h"
-
-namespace media {
-namespace learning {
-
-// Bagged forest of randomized trees.
-// TODO(liberato): consider a generic Bagging class, since this doesn't really
-// depend on RandomTree at all.
-class COMPONENT_EXPORT(LEARNING_IMPL) RandomForest : public Model {
- public:
-  RandomForest(std::vector<std::unique_ptr<Model>> trees);
-  ~RandomForest() override;
-
-  // Model
-  TargetDistribution PredictDistribution(
-      const FeatureVector& instance) override;
-
- private:
-  std::vector<std::unique_ptr<Model>> trees_;
-
-  DISALLOW_COPY_AND_ASSIGN(RandomForest);
-};
-
-}  // namespace learning
-}  // namespace media
-
-#endif  // MEDIA_LEARNING_IMPL_RANDOM_FOREST_H_
diff --git a/media/learning/impl/random_forest_trainer.cc b/media/learning/impl/random_forest_trainer.cc
index 6593f458..6895de7 100644
--- a/media/learning/impl/random_forest_trainer.cc
+++ b/media/learning/impl/random_forest_trainer.cc
@@ -7,8 +7,8 @@
 #include <set>
 
 #include "base/logging.h"
-#include "media/learning/impl/random_forest.h"
 #include "media/learning/impl/random_tree_trainer.h"
+#include "media/learning/impl/voting_ensemble.h"
 
 namespace media {
 namespace learning {
@@ -77,8 +77,8 @@
     trees.push_back(std::move(tree));
   }
 
-  std::unique_ptr<RandomForest> forest =
-      std::make_unique<RandomForest>(std::move(trees));
+  std::unique_ptr<VotingEnsemble> forest =
+      std::make_unique<VotingEnsemble>(std::move(trees));
 
   // Compute OOB accuracy.
   int num_correct = 0;
diff --git a/media/learning/impl/random_tree_trainer.h b/media/learning/impl/random_tree_trainer.h
index 534fec7..215d437 100644
--- a/media/learning/impl/random_tree_trainer.h
+++ b/media/learning/impl/random_tree_trainer.h
@@ -42,6 +42,22 @@
 // target values that ended up in each group.  The index with the best score is
 // chosen for the split.
 //
+// For nominal features, we split the feature into all of its nominal values.
+// This is somewhat nonstandard; one would normally convert to one-hot numeric
+// features first.  See OneHotConverter if you'd like to do this.
+//
+// For numeric features, we choose a split point uniformly at random between its
+// min and max values in the training data.  We do this because it's suitable
+// for extra trees.  RandomForest trees want to select the best split point for
+// each feature, rather than uniformly.  Either way, of course, we choose the
+// best split among the (feature, split point) pairs we're considering.
+//
+// Also note that for one-hot features, these are the same thing.  So, this
+// implementation is suitable for extra trees with numeric (possibly one hot)
+// features, or RF with one-hot nominal features.  Note that non-one-hot nominal
+// features probably work fine with RF too.  Numeric, non-binary features don't
+// work with RF, unless one changes the split point selection.
+//
 // The training algorithm then recurses to build child nodes.  One child node is
 // created for each observed value of the |i|-th feature in the training set.
 // The child node is trained using the subset of the training set that shares
diff --git a/media/learning/impl/voting_ensemble.cc b/media/learning/impl/voting_ensemble.cc
new file mode 100644
index 0000000..667e7399
--- /dev/null
+++ b/media/learning/impl/voting_ensemble.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/learning/impl/voting_ensemble.h"
+
+namespace media {
+namespace learning {
+
+VotingEnsemble::VotingEnsemble(std::vector<std::unique_ptr<Model>> models)
+    : models_(std::move(models)) {}
+
+VotingEnsemble::~VotingEnsemble() = default;
+
+TargetDistribution VotingEnsemble::PredictDistribution(
+    const FeatureVector& instance) {
+  TargetDistribution distribution;
+
+  for (auto iter = models_.begin(); iter != models_.end(); iter++)
+    distribution += (*iter)->PredictDistribution(instance);
+
+  return distribution;
+}
+
+}  // namespace learning
+}  // namespace media
diff --git a/media/learning/impl/voting_ensemble.h b/media/learning/impl/voting_ensemble.h
new file mode 100644
index 0000000..2b4bf11
--- /dev/null
+++ b/media/learning/impl/voting_ensemble.h
@@ -0,0 +1,38 @@
+// 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_LEARNING_IMPL_VOTING_ENSEMBLE_H_
+#define MEDIA_LEARNING_IMPL_VOTING_ENSEMBLE_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/component_export.h"
+#include "base/macros.h"
+#include "media/learning/impl/model.h"
+
+namespace media {
+namespace learning {
+
+// Ensemble classifier.  Takes multiple models and returns an aggregate of the
+// individual predictions.
+class COMPONENT_EXPORT(LEARNING_IMPL) VotingEnsemble : public Model {
+ public:
+  VotingEnsemble(std::vector<std::unique_ptr<Model>> models);
+  ~VotingEnsemble() override;
+
+  // Model
+  TargetDistribution PredictDistribution(
+      const FeatureVector& instance) override;
+
+ private:
+  std::vector<std::unique_ptr<Model>> models_;
+
+  DISALLOW_COPY_AND_ASSIGN(VotingEnsemble);
+};
+
+}  // namespace learning
+}  // namespace media
+
+#endif  // MEDIA_LEARNING_IMPL_VOTING_ENSEMBLE_H_
diff --git a/services/tracing/coordinator.cc b/services/tracing/coordinator.cc
index ba0271e..59a0bac 100644
--- a/services/tracing/coordinator.cc
+++ b/services/tracing/coordinator.cc
@@ -87,10 +87,6 @@
                                   agent_entry, std::move(ptr)));
   }
 
-  // Called from |backend_task_runner_| to close the recorder proxy on the
-  // correct task runner.
-  void CloseRecorder(mojom::RecorderPtr recorder) {}
-
   // Called from |main_task_runner_| either after flushing is complete or at
   // shutdown. We either will not write to the stream afterwards or do not care
   // what happens to what we try to write.
@@ -435,8 +431,7 @@
     // Recorders are created and closed on |backend_task_runner_|.
     backend_task_runner_->PostTask(
         FROM_HERE,
-        base::BindOnce(&Coordinator::TraceStreamer::CloseRecorder,
-                       trace_streamer_->AsWeakPtr(), std::move(recorder)));
+        base::BindOnce([](mojom::RecorderPtr ptr) {}, std::move(recorder)));
   }
 }
 
diff --git a/testing/android/proguard_for_test.flags b/testing/android/proguard_for_test.flags
index a013550..cb65c1cd 100644
--- a/testing/android/proguard_for_test.flags
+++ b/testing/android/proguard_for_test.flags
@@ -21,6 +21,16 @@
 # all classes from the test apk.
 -keep class **.test.** { *; }
 
+# These 2 rules together keep all interfaces that are mocked by Mockito, since
+# Mockito generates these mocks at runtime, and the interfaces could get methods
+# stripped out.
+-if class * {
+  @org.mockito.Mock * *;
+}
+-keep interface <2> {
+  <methods>;
+}
+
 # Keep all enum members since they might be reflectively called by JUnit4 runner
 -keepclassmembers enum * { *; }
 
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 424d0099..b9eaeed 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -431,9 +431,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_ash_unittests",
+        "name": "non_single_process_mash_ash_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -510,10 +510,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--override-use-software-gl-for-tests"
         ],
-        "name": "single_process_mash_browser_tests",
+        "name": "non_single_process_mash_browser_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "shards": 20
@@ -621,6 +621,18 @@
       },
       {
         "args": [
+          "--disable-features=SingleProcessMash",
+          "--override-use-software-gl-for-tests"
+        ],
+        "name": "non_single_process_mash_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 5
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
           "--enable-features=TracingPerfettoBackend",
           "--gtest_filter=TracingControllerTest.*"
         ],
@@ -632,18 +644,6 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
-          "--override-use-software-gl-for-tests"
-        ],
-        "name": "single_process_mash_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "shards": 5
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=VizDisplayCompositor"
         ],
         "name": "viz_content_browsertests",
@@ -661,9 +661,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_content_unittests",
+        "name": "non_single_process_mash_content_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -717,9 +717,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_exo_unittests",
+        "name": "non_single_process_mash_exo_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -788,9 +788,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_interactive_ui_tests",
+        "name": "non_single_process_mash_interactive_ui_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "shards": 3
@@ -1042,9 +1042,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_unit_tests",
+        "name": "non_single_process_mash_unit_tests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1137,9 +1137,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_ash_unittests",
+        "name": "non_single_process_mash_ash_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1216,10 +1216,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--override-use-software-gl-for-tests"
         ],
-        "name": "single_process_mash_browser_tests",
+        "name": "non_single_process_mash_browser_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "shards": 10
@@ -1327,6 +1327,18 @@
       },
       {
         "args": [
+          "--disable-features=SingleProcessMash",
+          "--override-use-software-gl-for-tests"
+        ],
+        "name": "non_single_process_mash_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 5
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
           "--enable-features=TracingPerfettoBackend",
           "--gtest_filter=TracingControllerTest.*"
         ],
@@ -1338,18 +1350,6 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
-          "--override-use-software-gl-for-tests"
-        ],
-        "name": "single_process_mash_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "shards": 5
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=VizDisplayCompositor"
         ],
         "name": "viz_content_browsertests",
@@ -1367,9 +1367,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_content_unittests",
+        "name": "non_single_process_mash_content_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1423,9 +1423,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_exo_unittests",
+        "name": "non_single_process_mash_exo_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1494,9 +1494,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_interactive_ui_tests",
+        "name": "non_single_process_mash_interactive_ui_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "shards": 3
@@ -1747,9 +1747,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_unit_tests",
+        "name": "non_single_process_mash_unit_tests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index d6324176..8f4a868 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -4169,10 +4169,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_ash_unittests",
+        "name": "non_single_process_mash_ash_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -4277,11 +4277,11 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--override-use-software-gl-for-tests",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_browser_tests",
+        "name": "non_single_process_mash_browser_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "shards": 30
@@ -4426,6 +4426,19 @@
       },
       {
         "args": [
+          "--disable-features=SingleProcessMash",
+          "--override-use-software-gl-for-tests",
+          "--test-launcher-print-test-stdio=always"
+        ],
+        "name": "non_single_process_mash_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 5
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
           "--enable-features=TracingPerfettoBackend",
           "--gtest_filter=TracingControllerTest.*",
           "--test-launcher-print-test-stdio=always"
@@ -4438,19 +4451,6 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
-          "--override-use-software-gl-for-tests",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "name": "single_process_mash_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "shards": 5
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=VizDisplayCompositor",
           "--test-launcher-print-test-stdio=always"
         ],
@@ -4472,10 +4472,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_content_unittests",
+        "name": "non_single_process_mash_content_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -4548,10 +4548,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_exo_unittests",
+        "name": "non_single_process_mash_exo_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -4641,10 +4641,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_interactive_ui_tests",
+        "name": "non_single_process_mash_interactive_ui_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "shards": 3
@@ -5010,10 +5010,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_unit_tests",
+        "name": "non_single_process_mash_unit_tests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -5181,10 +5181,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_ash_unittests",
+        "name": "non_single_process_mash_ash_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -5355,11 +5355,11 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--override-use-software-gl-for-tests",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_browser_tests",
+        "name": "non_single_process_mash_browser_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -5592,6 +5592,25 @@
       },
       {
         "args": [
+          "--disable-features=SingleProcessMash",
+          "--override-use-software-gl-for-tests",
+          "--test-launcher-print-test-stdio=always"
+        ],
+        "name": "non_single_process_mash_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Ubuntu-14.04"
+            }
+          ],
+          "shards": 5
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
           "--enable-features=TracingPerfettoBackend",
           "--gtest_filter=TracingControllerTest.*",
           "--test-launcher-print-test-stdio=always"
@@ -5610,25 +5629,6 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
-          "--override-use-software-gl-for-tests",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "name": "single_process_mash_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
-            }
-          ],
-          "shards": 5
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=VizDisplayCompositor",
           "--test-launcher-print-test-stdio=always"
         ],
@@ -5663,10 +5663,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_content_unittests",
+        "name": "non_single_process_mash_content_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -5787,10 +5787,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_exo_unittests",
+        "name": "non_single_process_mash_exo_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -5940,10 +5940,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_interactive_ui_tests",
+        "name": "non_single_process_mash_interactive_ui_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -6534,10 +6534,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_unit_tests",
+        "name": "non_single_process_mash_unit_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
diff --git a/testing/buildbot/filters/chromeos.mash.fyi.interactive_ui_tests.filter b/testing/buildbot/filters/chromeos.mash.fyi.interactive_ui_tests.filter
index 555a0a8c99..c987f6e 100644
--- a/testing/buildbot/filters/chromeos.mash.fyi.interactive_ui_tests.filter
+++ b/testing/buildbot/filters/chromeos.mash.fyi.interactive_ui_tests.filter
@@ -44,25 +44,9 @@
 -TestAsNormalAndGuestUser/SpokenFeedbackTest.OverviewMode/0
 -TestAsNormalAndGuestUser/SpokenFeedbackTest.OverviewMode/1
 
-# TabDragging: crbug.com/890071
--TabDragging/DetachToBrowserTabDragControllerTest.DeleteBeforeStartedDragging/0
--TabDragging/DetachToBrowserTabDragControllerTest.DeleteBeforeStartedDragging/1
--TabDragging/DetachToBrowserTabDragControllerTest.DetachToOwnWindowFromMaximizedWindow/0
--TabDragging/DetachToBrowserTabDragControllerTest.DetachToOwnWindowFromMaximizedWindow/1
--TabDragging/DetachToBrowserTabDragControllerTest.DetachToOwnWindowWhileInImmersiveFullscreenMode/0
--TabDragging/DetachToBrowserTabDragControllerTest.DetachToOwnWindowWhileInImmersiveFullscreenMode/1
--TabDragging/DetachToBrowserTabDragControllerTest.DoNotObserveDraggedWidgetAfterDragEnds/1
--TabDragging/DetachToBrowserTabDragControllerTest.DragAllToSeparateWindow/0
--TabDragging/DetachToBrowserTabDragControllerTest.DragAllToSeparateWindow/1
--TabDragging/DetachToBrowserTabDragControllerTest.DragAllToSeparateWindowAndCancel/0
--TabDragging/DetachToBrowserTabDragControllerTest.DragAllToSeparateWindowAndCancel/1
--TabDragging/DetachToBrowserTabDragControllerTest.DragToOverviewNewWindowItem/1
--TabDragging/DetachToBrowserTabDragControllerTest.DragToOverviewWindow/1
--TabDragging/DetachToBrowserTabDragControllerTest.DragToSeparateWindow/0
--TabDragging/DetachToBrowserTabDragControllerTest.DragToSeparateWindow/1
--TabDragging/DetachToBrowserTabDragControllerTest.DragWithMaskedWindows/0
--TabDragging/DetachToBrowserTabDragControllerTest.DragWithMaskedWindows/1
--TabDragging/DetachToBrowserTabDragControllerTestTouch.PressSecondFingerWhileDetached/0
+# TabDragging: https://crbug.com/890071
+-TabDragging/DetachToBrowserTabDragControllerTest.*
+-TabDragging/DetachToBrowserTabDragControllerTestTouch.*
 
 # This test is flaky. https://crbug.com/897879
 -ExtensionApiTest.DisplayModeWindowIsInFullscreen
diff --git a/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter b/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter
index f7fdf85..15bd28a 100644
--- a/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter
+++ b/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter
@@ -64,6 +64,7 @@
 -DeclarativeNetRequestBrowserTest.*
 -DeclarativeNetRequestHostPermissionsBrowserTest.*
 -DevToolsExtensionTest.*
+-DevToolsManagerDelegateTest.*
 -DevToolsSanityTest.*
 -DeviceManagementServiceIntegrationTestInstance/DeviceManagementServiceIntegrationTest.*
 -DiceBrowserTest.*
@@ -174,6 +175,7 @@
 -PrefsFunctionalTest.*
 -PrefsTabHelperBrowserTest.*
 -PrerenderBrowserTest.*
+-PresentationReceiverWindowViewBrowserTest.*
 -PrintBrowserTest.*
 -PrintPreviewDialogControllerBrowserTest.*
 -PrintPreviewSettingsSectionsTest.*
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 2cf7daea..b5572b16 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -957,6 +957,29 @@
       },
     },
   },
+  'non_single_process_mash_browser_tests': {
+    'modifications': {
+      # chromium.chromiumos
+      'linux-chromeos-dbg': {
+        'swarming': {
+          'shards': 20,
+        },
+      },
+      # chromium.memory
+      'Linux Chromium OS ASan LSan Tests (1)': {
+        # These are very slow on the ASAN trybot for some reason.
+        # crbug.com/794372
+        'swarming': {
+          'shards': 30,
+        },
+      },
+      'Linux ChromiumOS MSan Tests': {
+        'swarming': {
+          'shards': 20,
+        },
+      },
+    },
+  },
   'not_site_per_process_webkit_layout_tests': {
     'remove_from': [
       # chromium.linux
@@ -1024,29 +1047,6 @@
       'Linux MSan Tests',  # https://crbug.com/831676
     ],
   },
-  'single_process_mash_browser_tests': {
-    'modifications': {
-      # chromium.chromiumos
-      'linux-chromeos-dbg': {
-        'swarming': {
-          'shards': 20,
-        },
-      },
-      # chromium.memory
-      'Linux Chromium OS ASan LSan Tests (1)': {
-        # These are very slow on the ASAN trybot for some reason.
-        # crbug.com/794372
-        'swarming': {
-          'shards': 30,
-        },
-      },
-      'Linux ChromiumOS MSan Tests': {
-        'swarming': {
-          'shards': 20,
-        },
-      },
-    },
-  },
   'sizes': {
     'remove_from': [
       'win32-dbg',
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 272db5dd..1e492a4 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -3508,57 +3508,57 @@
       },
       'ozone_unittests': {},
       'ozone_x11_unittests': {},
-      'single_process_mash_ash_unittests': {
+      'non_single_process_mash_ash_unittests': {
         'test': 'ash_unittests',
         'args': [
-          '--enable-features=SingleProcessMash',
+          '--disable-features=SingleProcessMash',
         ],
       },
-      'single_process_mash_browser_tests': {
+      'non_single_process_mash_browser_tests': {
         'test': 'browser_tests',
         'args': [
-          '--enable-features=SingleProcessMash',
+          '--disable-features=SingleProcessMash',
           '--override-use-software-gl-for-tests'
         ],
         'swarming': {
           'shards': 10,
         },
       },
-      'single_process_mash_content_unittests': {
+      'non_single_process_mash_content_unittests': {
         'test': 'content_unittests',
         'args': [
-          '--enable-features=SingleProcessMash',
+          '--disable-features=SingleProcessMash',
         ],
       },
-      'single_process_mash_content_browsertests': {
+      'non_single_process_mash_content_browsertests': {
         'test': 'content_browsertests',
         'args': [
-          '--enable-features=SingleProcessMash',
+          '--disable-features=SingleProcessMash',
           '--override-use-software-gl-for-tests'
         ],
         'swarming': {
           'shards': 5,
         },
       },
-      'single_process_mash_exo_unittests': {
+      'non_single_process_mash_exo_unittests': {
         'test': 'exo_unittests',
         'args': [
-          '--enable-features=SingleProcessMash',
+          '--disable-features=SingleProcessMash',
         ],
       },
-      'single_process_mash_interactive_ui_tests': {
+      'non_single_process_mash_interactive_ui_tests': {
         'test': 'interactive_ui_tests',
         'args': [
-          '--enable-features=SingleProcessMash'
+          '--disable-features=SingleProcessMash'
         ],
         'swarming': {
           'shards': 3,
         },
       },
-      'single_process_mash_unit_tests': {
+      'non_single_process_mash_unit_tests': {
         'test': 'unit_tests',
         'args': [
-          '--enable-features=SingleProcessMash'
+          '--disable-features=SingleProcessMash'
         ],
       },
       'ui_chromeos_unittests': {},
diff --git a/testing/libfuzzer/fuzzers/libxml_xml_read_memory_fuzzer.cc b/testing/libfuzzer/fuzzers/libxml_xml_read_memory_fuzzer.cc
index 3c2589c..b90fbb5 100644
--- a/testing/libfuzzer/fuzzers/libxml_xml_read_memory_fuzzer.cc
+++ b/testing/libfuzzer/fuzzers/libxml_xml_read_memory_fuzzer.cc
@@ -33,7 +33,8 @@
   const int options[] = {0, random_option};
 
   for (const auto option_value : options) {
-    if (auto doc = xmlReadMemory(data_string.c_str(), data_string.length(),
+    // Intentionally pass raw data as the API does not require trailing \0.
+    if (auto doc = xmlReadMemory(reinterpret_cast<const char*>(data), size,
                                  "noname.xml", NULL, option_value)) {
       auto buffer = xmlBufferCreate();
       assert(buffer);
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 689ed65..683cd19f 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1990,6 +1990,25 @@
             ]
         }
     ],
+    "ForbidSyncXHRInPageDismissal": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ForbidSyncXHRInPageDismissal"
+                    ]
+                }
+            ]
+        }
+    ],
     "FrameTypePriorityExperiment": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/media/controls/accessibility-volume-slider.html b/third_party/WebKit/LayoutTests/media/controls/accessibility-volume-slider.html
new file mode 100644
index 0000000..f183a573
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/media/controls/accessibility-volume-slider.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>Media Controls: volume slider accessibility tests</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<video controls></video>
+<script>
+async_test(t => {
+  // Ensure that we will show the volume slider.
+  setPreferHiddenVolumeControlsForTest(t, false);
+
+  assert_true(internals.runtimeFlags.accessibilityObjectModelEnabled);
+
+  var video = document.querySelector('video');
+  video.src = '../content/test.ogv';
+
+  video.onloadedmetadata = t.step(function() {
+
+    var volume_slider = volumeSliderElement(video);
+    assert_not_equals(volume_slider, null);
+
+    assert_equals(volume_slider.getAttribute('aria-label'), 'volume');
+    assert_equals(volume_slider.getAttribute('aria-valuemin'), '0');
+    assert_equals(volume_slider.getAttribute('aria-valuemax'), '100');
+    assert_equals(volume_slider.getAttribute('aria-valuenow'), '100');
+
+    // Trigger events that will update the timeline internal state.
+    video.volume = 0.54;
+    video.onvolumechange = t.step_func_done(function(event) {
+      assert_equals(volume_slider.getAttribute('aria-valuenow'), '54');
+    });
+  });
+});
+</script>
diff --git a/third_party/accessibility_test_framework/proguard.flags b/third_party/accessibility_test_framework/proguard.flags
index c6b5e18b8..f4b89f75 100644
--- a/third_party/accessibility_test_framework/proguard.flags
+++ b/third_party/accessibility_test_framework/proguard.flags
@@ -1,2 +1 @@
 -dontwarn com.google.protobuf.**
--dontwarn com.google.android.apps.common.testing.accessibility.framework.proto.FrameworkProtos*
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 48096f1d..61549b7 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -137,6 +137,10 @@
 const base::Feature kWritableFilesAPI{"WritableFilesAPI",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Allows for synchronous XHR requests during page dismissal
+const base::Feature kForbidSyncXHRInPageDismissal{
+    "ForbidSyncXHRInPageDismissal", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const char kAutofillPreviewStyleExperimentBgColorParameterName[] = "bg_color";
 
 const char kAutofillPreviewStyleExperimentColorParameterName[] = "color";
diff --git a/third_party/blink/common/font_unique_name_lookup/OWNERS b/third_party/blink/common/font_unique_name_lookup/OWNERS
new file mode 100644
index 0000000..5819672
--- /dev/null
+++ b/third_party/blink/common/font_unique_name_lookup/OWNERS
@@ -0,0 +1,3 @@
+drott@chromium.org
+
+# COMPONENT: Blink>Fonts
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 05f70bf..eba61f2 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -40,6 +40,7 @@
 BLINK_COMMON_EXPORT extern const base::Feature kStopNonTimersInBackground;
 BLINK_COMMON_EXPORT extern const base::Feature kWasmCodeCache;
 BLINK_COMMON_EXPORT extern const base::Feature kWritableFilesAPI;
+BLINK_COMMON_EXPORT extern const base::Feature kForbidSyncXHRInPageDismissal;
 
 BLINK_COMMON_EXPORT extern const char
     kAutofillPreviewStyleExperimentBgColorParameterName[];
diff --git a/third_party/blink/public/common/font_unique_name_lookup/OWNERS b/third_party/blink/public/common/font_unique_name_lookup/OWNERS
new file mode 100644
index 0000000..9251fa1
--- /dev/null
+++ b/third_party/blink/public/common/font_unique_name_lookup/OWNERS
@@ -0,0 +1,6 @@
+drott@chromium.org
+
+# COMPONENT: Blink>Fonts
+
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/public/platform/web_feature.mojom b/third_party/blink/public/platform/web_feature.mojom
index 82de52a3..eaa37cb 100644
--- a/third_party/blink/public/platform/web_feature.mojom
+++ b/third_party/blink/public/platform/web_feature.mojom
@@ -2159,6 +2159,7 @@
   kV8HTMLMediaElement_CanPlayType_Method = 2718,
   kHistoryLength = 2719,
   kFeaturePolicyReportOnlyHeader = 2720,
+  kV8PaymentRequest_HasEnrolledInstrument_Method = 2721,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index 40ab6bc..d6dc342 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -220,6 +220,7 @@
   BLINK_PLATFORM_EXPORT static void EnableTranslateService(bool);
   BLINK_PLATFORM_EXPORT static void EnableMergeBlockingNonBlockingPools(bool);
   BLINK_PLATFORM_EXPORT static void EnableGetDisplayMedia(bool);
+  BLINK_PLATFORM_EXPORT static void EnableForbidSyncXHRInPageDismissal(bool);
 
  private:
   WebRuntimeFeatures();
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
index a5b05239..796219b 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
@@ -155,11 +155,6 @@
       }
       return script;
     }
-    // TODO(v8:8252): Remove the default case once deprecated options are
-    // removed from v8::ScriptCompiler::CompileOptions.
-    default:
-      NOTREACHED();
-      break;
   }
 
   // All switch branches should return and we should never get here.
diff --git a/third_party/blink/renderer/core/OWNERS b/third_party/blink/renderer/core/OWNERS
index 18b45eb..5109981f5 100644
--- a/third_party/blink/renderer/core/OWNERS
+++ b/third_party/blink/renderer/core/OWNERS
@@ -55,7 +55,7 @@
 pkasting@chromium.org
 rego@igalia.com
 rbyers@chromium.org
-rob.buis@samsung.com
+rbuis@igalia.com
 robhogan@gmail.com
 schenney@chromium.org
 senorblanco@chromium.org
diff --git a/third_party/blink/renderer/core/display_lock/BUILD.gn b/third_party/blink/renderer/core/display_lock/BUILD.gn
index 73e6b23..c0a8bc2 100644
--- a/third_party/blink/renderer/core/display_lock/BUILD.gn
+++ b/third_party/blink/renderer/core/display_lock/BUILD.gn
@@ -14,6 +14,8 @@
     "strict_yielding_display_lock_budget.h",
     "unyielding_display_lock_budget.cc",
     "unyielding_display_lock_budget.h",
+    "yielding_display_lock_budget.cc",
+    "yielding_display_lock_budget.h",
   ]
 
   public_deps = [
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_budget_test.cc b/third_party/blink/renderer/core/display_lock/display_lock_budget_test.cc
index 875829fa..d32b99b 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_budget_test.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_budget_test.cc
@@ -8,13 +8,16 @@
 #include "third_party/blink/renderer/core/display_lock/display_lock_context.h"
 #include "third_party/blink/renderer/core/display_lock/strict_yielding_display_lock_budget.h"
 #include "third_party/blink/renderer/core/display_lock/unyielding_display_lock_budget.h"
+#include "third_party/blink/renderer/core/display_lock/yielding_display_lock_budget.h"
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/testing/wtf/scoped_mock_clock.h"
 
 namespace blink {
 
 class DisplayLockBudgetTest : public RenderingTest {
+ public:
   void SetUp() override {
     RenderingTest::SetUp();
     features_backup_.emplace();
@@ -28,6 +31,10 @@
     }
   }
 
+  double GetBudgetMs(const YieldingDisplayLockBudget& budget) const {
+    return budget.GetCurrentBudgetMs();
+  }
+
  private:
   base::Optional<RuntimeEnabledFeatures::Backup> features_backup_;
 };
@@ -257,4 +264,101 @@
   // completing whenever necessary.
   EXPECT_FALSE(budget.NeedsLifecycleUpdates());
 }
+
+TEST_F(DisplayLockBudgetTest, YieldingBudget) {
+  // Note that we're not testing the display lock here, just the budget so we
+  // can do minimal work to ensure we have a context, ignoring containment and
+  // other requirements.
+  SetBodyInnerHTML(R"HTML(
+    <div id="container"></div>
+  )HTML");
+
+  auto* element = GetDocument().getElementById("container");
+  {
+    auto* script_state = ToScriptStateForMainWorld(GetDocument().GetFrame());
+    ScriptState::Scope scope(script_state);
+    element->getDisplayLockForBindings()->acquire(script_state, nullptr);
+  }
+
+  ASSERT_TRUE(element->GetDisplayLockContext());
+  YieldingDisplayLockBudget budget(element->GetDisplayLockContext());
+
+  WTF::ScopedMockClock clock;
+
+  // Since the lifecycle is clean, we don't actually need any updates.
+  EXPECT_FALSE(budget.NeedsLifecycleUpdates());
+
+  // Dirtying the element will cause us to do updates.
+  element->GetLayoutObject()->SetNeedsLayout("");
+  EXPECT_TRUE(budget.NeedsLifecycleUpdates());
+
+  budget.WillStartLifecycleUpdate();
+  // Initially all of the phase checks should return true, since we don't know
+  // which phase the system wants to process next.
+  EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kStyle));
+  EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kLayout));
+  EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kPrePaint));
+
+  // Not doing anything should ensure that we schedule another animation by
+  // returning true here.
+  EXPECT_TRUE(budget.NeedsLifecycleUpdates());
+
+  // Advancing the clock a bit will make us still want to the phases.
+  clock.Advance(TimeDelta::FromMillisecondsD(GetBudgetMs(budget) / 2));
+  EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kStyle));
+  EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kLayout));
+  EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kPrePaint));
+
+  // However, once we're out of budget, we will not do anything.
+  clock.Advance(TimeDelta::FromMillisecondsD(GetBudgetMs(budget)));
+  EXPECT_FALSE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kStyle));
+  EXPECT_FALSE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kLayout));
+  EXPECT_FALSE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kPrePaint));
+
+  // Starting a new lifecycle will reset the budget.
+  budget.WillStartLifecycleUpdate();
+  EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kStyle));
+  EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kLayout));
+  EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kPrePaint));
+
+  // Performing a phase still keeps the rest of the phases available for work
+  // since we haven't advanced the clock.
+  budget.DidPerformPhase(DisplayLockBudget::Phase::kStyle);
+  EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kStyle));
+  EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kLayout));
+  EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kPrePaint));
+
+  // Now that we're out of budget, phases performed previously should remain
+  // true.
+  clock.Advance(TimeDelta::FromMillisecondsD(GetBudgetMs(budget) * 2));
+  EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kStyle));
+  EXPECT_FALSE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kLayout));
+  EXPECT_FALSE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kPrePaint));
+
+  // Sanity check here: the element still needs layout.
+  EXPECT_TRUE(budget.NeedsLifecycleUpdates());
+
+  // Resetting the budget, and advnacing again should yield the same results as
+  // before.
+  budget.WillStartLifecycleUpdate();
+  EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kStyle));
+  EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kLayout));
+  EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kPrePaint));
+  clock.Advance(TimeDelta::FromMillisecondsD(GetBudgetMs(budget) * 2));
+  EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kStyle));
+  EXPECT_FALSE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kLayout));
+  EXPECT_FALSE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kPrePaint));
+
+  // Eventually the budget becomes essentially infinite.
+  for (int i = 0; i < 60; ++i)
+    budget.WillStartLifecycleUpdate();
+
+  EXPECT_GT(GetBudgetMs(budget), 1e6);
+  for (int i = 0; i < 60; ++i) {
+    EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kStyle));
+    EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kLayout));
+    EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kPrePaint));
+    clock.Advance(TimeDelta::FromMillisecondsD(10000));
+  }
+}
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.cc b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
index 5e631a67..932c9f8 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/core/display_lock/display_lock_options.h"
 #include "third_party/blink/renderer/core/display_lock/strict_yielding_display_lock_budget.h"
 #include "third_party/blink/renderer/core/display_lock/unyielding_display_lock_budget.h"
+#include "third_party/blink/renderer/core/display_lock/yielding_display_lock_budget.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
@@ -81,6 +82,7 @@
   // going to be disposed.
   FinishUpdateResolver(kDetach);
   FinishCommitResolver(kDetach);
+  CancelTimeoutTask();
   state_ = kUnlocked;
 
   if (element_ && element_->GetDocument().View())
@@ -139,8 +141,8 @@
 }
 
 ScriptPromise DisplayLockContext::update(ScriptState* script_state) {
-  // Reject if we're unlocked.
-  if (state_ == kUnlocked)
+  // Reject if we're unlocked or disconnected.
+  if (state_ == kUnlocked || !element_->isConnected())
     return GetRejectedPromise(script_state);
 
   // If we have a resolver, then we're at least updating already, just return
@@ -151,10 +153,7 @@
   }
 
   update_resolver_ = ScriptPromiseResolver::Create(script_state);
-  // We only need to kick off an Update if we're in a locked state, all other
-  // states are already updating.
-  if (state_ == kLocked)
-    StartUpdate();
+  StartUpdateIfNeeded();
   return update_resolver_->Promise();
 }
 
@@ -180,8 +179,9 @@
   // of the promises.
   DCHECK_NE(state_, kCommitting);
   commit_resolver_ = ScriptPromiseResolver::Create(script_state);
+  auto promise = commit_resolver_->Promise();
   StartCommit();
-  return commit_resolver_->Promise();
+  return promise;
 }
 
 void DisplayLockContext::FinishUpdateResolver(ResolverState state) {
@@ -354,6 +354,19 @@
 }
 
 void DisplayLockContext::StartCommit() {
+  // If we don't have an element or we're not connected, then the process of
+  // committing is the same as just unlocking the element.
+  if (!element_ || !element_->isConnected()) {
+    state_ = kUnlocked;
+    update_budget_.reset();
+    FinishUpdateResolver(kReject);
+    // TODO(vmpstr): Should we resolve here? What's the path to unlocking an
+    // element without connecting it (i.e. acquire the lock, then change your
+    // mind).
+    FinishCommitResolver(kReject);
+    return;
+  }
+
   DCHECK_LT(state_, kCommitting);
   if (state_ != kUpdating)
     ScheduleAnimation();
@@ -391,12 +404,19 @@
       layout_invalidation_reason::kDisplayLockCommitting);
 }
 
-void DisplayLockContext::StartUpdate() {
-  DCHECK_EQ(state_, kLocked);
-  state_ = kUpdating;
+void DisplayLockContext::StartUpdateIfNeeded() {
+  // We should not be calling this if we're unlocked.
+  DCHECK_NE(state_, kUnlocked);
+  // Any state other than kLocked means that we are already in the process of
+  // updating/committing, so we can piggy back on that process without kicking
+  // off any new updates.
+  if (state_ != kLocked)
+    return;
+
   // We don't need to mark anything dirty since the budget will take care of
   // that for us.
   update_budget_ = CreateNewBudget();
+  state_ = kUpdating;
   ScheduleAnimation();
 }
 
@@ -407,8 +427,7 @@
     case BudgetType::kStrictYieldBetweenLifecyclePhases:
       return base::WrapUnique(new StrictYieldingDisplayLockBudget(this));
     case BudgetType::kYieldBetweenLifecyclePhases:
-      NOTIMPLEMENTED();
-      return nullptr;
+      return base::WrapUnique(new YieldingDisplayLockBudget(this));
   }
   NOTREACHED();
   return nullptr;
@@ -486,6 +505,7 @@
   if (state_ == kCommitting) {
     FinishUpdateResolver(kResolve);
     FinishCommitResolver(kResolve);
+    CancelTimeoutTask();
     state_ = kUnlocked;
     return;
   }
@@ -493,6 +513,15 @@
   if (state_ != kUpdating)
     return;
 
+  // If we became disconnected for any reason, then we should reject the
+  // update promise and go back to the locked state.
+  if (!element_ || !element_->isConnected()) {
+    FinishUpdateResolver(kReject);
+    update_budget_.reset();
+    state_ = kLocked;
+    return;
+  }
+
   if (update_budget_->NeedsLifecycleUpdates()) {
     // Note that we post a task to schedule an animation, since rAF requests can
     // be ignored if they happen from within a lifecycle update.
@@ -509,6 +538,8 @@
 }
 
 void DisplayLockContext::ScheduleAnimation() {
+  DCHECK(element_->isConnected());
+
   // Schedule an animation to perform the lifecycle phases.
   element_->GetDocument().GetPage()->Animator().ScheduleVisualUpdate(
       element_->GetDocument().GetFrame());
@@ -539,8 +570,7 @@
 }
 
 void DisplayLockContext::TriggerTimeout() {
-  if (element_ && state_ < kCommitting)
-    StartCommit();
+  StartCommit();
   timeout_task_is_scheduled_ = false;
 }
 
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.h b/third_party/blink/renderer/core/display_lock/display_lock_context.h
index b8956f9..4bd5eb3 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.h
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.h
@@ -46,7 +46,7 @@
     kDoNotYield,
     kStrictYieldBetweenLifecyclePhases,
     kYieldBetweenLifecyclePhases,
-    kDefault = kDoNotYield
+    kDefault = kYieldBetweenLifecyclePhases
   };
 
   // See GetScopedPendingFrameRect() for description.
@@ -149,7 +149,7 @@
   // Initiate a commit.
   void StartCommit();
   // Initiate an update.
-  void StartUpdate();
+  void StartUpdateIfNeeded();
 
   // The following functions propagate dirty bits from the locked element up to
   // the ancestors in order to be reached. They return true if the element or
diff --git a/third_party/blink/renderer/core/display_lock/yielding_display_lock_budget.cc b/third_party/blink/renderer/core/display_lock/yielding_display_lock_budget.cc
new file mode 100644
index 0000000..c41dcc5
--- /dev/null
+++ b/third_party/blink/renderer/core/display_lock/yielding_display_lock_budget.cc
@@ -0,0 +1,89 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/display_lock/yielding_display_lock_budget.h"
+
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+#include <algorithm>
+
+namespace blink {
+
+YieldingDisplayLockBudget::YieldingDisplayLockBudget(
+    DisplayLockContext* context)
+    : DisplayLockBudget(context) {}
+
+bool YieldingDisplayLockBudget::ShouldPerformPhase(Phase phase) const {
+  // We should perform any phase earlier than the one we already completed.
+  if (last_completed_phase_ && phase <= *last_completed_phase_)
+    return true;
+
+  // Otherwise, we can still do work while we're not past the deadline.
+  return CurrentTimeTicks() < deadline_;
+}
+
+void YieldingDisplayLockBudget::DidPerformPhase(Phase phase) {
+  if (!last_completed_phase_ || phase > *last_completed_phase_)
+    last_completed_phase_ = phase;
+}
+
+void YieldingDisplayLockBudget::WillStartLifecycleUpdate() {
+  ++lifecycle_count_;
+  deadline_ =
+      CurrentTimeTicks() + TimeDelta::FromMillisecondsD(GetCurrentBudgetMs());
+
+  // Figure out the next phase we would run. If we had completed a phase before,
+  // then we should try to complete the next one, otherwise we'll start with the
+  // first phase.
+  Phase next_phase =
+      last_completed_phase_
+          ? static_cast<Phase>(
+                std::min(static_cast<unsigned>(*last_completed_phase_) + 1,
+                         static_cast<unsigned>(Phase::kLast)))
+          : Phase::kFirst;
+
+  // Mark the next phase we're scheduled to run.
+  for (auto phase = static_cast<unsigned>(next_phase);
+       phase <= static_cast<unsigned>(Phase::kLast); ++phase) {
+    if (MarkAncestorsDirtyForPhaseIfNeeded(static_cast<Phase>(phase)))
+      break;
+  }
+}
+
+bool YieldingDisplayLockBudget::NeedsLifecycleUpdates() const {
+  if (last_completed_phase_ && *last_completed_phase_ == Phase::kLast)
+    return false;
+
+  auto next_phase = last_completed_phase_
+                        ? static_cast<Phase>(
+                              static_cast<unsigned>(*last_completed_phase_) + 1)
+                        : Phase::kFirst;
+  // Check if any future phase needs updates.
+  for (auto phase = static_cast<unsigned>(next_phase);
+       phase <= static_cast<unsigned>(Phase::kLast); ++phase) {
+    if (IsElementDirtyForPhase(static_cast<Phase>(phase)))
+      return true;
+  }
+  return false;
+}
+
+double YieldingDisplayLockBudget::GetCurrentBudgetMs() const {
+  if (TimeTicks::IsHighResolution()) {
+    if (lifecycle_count_ < 3)
+      return 4.;
+    if (lifecycle_count_ < 10)
+      return 8.;
+    if (lifecycle_count_ < 60)
+      return 16.;
+  } else {
+    // Without a high resolution clock, the resolution can be as bad as 15ms, so
+    // increase the budgets accordingly to ensure we don't abort before doing
+    // any phases.
+    if (lifecycle_count_ < 60)
+      return 16.;
+  }
+  return 1e9;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/display_lock/yielding_display_lock_budget.h b/third_party/blink/renderer/core/display_lock/yielding_display_lock_budget.h
new file mode 100644
index 0000000..41a7d18
--- /dev/null
+++ b/third_party/blink/renderer/core/display_lock/yielding_display_lock_budget.h
@@ -0,0 +1,47 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DISPLAY_LOCK_YIELDING_DISPLAY_LOCK_BUDGET_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_DISPLAY_LOCK_YIELDING_DISPLAY_LOCK_BUDGET_H_
+
+#include "base/optional.h"
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/display_lock/display_lock_budget.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+// This budget yields between lifecycle phases even if that phase is quick. In
+// other words, it will only do one new lifecycle phase at a time, and block the
+// future ones. Any lifecycle phases that have already been allowed by this
+// budget in the past are not blocked.
+class CORE_EXPORT YieldingDisplayLockBudget final : public DisplayLockBudget {
+ public:
+  YieldingDisplayLockBudget(DisplayLockContext*);
+  ~YieldingDisplayLockBudget() override = default;
+
+  bool ShouldPerformPhase(Phase) const override;
+  void DidPerformPhase(Phase) override;
+  void WillStartLifecycleUpdate() override;
+  // Returns true if any of the lifecycles that have been previously blocked by
+  // this budget need updates. Note that this does not check lifecycle phases
+  // that have already completed by this budget even if they are now dirty
+  // again. This is done to prevent starvation (ie, it is possible for the
+  // budget to always schedule more work if something in rAF keeps dirtying
+  // layout, for example).
+  bool NeedsLifecycleUpdates() const override;
+
+ protected:
+  friend class DisplayLockBudgetTest;
+
+  double GetCurrentBudgetMs() const;
+
+  int lifecycle_count_ = 0;
+  TimeTicks deadline_;
+  base::Optional<Phase> last_completed_phase_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_DISPLAY_LOCK_YIELDING_DISPLAY_LOCK_BUDGET_H_
diff --git a/third_party/blink/renderer/core/inspector/InspectorOverlayPage.html b/third_party/blink/renderer/core/inspector/InspectorOverlayPage.html
index 0122dc5..27e8e59 100644
--- a/third_party/blink/renderer/core/inspector/InspectorOverlayPage.html
+++ b/third_party/blink/renderer/core/inspector/InspectorOverlayPage.html
@@ -546,24 +546,47 @@
 
 function _createMaterialTooltip(parentElement, bounds, contentElement, withArrow)
 {
-    var tooltipContainer = parentElement.createChild("div");
-    var tooltipContent = tooltipContainer.createChild("div", "tooltip-content");
+    const tooltipContainer = parentElement.createChild("div");
+    const tooltipContent = tooltipContainer.createChild("div", "tooltip-content");
     tooltipContent.appendChild(contentElement);
 
-    var titleWidth = tooltipContent.offsetWidth;
-    var titleHeight = tooltipContent.offsetHeight;
-    var arrowRadius = 8;
-    var pageMargin = 2;
+    const titleWidth = tooltipContent.offsetWidth;
+    const titleHeight = tooltipContent.offsetHeight;
+    const arrowHalfWidth = 8;
+    const pageMargin = 2;
+    const arrowWidth = arrowHalfWidth * 2;
+    const arrowInset = arrowHalfWidth + 2;
 
-    var boxX = Math.min(bounds.minX, canvasWidth - titleWidth - pageMargin);
+    const containerMinX = pageMargin + arrowInset;
+    const containerMaxX = canvasWidth - pageMargin - arrowInset - arrowWidth;
 
-    var boxY = bounds.minY - arrowRadius - titleHeight;
-    var onTop = true;
+    // Left align arrow to the tooltip but ensure it is pointing to the element.
+    // Center align arrow if the inspected element bounds are too narrow.
+    const boundsAreTooNarrow = bounds.maxX - bounds.minX < arrowWidth + 2 * arrowInset;
+    let arrowX;
+    if (boundsAreTooNarrow) {
+        arrowX = (bounds.minX + bounds.maxX) * 0.5 - arrowHalfWidth;
+    } else {
+        const xFromLeftBound = bounds.minX + arrowInset;
+        const xFromRightBound = bounds.maxX - arrowInset - arrowWidth;
+        if (xFromLeftBound > containerMinX && xFromLeftBound < containerMaxX)
+            arrowX = xFromLeftBound;
+        else
+            arrowX = Number.constrain(containerMinX, xFromLeftBound, xFromRightBound);
+    }
+    // Hide arrow if element is completely off the sides of the page.
+    const arrowHidden = arrowX < containerMinX || arrowX > containerMaxX;
+
+    let boxX = arrowX - arrowInset;
+    boxX = Number.constrain(boxX, pageMargin, canvasWidth - titleWidth - pageMargin);
+
+    let boxY = bounds.minY - arrowHalfWidth - titleHeight;
+    let onTop = true;
     if (boxY < 0) {
-        boxY = Math.min(canvasHeight - titleHeight, bounds.maxY + arrowRadius);
+        boxY = Math.min(canvasHeight - titleHeight, bounds.maxY + arrowHalfWidth);
         onTop = false;
     } else if (bounds.minY > canvasHeight) {
-        boxY = canvasHeight - arrowRadius - titleHeight;
+        boxY = canvasHeight - arrowHalfWidth - titleHeight;
     }
 
     tooltipContent.style.top = boxY + "px";
@@ -571,18 +594,11 @@
     if (!withArrow)
         return;
 
-    var tooltipArrow = tooltipContainer.createChild("div", "material-tooltip-arrow");
-    // Left align arrow to the tooltip but ensure it is pointing to the element.
-    var tooltipBorderRadius = 2;
-    var arrowX = boxX + arrowRadius + tooltipBorderRadius;
-    if (arrowX < bounds.minX)
-        arrowX = bounds.minX + arrowRadius;
-    // Hide arrow if element is completely off the sides of the page.
-    var arrowHidden = arrowX < pageMargin + tooltipBorderRadius || arrowX + arrowRadius * 2 > canvasWidth - pageMargin - tooltipBorderRadius;
+    const tooltipArrow = tooltipContainer.createChild("div", "material-tooltip-arrow");
     tooltipArrow.classList.toggle("hidden", arrowHidden);
     if (!arrowHidden) {
         tooltipArrow.classList.toggle("tooltip-arrow-top", onTop);
-        tooltipArrow.style.top = (onTop ? boxY + titleHeight : boxY - arrowRadius) + "px";
+        tooltipArrow.style.top = (onTop ? boxY + titleHeight : boxY - arrowHalfWidth) + "px";
         tooltipArrow.style.left = arrowX + "px";
     }
 }
@@ -910,6 +926,20 @@
     return this.substr(0, maxLength - 1) + "\u2026";
 }
 
+/**
+ * @param {number} num
+ * @param {number} min
+ * @param {number} max
+ * @return {number}
+ */
+ Number.constrain = function(num, min, max) {
+    if (num < min)
+        num = min;
+    else if (num > max)
+        num = max;
+    return num;
+};
+
 window.addEventListener("DOMContentLoaded", onLoaded);
 document.addEventListener("keydown", onDocumentKeyDown);
 </script>
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index 60c3aa3..fecc1dc2 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -3058,6 +3058,10 @@
       optional number deltaX
       # Y delta in CSS pixels for mouse wheel event (default: 0).
       optional number deltaY
+      # Pointer type (default: "mouse").
+      optional enum pointerType
+        mouse
+        pen
 
   # Dispatches a touch event to the page.
   command dispatchTouchEvent
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
index 9461cb1..ec37fc3b 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
@@ -352,10 +352,6 @@
       return "code";
     case v8::ScriptCompiler::kEagerCompile:
       return "full code";
-    // TODO(v8:8252): Remove the default branch once deprecated options are
-    // removed from v8::ScriptCompiler::CompileOptions.
-    default:
-      NOTREACHED();
   }
   NOTREACHED();
   return "";
diff --git a/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc b/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc
index f006da1..996bd3f 100644
--- a/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc
+++ b/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc
@@ -1461,45 +1461,4 @@
   last_set_worked_on_ = state.ColumnSet();
 }
 
-unsigned LayoutMultiColumnFlowThread::CalculateActualColumnCountAllowance()
-    const {
-  // To avoid performance problems, limit the maximum number of columns. Try to
-  // identify legitimate reasons for creating many columns, and allow many
-  // columns in such cases. The amount of "content" will determine the
-  // allowance.
-  unsigned allowance = 0;
-
-  // This isn't a particularly clever algorithm. For example, we don't account
-  // for parallel flows (absolute positioning, floats, visible overflow, table
-  // cells, flex items). We just generously add everything together.
-  for (const LayoutObject* descendant = this; descendant;) {
-    bool examine_children = false;
-    if (descendant->IsBox() && !descendant->IsInline() &&
-        !ToLayoutBox(descendant)->IsWritingModeRoot()) {
-      // Give one point to any kind of block level content.
-      allowance++;
-      if (descendant->IsLayoutBlockFlow() && descendant->ChildrenInline()) {
-        // It's a block-level block container in the same writing mode, and it
-        // has inline children. Count the lines and add it to the allowance.
-        allowance += ToLayoutBlockFlow(descendant)->LineCount();
-      } else {
-        // We could examine other types of layout modes (tables, flexbox, etc.)
-        // as well, but then again, that might be overkill. Just enter and see
-        // what we find.
-        examine_children = true;
-      }
-    }
-
-    if (allowance >= ColumnCountClampMax())
-      return ColumnCountClampMax();
-
-    descendant = examine_children
-                     ? descendant->NextInPreOrder(this)
-                     : descendant->NextInPreOrderAfterChildren(this);
-  }
-
-  DCHECK_LE(allowance, ColumnCountClampMax());
-  return std::max(allowance, ColumnCountClampMin());
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h b/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h
index 6b5e654..697e426 100644
--- a/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h
+++ b/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h
@@ -190,18 +190,6 @@
 
   unsigned ColumnCount() const { return column_count_; }
 
-  // Calculate and return the actual column count allowance. This method should
-  // only be called when we ended up with an actual column count larger than
-  // ColumnCountClampMin() in some fragmentainer group.
-  unsigned CalculateActualColumnCountAllowance() const;
-
-  // Any actual column count value lower than this will be allowed
-  // unconditionally.
-  static unsigned ColumnCountClampMin() { return 10; }
-
-  // The maximum actual column count we're going to allow.
-  static unsigned ColumnCountClampMax() { return 2000; }
-
   // Total height available to columns and spanners. This is the multicol
   // container's content box logical height, or 0 if auto.
   LayoutUnit ColumnHeightAvailable() const { return column_height_available_; }
diff --git a/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group.cc b/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group.cc
index 70fbce4..a5e8c94 100644
--- a/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group.cc
+++ b/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group.cc
@@ -10,6 +10,15 @@
 
 namespace blink {
 
+// Limit the maximum column count, to prevent potential performance problems.
+static const unsigned kColumnCountClampMax = 10000;
+
+// Clamp "infinite" clips to a number of pixels that can be losslessly
+// converted to and from floating point, to avoid loss of precision.
+// Note that tables have something similar, see
+// TableLayoutAlgorithm::kTableMaxWidth.
+static const int kMulticolMaxClipPixels = 1000000;
+
 MultiColumnFragmentainerGroup::MultiColumnFragmentainerGroup(
     const LayoutMultiColumnSet& column_set)
     : column_set_(column_set) {}
@@ -62,7 +71,6 @@
 }
 
 void MultiColumnFragmentainerGroup::ResetColumnHeight() {
-  actual_column_count_allowance_ = 0;
   max_logical_height_ = CalculateMaxColumnHeight();
 
   LayoutMultiColumnFlowThread* flow_thread =
@@ -138,24 +146,6 @@
   // height.
   is_logical_height_known_ = true;
 
-  unsigned column_count = UnclampedActualColumnCount();
-  if (column_count > LayoutMultiColumnFlowThread::ColumnCountClampMax() ||
-      (column_count > LayoutMultiColumnFlowThread::ColumnCountClampMin() &&
-       column_count > column_set_.UsedColumnCount())) {
-    // That's a lot of columns! We have either exceeded the maximum value, or we
-    // have overflowing columns, and the proposed count is within clamping
-    // range. Calculate allowance to make sure we have a legitimate reason for
-    // it, or else clamp it. We have quadratic performance complexity for
-    // painting columns.
-    if (!actual_column_count_allowance_) {
-      const auto* flow_thread = column_set_.MultiColumnFlowThread();
-      unsigned allowance = flow_thread->CalculateActualColumnCountAllowance();
-      DCHECK_GE(allowance, LayoutMultiColumnFlowThread::ColumnCountClampMin());
-      DCHECK_LE(allowance, LayoutMultiColumnFlowThread::ColumnCountClampMax());
-      actual_column_count_allowance_ = allowance;
-    }
-  }
-
   if (logical_height_ == old_column_height)
     return false;  // No change. We're done.
 
@@ -329,8 +319,7 @@
 
 unsigned MultiColumnFragmentainerGroup::ActualColumnCount() const {
   unsigned count = UnclampedActualColumnCount();
-  if (actual_column_count_allowance_)
-    count = std::min(count, actual_column_count_allowance_);
+  count = std::min(count, kColumnCountClampMax);
   DCHECK_GE(count, 1u);
   return count;
 }
@@ -453,12 +442,6 @@
                     column_set_.PageLogicalWidth());
 }
 
-// Clamp "infinite" clips to a number of pixels that can be losslessly
-// converted to and from floating point, to avoid loss of precision.
-// Note that tables have something similar, see
-// TableLayoutAlgorithm::kTableMaxWidth.
-static const int kMulticolMaxClipPixels = 1000000;
-
 LayoutRect MultiColumnFragmentainerGroup::FlowThreadPortionOverflowRectAt(
     unsigned column_index) const {
   // This function determines the portion of the flow thread that paints for the
diff --git a/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group.h b/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group.h
index 7252791..f5f0483c 100644
--- a/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group.h
+++ b/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group.h
@@ -194,8 +194,6 @@
   // Maximum logical height allowed.
   LayoutUnit max_logical_height_;
 
-  unsigned actual_column_count_allowance_ = 0;
-
   bool is_logical_height_known_ = false;
 };
 
diff --git a/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group_test.cc b/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group_test.cc
index 1d9420fa..f86cb314 100644
--- a/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group_test.cc
+++ b/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group_test.cc
@@ -97,32 +97,6 @@
   EXPECT_EQ(GroupCount(group_list), 1);
 }
 
-// The following test tests that we restrict actual column count, to not run
-// into unnecessary performance problems. The code that applies this limitation
-// is in MultiColumnFragmentainerGroup::ActualColumnCount().
-TEST_F(MultiColumnFragmentainerGroupTest, TallEmptyContent) {
-  SetBodyInnerHTML(R"HTML(
-    <div id="multicol" style="columns:3; column-gap:1px; width:101px; line-height:50px; height:200px;">
-      <div style="height:1000000px;"></div>
-    </div>
-  )HTML");
-  const auto* multicol = GetLayoutObjectByElementId("multicol");
-  ASSERT_TRUE(multicol);
-  ASSERT_TRUE(multicol->IsLayoutBlockFlow());
-  const auto* column_set = multicol->SlowLastChild();
-  ASSERT_TRUE(column_set);
-  ASSERT_TRUE(column_set->IsLayoutMultiColumnSet());
-  const auto& fragmentainer_group =
-      ToLayoutMultiColumnSet(column_set)->FirstFragmentainerGroup();
-  EXPECT_EQ(fragmentainer_group.ActualColumnCount(), 10U);
-  EXPECT_EQ(fragmentainer_group.GroupLogicalHeight(), LayoutUnit(200));
-  auto overflow = ToLayoutBox(multicol)->LayoutOverflowRect();
-  EXPECT_EQ(ToLayoutBox(multicol)->LogicalWidth(), LayoutUnit(101));
-  EXPECT_EQ(ToLayoutBox(multicol)->LogicalHeight(), LayoutUnit(200));
-  EXPECT_EQ(overflow.Width(), LayoutUnit(339));
-  EXPECT_EQ(overflow.Height(), LayoutUnit(998200));
-}
-
 // The following test tests that we DON'T restrict actual column count, when
 // there's a legitimate reason to use many columns. The code that checks the
 // allowance and potentially applies this limitation is in
diff --git a/third_party/blink/renderer/core/paint/block_painter.cc b/third_party/blink/renderer/core/paint/block_painter.cc
index a794330..8f261054 100644
--- a/third_party/blink/renderer/core/paint/block_painter.cc
+++ b/third_party/blink/renderer/core/paint/block_painter.cc
@@ -106,22 +106,12 @@
     layout_block_.PaintObject(local_paint_info, paint_offset);
   }
 
-  // Our scrollbar widgets paint exactly when we tell them to, so that they work
-  // properly with z-index. We paint after we painted the background/border, so
-  // that the scrollbars will sit above the background/border.
+  // We paint scrollbars after we painted the other things, so that the
+  // scrollbars will sit above them.
   local_paint_info.phase = original_phase;
-  PaintOverflowControlsIfNeeded(local_paint_info, paint_offset);
-}
-
-void BlockPainter::PaintOverflowControlsIfNeeded(
-    const PaintInfo& paint_info,
-    const LayoutPoint& paint_offset) {
-  if (layout_block_.HasOverflowClip() &&
-      layout_block_.StyleRef().Visibility() == EVisibility::kVisible &&
-      ShouldPaintSelfBlockBackground(paint_info.phase)) {
-    ScrollableAreaPainter(*layout_block_.Layer()->GetScrollableArea())
-        .PaintOverflowControls(paint_info, RoundedIntPoint(paint_offset),
-                               false /* painting_overlay_controls */);
+  if (auto* scrollable_area = layout_block_.GetScrollableArea()) {
+    ScrollableAreaPainter(*scrollable_area)
+        .PaintOverflowControls(local_paint_info, RoundedIntPoint(paint_offset));
   }
 }
 
diff --git a/third_party/blink/renderer/core/paint/block_painter.h b/third_party/blink/renderer/core/paint/block_painter.h
index e16f0be..7fe1e38 100644
--- a/third_party/blink/renderer/core/paint/block_painter.h
+++ b/third_party/blink/renderer/core/paint/block_painter.h
@@ -28,8 +28,6 @@
   void PaintContents(const PaintInfo&, const LayoutPoint& paint_offset);
   void PaintChildren(const PaintInfo&);
   void PaintChild(const LayoutBox&, const PaintInfo&);
-  void PaintOverflowControlsIfNeeded(const PaintInfo&,
-                                     const LayoutPoint& paint_offset);
 
   // See ObjectPainter::PaintAllPhasesAtomically().
   void PaintAllChildPhasesAtomically(const LayoutBox&, const PaintInfo&);
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
index 55f34d8e..8423165 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
@@ -3128,13 +3128,6 @@
         paint_info.paint_layer->SubpixelAccumulation());
     PaintLayerPainter(*paint_info.paint_layer)
         .PaintLayerContents(context, painting_info, paint_layer_flags);
-
-    if (paint_info.paint_layer->ContainsDirtyOverlayScrollbars()) {
-      PaintLayerPainter(*paint_info.paint_layer)
-          .PaintLayerContents(
-              context, painting_info,
-              paint_layer_flags | kPaintLayerPaintingOverlayScrollbars);
-    }
   } else {
     PaintLayerPaintingInfo painting_info(
         paint_info.paint_layer, CullRect(dirty_rect), kGlobalPaintNormalPhase,
diff --git a/third_party/blink/renderer/core/paint/frame_painter.cc b/third_party/blink/renderer/core/paint/frame_painter.cc
index 38b39e3d..f2bc7e39 100644
--- a/third_party/blink/renderer/core/paint/frame_painter.cc
+++ b/third_party/blink/renderer/core/paint/frame_painter.cc
@@ -104,11 +104,6 @@
   layer_painter.Paint(context, cull_rect, updated_global_paint_flags,
                       root_layer_paint_flags);
 
-  if (root_layer->ContainsDirtyOverlayScrollbars()) {
-    layer_painter.PaintOverlayScrollbars(context, cull_rect,
-                                         updated_global_paint_flags);
-  }
-
   // Regions may have changed as a result of the visibility/z-index of element
   // changing.
   if (document->AnnotatedRegionsDirty())
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
index 0390581..6efef55 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
@@ -192,11 +192,13 @@
     PaintObject(info, paint_offset);
   }
 
-  // Our scrollbar widgets paint exactly when we tell them to, so that they work
-  // properly with z-index. We paint after we painted the background/border, so
-  // that the scrollbars will sit above the background/border.
+  // We paint scrollbars after we painted other things, so that the scrollbars
+  // will sit above them.
   info.phase = original_phase;
-  PaintOverflowControlsIfNeeded(info, paint_offset);
+  if (box_fragment_.HasOverflowClip()) {
+    ScrollableAreaPainter(*PhysicalFragment().Layer()->GetScrollableArea())
+        .PaintOverflowControls(info, RoundedIntPoint(paint_offset));
+  }
 }
 
 void NGBoxFragmentPainter::RecordHitTestData(const PaintInfo& paint_info,
@@ -851,19 +853,6 @@
          box_fragment_.GetLayoutObject() == paint_info.PaintContainer();
 }
 
-// Clone of BlockPainter::PaintOverflowControlsIfNeeded
-void NGBoxFragmentPainter::PaintOverflowControlsIfNeeded(
-    const PaintInfo& paint_info,
-    const LayoutPoint& paint_offset) {
-  if (box_fragment_.HasOverflowClip() &&
-      box_fragment_.Style().Visibility() == EVisibility::kVisible &&
-      ShouldPaintSelfBlockBackground(paint_info.phase)) {
-    ScrollableAreaPainter(*PhysicalFragment().Layer()->GetScrollableArea())
-        .PaintOverflowControls(paint_info, RoundedIntPoint(paint_offset),
-                               false /* painting_overlay_controls */);
-  }
-}
-
 bool NGBoxFragmentPainter::ShouldPaint(
     const ScopedPaintState& paint_state) const {
   // TODO(layout-dev): Add support for scrolling, see BlockPainter::ShouldPaint.
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h
index d2887ee..061d6b94 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h
@@ -99,8 +99,6 @@
   void PaintFloatingChildren(NGPaintFragment::ChildList, const PaintInfo&);
   void PaintFloats(const PaintInfo&);
   void PaintMask(const PaintInfo&, const LayoutPoint& paint_offset);
-  void PaintOverflowControlsIfNeeded(const PaintInfo&,
-                                     const LayoutPoint& paint_offset);
   void PaintAtomicInline(const PaintInfo&);
   void PaintBackground(const PaintInfo&,
                        const LayoutRect&,
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index 4a65163..68e58825 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -148,7 +148,6 @@
       needs_position_update_(!IsRootLayer()),
 #endif
       has3d_transformed_descendant_(false),
-      contains_dirty_overlay_scrollbars_(false),
       needs_ancestor_dependent_compositing_inputs_update_(
           !RuntimeEnabledFeatures::CompositeAfterPaintEnabled()),
       child_needs_compositing_inputs_update_(
diff --git a/third_party/blink/renderer/core/paint/paint_layer.h b/third_party/blink/renderer/core/paint/paint_layer.h
index 8467c06..b2f4f9f6 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.h
+++ b/third_party/blink/renderer/core/paint/paint_layer.h
@@ -594,13 +594,6 @@
   bool BackgroundIsKnownToBeOpaqueInRect(const LayoutRect&,
                                          bool should_check_children) const;
 
-  bool ContainsDirtyOverlayScrollbars() const {
-    return contains_dirty_overlay_scrollbars_;
-  }
-  void SetContainsDirtyOverlayScrollbars(bool dirty_scrollbars) {
-    contains_dirty_overlay_scrollbars_ = dirty_scrollbars;
-  }
-
   // If the input CompositorFilterOperation is not empty, it will be populated
   // only if |filter_on_effect_node_dirty_| is true or the reference box has
   // changed. Otherwise it will be populated unconditionally.
@@ -1270,8 +1263,6 @@
   // in a preserves3D hierarchy. Hint to do 3D-aware hit testing.
   unsigned has3d_transformed_descendant_ : 1;
 
-  unsigned contains_dirty_overlay_scrollbars_ : 1;
-
   unsigned needs_ancestor_dependent_compositing_inputs_update_ : 1;
   unsigned child_needs_compositing_inputs_update_ : 1;
 
diff --git a/third_party/blink/renderer/core/paint/paint_layer_painter.cc b/third_party/blink/renderer/core/paint/paint_layer_painter.cc
index fddbe89..2f6e4bc 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_painter.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_painter.cc
@@ -181,8 +181,7 @@
   if (painting_info.GetGlobalPaintFlags() &
       kGlobalPaintFlattenCompositingLayers)
     return false;
-  if (paint_flags &
-      (kPaintLayerPaintingOverlayScrollbars | kPaintLayerUncachedClipRects))
+  if (paint_flags & kPaintLayerUncachedClipRects)
     return false;
 
   // When in FOUC-avoidance mode, don't cache any subsequences, to avoid having
@@ -372,8 +371,6 @@
   AdjustForPaintProperties(context, painting_info, paint_flags);
 
   bool is_self_painting_layer = paint_layer_.IsSelfPaintingLayer();
-  bool is_painting_overlay_scrollbars =
-      paint_flags & kPaintLayerPaintingOverlayScrollbars;
   bool is_painting_scrolling_content =
       paint_flags & kPaintLayerPaintingCompositingScrollingPhase;
   bool is_painting_composited_foreground =
@@ -390,7 +387,7 @@
   // It is painted as part of the decoration phase which paints content that
   // is not scrolled and should be above scrolled content.
   bool should_paint_self_outline =
-      is_self_painting_layer && !is_painting_overlay_scrollbars &&
+      is_self_painting_layer &&
       (is_painting_composited_decoration ||
        (!is_painting_overflow_contents && !is_painting_mask)) &&
       paint_layer_.GetLayoutObject().StyleRef().HasOutline();
@@ -449,13 +446,11 @@
       // Content under a LayoutSVGHiddenContainer is auxiliary resources for
       // painting. Foreign content should never paint in this situation, as it
       // is primary, not auxiliary.
-      !paint_layer_.IsUnderSVGHiddenContainer() && is_self_painting_layer &&
-      !is_painting_overlay_scrollbars;
+      !paint_layer_.IsUnderSVGHiddenContainer() && is_self_painting_layer;
 
   PaintLayerFragments layer_fragments;
 
-  if (should_paint_content || should_paint_self_outline ||
-      is_painting_overlay_scrollbars) {
+  if (should_paint_content || should_paint_self_outline) {
     // Collect the fragments. This will compute the clip rectangles and paint
     // offsets for each layer fragment.
     LayoutPoint offset_to_clipper;
@@ -532,7 +527,6 @@
         is_painting_composited_foreground && should_paint_content;
     bool should_paint_normal_flow_and_pos_z_order_lists =
         is_painting_composited_foreground;
-    bool should_paint_overlay_scrollbars = is_painting_overlay_scrollbars;
 
     base::Optional<ScopedPaintChunkProperties>
         subsequence_forced_chunk_properties;
@@ -590,12 +584,7 @@
         result = kMayBeClippedByCullRect;
     }
 
-    if (should_paint_overlay_scrollbars) {
-      PaintOverflowControlsForFragments(layer_fragments, context,
-                                        local_painting_info, paint_flags);
-    }
-
-    if (!is_painting_overlay_scrollbars && paint_layer_.PaintsWithFilters() &&
+    if (paint_layer_.PaintsWithFilters() &&
         display_item_list_size_before_painting ==
             context.GetPaintController().NewDisplayItemList().size()) {
       // If a layer with filters painted nothing, we need to issue a no-op
@@ -735,42 +724,6 @@
   return result;
 }
 
-void PaintLayerPainter::PaintOverflowControlsForFragments(
-    const PaintLayerFragments& layer_fragments,
-    GraphicsContext& context,
-    const PaintLayerPaintingInfo& painting_info,
-    PaintLayerFlags paint_flags) {
-  PaintLayerScrollableArea* scrollable_area = paint_layer_.GetScrollableArea();
-  if (!scrollable_area)
-    return;
-
-  ForAllFragments(
-      context, layer_fragments, [&](const PaintLayerFragment& fragment) {
-        ScopedPaintChunkProperties fragment_paint_chunk_properties(
-            context.GetPaintController(),
-            fragment.fragment_data->LocalBorderBoxProperties(), paint_layer_,
-            DisplayItem::kOverflowControls);
-
-        // We need to apply the same clips and transforms that
-        // paintFragmentWithPhase would have.
-        LayoutRect cull_rect = fragment.background_rect.Rect();
-        PaintInfo paint_info(
-            context, PixelSnappedIntRect(cull_rect),
-            PaintPhase::kSelfBlockBackgroundOnly,
-            painting_info.GetGlobalPaintFlags(), paint_flags,
-            &painting_info.root_layer->GetLayoutObject(),
-            fragment.fragment_data
-                ? fragment.fragment_data->LogicalTopInFlowThread()
-                : LayoutUnit());
-        // We pass IntPoint() as the paint offset here, because
-        // ScrollableArea::paintOverflowControls just ignores it and uses the
-        // offset found in a previous pass.
-        ScrollableAreaPainter(*scrollable_area)
-            .PaintOverflowControls(paint_info, IntPoint(),
-                                   true /* painting_overlay_controls */);
-      });
-}
-
 void PaintLayerPainter::PaintFragmentWithPhase(
     PaintPhase phase,
     const PaintLayerFragment& fragment,
@@ -974,20 +927,6 @@
       });
 }
 
-void PaintLayerPainter::PaintOverlayScrollbars(
-    GraphicsContext& context,
-    const CullRect& cull_rect,
-    const GlobalPaintFlags paint_flags) {
-  if (!paint_layer_.ContainsDirtyOverlayScrollbars())
-    return;
-
-  PaintLayerPaintingInfo painting_info(&paint_layer_, cull_rect, paint_flags,
-                                       LayoutSize());
-  Paint(context, painting_info, kPaintLayerPaintingOverlayScrollbars);
-
-  paint_layer_.SetContainsDirtyOverlayScrollbars(false);
-}
-
 void PaintLayerPainter::FillMaskingFragment(GraphicsContext& context,
                                             const ClipRect& clip_rect,
                                             const DisplayItemClient& client) {
diff --git a/third_party/blink/renderer/core/paint/paint_layer_painter.h b/third_party/blink/renderer/core/paint/paint_layer_painter.h
index 4c39e00..ac6d603 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_painter.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_painter.h
@@ -49,10 +49,6 @@
                                  const PaintLayerPaintingInfo&,
                                  PaintLayerFlags);
 
-  void PaintOverlayScrollbars(GraphicsContext&,
-                              const CullRect&,
-                              const GlobalPaintFlags);
-
   // Returns true if the painted output of this PaintLayer and its children is
   // invisible and therefore can't impact painted output.
   bool PaintedOutputInvisible(const ComputedStyle&,
@@ -96,10 +92,6 @@
                                     GraphicsContext&,
                                     const PaintLayerPaintingInfo&,
                                     PaintLayerFlags);
-  void PaintOverflowControlsForFragments(const PaintLayerFragments&,
-                                         GraphicsContext&,
-                                         const PaintLayerPaintingInfo&,
-                                         PaintLayerFlags);
   void PaintMaskForFragments(const PaintLayerFragments&,
                              GraphicsContext&,
                              const PaintLayerPaintingInfo&,
diff --git a/third_party/blink/renderer/core/paint/paint_layer_painting_info.h b/third_party/blink/renderer/core/paint/paint_layer_painting_info.h
index bee6fa8e..eecbf5e 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_painting_info.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_painting_info.h
@@ -63,7 +63,6 @@
   kPaintLayerNoFlag = 0,
   kPaintLayerHaveTransparency = 1,
   kPaintLayerUncachedClipRects = 1 << 2,
-  kPaintLayerPaintingOverlayScrollbars = 1 << 3,
   kPaintLayerPaintingCompositingBackgroundPhase = 1 << 4,
   kPaintLayerPaintingCompositingForegroundPhase = 1 << 5,
   kPaintLayerPaintingCompositingMaskPhase = 1 << 6,
@@ -140,8 +139,6 @@
     append("kPaintLayerHaveTransparency");
   if (flags & kPaintLayerUncachedClipRects)
     append("kPaintLayerUncachedClipRects");
-  if (flags & kPaintLayerPaintingOverlayScrollbars)
-    append("kPaintLayerPaintingOverlayScrollbars");
   if (flags & kPaintLayerPaintingCompositingScrollingPhase)
     append("kPaintLayerPaintingCompositingScrollingPhase");
   if (flags & kPaintLayerPaintingOverflowContents)
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
index 48def2b..225e030 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
@@ -447,13 +447,6 @@
 
   LayoutScrollbarPart* Resizer() const { return resizer_; }
 
-  const IntPoint& CachedOverlayScrollbarOffset() {
-    return cached_overlay_scrollbar_offset_;
-  }
-  void SetCachedOverlayScrollbarOffset(const IntPoint& offset) {
-    cached_overlay_scrollbar_offset_ = offset;
-  }
-
   IntRect RectForHorizontalScrollbar(const IntRect& border_box_rect) const;
   IntRect RectForVerticalScrollbar(const IntRect& border_box_rect) const;
 
@@ -679,8 +672,6 @@
   // This is the offset from the beginning of content flow.
   ScrollOffset scroll_offset_;
 
-  IntPoint cached_overlay_scrollbar_offset_;
-
   // LayoutObject to hold our custom scroll corner.
   LayoutScrollbarPart* scroll_corner_;
 
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 c09566bd..33fed0f1 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
@@ -5535,22 +5535,6 @@
   EXPECT_EQ(LayoutRect(390, -10, 20, 20), second_fragment->VisualRect());
 }
 
-// The following test tests that we restrict actual column count, to not run
-// into unnecessary performance problems. The code that applies this limitation
-// is in MultiColumnFragmentainerGroup::ActualColumnCount().
-TEST_P(PaintPropertyTreeBuilderTest, ShortColumnTallContent) {
-  SetBodyInnerHTML(R"HTML(
-    <div id="multicol" style="columns:3; column-gap:1px; width:101px; height:1px;">
-      <div style="height:1000000px;"></div>
-    </div>
-  )HTML");
-
-  const auto* flow_thread =
-      GetLayoutObjectByElementId("multicol")->SlowFirstChild();
-  ASSERT_TRUE(flow_thread->IsLayoutFlowThread());
-  EXPECT_EQ(10u, NumFragments(flow_thread));
-}
-
 TEST_P(PaintPropertyTreeBuilderTest, FragmentClipPixelSnapped) {
   SetBodyInnerHTML(R"HTML(
     <div id="container" style="columns: 2; column-gap: 0; width: 49.5px">
diff --git a/third_party/blink/renderer/core/paint/scrollable_area_painter.cc b/third_party/blink/renderer/core/paint/scrollable_area_painter.cc
index f2f01a0e..c02359c 100644
--- a/third_party/blink/renderer/core/paint/scrollable_area_painter.cc
+++ b/third_party/blink/renderer/core/paint/scrollable_area_painter.cc
@@ -123,56 +123,23 @@
 
 void ScrollableAreaPainter::PaintOverflowControls(
     const PaintInfo& paint_info,
-    const IntPoint& paint_offset,
-    bool painting_overlay_controls) {
+    const IntPoint& paint_offset) {
   // Don't do anything if we have no overflow.
-  if (!GetScrollableArea().GetLayoutBox()->HasOverflowClip())
+  const auto& box = *GetScrollableArea().GetLayoutBox();
+  if (!box.HasOverflowClip() ||
+      box.StyleRef().Visibility() != EVisibility::kVisible)
     return;
 
-  IntPoint adjusted_paint_offset = paint_offset;
-  if (painting_overlay_controls)
-    adjusted_paint_offset = GetScrollableArea().CachedOverlayScrollbarOffset();
-
-  CullRect adjusted_cull_rect = paint_info.GetCullRect();
-  adjusted_cull_rect.MoveBy(-adjusted_paint_offset);
-  // Overlay scrollbars paint in a second pass through the layer tree so that
-  // they will paint on top of everything else. If this is the normal painting
-  // pass, paintingOverlayControls will be false, and we should just tell the
-  // root layer that there are overlay scrollbars that need to be painted. That
-  // will cause the second pass through the layer tree to run, and we'll paint
-  // the scrollbars then. In the meantime, cache tx and ty so that the second
-  // pass doesn't need to re-enter the LayoutTree to get it right.
-  if (GetScrollableArea().HasOverlayScrollbars() &&
-      !painting_overlay_controls) {
-    GetScrollableArea().SetCachedOverlayScrollbarOffset(paint_offset);
-    // It's not necessary to do the second pass if the scrollbars paint into
-    // layers.
-    if ((GetScrollableArea().HorizontalScrollbar() &&
-         GetScrollableArea().LayerForHorizontalScrollbar()) ||
-        (GetScrollableArea().VerticalScrollbar() &&
-         GetScrollableArea().LayerForVerticalScrollbar()))
+  // Overlay scrollbars are painted in the foreground paint phase, and normal
+  // scrollbars are painted in the background paint phase.
+  if (GetScrollableArea().HasOverlayScrollbars()) {
+    if (paint_info.phase != PaintPhase::kForeground)
       return;
-    if (!OverflowControlsIntersectRect(adjusted_cull_rect))
-      return;
-
-    LayoutView* layout_view = GetScrollableArea().GetLayoutBox()->View();
-
-    PaintLayer* painting_root =
-        GetScrollableArea().Layer()->EnclosingLayerWithCompositedLayerMapping(
-            kIncludeSelf);
-    if (!painting_root)
-      painting_root = layout_view->Layer();
-
-    painting_root->SetContainsDirtyOverlayScrollbars(true);
+  } else if (!ShouldPaintSelfBlockBackground(paint_info.phase)) {
     return;
   }
 
-  // This check is required to avoid painting custom CSS scrollbars twice.
-  if (painting_overlay_controls && !GetScrollableArea().HasOverlayScrollbars())
-    return;
-
   GraphicsContext& context = paint_info.context;
-  const auto& box = *GetScrollableArea().GetLayoutBox();
   const auto* fragment = paint_info.FragmentToPaint(box);
   if (!fragment)
     return;
@@ -190,6 +157,8 @@
     }
   }
 
+  CullRect adjusted_cull_rect = paint_info.GetCullRect();
+  adjusted_cull_rect.MoveBy(-paint_offset);
   if (GetScrollableArea().HorizontalScrollbar() &&
       !GetScrollableArea().LayerForHorizontalScrollbar()) {
     GetScrollableArea().HorizontalScrollbar()->Paint(context,
@@ -199,51 +168,27 @@
       !GetScrollableArea().LayerForVerticalScrollbar()) {
     GetScrollableArea().VerticalScrollbar()->Paint(context, adjusted_cull_rect);
   }
+
   if (!GetScrollableArea().LayerForScrollCorner()) {
     // We fill our scroll corner with white if we have a scrollbar that doesn't
     // run all the way up to the edge of the box.
-    PaintScrollCorner(context, adjusted_paint_offset, paint_info.GetCullRect());
+    PaintScrollCorner(context, paint_offset, paint_info.GetCullRect());
 
     // Paint our resizer last, since it sits on top of the scroll corner.
-    PaintResizer(context, adjusted_paint_offset, paint_info.GetCullRect());
+    PaintResizer(context, paint_offset, paint_info.GetCullRect());
   }
 }
 
-bool ScrollableAreaPainter::OverflowControlsIntersectRect(
-    const CullRect& cull_rect) const {
-  const IntRect border_box =
-      GetScrollableArea().GetLayoutBox()->PixelSnappedBorderBoxRect(
-          GetScrollableArea().Layer()->SubpixelAccumulation());
-
-  if (cull_rect.Intersects(
-          GetScrollableArea().RectForHorizontalScrollbar(border_box)))
-    return true;
-
-  if (cull_rect.Intersects(
-          GetScrollableArea().RectForVerticalScrollbar(border_box)))
-    return true;
-
-  if (cull_rect.Intersects(GetScrollableArea().ScrollCornerRect()))
-    return true;
-
-  if (cull_rect.Intersects(GetScrollableArea().ResizerCornerRect(
-          border_box, kResizerForPointer)))
-    return true;
-
-  return false;
-}
-
-void ScrollableAreaPainter::PaintScrollCorner(
-    GraphicsContext& context,
-    const IntPoint& paint_offset,
-    const CullRect& adjusted_cull_rect) {
+void ScrollableAreaPainter::PaintScrollCorner(GraphicsContext& context,
+                                              const IntPoint& paint_offset,
+                                              const CullRect& cull_rect) {
   IntRect abs_rect = GetScrollableArea().ScrollCornerRect();
   if (abs_rect.IsEmpty())
     return;
   abs_rect.MoveBy(paint_offset);
 
   if (const auto* scroll_corner = GetScrollableArea().ScrollCorner()) {
-    if (!adjusted_cull_rect.Intersects(abs_rect))
+    if (!cull_rect.Intersects(abs_rect))
       return;
     ScrollbarPainter::PaintIntoRect(*scroll_corner, context, paint_offset,
                                     LayoutRect(abs_rect));
diff --git a/third_party/blink/renderer/core/paint/scrollable_area_painter.h b/third_party/blink/renderer/core/paint/scrollable_area_painter.h
index d6c884a..86346c3 100644
--- a/third_party/blink/renderer/core/paint/scrollable_area_painter.h
+++ b/third_party/blink/renderer/core/paint/scrollable_area_painter.h
@@ -26,10 +26,7 @@
       PaintLayerScrollableArea& paint_layer_scrollable_area)
       : scrollable_area_(&paint_layer_scrollable_area) {}
 
-  void PaintOverflowControls(const PaintInfo&,
-                             const IntPoint& paint_offset,
-                             bool painting_overlay_controls);
-
+  void PaintOverflowControls(const PaintInfo&, const IntPoint& paint_offset);
   void PaintResizer(GraphicsContext&,
                     const IntPoint& paint_offset,
                     const CullRect&);
@@ -39,7 +36,6 @@
 
  private:
   void DrawPlatformResizerImage(GraphicsContext&, IntRect resizer_corner_rect);
-  bool OverflowControlsIntersectRect(const CullRect&) const;
 
   PaintLayerScrollableArea& GetScrollableArea() const;
   const DisplayItemClient& DisplayItemClientForCorner() const;
diff --git a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
index 79a3c44..47793ce7 100644
--- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
+++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
@@ -26,7 +26,9 @@
 #include <memory>
 
 #include "base/auto_reset.h"
+#include "base/feature_list.h"
 #include "third_party/blink/public/common/blob/blob_utils.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/renderer/bindings/core/v8/array_buffer_or_array_buffer_view_or_blob_or_document_or_string_or_form_data_or_url_search_params.h"
@@ -1099,9 +1101,6 @@
     resource_loader_options.data_buffering_policy = kDoNotBufferData;
   }
 
-  exception_code_ = DOMExceptionCode::kNoError;
-  error_ = false;
-
   if (async_) {
     UseCounter::Count(&execution_context,
                       WebFeature::kXMLHttpRequestAsynchronous);
@@ -1133,11 +1132,22 @@
         DEFINE_STATIC_LOCAL(EnumerationHistogram, syncxhr_pagedismissal_histogram,
                             ("XHR.Sync.PageDismissal", 5));
         syncxhr_pagedismissal_histogram.Count(pagedismissal);
+        // Disallow synchronous requests on page dismissal
+        if (base::FeatureList::IsEnabled(
+                features::kForbidSyncXHRInPageDismissal)) {
+          HandleNetworkError();
+          ThrowForLoadFailureIfNeeded(exception_state,
+                                      "Synchronous XHR in page dismissal.");
+          return;
+        }
       }
     }
     resource_loader_options.synchronous_policy = kRequestSynchronously;
   }
 
+  exception_code_ = DOMExceptionCode::kNoError;
+  error_ = false;
+
   loader_ = MakeGarbageCollected<ThreadableLoader>(execution_context, this,
                                                    resource_loader_options);
   loader_->SetTimeout(timeout_);
diff --git a/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js b/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js
index 7531b84..1f2aeb17 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js
@@ -2028,15 +2028,20 @@
 UI.createExpandableText = function(text, maxLength) {
   const fragment = createDocumentFragment();
   fragment.textContent = text.slice(0, maxLength);
-  const hiddenText = text.slice(maxLength);
-
-  const expandButton = fragment.createChild('span', 'expandable-inline-button');
-  expandButton.setAttribute('data-text', ls`Show ${Number.withThousandsSeparator(hiddenText.length)} more`);
-  expandButton.addEventListener('click', () => {
-    if (expandButton.parentElement)
-      expandButton.parentElement.insertBefore(createTextNode(hiddenText), expandButton);
-    expandButton.remove();
-  });
+  const expandElement = fragment.createChild('span');
+  const totalBytes = Number.bytesToString(2 * text.length);
+  if (text.length < 10000000) {
+    expandElement.setAttribute('data-text', ls`Show more (${totalBytes})`);
+    expandElement.classList.add('expandable-inline-button');
+    expandElement.addEventListener('click', () => {
+      if (expandElement.parentElement)
+        expandElement.parentElement.insertBefore(createTextNode(text.slice(maxLength)), expandElement);
+      expandElement.remove();
+    });
+  } else {
+    expandElement.setAttribute('data-text', ls`long text was truncated (${totalBytes})`);
+    expandElement.classList.add('undisplayable-text');
+  }
 
   const copyButton = fragment.createChild('span', 'expandable-inline-button');
   copyButton.setAttribute('data-text', ls`Copy`);
diff --git a/third_party/blink/renderer/devtools/front_end/ui/inspectorCommon.css b/third_party/blink/renderer/devtools/front_end/ui/inspectorCommon.css
index 1ab2d02..dc4baa36 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/inspectorCommon.css
+++ b/third_party/blink/renderer/devtools/front_end/ui/inspectorCommon.css
@@ -402,21 +402,31 @@
 
 .expandable-inline-button {
     background-color: #dedede;
-    padding: 2px 4px;
-    margin: 0 2px;
     color: #333;
     cursor: pointer;
     border-radius: 3px;
+}
+
+.undisplayable-text,
+.expandable-inline-button {
+    padding: 2px 4px;
+    margin: 0 2px;
     font-size: 12px;
     font-family: sans-serif;
     white-space: nowrap;
     display: inline-block;
 }
 
+.undisplayable-text::after,
 .expandable-inline-button::after {
     content: attr(data-text);
 }
 
+.undisplayable-text {
+    color: rgb(128, 128, 128);
+    font-style: italic;
+}
+
 .expandable-inline-button:hover {
     background-color: #d5d5d5;
 }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_media_controls.cc b/third_party/blink/renderer/modules/accessibility/ax_media_controls.cc
index e5f5f9a..d8d69f7b 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_media_controls.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_media_controls.cc
@@ -58,10 +58,6 @@
     case kMediaSlider:
       return AccessibilityMediaTimeline::Create(layout_object, ax_object_cache);
 
-    case kMediaVolumeSlider:
-      return AccessibilityMediaVolumeSlider::Create(layout_object,
-                                                    ax_object_cache);
-
     case kMediaCurrentTimeDisplay:
     case kMediaTimeRemainingDisplay:
       return AccessibilityMediaTimeDisplay::Create(layout_object,
@@ -98,6 +94,11 @@
     case kMediaAnimatedArrowContainer:
       return MakeGarbageCollected<AccessibilityMediaControl>(layout_object,
                                                              ax_object_cache);
+    // Removed as a part of the a11y tree rewrite https://crbug.com/836549.
+    case kMediaVolumeSlider:
+      NOTREACHED();
+      return MakeGarbageCollected<AccessibilityMediaControl>(layout_object,
+                                                             ax_object_cache);
   }
 
   NOTREACHED();
@@ -372,38 +373,6 @@
 }
 
 //
-// AccessibilityMediaVolumeSlider
-
-AccessibilityMediaVolumeSlider::AccessibilityMediaVolumeSlider(
-    LayoutObject* layout_object,
-    AXObjectCacheImpl& ax_object_cache)
-    : AXSlider(layout_object, ax_object_cache) {}
-
-AXObject* AccessibilityMediaVolumeSlider::Create(
-    LayoutObject* layout_object,
-    AXObjectCacheImpl& ax_object_cache) {
-  return MakeGarbageCollected<AccessibilityMediaVolumeSlider>(layout_object,
-                                                              ax_object_cache);
-}
-
-String AccessibilityMediaVolumeSlider::Description(
-    ax::mojom::NameFrom name_from,
-    ax::mojom::DescriptionFrom& description_from,
-    AXObjectVector* description_objects) const {
-  return QueryString(WebLocalizedString::kAXMediaVolumeSliderHelp);
-}
-
-bool AccessibilityMediaVolumeSlider::InternalSetAccessibilityFocusAction() {
-  MediaControlElementsHelper::NotifyMediaControlAccessibleFocus(GetElement());
-  return AXSlider::InternalSetAccessibilityFocusAction();
-}
-
-bool AccessibilityMediaVolumeSlider::InternalClearAccessibilityFocusAction() {
-  MediaControlElementsHelper::NotifyMediaControlAccessibleBlur(GetElement());
-  return AXSlider::InternalClearAccessibilityFocusAction();
-}
-
-//
 // AccessibilityMediaTimeDisplay
 
 AccessibilityMediaTimeDisplay::AccessibilityMediaTimeDisplay(
diff --git a/third_party/blink/renderer/modules/accessibility/ax_media_controls.h b/third_party/blink/renderer/modules/accessibility/ax_media_controls.h
index 5299069..ad984667 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_media_controls.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_media_controls.h
@@ -81,24 +81,6 @@
   DISALLOW_COPY_AND_ASSIGN(AccessibilityMediaTimeline);
 };
 
-class AccessibilityMediaVolumeSlider final : public AXSlider {
- public:
-  static AXObject* Create(LayoutObject*, AXObjectCacheImpl&);
-
-  AccessibilityMediaVolumeSlider(LayoutObject*, AXObjectCacheImpl&);
-  ~AccessibilityMediaVolumeSlider() override = default;
-
-  String Description(ax::mojom::NameFrom,
-                     ax::mojom::DescriptionFrom&,
-                     AXObjectVector* description_objects) const override;
-
-  bool InternalSetAccessibilityFocusAction() override;
-  bool InternalClearAccessibilityFocusAction() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(AccessibilityMediaVolumeSlider);
-};
-
 class AXMediaControlsContainer final : public AccessibilityMediaControl {
  public:
   static AXObject* Create(LayoutObject*, AXObjectCacheImpl&);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 3ec0f3f..8ceeb86 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -82,6 +82,7 @@
 #include "third_party/blink/renderer/modules/accessibility/ax_slider.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_svg_root.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_virtual_object.h"
+#include "third_party/blink/renderer/modules/media_controls/elements/media_control_elements_helper.h"
 #include "third_party/blink/renderer/modules/permissions/permission_utils.h"
 
 namespace blink {
@@ -311,8 +312,13 @@
     return AXList::Create(layout_object, *this);
 
   // media controls
-  if (node && node->IsMediaControlElement())
+  // TODO(https://crbug.com/836549): Remove for the rest of the controls.
+  // kMediaVolumeSlider has already been removed.
+  if (node && node->IsMediaControlElement() &&
+      MediaControlElementsHelper::GetMediaControlElementType(node) !=
+          kMediaVolumeSlider) {
     return AccessibilityMediaControl::Create(layout_object, *this);
+  }
 
   if (IsHTMLOptionElement(node))
     return AXListBoxOption::Create(layout_object, *this);
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_volume_slider_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_volume_slider_element.cc
index c5dc536c..220580d4 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_volume_slider_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_volume_slider_element.cc
@@ -20,6 +20,9 @@
     MediaControlsImpl& media_controls)
     : MediaControlSliderElement(media_controls, kMediaVolumeSlider) {
   setAttribute(html_names::kMaxAttr, "1");
+  setAttribute(html_names::kAriaValuemaxAttr, "100");
+  setAttribute(html_names::kAriaValueminAttr, "0");
+  setAttribute(html_names::kAriaLabelAttr, "volume");
   SetShadowPseudoId(AtomicString("-webkit-media-controls-volume-slider"));
   SetVolumeInternal(MediaElement().volume());
 
@@ -104,6 +107,9 @@
 void MediaControlVolumeSliderElement::SetVolumeInternal(double volume) {
   SetupBarSegments();
   SetAfterSegmentPosition(MediaControlSliderElement::Position(0, volume));
+  int percent_vol = 100 * volume;
+  setAttribute(html_names::kAriaValuenowAttr,
+               WTF::AtomicString::Number(percent_vol));
 }
 
 bool MediaControlVolumeSliderElement::KeepEventInNode(
diff --git a/third_party/blink/renderer/modules/payments/payment_request.idl b/third_party/blink/renderer/modules/payments/payment_request.idl
index db1d392..e2a1c83d 100644
--- a/third_party/blink/renderer/modules/payments/payment_request.idl
+++ b/third_party/blink/renderer/modules/payments/payment_request.idl
@@ -17,7 +17,7 @@
     [CallWith=ScriptState, NewObject] Promise<PaymentResponse> show();
     [CallWith=ScriptState, NewObject] Promise<void> abort();
     [CallWith=ScriptState, HighEntropy, Measure, NewObject] Promise<boolean> canMakePayment();
-    [CallWith=ScriptState, NewObject, RuntimeEnabled=PaymentRequestHasEnrolledInstrument] Promise<boolean> hasEnrolledInstrument();
+    [CallWith=ScriptState, HighEntropy, Measure, NewObject, RuntimeEnabled=PaymentRequestHasEnrolledInstrument] Promise<boolean> hasEnrolledInstrument();
 
     readonly attribute DOMString id;
     [ImplementedAs=getShippingAddress] readonly attribute PaymentAddress? shippingAddress;
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
index 6797fb98..5424377 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -614,4 +614,8 @@
   RuntimeEnabledFeatures::SetGetDisplayMediaEnabled(enable);
 }
 
+void WebRuntimeFeatures::EnableForbidSyncXHRInPageDismissal(bool enable) {
+  RuntimeEnabledFeatures::SetForbidSyncXHRInPageDismissalEnabled(enable);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h
index 46899a7..06ec28e 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h
@@ -12,7 +12,7 @@
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 #if DCHECK_IS_ON()
-#include "third_party/blink/renderer/platform/wtf/list_hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/linked_hash_set.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 #endif
 
@@ -208,7 +208,7 @@
     return node;
   }
 
-  ListHashSet<const NodeType*> nodes_;
+  LinkedHashSet<const NodeType*> nodes_;
 };
 
 template <typename NodeType>
diff --git a/third_party/blink/renderer/platform/network/http_parsers_fuzzer.cc b/third_party/blink/renderer/platform/network/http_parsers_fuzzer.cc
index ad0dbf8..0d5dcc9 100644
--- a/third_party/blink/renderer/platform/network/http_parsers_fuzzer.cc
+++ b/third_party/blink/renderer/platform/network/http_parsers_fuzzer.cc
@@ -28,12 +28,19 @@
   unsigned failure_position = 0;
 
   std::string terminated(reinterpret_cast<const char*>(data), size);
+
+  // There are no guarantees regarding the string capacity, but we are doing our
+  // best to make it |size + 1|.
+  terminated.shrink_to_fit();
+
   blink::IsValidHTTPToken(terminated.c_str());
   blink::ParseCacheControlDirectives(terminated.c_str(), AtomicString());
   blink::ParseCommaDelimitedHeader(terminated.c_str(), set);
   blink::ParseHTTPRefresh(terminated.c_str(), nullptr, delay, url);
-  blink::ParseMultipartHeadersFromBody(terminated.c_str(), terminated.size(),
-                                       &response, &end);
+
+  // Intentionally pass raw data as the API does not require trailing \0.
+  blink::ParseMultipartHeadersFromBody(reinterpret_cast<const char*>(data),
+                                       size, &response, &end);
   blink::ParseServerTimingHeader(terminated.c_str());
   blink::ParseContentTypeOptionsHeader(terminated.c_str());
   blink::ParseXSSProtectionHeader(terminated.c_str(), failure_reason,
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index eca0ba09..b746e09 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -519,6 +519,10 @@
     },
     // For simulating Android's overlay fullscreen video in web tests on Linux.
     {
+      name: "ForbidSyncXHRInPageDismissal",
+      status: "test",
+    },
+    {
       name: "ForceOverlayFullscreenVideo",
     },
     {
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 40034ff4..4e7e22b 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1047,7 +1047,6 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/client-rects.html [ Crash Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/client-rects-rtl.html [ Crash Failure ]
 crbug.com/874506 virtual/layout_ng_experimental/fast/multicol/column-break-with-balancing.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/column-clamping.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/column-count-with-rules.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/column-rules.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/columns-shorthand-parsing.html [ Failure ]
@@ -2728,6 +2727,8 @@
 # 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 ]
+
 # 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 ]
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/acquire-commit.html b/third_party/blink/web_tests/display-lock/lock-before-append/acquire-commit.html
index cf250d4..74ed187 100644
--- a/third_party/blink/web_tests/display-lock/lock-before-append/acquire-commit.html
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/acquire-commit.html
@@ -34,7 +34,7 @@
 
 function runTest() {
   let container = document.createElement("div");
-  container.getDisplayLock().acquire().then(() => {
+  container.getDisplayLock().acquire({ timeout: Infinity }).then(() => {
     let child = document.createElement("div");
     child.id = "child";
     container.appendChild(child);
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/acquire-infinite-timeout-no-commit.html b/third_party/blink/web_tests/display-lock/lock-before-append/acquire-infinite-timeout-no-commit.html
index 78068c1..87ec706 100644
--- a/third_party/blink/web_tests/display-lock/lock-before-append/acquire-infinite-timeout-no-commit.html
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/acquire-infinite-timeout-no-commit.html
@@ -1,7 +1,7 @@
 <!doctype HTML>
 
 <!--
-Runs an acquireDisplayLock, which suspends the context.
+Runs an acquire, with no commit.
 The associated promise should never resolve.
 -->
 
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/acquire-update-measure-remove.html b/third_party/blink/web_tests/display-lock/lock-before-append/acquire-update-measure-remove.html
index 604eb696..94fb15d 100644
--- a/third_party/blink/web_tests/display-lock/lock-before-append/acquire-update-measure-remove.html
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/acquire-update-measure-remove.html
@@ -67,7 +67,7 @@
 function runTest() {
   let container = document.createElement("div");
   container.id = "container";
-  container.getDisplayLock().acquire().then(() => {
+  container.getDisplayLock().acquire({ timeout: Infinity }).then(() => {
     construct(container);
     document.getElementById("empty").appendChild(container);
     container.getDisplayLock().update().then(measureAndRemove).then(finish);
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/acquire-update-remove-expected.html b/third_party/blink/web_tests/display-lock/lock-before-append/acquire-update-remove-expected.html
new file mode 100644
index 0000000..1333caeb
--- /dev/null
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/acquire-update-remove-expected.html
@@ -0,0 +1,4 @@
+<!doctype HTML>
+
+<div id="log">PASS</div>
+
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/acquire-update-remove.html b/third_party/blink/web_tests/display-lock/lock-before-append/acquire-update-remove.html
new file mode 100644
index 0000000..d7043a1
--- /dev/null
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/acquire-update-remove.html
@@ -0,0 +1,44 @@
+<!doctype HTML>
+
+<!--
+Runs an acquire(), then calls update() but disconnects the element before update finishes.
+-->
+
+<style>
+#container {
+  contain: content;
+  width: 100px;
+  height: 100px;
+  background: lightblue;
+}
+</style>
+
+<div id="log"></div>
+
+<script>
+// TODO(vmpstr): In WPT this needs to be replaced with reftest-wait.
+if (window.testRunner)
+  window.testRunner.waitUntilDone();
+
+function finishTest(status_string) {
+  if (document.getElementById("log").innerHTML === "")
+    document.getElementById("log").innerHTML = status_string;
+  if (window.testRunner)
+    window.testRunner.notifyDone();
+}
+
+function runTest() {
+  let container = document.createElement("div");
+  container.id = "container";
+
+  container.getDisplayLock().acquire({ timeout: Infinity }).then(() => {
+    document.body.appendChild(container);
+    container.getDisplayLock().update().then(
+      () => { finishTest("FAIL"); },
+      () => { finishTest("PASS"); });
+    container.remove();
+  });
+}
+
+window.onload = runTest;
+</script>
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/commit-on-added-containment.html b/third_party/blink/web_tests/display-lock/lock-before-append/commit-on-added-containment.html
index c588863..12e0c92 100644
--- a/third_party/blink/web_tests/display-lock/lock-before-append/commit-on-added-containment.html
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/commit-on-added-containment.html
@@ -29,7 +29,7 @@
 
 async function runTest() {
   let container = document.createElement("div");
-  await container.getDisplayLock().acquire();
+  await container.getDisplayLock().acquire({ timeout: Infinity });
 
   container.classList = "contained";
   document.body.appendChild(container);
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/commit-on-inline-fails.html b/third_party/blink/web_tests/display-lock/lock-before-append/commit-on-inline-fails.html
index cc777c5..54a7009 100644
--- a/third_party/blink/web_tests/display-lock/lock-before-append/commit-on-inline-fails.html
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/commit-on-inline-fails.html
@@ -28,7 +28,7 @@
 async function acquire() {
   let container = document.createElement("span");
   container.id = "container";
-  await container.getDisplayLock().acquire();
+  await container.getDisplayLock().acquire({ timeout: Infinity });
   document.body.appendChild(container);
   container.getDisplayLock().commit().then(
     () => { finishTest("FAIL"); },
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/commit-on-no-containment-fails.html b/third_party/blink/web_tests/display-lock/lock-before-append/commit-on-no-containment-fails.html
index f61e972..69eca2d8 100644
--- a/third_party/blink/web_tests/display-lock/lock-before-append/commit-on-no-containment-fails.html
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/commit-on-no-containment-fails.html
@@ -21,7 +21,7 @@
 
 async function acquire() {
   let container = document.createElement("div");
-  await container.getDisplayLock().acquire();
+  await container.getDisplayLock().acquire({ timeout: Infinity });
   document.body.appendChild(container);
   container.getDisplayLock().commit().then(
     () => { finishTest("FAIL"); },
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/commit-while-disconnected-expected.html b/third_party/blink/web_tests/display-lock/lock-before-append/commit-while-disconnected-expected.html
new file mode 100644
index 0000000..a2fa191
--- /dev/null
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/commit-while-disconnected-expected.html
@@ -0,0 +1,14 @@
+<!doctype HTML>
+
+<style>
+#container {
+  contain: content;
+  width: 100px;
+  height: 100px;
+  background: lightblue;
+}
+</style>
+
+<div id="log">PASS</div>
+<div id="container"></div>
+
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/commit-while-disconnected.html b/third_party/blink/web_tests/display-lock/lock-before-append/commit-while-disconnected.html
new file mode 100644
index 0000000..962f85de
--- /dev/null
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/commit-while-disconnected.html
@@ -0,0 +1,45 @@
+<!doctype HTML>
+
+<!--
+Runs an acquire(), then commits without connecting the element, which should just unlock and fail.
+-->
+
+<style>
+#container {
+  contain: content;
+  width: 100px;
+  height: 100px;
+  background: lightblue;
+}
+</style>
+
+<div id="log"></div>
+
+<script>
+// TODO(vmpstr): In WPT this needs to be replaced with reftest-wait.
+if (window.testRunner)
+  window.testRunner.waitUntilDone();
+
+function finishTest(status_string) {
+  if (document.getElementById("log").innerHTML === "")
+    document.getElementById("log").innerHTML = status_string;
+  if (window.testRunner)
+    window.testRunner.notifyDone();
+}
+
+function runTest() {
+  let container = document.createElement("div");
+  container.id = "container";
+
+  container.getDisplayLock().acquire({ timeout: Infinity }).then(() => {
+    container.getDisplayLock().commit().then(
+      () => { finishTest("FAIL"); },
+      () => {
+        document.body.appendChild(container);
+        finishTest("PASS");
+      });
+  });
+}
+
+window.onload = runTest;
+</script>
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/measure-forced-layout.html b/third_party/blink/web_tests/display-lock/lock-before-append/measure-forced-layout.html
index c6df5c9..ef6cb4e3 100644
--- a/third_party/blink/web_tests/display-lock/lock-before-append/measure-forced-layout.html
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/measure-forced-layout.html
@@ -74,7 +74,7 @@
   async function runTest() {
     let container = document.createElement("div");
     container.id = "container";
-    await container.getDisplayLock().acquire();
+    await container.getDisplayLock().acquire({ timeout: Infinity });
 
     document.getElementById("parent").appendChild(container);
     construct(container);
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/measure-updated-layout.html b/third_party/blink/web_tests/display-lock/lock-before-append/measure-updated-layout.html
index 67ad858..e5c2c7b 100644
--- a/third_party/blink/web_tests/display-lock/lock-before-append/measure-updated-layout.html
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/measure-updated-layout.html
@@ -74,7 +74,7 @@
   async function runTest() {
     let container = document.createElement("div");
     container.id = "container";
-    await container.getDisplayLock().acquire();
+    await container.getDisplayLock().acquire({ timeout: Infinity });
 
     document.getElementById("parent").appendChild(container);
     construct(container);
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/multiple-acquires-all-succeed.html b/third_party/blink/web_tests/display-lock/lock-before-append/multiple-acquires-all-succeed.html
index 9d9444ed2..eb47bb6 100644
--- a/third_party/blink/web_tests/display-lock/lock-before-append/multiple-acquires-all-succeed.html
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/multiple-acquires-all-succeed.html
@@ -35,10 +35,10 @@
 function runTest() {
   let container = document.createElement("div");
   Promise.all([
-    container.getDisplayLock().acquire(),
-    container.getDisplayLock().acquire(),
-    container.getDisplayLock().acquire(),
-    container.getDisplayLock().acquire()
+    container.getDisplayLock().acquire({ timeout: Infinity }),
+    container.getDisplayLock().acquire({ timeout: Infinity }),
+    container.getDisplayLock().acquire({ timeout: Infinity }),
+    container.getDisplayLock().acquire({ timeout: Infinity })
   ]).then(() => {
     let child = document.createElement("div");
     child.id = "child";
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/timeout-while-disconnected-expected.html b/third_party/blink/web_tests/display-lock/lock-before-append/timeout-while-disconnected-expected.html
new file mode 100644
index 0000000..09d5a52d4
--- /dev/null
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/timeout-while-disconnected-expected.html
@@ -0,0 +1,13 @@
+<!doctype HTML>
+
+<style>
+#container {
+  contain: content;
+  width: 100px;
+  height: 100px;
+  background: lightblue;
+}
+</style>
+<div id="log">PASS if container is visible</div>
+<div id="container"></div>
+
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/timeout-while-disconnected.html b/third_party/blink/web_tests/display-lock/lock-before-append/timeout-while-disconnected.html
new file mode 100644
index 0000000..6d9d2b93
--- /dev/null
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/timeout-while-disconnected.html
@@ -0,0 +1,43 @@
+<!doctype HTML>
+
+<!--
+Runs an acquire(), times out without ever connecting the element.
+-->
+
+<style>
+#container {
+  contain: content;
+  width: 100px;
+  height: 100px;
+  background: lightblue;
+}
+</style>
+
+<div id="log"></div>
+
+<script>
+// TODO(vmpstr): In WPT this needs to be replaced with reftest-wait.
+if (window.testRunner)
+  window.testRunner.waitUntilDone();
+
+function finishTest(status_string) {
+  if (document.getElementById("log").innerHTML === "")
+    document.getElementById("log").innerHTML = status_string;
+  if (window.testRunner)
+    window.testRunner.notifyDone();
+}
+
+function runTest() {
+  let container = document.createElement("div");
+  container.id = "container";
+
+  container.getDisplayLock().acquire({ timeout: 100 }).then(() => {
+    setTimeout(() => {
+      document.body.appendChild(container);
+      finishTest("PASS if container is visible");
+    }, 200);
+  });
+}
+
+window.onload = runTest;
+</script>
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/update-together-with-commit-both-succeed.html b/third_party/blink/web_tests/display-lock/lock-before-append/update-together-with-commit-both-succeed.html
index f2dcef7c..3b657b6 100644
--- a/third_party/blink/web_tests/display-lock/lock-before-append/update-together-with-commit-both-succeed.html
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/update-together-with-commit-both-succeed.html
@@ -34,7 +34,7 @@
 
 function runTest() {
   let container = document.createElement("div");
-  container.getDisplayLock().acquire().then(() => {
+  container.getDisplayLock().acquire({ timeout: Infinity }).then(() => {
     let child = document.createElement("div");
     child.id = "child";
     container.appendChild(child);
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/update-while-disconnected-expected.html b/third_party/blink/web_tests/display-lock/lock-before-append/update-while-disconnected-expected.html
new file mode 100644
index 0000000..1333caeb
--- /dev/null
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/update-while-disconnected-expected.html
@@ -0,0 +1,4 @@
+<!doctype HTML>
+
+<div id="log">PASS</div>
+
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/update-while-disconnected.html b/third_party/blink/web_tests/display-lock/lock-before-append/update-while-disconnected.html
new file mode 100644
index 0000000..9f0e4ed
--- /dev/null
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/update-while-disconnected.html
@@ -0,0 +1,33 @@
+<!doctype HTML>
+
+<!--
+Runs an acquire(), then updates without connecting the element.
+-->
+
+<div id="log"></div>
+
+<script>
+// TODO(vmpstr): In WPT this needs to be replaced with reftest-wait.
+if (window.testRunner)
+  window.testRunner.waitUntilDone();
+
+function finishTest(status_string) {
+  if (document.getElementById("log").innerHTML === "")
+    document.getElementById("log").innerHTML = status_string;
+  if (window.testRunner)
+    window.testRunner.notifyDone();
+}
+
+function runTest() {
+  let container = document.createElement("div");
+  container.id = "container";
+
+  container.getDisplayLock().acquire({ timeout: Infinity }).then(() => {
+    container.getDisplayLock().update().then(
+      () => { finishTest("FAIL"); },
+      () => { finishTest("PASS"); });
+  });
+}
+
+window.onload = runTest;
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/large-actual-column-count.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/large-actual-column-count.html
new file mode 100644
index 0000000..615e3f2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/large-actual-column-count.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-multicol-1/#pseudo-algorithm">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div id="container" style="overflow:hidden; columns:1; column-fill:auto; column-gap:0; width:100px; height:100px; background:red;">
+  <div style="height:300000px;"></div>
+  <div style="width:100px; height:100px; background:green;"></div>
+  <div style="height:123456px;"></div>
+</div>
+<script>
+  document.getElementById("container").scrollLeft = 300000;
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_pointerout_pen-manual.html b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_pointerout_pen.html
similarity index 85%
rename from third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_pointerout_pen-manual.html
rename to third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_pointerout_pen.html
index 5e38952..972f99d9 100644
--- a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_pointerout_pen-manual.html
+++ b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_pointerout_pen.html
@@ -6,6 +6,9 @@
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
         <script src="/resources/testharnessreport.js"></script>
+        <script src="/resources/testdriver.js"></script>
+        <script src="/resources/testdriver-actions.js"></script>
+        <script src="/resources/testdriver-vendor.js"></script>
         <!-- Additional helper script for common checks across event types -->
         <script type="text/javascript" src="pointerevent_support.js"></script>
     </head>
@@ -45,6 +48,13 @@
                         }, "you have to use pen for this test");
                     }
                 });
+
+                // Inject pen inputs.
+                new test_driver.Actions()
+                  .addPointer("pointer1", "pen")
+                  .pointerMove(0, 0, {origin: target0})
+                  .pointerMove(0, 0)
+                  .send();
             }
 
         </script>
diff --git a/third_party/blink/web_tests/external/wpt_automation/pointerevents/pointerevent_pointerout_pen-manual-automation.js b/third_party/blink/web_tests/external/wpt_automation/pointerevents/pointerevent_pointerout_pen-manual-automation.js
deleted file mode 100644
index d2fa8fc..0000000
--- a/third_party/blink/web_tests/external/wpt_automation/pointerevents/pointerevent_pointerout_pen-manual-automation.js
+++ /dev/null
@@ -1,8 +0,0 @@
-importAutomationScript('/pointerevents/pointerevent_common_input.js');
-
-function inject_input() {
-  return penMoveIntoTarget('#target0').then(function() {
-    penMoveToDocument();
-  });
-}
-
diff --git a/third_party/blink/web_tests/fast/multicol/column-clamping-expected.html b/third_party/blink/web_tests/fast/multicol/column-clamping-expected.html
deleted file mode 100644
index 703581c6..0000000
--- a/third_party/blink/web_tests/fast/multicol/column-clamping-expected.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE html>
-<!-- See crbug.com/808189 - this test assumes that we're not allowed
-     to use more than 10 columns if the column height is 10px. -->
-<p>There should be a hotpink square below.</p>
-<div style="width:100px; height:100px; background:hotpink;"></div>
diff --git a/third_party/blink/web_tests/fast/multicol/column-clamping.html b/third_party/blink/web_tests/fast/multicol/column-clamping.html
deleted file mode 100644
index aa2f7430..0000000
--- a/third_party/blink/web_tests/fast/multicol/column-clamping.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<!DOCTYPE html>
-<!-- See crbug.com/808189 - this test assumes that we're not allowed
-     to use more than 10 columns if the column height is 10px. -->
-<p>There should be a hotpink square below.</p>
-<div style="columns:3; column-gap:0; width:30px; height:10px;">
-  <div style="height:190px; background:hotpink;"></div>
-</div>
-<div style="width:90px; height:90px; background:hotpink;"></div>
diff --git a/third_party/blink/web_tests/paint/invalidation/scroll/destroy-overlay-scrollbar-expected.txt b/third_party/blink/web_tests/paint/invalidation/scroll/destroy-overlay-scrollbar-expected.txt
index 631ee54..0ba1c4a2 100644
--- a/third_party/blink/web_tests/paint/invalidation/scroll/destroy-overlay-scrollbar-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/scroll/destroy-overlay-scrollbar-expected.txt
@@ -18,7 +18,7 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "LayoutBlockFlow (positioned) DIV",
+          "object": "VerticalScrollbar",
           "rect": [193, 100, 7, 200],
           "reason": "chunk disappeared"
         }
diff --git a/third_party/crashpad/crashpad/third_party/cpp-httplib/cpp-httplib/httplib.h b/third_party/crashpad/crashpad/third_party/cpp-httplib/cpp-httplib/httplib.h
index a2a5c46..dadab1d 100644
--- a/third_party/crashpad/crashpad/third_party/cpp-httplib/cpp-httplib/httplib.h
+++ b/third_party/crashpad/crashpad/third_party/cpp-httplib/cpp-httplib/httplib.h
@@ -39,7 +39,6 @@
 #endif
 
 typedef SOCKET socket_t;
-constexpr socket_t kInvalidSocket = INVALID_SOCKET;
 #else
 #include <pthread.h>
 #include <unistd.h>
diff --git a/third_party/feed/README.chromium b/third_party/feed/README.chromium
index 8ec6138b..6bb040c 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: e2809cef2bf967cf572527e0944307fd2a9277d0
+Revision: 84617ef81ded68396acf495e8392266950972223
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/feed/java_sources.gni b/third_party/feed/java_sources.gni
index 7dd0774..0000d38 100644
--- a/third_party/feed/java_sources.gni
+++ b/third_party/feed/java_sources.gni
@@ -18,6 +18,7 @@
   "src/src/main/java/com/google/android/libraries/feed/api/knowncontent/ContentRemoval.java",
   "src/src/main/java/com/google/android/libraries/feed/api/knowncontent/KnownContentApi.java",
   "src/src/main/java/com/google/android/libraries/feed/api/lifecycle/AppLifecycleListener.java",
+  "src/src/main/java/com/google/android/libraries/feed/api/lifecycle/Resettable.java",
   "src/src/main/java/com/google/android/libraries/feed/api/modelprovider/FeatureChange.java",
   "src/src/main/java/com/google/android/libraries/feed/api/modelprovider/FeatureChangeObserver.java",
   "src/src/main/java/com/google/android/libraries/feed/api/modelprovider/ModelChild.java",
@@ -34,6 +35,7 @@
   "src/src/main/java/com/google/android/libraries/feed/api/modelprovider/TokenCompletedObserver.java",
   "src/src/main/java/com/google/android/libraries/feed/api/protocoladapter/ProtocolAdapter.java",
   "src/src/main/java/com/google/android/libraries/feed/api/requestmanager/RequestManager.java",
+  "src/src/main/java/com/google/android/libraries/feed/api/scope/ClearAllListener.java",
   "src/src/main/java/com/google/android/libraries/feed/api/scope/FeedProcessScope.java",
   "src/src/main/java/com/google/android/libraries/feed/api/scope/FeedStreamScope.java",
   "src/src/main/java/com/google/android/libraries/feed/api/sessionmanager/SessionManager.java",
@@ -64,12 +66,6 @@
   "src/src/main/java/com/google/android/libraries/feed/basicstream/internal/drivers/NoContentDriver.java",
   "src/src/main/java/com/google/android/libraries/feed/basicstream/internal/drivers/StreamDriver.java",
   "src/src/main/java/com/google/android/libraries/feed/basicstream/internal/drivers/ZeroStateDriver.java",
-  "src/src/main/java/com/google/android/libraries/feed/basicstream/internal/logging/LoggingListener.java",
-  "src/src/main/java/com/google/android/libraries/feed/basicstream/internal/logging/OneShotVisibilityLoggingListener.java",
-  "src/src/main/java/com/google/android/libraries/feed/basicstream/internal/logging/SpinnerLogger.java",
-  "src/src/main/java/com/google/android/libraries/feed/basicstream/internal/logging/StreamContentLoggingData.java",
-  "src/src/main/java/com/google/android/libraries/feed/basicstream/internal/logging/VisibilityListener.java",
-  "src/src/main/java/com/google/android/libraries/feed/basicstream/internal/logging/VisibilityMonitor.java",
   "src/src/main/java/com/google/android/libraries/feed/basicstream/internal/scroll/ScrollRestorer.java",
   "src/src/main/java/com/google/android/libraries/feed/basicstream/internal/viewholders/ContinuationViewHolder.java",
   "src/src/main/java/com/google/android/libraries/feed/basicstream/internal/viewholders/FeedViewHolder.java",
@@ -91,7 +87,6 @@
   "src/src/main/java/com/google/android/libraries/feed/common/functional/Consumer.java",
   "src/src/main/java/com/google/android/libraries/feed/common/functional/Function.java",
   "src/src/main/java/com/google/android/libraries/feed/common/functional/Predicate.java",
-  "src/src/main/java/com/google/android/libraries/feed/common/functional/SettableSupplier.java",
   "src/src/main/java/com/google/android/libraries/feed/common/functional/Supplier.java",
   "src/src/main/java/com/google/android/libraries/feed/common/locale/LocaleUtils.java",
   "src/src/main/java/com/google/android/libraries/feed/common/logging/Dumpable.java",
@@ -237,13 +232,18 @@
   "src/src/main/java/com/google/android/libraries/feed/piet/host/HostBindingProvider.java",
   "src/src/main/java/com/google/android/libraries/feed/piet/ui/AspectRatioScalingImageView.java",
   "src/src/main/java/com/google/android/libraries/feed/piet/ui/BorderDrawable.java",
-  "src/src/main/java/com/google/android/libraries/feed/piet/ui/DrawableScalingHelper.java",
   "src/src/main/java/com/google/android/libraries/feed/piet/ui/GridRowView.java",
   "src/src/main/java/com/google/android/libraries/feed/piet/ui/RoundedCornerViewHelper.java",
   "src/src/main/java/com/google/android/libraries/feed/piet/ui/RoundedCornerWrapperView.java",
   "src/src/main/java/com/google/android/libraries/feed/sharedstream/contentchanged/StreamContentChangedListener.java",
   "src/src/main/java/com/google/android/libraries/feed/sharedstream/contextmenumanager/ContextMenuManager.java",
   "src/src/main/java/com/google/android/libraries/feed/sharedstream/contextmenumanager/ContextMenuManagerImpl.java",
+  "src/src/main/java/com/google/android/libraries/feed/sharedstream/logging/LoggingListener.java",
+  "src/src/main/java/com/google/android/libraries/feed/sharedstream/logging/OneShotVisibilityLoggingListener.java",
+  "src/src/main/java/com/google/android/libraries/feed/sharedstream/logging/SpinnerLogger.java",
+  "src/src/main/java/com/google/android/libraries/feed/sharedstream/logging/StreamContentLoggingData.java",
+  "src/src/main/java/com/google/android/libraries/feed/sharedstream/logging/VisibilityListener.java",
+  "src/src/main/java/com/google/android/libraries/feed/sharedstream/logging/VisibilityMonitor.java",
   "src/src/main/java/com/google/android/libraries/feed/sharedstream/offlinemonitor/StreamOfflineMonitor.java",
   "src/src/main/java/com/google/android/libraries/feed/sharedstream/piet/PietAssetProvider.java",
   "src/src/main/java/com/google/android/libraries/feed/sharedstream/piet/PietCustomElementProvider.java",
@@ -258,6 +258,7 @@
 feed_conformance_test_lib_sources = [
   "src/src/main/java/com/google/android/libraries/feed/api/common/testing/ContentIdGenerators.java",
   "src/src/main/java/com/google/android/libraries/feed/api/common/testing/InternalProtocolBuilder.java",
+  "src/src/main/java/com/google/android/libraries/feed/common/concurrent/testing/FakeMainThreadRunner.java",
   "src/src/main/java/com/google/android/libraries/feed/common/testing/FakeClock.java",
   "src/src/main/java/com/google/android/libraries/feed/common/testing/FakeRequestManager.java",
   "src/src/main/java/com/google/android/libraries/feed/common/testing/InfrastructureIntegrationScope.java",
diff --git a/third_party/feed/update_java_sources.sh b/third_party/feed/update_java_sources.sh
index 11e3a6f..ffc39cd 100755
--- a/third_party/feed/update_java_sources.sh
+++ b/third_party/feed/update_java_sources.sh
@@ -24,7 +24,7 @@
 " >> java_sources.gni
 
 echo "feed_conformance_test_lib_sources = [" >> java_sources.gni
-find src/src/main/java/com/google/android/libraries/feed -wholename "*/common/testing/*.java" |\
+find src/src/main/java/com/google/android/libraries/feed -regex ".*/common/\(.*/\)*testing/.*\.java" |\
     env LC_COLLATE=en_US.ASCII sort | sed 's/^\(.*\)$/  "\1",/g' >> java_sources.gni
 find src/src/main/java/com/google/android/libraries/feed/testing/conformance -wholename "*.java" |\
     env LC_COLLATE=en_US.ASCII sort | sed 's/^\(.*\)$/  "\1",/g' >> java_sources.gni
diff --git a/third_party/qcms/qcms_color_space_fuzzer.cc b/third_party/qcms/qcms_color_space_fuzzer.cc
index be08c88..106149d 100644
--- a/third_party/qcms/qcms_color_space_fuzzer.cc
+++ b/third_party/qcms/qcms_color_space_fuzzer.cc
@@ -7,6 +7,7 @@
 #include <random>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "testing/libfuzzer/fuzzers/color_space_data.h"
 #include "third_party/qcms/src/qcms.h"
 
@@ -18,7 +19,7 @@
   static std::uniform_int_distribution<uint32_t> uniform(0u, ~0u);
 
   std::mt19937_64 random(hash);
-  for (size_t i = 0; i < arraysize(pixels); ++i)
+  for (size_t i = 0; i < base::size(pixels); ++i)
     pixels[i] = uniform(random);
 }
 
@@ -46,9 +47,9 @@
 
 static qcms_profile* SelectProfile(size_t hash) {
   static qcms_profile* profiles[4] = {
-      qcms_profile_from_memory(kSRGBData, arraysize(kSRGBData)),
-      qcms_profile_from_memory(kSRGBPara, arraysize(kSRGBPara)),
-      qcms_profile_from_memory(kAdobeData, arraysize(kAdobeData)),
+      qcms_profile_from_memory(kSRGBData, base::size(kSRGBData)),
+      qcms_profile_from_memory(kSRGBPara, base::size(kSRGBPara)),
+      qcms_profile_from_memory(kAdobeData, base::size(kAdobeData)),
       qcms_profile_sRGB(),
   };
 
diff --git a/third_party/zlib/google/compression_utils_unittest.cc b/third_party/zlib/google/compression_utils_unittest.cc
index adcb773..942b7c03 100644
--- a/third_party/zlib/google/compression_utils_unittest.cc
+++ b/third_party/zlib/google/compression_utils_unittest.cc
@@ -10,7 +10,7 @@
 #include <string>
 
 #include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace compression {
@@ -34,24 +34,24 @@
 }  // namespace
 
 TEST(CompressionUtilsTest, GzipCompression) {
-  std::string data(reinterpret_cast<const char*>(kData), arraysize(kData));
+  std::string data(reinterpret_cast<const char*>(kData), base::size(kData));
   std::string compressed_data;
   EXPECT_TRUE(GzipCompress(data, &compressed_data));
   std::string golden_compressed_data(
       reinterpret_cast<const char*>(kCompressedData),
-      arraysize(kCompressedData));
+      base::size(kCompressedData));
   EXPECT_EQ(golden_compressed_data, compressed_data);
 }
 
 TEST(CompressionUtilsTest, GzipUncompression) {
   std::string compressed_data(reinterpret_cast<const char*>(kCompressedData),
-                              arraysize(kCompressedData));
+                              base::size(kCompressedData));
 
   std::string uncompressed_data;
   EXPECT_TRUE(GzipUncompress(compressed_data, &uncompressed_data));
 
   std::string golden_data(reinterpret_cast<const char*>(kData),
-                          arraysize(kData));
+                          base::size(kData));
   EXPECT_EQ(golden_data, uncompressed_data);
 }
 
@@ -76,10 +76,10 @@
 
 TEST(CompressionUtilsTest, InPlace) {
   const std::string original_data(reinterpret_cast<const char*>(kData),
-                                  arraysize(kData));
+                                  base::size(kData));
   const std::string golden_compressed_data(
       reinterpret_cast<const char*>(kCompressedData),
-      arraysize(kCompressedData));
+      base::size(kCompressedData));
 
   std::string data(original_data);
   EXPECT_TRUE(GzipCompress(data, &data));
diff --git a/tools/android/roll/android_deps/README.md b/tools/android/roll/android_deps/README.md
index fe37c23..1ba9a7b 100644
--- a/tools/android/roll/android_deps/README.md
+++ b/tools/android/roll/android_deps/README.md
@@ -38,8 +38,8 @@
       variable in `BuildConfigGenerator.groovy` in order to ensure that each
       tag in CIPD is unique.
       - One option to thoroughly test your change is to run
-        `rm -rf third_party/android_deps/libs/[!O]*` before running with
-        `--update-all`. This will ensure all your deps are fresh. The commands
+        `rm -rf third_party/android_deps/libs/[!O]* && tools/android/roll/android_deps/fetch_all.py --update-all`.
+        This will ensure all your deps are fresh. The commands
         printed out in the following step will ensure you do not upload
         duplicate instances.
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 56ad330..e25e761b 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -3502,6 +3502,7 @@
   <int value="15" label="Component update task"/>
   <int value="16" label="Deprecated Explore Sites refresh task"/>
   <int value="17" label="Explore Sites refresh task"/>
+  <int value="18" label="Download auto-resumption task"/>
 </enum>
 
 <enum name="BackgroundTracingState">
@@ -21145,6 +21146,7 @@
   <int value="2718" label="V8HTMLMediaElement_CanPlayType_Method"/>
   <int value="2719" label="HistoryLength"/>
   <int value="2720" label="FeaturePolicyReportOnlyHeader"/>
+  <int value="2721" label="V8PaymentRequest_HasEnrolledInstrument_Method"/>
 </enum>
 
 <enum name="FeaturePolicyFeature">
@@ -31367,6 +31369,7 @@
   <int value="105046382" label="ParallelDownloading:disabled"/>
   <int value="106840653" label="mus"/>
   <int value="107900612" label="ChromeHomePersistentIph:disabled"/>
+  <int value="109577361" label="ForbidSyncXHRInPageDismissal:enabled"/>
   <int value="115915570"
       label="OmniboxUIExperimentHideSteadyStateUrlPathQueryAndRef:enabled"/>
   <int value="118991027" label="enable-accelerated-fixed-root-background"/>
@@ -31980,6 +31983,7 @@
   <int value="1237297772" label="no-pings"/>
   <int value="1240073971" label="ash-disable-smooth-screen-rotation"/>
   <int value="1242632259" label="ContentSuggestionsCategoryOrder:disabled"/>
+  <int value="1243890754" label="ForbidSyncXHRInPageDismissal:disabled"/>
   <int value="1245889469" label="enable-surface-worker"/>
   <int value="1247293682" label="topchrome-md"/>
   <int value="1250071868" label="disable-timezone-tracking-option"/>
@@ -32402,6 +32406,7 @@
   <int value="1971964569" label="NewEncodeCpuLoadEstimator:disabled"/>
   <int value="1972232935" label="DisplayMoveWindowAccels:enabled"/>
   <int value="1972720114" label="WebPaymentsJustInTimePaymentApp:enabled"/>
+  <int value="1974565950" label="enable-experimental-accessibility-labels"/>
   <int value="1979222611" label="XRSandbox:disabled"/>
   <int value="1980011075" label="debug-packed-apps"/>
   <int value="1980648371" label="PointerEventV1SpecCapturing:enabled"/>
@@ -42923,7 +42928,7 @@
   <int value="0" label="Path suffix"/>
   <int value="1" label="Navigation to Previews Domain"/>
   <int value="2" label="Navigation to Private Domain"/>
-  <int value="3" label="The requested host was blacklisted by the server"/>
+  <int value="3" label="Host was blacklisted in a previous server request"/>
 </enum>
 
 <enum name="PreviewsServerLitePageIneligibleReason">
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 5a01aac..840526f2 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -164,6 +164,7 @@
  <item id="oauth2_mint_token_flow" hash_code="1112842" type="1" second_id="29188932" content_hash_code="91581432" os_list="linux,windows" semantics_fields="1,2,3,4,5" policy_fields="3,4" file_path="google_apis/gaia/oauth2_mint_token_flow.cc"/>
  <item id="ocsp_start_url_request" hash_code="60921996" type="0" content_hash_code="24127780" os_list="linux" file_path="net/cert_net/nss_ocsp.cc"/>
  <item id="offline_prefetch" hash_code="19185953" type="0" content_hash_code="112039446" os_list="linux,windows" file_path="components/offline_pages/core/prefetch/prefetch_request_fetcher.cc"/>
+ <item id="omnibox_debug_results_change" hash_code="71252052" type="0" content_hash_code="88668874" os_list="linux,windows" file_path="chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc"/>
  <item id="omnibox_documentsuggest" hash_code="6055066" type="0" content_hash_code="126973249" os_list="linux,windows" file_path="components/omnibox/browser/document_suggestions_service.cc"/>
  <item id="omnibox_navigation_observer" hash_code="61684939" type="0" content_hash_code="70941231" os_list="linux,windows" file_path="chrome/browser/ui/omnibox/chrome_omnibox_navigation_observer.cc"/>
  <item id="omnibox_prefetch_image" hash_code="109200878" type="0" content_hash_code="107906693" os_list="linux,windows" file_path="chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc"/>
@@ -207,8 +208,8 @@
  <item id="proxy_config_system" hash_code="11258689" type="0" content_hash_code="77057929" os_list="linux,windows" file_path="net/proxy_resolution/proxy_resolution_service.cc"/>
  <item id="proxy_script_fetcher" hash_code="37531401" type="0" deprecated="2018-03-16" content_hash_code="31866133" file_path=""/>
  <item id="puch_client_channel" hash_code="34459548" type="0" content_hash_code="92475475" os_list="linux,windows" file_path="components/invalidation/impl/push_client_channel.cc"/>
- <item id="quic_chromium_incoming_session" hash_code="87635401" type="0" content_hash_code="78573093" os_list="linux,windows" file_path="net/quic/quic_chromium_client_session.cc"/>
  <item id="quic_chromium_incoming_pending_session" hash_code="120830730" type="0" content_hash_code="52904665" os_list="linux,windows" file_path="net/quic/quic_chromium_client_session.cc"/>
+ <item id="quic_chromium_incoming_session" hash_code="87635401" type="0" content_hash_code="78573093" os_list="linux,windows" file_path="net/quic/quic_chromium_client_session.cc"/>
  <item id="quic_chromium_packet_writer" hash_code="20153177" type="0" content_hash_code="29657765" os_list="linux,windows" file_path="net/quic/quic_chromium_packet_writer.cc"/>
  <item id="ranker_url_fetcher" hash_code="95682324" type="0" content_hash_code="45958626" os_list="linux,windows" file_path="components/assist_ranker/ranker_url_fetcher.cc"/>
  <item id="rappor_report" hash_code="44606780" type="0" content_hash_code="111287826" os_list="linux,windows" file_path="components/rappor/log_uploader.cc"/>
diff --git a/ui/accessibility/accessibility_switches.cc b/ui/accessibility/accessibility_switches.cc
index 5428cb2..06315d994 100644
--- a/ui/accessibility/accessibility_switches.cc
+++ b/ui/accessibility/accessibility_switches.cc
@@ -17,6 +17,10 @@
 const char kEnableExperimentalAccessibilityAutoclick[] =
     "enable-experimental-accessibility-autoclick";
 
+// Enables additional image label features that haven't launched yet.
+const char kEnableExperimentalAccessibilityLabels[] =
+    "enable-experimental-accessibility-labels";
+
 // Shows setting to enable Switch Access before it has launched.
 const char kEnableExperimentalAccessibilitySwitchAccess[] =
     "enable-experimental-accessibility-switch-access";
diff --git a/ui/accessibility/accessibility_switches.h b/ui/accessibility/accessibility_switches.h
index 70588cd..31e05d9 100644
--- a/ui/accessibility/accessibility_switches.h
+++ b/ui/accessibility/accessibility_switches.h
@@ -12,6 +12,7 @@
 
 AX_EXPORT extern const char kEnableExperimentalAccessibilityFeatures[];
 AX_EXPORT extern const char kEnableExperimentalAccessibilityAutoclick[];
+AX_EXPORT extern const char kEnableExperimentalAccessibilityLabels[];
 AX_EXPORT extern const char kEnableExperimentalAccessibilitySwitchAccess[];
 
 // Returns true if experimental accessibility features are enabled.
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 8678d04..b0d271f 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -47,11 +47,7 @@
 
   defines = [ "UI_DATA_PACK_IMPLEMENTATION" ]
 
-  configs += [
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    "//build/config/compiler:no_size_t_to_int_warning",
-    "//build/config/compiler:wexit_time_destructors",
-  ]
+  configs += [ "//build/config/compiler:wexit_time_destructors" ]
 }
 
 buildflag_header("ui_features") {
@@ -406,11 +402,7 @@
     ]
   }
 
-  configs += [
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    "//build/config/compiler:no_size_t_to_int_warning",
-    "//build/config/compiler:wexit_time_destructors",
-  ]
+  configs += [ "//build/config/compiler:wexit_time_destructors" ]
 
   defines = [ "UI_BASE_IMPLEMENTATION" ]
 
@@ -947,9 +939,6 @@
     }
   }
 
-  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
-
   deps = [
     ":ui_base_test_resources_grit",
     ":ui_base_unittests_bundle_data",
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc
index fe87e8fb..4d083be 100644
--- a/ui/base/ui_base_features.cc
+++ b/ui/base/ui_base_features.cc
@@ -138,8 +138,13 @@
 const base::Feature kMashOopViz = {"MashOopViz",
                                    base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Runs the window service in-process. Launch bug https://crbug.com/909816
 const base::Feature kSingleProcessMash = {"SingleProcessMash",
+#if defined(OS_CHROMEOS)
+                                          base::FEATURE_ENABLED_BY_DEFAULT};
+#else
                                           base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
 
 bool IsUsingWindowService() {
   return IsSingleProcessMash() || IsMultiProcessMash();
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index a983afc..f8f5508 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -226,9 +226,6 @@
 
   configs += [
     "//build/config:precompiled_headers",
-
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    "//build/config/compiler:no_size_t_to_int_warning",
     "//build/config/compiler:wexit_time_destructors",
   ]
 
@@ -691,9 +688,6 @@
     sources += [ "paint_vector_icon_unittest.cc" ]
   }
 
-  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
-
   deps = [
     ":gfx",
     ":test_support",
diff --git a/ui/gfx/platform_font_skia.cc b/ui/gfx/platform_font_skia.cc
index 81a77ad..b8910fe 100644
--- a/ui/gfx/platform_font_skia.cc
+++ b/ui/gfx/platform_font_skia.cc
@@ -13,8 +13,8 @@
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
+#include "third_party/skia/include/core/SkFont.h"
 #include "third_party/skia/include/core/SkFontStyle.h"
-#include "third_party/skia/include/core/SkPaint.h"
 #include "third_party/skia/include/core/SkString.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/font.h"
@@ -306,18 +306,14 @@
   if (metrics_need_computation_) {
     metrics_need_computation_ = false;
 
-    SkPaint paint;
-    paint.setAntiAlias(false);
-    paint.setSubpixelText(false);
-    paint.setTextSize(font_size_pixels_);
-    paint.setTypeface(typeface_);
-    paint.setFakeBoldText(weight_ >= Font::Weight::BOLD &&
-                          !typeface_->isBold());
-    paint.setTextSkewX((Font::ITALIC & style_) && !typeface_->isItalic()
-                           ? -SK_Scalar1 / 4
-                           : 0);
+    SkFont font(typeface_, font_size_pixels_);
+    font.setEdging(SkFont::Edging::kAlias);
+    font.setEmbolden(weight_ >= Font::Weight::BOLD && !typeface_->isBold());
+    font.setSkewX((Font::ITALIC & style_) && !typeface_->isItalic()
+                      ? -SK_Scalar1 / 4
+                      : 0);
     SkFontMetrics metrics;
-    paint.getFontMetrics(&metrics);
+    font.getMetrics(&metrics);
     ascent_pixels_ = SkScalarCeilToInt(-metrics.fAscent);
     height_pixels_ = ascent_pixels_ + SkScalarCeilToInt(metrics.fDescent);
     cap_height_pixels_ = SkScalarCeilToInt(metrics.fCapHeight);
diff --git a/ui/gfx/platform_font_win.cc b/ui/gfx/platform_font_win.cc
index 0680cde..a904ec3 100644
--- a/ui/gfx/platform_font_win.cc
+++ b/ui/gfx/platform_font_win.cc
@@ -673,21 +673,20 @@
       FontRenderParams::SubpixelRenderingToSkiaLCDOrientation(
           font_params.subpixel_rendering));
 
-  SkPaint paint;
-  paint.setAntiAlias(font_params.antialiasing);
-  paint.setTypeface(std::move(skia_face));
-  paint.setTextSize(-font_info.lfHeight);
+  SkFont font(std::move(skia_face), -font_info.lfHeight);
+  font.setEdging(font_params.antialiasing ? SkFont::Edging::kAntiAlias
+                                          : SkFont::Edging::kAlias);
   SkFontMetrics skia_metrics;
-  paint.getFontMetrics(&skia_metrics);
+  font.getMetrics(&skia_metrics);
 
   // The calculations below are similar to those in the CreateHFontRef
   // function. The height, baseline and cap height are rounded up to ensure
   // that they match up closely with GDI.
   const int height = std::ceil(skia_metrics.fDescent - skia_metrics.fAscent);
   const int baseline = std::max<int>(1, std::ceil(-skia_metrics.fAscent));
-  const int cap_height = std::ceil(paint.getTextSize() *
-      static_cast<double>(dwrite_font_metrics.capHeight) /
-          dwrite_font_metrics.designUnitsPerEm);
+  const int cap_height = std::ceil(
+      font.getSize() * static_cast<double>(dwrite_font_metrics.capHeight) /
+      dwrite_font_metrics.designUnitsPerEm);
 
   // The metrics retrieved from skia don't have the average character width. In
   // any case if we get the average character width from skia then use that or
diff --git a/ui/gfx/render_text_harfbuzz.cc b/ui/gfx/render_text_harfbuzz.cc
index 849a1a8..926b9e8c 100644
--- a/ui/gfx/render_text_harfbuzz.cc
+++ b/ui/gfx/render_text_harfbuzz.cc
@@ -490,12 +490,12 @@
     line->segments.push_back(segment);
     line->size.set_width(line->size.width() + segment.width());
 
-    SkPaint paint;
-    paint.setTypeface(run.font_params.skia_face);
-    paint.setTextSize(SkIntToScalar(run.font_params.font_size));
-    paint.setAntiAlias(run.font_params.render_params.antialiasing);
+    SkFont font(run.font_params.skia_face, run.font_params.font_size);
+    font.setEdging(run.font_params.render_params.antialiasing
+                       ? SkFont::Edging::kAntiAlias
+                       : SkFont::Edging::kAlias);
     SkFontMetrics metrics;
-    paint.getFontMetrics(&metrics);
+    font.getMetrics(&metrics);
 
     // max_descent_ is y-down, fDescent is y-down, baseline_offset is y-down
     max_descent_ = std::max(max_descent_,
diff --git a/ui/gl/gl_image_ahardwarebuffer.cc b/ui/gl/gl_image_ahardwarebuffer.cc
index 7e709d18..afee6bbe 100644
--- a/ui/gl/gl_image_ahardwarebuffer.cc
+++ b/ui/gl/gl_image_ahardwarebuffer.cc
@@ -27,7 +27,7 @@
     case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
       return GL_RGB;
     default:
-      NOTREACHED();
+      // For all other buffer formats, use GL_RGBA as internal format.
       return GL_RGBA;
   }
 }
diff --git a/ui/message_center/BUILD.gn b/ui/message_center/BUILD.gn
index ee5189f..ba0038b3 100644
--- a/ui/message_center/BUILD.gn
+++ b/ui/message_center/BUILD.gn
@@ -55,12 +55,7 @@
       "//ui/resources",
     ]
 
-    configs += [
-      "//build/config:precompiled_headers",
-
-      # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-      "//build/config/compiler:no_size_t_to_int_warning",
-    ]
+    configs += [ "//build/config:precompiled_headers" ]
     sources = [
       "lock_screen/empty_lock_screen_controller.cc",
       "lock_screen/empty_lock_screen_controller.h",
diff --git a/ui/native_theme/common_theme.cc b/ui/native_theme/common_theme.cc
index 9c42ce6..4520663 100644
--- a/ui/native_theme/common_theme.cc
+++ b/ui/native_theme/common_theme.cc
@@ -54,6 +54,9 @@
         return SkColorSetA(gfx::kGoogleGrey800, 0xCC);
       case NativeTheme::kColorId_TextfieldDefaultBackground:
         return SkColorSetA(SK_ColorBLACK, 0x4D);
+      case NativeTheme::kColorId_LinkEnabled:
+      case NativeTheme::kColorId_LinkPressed:
+        return gfx::kGoogleBlue300;
 
       default:
         break;
diff --git a/ui/ozone/public/surface_ozone_canvas.h b/ui/ozone/public/surface_ozone_canvas.h
index 65f6de4..05577a9 100644
--- a/ui/ozone/public/surface_ozone_canvas.h
+++ b/ui/ozone/public/surface_ozone_canvas.h
@@ -32,13 +32,13 @@
   virtual sk_sp<SkSurface> GetSurface() = 0;
 
   // Attempts to resize the canvas to match the viewport size. After
-  // resizing, the compositor must call GetCanvas() to get the next
-  // canvas - this invalidates any previous canvas from GetCanvas().
+  // resizing, the compositor must call GetSurface() to get the next
+  // surface - this invalidates any previous surface from GetSurface().
   virtual void ResizeCanvas(const gfx::Size& viewport_size) = 0;
 
-  // Present the current canvas. After presenting, the compositor must
-  // call GetCanvas() to get the next canvas - this invalidates any
-  // previous canvas from GetCanvas().
+  // Present the current surface. After presenting, the compositor must
+  // call GetSurface() to get the next surface - this invalidates any
+  // previous surface from GetSurface().
   //
   // The implementation may assume that any pixels outside the damage
   // rectangle are unchanged since the previous call to PresentCanvas().
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 714a80c..868136f 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -490,14 +490,7 @@
   ]
 
   sources += get_target_outputs(":views_vector_icons")
-
-  configs += [
-    "//build/config:precompiled_headers",
-
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    "//build/config/compiler:no_size_t_to_int_warning",
-  ]
-
+  configs += [ "//build/config:precompiled_headers" ]
   defines = [ "VIEWS_IMPLEMENTATION" ]
 
   deps = [
diff --git a/ui/views/bubble/bubble_frame_view.cc b/ui/views/bubble/bubble_frame_view.cc
index 95115bb..d6f764e0 100644
--- a/ui/views/bubble/bubble_frame_view.cc
+++ b/ui/views/bubble/bubble_frame_view.cc
@@ -88,7 +88,7 @@
   default_title_->SetVisible(false);
   AddChildView(default_title_);
 
-  close_ = CreateCloseButton(this);
+  close_ = CreateCloseButton(this, GetNativeTheme()->SystemDarkModeEnabled());
   close_->SetVisible(false);
 #if defined(OS_WIN)
   // Windows will automatically create a tooltip for the close button based on
@@ -111,10 +111,13 @@
 }
 
 // static
-Button* BubbleFrameView::CreateCloseButton(ButtonListener* listener) {
+Button* BubbleFrameView::CreateCloseButton(ButtonListener* listener,
+                                           bool is_dark_mode) {
   ImageButton* close_button = nullptr;
   close_button = CreateVectorImageButton(listener);
-  SetImageFromVectorIcon(close_button, vector_icons::kCloseRoundedIcon);
+  SetImageFromVectorIconWithColor(
+      close_button, vector_icons::kCloseRoundedIcon,
+      is_dark_mode ? SkColorSetA(SK_ColorWHITE, 0xDD) : gfx::kGoogleGrey700);
   close_button->SetTooltipText(l10n_util::GetStringUTF16(IDS_APP_CLOSE));
   close_button->SizeToPreferredSize();
 
diff --git a/ui/views/bubble/bubble_frame_view.h b/ui/views/bubble/bubble_frame_view.h
index ca1a22c..8fe9fd5e 100644
--- a/ui/views/bubble/bubble_frame_view.h
+++ b/ui/views/bubble/bubble_frame_view.h
@@ -38,7 +38,7 @@
       const base::string16& title_text);
 
   // Creates a close button used in the corner of the dialog.
-  static Button* CreateCloseButton(ButtonListener* listener);
+  static Button* CreateCloseButton(ButtonListener* listener, bool is_dark_mode);
 
   // NonClientFrameView:
   gfx::Rect GetBoundsForClientView() const override;
diff --git a/ui/views/cocoa/bridged_native_widget_host_impl.h b/ui/views/cocoa/bridged_native_widget_host_impl.h
index 45f5b4a..2bbec19 100644
--- a/ui/views/cocoa/bridged_native_widget_host_impl.h
+++ b/ui/views/cocoa/bridged_native_widget_host_impl.h
@@ -228,7 +228,6 @@
                  bool* found_word,
                  gfx::DecoratedText* decorated_word,
                  gfx::Point* baseline_point) override;
-  double SheetPositionY() override;
   views_bridge_mac::DragDropClient* GetDragDropClient() override;
   ui::TextInputClient* GetTextInputClient() override;
   void GetHasInputContext(bool* has_text_input_context) override;
@@ -240,6 +239,7 @@
   void OnVisibilityChanged(bool visible) override;
   void OnWindowNativeThemeChanged() override;
   void OnViewSizeChanged(const gfx::Size& new_size) override;
+  bool GetSheetOffsetY(int32_t* offset_y) override;
   void SetKeyboardAccessible(bool enabled) override;
   void OnIsFirstResponderChanged(bool is_first_responder) override;
   void OnMouseCaptureActiveChanged(bool capture_is_active) override;
@@ -303,6 +303,7 @@
                          bool* was_handled) override;
 
   // views_bridge_mac::mojom::BridgedNativeWidgetHost, synchronous callbacks:
+  void GetSheetOffsetY(GetSheetOffsetYCallback callback) override;
   void DispatchKeyEventRemote(std::unique_ptr<ui::Event> event,
                               DispatchKeyEventRemoteCallback callback) override;
   void DispatchKeyEventToMenuControllerRemote(
diff --git a/ui/views/cocoa/bridged_native_widget_host_impl.mm b/ui/views/cocoa/bridged_native_widget_host_impl.mm
index 4319ee1..d8e8e14 100644
--- a/ui/views/cocoa/bridged_native_widget_host_impl.mm
+++ b/ui/views/cocoa/bridged_native_widget_host_impl.mm
@@ -519,10 +519,6 @@
   return false;
 }
 
-double BridgedNativeWidgetHostImpl::SheetPositionY() {
-  return native_widget_mac_->SheetPositionY();
-}
-
 views_bridge_mac::DragDropClient*
 BridgedNativeWidgetHostImpl::GetDragDropClient() {
   return drag_drop_client_.get();
@@ -619,6 +615,11 @@
   root_view_->SetSize(new_size);
 }
 
+bool BridgedNativeWidgetHostImpl::GetSheetOffsetY(int32_t* offset_y) {
+  *offset_y = native_widget_mac_->SheetOffsetY();
+  return true;
+}
+
 void BridgedNativeWidgetHostImpl::SetKeyboardAccessible(bool enabled) {
   views::FocusManager* focus_manager =
       root_view_->GetWidget()->GetFocusManager();
@@ -973,6 +974,13 @@
 // BridgedNativeWidgetHostImpl,
 // views_bridge_mac::mojom::BridgedNativeWidgetHost synchronous callbacks:
 
+void BridgedNativeWidgetHostImpl::GetSheetOffsetY(
+    GetSheetOffsetYCallback callback) {
+  int32_t offset_y = 0;
+  GetSheetOffsetY(&offset_y);
+  std::move(callback).Run(offset_y);
+}
+
 void BridgedNativeWidgetHostImpl::DispatchKeyEventRemote(
     std::unique_ptr<ui::Event> event,
     DispatchKeyEventRemoteCallback callback) {
diff --git a/ui/views/widget/native_widget_mac.h b/ui/views/widget/native_widget_mac.h
index 6201956..cf1d7436 100644
--- a/ui/views/widget/native_widget_mac.h
+++ b/ui/views/widget/native_widget_mac.h
@@ -50,9 +50,9 @@
   // destroyed.
   void WindowDestroyed();
 
-  // Returns the vertical position that sheets should be anchored, in pixels
-  // from the bottom of the window.
-  virtual int SheetPositionY();
+  // The vertical position from which sheets should be anchored, from the top
+  // of the content view.
+  virtual int32_t SheetOffsetY();
 
   // Returns in |override_titlebar_height| whether or not to override the
   // titlebar height and in |titlebar_height| the height of the titlebar.
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm
index 9adc67f..d93c719 100644
--- a/ui/views/widget/native_widget_mac.mm
+++ b/ui/views/widget/native_widget_mac.mm
@@ -96,10 +96,8 @@
     delete this;
 }
 
-int NativeWidgetMac::SheetPositionY() {
-  NSView* view = GetNativeView().GetNativeNSView();
-  return
-      [view convertPoint:NSMakePoint(0, NSHeight([view frame])) toView:nil].y;
+int32_t NativeWidgetMac::SheetOffsetY() {
+  return 0;
 }
 
 void NativeWidgetMac::GetWindowFrameTitlebarHeight(
diff --git a/ui/views_bridge_mac/bridge_factory_impl.mm b/ui/views_bridge_mac/bridge_factory_impl.mm
index 6ddab334..e4168c1 100644
--- a/ui/views_bridge_mac/bridge_factory_impl.mm
+++ b/ui/views_bridge_mac/bridge_factory_impl.mm
@@ -72,7 +72,6 @@
                  gfx::Point* baseline_point) override {
     *found_word = false;
   }
-  double SheetPositionY() override { return 0; }
   views_bridge_mac::DragDropClient* GetDragDropClient() override {
     // Drag-drop only doesn't work across mojo yet.
     return nullptr;
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 2d19cbc..2d8d15d 100644
--- a/ui/views_bridge_mac/bridged_native_widget_host_helper.h
+++ b/ui/views_bridge_mac/bridged_native_widget_host_helper.h
@@ -52,12 +52,6 @@
                          gfx::DecoratedText* decorated_word,
                          gfx::Point* baseline_point) = 0;
 
-  // Returns the vertical position that sheets should be anchored, in pixels
-  // from the bottom of the window.
-  // TODO(ccameron): This should be either moved to the mojo interface or
-  // separated out in such a way as to avoid needing to go through mojo.
-  virtual double SheetPositionY() = 0;
-
   // Return a pointer to host's DragDropClientMac.
   // TODO(ccameron): Drag-drop behavior needs to be implemented over mojo.
   virtual DragDropClient* GetDragDropClient() = 0;
diff --git a/ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom b/ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom
index 40ea3fed..2a36cc3 100644
--- a/ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom
+++ b/ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom
@@ -50,6 +50,11 @@
   // bounds from OnWindowGeometryChanged.
   OnViewSizeChanged(gfx.mojom.Size new_size);
 
+  // The vertical position from which sheets should be anchored, from the top
+  // of the content view.
+  [Sync]
+  GetSheetOffsetY() => (int32 offset_y);
+
   // Indicate if full keyboard accessibility is needed and update focus if
   // needed.
   SetKeyboardAccessible(bool enabled);
diff --git a/ui/views_bridge_mac/views_nswindow_delegate.mm b/ui/views_bridge_mac/views_nswindow_delegate.mm
index 7d1fbfa..56869eb 100644
--- a/ui/views_bridge_mac/views_nswindow_delegate.mm
+++ b/ui/views_bridge_mac/views_nswindow_delegate.mm
@@ -196,15 +196,18 @@
 - (NSRect)window:(NSWindow*)window
     willPositionSheet:(NSWindow*)sheet
             usingRect:(NSRect)defaultSheetLocation {
-  // TODO(ccameron): This should go through the BridgedNativeWidgetHost
-  // interface.
-  CGFloat sheetPositionY = parent_->host_helper()->SheetPositionY();
+  int32_t sheetPositionY = 0;
+  parent_->host()->GetSheetOffsetY(&sheetPositionY);
+  NSView* view = [window contentView];
+  NSPoint pointInView =
+      NSMakePoint(0, NSMaxY([view bounds]) - sheetPositionY);
+  NSPoint pointInWindow = [view convertPoint:pointInView toView:nil];
 
   // As per NSWindowDelegate documentation, the origin indicates the top left
   // point of the host frame in window coordinates. The width changes the
   // animation from vertical to trapezoid if it is smaller than the width of the
   // dialog. The height is ignored but should be set to zero.
-  return NSMakeRect(0, sheetPositionY, NSWidth(defaultSheetLocation), 0);
+  return NSMakeRect(0, pointInWindow.y, NSWidth(defaultSheetLocation), 0);
 }
 
 @end
diff --git a/ui/webui/resources/html/webui_listener_tracker.html b/ui/webui/resources/html/webui_listener_tracker.html
deleted file mode 100644
index f63f03e..0000000
--- a/ui/webui/resources/html/webui_listener_tracker.html
+++ /dev/null
@@ -1 +0,0 @@
-<script src="../js/webui_listener_tracker.js"></script>
diff --git a/ui/webui/resources/js/BUILD.gn b/ui/webui/resources/js/BUILD.gn
index 6ad7c092..9b0a553 100644
--- a/ui/webui/resources/js/BUILD.gn
+++ b/ui/webui/resources/js/BUILD.gn
@@ -30,7 +30,6 @@
     ":search_highlight_utils",
     ":util",
     ":web_ui_listener_behavior",
-    ":webui_listener_tracker",
     ":webui_resource_test",
   ]
 }
@@ -60,12 +59,6 @@
 js_library("event_tracker") {
 }
 
-js_library("webui_listener_tracker") {
-  deps = [
-    ":cr",
-  ]
-}
-
 js_library("search_highlight_utils") {
   deps = [
     ":cr",
diff --git a/ui/webui/resources/js/webui_listener_tracker.js b/ui/webui/resources/js/webui_listener_tracker.js
deleted file mode 100644
index 51ffb08..0000000
--- a/ui/webui/resources/js/webui_listener_tracker.js
+++ /dev/null
@@ -1,42 +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.
-
-/**
- * @fileoverview Class that manages the addition and removal of WebUI
- * listeners.
- */
-
-/**
- * Create a WebUIListenerTracker to track a set of Web UI listeners.
- * @constructor
- */
-function WebUIListenerTracker() {
-  /**
-   * The Web UI listeners being tracked.
-   * @private {!Array<!WebUIListener>}
-   */
-  this.listeners_ = [];
-}
-
-WebUIListenerTracker.prototype = {
-  /**
-   * Adds a WebUI listener to the array of listeners being tracked, which
-   * will be removed when removeAll() is called.
-   * Do not use this method if the listener will be removed manually.
-   * @param {string} eventName The event to listen to.
-   * @param {!Function} callback The callback to run when the event is fired.
-   */
-  add: function(eventName, callback) {
-    this.listeners_.push(cr.addWebUIListener(eventName, callback));
-  },
-
-  /**
-   * Removes all Web UI listeners that are currently being tracked.
-   */
-  removeAll: function() {
-    while (this.listeners_.length > 0) {
-      cr.removeWebUIListener(this.listeners_.pop());
-    }
-  },
-};
diff --git a/ui/webui/resources/webui_resources.grd b/ui/webui/resources/webui_resources.grd
index 942555b..b22111a 100644
--- a/ui/webui/resources/webui_resources.grd
+++ b/ui/webui/resources/webui_resources.grd
@@ -371,9 +371,6 @@
                  compress="gzip" />
       <structure name="IDR_WEBUI_HTML_UTIL"
                  file="html/util.html" type="chrome_html" compress="gzip" />
-      <structure name="IDR_WEBUI_HTML_WEBUI_LISTENER_TRACKER"
-                 file="html/webui_listener_tracker.html" type="chrome_html"
-                 compress="gzip" />
 
       <structure name="IDR_WEBUI_JS_ACTION_LINK"
                  file="js/action_link.js" type="chrome_html" compress="gzip" />
@@ -521,9 +518,6 @@
       <structure name="IDR_WEBUI_JS_EVENT_TRACKER"
                  file="js/event_tracker.js" type="chrome_html"
                  compress="gzip" />
-      <structure name="IDR_WEBUI_JS_WEBUI_LISTENER_TRACKER"
-                 file="js/webui_listener_tracker.js" type="chrome_html"
-                 compress="gzip" />
       <structure name="IDR_WEBUI_JS_ICON"
                  file="js/icon.js" type="chrome_html" compress="gzip"
                  flattenhtml="true" />