diff --git a/AUTHORS b/AUTHORS
index f5cf066f..b4f8615 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -303,6 +303,7 @@
 Ilia K <ki.stfu@gmail.com>
 Ilya Konstantinov <ilya.konstantinov@gmail.com>
 Ion Rosca <rosca@adobe.com>
+Irmak Kavasoglu <irmakkavasoglu@gmail.com>
 Isaac Reilly <reillyi@amazon.com>
 Ivan Sham <ivansham@amazon.com>
 J. Ryan Stinnett <jryans@chromium.org>
diff --git a/DEPS b/DEPS
index eb807fb..0f6aa124 100644
--- a/DEPS
+++ b/DEPS
@@ -41,11 +41,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': 'a83b903b601a28c5e545ba307d6d6c776f1b5660',
+  'skia_revision': 'c49d11e0651939919bed6e7a8ae6afebf260d2bc',
   # 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': 'ecf9147d2b8322eefdeb737df265c3c47b2432ac',
+  'v8_revision': '468b6a4f3f785a7fc92ea1a7322ffe45cc3b6a64',
   # 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.
@@ -65,7 +65,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '09b5ce623fc47bc3d19d60aa2a219c838971dd4b',
+  'pdfium_revision': '92827b695a69c7a3fd5940dc0aa1713fe3f3ee96',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -93,11 +93,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': '39ce3ac499d4cd7371031a062f410953c8ecce29',
+  'freetype_revision': '1ad07c1c79841e54ff3d5c37e28bfb91f402ee84',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'b233ea0e0e23e18000a6e7b6c7232cb67dbee7de',
+  'catapult_revision': 'aa736cc76ee5e35215abcfb83a8c354f12d0c684',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -240,7 +240,7 @@
     Var('chromium_git') + '/native_client/src/third_party/scons-2.0.1.git' + '@' + '1c1550e17fc26355d08627fbdec13d8291227067',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '99a81b613d754baad33f68a0c3f8fdd60ee8f186', # commit position 19911
+    Var('webrtc_git') + '/src.git' + '@' + '433b11ede72a8047ad5b34d8ab522e2da07f99f8', # commit position 19922
 
   'src/third_party/openmax_dl':
     Var('webrtc_git') + '/deps/third_party/openmax.git' + '@' +  Var('openmax_dl_revision'),
@@ -1246,4 +1246,6 @@
   'src/third_party/android_tools',
   # ANGLE manages DEPS that it also owns the build files for, such as dEQP.
   ("src/third_party/angle", "DEPS.chromium"),
+  # src-internal has its own DEPS file to pull additional internal repos
+  'src-internal',
 ]
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 4774d189..24c1c64 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -343,7 +343,7 @@
       (),
     ),
     (
-      r'/(WebThread|BrowserThread)::(FILE|FILE_USER_BLOCKING|DB|CACHE)',
+      'BrowserThread::FILE',
       (
         'The non-UI/IO BrowserThreads are deprecated, please migrate this',
         'code to TaskScheduler. See https://goo.gl/mDSxKl for details.',
diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc
index 76203d79..5b26cd6 100644
--- a/android_webview/browser/aw_browser_context.cc
+++ b/android_webview/browser/aw_browser_context.cc
@@ -315,6 +315,13 @@
   return permission_manager_.get();
 }
 
+content::BackgroundFetchDelegate*
+AwBrowserContext::GetBackgroundFetchDelegate() {
+  // TODO(crbug.com/766077): Resolve whether to support or disable background
+  // fetch on WebView.
+  return nullptr;
+}
+
 content::BackgroundSyncController*
 AwBrowserContext::GetBackgroundSyncController() {
   return nullptr;
diff --git a/android_webview/browser/aw_browser_context.h b/android_webview/browser/aw_browser_context.h
index 94bdd32..f0b24ef4 100644
--- a/android_webview/browser/aw_browser_context.h
+++ b/android_webview/browser/aw_browser_context.h
@@ -98,6 +98,7 @@
   content::PushMessagingService* GetPushMessagingService() override;
   content::SSLHostStateDelegate* GetSSLHostStateDelegate() override;
   content::PermissionManager* GetPermissionManager() override;
+  content::BackgroundFetchDelegate* GetBackgroundFetchDelegate() override;
   content::BackgroundSyncController* GetBackgroundSyncController() override;
   content::BrowsingDataRemoverDelegate* GetBrowsingDataRemoverDelegate()
       override;
diff --git a/ash/system/bluetooth/bluetooth_power_controller.h b/ash/system/bluetooth/bluetooth_power_controller.h
index 7ae60898..f683d2f 100644
--- a/ash/system/bluetooth/bluetooth_power_controller.h
+++ b/ash/system/bluetooth/bluetooth_power_controller.h
@@ -15,8 +15,6 @@
 #include "components/user_manager/user_manager.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 
-#include <queue>
-
 class PrefRegistrySimple;
 class PrefService;
 
diff --git a/ash/system/ime_menu/ime_menu_tray.cc b/ash/system/ime_menu/ime_menu_tray.cc
index 9a0cfd66..cb672a16 100644
--- a/ash/system/ime_menu/ime_menu_tray.cc
+++ b/ash/system/ime_menu/ime_menu_tray.cc
@@ -311,9 +311,6 @@
   SystemTrayNotifier* tray_notifier = Shell::Get()->system_tray_notifier();
   tray_notifier->AddIMEObserver(this);
   tray_notifier->AddVirtualKeyboardObserver(this);
-
-  if (!drag_controller())
-    set_drag_controller(base::MakeUnique<TrayDragController>(shelf));
 }
 
 ImeMenuTray::~ImeMenuTray() {
diff --git a/ash/system/palette/palette_tray.cc b/ash/system/palette/palette_tray.cc
index b7009355..91a25bb 100644
--- a/ash/system/palette/palette_tray.cc
+++ b/ash/system/palette/palette_tray.cc
@@ -186,9 +186,6 @@
 
   Shell::Get()->AddShellObserver(this);
   ui::InputDeviceManager::GetInstance()->AddObserver(this);
-
-  if (!drag_controller())
-    set_drag_controller(base::MakeUnique<TrayDragController>(shelf));
 }
 
 PaletteTray::~PaletteTray() {
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index b7d6f9cf..6c0aa39 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -251,9 +251,6 @@
   // horizontal shelf and that is sufficient to indicate separation, no
   // separator is required.
   set_separator_visibility(false);
-
-  if (!drag_controller())
-    set_drag_controller(base::MakeUnique<TrayDragController>(shelf));
 }
 
 SystemTray::~SystemTray() {
diff --git a/ash/system/tray/system_tray_unittest.cc b/ash/system/tray/system_tray_unittest.cc
index 548aca2..1483683 100644
--- a/ash/system/tray/system_tray_unittest.cc
+++ b/ash/system/tray/system_tray_unittest.cc
@@ -164,9 +164,12 @@
   DISALLOW_COPY_AND_ASSIGN(SystemTrayTest);
 };
 
+// TODO(minch): Swiping the status area tray or associated bubble in tablet mode
+// has been disabled currently, it may be added back in the future.
+// http://crbug.com/767679.
 // Swiping on the overlap area of shelf and system tray bubble during the
 // animation should close the bubble.
-TEST_F(SystemTrayTest, SwipingOnShelfDuringAnimation) {
+TEST_F(SystemTrayTest, DISABLED_SwipingOnShelfDuringAnimation) {
   Shelf* shelf = GetPrimaryShelf();
   SystemTray* system_tray = GetPrimarySystemTray();
   gfx::Point start = system_tray->GetLocalBounds().CenterPoint();
@@ -218,8 +221,11 @@
   }
 }
 
+// TODO(minch): Swiping the status area tray or associated bubble in tablet mode
+// has been disabled currently, it may be added back in the future.
+// http://crbug.com/767679.
 // Swiping on the system tray ends with fling event.
-TEST_F(SystemTrayTest, FlingOnSystemTray) {
+TEST_F(SystemTrayTest, DISABLED_FlingOnSystemTray) {
   Shelf* shelf = GetPrimaryShelf();
   SystemTray* system_tray = GetPrimarySystemTray();
   gfx::Point start = system_tray->GetLocalBounds().CenterPoint();
@@ -288,8 +294,11 @@
   EXPECT_FALSE(system_tray->HasSystemBubble());
 }
 
+// TODO(minch): Swiping the status area tray or associated bubble in tablet mode
+// has been disabled currently, it may be added back in the future.
+// http://crbug.com/767679.
 // Touch outside the system tray bubble during swiping should close the bubble.
-TEST_F(SystemTrayTest, TapOutsideCloseBubble) {
+TEST_F(SystemTrayTest, DISABLED_TapOutsideCloseBubble) {
   Shelf* shelf = GetPrimaryShelf();
   SystemTray* system_tray = GetPrimarySystemTray();
   gfx::Point start = system_tray->GetLocalBounds().CenterPoint();
@@ -308,8 +317,11 @@
   EXPECT_FALSE(system_tray->HasSystemBubble());
 }
 
+// TODO(minch): Swiping the status area tray or associated bubble in tablet mode
+// has been disabled currently, it may be added back in the future.
+// http://crbug.com/767679.
 // Swiping on the system tray ends with scroll event.
-TEST_F(SystemTrayTest, SwipingOnSystemTray) {
+TEST_F(SystemTrayTest, DISABLED_SwipingOnSystemTray) {
   Shelf* shelf = GetPrimaryShelf();
   SystemTray* system_tray = GetPrimarySystemTray();
   gfx::Point start = system_tray->GetLocalBounds().CenterPoint();
@@ -362,9 +374,12 @@
   EXPECT_FALSE(system_tray->HasSystemBubble());
 }
 
+// TODO(minch): Swiping the status area tray or associated bubble in tablet mode
+// has been disabled currently, it may be added back in the future.
+// http://crbug.com/767679.
 // Tests for swiping down on an open system tray bubble in order to
 // close it.
-TEST_F(SystemTrayTest, SwipingOnSystemTrayBubble) {
+TEST_F(SystemTrayTest, DISABLED_SwipingOnSystemTrayBubble) {
   Shelf* shelf = GetPrimaryShelf();
   SystemTray* system_tray = GetPrimarySystemTray();
   EXPECT_EQ(SHELF_ALIGNMENT_BOTTOM, shelf->alignment());
diff --git a/ash/system/web_notification/web_notification_tray.cc b/ash/system/web_notification/web_notification_tray.cc
index d2629b7..0c1cc8b 100644
--- a/ash/system/web_notification/web_notification_tray.cc
+++ b/ash/system/web_notification/web_notification_tray.cc
@@ -295,9 +295,6 @@
   OnMessageCenterTrayChanged();
 
   tray_container()->SetMargin(kTrayMainAxisInset, kTrayCrossAxisInset);
-
-  if (!drag_controller())
-    set_drag_controller(base::MakeUnique<TrayDragController>(shelf));
 }
 
 WebNotificationTray::~WebNotificationTray() {
diff --git a/base/macros.h b/base/macros.h
index 2f0de40..d7abfc7 100644
--- a/base/macros.h
+++ b/base/macros.h
@@ -13,12 +13,12 @@
 #include <stddef.h>  // For size_t.
 
 // Distinguish mips32.
-#if defined(__mips__) && (_MIPS_SIM == _ABIO32)
+#if defined(__mips__) && (_MIPS_SIM == _ABIO32) && !defined(__mips32__)
 #define __mips32__
 #endif
 
 // Distinguish mips64.
-#if defined(__mips__) && (_MIPS_SIM == _ABI64)
+#if defined(__mips__) && (_MIPS_SIM == _ABI64) && !defined(__mips64__)
 #define __mips64__
 #endif
 
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py
index 34b2297..395ef1b 100755
--- a/build/android/gyp/write_build_config.py
+++ b/build/android/gyp/write_build_config.py
@@ -449,7 +449,7 @@
       all_java_sources.append(options.java_sources_file)
     config['jni']['all_source'] = all_java_sources
 
-  if (options.type in ('java_binary', 'java_library')):
+  if (options.type in ('java_binary', 'java_library', 'dist_jar')):
     deps_info['requires_android'] = options.requires_android
     deps_info['supports_android'] = options.supports_android
 
@@ -640,12 +640,14 @@
     deps_info['proguard_configs'] = (
         build_utils.ParseGnList(options.proguard_configs))
 
-  if options.type == 'android_apk':
+  if options.type in ('android_apk', 'dist_jar'):
     deps_info['proguard_enabled'] = options.proguard_enabled
     deps_info['proguard_info'] = options.proguard_info
     config['proguard'] = {}
     proguard_config = config['proguard']
-    proguard_config['input_paths'] = [options.jar_path] + java_full_classpath
+    proguard_config['input_paths'] = list(java_full_classpath)
+    if options.jar_path:
+      proguard_config['input_paths'].insert(0, options.jar_path)
     extra_jars = set()
     lib_configs = set()
     for c in all_library_deps:
diff --git a/build/android/pylib/symbols/deobfuscator.py b/build/android/pylib/symbols/deobfuscator.py
index c9287fd..67347c4 100644
--- a/build/android/pylib/symbols/deobfuscator.py
+++ b/build/android/pylib/symbols/deobfuscator.py
@@ -6,14 +6,16 @@
 import os
 import subprocess
 import threading
+import time
 import uuid
 
 from devil.utils import reraiser_thread
 from pylib import constants
 
 
-_MINIUMUM_TIMEOUT = 5.0  # Large enough to account for process start-up.
+_MINIUMUM_TIMEOUT = 3.0
 _PER_LINE_TIMEOUT = .002  # Should be able to process 500 lines per second.
+_PROCESS_START_TIMEOUT = 10.0
 
 
 class Deobfuscator(object):
@@ -27,6 +29,7 @@
     # Assign to None so that attribute exists if Popen() throws.
     self._proc = None
     # Start process eagerly to hide start-up latency.
+    self._proc_start_time = time.time()
     self._proc = subprocess.Popen(
         cmd, bufsize=1, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
         close_fds=True)
@@ -71,7 +74,7 @@
           break
         out_lines.append(line)
 
-    if not self.IsReady():
+    if self.IsBusy():
       logging.warning('deobfuscator: Having to wait for Java deobfuscation.')
 
     # Allow only one thread to operate at a time.
@@ -92,7 +95,9 @@
         self._proc.stdin.write('\n'.join(lines))
         self._proc.stdin.write('\n{}\n'.format(eof_line))
         self._proc.stdin.flush()
-        timeout = max(_MINIUMUM_TIMEOUT, len(lines) * _PER_LINE_TIMEOUT)
+        time_since_proc_start = time.time() - self._proc_start_time
+        timeout = (max(0, _PROCESS_START_TIMEOUT - time_since_proc_start) +
+                   max(_MINIUMUM_TIMEOUT, len(lines) * _PER_LINE_TIMEOUT))
         reader_thread.join(timeout)
         if self.IsClosed():
           logging.warning(
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 23d1b92f..7ae426e 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -145,47 +145,43 @@
       rebase_path(build_config, root_build_dir),
     ]
 
-    is_java = type == "java_library" || type == "java_binary" ||
-              type == "java_prebuilt"
     is_apk = type == "android_apk"
     is_android_assets = type == "android_assets"
     is_android_resources = type == "android_resources"
     is_deps_dex = type == "deps_dex"
     is_group = type == "group"
 
-    supports_android = is_apk || is_android_assets || is_android_resources ||
-                       is_deps_dex || is_group ||
-                       (is_java && defined(invoker.supports_android) &&
-                        invoker.supports_android)
+    supports_android =
+        is_apk || is_android_assets || is_android_resources || is_deps_dex ||
+        is_group ||
+        (defined(invoker.supports_android) && invoker.supports_android)
     requires_android =
         is_apk || is_android_assets || is_android_resources || is_deps_dex ||
-        (is_java && defined(invoker.requires_android) &&
-         invoker.requires_android)
+        (defined(invoker.requires_android) && invoker.requires_android)
 
     assert(!requires_android || supports_android,
            "requires_android requires" + " supports_android")
 
     # Mark these variables as used.
-    assert(is_java || true)
     assert(is_apk || true)
     assert(is_android_resources || true)
     assert(is_deps_dex || true)
     assert(is_group || true)
 
-    if (is_java || is_apk) {
+    if (defined(invoker.jar_path)) {
       args += [
         "--jar-path",
         rebase_path(invoker.jar_path, root_build_dir),
       ]
     }
 
-    if (is_java && defined(invoker.java_resources_jar)) {
+    if (defined(invoker.java_resources_jar)) {
       args += [
         "--java-resources-jar-path",
         rebase_path(invoker.java_resources_jar, root_build_dir),
       ]
     }
-    if (is_apk || is_deps_dex || (is_java && supports_android)) {
+    if (defined(invoker.dex_path)) {
       args += [
         "--dex-path",
         rebase_path(invoker.dex_path, root_build_dir),
@@ -291,14 +287,6 @@
         ]
       }
 
-      if (defined(invoker.proguard_enabled) && invoker.proguard_enabled) {
-        args += [
-          "--proguard-enabled",
-          "--proguard-info",
-          rebase_path(invoker.proguard_info, root_build_dir),
-        ]
-      }
-
       if (defined(invoker.apk_path)) {
         _rebased_apk_path = rebase_path(invoker.apk_path, root_build_dir)
         _rebased_incremental_apk_path =
@@ -344,6 +332,13 @@
           rebase_path(invoker.input_jars_paths, root_build_dir)
       args += [ "--extra-classpath-jars=$_rebased_input_jars_paths" ]
     }
+    if (defined(invoker.proguard_enabled) && invoker.proguard_enabled) {
+      args += [
+        "--proguard-enabled",
+        "--proguard-info",
+        rebase_path(invoker.proguard_info, root_build_dir),
+      ]
+    }
     if (defined(invoker.proguard_configs)) {
       _rebased_proguard_configs =
           rebase_path(invoker.proguard_configs, root_build_dir)
@@ -855,8 +850,9 @@
       set_sources_assignment_filter([])
       forward_variables_from(invoker,
                              [
-                               "deps",
+                               "data",
                                "data_deps",
+                               "deps",
                                "public_deps",
                                "testonly",
                              ])
@@ -2612,17 +2608,7 @@
         } else {
           type = "java_library"
         }
-        if (defined(invoker.deps)) {
-          possible_config_deps = invoker.deps
-          if (_enable_build_hooks) {
-            possible_config_deps +=
-                [ "//build/android/buildhooks:build_hooks_java" ]
-          }
-          if (_enable_build_hooks_android) {
-            possible_config_deps +=
-                [ "//build/android/buildhooks:build_hooks_android_java" ]
-          }
-        }
+        possible_config_deps = _accumulated_deps
         supports_android = _supports_android
         requires_android = _requires_android
         bypass_platform_checks = defined(invoker.bypass_platform_checks) &&
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 026e042..6332b8f6 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -1296,18 +1296,63 @@
   #
   # Variables:
   #   output: Path to the output jar.
+  #   dex_path: Path to dex()'ed output (optional).
   #   override_build_config: Use a pre-existing .build_config. Must be of type
   #     "apk".
   #   use_interface_jars: Use all dependent interface .jars rather than
   #     implementation .jars.
   #   direct_deps_only: Do not recurse on deps.
   #   data, deps, testonly, visibility: Usual meaning.
+  #   proguard_enabled: Whether to run ProGuard on resulting jar.
+  #   proguard_configs: List of proguard configs.
+  #   proguard_jar_path: The path to proguard.jar you wish to use. If undefined,
+  #     the proguard used will be the checked in one in //third_party/proguard.
+  #   alternative_android_sdk_jar: System jar to use when proguard is enabled.
   #
   # Example
   #   dist_jar("lib_fatjar") {
   #     deps = [ ":my_java_lib" ]
+  #     output = "$root_build_dir/MyLibrary.jar"
   #   }
   template("dist_jar") {
+    forward_variables_from(invoker, [ "testonly" ])
+    _supports_android =
+        !defined(invoker.supports_android) || invoker.supports_android
+    _requires_android =
+        defined(invoker.requires_android) && invoker.requires_android
+    _proguard_enabled =
+        defined(invoker.proguard_enabled) && invoker.proguard_enabled
+    _use_interface_jars =
+        defined(invoker.use_interface_jars) && invoker.use_interface_jars
+    _direct_deps_only =
+        defined(invoker.direct_deps_only) && invoker.direct_deps_only
+    assert(
+        !(_proguard_enabled && _use_interface_jars),
+        "Cannot set both proguard_enabled and use_interface_jars (target=$target_name)")
+    assert(
+        !(_proguard_enabled && _direct_deps_only),
+        "Cannot set both proguard_enabled and direct_deps_only (target=$target_name)")
+
+    _jar_target_name = target_name
+    if (defined(invoker.dex_path)) {
+      if (_proguard_enabled) {
+        _jar_target_name = "${target_name}__proguard"
+      } else {
+        _jar_target_name = "${target_name}__dist_jar"
+      }
+    }
+
+    _deps = []
+    if (defined(invoker.deps)) {
+      _deps = invoker.deps
+    }
+    _enable_build_hooks =
+        _supports_android &&
+        (!defined(invoker.no_build_hooks) || !invoker.no_build_hooks)
+    if (_enable_build_hooks && _requires_android) {
+      _deps += [ "//build/android/buildhooks:build_hooks_android_impl_java" ]
+    }
+
     if (defined(invoker.override_build_config)) {
       _build_config = invoker.override_build_config
     } else {
@@ -1315,59 +1360,103 @@
       _build_config_target_name = "${target_name}__build_config"
 
       write_build_config(_build_config_target_name) {
-        forward_variables_from(invoker, [ "testonly" ])
         type = "dist_jar"
-        if (defined(invoker.deps)) {
-          possible_config_deps = invoker.deps
-        }
+        forward_variables_from(invoker,
+                               [
+                                 "dex_path",
+                                 "proguard_enabled",
+                               ])
+        supports_android = _supports_android
+        requires_android = _requires_android
+        possible_config_deps = _deps
         build_config = _build_config
+        if (_proguard_enabled) {
+          proguard_info = "${invoker.output}.info"
+        }
       }
+
+      _deps += [ ":$_build_config_target_name" ]
     }
 
-    action(target_name) {
-      forward_variables_from(invoker,
-                             [
-                               "data",
-                               "deps",
-                               "testonly",
-                               "visibility",
-                             ])
-      script = "//build/android/gyp/create_dist_jar.py"
-      depfile = "$target_gen_dir/$target_name.d"
+    _rebased_build_config = rebase_path(_build_config, root_build_dir)
 
-      inputs = [
-        _build_config,
-      ]
+    if (_proguard_enabled) {
+      proguard(_jar_target_name) {
+        forward_variables_from(invoker,
+                               [
+                                 "alternative_android_sdk_jar",
+                                 "data",
+                                 "proguard_jar_path",
+                               ])
+        deps = _deps
+        inputs = [
+          _build_config,
+        ]
 
-      outputs = [
-        invoker.output,
-      ]
-
-      if (defined(_build_config_target_name)) {
-        deps += [ ":$_build_config_target_name" ]
+        output_jar_path = invoker.output
+        args = [
+          "--proguard-configs=@FileArg($_rebased_build_config:proguard:lib_configs)",
+          "--input-paths=@FileArg($_rebased_build_config:proguard:input_paths)",
+          "--classpath=@FileArg($_rebased_build_config:proguard:lib_paths)",
+        ]
+        if (defined(invoker.proguard_configs)) {
+          _rebased_proguard_configs =
+              rebase_path(invoker.proguard_configs, root_build_dir)
+          args += [ "--proguard-configs=$_rebased_proguard_configs" ]
+        }
+        if (defined(invoker.proguard_config_exclusions)) {
+          _rebased_proguard_config_exclusions =
+              rebase_path(invoker.proguard_config_exclusions, root_build_dir)
+          args += [ "--proguard-config-exclusions=$_rebased_proguard_config_exclusions" ]
+        }
       }
+    } else {
+      action(_jar_target_name) {
+        forward_variables_from(invoker, [ "data" ])
+        script = "//build/android/gyp/create_dist_jar.py"
+        depfile = "$target_gen_dir/$target_name.d"
+        deps = _deps
 
-      args = [
-        "--depfile",
-        rebase_path(depfile, root_build_dir),
-        "--output",
-        rebase_path(invoker.output, root_build_dir),
-      ]
+        inputs = [
+          _build_config,
+        ]
 
-      _rebased_build_config = rebase_path(_build_config, root_build_dir)
-      if (defined(invoker.direct_deps_only) && invoker.direct_deps_only) {
-        if (defined(invoker.use_interface_jars) && invoker.use_interface_jars) {
-          args += [ "--inputs=@FileArg($_rebased_build_config:javac:interface_classpath)" ]
+        outputs = [
+          invoker.output,
+        ]
+
+        args = [
+          "--depfile",
+          rebase_path(depfile, root_build_dir),
+          "--output",
+          rebase_path(invoker.output, root_build_dir),
+        ]
+
+        if (_direct_deps_only) {
+          if (_use_interface_jars) {
+            args += [ "--inputs=@FileArg($_rebased_build_config:javac:interface_classpath)" ]
+          } else {
+            args +=
+                [ "--inputs=@FileArg($_rebased_build_config:javac:classpath)" ]
+          }
         } else {
-          args +=
-              [ "--inputs=@FileArg($_rebased_build_config:javac:classpath)" ]
+          if (_use_interface_jars) {
+            args += [ "--inputs=@FileArg($_rebased_build_config:dist_jar:all_interface_jars)" ]
+          } else {
+            args += [ "--inputs=@FileArg($_rebased_build_config:dist_jar:dependency_jars)" ]
+          }
         }
-      } else {
-        if (defined(invoker.use_interface_jars) && invoker.use_interface_jars) {
-          args += [ "--inputs=@FileArg($_rebased_build_config:dist_jar:all_interface_jars)" ]
-        } else {
-          args += [ "--inputs=@FileArg($_rebased_build_config:dist_jar:dependency_jars)" ]
-        }
+      }
+    }
+    if (defined(invoker.dex_path)) {
+      dex(target_name) {
+        deps = [
+          ":$_jar_target_name",
+        ]
+        sources = [
+          invoker.output,
+        ]
+        output = invoker.dex_path
       }
     }
   }
diff --git a/build/config/ios/hardlink.py b/build/config/ios/hardlink.py
new file mode 100644
index 0000000..91dbf62
--- /dev/null
+++ b/build/config/ios/hardlink.py
@@ -0,0 +1,69 @@
+# 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.
+
+"""Recursively create hardlink to target named output."""
+
+
+import argparse
+import os
+import shutil
+
+
+def CreateHardlinkHelper(target, output):
+  """Recursively create a hardlink named output pointing to target.
+
+  Args:
+    target: path to an existing file or directory
+    output: path to the newly created hardlink
+
+  This function assumes that output does not exists but that the parent
+  directory containing output does. If those conditions are false, then
+  the function will fails with an exception corresponding to an OS error.
+  """
+  if os.path.islink(target):
+    os.symlink(os.readlink(target), output)
+  elif not os.path.isdir(target):
+    try:
+      os.link(target, output)
+    except:
+      shutil.copy(target, output)
+  else:
+    os.mkdir(output)
+    for name in os.listdir(target):
+      CreateHardlinkHelper(
+          os.path.join(target, name),
+          os.path.join(output, name))
+
+
+def CreateHardlink(target, output):
+  """Recursively create a hardlink named output pointing to target.
+
+  Args:
+    target: path to an existing file or directory
+    output: path to the newly created hardlink
+
+  If output already exists, it is first removed. In all cases, the
+  parent directory containing output is created.
+  """
+  if os.path.exists(output):
+    shutil.rmtree(output)
+
+  parent_dir = os.path.dirname(os.path.abspath(output))
+  if not os.path.isdir(parent_dir):
+    os.makedirs(parent_dir)
+
+  CreateHardlinkHelper(target, output)
+
+
+def Main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('target', help='path to the file or directory to link to')
+  parser.add_argument('output', help='name of the hardlink to create')
+  args = parser.parse_args()
+
+  CreateHardlink(args.target, args.output)
+
+
+if __name__ == '__main__':
+  Main()
diff --git a/build/config/ios/rules.gni b/build/config/ios/rules.gni
index e311e25..699bc27 100644
--- a/build/config/ios/rules.gni
+++ b/build/config/ios/rules.gni
@@ -122,12 +122,16 @@
 #   product_type
 #       string, product type for the generated Xcode project.
 #
+#   bundle_gen_dir
+#       (optional) directory where the bundle is generated; must be below
+#       root_out_dir and defaults to root_out_dir if omitted.
+#
 #   bundle_deps
-#       (optional) list of additional dependencies
+#       (optional) list of additional dependencies.
 #
 #   bundle_deps_filter
 #       (optional) list of dependencies to filter (for more information
-#       see "gn help bundle_deps_filter")
+#       see "gn help bundle_deps_filter").
 #
 #   bundle_extension
 #       string, extension of the bundle, used to generate bundle name.
@@ -220,8 +224,12 @@
         "/$_bundle_binary_output"
   }
 
+  _bundle_gen_dir = root_out_dir
+  if (defined(invoker.bundle_gen_dir)) {
+    _bundle_gen_dir = invoker.bundle_gen_dir
+  }
+
   _bundle_extension = invoker.bundle_extension
-  _bundle_root_dir = "$root_out_dir/$_output_name$_bundle_extension"
 
   if (!defined(invoker.entitlements_target)) {
     _entitlements_path = "//build/config/ios/entitlements.plist"
@@ -259,10 +267,11 @@
                              "xcode_test_application_name",
                            ])
 
-    bundle_root_dir = _bundle_root_dir
-    bundle_resources_dir = _bundle_root_dir
-    bundle_executable_dir = _bundle_root_dir
-    bundle_plugins_dir = "$_bundle_root_dir/PlugIns"
+    bundle_root_dir = "$_bundle_gen_dir/$_output_name$_bundle_extension"
+    bundle_contents_dir = bundle_root_dir
+    bundle_resources_dir = bundle_contents_dir
+    bundle_executable_dir = bundle_contents_dir
+    bundle_plugins_dir = "$bundle_contents_dir/PlugIns"
 
     if (!defined(public_deps)) {
       public_deps = []
@@ -290,18 +299,19 @@
       _entitlements_path,
       _bundle_binary_path,
     ]
-    code_signing_outputs = [ "$_bundle_root_dir/$_output_name" ]
+    code_signing_outputs = [ "$bundle_contents_dir/$_output_name" ]
     if (_enable_code_signing) {
       code_signing_outputs +=
-          [ "$_bundle_root_dir/_CodeSignature/CodeResources" ]
+          [ "$bundle_contents_dir/_CodeSignature/CodeResources" ]
     }
     if (ios_code_signing_identity != "" && !use_ios_simulator) {
-      code_signing_outputs += [ "$_bundle_root_dir/embedded.mobileprovision" ]
+      code_signing_outputs +=
+          [ "$bundle_contents_dir/embedded.mobileprovision" ]
     }
 
     if (defined(invoker.extra_system_frameworks)) {
       foreach(_framework, invoker.extra_system_frameworks) {
-        code_signing_outputs += [ "$bundle_root_dir/Frameworks/" +
+        code_signing_outputs += [ "$bundle_contents_dir/Frameworks/" +
                                   get_path_info(_framework, "file") ]
       }
     }
@@ -342,7 +352,7 @@
 
       code_signing_sources += _partial_info_plists
       code_signing_sources += [ _plist_compiler_path ]
-      code_signing_outputs += [ "$bundle_root_dir/Info.plist" ]
+      code_signing_outputs += [ "$bundle_contents_dir/Info.plist" ]
 
       code_signing_args +=
           [ "-P=" + rebase_path(_plist_compiler_path, root_build_dir) ]
@@ -469,6 +479,13 @@
 #       (optional) boolean, control whether code signing is enabled or not,
 #       default to ios_enable_code_signing if not defined.
 #
+#   variants
+#       (optional) list of scopes, each scope needs to define the attributes
+#       "name" and "bundle_deps"; if defined and non-empty, then one bundle
+#       named $target_out_dir/$variant/$output_name will be created for each
+#       variant with the same binary but the correct bundle_deps, the bundle
+#       at $target_out_dir/$output_name will be a copy of the first variant.
+#
 # For more information, see "gn help executable".
 template("ios_app_bundle") {
   _output_name = target_name
@@ -481,6 +498,45 @@
   _arch_executable_target = _target_name + "_arch_executable"
   _lipo_executable_target = _target_name + "_executable"
 
+  if (defined(invoker.variants) && invoker.variants != []) {
+    _variants = []
+
+    foreach(_variant, invoker.variants) {
+      assert(defined(_variant.name) && _variant.name != "",
+             "name must be defined for all $target_name variants")
+
+      assert(defined(_variant.bundle_deps),
+             "bundle_deps must be defined for all $target_name variants")
+
+      _variants += [ {
+            name = _variant.name
+            bundle_deps = _variant.bundle_deps
+            target_name = "${_target_name}_variants_${_variant.name}"
+            bundle_gen_dir = "$root_out_dir/variants/${_variant.name}"
+          } ]
+    }
+  } else {
+    # If no variants are passed to the template, use a fake variant with
+    # no name to avoid duplicating code. As no variant can have an empty
+    # name except this fake variant, it is possible to know if a variant
+    # is fake or not.
+    _variants = [ {
+          name = ""
+          bundle_deps = []
+          target_name = _target_name
+          bundle_gen_dir = root_out_dir
+        } ]
+  }
+
+  _default_variant = _variants[0]
+
+  if (current_toolchain != default_toolchain) {
+    # For use of _variants and _default_variant for secondary toolchain to
+    # avoid the "Assignment had no effect" error from gn.
+    assert(_variants != [])
+    assert(_default_variant.target_name != "")
+  }
+
   source_set(_arch_executable_source) {
     forward_variables_from(invoker,
                            "*",
@@ -610,7 +666,11 @@
                                "testonly",
                              ])
 
-      visibility = [ ":$_target_name" ]
+      visibility = []
+      foreach(_variant, _variants) {
+        visibility += [ ":${_variant.target_name}" ]
+      }
+
       output_name = _output_name
       arch_binary_target = ":$_arch_executable_target"
       arch_binary_output = _output_name
@@ -727,51 +787,79 @@
       }
     }
 
-    create_signed_bundle(_target_name) {
-      forward_variables_from(invoker,
-                             [
-                               "bundle_deps",
-                               "bundle_deps_filter",
-                               "data_deps",
-                               "deps",
-                               "enable_code_signing",
-                               "entitlements_path",
-                               "entitlements_target",
-                               "extra_system_frameworks",
-                               "public_configs",
-                               "public_deps",
-                               "testonly",
-                               "visibility",
-                             ])
+    foreach(_variant, _variants) {
+      create_signed_bundle(_variant.target_name) {
+        forward_variables_from(invoker,
+                               [
+                                 "bundle_deps",
+                                 "bundle_deps_filter",
+                                 "data_deps",
+                                 "deps",
+                                 "enable_code_signing",
+                                 "entitlements_path",
+                                 "entitlements_target",
+                                 "extra_system_frameworks",
+                                 "public_configs",
+                                 "public_deps",
+                                 "testonly",
+                                 "visibility",
+                               ])
 
-      output_name = _output_name
-      bundle_binary_target = ":$_lipo_executable_target"
-      bundle_binary_output = _output_name
-      bundle_extension = _bundle_extension
-      product_type = _product_type
+        output_name = _output_name
+        bundle_gen_dir = _variant.bundle_gen_dir
+        bundle_binary_target = ":$_lipo_executable_target"
+        bundle_binary_output = _output_name
+        bundle_extension = _bundle_extension
+        product_type = _product_type
 
-      _generate_info_plist_outputs =
-          get_target_outputs(":$_generate_info_plist")
-      primary_info_plist = _generate_info_plist_outputs[0]
-      partial_info_plist = "$target_gen_dir/${_target_name}_partial_info.plist"
+        _generate_info_plist_outputs =
+            get_target_outputs(":$_generate_info_plist")
+        primary_info_plist = _generate_info_plist_outputs[0]
+        partial_info_plist =
+            "$target_gen_dir/${_variant.target_name}_partial_info.plist"
 
-      if (!defined(deps)) {
-        deps = []
-      }
-      deps += [ ":$_generate_info_plist" ]
+        if (!defined(deps)) {
+          deps = []
+        }
+        deps += [ ":$_generate_info_plist" ]
 
-      if (_write_pkg_info) {
         if (!defined(bundle_deps)) {
           bundle_deps = []
         }
-        bundle_deps += [ ":$_bundle_data_pkg_info" ]
-      }
-
-      if (use_ios_simulator) {
-        if (!defined(data_deps)) {
-          data_deps = []
+        if (_write_pkg_info) {
+          bundle_deps += [ ":$_bundle_data_pkg_info" ]
         }
-        data_deps += [ "//testing/iossim" ]
+        bundle_deps += _variant.bundle_deps
+
+        if (use_ios_simulator) {
+          if (!defined(data_deps)) {
+            data_deps = []
+          }
+          data_deps += [ "//testing/iossim" ]
+        }
+      }
+    }
+
+    if (_default_variant.name != "") {
+      _bundle_short_name = "$_output_name$_bundle_extension"
+      action(_target_name) {
+        forward_variables_from(invoker, [ "testonly" ])
+
+        script = "//build/config/ios/hardlink.py"
+        public_deps = []
+        foreach(_variant, _variants) {
+          public_deps += [ ":${_variant.target_name}" ]
+        }
+
+        sources = [
+          "${_default_variant.bundle_gen_dir}/$_bundle_short_name",
+        ]
+        outputs = [
+          "$root_out_dir/$_bundle_short_name",
+        ]
+
+        args = rebase_path(sources, root_out_dir) +
+               rebase_path(outputs, root_out_dir)
       }
     }
   }
@@ -996,7 +1084,7 @@
 #     bundle_data("core_teleportation_bundle_data") {
 #       deps = [ ":CoreTeleportation" ]
 #       sources = [ "$root_out_dir/CoreTeleportation.framework" ]
-#       outputs = [ "{{bundle_root_dir}}/Frameworks/{{source_file_part}}" ]
+#       outputs = [ "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}" ]
 #     }
 #
 #     app_bundle("GoatTeleporter") {
@@ -1023,7 +1111,7 @@
 #     bundle_data("core_teleportation_bundle_data") {
 #       deps = [ ":CoreTeleportation+link" ]
 #       sources = [ "$root_out_dir/CoreTeleportation.framework" ]
-#       outputs = [ "{{bundle_root_dir}}/Frameworks/{{source_file_part}}" ]
+#       outputs = [ "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}" ]
 #     }
 #
 #     app_bundle("GoatTeleporter") {
@@ -1313,7 +1401,7 @@
       forward_variables_from(invoker, [ "testonly" ])
       sources = get_target_outputs(":$_info_plist_target")
       outputs = [
-        "{{bundle_root_dir}}/Info.plist",
+        "{{bundle_contents_dir}}/Info.plist",
       ]
       public_deps = [
         ":$_info_plist_target",
@@ -1520,7 +1608,7 @@
 
       sources = get_target_outputs(":$_info_plist_target")
       outputs = [
-        "{{bundle_root_dir}}/Info.plist",
+        "{{bundle_contents_dir}}/Info.plist",
       ]
     }
 
@@ -1772,7 +1860,7 @@
 
     sources = get_target_outputs(":$_info_plist_target")
     outputs = [
-      "{{bundle_root_dir}}/Info.plist",
+      "{{bundle_contents_dir}}/Info.plist",
     ]
   }
 
@@ -1786,7 +1874,7 @@
     ]
 
     outputs = [
-      "{{bundle_root_dir}}/PkgInfo",
+      "{{bundle_contents_dir}}/PkgInfo",
     ]
   }
 
diff --git a/build/config/mac/rules.gni b/build/config/mac/rules.gni
index dd7b4a5..d5a4561 100644
--- a/build/config/mac/rules.gni
+++ b/build/config/mac/rules.gni
@@ -175,7 +175,7 @@
 #     bundle_data("core_teleportation_bundle_data") {
 #       deps = [ ":CoreTeleportation" ]
 #       sources = [ "$root_out_dir/CoreTeleportation.framework" ]
-#       outputs = [ "{{bundle_root_dir}}/Frameworks/{{source_file_part}}" ]
+#       outputs = [ "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}" ]
 #     }
 #
 #     app_bundle("GoatTeleporter") {
@@ -202,7 +202,7 @@
 #     bundle_data("core_teleportation_bundle_data") {
 #       deps = [ ":CoreTeleportation+link" ]
 #       sources = [ "$root_out_dir/CoreTeleportation.framework" ]
-#       outputs = [ "{{bundle_root_dir}}/Frameworks/{{source_file_part}}" ]
+#       outputs = [ "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}" ]
 #     }
 #
 #     app_bundle("GoatTeleporter") {
@@ -421,9 +421,10 @@
       ":$_shared_library_bundle_data",
     ]
 
-    bundle_root_dir = _framework_root_dir
-    bundle_resources_dir = "$bundle_root_dir/Resources"
-    bundle_executable_dir = "$bundle_root_dir"
+    bundle_root_dir = _framework_base_dir
+    bundle_contents_dir = _framework_root_dir
+    bundle_resources_dir = "$bundle_contents_dir/Resources"
+    bundle_executable_dir = bundle_contents_dir
   }
 
   group(_target_name + "+link") {
@@ -579,7 +580,7 @@
     visibility = [ ":$_target_name" ]
     sources = get_target_outputs(":$_info_plist_target")
     outputs = [
-      "{{bundle_root_dir}}/Info.plist",
+      "{{bundle_contents_dir}}/Info.plist",
     ]
     public_deps = [
       ":$_info_plist_target",
@@ -594,7 +595,7 @@
       visibility = [ ":$_target_name" ]
       sources = get_target_outputs(":$_pkg_info_target")
       outputs = [
-        "{{bundle_root_dir}}/PkgInfo",
+        "{{bundle_contents_dir}}/PkgInfo",
       ]
       public_deps = [
         ":$_pkg_info_target",
@@ -621,10 +622,10 @@
       deps += [ ":$_pkg_info_bundle_data" ]
     }
     product_type = _product_type
-    bundle_root_dir =
-        "$root_out_dir/${_output_name}.${_output_extension}/Contents"
-    bundle_resources_dir = "$bundle_root_dir/Resources"
-    bundle_executable_dir = "$bundle_root_dir/MacOS"
+    bundle_root_dir = "$root_out_dir/${_output_name}.${_output_extension}"
+    bundle_contents_dir = "$bundle_root_dir/Contents"
+    bundle_resources_dir = "$bundle_contents_dir/Resources"
+    bundle_executable_dir = "$bundle_contents_dir/MacOS"
   }
 }
 
@@ -686,7 +687,8 @@
     }
     deps += [ ":$_loadable_module_bundle_data" ]
 
-    bundle_root_dir = "$root_out_dir/$_output_name.plugin/Contents"
-    bundle_executable_dir = "$bundle_root_dir/MacOS"
+    bundle_root_dir = "$root_out_dir/$_output_name.plugin"
+    bundle_contents_dir = "$bundle_root_dir/Contents"
+    bundle_executable_dir = "$bundle_contents_dir/MacOS"
   }
 }
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 65d2b69..56d68f26 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -282,8 +282,6 @@
     "trees/element_id.h",
     "trees/frame_rate_counter.cc",
     "trees/frame_rate_counter.h",
-    "trees/image_animation_controller.cc",
-    "trees/image_animation_controller.h",
     "trees/latency_info_swap_promise.cc",
     "trees/latency_info_swap_promise.h",
     "trees/latency_info_swap_promise_monitor.cc",
@@ -412,8 +410,6 @@
     "test/fake_output_surface.h",
     "test/fake_output_surface_client.cc",
     "test/fake_output_surface_client.h",
-    "test/fake_paint_image_generator.cc",
-    "test/fake_paint_image_generator.h",
     "test/fake_painted_scrollbar_layer.cc",
     "test/fake_painted_scrollbar_layer.h",
     "test/fake_picture_layer.cc",
@@ -489,6 +485,8 @@
     "test/stub_layer_tree_host_client.h",
     "test/stub_layer_tree_host_single_thread_client.cc",
     "test/stub_layer_tree_host_single_thread_client.h",
+    "test/stub_paint_image_generator.cc",
+    "test/stub_paint_image_generator.h",
     "test/task_graph_runner_test_template.cc",
     "test/task_graph_runner_test_template.h",
     "test/test_context_provider.cc",
@@ -647,7 +645,6 @@
     "tiles/tile_priority_unittest.cc",
     "trees/blocking_task_runner_unittest.cc",
     "trees/damage_tracker_unittest.cc",
-    "trees/image_animation_controller_unittest.cc",
     "trees/layer_tree_frame_sink_unittest.cc",
     "trees/layer_tree_host_common_unittest.cc",
     "trees/layer_tree_host_impl_unittest.cc",
diff --git a/cc/benchmarks/rasterize_and_record_benchmark_impl.cc b/cc/benchmarks/rasterize_and_record_benchmark_impl.cc
index e28e367..c0690b68 100644
--- a/cc/benchmarks/rasterize_and_record_benchmark_impl.cc
+++ b/cc/benchmarks/rasterize_and_record_benchmark_impl.cc
@@ -60,7 +60,7 @@
 
       PlaybackImageProvider image_provider(false, PaintImageIdFlatSet(), {},
                                            image_decode_cache,
-                                           gfx::ColorSpace(), {});
+                                           gfx::ColorSpace());
       RasterSource::PlaybackSettings settings;
       settings.image_provider = &image_provider;
 
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index e640106..f81e710 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -20,7 +20,6 @@
 #include "cc/debug/debug_colors.h"
 #include "cc/layers/append_quads_data.h"
 #include "cc/layers/solid_color_layer_impl.h"
-#include "cc/paint/display_item_list.h"
 #include "cc/tiles/tile_manager.h"
 #include "cc/tiles/tiling_set_raster_queue_all.h"
 #include "cc/trees/layer_tree_impl.h"
@@ -120,9 +119,6 @@
   if (twin_layer_)
     twin_layer_->twin_layer_ = nullptr;
   layer_tree_impl()->UnregisterPictureLayerImpl(this);
-
-  // Unregister for all images on the current raster source.
-  UnregisterAnimatedImages();
 }
 
 void PictureLayerImpl::SetLayerMaskType(Layer::LayerMaskType mask_type) {
@@ -602,27 +598,11 @@
       << " bounds " << bounds().ToString() << " pile "
       << raster_source->GetSize().ToString();
 
-  // We have an updated recording if the DisplayItemList in the new RasterSource
-  // is different.
-  const bool recording_updated =
-      !raster_source_ || raster_source_->GetDisplayItemList() !=
-                             raster_source->GetDisplayItemList();
-
-  // Unregister for all images on the current raster source, if the recording
-  // was updated.
-  if (recording_updated)
-    UnregisterAnimatedImages();
-
   // The |raster_source_| is initially null, so have to check for that for the
   // first frame.
   bool could_have_tilings = raster_source_.get() && CanHaveTilings();
   raster_source_.swap(raster_source);
 
-  // Register images from the new raster source, if the recording was updated.
-  // TODO(khushalsagar): UMA the number of animated images in layer?
-  if (recording_updated)
-    RegisterAnimatedImages();
-
   // The |new_invalidation| must be cleared before updating tilings since they
   // access the invalidation through the PictureLayerTilingClient interface.
   invalidation_.Clear();
@@ -766,26 +746,6 @@
   return GetScaledEnclosingRectInTargetSpace(MaximumTilingContentsScale());
 }
 
-bool PictureLayerImpl::ShouldAnimate(PaintImage::Id paint_image_id) const {
-  DCHECK(raster_source_);
-
-  // Only animate images for layers which HasValidTilePriorities. This check is
-  // important for 2 reasons:
-  // 1) It avoids doing additional work for layers we don't plan to rasterize
-  //    and/or draw. The updated state will be pulled by the animation system
-  //    if the draw properties change.
-  // 2) It eliminates considering layers on the recycle tree. Once the pending
-  //    tree is activated, the layers on the recycle tree remain registered as
-  //    animation drivers, but should not drive animations since they don't have
-  //    updated draw properties.
-  //
-  //  Additionally only animate images which are on-screen, animations are
-  //  paused once they are not visible.
-  return HasValidTilePriorities() &&
-         raster_source_->GetRectForImage(paint_image_id)
-             .Intersects(visible_layer_rect());
-}
-
 gfx::Size PictureLayerImpl::CalculateTileSize(
     const gfx::Size& content_bounds) const {
   int max_texture_size =
@@ -1542,38 +1502,4 @@
                    "Invalidation", invalidation.ToString());
 }
 
-void PictureLayerImpl::RegisterAnimatedImages() {
-  if (!raster_source_ || !raster_source_->GetDisplayItemList())
-    return;
-
-  auto* controller = layer_tree_impl()->image_animation_controller();
-  if (!controller)
-    return;
-
-  const auto& metadata = raster_source_->GetDisplayItemList()
-                             ->discardable_image_map()
-                             .animated_images_metadata();
-  for (const auto& data : metadata) {
-    // Only update the metadata from updated recordings received from a commit.
-    if (layer_tree_impl()->IsSyncTree())
-      controller->UpdateAnimatedImage(data);
-    controller->RegisterAnimationDriver(data.paint_image_id, this);
-  }
-}
-
-void PictureLayerImpl::UnregisterAnimatedImages() {
-  if (!raster_source_ || !raster_source_->GetDisplayItemList())
-    return;
-
-  auto* controller = layer_tree_impl()->image_animation_controller();
-  if (!controller)
-    return;
-
-  const auto& metadata = raster_source_->GetDisplayItemList()
-                             ->discardable_image_map()
-                             .animated_images_metadata();
-  for (const auto& data : metadata)
-    controller->UnregisterAnimationDriver(data.paint_image_id, this);
-}
-
 }  // namespace cc
diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h
index 10ebaed..62cdcff 100644
--- a/cc/layers/picture_layer_impl.h
+++ b/cc/layers/picture_layer_impl.h
@@ -20,7 +20,6 @@
 #include "cc/tiles/picture_layer_tiling.h"
 #include "cc/tiles/picture_layer_tiling_set.h"
 #include "cc/tiles/tiling_set_eviction_queue.h"
-#include "cc/trees/image_animation_controller.h"
 
 namespace cc {
 
@@ -28,10 +27,8 @@
 class MicroBenchmarkImpl;
 class Tile;
 
-class CC_EXPORT PictureLayerImpl
-    : public LayerImpl,
-      public PictureLayerTilingClient,
-      public ImageAnimationController::AnimationDriver {
+class CC_EXPORT PictureLayerImpl : public LayerImpl,
+                                   public PictureLayerTilingClient {
  public:
   static std::unique_ptr<PictureLayerImpl>
   Create(LayerTreeImpl* tree_impl, int id, Layer::LayerMaskType mask_type) {
@@ -66,9 +63,6 @@
   bool RequiresHighResToDraw() const override;
   gfx::Rect GetEnclosingRectInTargetSpace() const override;
 
-  // ImageAnimationController::AnimationDriver overrides.
-  bool ShouldAnimate(PaintImage::Id paint_image_id) const override;
-
   void set_gpu_raster_max_texture_size(gfx::Size gpu_raster_max_texture_size) {
     gpu_raster_max_texture_size_ = gpu_raster_max_texture_size;
   }
@@ -113,8 +107,6 @@
 
   bool RasterSourceUsesLCDTextForTesting() const { return can_use_lcd_text_; }
 
-  const Region& InvalidationForTesting() const { return invalidation_; }
-
  protected:
   PictureLayerImpl(LayerTreeImpl* tree_impl,
                    int id,
@@ -144,9 +136,6 @@
   float MaximumTilingContentsScale() const;
   std::unique_ptr<PictureLayerTilingSet> CreatePictureLayerTilingSet();
 
-  void RegisterAnimatedImages();
-  void UnregisterAnimatedImages();
-
   PictureLayerImpl* twin_layer_;
 
   std::unique_ptr<PictureLayerTilingSet> tilings_;
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc
index 465463d..f80d13ca 100644
--- a/cc/layers/picture_layer_impl_unittest.cc
+++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -29,7 +29,6 @@
 #include "cc/test/fake_recording_source.h"
 #include "cc/test/geometry_test_utils.h"
 #include "cc/test/layer_test_common.h"
-#include "cc/test/skia_common.h"
 #include "cc/test/test_layer_tree_host_base.h"
 #include "cc/test/test_task_graph_runner.h"
 #include "cc/test/test_web_graphics_context_3d.h"
@@ -85,7 +84,6 @@
     settings.create_low_res_tiling = true;
     settings.resource_settings.buffer_to_texture_target_map =
         viz::DefaultBufferToTextureTargetMapForTesting();
-    settings.enable_image_animations = true;
     return settings;
   }
 
@@ -5201,59 +5199,5 @@
   }
 }
 
-TEST_F(PictureLayerImplTest, AnimatedImages) {
-  gfx::Size layer_bounds(1000, 1000);
-
-  // Set up a raster source with 2 animated images.
-  auto recording_source = FakeRecordingSource::CreateRecordingSource(
-      gfx::Rect(layer_bounds), layer_bounds);
-  std::vector<FrameMetadata> frames = {
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(1)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(1))};
-  PaintImage image1 = CreateAnimatedImage(gfx::Size(200, 200), frames);
-  PaintImage image2 = CreateAnimatedImage(gfx::Size(200, 200), frames);
-  recording_source->add_draw_image(image1, gfx::Point(100, 100));
-  recording_source->add_draw_image(image2, gfx::Point(500, 500));
-  recording_source->Rerecord();
-  scoped_refptr<RasterSource> raster_source =
-      recording_source->CreateRasterSource();
-
-  // All images should be registered on the pending layer.
-  SetupPendingTree(raster_source, gfx::Size(), Region(gfx::Rect(layer_bounds)));
-  auto* controller = host_impl()->image_animation_controller();
-  EXPECT_EQ(controller->GetDriversForTesting(image1.stable_id())
-                .count(pending_layer()),
-            1u);
-  EXPECT_EQ(controller->GetDriversForTesting(image2.stable_id())
-                .count(pending_layer()),
-            1u);
-
-  // Make only the first image visible and verify that only this image is
-  // animated.
-  gfx::Rect visible_rect(0, 0, 300, 300);
-  pending_layer()->set_visible_layer_rect(visible_rect);
-  EXPECT_TRUE(pending_layer()->ShouldAnimate(image1.stable_id()));
-  EXPECT_FALSE(pending_layer()->ShouldAnimate(image2.stable_id()));
-
-  // Now activate and make sure the active layer is registered as well.
-  ActivateTree();
-  active_layer()->set_visible_layer_rect(visible_rect);
-  EXPECT_EQ(controller->GetDriversForTesting(image1.stable_id())
-                .count(active_layer()),
-            1u);
-  EXPECT_EQ(controller->GetDriversForTesting(image2.stable_id())
-                .count(active_layer()),
-            1u);
-
-  // Once activated, only the active layer should drive animations for these
-  // images. Since DrawProperties are not updated on the recycle tree, it has
-  // stale state for visibility of images.
-  ASSERT_EQ(old_pending_layer()->visible_layer_rect(), visible_rect);
-  EXPECT_FALSE(old_pending_layer()->ShouldAnimate(image1.stable_id()));
-  EXPECT_FALSE(old_pending_layer()->ShouldAnimate(image2.stable_id()));
-  EXPECT_TRUE(active_layer()->ShouldAnimate(image1.stable_id()));
-  EXPECT_FALSE(active_layer()->ShouldAnimate(image2.stable_id()));
-}
-
 }  // namespace
 }  // namespace cc
diff --git a/cc/layers/recording_source_unittest.cc b/cc/layers/recording_source_unittest.cc
index 82c424f..002ec5c 100644
--- a/cc/layers/recording_source_unittest.cc
+++ b/cc/layers/recording_source_unittest.cc
@@ -111,8 +111,7 @@
     std::vector<const DrawImage*> images;
     raster_source->GetDiscardableImagesInRect(gfx::Rect(130, 0, 128, 128),
                                               &images);
-    DrawImage image(*images[0], scale, PaintImage::kDefaultFrameIndex,
-                    DefaultColorSpace());
+    DrawImage image(*images[0], scale, DefaultColorSpace());
     EXPECT_EQ(1u, images.size());
     EXPECT_FLOAT_EQ(scale, image.scale().width());
     EXPECT_FLOAT_EQ(scale, image.scale().height());
diff --git a/cc/paint/discardable_image_map.h b/cc/paint/discardable_image_map.h
index d3b66aa..b3113020 100644
--- a/cc/paint/discardable_image_map.h
+++ b/cc/paint/discardable_image_map.h
@@ -30,7 +30,7 @@
 // rect and get back a list of DrawImages in that rect.
 class CC_PAINT_EXPORT DiscardableImageMap {
  public:
-  struct CC_PAINT_EXPORT AnimatedImageMetadata {
+  struct AnimatedImageMetadata {
     AnimatedImageMetadata(PaintImage::Id paint_image_id,
                           PaintImage::CompletionState completion_state,
                           std::vector<FrameMetadata> frames,
diff --git a/cc/paint/discardable_image_map_unittest.cc b/cc/paint/discardable_image_map_unittest.cc
index 0e8a6d20e..fcfc074 100644
--- a/cc/paint/discardable_image_map_unittest.cc
+++ b/cc/paint/discardable_image_map_unittest.cc
@@ -9,7 +9,6 @@
 #include <memory>
 
 #include "base/memory/ref_counted.h"
-#include "base/test/gtest_util.h"
 #include "base/values.h"
 #include "cc/base/region.h"
 #include "cc/paint/paint_flags.h"
@@ -61,8 +60,7 @@
     image_map.GetDiscardableImagesInRect(rect, &draw_image_ptrs);
     std::vector<DrawImage> draw_images;
     for (const auto* image : draw_image_ptrs)
-      draw_images.push_back(DrawImage(
-          *image, 1.f, PaintImage::kDefaultFrameIndex, target_color_space));
+      draw_images.push_back(DrawImage(*image, 1.f, target_color_space));
 
     std::vector<PositionScaleDrawImage> position_draw_images;
     for (DrawImage& image : image_map.images_rtree_.Search(rect)) {
@@ -700,16 +698,11 @@
   FakeContentLayerClient content_layer_client;
   content_layer_client.set_bounds(visible_rect.size());
 
-  std::vector<FrameMetadata> frames = {
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))};
-
   gfx::Size image_size(100, 100);
   PaintImage static_image = CreateDiscardablePaintImage(image_size);
   PaintImage animated_loop_none =
-      CreateAnimatedImage(image_size, frames, kAnimationNone);
-  PaintImage animation_loop_infinite =
-      CreateAnimatedImage(image_size, frames, 1u);
+      CreateAnimatedImage(image_size, {FrameMetadata()}, kAnimationNone);
+  PaintImage animation_loop_infinite = CreateAnimatedImage(image_size);
 
   PaintFlags flags;
   content_layer_client.add_draw_image(static_image, gfx::Point(0, 0), flags);
@@ -734,17 +727,6 @@
             animation_loop_infinite.GetFrameMetadata());
   EXPECT_EQ(animated_images_metadata[0].repetition_count,
             animation_loop_infinite.repetition_count());
-
-  std::vector<const DrawImage*> images;
-  display_list->discardable_image_map().GetDiscardableImagesInRect(visible_rect,
-                                                                   &images);
-  ASSERT_EQ(images.size(), 3u);
-  EXPECT_EQ(images[0]->paint_image(), static_image);
-  EXPECT_DCHECK_DEATH(images[0]->frame_index());
-  EXPECT_EQ(images[1]->paint_image(), animated_loop_none);
-  EXPECT_DCHECK_DEATH(images[1]->frame_index());
-  EXPECT_EQ(images[2]->paint_image(), animation_loop_infinite);
-  EXPECT_DCHECK_DEATH(images[2]->frame_index());
 }
 
 class DiscardableImageMapColorSpaceTest
diff --git a/cc/paint/draw_image.cc b/cc/paint/draw_image.cc
index a1a78af..7b7314b 100644
--- a/cc/paint/draw_image.cc
+++ b/cc/paint/draw_image.cc
@@ -32,19 +32,16 @@
                      const SkIRect& src_rect,
                      SkFilterQuality filter_quality,
                      const SkMatrix& matrix,
-                     base::Optional<size_t> frame_index,
                      const base::Optional<gfx::ColorSpace>& color_space)
     : paint_image_(std::move(image)),
       src_rect_(src_rect),
       filter_quality_(filter_quality),
-      frame_index_(frame_index),
       target_color_space_(color_space) {
   matrix_is_decomposable_ = ExtractScale(matrix, &scale_);
 }
 
 DrawImage::DrawImage(const DrawImage& other,
                      float scale_adjustment,
-                     size_t frame_index,
                      const gfx::ColorSpace& color_space)
     : paint_image_(other.paint_image_),
       src_rect_(other.src_rect_),
@@ -52,7 +49,6 @@
       scale_(SkSize::Make(other.scale_.width() * scale_adjustment,
                           other.scale_.height() * scale_adjustment)),
       matrix_is_decomposable_(other.matrix_is_decomposable_),
-      frame_index_(frame_index),
       target_color_space_(color_space) {}
 
 DrawImage::DrawImage(const DrawImage& other) = default;
diff --git a/cc/paint/draw_image.h b/cc/paint/draw_image.h
index 81c45d1..58c46d9 100644
--- a/cc/paint/draw_image.h
+++ b/cc/paint/draw_image.h
@@ -25,13 +25,11 @@
             const SkIRect& src_rect,
             SkFilterQuality filter_quality,
             const SkMatrix& matrix,
-            base::Optional<size_t> frame_index = base::nullopt,
             const base::Optional<gfx::ColorSpace>& color_space = base::nullopt);
   // Constructs a DrawImage from |other| by adjusting its scale and setting a
   // new color_space.
   DrawImage(const DrawImage& other,
             float scale_adjustment,
-            size_t frame_index,
             const gfx::ColorSpace& color_space);
   DrawImage(const DrawImage& other);
   DrawImage(DrawImage&& other);
@@ -52,11 +50,7 @@
     return *target_color_space_;
   }
   PaintImage::FrameKey frame_key() const {
-    return paint_image_.GetKeyForFrame(frame_index());
-  }
-  size_t frame_index() const {
-    DCHECK(frame_index_.has_value());
-    return frame_index_.value();
+    return paint_image_.GetKeyForFrame(paint_image_.frame_index());
   }
 
  private:
@@ -65,7 +59,6 @@
   SkFilterQuality filter_quality_;
   SkSize scale_;
   bool matrix_is_decomposable_;
-  base::Optional<size_t> frame_index_;
   base::Optional<gfx::ColorSpace> target_color_space_;
 };
 
diff --git a/cc/paint/paint_image.cc b/cc/paint/paint_image.cc
index f941d60..b18eb20 100644
--- a/cc/paint/paint_image.cc
+++ b/cc/paint/paint_image.cc
@@ -18,9 +18,6 @@
 base::AtomicSequenceNumber s_next_content_id_;
 }  // namespace
 
-const int PaintImage::kNonLazyStableId = -1;
-const size_t PaintImage::kDefaultFrameIndex = 0u;
-
 PaintImage::PaintImage() = default;
 PaintImage::PaintImage(const PaintImage& other) = default;
 PaintImage::PaintImage(PaintImage&& other) = default;
@@ -121,8 +118,7 @@
 
 bool PaintImage::Decode(void* memory,
                         SkImageInfo* info,
-                        sk_sp<SkColorSpace> color_space,
-                        size_t frame_index) const {
+                        sk_sp<SkColorSpace> color_space) const {
   // We only support decode to supported decode size.
   DCHECK(info->dimensions() == GetSupportedDecodeSize(info->dimensions()));
 
@@ -133,15 +129,13 @@
   // requested size into the space of the original image. For now, fallback to
   // DecodeFromSkImage().
   if (paint_image_generator_ && subset_rect_.IsEmpty())
-    return DecodeFromGenerator(memory, info, std::move(color_space),
-                               frame_index);
-  return DecodeFromSkImage(memory, info, std::move(color_space), frame_index);
+    return DecodeFromGenerator(memory, info, std::move(color_space));
+  return DecodeFromSkImage(memory, info, std::move(color_space));
 }
 
 bool PaintImage::DecodeFromGenerator(void* memory,
                                      SkImageInfo* info,
-                                     sk_sp<SkColorSpace> color_space,
-                                     size_t frame_index) const {
+                                     sk_sp<SkColorSpace> color_space) const {
   DCHECK(subset_rect_.IsEmpty());
 
   // First convert the info to have the requested color space, since the decoder
@@ -157,7 +151,7 @@
 
     bool result = paint_image_generator_->GetPixels(n32info, n32memory.get(),
                                                     n32info.minRowBytes(),
-                                                    frame_index, unique_id());
+                                                    frame_index(), unique_id());
     if (!result)
       return false;
 
@@ -178,14 +172,13 @@
   }
 
   return paint_image_generator_->GetPixels(*info, memory, info->minRowBytes(),
-                                           frame_index, unique_id());
+                                           frame_index(), unique_id());
 }
 
 bool PaintImage::DecodeFromSkImage(void* memory,
                                    SkImageInfo* info,
-                                   sk_sp<SkColorSpace> color_space,
-                                   size_t frame_index) const {
-  auto image = GetSkImageForFrame(frame_index);
+                                   sk_sp<SkColorSpace> color_space) const {
+  auto image = GetSkImage();
   DCHECK(image);
   if (color_space) {
     image =
@@ -203,7 +196,7 @@
 
 bool PaintImage::ShouldAnimate() const {
   return animation_type_ == AnimationType::ANIMATED &&
-         repetition_count_ != kAnimationNone && FrameCount() > 1;
+         repetition_count_ != kAnimationNone;
 }
 
 PaintImage::FrameKey PaintImage::GetKeyForFrame(size_t frame_index) const {
@@ -237,19 +230,6 @@
              : 1u;
 }
 
-sk_sp<SkImage> PaintImage::GetSkImageForFrame(size_t index) const {
-  DCHECK_LT(index, FrameCount());
-
-  if (index == frame_index_)
-    return GetSkImage();
-
-  sk_sp<SkImage> image = SkImage::MakeFromGenerator(
-      base::MakeUnique<SkiaPaintImageGenerator>(paint_image_generator_, index));
-  if (!subset_rect_.IsEmpty())
-    image = image->makeSubset(gfx::RectToSkIRect(subset_rect_));
-  return image;
-}
-
 std::string PaintImage::ToString() const {
   std::ostringstream str;
   str << "sk_image_: " << sk_image_ << " paint_record_: " << paint_record_
diff --git a/cc/paint/paint_image.h b/cc/paint/paint_image.h
index 83e237b..9eeff94 100644
--- a/cc/paint/paint_image.h
+++ b/cc/paint/paint_image.h
@@ -39,11 +39,11 @@
   // An id that can be used for all non-lazy images. Note that if an image is
   // not lazy, it does not mean that this id must be used; one can still use
   // GetNextId to generate a stable id for such images.
-  static const Id kNonLazyStableId;
+  static const Id kNonLazyStableId = -1;
 
   // The default frame index to use if no index is provided. For multi-frame
   // images, this would imply the first frame of the animation.
-  static const size_t kDefaultFrameIndex;
+  static const size_t kDefaultFrameIndex = 0;
 
   class CC_PAINT_EXPORT FrameKey {
    public:
@@ -53,7 +53,6 @@
 
     uint64_t hash() const { return hash_; }
     std::string ToString() const;
-    size_t frame_index() const { return frame_index_; }
 
    private:
     ContentId content_id_;
@@ -113,8 +112,7 @@
   // is texture backed.
   bool Decode(void* memory,
               SkImageInfo* info,
-              sk_sp<SkColorSpace> color_space,
-              size_t frame_index) const;
+              sk_sp<SkColorSpace> color_space) const;
 
   Id stable_id() const { return id_; }
   const sk_sp<SkImage>& GetSkImage() const;
@@ -144,9 +142,6 @@
   // Returns the total number of frames known to exist in this image.
   size_t FrameCount() const;
 
-  // Returns an SkImage for the frame at |index|.
-  sk_sp<SkImage> GetSkImageForFrame(size_t index) const;
-
   std::string ToString() const;
 
  private:
@@ -156,12 +151,10 @@
 
   bool DecodeFromGenerator(void* memory,
                            SkImageInfo* info,
-                           sk_sp<SkColorSpace> color_space,
-                           size_t frame_index) const;
+                           sk_sp<SkColorSpace> color_space) const;
   bool DecodeFromSkImage(void* memory,
                          SkImageInfo* info,
-                         sk_sp<SkColorSpace> color_space,
-                         size_t frame_index) const;
+                         sk_sp<SkColorSpace> color_space) const;
 
   sk_sp<SkImage> sk_image_;
 
diff --git a/cc/paint/paint_image_builder.cc b/cc/paint/paint_image_builder.cc
index da6387db..399ac0b 100644
--- a/cc/paint/paint_image_builder.cc
+++ b/cc/paint/paint_image_builder.cc
@@ -43,13 +43,6 @@
     DCHECK(!paint_image_.sk_image_);
     DCHECK(!paint_image_.paint_record_);
   }
-
-  if (paint_image_.ShouldAnimate()) {
-    DCHECK(paint_image_.paint_image_generator_)
-        << "Animated images must provide a generator";
-    for (const auto& frame : paint_image_.GetFrameMetadata())
-      DCHECK_GT(frame.duration, base::TimeDelta());
-  }
 #endif
 
   return std::move(paint_image_);
diff --git a/cc/paint/paint_image_unittest.cc b/cc/paint/paint_image_unittest.cc
index d3f743b..0fe9a4ff 100644
--- a/cc/paint/paint_image_unittest.cc
+++ b/cc/paint/paint_image_unittest.cc
@@ -5,8 +5,6 @@
 #include "cc/paint/paint_image.h"
 
 #include "base/test/gtest_util.h"
-#include "cc/paint/paint_image_builder.h"
-#include "cc/test/fake_paint_image_generator.h"
 #include "cc/test/skia_common.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -33,42 +31,4 @@
   EXPECT_DCHECK_DEATH(image.MakeSubset(gfx::Rect()));
 }
 
-TEST(PaintImageTest, DecodesCorrectFrames) {
-  std::vector<FrameMetadata> frames = {
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))};
-  sk_sp<FakePaintImageGenerator> generator =
-      sk_make_sp<FakePaintImageGenerator>(SkImageInfo::MakeN32Premul(10, 10),
-                                          frames);
-  PaintImage image = PaintImageBuilder()
-                         .set_id(PaintImage::GetNextId())
-                         .set_paint_image_generator(generator)
-                         .set_frame_index(0u)
-                         .TakePaintImage();
-
-  // The recorded index is 0u but ask for 1u frame.
-  SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
-  std::vector<size_t> memory(info.getSafeSize(info.minRowBytes()));
-  image.Decode(memory.data(), &info, nullptr, 1u);
-  ASSERT_EQ(generator->frames_decoded().size(), 1u);
-  EXPECT_EQ(generator->frames_decoded().count(1u), 1u);
-  generator->reset_frames_decoded();
-
-  // Subsetted.
-  PaintImage subset_image = image.MakeSubset(gfx::Rect(0, 0, 5, 5));
-  SkImageInfo subset_info = info.makeWH(5, 5);
-  subset_image.Decode(memory.data(), &subset_info, nullptr, 1u);
-  ASSERT_EQ(generator->frames_decoded().size(), 1u);
-  EXPECT_EQ(generator->frames_decoded().count(1u), 1u);
-  generator->reset_frames_decoded();
-
-  // Not N32 color type.
-  info.makeColorType(kRGB_565_SkColorType);
-  memory = std::vector<size_t>(info.getSafeSize(info.minRowBytes()));
-  image.Decode(memory.data(), &info, nullptr, 1u);
-  ASSERT_EQ(generator->frames_decoded().size(), 1u);
-  EXPECT_EQ(generator->frames_decoded().count(1u), 1u);
-  generator->reset_frames_decoded();
-}
-
 }  // namespace cc
diff --git a/cc/raster/playback_image_provider.cc b/cc/raster/playback_image_provider.cc
index 86bc53b..d6856fd 100644
--- a/cc/raster/playback_image_provider.cc
+++ b/cc/raster/playback_image_provider.cc
@@ -22,14 +22,12 @@
     PaintImageIdFlatSet images_to_skip,
     std::vector<DrawImage> at_raster_images,
     ImageDecodeCache* cache,
-    const gfx::ColorSpace& target_color_space,
-    base::flat_map<PaintImage::Id, size_t> image_to_current_frame_index)
+    const gfx::ColorSpace& target_color_space)
     : skip_all_images_(skip_all_images),
       images_to_skip_(std::move(images_to_skip)),
       at_raster_images_(std::move(at_raster_images)),
       cache_(cache),
-      target_color_space_(target_color_space),
-      image_to_current_frame_index_(std::move(image_to_current_frame_index)) {
+      target_color_space_(target_color_space) {
   DCHECK(cache_);
 }
 
@@ -79,14 +77,8 @@
                          SkSize::Make(1.f, 1.f), draw_image.filter_quality()));
   }
 
-  const auto& it = image_to_current_frame_index_.find(paint_image.stable_id());
-  size_t frame_index = it == image_to_current_frame_index_.end()
-                           ? paint_image.frame_index()
-                           : it->second;
-
-  DrawImage adjusted_image(draw_image, 1.f, frame_index, target_color_space_);
+  DrawImage adjusted_image(draw_image, 1.f, target_color_space_);
   auto decoded_draw_image = cache_->GetDecodedImageForDraw(adjusted_image);
-
   return ScopedDecodedDrawImage(
       decoded_draw_image,
       base::BindOnce(&UnrefImageFromCache, std::move(adjusted_image), cache_));
diff --git a/cc/raster/playback_image_provider.h b/cc/raster/playback_image_provider.h
index ddd2633c..bb2fc34 100644
--- a/cc/raster/playback_image_provider.h
+++ b/cc/raster/playback_image_provider.h
@@ -5,7 +5,6 @@
 #ifndef CC_RASTER_PLAYBACK_IMAGE_PROVIDER_H_
 #define CC_RASTER_PLAYBACK_IMAGE_PROVIDER_H_
 
-#include "base/containers/flat_map.h"
 #include "cc/cc_export.h"
 #include "cc/paint/image_id.h"
 #include "cc/paint/image_provider.h"
@@ -22,13 +21,11 @@
 //    only be used for lazy generated images.
 class CC_EXPORT PlaybackImageProvider : public ImageProvider {
  public:
-  PlaybackImageProvider(
-      bool skip_all_images,
-      PaintImageIdFlatSet images_to_skip,
-      std::vector<DrawImage> at_raster_images,
-      ImageDecodeCache* cache,
-      const gfx::ColorSpace& taget_color_space,
-      base::flat_map<PaintImage::Id, size_t> image_to_current_frame_index);
+  PlaybackImageProvider(bool skip_all_images,
+                        PaintImageIdFlatSet images_to_skip,
+                        std::vector<DrawImage> at_raster_images,
+                        ImageDecodeCache* cache,
+                        const gfx::ColorSpace& taget_color_space);
   ~PlaybackImageProvider() override;
 
   void BeginRaster() override;
@@ -49,7 +46,6 @@
   std::vector<ImageProvider::ScopedDecodedDrawImage> decoded_at_raster_;
   ImageDecodeCache* cache_;
   gfx::ColorSpace target_color_space_;
-  base::flat_map<PaintImage::Id, size_t> image_to_current_frame_index_;
 
   DISALLOW_COPY_AND_ASSIGN(PlaybackImageProvider);
 };
diff --git a/cc/raster/playback_image_provider_unittest.cc b/cc/raster/playback_image_provider_unittest.cc
index 153b484..42d7783 100644
--- a/cc/raster/playback_image_provider_unittest.cc
+++ b/cc/raster/playback_image_provider_unittest.cc
@@ -30,7 +30,6 @@
 
   DecodedDrawImage GetDecodedImageForDraw(
       const DrawImage& draw_image) override {
-    last_image_ = draw_image;
     images_decoded_++;
     refed_image_count_++;
     return CreateDecode();
@@ -45,17 +44,15 @@
 
   int refed_image_count() const { return refed_image_count_; }
   int images_decoded() const { return images_decoded_; }
-  const DrawImage& last_image() { return last_image_; }
 
  private:
   int refed_image_count_ = 0;
   int images_decoded_ = 0;
-  DrawImage last_image_;
 };
 
 TEST(PlaybackImageProviderTest, SkipsAllImages) {
   MockDecodeCache cache;
-  PlaybackImageProvider provider(true, {}, {}, &cache, gfx::ColorSpace(), {});
+  PlaybackImageProvider provider(true, {}, {}, &cache, gfx::ColorSpace());
   provider.BeginRaster();
 
   SkIRect rect = SkIRect::MakeWH(10, 10);
@@ -80,7 +77,7 @@
   MockDecodeCache cache;
   PaintImage skip_image = CreateDiscardablePaintImage(gfx::Size(10, 10));
   PlaybackImageProvider provider(false, {skip_image.stable_id()}, {}, &cache,
-                                 gfx::ColorSpace(), {});
+                                 gfx::ColorSpace());
   provider.BeginRaster();
 
   SkIRect rect = SkIRect::MakeWH(10, 10);
@@ -93,7 +90,7 @@
 
 TEST(PlaybackImageProviderTest, RefAndUnrefDecode) {
   MockDecodeCache cache;
-  PlaybackImageProvider provider(false, {}, {}, &cache, gfx::ColorSpace(), {});
+  PlaybackImageProvider provider(false, {}, {}, &cache, gfx::ColorSpace());
   provider.BeginRaster();
 
   {
@@ -123,7 +120,7 @@
       size, nullptr, rect, kMedium_SkFilterQuality, matrix);
 
   PlaybackImageProvider provider(false, {}, {draw_image1, draw_image2}, &cache,
-                                 gfx::ColorSpace(), {});
+                                 gfx::ColorSpace());
 
   EXPECT_EQ(cache.refed_image_count(), 0);
   provider.BeginRaster();
@@ -135,29 +132,5 @@
   EXPECT_EQ(cache.images_decoded(), 2);
 }
 
-TEST(PlaybackImageProviderTest, SwapsGivenFrames) {
-  MockDecodeCache cache;
-  std::vector<FrameMetadata> frames = {
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))};
-  PaintImage image = CreateAnimatedImage(gfx::Size(10, 10), frames);
-
-  base::flat_map<PaintImage::Id, size_t> image_to_frame;
-  image_to_frame[image.stable_id()] = 1u;
-  PlaybackImageProvider provider(false, {}, {}, &cache, gfx::ColorSpace(),
-                                 image_to_frame);
-  provider.BeginRaster();
-
-  SkIRect rect = SkIRect::MakeWH(10, 10);
-  SkMatrix matrix = SkMatrix::I();
-  DrawImage draw_image(image, rect, kMedium_SkFilterQuality, matrix);
-  provider.GetDecodedDrawImage(draw_image);
-  ASSERT_TRUE(cache.last_image().paint_image());
-  ASSERT_EQ(cache.last_image().paint_image(), image);
-  ASSERT_EQ(cache.last_image().frame_index(), 1u);
-
-  provider.EndRaster();
-}
-
 }  // namespace
 }  // namespace cc
diff --git a/cc/raster/raster_source_unittest.cc b/cc/raster/raster_source_unittest.cc
index b89083e..b6de90c 100644
--- a/cc/raster/raster_source_unittest.cc
+++ b/cc/raster/raster_source_unittest.cc
@@ -215,8 +215,7 @@
     std::vector<const DrawImage*> images;
     raster->GetDiscardableImagesInRect(gfx::Rect(0, 0, 256, 256), &images);
     EXPECT_EQ(1u, images.size());
-    DrawImage image(*images[0], 1.f, PaintImage::kDefaultFrameIndex,
-                    target_color_space);
+    DrawImage image(*images[0], 1.f, target_color_space);
     EXPECT_EQ(discardable_image[0][0], images[0]->paint_image());
     EXPECT_EQ(target_color_space, image.target_color_space());
   }
@@ -226,8 +225,7 @@
     std::vector<const DrawImage*> images;
     raster->GetDiscardableImagesInRect(gfx::Rect(260, 260, 256, 256), &images);
     EXPECT_EQ(1u, images.size());
-    DrawImage image(*images[0], 1.f, PaintImage::kDefaultFrameIndex,
-                    target_color_space);
+    DrawImage image(*images[0], 1.f, target_color_space);
     EXPECT_EQ(discardable_image[1][1], images[0]->paint_image());
     EXPECT_EQ(target_color_space, image.target_color_space());
   }
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc
index f101af6..494ecabe 100644
--- a/cc/scheduler/scheduler_state_machine.cc
+++ b/cc/scheduler/scheduler_state_machine.cc
@@ -592,8 +592,6 @@
 
   // Only perform impl side invalidation after the frame ends so that we wait
   // for any commit to happen before invalidating.
-  // TODO(khushalsagar): Invalidate at the beginning of the frame if there is no
-  // commit request from the main thread.
   if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE)
     return false;
 
diff --git a/cc/test/fake_paint_image_generator.cc b/cc/test/fake_paint_image_generator.cc
deleted file mode 100644
index bf0412d..0000000
--- a/cc/test/fake_paint_image_generator.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <cc/test/fake_paint_image_generator.h>
-
-namespace cc {
-
-FakePaintImageGenerator::FakePaintImageGenerator(
-    const SkImageInfo& info,
-    std::vector<FrameMetadata> frames)
-    : PaintImageGenerator(info, std::move(frames)),
-      image_backing_memory_(info.getSafeSize(info.minRowBytes()), 0),
-      image_pixmap_(info, image_backing_memory_.data(), info.minRowBytes()) {}
-
-FakePaintImageGenerator::~FakePaintImageGenerator() = default;
-
-sk_sp<SkData> FakePaintImageGenerator::GetEncodedData() const {
-  return nullptr;
-}
-
-bool FakePaintImageGenerator::GetPixels(const SkImageInfo& info,
-                                        void* pixels,
-                                        size_t row_bytes,
-                                        size_t frame_index,
-                                        uint32_t lazy_pixel_ref) {
-  frames_decoded_.insert(frame_index);
-  return true;
-}
-
-bool FakePaintImageGenerator::QueryYUV8(SkYUVSizeInfo* info,
-                                        SkYUVColorSpace* color_space) const {
-  return false;
-}
-
-bool FakePaintImageGenerator::GetYUV8Planes(const SkYUVSizeInfo& info,
-                                            void* planes[3],
-                                            size_t frame_index,
-                                            uint32_t lazy_pixel_ref) {
-  NOTREACHED();
-  return false;
-}
-
-}  // namespace cc
diff --git a/cc/test/fake_paint_image_generator.h b/cc/test/fake_paint_image_generator.h
deleted file mode 100644
index c8f4491..0000000
--- a/cc/test/fake_paint_image_generator.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CC_TEST_FAKE_PAINT_IMAGE_GENERATOR_H_
-#define CC_TEST_FAKE_PAINT_IMAGE_GENERATOR_H_
-
-#include "base/containers/flat_set.h"
-#include "cc/paint/paint_image_generator.h"
-
-namespace cc {
-
-class FakePaintImageGenerator : public PaintImageGenerator {
- public:
-  explicit FakePaintImageGenerator(const SkImageInfo& info,
-                                   std::vector<FrameMetadata> frames = {
-                                       FrameMetadata()});
-  ~FakePaintImageGenerator() override;
-
-  sk_sp<SkData> GetEncodedData() const override;
-  bool GetPixels(const SkImageInfo& info,
-                 void* pixels,
-                 size_t row_bytes,
-                 size_t frame_index,
-                 uint32_t lazy_pixel_ref) override;
-  bool QueryYUV8(SkYUVSizeInfo* info,
-                 SkYUVColorSpace* color_space) const override;
-  bool GetYUV8Planes(const SkYUVSizeInfo& info,
-                     void* planes[3],
-                     size_t frame_index,
-                     uint32_t lazy_pixel_ref) override;
-
-  const base::flat_set<size_t>& frames_decoded() const {
-    return frames_decoded_;
-  }
-  void reset_frames_decoded() { frames_decoded_.clear(); }
-
- private:
-  std::vector<uint8_t> image_backing_memory_;
-  SkPixmap image_pixmap_;
-  base::flat_set<size_t> frames_decoded_;
-};
-
-}  // namespace cc
-
-#endif  // CC_TEST_FAKE_PAINT_IMAGE_GENERATOR_H_
diff --git a/cc/test/fake_tile_manager_client.cc b/cc/test/fake_tile_manager_client.cc
index 28bd304..8325acc 100644
--- a/cc/test/fake_tile_manager_client.cc
+++ b/cc/test/fake_tile_manager_client.cc
@@ -29,10 +29,4 @@
   return gfx::ColorSpace();
 }
 
-size_t FakeTileManagerClient::GetFrameIndexForImage(
-    const PaintImage& paint_image,
-    WhichTree tree) const {
-  return paint_image.frame_index();
-}
-
 }  // namespace cc
diff --git a/cc/test/fake_tile_manager_client.h b/cc/test/fake_tile_manager_client.h
index c618a39dc..3597d79 100644
--- a/cc/test/fake_tile_manager_client.h
+++ b/cc/test/fake_tile_manager_client.h
@@ -29,8 +29,6 @@
   void SetIsLikelyToRequireADraw(bool is_likely_to_require_a_draw) override {}
   gfx::ColorSpace GetRasterColorSpace() const override;
   void RequestImplSideInvalidationForCheckerImagedTiles() override {}
-  size_t GetFrameIndexForImage(const PaintImage& paint_image,
-                               WhichTree tree) const override;
 };
 
 }  // namespace cc
diff --git a/cc/test/skia_common.cc b/cc/test/skia_common.cc
index 0219b14..2effb65 100644
--- a/cc/test/skia_common.cc
+++ b/cc/test/skia_common.cc
@@ -11,7 +11,7 @@
 #include "cc/paint/draw_image.h"
 #include "cc/paint/paint_canvas.h"
 #include "cc/paint/paint_image_builder.h"
-#include "cc/test/fake_paint_image_generator.h"
+#include "cc/test/stub_paint_image_generator.h"
 #include "third_party/skia/include/core/SkImageGenerator.h"
 #include "third_party/skia/include/core/SkPixmap.h"
 #include "ui/gfx/geometry/rect.h"
@@ -19,6 +19,30 @@
 
 namespace cc {
 
+namespace {
+
+class TestImageGenerator : public StubPaintImageGenerator {
+ public:
+  explicit TestImageGenerator(const SkImageInfo& info)
+      : StubPaintImageGenerator(info),
+        image_backing_memory_(info.getSafeSize(info.minRowBytes()), 0),
+        image_pixmap_(info, image_backing_memory_.data(), info.minRowBytes()) {}
+
+  bool GetPixels(const SkImageInfo& info,
+                 void* pixels,
+                 size_t rowBytes,
+                 size_t frame_index,
+                 uint32_t lazy_pixel_ref) override {
+    return image_pixmap_.readPixels(info, pixels, rowBytes, 0, 0);
+  }
+
+ private:
+  std::vector<uint8_t> image_backing_memory_;
+  SkPixmap image_pixmap_;
+};
+
+}  // anonymous namespace
+
 void DrawDisplayList(unsigned char* buffer,
                      const gfx::Rect& layer_rect,
                      scoped_refptr<const DisplayItemList> list) {
@@ -47,7 +71,7 @@
 }
 
 sk_sp<PaintImageGenerator> CreatePaintImageGenerator(const gfx::Size& size) {
-  return sk_make_sp<FakePaintImageGenerator>(
+  return sk_make_sp<TestImageGenerator>(
       SkImageInfo::MakeN32Premul(size.width(), size.height()));
 }
 
@@ -55,7 +79,7 @@
                                        sk_sp<SkColorSpace> color_space) {
   return PaintImageBuilder()
       .set_id(PaintImage::GetNextId())
-      .set_paint_image_generator(sk_make_sp<FakePaintImageGenerator>(
+      .set_paint_image_generator(sk_make_sp<TestImageGenerator>(
           SkImageInfo::MakeN32Premul(size.width(), size.height(), color_space)))
       .TakePaintImage();
 }
@@ -74,16 +98,13 @@
 
 PaintImage CreateAnimatedImage(const gfx::Size& size,
                                std::vector<FrameMetadata> frames,
-                               int repetition_count,
-                               size_t frame_index) {
+                               int repetition_count) {
   return PaintImageBuilder()
       .set_id(PaintImage::GetNextId())
-      .set_paint_image_generator(sk_make_sp<FakePaintImageGenerator>(
-          SkImageInfo::MakeN32Premul(size.width(), size.height()),
-          std::move(frames)))
+      .set_paint_image_generator(sk_make_sp<TestImageGenerator>(
+          SkImageInfo::MakeN32Premul(size.width(), size.height())))
       .set_animation_type(PaintImage::AnimationType::ANIMATED)
       .set_repetition_count(repetition_count)
-      .set_frame_index(frame_index)
       .TakePaintImage();
 }
 
diff --git a/cc/test/skia_common.h b/cc/test/skia_common.h
index b0c3241..4cc4429e 100644
--- a/cc/test/skia_common.h
+++ b/cc/test/skia_common.h
@@ -45,9 +45,8 @@
 
 PaintImage CreateAnimatedImage(
     const gfx::Size& size,
-    std::vector<FrameMetadata> frames,
-    int repetition_count = kAnimationLoopInfinite,
-    size_t frame_index = PaintImage::kDefaultFrameIndex);
+    std::vector<FrameMetadata> frames = {FrameMetadata()},
+    int repetition_count = kAnimationLoopInfinite);
 
 }  // namespace cc
 
diff --git a/cc/test/stub_paint_image_generator.cc b/cc/test/stub_paint_image_generator.cc
new file mode 100644
index 0000000..dce8372
--- /dev/null
+++ b/cc/test/stub_paint_image_generator.cc
@@ -0,0 +1,33 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/test/stub_paint_image_generator.h"
+
+namespace cc {
+
+sk_sp<SkData> StubPaintImageGenerator::GetEncodedData() const {
+  return nullptr;
+}
+
+bool StubPaintImageGenerator::GetPixels(const SkImageInfo& info,
+                                        void* pixels,
+                                        size_t row_bytes,
+                                        size_t frame_index,
+                                        uint32_t lazy_pixel_ref) {
+  return false;
+}
+
+bool StubPaintImageGenerator::QueryYUV8(SkYUVSizeInfo* info,
+                                        SkYUVColorSpace* color_space) const {
+  return false;
+}
+
+bool StubPaintImageGenerator::GetYUV8Planes(const SkYUVSizeInfo& info,
+                                            void* planes[3],
+                                            size_t frame_index,
+                                            uint32_t lazy_pixel_ref) {
+  return false;
+}
+
+}  // namespace cc
diff --git a/cc/test/stub_paint_image_generator.h b/cc/test/stub_paint_image_generator.h
new file mode 100644
index 0000000..0d566cd2
--- /dev/null
+++ b/cc/test/stub_paint_image_generator.h
@@ -0,0 +1,33 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_TEST_STUB_PAINT_IMAGE_GENERATOR_H_
+#define CC_TEST_STUB_PAINT_IMAGE_GENERATOR_H_
+
+#include "cc/paint/paint_image_generator.h"
+
+namespace cc {
+
+class StubPaintImageGenerator : public PaintImageGenerator {
+ public:
+  explicit StubPaintImageGenerator(const SkImageInfo& info)
+      : PaintImageGenerator(info) {}
+
+  sk_sp<SkData> GetEncodedData() const override;
+  bool GetPixels(const SkImageInfo& info,
+                 void* pixels,
+                 size_t row_bytes,
+                 size_t frame_index,
+                 uint32_t lazy_pixel_ref) override;
+  bool QueryYUV8(SkYUVSizeInfo* info,
+                 SkYUVColorSpace* color_space) const override;
+  bool GetYUV8Planes(const SkYUVSizeInfo& info,
+                     void* planes[3],
+                     size_t frame_index,
+                     uint32_t lazy_pixel_ref) override;
+};
+
+}  // namespace cc
+
+#endif  // CC_TEST_STUB_PAINT_IMAGE_GENERATOR_H_
diff --git a/cc/tiles/checker_image_tracker.cc b/cc/tiles/checker_image_tracker.cc
index d1f221a1..ccdfd3f 100644
--- a/cc/tiles/checker_image_tracker.cc
+++ b/cc/tiles/checker_image_tracker.cc
@@ -362,7 +362,6 @@
   decode_state->filter_quality =
       std::max(decode_state->filter_quality, draw_image.filter_quality());
   decode_state->color_space = draw_image.target_color_space();
-  decode_state->frame_index = draw_image.frame_index();
 }
 
 void CheckerImageTracker::ScheduleNextImageDecode() {
@@ -401,7 +400,7 @@
         it->second.filter_quality,
         SkMatrix::MakeScale(it->second.scale.width(),
                             it->second.scale.height()),
-        it->second.frame_index, it->second.color_space);
+        it->second.color_space);
     outstanding_image_decode_.emplace(candidate);
     break;
   }
diff --git a/cc/tiles/checker_image_tracker.h b/cc/tiles/checker_image_tracker.h
index f415f86..095c868f 100644
--- a/cc/tiles/checker_image_tracker.h
+++ b/cc/tiles/checker_image_tracker.h
@@ -130,7 +130,6 @@
     SkFilterQuality filter_quality = kNone_SkFilterQuality;
     SkSize scale = SkSize::MakeEmpty();
     gfx::ColorSpace color_space;
-    size_t frame_index = PaintImage::kDefaultFrameIndex;
   };
 
   // Wrapper to unlock an image decode requested from the ImageController on
diff --git a/cc/tiles/checker_image_tracker_unittest.cc b/cc/tiles/checker_image_tracker_unittest.cc
index 504a16f9..ef0fcddd 100644
--- a/cc/tiles/checker_image_tracker_unittest.cc
+++ b/cc/tiles/checker_image_tracker_unittest.cc
@@ -121,8 +121,7 @@
                          .set_is_multipart(is_multipart)
                          .TakePaintImage(),
                      SkIRect::MakeWH(dimension, dimension),
-                     kNone_SkFilterQuality, SkMatrix::I(),
-                     PaintImage::kDefaultFrameIndex, gfx::ColorSpace());
+                     kNone_SkFilterQuality, SkMatrix::I(), gfx::ColorSpace());
   }
 
   bool ShouldCheckerImage(const DrawImage& draw_image, WhichTree tree) {
@@ -434,8 +433,7 @@
           .set_paint_image_generator(CreatePaintImageGenerator(image_size))
           .TakePaintImage(),
       SkIRect::MakeWH(image_size.width(), image_size.height()),
-      kNone_SkFilterQuality, SkMatrix::I(), PaintImage::kDefaultFrameIndex,
-      gfx::ColorSpace());
+      kNone_SkFilterQuality, SkMatrix::I(), gfx::ColorSpace());
   EXPECT_FALSE(
       ShouldCheckerImage(completed_paint_image, WhichTree::PENDING_TREE));
 }
@@ -462,12 +460,10 @@
   SetUpTracker(true);
 
   DrawImage image = CreateImage(ImageType::CHECKERABLE);
-  DrawImage scaled_image1(image, 0.5f, PaintImage::kDefaultFrameIndex,
-                          gfx::ColorSpace());
+  DrawImage scaled_image1(image, 0.5f, gfx::ColorSpace());
   DrawImage scaled_image2 =
       DrawImage(image.paint_image(), image.src_rect(), kHigh_SkFilterQuality,
-                SkMatrix::MakeScale(1.8f), PaintImage::kDefaultFrameIndex,
-                gfx::ColorSpace());
+                SkMatrix::MakeScale(1.8f), gfx::ColorSpace());
 
   std::vector<DrawImage> draw_images = {scaled_image1, scaled_image2};
   CheckerImageTracker::ImageDecodeQueue image_decode_queue =
@@ -542,7 +538,7 @@
   DrawImage image = CreateImage(ImageType::CHECKERABLE);
   image = DrawImage(image.paint_image(), SkIRect::MakeWH(200, 200),
                     image.filter_quality(), SkMatrix::I(),
-                    PaintImage::kDefaultFrameIndex, image.target_color_space());
+                    image.target_color_space());
   EXPECT_FALSE(ShouldCheckerImage(image, WhichTree::PENDING_TREE));
 }
 
diff --git a/cc/tiles/decoded_image_tracker.cc b/cc/tiles/decoded_image_tracker.cc
index ebe3cc0..00cf27a 100644
--- a/cc/tiles/decoded_image_tracker.cc
+++ b/cc/tiles/decoded_image_tracker.cc
@@ -29,8 +29,10 @@
   gfx::ColorSpace target_color_space;
 
   auto image_bounds = SkIRect::MakeWH(image.width(), image.height());
+  // TODO(khushalsagar): Eliminate the use of an incorrect id here and have all
+  // call-sites provide PaintImage to the ImageController.
   DrawImage draw_image(image, image_bounds, kNone_SkFilterQuality,
-                       SkMatrix::I(), image.frame_index(), target_color_space);
+                       SkMatrix::I(), target_color_space);
   image_controller_->QueueImageDecode(
       draw_image, base::Bind(&DecodedImageTracker::ImageDecodeFinished,
                              base::Unretained(this), callback));
diff --git a/cc/tiles/gpu_image_decode_cache.cc b/cc/tiles/gpu_image_decode_cache.cc
index 6c2028f..c96aeff1 100644
--- a/cc/tiles/gpu_image_decode_cache.cc
+++ b/cc/tiles/gpu_image_decode_cache.cc
@@ -121,8 +121,7 @@
 // if not, decodes to a compatible temporary pixmap and then converts that into
 // the |target_pixmap|.
 bool DrawAndScaleImage(const DrawImage& draw_image, SkPixmap* target_pixmap) {
-  sk_sp<SkImage> image =
-      draw_image.paint_image().GetSkImageForFrame(draw_image.frame_index());
+  const SkImage* image = draw_image.paint_image().GetSkImage().get();
   if (image->dimensions() == target_pixmap->bounds().size() ||
       target_pixmap->info().colorType() == kN32_SkColorType) {
     // If no scaling is occurring, or if the target colortype is already N32,
@@ -211,7 +210,7 @@
     TRACE_EVENT2("cc", "ImageDecodeTaskImpl::RunOnWorkerThread", "mode", "gpu",
                  "source_prepare_tiles_id", tracing_info_.prepare_tiles_id);
     devtools_instrumentation::ScopedImageDecodeTask image_decode_task(
-        &image_.paint_image(),
+        image_.paint_image().GetSkImage().get(),
         devtools_instrumentation::ScopedImageDecodeTask::kGpu,
         ImageDecodeCache::ToScopedTaskType(tracing_info_.task_type));
     cache_->DecodeImage(image_, tracing_info_.task_type);
@@ -1179,8 +1178,7 @@
         // TODO(crbug.com/649167): Params should not have changed since initial
         // sizing. Somehow this still happens. We should investigate and re-add
         // DCHECKs here to enforce this.
-        sk_sp<SkImage> image = draw_image.paint_image().GetSkImageForFrame(
-            draw_image.frame_index());
+        SkImage* image = draw_image.paint_image().GetSkImage().get();
         if (!image->getDeferredTextureImageData(
                 *context_threadsafe_proxy_.get(), &image_data->upload_params, 1,
                 backing_memory->data(), nullptr, color_type_)) {
@@ -1285,8 +1283,7 @@
   auto params = SkImage::DeferredTextureImageUsageParams(
       SkMatrix::I(), CalculateDesiredFilterQuality(draw_image),
       upload_scale_mip_level);
-  sk_sp<SkImage> image =
-      draw_image.paint_image().GetSkImageForFrame(draw_image.frame_index());
+  SkImage* image = draw_image.paint_image().GetSkImage().get();
   size_t data_size = image->getDeferredTextureImageData(
       *context_threadsafe_proxy_.get(), &params, 1, nullptr, nullptr,
       color_type_);
diff --git a/cc/tiles/gpu_image_decode_cache_unittest.cc b/cc/tiles/gpu_image_decode_cache_unittest.cc
index 56b5d1f..61c4e0b 100644
--- a/cc/tiles/gpu_image_decode_cache_unittest.cc
+++ b/cc/tiles/gpu_image_decode_cache_unittest.cc
@@ -6,7 +6,6 @@
 
 #include "cc/paint/draw_image.h"
 #include "cc/paint/paint_image_builder.h"
-#include "cc/test/fake_paint_image_generator.h"
 #include "cc/test/skia_common.h"
 #include "cc/test/test_context_provider.h"
 #include "cc/test/test_tile_task_runner.h"
@@ -57,7 +56,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -66,7 +65,7 @@
   DrawImage another_draw_image(
       image, SkIRect::MakeWH(image.width(), image.height()), quality,
       CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult another_result = cache.GetTaskForImageAndRef(
       another_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(another_result.need_unref);
@@ -90,7 +89,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -99,7 +98,7 @@
   DrawImage another_draw_image(
       image, SkIRect::MakeWH(image.width(), image.height()), quality,
       CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult another_result = cache.GetTaskForImageAndRef(
       another_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(another_result.need_unref);
@@ -121,8 +120,7 @@
   SkMatrix matrix = CreateMatrix(SkSize::Make(0.4f, 0.4f), is_decomposable);
 
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
-                       kHigh_SkFilterQuality, matrix,
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       kHigh_SkFilterQuality, matrix, DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -130,8 +128,7 @@
 
   DrawImage another_draw_image(
       image, SkIRect::MakeWH(image.width(), image.height()),
-      kLow_SkFilterQuality, matrix, PaintImage::kDefaultFrameIndex,
-      DefaultColorSpace());
+      kLow_SkFilterQuality, matrix, DefaultColorSpace());
   ImageDecodeCache::TaskResult another_result = cache.GetTaskForImageAndRef(
       another_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(another_result.need_unref);
@@ -155,7 +152,7 @@
   DrawImage first_draw_image(
       first_image, SkIRect::MakeWH(first_image.width(), first_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult first_result = cache.GetTaskForImageAndRef(
       first_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(first_result.need_unref);
@@ -166,7 +163,7 @@
       second_image,
       SkIRect::MakeWH(second_image.width(), second_image.height()), quality,
       CreateMatrix(SkSize::Make(0.25f, 0.25f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult second_result = cache.GetTaskForImageAndRef(
       second_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(second_result.need_unref);
@@ -193,7 +190,7 @@
   DrawImage first_draw_image(
       first_image, SkIRect::MakeWH(first_image.width(), first_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult first_result = cache.GetTaskForImageAndRef(
       first_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(first_result.need_unref);
@@ -207,7 +204,7 @@
   DrawImage second_draw_image(
       first_image, SkIRect::MakeWH(first_image.width(), first_image.height()),
       quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult second_result = cache.GetTaskForImageAndRef(
       second_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(second_result.need_unref);
@@ -217,7 +214,7 @@
   DrawImage third_draw_image(
       first_image, SkIRect::MakeWH(first_image.width(), first_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult third_result = cache.GetTaskForImageAndRef(
       third_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(third_result.need_unref);
@@ -241,7 +238,7 @@
   DrawImage first_draw_image(
       first_image, SkIRect::MakeWH(first_image.width(), first_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult first_result = cache.GetTaskForImageAndRef(
       first_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(first_result.need_unref);
@@ -250,7 +247,7 @@
   DrawImage second_draw_image(
       first_image, SkIRect::MakeWH(first_image.width(), first_image.height()),
       quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult second_result = cache.GetTaskForImageAndRef(
       second_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(second_result.need_unref);
@@ -260,7 +257,7 @@
   DrawImage third_draw_image(
       first_image, SkIRect::MakeWH(first_image.width(), first_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult third_result = cache.GetTaskForImageAndRef(
       third_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(third_result.need_unref);
@@ -286,8 +283,7 @@
   PaintImage first_image = CreateDiscardablePaintImage(gfx::Size(100, 100));
   DrawImage first_draw_image(
       first_image, SkIRect::MakeWH(first_image.width(), first_image.height()),
-      kLow_SkFilterQuality, matrix, PaintImage::kDefaultFrameIndex,
-      DefaultColorSpace());
+      kLow_SkFilterQuality, matrix, DefaultColorSpace());
   ImageDecodeCache::TaskResult first_result = cache.GetTaskForImageAndRef(
       first_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(first_result.need_unref);
@@ -300,8 +296,7 @@
 
   DrawImage second_draw_image(
       first_image, SkIRect::MakeWH(first_image.width(), first_image.height()),
-      kHigh_SkFilterQuality, matrix, PaintImage::kDefaultFrameIndex,
-      DefaultColorSpace());
+      kHigh_SkFilterQuality, matrix, DefaultColorSpace());
   ImageDecodeCache::TaskResult second_result = cache.GetTaskForImageAndRef(
       second_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(second_result.need_unref);
@@ -325,7 +320,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -369,7 +364,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -413,7 +408,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -447,7 +442,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -492,7 +487,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -541,7 +536,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -573,7 +568,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -608,7 +603,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -646,7 +641,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
@@ -678,7 +673,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        kLow_SkFilterQuality,
                        CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -690,7 +685,7 @@
   DrawImage larger_draw_image(
       image, SkIRect::MakeWH(image.width(), image.height()), quality,
       CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult larger_result = cache.GetTaskForImageAndRef(
       larger_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(larger_result.need_unref);
@@ -737,8 +732,7 @@
 
   PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100));
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
-                       kLow_SkFilterQuality, matrix,
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       kLow_SkFilterQuality, matrix, DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -749,8 +743,7 @@
 
   DrawImage higher_quality_draw_image(
       image, SkIRect::MakeWH(image.width(), image.height()),
-      kHigh_SkFilterQuality, matrix, PaintImage::kDefaultFrameIndex,
-      DefaultColorSpace());
+      kHigh_SkFilterQuality, matrix, DefaultColorSpace());
   ImageDecodeCache::TaskResult hq_result = cache.GetTaskForImageAndRef(
       higher_quality_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(hq_result.need_unref);
@@ -799,7 +792,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(-0.5f, 0.5f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -836,7 +829,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -877,7 +870,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
@@ -922,7 +915,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
 
   // Must hold context lock before calling GetDecodedImageForDraw /
   // DrawWithImageFinished.
@@ -956,7 +949,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
 
   // Must hold context lock before calling GetDecodedImageForDraw /
   // DrawWithImageFinished.
@@ -996,7 +989,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(0.f, 0.f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
@@ -1024,7 +1017,7 @@
   DrawImage draw_image(
       image, SkIRect::MakeXYWH(150, 150, image.width(), image.height()),
       quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
@@ -1052,7 +1045,7 @@
   DrawImage draw_image(
       image, SkIRect::MakeXYWH(0, 0, image.width(), image.height()), quality,
       CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
@@ -1080,7 +1073,8 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
+
   {
     ImageDecodeCache::TaskResult result = cache.GetTaskForImageAndRef(
         draw_image, ImageDecodeCache::TracingInfo());
@@ -1144,7 +1138,7 @@
   DrawImage first_draw_image(
       first_image, SkIRect::MakeWH(first_image.width(), first_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult first_result = cache.GetTaskForImageAndRef(
       first_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(first_result.need_unref);
@@ -1159,7 +1153,7 @@
   DrawImage second_draw_image(
       first_image, SkIRect::MakeWH(first_image.width(), first_image.height()),
       quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult second_result = cache.GetTaskForImageAndRef(
       second_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(second_result.need_unref);
@@ -1199,7 +1193,7 @@
   DrawImage first_draw_image(
       first_image, SkIRect::MakeWH(first_image.width(), first_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult first_result = cache.GetTaskForImageAndRef(
       first_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(first_result.need_unref);
@@ -1218,7 +1212,7 @@
   DrawImage second_draw_image(
       first_image, SkIRect::MakeWH(first_image.width(), first_image.height()),
       quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult second_result = cache.GetTaskForImageAndRef(
       second_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(second_result.need_unref);
@@ -1246,8 +1240,7 @@
   // Create an image with kLow_FilterQuality.
   DrawImage low_draw_image(image,
                            SkIRect::MakeWH(image.width(), image.height()),
-                           kLow_SkFilterQuality, matrix,
-                           PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                           kLow_SkFilterQuality, matrix, DefaultColorSpace());
   ImageDecodeCache::TaskResult low_result = cache.GetTaskForImageAndRef(
       low_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(low_result.need_unref);
@@ -1257,8 +1250,7 @@
   // should get a new task/ref.
   DrawImage medium_draw_image(
       image, SkIRect::MakeWH(image.width(), image.height()),
-      kMedium_SkFilterQuality, matrix, PaintImage::kDefaultFrameIndex,
-      DefaultColorSpace());
+      kMedium_SkFilterQuality, matrix, DefaultColorSpace());
   ImageDecodeCache::TaskResult medium_result = cache.GetTaskForImageAndRef(
       medium_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(medium_result.need_unref);
@@ -1268,8 +1260,7 @@
   // Get the same image at kHigh_FilterQuality. We should re-use medium.
   DrawImage large_draw_image(
       image, SkIRect::MakeWH(image.width(), image.height()),
-      kHigh_SkFilterQuality, matrix, PaintImage::kDefaultFrameIndex,
-      DefaultColorSpace());
+      kHigh_SkFilterQuality, matrix, DefaultColorSpace());
   ImageDecodeCache::TaskResult large_result = cache.GetTaskForImageAndRef(
       large_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(large_result.need_unref);
@@ -1299,7 +1290,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -1321,7 +1312,7 @@
   DrawImage draw_image_mips(
       image, SkIRect::MakeWH(image.width(), image.height()), quality,
       CreateMatrix(SkSize::Make(0.6f, 0.6f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   DecodedDrawImage decoded_draw_image =
       cache.GetDecodedImageForDraw(draw_image_mips);
   cache.DrawWithImageFinished(draw_image_mips, decoded_draw_image);
@@ -1337,8 +1328,7 @@
   bool is_decomposable = true;
   SkMatrix matrix = CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable);
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
-                       kLow_SkFilterQuality, matrix,
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       kLow_SkFilterQuality, matrix, DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -1406,8 +1396,7 @@
   bool is_decomposable = true;
   SkMatrix matrix = CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable);
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
-                       kLow_SkFilterQuality, matrix,
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       kLow_SkFilterQuality, matrix, DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result =
       cache.GetOutOfRasterDecodeTaskForImageAndRef(draw_image);
@@ -1438,7 +1427,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -1490,13 +1479,13 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
 
   PaintImage image2 = CreateDiscardablePaintImage(gfx::Size(100, 100));
   DrawImage draw_image2(
       image2, SkIRect::MakeWH(image2.width(), image2.height()), quality,
       CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   // Add an image to the cache and un-ref it.
   {
@@ -1575,7 +1564,7 @@
     DrawImage draw_image(
         image, SkIRect::MakeWH(image.width(), image.height()), quality,
         CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-        PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+        DefaultColorSpace());
     ImageDecodeCache::TaskResult result = cache.GetTaskForImageAndRef(
         draw_image, ImageDecodeCache::TracingInfo());
     EXPECT_TRUE(result.need_unref);
@@ -1609,7 +1598,7 @@
   DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
                        quality,
                        CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -1648,7 +1637,7 @@
   DrawImage first_draw_image(
       first_image, SkIRect::MakeWH(first_image.width(), first_image.height()),
       quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, color_space_a);
+      color_space_a);
   ImageDecodeCache::TaskResult first_result = cache.GetTaskForImageAndRef(
       first_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(first_result.need_unref);
@@ -1657,7 +1646,7 @@
   DrawImage second_draw_image(
       first_image, SkIRect::MakeWH(first_image.width(), first_image.height()),
       quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, color_space_b);
+      color_space_b);
   ImageDecodeCache::TaskResult second_result = cache.GetTaskForImageAndRef(
       second_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(second_result.need_unref);
@@ -1667,7 +1656,7 @@
   DrawImage third_draw_image(
       first_image, SkIRect::MakeWH(first_image.width(), first_image.height()),
       quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, color_space_a);
+      color_space_a);
   ImageDecodeCache::TaskResult third_result = cache.GetTaskForImageAndRef(
       third_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(third_result.need_unref);
@@ -1696,7 +1685,7 @@
     DrawImage draw_image(
         image, SkIRect::MakeWH(image.width(), image.height()), quality,
         CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-        PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+        DefaultColorSpace());
     frame_keys.push_back(draw_image.frame_key());
     ImageDecodeCache::TaskResult result = cache.GetTaskForImageAndRef(
         draw_image, ImageDecodeCache::TracingInfo());
@@ -1718,64 +1707,6 @@
   }
 }
 
-TEST_P(GpuImageDecodeCacheTest, CacheDecodesExpectedFrames) {
-  auto context_provider = TestContextProvider::Create();
-  context_provider->BindToCurrentThread();
-  TestGpuImageDecodeCache cache(context_provider.get(), GetParam());
-
-  std::vector<FrameMetadata> frames = {
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(3)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(4)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(5)),
-  };
-  sk_sp<FakePaintImageGenerator> generator =
-      sk_make_sp<FakePaintImageGenerator>(SkImageInfo::MakeN32Premul(10, 10),
-                                          frames);
-  PaintImage image = PaintImageBuilder()
-                         .set_id(PaintImage::GetNextId())
-                         .set_paint_image_generator(generator)
-                         .set_frame_index(0u)
-                         .TakePaintImage();
-
-  viz::ContextProvider::ScopedContextLock context_lock(context_provider.get());
-
-  bool is_decomposable = true;
-  SkFilterQuality quality = kHigh_SkFilterQuality;
-  DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
-                       quality,
-                       CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-                       1u, DefaultColorSpace());
-  auto decoded_image = cache.GetDecodedImageForDraw(draw_image);
-  ASSERT_TRUE(decoded_image.image());
-  ASSERT_EQ(generator->frames_decoded().size(), 1u);
-  EXPECT_EQ(generator->frames_decoded().count(1u), 1u);
-  generator->reset_frames_decoded();
-  cache.DrawWithImageFinished(draw_image, decoded_image);
-
-  // Scaled.
-  DrawImage scaled_draw_image(draw_image, 0.5f, 2u,
-                              draw_image.target_color_space());
-  decoded_image = cache.GetDecodedImageForDraw(scaled_draw_image);
-  ASSERT_TRUE(decoded_image.image());
-  ASSERT_EQ(generator->frames_decoded().size(), 1u);
-  EXPECT_EQ(generator->frames_decoded().count(2u), 1u);
-  generator->reset_frames_decoded();
-  cache.DrawWithImageFinished(scaled_draw_image, decoded_image);
-
-  // Subset.
-  DrawImage subset_draw_image(
-      image, SkIRect::MakeWH(5, 5), quality,
-      CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), 3u,
-      DefaultColorSpace());
-  decoded_image = cache.GetDecodedImageForDraw(subset_draw_image);
-  ASSERT_TRUE(decoded_image.image());
-  ASSERT_EQ(generator->frames_decoded().size(), 1u);
-  EXPECT_EQ(generator->frames_decoded().count(3u), 1u);
-  generator->reset_frames_decoded();
-  cache.DrawWithImageFinished(subset_draw_image, decoded_image);
-}
-
 INSTANTIATE_TEST_CASE_P(GpuImageDecodeCacheTests,
                         GpuImageDecodeCacheTest,
                         ::testing::Values(kN32_SkColorType,
diff --git a/cc/tiles/image_controller_unittest.cc b/cc/tiles/image_controller_unittest.cc
index 081c46c..fd4d809 100644
--- a/cc/tiles/image_controller_unittest.cc
+++ b/cc/tiles/image_controller_unittest.cc
@@ -228,8 +228,7 @@
 DrawImage CreateDiscardableDrawImage(gfx::Size size) {
   return DrawImage(CreateDiscardablePaintImage(size),
                    SkIRect::MakeWH(size.width(), size.height()),
-                   kNone_SkFilterQuality, SkMatrix::I(),
-                   PaintImage::kDefaultFrameIndex, gfx::ColorSpace());
+                   kNone_SkFilterQuality, SkMatrix::I(), gfx::ColorSpace());
 }
 
 class ImageControllerTest : public testing::Test {
@@ -318,13 +317,12 @@
 
   SkBitmap bitmap;
   bitmap.allocN32Pixels(1, 1);
-  DrawImage image =
-      DrawImage(PaintImageBuilder()
-                    .set_id(PaintImage::GetNextId())
-                    .set_image(SkImage::MakeFromBitmap(bitmap))
-                    .TakePaintImage(),
-                SkIRect::MakeWH(1, 1), kNone_SkFilterQuality, SkMatrix::I(),
-                PaintImage::kDefaultFrameIndex, gfx::ColorSpace());
+  DrawImage image = DrawImage(PaintImageBuilder()
+                                  .set_id(PaintImage::GetNextId())
+                                  .set_image(SkImage::MakeFromBitmap(bitmap))
+                                  .TakePaintImage(),
+                              SkIRect::MakeWH(1, 1), kNone_SkFilterQuality,
+                              SkMatrix::I(), gfx::ColorSpace());
 
   ImageController::ImageDecodeRequestId expected_id =
       controller()->QueueImageDecode(
diff --git a/cc/tiles/software_image_decode_cache.cc b/cc/tiles/software_image_decode_cache.cc
index def638c7..786fefa 100644
--- a/cc/tiles/software_image_decode_cache.cc
+++ b/cc/tiles/software_image_decode_cache.cc
@@ -596,8 +596,7 @@
                  "SoftwareImageDecodeCache::GetOriginalSizeImageDecode - "
                  "decode");
     bool result = paint_image.Decode(decoded_pixels->data(), &decoded_info,
-                                     key.target_color_space().ToSkColorSpace(),
-                                     key.frame_key().frame_index());
+                                     key.target_color_space().ToSkColorSpace());
     if (!result) {
       decoded_pixels->Unlock();
       return nullptr;
@@ -620,8 +619,7 @@
   // so. We could also subrect scaled images.
   SkIRect exact_size_rect = SkIRect::MakeWH(image.width(), image.height());
   DrawImage exact_size_draw_image(image, exact_size_rect, kNone_SkFilterQuality,
-                                  SkMatrix::I(), key.frame_key().frame_index(),
-                                  key.target_color_space());
+                                  SkMatrix::I(), key.target_color_space());
   ImageKey exact_size_key =
       ImageKey::FromDrawImage(exact_size_draw_image, color_type_);
 
@@ -695,8 +693,7 @@
                 key.target_size().width(), key.target_size().height())));
 
   DrawImage exact_size_draw_image(image, exact_size_rect, kNone_SkFilterQuality,
-                                  SkMatrix::I(), key.frame_key().frame_index(),
-                                  key.target_color_space());
+                                  SkMatrix::I(), key.target_color_space());
   ImageKey exact_size_key =
       ImageKey::FromDrawImage(exact_size_draw_image, color_type_);
 
diff --git a/cc/tiles/software_image_decode_cache_perftest.cc b/cc/tiles/software_image_decode_cache_perftest.cc
index 02fb946..fd9f4e8 100644
--- a/cc/tiles/software_image_decode_cache_perftest.cc
+++ b/cc/tiles/software_image_decode_cache_perftest.cc
@@ -62,7 +62,7 @@
                   .set_image(CreateImage(rect.width(), rect.height()))
                   .TakePaintImage(),
               subrect, quality,
-              CreateMatrix(SkSize::Make(scale.first, scale.second)), 0u,
+              CreateMatrix(SkSize::Make(scale.first, scale.second)),
               gfx::ColorSpace());
         }
       }
diff --git a/cc/tiles/software_image_decode_cache_unittest.cc b/cc/tiles/software_image_decode_cache_unittest.cc
index 807902cd..52499201f 100644
--- a/cc/tiles/software_image_decode_cache_unittest.cc
+++ b/cc/tiles/software_image_decode_cache_unittest.cc
@@ -6,7 +6,6 @@
 
 #include "cc/paint/draw_image.h"
 #include "cc/paint/paint_image_builder.h"
-#include "cc/test/fake_paint_image_generator.h"
 #include "cc/test/skia_common.h"
 #include "cc/test/test_tile_task_runner.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -52,7 +51,7 @@
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       kNone_SkFilterQuality,
       CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -73,7 +72,7 @@
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       kLow_SkFilterQuality,
       CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -91,7 +90,7 @@
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       kMedium_SkFilterQuality,
       CreateMatrix(SkSize::Make(0.75f, 0.75f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -109,7 +108,7 @@
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       kLow_SkFilterQuality,
       CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key =
       ImageDecodeCacheKey::FromDrawImage(draw_image, kARGB_4444_SkColorType);
@@ -128,7 +127,7 @@
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       kHigh_SkFilterQuality,
       CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key =
       ImageDecodeCacheKey::FromDrawImage(draw_image, kARGB_4444_SkColorType);
@@ -147,7 +146,7 @@
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       kLow_SkFilterQuality,
       CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -166,7 +165,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.4f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -185,7 +184,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -204,7 +203,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -224,7 +223,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(1.001f, 1.001f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -244,7 +243,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.999f, 0.999f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -264,7 +263,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -284,7 +283,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -303,7 +302,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -322,7 +321,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.75f, 0.75f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -341,7 +340,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -360,7 +359,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.49f, 0.49f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -379,7 +378,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.1f, 0.1f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -398,7 +397,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.01f, 0.01f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -418,7 +417,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.2f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -437,7 +436,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(2.5f, 1.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -459,7 +458,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.45f, 0.45f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -479,7 +478,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -498,7 +497,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -518,7 +517,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(1.001f, 1.001f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -538,7 +537,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.999f, 0.999f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -557,7 +556,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -570,7 +569,7 @@
   DrawImage another_draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(1.5f, 1.5), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto another_key =
       ImageDecodeCacheKey::FromDrawImage(another_draw_image, kN32_SkColorType);
@@ -593,7 +592,7 @@
       paint_image,
       SkIRect::MakeXYWH(25, 35, paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -613,7 +612,7 @@
       paint_image,
       SkIRect::MakeXYWH(20, 30, paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, kN32_SkColorType);
   EXPECT_EQ(draw_image.frame_key(), key.frame_key());
@@ -633,7 +632,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -642,7 +641,7 @@
   DrawImage another_draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult another_result = cache.GetTaskForImageAndRef(
       another_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(another_result.need_unref);
@@ -663,7 +662,7 @@
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       kHigh_SkFilterQuality,
       CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult high_quality_result =
       cache.GetTaskForImageAndRef(high_quality_draw_image,
                                   ImageDecodeCache::TracingInfo());
@@ -674,7 +673,7 @@
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       kNone_SkFilterQuality,
       CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult none_quality_result =
       cache.GetTaskForImageAndRef(none_quality_draw_image,
                                   ImageDecodeCache::TracingInfo());
@@ -698,7 +697,7 @@
   DrawImage half_size_draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult half_size_result = cache.GetTaskForImageAndRef(
       half_size_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(half_size_result.need_unref);
@@ -707,7 +706,7 @@
   DrawImage quarter_size_draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.25f, 0.25f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult quarter_size_result =
       cache.GetTaskForImageAndRef(quarter_size_draw_image,
                                   ImageDecodeCache::TracingInfo());
@@ -732,7 +731,7 @@
       first_paint_image,
       SkIRect::MakeWH(first_paint_image.width(), first_paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult first_result = cache.GetTaskForImageAndRef(
       first_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(first_result.need_unref);
@@ -743,7 +742,7 @@
       second_paint_image,
       SkIRect::MakeWH(second_paint_image.width(), second_paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.25f, 0.25f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult second_result = cache.GetTaskForImageAndRef(
       second_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(second_result.need_unref);
@@ -780,7 +779,7 @@
   DrawImage first_draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, color_space_b);
+      color_space_b);
   ImageDecodeCache::TaskResult first_result = cache.GetTaskForImageAndRef(
       first_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(first_result.need_unref);
@@ -789,7 +788,7 @@
   DrawImage second_draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, color_space_c);
+      color_space_c);
   ImageDecodeCache::TaskResult second_result = cache.GetTaskForImageAndRef(
       second_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(second_result.need_unref);
@@ -799,7 +798,7 @@
   DrawImage third_draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, color_space_b);
+      color_space_b);
   ImageDecodeCache::TaskResult third_result = cache.GetTaskForImageAndRef(
       third_draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(third_result.need_unref);
@@ -823,7 +822,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -852,7 +851,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -887,7 +886,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -928,7 +927,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -968,7 +967,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -1002,7 +1001,7 @@
       paint_image,
       SkIRect::MakeXYWH(20, 30, paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
@@ -1034,7 +1033,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   DecodedDrawImage decoded_draw_image =
       cache.GetDecodedImageForDraw(draw_image);
@@ -1060,7 +1059,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   DecodedDrawImage decoded_draw_image =
       cache.GetDecodedImageForDraw(draw_image);
@@ -1092,7 +1091,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   DecodedDrawImage decoded_draw_image =
       cache.GetDecodedImageForDraw(draw_image);
@@ -1137,7 +1136,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   DecodedDrawImage decoded_draw_image =
       cache.GetDecodedImageForDraw(draw_image);
@@ -1182,7 +1181,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.f, 0.f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
@@ -1206,7 +1205,7 @@
       paint_image,
       SkIRect::MakeXYWH(150, 150, paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
@@ -1229,7 +1228,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
@@ -1257,7 +1256,7 @@
   PaintImage paint_image = CreatePaintImage(100, 100);
   DrawImage draw_image(paint_image, SkIRect::MakeXYWH(10, 10, 80, 80), quality,
                        CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
@@ -1290,7 +1289,7 @@
   PaintImage paint_image = CreatePaintImage(100, 100);
   DrawImage draw_image(paint_image, SkIRect::MakeXYWH(10, 10, 80, 80), quality,
                        CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-                       PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+                       DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
@@ -1321,7 +1320,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
@@ -1352,7 +1351,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
@@ -1383,7 +1382,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.75f, 0.75f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
@@ -1414,7 +1413,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
@@ -1445,7 +1444,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.49f, 0.49f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
@@ -1476,7 +1475,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.1f, 0.1f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
@@ -1507,7 +1506,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.01f, 0.01f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
@@ -1538,7 +1537,7 @@
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.001f, 0.001f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result =
       cache.GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
@@ -1562,11 +1561,11 @@
   DrawImage draw_image_50(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
   DrawImage draw_image_49(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       quality, CreateMatrix(SkSize::Make(0.49f, 0.49f), is_decomposable),
-      PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+      DefaultColorSpace());
 
   ImageDecodeCache::TaskResult result_50 = cache.GetTaskForImageAndRef(
       draw_image_50, ImageDecodeCache::TracingInfo());
@@ -1613,7 +1612,7 @@
     DrawImage draw_image(
         paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
         quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-        PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+        DefaultColorSpace());
     ImageDecodeCache::TaskResult result = cache.GetTaskForImageAndRef(
         draw_image, ImageDecodeCache::TracingInfo());
     EXPECT_TRUE(result.need_unref);
@@ -1642,7 +1641,7 @@
     DrawImage draw_image(
         paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
         quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-        PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+        DefaultColorSpace());
     frame_keys.push_back(draw_image.frame_key());
     DecodedDrawImage decoded_draw_image =
         cache.GetDecodedImageForDraw(draw_image);
@@ -1665,58 +1664,5 @@
   }
 }
 
-TEST(SoftwareImageDecodeCacheTest, CacheDecodesExpectedFrames) {
-  TestSoftwareImageDecodeCache cache;
-  std::vector<FrameMetadata> frames = {
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(3)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(4)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(5)),
-  };
-  sk_sp<FakePaintImageGenerator> generator =
-      sk_make_sp<FakePaintImageGenerator>(SkImageInfo::MakeN32Premul(10, 10),
-                                          frames);
-  PaintImage image = PaintImageBuilder()
-                         .set_id(PaintImage::GetNextId())
-                         .set_paint_image_generator(generator)
-                         .set_frame_index(0u)
-                         .TakePaintImage();
-
-  bool is_decomposable = true;
-  SkFilterQuality quality = kHigh_SkFilterQuality;
-  DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
-                       quality,
-                       CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
-                       1u, DefaultColorSpace());
-  auto decoded_image = cache.GetDecodedImageForDraw(draw_image);
-  ASSERT_TRUE(decoded_image.image());
-  ASSERT_EQ(generator->frames_decoded().size(), 1u);
-  EXPECT_EQ(generator->frames_decoded().count(1u), 1u);
-  generator->reset_frames_decoded();
-  cache.DrawWithImageFinished(draw_image, decoded_image);
-
-  // Scaled.
-  DrawImage scaled_draw_image(draw_image, 0.5f, 2u,
-                              draw_image.target_color_space());
-  decoded_image = cache.GetDecodedImageForDraw(scaled_draw_image);
-  ASSERT_TRUE(decoded_image.image());
-  ASSERT_EQ(generator->frames_decoded().size(), 1u);
-  EXPECT_EQ(generator->frames_decoded().count(2u), 1u);
-  generator->reset_frames_decoded();
-  cache.DrawWithImageFinished(scaled_draw_image, decoded_image);
-
-  // Subset.
-  DrawImage subset_draw_image(
-      image, SkIRect::MakeWH(5, 5), quality,
-      CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), 3u,
-      DefaultColorSpace());
-  decoded_image = cache.GetDecodedImageForDraw(subset_draw_image);
-  ASSERT_TRUE(decoded_image.image());
-  ASSERT_EQ(generator->frames_decoded().size(), 1u);
-  EXPECT_EQ(generator->frames_decoded().count(3u), 1u);
-  generator->reset_frames_decoded();
-  cache.DrawWithImageFinished(subset_draw_image, decoded_image);
-}
-
 }  // namespace
 }  // namespace cc
diff --git a/cc/tiles/tile_manager.cc b/cc/tiles/tile_manager.cc
index 21c016b..618b973 100644
--- a/cc/tiles/tile_manager.cc
+++ b/cc/tiles/tile_manager.cc
@@ -883,8 +883,7 @@
     const PrioritizedTile& prioritized_tile,
     const gfx::ColorSpace& raster_color_space,
     std::vector<DrawImage>* sync_decoded_images,
-    std::vector<PaintImage>* checkered_images,
-    base::flat_map<PaintImage::Id, size_t>* image_to_frame_index) {
+    std::vector<PaintImage>* checkered_images) {
   Tile* tile = prioritized_tile.tile();
   std::vector<const DrawImage*> images_in_tile;
   prioritized_tile.raster_source()->GetDiscardableImagesInRect(
@@ -892,15 +891,8 @@
   WhichTree tree = tile->tiling()->tree();
 
   for (const auto* original_draw_image : images_in_tile) {
-    size_t frame_index = client_->GetFrameIndexForImage(
-        original_draw_image->paint_image(), tree);
     DrawImage draw_image(*original_draw_image, tile->raster_transform().scale(),
-                         frame_index, raster_color_space);
-    if (image_to_frame_index) {
-      (*image_to_frame_index)[draw_image.paint_image().stable_id()] =
-          frame_index;
-    }
-
+                         raster_color_space);
     if (checker_image_tracker_.ShouldCheckerImage(
             draw_image, tree, tile->required_for_activation()))
       checkered_images->push_back(draw_image.paint_image());
@@ -921,10 +913,9 @@
   WhichTree tree = tile->tiling()->tree();
 
   for (const auto* original_draw_image : images_in_tile) {
-    size_t frame_index = client_->GetFrameIndexForImage(
-        original_draw_image->paint_image(), tree);
     DrawImage draw_image(*original_draw_image, tile->raster_transform().scale(),
-                         frame_index, raster_color_space);
+                         raster_color_space);
+
     if (checker_image_tracker_.ShouldCheckerImage(
             draw_image, tree, tile->required_for_activation())) {
       image_decode_queue->emplace_back(draw_image.paint_image(), decode_type);
@@ -1142,15 +1133,11 @@
       scheduled_draw_images_[tile->id()];
   sync_decoded_images.clear();
   PaintImageIdFlatSet images_to_skip;
-  base::flat_map<PaintImage::Id, size_t> image_id_to_current_frame_index;
   if (!skip_images) {
     std::vector<PaintImage> checkered_images;
     PartitionImagesForCheckering(prioritized_tile, color_space,
-                                 &sync_decoded_images, &checkered_images,
-                                 &image_id_to_current_frame_index);
+                                 &sync_decoded_images, &checkered_images);
     for (const auto& image : checkered_images) {
-      DCHECK(!image.ShouldAnimate());
-
       images_to_skip.insert(image.stable_id());
 
       // This can be the case for tiles on the active tree that will be replaced
@@ -1188,8 +1175,7 @@
       has_at_raster_images) {
     image_provider.emplace(skip_images, std::move(images_to_skip),
                            std::move(at_raster_images),
-                           image_controller_.cache(), color_space,
-                           std::move(image_id_to_current_frame_index));
+                           image_controller_.cache(), color_space);
   }
 
   return make_scoped_refptr(new RasterTaskImpl(
diff --git a/cc/tiles/tile_manager.h b/cc/tiles/tile_manager.h
index 58de5f05..d9a7cbf 100644
--- a/cc/tiles/tile_manager.h
+++ b/cc/tiles/tile_manager.h
@@ -87,9 +87,6 @@
   // rasterized with missing images need to be invalidated.
   virtual void RequestImplSideInvalidationForCheckerImagedTiles() = 0;
 
-  virtual size_t GetFrameIndexForImage(const PaintImage& paint_image,
-                                       WhichTree tree) const = 0;
-
  protected:
   virtual ~TileManagerClient() {}
 };
@@ -360,12 +357,10 @@
   PrioritizedWorkToSchedule AssignGpuMemoryToTiles();
   void ScheduleTasks(PrioritizedWorkToSchedule work_to_schedule);
 
-  void PartitionImagesForCheckering(
-      const PrioritizedTile& prioritized_tile,
-      const gfx::ColorSpace& raster_color_space,
-      std::vector<DrawImage>* sync_decoded_images,
-      std::vector<PaintImage>* checkered_images,
-      base::flat_map<PaintImage::Id, size_t>* image_to_frame_index = nullptr);
+  void PartitionImagesForCheckering(const PrioritizedTile& prioritized_tile,
+                                    const gfx::ColorSpace& raster_color_space,
+                                    std::vector<DrawImage>* sync_decoded_images,
+                                    std::vector<PaintImage>* checkered_images);
   void AddCheckeredImagesToDecodeQueue(
       const PrioritizedTile& prioritized_tile,
       const gfx::ColorSpace& raster_color_space,
diff --git a/cc/tiles/tile_manager_unittest.cc b/cc/tiles/tile_manager_unittest.cc
index 71cf49c1..02b1a75 100644
--- a/cc/tiles/tile_manager_unittest.cc
+++ b/cc/tiles/tile_manager_unittest.cc
@@ -22,7 +22,6 @@
 #include "cc/test/fake_layer_tree_frame_sink.h"
 #include "cc/test/fake_layer_tree_frame_sink_client.h"
 #include "cc/test/fake_layer_tree_host_impl.h"
-#include "cc/test/fake_paint_image_generator.h"
 #include "cc/test/fake_picture_layer_impl.h"
 #include "cc/test/fake_picture_layer_tiling_client.h"
 #include "cc/test/fake_raster_source.h"
@@ -30,6 +29,7 @@
 #include "cc/test/fake_tile_manager.h"
 #include "cc/test/fake_tile_task_manager.h"
 #include "cc/test/skia_common.h"
+#include "cc/test/stub_paint_image_generator.h"
 #include "cc/test/test_layer_tree_host_base.h"
 #include "cc/test/test_task_graph_runner.h"
 #include "cc/test/test_tile_priorities.h"
@@ -2307,10 +2307,10 @@
 
 class CheckerImagingTileManagerTest : public TestLayerTreeHostBase {
  public:
-  class MockImageGenerator : public FakePaintImageGenerator {
+  class MockImageGenerator : public StubPaintImageGenerator {
    public:
     explicit MockImageGenerator(const gfx::Size& size)
-        : FakePaintImageGenerator(
+        : StubPaintImageGenerator(
               SkImageInfo::MakeN32Premul(size.width(), size.height())) {}
 
     MOCK_METHOD5(GetPixels,
diff --git a/cc/trees/image_animation_controller.cc b/cc/trees/image_animation_controller.cc
deleted file mode 100644
index 09e3023..0000000
--- a/cc/trees/image_animation_controller.cc
+++ /dev/null
@@ -1,401 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "cc/trees/image_animation_controller.h"
-
-#include "base/bind.h"
-#include "base/trace_event/trace_event.h"
-#include "cc/paint/image_animation_count.h"
-
-namespace cc {
-namespace {
-
-// The maximum number of time an animation can be delayed before it is reset to
-// start from the beginning, instead of fast-forwarding to catch up to the
-// desired frame.
-const base::TimeDelta kAnimationResyncCutoff = base::TimeDelta::FromMinutes(5);
-
-}  //  namespace
-
-ImageAnimationController::ImageAnimationController(
-    base::SingleThreadTaskRunner* task_runner,
-    base::Closure invalidation_callback)
-    : notifier_(task_runner, invalidation_callback) {}
-
-ImageAnimationController::~ImageAnimationController() = default;
-
-void ImageAnimationController::UpdateAnimatedImage(
-    const DiscardableImageMap::AnimatedImageMetadata& data) {
-  AnimationState& animation_state = animation_state_map_[data.paint_image_id];
-  animation_state.UpdateMetadata(data);
-}
-
-void ImageAnimationController::RegisterAnimationDriver(
-    PaintImage::Id paint_image_id,
-    AnimationDriver* driver) {
-  auto it = animation_state_map_.find(paint_image_id);
-  DCHECK(it != animation_state_map_.end());
-  it->second.AddDriver(driver);
-}
-
-void ImageAnimationController::UnregisterAnimationDriver(
-    PaintImage::Id paint_image_id,
-    AnimationDriver* driver) {
-  auto it = animation_state_map_.find(paint_image_id);
-  DCHECK(it != animation_state_map_.end());
-  it->second.RemoveDriver(driver);
-}
-
-const PaintImageIdFlatSet& ImageAnimationController::AnimateForSyncTree(
-    base::TimeTicks now) {
-  TRACE_EVENT0("cc", "ImageAnimationController::AnimateImagesForSyncTree");
-  DCHECK(images_animated_on_sync_tree_.empty());
-
-  notifier_.WillAnimate();
-  base::Optional<base::TimeTicks> next_invalidation_time;
-
-  for (auto id : active_animations_) {
-    auto it = animation_state_map_.find(id);
-    DCHECK(it != animation_state_map_.end());
-    AnimationState& state = it->second;
-
-    // Is anyone still interested in animating this image?
-    if (!state.ShouldAnimate())
-      continue;
-
-    // If we were able to advance this animation, invalidate it on the sync
-    // tree.
-    if (state.AdvanceFrame(now))
-      images_animated_on_sync_tree_.insert(id);
-
-    // Update the next invalidation time to the earliest time at which we need
-    // a frame to animate an image.
-    // Note its important to check ShouldAnimate() here again since advancing to
-    // a new frame on the sync tree means we might not need to animate this
-    // image any longer.
-    if (!state.ShouldAnimate())
-      continue;
-
-    DCHECK_GT(state.next_desired_frame_time(), now);
-    if (!next_invalidation_time.has_value()) {
-      next_invalidation_time.emplace(state.next_desired_frame_time());
-    } else {
-      next_invalidation_time = std::min(state.next_desired_frame_time(),
-                                        next_invalidation_time.value());
-    }
-  }
-
-  if (next_invalidation_time.has_value())
-    notifier_.Schedule(now, next_invalidation_time.value());
-  else
-    notifier_.Cancel();
-
-  return images_animated_on_sync_tree_;
-}
-
-void ImageAnimationController::UpdateStateFromDrivers(base::TimeTicks now) {
-  TRACE_EVENT0("cc", "UpdateStateFromAnimationDrivers");
-
-  base::Optional<base::TimeTicks> next_invalidation_time;
-  for (auto& it : animation_state_map_) {
-    AnimationState& state = it.second;
-    state.UpdateStateFromDrivers();
-
-    // If we don't need to animate this image anymore, remove it from the list
-    // of active animations.
-    // Note that by not updating the |next_invalidation_time| from this image
-    // here, we will cancel any pending invalidation scheduled for this image
-    // when updating the |notifier_| at the end of this loop.
-    if (!state.ShouldAnimate()) {
-      active_animations_.erase(it.first);
-      continue;
-    }
-
-    active_animations_.insert(it.first);
-    if (!next_invalidation_time.has_value()) {
-      next_invalidation_time.emplace(state.next_desired_frame_time());
-    } else {
-      next_invalidation_time = std::min(next_invalidation_time.value(),
-                                        state.next_desired_frame_time());
-    }
-  }
-
-  if (next_invalidation_time.has_value())
-    notifier_.Schedule(now, next_invalidation_time.value());
-  else
-    notifier_.Cancel();
-}
-
-void ImageAnimationController::DidActivate() {
-  TRACE_EVENT0("cc", "ImageAnimationController::WillActivate");
-
-  for (auto id : images_animated_on_sync_tree_) {
-    auto it = animation_state_map_.find(id);
-    DCHECK(it != animation_state_map_.end());
-    it->second.PushPendingToActive();
-  }
-
-  images_animated_on_sync_tree_.clear();
-}
-
-size_t ImageAnimationController::GetFrameIndexForImage(
-    PaintImage::Id paint_image_id,
-    WhichTree tree) const {
-  const auto& it = animation_state_map_.find(paint_image_id);
-  DCHECK(it != animation_state_map_.end());
-  return tree == WhichTree::PENDING_TREE ? it->second.pending_index()
-                                         : it->second.active_index();
-}
-
-const base::flat_set<ImageAnimationController::AnimationDriver*>&
-ImageAnimationController::GetDriversForTesting(
-    PaintImage::Id paint_image_id) const {
-  const auto& it = animation_state_map_.find(paint_image_id);
-  DCHECK(it != animation_state_map_.end());
-  return it->second.drivers_for_testing();
-}
-
-ImageAnimationController::AnimationState::AnimationState() = default;
-
-ImageAnimationController::AnimationState::AnimationState(
-    AnimationState&& other) = default;
-
-ImageAnimationController::AnimationState&
-ImageAnimationController::AnimationState::operator=(AnimationState&& other) =
-    default;
-
-ImageAnimationController::AnimationState::~AnimationState() {
-  DCHECK(drivers_.empty());
-}
-
-bool ImageAnimationController::AnimationState::ShouldAnimate() const {
-  DCHECK(repetitions_completed_ == 0 || is_complete());
-
-  // If we have no drivers for this image, no need to animate it.
-  if (!should_animate_from_drivers_)
-    return false;
-
-  switch (requested_repetitions_) {
-    case kAnimationLoopOnce:
-      if (repetitions_completed_ >= 1)
-        return false;
-      break;
-    case kAnimationNone:
-      NOTREACHED() << "We shouldn't be tracking kAnimationNone images";
-      break;
-    case kAnimationLoopInfinite:
-      break;
-    default:
-      if (requested_repetitions_ <= repetitions_completed_)
-        return false;
-  }
-
-  // If we have all data for this image and the policy allows it, we can
-  // continue animating it.
-  if (completion_state_ == PaintImage::CompletionState::DONE)
-    return true;
-
-  // If we have not yet received all data for this image, we can not advance to
-  // an incomplete frame.
-  if (!frames_[NextFrameIndex()].complete)
-    return false;
-
-  // If we don't have all data for this image, we can not trust the frame count
-  // and loop back to the first frame.
-  size_t last_frame_index = frames_.size() - 1;
-  if (pending_index_ == last_frame_index)
-    return false;
-
-  return true;
-}
-
-bool ImageAnimationController::AnimationState::AdvanceFrame(
-    base::TimeTicks now) {
-  DCHECK(ShouldAnimate());
-
-  // Start the animation from the first frame, if not yet started. We don't need
-  // an invalidation here since the pending/active tree should be displaying the
-  // first frame.
-  if (!animation_started_) {
-    DCHECK_EQ(pending_index_, 0u);
-    DCHECK_EQ(pending_index_, active_index_);
-
-    next_desired_frame_time_ = now + frames_[0].duration;
-    animation_started_ = true;
-    return false;
-  }
-
-  // Don't advance the animation if its not time yet to move to the next frame.
-  if (now < next_desired_frame_time_)
-    return false;
-
-  // If the animation is more than 5 min out of date, we don't bother catching
-  // up and start again from the current frame.
-  // Note that we don't need to invalidate this image since the active tree
-  // is already displaying the current frame.
-  if (now - next_desired_frame_time_ > kAnimationResyncCutoff) {
-    DCHECK_EQ(pending_index_, active_index_);
-    next_desired_frame_time_ = now + frames_[pending_index_].duration;
-    return false;
-  }
-
-  // Keep catching up the animation until we reach the frame we should be
-  // displaying now.
-  // TODO(khushalsagar): Avoid unnecessary iterations for skipping whole loops
-  // in the animations.
-  size_t last_frame_index = frames_.size() - 1;
-  while (next_desired_frame_time_ <= now && ShouldAnimate()) {
-    size_t next_frame_index = NextFrameIndex();
-    base::TimeTicks next_desired_frame_time =
-        next_desired_frame_time_ + frames_[next_frame_index].duration;
-
-    // The image may load more slowly than it's supposed to animate, so that by
-    // the time we reach the end of the first repetition, we're well behind.
-    // Start the animation from the first frame in this case, so that we don't
-    // skip frames (or whole iterations) trying to "catch up".  This is a
-    // tradeoff: It guarantees users see the whole animation the second time
-    // through and don't miss any repetitions, and is closer to what other
-    // browsers do; on the other hand, it makes animations "less accurate" for
-    // pages that try to sync an image and some other resource (e.g. audio),
-    // especially if users switch tabs (and thus stop drawing the animation,
-    // which will pause it) during that initial loop, then switch back later.
-    if (next_frame_index == 0u && repetitions_completed_ == 1 &&
-        next_desired_frame_time <= now) {
-      pending_index_ = 0u;
-      next_desired_frame_time_ = now + frames_[0].duration;
-      repetitions_completed_ = 0;
-      break;
-    }
-
-    pending_index_ = next_frame_index;
-    next_desired_frame_time_ = next_desired_frame_time;
-
-    // If we are advancing to the last frame and the image has been completely
-    // loaded (which means that the frame count is known to be accurate), we
-    // just finished a loop in the animation.
-    if (pending_index_ == last_frame_index && is_complete())
-      repetitions_completed_++;
-  }
-
-  return pending_index_ != active_index_;
-}
-
-void ImageAnimationController::AnimationState::UpdateMetadata(
-    const DiscardableImageMap::AnimatedImageMetadata& data) {
-  paint_image_id_ = data.paint_image_id;
-
-  DCHECK_NE(data.repetition_count, kAnimationNone);
-  requested_repetitions_ = data.repetition_count;
-
-  DCHECK(frames_.size() <= data.frames.size())
-      << "Updated recordings can only append frames";
-  frames_ = data.frames;
-  DCHECK_GT(frames_.size(), 1u);
-
-  DCHECK(completion_state_ != PaintImage::CompletionState::DONE ||
-         data.completion_state == PaintImage::CompletionState::DONE)
-      << "If the image was marked complete before, it can not be incomplete in "
-         "a new update";
-  completion_state_ = data.completion_state;
-
-  // Update the repetition count in case we have displayed the last frame and
-  // we now know the frame count to be accurate.
-  size_t last_frame_index = frames_.size() - 1;
-  if (pending_index_ == last_frame_index && is_complete() &&
-      repetitions_completed_ == 0)
-    repetitions_completed_++;
-}
-
-void ImageAnimationController::AnimationState::PushPendingToActive() {
-  active_index_ = pending_index_;
-}
-
-void ImageAnimationController::AnimationState::AddDriver(
-    AnimationDriver* driver) {
-  drivers_.insert(driver);
-}
-
-void ImageAnimationController::AnimationState::RemoveDriver(
-    AnimationDriver* driver) {
-  drivers_.erase(driver);
-}
-
-void ImageAnimationController::AnimationState::UpdateStateFromDrivers() {
-  should_animate_from_drivers_ = false;
-  for (auto* driver : drivers_) {
-    if (driver->ShouldAnimate(paint_image_id_)) {
-      should_animate_from_drivers_ = true;
-      break;
-    }
-  }
-}
-
-size_t ImageAnimationController::AnimationState::NextFrameIndex() const {
-  if (!animation_started_)
-    return 0u;
-  return (pending_index_ + 1) % frames_.size();
-}
-
-ImageAnimationController::DelayedNotifier::DelayedNotifier(
-    base::SingleThreadTaskRunner* task_runner,
-    base::Closure closure)
-    : task_runner_(task_runner),
-      closure_(std::move(closure)),
-      weak_factory_(this) {
-  DCHECK(task_runner_->BelongsToCurrentThread());
-}
-
-ImageAnimationController::DelayedNotifier::~DelayedNotifier() {
-  DCHECK(task_runner_->BelongsToCurrentThread());
-}
-
-void ImageAnimationController::DelayedNotifier::Schedule(
-    base::TimeTicks now,
-    base::TimeTicks notification_time) {
-  // If an animation is already pending, don't schedule another invalidation.
-  // We will schedule the next invalidation based on the latest animation state
-  // during AnimateForSyncTree.
-  if (animation_pending_)
-    return;
-
-  // The requested notification time can be in the past. For instance, if an
-  // animation was paused because the image became invisible.
-  if (notification_time < now)
-    notification_time = now;
-
-  // If we already have a notification scheduled to run at this time, no need to
-  // Cancel it.
-  if (pending_notification_time_.has_value() &&
-      notification_time == pending_notification_time_.value())
-    return;
-
-  // Cancel the pending notification since we the requested notification time
-  // has changed.
-  Cancel();
-
-  TRACE_EVENT2("cc", "ScheduleInvalidationForImageAnimation",
-               "notification_time", notification_time, "now", now);
-  pending_notification_time_.emplace(notification_time);
-  task_runner_->PostDelayedTask(
-      FROM_HERE,
-      base::Bind(&DelayedNotifier::Notify, weak_factory_.GetWeakPtr()),
-      notification_time - now);
-}
-
-void ImageAnimationController::DelayedNotifier::Cancel() {
-  pending_notification_time_.reset();
-  weak_factory_.InvalidateWeakPtrs();
-}
-
-void ImageAnimationController::DelayedNotifier::Notify() {
-  pending_notification_time_.reset();
-  animation_pending_ = true;
-  closure_.Run();
-}
-
-void ImageAnimationController::DelayedNotifier::WillAnimate() {
-  animation_pending_ = false;
-}
-
-}  // namespace cc
diff --git a/cc/trees/image_animation_controller.h b/cc/trees/image_animation_controller.h
deleted file mode 100644
index f536021..0000000
--- a/cc/trees/image_animation_controller.h
+++ /dev/null
@@ -1,224 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CC_TREES_IMAGE_ANIMATION_CONTROLLER_H_
-#define CC_TREES_IMAGE_ANIMATION_CONTROLLER_H_
-
-#include "base/cancelable_callback.h"
-#include "base/containers/flat_map.h"
-#include "base/containers/flat_set.h"
-#include "base/memory/weak_ptr.h"
-#include "base/optional.h"
-#include "base/time/time.h"
-#include "cc/cc_export.h"
-#include "cc/paint/discardable_image_map.h"
-#include "cc/paint/image_id.h"
-#include "cc/paint/paint_image.h"
-#include "cc/paint/paint_image_generator.h"
-#include "cc/tiles/tile_priority.h"
-
-namespace cc {
-class PaintImage;
-
-// ImageAnimationController is responsible for tracking state for ticking image
-// animations in the compositor.
-//
-// 1) It receives the updated metadata for these images from new recordings
-//    received from the client using UpdateAnimatedImage. The controller tracks
-//    the frame index of an image used on a tree, and advances the animation to
-//    the desired frame each time a new sync tree is created.
-//
-// 2) An AnimationDriver can register itself for deciding whether the
-//    controller animates an image. The animation is paused if there are no
-//    registered drivers interested in animating it.
-//
-//  3) An animation is only advanced on the sync tree, which is requested to be
-//     created using the |invalidation_callback|. This effectively means that
-//     the frame of the image used remains consistent throughout the lifetime of
-//     a tree, guaranteeing that the image update is atomic.
-class CC_EXPORT ImageAnimationController {
- public:
-  // AnimationDrivers are clients interested in driving image animations. An
-  // animation is ticked if there is at least one driver registered for which
-  // ShouldAnimate returns true. Once
-  // no drivers are registered for an image, or none of the registered drivers
-  // want us to animate, the animation is no longer ticked.
-  class CC_EXPORT AnimationDriver {
-   public:
-    virtual ~AnimationDriver() {}
-
-    // Returns true if the image should be animated.
-    virtual bool ShouldAnimate(PaintImage::Id paint_image_id) const = 0;
-  };
-
-  // |invalidation_callback| is the callback to trigger an invalidation and
-  // create a sync tree for advancing an image animation. The controller is
-  // guaranteed to receive a call to AnimateForSyncTree when the sync tree is
-  // created.
-  // |task_runner| is the thread on which the controller is used. The
-  // invalidation_callback can only be run on this thread.
-  ImageAnimationController(base::SingleThreadTaskRunner* task_runner,
-                           base::Closure invalidation_callback);
-  ~ImageAnimationController();
-
-  // Called to update the state for a PaintImage received in a new recording.
-  void UpdateAnimatedImage(
-      const DiscardableImageMap::AnimatedImageMetadata& data);
-
-  // Registers/Unregisters an animation driver interested in animating this
-  // image.
-  // Note that the state for this image must have been populated to the
-  // controller using UpdatePaintImage prior to registering any drivers.
-  void RegisterAnimationDriver(PaintImage::Id paint_image_id,
-                               AnimationDriver* driver);
-  void UnregisterAnimationDriver(PaintImage::Id paint_image_id,
-                                 AnimationDriver* driver);
-
-  // Called to advance the animations to the frame to be used on the sync tree.
-  // This should be called only once for a sync tree and must be followed with
-  // a call to DidActivate when this tree is activated.
-  // Returns the set of images that were animated and should be invalidated on
-  // this sync tree.
-  const PaintImageIdFlatSet& AnimateForSyncTree(base::TimeTicks now);
-
-  // Called whenever the ShouldAnimate response for a driver could have changed.
-  // For instance on a change in the visibility of the image, we would pause
-  // off-screen animations.
-  // This is called after every DrawProperties update and commit.
-  void UpdateStateFromDrivers(base::TimeTicks now);
-
-  // Called when the sync tree was activated and the animations' associated
-  // state should be pushed to the active tree.
-  void DidActivate();
-
-  // Returns the frame index to use for the given PaintImage and tree.
-  size_t GetFrameIndexForImage(PaintImage::Id paint_image_id,
-                               WhichTree tree) const;
-
-  const base::flat_set<AnimationDriver*>& GetDriversForTesting(
-      PaintImage::Id paint_image_id) const;
-
- private:
-  class AnimationState {
-   public:
-    AnimationState();
-    AnimationState(AnimationState&& other);
-    AnimationState& operator=(AnimationState&& other);
-    ~AnimationState();
-
-    bool ShouldAnimate() const;
-    bool AdvanceFrame(base::TimeTicks now);
-    void UpdateMetadata(const DiscardableImageMap::AnimatedImageMetadata& data);
-    void PushPendingToActive();
-
-    void AddDriver(AnimationDriver* driver);
-    void RemoveDriver(AnimationDriver* driver);
-    void UpdateStateFromDrivers();
-
-    size_t pending_index() const { return pending_index_; }
-    size_t active_index() const { return active_index_; }
-    base::TimeTicks next_desired_frame_time() const {
-      return next_desired_frame_time_;
-    }
-    const base::flat_set<AnimationDriver*>& drivers_for_testing() const {
-      return drivers_;
-    }
-
-   private:
-    size_t NextFrameIndex() const;
-    bool is_complete() const {
-      return completion_state_ == PaintImage::CompletionState::DONE;
-    }
-
-    PaintImage::Id paint_image_id_ = PaintImage::kNonLazyStableId;
-
-    // The frame metadata received from the most updated recording with this
-    // PaintImage.
-    std::vector<FrameMetadata> frames_;
-
-    // The number of animation loops requested for this image. For a value > 0,
-    // this number represents the exact number of iterations requested. A few
-    // special cases are represented using constants defined in
-    // cc/paint/image_animation_count.h
-    int requested_repetitions_ = kAnimationNone;
-
-    // The number of loops the animation has finished so far.
-    int repetitions_completed_ = 0;
-
-    // A set of drivers interested in animating this image.
-    base::flat_set<AnimationDriver*> drivers_;
-
-    // The index being used on the active tree, if a recording with this image
-    // is still present.
-    size_t active_index_ = PaintImage::kDefaultFrameIndex;
-
-    // The index being displayed on the pending tree.
-    size_t pending_index_ = PaintImage::kDefaultFrameIndex;
-
-    // The time at which we would like to display the next frame. This can be in
-    // the past, for instance, if we pause the animation from the image becoming
-    // invisible.
-    base::TimeTicks next_desired_frame_time_;
-
-    // Set if there is at least one driver interested in animating this image,
-    // cached from the last update.
-    bool should_animate_from_drivers_ = false;
-
-    // Set if the animation has been started.
-    bool animation_started_ = false;
-
-    // Whether the image is known to be completely loaded in the most recent
-    // recording received.
-    PaintImage::CompletionState completion_state_ =
-        PaintImage::CompletionState::PARTIALLY_DONE;
-
-    DISALLOW_COPY_AND_ASSIGN(AnimationState);
-  };
-
-  class DelayedNotifier {
-   public:
-    DelayedNotifier(base::SingleThreadTaskRunner* task_runner,
-                    base::Closure closure);
-    ~DelayedNotifier();
-
-    void Schedule(base::TimeTicks now, base::TimeTicks notification_time);
-    void Cancel();
-    void WillAnimate();
-
-   private:
-    void Notify();
-
-    base::SingleThreadTaskRunner* task_runner_;
-    base::Closure closure_;
-
-    // Set if a notification is currently pending.
-    base::Optional<base::TimeTicks> pending_notification_time_;
-
-    // Set if the notification was dispatched and the resulting animation on the
-    // next sync tree is pending.
-    bool animation_pending_ = false;
-
-    base::WeakPtrFactory<DelayedNotifier> weak_factory_;
-  };
-
-  // The AnimationState for images is persisted until they are cleared on
-  // navigation. This is because while an image might not be painted anymore, if
-  // it moves out of the interest rect for instance, the state retained is
-  // necessary to resume the animation.
-  // TODO(khushalsagar): Implement clearing of state on navigations.
-  using AnimationStateMap = base::flat_map<PaintImage::Id, AnimationState>;
-  AnimationStateMap animation_state_map_;
-
-  // The set of currently active animations.
-  PaintImageIdFlatSet active_animations_;
-
-  // The set of images that were animated and invalidated on the last sync tree.
-  PaintImageIdFlatSet images_animated_on_sync_tree_;
-
-  DelayedNotifier notifier_;
-};
-
-}  // namespace cc
-
-#endif  // CC_TREES_IMAGE_ANIMATION_CONTROLLER_H_
diff --git a/cc/trees/image_animation_controller_unittest.cc b/cc/trees/image_animation_controller_unittest.cc
deleted file mode 100644
index db983ea..0000000
--- a/cc/trees/image_animation_controller_unittest.cc
+++ /dev/null
@@ -1,679 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "cc/trees/image_animation_controller.h"
-
-#include "base/memory/ptr_util.h"
-#include "base/run_loop.h"
-#include "base/test/gtest_util.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace cc {
-
-class FakeAnimationDriver : public ImageAnimationController::AnimationDriver {
- public:
-  FakeAnimationDriver() {}
-  ~FakeAnimationDriver() override {}
-
-  void set_should_animate(bool should_animate) {
-    should_animate_ = should_animate;
-  }
-
-  // ImageAnimationController::AnimationDriver implementation.
-  bool ShouldAnimate(PaintImage::Id paint_image_id) const override {
-    return should_animate_;
-  }
-
- private:
-  bool should_animate_ = true;
-};
-
-class DelayTrackingTaskRunner : public base::SingleThreadTaskRunner {
- public:
-  explicit DelayTrackingTaskRunner(base::SingleThreadTaskRunner* task_runner)
-      : task_runner_(task_runner) {}
-
-  bool PostDelayedTask(const base::Location& from_here,
-                       base::OnceClosure task,
-                       base::TimeDelta delay) override {
-    last_delay_.emplace(delay);
-    return task_runner_->PostTask(from_here, std::move(task));
-  }
-
-  bool RunsTasksInCurrentSequence() const override {
-    return task_runner_->RunsTasksInCurrentSequence();
-  }
-
-  bool PostNonNestableDelayedTask(const base::Location& from_here,
-                                  base::OnceClosure task,
-                                  base::TimeDelta delay) override {
-    last_delay_.emplace(delay);
-    return task_runner_->PostTask(from_here, std::move(task));
-  }
-
-  void VerifyDelay(base::TimeDelta expected) {
-    DCHECK(last_delay_.has_value());
-    EXPECT_EQ(last_delay_.value(), expected);
-    last_delay_.reset();
-  }
-
-  bool has_delay() const { return last_delay_.has_value(); }
-
- private:
-  ~DelayTrackingTaskRunner() override = default;
-
-  base::Optional<base::TimeDelta> last_delay_;
-  base::SingleThreadTaskRunner* task_runner_;
-};
-
-class ImageAnimationControllerTest : public testing::Test {
- public:
-  void SetUp() override {
-    task_runner_ =
-        new DelayTrackingTaskRunner(base::ThreadTaskRunnerHandle::Get().get());
-    base::Closure invalidation_callback =
-        base::Bind(&ImageAnimationControllerTest::RequestInvalidation,
-                   base::Unretained(this));
-    controller_ = base::MakeUnique<ImageAnimationController>(
-        task_runner_.get(), invalidation_callback);
-    now_ += base::TimeDelta::FromSeconds(10);
-  }
-
-  void TearDown() override { controller_.reset(); }
-
-  void LoopOnceNoDelay(PaintImage::Id paint_image_id,
-                       const std::vector<FrameMetadata>& frames,
-                       size_t num_of_frames_to_loop,
-                       int repetitions_completed) {
-    DCHECK_LE(num_of_frames_to_loop, frames.size());
-
-    invalidation_count_ = 0;
-    for (size_t i = 0; i < num_of_frames_to_loop; ++i) {
-      SCOPED_TRACE(i);
-
-      // Run the pending invalidation.
-      base::RunLoop().RunUntilIdle();
-      EXPECT_EQ(invalidation_count_, static_cast<int>(i + 1));
-
-      // Animate the image on the sync tree.
-      auto animated_images = controller_->AnimateForSyncTree(now_);
-      EXPECT_EQ(controller_->GetFrameIndexForImage(paint_image_id,
-                                                   WhichTree::PENDING_TREE),
-                i);
-      size_t active_index = i - 1;
-      if (i == 0u) {
-        // If we are displaying the first frame on the pending tree, then the
-        // active tree has the first frame as well if this is the first loop,
-        // otherwise it should be the last frame since we are starting a new
-        // loop.
-        if (repetitions_completed == 0)
-          active_index = 0u;
-        else
-          active_index = frames.size() - 1;
-      }
-
-      EXPECT_EQ(controller_->GetFrameIndexForImage(paint_image_id,
-                                                   WhichTree::ACTIVE_TREE),
-                active_index);
-
-      if (i == 0u && repetitions_completed == 0) {
-        // Starting the animation does not perform any invalidation.
-        EXPECT_EQ(animated_images.size(), 0u);
-      } else {
-        EXPECT_EQ(animated_images.size(), 1u);
-        EXPECT_EQ(animated_images.count(paint_image_id), 1u);
-      }
-
-      // Animating should schedule an invalidation for the next frame, until we
-      // reach the last frame.
-      if (i != num_of_frames_to_loop - 1)
-        task_runner_->VerifyDelay(frames[i].duration);
-
-      // Activate and advance time to the next frame.
-      controller_->DidActivate();
-      AdvanceNow(frames[i].duration);
-    }
-  }
-
- protected:
-  void RequestInvalidation() { invalidation_count_++; }
-
-  void AdvanceNow(base::TimeDelta delta) { now_ += delta; }
-
-  base::TimeTicks now_;
-  int invalidation_count_ = 0;
-  std::unique_ptr<ImageAnimationController> controller_;
-  scoped_refptr<DelayTrackingTaskRunner> task_runner_;
-};
-
-TEST_F(ImageAnimationControllerTest, AnimationWithDelays) {
-  std::vector<FrameMetadata> frames = {
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(5)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(3)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(4))};
-
-  DiscardableImageMap::AnimatedImageMetadata data(
-      PaintImage::GetNextId(), PaintImage::CompletionState::DONE, frames,
-      kAnimationLoopInfinite);
-  controller_->UpdateAnimatedImage(data);
-  FakeAnimationDriver driver;
-  controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
-  controller_->UpdateStateFromDrivers(now_);
-
-  // Display 2 loops in the animation.
-  LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 0);
-  LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 1);
-
-  // now_ is set to the time at which the first frame should be displayed for
-  // the third iteration. Add a delay that causes us to skip the first frame.
-  base::TimeDelta additional_delay = base::TimeDelta::FromMilliseconds(1);
-  AdvanceNow(data.frames[0].duration + additional_delay);
-  auto animated_images = controller_->AnimateForSyncTree(now_);
-  EXPECT_EQ(animated_images.size(), 1u);
-  EXPECT_EQ(animated_images.count(data.paint_image_id), 1u);
-
-  // The pending tree displays the second frame while the active tree has the
-  // third frame.
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::PENDING_TREE),
-            1u);
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::ACTIVE_TREE),
-            2u);
-
-  // Invalidation delay is based on the duration of the second frame and the
-  // delay in creating this sync tree.
-  task_runner_->VerifyDelay(frames[1].duration - additional_delay);
-  invalidation_count_ = 0;
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(invalidation_count_, 1);
-
-  // Activate and animate with a delay that causes us to skip another frame.
-  controller_->DidActivate();
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::ACTIVE_TREE),
-            1u);
-  AdvanceNow(data.frames[1].duration + data.frames[2].duration);
-  animated_images = controller_->AnimateForSyncTree(now_);
-  EXPECT_EQ(animated_images.size(), 1u);
-  EXPECT_EQ(animated_images.count(data.paint_image_id), 1u);
-
-  // The pending tree displays the first frame, while the active tree has the
-  // second frame.
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::PENDING_TREE),
-            0u);
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::ACTIVE_TREE),
-            1u);
-
-  // Invalidation delay is based on the duration of the first frame and the
-  // initial additionaly delay.
-  task_runner_->VerifyDelay(frames[0].duration - additional_delay);
-  invalidation_count_ = 0;
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(invalidation_count_, 1);
-
-  controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
-}
-
-TEST_F(ImageAnimationControllerTest, DriversControlAnimationTicking) {
-  std::vector<FrameMetadata> first_image_frames = {
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))};
-  DiscardableImageMap::AnimatedImageMetadata first_data(
-      PaintImage::GetNextId(), PaintImage::CompletionState::DONE,
-      first_image_frames, kAnimationLoopOnce);
-  controller_->UpdateAnimatedImage(first_data);
-  FakeAnimationDriver first_driver;
-  controller_->RegisterAnimationDriver(first_data.paint_image_id,
-                                       &first_driver);
-
-  std::vector<FrameMetadata> second_image_frames = {
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(5)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))};
-  DiscardableImageMap::AnimatedImageMetadata second_data(
-      PaintImage::GetNextId(), PaintImage::CompletionState::DONE,
-      second_image_frames, kAnimationLoopOnce);
-  controller_->UpdateAnimatedImage(second_data);
-  FakeAnimationDriver second_driver;
-  controller_->RegisterAnimationDriver(second_data.paint_image_id,
-                                       &second_driver);
-
-  // Disable animating from all drivers, no invalidation request should be made.
-  first_driver.set_should_animate(false);
-  second_driver.set_should_animate(false);
-  controller_->UpdateStateFromDrivers(now_);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(invalidation_count_, 0);
-
-  // Enable animating from the first driver, which should schedule an
-  // invalidation to advance this animation.
-  first_driver.set_should_animate(true);
-  controller_->UpdateStateFromDrivers(now_);
-  task_runner_->VerifyDelay(base::TimeDelta());
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(invalidation_count_, 1);
-
-  // Start animating the first image.
-  auto animated_images = controller_->AnimateForSyncTree(now_);
-  EXPECT_EQ(animated_images.size(), 0u);
-
-  // Invalidation should be scheduled for this image.
-  task_runner_->VerifyDelay(first_image_frames[0].duration);
-
-  // Now enable animating the second image instead.
-  second_driver.set_should_animate(true);
-  controller_->UpdateStateFromDrivers(now_);
-
-  // Invalidation is triggered to start with no delay since the second image has
-  // not started animating yet.
-  task_runner_->VerifyDelay(base::TimeDelta());
-
-  // Disable animating all images.
-  first_driver.set_should_animate(false);
-  second_driver.set_should_animate(false);
-  controller_->UpdateStateFromDrivers(now_);
-
-  // Any scheduled invalidation should be cancelled.
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(invalidation_count_, 1);
-
-  controller_->UnregisterAnimationDriver(first_data.paint_image_id,
-                                         &first_driver);
-  controller_->UnregisterAnimationDriver(second_data.paint_image_id,
-                                         &second_driver);
-}
-
-TEST_F(ImageAnimationControllerTest, RepetitionsRequested) {
-  std::vector<FrameMetadata> frames = {
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(3)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(4))};
-
-  DiscardableImageMap::AnimatedImageMetadata data(
-      PaintImage::GetNextId(), PaintImage::CompletionState::DONE, frames,
-      kAnimationLoopOnce);
-  controller_->UpdateAnimatedImage(data);
-  FakeAnimationDriver driver;
-  controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
-  controller_->UpdateStateFromDrivers(now_);
-
-  // Finish a single loop in the animation.
-  LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 0);
-
-  // No invalidation should be scheduled now, since the requested number of
-  // loops have been completed.
-  invalidation_count_ = 0;
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(invalidation_count_, 0);
-  controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
-
-  // Now with a repetition count of 5.
-  data.paint_image_id = PaintImage::GetNextId();
-  data.repetition_count = 5;
-  controller_->UpdateAnimatedImage(data);
-  controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
-  controller_->UpdateStateFromDrivers(now_);
-  for (int i = 0; i < data.repetition_count; ++i) {
-    LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), i);
-
-    // Since we will be looping back to the first frame, the invalidation should
-    // have the delay of the last frame. Until we reach the end of requested
-    // iterations.
-    if (i < data.repetition_count - 1)
-      task_runner_->VerifyDelay(frames.back().duration);
-    invalidation_count_ = 0;
-  }
-
-  // No invalidation should be scheduled now, since the requested number of
-  // loops have been completed.
-  invalidation_count_ = 0;
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(invalidation_count_, 0);
-  controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
-
-  // Now with kAnimationLoopInfinite.
-  data.paint_image_id = PaintImage::GetNextId();
-  data.repetition_count = kAnimationLoopInfinite;
-  controller_->UpdateAnimatedImage(data);
-  controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
-  controller_->UpdateStateFromDrivers(now_);
-  for (int i = 0; i < 7; ++i) {
-    LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), i);
-
-    // Since we will be looping back to the first frame, the invalidation should
-    // have the delay of the last frame. Until we reach the end of requested
-    // iterations.
-    if (i < data.repetition_count - 1)
-      task_runner_->VerifyDelay(frames.back().duration);
-    invalidation_count_ = 0;
-  }
-
-  // We still have an invalidation scheduled since the image will keep looping
-  // till the drivers keep the animation active.
-  invalidation_count_ = 0;
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(invalidation_count_, 1);
-  controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
-
-  // Now try with a kAnimationNone image, which should result in a DCHECK
-  // failure.
-  data.paint_image_id = PaintImage::GetNextId();
-  data.repetition_count = kAnimationNone;
-  EXPECT_DCHECK_DEATH(controller_->UpdateAnimatedImage(data));
-}
-
-TEST_F(ImageAnimationControllerTest, DisplayCompleteFrameOnly) {
-  std::vector<FrameMetadata> frames = {
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(3)),
-      FrameMetadata(false, base::TimeDelta::FromMilliseconds(4))};
-
-  DiscardableImageMap::AnimatedImageMetadata data(
-      PaintImage::GetNextId(), PaintImage::CompletionState::PARTIALLY_DONE,
-      frames, kAnimationLoopInfinite);
-  controller_->UpdateAnimatedImage(data);
-  FakeAnimationDriver driver;
-  controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
-  controller_->UpdateStateFromDrivers(now_);
-
-  // Advance until the second frame.
-  LoopOnceNoDelay(data.paint_image_id, frames, 2, 0);
-
-  // We have no invalidation scheduled since its not possible to animate the
-  // image further until the second frame is completed.
-  invalidation_count_ = 0;
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(invalidation_count_, 0);
-
-  // The frame is still incomplete but the image has been marked complete, which
-  // should push the animation forward.
-  data.completion_state = PaintImage::CompletionState::DONE;
-  controller_->UpdateAnimatedImage(data);
-  controller_->UpdateStateFromDrivers(now_);
-
-  // There is no delay for advancing to this frame since we advanced the time
-  // in the loop iteration above.
-  task_runner_->VerifyDelay(base::TimeDelta());
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(invalidation_count_, 1);
-
-  controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
-}
-
-TEST_F(ImageAnimationControllerTest, DontLoopPartiallyLoadedImages) {
-  std::vector<FrameMetadata> frames = {
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))};
-
-  DiscardableImageMap::AnimatedImageMetadata data(
-      PaintImage::GetNextId(), PaintImage::CompletionState::PARTIALLY_DONE,
-      frames, 2);
-  controller_->UpdateAnimatedImage(data);
-  FakeAnimationDriver driver;
-  controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
-  controller_->UpdateStateFromDrivers(now_);
-
-  // Finish the first loop.
-  LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 0);
-
-  // We shouldn't be looping back to the first frame until the image is known to
-  // be completely loaded.
-  invalidation_count_ = 0;
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(invalidation_count_, 0);
-
-  // Now add another frame and mark the image complete. The animation should
-  // advance and we should see another repetition. This verifies that we don't
-  // mark loops complete on reaching the last frame until the image is
-  // completely loaded and the frame count is known to be accurate.
-  frames.push_back(FrameMetadata(true, base::TimeDelta::FromMilliseconds(4)));
-  data.completion_state = PaintImage::CompletionState::DONE;
-  data.frames = frames;
-  controller_->UpdateAnimatedImage(data);
-  controller_->UpdateStateFromDrivers(now_);
-
-  // The animation advances to the last frame. We don't have a delay since we
-  // already advanced to the desired time in the loop above.
-  task_runner_->VerifyDelay(base::TimeDelta());
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(invalidation_count_, 1);
-  auto animated_images = controller_->AnimateForSyncTree(now_);
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::PENDING_TREE),
-            2u);
-  EXPECT_EQ(animated_images.size(), 1u);
-  EXPECT_EQ(animated_images.count(data.paint_image_id), 1u);
-  controller_->DidActivate();
-
-  // Advancing the animation scheduled an invalidation for the next iteration.
-  task_runner_->VerifyDelay(frames.back().duration);
-
-  // Perform another loop in the animation.
-  AdvanceNow(frames.back().duration);
-  LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 1);
-
-  // No invalidation should have been requested at the end of the second loop.
-  invalidation_count_ = 0;
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(invalidation_count_, 0);
-
-  controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
-}
-
-TEST_F(ImageAnimationControllerTest, DontAdvanceUntilDesiredTime) {
-  std::vector<FrameMetadata> frames = {
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))};
-
-  DiscardableImageMap::AnimatedImageMetadata data(
-      PaintImage::GetNextId(), PaintImage::CompletionState::DONE, frames,
-      kAnimationLoopOnce);
-  controller_->UpdateAnimatedImage(data);
-  FakeAnimationDriver driver;
-  controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
-  controller_->UpdateStateFromDrivers(now_);
-
-  // Advance the first frame.
-  task_runner_->VerifyDelay(base::TimeDelta());
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(invalidation_count_, 1);
-  auto animated_images = controller_->AnimateForSyncTree(now_);
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::PENDING_TREE),
-            0u);
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::ACTIVE_TREE),
-            0u);
-  EXPECT_EQ(animated_images.size(), 0u);
-  controller_->DidActivate();
-
-  // We have an invalidation request for the second frame.
-  task_runner_->VerifyDelay(frames[0].duration);
-
-  // While there is still time for the second frame, we get a new sync tree. The
-  // animation is not advanced.
-  base::TimeDelta time_remaining = base::TimeDelta::FromMilliseconds(1);
-  AdvanceNow(frames[0].duration - time_remaining);
-  animated_images = controller_->AnimateForSyncTree(now_);
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::PENDING_TREE),
-            0u);
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::ACTIVE_TREE),
-            0u);
-  EXPECT_EQ(animated_images.size(), 0u);
-  controller_->DidActivate();
-
-  // We did not get another invalidation request because there is no change in
-  // the desired time and the previous request is still pending.
-  EXPECT_FALSE(task_runner_->has_delay());
-
-  // We have a sync tree before the invalidation task could run.
-  AdvanceNow(time_remaining);
-  animated_images = controller_->AnimateForSyncTree(now_);
-  EXPECT_EQ(animated_images.size(), 1u);
-  EXPECT_EQ(animated_images.count(data.paint_image_id), 1u);
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::PENDING_TREE),
-            1u);
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::ACTIVE_TREE),
-            0u);
-  controller_->DidActivate();
-
-  // We shouldn't have an invalidation because the animation was already
-  // advanced to the last frame and the previous one should have been cancelled.
-  invalidation_count_ = 0;
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(invalidation_count_, 0);
-
-  controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
-}
-
-TEST_F(ImageAnimationControllerTest, RestartAfterSyncCutoff) {
-  std::vector<FrameMetadata> frames = {
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))};
-
-  DiscardableImageMap::AnimatedImageMetadata data(
-      PaintImage::GetNextId(), PaintImage::CompletionState::DONE, frames,
-      kAnimationLoopOnce);
-  controller_->UpdateAnimatedImage(data);
-  FakeAnimationDriver driver;
-  controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
-  controller_->UpdateStateFromDrivers(now_);
-
-  // Advance the first frame.
-  task_runner_->VerifyDelay(base::TimeDelta());
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(invalidation_count_, 1);
-  auto animated_images = controller_->AnimateForSyncTree(now_);
-  EXPECT_EQ(animated_images.size(), 0u);
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::PENDING_TREE),
-            0u);
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::ACTIVE_TREE),
-            0u);
-  controller_->DidActivate();
-
-  // Invalidation request for the second frame.
-  task_runner_->VerifyDelay(frames[0].duration);
-
-  // Advance the time by 10 min.
-  AdvanceNow(base::TimeDelta::FromMinutes(10));
-
-  // Animate again, it starts from the first frame. We don't see an
-  // invalidation, because that's the frame we are already displaying.
-  animated_images = controller_->AnimateForSyncTree(now_);
-  EXPECT_EQ(animated_images.size(), 0u);
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::PENDING_TREE),
-            0u);
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::ACTIVE_TREE),
-            0u);
-  controller_->DidActivate();
-
-  // New invalidation request since the desired invalidation time changed.
-  task_runner_->VerifyDelay(frames[0].duration);
-  invalidation_count_ = 0;
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(invalidation_count_, 1);
-
-  controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
-}
-
-TEST_F(ImageAnimationControllerTest, DontSkipLoopsToCatchUpAfterLoad) {
-  std::vector<FrameMetadata> frames = {
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(3)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(4)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(5))};
-
-  DiscardableImageMap::AnimatedImageMetadata data(
-      PaintImage::GetNextId(), PaintImage::CompletionState::PARTIALLY_DONE,
-      frames, kAnimationLoopInfinite);
-  controller_->UpdateAnimatedImage(data);
-  FakeAnimationDriver driver;
-  controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
-  controller_->UpdateStateFromDrivers(now_);
-
-  // Perform the first loop while the image is partially loaded, until the third
-  // frame.
-  LoopOnceNoDelay(data.paint_image_id, frames, 3u, 0);
-
-  // The invalidation has been scheduled with a delay for the third frame's
-  // duration.
-  task_runner_->VerifyDelay(frames[2].duration);
-
-  // |now_| is set to the desired time for the fourth frame. Advance further so
-  // we would reach the time for the second frame.
-  AdvanceNow(frames[3].duration + frames[0].duration);
-
-  // Finish the image load.
-  data.completion_state = PaintImage::CompletionState::DONE;
-  controller_->UpdateAnimatedImage(data);
-  controller_->UpdateStateFromDrivers(now_);
-
-  // Invalidation is scheduled immediately because we are way past the desired
-  // time. We should start from the first frame after the image is loaded
-  // instead of skipping frames.
-  task_runner_->VerifyDelay(base::TimeDelta());
-  auto animated_images = controller_->AnimateForSyncTree(now_);
-  EXPECT_EQ(animated_images.size(), 1u);
-  EXPECT_EQ(animated_images.count(data.paint_image_id), 1u);
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::PENDING_TREE),
-            0u);
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::ACTIVE_TREE),
-            2u);
-  controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
-}
-
-TEST_F(ImageAnimationControllerTest, FinishRepetitionsDuringCatchUp) {
-  std::vector<FrameMetadata> frames = {
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(3)),
-      FrameMetadata(true, base::TimeDelta::FromMilliseconds(4))};
-
-  // The animation wants 3 loops.
-  DiscardableImageMap::AnimatedImageMetadata data(
-      PaintImage::GetNextId(), PaintImage::CompletionState::DONE, frames, 3);
-  controller_->UpdateAnimatedImage(data);
-  FakeAnimationDriver driver;
-  controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
-  controller_->UpdateStateFromDrivers(now_);
-
-  // Finish 2 loops.
-  LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 0);
-  LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 1);
-
-  // now_ is set to the desired time for the first frame. Advance it so we would
-  // reach way beyond the third repeition.
-  AdvanceNow(base::TimeDelta::FromMinutes(1));
-
-  // Advance the animation, we should see the last frame since the desired
-  // repetition count will be reached during catch up.
-  invalidation_count_ = 0;
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(invalidation_count_, 1);
-  auto animated_images = controller_->AnimateForSyncTree(now_);
-  // No invalidation since the active tree is already at the last frame.
-  EXPECT_EQ(animated_images.size(), 0u);
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::PENDING_TREE),
-            frames.size() - 1);
-  EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
-                                               WhichTree::ACTIVE_TREE),
-            frames.size() - 1);
-
-  controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
-}
-
-}  // namespace cc
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 3ce444d..e7605f17 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -67,7 +67,6 @@
 #include "cc/trees/draw_property_utils.h"
 #include "cc/trees/effect_node.h"
 #include "cc/trees/frame_rate_counter.h"
-#include "cc/trees/image_animation_controller.h"
 #include "cc/trees/latency_info_swap_promise_monitor.h"
 #include "cc/trees/layer_tree_frame_sink.h"
 #include "cc/trees/layer_tree_host_common.h"
@@ -271,16 +270,6 @@
       settings.top_controls_hide_threshold);
 
   tile_manager_.SetDecodedImageTracker(&decoded_image_tracker_);
-
-  if (settings_.enable_image_animations) {
-    // It is safe to use base::Unretained here since we will outlive the
-    // ImageAnimationController.
-    base::Closure invalidation_callback =
-        base::Bind(&LayerTreeHostImpl::RequestInvalidationForAnimatedImages,
-                   base::Unretained(this));
-    image_animation_controller_.emplace(GetTaskRunner(),
-                                        std::move(invalidation_callback));
-  }
 }
 
 LayerTreeHostImpl::~LayerTreeHostImpl() {
@@ -355,6 +344,9 @@
 }
 
 void LayerTreeHostImpl::UpdateSyncTreeAfterCommitOrImplSideInvalidation() {
+  sync_tree()->InvalidateRegionForImages(
+      tile_manager_.TakeImagesToInvalidateOnSyncTree());
+
   if (CommitToActiveTree()) {
     active_tree_->HandleScrollbarShowRequestsFromMain();
 
@@ -390,20 +382,6 @@
   // layer can or cannot use lcd text.  So, this is the cleanup pass to
   // determine if lcd state needs to switch due to draw properties.
   sync_tree()->UpdateCanUseLCDText();
-
-  // Defer invalidating images until UpdateDrawProperties is performed since
-  // that updates whether an image should be animated based on its visibility
-  // and the updated data for the image from the main frame.
-  PaintImageIdFlatSet images_to_invalidate =
-      tile_manager_.TakeImagesToInvalidateOnSyncTree();
-  if (image_animation_controller_.has_value()) {
-    const auto& animated_images =
-        image_animation_controller_.value().AnimateForSyncTree(
-            CurrentBeginFrameArgs().frame_time);
-    images_to_invalidate.insert(animated_images.begin(), animated_images.end());
-  }
-  sync_tree()->InvalidateRegionForImages(images_to_invalidate);
-
   // Start working on newly created tiles immediately if needed.
   // TODO(vmpstr): Investigate always having PrepareTiles issue
   // NotifyReadyToActivate, instead of handling it here.
@@ -1428,15 +1406,6 @@
   client_->NeedsImplSideInvalidation(needs_first_draw_on_activation);
 }
 
-size_t LayerTreeHostImpl::GetFrameIndexForImage(const PaintImage& paint_image,
-                                                WhichTree tree) const {
-  if (!paint_image.ShouldAnimate() || !image_animation_controller_.has_value())
-    return paint_image.frame_index();
-
-  return image_animation_controller_.value().GetFrameIndexForImage(
-      paint_image.stable_id(), tree);
-}
-
 void LayerTreeHostImpl::NotifyReadyToActivate() {
   pending_tree_raster_duration_timer_.reset();
   client_->NotifyReadyToActivate();
@@ -2237,17 +2206,8 @@
 
   UpdateViewportContainerSizes();
 
-  // Inform the ImageAnimationController and TileManager before dirtying tile
-  // priorities. Since these components cache tree specific state, these should
-  // be updated before DidModifyTilePriorities which can synchronously issue a
-  // PrepareTiles.
-  if (image_animation_controller_)
-    image_animation_controller_->DidActivate();
-  tile_manager_.DidActivateSyncTree();
-
   active_tree_->DidBecomeActive();
   client_->RenewTreePriority();
-
   // If we have any picture layers, then by activating we also modified tile
   // priorities.
   if (!active_tree_->picture_layers().empty())
@@ -4540,13 +4500,4 @@
     animation_controller->DidScrollUpdate();
 }
 
-void LayerTreeHostImpl::RequestInvalidationForAnimatedImages() {
-  DCHECK(image_animation_controller_);
-
-  // If we are animating an image, we want at least one draw of the active tree
-  // before a new tree is activated.
-  bool needs_first_draw_on_activation = true;
-  client_->NeedsImplSideInvalidation(needs_first_draw_on_activation);
-}
-
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 0373670..250d679 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -59,7 +59,6 @@
 class DebugRectHistory;
 class EvictionTilePriorityQueue;
 class FrameRateCounter;
-class ImageAnimationController;
 class LayerImpl;
 class LayerTreeImpl;
 class MemoryHistory;
@@ -355,8 +354,6 @@
   void SetIsLikelyToRequireADraw(bool is_likely_to_require_a_draw) override;
   gfx::ColorSpace GetRasterColorSpace() const override;
   void RequestImplSideInvalidationForCheckerImagedTiles() override;
-  size_t GetFrameIndexForImage(const PaintImage& paint_image,
-                               WhichTree tree) const override;
 
   // ScrollbarAnimationControllerClient implementation.
   void PostDelayedScrollbarAnimationTask(const base::Closure& task,
@@ -420,11 +417,6 @@
   }
   ResourcePool* resource_pool() { return resource_pool_.get(); }
   ImageDecodeCache* image_decode_cache() { return image_decode_cache_.get(); }
-  ImageAnimationController* image_animation_controller() {
-    if (!image_animation_controller_.has_value())
-      return nullptr;
-    return &image_animation_controller_.value();
-  }
 
   virtual void WillBeginImplFrame(const viz::BeginFrameArgs& args);
   virtual void DidFinishImplFrame();
@@ -741,9 +733,6 @@
   // tree, because the active tree value always takes precedence for scrollbars.
   void PushScrollbarOpacitiesFromActiveToPending();
 
-  // Request an impl-side invalidation to animate an image.
-  void RequestInvalidationForAnimatedImages();
-
   using UIResourceMap = std::unordered_map<UIResourceId, UIResourceData>;
   UIResourceMap ui_resource_map_;
 
@@ -907,8 +896,6 @@
 
   ImplThreadPhase impl_thread_phase_;
 
-  base::Optional<ImageAnimationController> image_animation_controller_;
-
   DISALLOW_COPY_AND_ASSIGN(LayerTreeHostImpl);
 };
 
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 84dcd17..67ab40aa 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -25,12 +25,10 @@
 #include "cc/layers/picture_layer.h"
 #include "cc/layers/solid_color_layer.h"
 #include "cc/layers/video_layer.h"
-#include "cc/paint/image_animation_count.h"
 #include "cc/resources/ui_resource_manager.h"
 #include "cc/test/fake_content_layer_client.h"
 #include "cc/test/fake_layer_tree_host_client.h"
 #include "cc/test/fake_output_surface.h"
-#include "cc/test/fake_paint_image_generator.h"
 #include "cc/test/fake_painted_scrollbar_layer.h"
 #include "cc/test/fake_picture_layer.h"
 #include "cc/test/fake_picture_layer_impl.h"
@@ -7843,8 +7841,7 @@
 
     image_ = DrawImage(CreateDiscardablePaintImage(gfx::Size(400, 400)),
                        SkIRect::MakeWH(400, 400), kNone_SkFilterQuality,
-                       SkMatrix::I(), PaintImage::kDefaultFrameIndex,
-                       gfx::ColorSpace());
+                       SkMatrix::I(), gfx::ColorSpace());
     auto callback =
         base::Bind(&LayerTreeHostTestQueueImageDecode::ImageDecodeFinished,
                    base::Unretained(this));
@@ -8044,86 +8041,5 @@
 
 SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestDiscardAckAfterRelease);
 
-class LayerTreeHostTestImageAnimation : public LayerTreeHostTest {
- public:
-  void BeginTest() override { PostSetNeedsCommitToMainThread(); }
-
-  void InitializeSettings(LayerTreeSettings* settings) override {
-    settings->enable_image_animations = true;
-  }
-
-  void SetupTree() override {
-    gfx::Size layer_size(1000, 500);
-    content_layer_client_.set_bounds(layer_size);
-    content_layer_client_.set_fill_with_nonsolid_color(true);
-    std::vector<FrameMetadata> frames = {
-        FrameMetadata(true, base::TimeDelta::FromSeconds(1)),
-        FrameMetadata(true, base::TimeDelta::FromSeconds(1)),
-        FrameMetadata(true, base::TimeDelta::FromSeconds(1))};
-    generator_ = sk_make_sp<FakePaintImageGenerator>(
-        SkImageInfo::MakeN32Premul(500, 500), frames);
-    PaintImage image =
-        PaintImageBuilder()
-            .set_id(PaintImage::GetNextId())
-            .set_paint_image_generator(generator_)
-            .set_frame_index(0u)
-            .set_animation_type(PaintImage::AnimationType::ANIMATED)
-            .set_repetition_count(kAnimationLoopOnce)
-            .TakePaintImage();
-    content_layer_client_.add_draw_image(image, gfx::Point(0, 0), PaintFlags());
-
-    layer_tree_host()->SetRootLayer(
-        FakePictureLayer::Create(&content_layer_client_));
-    layer_tree_host()->root_layer()->SetBounds(layer_size);
-    LayerTreeTest::SetupTree();
-  }
-
-  void WillPrepareToDrawOnThread(LayerTreeHostImpl* host_impl) override {
-    gfx::Rect image_rect(-1, -1, 502, 502);
-    auto* layer = static_cast<PictureLayerImpl*>(
-        host_impl->active_tree()->root_layer_for_testing());
-    switch (++draw_count_) {
-      case 1:
-        // First draw, everything is invalid.
-        EXPECT_EQ(layer->InvalidationForTesting().bounds(),
-                  gfx::Rect(layer->bounds()));
-        EXPECT_EQ(layer->update_rect(), gfx::Rect(layer->bounds()));
-        EXPECT_EQ(generator_->frames_decoded().size(), 1u);
-        EXPECT_EQ(generator_->frames_decoded().count(0u), 1u);
-        break;
-      case 2:
-        // Every frame after the first one should invalidate only the image.
-        EXPECT_EQ(layer->InvalidationForTesting().bounds(), image_rect);
-        EXPECT_EQ(layer->update_rect(), image_rect);
-        EXPECT_EQ(generator_->frames_decoded().size(), 2u);
-        EXPECT_EQ(generator_->frames_decoded().count(1u), 1u);
-        break;
-      case 3:
-        EXPECT_EQ(layer->InvalidationForTesting().bounds(), image_rect);
-        EXPECT_EQ(layer->update_rect(), image_rect);
-        EXPECT_EQ(generator_->frames_decoded().size(), 3u);
-        EXPECT_EQ(generator_->frames_decoded().count(2u), 1u);
-        break;
-      default:
-        // Only 3 draws should happen for 3 frames of the animate image.
-        NOTREACHED();
-    }
-
-    if (draw_count_ == 3)
-      EndTest();
-  }
-
-  void AfterTest() override {
-    EXPECT_EQ(generator_->frames_decoded().size(), 3u);
-  }
-
- private:
-  FakeContentLayerClient content_layer_client_;
-  sk_sp<FakePaintImageGenerator> generator_;
-  int draw_count_ = 0;
-};
-
-MULTI_THREAD_TEST_F(LayerTreeHostTestImageAnimation);
-
 }  // namespace
 }  // namespace cc
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 55b4dae..fa1b7be 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -1119,11 +1119,6 @@
                      "layers_updated_count", layers_updated_count);
   }
 
-  if (image_animation_controller()) {
-    image_animation_controller()->UpdateStateFromDrivers(
-        host_impl_->CurrentBeginFrameArgs().frame_time);
-  }
-
   DCHECK(!needs_update_draw_properties_)
       << "CalcDrawProperties should not set_needs_update_draw_properties()";
   return true;
@@ -1333,10 +1328,6 @@
   return host_impl_->image_decode_cache();
 }
 
-ImageAnimationController* LayerTreeImpl::image_animation_controller() const {
-  return host_impl_->image_animation_controller();
-}
-
 FrameRateCounter* LayerTreeImpl::frame_rate_counter() const {
   return host_impl_->fps_counter();
 }
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index 38bf8c2..a4a3fed 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -111,7 +111,6 @@
   LayerTreeResourceProvider* resource_provider() const;
   TileManager* tile_manager() const;
   ImageDecodeCache* image_decode_cache() const;
-  ImageAnimationController* image_animation_controller() const;
   FrameRateCounter* frame_rate_counter() const;
   MemoryHistory* memory_history() const;
   gfx::Size device_viewport_size() const;
diff --git a/cc/trees/layer_tree_settings.h b/cc/trees/layer_tree_settings.h
index 804478ac..0b68410 100644
--- a/cc/trees/layer_tree_settings.h
+++ b/cc/trees/layer_tree_settings.h
@@ -145,9 +145,6 @@
   // Whether to use out of process raster.  If true, whenever gpu raster
   // would have been used, out of process gpu raster will be used instead.
   bool enable_oop_rasterization = false;
-
-  // Whether images should be animated in the compositor.
-  bool enable_image_animations = false;
 };
 
 }  // namespace cc
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index ffbe312d..cf36e40 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -586,7 +586,7 @@
     ]
 
     outputs = [
-      "{{bundle_root_dir}}/XPCServices/{{source_file_part}}",
+      "{{bundle_contents_dir}}/XPCServices/{{source_file_part}}",
     ]
 
     public_deps = [
@@ -737,7 +737,7 @@
       ]
 
       outputs = [
-        "{{bundle_root_dir}}/Helpers/{{source_file_part}}",
+        "{{bundle_contents_dir}}/Helpers/{{source_file_part}}",
       ]
     }
   }
@@ -748,7 +748,7 @@
       "$root_out_dir/$chrome_helper_name.app",
     ]
     outputs = [
-      "{{bundle_root_dir}}/Versions/$chrome_version_full/{{source_file_part}}",
+      "{{bundle_contents_dir}}/Versions/$chrome_version_full/{{source_file_part}}",
     ]
     public_deps = [
       ":chrome_helper_app",
@@ -846,7 +846,7 @@
     ]
 
     outputs = [
-      "{{bundle_root_dir}}/Helpers/{{source_file_part}}",
+      "{{bundle_contents_dir}}/Helpers/{{source_file_part}}",
     ]
 
     public_deps = [
@@ -949,7 +949,7 @@
     bundle_data("chrome_framework_plugins") {
       sources = []
       outputs = [
-        "{{bundle_root_dir}}/Internet Plug-Ins/{{source_file_part}}",
+        "{{bundle_contents_dir}}/Internet Plug-Ins/{{source_file_part}}",
       ]
       public_deps = []
 
@@ -979,7 +979,7 @@
         "$root_out_dir/libwidevinecdm.dylib",
       ]
       outputs = [
-        "{{bundle_root_dir}}/Libraries/$widevine_cdm_path/{{source_file_part}}",
+        "{{bundle_contents_dir}}/Libraries/$widevine_cdm_path/{{source_file_part}}",
       ]
       public_deps = [
         # Need this intermediate dependency because "widevinecdm" is a
@@ -1021,7 +1021,7 @@
         "$root_out_dir/WidevineCdm/manifest.json",
       ]
       outputs = [
-        "{{bundle_root_dir}}/Libraries/WidevineCdm/{{source_file_part}}",
+        "{{bundle_contents_dir}}/Libraries/WidevineCdm/{{source_file_part}}",
       ]
       public_deps = [
         "//third_party/widevine/cdm:widevine_cdm_manifest",
@@ -1061,7 +1061,7 @@
     bundle_data("widevine_resources_plist_bundle_data") {
       sources = get_target_outputs(":widevine_resources_plist")
       outputs = [
-        "{{bundle_root_dir}}/Info.plist",
+        "{{bundle_contents_dir}}/Info.plist",
       ]
       public_deps = [
         ":widevine_resources_plist",
@@ -1084,8 +1084,9 @@
 
     create_bundle("widevine_resources_bundle") {
       output_name = "Widevine Resources.bundle"
-      bundle_root_dir = "$root_build_dir/$output_name/Contents"
-      bundle_resources_dir = bundle_root_dir + "/Resources"
+      bundle_root_dir = "$root_build_dir/$output_name"
+      bundle_contents_dir = "$bundle_root_dir/Contents"
+      bundle_resources_dir = "$bundle_contents_dir/Resources"
 
       deps = [
         ":framework_widevine_signature",
@@ -1611,7 +1612,7 @@
       ]
     } else {
       outputs = [
-        "{{bundle_root_dir}}/Default Apps/{{source_file_part}}",
+        "{{bundle_contents_dir}}/Default Apps/{{source_file_part}}",
       ]
     }
 
diff --git a/chrome/android/java/OWNERS b/chrome/android/java/OWNERS
index 3049b09d..fbfed0a 100644
--- a/chrome/android/java/OWNERS
+++ b/chrome/android/java/OWNERS
@@ -1,2 +1,2 @@
 per-file proguard.flags=agrieve@chromium.org
-per-file *.grd=twellington@chomium.org
\ No newline at end of file
+per-file *.grd*=twellington@chromium.org
diff --git a/chrome/android/java/res/layout/content_suggestions_card_modern.xml b/chrome/android/java/res/layout/content_suggestions_card_modern.xml
index 4fdc09c..d7d8fe20 100644
--- a/chrome/android/java/res/layout/content_suggestions_card_modern.xml
+++ b/chrome/android/java/res/layout/content_suggestions_card_modern.xml
@@ -21,8 +21,8 @@
 
         <org.chromium.chrome.browser.widget.TintedImageView
             android:id="@+id/article_thumbnail"
-            android:layout_width="@dimen/snippets_thumbnail_size_large"
-            android:layout_height="@dimen/snippets_thumbnail_size_large"
+            android:layout_width="@dimen/snippets_thumbnail_size_modern"
+            android:layout_height="@dimen/snippets_thumbnail_size_modern"
             android:layout_alignParentTop="true"
             android:layout_alignParentStart="true"
             android:scaleType="centerCrop"
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml
index 5e7be4d..4917a24d 100644
--- a/chrome/android/java/res/values-v17/styles.xml
+++ b/chrome/android/java/res/values-v17/styles.xml
@@ -737,14 +737,14 @@
         <item name="android:background">@drawable/content_card_modern_background</item>
         <item name="android:foreground">@drawable/button_borderless_compat</item>
     </style>
-    <style name="SuggestionCardTitleModern" parent="BlackTitle1">
+    <style name="SuggestionCardTitleModern" parent="BlackBodyDefault">
         <item name="android:ellipsize">end</item>
-        <item name="leading">22dp</item>
+        <item name="leading">16sp</item>
     </style>
     <style name="SuggestionCardBodyModern" parent="BlackBody">
         <item name="android:layout_marginTop">8dp</item>
         <item name="android:ellipsize">end</item>
-        <item name="leading">20dp</item>
+        <item name="leading">16sp</item>
     </style>
     <style name="SuggestionCardActionModern" parent="ButtonCompatBorderless">
         <item name="android:textAppearance">@style/BlueButtonText2</item>
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index fb4bae9..bd625a1d 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -266,7 +266,6 @@
     <dimen name="bottom_toolbar_top_margin">2dp</dimen>
     <dimen name="bottom_toolbar_url_bar_top_margin">3.5dp</dimen>
     <dimen name="bottom_toolbar_background_focused_left_margin">6dp</dimen>
-    <dimen name="bottom_location_bar_vertical_margin">9dp</dimen>
     <dimen name="bottom_location_bar_content_lateral_inset">8dp</dimen>
     <dimen name="bottom_location_bar_content_vertical_inset">4dp</dimen>
 
@@ -339,6 +338,7 @@
     <dimen name="ntp_progress_indicator_diameter">56dp</dimen>
     <dimen name="snippets_thumbnail_size">72dp</dimen>
     <dimen name="snippets_thumbnail_size_large">124dp</dimen>
+    <dimen name="snippets_thumbnail_size_modern">114dp</dimen>
     <dimen name="snippets_thumbnail_margin">16dp</dimen>
     <!-- The default padding for the peeking card and the amount the card peeks by in the current
          peeking card animation (the calculations assume this is the same). -->
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index e943b979..b6a7b27 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -1011,10 +1011,6 @@
             if (isActivityDestroyed()) return;
             ForcedSigninProcessor.checkCanSignIn(ChromeActivity.this);
         });
-        DeferredStartupHandler.getInstance().addDeferredTask(() -> {
-            if (isActivityDestroyed() || mBottomSheetContentController == null) return;
-            mBottomSheetContentController.initializeDefaultContent();
-        });
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index de66114..9358d56 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -157,6 +157,7 @@
     public static final String CCT_POST_MESSAGE_API = "CCTPostMessageAPI";
     public static final String CCT_REDIRECT_PRECONNECT = "CCTRedirectPreconnect";
     public static final String CHROME_HOME = "ChromeHome";
+    public static final String CHROME_HOME_DESTROY_SUGGESTIONS = "ChromeHomeDestroySuggestions";
     public static final String CHROME_HOME_DOODLE = "ChromeHomeDoodle";
     public static final String CHROME_HOME_EXPAND_BUTTON = "ChromeHomeExpandButton";
     public static final String CONTENT_SUGGESTIONS_FAVICONS_FROM_NEW_SERVER =
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 6b9c0a1..a2a1c096 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
@@ -427,7 +427,12 @@
 
     private Tab getCurrentTab() {
         if (mLayoutManager == null || mTabModelSelector == null) return null;
-        return mTabModelSelector.getCurrentTab();
+        Tab currentTab = mTabModelSelector.getCurrentTab();
+
+        // If the tab model selector doesn't know of a current tab, use the last visible one.
+        if (currentTab == null) currentTab = mTabVisible;
+
+        return currentTab;
     }
 
     private View getActiveView() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/BackendProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/BackendProvider.java
index d9bbc8a..90b49d95 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/BackendProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/BackendProvider.java
@@ -6,6 +6,7 @@
 
 import org.chromium.chrome.browser.download.DownloadItem;
 import org.chromium.chrome.browser.download.DownloadManagerService;
+import org.chromium.chrome.browser.widget.ThumbnailProvider;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
 import org.chromium.components.offline_items_collection.OfflineContentProvider;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java
index 177c6a0..d15eab9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java
@@ -21,6 +21,7 @@
 import org.chromium.chrome.browser.download.DownloadUtils;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.widget.MaterialProgressBar;
+import org.chromium.chrome.browser.widget.ThumbnailProvider;
 import org.chromium.chrome.browser.widget.TintedImageButton;
 import org.chromium.chrome.browser.widget.selection.SelectableItemView;
 import org.chromium.components.offline_items_collection.OfflineItem.Progress;
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 eac07fa..f18cb176 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
@@ -34,6 +34,8 @@
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController;
 import org.chromium.chrome.browser.util.FeatureUtilities;
+import org.chromium.chrome.browser.widget.ThumbnailProvider;
+import org.chromium.chrome.browser.widget.ThumbnailProviderImpl;
 import org.chromium.chrome.browser.widget.selection.SelectableListLayout;
 import org.chromium.chrome.browser.widget.selection.SelectableListToolbar;
 import org.chromium.chrome.browser.widget.selection.SelectableListToolbar.SearchDelegate;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
index 698c723..5c0a6f8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
@@ -384,6 +384,8 @@
 
             @Override
             public void onSheetContentChanged(BottomSheetContent newContent) {
+                if (newContent == null) return;
+
                 @ContentType
                 int type = newContent.getType();
                 if (type != BottomSheetContentController.TYPE_AUXILIARY_CONTENT) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/ImageFetcher.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/ImageFetcher.java
index f295bb0..316c229 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/ImageFetcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/ImageFetcher.java
@@ -13,7 +13,6 @@
 import org.chromium.base.Promise;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.NativePageHost;
-import org.chromium.chrome.browser.download.ui.ThumbnailProvider;
 import org.chromium.chrome.browser.favicon.FaviconHelper;
 import org.chromium.chrome.browser.favicon.LargeIconBridge;
 import org.chromium.chrome.browser.ntp.cards.CardsVariationParameters;
@@ -22,6 +21,7 @@
 import org.chromium.chrome.browser.ntp.snippets.SnippetsConfig;
 import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.widget.ThumbnailProvider;
 
 import java.net.URI;
 import java.net.URISyntaxException;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java
index 6ade6e4f..e215c826 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.suggestions;
 
 import android.content.res.ColorStateList;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
@@ -12,6 +13,7 @@
 import android.media.ThumbnailUtils;
 import android.os.StrictMode;
 import android.os.SystemClock;
+import android.support.annotation.DimenRes;
 import android.support.annotation.Nullable;
 import android.support.v4.text.BidiFormatter;
 import android.text.TextUtils;
@@ -33,6 +35,7 @@
 import org.chromium.chrome.browser.download.ui.DownloadFilter;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder;
 import org.chromium.chrome.browser.ntp.snippets.SnippetArticle;
+import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.widget.TintedImageView;
 
 /**
@@ -84,11 +87,7 @@
         mPublisherBar = mCardContainerView.findViewById(R.id.publisher_bar);
         mOfflineBadge = mCardContainerView.findViewById(R.id.offline_icon);
 
-        boolean useLargeThumbnailLayout =
-                ChromeFeatureList.isEnabled(ChromeFeatureList.CONTENT_SUGGESTIONS_LARGE_THUMBNAIL);
-        mThumbnailSize = mCardContainerView.getResources().getDimensionPixelSize(
-                useLargeThumbnailLayout ? R.dimen.snippets_thumbnail_size_large
-                                        : R.dimen.snippets_thumbnail_size);
+        mThumbnailSize = getThumbnailSize(mCardContainerView.getResources());
         mThumbnailFootprintPx = mThumbnailSize
                 + mCardContainerView.getResources().getDimensionPixelSize(
                           R.dimen.snippets_thumbnail_margin);
@@ -398,4 +397,18 @@
         assert !bitmap.isRecycled();
         assert bitmap.getWidth() <= mThumbnailSize || bitmap.getHeight() <= mThumbnailSize;
     }
+
+    private static int getThumbnailSize(Resources resources) {
+        @DimenRes
+        final int dimension;
+        if (FeatureUtilities.isChromeHomeEnabled()) {
+            dimension = R.dimen.snippets_thumbnail_size_modern;
+        } else if (ChromeFeatureList.isEnabled(
+                           ChromeFeatureList.CONTENT_SUGGESTIONS_LARGE_THUMBNAIL)) {
+            dimension = R.dimen.snippets_thumbnail_size_large;
+        } else {
+            dimension = R.dimen.snippets_thumbnail_size;
+        }
+        return resources.getDimensionPixelSize(dimension);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetContent.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetContent.java
index 0546fe5..7588253b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetContent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetContent.java
@@ -261,6 +261,7 @@
         mSuggestionsUiDelegate.onDestroy();
         mTileGroupDelegate.destroy();
         TemplateUrlService.getInstance().removeObserver(this);
+        mSheet.getNewTabController().removeObserver(this);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsDependencyFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsDependencyFactory.java
index 1ff6759..d3b26e4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsDependencyFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsDependencyFactory.java
@@ -7,13 +7,13 @@
 import org.chromium.base.DiscardableReferencePool;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.browser.download.ui.ThumbnailProvider;
-import org.chromium.chrome.browser.download.ui.ThumbnailProviderImpl;
 import org.chromium.chrome.browser.favicon.FaviconHelper;
 import org.chromium.chrome.browser.favicon.LargeIconBridge;
 import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge;
 import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.widget.ThumbnailProvider;
+import org.chromium.chrome.browser.widget.ThumbnailProviderImpl;
 
 /**
  * Provides an injection mechanisms for dependencies of the suggestions package.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java
index 683b60b4f..41724e1a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java
@@ -13,14 +13,15 @@
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.LayerDrawable;
 import android.os.SystemClock;
 import android.support.v4.graphics.drawable.DrawableCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v7.widget.Toolbar;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
@@ -36,6 +37,7 @@
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabObserver;
+import org.chromium.chrome.browser.util.AccessibilityUtil;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.MathUtils;
@@ -65,16 +67,13 @@
                 // content description. If the content description is changed while the view is
                 // focused, the new content description is read immediately.
                 if (hasFocus() && !urlHasFocus()) mBottomSheet.requestFocus();
-                updateContentDescription();
             }
         }
 
         @Override
         public void onSheetClosed(@StateChangeReason int reason) {
             onPrimaryColorChanged(true);
-
             updateMenuButtonClickableState();
-            updateContentDescription();
         }
 
         @Override
@@ -95,7 +94,7 @@
             boolean buttonsClickable = heightFraction == 0.f;
             mToggleTabStackButton.setClickable(buttonsClickable);
             updateMenuButtonClickableState();
-            if (!mUseToolbarHandle) mExpandButton.setClickable(buttonsClickable);
+            mExpandButton.setClickable(buttonsClickable);
         }
 
         @Override
@@ -175,6 +174,9 @@
     /** Whether the menu button should be shown while the sheet is open. */
     private boolean mShowMenuButtonWhenSheetOpen;
 
+    /** The height of the location bar background. */
+    private float mLocationBarBackgroundHeight;
+
     /**
      * The float used to inset the rect returned by {@link #getLocationBarContentRect(Rect)}.
      * This extra vertical inset is needed to ensure the anonymize layer doesn't draw outside of the
@@ -227,7 +229,6 @@
         mLocationBarExtraFocusedLeftMargin =
                 res.getDimensionPixelSize(R.dimen.bottom_toolbar_background_focused_left_margin);
 
-        mUseToolbarHandle = true;
         mToolbarShadowPermanentlyHidden = true;
         mToolbarButtonVisibilityPercent = 1.f;
         mToolbarButtonAnimationIterpolator = BakedBezierInterpolator.FADE_OUT_CURVE;
@@ -294,6 +295,9 @@
         mBottomToolbarTopShadow.setImageDrawable(mBottomToolbarTopShadowDrawable);
     }
 
+    /**
+     * @param activity The {@link ChromeActivity} displaying this toolbar.
+     */
     public void setActivity(ChromeActivity activity) {
         mActivity = activity;
     }
@@ -306,6 +310,13 @@
     }
 
     /**
+     * @return Whether the expand button is currently being used.
+     */
+    public boolean isUsingExpandButton() {
+        return !mUseToolbarHandle;
+    }
+
+    /**
      * Set the color of the pull handle used by the toolbar.
      * @param useLightDrawable If the handle color should be light.
      */
@@ -357,8 +368,7 @@
     protected void setTabSwitcherMode(boolean inTabSwitcherMode, boolean showToolbar,
             boolean delayAnimation, boolean animate) {
         super.setTabSwitcherMode(inTabSwitcherMode, showToolbar, delayAnimation, animate);
-        if (!mUseToolbarHandle) mExpandButton.setClickable(!inTabSwitcherMode);
-        updateContentDescription();
+        mExpandButton.setClickable(!inTabSwitcherMode);
 
         // Reset top shadow drawable state.
         if (inTabSwitcherMode) {
@@ -368,12 +378,6 @@
     }
 
     @Override
-    protected void onTabSwitcherTransitionFinished() {
-        super.onTabSwitcherTransitionFinished();
-        updateContentDescription();
-    }
-
-    @Override
     protected void onTabOrModelChanged() {
         super.onTabOrModelChanged();
         attachShadowTabObserverToCurrentTab();
@@ -541,7 +545,7 @@
 
     @Override
     protected int getLocationBarBackgroundVerticalMargin(float expansion) {
-        return mLocationBarVerticalMargin;
+        return (int) ((mLocationBar.getHeight() - mLocationBarBackgroundHeight) / 2);
     }
 
     @Override
@@ -614,6 +618,27 @@
         super.onFinishInflate();
 
         mExpandButton = (TintedImageButton) findViewById(R.id.expand_sheet_button);
+        mExpandButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mBottomSheet != null && mTabSwitcherState == STATIC_TAB) {
+                    mBottomSheet.onExpandButtonPressed();
+                }
+            }
+        });
+        mExpandButton.setAccessibilityDelegate(new AccessibilityDelegate() {
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+
+                AccessibilityNodeInfoCompat infoCompat = new AccessibilityNodeInfoCompat(info);
+                infoCompat.setClickable(true);
+                infoCompat.addAction(new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+                        AccessibilityNodeInfoCompat.ACTION_CLICK,
+                        getResources().getString(
+                                R.string.bottom_sheet_expand_button_accessibility)));
+            }
+        });
 
         // Add extra top margin to the URL bar to compensate for the change to location bar's
         // vertical margin in the constructor.
@@ -625,27 +650,13 @@
         mBrowsingModeViews.remove(mLocationBar);
 
         updateToolbarTopMargin();
-
-        mLocationBar.addOnLayoutChangeListener(new OnLayoutChangeListener() {
-            @Override
-            public void onLayoutChange(View v, int left, int top, int right, int bottom,
-                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
-                // TODO(twellington): remove this after we have decided whether to use the expand
-                // button or the pull handle and the location bar background is a predictable
-                // height.
-                setLocationBarBackgroundCornerRadius();
-
-                mLocationBar.removeOnLayoutChangeListener(this);
-            }
-        });
     }
 
     @Override
     protected void initLocationBarBackground() {
         Resources res = getResources();
-        mLocationBarVerticalMargin =
-                res.getDimensionPixelOffset(R.dimen.bottom_location_bar_vertical_margin);
-
+        mLocationBarBackgroundHeight =
+                res.getDimensionPixelSize(R.dimen.modern_toolbar_background_size);
         mLocationBarBackground =
                 ApiCompatibilityUtils.getDrawable(res, R.drawable.modern_toolbar_background);
         mLocationBarBackground.getPadding(mLocationBarBackgroundPadding);
@@ -698,6 +709,8 @@
         mToolbarHandleView = (ImageView) getRootView().findViewById(R.id.toolbar_handle);
         mToolbarHandleView.setImageDrawable(mHandleDark);
 
+        setUseToolbarHandle();
+
         initBottomToolbarTopShadow();
 
         if (mToolbarShadowPermanentlyHidden) mToolbarShadow.setVisibility(View.GONE);
@@ -707,54 +720,10 @@
     public void onNativeLibraryReady() {
         super.onNativeLibraryReady();
 
-        mUseToolbarHandle = !FeatureUtilities.isChromeHomeExpandButtonEnabled();
-
-        if (!mUseToolbarHandle) {
-            initExpandButton();
-        } else {
-            updateContentDescription();
-        }
-
+        setUseToolbarHandle();
         mNewTabButton.setIsModern();
     }
 
-    /**
-     * Initialize the "expand" button if it is being used.
-     */
-    private void initExpandButton() {
-        mLocationBarVerticalMargin =
-                getResources().getDimensionPixelOffset(R.dimen.location_bar_vertical_margin);
-
-        mToolbarHandleView.setVisibility(View.GONE);
-
-        mExpandButton.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if (mBottomSheet != null && mTabSwitcherState == STATIC_TAB) {
-                    mBottomSheet.onExpandButtonPressed();
-                }
-            }
-        });
-
-        mExpandButton.setVisibility(View.VISIBLE);
-
-        updateToolbarTopMargin();
-
-        // Recalculate the corner radius since the location bar vertical margin has changed.
-        setLocationBarBackgroundCornerRadius();
-    }
-
-    private void setLocationBarBackgroundCornerRadius() {
-        // Programatically set the corner radius based on the actual location bar height so
-        // that its edges are perfectly round.
-        float locationBarBackgroundHeight = mLocationBar.getBottom()
-                - (mLocationBarVerticalMargin * 2) - mLocationBar.getTop();
-        mLocationBarBackgroundCornerRadius = (int) (locationBarBackgroundHeight / 2);
-        mLocationBarBackground.mutate();
-        ((GradientDrawable) mLocationBarBackground).setCornerRadius(
-                locationBarBackgroundHeight / 2);
-    }
-
     @Override
     protected void updateVisualsForToolbarState() {
         super.updateVisualsForToolbarState();
@@ -928,6 +897,30 @@
                 ApiCompatibilityUtils.getPaddingEnd(otherToolbar), otherToolbar.getPaddingBottom());
     }
 
+    @Override
+    protected void onAccessibilityStatusChanged(boolean enabled) {
+        setUseToolbarHandle();
+    }
+
+    /**
+     * Sets whether or not the toolbar handle is used and updates the handle view and expand button
+     * accordingly.
+     */
+    private void setUseToolbarHandle() {
+        mUseToolbarHandle = !AccessibilityUtil.isAccessibilityEnabled()
+                && !FeatureUtilities.isChromeHomeExpandButtonEnabled();
+
+        // This method may be called due to an accessibility state change. Return early if the
+        // needed views are null.
+        if (mToolbarHandleView == null || mExpandButton == null) return;
+
+        mToolbarHandleView.setVisibility(mUseToolbarHandle ? View.VISIBLE : View.GONE);
+        mExpandButton.setVisibility(mUseToolbarHandle ? View.GONE : View.VISIBLE);
+
+        updateToolbarTopMargin();
+        updateVisualsForToolbarState();
+    }
+
     /**
      * Called when the sheet is transitioning from peek <-> half to update the toolbar button
      * animation.
@@ -1149,18 +1142,4 @@
         mMenuButton.setClickable(
                 !urlHasFocus() && (!mBottomSheet.isSheetOpen() || mBottomSheet.isShowingNewTab()));
     }
-
-    private void updateContentDescription() {
-        if (!mUseToolbarHandle) return;
-
-        if (isInTabSwitcherMode()) {
-            setContentDescription(null);
-        } else if (mBottomSheet.isSheetOpen()) {
-            setContentDescription(
-                    getResources().getString(R.string.bottom_sheet_open_accessibility_toolbar));
-        } else {
-            setContentDescription(
-                    getResources().getString(R.string.bottom_sheet_accessibility_toolbar));
-        }
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/widget/OWNERS
index 8042524..456674a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/OWNERS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/OWNERS
@@ -1,3 +1,4 @@
+bauerb@chromium.org
 twellington@chromium.org
 
 per-file ToolbarProgressBar*=mdjones@chromium.org
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/ThumbnailProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/ThumbnailProvider.java
similarity index 96%
rename from chrome/android/java/src/org/chromium/chrome/browser/download/ui/ThumbnailProvider.java
rename to chrome/android/java/src/org/chromium/chrome/browser/widget/ThumbnailProvider.java
index d3b8df71..a39a83a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/ThumbnailProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/ThumbnailProvider.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.download.ui;
+package org.chromium.chrome.browser.widget;
 
 import android.graphics.Bitmap;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/ThumbnailProviderImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/ThumbnailProviderImpl.java
similarity index 99%
rename from chrome/android/java/src/org/chromium/chrome/browser/download/ui/ThumbnailProviderImpl.java
rename to chrome/android/java/src/org/chromium/chrome/browser/widget/ThumbnailProviderImpl.java
index 07fd2d9..8456c9c9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/ThumbnailProviderImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/ThumbnailProviderImpl.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.download.ui;
+package org.chromium.chrome.browser.widget;
 
 import android.graphics.Bitmap;
 import android.os.Looper;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
index d8a43c5..0d1fd236 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
@@ -34,7 +34,6 @@
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.NativePageHost;
 import org.chromium.chrome.browser.TabLoadStatus;
@@ -221,6 +220,7 @@
     private ChromeFullscreenManager mFullscreenManager;
 
     /** A handle to the content being shown by the sheet. */
+    @Nullable
     private BottomSheetContent mSheetContent;
 
     /** A handle to the toolbar control container. */
@@ -962,21 +962,27 @@
 
     /**
      * Show content in the bottom sheet's content area.
-     * @param content The {@link BottomSheetContent} to show.
+     * @param content The {@link BottomSheetContent} to show, or null if no content should be shown.
      */
-    public void showContent(final BottomSheetContent content) {
+    public void showContent(@Nullable final BottomSheetContent content) {
         // If an animation is already running, end it.
         if (mContentSwapAnimatorSet != null) mContentSwapAnimatorSet.end();
 
         // If the desired content is already showing, do nothing.
         if (mSheetContent == content) return;
 
+        View oldContent = mSheetContent != null ? mSheetContent.getContentView() : null;
+        if (content == null) {
+            onSheetContentChanged(null);
+            ((ViewGroup) oldContent.getParent()).removeView(oldContent);
+            return;
+        }
+
         View newToolbar =
                 content.getToolbarView() != null ? content.getToolbarView() : mDefaultToolbarView;
         View oldToolbar = mSheetContent != null && mSheetContent.getToolbarView() != null
                 ? mSheetContent.getToolbarView()
                 : mDefaultToolbarView;
-        View oldContent = mSheetContent != null ? mSheetContent.getContentView() : null;
 
         List<Animator> animators = new ArrayList<>();
         mContentSwapAnimatorSet = new AnimatorSet();
@@ -985,12 +991,7 @@
             public void onAnimationEnd(Animator animation) {
                 if (mIsDestroyed) return;
 
-                mSheetContent = content;
-                for (BottomSheetObserver o : mObservers) {
-                    o.onSheetContentChanged(content);
-                }
-                updateHandleTint();
-                mToolbarHolder.setBackgroundColor(Color.TRANSPARENT);
+                onSheetContentChanged(content);
                 mContentSwapAnimatorSet = null;
             }
         });
@@ -1123,12 +1124,6 @@
         for (BottomSheetObserver o : mObservers) o.onSheetOpened(reason);
         mActivity.addViewObscuringAllTabs(this);
 
-        setFocusable(true);
-        setFocusableInTouchMode(true);
-        setContentDescription(
-                getResources().getString(R.string.bottom_sheet_accessibility_description));
-        if (getFocusedChild() == null) requestFocus();
-
         Tracker tracker = TrackerFactory.getTrackerForProfile(Profile.getLastUsedProfile());
         tracker.notifyEvent(EventConstants.BOTTOM_SHEET_EXPANDED);
     }
@@ -1431,10 +1426,18 @@
 
         mCurrentState = state;
 
-        if (mCurrentState == SHEET_STATE_HALF) {
-            announceForAccessibility(getResources().getString(R.string.bottom_sheet_opened_half));
-        } else if (mCurrentState == SHEET_STATE_FULL) {
-            announceForAccessibility(getResources().getString(R.string.bottom_sheet_opened_full));
+        if (mCurrentState == SHEET_STATE_HALF || mCurrentState == SHEET_STATE_FULL) {
+            announceForAccessibility(mCurrentState == SHEET_STATE_FULL
+                            ? getResources().getString(R.string.bottom_sheet_opened_full)
+                            : getResources().getString(R.string.bottom_sheet_opened_half));
+
+            // TalkBack will announce the content description if it has changed, so wait to set the
+            // content description until after announcing full/half height.
+            setFocusable(true);
+            setFocusableInTouchMode(true);
+            setContentDescription(
+                    getResources().getString(R.string.bottom_sheet_accessibility_description));
+            if (getFocusedChild() == null) requestFocus();
         }
 
         for (BottomSheetObserver o : mObservers) {
@@ -1456,8 +1459,11 @@
         return mSettleAnimator != null;
     }
 
+    /**
+     * @return The current sheet content, or null if there is no content.
+     */
     @VisibleForTesting
-    public BottomSheetContent getCurrentSheetContent() {
+    public @Nullable BottomSheetContent getCurrentSheetContent() {
         return mSheetContent;
     }
 
@@ -1648,8 +1654,7 @@
                 tracker.shouldTriggerHelpUI(FeatureConstants.CHROME_HOME_EXPAND_FEATURE);
         if (!fromMenu && !notifyDismissed) return;
 
-        boolean showExpandButtonHelpBubble =
-                ChromeFeatureList.isEnabled(ChromeFeatureList.CHROME_HOME_EXPAND_BUTTON);
+        boolean showExpandButtonHelpBubble = mDefaultToolbarView.isUsingExpandButton();
 
         View anchorView = showExpandButtonHelpBubble
                 ? mControlContainer.findViewById(R.id.expand_sheet_button)
@@ -1659,7 +1664,7 @@
                 : R.string.bottom_sheet_help_bubble_message;
         int accessibilityStringId = showExpandButtonHelpBubble
                 ? R.string.bottom_sheet_accessibility_expand_button_help_bubble_message
-                : R.string.bottom_sheet_accessibility_help_bubble_message;
+                : stringId;
 
         ViewAnchoredTextBubble helpBubble = new ViewAnchoredTextBubble(
                 getContext(), anchorView, stringId, accessibilityStringId);
@@ -1682,4 +1687,17 @@
         helpBubble.setInsetPx(0, inset, 0, inset);
         helpBubble.show();
     }
+
+    /**
+     * Called when the sheet content has changed, to update dependent state and notify observers.
+     * @param content The new sheet content, or null if the sheet has no content.
+     */
+    private void onSheetContentChanged(@Nullable final BottomSheetContent content) {
+        mSheetContent = content;
+        for (BottomSheetObserver o : mObservers) {
+            o.onSheetContentChanged(content);
+        }
+        updateHandleTint();
+        mToolbarHolder.setBackgroundColor(Color.TRANSPARENT);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetContentController.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetContentController.java
index 1477a33..723d8c2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetContentController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetContentController.java
@@ -23,6 +23,7 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.bookmarks.BookmarkSheetContent;
 import org.chromium.chrome.browser.download.DownloadSheetContent;
 import org.chromium.chrome.browser.history.HistorySheetContent;
@@ -70,6 +71,9 @@
     // content with this type, the back button will close the sheet.
     public static final int TYPE_AUXILIARY_CONTENT = 5;
 
+    // Used if there is no bottom sheet content.
+    private static final int NO_CONTENT_ID = 0;
+
     // R.id.action_home is overloaded, so an invalid ID is used to reference the incognito version
     // of the home content.
     private static final int INCOGNITO_HOME_ID = -1;
@@ -102,8 +106,6 @@
 
         @Override
         public void onSheetOpened(@StateChangeReason int reason) {
-            initializeDefaultContent();
-
             if (reason == StateChangeReason.OMNIBOX_FOCUS) {
                 // If the omnibox is being focused, show the placeholder.
                 mBottomSheet.showContent(mPlaceholderContent);
@@ -113,6 +115,8 @@
                 return;
             }
 
+            if (mSelectedItemId == NO_CONTENT_ID) showBottomSheetContent(R.id.action_home);
+
             if (mHighlightItemId != null) {
                 mHighlightedView = mActivity.findViewById(mHighlightItemId);
                 ViewHighlighter.turnOnHighlight(mHighlightedView, false);
@@ -121,16 +125,23 @@
 
         @Override
         public void onSheetClosed(@StateChangeReason int reason) {
-            if (mSelectedItemId != 0 && mSelectedItemId != R.id.action_home) {
-                showBottomSheetContent(R.id.action_home);
+            if (ChromeFeatureList.isEnabled(ChromeFeatureList.CHROME_HOME_DESTROY_SUGGESTIONS)) {
+                // TODO(bauerb): Implement support for destroying the home sheet after a delay.
+                mSelectedItemId = NO_CONTENT_ID;
+                mBottomSheet.showContent(null);
+                clearBottomSheetContents(true);
             } else {
-                clearBottomSheetContents(false);
+                if (mSelectedItemId != NO_CONTENT_ID && mSelectedItemId != R.id.action_home) {
+                    showBottomSheetContent(R.id.action_home);
+                } else {
+                    clearBottomSheetContents(false);
+                }
             }
+
             // The keyboard should be hidden when the sheet is closed in case it was made visible by
             // sheet content.
-            UiUtils.hideKeyboard((View) BottomSheetContentController.this);
-            // TODO(twellington): determine a policy for destroying the
-            //                    SuggestionsBottomSheetContent.
+            UiUtils.hideKeyboard(BottomSheetContentController.this);
+
             ViewHighlighter.turnOffHighlight(mHighlightedView);
             mHighlightedView = null;
             mHighlightItemId = null;
@@ -164,7 +175,6 @@
     private SnackbarManager mSnackbarManager;
     private float mDistanceBelowToolbarPx;
     private int mSelectedItemId;
-    private boolean mDefaultContentInitialized;
     private ChromeActivity mActivity;
     private boolean mShouldOpenSheetOnNextContentChange;
     private PlaceholderSheetContent mPlaceholderContent;
@@ -266,16 +276,6 @@
     }
 
     /**
-     * Initialize the default {@link BottomSheetContent}.
-     */
-    public void initializeDefaultContent() {
-        if (mDefaultContentInitialized) return;
-        createAndCacheContentForId(R.id.action_home);
-        if (!mOmniboxHasFocus) showBottomSheetContent(R.id.action_home);
-        mDefaultContentInitialized = true;
-    }
-
-    /**
      * @param itemId The id of the MenuItem to select.
      */
     public void selectItem(int itemId) {
@@ -389,7 +389,7 @@
     }
 
     private void showBottomSheetContent(int navItemId) {
-        // There are some bugs related to programatically selecting menu items that are fixed in
+        // There are some bugs related to programmatically selecting menu items that are fixed in
         // newer support library versions.
         // TODO(twellington): remove this after the support library is rolled.
         if (mSelectedItemId > 0) getMenu().findItem(mSelectedItemId).setChecked(false);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetObserver.java
index 2f55205..8607884 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetObserver.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.widget.bottomsheet;
 
+import android.support.annotation.Nullable;
+
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet.BottomSheetContent;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet.SheetState;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet.StateChangeReason;
@@ -76,7 +78,7 @@
 
     /**
      * An event for when the sheet content changes.
-     * @param newContent The new {@link BottomSheetContent}.
+     * @param newContent The new {@link BottomSheetContent}, or null if the sheet has no content.
      */
-    void onSheetContentChanged(BottomSheetContent newContent);
+    void onSheetContentChanged(@Nullable BottomSheetContent newContent);
 }
\ No newline at end of file
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index bf1ab1c..8651a2f3 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -3160,23 +3160,17 @@
       <message name="IDS_BOTTOM_SHEET_HELP_BUBBLE_MESSAGE" desc="Text displayed in a help bubble above the bottom navigation sheet prompting users to pull the sheet up to see their bookmarks and other content.">
         Pull up to see bookmarks and more
       </message>
-      <message name="IDS_BOTTOM_SHEET_ACCESSIBILITY_HELP_BUBBLE_MESSAGE" desc="Text displayed in a help bubble above the bottom navigation sheet prompting users to pull the sheet up to see their bookmarks and other content when accessibility is enabled.">
-        Pull up on the address bar with two fingers to see bookmarks, downloads, and history
-      </message>
-      <message name="IDS_BOTTOM_SHEET_ACCESSIBILITY_TOOLBAR" desc="Accessibilty string read when the bottom toolbar, containing the adddress bar and some buttons, is focused. Informs users that they can pull up on the address bar to see their bookmarks and other content.">
-        Address bar. Pull up with two fingers to see bookmarks, downloads, and history.
-      </message>
-      <message name="IDS_BOTTOM_SHEET_OPEN_ACCESSIBILITY_TOOLBAR" desc="Accessibilty string read when the bottom toolbar, containing the adddress bar and some buttons, is focused and the Chrome Home navigation panel is open. Informs users that they can pull down on the address bar to close the navigation panel.">
-        Address bar. Pull down with two fingers to close navigation panel.
-      </message>
       <message name="IDS_BOTTOM_SHEET_EXPAND_BUTTON_HELP_BUBBLE_MESSAGE" desc="Text displayed in a help bubble above the toolbar expand button prompting users to tap the button to see their bookmarks and other content.">
         Tap to see bookmarks and more
       </message>
       <message name="IDS_BOTTOM_SHEET_ACCESSIBILITY_EXPAND_BUTTON_HELP_BUBBLE_MESSAGE" desc="Text displayed in a help bubble above the toolbar expand button prompting users to tap the button to see their bookmarks and other content when accessibility is enabled.">
         Tap to see bookmarks, downloads, and history
       </message>
-     <message name="IDS_BOTTOM_SHEET_ACCESSIBILITY_DESCRIPTION" desc="Accessibility string read when the navigation panel is focused.">
-        Navigation panel
+      <message name="IDS_BOTTOM_SHEET_EXPAND_BUTTON_ACCESSIBILITY" desc="Part of the accessibility string read when the expand button is tapped informing users that tapping button will show bookmarks, downloads, and history. TalkBack will read 'Expand button. Double tap to see bookmarks, downloads, and history'.">
+        see bookmarks, downloads, and history.
+      </message>
+     <message name="IDS_BOTTOM_SHEET_ACCESSIBILITY_DESCRIPTION" desc="Accessibility string read when the navigation panel is focused informing the user that the panel can be closed by swiping down.">
+        Navigation panel. Swipe down to close.
       </message>
       <message name="IDS_BOTTOM_SHEET_OPENED_HALF" desc="Accessibility string read when the bottom navigation panel is opened at half height. The navigation panel will occupy the bottom half the screen.">
         Navigation panel opened at half height
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 3b103e0..e43bd3e66 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -375,8 +375,6 @@
   "java/src/org/chromium/chrome/browser/download/ui/LoadingStateDelegate.java",
   "java/src/org/chromium/chrome/browser/download/ui/OfflineGroupHeaderView.java",
   "java/src/org/chromium/chrome/browser/download/ui/SpaceDisplay.java",
-  "java/src/org/chromium/chrome/browser/download/ui/ThumbnailProvider.java",
-  "java/src/org/chromium/chrome/browser/download/ui/ThumbnailProviderImpl.java",
   "java/src/org/chromium/chrome/browser/engagement/SiteEngagementService.java",
   "java/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtils.java",
   "java/src/org/chromium/chrome/browser/externalauth/UserRecoverableErrorHandler.java",
@@ -1286,6 +1284,8 @@
   "java/src/org/chromium/chrome/browser/widget/RadioButtonLayout.java",
   "java/src/org/chromium/chrome/browser/widget/RadioButtonWithDescription.java",
   "java/src/org/chromium/chrome/browser/widget/RoundedIconGenerator.java",
+  "java/src/org/chromium/chrome/browser/widget/ThumbnailProvider.java",
+  "java/src/org/chromium/chrome/browser/widget/ThumbnailProviderImpl.java",
   "java/src/org/chromium/chrome/browser/widget/TintedDrawable.java",
   "java/src/org/chromium/chrome/browser/widget/TintedImageButton.java",
   "java/src/org/chromium/chrome/browser/widget/TintedImageView.java",
@@ -1457,7 +1457,6 @@
   "javatests/src/org/chromium/chrome/browser/download/OMADownloadHandlerTest.java",
   "javatests/src/org/chromium/chrome/browser/download/ui/DownloadHistoryAdapterTest.java",
   "javatests/src/org/chromium/chrome/browser/download/ui/StubbedProvider.java",
-  "javatests/src/org/chromium/chrome/browser/download/ui/ThumbnailProviderImplTest.java",
   "javatests/src/org/chromium/chrome/browser/engagement/SiteEngagementServiceTest.java",
   "javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImplTest.java",
   "javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java",
@@ -1753,6 +1752,7 @@
   "javatests/src/org/chromium/chrome/browser/widget/PromoDialogTest.java",
   "javatests/src/org/chromium/chrome/browser/widget/RadioButtonLayoutTest.java",
   "javatests/src/org/chromium/chrome/browser/widget/RoundedIconGeneratorTest.java",
+  "javatests/src/org/chromium/chrome/browser/widget/ThumbnailProviderImplTest.java",
   "javatests/src/org/chromium/chrome/browser/widget/ToolbarProgressBarTest.java",
   "javatests/src/org/chromium/chrome/browser/widget/ViewHighlighterTest.java",
   "javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetBackBehaviorTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/StubbedProvider.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/StubbedProvider.java
index 09db9dc..4283c73 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/StubbedProvider.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/StubbedProvider.java
@@ -14,6 +14,7 @@
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.chrome.browser.download.DownloadInfo;
 import org.chromium.chrome.browser.download.DownloadItem;
+import org.chromium.chrome.browser.widget.ThumbnailProvider;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
 import org.chromium.components.offline_items_collection.ContentId;
 import org.chromium.components.offline_items_collection.LegacyHelpers;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java
index d6635cf..eb9a5c31 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java
@@ -41,8 +41,6 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.download.ui.ThumbnailProvider;
-import org.chromium.chrome.browser.download.ui.ThumbnailProvider.ThumbnailRequest;
 import org.chromium.chrome.browser.favicon.LargeIconBridge;
 import org.chromium.chrome.browser.ntp.ContextMenuManager;
 import org.chromium.chrome.browser.ntp.ContextMenuManager.TouchEnabledDelegate;
@@ -63,6 +61,8 @@
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
 import org.chromium.chrome.browser.suggestions.ThumbnailGradient;
 import org.chromium.chrome.browser.util.FeatureUtilities;
+import org.chromium.chrome.browser.widget.ThumbnailProvider;
+import org.chromium.chrome.browser.widget.ThumbnailProvider.ThumbnailRequest;
 import org.chromium.chrome.browser.widget.displaystyle.HorizontalDisplayStyle;
 import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
 import org.chromium.chrome.browser.widget.displaystyle.VerticalDisplayStyle;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/HomeSheetTilesUiCaptureTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/HomeSheetTilesUiCaptureTest.java
index e705d08..22bd607 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/HomeSheetTilesUiCaptureTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/HomeSheetTilesUiCaptureTest.java
@@ -21,6 +21,7 @@
 import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.ScreenShooter;
 import org.chromium.chrome.browser.ntp.NtpUiCaptureTestData;
+import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
 import org.chromium.chrome.test.BottomSheetTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -51,6 +52,7 @@
 
     @Before
     public void setup() throws InterruptedException {
+        ChromePreferenceManager.getInstance().setNewTabPageGenericSigninPromoDismissed(true);
         mActivityRule.startMainActivityOnBlankPage();
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetUiCaptureTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetUiCaptureTest.java
index 10de79e3..44d88d20 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetUiCaptureTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetUiCaptureTest.java
@@ -24,8 +24,10 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ntp.NtpUiCaptureTestData;
 import org.chromium.chrome.browser.ntp.cards.ItemViewType;
+import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.util.browser.suggestions.FakeSuggestionsSource;
 import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule;
 import org.chromium.ui.test.util.UiRestriction;
 
@@ -40,7 +42,11 @@
 
     @Rule
     public SuggestionsDependenciesRule createSuggestions() {
-        return new SuggestionsDependenciesRule(NtpUiCaptureTestData.createFactory());
+        SuggestionsDependenciesRule.TestFactory depsFactory = NtpUiCaptureTestData.createFactory();
+        FakeSuggestionsSource suggestionsSource = new FakeSuggestionsSource();
+        NtpUiCaptureTestData.registerArticleSamples(suggestionsSource);
+        depsFactory.suggestionsSource = suggestionsSource;
+        return new SuggestionsDependenciesRule(depsFactory);
     }
 
     @Rule
@@ -48,6 +54,7 @@
 
     @Before
     public void setup() throws InterruptedException {
+        ChromePreferenceManager.getInstance().setNewTabPageGenericSigninPromoDismissed(true);
         mActivityRule.startMainActivityOnBlankPage();
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/SuggestionsSheetVisibilityChangeObserverTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/SuggestionsSheetVisibilityChangeObserverTest.java
index 24858af7..e809870 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/SuggestionsSheetVisibilityChangeObserverTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/SuggestionsSheetVisibilityChangeObserverTest.java
@@ -9,7 +9,6 @@
 import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
 import static org.chromium.chrome.browser.suggestions.SuggestionsSheetVisibilityChangeObserverTest.TestVisibilityChangeObserver.Event.Hidden;
 import static org.chromium.chrome.browser.suggestions.SuggestionsSheetVisibilityChangeObserverTest.TestVisibilityChangeObserver.Event.InitialReveal;
-import static org.chromium.chrome.browser.suggestions.SuggestionsSheetVisibilityChangeObserverTest.TestVisibilityChangeObserver.Event.Shown;
 import static org.chromium.chrome.browser.suggestions.SuggestionsSheetVisibilityChangeObserverTest.TestVisibilityChangeObserver.Event.StateChange;
 import static org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContentController.TYPE_SUGGESTIONS;
 
@@ -155,7 +154,7 @@
         Espresso.pressBack();
         waitForWindowUpdates();
 
-        mObserver.expectEvents(Shown, StateChange);
+        mObserver.expectEvents(InitialReveal, StateChange);
         assertEquals(BottomSheet.SHEET_STATE_FULL, mActivityRule.getBottomSheet().getSheetState());
         mEventReporter.surfaceOpenedHelper.waitForCallback();
         mEventReporter.surfaceOpenedHelper.verifyCallCount();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/ThumbnailProviderImplTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/ThumbnailProviderImplTest.java
similarity index 98%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/ThumbnailProviderImplTest.java
rename to chrome/android/javatests/src/org/chromium/chrome/browser/widget/ThumbnailProviderImplTest.java
index d629c427..d8d6f1c0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/ThumbnailProviderImplTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/ThumbnailProviderImplTest.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.download.ui;
+package org.chromium.chrome.browser.widget;
 
 import android.graphics.Bitmap;
 import android.support.test.filters.MediumTest;
@@ -21,7 +21,7 @@
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.download.ui.ThumbnailProvider.ThumbnailRequest;
+import org.chromium.chrome.browser.widget.ThumbnailProvider.ThumbnailRequest;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetContentControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetContentControllerTest.java
index a821317d..10fbe71 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetContentControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetContentControllerTest.java
@@ -24,7 +24,6 @@
 import org.chromium.chrome.browser.download.DownloadUtils;
 import org.chromium.chrome.browser.history.HistoryManagerUtils;
 import org.chromium.chrome.browser.history.HistorySheetContent;
-import org.chromium.chrome.browser.suggestions.SuggestionsBottomSheetContent;
 import org.chromium.chrome.test.BottomSheetTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.ui.test.util.UiRestriction;
@@ -59,14 +58,12 @@
         int closedCount = mObserver.mClosedCallbackHelper.getCallCount();
 
         mBottomSheetTestRule.setSheetState(BottomSheet.SHEET_STATE_HALF, false);
-        mObserver.mOpenedCallbackHelper.waitForCallback(openedCount, 1);
-        openedCount++;
-        assertEquals(contentChangedCount, mObserver.mContentChangedCallbackHelper.getCallCount());
+        mObserver.mOpenedCallbackHelper.waitForCallback(openedCount++, 1);
+        mObserver.mContentChangedCallbackHelper.waitForCallback(contentChangedCount++, 1);
         assertEquals(closedCount, mObserver.mClosedCallbackHelper.getCallCount());
 
         mBottomSheetTestRule.selectBottomSheetContent(R.id.action_history);
-        mObserver.mContentChangedCallbackHelper.waitForCallback(contentChangedCount, 1);
-        contentChangedCount++;
+        mObserver.mContentChangedCallbackHelper.waitForCallback(contentChangedCount++, 1);
         assertEquals(openedCount, mObserver.mOpenedCallbackHelper.getCallCount());
         assertEquals(closedCount, mObserver.mClosedCallbackHelper.getCallCount());
         assertTrue(mBottomSheet.getCurrentSheetContent() instanceof HistorySheetContent);
@@ -75,10 +72,10 @@
 
         mBottomSheetTestRule.setSheetState(BottomSheet.SHEET_STATE_PEEK, false);
         mObserver.mClosedCallbackHelper.waitForCallback(closedCount, 1);
-        mObserver.mContentChangedCallbackHelper.waitForCallback(contentChangedCount, 1);
+        mObserver.mContentChangedCallbackHelper.waitForCallback(contentChangedCount++, 1);
         assertEquals(openedCount, mObserver.mOpenedCallbackHelper.getCallCount());
-        assertTrue(mBottomSheet.getCurrentSheetContent() instanceof SuggestionsBottomSheetContent);
-        assertEquals(R.id.action_home, mBottomSheetContentController.getSelectedItemIdForTests());
+        assertEquals(null, mBottomSheet.getCurrentSheetContent());
+        assertEquals(0, mBottomSheetContentController.getSelectedItemIdForTests());
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/ImageFetcherTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/ImageFetcherTest.java
index 7730e40..b9a3fd6 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/ImageFetcherTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/ImageFetcherTest.java
@@ -24,7 +24,6 @@
 import org.chromium.base.DiscardableReferencePool;
 import org.chromium.chrome.browser.DisableHistogramsRule;
 import org.chromium.chrome.browser.NativePageHost;
-import org.chromium.chrome.browser.download.ui.ThumbnailProvider;
 import org.chromium.chrome.browser.favicon.FaviconHelper;
 import org.chromium.chrome.browser.favicon.FaviconHelper.FaviconImageCallback;
 import org.chromium.chrome.browser.favicon.LargeIconBridge;
@@ -35,6 +34,7 @@
 import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.suggestions.ImageFetcher.DownloadThumbnailRequest;
+import org.chromium.chrome.browser.widget.ThumbnailProvider;
 import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule;
 import org.chromium.testing.local.LocalRobolectricTestRunner;
 
diff --git a/chrome/android/webapk/libs/runtime_library/BUILD.gn b/chrome/android/webapk/libs/runtime_library/BUILD.gn
index 315ceb4..4413aae 100644
--- a/chrome/android/webapk/libs/runtime_library/BUILD.gn
+++ b/chrome/android/webapk/libs/runtime_library/BUILD.gn
@@ -30,7 +30,6 @@
 # runtime_library_from_assets_java cannot be used as a dependency of another
 # library because the dx tool expects files ending in .dex.jar
 android_library("runtime_library_for_assets_java") {
-  dex_path = "$target_gen_dir/$runtime_library_dex_asset_name"
   java_files =
       [ "src/org/chromium/webapk/lib/runtime_library/WebApkServiceImpl.java" ]
   srcjar_deps = [ ":webapk_service_aidl" ]
@@ -45,6 +44,21 @@
   ]
 }
 
+dist_jar("webapk_runtime_library") {
+  requires_android = true
+  deps = [
+    ":runtime_library_for_assets_java",
+  ]
+  proguard_enabled = true
+  proguard_configs = [
+    "runtime_library.proguard.flags",
+    "//base/android/proguard/chromium_code.flags",
+    "//base/android/proguard/chromium_apk.flags",
+  ]
+  output = "$target_gen_dir/$target_name.jar"
+  dex_path = "$target_gen_dir/$runtime_library_dex_asset_name"
+}
+
 android_assets("runtime_library_assets") {
   write_file("$target_gen_dir/webapk_dex_version.txt", runtime_library_version)
 
@@ -54,7 +68,7 @@
   ]
 
   deps = [
-    ":runtime_library_for_assets_java",
+    ":webapk_runtime_library",
   ]
 }
 
diff --git a/chrome/android/webapk/libs/runtime_library/runtime_library.proguard.flags b/chrome/android/webapk/libs/runtime_library/runtime_library.proguard.flags
new file mode 100644
index 0000000..e2cfaee
--- /dev/null
+++ b/chrome/android/webapk/libs/runtime_library/runtime_library.proguard.flags
@@ -0,0 +1,5 @@
+# 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.
+
+-keep class org.chromium.** { *; }
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 5366931..b8e580b 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -9841,9 +9841,6 @@
         Hide password
       </message>
 
-      <message name="IDS_MANAGE_PASSWORDS_AUTO_SIGNIN_TITLE" desc="The title of the auto-signin toast.">
-        Signing in as <ph name="username">$1<ex>chef@google.com</ex></ph>
-      </message>
       <message name="IDS_MANAGE_PASSWORDS_AUTO_SIGNIN_TITLE_MD" desc="The title of the auto-signin toast in Material Design mode.">
         Signing in as
       </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index f6891940..75ca1340 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -97,6 +97,10 @@
     "autofill/risk_util.h",
     "autofill/validation_rules_storage_factory.cc",
     "autofill/validation_rules_storage_factory.h",
+    "background_fetch/background_fetch_delegate_factory.cc",
+    "background_fetch/background_fetch_delegate_factory.h",
+    "background_fetch/background_fetch_delegate_impl.cc",
+    "background_fetch/background_fetch_delegate_impl.h",
     "background_sync/background_sync_controller_factory.cc",
     "background_sync/background_sync_controller_factory.h",
     "background_sync/background_sync_controller_impl.cc",
@@ -4060,7 +4064,6 @@
       "../android/java/src/org/chromium/chrome/browser/download/items/OfflineContentAggregatorFactory.java",
       "../android/java/src/org/chromium/chrome/browser/download/service/DownloadBackgroundTask.java",
       "../android/java/src/org/chromium/chrome/browser/download/service/DownloadTaskScheduler.java",
-      "../android/java/src/org/chromium/chrome/browser/download/ui/ThumbnailProviderImpl.java",
       "../android/java/src/org/chromium/chrome/browser/engagement/SiteEngagementService.java",
       "../android/java/src/org/chromium/chrome/browser/favicon/FaviconHelper.java",
       "../android/java/src/org/chromium/chrome/browser/favicon/LargeIconBridge.java",
@@ -4203,6 +4206,7 @@
       "../android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java",
       "../android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java",
       "../android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java",
+      "../android/java/src/org/chromium/chrome/browser/widget/ThumbnailProviderImpl.java",
     ]
 
     # Used for testing only, should not be shipped to end users.
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 7c9585c3..79f94ae8b 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3490,6 +3490,12 @@
      flag_descriptions::kPasswordSelectionDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(password_manager::features::kEnablePasswordSelection)},
 
+    {"enable-html-base-username-detector",
+     flag_descriptions::kHtmlBasedUsernameDetectorName,
+     flag_descriptions::kHtmlBasedUsernameDetectorDescription, kOsAll,
+     FEATURE_VALUE_TYPE(
+         password_manager::features::kEnableHtmlBasedUsernameDetector)},
+
     // NOTE: Adding new command-line switches requires adding corresponding
     // entries to enum "LoginCustomFlags" in histograms/enums.xml. See note in
     // enums.xml and don't forget to run AboutFlagsHistogramTest unit test.
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index d657b54..e0107876 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -58,6 +58,7 @@
     &kCCTPostMessageAPI,
     &kCCTRedirectPreconnect,
     &kChromeHomeFeature,
+    &kChromeHomeDestroySuggestions,
     &kChromeHomeDoodle,
     &kChromeHomeExpandButton,
     &kChromeHomeSwipeLogic,
@@ -161,6 +162,9 @@
 const base::Feature kChromeHomeFeature{"ChromeHome",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kChromeHomeDestroySuggestions{
+    "ChromeHomeDestroySuggestions", base::FEATURE_ENABLED_BY_DEFAULT};
+
 const base::Feature kChromeHomeDoodle{"ChromeHomeDoodle",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -265,7 +269,7 @@
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kPwaPersistentNotification{
-    "PwaPersistentNotification", base::FEATURE_ENABLED_BY_DEFAULT};
+    "PwaPersistentNotification", base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kReaderModeInCCT{"ReaderModeInCCT",
                                      base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 003c870b..815879da 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -22,6 +22,7 @@
 extern const base::Feature kCCTPostMessageAPI;
 extern const base::Feature kCCTRedirectPreconnect;
 extern const base::Feature kChromeHomeFeature;
+extern const base::Feature kChromeHomeDestroySuggestions;
 extern const base::Feature kChromeHomeDoodle;
 extern const base::Feature kChromeHomeExpandButton;
 extern const base::Feature kChromeHomeSwipeLogic;
diff --git a/chrome/browser/android/vr_shell/gl_browser_interface.h b/chrome/browser/android/vr_shell/gl_browser_interface.h
index 53c7c4d4..1a8ad0f 100644
--- a/chrome/browser/android/vr_shell/gl_browser_interface.h
+++ b/chrome/browser/android/vr_shell/gl_browser_interface.h
@@ -20,9 +20,8 @@
 
 namespace vr_shell {
 
-// An interface for the GL thread to communicate with the rest of the system
-// (UI, VrShell, etc). Many of the functions in this interface are proxies to
-// methods on VrShell.
+// VrShellGl talks to VrShell through this interface. This could be split up if
+// VrShellGl is refactored into components.
 class GlBrowserInterface {
  public:
   virtual ~GlBrowserInterface() = default;
@@ -30,18 +29,11 @@
   virtual void ContentSurfaceChanged(jobject surface) = 0;
   virtual void GvrDelegateReady(gvr::ViewerType viewer_type) = 0;
   virtual void UpdateGamepadData(device::GvrGamepadData) = 0;
-  virtual void AppButtonGesturePerformed(
-      vr::UiInterface::Direction direction) = 0;
-  virtual void AppButtonClicked() = 0;
   virtual void ProcessContentGesture(
       std::unique_ptr<blink::WebInputEvent> event) = 0;
   virtual void ForceExitVr() = 0;
   virtual void OnContentPaused(bool enabled) = 0;
   virtual void ToggleCardboardGamepad(bool enabled) = 0;
-  virtual void OnGlInitialized(unsigned int content_texture_id) = 0;
-  virtual void OnWebVrFrameAvailable() = 0;
-  virtual void OnWebVrTimedOut() = 0;
-  virtual void OnProjMatrixChanged(const gfx::Transform& proj_matrix) = 0;
 };
 
 }  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/vr_gl_thread.cc b/chrome/browser/android/vr_shell/vr_gl_thread.cc
index d2dd81b..a0c8881 100644
--- a/chrome/browser/android/vr_shell/vr_gl_thread.cc
+++ b/chrome/browser/android/vr_shell/vr_gl_thread.cc
@@ -10,8 +10,8 @@
 #include "chrome/browser/android/vr_shell/vr_input_manager.h"
 #include "chrome/browser/android/vr_shell/vr_shell.h"
 #include "chrome/browser/android/vr_shell/vr_shell_gl.h"
+#include "chrome/browser/vr/browser_ui_interface.h"
 #include "chrome/browser/vr/toolbar_state.h"
-#include "chrome/browser/vr/ui_interface.h"
 #include "chrome/browser/vr/ui_scene.h"
 #include "chrome/browser/vr/ui_scene_manager.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -41,6 +41,10 @@
   Stop();
 }
 
+base::WeakPtr<VrShellGl> VrGLThread::GetVrShellGl() {
+  return vr_shell_gl_->GetWeakPtr();
+}
+
 void VrGLThread::Init() {
   scene_ = base::MakeUnique<vr::UiScene>();
   vr_shell_gl_ = base::MakeUnique<VrShellGl>(this, gvr_api_, initially_web_vr_,
@@ -49,9 +53,14 @@
   scene_manager_ = base::MakeUnique<vr::UiSceneManager>(
       this, scene_.get(), vr_shell_gl_.get(), in_cct_, initially_web_vr_,
       web_vr_autopresentation_expected_);
+  // TODO(cjgrant): Alleviate this circular initialization dependency by moving
+  // ownership of all UI components into a single UI object that can manage its
+  // own initialization.
+  vr_shell_gl_->set_ui(scene_manager_.get());
 
   weak_vr_shell_gl_ = vr_shell_gl_->GetWeakPtr();
-  weak_scene_manager_ = scene_manager_->GetWeakPtr();
+  browser_ui_ = scene_manager_->GetWeakPtr();
+
   vr_shell_gl_->Initialize();
 }
 
@@ -62,207 +71,213 @@
 }
 
 void VrGLThread::ContentSurfaceChanged(jobject surface) {
+  DCHECK(OnGlThread());
   main_thread_task_runner_->PostTask(
       FROM_HERE,
       base::Bind(&VrShell::ContentSurfaceChanged, weak_vr_shell_, surface));
 }
 
 void VrGLThread::GvrDelegateReady(gvr::ViewerType viewer_type) {
+  DCHECK(OnGlThread());
   main_thread_task_runner_->PostTask(
       FROM_HERE,
       base::Bind(&VrShell::GvrDelegateReady, weak_vr_shell_, viewer_type));
 }
 
 void VrGLThread::UpdateGamepadData(device::GvrGamepadData pad) {
+  DCHECK(OnGlThread());
   main_thread_task_runner_->PostTask(
       FROM_HERE, base::Bind(&VrShell::UpdateGamepadData, weak_vr_shell_, pad));
 }
 
-void VrGLThread::AppButtonClicked() {
-  task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(&vr::UiSceneManager::OnAppButtonClicked, weak_scene_manager_));
-}
-
-void VrGLThread::AppButtonGesturePerformed(
-    vr::UiInterface::Direction direction) {
-  task_runner()->PostTask(
-      FROM_HERE, base::Bind(&vr::UiSceneManager::OnAppButtonGesturePerformed,
-                            weak_scene_manager_, direction));
-}
-
 void VrGLThread::ProcessContentGesture(
     std::unique_ptr<blink::WebInputEvent> event) {
+  DCHECK(OnGlThread());
   main_thread_task_runner_->PostTask(
       FROM_HERE, base::Bind(&VrShell::ProcessContentGesture, weak_vr_shell_,
                             base::Passed(std::move(event))));
 }
 
 void VrGLThread::ForceExitVr() {
+  DCHECK(OnGlThread());
   main_thread_task_runner_->PostTask(
       FROM_HERE, base::Bind(&VrShell::ForceExitVr, weak_vr_shell_));
 }
 
 void VrGLThread::ExitPresent() {
+  DCHECK(OnGlThread());
   main_thread_task_runner_->PostTask(
       FROM_HERE, base::Bind(&VrShell::ExitPresent, weak_vr_shell_));
 }
 
 void VrGLThread::ExitFullscreen() {
+  DCHECK(OnGlThread());
   main_thread_task_runner_->PostTask(
       FROM_HERE, base::Bind(&VrShell::ExitFullscreen, weak_vr_shell_));
 }
 
 void VrGLThread::OnContentPaused(bool enabled) {
+  DCHECK(OnGlThread());
   main_thread_task_runner_->PostTask(
       FROM_HERE,
       base::Bind(&VrShell::OnContentPaused, weak_vr_shell_, enabled));
 }
 
 void VrGLThread::NavigateBack() {
+  DCHECK(OnGlThread());
   main_thread_task_runner_->PostTask(
       FROM_HERE, base::Bind(&VrShell::NavigateBack, weak_vr_shell_));
 }
 
 void VrGLThread::ExitCct() {
+  DCHECK(OnGlThread());
   main_thread_task_runner_->PostTask(
       FROM_HERE, base::Bind(&VrShell::ExitCct, weak_vr_shell_));
 }
 
 void VrGLThread::ToggleCardboardGamepad(bool enabled) {
+  DCHECK(OnGlThread());
   main_thread_task_runner_->PostTask(
       FROM_HERE,
       base::Bind(&VrShell::ToggleCardboardGamepad, weak_vr_shell_, enabled));
 }
 
-void VrGLThread::OnGlInitialized(unsigned int content_texture_id) {
-  task_runner()->PostTask(FROM_HERE,
-                          base::Bind(&vr::UiSceneManager::OnGlInitialized,
-                                     weak_scene_manager_, content_texture_id));
-}
-
 void VrGLThread::OnUnsupportedMode(vr::UiUnsupportedMode mode) {
+  DCHECK(OnGlThread());
   main_thread_task_runner_->PostTask(
       FROM_HERE, base::Bind(&VrShell::OnUnsupportedMode, weak_vr_shell_, mode));
 }
 
 void VrGLThread::OnExitVrPromptResult(vr::UiUnsupportedMode reason,
                                       vr::ExitVrPromptChoice choice) {
+  DCHECK(OnGlThread());
   main_thread_task_runner_->PostTask(
       FROM_HERE, base::Bind(&VrShell::OnExitVrPromptResult, weak_vr_shell_,
                             reason, choice));
 }
 
 void VrGLThread::OnContentScreenBoundsChanged(const gfx::SizeF& bounds) {
+  DCHECK(OnGlThread());
   main_thread_task_runner_->PostTask(
       FROM_HERE, base::Bind(&VrShell::OnContentScreenBoundsChanged,
                             weak_vr_shell_, bounds));
 }
 
 void VrGLThread::SetFullscreen(bool enabled) {
-  task_runner()->PostTask(FROM_HERE,
-                          base::Bind(&vr::UiSceneManager::SetFullscreen,
-                                     weak_scene_manager_, enabled));
+  DCHECK(OnMainThread());
+  task_runner()->PostTask(
+      FROM_HERE,
+      base::Bind(&vr::BrowserUiInterface::SetFullscreen, browser_ui_, enabled));
 }
 
 void VrGLThread::SetIncognito(bool incognito) {
-  task_runner()->PostTask(FROM_HERE,
-                          base::Bind(&vr::UiSceneManager::SetIncognito,
-                                     weak_scene_manager_, incognito));
+  DCHECK(OnMainThread());
+  task_runner()->PostTask(
+      FROM_HERE, base::Bind(&vr::BrowserUiInterface::SetIncognito, browser_ui_,
+                            incognito));
 }
 
 void VrGLThread::SetHistoryButtonsEnabled(bool can_go_back,
                                           bool can_go_forward) {
+  DCHECK(OnMainThread());
   task_runner()->PostTask(
-      FROM_HERE, base::Bind(&vr::UiSceneManager::SetHistoryButtonsEnabled,
-                            weak_scene_manager_, can_go_back, can_go_forward));
+      FROM_HERE, base::Bind(&vr::BrowserUiInterface::SetHistoryButtonsEnabled,
+                            browser_ui_, can_go_back, can_go_forward));
 }
 
 void VrGLThread::SetLoadProgress(float progress) {
+  DCHECK(OnMainThread());
   task_runner()->PostTask(FROM_HERE,
-                          base::Bind(&vr::UiSceneManager::SetLoadProgress,
-                                     weak_scene_manager_, progress));
+                          base::Bind(&vr::BrowserUiInterface::SetLoadProgress,
+                                     browser_ui_, progress));
 }
 
 void VrGLThread::SetLoading(bool loading) {
-  task_runner()->PostTask(FROM_HERE, base::Bind(&vr::UiSceneManager::SetLoading,
-                                                weak_scene_manager_, loading));
+  DCHECK(OnMainThread());
+  task_runner()->PostTask(
+      FROM_HERE,
+      base::Bind(&vr::BrowserUiInterface::SetLoading, browser_ui_, loading));
 }
 
 void VrGLThread::SetToolbarState(const vr::ToolbarState& state) {
-  task_runner()->PostTask(FROM_HERE,
-                          base::Bind(&vr::UiSceneManager::SetToolbarState,
-                                     weak_scene_manager_, state));
+  DCHECK(OnMainThread());
+  task_runner()->PostTask(
+      FROM_HERE,
+      base::Bind(&vr::BrowserUiInterface::SetToolbarState, browser_ui_, state));
 }
 
 void VrGLThread::SetWebVrMode(bool enabled, bool show_toast) {
-  task_runner()->PostTask(FROM_HERE,
-                          base::Bind(&vr::UiSceneManager::SetWebVrMode,
-                                     weak_scene_manager_, enabled, show_toast));
+  DCHECK(OnMainThread());
+  task_runner()->PostTask(
+      FROM_HERE, base::Bind(&vr::BrowserUiInterface::SetWebVrMode, browser_ui_,
+                            enabled, show_toast));
 }
 
 void VrGLThread::SetWebVrSecureOrigin(bool secure) {
-  task_runner()->PostTask(FROM_HERE,
-                          base::Bind(&vr::UiSceneManager::SetWebVrSecureOrigin,
-                                     weak_scene_manager_, secure));
+  DCHECK(OnMainThread());
+  task_runner()->PostTask(
+      FROM_HERE, base::Bind(&vr::BrowserUiInterface::SetWebVrSecureOrigin,
+                            browser_ui_, secure));
 }
 
 void VrGLThread::SetAudioCapturingIndicator(bool enabled) {
+  DCHECK(OnMainThread());
   task_runner()->PostTask(
-      FROM_HERE, base::Bind(&vr::UiSceneManager::SetAudioCapturingIndicator,
-                            weak_scene_manager_, enabled));
+      FROM_HERE, base::Bind(&vr::BrowserUiInterface::SetAudioCapturingIndicator,
+                            browser_ui_, enabled));
 }
 
 void VrGLThread::SetLocationAccessIndicator(bool enabled) {
+  DCHECK(OnMainThread());
   task_runner()->PostTask(
-      FROM_HERE, base::Bind(&vr::UiSceneManager::SetLocationAccessIndicator,
-                            weak_scene_manager_, enabled));
+      FROM_HERE, base::Bind(&vr::BrowserUiInterface::SetLocationAccessIndicator,
+                            browser_ui_, enabled));
 }
 
 void VrGLThread::SetVideoCapturingIndicator(bool enabled) {
+  DCHECK(OnMainThread());
   task_runner()->PostTask(
-      FROM_HERE, base::Bind(&vr::UiSceneManager::SetVideoCapturingIndicator,
-                            weak_scene_manager_, enabled));
+      FROM_HERE, base::Bind(&vr::BrowserUiInterface::SetVideoCapturingIndicator,
+                            browser_ui_, enabled));
 }
 
 void VrGLThread::SetScreenCapturingIndicator(bool enabled) {
+  DCHECK(OnMainThread());
   task_runner()->PostTask(
-      FROM_HERE, base::Bind(&vr::UiSceneManager::SetScreenCapturingIndicator,
-                            weak_scene_manager_, enabled));
+      FROM_HERE,
+      base::Bind(&vr::BrowserUiInterface::SetScreenCapturingIndicator,
+                 browser_ui_, enabled));
 }
 
 void VrGLThread::SetBluetoothConnectedIndicator(bool enabled) {
+  DCHECK(OnMainThread());
   task_runner()->PostTask(
-      FROM_HERE, base::Bind(&vr::UiSceneManager::SetBluetoothConnectedIndicator,
-                            weak_scene_manager_, enabled));
+      FROM_HERE,
+      base::Bind(&vr::BrowserUiInterface::SetBluetoothConnectedIndicator,
+                 browser_ui_, enabled));
 }
 
 void VrGLThread::SetIsExiting() {
+  DCHECK(OnMainThread());
   task_runner()->PostTask(
       FROM_HERE,
-      base::Bind(&vr::UiSceneManager::SetIsExiting, weak_scene_manager_));
-}
-
-void VrGLThread::OnWebVrFrameAvailable() {
-  DCHECK(task_runner()->BelongsToCurrentThread());
-  scene_manager_->OnWebVrFrameAvailable();
-}
-
-void VrGLThread::OnWebVrTimedOut() {
-  DCHECK(task_runner()->BelongsToCurrentThread());
-  scene_manager_->OnWebVrTimedOut();
-}
-
-void VrGLThread::OnProjMatrixChanged(const gfx::Transform& proj_matrix) {
-  DCHECK(task_runner()->BelongsToCurrentThread());
-  scene_manager_->OnProjMatrixChanged(proj_matrix);
+      base::Bind(&vr::BrowserUiInterface::SetIsExiting, browser_ui_));
 }
 
 void VrGLThread::SetExitVrPromptEnabled(bool enabled,
                                         vr::UiUnsupportedMode reason) {
+  DCHECK(OnMainThread());
   task_runner()->PostTask(
-      FROM_HERE, base::Bind(&vr::UiSceneManager::SetExitVrPromptEnabled,
-                            weak_scene_manager_, enabled, reason));
+      FROM_HERE, base::Bind(&vr::BrowserUiInterface::SetExitVrPromptEnabled,
+                            browser_ui_, enabled, reason));
+}
+
+bool VrGLThread::OnMainThread() const {
+  return main_thread_task_runner_->BelongsToCurrentThread();
+}
+
+bool VrGLThread::OnGlThread() const {
+  return task_runner()->BelongsToCurrentThread();
 }
 
 }  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/vr_gl_thread.h b/chrome/browser/android/vr_shell/vr_gl_thread.h
index c2f9ed3..f398c3b 100644
--- a/chrome/browser/android/vr_shell/vr_gl_thread.h
+++ b/chrome/browser/android/vr_shell/vr_gl_thread.h
@@ -12,6 +12,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
 #include "chrome/browser/android/vr_shell/gl_browser_interface.h"
+#include "chrome/browser/vr/browser_ui_interface.h"
 #include "chrome/browser/vr/ui_browser_interface.h"
 #include "chrome/browser/vr/ui_interface.h"
 #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
@@ -29,7 +30,7 @@
 class VrGLThread : public base::android::JavaHandlerThread,
                    public GlBrowserInterface,
                    public vr::UiBrowserInterface,
-                   public vr::UiInterface {
+                   public vr::BrowserUiInterface {
  public:
   VrGLThread(
       const base::WeakPtr<VrShell>& weak_vr_shell,
@@ -42,23 +43,17 @@
       bool daydream_support);
 
   ~VrGLThread() override;
-  base::WeakPtr<VrShellGl> GetVrShellGl() { return weak_vr_shell_gl_; }
+  base::WeakPtr<VrShellGl> GetVrShellGl();
 
-  // GlBrowserInterface implementation (VrShellGl calling out to UI/VrShell).
+  // vr::GlBrowserInterface implementation (GL calling to VrShell).
   void ContentSurfaceChanged(jobject surface) override;
   void GvrDelegateReady(gvr::ViewerType viewer_type) override;
   void UpdateGamepadData(device::GvrGamepadData) override;
-  void AppButtonClicked() override;
-  void AppButtonGesturePerformed(vr::UiInterface::Direction direction) override;
   void ProcessContentGesture(
       std::unique_ptr<blink::WebInputEvent> event) override;
   void ForceExitVr() override;
   void OnContentPaused(bool enabled) override;
   void ToggleCardboardGamepad(bool enabled) override;
-  void OnGlInitialized(unsigned int content_texture_id) override;
-  void OnWebVrFrameAvailable() override;
-  void OnWebVrTimedOut() override;
-  void OnProjMatrixChanged(const gfx::Transform& proj_matrix) override;
 
   // vr::UiBrowserInterface implementation (UI calling to VrShell).
   void ExitPresent() override;
@@ -70,21 +65,21 @@
                             vr::ExitVrPromptChoice choice) override;
   void OnContentScreenBoundsChanged(const gfx::SizeF& bounds) override;
 
-  // vr::UiInterface implementation (VrShell and GL calling to the UI).
-  void SetFullscreen(bool enabled) override;
-  void SetIncognito(bool incognito) override;
-  void SetHistoryButtonsEnabled(bool can_go_back, bool can_go_forward) override;
-  void SetLoadProgress(float progress) override;
-  void SetLoading(bool loading) override;
-  void SetToolbarState(const vr::ToolbarState& state) override;
+  // vr::BrowserUiInterface implementation (Browser calling to UI).
   void SetWebVrMode(bool enabled, bool show_toast) override;
+  void SetFullscreen(bool enabled) override;
+  void SetToolbarState(const vr::ToolbarState& state) override;
+  void SetIncognito(bool incognito) override;
   void SetWebVrSecureOrigin(bool secure) override;
+  void SetLoading(bool loading) override;
+  void SetLoadProgress(float progress) override;
+  void SetIsExiting() override;
+  void SetHistoryButtonsEnabled(bool can_go_back, bool can_go_forward) override;
   void SetVideoCapturingIndicator(bool enabled) override;
   void SetScreenCapturingIndicator(bool enabled) override;
   void SetAudioCapturingIndicator(bool enabled) override;
   void SetBluetoothConnectedIndicator(bool enabled) override;
   void SetLocationAccessIndicator(bool enabled) override;
-  void SetIsExiting() override;
   void SetExitVrPromptEnabled(bool enabled,
                               vr::UiUnsupportedMode reason) override;
 
@@ -93,15 +88,19 @@
   void CleanUp() override;
 
  private:
+  bool OnMainThread() const;
+  bool OnGlThread() const;
+
   // Created on GL thread.
   std::unique_ptr<vr::UiScene> scene_;
   std::unique_ptr<vr::UiSceneManager> scene_manager_;
-  base::WeakPtr<vr::UiSceneManager> weak_scene_manager_;
   std::unique_ptr<VrShellGl> vr_shell_gl_;
+
+  base::WeakPtr<VrShell> weak_vr_shell_;
   base::WeakPtr<VrShellGl> weak_vr_shell_gl_;
+  base::WeakPtr<BrowserUiInterface> browser_ui_;
 
   // This state is used for initializing vr_shell_gl_.
-  base::WeakPtr<VrShell> weak_vr_shell_;
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
   gvr_context* gvr_api_;
   bool initially_web_vr_;
diff --git a/chrome/browser/android/vr_shell/vr_shell.h b/chrome/browser/android/vr_shell/vr_shell.h
index 4bdf073..f22ff4f2 100644
--- a/chrome/browser/android/vr_shell/vr_shell.h
+++ b/chrome/browser/android/vr_shell/vr_shell.h
@@ -15,7 +15,6 @@
 #include "base/single_thread_task_runner.h"
 #include "chrome/browser/ui/toolbar/chrome_toolbar_model_delegate.h"
 #include "chrome/browser/vr/exit_vr_prompt_choice.h"
-#include "chrome/browser/vr/ui_interface.h"
 #include "chrome/browser/vr/ui_unsupported_mode.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "device/geolocation/public/interfaces/geolocation_config.mojom.h"
@@ -37,8 +36,8 @@
 }  // namespace ui
 
 namespace vr {
+class BrowserUiInterface;
 class ToolbarHelper;
-class UiInterface;
 }  // namespace vr
 
 namespace vr_shell {
@@ -242,7 +241,7 @@
   std::unique_ptr<VrGLThread> gl_thread_;
   bool reprojected_rendering_;
 
-  vr::UiInterface* ui_;
+  vr::BrowserUiInterface* ui_;
   std::unique_ptr<vr::ToolbarHelper> toolbar_;
 
   device::mojom::GeolocationConfigPtr geolocation_config_;
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.cc b/chrome/browser/android/vr_shell/vr_shell_gl.cc
index 7ca96f7..f9abd4d0 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.cc
@@ -253,12 +253,7 @@
 
   InitializeRenderer();
 
-  // TODO(vollick): this is really going to the UI, not the browser. It would be
-  // nice to hold a pointer to a, possibly limited, UI interface that could be
-  // invoked synchronously on this thread (cf the post tasking to the current
-  // thread in VrGlThread). I.e., we could probably split GlBrowserInterface
-  // into parts.
-  browser_->OnGlInitialized(content_texture_id_);
+  ui_->OnGlInitialized(content_texture_id_);
 
   webvr_vsync_align_ = base::FeatureList::IsEnabled(features::kWebVrVsyncAlign);
 
@@ -393,7 +388,7 @@
   TRACE_EVENT1("gpu", "VrShellGl::OnWebVRFrameAvailable", "frame", frame_index);
   pending_frames_.pop();
 
-  browser_->OnWebVrFrameAvailable();
+  ui_->OnWebVrFrameAvailable();
 
   DrawFrame(frame_index);
   if (web_vr_mode_) {
@@ -420,7 +415,7 @@
 }
 
 void VrShellGl::OnWebVrFrameTimedOut() {
-  browser_->OnWebVrTimedOut();
+  ui_->OnWebVrTimedOut();
 }
 
 void VrShellGl::GvrInit(gvr_context* gvr_api) {
@@ -723,11 +718,17 @@
       if (fabs(gesture_xz_angle) > kMinAppButtonGestureAngleRad) {
         direction = gesture_xz_angle < 0 ? vr::UiInterface::LEFT
                                          : vr::UiInterface::RIGHT;
-        browser_->AppButtonGesturePerformed(direction);
+        // Post a task, rather than calling the UI directly, so as not to modify
+        // UI state in the midst of frame rendering.
+        base::ThreadTaskRunnerHandle::Get()->PostTask(
+            FROM_HERE, base::Bind(&vr::UiInterface::OnAppButtonGesturePerformed,
+                                  base::Unretained(ui_), direction));
       }
     }
     if (direction == vr::UiInterface::NONE)
-      browser_->AppButtonClicked();
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::Bind(&vr::UiInterface::OnAppButtonClicked,
+                                base::Unretained(ui_)));
   }
 }
 
@@ -863,7 +864,7 @@
                  &render_info_primary_);
 
   // Measure projected content size and bubble up if delta exceeds threshold.
-  browser_->OnProjMatrixChanged(render_info_primary_.left_eye_info.proj_matrix);
+  ui_->OnProjMatrixChanged(render_info_primary_.left_eye_info.proj_matrix);
 
   // At this point, we draw non-WebVR content that could, potentially, fill the
   // viewport.  NB: this is not just 2d browsing stuff, we may have a splash
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.h b/chrome/browser/android/vr_shell/vr_shell_gl.h
index 743bb711..378917f 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.h
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.h
@@ -50,6 +50,7 @@
 namespace vr {
 class FPSMeter;
 class SlidingAverage;
+class UiInterface;
 class UiScene;
 class VrShellRenderer;
 }  // namespace vr
@@ -87,6 +88,8 @@
   void Initialize();
   void InitializeGl(gfx::AcceleratedWidget window);
 
+  void set_ui(vr::UiInterface* ui) { ui_ = ui; }
+
   void OnTriggerEvent();
   void OnPause();
   void OnResume();
@@ -249,6 +252,7 @@
   mojo::Binding<device::mojom::VRPresentationProvider> binding_;
   device::mojom::VRSubmitFrameClientPtr submit_client_;
 
+  vr::UiInterface* ui_;
   GlBrowserInterface* browser_;
 
   vr::UiScene* scene_ = nullptr;
diff --git a/chrome/browser/android/vr_shell/vr_web_contents_observer.cc b/chrome/browser/android/vr_shell/vr_web_contents_observer.cc
index 56e9846..24d1831 100644
--- a/chrome/browser/android/vr_shell/vr_web_contents_observer.cc
+++ b/chrome/browser/android/vr_shell/vr_web_contents_observer.cc
@@ -13,10 +13,11 @@
 
 namespace vr_shell {
 
-VrWebContentsObserver::VrWebContentsObserver(content::WebContents* web_contents,
-                                             VrShell* vr_shell,
-                                             vr::UiInterface* ui_interface,
-                                             vr::ToolbarHelper* toolbar)
+VrWebContentsObserver::VrWebContentsObserver(
+    content::WebContents* web_contents,
+    VrShell* vr_shell,
+    vr::BrowserUiInterface* ui_interface,
+    vr::ToolbarHelper* toolbar)
     : WebContentsObserver(web_contents),
       vr_shell_(vr_shell),
       ui_interface_(ui_interface),
@@ -26,7 +27,8 @@
 
 VrWebContentsObserver::~VrWebContentsObserver() {}
 
-void VrWebContentsObserver::SetUiInterface(vr::UiInterface* ui_interface) {
+void VrWebContentsObserver::SetUiInterface(
+    vr::BrowserUiInterface* ui_interface) {
   ui_interface_ = ui_interface;
 }
 
diff --git a/chrome/browser/android/vr_shell/vr_web_contents_observer.h b/chrome/browser/android/vr_shell/vr_web_contents_observer.h
index f6624c9..0c149f1e 100644
--- a/chrome/browser/android/vr_shell/vr_web_contents_observer.h
+++ b/chrome/browser/android/vr_shell/vr_web_contents_observer.h
@@ -13,8 +13,8 @@
 }  // namespace content
 
 namespace vr {
+class BrowserUiInterface;
 class ToolbarHelper;
-class UiInterface;
 }  // namespace vr
 
 namespace vr_shell {
@@ -26,11 +26,11 @@
  public:
   VrWebContentsObserver(content::WebContents* web_contents,
                         VrShell* vr_shell,
-                        vr::UiInterface* ui_interface,
+                        vr::BrowserUiInterface* ui_interface,
                         vr::ToolbarHelper* toolbar);
   ~VrWebContentsObserver() override;
 
-  void SetUiInterface(vr::UiInterface* ui_interface);
+  void SetUiInterface(vr::BrowserUiInterface* ui_interface);
 
  private:
   // WebContentsObserver implementation.
@@ -54,7 +54,7 @@
 
   // This class does not own these pointers.
   VrShell* vr_shell_;
-  vr::UiInterface* ui_interface_;
+  vr::BrowserUiInterface* ui_interface_;
   vr::ToolbarHelper* toolbar_;
 
   DISALLOW_COPY_AND_ASSIGN(VrWebContentsObserver);
diff --git a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc
index 2188e935..905b811 100644
--- a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc
+++ b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc
@@ -59,9 +59,14 @@
 }
 
 InstallableParams ParamsToPerformInstallableCheck(
+    int ideal_icon_size_in_px,
+    int minimum_icon_size_in_px,
     bool check_webapk_compatibility) {
   InstallableParams params;
+  params.ideal_primary_icon_size_in_px = ideal_icon_size_in_px;
+  params.minimum_primary_icon_size_in_px = minimum_icon_size_in_px;
   params.check_installable = check_webapk_compatibility;
+  params.fetch_valid_primary_icon = check_webapk_compatibility;
   return params;
 }
 
@@ -122,6 +127,7 @@
       data_timeout_ms_(data_timeout_ms),
       check_webapk_compatibility_(check_webapk_compatibility),
       is_waiting_for_web_application_info_(true),
+      is_waiting_for_manifest_(true),
       weak_ptr_factory_(this) {
   DCHECK(minimum_icon_size_in_px <= ideal_icon_size_in_px);
   DCHECK(minimum_splash_image_size_in_px <= ideal_splash_image_size_in_px);
@@ -214,6 +220,21 @@
   if (!web_contents())
     return;
 
+  if (is_waiting_for_manifest_) {
+    // Explicitly trigger a GetData call with a no-op callback to complete the
+    // installability check. This is so we can accurately record whether or not
+    // a site is a PWA, assuming that the check finishes prior to a reset
+    // operation. If it does not complete, an "UNKNOWN" will be recorded.
+    installable_manager_->RecordAddToHomescreenManifestAndIconTimeout();
+    installable_manager_->GetData(
+        ParamsToPerformInstallableCheck(ideal_icon_size_in_px_,
+                                        minimum_icon_size_in_px_,
+                                        check_webapk_compatibility_),
+        InstallableCallback());
+  } else {
+    installable_manager_->RecordAddToHomescreenInstallabilityTimeout();
+  }
+
   if (check_webapk_compatibility_)
     observer_->OnDidDetermineWebApkCompatibility(false);
   observer_->OnUserTitleAvailable(shortcut_info_.user_title,
@@ -227,6 +248,8 @@
   if (!web_contents())
     return;
 
+  is_waiting_for_manifest_ = false;
+
   if (!data.manifest.IsEmpty()) {
     base::RecordAction(base::UserMetricsAction("webapps.AddShortcut.Manifest"));
     shortcut_info_.UpdateFromManifest(data.manifest);
@@ -241,6 +264,7 @@
     observer_->OnUserTitleAvailable(shortcut_info_.user_title,
                                     shortcut_info_.url);
     data_timeout_timer_.Stop();
+    installable_manager_->RecordAddToHomescreenNoTimeout();
     FetchFavicon();
     return;
   }
@@ -263,7 +287,9 @@
   }
 
   installable_manager_->GetData(
-      ParamsToPerformInstallableCheck(check_webapk_compatibility_),
+      ParamsToPerformInstallableCheck(ideal_icon_size_in_px_,
+                                      minimum_icon_size_in_px_,
+                                      check_webapk_compatibility_),
       base::Bind(&AddToHomescreenDataFetcher::OnDidPerformInstallableCheck,
                  weak_ptr_factory_.GetWeakPtr()));
 }
@@ -275,6 +301,8 @@
   if (!web_contents())
     return;
 
+  installable_manager_->RecordAddToHomescreenNoTimeout();
+
   bool webapk_compatible = false;
   if (check_webapk_compatibility_) {
     webapk_compatible =
diff --git a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.h b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.h
index 7916c883..a0115307 100644
--- a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.h
+++ b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.h
@@ -120,6 +120,7 @@
   // Indicates whether to check WebAPK compatibility.
   bool check_webapk_compatibility_;
   bool is_waiting_for_web_application_info_;
+  bool is_waiting_for_manifest_;
 
   base::WeakPtrFactory<AddToHomescreenDataFetcher> weak_ptr_factory_;
 
diff --git a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher_unittest.cc b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher_unittest.cc
index cd8487b7..4e048b8 100644
--- a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher_unittest.cc
+++ b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher_unittest.cc
@@ -6,7 +6,9 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 
+#include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
@@ -14,7 +16,9 @@
 #include "base/strings/nullable_string16.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/histogram_tester.h"
 #include "chrome/browser/installable/installable_manager.h"
+#include "chrome/browser/installable/installable_metrics.h"
 #include "chrome/common/web_application_info.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
@@ -133,12 +137,12 @@
   explicit TestInstallableManager(content::WebContents* web_contents)
       : InstallableManager(web_contents) {}
 
+  // Mock out the GetData API so we can control exactly what is returned to the
+  // data fetcher. We manually call the metrics logging method which is normally
+  // called by the superclass method.
   void GetData(const InstallableParams& params,
                const InstallableCallback& callback) override {
-    if (should_manifest_time_out_ ||
-        (params.check_installable && should_installable_time_out_)) {
-      return;
-    }
+    metrics_->Start();
 
     InstallableStatusCode code = NO_ERROR_DETECTED;
     bool is_installable = is_installable_;
@@ -155,6 +159,21 @@
       }
     }
 
+    if (should_manifest_time_out_ ||
+        (params.check_installable && should_installable_time_out_)) {
+      // Bind the metrics resolution callback. We want to test when this is
+      // and isn't called (corresponding to InstallableManager finishing work
+      // after the timeout, and when it never finishes at all).
+      queued_metrics_callback_ =
+          base::Bind(&InstallableManager::ResolveMetrics,
+                     base::Unretained(this), params, is_installable);
+      return;
+    }
+
+    // Otherwise, directly call the metrics finalisation.
+    if (params.check_installable && is_installable)
+      ResolveMetrics(params, is_installable);
+
     callback.Run(
         {code, GURL(kDefaultManifestUrl), manifest_,
          params.fetch_valid_primary_icon ? primary_icon_url_ : GURL(),
@@ -188,6 +207,8 @@
     should_installable_time_out_ = should_time_out;
   }
 
+  void ResolveQueuedMetrics() { std::move(queued_metrics_callback_).Run(); }
+
  private:
   content::Manifest manifest_;
   GURL primary_icon_url_;
@@ -195,6 +216,8 @@
   std::unique_ptr<SkBitmap> primary_icon_;
   std::unique_ptr<SkBitmap> badge_icon_;
 
+  base::OnceClosure queued_metrics_callback_;
+
   bool is_installable_ = true;
 
   bool should_manifest_time_out_ = false;
@@ -269,6 +292,14 @@
                is_webapk_compatible);
   }
 
+  void CheckHistograms(
+      base::HistogramTester& histograms,
+      AddToHomescreenTimeoutStatus expected_status_for_histogram) {
+    histograms.ExpectUniqueSample(
+        "Webapp.InstallabilityCheckStatus.AddToHomescreenTimeout",
+        static_cast<int>(expected_status_for_histogram), 1);
+  }
+
   void SetManifest(const content::Manifest& manifest) {
     installable_manager_->SetManifest(manifest);
   }
@@ -285,6 +316,8 @@
     installable_manager_->SetShouldInstallableTimeOut(should_time_out);
   }
 
+  void ResolveQueuedMetrics() { installable_manager_->ResolveQueuedMetrics(); }
+
   virtual bool check_webapk_compatibility() { return true; }
 
  private:
@@ -318,10 +351,14 @@
 
 TEST_P(AddToHomescreenDataFetcherTestCommon, EmptyManifest) {
   // Check that an empty manifest has the appropriate methods run.
+  base::HistogramTester histograms;
   ObserverWaiter waiter;
   std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
   RunFetcher(fetcher.get(), waiter, kWebApplicationInfoTitle,
              blink::kWebDisplayModeBrowser, false);
+  CheckHistograms(
+      histograms,
+      AddToHomescreenTimeoutStatus::NO_TIMEOUT_NON_PROGRESSIVE_WEB_APP);
 }
 
 TEST_P(AddToHomescreenDataFetcherTestCommon, NoIconManifest) {
@@ -331,49 +368,155 @@
   manifest.icons.clear();
   SetManifest(manifest);
 
+  base::HistogramTester histograms;
   ObserverWaiter waiter;
   std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
   RunFetcher(fetcher.get(), waiter, kDefaultManifestShortName,
              blink::kWebDisplayModeStandalone, false);
+  CheckHistograms(
+      histograms,
+      AddToHomescreenTimeoutStatus::NO_TIMEOUT_NON_PROGRESSIVE_WEB_APP);
 
   EXPECT_TRUE(fetcher->shortcut_info().best_primary_icon_url.is_empty());
   EXPECT_TRUE(fetcher->badge_icon().drawsNothing());
   EXPECT_TRUE(fetcher->shortcut_info().best_badge_icon_url.is_empty());
 }
 
-TEST_P(AddToHomescreenDataFetcherTestCommon, ManifestFetchTimesOut) {
-  // Check that the AddToHomescreenDataFetcher::Observer methods are called
-  // if the first call to InstallableManager::GetData() times out. This should
-  // fall back to the metadata title and have a non-empty icon (taken from the
-  // favicon).
+// Check that the AddToHomescreenDataFetcher::Observer methods are called
+// if the first call to InstallableManager::GetData() times out. This should
+// fall back to the metadata title and have a non-empty icon (taken from the
+// favicon).
+TEST_F(AddToHomescreenDataFetcherTest, ManifestFetchTimesOutPwa) {
   SetShouldManifestTimeOut(true);
   SetManifest(BuildDefaultManifest());
 
-  // Check a site with no offline-capable service worker.
-  SetInstallable(false);
+  // Check a site where InstallableManager finishes working after the time out
+  // and determines PWA-ness. This is only relevant when checking WebAPK
+  // compatibility.
+  base::HistogramTester histograms;
   ObserverWaiter waiter;
-  std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
+  std::unique_ptr<AddToHomescreenDataFetcher> fetcher =
+      BuildFetcher(true, &waiter);
   RunFetcher(fetcher.get(), waiter, kWebApplicationInfoTitle,
              blink::kWebDisplayModeBrowser, false);
+  ResolveQueuedMetrics();
+  CheckHistograms(
+      histograms,
+      AddToHomescreenTimeoutStatus::TIMEOUT_MANIFEST_FETCH_PROGRESSIVE_WEB_APP);
 
   EXPECT_FALSE(fetcher->primary_icon().drawsNothing());
   EXPECT_TRUE(fetcher->shortcut_info().best_primary_icon_url.is_empty());
 }
 
-TEST_F(AddToHomescreenDataFetcherTest, ServiceWorkerCheckTimesOut) {
-  // Check that the AddToHomescreenDataFetcher::Observer methods are called if
-  // the service worker check times out on a page that is installable (i.e. it's
-  // taken too long). This should use the short_name and icon from the manifest,
-  // but not be WebAPK-compatible. Only relevant when checking WebAPK
-  // compatibility.
+TEST_P(AddToHomescreenDataFetcherTestCommon, ManifestFetchTimesOutNonPwa) {
+  SetShouldManifestTimeOut(true);
+  SetManifest(BuildDefaultManifest());
+  SetInstallable(false);
+
+  // Check where InstallableManager finishes working after the time out and
+  // determines non-PWA-ness.
+  base::HistogramTester histograms;
+  ObserverWaiter waiter;
+  std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
+  RunFetcher(fetcher.get(), waiter, kWebApplicationInfoTitle,
+             blink::kWebDisplayModeBrowser, false);
+  ResolveQueuedMetrics();
+  CheckHistograms(histograms,
+                  AddToHomescreenTimeoutStatus::
+                      TIMEOUT_MANIFEST_FETCH_NON_PROGRESSIVE_WEB_APP);
+
+  EXPECT_FALSE(fetcher->primary_icon().drawsNothing());
+  EXPECT_TRUE(fetcher->shortcut_info().best_primary_icon_url.is_empty());
+}
+
+TEST_P(AddToHomescreenDataFetcherTestCommon, ManifestFetchTimesOutUnknown) {
+  SetShouldManifestTimeOut(true);
+  SetShouldInstallableTimeOut(true);
+  SetManifest(BuildDefaultManifest());
+
+  // Check where InstallableManager doesn't finish working after the time out.
+  base::HistogramTester histograms;
+  ObserverWaiter waiter;
+  std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
+  RunFetcher(fetcher.get(), waiter, kWebApplicationInfoTitle,
+             blink::kWebDisplayModeBrowser, false);
+  NavigateAndCommit(GURL("about:blank"));
+  CheckHistograms(histograms,
+                  AddToHomescreenTimeoutStatus::TIMEOUT_MANIFEST_FETCH_UNKNOWN);
+
+  EXPECT_FALSE(fetcher->primary_icon().drawsNothing());
+  EXPECT_TRUE(fetcher->shortcut_info().best_primary_icon_url.is_empty());
+}
+
+// Check that the AddToHomescreenDataFetcher::Observer methods are called if
+// the service worker check times out on a page that is installable (i.e. it's
+// taken too long). This should use the short_name and icon from the manifest,
+// but not be WebAPK-compatible. Only relevant when checking WebAPK
+// compatibility.
+TEST_F(AddToHomescreenDataFetcherTest, ServiceWorkerCheckTimesOutPwa) {
   SetManifest(BuildDefaultManifest());
   SetShouldInstallableTimeOut(true);
 
+  // Check where InstallableManager finishes working after the timeout and
+  // determines PWA-ness.
+  base::HistogramTester histograms;
   ObserverWaiter waiter;
   std::unique_ptr<AddToHomescreenDataFetcher> fetcher =
       BuildFetcher(true, &waiter);
   RunFetcher(fetcher.get(), waiter, kDefaultManifestShortName,
              blink::kWebDisplayModeStandalone, false);
+  ResolveQueuedMetrics();
+  CheckHistograms(histograms,
+                  AddToHomescreenTimeoutStatus::
+                      TIMEOUT_INSTALLABILITY_CHECK_PROGRESSIVE_WEB_APP);
+
+  EXPECT_FALSE(fetcher->primary_icon().drawsNothing());
+  EXPECT_EQ(fetcher->shortcut_info().best_primary_icon_url,
+            GURL(kDefaultIconUrl));
+}
+
+TEST_F(AddToHomescreenDataFetcherTest, ServiceWorkerCheckTimesOutNonPwa) {
+  SetManifest(BuildDefaultManifest());
+  SetShouldInstallableTimeOut(true);
+  SetInstallable(false);
+
+  // Check where InstallableManager finishes working after the timeout and
+  // determines non-PWA-ness.
+  base::HistogramTester histograms;
+  ObserverWaiter waiter;
+  std::unique_ptr<AddToHomescreenDataFetcher> fetcher =
+      BuildFetcher(true, &waiter);
+  RunFetcher(fetcher.get(), waiter, kDefaultManifestShortName,
+             blink::kWebDisplayModeStandalone, false);
+  ResolveQueuedMetrics();
+  CheckHistograms(histograms,
+                  AddToHomescreenTimeoutStatus::
+                      TIMEOUT_INSTALLABILITY_CHECK_NON_PROGRESSIVE_WEB_APP);
+
+  EXPECT_FALSE(fetcher->primary_icon().drawsNothing());
+  EXPECT_EQ(fetcher->shortcut_info().best_primary_icon_url,
+            GURL(kDefaultIconUrl));
+}
+
+TEST_F(AddToHomescreenDataFetcherTest, ServiceWorkerCheckTimesOutUnknown) {
+  SetManifest(BuildDefaultManifest());
+  SetShouldInstallableTimeOut(true);
+  SetInstallable(false);
+
+  // Check where InstallableManager doesn't finish working after the timeout.
+  // This is akin to waiting for a service worker forever.
+  base::HistogramTester histograms;
+  ObserverWaiter waiter;
+  std::unique_ptr<AddToHomescreenDataFetcher> fetcher =
+      BuildFetcher(true, &waiter);
+  RunFetcher(fetcher.get(), waiter, kDefaultManifestShortName,
+             blink::kWebDisplayModeStandalone, false);
+
+  // Navigate to ensure the histograms are written.
+  NavigateAndCommit(GURL("about:blank"));
+  CheckHistograms(
+      histograms,
+      AddToHomescreenTimeoutStatus::TIMEOUT_INSTALLABILITY_CHECK_UNKNOWN);
 
   EXPECT_FALSE(fetcher->primary_icon().drawsNothing());
   EXPECT_EQ(fetcher->shortcut_info().best_primary_icon_url,
@@ -385,6 +528,7 @@
   content::Manifest manifest(BuildDefaultManifest());
   SetManifest(manifest);
 
+  base::HistogramTester histograms;
   ObserverWaiter waiter;
   std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
   RunFetcher(fetcher.get(), waiter, kDefaultManifestShortName,
@@ -402,9 +546,15 @@
     EXPECT_FALSE(fetcher->badge_icon().drawsNothing());
     EXPECT_EQ(fetcher->shortcut_info().best_badge_icon_url,
               GURL(kDefaultIconUrl));
+    CheckHistograms(
+        histograms,
+        AddToHomescreenTimeoutStatus::NO_TIMEOUT_PROGRESSIVE_WEB_APP);
   } else {
     EXPECT_TRUE(fetcher->badge_icon().drawsNothing());
     EXPECT_TRUE(fetcher->shortcut_info().best_badge_icon_url.is_empty());
+    CheckHistograms(
+        histograms,
+        AddToHomescreenTimeoutStatus::NO_TIMEOUT_NON_PROGRESSIVE_WEB_APP);
   }
 }
 
diff --git a/chrome/browser/background_fetch/OWNERS b/chrome/browser/background_fetch/OWNERS
new file mode 100644
index 0000000..1b61f56b
--- /dev/null
+++ b/chrome/browser/background_fetch/OWNERS
@@ -0,0 +1 @@
+file://content/browser/background_fetch/OWNERS
diff --git a/chrome/browser/background_fetch/background_fetch_delegate_factory.cc b/chrome/browser/background_fetch/background_fetch_delegate_factory.cc
new file mode 100644
index 0000000..137e4b40
--- /dev/null
+++ b/chrome/browser/background_fetch/background_fetch_delegate_factory.cc
@@ -0,0 +1,34 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/background_fetch/background_fetch_delegate_factory.h"
+
+#include "chrome/browser/background_fetch/background_fetch_delegate_impl.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "content/public/browser/background_fetch_delegate.h"
+
+// static
+BackgroundFetchDelegateImpl* BackgroundFetchDelegateFactory::GetForProfile(
+    Profile* profile) {
+  return static_cast<BackgroundFetchDelegateImpl*>(
+      GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+// static
+BackgroundFetchDelegateFactory* BackgroundFetchDelegateFactory::GetInstance() {
+  return base::Singleton<BackgroundFetchDelegateFactory>::get();
+}
+
+BackgroundFetchDelegateFactory::BackgroundFetchDelegateFactory()
+    : BrowserContextKeyedServiceFactory(
+          "BackgroundFetchService",
+          BrowserContextDependencyManager::GetInstance()) {}
+
+BackgroundFetchDelegateFactory::~BackgroundFetchDelegateFactory() {}
+
+KeyedService* BackgroundFetchDelegateFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  return new BackgroundFetchDelegateImpl(Profile::FromBrowserContext(context));
+}
diff --git a/chrome/browser/background_fetch/background_fetch_delegate_factory.h b/chrome/browser/background_fetch/background_fetch_delegate_factory.h
new file mode 100644
index 0000000..eff2307
--- /dev/null
+++ b/chrome/browser/background_fetch/background_fetch_delegate_factory.h
@@ -0,0 +1,36 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_DELEGATE_FACTORY_H_
+#define CHROME_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_DELEGATE_FACTORY_H_
+
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class BackgroundFetchDelegateImpl;
+class Profile;
+
+class BackgroundFetchDelegateFactory
+    : public BrowserContextKeyedServiceFactory {
+ public:
+  static BackgroundFetchDelegateImpl* GetForProfile(Profile* profile);
+  static BackgroundFetchDelegateFactory* GetInstance();
+
+ private:
+  friend struct base::DefaultSingletonTraits<BackgroundFetchDelegateFactory>;
+
+  BackgroundFetchDelegateFactory();
+  ~BackgroundFetchDelegateFactory() override;
+
+  // BrowserContextKeyedBaseFactory methods:
+  // TODO(crbug.com/766082): Override GetBrowserContextToUse to handle Incognito
+  // mode.
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+
+  DISALLOW_COPY_AND_ASSIGN(BackgroundFetchDelegateFactory);
+};
+
+#endif  // CHROME_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_DELEGATE_FACTORY_H_
diff --git a/content/browser/background_fetch/background_fetch_delegate_impl.cc b/chrome/browser/background_fetch/background_fetch_delegate_impl.cc
similarity index 65%
rename from content/browser/background_fetch/background_fetch_delegate_impl.cc
rename to chrome/browser/background_fetch/background_fetch_delegate_impl.cc
index cca2031..cbce1de1 100644
--- a/content/browser/background_fetch/background_fetch_delegate_impl.cc
+++ b/chrome/browser/background_fetch/background_fetch_delegate_impl.cc
@@ -2,14 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/background_fetch/background_fetch_delegate_impl.h"
+#include "chrome/browser/background_fetch/background_fetch_delegate_impl.h"
 
 #include <utility>
 
-#include "base/memory/ptr_util.h"
 #include "build/build_config.h"
-#include "content/browser/background_fetch/background_fetch_response.h"
-#include "content/public/browser/browser_context.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/background_fetch_response.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_item.h"
 #include "content/public/browser/download_manager.h"
@@ -21,20 +20,18 @@
 #include "base/files/file_path.h"
 #endif
 
-namespace content {
-
 namespace {
 
-class DownloadItemObserver : public DownloadItem::Observer {
+class DownloadItemObserver : public content::DownloadItem::Observer {
  public:
   explicit DownloadItemObserver(
-      base::WeakPtr<BackgroundFetchDelegate::Client> client)
+      base::WeakPtr<content::BackgroundFetchDelegate::Client> client)
       : client_(client) {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   }
 
-  void OnDownloadUpdated(DownloadItem* download_item) override {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  void OnDownloadUpdated(content::DownloadItem* download_item) override {
+    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
     if (!client_.get()) {
       download_item->RemoveObserver(this);
@@ -43,41 +40,41 @@
     }
 
     switch (download_item->GetState()) {
-      case DownloadItem::DownloadState::COMPLETE:
+      case content::DownloadItem::DownloadState::COMPLETE:
         client_->OnDownloadComplete(
             download_item->GetGuid(),
-            base::MakeUnique<BackgroundFetchResult>(
+            std::make_unique<content::BackgroundFetchResult>(
                 download_item->GetEndTime(), download_item->GetTargetFilePath(),
                 download_item->GetReceivedBytes()));
         download_item->RemoveObserver(this);
         delete this;
         // Cannot access this after deleting itself so return immediately.
         return;
-      case DownloadItem::DownloadState::CANCELLED:
+      case content::DownloadItem::DownloadState::CANCELLED:
         // TODO(delphick): Consider how we want to handle cancelled downloads.
         break;
-      case DownloadItem::DownloadState::INTERRUPTED:
+      case content::DownloadItem::DownloadState::INTERRUPTED:
         // TODO(delphick): Just update the notification that it is paused.
         break;
-      case DownloadItem::DownloadState::IN_PROGRESS:
+      case content::DownloadItem::DownloadState::IN_PROGRESS:
         // TODO(delphick): If the download was previously paused, this should
         // now unpause the notification.
         break;
-      case DownloadItem::DownloadState::MAX_DOWNLOAD_STATE:
+      case content::DownloadItem::DownloadState::MAX_DOWNLOAD_STATE:
         NOTREACHED();
         break;
     }
   }
 
-  void OnDownloadDestroyed(DownloadItem* download_item) override {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  void OnDownloadDestroyed(content::DownloadItem* download_item) override {
+    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
     download_item->RemoveObserver(this);
     delete this;
   }
 
  private:
-  base::WeakPtr<BackgroundFetchDelegate::Client> client_;
+  base::WeakPtr<content::BackgroundFetchDelegate::Client> client_;
 };
 
 #if defined(OS_ANDROID)
@@ -88,29 +85,36 @@
 
 }  // namespace
 
-BackgroundFetchDelegateImpl::BackgroundFetchDelegateImpl(
-    BrowserContext* browser_context)
-    : browser_context_(browser_context), weak_ptr_factory_(this) {}
+BackgroundFetchDelegateImpl::BackgroundFetchDelegateImpl(Profile* profile)
+    : profile_(profile), weak_ptr_factory_(this) {}
 
 BackgroundFetchDelegateImpl::~BackgroundFetchDelegateImpl() {}
 
+void BackgroundFetchDelegateImpl::Shutdown() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  if (client()) {
+    client()->OnDelegateShutdown();
+  }
+}
+
 void BackgroundFetchDelegateImpl::DownloadUrl(
     const std::string& guid,
     const std::string& method,
     const GURL& url,
     const net::NetworkTrafficAnnotationTag& traffic_annotation,
     const net::HttpRequestHeaders& headers) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  DownloadManager* download_manager =
-      BrowserContext::GetDownloadManager(browser_context_);
+  content::DownloadManager* download_manager =
+      content::BrowserContext::GetDownloadManager(profile_);
   DCHECK(download_manager);
 
-  StoragePartition* storage_partition =
-      BrowserContext::GetStoragePartitionForSite(browser_context_, url);
+  content::StoragePartition* storage_partition =
+      content::BrowserContext::GetStoragePartitionForSite(profile_, url);
 
-  std::unique_ptr<DownloadUrlParameters> download_parameters(
-      base::MakeUnique<DownloadUrlParameters>(
+  std::unique_ptr<content::DownloadUrlParameters> download_parameters(
+      std::make_unique<content::DownloadUrlParameters>(
           url, storage_partition->GetURLRequestContext(), traffic_annotation));
 
   net::HttpRequestHeaders::Iterator iterator(headers);
@@ -144,14 +148,14 @@
 }
 
 void BackgroundFetchDelegateImpl::DidStartRequest(
-    DownloadItem* download_item,
-    DownloadInterruptReason interrupt_reason) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+    content::DownloadItem* download_item,
+    content::DownloadInterruptReason interrupt_reason) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   // TODO(peter): These two DCHECKs are assumptions our implementation
   // currently makes, but are not fit for production. We need to handle such
   // failures gracefully.
-  DCHECK_EQ(interrupt_reason, DOWNLOAD_INTERRUPT_REASON_NONE);
+  DCHECK_EQ(interrupt_reason, content::DOWNLOAD_INTERRUPT_REASON_NONE);
   DCHECK(download_item);
 
   // Register for updates on the download's progress.
@@ -159,8 +163,6 @@
 
   client()->OnDownloadStarted(
       download_item->GetGuid(),
-      base::MakeUnique<BackgroundFetchResponse>(
+      std::make_unique<content::BackgroundFetchResponse>(
           download_item->GetUrlChain(), download_item->GetResponseHeaders()));
 }
-
-}  // namespace content
diff --git a/chrome/browser/background_fetch/background_fetch_delegate_impl.h b/chrome/browser/background_fetch/background_fetch_delegate_impl.h
new file mode 100644
index 0000000..f1d0957
--- /dev/null
+++ b/chrome/browser/background_fetch/background_fetch_delegate_impl.h
@@ -0,0 +1,53 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_DELEGATE_IMPL_H_
+#define CHROME_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_DELEGATE_IMPL_H_
+
+#include <memory>
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "content/public/browser/background_fetch_delegate.h"
+#include "content/public/browser/download_interrupt_reasons.h"
+
+namespace content {
+class DownloadItem;
+}  // namespace content
+
+class Profile;
+
+// Implementation of BackgroundFetchDelegate using the legacy DownloadManager.
+class BackgroundFetchDelegateImpl : public content::BackgroundFetchDelegate,
+                                    public KeyedService {
+ public:
+  explicit BackgroundFetchDelegateImpl(Profile* profile);
+
+  ~BackgroundFetchDelegateImpl() override;
+
+  // KeyedService implementation:
+  void Shutdown() override;
+
+  // BackgroundFetchDelegate implementation:
+  void DownloadUrl(const std::string& guid,
+                   const std::string& method,
+                   const GURL& url,
+                   const net::NetworkTrafficAnnotationTag& traffic_annotation,
+                   const net::HttpRequestHeaders& headers) override;
+
+  base::WeakPtr<BackgroundFetchDelegateImpl> GetWeakPtr();
+
+ private:
+  void DidStartRequest(content::DownloadItem* download_item,
+                       content::DownloadInterruptReason interrupt_reason);
+
+  Profile* profile_;
+
+  base::WeakPtrFactory<BackgroundFetchDelegateImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(BackgroundFetchDelegateImpl);
+};
+
+#endif  // CHROME_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_DELEGATE_IMPL_H_
diff --git a/chrome/browser/banners/app_banner_manager.cc b/chrome/browser/banners/app_banner_manager.cc
index 425bb1be..546f7d45 100644
--- a/chrome/browser/banners/app_banner_manager.cc
+++ b/chrome/browser/banners/app_banner_manager.cc
@@ -171,6 +171,10 @@
   return InstallableManager::GetMinimumIconSizeInPx();
 }
 
+bool AppBannerManager::HasSufficientEngagement() const {
+  return has_sufficient_engagement_ || IsDebugMode();
+}
+
 bool AppBannerManager::IsDebugMode() const {
   return triggered_by_devtools_ ||
          base::CommandLine::ForCurrentProcess()->HasSwitch(
@@ -251,7 +255,7 @@
   // we don't have enough engagement yet. If that's the case, return here but
   // don't call Stop(). We wait for OnEngagementIncreased to tell us that we
   // should trigger.
-  if (!has_sufficient_engagement_) {
+  if (!HasSufficientEngagement()) {
     UpdateState(State::PENDING_ENGAGEMENT);
     return;
   }
@@ -386,11 +390,9 @@
   load_finished_ = true;
   validated_url_ = validated_url;
 
-  // If the bypass flag is on, or if we require no engagement to trigger the
-  // banner, the rest of the banner pipeline should operate as if the engagement
-  // threshold has been met.
-  // Additionally, if the page already has enough engagement, trigger the
-  // pipeline immediately.
+  // If we already have enough engagement, or require no engagement to trigger
+  // the banner, the rest of the banner pipeline should operate as if the
+  // engagement threshold has been met.
   if (AppBannerSettingsHelper::HasSufficientEngagement(0) ||
       AppBannerSettingsHelper::HasSufficientEngagement(
           GetSiteEngagementService()->GetScore(validated_url))) {
diff --git a/chrome/browser/banners/app_banner_manager.h b/chrome/browser/banners/app_banner_manager.h
index 738a38ca..947e7ea6 100644
--- a/chrome/browser/banners/app_banner_manager.h
+++ b/chrome/browser/banners/app_banner_manager.h
@@ -152,6 +152,10 @@
   virtual int GetIdealPrimaryIconSizeInPx();
   virtual int GetMinimumPrimaryIconSizeInPx();
 
+  // Returns true if |has_sufficient_engagement_| is true or IsDebugMode()
+  // returns true.
+  bool HasSufficientEngagement() const;
+
   // Returns true if |triggered_by_devtools_| is true or the
   // kBypassAppBannerEngagementChecks flag is set.
   virtual bool IsDebugMode() const;
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index e28cf5b..1e251f0 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -266,6 +266,8 @@
             <include name="IDR_MD_BOOKMARKS_COMMAND_MANAGER_JS" file="resources\md_bookmarks\command_manager.js" type="BINDATA" />
             <include name="IDR_MD_BOOKMARKS_CONSTANTS_HTML" file="resources\md_bookmarks\constants.html" type="BINDATA" />
             <include name="IDR_MD_BOOKMARKS_CONSTANTS_JS" file="resources\md_bookmarks\constants.js" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_DEBOUNCER_HTML" file="resources\md_bookmarks\debouncer.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_DEBOUNCER_JS" file="resources\md_bookmarks\debouncer.js" type="BINDATA" />
             <include name="IDR_MD_BOOKMARKS_DIALOG_FOCUS_MANAGER_HTML" file="resources\md_bookmarks\dialog_focus_manager.html" type="BINDATA" />
             <include name="IDR_MD_BOOKMARKS_DIALOG_FOCUS_MANAGER_JS" file="resources\md_bookmarks\dialog_focus_manager.js" type="BINDATA" />
             <include name="IDR_MD_BOOKMARKS_DND_CHIP_HTML" file="resources\md_bookmarks\dnd_chip.html" type="BINDATA" />
@@ -292,8 +294,6 @@
             <include name="IDR_MD_BOOKMARKS_STORE_CLIENT_JS" file="resources\md_bookmarks\store_client.js" type="BINDATA" />
             <include name="IDR_MD_BOOKMARKS_STORE_HTML" file="resources\md_bookmarks\store.html" type="BINDATA" />
             <include name="IDR_MD_BOOKMARKS_STORE_JS" file="resources\md_bookmarks\store.js" type="BINDATA" />
-            <include name="IDR_MD_BOOKMARKS_TIMER_PROXY_HTML" file="resources\md_bookmarks\timer_proxy.html" type="BINDATA" />
-            <include name="IDR_MD_BOOKMARKS_TIMER_PROXY_JS" file="resources\md_bookmarks\timer_proxy.js" type="BINDATA" />
             <include name="IDR_MD_BOOKMARKS_TOAST_MANAGER_HTML" file="resources\md_bookmarks\toast_manager.html" type="BINDATA" />
             <include name="IDR_MD_BOOKMARKS_TOAST_MANAGER_JS" file="resources\md_bookmarks\toast_manager.js" type="BINDATA" />
             <include name="IDR_MD_BOOKMARKS_TOOLBAR_HTML" file="resources\md_bookmarks\toolbar.html" type="BINDATA" />
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.cc b/chrome/browser/chromeos/arc/arc_session_manager.cc
index ccfc4634..061f722 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager.cc
@@ -39,6 +39,7 @@
 #include "chromeos/cryptohome/cryptohome_parameters.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/session_manager_client.h"
+#include "components/arc/arc_data_remover.h"
 #include "components/arc/arc_prefs.h"
 #include "components/arc/arc_session_runner.h"
 #include "components/arc/arc_util.h"
@@ -409,6 +410,10 @@
     support_host_ = base::MakeUnique<ArcSupportHost>(profile_);
     support_host_->SetErrorDelegate(this);
   }
+  data_remover_ = std::make_unique<ArcDataRemover>(
+      profile_->GetPrefs(),
+      cryptohome::Identification(
+          multi_user_util::GetAccountIdFromProfile(profile_)));
 
   context_ = base::MakeUnique<ArcAuthContext>(profile_);
 
@@ -426,6 +431,7 @@
   enable_requested_ = false;
   ResetArcState();
   arc_session_runner_->OnShutdown();
+  data_remover_.reset();
   if (support_host_) {
     support_host_->SetErrorDelegate(nullptr);
     support_host_->Close();
@@ -674,6 +680,7 @@
 void ArcSessionManager::RequestArcDataRemoval() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(profile_);
+  DCHECK(data_remover_);
 
   // TODO(hidehiko): DCHECK the previous state. This is called for four cases;
   // 1) Supporting managed user initial disabled case (Please see also
@@ -688,11 +695,7 @@
   // STOPPED, then.
   // TODO(hidehiko): Think a way to get rid of 1), too.
 
-  // Just remember the request in persistent data. The actual removal
-  // is done via MaybeStartArcDataRemoval(). On completion (in
-  // OnArcDataRemoved()), this flag should be reset.
-  profile_->GetPrefs()->SetBoolean(prefs::kArcDataRemoveRequested, true);
-
+  data_remover_->Schedule();
   // To support 1) case above, maybe start data removal.
   if (state_ == State::STOPPED) {
     DCHECK(arc_session_runner_->IsStopped());
@@ -938,58 +941,36 @@
 void ArcSessionManager::MaybeStartArcDataRemoval() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(profile_);
+
   // Data removal cannot run in parallel with ARC session.
   // LoginScreen instance does not use data directory, so removing should work.
   DCHECK(arc_session_runner_->IsStopped() ||
          arc_session_runner_->IsLoginScreenInstanceStarting());
   DCHECK_EQ(state_, State::STOPPED);
 
-  // TODO(hidehiko): Extract the implementation of data removal, so that
-  // shutdown can cancel the operation not to call OnArcDataRemoved callback.
-  if (!profile_->GetPrefs()->GetBoolean(prefs::kArcDataRemoveRequested)) {
-    // ARC data removal is not requested. Just move to the next state.
-    MaybeReenableArc();
-    return;
-  }
-
-  VLOG(1) << "Starting ARC data removal";
   state_ = State::REMOVING_DATA_DIR;
-
-  // Remove Play user ID for Active Directory managed devices.
-  profile_->GetPrefs()->SetString(prefs::kArcActiveDirectoryPlayUserId,
-                                  std::string());
-
-  chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->RemoveArcData(
-      cryptohome::Identification(
-          multi_user_util::GetAccountIdFromProfile(profile_)),
-      base::Bind(&ArcSessionManager::OnArcDataRemoved,
-                 weak_ptr_factory_.GetWeakPtr()));
+  data_remover_->Run(base::BindOnce(&ArcSessionManager::OnArcDataRemoved,
+                                    weak_ptr_factory_.GetWeakPtr()));
 }
 
-void ArcSessionManager::OnArcDataRemoved(bool success) {
+void ArcSessionManager::OnArcDataRemoved(base::Optional<bool> result) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  // TODO(khmel): Browser tests may shutdown profile by itself. Update browser
-  // tests and remove this check.
-  if (state() == State::NOT_INITIALIZED)
-    return;
-
   DCHECK_EQ(state_, State::REMOVING_DATA_DIR);
   DCHECK(profile_);
-  DCHECK(profile_->GetPrefs()->GetBoolean(prefs::kArcDataRemoveRequested));
   state_ = State::STOPPED;
 
-  if (success) {
-    VLOG(1) << "ARC data removal successful";
-  } else {
-    LOG(ERROR) << "Request for ARC user data removal failed. "
-               << "See session_manager logs for more details.";
-  }
-  profile_->GetPrefs()->SetBoolean(prefs::kArcDataRemoveRequested, false);
+  if (result.has_value()) {
+    // Remove Play user ID for Active Directory managed devices.
+    profile_->GetPrefs()->SetString(prefs::kArcActiveDirectoryPlayUserId,
+                                    std::string());
 
-  // Regardless of whether it is successfully done or not, notify observers.
-  for (auto& observer : observer_list_)
-    observer.OnArcDataRemoved();
+    // Regardless of whether it is successfully done or not, notify observers.
+    for (auto& observer : observer_list_)
+      observer.OnArcDataRemoved();
+
+    // Note: Currently, we may re-enable ARC even if data removal fails.
+    // We may have to avoid it.
+  }
 
   MaybeReenableArc();
 }
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.h b/chrome/browser/chromeos/arc/arc_session_manager.h
index 6ad96b6..4b645b3 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager.h
+++ b/chrome/browser/chromeos/arc/arc_session_manager.h
@@ -25,6 +25,7 @@
 
 class ArcAndroidManagementChecker;
 class ArcAuthContext;
+class ArcDataRemover;
 class ArcPaiStarter;
 class ArcTermsOfServiceNegotiator;
 enum class ProvisioningResult : int;
@@ -336,7 +337,7 @@
   // If not requested, just skipping the data removal, and moves to
   // MaybeReenableArc() directly.
   void MaybeStartArcDataRemoval();
-  void OnArcDataRemoved(bool success);
+  void OnArcDataRemoved(base::Optional<bool> success);
 
   // On ARC session stopped and/or data removal completion, this is called
   // so that, if necessary, ARC session is restarted.
@@ -373,6 +374,7 @@
   base::OneShotTimer arc_sign_in_timer_;
 
   std::unique_ptr<ArcSupportHost> support_host_;
+  std::unique_ptr<ArcDataRemover> data_remover_;
 
   std::unique_ptr<ArcTermsOfServiceNegotiator> terms_of_service_negotiator_;
 
diff --git a/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc b/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc
index a2a2864..2e6fd54 100644
--- a/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc
+++ b/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc
@@ -28,6 +28,7 @@
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_features.h"
 #include "components/arc/arc_prefs.h"
 #include "components/arc/arc_service_manager.h"
diff --git a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service_unittest.cc b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service_unittest.cc
index 60c792b..bff9f983 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service_unittest.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service_unittest.cc
@@ -13,6 +13,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_cras_audio_client.h"
+#include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_util.h"
 #include "components/arc/test/fake_arc_session.h"
 #include "components/arc/test/fake_voice_interaction_framework_instance.h"
diff --git a/chrome/browser/chromeos/login/screens/reset_screen.cc b/chrome/browser/chromeos/login/screens/reset_screen.cc
index d9cd13b2..f440cacd 100644
--- a/chrome/browser/chromeos/login/screens/reset_screen.cc
+++ b/chrome/browser/chromeos/login/screens/reset_screen.cc
@@ -47,6 +47,8 @@
     "tpm-firmware-update-available";
 constexpr const char kContextKeyIsTPMFirmwareUpdateChecked[] =
     "tpm-firmware-update-checked";
+constexpr const char kContextKeyIsTPMFirmwareUpdateEditable[] =
+    "tpm-firmware-update-editable";
 constexpr const char kContextKeyIsConfirmational[] = "is-confirmational-view";
 constexpr const char kContextKeyIsOfficialBuild[] = "is-official-build";
 constexpr const char kContextKeyScreenState[] = "screen-state";
@@ -69,6 +71,7 @@
   context_.SetBoolean(kContextKeyIsRollbackChecked, false);
   context_.SetBoolean(kContextKeyIsTPMFirmwareUpdateAvailable, false);
   context_.SetBoolean(kContextKeyIsTPMFirmwareUpdateChecked, false);
+  context_.SetBoolean(kContextKeyIsTPMFirmwareUpdateEditable, true);
   context_.SetBoolean(kContextKeyIsConfirmational, false);
   context_.SetBoolean(kContextKeyIsOfficialBuild, false);
 #if defined(OFFICIAL_BUILD)
@@ -82,6 +85,13 @@
   DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
 }
 
+// static
+void ResetScreen::RegisterPrefs(PrefRegistrySimple* registry) {
+  registry->RegisterBooleanPref(prefs::kFactoryResetRequested, false);
+  registry->RegisterBooleanPref(prefs::kFactoryResetTPMFirmwareUpdateRequested,
+                                false);
+}
+
 void ResetScreen::Show() {
   if (view_)
     view_->Show();
@@ -128,7 +138,17 @@
   }
 
   PrefService* prefs = g_browser_process->local_state();
-  prefs->SetBoolean(prefs::kFactoryResetRequested, false);
+  bool tpm_firmware_update_requested =
+      prefs->GetBoolean(prefs::kFactoryResetTPMFirmwareUpdateRequested);
+  context_editor.SetBoolean(kContextKeyIsTPMFirmwareUpdateChecked,
+                            tpm_firmware_update_requested);
+  context_editor.SetBoolean(kContextKeyIsTPMFirmwareUpdateEditable,
+                            !tpm_firmware_update_requested);
+
+  // Clear prefs so the reset screen isn't triggered again the next time the
+  // device is about to show the login screen.
+  prefs->ClearPref(prefs::kFactoryResetRequested);
+  prefs->ClearPref(prefs::kFactoryResetTPMFirmwareUpdateRequested);
   prefs->CommitPendingWrite();
 }
 
@@ -206,6 +226,7 @@
 void ResetScreen::OnRestart() {
   PrefService* prefs = g_browser_process->local_state();
   prefs->SetBoolean(prefs::kFactoryResetRequested, true);
+  prefs->ClearPref(prefs::kFactoryResetTPMFirmwareUpdateRequested);
   prefs->CommitPendingWrite();
 
   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart(
diff --git a/chrome/browser/chromeos/login/screens/reset_screen.h b/chrome/browser/chromeos/login/screens/reset_screen.h
index aef88e5..687d3a7 100644
--- a/chrome/browser/chromeos/login/screens/reset_screen.h
+++ b/chrome/browser/chromeos/login/screens/reset_screen.h
@@ -15,6 +15,7 @@
 #include "chrome/browser/chromeos/login/screens/base_screen.h"
 #include "chromeos/dbus/update_engine_client.h"
 
+class PrefRegistrySimple;
 
 namespace chromeos {
 
@@ -31,6 +32,9 @@
   // Called when view is destroyed so there's no dead reference to it.
   void OnViewDestroyed(ResetView* view);
 
+  // Registers Local State preferences.
+  static void RegisterPrefs(PrefRegistrySimple* registry);
+
  private:
   // BaseScreen implementation:
   void Show() override;
diff --git a/chrome/browser/component_updater/pnacl_component_installer.cc b/chrome/browser/component_updater/pnacl_component_installer.cc
index 6ef1327..de3aa385 100644
--- a/chrome/browser/component_updater/pnacl_component_installer.cc
+++ b/chrome/browser/component_updater/pnacl_component_installer.cc
@@ -222,7 +222,9 @@
     const base::FilePath& install_dir,
     std::unique_ptr<base::DictionaryValue> manifest) {
   CheckVersionCompatiblity(version);
-  OverrideDirPnaclComponent(install_dir);
+  base::PostTaskWithTraits(
+      FROM_HERE, {base::TaskPriority::BACKGROUND, base::MayBlock()},
+      base::BindOnce(&OverrideDirPnaclComponent, install_dir));
 }
 
 base::FilePath PnaclComponentInstallerTraits::GetRelativeInstallDir() const {
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 746e00dc..ec9d80a3 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -689,6 +689,10 @@
 const char kHostedAppShimCreationDescription[] =
     "Create app shims on Mac when creating a hosted app.";
 
+const char kHtmlBasedUsernameDetectorName[] = "HTML-based username detector";
+const char kHtmlBasedUsernameDetectorDescription[] =
+    "Use HTML-based username detector for the password manager.";
+
 const char kIconNtpName[] = "Large icons on the New Tab page";
 const char kIconNtpDescription[] =
     "Enable the experimental New Tab page using large icons.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 7f3233a2..67262e3f 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -438,6 +438,9 @@
 extern const char kHostedAppShimCreationName[];
 extern const char kHostedAppShimCreationDescription[];
 
+extern const char kHtmlBasedUsernameDetectorName[];
+extern const char kHtmlBasedUsernameDetectorDescription[];
+
 extern const char kIconNtpName[];
 extern const char kIconNtpDescription[];
 
diff --git a/chrome/browser/installable/installable_manager.cc b/chrome/browser/installable/installable_manager.cc
index e29d7f064..05b3111 100644
--- a/chrome/browser/installable/installable_manager.cc
+++ b/chrome/browser/installable/installable_manager.cc
@@ -83,14 +83,11 @@
 
 InstallableManager::InstallableManager(content::WebContents* web_contents)
     : content::WebContentsObserver(web_contents),
+      metrics_(base::MakeUnique<InstallableMetrics>()),
       manifest_(base::MakeUnique<ManifestProperty>()),
       valid_manifest_(base::MakeUnique<ValidManifestProperty>()),
       worker_(base::MakeUnique<ServiceWorkerProperty>()),
       service_worker_context_(nullptr),
-      page_status_(InstallabilityCheckStatus::NOT_STARTED),
-      menu_open_count_(0),
-      menu_item_add_to_homescreen_count_(0),
-      is_pwa_check_complete_(false),
       weak_factory_(this) {
   // This is null in unit tests.
   if (web_contents) {
@@ -144,61 +141,28 @@
   if (was_active)
     return;
 
-  if (page_status_ == InstallabilityCheckStatus::NOT_STARTED)
-    page_status_ = InstallabilityCheckStatus::NOT_COMPLETED;
-
+  metrics_->Start();
   WorkOnTask();
 }
 
 void InstallableManager::RecordMenuOpenHistogram() {
-  if (is_pwa_check_complete_)
-    InstallableMetrics::RecordMenuOpenHistogram(page_status_);
-  else
-    ++menu_open_count_;
+  metrics_->RecordMenuOpen();
 }
 
 void InstallableManager::RecordMenuItemAddToHomescreenHistogram() {
-  if (is_pwa_check_complete_)
-    InstallableMetrics::RecordMenuItemAddToHomescreenHistogram(page_status_);
-  else
-    ++menu_item_add_to_homescreen_count_;
+  metrics_->RecordMenuItemAddToHomescreen();
 }
 
-void InstallableManager::RecordQueuedMetricsOnTaskCompletion(
-    const InstallableParams& params,
-    bool check_passed) {
-  // Don't do anything if we've:
-  //  - already finished the PWA check, or
-  //  - we passed the check AND it was not for the full PWA params.
-  // In the latter case (i.e. the check passed but we weren't checking
-  // everything), we don't yet know if the site is installable. However, if the
-  // check didn't pass, we know for sure the site isn't installable, regardless
-  // of how much we checked.
-  //
-  // Once a full check is completed, metrics will be directly recorded in
-  // Record*Histogram since |is_pwa_check_complete_| will be true.
-  if (is_pwa_check_complete_ || (check_passed && !IsParamsForPwaCheck(params)))
-    return;
+void InstallableManager::RecordAddToHomescreenNoTimeout() {
+  metrics_->RecordAddToHomescreenNoTimeout();
+}
 
-  is_pwa_check_complete_ = true;
-  page_status_ =
-      check_passed
-          ? InstallabilityCheckStatus::COMPLETE_PROGRESSIVE_WEB_APP
-          : InstallabilityCheckStatus::COMPLETE_NON_PROGRESSIVE_WEB_APP;
+void InstallableManager::RecordAddToHomescreenManifestAndIconTimeout() {
+  metrics_->RecordAddToHomescreenManifestAndIconTimeout();
+}
 
-  // Compute what the status would have been for any queued calls to
-  // Record*Histogram, and record appropriately.
-  InstallabilityCheckStatus prev_status =
-      check_passed
-          ? InstallabilityCheckStatus::IN_PROGRESS_PROGRESSIVE_WEB_APP
-          : InstallabilityCheckStatus::IN_PROGRESS_NON_PROGRESSIVE_WEB_APP;
-  for (; menu_open_count_ > 0; --menu_open_count_)
-    InstallableMetrics::RecordMenuOpenHistogram(prev_status);
-
-  for (; menu_item_add_to_homescreen_count_ > 0;
-       --menu_item_add_to_homescreen_count_) {
-    InstallableMetrics::RecordMenuItemAddToHomescreenHistogram(prev_status);
-  }
+void InstallableManager::RecordAddToHomescreenInstallabilityTimeout() {
+  metrics_->RecordAddToHomescreenInstallabilityTimeout();
 }
 
 InstallableManager::IconParams InstallableManager::ParamsForPrimaryIcon(
@@ -305,27 +269,26 @@
           IsIconFetched(ParamsForBadgeIcon(params)));
 }
 
+void InstallableManager::ResolveMetrics(const InstallableParams& params,
+                                        bool check_passed) {
+  // Don't do anything if we passed the check AND it was not for the full PWA
+  // params. We don't yet know if the site is installable. However, if the check
+  // didn't pass, we know for sure the site isn't installable, regardless of how
+  // much we checked.
+  if (check_passed && !IsParamsForPwaCheck(params))
+    return;
+
+  metrics_->Resolve(check_passed);
+}
+
 void InstallableManager::Reset() {
   // Prevent any outstanding callbacks to or from this object from being called.
   weak_factory_.InvalidateWeakPtrs();
   task_queue_.Reset();
   icons_.clear();
+  metrics_->Flush();
 
-  // We may have reset prior to completion, in which case |menu_open_count_| or
-  // |menu_item_add_to_homescreen_count_| might be nonzero and |page_status_| is
-  // one of NOT_STARTED or NOT_COMPLETED. If we completed, then these values
-  // cannot be anything except 0.
-  is_pwa_check_complete_ = false;
-
-  for (; menu_open_count_ > 0; --menu_open_count_)
-    InstallableMetrics::RecordMenuOpenHistogram(page_status_);
-
-  for (; menu_item_add_to_homescreen_count_ > 0;
-       --menu_item_add_to_homescreen_count_) {
-    InstallableMetrics::RecordMenuItemAddToHomescreenHistogram(page_status_);
-  }
-
-  page_status_ = InstallabilityCheckStatus::NOT_STARTED;
+  metrics_ = base::MakeUnique<InstallableMetrics>();
   manifest_ = base::MakeUnique<ManifestProperty>();
   valid_manifest_ = base::MakeUnique<ValidManifestProperty>();
   worker_ = base::MakeUnique<ServiceWorkerProperty>();
@@ -373,7 +336,7 @@
   InstallableStatusCode code = GetErrorCode(params);
   bool check_passed = (code == NO_ERROR_DETECTED);
   if (!check_passed || IsComplete(params)) {
-    RecordQueuedMetricsOnTaskCompletion(params, check_passed);
+    ResolveMetrics(params, check_passed);
     RunCallback(task, code);
 
     // Sites can always register a service worker after we finish checking, so
diff --git a/chrome/browser/installable/installable_manager.h b/chrome/browser/installable/installable_manager.h
index 7d3873c..6ca06e1 100644
--- a/chrome/browser/installable/installable_manager.h
+++ b/chrome/browser/installable/installable_manager.h
@@ -66,8 +66,13 @@
   // is opened on Android.
   void RecordMenuOpenHistogram();
   void RecordMenuItemAddToHomescreenHistogram();
-  void RecordQueuedMetricsOnTaskCompletion(const InstallableParams& params,
-                                           bool check_passed);
+
+  // Called via AddToHomescreenDataFetcher to record metrics on how often the
+  // installable check is completed before timing out when a user is shown the
+  // add to homescreen dialog for a shortcut or PWA on Android.
+  void RecordAddToHomescreenNoTimeout();
+  void RecordAddToHomescreenManifestAndIconTimeout();
+  void RecordAddToHomescreenInstallabilityTimeout();
 
  protected:
   // For mocking in tests.
@@ -158,6 +163,8 @@
   // Returns true if |params| requires no more work to be done.
   bool IsComplete(const InstallableParams& params) const;
 
+  void ResolveMetrics(const InstallableParams& params, bool check_passed);
+
   // Resets members to empty and removes all queued tasks.
   // Called when navigating to a new page or if the WebContents is destroyed
   // whilst waiting for a callback.
@@ -198,6 +205,7 @@
   bool is_installable() const;
 
   InstallableTaskQueue task_queue_;
+  std::unique_ptr<InstallableMetrics> metrics_;
 
   // Installable properties cached on this object.
   std::unique_ptr<ManifestProperty> manifest_;
@@ -209,19 +217,6 @@
   // this object is scoped to.
   content::ServiceWorkerContext* service_worker_context_;
 
-  // Whether or not the current page is a PWA. This is reset per navigation and
-  // is independent of the caching mechanism, i.e. if a PWA check is run
-  // multiple times for one page, this will be set on the first check.
-  InstallabilityCheckStatus page_status_;
-
-  // Counts for the number of queued requests of the menu and add to homescreen
-  // menu item there have been whilst the installable check is awaiting
-  // completion. Used for metrics recording.
-  int menu_open_count_;
-  int menu_item_add_to_homescreen_count_;
-
-  bool is_pwa_check_complete_;
-
   base::WeakPtrFactory<InstallableManager> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(InstallableManager);
diff --git a/chrome/browser/installable/installable_manager_browsertest.cc b/chrome/browser/installable/installable_manager_browsertest.cc
index 2fece1f..dbce2ba 100644
--- a/chrome/browser/installable/installable_manager_browsertest.cc
+++ b/chrome/browser/installable/installable_manager_browsertest.cc
@@ -7,8 +7,11 @@
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
+#include "base/test/histogram_tester.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/banners/app_banner_manager_desktop.h"
+#include "chrome/browser/installable/installable_manager.h"
+#include "chrome/browser/installable/installable_metrics.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -217,8 +220,6 @@
 
     return manager;
   }
-
-  InstallabilityCheckStatus GetStatus() { return GetManager()->page_status_; }
 };
 
 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
@@ -240,12 +241,17 @@
 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckNoManifest) {
   // Ensure that a page with no manifest returns the appropriate error and with
   // null fields for everything.
+  base::HistogramTester histograms;
   base::RunLoop run_loop;
   std::unique_ptr<CallbackTester> tester(
       new CallbackTester(run_loop.QuitClosure()));
 
-  NavigateAndRunInstallableManager(tester.get(), GetManifestParams(),
-                                   "/banners/no_manifest_test_page.html");
+  // Navigating resets histogram state, so do it before recording a histogram.
+  ui_test_utils::NavigateToURL(
+      browser(),
+      embedded_test_server()->GetURL("/banners/no_manifest_test_page.html"));
+  GetManager()->RecordMenuOpenHistogram();
+  RunInstallableManager(tester.get(), GetManifestParams());
   run_loop.Run();
 
   // If there is no manifest, everything should be empty.
@@ -257,8 +263,24 @@
   EXPECT_TRUE(tester->badge_icon_url().is_empty());
   EXPECT_EQ(nullptr, tester->badge_icon());
   EXPECT_EQ(NO_MANIFEST, tester->error_code());
-  EXPECT_EQ(GetStatus(),
-            InstallabilityCheckStatus::COMPLETE_NON_PROGRESSIVE_WEB_APP);
+
+  histograms.ExpectUniqueSample(
+      "Webapp.InstallabilityCheckStatus.MenuOpen",
+      static_cast<int>(InstallabilityCheckStatus::NOT_STARTED), 1);
+
+  GetManager()->RecordMenuItemAddToHomescreenHistogram();
+  histograms.ExpectUniqueSample(
+      "Webapp.InstallabilityCheckStatus.MenuItemAddToHomescreen",
+      static_cast<int>(
+          InstallabilityCheckStatus::COMPLETE_NON_PROGRESSIVE_WEB_APP),
+      1);
+
+  GetManager()->RecordAddToHomescreenNoTimeout();
+  histograms.ExpectUniqueSample(
+      "Webapp.InstallabilityCheckStatus.AddToHomescreenTimeout",
+      static_cast<int>(
+          AddToHomescreenTimeoutStatus::NO_TIMEOUT_NON_PROGRESSIVE_WEB_APP),
+      1);
 }
 
 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckManifest404) {
@@ -282,8 +304,6 @@
   EXPECT_TRUE(tester->badge_icon_url().is_empty());
   EXPECT_EQ(nullptr, tester->badge_icon());
   EXPECT_EQ(MANIFEST_EMPTY, tester->error_code());
-  EXPECT_EQ(GetStatus(),
-            InstallabilityCheckStatus::COMPLETE_NON_PROGRESSIVE_WEB_APP);
 }
 
 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckManifestOnly) {
@@ -305,7 +325,6 @@
   EXPECT_TRUE(tester->badge_icon_url().is_empty());
   EXPECT_EQ(nullptr, tester->badge_icon());
   EXPECT_EQ(NO_ERROR_DETECTED, tester->error_code());
-  EXPECT_EQ(GetStatus(), InstallabilityCheckStatus::NOT_COMPLETED);
 }
 
 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
@@ -330,7 +349,6 @@
   EXPECT_TRUE(tester->badge_icon_url().is_empty());
   EXPECT_EQ(nullptr, tester->badge_icon());
   EXPECT_EQ(NO_ERROR_DETECTED, tester->error_code());
-  EXPECT_EQ(GetStatus(), InstallabilityCheckStatus::NOT_COMPLETED);
 }
 
 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
@@ -356,7 +374,6 @@
     EXPECT_TRUE(tester->badge_icon_url().is_empty());
     EXPECT_EQ(nullptr, tester->badge_icon());
     EXPECT_EQ(NO_ERROR_DETECTED, tester->error_code());
-    EXPECT_EQ(GetStatus(), InstallabilityCheckStatus::NOT_COMPLETED);
   }
 
   // Ask for a primary icon (but don't navigate). This should fail with
@@ -379,8 +396,6 @@
     EXPECT_TRUE(tester->badge_icon_url().is_empty());
     EXPECT_EQ(nullptr, tester->badge_icon());
     EXPECT_EQ(NO_ACCEPTABLE_ICON, tester->error_code());
-    EXPECT_EQ(GetStatus(),
-              InstallabilityCheckStatus::COMPLETE_NON_PROGRESSIVE_WEB_APP);
   }
 
   // Ask for everything except badge icon. This should fail with
@@ -404,8 +419,6 @@
     EXPECT_TRUE(tester->badge_icon_url().is_empty());
     EXPECT_EQ(nullptr, tester->badge_icon());
     EXPECT_EQ(NO_ACCEPTABLE_ICON, tester->error_code());
-    EXPECT_EQ(GetStatus(),
-              InstallabilityCheckStatus::COMPLETE_NON_PROGRESSIVE_WEB_APP);
   }
 
   // Do not ask for primary icon. This should fail with START_URL_NOT_VALID.
@@ -429,8 +442,6 @@
     EXPECT_EQ(nullptr, tester->badge_icon());
     EXPECT_FALSE(tester->is_installable());
     EXPECT_EQ(START_URL_NOT_VALID, tester->error_code());
-    EXPECT_EQ(GetStatus(),
-              InstallabilityCheckStatus::COMPLETE_NON_PROGRESSIVE_WEB_APP);
   }
 }
 
@@ -454,7 +465,6 @@
     EXPECT_TRUE(tester->badge_icon_url().is_empty());
     EXPECT_EQ(nullptr, tester->badge_icon());
     EXPECT_EQ(NO_ERROR_DETECTED, tester->error_code());
-    EXPECT_EQ(GetStatus(), InstallabilityCheckStatus::NOT_COMPLETED);
   }
 
   // Add to homescreen checks for manifest + primary icon + badge icon.
@@ -475,7 +485,6 @@
     EXPECT_FALSE(tester->badge_icon_url().is_empty());
     EXPECT_NE(nullptr, tester->badge_icon());
     EXPECT_EQ(NO_ERROR_DETECTED, tester->error_code());
-    EXPECT_EQ(GetStatus(), InstallabilityCheckStatus::NOT_COMPLETED);
   }
 
   // Request an oversized badge icon. This should fetch only the manifest and
@@ -500,7 +509,6 @@
     EXPECT_TRUE(tester->badge_icon_url().is_empty());
     EXPECT_EQ(nullptr, tester->badge_icon());
     EXPECT_EQ(NO_ERROR_DETECTED, tester->error_code());
-    EXPECT_EQ(GetStatus(), InstallabilityCheckStatus::NOT_COMPLETED);
   }
 
   // Navigate to a page with a bad badge icon. This should now fail with
@@ -525,20 +533,23 @@
     EXPECT_EQ(nullptr, tester->badge_icon());
     EXPECT_FALSE(tester->is_installable());
     EXPECT_EQ(NO_ICON_AVAILABLE, tester->error_code());
-    EXPECT_EQ(GetStatus(),
-              InstallabilityCheckStatus::COMPLETE_NON_PROGRESSIVE_WEB_APP);
   }
 }
 
 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckWebapp) {
   // Request everything except badge icon.
   {
+    base::HistogramTester histograms;
     base::RunLoop run_loop;
     std::unique_ptr<CallbackTester> tester(
         new CallbackTester(run_loop.QuitClosure()));
 
-    NavigateAndRunInstallableManager(tester.get(), GetWebAppParams(),
-                                     "/banners/manifest_test_page.html");
+    // Navigating resets histogram state, so do it before recording a histogram.
+    ui_test_utils::NavigateToURL(
+        browser(),
+        embedded_test_server()->GetURL("/banners/manifest_test_page.html"));
+    GetManager()->RecordMenuItemAddToHomescreenHistogram();
+    RunInstallableManager(tester.get(), GetWebAppParams());
     run_loop.Run();
 
     EXPECT_FALSE(tester->manifest().IsEmpty());
@@ -549,8 +560,6 @@
     EXPECT_TRUE(tester->badge_icon_url().is_empty());
     EXPECT_EQ(nullptr, tester->badge_icon());
     EXPECT_EQ(NO_ERROR_DETECTED, tester->error_code());
-    EXPECT_EQ(GetStatus(),
-              InstallabilityCheckStatus::COMPLETE_PROGRESSIVE_WEB_APP);
 
     // Verify that the returned state matches manager internal state.
     InstallableManager* manager = GetManager();
@@ -566,6 +575,24 @@
     EXPECT_EQ(NO_ERROR_DETECTED, manager->worker_error());
     EXPECT_EQ(NO_ERROR_DETECTED, (manager->icon_error(kPrimaryIconParams)));
     EXPECT_TRUE(!manager->task_queue_.HasCurrent());
+
+    histograms.ExpectUniqueSample(
+        "Webapp.InstallabilityCheckStatus.MenuItemAddToHomescreen",
+        static_cast<int>(InstallabilityCheckStatus::NOT_STARTED), 1);
+
+    GetManager()->RecordMenuOpenHistogram();
+    histograms.ExpectUniqueSample(
+        "Webapp.InstallabilityCheckStatus.MenuOpen",
+        static_cast<int>(
+            InstallabilityCheckStatus::COMPLETE_PROGRESSIVE_WEB_APP),
+        1);
+
+    GetManager()->RecordAddToHomescreenNoTimeout();
+    histograms.ExpectUniqueSample(
+        "Webapp.InstallabilityCheckStatus.AddToHomescreenTimeout",
+        static_cast<int>(
+            AddToHomescreenTimeoutStatus::NO_TIMEOUT_PROGRESSIVE_WEB_APP),
+        1);
   }
 
   // Request everything except badge icon again without navigating away. This
@@ -586,8 +613,6 @@
     EXPECT_TRUE(tester->badge_icon_url().is_empty());
     EXPECT_EQ(nullptr, tester->badge_icon());
     EXPECT_EQ(NO_ERROR_DETECTED, tester->error_code());
-    EXPECT_EQ(GetStatus(),
-              InstallabilityCheckStatus::COMPLETE_PROGRESSIVE_WEB_APP);
 
     // Verify that the returned state matches manager internal state.
     InstallableManager* manager = GetManager();
@@ -621,6 +646,50 @@
   }
 }
 
+IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
+                       CheckNavigationWithoutRunning) {
+  // Verify that we record "not started" metrics if we don't run the installable
+  // manager and navigate away.
+  base::HistogramTester histograms;
+  ui_test_utils::NavigateToURL(
+      browser(),
+      embedded_test_server()->GetURL("/banners/no_manifest_test_page.html"));
+
+  InstallableManager* manager = GetManager();
+  manager->RecordMenuOpenHistogram();
+  manager->RecordMenuOpenHistogram();
+  manager->RecordMenuItemAddToHomescreenHistogram();
+  manager->RecordMenuItemAddToHomescreenHistogram();
+  manager->RecordAddToHomescreenManifestAndIconTimeout();
+  manager->RecordAddToHomescreenInstallabilityTimeout();
+  manager->RecordAddToHomescreenInstallabilityTimeout();
+
+  ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
+
+  histograms.ExpectUniqueSample(
+      "Webapp.InstallabilityCheckStatus.MenuOpen",
+      static_cast<int>(InstallabilityCheckStatus::NOT_STARTED), 2);
+
+  histograms.ExpectUniqueSample(
+      "Webapp.InstallabilityCheckStatus.MenuItemAddToHomescreen",
+      static_cast<int>(InstallabilityCheckStatus::NOT_STARTED), 2);
+
+  histograms.ExpectBucketCount(
+      "Webapp.InstallabilityCheckStatus.AddToHomescreenTimeout",
+      static_cast<int>(
+          AddToHomescreenTimeoutStatus::TIMEOUT_MANIFEST_FETCH_UNKNOWN),
+      1);
+
+  histograms.ExpectBucketCount(
+      "Webapp.InstallabilityCheckStatus.AddToHomescreenTimeout",
+      static_cast<int>(
+          AddToHomescreenTimeoutStatus::TIMEOUT_INSTALLABILITY_CHECK_UNKNOWN),
+      2);
+
+  histograms.ExpectTotalCount(
+      "Webapp.InstallabilityCheckStatus.AddToHomescreenTimeout", 3);
+}
+
 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckWebappInIframe) {
   base::RunLoop run_loop;
   std::unique_ptr<CallbackTester> tester(
@@ -640,8 +709,6 @@
   EXPECT_TRUE(tester->badge_icon_url().is_empty());
   EXPECT_EQ(nullptr, tester->badge_icon());
   EXPECT_EQ(NO_MANIFEST, tester->error_code());
-  EXPECT_EQ(GetStatus(),
-            InstallabilityCheckStatus::COMPLETE_NON_PROGRESSIVE_WEB_APP);
 }
 
 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
@@ -666,7 +733,6 @@
     EXPECT_TRUE(tester->badge_icon_url().is_empty());
     EXPECT_EQ(nullptr, tester->badge_icon());
     EXPECT_EQ(NO_ERROR_DETECTED, tester->error_code());
-    EXPECT_EQ(GetStatus(), InstallabilityCheckStatus::NOT_COMPLETED);
   }
 
   // Fetching the full criteria should fail if we don't wait for the worker.
@@ -689,8 +755,6 @@
     EXPECT_TRUE(tester->badge_icon_url().is_empty());
     EXPECT_EQ(nullptr, tester->badge_icon());
     EXPECT_EQ(NO_MATCHING_SERVICE_WORKER, tester->error_code());
-    EXPECT_EQ(GetStatus(),
-              InstallabilityCheckStatus::COMPLETE_NON_PROGRESSIVE_WEB_APP);
   }
 }
 
@@ -768,8 +832,6 @@
   EXPECT_TRUE(tester->badge_icon_url().is_empty());
   EXPECT_EQ(nullptr, tester->badge_icon());
   EXPECT_EQ(NO_ERROR_DETECTED, tester->error_code());
-  EXPECT_EQ(manager->page_status_,
-            InstallabilityCheckStatus::COMPLETE_PROGRESSIVE_WEB_APP);
 
   // Verify internal state.
   EXPECT_FALSE(manager->manifest().IsEmpty());
@@ -827,8 +889,6 @@
   EXPECT_TRUE(tester->badge_icon_url().is_empty());
   EXPECT_EQ(nullptr, tester->badge_icon());
   EXPECT_EQ(NOT_OFFLINE_CAPABLE, tester->error_code());
-  EXPECT_EQ(manager->page_status_,
-            InstallabilityCheckStatus::COMPLETE_NON_PROGRESSIVE_WEB_APP);
 }
 
 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
@@ -908,8 +968,60 @@
   EXPECT_TRUE(tester->badge_icon_url().is_empty());
   EXPECT_EQ(nullptr, tester->badge_icon());
   EXPECT_EQ(NOT_OFFLINE_CAPABLE, tester->error_code());
-  EXPECT_EQ(GetStatus(),
-            InstallabilityCheckStatus::COMPLETE_NON_PROGRESSIVE_WEB_APP);
+}
+
+IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
+                       WaitingForServiceWorkerRecordsNonPwa) {
+  base::RunLoop tester_run_loop, sw_run_loop;
+  base::HistogramTester histograms;
+  std::unique_ptr<CallbackTester> tester(
+      new CallbackTester(tester_run_loop.QuitClosure()));
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  auto manager = base::MakeUnique<LazyWorkerInstallableManager>(
+      web_contents, sw_run_loop.QuitClosure());
+
+  manager->RecordMenuOpenHistogram();
+  manager->RecordMenuItemAddToHomescreenHistogram();
+
+  {
+    // Load a URL with no service worker.
+    GURL test_url = embedded_test_server()->GetURL(
+        "/banners/manifest_no_service_worker.html");
+    ui_test_utils::NavigateToURL(browser(), test_url);
+
+    // Kick off fetching the data. This should block on waiting for a worker.
+    manager->GetData(GetWebAppParams(),
+                     base::Bind(&CallbackTester::OnDidFinishInstallableCheck,
+                                base::Unretained(tester.get())));
+    sw_run_loop.Run();
+  }
+
+  manager->RecordMenuOpenHistogram();
+  manager->RecordMenuItemAddToHomescreenHistogram();
+  manager->RecordMenuItemAddToHomescreenHistogram();
+
+  // Navigate to force metrics recording.
+  ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
+
+  // Expect to record that we didn't finish the check since we waited until
+  // navigation and didn't get a service worker.
+  histograms.ExpectBucketCount(
+      "Webapp.InstallabilityCheckStatus.MenuOpen",
+      static_cast<int>(InstallabilityCheckStatus::NOT_STARTED), 1);
+  histograms.ExpectBucketCount(
+      "Webapp.InstallabilityCheckStatus.MenuOpen",
+      static_cast<int>(InstallabilityCheckStatus::IN_PROGRESS_UNKNOWN), 1);
+  histograms.ExpectTotalCount("Webapp.InstallabilityCheckStatus.MenuOpen", 2);
+  histograms.ExpectBucketCount(
+      "Webapp.InstallabilityCheckStatus.MenuItemAddToHomescreen",
+      static_cast<int>(InstallabilityCheckStatus::NOT_STARTED), 1);
+  histograms.ExpectBucketCount(
+      "Webapp.InstallabilityCheckStatus.MenuItemAddToHomescreen",
+      static_cast<int>(InstallabilityCheckStatus::IN_PROGRESS_UNKNOWN), 2);
+  histograms.ExpectTotalCount(
+      "Webapp.InstallabilityCheckStatus.MenuItemAddToHomescreen", 3);
 }
 
 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckDataUrlIcon) {
@@ -933,8 +1045,6 @@
   EXPECT_TRUE(tester->badge_icon_url().is_empty());
   EXPECT_EQ(nullptr, tester->badge_icon());
   EXPECT_EQ(NO_ERROR_DETECTED, tester->error_code());
-  EXPECT_EQ(GetStatus(),
-            InstallabilityCheckStatus::COMPLETE_PROGRESSIVE_WEB_APP);
 }
 
 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
@@ -958,8 +1068,6 @@
   EXPECT_TRUE(tester->badge_icon_url().is_empty());
   EXPECT_EQ(nullptr, tester->badge_icon());
   EXPECT_EQ(NO_ICON_AVAILABLE, tester->error_code());
-  EXPECT_EQ(GetStatus(),
-            InstallabilityCheckStatus::COMPLETE_NON_PROGRESSIVE_WEB_APP);
 }
 
 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
diff --git a/chrome/browser/installable/installable_metrics.cc b/chrome/browser/installable/installable_metrics.cc
index 82f3b54..51dcfd3 100644
--- a/chrome/browser/installable/installable_metrics.cc
+++ b/chrome/browser/installable/installable_metrics.cc
@@ -4,17 +4,241 @@
 
 #include "chrome/browser/installable/installable_metrics.h"
 
+#include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 
-void InstallableMetrics::RecordMenuOpenHistogram(
-    InstallabilityCheckStatus status) {
-  UMA_HISTOGRAM_ENUMERATION("Webapp.InstallabilityCheckStatus.MenuOpen", status,
-                            InstallabilityCheckStatus::COUNT);
+namespace {
+
+void WriteMenuOpenHistogram(InstallabilityCheckStatus status, int count) {
+  for (int i = 0; i < count; ++i) {
+    UMA_HISTOGRAM_ENUMERATION("Webapp.InstallabilityCheckStatus.MenuOpen",
+                              status, InstallabilityCheckStatus::COUNT);
+  }
 }
 
-void InstallableMetrics::RecordMenuItemAddToHomescreenHistogram(
-    InstallabilityCheckStatus status) {
-  UMA_HISTOGRAM_ENUMERATION(
-      "Webapp.InstallabilityCheckStatus.MenuItemAddToHomescreen", status,
-      InstallabilityCheckStatus::COUNT);
+void WriteMenuItemAddToHomescreenHistogram(InstallabilityCheckStatus status,
+                                           int count) {
+  for (int i = 0; i < count; ++i) {
+    UMA_HISTOGRAM_ENUMERATION(
+        "Webapp.InstallabilityCheckStatus.MenuItemAddToHomescreen", status,
+        InstallabilityCheckStatus::COUNT);
+  }
+}
+
+void WriteAddToHomescreenHistogram(AddToHomescreenTimeoutStatus status,
+                                   int count) {
+  for (int i = 0; i < count; ++i) {
+    UMA_HISTOGRAM_ENUMERATION(
+        "Webapp.InstallabilityCheckStatus.AddToHomescreenTimeout", status,
+        AddToHomescreenTimeoutStatus::COUNT);
+  }
+}
+
+// Buffers metrics calls until we have resolved whether a site is a PWA.
+class AccumulatingRecorder : public InstallableMetrics::Recorder {
+ public:
+  AccumulatingRecorder()
+      : InstallableMetrics::Recorder(),
+        menu_open_count_(0),
+        menu_item_add_to_homescreen_count_(0),
+        add_to_homescreen_manifest_timeout_count_(0),
+        add_to_homescreen_installability_timeout_count_(0),
+        started_(false) {}
+
+  ~AccumulatingRecorder() override {
+    DCHECK_EQ(0, menu_open_count_);
+    DCHECK_EQ(0, menu_item_add_to_homescreen_count_);
+    DCHECK_EQ(0, add_to_homescreen_manifest_timeout_count_);
+    DCHECK_EQ(0, add_to_homescreen_installability_timeout_count_);
+  }
+
+  void Resolve(bool check_passed) override {
+    // Resolve queued metrics to their appropriate state based on whether or not
+    // we passed the installability check.
+    if (check_passed) {
+      WriteMetricsAndResetCounts(
+          InstallabilityCheckStatus::IN_PROGRESS_PROGRESSIVE_WEB_APP,
+          AddToHomescreenTimeoutStatus::
+              TIMEOUT_MANIFEST_FETCH_PROGRESSIVE_WEB_APP,
+          AddToHomescreenTimeoutStatus::
+              TIMEOUT_INSTALLABILITY_CHECK_PROGRESSIVE_WEB_APP);
+    } else {
+      WriteMetricsAndResetCounts(
+          InstallabilityCheckStatus::IN_PROGRESS_NON_PROGRESSIVE_WEB_APP,
+          AddToHomescreenTimeoutStatus::
+              TIMEOUT_MANIFEST_FETCH_NON_PROGRESSIVE_WEB_APP,
+          AddToHomescreenTimeoutStatus::
+              TIMEOUT_INSTALLABILITY_CHECK_NON_PROGRESSIVE_WEB_APP);
+    }
+  }
+
+  void Flush() override {
+    WriteMetricsAndResetCounts(
+        started_ ? InstallabilityCheckStatus::IN_PROGRESS_UNKNOWN
+                 : InstallabilityCheckStatus::NOT_STARTED,
+        AddToHomescreenTimeoutStatus::TIMEOUT_MANIFEST_FETCH_UNKNOWN,
+        AddToHomescreenTimeoutStatus::TIMEOUT_INSTALLABILITY_CHECK_UNKNOWN);
+  }
+
+  void RecordMenuOpen() override {
+    if (started_)
+      ++menu_open_count_;
+    else
+      WriteMenuOpenHistogram(InstallabilityCheckStatus::NOT_STARTED, 1);
+  }
+
+  void RecordMenuItemAddToHomescreen() override {
+    if (started_) {
+      ++menu_item_add_to_homescreen_count_;
+    } else {
+      WriteMenuItemAddToHomescreenHistogram(
+          InstallabilityCheckStatus::NOT_STARTED, 1);
+    }
+  }
+
+  void RecordAddToHomescreenNoTimeout() override {
+    // If this class is instantiated and there is no timeout, we must have
+    // failed the installability check early.
+    WriteAddToHomescreenHistogram(
+        AddToHomescreenTimeoutStatus::NO_TIMEOUT_NON_PROGRESSIVE_WEB_APP, 1);
+  }
+
+  void RecordAddToHomescreenManifestAndIconTimeout() override {
+    ++add_to_homescreen_manifest_timeout_count_;
+  }
+
+  void RecordAddToHomescreenInstallabilityTimeout() override {
+    ++add_to_homescreen_installability_timeout_count_;
+  }
+
+  void Start() override { started_ = true; }
+
+ private:
+  void WriteMetricsAndResetCounts(
+      InstallabilityCheckStatus status,
+      AddToHomescreenTimeoutStatus manifest_status,
+      AddToHomescreenTimeoutStatus installability_status) {
+    WriteMenuOpenHistogram(status, menu_open_count_);
+    WriteMenuItemAddToHomescreenHistogram(status,
+                                          menu_item_add_to_homescreen_count_);
+    WriteAddToHomescreenHistogram(manifest_status,
+                                  add_to_homescreen_manifest_timeout_count_);
+    WriteAddToHomescreenHistogram(
+        installability_status, add_to_homescreen_installability_timeout_count_);
+
+    menu_open_count_ = 0;
+    menu_item_add_to_homescreen_count_ = 0;
+    add_to_homescreen_manifest_timeout_count_ = 0;
+    add_to_homescreen_installability_timeout_count_ = 0;
+  }
+
+  // Counts for the number of queued requests of the menu and add to homescreen
+  // menu item there have been whilst the installable check is running.
+  int menu_open_count_;
+  int menu_item_add_to_homescreen_count_;
+
+  // Counts for the number of times the add to homescreen dialog times out at
+  // different stages of the installable check.
+  int add_to_homescreen_manifest_timeout_count_;
+  int add_to_homescreen_installability_timeout_count_;
+
+  // True if we have started working yet.
+  bool started_;
+};
+
+// Directly writes metrics calls with the current page's PWA status.
+class DirectRecorder : public InstallableMetrics::Recorder {
+ public:
+  explicit DirectRecorder(bool check_passed)
+      : InstallableMetrics::Recorder(),
+        installability_check_status_(
+            check_passed
+                ? InstallabilityCheckStatus::COMPLETE_PROGRESSIVE_WEB_APP
+                : InstallabilityCheckStatus::COMPLETE_NON_PROGRESSIVE_WEB_APP),
+        no_timeout_status_(
+            check_passed
+                ? AddToHomescreenTimeoutStatus::NO_TIMEOUT_PROGRESSIVE_WEB_APP
+                : AddToHomescreenTimeoutStatus::
+                      NO_TIMEOUT_NON_PROGRESSIVE_WEB_APP),
+        manifest_timeout_status_(
+            check_passed ? AddToHomescreenTimeoutStatus::
+                               TIMEOUT_MANIFEST_FETCH_PROGRESSIVE_WEB_APP
+                         : AddToHomescreenTimeoutStatus::
+                               TIMEOUT_MANIFEST_FETCH_NON_PROGRESSIVE_WEB_APP),
+        installability_timeout_status_(
+            check_passed
+                ? AddToHomescreenTimeoutStatus::
+                      TIMEOUT_INSTALLABILITY_CHECK_PROGRESSIVE_WEB_APP
+                : AddToHomescreenTimeoutStatus::
+                      TIMEOUT_INSTALLABILITY_CHECK_NON_PROGRESSIVE_WEB_APP) {}
+
+  ~DirectRecorder() override {}
+
+  void Resolve(bool check_passed) override {}
+  void Flush() override {}
+  void Start() override {}
+  void RecordMenuOpen() override {
+    WriteMenuOpenHistogram(installability_check_status_, 1);
+  }
+
+  void RecordMenuItemAddToHomescreen() override {
+    WriteMenuItemAddToHomescreenHistogram(installability_check_status_, 1);
+  }
+
+  void RecordAddToHomescreenNoTimeout() override {
+    WriteAddToHomescreenHistogram(no_timeout_status_, 1);
+  }
+
+  void RecordAddToHomescreenManifestAndIconTimeout() override {
+    WriteAddToHomescreenHistogram(manifest_timeout_status_, 1);
+  }
+
+  void RecordAddToHomescreenInstallabilityTimeout() override {
+    WriteAddToHomescreenHistogram(installability_timeout_status_, 1);
+  }
+
+  InstallabilityCheckStatus installability_check_status_;
+  AddToHomescreenTimeoutStatus no_timeout_status_;
+  AddToHomescreenTimeoutStatus manifest_timeout_status_;
+  AddToHomescreenTimeoutStatus installability_timeout_status_;
+};
+
+}  // anonymous namespace
+
+InstallableMetrics::InstallableMetrics()
+    : recorder_(base::MakeUnique<AccumulatingRecorder>()) {}
+
+InstallableMetrics::~InstallableMetrics() {}
+
+void InstallableMetrics::RecordMenuOpen() {
+  recorder_->RecordMenuOpen();
+}
+
+void InstallableMetrics::RecordMenuItemAddToHomescreen() {
+  recorder_->RecordMenuItemAddToHomescreen();
+}
+
+void InstallableMetrics::RecordAddToHomescreenNoTimeout() {
+  recorder_->RecordAddToHomescreenNoTimeout();
+}
+
+void InstallableMetrics::RecordAddToHomescreenManifestAndIconTimeout() {
+  recorder_->RecordAddToHomescreenManifestAndIconTimeout();
+}
+
+void InstallableMetrics::RecordAddToHomescreenInstallabilityTimeout() {
+  recorder_->RecordAddToHomescreenInstallabilityTimeout();
+}
+
+void InstallableMetrics::Resolve(bool check_passed) {
+  recorder_->Resolve(check_passed);
+  recorder_ = base::MakeUnique<DirectRecorder>(check_passed);
+}
+
+void InstallableMetrics::Start() {
+  recorder_->Start();
+}
+
+void InstallableMetrics::Flush() {
+  recorder_->Flush();
 }
diff --git a/chrome/browser/installable/installable_metrics.h b/chrome/browser/installable/installable_metrics.h
index 1350660..fe20094 100644
--- a/chrome/browser/installable/installable_metrics.h
+++ b/chrome/browser/installable/installable_metrics.h
@@ -5,10 +5,12 @@
 #ifndef CHROME_BROWSER_INSTALLABLE_INSTALLABLE_METRICS_H_
 #define CHROME_BROWSER_INSTALLABLE_INSTALLABLE_METRICS_H_
 
+#include <memory>
+
 // This enum backs a UMA histogram and must be treated as append-only.
 enum class InstallabilityCheckStatus {
   NOT_STARTED,
-  NOT_COMPLETED,
+  IN_PROGRESS_UNKNOWN,
   IN_PROGRESS_NON_PROGRESSIVE_WEB_APP,
   IN_PROGRESS_PROGRESSIVE_WEB_APP,
   COMPLETE_NON_PROGRESSIVE_WEB_APP,
@@ -16,11 +18,70 @@
   COUNT,
 };
 
+// This enum backs a UMA histogram and must be treated as append-only.
+enum class AddToHomescreenTimeoutStatus {
+  NO_TIMEOUT_NON_PROGRESSIVE_WEB_APP,
+  NO_TIMEOUT_PROGRESSIVE_WEB_APP,
+  TIMEOUT_MANIFEST_FETCH_NON_PROGRESSIVE_WEB_APP,
+  TIMEOUT_MANIFEST_FETCH_PROGRESSIVE_WEB_APP,
+  TIMEOUT_MANIFEST_FETCH_UNKNOWN,
+  TIMEOUT_INSTALLABILITY_CHECK_NON_PROGRESSIVE_WEB_APP,
+  TIMEOUT_INSTALLABILITY_CHECK_PROGRESSIVE_WEB_APP,
+  TIMEOUT_INSTALLABILITY_CHECK_UNKNOWN,
+  COUNT,
+};
+
 class InstallableMetrics {
  public:
-  static void RecordMenuOpenHistogram(InstallabilityCheckStatus status);
-  static void RecordMenuItemAddToHomescreenHistogram(
-      InstallabilityCheckStatus status);
+  class Recorder {
+   public:
+    virtual ~Recorder() {}
+    virtual void Resolve(bool check_passed) {}
+    virtual void Flush() {}
+
+    virtual void RecordMenuOpen() = 0;
+    virtual void RecordMenuItemAddToHomescreen() = 0;
+    virtual void RecordAddToHomescreenNoTimeout() = 0;
+    virtual void RecordAddToHomescreenManifestAndIconTimeout() = 0;
+    virtual void RecordAddToHomescreenInstallabilityTimeout() = 0;
+    virtual void Start() = 0;
+  };
+
+  InstallableMetrics();
+  ~InstallableMetrics();
+
+  // This records the state of the installability check when the Android menu is
+  // opened.
+  void RecordMenuOpen();
+
+  // This records the state of the installability check when the add to
+  // homescreen menu item on Android is tapped.
+  void RecordMenuItemAddToHomescreen();
+
+  // Called to record the add to homescreen dialog not timing out on the
+  // InstallableManager work.
+  void RecordAddToHomescreenNoTimeout();
+
+  // Called to record the add to homescreen dialog timing out during the
+  // manifest and icon fetch.
+  void RecordAddToHomescreenManifestAndIconTimeout();
+
+  // Called to record the add to homescreen dialog timing out on the service
+  // worker + installability check.
+  void RecordAddToHomescreenInstallabilityTimeout();
+
+  // Called to resolve any queued metrics from incomplete tasks.
+  void Resolve(bool check_passed);
+
+  // Called to save any queued metrics.
+  void Flush();
+
+  // Called to indicate that the InstallableManager has started working on the
+  // current page.
+  void Start();
+
+ private:
+  std::unique_ptr<Recorder> recorder_;
 };
 
 #endif  // CHROME_BROWSER_INSTALLABLE_INSTALLABLE_METRICS_H_
diff --git a/chrome/browser/metrics/thread_watcher.cc b/chrome/browser/metrics/thread_watcher.cc
index fce991e..41c3eab 100644
--- a/chrome/browser/metrics/thread_watcher.cc
+++ b/chrome/browser/metrics/thread_watcher.cc
@@ -552,12 +552,8 @@
                 unresponsive_threshold, crash_on_hang_threads);
   StartWatching(BrowserThread::IO, "IO", kSleepTime, kUnresponsiveTime,
                 unresponsive_threshold, crash_on_hang_threads);
-  StartWatching(BrowserThread::DB, "DB", kSleepTime, kUnresponsiveTime,
-                unresponsive_threshold, crash_on_hang_threads);
   StartWatching(BrowserThread::FILE, "FILE", kSleepTime, kUnresponsiveTime,
                 unresponsive_threshold, crash_on_hang_threads);
-  StartWatching(BrowserThread::CACHE, "CACHE", kSleepTime, kUnresponsiveTime,
-                unresponsive_threshold, crash_on_hang_threads);
 }
 
 // static
diff --git a/chrome/browser/metrics/thread_watcher_report_hang.cc b/chrome/browser/metrics/thread_watcher_report_hang.cc
index 9ba6a7f..917eeec 100644
--- a/chrome/browser/metrics/thread_watcher_report_hang.cc
+++ b/chrome/browser/metrics/thread_watcher_report_hang.cc
@@ -50,60 +50,39 @@
   ReportThreadHang();
 }
 
-NOINLINE void ThreadUnresponsive_DB() {
-  volatile int inhibit_comdat = __LINE__;
-  ALLOW_UNUSED_LOCAL(inhibit_comdat);
-  ReportThreadHang();
-}
-
 NOINLINE void ThreadUnresponsive_FILE() {
   volatile int inhibit_comdat = __LINE__;
   ALLOW_UNUSED_LOCAL(inhibit_comdat);
   ReportThreadHang();
 }
 
-NOINLINE void ThreadUnresponsive_FILE_USER_BLOCKING() {
-  volatile int inhibit_comdat = __LINE__;
-  ALLOW_UNUSED_LOCAL(inhibit_comdat);
-  ReportThreadHang();
-}
-
 NOINLINE void ThreadUnresponsive_PROCESS_LAUNCHER() {
   volatile int inhibit_comdat = __LINE__;
   ALLOW_UNUSED_LOCAL(inhibit_comdat);
   ReportThreadHang();
 }
 
-NOINLINE void ThreadUnresponsive_CACHE() {
-  volatile int inhibit_comdat = __LINE__;
-  ALLOW_UNUSED_LOCAL(inhibit_comdat);
-  ReportThreadHang();
-}
-
 NOINLINE void ThreadUnresponsive_IO() {
   volatile int inhibit_comdat = __LINE__;
   ALLOW_UNUSED_LOCAL(inhibit_comdat);
   ReportThreadHang();
 }
 
-NOINLINE void CrashBecauseThreadWasUnresponsive(int thread_id) {
-  // TODO(rtenneti): The following is a temporary change to check thread_id
-  // numbers explicitly so that we will have minimum code. Will change after the
-  // test run to use content::BrowserThread::ID enum.
-  if (thread_id == 0)
-    return ThreadUnresponsive_UI();
-  else if (thread_id == 1)
-    return ThreadUnresponsive_DB();
-  else if (thread_id == 2)
-    return ThreadUnresponsive_FILE();
-  else if (thread_id == 3)
-    return ThreadUnresponsive_FILE_USER_BLOCKING();
-  else if (thread_id == 4)
-    return ThreadUnresponsive_PROCESS_LAUNCHER();
-  else if (thread_id == 5)
-    return ThreadUnresponsive_CACHE();
-  else if (thread_id == 6)
-    return ThreadUnresponsive_IO();
+NOINLINE void CrashBecauseThreadWasUnresponsive(
+    content::BrowserThread::ID thread_id) {
+  switch (thread_id) {
+    case content::BrowserThread::UI:
+      return ThreadUnresponsive_UI();
+    case content::BrowserThread::FILE:
+      return ThreadUnresponsive_FILE();
+    case content::BrowserThread::PROCESS_LAUNCHER:
+      return ThreadUnresponsive_PROCESS_LAUNCHER();
+    case content::BrowserThread::IO:
+      return ThreadUnresponsive_IO();
+    case content::BrowserThread::ID_COUNT:
+      NOTREACHED();
+      break;
+  }
 }
 
 }  // namespace metrics
diff --git a/chrome/browser/metrics/thread_watcher_report_hang.h b/chrome/browser/metrics/thread_watcher_report_hang.h
index 69acc85f..16f82614 100644
--- a/chrome/browser/metrics/thread_watcher_report_hang.h
+++ b/chrome/browser/metrics/thread_watcher_report_hang.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_METRICS_THREAD_WATCHER_REPORT_HANG_H_
 
 #include "base/compiler_specific.h"
+#include "content/public/browser/browser_thread.h"
 
 namespace metrics {
 
@@ -19,7 +20,8 @@
 
 // This function makes it possible to tell from the callstack alone what thread
 // was unresponsive.
-NOINLINE void CrashBecauseThreadWasUnresponsive(int thread_id);
+NOINLINE void CrashBecauseThreadWasUnresponsive(
+    content::BrowserThread::ID thread_id);
 
 }  // namespace metrics
 
diff --git a/chrome/browser/page_load_metrics/observers/protocol_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/protocol_page_load_metrics_observer.cc
index b0e41f09..1702466 100644
--- a/chrome/browser/page_load_metrics/observers/protocol_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/protocol_page_load_metrics_observer.cc
@@ -63,6 +63,7 @@
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_39:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_40:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_41:
+    case net::HttpResponseInfo::CONNECTION_INFO_QUIC_42:
       PAGE_LOAD_HISTOGRAM(
           "PageLoad.Clients.Protocol.QUIC.ParseTiming.NavigationToParseStart",
           timing.parse_timing->parse_start.value());
@@ -116,6 +117,7 @@
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_39:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_40:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_41:
+    case net::HttpResponseInfo::CONNECTION_INFO_QUIC_42:
       PAGE_LOAD_HISTOGRAM(
           "PageLoad.Clients.Protocol.QUIC.PaintTiming."
           "NavigationToFirstContentfulPaint",
@@ -175,6 +177,7 @@
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_39:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_40:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_41:
+    case net::HttpResponseInfo::CONNECTION_INFO_QUIC_42:
       PAGE_LOAD_HISTOGRAM(
           "PageLoad.Clients.Protocol.QUIC.Experimental.PaintTiming."
           "NavigationToFirstMeaningfulPaint",
@@ -224,6 +227,7 @@
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_39:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_40:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_41:
+    case net::HttpResponseInfo::CONNECTION_INFO_QUIC_42:
       PAGE_LOAD_HISTOGRAM(
           "PageLoad.Clients.Protocol.QUIC.DocumentTiming."
           "NavigationToDOMContentLoadedEventFired",
@@ -268,6 +272,7 @@
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_39:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_40:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_41:
+    case net::HttpResponseInfo::CONNECTION_INFO_QUIC_42:
       PAGE_LOAD_HISTOGRAM(
           "PageLoad.Clients.Protocol.QUIC.DocumentTiming."
           "NavigationToLoadEventFired",
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 1d56ca9..0f30e03 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -208,6 +208,7 @@
 #include "chrome/browser/chromeos/login/quick_unlock/pin_storage.h"
 #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h"
 #include "chrome/browser/chromeos/login/saml/saml_offline_signin_limiter.h"
+#include "chrome/browser/chromeos/login/screens/reset_screen.h"
 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
 #include "chrome/browser/chromeos/login/users/avatar/user_image_manager.h"
@@ -238,7 +239,6 @@
 #include "chrome/browser/metrics/chromeos_metrics_provider.h"
 #include "chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.h"
-#include "chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
 #include "chromeos/audio/audio_devices_pref_handler_impl.h"
 #include "chromeos/chromeos_switches.h"
@@ -411,7 +411,7 @@
   chromeos::NetworkThrottlingObserver::RegisterPrefs(registry);
   chromeos::Preferences::RegisterPrefs(registry);
   chromeos::RegisterDisplayLocalStatePrefs(registry);
-  chromeos::ResetScreenHandler::RegisterPrefs(registry);
+  chromeos::ResetScreen::RegisterPrefs(registry);
   chromeos::ResourceReporter::RegisterPrefs(registry);
   chromeos::ServicesCustomizationDocument::RegisterPrefs(registry);
   chromeos::SigninScreenHandler::RegisterPrefs(registry);
diff --git a/chrome/browser/profiles/off_the_record_profile_impl.cc b/chrome/browser/profiles/off_the_record_profile_impl.cc
index 177598f..a2c601f 100644
--- a/chrome/browser/profiles/off_the_record_profile_impl.cc
+++ b/chrome/browser/profiles/off_the_record_profile_impl.cc
@@ -17,6 +17,8 @@
 #include "base/threading/sequenced_worker_pool.h"
 #include "build/build_config.h"
 #include "chrome/browser/background/background_contents_service_factory.h"
+#include "chrome/browser/background_fetch/background_fetch_delegate_factory.h"
+#include "chrome/browser/background_fetch/background_fetch_delegate_impl.h"
 #include "chrome/browser/background_sync/background_sync_controller_factory.h"
 #include "chrome/browser/background_sync/background_sync_controller_impl.h"
 #include "chrome/browser/browser_process.h"
@@ -450,6 +452,11 @@
   return PermissionManagerFactory::GetForProfile(this);
 }
 
+content::BackgroundFetchDelegate*
+OffTheRecordProfileImpl::GetBackgroundFetchDelegate() {
+  return BackgroundFetchDelegateFactory::GetForProfile(this);
+}
+
 content::BackgroundSyncController*
 OffTheRecordProfileImpl::GetBackgroundSyncController() {
   return BackgroundSyncControllerFactory::GetForProfile(this);
diff --git a/chrome/browser/profiles/off_the_record_profile_impl.h b/chrome/browser/profiles/off_the_record_profile_impl.h
index 360846a..efd966f3 100644
--- a/chrome/browser/profiles/off_the_record_profile_impl.h
+++ b/chrome/browser/profiles/off_the_record_profile_impl.h
@@ -109,6 +109,7 @@
   content::PushMessagingService* GetPushMessagingService() override;
   content::SSLHostStateDelegate* GetSSLHostStateDelegate() override;
   content::PermissionManager* GetPermissionManager() override;
+  content::BackgroundFetchDelegate* GetBackgroundFetchDelegate() override;
   content::BackgroundSyncController* GetBackgroundSyncController() override;
   content::BrowsingDataRemoverDelegate* GetBrowsingDataRemoverDelegate()
       override;
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 4470320..650f819d 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -35,6 +35,8 @@
 #include "base/version.h"
 #include "build/build_config.h"
 #include "chrome/browser/background/background_contents_service_factory.h"
+#include "chrome/browser/background_fetch/background_fetch_delegate_factory.h"
+#include "chrome/browser/background_fetch/background_fetch_delegate_impl.h"
 #include "chrome/browser/background_sync/background_sync_controller_factory.h"
 #include "chrome/browser/background_sync/background_sync_controller_impl.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
@@ -1060,6 +1062,10 @@
   return PermissionManagerFactory::GetForProfile(this);
 }
 
+content::BackgroundFetchDelegate* ProfileImpl::GetBackgroundFetchDelegate() {
+  return BackgroundFetchDelegateFactory::GetForProfile(this);
+}
+
 content::BackgroundSyncController* ProfileImpl::GetBackgroundSyncController() {
   return BackgroundSyncControllerFactory::GetForProfile(this);
 }
diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h
index e0908ed..62669970 100644
--- a/chrome/browser/profiles/profile_impl.h
+++ b/chrome/browser/profiles/profile_impl.h
@@ -91,6 +91,7 @@
   content::BrowsingDataRemoverDelegate* GetBrowsingDataRemoverDelegate()
       override;
   content::PermissionManager* GetPermissionManager() override;
+  content::BackgroundFetchDelegate* GetBackgroundFetchDelegate() override;
   content::BackgroundSyncController* GetBackgroundSyncController() override;
   net::URLRequestContextGetter* CreateRequestContext(
       content::ProtocolHandlerMap* protocol_handlers,
diff --git a/chrome/browser/resources/chromeos/login/oobe_reset.html b/chrome/browser/resources/chromeos/login/oobe_reset.html
index 23b4fd1..2ca1e50 100644
--- a/chrome/browser/resources/chromeos/login/oobe_reset.html
+++ b/chrome/browser/resources/chromeos/login/oobe_reset.html
@@ -70,6 +70,7 @@
             hidden="[[!tpmFirmwareUpdateAvailable_]]">
           <paper-checkbox id="tpmFirmwareUpdateCheckbox"
               checked="{{tpmFirmwareUpdateChecked_}}"
+              disabled="{{!tpmFirmwareUpdateEditable_}}"
               on-change="onTPMFirmwareUpdateChanged_">
             <div id="tpmFirmwareUpdateContainer">
               <span i18n-content="resetTPMFirmwareUpdate"></span>
diff --git a/chrome/browser/resources/chromeos/login/oobe_reset.js b/chrome/browser/resources/chromeos/login/oobe_reset.js
index 501aa72..c63e695 100644
--- a/chrome/browser/resources/chromeos/login/oobe_reset.js
+++ b/chrome/browser/resources/chromeos/login/oobe_reset.js
@@ -32,6 +32,11 @@
     tpmFirmwareUpdateChecked_: Boolean,
 
     /**
+     * If the checkbox to request a TPM firmware update is editable.
+     */
+    tpmFirmwareUpdateEditable_: Boolean,
+
+    /**
      * Reference to OOBE screen object.
      * @type {!OobeTypes.Screen}
      */
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_reset.js b/chrome/browser/resources/chromeos/login/oobe_screen_reset.js
index f857afe..a091a6bc 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screen_reset.js
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_reset.js
@@ -18,6 +18,7 @@
   var CONTEXT_KEY_TPM_FIRMWARE_UPDATE_AVAILABLE =
       'tpm-firmware-update-available';
   var CONTEXT_KEY_TPM_FIRMWARE_UPDATE_CHECKED = 'tpm-firmware-update-checked';
+  var CONTEXT_KEY_TPM_FIRMWARE_UPDATE_EDITABLE = 'tpm-firmware-update-editable';
   var CONTEXT_KEY_IS_OFFICIAL_BUILD = 'is-official-build';
   var CONTEXT_KEY_IS_CONFIRMATIONAL_VIEW = 'is-confirmational-view';
   var CONTEXT_KEY_SCREEN_STATE = 'screen-state';
@@ -94,6 +95,10 @@
             self.setTPMFirmwareUpdateView_();
           });
       this.context.addObserver(
+          CONTEXT_KEY_TPM_FIRMWARE_UPDATE_EDITABLE, function() {
+            self.setTPMFirmwareUpdateView_();
+          });
+      this.context.addObserver(
           CONTEXT_KEY_TPM_FIRMWARE_UPDATE_AVAILABLE, function() {
             self.setTPMFirmwareUpdateView_();
           });
@@ -297,6 +302,8 @@
           this.context.get(CONTEXT_KEY_TPM_FIRMWARE_UPDATE_AVAILABLE);
       $('oobe-reset-md').tpmFirmwareUpdateChecked_ =
           this.context.get(CONTEXT_KEY_TPM_FIRMWARE_UPDATE_CHECKED);
+      $('oobe-reset-md').tpmFirmwareUpdateEditable_ =
+          this.context.get(CONTEXT_KEY_TPM_FIRMWARE_UPDATE_EDITABLE);
     },
 
     onTPMFirmwareUpdateChanged_: function(value) {
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.cc b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.cc
index 35225e52..abc70bf 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.cc
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.cc
@@ -163,9 +163,9 @@
   return 0;
 }
 
-const char* GetPassphrase(VolumeArchiveMinizip* archive_minizip) {
-  const char* password = archive_minizip->reader()->Passphrase();
-  return password;
+std::unique_ptr<std::string> GetPassphrase(
+    VolumeArchiveMinizip* archive_minizip) {
+  return archive_minizip->reader()->Passphrase();
 }
 
 }  // namespace volume_archive_functions
@@ -336,8 +336,24 @@
   // If the archive is encrypted, the lowest bit of raw_file_info.flag is set.
   // Directories cannot be encrypted with the basic zip encrytion algorithm.
   if (((raw_file_info.flag & 1) != 0) && !is_directory) {
-    const char* password = volume_archive_functions::GetPassphrase(this);
-    open_result = unzOpenCurrentFilePassword(zip_file_, password);
+    do {
+      if (password_cache_ == nullptr) {
+        // Save passphrase for upcoming file requests.
+        password_cache_ = volume_archive_functions::GetPassphrase(this);
+        // check if |password_cache_| is nullptr in case when user clicks Cancel
+        if (password_cache_ == nullptr) {
+          return false;
+        }
+      }
+
+      open_result =
+          unzOpenCurrentFilePassword(zip_file_, password_cache_.get()->c_str());
+
+      // If password is incorrect then password cache ought to be reseted.
+      if (open_result == UNZ_BADPASSWORD && password_cache_ != nullptr)
+        password_cache_.reset();
+
+    } while (open_result == UNZ_BADPASSWORD);
   } else {
     open_result = unzOpenCurrentFile(zip_file_);
   }
@@ -446,6 +462,7 @@
     }
   }
   zip_file_ = nullptr;
+  password_cache_.reset();
 
   CleanupReader();
 
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.h b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.h
index dde1579c..145ba64f 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.h
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.h
@@ -213,6 +213,9 @@
 
   // True if VolumeArchiveMinizip::DecompressData failed.
   bool decompressed_error_;
+
+  // The password cache to access password protected files.
+  std::unique_ptr<std::string> password_cache_;
 };
 
 #endif  // CHROME_BROWSER_RESOURCES_CHROMEOS_ZIP_ARCHIVER_CPP_VOLUME_ARCHIVE_MINIZIP_H_
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader.h b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader.h
index 484f8ce..675f33a3 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader.h
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader.h
@@ -32,7 +32,7 @@
 
   // Fetches a passphrase for reading. If the passphrase is not available it
   // returns nullptr.
-  virtual const char* Passphrase() = 0;
+  virtual std::unique_ptr<std::string> Passphrase() = 0;
 
   virtual int64_t offset() = 0;
 
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader_javascript_stream.cc b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader_javascript_stream.cc
index dc852553d..9a6127f 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader_javascript_stream.cc
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader_javascript_stream.cc
@@ -218,14 +218,15 @@
   request_id_ = request_id;
 }
 
-const char* VolumeReaderJavaScriptStream::Passphrase() {
+std::unique_ptr<std::string> VolumeReaderJavaScriptStream::Passphrase() {
+  std::unique_ptr<std::string> result;
   // The error is not recoverable. Once passphrase fails to be provided, it is
   // never asked again. Note, that still users are able to retry entering the
   // password, unless they click Cancel.
   pthread_mutex_lock(&shared_state_lock_);
   if (passphrase_error_) {
     pthread_mutex_unlock(&shared_state_lock_);
-    return nullptr;
+    return result;
   }
   pthread_mutex_unlock(&shared_state_lock_);
 
@@ -235,9 +236,10 @@
   pthread_mutex_lock(&shared_state_lock_);
   // Wait for the passphrase from JavaScript.
   pthread_cond_wait(&available_passphrase_cond_, &shared_state_lock_);
-  const char* result = nullptr;
+
   if (!passphrase_error_)
-    result = strdup(available_passphrase_.c_str());
+    result.reset(new std::string(available_passphrase_));
+
   pthread_mutex_unlock(&shared_state_lock_);
 
   return result;
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader_javascript_stream.h b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader_javascript_stream.h
index c91405e..0134c5d 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader_javascript_stream.h
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader_javascript_stream.h
@@ -66,7 +66,7 @@
   // See volume_reader.h for description. The method blocks on
   // available_passphrase_cond_. SetPassphraseAndSignal should unblock it from
   // another thread.
-  virtual const char* Passphrase();
+  virtual std::unique_ptr<std::string> Passphrase();
 
   virtual int64_t offset() { return offset_; }
 
diff --git a/chrome/browser/resources/chromeos/zip_archiver/js/app.js b/chrome/browser/resources/chromeos/zip_archiver/js/app.js
index 82e1acc..f254bca9 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/js/app.js
+++ b/chrome/browser/resources/chromeos/zip_archiver/js/app.js
@@ -245,7 +245,7 @@
    * @param {!Object<!unpacker.types.RequestId,
    *                 !unpacker.types.OpenFileRequestedOptions>}
    *     openedFiles Previously opened files before a suspend.
-   * @param {string} passphrase Previously used passphrase before a suspend.
+   * @param {?string} passphrase Previously used passphrase before a suspend.
    * @return {!Promise} Promise fulfilled on success and rejected on failure.
    * @private
    */
@@ -512,6 +512,16 @@
   },
 
   /**
+   * Updates the state in case of restarts, event page suspend, crashes, etc.
+   * Use this method to update or save the state out side of the object in case
+   * when password changes, etc.
+   * @param {!Array<!unpacker.types.FileSystemId>} fileSystemIdsArray
+   */
+  updateState: function(fileSystemIdsArray) {
+    unpacker.app.saveState_(fileSystemIdsArray);
+  },
+
+  /**
    * Unmounts a volume and removes any resources related to the volume from both
    * the extension and the local storage state.
    * @param {!unpacker.types.FileSystemId} fileSystemId
@@ -796,7 +806,7 @@
                   };
 
                   var loadPromise = unpacker.app.loadVolume_(
-                      fileSystemId, entry, {}, '' /* passphrase */);
+                      fileSystemId, entry, {}, null /* passphrase */);
                   loadPromise
                       .then(function() {
                         // Mount the volume and save its information in local
diff --git a/chrome/browser/resources/chromeos/zip_archiver/js/decompressor.js b/chrome/browser/resources/chromeos/zip_archiver/js/decompressor.js
index 504c2ac..aa1e986 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/js/decompressor.js
+++ b/chrome/browser/resources/chromeos/zip_archiver/js/decompressor.js
@@ -296,6 +296,8 @@
 unpacker.Decompressor.prototype.readPassphrase_ = function(data, requestId) {
   this.passphraseManager.getPassphrase()
       .then(function(passphrase) {
+        // Update remembered password
+        unpacker.app.updateState([this.fileSystemId_]);
         this.naclModule_.postMessage(
             unpacker.request.createReadPassphraseDoneResponse(
                 this.fileSystemId_, requestId, passphrase));
diff --git a/chrome/browser/resources/chromeos/zip_archiver/js/passphrase-manager.js b/chrome/browser/resources/chromeos/zip_archiver/js/passphrase-manager.js
index 655d45b..71755e73 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/js/passphrase-manager.js
+++ b/chrome/browser/resources/chromeos/zip_archiver/js/passphrase-manager.js
@@ -29,7 +29,7 @@
   return new Promise(function(fulfill, reject) {
     // For the first passphrase request try the init passphrase (which may be
     // incorrect though, so do it only once).
-    if (this.initPassphrase_) {
+    if (this.initPassphrase_ != null) {
       fulfill(this.initPassphrase_);
       this.initPassphrase_ = null;
       return;
diff --git a/chrome/browser/resources/chromeos/zip_archiver/js/volume.js b/chrome/browser/resources/chromeos/zip_archiver/js/volume.js
index 0ba76e7..663cc20 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/js/volume.js
+++ b/chrome/browser/resources/chromeos/zip_archiver/js/volume.js
@@ -12,7 +12,6 @@
  */
 function DateFromTimeT(timestamp) {
   var local = new Date(1000 * timestamp);
-  console.info(local.getHours());
   return new Date(local.getTime() + (local.getTimezoneOffset() * 60000));
 }
 
diff --git a/chrome/browser/resources/md_bookmarks/api_listener.html b/chrome/browser/resources/md_bookmarks/api_listener.html
index 3fc0a597..0ee48ff 100644
--- a/chrome/browser/resources/md_bookmarks/api_listener.html
+++ b/chrome/browser/resources/md_bookmarks/api_listener.html
@@ -1,5 +1,6 @@
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://bookmarks/actions.html">
+<link rel="import" href="chrome://bookmarks/debouncer.html">
 <link rel="import" href="chrome://bookmarks/store.html">
 <link rel="import" href="chrome://bookmarks/util.html">
 <script src="chrome://bookmarks/api_listener.js"></script>
diff --git a/chrome/browser/resources/md_bookmarks/compiled_resources2.gyp b/chrome/browser/resources/md_bookmarks/compiled_resources2.gyp
index 0a35dc0..bc67466 100644
--- a/chrome/browser/resources/md_bookmarks/compiled_resources2.gyp
+++ b/chrome/browser/resources/md_bookmarks/compiled_resources2.gyp
@@ -19,8 +19,8 @@
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
         '<(EXTERNS_GYP):chrome_extensions',
         'actions',
+        'debouncer',
         'store',
-        'timer_proxy',
         'util',
       ],
       'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
@@ -63,6 +63,13 @@
       'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi']
     },
     {
+      'target_name': 'debouncer',
+      'dependencies': [
+        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+      ],
+      'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
+    },
+    {
       'target_name': 'dialog_focus_manager',
       'dependencies': [
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
@@ -85,10 +92,10 @@
         '<(EXTERNS_GYP):bookmark_manager_private',
         '<(EXTERNS_GYP):metrics_private',
         'api_listener',
+        'debouncer',
         'dnd_chip',
         'folder_node',
         'store',
-        'timer_proxy',
         'types',
         'util',
       ],
@@ -186,13 +193,6 @@
       'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi']
     },
     {
-      'target_name': 'timer_proxy',
-      'dependencies': [
-        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
-      ],
-      'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
-    },
-    {
       'target_name': 'toast_manager',
       'dependencies': [
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
diff --git a/chrome/browser/resources/md_bookmarks/debouncer.html b/chrome/browser/resources/md_bookmarks/debouncer.html
new file mode 100644
index 0000000..567579df
--- /dev/null
+++ b/chrome/browser/resources/md_bookmarks/debouncer.html
@@ -0,0 +1,2 @@
+<link rel="import" href="chrome://resources/html/cr.html">
+<script src="chrome://bookmarks/debouncer.js"></script>
diff --git a/chrome/browser/resources/md_bookmarks/debouncer.js b/chrome/browser/resources/md_bookmarks/debouncer.js
new file mode 100644
index 0000000..3bc7464
--- /dev/null
+++ b/chrome/browser/resources/md_bookmarks/debouncer.js
@@ -0,0 +1,86 @@
+// 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 A debouncer which fires the given callback after a delay. The
+ * delay can be refreshed by calling restartTimeout. Resetting the timeout with
+ * no delay moves the callback to the end of the task queue.
+ */
+
+cr.define('bookmarks', function() {
+  class Debouncer {
+    /** @param {!function()} callback */
+    constructor(callback) {
+      /** @private {!function()} */
+      this.callback_ = callback;
+      /** @private {!Object} */
+      this.timerProxy_ = window;
+      /** @private {?number} */
+      this.timer_ = null;
+      /** @private {!function()} */
+      this.boundTimerCallback_ = this.timerCallback_.bind(this);
+      /** @private {boolean} */
+      this.isDone_ = false;
+      /** @private {!PromiseResolver} */
+      this.promiseResolver_ = new PromiseResolver();
+    }
+
+    /**
+     * Starts the timer for the callback, cancelling the old timer if there is
+     * one.
+     * @param {number=} delay
+     */
+    restartTimeout(delay) {
+      assert(!this.isDone_);
+
+      this.cancelTimeout_();
+      this.timer_ =
+          this.timerProxy_.setTimeout(this.boundTimerCallback_, delay || 0);
+    }
+
+    /**
+     * @return {boolean} True if the Debouncer has finished processing.
+     */
+    done() {
+      return this.isDone_;
+    }
+
+    /**
+     * @return {!Promise} Promise which resolves immediately after the callback.
+     */
+    get promise() {
+      return this.promiseResolver_.promise;
+    }
+
+    /**
+     * Resets the debouncer as if it had been newly instantiated.
+     */
+    reset() {
+      this.isDone_ = false;
+      this.promiseResolver_ = new PromiseResolver();
+      this.cancelTimeout_();
+    }
+
+    /**
+     * Cancel the timer callback, which can be restarted by calling
+     * restartTimeout().
+     * @private
+     */
+    cancelTimeout_() {
+      if (this.timer_)
+        this.timerProxy_.clearTimeout(this.timer_);
+    }
+
+    /** @private */
+    timerCallback_() {
+      this.isDone_ = true;
+      this.callback_.call();
+      this.promiseResolver_.resolve();
+    }
+  }
+
+  return {
+    Debouncer: Debouncer,
+  };
+});
diff --git a/chrome/browser/resources/md_bookmarks/dnd_manager.html b/chrome/browser/resources/md_bookmarks/dnd_manager.html
index dc6db92b..0a8b34b8 100644
--- a/chrome/browser/resources/md_bookmarks/dnd_manager.html
+++ b/chrome/browser/resources/md_bookmarks/dnd_manager.html
@@ -1,4 +1,4 @@
 <link rel="import" href="chrome://bookmarks/constants.html">
+<link rel="import" href="chrome://bookmarks/debouncer.html">
 <link rel="import" href="chrome://bookmarks/dnd_chip.html">
-<link rel="import" href="chrome://bookmarks/timer_proxy.html">
 <script src="chrome://bookmarks/dnd_manager.js"></script>
diff --git a/chrome/browser/resources/md_bookmarks/dnd_manager.js b/chrome/browser/resources/md_bookmarks/dnd_manager.js
index 06e6c73d..f4da577 100644
--- a/chrome/browser/resources/md_bookmarks/dnd_manager.js
+++ b/chrome/browser/resources/md_bookmarks/dnd_manager.js
@@ -288,9 +288,9 @@
 
     /**
      * Used to instantly remove the indicator style in tests.
-     * @private {bookmarks.TimerProxy}
+     * @private {!Object}
      */
-    this.timerProxy = new bookmarks.TimerProxy();
+    this.timerProxy = window;
   }
 
   DropIndicator.prototype = {
@@ -393,9 +393,9 @@
 
     /**
      * Used to instantly clearDragData in tests.
-     * @private {bookmarks.TimerProxy}
+     * @private {!Object}
      */
-    this.timerProxy_ = new bookmarks.TimerProxy();
+    this.timerProxy_ = window;
 
     /**
      * The bookmark drag and drop indicator chip.
@@ -981,7 +981,7 @@
           isBookmarkList(dropDestination.element);
     },
 
-    /** @param {bookmarks.TimerProxy} timerProxy */
+    /** @param {!Object} timerProxy */
     setTimerProxyForTesting: function(timerProxy) {
       this.timerProxy_ = timerProxy;
       this.dropIndicator_.timerProxy = timerProxy;
diff --git a/chrome/browser/resources/md_bookmarks/timer_proxy.html b/chrome/browser/resources/md_bookmarks/timer_proxy.html
deleted file mode 100644
index d70600a..0000000
--- a/chrome/browser/resources/md_bookmarks/timer_proxy.html
+++ /dev/null
@@ -1,2 +0,0 @@
-<link rel="import" href="chrome://resources/html/cr.html">
-<script src="chrome://bookmarks/timer_proxy.js"></script>
diff --git a/chrome/browser/resources/md_bookmarks/timer_proxy.js b/chrome/browser/resources/md_bookmarks/timer_proxy.js
deleted file mode 100644
index 688b3e1..0000000
--- a/chrome/browser/resources/md_bookmarks/timer_proxy.js
+++ /dev/null
@@ -1,111 +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 A class that mediates window timer calls to support mockability
- * in tests.
- */
-
-cr.define('bookmarks', function() {
-  /** @constructor */
-  function TimerProxy() {}
-
-  TimerProxy.prototype = {
-    /**
-     * @param {function()|string} fn
-     * @param {number=} delay
-     * @return {number}
-     */
-    setTimeout: function(fn, delay) {
-      return window.setTimeout(fn, delay);
-    },
-
-    /** @param {number|undefined?} id */
-    clearTimeout: function(id) {
-      window.clearTimeout(id);
-    },
-  };
-
-  /**
-   * A debouncer which fires the given callback after a delay. The delay can be
-   * refreshed by calling restartTimeout. Resetting the timeout with no delay
-   * moves the callback to the end of the task queue.
-   * @param {!function()} callback
-   * @constructor
-   */
-  function Debouncer(callback) {
-    /** @private {!function()} */
-    this.callback_ = callback;
-    /** @private {!bookmarks.TimerProxy} */
-    this.timerProxy_ = new TimerProxy();
-    /** @private {?number} */
-    this.timer_ = null;
-    /** @private {!function()} */
-    this.boundTimerCallback_ = this.timerCallback_.bind(this);
-    /** @private {boolean} */
-    this.isDone_ = false;
-    /** @private {!PromiseResolver} */
-    this.promiseResolver_ = new PromiseResolver();
-  }
-
-  Debouncer.prototype = {
-    /**
-     * Starts the timer for the callback, cancelling the old timer if there is
-     * one.
-     * @param {number=} delay
-     */
-    restartTimeout: function(delay) {
-      assert(!this.isDone_);
-
-      this.cancelTimeout_();
-      this.timer_ =
-          this.timerProxy_.setTimeout(this.boundTimerCallback_, delay);
-    },
-
-    /**
-     * @return {boolean} True if the Debouncer has finished processing.
-     */
-    done: function() {
-      return this.isDone_;
-    },
-
-    /**
-     * @return {Promise} Promise which resolves immediately after the callback.
-     */
-    get promise() {
-      return this.promiseResolver_.promise;
-    },
-
-    /**
-     * Resets the debouncer as if it had been newly instantiated.
-     */
-    reset: function() {
-      this.isDone_ = false;
-      this.promiseResolver_ = new PromiseResolver();
-      this.cancelTimeout_();
-    },
-
-    /**
-     * Cancel the timer callback, which can be restarted by calling
-     * restartTimeout().
-     * @private
-     */
-    cancelTimeout_: function() {
-      if (this.timer_)
-        this.timerProxy_.clearTimeout(this.timer_);
-    },
-
-    /** @private */
-    timerCallback_: function() {
-      this.isDone_ = true;
-      this.callback_.call();
-      this.promiseResolver_.resolve();
-    },
-  };
-
-  return {
-    Debouncer: Debouncer,
-    TimerProxy: TimerProxy,
-  };
-});
diff --git a/chrome/browser/resources/settings/internet_page/internet_detail_page.html b/chrome/browser/resources/settings/internet_page/internet_detail_page.html
index 7ecb1c1b..ac16520 100644
--- a/chrome/browser/resources/settings/internet_page/internet_detail_page.html
+++ b/chrome/browser/resources/settings/internet_page/internet_detail_page.html
@@ -169,13 +169,14 @@
       </template>
       <!-- Data roaming (Cellular only). -->
       <template is="dom-if" if="[[isCellular_(networkProperties)]]">
-      <settings-toggle-button id="allowDataRoaming"
-          pref="{{prefs.cros.signed.data_roaming_enabled}}"
-          label="$i18n{networkAllowDataRoaming}">
-      </settings-toggle-button>
+        <settings-toggle-button id="allowDataRoaming"
+            pref="{{prefs.cros.signed.data_roaming_enabled}}"
+            label="$i18n{networkAllowDataRoaming}">
+        </settings-toggle-button>
       </template>
       <!-- SIM Info (Cellular only). -->
-      <template is="dom-if" if="[[showCellularSim_(networkProperties)]]">
+      <template is="dom-if" if="[[showCellularSim_(networkProperties)]]"
+          restamp>
         <div class="settings-box single-column stretch">
           <network-siminfo
               editable on-siminfo-change="onNetworkPropertyChange_"
@@ -185,10 +186,11 @@
         </div>
       </template>
       <!-- IP Address. -->
-      <template is="dom-if" if="[[IPAddress_]]">
+      <template is="dom-if"
+          if="[[showIpAddress_(ipAddress_, networkProperties)]]">
         <div class="settings-box two-line single-column stretch">
           <div>$i18n{networkIPAddress}</div>
-          <div class="secondary">[[IPAddress_]]</div>
+          <div class="secondary">[[ipAddress_]]</div>
         </div>
       </template>
       <!-- Properties to always show if present. -->
diff --git a/chrome/browser/resources/settings/internet_page/internet_detail_page.js b/chrome/browser/resources/settings/internet_page/internet_detail_page.js
index 47590b07..f0dd57a 100644
--- a/chrome/browser/resources/settings/internet_page/internet_detail_page.js
+++ b/chrome/browser/resources/settings/internet_page/internet_detail_page.js
@@ -105,7 +105,7 @@
      * The network IP Address.
      * @private
      */
-    IPAddress_: {
+    ipAddress_: {
       type: String,
       value: '',
     },
@@ -219,7 +219,7 @@
     // Set the IPAddress property to the IPV4 Address.
     var ipv4 =
         CrOnc.getIPConfigForType(this.networkProperties, CrOnc.IPType.IPV4);
-    this.IPAddress_ = (ipv4 && ipv4.IPAddress) || '';
+    this.ipAddress_ = (ipv4 && ipv4.IPAddress) || '';
 
     // Update the detail page title.
     this.parentNode.pageTitle = CrOnc.getNetworkName(this.networkProperties);
@@ -229,11 +229,10 @@
     if (!this.didSetFocus_) {
       // Focus a button once the initial state is set.
       this.didSetFocus_ = true;
-      var button = this.$$('#titleDiv .primary-button:not([hidden])');
-      if (!button)
-        button = this.$$('#titleDiv paper-button:not([hidden])');
-      assert(button);  // At least one button will always be visible.
-      button.focus();
+      var button = this.$$('#titleDiv .primary-button:not([hidden])') ||
+          this.$$('#titleDiv paper-button:not([hidden])');
+      if (button)
+        button.focus();
     }
 
     if (this.shouldShowConfigureWhenNetworkLoaded_ &&
@@ -1016,6 +1015,16 @@
   },
 
   /**
+   * @param {string} ipAddress
+   * @param {!CrOnc.NetworkProperties} networkProperties
+   * @return {boolean}
+   * @private
+   */
+  showIpAddress_: function(ipAddress, networkProperties) {
+    return !!ipAddress && this.isConnectedState_(networkProperties);
+  },
+
+  /**
    * @param {!Object} curValue
    * @param {!Object} newValue
    * @return {boolean} True if all properties set in |newValue| are equal to
diff --git a/chrome/browser/resources/settings/internet_page/network_siminfo.js b/chrome/browser/resources/settings/internet_page/network_siminfo.js
index cb0eb9c..2ea134f 100644
--- a/chrome/browser/resources/settings/internet_page/network_siminfo.js
+++ b/chrome/browser/resources/settings/internet_page/network_siminfo.js
@@ -71,6 +71,18 @@
 
   sendSimLockEnabled_: false,
 
+  /** @override */
+  detached: function() {
+    if (this.$.enterPinDialog.open)
+      this.$.enterPinDialog.close();
+    if (this.$.changePinDialog.open)
+      this.$.changePinDialog.close();
+    if (this.$.unlockPinDialog.open)
+      this.$.unlockPinDialog.close();
+    if (this.$.unlockPukDialog.open)
+      this.$.unlockPukDialog.close();
+  },
+
   /** @private */
   networkPropertiesChanged_: function() {
     if (!this.networkProperties || !this.networkProperties.Cellular)
@@ -135,6 +147,7 @@
    * @private
    */
   sendEnterPin_: function(event) {
+    event.stopPropagation();
     var guid = (this.networkProperties && this.networkProperties.GUID) || '';
     var pin = this.$.enterPin.value;
     if (!this.validatePin_(pin)) {
@@ -162,9 +175,9 @@
    * @private
    */
   onChangePinTap_: function(event) {
+    event.stopPropagation();
     if (!this.networkProperties || !this.networkProperties.Cellular)
       return;
-    event.stopPropagation();
     this.error_ = ErrorType.NONE;
     this.$.changePinOld.value = '';
     this.$.changePinNew1.value = '';
@@ -178,6 +191,7 @@
    * @private
    */
   sendChangePin_: function(event) {
+    event.stopPropagation();
     var guid = (this.networkProperties && this.networkProperties.GUID) || '';
     var newPin = this.$.changePinNew1.value;
     if (!this.validatePin_(newPin, this.$.changePinNew2.value))
@@ -219,6 +233,7 @@
    * @private
    */
   sendUnlockPin_: function(event) {
+    event.stopPropagation();
     var guid = (this.networkProperties && this.networkProperties.GUID) || '';
     var pin = this.$.unlockPin.value;
     if (!this.validatePin_(pin))
@@ -257,6 +272,7 @@
    * @private
    */
   sendUnlockPuk_: function(event) {
+    event.stopPropagation();
     var guid = (this.networkProperties && this.networkProperties.GUID) || '';
     var puk = this.$.unlockPuk.value;
     if (!this.validatePuk_(puk))
diff --git a/chrome/browser/resources/settings/internet_page/network_summary_item.html b/chrome/browser/resources/settings/internet_page/network_summary_item.html
index 6a9295ee..0c535cf 100644
--- a/chrome/browser/resources/settings/internet_page/network_summary_item.html
+++ b/chrome/browser/resources/settings/internet_page/network_summary_item.html
@@ -46,8 +46,8 @@
         </div>
       </div>
 
-      <template is="dom-if" if="[[showSimInfo_(deviceState)]]">
-        <network-siminfo editable
+      <template is="dom-if" if="[[showSimInfo_(deviceState)]]" restamp>
+        <network-siminfo editable on-tap="doNothing_"
             network-properties="[[getCellularState_(deviceState)]]"
             networking-private="[[networkingPrivate]]">
         </network-siminfo>
@@ -55,7 +55,8 @@
 
       <template is="dom-if" if="[[showPolicyIndicator_(activeNetworkState)]]">
         <cr-policy-indicator indicator-type="[[getIndicatorTypeForSource(
-            activeNetworkState.Source)]]">
+            activeNetworkState.Source)]]"
+            on-tap="doNothing_">
         </cr-policy-indicator>
       </template>
 
diff --git a/chrome/browser/resources/settings/internet_page/network_summary_item.js b/chrome/browser/resources/settings/internet_page/network_summary_item.js
index 656d666..022c8cb 100644
--- a/chrome/browser/resources/settings/internet_page/network_summary_item.js
+++ b/chrome/browser/resources/settings/internet_page/network_summary_item.js
@@ -306,4 +306,13 @@
     // Make sure this does not propagate to onDetailsTap_.
     event.stopPropagation();
   },
+
+  /**
+   * Make sure events in embedded components do not propagate to onDetailsTap_.
+   * @param {!Event} event
+   * @private
+   */
+  doNothing_: function(event) {
+    event.stopPropagation();
+  },
 });
diff --git a/chrome/browser/signin/account_reconcilor_unittest.cc b/chrome/browser/signin/account_reconcilor_unittest.cc
index 7d986a9d9..e8fe92c 100644
--- a/chrome/browser/signin/account_reconcilor_unittest.cc
+++ b/chrome/browser/signin/account_reconcilor_unittest.cc
@@ -320,6 +320,207 @@
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
 
+struct AccountReconcilorTestDiceParam {
+  const char* tokens;
+  const char* cookies;
+  bool is_first_reconcile;
+  const char* gaia_api_calls;
+  const char* cookies_after_reconcile;
+};
+
+// clang-format off
+const AccountReconcilorTestDiceParam kDiceParams[] = {
+    // This table encodes the initial state and expectations of a reconcile.
+    // The syntax is:
+    // - Tokens:
+    //   A, B, C: Accounts for which we have a token in Chrome.
+    //   *: The next account is the main Chrome account (i.e. in SigninManager).
+    //   x: The next account has a token error.
+    // - Cookies:
+    //   A, B, C: Accounts in the Gaia cookie (returned by ListAccounts).
+    // - First Run: true if this is the first reconcile (i.e. Chrome startup).
+    // - API calls:
+    //   X: Logout all accounts.
+    //   A, B, C: Merge account.
+    // -------------------------------------------------------------------
+    // Tokens | Cookies | First Run | Gaia calls | Cookies after reconcile
+    // -------------------------------------------------------------------
+    // Sync enabled, first reconcile.
+    {  "*AB",   "AB",     true,       "",          "AB"},
+    {  "*AB",   "BA",     true,       "XAB",       "AB"},
+    {  "*AB",   "A",      true,       "B",         "AB"},
+    {  "*AB",   "B",      true,       "XAB",       "AB"},
+    {  "*AB",   "",       true,       "AB",        "AB"},
+    // Sync enabled, first reconcile, token error on primary.
+    {  "*xAB",  "AB",     true,       "X",         ""},
+    {  "*xAB",  "BA",     true,       "X",         ""},
+    {  "*xAB",  "A",      true,       "X",         ""},
+    {  "*xAB",  "B",      true,       "X",         ""},
+    {  "*xAB",  "",       true,       "",          ""},
+    // Sync enabled, first reconcile, token error on secondary.
+    {  "*AxB",  "AB",     true,       "XA",        "A"},
+    {  "*AxB",  "BA",     true,       "XA",        "A"},
+    {  "*AxB",  "A",      true,       "",          "A"},
+    {  "*AxB",  "B",      true,       "XA",        "A"},
+    {  "*AxB",  "",       true,       "A",         "A"},
+    // Sync enabled, first reconcile, token error on both accounts.
+    {  "*xAxB", "AB",     true,       "X",         ""},
+    {  "*xAxB", "BA",     true,       "X",         ""},
+    {  "*xAxB", "A",      true,       "X",         ""},
+    {  "*xAxB", "B",      true,       "X",         ""},
+    {  "*xAxB", "",       true,       "",          ""},
+    // Sync disabled, first reconcile.
+    {  "AB",    "AB",     true,       "",          "AB"},
+    {  "AB",    "BA",     true,       "",          "BA"},
+    {  "AB",    "A",      true,       "B",         "AB"},
+    {  "AB",    "B",      true,       "A",         "BA"},
+    {  "AB",    "",       true,       "AB",        "AB"},
+    // Sync disabled, first reconcile, token error on first account.
+    {  "xAB",   "AB",     true,       "XB",        "B"},
+    {  "xAB",   "BA",     true,       "XB",        "B"},
+    {  "xAB",   "A",      true,       "XB",        "B"},
+    {  "xAB",   "B",      true,       "",          "B"},
+    {  "xAB",   "",       true,       "B",         "B"},
+    // Sync disabled, first reconcile, token error on second account.
+    {  "AxB",   "AB",     true,       "XA",        "A"},
+    {  "AxB",   "BA",     true,       "XA",        "A"},
+    {  "AxB",   "A",      true,       "",          "A"},
+    {  "AxB",   "B",      true,       "XA",        "A"},
+    {  "AxB",   "",       true,       "A",         "A"},
+    // Sync disabled, first reconcile, token error on both accounts.
+    {  "xAxB",  "AB",     true,       "X",         ""},
+    {  "xAxB",  "BA",     true,       "X",         ""},
+    {  "xAxB",  "A",      true,       "X",         ""},
+    {  "xAxB",  "B",      true,       "X",         ""},
+    {  "xAxB",  "",       true,       "",          ""},
+    // Check that Gaia default account is kept in case of mismatch.
+    {  "AB",    "BC",     true,       "XBA",       "BA"},
+};
+// clang-format on
+
+// Parameterized version of AccountReconcilorTest.
+class AccountReconcilorTestDice
+    : public AccountReconcilorTest,
+      public ::testing::WithParamInterface<AccountReconcilorTestDiceParam> {
+ protected:
+  struct Account {
+    std::string email;
+    std::string gaia_id;
+  };
+
+  AccountReconcilorTestDice() {
+    accounts_['A'] = {"a@gmail.com", "A"};
+    accounts_['B'] = {"b@gmail.com", "B"};
+    accounts_['C'] = {"c@gmail.com", "C"};
+  }
+
+  std::map<char, Account> accounts_;
+};
+
+// Checks one row of the kDiceParams table above.
+TEST_P(AccountReconcilorTestDice, TableRowTest) {
+  // Enable Dice.
+  signin::ScopedAccountConsistencyDice scoped_dice;
+
+  // Setup tokens.
+  bool signin = false;
+  bool token_error = false;
+  for (int i = 0; GetParam().tokens[i] != '\0'; ++i) {
+    char token_code = GetParam().tokens[i];
+    if (token_code == '*') {
+      signin = true;
+      continue;
+    }
+    if (token_code == 'x') {
+      token_error = true;
+      continue;
+    }
+
+    std::string account_id = PickAccountIdForAccount(
+        accounts_[token_code].gaia_id, accounts_[token_code].email);
+    if (signin) {
+      ConnectProfileToAccount(accounts_[token_code].gaia_id,
+                              accounts_[token_code].email);
+      signin = false;
+    } else {
+      token_service()->UpdateCredentials(account_id, "refresh_token");
+    }
+    if (token_error) {
+      token_service_delegate()->SetLastErrorForAccount(
+          account_id, GoogleServiceAuthError(
+                          GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
+      token_error = false;
+    }
+  }
+
+  // Setup cookies.
+  std::string cookies(GetParam().cookies);
+  if (cookies.size() == 0) {
+    cookie_manager_service()->SetListAccountsResponseNoAccounts();
+  } else if (cookies.size() == 1) {
+    cookie_manager_service()->SetListAccountsResponseOneAccount(
+        accounts_[GetParam().cookies[0]].email.c_str(),
+        accounts_[GetParam().cookies[0]].gaia_id.c_str());
+  } else {
+    ASSERT_EQ(2u, cookies.size());
+    cookie_manager_service()->SetListAccountsResponseTwoAccounts(
+        accounts_[GetParam().cookies[0]].email.c_str(),
+        accounts_[GetParam().cookies[0]].gaia_id.c_str(),
+        accounts_[GetParam().cookies[1]].email.c_str(),
+        accounts_[GetParam().cookies[1]].gaia_id.c_str());
+  }
+
+  // Dummy reconcile if needed.
+  if (!GetParam().is_first_reconcile) {
+    // TODO: dummy reconcile.
+    NOTREACHED();
+  }
+
+  // Setup expectations.
+  testing::InSequence mock_sequence;
+  bool logout_action = false;
+  for (int i = 0; GetParam().gaia_api_calls[i] != '\0'; ++i) {
+    if (GetParam().gaia_api_calls[i] == 'X') {
+      logout_action = true;
+      EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction())
+          .Times(1);
+      cookies.clear();
+      continue;
+    }
+    std::string cookie(1, GetParam().gaia_api_calls[i]);
+    EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(cookie)).Times(1);
+    cookies += cookie;
+  }
+  if (!logout_action) {
+    EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction())
+        .Times(0);
+  }
+
+  // Check the expected cookies after reconcile.
+  ASSERT_EQ(GetParam().cookies_after_reconcile, cookies);
+
+  // Reconcile.
+  AccountReconcilor* reconcilor = GetMockReconcilor();
+  reconcilor->StartReconcile();
+  ASSERT_TRUE(reconcilor->is_reconcile_started_);
+  base::RunLoop().RunUntilIdle();
+  for (int i = 0; GetParam().gaia_api_calls[i] != '\0'; ++i) {
+    if (GetParam().gaia_api_calls[i] == 'X')
+      continue;
+    std::string account_id =
+        PickAccountIdForAccount(accounts_[GetParam().gaia_api_calls[i]].gaia_id,
+                                accounts_[GetParam().gaia_api_calls[i]].email);
+    SimulateAddAccountToCookieCompleted(
+        reconcilor, account_id, GoogleServiceAuthError::AuthErrorNone());
+  }
+  ASSERT_FALSE(reconcilor->is_reconcile_started_);
+  ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState());
+}
+
+INSTANTIATE_TEST_CASE_P(DiceTable,
+                        AccountReconcilorTestDice,
+                        ::testing::ValuesIn(kDiceParams));
+
 // Tests that the AccountReconcilor is enabled when Dice is enabled.
 TEST_F(AccountReconcilorTest, EnabledWithDice) {
   signin::ScopedAccountConsistencyDice scoped_dice;
diff --git a/chrome/browser/ui/android/snackbars/auto_signin_prompt_controller.cc b/chrome/browser/ui/android/snackbars/auto_signin_prompt_controller.cc
index 35d8dcb..873cf64 100644
--- a/chrome/browser/ui/android/snackbars/auto_signin_prompt_controller.cc
+++ b/chrome/browser/ui/android/snackbars/auto_signin_prompt_controller.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/password_manager/core/browser/password_bubble_experiment.h"
+#include "components/strings/grit/components_strings.h"
 #include "content/public/browser/web_contents.h"
 #include "jni/AutoSigninSnackbarController_jni.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -31,4 +32,3 @@
   Java_AutoSigninSnackbarController_showSnackbar(env, tab->GetJavaObject(),
                                                  java_message);
 }
-
diff --git a/chrome/browser/ui/app_list/test/fake_profile.cc b/chrome/browser/ui/app_list/test/fake_profile.cc
index 73a371e..ae603ab 100644
--- a/chrome/browser/ui/app_list/test/fake_profile.cc
+++ b/chrome/browser/ui/app_list/test/fake_profile.cc
@@ -65,6 +65,10 @@
   return nullptr;
 }
 
+content::BackgroundFetchDelegate* FakeProfile::GetBackgroundFetchDelegate() {
+  return nullptr;
+}
+
 content::BackgroundSyncController* FakeProfile::GetBackgroundSyncController() {
   return nullptr;
 }
diff --git a/chrome/browser/ui/app_list/test/fake_profile.h b/chrome/browser/ui/app_list/test/fake_profile.h
index 741087ea..f07e95fe 100644
--- a/chrome/browser/ui/app_list/test/fake_profile.h
+++ b/chrome/browser/ui/app_list/test/fake_profile.h
@@ -45,6 +45,7 @@
   content::PushMessagingService* GetPushMessagingService() override;
   content::SSLHostStateDelegate* GetSSLHostStateDelegate() override;
   content::PermissionManager* GetPermissionManager() override;
+  content::BackgroundFetchDelegate* GetBackgroundFetchDelegate() override;
   content::BackgroundSyncController* GetBackgroundSyncController() override;
   content::BrowsingDataRemoverDelegate* GetBrowsingDataRemoverDelegate()
       override;
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
index f2e9892..f45ccfb 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -190,15 +190,18 @@
   DCHECK(!instance_);
   instance_ = this;
 
+  // ShelfModel initializes the app list and browser shortcut items.
   DCHECK(model_);
+  DCHECK_EQ(2, model_->item_count());
+  DCHECK_EQ(ash::kAppListId, model_->items()[0].id.app_id);
+  DCHECK_EQ(kChromeAppId, model_->items()[1].id.app_id);
 
-  // Synchronization is required in the Mash config, since Chrome and Ash run in
-  // separate processes; it's optional via kAshEnableShelfModelSynchronization
-  // in the Classic Ash config, where Chrome can uses Ash's ShelfModel directly.
-  should_synchronize_shelf_models_ =
-      ash_util::IsRunningInMash() ||
-      base::CommandLine::ForCurrentProcess()->HasSwitch(
-          ash::switches::kAshEnableShelfModelSynchronization);
+  // Start observing the shelf controller.
+  if (ConnectToShelfController()) {
+    ash::mojom::ShelfObserverAssociatedPtrInfo ptr_info;
+    observer_binding_.Bind(mojo::MakeRequest(&ptr_info));
+    shelf_controller_->AddObserver(std::move(ptr_info));
+  }
 
   if (!profile) {
     // If no profile was passed, we take the currently active profile and use it
@@ -282,18 +285,6 @@
 }
 
 void ChromeLauncherController::Init() {
-  // ShelfModel initializes the app list and browser shortcut items.
-  DCHECK_EQ(2, model_->item_count());
-  DCHECK_EQ(ash::kAppListId, model_->items()[0].id.app_id);
-  DCHECK_EQ(kChromeAppId, model_->items()[1].id.app_id);
-
-  // Start observing the shelf controller.
-  if (ConnectToShelfController()) {
-    ash::mojom::ShelfObserverAssociatedPtrInfo ptr_info;
-    observer_binding_.Bind(mojo::MakeRequest(&ptr_info));
-    shelf_controller_->AddObserver(std::move(ptr_info));
-  }
-
   InitBrowserShortcutItem();
   UpdateAppLaunchersFromPref();
 
@@ -823,6 +814,15 @@
 // ChromeLauncherController protected:
 
 bool ChromeLauncherController::ConnectToShelfController() {
+  // Synchronization is required in the Mash config, since Chrome and Ash run in
+  // separate processes; it's optional via kAshEnableShelfModelSynchronization
+  // in the Classic Ash config, where Chrome can uses Ash's ShelfModel directly.
+  if (!ash_util::IsRunningInMash() &&
+      !base::CommandLine::ForCurrentProcess()->HasSwitch(
+          ash::switches::kAshEnableShelfModelSynchronization)) {
+    return false;
+  }
+
   if (shelf_controller_.is_bound())
     return true;
 
@@ -1228,7 +1228,7 @@
 
 void ChromeLauncherController::OnShelfItemAdded(int32_t index,
                                                 const ash::ShelfItem& item) {
-  DCHECK(should_synchronize_shelf_models_) << " Unexpected model sync";
+  DCHECK(shelf_controller_) << " Unexpected model sync";
   DCHECK(!applying_remote_shelf_model_changes_) << " Unexpected model change";
 
   // Ignore the app list and browser shortcut items; they should already exist.
@@ -1245,7 +1245,7 @@
 }
 
 void ChromeLauncherController::OnShelfItemRemoved(const ash::ShelfID& id) {
-  DCHECK(should_synchronize_shelf_models_) << " Unexpected model sync";
+  DCHECK(shelf_controller_) << " Unexpected model sync";
   DCHECK(!applying_remote_shelf_model_changes_) << " Unexpected model change";
   const int index = model_->ItemIndexByID(id);
   DCHECK_GE(index, 0) << " No item found with the id: " << id;
@@ -1258,7 +1258,7 @@
 
 void ChromeLauncherController::OnShelfItemMoved(const ash::ShelfID& id,
                                                 int32_t index) {
-  DCHECK(should_synchronize_shelf_models_) << " Unexpected model sync";
+  DCHECK(shelf_controller_) << " Unexpected model sync";
   DCHECK(!applying_remote_shelf_model_changes_) << " Unexpected model change";
   const int current_index = model_->ItemIndexByID(id);
   DCHECK_GE(current_index, 0) << " No item found with the id: " << id;
@@ -1276,7 +1276,7 @@
 }
 
 void ChromeLauncherController::OnShelfItemUpdated(const ash::ShelfItem& item) {
-  DCHECK(should_synchronize_shelf_models_) << " Unexpected model sync";
+  DCHECK(shelf_controller_) << " Unexpected model sync";
   DCHECK(!applying_remote_shelf_model_changes_) << " Unexpected model change";
   const int index = model_->ItemIndexByID(item.id);
   DCHECK_GE(index, 0) << " No item found with the id: " << item.id;
@@ -1289,7 +1289,7 @@
 void ChromeLauncherController::OnShelfItemDelegateChanged(
     const ash::ShelfID& id,
     ash::mojom::ShelfItemDelegatePtr delegate) {
-  DCHECK(should_synchronize_shelf_models_) << " Unexpected model sync";
+  DCHECK(shelf_controller_) << " Unexpected model sync";
   DCHECK(!applying_remote_shelf_model_changes_) << " Unexpected model change";
   base::AutoReset<bool> reset(&applying_remote_shelf_model_changes_, true);
   if (delegate.is_bound()) {
@@ -1306,10 +1306,8 @@
 
 void ChromeLauncherController::ShelfItemAdded(int index) {
   ash::ShelfItem item = model_->items()[index];
-  if (shelf_controller_ && !applying_remote_shelf_model_changes_ &&
-      should_synchronize_shelf_models_) {
+  if (shelf_controller_ && !applying_remote_shelf_model_changes_)
     shelf_controller_->AddShelfItem(index, item);
-  }
 
   // Update the pin position preference as needed.
   if (ItemTypeIsPinned(item) && should_sync_pin_changes_)
@@ -1358,10 +1356,8 @@
 void ChromeLauncherController::ShelfItemRemoved(
     int index,
     const ash::ShelfItem& old_item) {
-  if (shelf_controller_ && !applying_remote_shelf_model_changes_ &&
-      should_synchronize_shelf_models_) {
+  if (shelf_controller_ && !applying_remote_shelf_model_changes_)
     shelf_controller_->RemoveShelfItem(old_item.id);
-  }
 
   // Remove the pin position from preferences as needed.
   if (ItemTypeIsPinned(old_item) && should_sync_pin_changes_)
@@ -1375,10 +1371,8 @@
 void ChromeLauncherController::ShelfItemMoved(int start_index,
                                               int target_index) {
   const ash::ShelfItem& item = model_->items()[target_index];
-  if (shelf_controller_ && !applying_remote_shelf_model_changes_ &&
-      should_synchronize_shelf_models_) {
+  if (shelf_controller_ && !applying_remote_shelf_model_changes_)
     shelf_controller_->MoveShelfItem(item.id, target_index);
-  }
 
   // Update the pin position preference as needed.
   DCHECK_NE(ash::TYPE_APP_LIST, item.type);
@@ -1390,10 +1384,8 @@
     int index,
     const ash::ShelfItem& old_item) {
   const ash::ShelfItem& item = model_->items()[index];
-  if (shelf_controller_ && !applying_remote_shelf_model_changes_ &&
-      should_synchronize_shelf_models_) {
+  if (shelf_controller_ && !applying_remote_shelf_model_changes_)
     shelf_controller_->UpdateShelfItem(item);
-  }
 
   if (!should_sync_pin_changes_)
     return;
@@ -1408,8 +1400,7 @@
 void ChromeLauncherController::ShelfItemDelegateChanged(
     const ash::ShelfID& id,
     ash::ShelfItemDelegate* delegate) {
-  if (shelf_controller_ && !applying_remote_shelf_model_changes_ &&
-      should_synchronize_shelf_models_) {
+  if (shelf_controller_ && !applying_remote_shelf_model_changes_) {
     shelf_controller_->SetShelfItemDelegate(
         id, delegate ? delegate->CreateInterfacePtrAndBind()
                      : ash::mojom::ShelfItemDelegatePtr());
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
index 81f4d51..d9425d57 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
@@ -397,9 +397,6 @@
   // The binding this instance uses to implment mojom::ShelfObserver
   mojo::AssociatedBinding<ash::mojom::ShelfObserver> observer_binding_;
 
-  // True if Ash and Chrome should synchronize separate ShelfModel instances.
-  bool should_synchronize_shelf_models_ = false;
-
   // True when applying changes from the remote ShelfModel owned by Ash.
   // Changes to the local ShelfModel should not be reported during this time.
   bool applying_remote_shelf_model_changes_ = false;
diff --git a/chrome/browser/ui/cocoa/app_menu/app_menu_controller.mm b/chrome/browser/ui/cocoa/app_menu/app_menu_controller.mm
index deaffe0..d5c5c03 100644
--- a/chrome/browser/ui/cocoa/app_menu/app_menu_controller.mm
+++ b/chrome/browser/ui/cocoa/app_menu/app_menu_controller.mm
@@ -315,6 +315,7 @@
       [buttonViewController_ toolbarActionsOverflowItem];
   BrowserActionsContainerView* containerView =
       [buttonViewController_ overflowActionsContainerView];
+  [containerView setHidden:[browserActionsController_ buttonCount] == 0];
 
   // Find the preferred container size for the menu width.
   int menuWidth = [[self menu] size].width;
diff --git a/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm b/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm
index ae081ed3..cb96dd19 100644
--- a/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm
+++ b/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm
@@ -444,7 +444,7 @@
 }
 
 - (void)redraw {
-  if (![self updateContainerVisibility])
+  if ([containerView_ isHidden])
     return;  // Container is hidden; no need to update.
 
   std::unique_ptr<ui::NinePartImageIds> highlight;
@@ -622,6 +622,7 @@
   [self updateButtonPositions];
   [self updateButtonOpacity];
   [[containerView_ window] invalidateCursorRectsForView:containerView_];
+  [self redraw];
 }
 
 - (void)containerDragStart:(NSNotification*)notification {
diff --git a/chrome/browser/ui/cocoa/passwords/auto_signin_view_controller.mm b/chrome/browser/ui/cocoa/passwords/auto_signin_view_controller.mm
index 1d21e5c5..60f79f09 100644
--- a/chrome/browser/ui/cocoa/passwords/auto_signin_view_controller.mm
+++ b/chrome/browser/ui/cocoa/passwords/auto_signin_view_controller.mm
@@ -17,6 +17,7 @@
 #include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
 #include "chrome/browser/ui/passwords/manage_passwords_view_utils.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
 #include "skia/ext/skia_utils_mac.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
diff --git a/chrome/browser/ui/cocoa/web_contents_modal_dialog_manager_views_mac.h b/chrome/browser/ui/cocoa/web_contents_modal_dialog_manager_views_mac.h
index 08b4ffc..4e7302e 100644
--- a/chrome/browser/ui/cocoa/web_contents_modal_dialog_manager_views_mac.h
+++ b/chrome/browser/ui/cocoa/web_contents_modal_dialog_manager_views_mac.h
@@ -53,6 +53,8 @@
 
   views::Widget* widget_;  // Weak. Deletes |this| when closing.
 
+  bool was_shown_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(SingleWebContentsDialogManagerViewsMac);
 };
 
diff --git a/chrome/browser/ui/cocoa/web_contents_modal_dialog_manager_views_mac.mm b/chrome/browser/ui/cocoa/web_contents_modal_dialog_manager_views_mac.mm
index 4010841..6e38897a 100644
--- a/chrome/browser/ui/cocoa/web_contents_modal_dialog_manager_views_mac.mm
+++ b/chrome/browser/ui/cocoa/web_contents_modal_dialog_manager_views_mac.mm
@@ -137,8 +137,15 @@
           showSheet:sheet_
       forParentView:parent_view];
 
-  if (!widget_->IsVisible())
+  if (!widget_->IsVisible()) {
+    if (was_shown_) {
+      // Disable animations when switching tabs.
+      widget_->SetVisibilityChangedAnimationsEnabled(false);
+    }
     widget_->Show();
+    widget_->SetVisibilityChangedAnimationsEnabled(true);
+    was_shown_ = true;
+  }
 }
 
 void SingleWebContentsDialogManagerViewsMac::Hide() {
@@ -146,12 +153,15 @@
       delegate_->GetWebContents()->GetTopLevelNativeWindow();
   [[ConstrainedWindowSheetController controllerForParentWindow:parent_window]
       hideSheet:sheet_];
+
+  widget_->Hide();
 }
 
 void SingleWebContentsDialogManagerViewsMac::Close() {
   // When the WebContents is destroyed, WebContentsModalDialogManager
   // ::CloseAllDialogs will call this. Close the Widget in the same manner as
   // the dialogs so that codepaths are consistent.
+  widget_->SetVisibilityChangedAnimationsEnabled(false);
   widget_->Close();  // Note: Synchronously calls OnWidgetClosing() below.
 }
 
diff --git a/chrome/browser/ui/cocoa/web_contents_modal_dialog_manager_views_mac_browsertest.mm b/chrome/browser/ui/cocoa/web_contents_modal_dialog_manager_views_mac_browsertest.mm
index 510b5d9..dd928ff5b 100644
--- a/chrome/browser/ui/cocoa/web_contents_modal_dialog_manager_views_mac_browsertest.mm
+++ b/chrome/browser/ui/cocoa/web_contents_modal_dialog_manager_views_mac_browsertest.mm
@@ -116,19 +116,21 @@
   AddTabAtIndex(1, GURL("about:blank"), ui::PAGE_TRANSITION_LINK);
   Widget* dialog1 = ShowViewsDialogOn(1, true);
   Widget* dialog0 = ShowViewsDialogOn(0, true);
-  // On Mac, both dialogs report themselves as visible to avoid Spaces
-  // transitions when switching tabs.
-  EXPECT_TRUE(dialog0->IsVisible());
-  EXPECT_TRUE(dialog1->IsVisible());
 
-  // But the inactive, second dialog will be fully transparent.
+  // Tab 0 is active, so only |dialog0| should become visible.
+  EXPECT_TRUE(dialog0->IsVisible());
+  EXPECT_FALSE(dialog1->IsVisible());
+
+  // The inactive, second dialog will also be fully transparent.
   EXPECT_EQ(0.0, [dialog1->GetNativeWindow() alphaValue]);
   EXPECT_EQ(1.0, [dialog0->GetNativeWindow() alphaValue]);
 
-  // Activate the second tab.
+  // Activate the second tab, visibility should switch.
   browser()->tab_strip_model()->ActivateTabAt(1, true);
   EXPECT_EQ(1.0, [dialog1->GetNativeWindow() alphaValue]);
   EXPECT_EQ(0.0, [dialog0->GetNativeWindow() alphaValue]);
+  EXPECT_FALSE(dialog0->IsVisible());
+  EXPECT_TRUE(dialog1->IsVisible());
 
   // Close the background tab.
   browser()->tab_strip_model()->CloseWebContentsAt(
diff --git a/chrome/browser/ui/views/passwords/manage_password_items_view.cc b/chrome/browser/ui/views/passwords/manage_password_items_view.cc
index d8f2b17..a37d7789 100644
--- a/chrome/browser/ui/views/passwords/manage_password_items_view.cc
+++ b/chrome/browser/ui/views/passwords/manage_password_items_view.cc
@@ -125,7 +125,8 @@
 }
 
 std::unique_ptr<views::Label> GeneratePasswordLabel(
-    const autofill::PasswordForm& form) {
+    const autofill::PasswordForm& form,
+    bool is_password_visible) {
   base::string16 text =
       form.federation_origin.unique()
           ? form.password_value
@@ -134,7 +135,7 @@
                 base::UTF8ToUTF16(form.federation_origin.host()));
   auto label = base::MakeUnique<views::Label>(text, CONTEXT_DEPRECATED_SMALL);
   label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  if (form.federation_origin.unique())
+  if (form.federation_origin.unique() && !is_password_visible)
     label->SetObscured(true);
   return label;
 }
@@ -222,7 +223,7 @@
   std::unique_ptr<views::Label> username_label(
       GenerateUsernameLabel(*password_form_));
   std::unique_ptr<views::Label> password_label(
-      GeneratePasswordLabel(*password_form_));
+      GeneratePasswordLabel(*password_form_, false));
   delete_button_ = GenerateDeleteButton(this).release();
   // TODO(https://crbug.com/761767): Remove this workaround once the grid layout
   // bug is fixed.
diff --git a/chrome/browser/ui/views/passwords/manage_password_items_view.h b/chrome/browser/ui/views/passwords/manage_password_items_view.h
index 82b750f..3d24891 100644
--- a/chrome/browser/ui/views/passwords/manage_password_items_view.h
+++ b/chrome/browser/ui/views/passwords/manage_password_items_view.h
@@ -23,7 +23,8 @@
 std::unique_ptr<views::Label> GenerateUsernameLabel(
     const autofill::PasswordForm& form);
 std::unique_ptr<views::Label> GeneratePasswordLabel(
-    const autofill::PasswordForm& form);
+    const autofill::PasswordForm& form,
+    bool is_password_visible);
 std::unique_ptr<views::Textfield> GenerateUsernameEditable(
     const autofill::PasswordForm& form);
 
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
index 6580c38..6475f3797 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
+++ b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
@@ -233,14 +233,19 @@
 }
 
 // Creates a dropdown from the other possible passwords.
-// The items are made of '*'s.
+// The items are made of '*'s if not visible.
 std::unique_ptr<views::Combobox> GeneratePasswordDropdownView(
-    const autofill::PasswordForm& form) {
+    const autofill::PasswordForm& form,
+    bool visible) {
   DCHECK(!form.other_possible_passwords.empty());
   std::vector<base::string16> passwords;
-  for (const base::string16& possible_password :
-       form.other_possible_passwords) {
-    passwords.push_back(base::string16(possible_password.length(), '*'));
+  if (visible) {
+    passwords = form.other_possible_passwords;
+  } else {
+    for (const base::string16& possible_password :
+         form.other_possible_passwords) {
+      passwords.push_back(base::string16(possible_password.length(), '*'));
+    }
   }
   std::unique_ptr<views::Combobox> combobox = std::make_unique<views::Combobox>(
       std::make_unique<ui::SimpleComboboxModel>(passwords));
@@ -420,20 +425,27 @@
   ~PendingView() override;
 
  private:
-  void CreateAndSetLayout();
   // views::ButtonListener:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
 
+  // View:
   gfx::Size CalculatePreferredSize() const override;
 
+  void CreateAndSetLayout();
+  void CreatePasswordField();
+  void TogglePasswordVisibility();
+  void UpdateUsernameAndPasswordInModel();
+
   ManagePasswordsBubbleView* parent_;
 
   views::Button* save_button_;
   views::Button* never_button_;
-  // Those views can be recreated, thus they are owned by the client.
-  std::unique_ptr<views::View> username_field_;
+  views::View* username_field_;
+  views::ToggleImageButton* password_view_button_;
+  // Can be recreated, thus owned by the client.
   std::unique_ptr<views::View> password_field_;
-  std::unique_ptr<views::ToggleImageButton> password_view_button_;
+
+  bool password_visible_;
 
   DISALLOW_COPY_AND_ASSIGN(PendingView);
 };
@@ -444,99 +456,43 @@
       save_button_(nullptr),
       never_button_(nullptr),
       username_field_(nullptr),
-      password_field_(nullptr),
-      password_view_button_(nullptr) {
+      password_view_button_(nullptr),
+      password_visible_(false) {
+  // Create credentials row.
+  const autofill::PasswordForm& password_form =
+      parent_->model()->pending_password();
+  if (base::FeatureList::IsEnabled(
+          password_manager::features::kEnableUsernameCorrection)) {
+    username_field_ = GenerateUsernameEditable(password_form).release();
+  } else {
+    username_field_ = GenerateUsernameLabel(password_form).release();
+  }
+
+  CreatePasswordField();
+
+  if (base::FeatureList::IsEnabled(
+          password_manager::features::kEnablePasswordSelection)) {
+    password_view_button_ = GeneratePasswordViewButton(this).release();
+  }
+
+  // Create buttons.
+  save_button_ = views::MdTextButton::CreateSecondaryUiBlueButton(
+      this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SAVE_BUTTON));
+  never_button_ = views::MdTextButton::CreateSecondaryUiButton(
+      this,
+      l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_BUBBLE_BLACKLIST_BUTTON));
+
   CreateAndSetLayout();
   parent_->set_initially_focused_view(save_button_);
 }
 
-void ManagePasswordsBubbleView::PendingView::CreateAndSetLayout() {
-  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
-  layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
-
-  // Create buttons.
-  if (!save_button_) {
-    save_button_ = views::MdTextButton::CreateSecondaryUiBlueButton(
-        this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SAVE_BUTTON));
-  }
-  if (!never_button_) {
-    never_button_ = views::MdTextButton::CreateSecondaryUiButton(
-        this, l10n_util::GetStringUTF16(
-                  IDS_PASSWORD_MANAGER_BUBBLE_BLACKLIST_BUTTON));
-  }
-
-  // Credentials row.
-  const autofill::PasswordForm& password_form =
-      parent_->model()->pending_password();
-  DCHECK(!username_field_);
-  if (base::FeatureList::IsEnabled(
-          password_manager::features::kEnableUsernameCorrection)) {
-    username_field_ = GenerateUsernameEditable(password_form);
-  } else {
-    username_field_ = GenerateUsernameLabel(password_form);
-  }
-  username_field_->set_owned_by_client();
-
-  DCHECK(!password_field_);
-  const bool enable_password_selection = base::FeatureList::IsEnabled(
-      password_manager::features::kEnablePasswordSelection);
-  if (enable_password_selection &&
-      password_form.other_possible_passwords.size() > 1) {
-    password_field_ = GeneratePasswordDropdownView(password_form);
-  } else {
-    password_field_ = GeneratePasswordLabel(password_form);
-  }
-  password_field_->set_owned_by_client();
-
-  DCHECK(!password_view_button_);
-  if (enable_password_selection) {
-    password_view_button_ = GeneratePasswordViewButton(this);
-    password_view_button_->set_owned_by_client();
-  }
-
-  BuildCredentialRow(layout, username_field_.get(), password_field_.get(),
-                     password_view_button_.get());
-  layout->AddPaddingRow(
-      0, ChromeLayoutProvider::Get()->GetDistanceMetric(
-             views::DISTANCE_DIALOG_CONTENT_MARGIN_BOTTOM_CONTROL));
-
-  // Button row.
-  BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET);
-  layout->StartRow(0, DOUBLE_BUTTON_COLUMN_SET);
-  layout->AddView(save_button_);
-  layout->AddView(never_button_);
-}
-
-ManagePasswordsBubbleView::PendingView::~PendingView() {
-}
+ManagePasswordsBubbleView::PendingView::~PendingView() = default;
 
 void ManagePasswordsBubbleView::PendingView::ButtonPressed(
     views::Button* sender,
     const ui::Event& event) {
   if (sender == save_button_) {
-    const bool username_editable = base::FeatureList::IsEnabled(
-        password_manager::features::kEnableUsernameCorrection);
-    const bool password_editable = base::FeatureList::IsEnabled(
-        password_manager::features::kEnablePasswordSelection);
-    if (username_editable || password_editable) {
-      base::string16 new_username =
-          parent_->model()->pending_password().username_value;
-      base::string16 new_password =
-          parent_->model()->pending_password().password_value;
-      if (username_editable) {
-        new_username =
-            static_cast<views::Textfield*>(username_field_.get())->text();
-      }
-      if (password_editable &&
-          parent_->model()->pending_password().other_possible_passwords.size() >
-              1) {
-        new_password =
-            parent_->model()->pending_password().other_possible_passwords.at(
-                static_cast<views::Combobox*>(password_field_.get())
-                    ->selected_index());
-      }
-      parent_->model()->OnCredentialEdited(new_username, new_password);
-    }
+    UpdateUsernameAndPasswordInModel();
     parent_->model()->OnSaveClicked();
     if (parent_->model()->ReplaceToShowPromotionIfNeeded()) {
       parent_->Refresh();
@@ -544,8 +500,8 @@
     }
   } else if (sender == never_button_) {
     parent_->model()->OnNeverForThisSiteClicked();
-  } else if (sender == password_view_button_.get()) {
-    // TODO(https://crbug.com/753806): Implement making passwords visible logic.
+  } else if (sender == password_view_button_) {
+    TogglePasswordVisibility();
     return;
   } else {
     NOTREACHED();
@@ -561,6 +517,75 @@
                        this, kDesiredBubbleWidth));
 }
 
+void ManagePasswordsBubbleView::PendingView::CreateAndSetLayout() {
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
+  layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
+
+  BuildCredentialRow(layout, username_field_, password_field_.get(),
+                     password_view_button_);
+  layout->AddPaddingRow(
+      0, ChromeLayoutProvider::Get()->GetDistanceMetric(
+             views::DISTANCE_DIALOG_CONTENT_MARGIN_BOTTOM_CONTROL));
+
+  // Button row.
+  BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET);
+  layout->StartRow(0, DOUBLE_BUTTON_COLUMN_SET);
+  layout->AddView(save_button_);
+  layout->AddView(never_button_);
+}
+
+void ManagePasswordsBubbleView::PendingView::CreatePasswordField() {
+  const bool enable_password_selection = base::FeatureList::IsEnabled(
+      password_manager::features::kEnablePasswordSelection);
+  const autofill::PasswordForm& password_form =
+      parent_->model()->pending_password();
+  if (enable_password_selection &&
+      password_form.other_possible_passwords.size() > 1) {
+    password_field_ =
+        GeneratePasswordDropdownView(password_form, password_visible_);
+  } else {
+    password_field_ = GeneratePasswordLabel(password_form, password_visible_);
+  }
+  password_field_->set_owned_by_client();
+}
+
+void ManagePasswordsBubbleView::PendingView::TogglePasswordVisibility() {
+  UpdateUsernameAndPasswordInModel();
+  password_visible_ = !password_visible_;
+  password_view_button_->SetToggled(password_visible_);
+  password_field_.reset();
+  CreatePasswordField();
+  CreateAndSetLayout();
+  Layout();
+  parent_->SizeToContents();
+}
+
+void ManagePasswordsBubbleView::PendingView::
+    UpdateUsernameAndPasswordInModel() {
+  const bool username_editable = base::FeatureList::IsEnabled(
+      password_manager::features::kEnableUsernameCorrection);
+  const bool password_editable = base::FeatureList::IsEnabled(
+      password_manager::features::kEnablePasswordSelection);
+  if (!username_editable && !password_editable)
+    return;
+  base::string16 new_username =
+      parent_->model()->pending_password().username_value;
+  base::string16 new_password =
+      parent_->model()->pending_password().password_value;
+  if (username_editable) {
+    new_username = static_cast<views::Textfield*>(username_field_)->text();
+  }
+  if (password_editable &&
+      parent_->model()->pending_password().other_possible_passwords.size() >
+          1) {
+    new_password =
+        parent_->model()->pending_password().other_possible_passwords.at(
+            static_cast<views::Combobox*>(password_field_.get())
+                ->selected_index());
+  }
+  parent_->model()->OnCredentialEdited(new_username, new_password);
+}
+
 // ManagePasswordsBubbleView::ManageView --------------------------------------
 
 // A view offering the user a list of their currently saved credentials
@@ -832,7 +857,8 @@
     const autofill::PasswordForm& password_form =
         parent_->model()->pending_password();
     BuildCredentialRow(layout, GenerateUsernameLabel(password_form).release(),
-                       GeneratePasswordLabel(password_form).release(), nullptr);
+                       GeneratePasswordLabel(password_form, false).release(),
+                       nullptr);
   }
   layout->AddPaddingRow(
       0, layout_provider->GetDistanceMetric(
diff --git a/chrome/browser/ui/views/payments/payment_request_shipping_address_instance_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_shipping_address_instance_browsertest.cc
new file mode 100644
index 0000000..b8cc0fd
--- /dev/null
+++ b/chrome/browser/ui/views/payments/payment_request_shipping_address_instance_browsertest.cc
@@ -0,0 +1,48 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/views/payments/payment_request_browsertest_base.h"
+#include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/credit_card.h"
+
+namespace payments {
+namespace {
+
+class PaymentRequestShippingAddressInstanceTest
+    : public PaymentRequestBrowserTestBase {
+ protected:
+  PaymentRequestShippingAddressInstanceTest()
+      : PaymentRequestBrowserTestBase(
+            "/payment_request_shipping_address_instance_test.html") {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PaymentRequestShippingAddressInstanceTest);
+};
+
+// If the page creates multiple PaymentRequest objects, it should not crash.
+IN_PROC_BROWSER_TEST_F(PaymentRequestShippingAddressInstanceTest,
+                       ShouldBeSameInstance) {
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+  autofill::CreditCard card = autofill::test::GetCreditCard();
+  card.set_billing_address_id(billing_address.guid());
+  AddCreditCard(card);
+
+  InvokePaymentRequestUI();
+
+  ResetEventObserver(DialogEvent::DIALOG_CLOSED);
+
+  PayWithCreditCardAndWait(base::ASCIIToUTF16("123"));
+
+  WaitForObservedEvent();
+
+  ExpectBodyContains({"Same instance: true"});
+}
+
+}  // namespace
+}  // namespace payments
diff --git a/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
index ae698ed9..8608f08 100644
--- a/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
@@ -11,12 +11,10 @@
 #include "chrome/browser/chromeos/login/help_app_launcher.h"
 #include "chrome/browser/chromeos/login/oobe_screen.h"
 #include "chrome/browser/chromeos/login/screens/reset_screen.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/dbus/session_manager_client.h"
 #include "components/login/localized_values_builder.h"
-#include "components/prefs/pref_registry_simple.h"
 #include "components/strings/grit/components_strings.h"
 
 namespace {
@@ -95,11 +93,6 @@
   builder->Add("confirmResetButton", IDS_RESET_SCREEN_POPUP_CONFIRM_BUTTON);
 }
 
-// static
-void ResetScreenHandler::RegisterPrefs(PrefRegistrySimple* registry) {
-  registry->RegisterBooleanPref(prefs::kFactoryResetRequested, false);
-}
-
 void ResetScreenHandler::Initialize() {
   if (!page_is_ready())
     return;
diff --git a/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h
index ff95443..f6dc3f2 100644
--- a/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h
@@ -11,8 +11,6 @@
 #include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
 #include "content/public/browser/web_ui.h"
 
-class PrefRegistrySimple;
-
 namespace chromeos {
 
 // WebUI implementation of ResetScreenActor.
@@ -33,9 +31,6 @@
       ::login::LocalizedValuesBuilder* builder) override;
   void Initialize() override;
 
-  // Registers Local State preferences.
-  static void RegisterPrefs(PrefRegistrySimple* registry);
-
  private:
   ResetScreen* screen_ = nullptr;
 
diff --git a/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc b/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc
index 4984dddb..bce9b001 100644
--- a/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc
+++ b/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc
@@ -146,6 +146,8 @@
                           IDR_MD_BOOKMARKS_COMMAND_MANAGER_JS);
   source->AddResourcePath("constants.html", IDR_MD_BOOKMARKS_CONSTANTS_HTML);
   source->AddResourcePath("constants.js", IDR_MD_BOOKMARKS_CONSTANTS_JS);
+  source->AddResourcePath("debouncer.html", IDR_MD_BOOKMARKS_DEBOUNCER_HTML);
+  source->AddResourcePath("debouncer.js", IDR_MD_BOOKMARKS_DEBOUNCER_JS);
   source->AddResourcePath("dialog_focus_manager.html",
                           IDR_MD_BOOKMARKS_DIALOG_FOCUS_MANAGER_HTML);
   source->AddResourcePath("dialog_focus_manager.js",
@@ -182,9 +184,6 @@
   source->AddResourcePath("store_client.html",
                           IDR_MD_BOOKMARKS_STORE_CLIENT_HTML);
   source->AddResourcePath("store_client.js", IDR_MD_BOOKMARKS_STORE_CLIENT_JS);
-  source->AddResourcePath("timer_proxy.html",
-                          IDR_MD_BOOKMARKS_TIMER_PROXY_HTML);
-  source->AddResourcePath("timer_proxy.js", IDR_MD_BOOKMARKS_TIMER_PROXY_JS);
   source->AddResourcePath("toast_manager.html",
                           IDR_MD_BOOKMARKS_TOAST_MANAGER_HTML);
   source->AddResourcePath("toast_manager.js",
diff --git a/chrome/browser/ui/webui/settings/chromeos/android_apps_handler.cc b/chrome/browser/ui/webui/settings/chromeos/android_apps_handler.cc
index fccd102..c84e019 100644
--- a/chrome/browser/ui/webui/settings/chromeos/android_apps_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/android_apps_handler.cc
@@ -22,6 +22,7 @@
 AndroidAppsHandler::~AndroidAppsHandler() {}
 
 void AndroidAppsHandler::RegisterMessages() {
+  // Note: requestAndroidAppsInfo must be called before observers will be added.
   web_ui()->RegisterMessageCallback(
       "requestAndroidAppsInfo",
       base::Bind(&AndroidAppsHandler::HandleRequestAndroidAppsInfo,
@@ -82,11 +83,11 @@
 
 void AndroidAppsHandler::HandleRequestAndroidAppsInfo(
     const base::ListValue* args) {
+  AllowJavascript();
   SendAndroidAppsInfo();
 }
 
 void AndroidAppsHandler::SendAndroidAppsInfo() {
-  AllowJavascript();
   std::unique_ptr<base::DictionaryValue> info = BuildAndroidAppsInfo();
   FireWebUIListener("android-apps-info-update", *info);
 }
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_stylus_handler.cc b/chrome/browser/ui/webui/settings/chromeos/device_stylus_handler.cc
index ea381ab..8f7c191 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_stylus_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/device_stylus_handler.cc
@@ -35,23 +35,26 @@
 void StylusHandler::RegisterMessages() {
   DCHECK(web_ui());
 
+  // Note: initializeStylusSettings must be called before observers will be
+  // added.
   web_ui()->RegisterMessageCallback(
       "initializeStylusSettings",
       base::Bind(&StylusHandler::HandleInitialize, base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
       "requestNoteTakingApps",
-      base::Bind(&StylusHandler::RequestApps, base::Unretained(this)));
+      base::Bind(&StylusHandler::HandleRequestApps, base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
       "setPreferredNoteTakingApp",
-      base::Bind(&StylusHandler::SetPreferredNoteTakingApp,
+      base::Bind(&StylusHandler::HandleSetPreferredNoteTakingApp,
                  base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
       "setPreferredNoteTakingAppEnabledOnLockScreen",
-      base::Bind(&StylusHandler::SetPreferredNoteTakingAppEnabledOnLockScreen,
-                 base::Unretained(this)));
+      base::Bind(
+          &StylusHandler::HandleSetPreferredNoteTakingAppEnabledOnLockScreen,
+          base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      "showPlayStoreApps",
-      base::Bind(&StylusHandler::ShowPlayStoreApps, base::Unretained(this)));
+      "showPlayStoreApps", base::Bind(&StylusHandler::HandleShowPlayStoreApps,
+                                      base::Unretained(this)));
 }
 
 void StylusHandler::OnJavascriptAllowed() {
@@ -107,12 +110,13 @@
                     base::Value(waiting_for_android));
 }
 
-void StylusHandler::RequestApps(const base::ListValue* unused_args) {
+void StylusHandler::HandleRequestApps(const base::ListValue* unused_args) {
   AllowJavascript();
   UpdateNoteTakingApps();
 }
 
-void StylusHandler::SetPreferredNoteTakingApp(const base::ListValue* args) {
+void StylusHandler::HandleSetPreferredNoteTakingApp(
+    const base::ListValue* args) {
   std::string app_id;
   CHECK(args->GetString(0, &app_id));
 
@@ -127,7 +131,7 @@
                                            app_id);
 }
 
-void StylusHandler::SetPreferredNoteTakingAppEnabledOnLockScreen(
+void StylusHandler::HandleSetPreferredNoteTakingAppEnabledOnLockScreen(
     const base::ListValue* args) {
   bool enabled = false;
   CHECK(args->GetBoolean(0, &enabled));
@@ -148,7 +152,7 @@
                     base::Value(ash::palette_utils::HasStylusInput()));
 }
 
-void StylusHandler::ShowPlayStoreApps(const base::ListValue* args) {
+void StylusHandler::HandleShowPlayStoreApps(const base::ListValue* args) {
   std::string apps_url;
   args->GetString(0, &apps_url);
   Profile* profile = Profile::FromWebUI(web_ui());
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_stylus_handler.h b/chrome/browser/ui/webui/settings/chromeos/device_stylus_handler.h
index 8f2800d..676d939 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_stylus_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/device_stylus_handler.h
@@ -47,18 +47,17 @@
 
  private:
   void UpdateNoteTakingApps();
-  void RequestApps(const base::ListValue* unused_args);
-  void SetPreferredNoteTakingApp(const base::ListValue* args);
-  void SetPreferredNoteTakingAppEnabledOnLockScreen(
+  void HandleRequestApps(const base::ListValue* unused_args);
+  void HandleSetPreferredNoteTakingApp(const base::ListValue* args);
+  void HandleSetPreferredNoteTakingAppEnabledOnLockScreen(
       const base::ListValue* args);
-
-  // Called by JS to request a |SendHasStylus| call.
   void HandleInitialize(const base::ListValue* args);
+
   // Enables or disables the stylus UI section.
   void SendHasStylus();
 
   // Called by JS to show the Play Store Android app.
-  void ShowPlayStoreApps(const base::ListValue* args);
+  void HandleShowPlayStoreApps(const base::ListValue* args);
 
   // IDs of available note-taking apps.
   std::set<std::string> note_taking_app_ids_;
diff --git a/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc b/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc
index 4e9147b..71d2e40 100644
--- a/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc
@@ -63,17 +63,13 @@
   binding_.Bind(mojo::MakeRequest(&observer));
   fp_service_->AddFingerprintObserver(std::move(observer));
   user_id_ = ProfileHelper::Get()->GetUserIdHashFromProfile(profile);
-  // SessionManager may not exist in some tests.
-  if (SessionManager::Get())
-    SessionManager::Get()->AddObserver(this);
 }
 
 FingerprintHandler::~FingerprintHandler() {
-  if (SessionManager::Get())
-    SessionManager::Get()->RemoveObserver(this);
 }
 
 void FingerprintHandler::RegisterMessages() {
+  // Note: getFingerprintsList must be called before observers will be added.
   web_ui()->RegisterMessageCallback(
       "getFingerprintsList",
       base::Bind(&FingerprintHandler::HandleGetFingerprintsList,
@@ -112,16 +108,22 @@
                  base::Unretained(this)));
 }
 
-void FingerprintHandler::OnJavascriptAllowed() {}
+void FingerprintHandler::OnJavascriptAllowed() {
+  // SessionManager may not exist in some tests.
+  if (SessionManager::Get())
+    SessionManager::Get()->AddObserver(this);
+}
 
-void FingerprintHandler::OnJavascriptDisallowed() {}
+void FingerprintHandler::OnJavascriptDisallowed() {
+  if (SessionManager::Get())
+    SessionManager::Get()->RemoveObserver(this);
+}
 
 void FingerprintHandler::OnRestarted() {}
 
 void FingerprintHandler::OnEnrollScanDone(uint32_t scan_result,
                                           bool enroll_session_complete,
                                           int percent_complete) {
-  AllowJavascript();
   auto scan_attempt = base::MakeUnique<base::DictionaryValue>();
   scan_attempt->SetInteger("result", scan_result);
   scan_attempt->SetBoolean("isComplete", enroll_session_complete);
@@ -142,7 +144,6 @@
   if (it == matches.end() || it->second.size() < 1)
     return;
 
-  AllowJavascript();
   auto fingerprint_ids = base::MakeUnique<base::ListValue>();
 
   for (const std::string& matched_path : it->second) {
@@ -165,7 +166,6 @@
 void FingerprintHandler::OnSessionStateChanged() {
   SessionState state = SessionManager::Get()->session_state();
 
-  AllowJavascript();
   FireWebUIListener("on-screen-locked",
                     base::Value(state == SessionState::LOCKED));
 }
@@ -176,6 +176,7 @@
   std::string callback_id;
   CHECK(args->GetString(0, &callback_id));
 
+  AllowJavascript();
   fp_service_->GetRecordsForUser(
       user_id_, base::Bind(&FingerprintHandler::OnGetFingerprintsList,
                            weak_ptr_factory_.GetWeakPtr(), callback_id));
@@ -185,7 +186,6 @@
     const std::string& callback_id,
     const std::unordered_map<std::string, std::string>&
         fingerprints_list_mapping) {
-  AllowJavascript();
   fingerprints_labels_.clear();
   fingerprints_paths_.clear();
   for (auto it = fingerprints_list_mapping.begin();
@@ -203,8 +203,6 @@
 }
 
 void FingerprintHandler::HandleGetNumFingerprints(const base::ListValue* args) {
-  AllowJavascript();
-
   CHECK_EQ(1U, args->GetSize());
   std::string callback_id;
   CHECK(args->GetString(0, &callback_id));
@@ -212,6 +210,7 @@
   int fingerprints_num =
       profile_->GetPrefs()->GetInteger(prefs::kQuickUnlockFingerprintRecord);
 
+  AllowJavascript();
   ResolveJavascriptCallback(base::Value(callback_id),
                             base::Value(fingerprints_num));
 }
@@ -247,8 +246,9 @@
   int index;
   CHECK(args->GetString(0, &callback_id));
   CHECK(args->GetInteger(1, &index));
-
   DCHECK_LT(index, static_cast<int>(fingerprints_labels_.size()));
+
+  AllowJavascript();
   fp_service_->RequestRecordLabel(
       fingerprints_paths_[index],
       base::Bind(&FingerprintHandler::OnRequestRecordLabel,
@@ -257,7 +257,6 @@
 
 void FingerprintHandler::OnRequestRecordLabel(const std::string& callback_id,
                                               const std::string& label) {
-  AllowJavascript();
   ResolveJavascriptCallback(base::Value(callback_id), base::Value(label));
 }
 
@@ -267,8 +266,9 @@
   int index;
   CHECK(args->GetString(0, &callback_id));
   CHECK(args->GetInteger(1, &index));
-
   DCHECK_LT(index, static_cast<int>(fingerprints_paths_.size()));
+
+  AllowJavascript();
   fp_service_->RemoveRecord(
       fingerprints_paths_[index],
       base::Bind(&FingerprintHandler::OnRemoveRecord,
@@ -279,7 +279,6 @@
                                         bool success) {
   if (!success)
     LOG(ERROR) << "Failed to remove fingerprint record.";
-  AllowJavascript();
   ResolveJavascriptCallback(base::Value(callback_id), base::Value(success));
 }
 
@@ -294,6 +293,7 @@
   CHECK(args->GetInteger(1, &index));
   CHECK(args->GetString(2, &new_label));
 
+  AllowJavascript();
   fp_service_->SetRecordLabel(
       new_label, fingerprints_paths_[index],
       base::Bind(&FingerprintHandler::OnSetRecordLabel,
@@ -304,7 +304,6 @@
                                           bool success) {
   if (!success)
     LOG(ERROR) << "Failed to set fingerprint record label.";
-  AllowJavascript();
   ResolveJavascriptCallback(base::Value(callback_id), base::Value(success));
 }
 
diff --git a/chrome/browser/vr/BUILD.gn b/chrome/browser/vr/BUILD.gn
index 11ee5162..747ff075 100644
--- a/chrome/browser/vr/BUILD.gn
+++ b/chrome/browser/vr/BUILD.gn
@@ -16,6 +16,7 @@
   sources = [
     "animation_player.cc",
     "animation_player.h",
+    "browser_ui_interface.h",
     "color_scheme.cc",
     "color_scheme.h",
     "databinding/binding.h",
diff --git a/chrome/browser/vr/browser_ui_interface.h b/chrome/browser/vr/browser_ui_interface.h
new file mode 100644
index 0000000..17a682e
--- /dev/null
+++ b/chrome/browser/vr/browser_ui_interface.h
@@ -0,0 +1,49 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_VR_BROWSER_UI_INTERFACE_H_
+#define CHROME_BROWSER_VR_BROWSER_UI_INTERFACE_H_
+
+#include "chrome/browser/vr/ui_unsupported_mode.h"
+#include "components/security_state/core/security_state.h"
+
+namespace vr {
+
+struct ToolbarState;
+
+// The browser communicates state changes to the VR UI via this interface.
+class BrowserUiInterface {
+ public:
+  virtual ~BrowserUiInterface() {}
+
+  virtual void SetWebVrMode(bool enabled, bool show_toast) = 0;
+  virtual void SetFullscreen(bool enabled) = 0;
+  virtual void SetToolbarState(const ToolbarState& state) = 0;
+  virtual void SetIncognito(bool enabled) = 0;
+  virtual void SetWebVrSecureOrigin(bool secure) = 0;
+  virtual void SetLoading(bool loading) = 0;
+  virtual void SetLoadProgress(float progress) = 0;
+  virtual void SetIsExiting() = 0;
+  virtual void SetHistoryButtonsEnabled(bool can_go_back,
+                                        bool can_go_forward) = 0;
+  virtual void SetVideoCapturingIndicator(bool enabled) = 0;
+  virtual void SetScreenCapturingIndicator(bool enabled) = 0;
+  virtual void SetAudioCapturingIndicator(bool enabled) = 0;
+  virtual void SetBluetoothConnectedIndicator(bool enabled) = 0;
+  virtual void SetLocationAccessIndicator(bool enabled) = 0;
+  virtual void SetExitVrPromptEnabled(bool enabled,
+                                      UiUnsupportedMode reason) = 0;
+
+  // Tab handling.
+  virtual void AppendToTabList(bool incognito,
+                               int id,
+                               const base::string16& title) {}
+  virtual void FlushTabList() {}
+  virtual void UpdateTab(bool incognito, int id, const std::string& title) {}
+  virtual void RemoveTab(bool incognito, int id) {}
+};
+
+}  // namespace vr
+
+#endif  // CHROME_BROWSER_VR_BROWSER_UI_INTERFACE_H_
diff --git a/chrome/browser/vr/toolbar_helper.cc b/chrome/browser/vr/toolbar_helper.cc
index e7f5e12c5..2212ff9 100644
--- a/chrome/browser/vr/toolbar_helper.cc
+++ b/chrome/browser/vr/toolbar_helper.cc
@@ -19,7 +19,8 @@
 
 }  // namespace
 
-ToolbarHelper::ToolbarHelper(UiInterface* ui, ToolbarModelDelegate* delegate)
+ToolbarHelper::ToolbarHelper(BrowserUiInterface* ui,
+                             ToolbarModelDelegate* delegate)
     : ui_(ui),
       toolbar_model_(
           base::MakeUnique<ToolbarModelImpl>(delegate, kMaxURLDisplayChars)) {}
diff --git a/chrome/browser/vr/toolbar_helper.h b/chrome/browser/vr/toolbar_helper.h
index d29e184c..7c9d655 100644
--- a/chrome/browser/vr/toolbar_helper.h
+++ b/chrome/browser/vr/toolbar_helper.h
@@ -5,28 +5,28 @@
 #ifndef CHROME_BROWSER_VR_TOOLBAR_HELPER_H_
 #define CHROME_BROWSER_VR_TOOLBAR_HELPER_H_
 
+#include "chrome/browser/vr/browser_ui_interface.h"
 #include "chrome/browser/vr/toolbar_state.h"
-#include "chrome/browser/vr/ui_interface.h"
 
 class ToolbarModel;
 class ToolbarModelDelegate;
 
 namespace vr {
 
-class UiInterface;
+class BrowserUiInterface;
 
 // This class houses an instance of ToolbarModel, and queries it when requested,
 // passing a snapshot of the toolbar state to the UI when necessary.
 class ToolbarHelper {
  public:
-  ToolbarHelper(UiInterface* ui, ToolbarModelDelegate* delegate);
+  ToolbarHelper(BrowserUiInterface* ui, ToolbarModelDelegate* delegate);
   virtual ~ToolbarHelper();
 
   // Poll ToolbarModel and post an update to the UI if state has changed.
   void Update();
 
  private:
-  UiInterface* ui_;
+  BrowserUiInterface* ui_;
   std::unique_ptr<ToolbarModel> toolbar_model_;
   ToolbarState current_state_;
 };
diff --git a/chrome/browser/vr/ui_interface.h b/chrome/browser/vr/ui_interface.h
index 62e8b1d..89cb90e 100644
--- a/chrome/browser/vr/ui_interface.h
+++ b/chrome/browser/vr/ui_interface.h
@@ -5,15 +5,13 @@
 #ifndef CHROME_BROWSER_VR_UI_INTERFACE_H_
 #define CHROME_BROWSER_VR_UI_INTERFACE_H_
 
-#include "chrome/browser/vr/ui_unsupported_mode.h"
-#include "components/security_state/core/security_state.h"
+namespace gfx {
+class Transform;
+}
 
 namespace vr {
 
-struct ToolbarState;
-
-// This class manages the communication of browser state from VR shell to the
-// HTML UI. State information is asynchronous and unidirectional.
+// This is the platform-specific interface to the VR UI.
 class UiInterface {
  public:
   enum Direction {
@@ -26,33 +24,13 @@
 
   virtual ~UiInterface() {}
 
-  virtual void SetWebVrMode(bool enabled, bool show_toast) = 0;
-  virtual void SetFullscreen(bool enabled) = 0;
-  virtual void SetToolbarState(const ToolbarState& state) = 0;
-  virtual void SetIncognito(bool enabled) = 0;
-  virtual void SetWebVrSecureOrigin(bool secure) = 0;
-  virtual void SetLoading(bool loading) = 0;
-  virtual void SetLoadProgress(float progress) = 0;
-  virtual void SetIsExiting() = 0;
-  virtual void SetHistoryButtonsEnabled(bool can_go_back,
-                                        bool can_go_forward) = 0;
-  virtual void SetVideoCapturingIndicator(bool enabled) = 0;
-  virtual void SetScreenCapturingIndicator(bool enabled) = 0;
-  virtual void SetAudioCapturingIndicator(bool enabled) = 0;
-  virtual void SetBluetoothConnectedIndicator(bool enabled) = 0;
-  virtual void SetLocationAccessIndicator(bool enabled) = 0;
-
-  // Tab handling.
-  virtual void InitTabList() {}
-  virtual void AppendToTabList(bool incognito,
-                               int id,
-                               const base::string16& title) {}
-  virtual void FlushTabList() {}
-  virtual void UpdateTab(bool incognito, int id, const std::string& title) {}
-  virtual void RemoveTab(bool incognito, int id) {}
-
-  virtual void SetExitVrPromptEnabled(bool enabled,
-                                      UiUnsupportedMode reason) = 0;
+  virtual void OnGlInitialized(unsigned int content_texture_id) = 0;
+  virtual void OnAppButtonClicked() = 0;
+  virtual void OnAppButtonGesturePerformed(
+      UiInterface::Direction direction) = 0;
+  virtual void OnProjMatrixChanged(const gfx::Transform& proj_matrix) = 0;
+  virtual void OnWebVrFrameAvailable() = 0;
+  virtual void OnWebVrTimedOut() = 0;
 };
 
 }  // namespace vr
diff --git a/chrome/browser/vr/ui_scene_manager.h b/chrome/browser/vr/ui_scene_manager.h
index 55b7a36..791bcf5 100644
--- a/chrome/browser/vr/ui_scene_manager.h
+++ b/chrome/browser/vr/ui_scene_manager.h
@@ -9,6 +9,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/values.h"
+#include "chrome/browser/vr/browser_ui_interface.h"
 #include "chrome/browser/vr/color_scheme.h"
 #include "chrome/browser/vr/elements/simple_textured_element.h"
 #include "chrome/browser/vr/ui_interface.h"
@@ -77,7 +78,7 @@
 //
 // TODO(vollick): The above hierarchy is complex, brittle, and would be easier
 // to manage if it were specified in a declarative format.
-class UiSceneManager {
+class UiSceneManager : public UiInterface, public BrowserUiInterface {
  public:
   UiSceneManager(UiBrowserInterface* browser,
                  UiScene* scene,
@@ -85,35 +86,36 @@
                  bool in_cct,
                  bool in_web_vr,
                  bool web_vr_autopresentation_expected);
-  ~UiSceneManager();
+
+  ~UiSceneManager() override;
 
   base::WeakPtr<UiSceneManager> GetWeakPtr();
 
-  void SetFullscreen(bool fullscreen);
-  void SetIncognito(bool incognito);
-  void SetToolbarState(const ToolbarState& state);
-  void SetWebVrSecureOrigin(bool secure);
-  void SetWebVrMode(bool web_vr, bool show_toast);
-  void SetLoading(bool loading);
-  void SetLoadProgress(float progress);
-  void SetIsExiting();
-  void SetVideoCapturingIndicator(bool enabled);
-  void SetScreenCapturingIndicator(bool enabled);
-  void SetAudioCapturingIndicator(bool enabled);
-  void SetLocationAccessIndicator(bool enabled);
-  void SetBluetoothConnectedIndicator(bool enabled);
+  // UiBrowserInterface.
+  void SetFullscreen(bool fullscreen) override;
+  void SetIncognito(bool incognito) override;
+  void SetToolbarState(const ToolbarState& state) override;
+  void SetWebVrSecureOrigin(bool secure) override;
+  void SetWebVrMode(bool web_vr, bool show_toast) override;
+  void SetLoading(bool loading) override;
+  void SetLoadProgress(float progress) override;
+  void SetIsExiting() override;
+  void SetVideoCapturingIndicator(bool enabled) override;
+  void SetScreenCapturingIndicator(bool enabled) override;
+  void SetAudioCapturingIndicator(bool enabled) override;
+  void SetLocationAccessIndicator(bool enabled) override;
+  void SetBluetoothConnectedIndicator(bool enabled) override;
+  void SetHistoryButtonsEnabled(bool can_go_back, bool can_go_forward) override;
 
-  // These methods are currently stubbed.
-  void SetHistoryButtonsEnabled(bool can_go_back, bool can_go_forward);
+  // UiInterface.
+  void OnGlInitialized(unsigned int content_texture_id) override;
+  void OnAppButtonClicked() override;
+  void OnAppButtonGesturePerformed(UiInterface::Direction direction) override;
+  void OnProjMatrixChanged(const gfx::Transform& proj_matrix) override;
+  void OnWebVrFrameAvailable() override;
+  void OnWebVrTimedOut() override;
 
-  void OnGlInitialized(unsigned int content_texture_id);
-  void OnAppButtonClicked();
-  void OnAppButtonGesturePerformed(UiInterface::Direction direction);
-  void OnWebVrFrameAvailable();
-  void OnWebVrTimedOut();
-  void OnProjMatrixChanged(const gfx::Transform& proj_matrix);
-
-  void SetExitVrPromptEnabled(bool enabled, UiUnsupportedMode reason);
+  void SetExitVrPromptEnabled(bool enabled, UiUnsupportedMode reason) override;
 
   void OnSecurityIconClickedForTesting();
   void OnExitPromptChoiceForTesting(bool chose_exit);
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index effe002..370ee3b 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1892,6 +1892,11 @@
 // Indicates that factory reset was requested from options page or reset screen.
 const char kFactoryResetRequested[] = "FactoryResetRequested";
 
+// Indicates that a TPM firmware update should be requested when triggering a
+// factory reset.
+const char kFactoryResetTPMFirmwareUpdateRequested[] =
+    "FactoryResetTPMFirmwareUpdateRequested";
+
 // Indicates that debugging features were requested from oobe screen.
 const char kDebuggingFeaturesRequested[] = "DebuggingFeaturesRequested";
 
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 95acc6b..acb4e9e9 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -708,6 +708,7 @@
 extern const char kDevicePolicyRefreshRate[];
 
 extern const char kFactoryResetRequested[];
+extern const char kFactoryResetTPMFirmwareUpdateRequested[];
 extern const char kDebuggingFeaturesRequested[];
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 67e97b5d..07beb9d 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2098,6 +2098,7 @@
         "../browser/ui/views/payments/payment_request_journey_logger_browsertest.cc",
         "../browser/ui/views/payments/payment_request_payment_app_browsertest.cc",
         "../browser/ui/views/payments/payment_request_payment_response_browsertest.cc",
+        "../browser/ui/views/payments/payment_request_shipping_address_instance_browsertest.cc",
         "../browser/ui/views/payments/payment_request_use_stats_browsertest.cc",
         "../browser/ui/views/payments/payment_sheet_view_controller_browsertest.cc",
         "../browser/ui/views/payments/profile_list_view_controller_browsertest.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/SuggestionsDependenciesRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/SuggestionsDependenciesRule.java
index b687532..b8282d6 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/SuggestionsDependenciesRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/SuggestionsDependenciesRule.java
@@ -9,7 +9,6 @@
 
 import org.chromium.base.DiscardableReferencePool;
 import org.chromium.base.annotations.SuppressFBWarnings;
-import org.chromium.chrome.browser.download.ui.ThumbnailProvider;
 import org.chromium.chrome.browser.favicon.FaviconHelper;
 import org.chromium.chrome.browser.favicon.LargeIconBridge;
 import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource;
@@ -17,6 +16,7 @@
 import org.chromium.chrome.browser.suggestions.MostVisitedSites;
 import org.chromium.chrome.browser.suggestions.SuggestionsDependencyFactory;
 import org.chromium.chrome.browser.suggestions.SuggestionsEventReporter;
+import org.chromium.chrome.browser.widget.ThumbnailProvider;
 
 /**
  * Rule that allows mocking native dependencies of the suggestions package.
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index 13b04dc9..f50f67d 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -964,6 +964,10 @@
   return NULL;
 }
 
+content::BackgroundFetchDelegate* TestingProfile::GetBackgroundFetchDelegate() {
+  return nullptr;
+}
+
 content::BackgroundSyncController*
 TestingProfile::GetBackgroundSyncController() {
   return nullptr;
diff --git a/chrome/test/base/testing_profile.h b/chrome/test/base/testing_profile.h
index 4302370..e058e31e 100644
--- a/chrome/test/base/testing_profile.h
+++ b/chrome/test/base/testing_profile.h
@@ -244,6 +244,7 @@
   content::PushMessagingService* GetPushMessagingService() override;
   content::SSLHostStateDelegate* GetSSLHostStateDelegate() override;
   content::PermissionManager* GetPermissionManager() override;
+  content::BackgroundFetchDelegate* GetBackgroundFetchDelegate() override;
   content::BackgroundSyncController* GetBackgroundSyncController() override;
   content::BrowsingDataRemoverDelegate* GetBrowsingDataRemoverDelegate()
       override;
diff --git a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-download_snippet_placeholder.Nexus_5-19.png b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-download_snippet_placeholder.Nexus_5-19.png
index c8c6e40..cff17c4 100644
--- a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-download_snippet_placeholder.Nexus_5-19.png
+++ b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-download_snippet_placeholder.Nexus_5-19.png
Binary files differ
diff --git a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-download_snippet_thumbnail.Nexus_5-19.png b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-download_snippet_thumbnail.Nexus_5-19.png
index 7ef2460..091e1c0 100644
--- a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-download_snippet_thumbnail.Nexus_5-19.png
+++ b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-download_snippet_thumbnail.Nexus_5-19.png
Binary files differ
diff --git a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-long_minimal_snippet_narrow.Nexus_5-19.png b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-long_minimal_snippet_narrow.Nexus_5-19.png
index 497357bb..85517778 100644
--- a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-long_minimal_snippet_narrow.Nexus_5-19.png
+++ b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-long_minimal_snippet_narrow.Nexus_5-19.png
Binary files differ
diff --git a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-long_snippet.Nexus_5-19.png b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-long_snippet.Nexus_5-19.png
index 4532bf1..35e5fd45 100644
--- a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-long_snippet.Nexus_5-19.png
+++ b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-long_snippet.Nexus_5-19.png
Binary files differ
diff --git a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-long_snippet_narrow.Nexus_5-19.png b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-long_snippet_narrow.Nexus_5-19.png
index a4ec836..1bcd749 100644
--- a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-long_snippet_narrow.Nexus_5-19.png
+++ b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-long_snippet_narrow.Nexus_5-19.png
Binary files differ
diff --git a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-minimal_snippet.Nexus_5-19.png b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-minimal_snippet.Nexus_5-19.png
index 545a9266..4f1ad1d3 100644
--- a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-minimal_snippet.Nexus_5-19.png
+++ b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-minimal_snippet.Nexus_5-19.png
Binary files differ
diff --git a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-short_minimal_snippet_narrow.Nexus_5-19.png b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-short_minimal_snippet_narrow.Nexus_5-19.png
index 59ba15f..29b9213bd 100644
--- a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-short_minimal_snippet_narrow.Nexus_5-19.png
+++ b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-short_minimal_snippet_narrow.Nexus_5-19.png
Binary files differ
diff --git a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-short_snippet.Nexus_5-19.png b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-short_snippet.Nexus_5-19.png
index 35994f29..65064e8 100644
--- a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-short_snippet.Nexus_5-19.png
+++ b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-short_snippet.Nexus_5-19.png
Binary files differ
diff --git a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-short_snippet_narrow.Nexus_5-19.png b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-short_snippet_narrow.Nexus_5-19.png
index a544a6f..a4abd3f 100644
--- a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-short_snippet_narrow.Nexus_5-19.png
+++ b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-short_snippet_narrow.Nexus_5-19.png
Binary files differ
diff --git a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-signin_promo.Nexus_5-19.png b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-signin_promo.Nexus_5-19.png
index c4e8d74..887697d 100644
--- a/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-signin_promo.Nexus_5-19.png
+++ b/chrome/test/data/android/render_tests/ArticleSnippetsTest.modern-signin_promo.Nexus_5-19.png
Binary files differ
diff --git a/chrome/test/data/webui/md_bookmarks/test_timer_proxy.js b/chrome/test/data/webui/md_bookmarks/test_timer_proxy.js
index b165726..7229fcc 100644
--- a/chrome/test/data/webui/md_bookmarks/test_timer_proxy.js
+++ b/chrome/test/data/webui/md_bookmarks/test_timer_proxy.js
@@ -4,63 +4,61 @@
 
 suiteSetup(function() {
   cr.define('bookmarks', function() {
-    var TestTimerProxy = function(data) {
-      bookmarks.TimerProxy.call(this);
+    // TODO(calamity): Remove TestTimerProxy in favor of MockTimer.
+    class TestTimerProxy {
+      constructor() {
+        this.immediatelyResolveTimeouts = true;
 
-      this.immediatelyResolveTimeouts = true;
+        /** @private {number} */
+        this.nextTimeoutId_ = 0;
 
-      /** @private */
-      this.timeoutIds_ = 0;
-
-      /** @private {!Map<number, !Function>} */
-      this.activeTimeouts_ = new Map();
-    };
-
-    TestTimerProxy.prototype = {
-      __proto__: bookmarks.TimerProxy.prototype,
+        /** @private {!Map<number, !Function>} */
+        this.activeTimeouts_ = new Map();
+      }
 
       /**
-       * @param {Function|string} fn
+       * @param {Function} fn
        * @param {number=} delay
        * @return {number}
        * @override
        */
-      setTimeout: function(fn, delay) {
+      setTimeout(fn, delay) {
         if (this.immediatelyResolveTimeouts)
           fn();
         else
-          this.activeTimeouts_[this.timeoutIds_] = fn;
+          this.activeTimeouts_.set(this.nextTimeoutId_, fn);
 
-        return this.timeoutIds_++;
-      },
+        return this.nextTimeoutId_++;
+      }
 
       /**
-       * @param {number|undefined?} id
+       * @param {number} id
        * @override
        */
-      clearTimeout: function(id) {
+      clearTimeout(id) {
         this.activeTimeouts_.delete(id);
-      },
+      }
 
       /**
        * Run the function associated with a timeout id and clear it from the
        * active timeouts.
        * @param {number} id
        */
-      runTimeoutFn: function(id) {
-        this.activeTimeouts_[id]();
+      runTimeoutFn(id) {
+        this.activeTimeouts_.get(id)();
         this.clearTimeout(id);
-      },
+      }
 
       /**
        * Returns true if a given timeout id has not been run or cleared.
        * @param {number} id
-       * @return {boolean}
+       * @return {boolean} Whether a given timeout id has not been run or
+       * cleared.
        */
-      hasTimeout: function(id) {
+      hasTimeout(id) {
         return this.activeTimeouts_.has(id);
-      },
-    };
+      }
+    }
 
     return {
       TestTimerProxy: TestTimerProxy,
diff --git a/chromecast/browser/cast_browser_context.cc b/chromecast/browser/cast_browser_context.cc
index 33a0ea4..9f091d0 100644
--- a/chromecast/browser/cast_browser_context.cc
+++ b/chromecast/browser/cast_browser_context.cc
@@ -140,6 +140,11 @@
   return permission_manager_.get();
 }
 
+content::BackgroundFetchDelegate*
+CastBrowserContext::GetBackgroundFetchDelegate() {
+  return nullptr;
+}
+
 content::BackgroundSyncController*
 CastBrowserContext::GetBackgroundSyncController() {
   return nullptr;
diff --git a/chromecast/browser/cast_browser_context.h b/chromecast/browser/cast_browser_context.h
index ae8c7eef..1623b0c 100644
--- a/chromecast/browser/cast_browser_context.h
+++ b/chromecast/browser/cast_browser_context.h
@@ -38,6 +38,7 @@
   content::PushMessagingService* GetPushMessagingService() override;
   content::SSLHostStateDelegate* GetSSLHostStateDelegate() override;
   content::PermissionManager* GetPermissionManager() override;
+  content::BackgroundFetchDelegate* GetBackgroundFetchDelegate() override;
   content::BackgroundSyncController* GetBackgroundSyncController() override;
   content::BrowsingDataRemoverDelegate* GetBrowsingDataRemoverDelegate()
       override;
diff --git a/chromeos/system/statistics_provider.cc b/chromeos/system/statistics_provider.cc
index 9e12ad4c..10856bc 100644
--- a/chromeos/system/statistics_provider.cc
+++ b/chromeos/system/statistics_provider.cc
@@ -184,14 +184,11 @@
 const char kFirmwareTypeValueNormal[] = "normal";
 const char kHardwareClassKey[] = "hardware_class";
 const char kIsVmKey[] = "is_vm";
-const char kIsVmValueTrue[] = "1";
 const char kIsVmValueFalse[] = "0";
+const char kIsVmValueTrue[] = "1";
 const char kOffersCouponCodeKey[] = "ubind_attribute";
 const char kOffersGroupCodeKey[] = "gbind_attribute";
 const char kRlzBrandCodeKey[] = "rlz_brand_code";
-const char kWriteProtectSwitchBootKey[] = "wpsw_boot";
-const char kWriteProtectSwitchBootValueOff[] = "0";
-const char kWriteProtectSwitchBootValueOn[] = "1";
 const char kRegionKey[] = "region";
 const char kSerialNumberKey[] = "serial_number";
 const char kInitialLocaleKey[] = "initial_locale";
diff --git a/chromeos/system/statistics_provider.h b/chromeos/system/statistics_provider.h
index 154336d..85a939d 100644
--- a/chromeos/system/statistics_provider.h
+++ b/chromeos/system/statistics_provider.h
@@ -24,8 +24,8 @@
 
 // Developer switch value.
 CHROMEOS_EXPORT extern const char kDevSwitchBootKey[];
-CHROMEOS_EXPORT extern const char kDevSwitchBootValueVerified[];
 CHROMEOS_EXPORT extern const char kDevSwitchBootValueDev[];
+CHROMEOS_EXPORT extern const char kDevSwitchBootValueVerified[];
 
 // Firmware type and associated values. The values are from crossystem output
 // for the mainfw_type key. Normal and developer correspond to Chrome OS
@@ -37,18 +37,14 @@
 CHROMEOS_EXPORT extern const char kFirmwareTypeValueNonchrome[];
 CHROMEOS_EXPORT extern const char kFirmwareTypeValueNormal[];
 
-// Serial number key (only VPD v2+ devices). Use GetEnterpriseMachineID() to
-// cover legacy devices.
-CHROMEOS_EXPORT extern const char kSerialNumberKey[];
-
 // HWID key.
 CHROMEOS_EXPORT extern const char kHardwareClassKey[];
 
 // Key/values reporting if Chrome OS is running in a VM or not. These values are
 // read from crossystem output. See crossystem source for VM detection logic.
 CHROMEOS_EXPORT extern const char kIsVmKey[];
-CHROMEOS_EXPORT extern const char kIsVmValueTrue[];
 CHROMEOS_EXPORT extern const char kIsVmValueFalse[];
+CHROMEOS_EXPORT extern const char kIsVmValueTrue[];
 
 // OEM customization flag that permits exiting enterprise enrollment flow in
 // OOBE when 'oem_enterprise_managed' flag is set.
@@ -73,17 +69,16 @@
 // Release Brand Code key.
 CHROMEOS_EXPORT extern const char kRlzBrandCodeKey[];
 
-// Write protect switch value.
-CHROMEOS_EXPORT extern const char kWriteProtectSwitchBootKey[];
-CHROMEOS_EXPORT extern const char kWriteProtectSwitchBootValueOff[];
-CHROMEOS_EXPORT extern const char kWriteProtectSwitchBootValueOn[];
-
 // Regional data
 CHROMEOS_EXPORT extern const char kRegionKey[];
 CHROMEOS_EXPORT extern const char kInitialLocaleKey[];
 CHROMEOS_EXPORT extern const char kInitialTimezoneKey[];
 CHROMEOS_EXPORT extern const char kKeyboardLayoutKey[];
 
+// Serial number key (only VPD v2+ devices). Use GetEnterpriseMachineID() to
+// cover legacy devices.
+CHROMEOS_EXPORT extern const char kSerialNumberKey[];
+
 // This interface provides access to Chrome OS statistics.
 class CHROMEOS_EXPORT StatisticsProvider {
  public:
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index 64995087..6f737f8 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -104,7 +104,7 @@
   defines = [ "ARC_IMPLEMENTATION" ]
 
   deps = [
-    "//components/pref_registry",
+    "//components/prefs",
   ]
 }
 
@@ -117,6 +117,8 @@
     "arc_bridge_service.cc",
     "arc_bridge_service.h",
     "arc_browser_context_keyed_service_factory_base.h",
+    "arc_data_remover.cc",
+    "arc_data_remover.h",
     "arc_features.cc",
     "arc_features.h",
     "arc_service_manager.cc",
@@ -136,6 +138,7 @@
     "//base",
     "//chromeos",
     "//components/keyed_service/content",
+    "//components/prefs",
     "//components/signin/core/account_id",
     "//components/user_manager",
     "//mojo/edk/system",
@@ -144,6 +147,7 @@
 
   public_deps = [
     ":arc_bindings",
+    ":prefs",
   ]
 }
 
@@ -237,6 +241,7 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "arc_data_remover_unittest.cc",
     "arc_session_runner_unittest.cc",
     "arc_util_unittest.cc",
     "bluetooth/bluetooth_struct_traits_unittest.cc",
@@ -257,6 +262,7 @@
     "//base/test:test_support",
     "//chromeos",
     "//chromeos:test_support_without_gmock",
+    "//components/prefs:test_support",
     "//components/signin/core/account_id",
     "//components/user_manager",
     "//components/user_manager:test_support",
diff --git a/components/arc/arc_data_remover.cc b/components/arc/arc_data_remover.cc
new file mode 100644
index 0000000..dad2343
--- /dev/null
+++ b/components/arc/arc_data_remover.cc
@@ -0,0 +1,68 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/arc/arc_data_remover.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/session_manager_client.h"
+#include "components/arc/arc_prefs.h"
+
+namespace arc {
+
+ArcDataRemover::ArcDataRemover(PrefService* prefs,
+                               const cryptohome::Identification& cryptohome_id)
+    : cryptohome_id_(cryptohome_id), weak_factory_(this) {
+  pref_.Init(prefs::kArcDataRemoveRequested, prefs);
+}
+
+ArcDataRemover::~ArcDataRemover() = default;
+
+void ArcDataRemover::Schedule() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  pref_.SetValue(true);
+}
+
+bool ArcDataRemover::IsScheduledForTesting() const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return pref_.GetValue();
+}
+
+void ArcDataRemover::Run(RunCallback callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (!pref_.GetValue()) {
+    // Data removal is not scheduled.
+    std::move(callback).Run(base::nullopt);
+    return;
+  }
+
+  VLOG(1) << "Starting ARC data removal";
+  auto* session_manager_client =
+      chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
+  DCHECK(session_manager_client);
+  session_manager_client->RemoveArcData(
+      cryptohome_id_, base::AdaptCallbackForRepeating(base::BindOnce(
+                          &ArcDataRemover::OnDataRemoved,
+                          weak_factory_.GetWeakPtr(), std::move(callback))));
+}
+
+void ArcDataRemover::OnDataRemoved(RunCallback callback, bool success) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (success) {
+    VLOG(1) << "ARC data removal successful";
+  } else {
+    LOG(ERROR) << "Request for ARC user data removal failed. "
+               << "See session_manager logs for more details.";
+  }
+  pref_.SetValue(false);
+
+  std::move(callback).Run(success);
+}
+
+}  // namespace arc
diff --git a/components/arc/arc_data_remover.h b/components/arc/arc_data_remover.h
new file mode 100644
index 0000000..107b521
--- /dev/null
+++ b/components/arc/arc_data_remover.h
@@ -0,0 +1,57 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_ARC_ARC_DATA_REMOVER_H_
+#define COMPONENTS_ARC_ARC_DATA_REMOVER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/threading/thread_checker.h"
+#include "chromeos/cryptohome/cryptohome_parameters.h"
+#include "components/prefs/pref_member.h"
+
+class PrefService;
+
+namespace arc {
+
+// Manages ARC's user data removal operation.
+class ArcDataRemover {
+ public:
+  ArcDataRemover(PrefService* prefs,
+                 const cryptohome::Identification& cryptohome_id);
+  ~ArcDataRemover();
+
+  // Schedules to remove the data. This is persistent, calling Run() just
+  // after rebooting may execute the removing.
+  void Schedule();
+
+  // Returns whether data removal is scheduled or not for testing purpose.
+  bool IsScheduledForTesting() const;
+
+  // Executes the removing, if scheduled.
+  // This must run while ARC instance is stopped.
+  // If not scheduled, |callback| will be synchronously called with nullopt.
+  using RunCallback = base::OnceCallback<void(base::Optional<bool> result)>;
+  void Run(RunCallback callback);
+
+ private:
+  void OnDataRemoved(RunCallback callback, bool success);
+
+  THREAD_CHECKER(thread_checker_);
+
+  // Pref accessor to the "arc.data.remove_requested".
+  BooleanPrefMember pref_;
+
+  // Cryptohome ID for the user whose /data is being deleted.
+  const cryptohome::Identification cryptohome_id_;
+
+  base::WeakPtrFactory<ArcDataRemover> weak_factory_;
+  DISALLOW_COPY_AND_ASSIGN(ArcDataRemover);
+};
+
+}  // namespace arc
+
+#endif  // COMPONENTS_ARC_ARC_DATA_REMOVER_H_
diff --git a/components/arc/arc_data_remover_unittest.cc b/components/arc/arc_data_remover_unittest.cc
new file mode 100644
index 0000000..4e3a974
--- /dev/null
+++ b/components/arc/arc_data_remover_unittest.cc
@@ -0,0 +1,111 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/arc/arc_data_remover.h"
+
+#include "base/bind.h"
+#include "base/test/scoped_task_environment.h"
+#include "chromeos/cryptohome/cryptohome_parameters.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/fake_session_manager_client.h"
+#include "components/arc/arc_prefs.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/signin/core/account_id/account_id.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace arc {
+namespace {
+
+class ArcDataRemoverTest : public testing::Test {
+ public:
+  ArcDataRemoverTest() = default;
+
+  void SetUp() override {
+    chromeos::DBusThreadManager::GetSetterForTesting()->SetSessionManagerClient(
+        std::make_unique<chromeos::FakeSessionManagerClient>());
+    chromeos::DBusThreadManager::Initialize();
+
+    prefs::RegisterProfilePrefs(prefs_.registry());
+  }
+
+  void TearDown() override { chromeos::DBusThreadManager::Shutdown(); }
+
+  PrefService* prefs() { return &prefs_; }
+
+  const cryptohome::Identification& cryptohome_id() const {
+    return cryptohome_id_;
+  }
+
+  chromeos::FakeSessionManagerClient* session_manager_client() {
+    return static_cast<chromeos::FakeSessionManagerClient*>(
+        chromeos::DBusThreadManager::Get()->GetSessionManagerClient());
+  }
+
+ private:
+  TestingPrefServiceSimple prefs_;
+  const cryptohome::Identification cryptohome_id_{EmptyAccountId()};
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcDataRemoverTest);
+};
+
+TEST_F(ArcDataRemoverTest, NotScheduled) {
+  ArcDataRemover data_remover(prefs(), cryptohome_id());
+
+  base::RunLoop loop;
+  data_remover.Run(base::BindOnce(
+      [](base::RunLoop* loop, base::Optional<bool> result) {
+        EXPECT_EQ(result, base::nullopt);
+        loop->Quit();
+      },
+      &loop));
+  loop.Run();
+}
+
+TEST_F(ArcDataRemoverTest, Success) {
+  session_manager_client()->set_arc_available(true);
+
+  ArcDataRemover data_remover(prefs(), cryptohome_id());
+  data_remover.Schedule();
+
+  base::RunLoop loop;
+  data_remover.Run(base::BindOnce(
+      [](base::RunLoop* loop, base::Optional<bool> result) {
+        EXPECT_EQ(result, base::make_optional(true));
+        loop->Quit();
+      },
+      &loop));
+  loop.Run();
+}
+
+TEST_F(ArcDataRemoverTest, Fail) {
+  ArcDataRemover data_remover(prefs(), cryptohome_id());
+  data_remover.Schedule();
+
+  base::RunLoop loop;
+  data_remover.Run(base::BindOnce(
+      [](base::RunLoop* loop, base::Optional<bool> result) {
+        EXPECT_EQ(result, base::make_optional(false));
+        loop->Quit();
+      },
+      &loop));
+  loop.Run();
+}
+
+TEST_F(ArcDataRemoverTest, PrefPersistsAcrossInstances) {
+  {
+    ArcDataRemover data_remover(prefs(), cryptohome_id());
+    data_remover.Schedule();
+    EXPECT_TRUE(data_remover.IsScheduledForTesting());
+  }
+
+  {
+    ArcDataRemover data_remover(prefs(), cryptohome_id());
+    EXPECT_TRUE(data_remover.IsScheduledForTesting());
+  }
+}
+
+}  // namespace
+}  // namespace arc
diff --git a/components/arc/arc_prefs.cc b/components/arc/arc_prefs.cc
index 5dd43e7e..6aad83150 100644
--- a/components/arc/arc_prefs.cc
+++ b/components/arc/arc_prefs.cc
@@ -6,7 +6,7 @@
 
 #include <string>
 
-#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_registry_simple.h"
 
 namespace arc {
 namespace prefs {
@@ -70,7 +70,7 @@
 const char kVoiceInteractionPrefSynced[] =
     "settings.voice_interaction.context.synced";
 
-void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
+void RegisterProfilePrefs(PrefRegistrySimple* registry) {
   // TODO(dspaid): Implement a mechanism to allow this to sync on first boot
   // only.
 
diff --git a/components/arc/arc_prefs.h b/components/arc/arc_prefs.h
index 2ce313d6..b3608af 100644
--- a/components/arc/arc_prefs.h
+++ b/components/arc/arc_prefs.h
@@ -7,9 +7,7 @@
 
 #include "components/arc/arc_export.h"
 
-namespace user_prefs {
-class PrefRegistrySyncable;
-}  // namespace user_prefs
+class PrefRegistrySimple;
 
 namespace arc {
 namespace prefs {
@@ -35,7 +33,7 @@
 ARC_EXPORT extern const char kVoiceInteractionContextEnabled[];
 ARC_EXPORT extern const char kVoiceInteractionPrefSynced[];
 
-void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
 }  // namespace prefs
 }  // namespace arc
diff --git a/components/arc/arc_session.cc b/components/arc/arc_session.cc
index 64b543e..05113e7 100644
--- a/components/arc/arc_session.cc
+++ b/components/arc/arc_session.cc
@@ -202,7 +202,9 @@
   void StartForLoginScreen() override;
   bool IsForLoginScreen() override;
   void Start() override;
+  bool IsRunning() override;
   void Stop() override;
+  bool IsStopRequested() override;
   void OnShutdown() override;
 
  private:
@@ -637,15 +639,24 @@
   return login_screen_instance_requested_;
 }
 
+bool ArcSessionImpl::IsRunning() {
+  return state_ == State::RUNNING;
+}
+
+bool ArcSessionImpl::IsStopRequested() {
+  return stop_requested_;
+}
+
 void ArcSessionImpl::OnStopped(ArcStopReason reason) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // OnStopped() should be called once per instance.
   DCHECK_NE(state_, State::STOPPED);
   VLOG(2) << "ARC session is stopped.";
+  const bool was_running = IsRunning();
   arc_bridge_host_.reset();
   state_ = State::STOPPED;
   for (auto& observer : observer_list_)
-    observer.OnSessionStopped(reason);
+    observer.OnSessionStopped(reason, was_running);
 }
 
 void ArcSessionImpl::OnShutdown() {
diff --git a/components/arc/arc_session.h b/components/arc/arc_session.h
index 01c8c939..4fa74c3 100644
--- a/components/arc/arc_session.h
+++ b/components/arc/arc_session.h
@@ -9,11 +9,12 @@
 
 #include "base/macros.h"
 #include "base/observer_list.h"
-#include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_stop_reason.h"
 
 namespace arc {
 
+class ArcBridgeService;
+
 // Starts the ARC instance and bootstraps the bridge connection.
 // Clients should implement the Delegate to be notified upon communications
 // being available.
@@ -31,7 +32,9 @@
 
     // Called when ARC instance is stopped. This is called exactly once
     // per instance which is Start()ed.
-    virtual void OnSessionStopped(ArcStopReason reason) = 0;
+    // |was_running| is true, if the stopped instance was fully set up
+    // and running.
+    virtual void OnSessionStopped(ArcStopReason reason, bool was_running) = 0;
 
    protected:
     virtual ~Observer() = default;
@@ -61,6 +64,14 @@
   // The completion is notified via OnSessionStopped() of the Observer.
   virtual void Stop() = 0;
 
+  // Returns true if this instance is fully set up successfully, and running.
+  // Currently, this means, this is a fully functional instance, and
+  // Mojo connection is already connected successfully.
+  virtual bool IsRunning() = 0;
+
+  // Returns true if Stop() has been called already.
+  virtual bool IsStopRequested() = 0;
+
   // Called when Chrome is in shutdown state. This is called when the message
   // loop is already stopped, and the instance will soon be deleted. Caller
   // may expect that OnSessionStopped() is synchronously called back except
diff --git a/components/arc/arc_session_runner.cc b/components/arc/arc_session_runner.cc
index 5e136e3f..81e8e79 100644
--- a/components/arc/arc_session_runner.cc
+++ b/components/arc/arc_session_runner.cc
@@ -5,7 +5,6 @@
 #include "components/arc/arc_session_runner.h"
 
 #include "base/logging.h"
-#include "base/memory/ref_counted.h"
 #include "base/task_runner.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "components/arc/arc_util.h"
@@ -27,6 +26,40 @@
   return chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
 }
 
+// Returns true if restart is needed for given conditions.
+bool IsRestartNeeded(bool run_requested,
+                     ArcStopReason stop_reason,
+                     bool was_running) {
+  if (!run_requested) {
+    // The request to run ARC is canceled by the caller. No need to restart.
+    return false;
+  }
+
+  switch (stop_reason) {
+    case ArcStopReason::SHUTDOWN:
+      // This is a part of stop requested by ArcSessionRunner.
+      // If ARC is re-requested to start, restart is necessary.
+      // This case happens, e.g., RequestStart() -> RequestStop() ->
+      // RequestStart(), case. If the second RequestStart() is called before
+      // the instance previously running is stopped, then just |run_requested|
+      // flag is set. On completion, restart is needed.
+      return true;
+    case ArcStopReason::GENERIC_BOOT_FAILURE:
+    case ArcStopReason::LOW_DISK_SPACE:
+      // These two are errors on starting. To prevent failure loop, do not
+      // restart.
+      return false;
+    case ArcStopReason::CRASH:
+      // ARC instance is crashed unexpectedly, so automatically restart.
+      // However, to avoid crash loop, do not restart if it is not successfully
+      // started yet. So, check |was_running|.
+      return was_running;
+  }
+
+  NOTREACHED();
+  return false;
+}
+
 }  // namespace
 
 ArcSessionRunner::ArcSessionRunner(const ArcSessionFactory& factory)
@@ -64,23 +97,23 @@
   if (run_requested_)
     return;
 
-  VLOG(1) << "Session started";
+  VLOG(1) << "Session start requested";
   run_requested_ = true;
   // Here |run_requested_| transitions from false to true. So, |restart_timer_|
   // must be stopped (either not even started, or has been cancelled in
   // previous RequestStop() call).
   DCHECK(!restart_timer_.IsRunning());
 
-  if (arc_session_ && state_ >= State::STARTING) {
-    // In this case, RequestStop() was called, and before |arc_session_| had
-    // finished stopping, RequestStart() was called. Do nothing in that case,
-    // since when |arc_session_| does actually stop, OnSessionStopped() will
-    // be called, where it should automatically restart.
-    DCHECK_EQ(state_, State::STOPPING);
-  } else {
-    DCHECK_LE(state_, State::STARTING_FOR_LOGIN_SCREEN);
-    StartArcSession();
+  if (arc_session_ && arc_session_->IsStopRequested()) {
+    // This is the case where RequestStop() was called, but before
+    // |arc_session_| had finshed stopping, RequestStart() is called.
+    // Do nothing in the that case, since when |arc_session_| does actually
+    // stop, OnSessionStopped() will be called, where it should automatically
+    // restart.
+    return;
   }
+
+  StartArcSession();
 }
 
 void ArcSessionRunner::RequestStop(bool always_stop_session) {
@@ -93,28 +126,22 @@
       return;
   }
 
-  VLOG(1) << "Session ended";
+  VLOG(1) << "Session stop requested";
   run_requested_ = false;
 
   if (arc_session_) {
-    // The |state_| could be either STARTING*, RUNNING or STOPPING.
-    DCHECK_NE(state_, State::STOPPED);
-
-    if (state_ == State::STOPPING) {
-      // STOPPING is found in the senario of "RequestStart() -> RequestStop()
-      // -> RequestStart() -> RequestStop()" case.
-      // In the first RequestStop() call, |state_| is set to STOPPING,
-      // and in the second RequestStop() finds it (so this is the second call).
-      // In that case, ArcSession::Stop() is already called, so do nothing.
-      return;
-    }
-    state_ = State::STOPPING;
+    // If |arc_session_| is running, stop it.
+    // Note that |arc_session_| may be already in the process of stopping or
+    // be stopped.
+    // E.g. RequestStart() -> RequestStop() -> RequestStart() -> RequestStop()
+    // case. If the second RequestStop() is called before the first
+    // RequestStop() is not yet completed for the instance, Stop() of the
+    // instance is called again, but it is no-op as expected.
     arc_session_->Stop();
-  } else {
-    DCHECK_EQ(state_, State::STOPPED);
-    // In case restarting is in progress, cancel it.
-    restart_timer_.Stop();
   }
+
+  // In case restarting is in progress, cancel it.
+  restart_timer_.Stop();
 }
 
 void ArcSessionRunner::OnShutdown() {
@@ -123,39 +150,40 @@
   VLOG(1) << "OnShutdown";
   run_requested_ = false;
   restart_timer_.Stop();
-  if (arc_session_) {
-    DCHECK_NE(state_, State::STOPPED);
-    state_ = State::STOPPING;
+  if (arc_session_)
     arc_session_->OnShutdown();
-  }
   // ArcSession::OnShutdown() invokes OnSessionStopped() synchronously.
   // In the observer method, |arc_session_| should be destroyed.
   DCHECK(!arc_session_);
 }
 
+// TODO(hidehiko,lhchavez,yusukes): Revisit following state accessors.
 bool ArcSessionRunner::IsRunning() const {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  return state_ == State::RUNNING;
+  // For historical reason, exclude "stopping" instance phase.
+  return arc_session_ && arc_session_->IsRunning() &&
+         !arc_session_->IsStopRequested();
 }
 
 bool ArcSessionRunner::IsStopped() const {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  return state_ == State::STOPPED;
+  return !arc_session_;
 }
 
 bool ArcSessionRunner::IsStopping() const {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  return state_ == State::STOPPING;
+  return arc_session_ && arc_session_->IsStopRequested();
 }
 
 bool ArcSessionRunner::IsLoginScreenInstanceStarting() const {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  return state_ == State::STARTING_FOR_LOGIN_SCREEN;
+  return arc_session_ && arc_session_->IsForLoginScreen() &&
+         !arc_session_->IsStopRequested();
 }
 
 void ArcSessionRunner::SetRestartDelayForTesting(
     const base::TimeDelta& restart_delay) {
-  DCHECK_EQ(state_, State::STOPPED);
+  DCHECK(!arc_session_);
   DCHECK(!restart_timer_.IsRunning());
   restart_delay_ = restart_delay;
 }
@@ -166,13 +194,11 @@
 
   VLOG(1) << "Starting ARC instance";
   if (!arc_session_) {
-    DCHECK_EQ(state_, State::STOPPED);
     arc_session_ = factory_.Run();
     arc_session_->AddObserver(this);
   } else {
-    DCHECK_EQ(state_, State::STARTING_FOR_LOGIN_SCREEN);
+    DCHECK(arc_session_->IsForLoginScreen());
   }
-  state_ = State::STARTING;
   arc_session_->Start();
 }
 
@@ -186,17 +212,14 @@
 
 void ArcSessionRunner::OnSessionReady() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK_EQ(state_, State::STARTING);
   DCHECK(arc_session_);
   DCHECK(!restart_timer_.IsRunning());
-
   VLOG(0) << "ARC ready";
-  state_ = State::RUNNING;
 }
 
-void ArcSessionRunner::OnSessionStopped(ArcStopReason stop_reason) {
+void ArcSessionRunner::OnSessionStopped(ArcStopReason stop_reason,
+                                        bool was_running) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK_NE(state_, State::STOPPED);
   DCHECK(arc_session_);
   DCHECK(!restart_timer_.IsRunning());
 
@@ -209,21 +232,9 @@
   arc_session_->RemoveObserver(this);
   arc_session_.reset();
 
-  // If RUNNING, ARC instance unexpectedly crashed so we need to restart it
-  // automatically.
-  // If STOPPING, at least once RequestStop() is called. If |session_started_|
-  // is true, RequestStart() is following so schedule to restart ARC session.
-  // Otherwise, do nothing.
-  // If STARTING, ARC instance has not been booted properly, so do not
-  // restart it automatically.
-  const bool restarting = (state_ == State::RUNNING ||
-                           (state_ == State::STOPPING && run_requested_));
+  const bool restarting =
+      IsRestartNeeded(run_requested_, stop_reason, was_running);
   if (restarting) {
-    // This check is for RUNNING case. In RUNNING case |run_requested_| should
-    // be always true, because if once RequestStop() is called, the state_
-    // will be set to STOPPING.
-    DCHECK(run_requested_);
-
     // There was a previous invocation and it crashed for some reason. Try
     // starting ARC instance later again.
     // Note that even |restart_delay_| is 0 (for testing), it needs to
@@ -234,7 +245,6 @@
                                     weak_ptr_factory_.GetWeakPtr()));
   }
 
-  state_ = State::STOPPED;
   if (notify_observers) {
     for (auto& observer : observer_list_)
       observer.OnSessionStopped(stop_reason, restarting);
@@ -251,10 +261,8 @@
   // container may depend on such as cras, EmitLoginPromptVisibleCalled() is the
   // safe place to start the container for login screen.
   DCHECK(!arc_session_);
-  DCHECK_EQ(state_, State::STOPPED);
   arc_session_ = factory_.Run();
   arc_session_->AddObserver(this);
-  state_ = State::STARTING_FOR_LOGIN_SCREEN;
   arc_session_->StartForLoginScreen();
 }
 
diff --git a/components/arc/arc_session_runner.h b/components/arc/arc_session_runner.h
index 3c740ad..37dfe40 100644
--- a/components/arc/arc_session_runner.h
+++ b/components/arc/arc_session_runner.h
@@ -9,6 +9,8 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "chromeos/dbus/session_manager_client.h"
@@ -84,43 +86,6 @@
   void SetRestartDelayForTesting(const base::TimeDelta& restart_delay);
 
  private:
-  // The possible states.  In the normal flow, the state changes in the
-  // following sequence:
-  //
-  // STOPPED
-  //   RequestStart() ->
-  // STARTING
-  //   OnSessionReady() ->
-  // RUNNING
-  //
-  // The ArcSession state machine can be thought of being substates of
-  // ArcBridgeService's STARTING state.
-  // ArcBridgeService's state machine can be stopped at any phase.
-  //
-  // *
-  //   RequestStop() ->
-  // STOPPING
-  //   OnSessionStopped() ->
-  // STOPPED
-  enum class State {
-    // ARC instance is not currently running.
-    STOPPED,
-
-    // Request to start ARC instance for login screen is received. Starting an
-    // ARC instance.
-    STARTING_FOR_LOGIN_SCREEN,
-
-    // Request to start ARC instance is received. Starting an ARC instance.
-    STARTING,
-
-    // ARC instance has finished initializing, and is now ready for interaction
-    // with other services.
-    RUNNING,
-
-    // Request to stop ARC instance is recieved. Stopping the ARC instance.
-    STOPPING,
-  };
-
   // Starts to run an ARC instance.
   void StartArcSession();
 
@@ -129,7 +94,7 @@
 
   // ArcSession::Observer:
   void OnSessionReady() override;
-  void OnSessionStopped(ArcStopReason reason) override;
+  void OnSessionStopped(ArcStopReason reason, bool was_running) override;
 
   // chromeos::SessionManagerClient::Observer:
   void EmitLoginPromptVisibleCalled() override;
@@ -150,9 +115,6 @@
   // Factory to inject a fake ArcSession instance for testing.
   ArcSessionFactory factory_;
 
-  // Current runner's state.
-  State state_ = State::STOPPED;
-
   // ArcSession object for currently running ARC instance. This should be
   // nullptr if the state is STOPPED, otherwise non-nullptr.
   std::unique_ptr<ArcSession> arc_session_;
diff --git a/components/arc/arc_session_runner_unittest.cc b/components/arc/arc_session_runner_unittest.cc
index ab2204c..7e93589 100644
--- a/components/arc/arc_session_runner_unittest.cc
+++ b/components/arc/arc_session_runner_unittest.cc
@@ -180,6 +180,7 @@
 
   arc_session_runner()->RequestStop(false);
   EXPECT_TRUE(arc_session_runner()->IsStopped());
+  EXPECT_FALSE(restarting());
 }
 
 // If the boot procedure is failed, then restarting mechanism should not
@@ -193,6 +194,7 @@
   arc_session_runner()->RequestStart();
   EXPECT_EQ(ArcStopReason::GENERIC_BOOT_FAILURE, stop_reason());
   EXPECT_TRUE(arc_session_runner()->IsStopped());
+  EXPECT_FALSE(restarting());
 }
 
 // Does the same with the mini instance for login screen.
@@ -244,43 +246,22 @@
   ASSERT_TRUE(arc_session());
   arc_session()->StopWithReason(ArcStopReason::CRASH);
   EXPECT_TRUE(arc_session_runner()->IsStopped());
+  EXPECT_TRUE(restarting());
   base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(restarting_called());
   EXPECT_TRUE(arc_session_runner()->IsRunning());
 
   arc_session_runner()->RequestStop(false);
   EXPECT_TRUE(arc_session_runner()->IsStopped());
 }
 
-// Makes sure OnSessionStopped is called on stop.
-TEST_F(ArcSessionRunnerTest, OnSessionStopped) {
+TEST_F(ArcSessionRunnerTest, GracefulStop) {
   arc_session_runner()->SetRestartDelayForTesting(base::TimeDelta());
   EXPECT_TRUE(arc_session_runner()->IsStopped());
 
   arc_session_runner()->RequestStart();
   EXPECT_TRUE(arc_session_runner()->IsRunning());
 
-  // Simulate boot failure.
-  ASSERT_TRUE(arc_session());
-  arc_session()->StopWithReason(ArcStopReason::GENERIC_BOOT_FAILURE);
-  EXPECT_EQ(ArcStopReason::GENERIC_BOOT_FAILURE, stop_reason());
-  EXPECT_TRUE(restarting());
-  EXPECT_FALSE(restarting_called());
-  EXPECT_TRUE(arc_session_runner()->IsStopped());
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(restarting_called());
-  EXPECT_TRUE(arc_session_runner()->IsRunning());
-
-  // Simulate crash.
-  ASSERT_TRUE(arc_session());
-  arc_session()->StopWithReason(ArcStopReason::CRASH);
-  EXPECT_EQ(ArcStopReason::CRASH, stop_reason());
-  EXPECT_TRUE(restarting());
-  EXPECT_FALSE(restarting_called());
-  EXPECT_TRUE(arc_session_runner()->IsStopped());
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(restarting_called());
-  EXPECT_TRUE(arc_session_runner()->IsRunning());
-
   // Graceful stop.
   arc_session_runner()->RequestStop(false);
   EXPECT_EQ(ArcStopReason::SHUTDOWN, stop_reason());
diff --git a/components/arc/test/fake_arc_session.cc b/components/arc/test/fake_arc_session.cc
index 6b3b5c1b..d46afadf 100644
--- a/components/arc/test/fake_arc_session.cc
+++ b/components/arc/test/fake_arc_session.cc
@@ -19,7 +19,7 @@
   is_for_login_screen_ = true;
   if (boot_failure_emulation_enabled_) {
     for (auto& observer : observer_list_)
-      observer.OnSessionStopped(boot_failure_reason_);
+      observer.OnSessionStopped(boot_failure_reason_, false);
   }
 }
 
@@ -31,14 +31,20 @@
   is_for_login_screen_ = false;
   if (boot_failure_emulation_enabled_) {
     for (auto& observer : observer_list_)
-      observer.OnSessionStopped(boot_failure_reason_);
+      observer.OnSessionStopped(boot_failure_reason_, false);
   } else if (!boot_suspended_) {
+    running_ = true;
     for (auto& observer : observer_list_)
       observer.OnSessionReady();
   }
 }
 
+bool FakeArcSession::IsRunning() {
+  return running_;
+}
+
 void FakeArcSession::Stop() {
+  stop_requested_ = true;
   StopWithReason(ArcStopReason::SHUTDOWN);
 }
 
@@ -46,9 +52,15 @@
   StopWithReason(ArcStopReason::SHUTDOWN);
 }
 
+bool FakeArcSession::IsStopRequested() {
+  return stop_requested_;
+}
+
 void FakeArcSession::StopWithReason(ArcStopReason reason) {
+  bool was_mojo_connected = running_;
+  running_ = false;
   for (auto& observer : observer_list_)
-    observer.OnSessionStopped(reason);
+    observer.OnSessionStopped(reason, was_mojo_connected);
 }
 
 void FakeArcSession::EnableBootFailureEmulation(ArcStopReason reason) {
diff --git a/components/arc/test/fake_arc_session.h b/components/arc/test/fake_arc_session.h
index 9e272d2..d360eef 100644
--- a/components/arc/test/fake_arc_session.h
+++ b/components/arc/test/fake_arc_session.h
@@ -23,7 +23,9 @@
   void StartForLoginScreen() override;
   bool IsForLoginScreen() override;
   void Start() override;
+  bool IsRunning() override;
   void Stop() override;
+  bool IsStopRequested() override;
   void OnShutdown() override;
 
   // To emulate unexpected stop, such as crash.
@@ -49,6 +51,8 @@
 
   bool boot_suspended_ = false;
   bool is_for_login_screen_ = false;
+  bool running_ = false;
+  bool stop_requested_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(FakeArcSession);
 };
diff --git a/components/autofill/content/renderer/BUILD.gn b/components/autofill/content/renderer/BUILD.gn
index 79e6a0b..431e75e 100644
--- a/components/autofill/content/renderer/BUILD.gn
+++ b/components/autofill/content/renderer/BUILD.gn
@@ -12,6 +12,8 @@
     "form_cache.h",
     "form_classifier.cc",
     "form_classifier.h",
+    "html_based_username_detector.cc",
+    "html_based_username_detector.h",
     "page_click_listener.h",
     "page_click_tracker.cc",
     "page_click_tracker.h",
diff --git a/components/autofill/content/renderer/OWNERS b/components/autofill/content/renderer/OWNERS
index c5e2808..d1737e7 100644
--- a/components/autofill/content/renderer/OWNERS
+++ b/components/autofill/content/renderer/OWNERS
@@ -2,3 +2,8 @@
 per-file *password*=kolos@chromium.org
 per-file *password*=vabr@chromium.org
 per-file *password*=vasilii@chromium.org
+
+per-file *username*=dvadym@chromium.org
+per-file *username*=kolos@chromium.org
+per-file *username*=vabr@chromium.org
+per-file *username*=vasilii@chromium.org
diff --git a/components/autofill/content/renderer/html_based_username_detector.cc b/components/autofill/content/renderer/html_based_username_detector.cc
new file mode 100644
index 0000000..ad62758
--- /dev/null
+++ b/components/autofill/content/renderer/html_based_username_detector.cc
@@ -0,0 +1,483 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/content/renderer/html_based_username_detector.h"
+
+#include "base/i18n/case_conversion.h"
+#include "base/strings/string_split.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/content/renderer/form_autofill_util.h"
+
+using blink::WebFormControlElement;
+using blink::WebInputElement;
+
+namespace autofill {
+
+namespace {
+
+// For each input element that can be username, we compute and save developer
+// and user group, along with associated short tokens lists (to handle finding
+// less than |kMinimumWordLength| letters long words).
+struct UsernameFieldData {
+  WebInputElement input_element;
+  base::string16 developer_value;
+  std::vector<base::string16> developer_short_tokens;
+  base::string16 user_value;
+  std::vector<base::string16> user_short_tokens;
+};
+
+// "Latin" translations are the translations of the words for which the
+// original translation is similar to the romanized translation (translation of
+// the word only using ISO basic Latin alphabet).
+// "Non-latin" translations are the translations of the words that have custom,
+// country specific characters.
+const char* const kNegativeLatin[] = {
+    "pin",    "parola",   "wagwoord",   "wachtwoord",
+    "fake",   "parole",   "givenname",  "achinsinsi",
+    "token",  "parool",   "firstname",  "facalfaire",
+    "fname",  "lozinka",  "pasahitza",  "focalfaire",
+    "lname",  "passord",  "pasiwedhi",  "iphasiwedi",
+    "geslo",  "huahuna",  "passwuert",  "katalaluan",
+    "heslo",  "fullname", "phasewete",  "adgangskode",
+    "parol",  "optional", "wachtwurd",  "contrasenya",
+    "sandi",  "lastname", "cyfrinair",  "contrasinal",
+    "senha",  "kupuhipa", "katasandi",  "kalmarsirri",
+    "hidden", "password", "loluszais",  "tenimiafina",
+    "second", "passwort", "middlename", "paroladordine",
+    "codice", "pasvorto", "familyname", "inomboloyokuvula",
+    "modpas", "salasana", "motdepasse", "numeraeleiloaesesi"};
+constexpr int kNegativeLatinSize = arraysize(kNegativeLatin);
+
+const char* const kNegativeNonLatin[] = {"fjalëkalim",
+                                         "የይለፍቃል",
+                                         "كلمهالسر",
+                                         "գաղտնաբառ",
+                                         "пароль",
+                                         "পাসওয়ার্ড",
+                                         "парола",
+                                         "密码",
+                                         "密碼",
+                                         "დაგავიწყდათ",
+                                         "κωδικόςπρόσβασης",
+                                         "પાસવર્ડ",
+                                         "סיסמה",
+                                         "पासवर्ड",
+                                         "jelszó",
+                                         "lykilorð",
+                                         "paswọọdụ",
+                                         "パスワード",
+                                         "ಪಾಸ್ವರ್ಡ್",
+                                         "пароль",
+                                         "ការពាក្យសម្ងាត់",
+                                         "암호",
+                                         "şîfre",
+                                         "купуясөз",
+                                         "ລະຫັດຜ່ານ",
+                                         "slaptažodis",
+                                         "лозинка",
+                                         "पासवर्ड",
+                                         "нууцүг",
+                                         "စကားဝှက်ကို",
+                                         "पासवर्ड",
+                                         "رمز",
+                                         "کلمهعبور",
+                                         "hasło",
+                                         "пароль",
+                                         "лозинка",
+                                         "پاسورڊ",
+                                         "මුරපදය",
+                                         "contraseña",
+                                         "lösenord",
+                                         "гузарвожа",
+                                         "கடவுச்சொல்",
+                                         "పాస్వర్డ్",
+                                         "รหัสผ่าน",
+                                         "пароль",
+                                         "پاسورڈ",
+                                         "mậtkhẩu",
+                                         "פּאַראָל",
+                                         "ọrọigbaniwọle"};
+constexpr int kNegativeNonLatinSize = arraysize(kNegativeNonLatin);
+
+const char* const kUsernameLatin[] = {
+    "gatti",      "uzantonomo",   "solonanarana",    "nombredeusuario",
+    "olumulo",    "nomenusoris",  "enwdefnyddiwr",   "nomdutilisateur",
+    "lolowera",   "notandanafn",  "nomedeusuario",   "vartotojovardas",
+    "username",   "ahanjirimara", "gebruikersnaam",  "numedeutilizator",
+    "brugernavn", "benotzernumm", "jinalamtumiaji",  "erabiltzaileizena",
+    "brukernavn", "benutzername", "sunanmaiamfani",  "foydalanuvchinomi",
+    "mosebedisi", "kasutajanimi", "ainmcleachdaidh", "igamalomsebenzisi",
+    "nomdusuari", "lomsebenzisi", "jenengpanganggo", "ingoakaiwhakamahi",
+    "nomeutente", "namapengguna"};
+constexpr int kUsernameLatinSize = arraysize(kUsernameLatin);
+
+const char* const kUsernameNonLatin[] = {"用户名",
+                                         "کاتيجونالو",
+                                         "用戶名",
+                                         "የተጠቃሚስም",
+                                         "логин",
+                                         "اسمالمستخدم",
+                                         "נאמען",
+                                         "کاصارفکانام",
+                                         "ユーザ名",
+                                         "όνομα χρήστη",
+                                         "brûkersnamme",
+                                         "корисничкоиме",
+                                         "nonitilizatè",
+                                         "корисничкоиме",
+                                         "ngaranpamaké",
+                                         "ຊື່ຜູ້ໃຊ້",
+                                         "användarnamn",
+                                         "యూజర్పేరు",
+                                         "korisničkoime",
+                                         "пайдаланушыаты",
+                                         "שםמשתמש",
+                                         "ім'якористувача",
+                                         "کارننوم",
+                                         "хэрэглэгчийннэр",
+                                         "nomedeusuário",
+                                         "имяпользователя",
+                                         "têntruynhập",
+                                         "பயனர்பெயர்",
+                                         "ainmúsáideora",
+                                         "ชื่อผู้ใช้",
+                                         "사용자이름",
+                                         "імякарыстальніка",
+                                         "lietotājvārds",
+                                         "потребителскоиме",
+                                         "uporabniškoime",
+                                         "колдонуучунунаты",
+                                         "kullanıcıadı",
+                                         "පරිශීලකනාමය",
+                                         "istifadəçiadı",
+                                         "օգտագործողիանունը",
+                                         "navêbikarhêner",
+                                         "ಬಳಕೆದಾರಹೆಸರು",
+                                         "emriipërdoruesit",
+                                         "वापरकर्तानाव",
+                                         "käyttäjätunnus",
+                                         "વપરાશકર્તાનામ",
+                                         "felhasználónév",
+                                         "उपयोगकर्तानाम",
+                                         "nazwaużytkownika",
+                                         "ഉപയോക്തൃനാമം",
+                                         "სახელი",
+                                         "အသုံးပြုသူအမည်",
+                                         "نامکاربری",
+                                         "प्रयोगकर्तानाम",
+                                         "uživatelskéjméno",
+                                         "ব্যবহারকারীরনাম",
+                                         "užívateľskémeno",
+                                         "ឈ្មោះអ្នកប្រើប្រាស់"};
+constexpr int kUsernameNonLatinSize = arraysize(kUsernameNonLatin);
+
+const char* const kUserLatin[] = {
+    "user",   "wosuta",   "gebruiker",  "utilizator",
+    "usor",   "notandi",  "gumagamit",  "vartotojas",
+    "fammi",  "olumulo",  "maiamfani",  "cleachdaidh",
+    "utent",  "pemakai",  "mpampiasa",  "umsebenzisi",
+    "bruger", "usuario",  "panganggo",  "utilisateur",
+    "bruker", "benotzer", "uporabnik",  "doutilizador",
+    "numake", "benutzer", "covneegsiv", "erabiltzaile",
+    "usuari", "kasutaja", "defnyddiwr", "kaiwhakamahi",
+    "utente", "korisnik", "mosebedisi", "foydalanuvchi",
+    "uzanto", "pengguna", "mushandisi"};
+constexpr int kUserLatinSize = arraysize(kUserLatin);
+
+const char* const kUserNonLatin[] = {"用户",
+                                     "użytkownik",
+                                     "tagatafaʻaaogā",
+                                     "دکارونکيعکس",
+                                     "用戶",
+                                     "užívateľ",
+                                     "корисник",
+                                     "карыстальнік",
+                                     "brûker",
+                                     "kullanıcı",
+                                     "истифода",
+                                     "អ្នកប្រើ",
+                                     "ọrụ",
+                                     "ተጠቃሚ",
+                                     "באַניצער",
+                                     "хэрэглэгчийн",
+                                     "يوزر",
+                                     "istifadəçi",
+                                     "ຜູ້ໃຊ້",
+                                     "пользователь",
+                                     "صارف",
+                                     "meahoʻohana",
+                                     "потребител",
+                                     "वापरकर्ता",
+                                     "uživatel",
+                                     "ユーザー",
+                                     "מִשׁתַמֵשׁ",
+                                     "ผู้ใช้งาน",
+                                     "사용자",
+                                     "bikaranîvan",
+                                     "колдонуучу",
+                                     "વપરાશકર્તા",
+                                     "përdorues",
+                                     "ngườidùng",
+                                     "корисникот",
+                                     "उपयोगकर्ता",
+                                     "itilizatè",
+                                     "χρήστης",
+                                     "користувач",
+                                     "օգտվողիանձնագիրը",
+                                     "használó",
+                                     "faoiúsáideoir",
+                                     "შესახებ",
+                                     "ব্যবহারকারী",
+                                     "lietotājs",
+                                     "பயனர்",
+                                     "ಬಳಕೆದಾರ",
+                                     "ഉപയോക്താവ്",
+                                     "کاربر",
+                                     "యూజర్",
+                                     "පරිශීලක",
+                                     "प्रयोगकर्ता",
+                                     "användare",
+                                     "المستعمل",
+                                     "пайдаланушы",
+                                     "အသုံးပြုသူကို",
+                                     "käyttäjä"};
+constexpr int kUserNonLatinSize = arraysize(kUserNonLatin);
+
+const char* const kTechnicalWords[] = {
+    "uid",         "newtel",     "uaccount",   "regaccount",  "ureg",
+    "loginid",     "laddress",   "accountreg", "regid",       "regname",
+    "loginname",   "membername", "uname",      "ucreate",     "loginmail",
+    "accountname", "umail",      "loginreg",   "accountid",   "loginaccount",
+    "ulogin",      "regemail",   "newmobile",  "accountlogin"};
+constexpr int kTechnicalWordsSize = arraysize(kTechnicalWords);
+
+const char* const kWeakWords[] = {"id", "login", "mail"};
+constexpr int kWeakWordsSize = arraysize(kWeakWords);
+
+// Words that the algorithm looks for are split into multiple categories.
+// A category may contain latin dictionary and non-latin dictionary. It is
+// mandatory that it has latin one, but non-latin might be missing.
+struct CategoryOfWords {
+  const char* const* const latin_dictionary;
+  const size_t latin_dictionary_size;
+  const char* const* const non_latin_dictionary;
+  const size_t non_latin_dictionary_size;
+};
+
+// Minimum length of a word, in order not to be considered short word.
+// Short words will have different treatment than the others.
+constexpr int kMinimumWordLength = 4;
+
+void BuildValueAndShortTokens(
+    const base::string16& raw_value,
+    base::string16* field_data_value,
+    std::vector<base::string16>* field_data_short_tokens) {
+  // List of separators that can appear in HTML attribute values.
+  static const std::string kDelimiters =
+      "\"\'?%*@!\\/&^#:+~`;,>|<.[](){}-_ 0123456789";
+  base::string16 lowercase_value = base::i18n::ToLower(raw_value);
+  std::vector<base::StringPiece16> tokens =
+      base::SplitStringPiece(lowercase_value, base::ASCIIToUTF16(kDelimiters),
+                             base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  *field_data_value = base::JoinString(tokens, base::string16());
+
+  std::vector<base::StringPiece16> short_tokens;
+  std::copy_if(tokens.begin(), tokens.end(), std::back_inserter(short_tokens),
+               [](const base::StringPiece16& token) {
+                 return token.size() < kMinimumWordLength;
+               });
+
+  for (const base::StringPiece16& token : short_tokens) {
+    field_data_short_tokens->push_back(token.as_string());
+  }
+}
+
+// For a given input element, compute developer and user value, along with
+// developer and user short tokens.
+UsernameFieldData ComputeFieldData(const blink::WebInputElement& input_element,
+                                   const FormFieldData& field) {
+  UsernameFieldData field_data;
+  field_data.input_element = input_element;
+  // When computing the developer value, '$' safety guard is being added
+  // between field name and id, so that forming of accidental words is
+  // prevented.
+  BuildValueAndShortTokens(field.name + base::ASCIIToUTF16("$") + field.id,
+                           &field_data.developer_value,
+                           &field_data.developer_short_tokens);
+  BuildValueAndShortTokens(field.label, &field_data.user_value,
+                           &field_data.user_short_tokens);
+  return field_data;
+}
+
+// For the fields of the given form that can be username fields, compute data
+// needed by the detector.
+void InferUsernameFieldData(
+    const std::vector<blink::WebInputElement>& all_possible_usernames,
+    const FormData& form_data,
+    std::vector<UsernameFieldData>* possible_usernames_data) {
+  // |all_possible_usernames| and |form_data.fields| may have different set of
+  // fields. Match them based on |WebInputElement.NameForAutofill| and
+  // |FormFieldData.name|.
+  size_t current_index = 0;
+
+  for (const blink::WebInputElement& input_element : all_possible_usernames) {
+    for (size_t i = current_index; i < form_data.fields.size(); ++i) {
+      const FormFieldData& field = form_data.fields[i];
+      if (input_element.NameForAutofill().IsEmpty())
+        continue;
+
+      // Find matching form data and web input element.
+      if (field.name == input_element.NameForAutofill().Utf16()) {
+        current_index = i + 1;
+        possible_usernames_data->push_back(
+            ComputeFieldData(input_element, field));
+        break;
+      }
+    }
+  }
+}
+
+// Check if any word from the dictionary is encountered in computed field
+// information.
+bool SearchFieldInDictionary(const base::string16& value,
+                             const std::vector<base::string16>& tokens,
+                             const char* const* dictionary,
+                             const size_t& dictionary_size) {
+  for (size_t i = 0; i < dictionary_size; ++i) {
+    if (strlen(dictionary[i]) < kMinimumWordLength) {
+      // Treat short words by looking up for them in the tokens list.
+      for (const base::string16& token : tokens) {
+        if (token == base::UTF8ToUTF16(dictionary[i]))
+          return true;
+      }
+    } else {
+      // Treat long words by looking for them as a substring in |value|.
+      if (value.find(base::UTF8ToUTF16(dictionary[i])) != std::string::npos)
+        return true;
+    }
+  }
+  return false;
+}
+
+// Check if any word from |category| is encountered in computed field
+// information.
+bool ContainsWordFromCategory(const UsernameFieldData& possible_username,
+                              const CategoryOfWords& category) {
+  // For user value, search in latin and non-latin dictionaries, because this
+  // value is user visible.
+  return SearchFieldInDictionary(
+             possible_username.user_value, possible_username.user_short_tokens,
+             category.latin_dictionary, category.latin_dictionary_size) ||
+         SearchFieldInDictionary(possible_username.user_value,
+                                 possible_username.user_short_tokens,
+                                 category.non_latin_dictionary,
+                                 category.non_latin_dictionary_size) ||
+         // For developer value, only look up in latin dictionaries.
+         SearchFieldInDictionary(possible_username.developer_value,
+                                 possible_username.developer_short_tokens,
+                                 category.latin_dictionary,
+                                 category.latin_dictionary_size);
+}
+
+// Remove from |possible_usernames_data| the elements that definitely cannot be
+// usernames, because their computed values contain at least one negative word.
+void RemoveFieldsWithNegativeWords(
+    std::vector<UsernameFieldData>* possible_usernames_data) {
+  // Words that certainly point to a non-username field.
+  // If field values contain at least one negative word, then the field is
+  // excluded from the list of possible usernames.
+  static const CategoryOfWords kNegativeCategory{
+      kNegativeLatin, kNegativeLatinSize, kNegativeNonLatin,
+      kNegativeNonLatinSize};
+
+  possible_usernames_data->erase(
+      std::remove_if(possible_usernames_data->begin(),
+                     possible_usernames_data->end(),
+                     [](const UsernameFieldData& possible_username) {
+                       return ContainsWordFromCategory(possible_username,
+                                                       kNegativeCategory);
+                     }),
+      possible_usernames_data->end());
+}
+
+// Check if any word from the given category appears in fields from the form.
+// If a word appears in more than 2 fields, we do not make a decision, because
+// it may just be a prefix.
+// If a word appears in 1 or 2 fields, we return the first field in which we
+// found the substring as |username_element|.
+bool FormContainsWordFromCategory(
+    const std::vector<UsernameFieldData>& possible_usernames_data,
+    const CategoryOfWords& category,
+    WebInputElement* username_element) {
+  // Auxiliary element that contains the first field (in order of appearance in
+  // the form) in which a substring is encountered.
+  WebInputElement chosen_field;
+
+  size_t count = 0;
+  for (const UsernameFieldData& field_data : possible_usernames_data) {
+    if (ContainsWordFromCategory(field_data, category)) {
+      if (count == 0)
+        chosen_field = field_data.input_element;
+      count++;
+    }
+  }
+
+  if (count && count <= 2) {
+    *username_element = chosen_field;
+    return true;
+  }
+  return false;
+}
+
+}  // namespace
+
+bool GetUsernameFieldBasedOnHtmlAttributes(
+    const std::vector<blink::WebInputElement>& all_possible_usernames,
+    const FormData& form_data,
+    WebInputElement* username_element) {
+  DCHECK(username_element);
+
+  // Translations of "username".
+  static const CategoryOfWords kUsernameCategory{
+      kUsernameLatin, kUsernameLatinSize, kUsernameNonLatin,
+      kUsernameNonLatinSize};
+
+  // Translations of "user".
+  static const CategoryOfWords kUserCategory{kUserLatin, kUserLatinSize,
+                                             kUserNonLatin, kUserNonLatinSize};
+
+  // Words that certainly point to a username field, if they appear in developer
+  // value. They are technical words, because they can only be used as variable
+  // names, and not as stand-alone words.
+  static const CategoryOfWords kTechnicalCategory{
+      kTechnicalWords, kTechnicalWordsSize, nullptr, 0};
+
+  // Words that might point to a username field.They have the smallest priority
+  // in the heuristic, because there are also field attribute values that
+  // contain them, but are not username fields.
+  static const CategoryOfWords kWeakCategory{kWeakWords, kWeakWordsSize,
+                                             nullptr, 0};
+
+  // These categories contain words that point to username field.
+  static const CategoryOfWords kPositiveCategories[] = {
+      kUsernameCategory, kUserCategory, kTechnicalCategory, kWeakCategory};
+
+  std::vector<UsernameFieldData> possible_usernames_data;
+  InferUsernameFieldData(all_possible_usernames, form_data,
+                         &possible_usernames_data);
+  RemoveFieldsWithNegativeWords(&possible_usernames_data);
+
+  // These are the searches performed by the username detector.
+  // Order of categories is vital: the detector searches for words in descending
+  // order of probability to point to a username field.
+  for (const CategoryOfWords& category : kPositiveCategories) {
+    if (FormContainsWordFromCategory(possible_usernames_data, category,
+                                     username_element)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+}  // namespace autofill
diff --git a/components/autofill/content/renderer/html_based_username_detector.h b/components/autofill/content/renderer/html_based_username_detector.h
new file mode 100644
index 0000000..22adc1f3
--- /dev/null
+++ b/components/autofill/content/renderer/html_based_username_detector.h
@@ -0,0 +1,24 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/common/password_form.h"
+#include "third_party/WebKit/public/web/WebFormControlElement.h"
+#include "third_party/WebKit/public/web/WebInputElement.h"
+
+namespace autofill {
+
+// Classifier for getting username field by analyzing HTML attribute values.
+// The algorithm looks for words that are likely to point to username field
+// (ex. "username", "loginid" etc.), in the attribute values.
+// When the first match is found, the currently analyzed field is saved in
+// |username_element|, and the algorithm ends.
+// By searching for words in order of their probability to be username words,
+// it is sure that the first match will also be the best one.
+// The function returns true if |username_element| was found.
+bool GetUsernameFieldBasedOnHtmlAttributes(
+    const std::vector<blink::WebInputElement>& all_possible_usernames,
+    const FormData& form_data,
+    blink::WebInputElement* username_element);
+
+}  // namespace autofill
diff --git a/components/autofill/content/renderer/password_form_conversion_utils.cc b/components/autofill/content/renderer/password_form_conversion_utils.cc
index 41b6312..151eae7 100644
--- a/components/autofill/content/renderer/password_form_conversion_utils.cc
+++ b/components/autofill/content/renderer/password_form_conversion_utils.cc
@@ -19,6 +19,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/content/renderer/form_autofill_util.h"
+#include "components/autofill/content/renderer/html_based_username_detector.h"
 #include "components/autofill/core/common/autofill_regex_constants.h"
 #include "components/autofill/core/common/autofill_regexes.h"
 #include "components/autofill/core/common/autofill_util.h"
@@ -126,8 +127,9 @@
   if (re2::RE2::FullMatch(
           re2::StringPiece(layout_sequence.data(),
                            base::checked_cast<int>(layout_sequence.size())),
-          g_login_and_signup_matcher.Get()))
+          g_login_and_signup_matcher.Get())) {
     return PasswordForm::Layout::LAYOUT_LOGIN_AND_SIGNUP;
+  }
   return PasswordForm::Layout::LAYOUT_OTHER;
 }
 
@@ -342,8 +344,9 @@
   for (auto& control_element : form.control_elements) {
     const WebInputElement* input_element = ToWebInputElement(&control_element);
     if (!input_element || !input_element->IsEnabled() ||
-        !input_element->IsTextField())
+        !input_element->IsTextField()) {
       continue;
+    }
 
     if (!form_util::IsWebElementVisible(*input_element))
       continue;
@@ -423,8 +426,9 @@
         layout_sequence.push_back('P');
       } else {
         if (FieldHasNonscriptModifiedValue(field_value_and_properties_map,
-                                           *input_element))
+                                           *input_element)) {
           ++number_of_non_empty_text_non_password_fields;
+        }
         if (element_is_invisible && ignore_invisible_usernames)
           continue;
         layout_sequence.push_back('N');
@@ -476,7 +480,6 @@
           // This makes us less confident that we have understood the form. We
           // will stick to our choice that the first such element was the real
           // username.
-
         } else {
           // The first element marked with autocomplete='username'. Take the
           // hint and treat it as the username (overruling the tentative choice
@@ -506,6 +509,15 @@
   if (passwords.empty())
     return false;
 
+  // Call HTML based username detector, only if corresponding flag is enabled.
+  if (base::FeatureList::IsEnabled(
+          password_manager::features::kEnableHtmlBasedUsernameDetector)) {
+    if (username_element.IsNull()) {
+      GetUsernameFieldBasedOnHtmlAttributes(
+          all_possible_usernames, password_form->form_data, &username_element);
+    }
+  }
+
   WebInputElement password;
   WebInputElement new_password;
   WebInputElement confirmation_password;
@@ -534,6 +546,7 @@
     }
   }
 
+  // Base heuristic for username detection.
   DCHECK_EQ(passwords.size(), last_text_input_before_password.size());
   if (username_element.IsNull()) {
     if (!password.IsNull())
@@ -601,9 +614,10 @@
     password_form->password_element = FieldName(password, "anonymous_password");
     blink::WebString password_value = password.Value();
     if (FieldHasNonscriptModifiedValue(field_value_and_properties_map,
-                                       password))
+                                       password)) {
       password_value = blink::WebString::FromUTF16(
           *field_value_and_properties_map->at(password).first);
+    }
     password_form->password_value = password_value.Utf16();
   }
   if (!new_password.IsNull()) {
@@ -704,12 +718,14 @@
   if (!WebFormElementToFormData(
           web_form, blink::WebFormControlElement(),
           field_value_and_properties_map, form_util::EXTRACT_NONE,
-          &password_form->form_data, NULL /* FormFieldData */))
+          &password_form->form_data, NULL /* FormFieldData */)) {
     return std::unique_ptr<PasswordForm>();
+  }
 
   if (!GetPasswordForm(synthetic_form, password_form.get(),
-                       field_value_and_properties_map, form_predictions))
+                       field_value_and_properties_map, form_predictions)) {
     return std::unique_ptr<PasswordForm>();
+  }
   return password_form;
 }
 
@@ -730,11 +746,13 @@
           synthetic_form.fieldsets, synthetic_form.control_elements, nullptr,
           frame.GetDocument(), field_value_and_properties_map,
           form_util::EXTRACT_NONE, &password_form->form_data,
-          nullptr /* FormFieldData */))
+          nullptr /* FormFieldData */)) {
     return std::unique_ptr<PasswordForm>();
+  }
   if (!GetPasswordForm(synthetic_form, password_form.get(),
-                       field_value_and_properties_map, form_predictions))
+                       field_value_and_properties_map, form_predictions)) {
     return std::unique_ptr<PasswordForm>();
+  }
 
   // No actual action on the form, so use the the origin as the action.
   password_form->action = password_form->origin;
diff --git a/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc b/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
index d618e3f..64b4d72 100644
--- a/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
+++ b/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
@@ -65,14 +65,29 @@
         name_and_id, name_and_id, value, autocomplete_attribute.c_str());
   }
 
+  // Add a text field with name, id, value, label and placeholder,
+  // without autocomplete.
+  void AddTextFieldWithoutAutocomplete(const char* name,
+                                       const char* id,
+                                       const char* value,
+                                       const char* label,
+                                       const char* placeholder) {
+    base::StringAppendF(&html_,
+                        "<LABEL for=\"%s\">%s</LABEL>"
+                        "<INPUT type=\"text\" name=\"%s\" id=\"%s\" "
+                        "value=\"%s\" placeholder=\"%s\"/>",
+                        id, label, name, id, value, placeholder);
+  }
+
   // Appends a new password-type field at the end of the form, having the
   // specified |name_and_id|, |value|, and |autocomplete| attributes. Special
   // values for |autocomplete| are the same as in AddTextField.
   void AddPasswordField(const char* name_and_id,
                         const char* value,
                         const char* autocomplete) {
-    std::string autocomplete_attribute(autocomplete ?
-        base::StringPrintf("autocomplete=\"%s\"", autocomplete): "");
+    std::string autocomplete_attribute(
+        autocomplete ? base::StringPrintf("autocomplete=\"%s\"", autocomplete)
+                     : "");
     base::StringAppendF(
         &html_,
         "<INPUT type=\"password\" name=\"%s\" id=\"%s\" value=\"%s\" %s/>",
@@ -94,32 +109,29 @@
 
   // Appends a new hidden-type field at the end of the form, having the
   // specified |name_and_id| and |value| attributes.
-  void AddHiddenField(const char* name_and_id,
-                      const char* value) {
+  void AddHiddenField(const char* name_and_id, const char* value) {
     base::StringAppendF(
-        &html_,
-        "<INPUT type=\"hidden\" name=\"%s\" id=\"%s\" value=\"%s\" />",
+        &html_, "<INPUT type=\"hidden\" name=\"%s\" id=\"%s\" value=\"%s\" />",
         name_and_id, name_and_id, value);
   }
 
   // Append a text field with "display: none".
-  void AddNonDisplayedTextField(const char* name_and_id,
-                                const char* value) {
+  void AddNonDisplayedTextField(const char* name_and_id, const char* value) {
     base::StringAppendF(
-            &html_,
-            "<INPUT type=\"text\" name=\"%s\" id=\"%s\" value=\"%s\""
-             "style=\"display: none;\"/>",
-            name_and_id, name_and_id, value);
+        &html_,
+        "<INPUT type=\"text\" name=\"%s\" id=\"%s\" value=\"%s\""
+        "style=\"display: none;\"/>",
+        name_and_id, name_and_id, value);
   }
 
   // Append a password field with "display: none".
   void AddNonDisplayedPasswordField(const char* name_and_id,
                                     const char* value) {
     base::StringAppendF(
-            &html_,
-            "<INPUT type=\"password\" name=\"%s\" id=\"%s\" value=\"%s\""
-            "style=\"display: none;\"/>",
-            name_and_id, name_and_id, value);
+        &html_,
+        "<INPUT type=\"password\" name=\"%s\" id=\"%s\" value=\"%s\""
+        "style=\"display: none;\"/>",
+        name_and_id, name_and_id, value);
   }
 
   // Append a text field with "visibility: hidden".
@@ -144,16 +156,12 @@
   // |name|.
   void AddSubmitButton(const char* name) {
     base::StringAppendF(
-        &html_,
-        "<INPUT type=\"submit\" name=\"%s\" value=\"Submit\"/>",
-        name);
+        &html_, "<INPUT type=\"submit\" name=\"%s\" value=\"Submit\"/>", name);
   }
 
   // Returns the HTML code for the form containing the fields that have been
   // added so far.
-  std::string ProduceHTML() const {
-    return html_ + "</FORM>";
-  }
+  std::string ProduceHTML() const { return html_ + "</FORM>"; }
 
   // Appends a field of |type| without name or id attribute at the end of the
   // form.
@@ -308,9 +316,262 @@
   EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value);
 }
 
-TEST_F(MAYBE_PasswordFormConversionUtilsTest, IdentifyingUsernameFields) {
+TEST_F(MAYBE_PasswordFormConversionUtilsTest,
+       IdentifyingUsernameFieldsFromDeveloperGroupWithHTMLDetector) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      password_manager::features::kEnableHtmlBasedUsernameDetector);
+
   // Each test case consists of a set of parameters to be plugged into the
   // PasswordFormBuilder below, plus the corresponding expectations.
+  // The test data contains cases that are identified by HTML detector, and not
+  // by base heuristic. Thus, username field does not necessarely have to be
+  // right before password field.
+  // These tests basically check searching in developer group (i.e. name and id
+  // attribute, concatenated, with "$" guard in between).
+  struct TestCase {
+    // Field parameters represent, in order of appearance, field name, field id
+    // and field value.
+    const char* first_text_field_parameters[3];
+    const char* second_text_field_parameters[3];
+    const char* expected_username_element;
+    const char* expected_username_value;
+  } cases[] = {
+      // There are both field name and id.
+      {{"username", "id", "johnsmith"},
+       {"email", "id", "js@google.com"},
+       "username",
+       "johnsmith"},
+      // there is no field id.
+      {{"username", "", "johnsmith"},
+       {"email", "", "js@google.com"},
+       "username",
+       "johnsmith"},
+      // Upper or mixed case shouldn't matter.
+      {{"uSeRnAmE", "id", "johnsmith"},
+       {"email", "id", "js@google.com"},
+       "uSeRnAmE",
+       "johnsmith"},
+      // Check removal of special characters.
+      {{"u1_s2-e3~r4/n5(a)6m#e", "", "johnsmith"},
+       {"email", "", "js@google.com"},
+       "u1_s2-e3~r4/n5(a)6m#e",
+       "johnsmith"},
+      // Check guard between field name and field id.
+      {{"us", "ername", "johnsmith"},
+       {"email", "", "js@google.com"},
+       "email",
+       "js@google.com"},
+      // Check removal of fields with latin negative words in developer group.
+      {{"email", "", "js@google.com"},
+       {"fake_username", "", "johnsmith"},
+       "email",
+       "js@google.com"},
+      {{"email", "mail", "js@google.com"},
+       {"user_name", "fullname", "johnsmith"},
+       "email",
+       "js@google.com"},
+      // Identify latin translations of "username".
+      {{"benutzername", "", "johnsmith"},
+       {"email", "", "js@google.com"},
+       "benutzername",
+       "johnsmith"},
+      // Identify latin translations of "user".
+      {{"utilizator", "", "johnsmith"},
+       {"email", "", "js@google.com"},
+       "utilizator",
+       "johnsmith"},
+      // Identify technical words.
+      {{"loginid", "", "johnsmith"},
+       {"email", "", "js@google.com"},
+       "loginid",
+       "johnsmith"},
+      // Identify weak words.
+      {{"usrname", "", "johnsmith"},
+       {"email", "", "js@google.com"},
+       "email",
+       "js@google.com"},
+      // If word matches in maximum 2 fields, it is accepted.
+      // First encounter is selected as username.
+      {{"loginusername", "", "johnsmith"},
+       {"loginemail", "", "js@google.com"},
+       "loginusername",
+       "johnsmith"},
+      // Check treatment for short dictionary words.
+      {{"identity_name", "", "johnsmith"},
+       {"email", "", "js@google.com"},
+       "email",
+       "js@google.com"}};
+
+  for (size_t i = 0; i < arraysize(cases); ++i) {
+    SCOPED_TRACE(testing::Message() << "Iteration " << i);
+
+    PasswordFormBuilder builder(kTestFormActionURL);
+    builder.AddTextFieldWithoutAutocomplete(
+        cases[i].first_text_field_parameters[0],
+        cases[i].first_text_field_parameters[1],
+        cases[i].first_text_field_parameters[2], "", "");
+    builder.AddTextFieldWithoutAutocomplete(
+        cases[i].second_text_field_parameters[0],
+        cases[i].second_text_field_parameters[1],
+        cases[i].second_text_field_parameters[2], "", "");
+    builder.AddPasswordField("password", "secret", nullptr);
+    builder.AddSubmitButton("submit");
+    std::string html = builder.ProduceHTML();
+
+    std::unique_ptr<PasswordForm> password_form =
+        LoadHTMLAndConvertForm(html, nullptr, false);
+
+    ASSERT_TRUE(password_form);
+
+    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_element),
+              password_form->username_element);
+    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_value),
+              password_form->username_value);
+  }
+
+  // If word matches in more than 2 fields, we don't match on it.
+  // We search for match with another word.
+  PasswordFormBuilder builder(kTestFormActionURL);
+  builder.AddTextFieldWithoutAutocomplete("address", "user", "someaddress", "",
+                                          "");
+  builder.AddTextFieldWithoutAutocomplete("loginid", "user", "johnsmith", "",
+                                          "");
+  builder.AddTextFieldWithoutAutocomplete("tel", "user", "sometel", "", "");
+  builder.AddPasswordField("password", "secret", nullptr);
+  builder.AddSubmitButton("submit");
+  std::string html = builder.ProduceHTML();
+
+  std::unique_ptr<PasswordForm> password_form =
+      LoadHTMLAndConvertForm(html, nullptr, false);
+
+  ASSERT_TRUE(password_form);
+
+  EXPECT_EQ(base::UTF8ToUTF16("loginid"), password_form->username_element);
+  EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value);
+}
+
+TEST_F(MAYBE_PasswordFormConversionUtilsTest,
+       IdentifyingUsernameFieldsFromUserGroupWithHTMLDetector) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      password_manager::features::kEnableHtmlBasedUsernameDetector);
+
+  // Each test case consists of a set of parameters to be plugged into the
+  // PasswordFormBuilder below, plus the corresponding expectations.
+  // The test data contains cases that are identified by HTML detector, and not
+  // by base heuristic. Thus, username field does not necessarely have to be
+  // right before password field.
+  // These tests basically check searching in user group.
+  struct TestCase {
+    // Field parameters represent, in order of appearance, field name, field
+    // id, field value and field label or placeholder.
+    const char* first_text_field_parameters[4];
+    const char* second_text_field_parameters[4];
+    const char* expected_username_element;
+    const char* expected_username_value;
+  } cases[] = {
+      // Developer group does not contain any significant information.
+      // Label information will decide username.
+      {{"name1", "id1", "johnsmith", "Username:"},
+       {"name2", "id2", "js@google.com", "Email:"},
+       "name1",
+       "johnsmith"},
+      // Placeholder information will decide username.
+      {{"name1", "id1", "js@google.com", "Email:"},
+       {"name2", "id2", "johnsmith", "Username:"},
+       "name2",
+       "johnsmith"},
+      // Check removal of special characters.
+      {{"name1", "id1", "johnsmith", "U s er n a m e:"},
+       {"name2", "id2", "js@google.com", "Email:"},
+       "name1",
+       "johnsmith"},
+      // Check removal of fields with latin negative words in user group.
+      {{"name1", "id1", "johnsmith", "Username password:"},
+       {"name2", "id2", "js@google.com", "Email:"},
+       "name2",
+       "js@google.com"},
+      // Check removal of fields with non-latin negative words in user group.
+      {{"name1", "id1", "js@google.com", "Email:"},
+       {"name2", "id2", "johnsmith", "የይለፍቃልየይለፍቃል:"},
+       "name1",
+       "js@google.com"},
+      // Identify latin translations of "username".
+      {{"name1", "id1", "johnsmith", "Username:"},
+       {"name2", "id2", "js@google.com", "Email:"},
+       "name1",
+       "johnsmith"},
+      // Identify non-latin translations of "username".
+      {{"name1", "id1", "johnsmith", "用户名:"},
+       {"name2", "id2", "js@google.com", "Email:"},
+       "name1",
+       "johnsmith"},
+      // Identify latin translations of "user".
+      {{"name1", "id1", "johnsmith", "Wosuta:"},
+       {"name2", "id2", "js@google.com", "Email:"},
+       "name1",
+       "johnsmith"},
+      // Identify non-latin translations of "user".
+      {{"name1", "id1", "johnsmith", "истифода:"},
+       {"name2", "id2", "js@google.com", "Email:"},
+       "name1",
+       "johnsmith"},
+      // Identify weak words.
+      {{"name1", "id1", "johnsmith", "Insert your login details:"},
+       {"name2", "id2", "js@google.com", "Insert your email:"},
+       "name1",
+       "johnsmith"},
+      // Check user group priority, compared to developer group.
+      // User group should have higher priority than developer group.
+      {{"email", "", "js@google.com", "Username:"},
+       {"username", "", "johnsmith", "Email:"},
+       "email",
+       "js@google.com"},
+      // Check treatment for short dictionary words.
+      {{"name1", "", "johnsmith", "Insert your id:"},
+       {"name2", "", "js@google.com", "Insert something:"},
+       "name1",
+       "johnsmith"}};
+
+  for (size_t i = 0; i < arraysize(cases); ++i) {
+    SCOPED_TRACE(testing::Message() << "Iteration " << i);
+
+    PasswordFormBuilder builder(kTestFormActionURL);
+    builder.AddTextFieldWithoutAutocomplete(
+        cases[i].first_text_field_parameters[0],
+        cases[i].first_text_field_parameters[1],
+        cases[i].first_text_field_parameters[2],
+        cases[i].first_text_field_parameters[3], "");
+    builder.AddTextFieldWithoutAutocomplete(
+        cases[i].second_text_field_parameters[0],
+        cases[i].second_text_field_parameters[1],
+        cases[i].second_text_field_parameters[2], "",
+        cases[i].second_text_field_parameters[3]);
+    builder.AddPasswordField("password", "secret", nullptr);
+    builder.AddSubmitButton("submit");
+    std::string html = builder.ProduceHTML();
+
+    std::unique_ptr<PasswordForm> password_form =
+        LoadHTMLAndConvertForm(html, nullptr, false);
+
+    ASSERT_TRUE(password_form);
+
+    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_element),
+              password_form->username_element);
+    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_value),
+              password_form->username_value);
+  }
+}
+
+TEST_F(MAYBE_PasswordFormConversionUtilsTest,
+       IdentifyingUsernameFieldsWithBaseHeuristic) {
+  // Each test case consists of a set of parameters to be plugged into the
+  // PasswordFormBuilder below, plus the corresponding expectations.
+  // The test data should not contain field names that are identified by the
+  // HTML based username detector, because with these tests only the base
+  // heuristic (i.e. select as username the field before the password field)
+  // is tested.
   struct TestCase {
     const char* autocomplete[3];
     const char* expected_username_element;
@@ -321,62 +582,62 @@
       // input field before the first password element should get selected as
       // the username, and the rest should be marked as alternatives.
       {{nullptr, nullptr, nullptr},
-       "username2",
+       "usrname2",
        "William",
-       "John+username1, Smith+username3"},
+       "John+usrname1, Smith+usrname3"},
       // When a sole element is marked with autocomplete='username', it should
-      // be treated as the username, but .other text fields should be added to
+      // be treated as the username, but other text fields should be added to
       // |other_possible_usernames|.
       {{"username", nullptr, nullptr},
-       "username1",
+       "usrname1",
        "John",
-       "William+username2, Smith+username3"},
+       "William+usrname2, Smith+usrname3"},
       {{nullptr, "username", nullptr},
-       "username2",
+       "usrname2",
        "William",
-       "John+username1, Smith+username3"},
+       "John+usrname1, Smith+usrname3"},
       {{nullptr, nullptr, "username"},
-       "username3",
+       "usrname3",
        "Smith",
-       "John+username1, William+username2"},
+       "John+usrname1, William+usrname2"},
       // When >=2 elements have the attribute, the first should be selected as
       // the username, and the rest should go to |other_possible_usernames|.
       {{"username", "username", nullptr},
-       "username1",
+       "usrname1",
        "John",
-       "William+username2, Smith+username3"},
+       "William+usrname2, Smith+usrname3"},
       {{nullptr, "username", "username"},
-       "username2",
+       "usrname2",
        "William",
-       "John+username1, Smith+username3"},
+       "John+usrname1, Smith+usrname3"},
       {{"username", nullptr, "username"},
-       "username1",
+       "usrname1",
        "John",
-       "William+username2, Smith+username3"},
+       "William+usrname2, Smith+usrname3"},
       {{"username", "username", "username"},
-       "username1",
+       "usrname1",
        "John",
-       "William+username2, Smith+username3"},
+       "William+usrname2, Smith+usrname3"},
       // When there is an empty autocomplete attribute (i.e. autocomplete=""),
       // it should have the same effect as having no attribute whatsoever.
-      {{"", "", ""}, "username2", "William", "John+username1, Smith+username3"},
+      {{"", "", ""}, "usrname2", "William", "John+usrname1, Smith+usrname3"},
       {{"", "", "username"},
-       "username3",
+       "usrname3",
        "Smith",
-       "John+username1, William+username2"},
+       "John+usrname1, William+usrname2"},
       {{"username", "", "username"},
-       "username1",
+       "usrname1",
        "John",
-       "William+username2, Smith+username3"},
+       "William+usrname2, Smith+usrname3"},
       // It should not matter if attribute values are upper or mixed case.
       {{"USERNAME", nullptr, "uSeRNaMe"},
-       "username1",
+       "usrname1",
        "John",
-       "William+username2, Smith+username3"},
+       "William+usrname2, Smith+usrname3"},
       {{"uSeRNaMe", nullptr, "USERNAME"},
-       "username1",
+       "usrname1",
        "John",
-       "William+username2, Smith+username3"}};
+       "William+usrname2, Smith+usrname3"}};
 
   for (size_t i = 0; i < arraysize(cases); ++i) {
     for (size_t nonempty_username_fields = 0; nonempty_username_fields < 2;
@@ -397,10 +658,10 @@
       }
 
       PasswordFormBuilder builder(kTestFormActionURL);
-      builder.AddTextField("username1", names[0], cases[i].autocomplete[0]);
-      builder.AddTextField("username2", names[1], cases[i].autocomplete[1]);
+      builder.AddTextField("usrname1", names[0], cases[i].autocomplete[0]);
+      builder.AddTextField("usrname2", names[1], cases[i].autocomplete[1]);
       builder.AddPasswordField("password", "secret", nullptr);
-      builder.AddTextField("username3", names[2], cases[i].autocomplete[2]);
+      builder.AddTextField("usrname3", names[2], cases[i].autocomplete[2]);
       builder.AddPasswordField("password2", "othersecret", nullptr);
       builder.AddSubmitButton("submit");
       std::string html = builder.ProduceHTML();
@@ -458,9 +719,9 @@
     SCOPED_TRACE(testing::Message() << "Iteration " << i);
 
     PasswordFormBuilder builder(kTestFormActionURL);
-    builder.AddTextField("username1", "William", nullptr);
+    builder.AddTextField("usrname1", "William", nullptr);
     builder.AddPasswordField("password1", cases[i].password_values[0], nullptr);
-    builder.AddTextField("username2", "Smith", nullptr);
+    builder.AddTextField("usrname2", "Smith", nullptr);
     builder.AddPasswordField("password2", cases[i].password_values[1], nullptr);
     builder.AddSubmitButton("submit");
     std::string html = builder.ProduceHTML();
@@ -481,12 +742,12 @@
               password_form->confirmation_password_element);
 
     // Do a basic sanity check that we are still selecting the right username.
-    EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element);
+    EXPECT_EQ(base::UTF8ToUTF16("usrname1"), password_form->username_element);
     EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value);
     EXPECT_THAT(
         password_form->other_possible_usernames,
         testing::ElementsAre(PossibleUsernamePair(
-            base::UTF8ToUTF16("Smith"), base::UTF8ToUTF16("username2"))));
+            base::UTF8ToUTF16("Smith"), base::UTF8ToUTF16("usrname2"))));
   }
 }
 
@@ -528,10 +789,10 @@
     SCOPED_TRACE(testing::Message() << "Iteration " << i);
 
     PasswordFormBuilder builder(kTestFormActionURL);
-    builder.AddTextField("username1", "William", nullptr);
+    builder.AddTextField("usrname1", "William", nullptr);
     builder.AddPasswordField("password1", cases[i].password_values[0], nullptr);
     builder.AddPasswordField("password2", cases[i].password_values[1], nullptr);
-    builder.AddTextField("username2", "Smith", nullptr);
+    builder.AddTextField("usrname2", "Smith", nullptr);
     builder.AddPasswordField("password3", cases[i].password_values[2], nullptr);
     builder.AddSubmitButton("submit");
     std::string html = builder.ProduceHTML();
@@ -552,12 +813,12 @@
               password_form->confirmation_password_element);
 
     // Do a basic sanity check that we are still selecting the right username.
-    EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element);
+    EXPECT_EQ(base::UTF8ToUTF16("usrname1"), password_form->username_element);
     EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value);
     EXPECT_THAT(
         password_form->other_possible_usernames,
         testing::ElementsAre(PossibleUsernamePair(
-            base::UTF8ToUTF16("Smith"), base::UTF8ToUTF16("username2"))));
+            base::UTF8ToUTF16("Smith"), base::UTF8ToUTF16("usrname2"))));
   }
 }
 
@@ -565,6 +826,10 @@
        IdentifyingPasswordFieldsWithAutocompleteAttributes) {
   // Each test case consists of a set of parameters to be plugged into the
   // PasswordFormBuilder below, plus the corresponding expectations.
+  // The test data should not contain field names that are identified by the
+  // HTML based username detector, because with these tests only the base
+  // heuristic (i.e. select as username the field before the password field)
+  // is tested.
   struct TestCase {
     const char* autocomplete[3];
     const char* expected_password_element;
@@ -590,7 +855,7 @@
        "",
        "",
        false,
-       "username1",
+       "usrname1",
        "William"},
       {{nullptr, "current-password", nullptr},
        "password2",
@@ -598,7 +863,7 @@
        "",
        "",
        false,
-       "username2",
+       "usrname2",
        "Smith"},
       {{nullptr, nullptr, "current-password"},
        "password3",
@@ -606,7 +871,7 @@
        "",
        "",
        false,
-       "username2",
+       "usrname2",
        "Smith"},
       {{nullptr, "current-password", "current-password"},
        "password2",
@@ -614,7 +879,7 @@
        "",
        "",
        false,
-       "username2",
+       "usrname2",
        "Smith"},
       {{"current-password", nullptr, "current-password"},
        "password1",
@@ -622,7 +887,7 @@
        "",
        "",
        false,
-       "username1",
+       "usrname1",
        "William"},
       {{"current-password", "current-password", nullptr},
        "password1",
@@ -630,7 +895,7 @@
        "",
        "",
        false,
-       "username1",
+       "usrname1",
        "William"},
       {{"current-password", "current-password", "current-password"},
        "password1",
@@ -638,7 +903,7 @@
        "",
        "",
        false,
-       "username1",
+       "usrname1",
        "William"},
       // The same goes vice versa for autocomplete='new-password'.
       {{"new-password", nullptr, nullptr},
@@ -647,7 +912,7 @@
        "password1",
        "alpha",
        true,
-       "username1",
+       "usrname1",
        "William"},
       {{nullptr, "new-password", nullptr},
        "",
@@ -655,7 +920,7 @@
        "password2",
        "beta",
        true,
-       "username2",
+       "usrname2",
        "Smith"},
       {{nullptr, nullptr, "new-password"},
        "",
@@ -663,7 +928,7 @@
        "password3",
        "gamma",
        true,
-       "username2",
+       "usrname2",
        "Smith"},
       {{nullptr, "new-password", "new-password"},
        "",
@@ -671,7 +936,7 @@
        "password2",
        "beta",
        true,
-       "username2",
+       "usrname2",
        "Smith"},
       {{"new-password", nullptr, "new-password"},
        "",
@@ -679,7 +944,7 @@
        "password1",
        "alpha",
        true,
-       "username1",
+       "usrname1",
        "William"},
       {{"new-password", "new-password", nullptr},
        "",
@@ -687,7 +952,7 @@
        "password1",
        "alpha",
        true,
-       "username1",
+       "usrname1",
        "William"},
       {{"new-password", "new-password", "new-password"},
        "",
@@ -695,7 +960,7 @@
        "password1",
        "alpha",
        true,
-       "username1",
+       "usrname1",
        "William"},
       // When there is one element marked with autocomplete='current-password',
       // and one with 'new-password', just comply. Ignore the unmarked password
@@ -706,7 +971,7 @@
        "password2",
        "beta",
        true,
-       "username1",
+       "usrname1",
        "William"},
       {{"current-password", nullptr, "new-password"},
        "password1",
@@ -714,7 +979,7 @@
        "password3",
        "gamma",
        true,
-       "username1",
+       "usrname1",
        "William"},
       {{nullptr, "current-password", "new-password"},
        "password2",
@@ -722,7 +987,7 @@
        "password3",
        "gamma",
        true,
-       "username2",
+       "usrname2",
        "Smith"},
       {{"new-password", "current-password", nullptr},
        "password2",
@@ -730,7 +995,7 @@
        "password1",
        "alpha",
        true,
-       "username2",
+       "usrname2",
        "Smith"},
       {{"new-password", nullptr, "current-password"},
        "password3",
@@ -738,7 +1003,7 @@
        "password1",
        "alpha",
        true,
-       "username2",
+       "usrname2",
        "Smith"},
       {{nullptr, "new-password", "current-password"},
        "password3",
@@ -746,7 +1011,7 @@
        "password2",
        "beta",
        true,
-       "username2",
+       "usrname2",
        "Smith"},
       // In case of duplicated elements of either kind, go with the first one of
       // its kind.
@@ -756,7 +1021,7 @@
        "password3",
        "gamma",
        true,
-       "username1",
+       "usrname1",
        "William"},
       {{"current-password", "new-password", "current-password"},
        "password1",
@@ -764,7 +1029,7 @@
        "password2",
        "beta",
        true,
-       "username1",
+       "usrname1",
        "William"},
       {{"new-password", "current-password", "current-password"},
        "password2",
@@ -772,7 +1037,7 @@
        "password1",
        "alpha",
        true,
-       "username2",
+       "usrname2",
        "Smith"},
       {{"current-password", "new-password", "new-password"},
        "password1",
@@ -780,7 +1045,7 @@
        "password2",
        "beta",
        true,
-       "username1",
+       "usrname1",
        "William"},
       {{"new-password", "current-password", "new-password"},
        "password2",
@@ -788,7 +1053,7 @@
        "password1",
        "alpha",
        true,
-       "username2",
+       "usrname2",
        "Smith"},
       {{"new-password", "new-password", "current-password"},
        "password3",
@@ -796,7 +1061,7 @@
        "password1",
        "alpha",
        true,
-       "username2",
+       "usrname2",
        "Smith"},
       // When there is an empty autocomplete attribute (i.e. autocomplete=""),
       // it should have the same effect as having no attribute whatsoever.
@@ -806,7 +1071,7 @@
        "",
        "",
        false,
-       "username1",
+       "usrname1",
        "William"},
       {{"", "", "new-password"},
        "",
@@ -814,7 +1079,7 @@
        "password3",
        "gamma",
        true,
-       "username2",
+       "usrname2",
        "Smith"},
       {{"", "new-password", ""},
        "",
@@ -822,7 +1087,7 @@
        "password2",
        "beta",
        true,
-       "username2",
+       "usrname2",
        "Smith"},
       {{"", "current-password", "current-password"},
        "password2",
@@ -830,7 +1095,7 @@
        "",
        "",
        false,
-       "username2",
+       "usrname2",
        "Smith"},
       {{"new-password", "", "new-password"},
        "",
@@ -838,7 +1103,7 @@
        "password1",
        "alpha",
        true,
-       "username1",
+       "usrname1",
        "William"},
       {{"new-password", "", "current-password"},
        "password3",
@@ -846,7 +1111,7 @@
        "password1",
        "alpha",
        true,
-       "username2",
+       "usrname2",
        "Smith"},
       // It should not matter if attribute values are upper or mixed case.
       {{nullptr, "current-password", nullptr},
@@ -855,7 +1120,7 @@
        "",
        "",
        false,
-       "username2",
+       "usrname2",
        "Smith"},
       {{nullptr, "CURRENT-PASSWORD", nullptr},
        "password2",
@@ -863,7 +1128,7 @@
        "",
        "",
        false,
-       "username2",
+       "usrname2",
        "Smith"},
       {{nullptr, "new-password", nullptr},
        "",
@@ -871,7 +1136,7 @@
        "password2",
        "beta",
        true,
-       "username2",
+       "usrname2",
        "Smith"},
       {{nullptr, "nEw-PaSsWoRd", nullptr},
        "",
@@ -879,7 +1144,7 @@
        "password2",
        "beta",
        true,
-       "username2",
+       "usrname2",
        "Smith"}};
 
   for (size_t i = 0; i < arraysize(cases); ++i) {
@@ -888,9 +1153,9 @@
     PasswordFormBuilder builder(kTestFormActionURL);
     builder.AddPasswordField("pin1", "123456", nullptr);
     builder.AddPasswordField("pin2", "789101", nullptr);
-    builder.AddTextField("username1", "William", nullptr);
+    builder.AddTextField("usrname1", "William", nullptr);
     builder.AddPasswordField("password1", "alpha", cases[i].autocomplete[0]);
-    builder.AddTextField("username2", "Smith", nullptr);
+    builder.AddTextField("usrname2", "Smith", nullptr);
     builder.AddPasswordField("password2", "beta", cases[i].autocomplete[1]);
     builder.AddPasswordField("password3", "gamma", cases[i].autocomplete[2]);
     builder.AddSubmitButton("submit");
@@ -911,12 +1176,12 @@
       EXPECT_THAT(
           password_form->other_possible_usernames,
           testing::ElementsAre(PossibleUsernamePair(
-              base::UTF8ToUTF16("Smith"), base::UTF8ToUTF16("username2"))));
+              base::UTF8ToUTF16("Smith"), base::UTF8ToUTF16("usrname2"))));
     } else {
       EXPECT_THAT(
           password_form->other_possible_usernames,
           testing::ElementsAre(PossibleUsernamePair(
-              base::UTF8ToUTF16("William"), base::UTF8ToUTF16("username1"))));
+              base::UTF8ToUTF16("William"), base::UTF8ToUTF16("usrname1"))));
     }
     EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element),
               password_form->password_element);
@@ -1178,7 +1443,7 @@
   EXPECT_EQ(base::ASCIIToUTF16("John"), password_form->username_value);
   EXPECT_EQ(base::ASCIIToUTF16("alpha1"), password_form->password_value);
   EXPECT_THAT(password_form->other_possible_passwords,
-              testing::ElementsAre(
+              testing::UnorderedElementsAre(
                   base::ASCIIToUTF16("alpha1"), base::ASCIIToUTF16("alpha2"),
                   base::ASCIIToUTF16("alpha3"), base::ASCIIToUTF16("alpha4")));
 }
@@ -1256,7 +1521,7 @@
   builder.AddTextField("username", "", nullptr);
   builder.AddHiddenField();
   builder.AddPasswordField("password", "", nullptr);
-  builder.AddTextField("username2", "", nullptr);
+  builder.AddTextField("usrname2", "", nullptr);
   builder.AddTextField("someotherfield", "", nullptr);
   builder.AddPasswordField("new_password", "", nullptr);
   builder.AddTextField("someotherfield2", "", nullptr);
diff --git a/components/autofill/core/common/password_form.cc b/components/autofill/core/common/password_form.cc
index cc44dd7..2ba7965 100644
--- a/components/autofill/core/common/password_form.cc
+++ b/components/autofill/core/common/password_form.cc
@@ -96,8 +96,14 @@
 
 PasswordForm::PasswordForm(const PasswordForm& other) = default;
 
+PasswordForm::PasswordForm(PasswordForm&& other) = default;
+
 PasswordForm::~PasswordForm() = default;
 
+PasswordForm& PasswordForm::operator=(const PasswordForm& form) = default;
+
+PasswordForm& PasswordForm::operator=(PasswordForm&& form) = default;
+
 bool PasswordForm::IsPossibleChangePasswordForm() const {
   return !new_password_element.empty() &&
          layout != PasswordForm::Layout::LAYOUT_LOGIN_AND_SIGNUP;
diff --git a/components/autofill/core/common/password_form.h b/components/autofill/core/common/password_form.h
index 0ef3c40..f9155b5 100644
--- a/components/autofill/core/common/password_form.h
+++ b/components/autofill/core/common/password_form.h
@@ -337,7 +337,11 @@
 
   PasswordForm();
   PasswordForm(const PasswordForm& other);
+  PasswordForm(PasswordForm&& other);
   ~PasswordForm();
+
+  PasswordForm& operator=(const PasswordForm& form);
+  PasswordForm& operator=(PasswordForm&& form);
 };
 
 // True if the unique keys for the forms are the same. The unique key is
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn
index 1d727bc..99e7a50 100644
--- a/components/cronet/android/BUILD.gn
+++ b/components/cronet/android/BUILD.gn
@@ -662,10 +662,9 @@
     "test/javatests/src/org/chromium/net/BrotliTest.java",
     "test/javatests/src/org/chromium/net/Criteria.java",
     "test/javatests/src/org/chromium/net/CronetEngineBuilderTest.java",
-    "test/javatests/src/org/chromium/net/CronetTestBase.java",
     "test/javatests/src/org/chromium/net/CronetTestCommon.java",
     "test/javatests/src/org/chromium/net/CronetTestRule.java",
-    "test/javatests/src/org/chromium/net/CronetTestBaseTest.java",
+    "test/javatests/src/org/chromium/net/CronetTestRuleTest.java",
     "test/javatests/src/org/chromium/net/CronetUploadTest.java",
     "test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java",
     "test/javatests/src/org/chromium/net/CronetUrlRequestTest.java",
diff --git a/components/cronet/android/test/cronet_test_util.cc b/components/cronet/android/test/cronet_test_util.cc
index 016e8115..3a589d0 100644
--- a/components/cronet/android/test/cronet_test_util.cc
+++ b/components/cronet/android/test/cronet_test_util.cc
@@ -94,8 +94,8 @@
 void CleanupNetworkThread(JNIEnv* env,
                           const JavaParamRef<jclass>& jcaller,
                           jlong jcontext_adapter) {
-  TestUtil::GetTaskRunner(jcontext_adapter)
-      ->PostTask(FROM_HERE, base::Bind(&CleanupNetworkThreadOnNetworkThread));
+  TestUtil::RunAfterContextInit(
+      jcontext_adapter, base::Bind(&CleanupNetworkThreadOnNetworkThread));
 }
 
 }  // namespace cronet
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamTest.java
index 8509b9e6..420523de 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamTest.java
@@ -14,6 +14,7 @@
 import static org.chromium.base.CollectionUtil.newHashSet;
 import static org.chromium.net.CronetTestRule.SERVER_CERT_PEM;
 import static org.chromium.net.CronetTestRule.SERVER_KEY_PKCS8_PEM;
+import static org.chromium.net.CronetTestRule.assertContains;
 import static org.chromium.net.CronetTestRule.getContext;
 
 import android.os.ConditionVariable;
@@ -222,7 +223,7 @@
         stream.start();
         callback.blockForDone();
         assertTrue(stream.isDone());
-        mTestRule.assertContains("Exception in BidirectionalStream: net::ERR_DISALLOWED_URL_SCHEME",
+        assertContains("Exception in BidirectionalStream: net::ERR_DISALLOWED_URL_SCHEME",
                 callback.mError.getMessage());
         assertEquals(-301, ((NetworkException) callback.mError).getCronetInternalErrorCode());
     }
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestBase.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestBase.java
deleted file mode 100644
index b4aa922..0000000
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestBase.java
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.net;
-
-import android.content.Context;
-import android.test.AndroidTestCase;
-
-import org.chromium.base.Log;
-import org.chromium.net.CronetTestCommon.CronetTestCommonCallback;
-import org.chromium.net.CronetTestRule.CompareDefaultWithCronet;
-import org.chromium.net.CronetTestRule.CronetTestFramework;
-import org.chromium.net.CronetTestRule.OnlyRunCronetHttpURLConnection;
-import org.chromium.net.CronetTestRule.OnlyRunNativeCronet;
-import org.chromium.net.CronetTestRule.RequiresMinApi;
-import org.chromium.net.impl.CronetEngineBase;
-import org.chromium.net.impl.JavaCronetEngine;
-import org.chromium.net.impl.UserAgent;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.AnnotatedElement;
-import java.net.URL;
-import java.net.URLStreamHandlerFactory;
-
-/**
- * Base test class for all CronetTest based tests.
- */
-public class CronetTestBase extends AndroidTestCase implements CronetTestCommonCallback {
-    private static final String TAG = CronetTestBase.class.getSimpleName();
-
-    private final CronetTestCommon mTestCommon;
-
-    public CronetTestBase() {
-        mTestCommon = new CronetTestCommon(this);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mTestCommon.setUp();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        mTestCommon.tearDown();
-        super.tearDown();
-    }
-
-    /**
-     * Starts the CronetTest framework.
-     */
-    protected CronetTestFramework startCronetTestFramework() {
-        return mTestCommon.startCronetTestFramework();
-    }
-
-    /**
-     * Returns {@code true} when test is being run against system HttpURLConnection implementation.
-     */
-    protected boolean testingSystemHttpURLConnection() {
-        return mTestCommon.testingSystemHttpURLConnection();
-    }
-
-    /**
-     * Returns {@code true} when test is being run against the java implementation of CronetEngine.
-     */
-    protected boolean testingJavaImpl() {
-        return mTestCommon.testingJavaImpl();
-    }
-
-
-    @Override
-    protected void runTest() throws Throwable {
-        mTestCommon.setTestingSystemHttpURLConnection(false);
-        mTestCommon.setTestingJavaImpl(false);
-        String packageName = getClass().getPackage().getName();
-
-        // Find the API version required by the test.
-        int requiredApiVersion = mTestCommon.getMaximumAvailableApiLevel();
-        for (Annotation a : getClass().getAnnotations()) {
-            if (a instanceof RequiresMinApi) {
-                requiredApiVersion = ((RequiresMinApi) a).value();
-            }
-        }
-        AnnotatedElement method = getClass().getMethod(getName(), (Class[]) null);
-        for (Annotation a : method.getAnnotations()) {
-            if (a instanceof RequiresMinApi) {
-                // Method scoped requirements take precedence over class scoped
-                // requirements.
-                requiredApiVersion = ((RequiresMinApi) a).value();
-            }
-        }
-
-        if (requiredApiVersion > mTestCommon.getMaximumAvailableApiLevel()) {
-            Log.i(TAG,
-                    getName() + " skipped because it requires API " + requiredApiVersion
-                            + " but only API " + mTestCommon.getMaximumAvailableApiLevel()
-                            + " is present.");
-        } else if (packageName.equals("org.chromium.net.urlconnection")) {
-            try {
-                if (method.isAnnotationPresent(CompareDefaultWithCronet.class)) {
-                    // Run with the default HttpURLConnection implementation first.
-                    mTestCommon.setTestingSystemHttpURLConnection(true);
-                    super.runTest();
-                    // Use Cronet's implementation, and run the same test.
-                    mTestCommon.setTestingSystemHttpURLConnection(false);
-                    URL.setURLStreamHandlerFactory(mTestCommon.getSteamHandlerFactory());
-                    super.runTest();
-                } else if (method.isAnnotationPresent(OnlyRunCronetHttpURLConnection.class)) {
-                    // Run only with Cronet's implementation.
-                    URL.setURLStreamHandlerFactory(mTestCommon.getSteamHandlerFactory());
-                    super.runTest();
-                } else {
-                    // For all other tests.
-                    super.runTest();
-                }
-            } catch (Throwable e) {
-                throw new Throwable("CronetTestBase#runTest failed.", e);
-            }
-        } else if (packageName.equals("org.chromium.net")) {
-            try {
-                super.runTest();
-                if (!method.isAnnotationPresent(OnlyRunNativeCronet.class)) {
-                    if (mTestCommon.getCronetTestFramework() != null) {
-                        ExperimentalCronetEngine.Builder builder = createJavaEngineBuilder();
-                        builder.setUserAgent(UserAgent.from(getContext()));
-                        mTestCommon.getCronetTestFramework().mCronetEngine =
-                                (CronetEngineBase) builder.build();
-                        // Make sure that the instantiated engine is JavaCronetEngine.
-                        assert mTestCommon.getCronetTestFramework().mCronetEngine.getClass()
-                                == JavaCronetEngine.class;
-                    }
-                    mTestCommon.setTestingJavaImpl(true);
-                    super.runTest();
-                }
-            } catch (Throwable e) {
-                throw new Throwable("CronetTestBase#runTest failed.", e);
-            }
-        } else {
-            super.runTest();
-        }
-    }
-
-    /**
-     * Creates and returns {@link ExperimentalCronetEngine.Builder} that creates
-     * Java (platform) based {@link CronetEngine.Builder}.
-     *
-     * @return the {@code CronetEngine.Builder} that builds Java-based {@code Cronet engine}.
-     */
-    ExperimentalCronetEngine.Builder createJavaEngineBuilder() {
-        return mTestCommon.createJavaEngineBuilder();
-    }
-
-    public void assertResponseEquals(UrlResponseInfo expected, UrlResponseInfo actual) {
-        mTestCommon.assertResponseEquals(expected, actual);
-    }
-
-    public static void assertContains(String expectedSubstring, String actualString) {
-        CronetTestCommon.assertContains(expectedSubstring, actualString);
-    }
-
-    public CronetEngine.Builder enableDiskCache(CronetEngine.Builder cronetEngineBuilder) {
-        return mTestCommon.enableDiskCache(cronetEngineBuilder);
-    }
-
-    /**
-     * Sets the {@link URLStreamHandlerFactory} from {@code cronetEngine}.  This should be called
-     * during setUp() and is installed by runTest() as the default when Cronet is tested.
-     */
-    public void setStreamHandlerFactory(CronetEngine cronetEngine) {
-        mTestCommon.setStreamHandlerFactory(cronetEngine);
-    }
-
-    @Override
-    public Context getContextForTestCommon() {
-        return getContext();
-    }
-
-    /**
-     * Prepares the path for the test storage (http cache, QUIC server info).
-     */
-    public static void prepareTestStorage(Context context) {
-        CronetTestCommon.prepareTestStorage(context);
-    }
-
-    /**
-     * Returns the path for the test storage (http cache, QUIC server info).
-     * Also ensures it exists.
-     */
-    static String getTestStorage(Context context) {
-        return CronetTestCommon.getTestStorage();
-    }
-}
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestCommon.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestCommon.java
index 223dff74..0927a7e 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestCommon.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestCommon.java
@@ -4,6 +4,8 @@
 
 package org.chromium.net;
 
+import static org.chromium.net.CronetTestRule.getContext;
+
 import android.content.Context;
 import android.os.StrictMode;
 
@@ -13,10 +15,13 @@
 import org.chromium.base.PathUtils;
 import org.chromium.base.annotations.SuppressFBWarnings;
 import org.chromium.net.CronetTestRule.CronetTestFramework;
+import org.chromium.net.impl.CronetEngineBase;
+import org.chromium.net.impl.JavaCronetEngine;
 import org.chromium.net.impl.JavaCronetProvider;
+import org.chromium.net.impl.UserAgent;
 
 import java.io.File;
-import java.net.URLStreamHandlerFactory;
+import java.net.URL;
 
 // TODO(yolandyan): move this class to its test rule once JUnit4 migration is over
 final class CronetTestCommon {
@@ -25,7 +30,6 @@
     private final CronetTestCommonCallback mCallback;
 
     private CronetTestFramework mCronetTestFramework;
-    private URLStreamHandlerFactory mStreamHandlerFactory;
 
     // {@code true} when test is being run against system HttpURLConnection implementation.
     private boolean mTestingSystemHttpURLConnection;
@@ -140,6 +144,13 @@
      */
     CronetTestFramework startCronetTestFramework() {
         mCronetTestFramework = new CronetTestFramework(mCallback.getContextForTestCommon());
+        if (testingJavaImpl()) {
+            ExperimentalCronetEngine.Builder builder = createJavaEngineBuilder();
+            builder.setUserAgent(UserAgent.from(getContext()));
+            mCronetTestFramework.mCronetEngine = (CronetEngineBase) builder.build();
+            // Make sure that the instantiated engine is JavaCronetEngine.
+            assert mCronetTestFramework.mCronetEngine.getClass() == JavaCronetEngine.class;
+        }
         return mCronetTestFramework;
     }
 
@@ -204,11 +215,9 @@
      * during setUp() and is installed by {@link runTest()} as the default when Cronet is tested.
      */
     void setStreamHandlerFactory(CronetEngine cronetEngine) {
-        mStreamHandlerFactory = cronetEngine.createURLStreamHandlerFactory();
-    }
-
-    URLStreamHandlerFactory getSteamHandlerFactory() {
-        return mStreamHandlerFactory;
+        if (!testingSystemHttpURLConnection()) {
+            URL.setURLStreamHandlerFactory(cronetEngine.createURLStreamHandlerFactory());
+        }
     }
 
     /**
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestRule.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestRule.java
index 8d06ceef..55021a3 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestRule.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestRule.java
@@ -14,15 +14,12 @@
 import org.chromium.base.Log;
 import org.chromium.net.CronetTestCommon.CronetTestCommonCallback;
 import org.chromium.net.impl.CronetEngineBase;
-import org.chromium.net.impl.JavaCronetEngine;
-import org.chromium.net.impl.UserAgent;
 
 import java.lang.annotation.Annotation;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
-import java.net.URL;
 import java.net.URLStreamHandlerFactory;
 
 /**
@@ -130,11 +127,6 @@
                     base.evaluate();
                     // Use Cronet's implementation, and run the same test.
                     mTestCommon.setTestingSystemHttpURLConnection(false);
-                    URL.setURLStreamHandlerFactory(mTestCommon.getSteamHandlerFactory());
-                    base.evaluate();
-                } else if (desc.getAnnotation(OnlyRunCronetHttpURLConnection.class) != null) {
-                    // Run only with Cronet's implementation.
-                    URL.setURLStreamHandlerFactory(mTestCommon.getSteamHandlerFactory());
                     base.evaluate();
                 } else {
                     // For all other tests.
@@ -147,15 +139,6 @@
             try {
                 base.evaluate();
                 if (desc.getAnnotation(OnlyRunNativeCronet.class) == null) {
-                    if (mTestCommon.getCronetTestFramework() != null) {
-                        ExperimentalCronetEngine.Builder builder = createJavaEngineBuilder();
-                        builder.setUserAgent(UserAgent.from(getContext()));
-                        mTestCommon.getCronetTestFramework().mCronetEngine =
-                                (CronetEngineBase) builder.build();
-                        // Make sure that the instantiated engine is JavaCronetEngine.
-                        assert mTestCommon.getCronetTestFramework().mCronetEngine.getClass()
-                                == JavaCronetEngine.class;
-                    }
                     mTestCommon.setTestingJavaImpl(true);
                     base.evaluate();
                 }
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestBaseTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestRuleTest.java
similarity index 62%
rename from components/cronet/android/test/javatests/src/org/chromium/net/CronetTestBaseTest.java
rename to components/cronet/android/test/javatests/src/org/chromium/net/CronetTestRuleTest.java
index 03b468e..fc07f4c 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestBaseTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestRuleTest.java
@@ -4,8 +4,20 @@
 
 package org.chromium.net;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
 import android.support.test.filters.SmallTest;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.net.CronetTestRule.CronetTestFramework;
 import org.chromium.net.CronetTestRule.OnlyRunNativeCronet;
@@ -14,35 +26,36 @@
 import org.chromium.net.impl.JavaCronetEngine;
 
 /**
- * Tests features of CronetTestBase.
+ * Tests features of CronetTestRule.
  */
-public class CronetTestBaseTest extends CronetTestBase {
+@RunWith(BaseJUnit4ClassRunner.class)
+public class CronetTestRuleTest {
+    @Rule
+    public final CronetTestRule mTestRule = new CronetTestRule();
+    @Rule
+    public final TestName mTestName = new TestName();
+
     private CronetTestFramework mTestFramework;
     /**
      * For any test whose name contains "MustRun", it's enforced that the test must run and set
      * {@code mTestWasRun} to {@code true}.
      */
     private boolean mTestWasRun;
-    /**
-     * For {@link #testRunBothImplsMustRun}, use {@link #mTestNativeImplRun} to verify that the
-     * test is run against the native implementation.
-     */
-    private boolean mTestNativeImplRun;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mTestFramework = startCronetTestFramework();
+    @Before
+    public void setUp() throws Exception {
+        mTestWasRun = false;
+        mTestFramework = mTestRule.startCronetTestFramework();
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        if (getName().contains("MustRun") && !mTestWasRun) {
-            fail(getName() + " should have run but didn't.");
+    @After
+    public void tearDown() throws Exception {
+        if (mTestName.getMethodName().contains("MustRun") && !mTestWasRun) {
+            fail(mTestName.getMethodName() + " should have run but didn't.");
         }
-        super.tearDown();
     }
 
+    @Test
     @SmallTest
     @RequiresMinApi(999999999)
     @Feature({"Cronet"})
@@ -50,6 +63,7 @@
         fail("RequiresMinApi failed to disable.");
     }
 
+    @Test
     @SmallTest
     @RequiresMinApi(-999999999)
     @Feature({"Cronet"})
@@ -57,27 +71,27 @@
         mTestWasRun = true;
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testRunBothImplsMustRun() {
-        if (testingJavaImpl()) {
+        if (mTestRule.testingJavaImpl()) {
             assertFalse(mTestWasRun);
-            assertTrue(mTestNativeImplRun);
             mTestWasRun = true;
             assertEquals(mTestFramework.mCronetEngine.getClass(), JavaCronetEngine.class);
         } else {
             assertFalse(mTestWasRun);
-            assertFalse(mTestNativeImplRun);
-            mTestNativeImplRun = true;
+            mTestWasRun = true;
             assertEquals(mTestFramework.mCronetEngine.getClass(), CronetUrlRequestContext.class);
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet
     public void testRunOnlyNativeMustRun() {
-        assertFalse(testingJavaImpl());
+        assertFalse(mTestRule.testingJavaImpl());
         assertFalse(mTestWasRun);
         mTestWasRun = true;
         assertEquals(mTestFramework.mCronetEngine.getClass(), CronetUrlRequestContext.class);
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
index f9e7f000..4437c2b 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
@@ -299,6 +299,7 @@
     @Test
     @SmallTest
     @Feature({"Cronet"})
+    @OnlyRunNativeCronet // JavaCronetEngine doesn't support throwing on repeat shutdown()
     public void testMultipleShutdown() throws Exception {
         final CronetTestFramework testFramework = mTestRule.startCronetTestFramework();
         try {
@@ -331,6 +332,7 @@
     @Test
     @SmallTest
     @Feature({"Cronet"})
+    @OnlyRunNativeCronet // JavaCronetEngine doesn't support throwing on shutdown()
     public void testShutdownAfterCancel() throws Exception {
         final CronetTestFramework testFramework = mTestRule.startCronetTestFramework();
         TestUrlRequestCallback callback = new TestUrlRequestCallback();
@@ -748,6 +750,7 @@
     @Test
     @SmallTest
     @Feature({"Cronet"})
+    @OnlyRunNativeCronet
     public void testNetLogStartMultipleTimes() throws Exception {
         final CronetTestFramework testFramework = mTestRule.startCronetTestFramework();
         File directory = new File(PathUtils.getDataDirectory());
@@ -774,6 +777,7 @@
     @Test
     @SmallTest
     @Feature({"Cronet"})
+    @OnlyRunNativeCronet
     public void testBoundedFileNetLogStartMultipleTimes() throws Exception {
         final CronetTestFramework testFramework = mTestRule.startCronetTestFramework();
         File directory = new File(PathUtils.getDataDirectory());
@@ -804,6 +808,7 @@
     @Test
     @SmallTest
     @Feature({"Cronet"})
+    @OnlyRunNativeCronet
     public void testNetLogStopMultipleTimes() throws Exception {
         final CronetTestFramework testFramework = mTestRule.startCronetTestFramework();
         File directory = new File(PathUtils.getDataDirectory());
@@ -831,6 +836,7 @@
     @Test
     @SmallTest
     @Feature({"Cronet"})
+    @OnlyRunNativeCronet
     public void testBoundedFileNetLogStopMultipleTimes() throws Exception {
         final CronetTestFramework testFramework = mTestRule.startCronetTestFramework();
         File directory = new File(PathUtils.getDataDirectory());
@@ -1219,6 +1225,7 @@
     @Test
     @SmallTest
     @Feature({"Cronet"})
+    @OnlyRunNativeCronet // Java engine doesn't produce metrics
     public void testGetGlobalMetricsDeltas() throws Exception {
         final CronetTestFramework testFramework = mTestRule.startCronetTestFramework();
 
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
index 66bcadab..86fb1b8 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
@@ -4,12 +4,29 @@
 
 package org.chromium.net;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import static org.chromium.net.CronetTestRule.assertContains;
+import static org.chromium.net.CronetTestRule.getContext;
+
 import android.os.Build;
 import android.os.ConditionVariable;
 import android.os.StrictMode;
 import android.support.test.filters.SmallTest;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import org.chromium.base.Log;
+import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.net.CronetTestRule.CronetTestFramework;
@@ -39,28 +56,34 @@
 /**
  * Test functionality of CronetUrlRequest.
  */
-public class CronetUrlRequestTest extends CronetTestBase {
+@RunWith(BaseJUnit4ClassRunner.class)
+public class CronetUrlRequestTest {
     // URL used for base tests.
     private static final String TEST_URL = "http://127.0.0.1:8000";
 
+    @Rule
+    public final CronetTestRule mTestRule = new CronetTestRule();
+
     private CronetTestFramework mTestFramework;
     private MockUrlRequestJobFactory mMockUrlRequestJobFactory;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mTestFramework = startCronetTestFramework();
+    @Before
+    public void setUp() throws Exception {
+        mTestFramework = mTestRule.startCronetTestFramework();
         assertTrue(NativeTestServer.startNativeTestServer(getContext()));
         // Add url interceptors after native application context is initialized.
-        mMockUrlRequestJobFactory = new MockUrlRequestJobFactory(mTestFramework.mCronetEngine);
+        if (!mTestRule.testingJavaImpl()) {
+            mMockUrlRequestJobFactory = new MockUrlRequestJobFactory(mTestFramework.mCronetEngine);
+        }
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        mMockUrlRequestJobFactory.shutdown();
+    @After
+    public void tearDown() throws Exception {
+        if (!mTestRule.testingJavaImpl()) {
+            mMockUrlRequestJobFactory.shutdown();
+        }
         NativeTestServer.shutdownNativeTestServer();
         mTestFramework.mCronetEngine.shutdown();
-        super.tearDown();
     }
 
     private TestUrlRequestCallback startAndWaitForComplete(String url) throws Exception {
@@ -97,6 +120,7 @@
         assertTrue(header.contains(headerValue));
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testBuilderChecks() throws Exception {
@@ -127,6 +151,7 @@
                 NativeTestServer.getRedirectURL(), callback, callback.getExecutor());
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testSimpleGet() throws Exception {
@@ -139,7 +164,7 @@
         assertEquals(callback.mResponseStep, ResponseStep.ON_SUCCEEDED);
         UrlResponseInfo urlResponseInfo = createUrlResponseInfo(new String[] {url}, "OK", 200, 86,
                 "Connection", "close", "Content-Length", "3", "Content-Type", "text/plain");
-        assertResponseEquals(urlResponseInfo, callback.mResponseInfo);
+        mTestRule.assertResponseEquals(urlResponseInfo, callback.mResponseInfo);
         checkResponseInfo(callback.mResponseInfo, NativeTestServer.getEchoMethodURL(), 200, "OK");
     }
 
@@ -185,6 +210,7 @@
     /**
      * Tests that disabling connection migration sets the URLRequest load flag correctly.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet
@@ -198,6 +224,7 @@
      * request works as expected. To make sure there are no unexpected pending
      * messages, does a GET between UrlRequest.Callback callbacks.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testRedirectAsync() throws Exception {
@@ -223,7 +250,7 @@
         UrlResponseInfo expected =
                 createUrlResponseInfo(new String[] {NativeTestServer.getRedirectURL()}, "Found",
                         302, 74, "Location", "/success.txt", "redirect-header", "header-value");
-        assertResponseEquals(expected, callback.mRedirectResponseInfoList.get(0));
+        mTestRule.assertResponseEquals(expected, callback.mRedirectResponseInfoList.get(0));
 
         // Wait for an unrelated request to finish. The request should not
         // advance until followRedirect is invoked.
@@ -274,7 +301,7 @@
                 "header-name", "header-value", "multi-header-name", "header-value1",
                 "multi-header-name", "header-value2");
 
-        assertResponseEquals(urlResponseInfo, callback.mResponseInfo);
+        mTestRule.assertResponseEquals(urlResponseInfo, callback.mResponseInfo);
         // Make sure there are no other pending messages, which would trigger
         // asserts in TestUrlRequestCallback.
         testSimpleGet();
@@ -283,6 +310,7 @@
     /**
      * Tests onRedirectReceived after cancel doesn't cause a crash.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testOnRedirectReceivedAfterCancel() throws Exception {
@@ -344,6 +372,7 @@
         assertTrue(callback.mOnCanceledCalled);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testNotFound() throws Exception {
@@ -360,6 +389,7 @@
     // Checks that UrlRequest.Callback.onFailed is only called once in the case
     // of ERR_CONTENT_LENGTH_MISMATCH, which has an unusual failure path.
     // See http://crbug.com/468803.
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet // No canonical exception to assert on
@@ -382,6 +412,7 @@
         testSimpleGet();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testSetHttpMethod() throws Exception {
@@ -404,6 +435,7 @@
         assertEquals(0, callback.mHttpResponseDataLength);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testBadMethod() throws Exception {
@@ -420,6 +452,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testBadHeaderName() throws Exception {
@@ -436,6 +469,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testAcceptEncodingIgnored() throws Exception {
@@ -449,6 +483,7 @@
         assertFalse(callback.mResponseAsString.contains("foozip"));
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testBadHeaderValue() throws Exception {
@@ -465,6 +500,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testAddHeader() throws Exception {
@@ -481,6 +517,7 @@
         assertEquals(headerValue, callback.mResponseAsString);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testMultiRequestHeaders() throws Exception {
@@ -506,6 +543,7 @@
         assertEquals("header-value2", actualValues.get(0));
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testCustomUserAgent() throws Exception {
@@ -521,6 +559,7 @@
         assertEquals(userAgentValue, callback.mResponseAsString);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testDefaultUserAgent() throws Exception {
@@ -537,6 +576,7 @@
                         ".+Cronet/\\d+\\.\\d+\\.\\d+\\.\\d+.+", callback.mResponseAsString));
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testMockSuccess() throws Exception {
@@ -553,6 +593,7 @@
         assertEquals("header-value2", multiHeader.get(1));
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testResponseHeadersList() throws Exception {
@@ -573,6 +614,7 @@
                 new AbstractMap.SimpleEntry<>("multi-header-name", "header-value2"));
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testMockMultiRedirect() throws Exception {
@@ -588,7 +630,7 @@
                 new String[] {NativeTestServer.getMultiRedirectURL()}, "Found", 302, 77, "Location",
                 "/redirect.html", "redirect-header0", "header-value");
         UrlResponseInfo firstRedirectResponseInfo = callback.mRedirectResponseInfoList.get(0);
-        assertResponseEquals(firstExpectedResponseInfo, firstRedirectResponseInfo);
+        mTestRule.assertResponseEquals(firstExpectedResponseInfo, firstRedirectResponseInfo);
 
         // Check second redirect (redirect.html -> success.txt)
         UrlResponseInfo secondExpectedResponseInfo = createUrlResponseInfo(
@@ -598,12 +640,13 @@
                 "header-name", "header-value", "multi-header-name", "header-value1",
                 "multi-header-name", "header-value2");
 
-        assertResponseEquals(secondExpectedResponseInfo, mResponseInfo);
+        mTestRule.assertResponseEquals(secondExpectedResponseInfo, mResponseInfo);
         assertTrue(callback.mHttpResponseDataLength != 0);
         assertEquals(2, callback.mRedirectCount);
         assertEquals(callback.mResponseStep, ResponseStep.ON_SUCCEEDED);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testMockNotFound() throws Exception {
@@ -611,13 +654,14 @@
                 startAndWaitForComplete(NativeTestServer.getNotFoundURL());
         UrlResponseInfo expected = createUrlResponseInfo(
                 new String[] {NativeTestServer.getNotFoundURL()}, "Not Found", 404, 121);
-        assertResponseEquals(expected, callback.mResponseInfo);
+        mTestRule.assertResponseEquals(expected, callback.mResponseInfo);
         assertTrue(callback.mHttpResponseDataLength != 0);
         assertEquals(0, callback.mRedirectCount);
         assertFalse(callback.mOnErrorCalled);
         assertEquals(callback.mResponseStep, ResponseStep.ON_SUCCEEDED);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet // Java impl doesn't support MockUrlRequestJobFactory
@@ -635,6 +679,7 @@
         assertEquals(ResponseStep.ON_FAILED, callback.mResponseStep);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet // Java impl doesn't support MockUrlRequestJobFactory
@@ -654,6 +699,7 @@
     }
 
     @DisabledTest(message = "crbug.com/738183")
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet // Java impl doesn't support MockUrlRequestJobFactory
@@ -675,6 +721,7 @@
     /**
      * Tests that request continues when client certificate is requested.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet
@@ -692,6 +739,7 @@
     /**
      * Tests that an SSL cert error will be reported via {@link UrlRequest.Callback#onFailed}.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet // Java impl doesn't support MockUrlRequestJobFactory
@@ -710,6 +758,7 @@
     /**
      * Checks that the buffer is updated correctly, when starting at an offset.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testSimpleGetBufferUpdates() throws Exception {
@@ -794,6 +843,7 @@
         testSimpleGet();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testBadBuffers() throws Exception {
@@ -835,6 +885,7 @@
         assertEquals("GET", callback.mResponseAsString);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testNoIoInCancel() throws Exception {
@@ -865,6 +916,7 @@
         assertEquals(true, callback.mOnCanceledCalled);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUnexpectedReads() throws Exception {
@@ -941,6 +993,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUnexpectedFollowRedirects() throws Exception {
@@ -1015,6 +1068,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadSetDataProvider() throws Exception {
@@ -1039,6 +1093,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadEmptyBodySync() throws Exception {
@@ -1062,6 +1117,7 @@
         dataProvider.assertClosed();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadSync() throws Exception {
@@ -1086,6 +1142,7 @@
         assertEquals("test", callback.mResponseAsString);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadMultiplePiecesSync() throws Exception {
@@ -1114,6 +1171,7 @@
         assertEquals("Yet another test", callback.mResponseAsString);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadMultiplePiecesAsync() throws Exception {
@@ -1142,6 +1200,7 @@
         assertEquals("Yet another test", callback.mResponseAsString);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadChangesDefaultMethod() throws Exception {
@@ -1162,6 +1221,7 @@
         assertEquals("POST", callback.mResponseAsString);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadWithSetMethod() throws Exception {
@@ -1185,6 +1245,7 @@
         assertEquals("PUT", callback.mResponseAsString);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadRedirectSync() throws Exception {
@@ -1209,6 +1270,7 @@
         assertEquals("test", callback.mResponseAsString);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadRedirectAsync() throws Exception {
@@ -1233,6 +1295,7 @@
         assertEquals("test", callback.mResponseAsString);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadWithBadLength() throws Exception {
@@ -1266,6 +1329,7 @@
         assertEquals(null, callback.mResponseInfo);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadWithBadLengthBufferAligned() throws Exception {
@@ -1299,6 +1363,7 @@
         assertEquals(null, callback.mResponseInfo);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadReadFailSync() throws Exception {
@@ -1326,6 +1391,7 @@
         assertEquals(null, callback.mResponseInfo);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadLengthFailSync() throws Exception {
@@ -1353,6 +1419,7 @@
         assertEquals(null, callback.mResponseInfo);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadReadFailAsync() throws Exception {
@@ -1381,6 +1448,7 @@
     }
 
     /** This test uses a direct executor for upload, and non direct for callbacks */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testDirectExecutorUploadProhibitedByDefault() throws Exception {
@@ -1415,6 +1483,7 @@
     }
 
     /** This test uses a direct executor for callbacks, and non direct for upload */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testDirectExecutorProhibitedByDefault() throws Exception {
@@ -1451,6 +1520,7 @@
         dataProvider.assertClosed();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testDirectExecutorAllowed() throws Exception {
@@ -1480,6 +1550,7 @@
         assertEquals("test", callback.mResponseAsString);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadReadFailThrown() throws Exception {
@@ -1507,6 +1578,7 @@
         assertEquals(null, callback.mResponseInfo);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadRewindFailSync() throws Exception {
@@ -1532,6 +1604,7 @@
         assertEquals(null, callback.mResponseInfo);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadRewindFailAsync() throws Exception {
@@ -1557,6 +1630,7 @@
         assertEquals(null, callback.mResponseInfo);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadRewindFailThrown() throws Exception {
@@ -1582,6 +1656,7 @@
         assertEquals(null, callback.mResponseInfo);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadChunked() throws Exception {
@@ -1607,6 +1682,7 @@
         assertEquals("test hello", callback.mResponseAsString);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadChunkedLastReadZeroLengthBody() throws Exception {
@@ -1637,6 +1713,7 @@
 
     // Test where an upload fails without ever initializing the
     // UploadDataStream, because it can't connect to the server.
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadFailsWithoutInitializingStream() throws Exception {
@@ -1655,7 +1732,7 @@
         dataProvider.assertClosed();
 
         assertNull(callback.mResponseInfo);
-        if (testingJavaImpl()) {
+        if (mTestRule.testingJavaImpl()) {
             Throwable cause = callback.mError.getCause();
             assertTrue("Exception was: " + cause, cause instanceof ConnectException);
         } else {
@@ -1700,6 +1777,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testFailures() throws Exception {
@@ -1731,6 +1809,7 @@
                 true, true);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testThrowOrCancelInOnSucceeded() {
@@ -1756,6 +1835,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testThrowOrCancelInOnFailed() {
@@ -1783,6 +1863,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testThrowOrCancelInOnCanceled() {
@@ -1812,6 +1893,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet // No destroyed callback for tests
@@ -1847,6 +1929,7 @@
         assertTrue(urlRequest.isDone());
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testUploadExecutorShutdown() throws Exception {
@@ -1911,6 +1994,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet // No adapter to destroy in pure java
@@ -1944,6 +2028,7 @@
     /*
      * Verifies error codes are passed through correctly.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet // Java impl doesn't support MockUrlRequestJobFactory
@@ -1971,6 +2056,7 @@
     /*
      * Verifies no cookies are saved or sent by default.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testCookiesArentSavedOrSent() throws Exception {
@@ -1988,6 +2074,7 @@
         assertEquals("Header not found. :(", callback2.mResponseAsString);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet
@@ -2009,6 +2096,7 @@
      * Tests that legacy onFailed callback is invoked with UrlRequestException if there
      * is no onFailed callback implementation that takes CronetException.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet
@@ -2106,6 +2194,7 @@
         assertEquals(ResponseStep.ON_CANCELED, callback.mResponseStep);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet
@@ -2126,6 +2215,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     /**
@@ -2161,6 +2251,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @RequiresMinApi(8) // JavaUrlRequest fixed in API level 8: crrev.com/499303
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/NQETest.java b/components/cronet/android/test/javatests/src/org/chromium/net/NQETest.java
index 7889c77..52c70ebc 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/NQETest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/NQETest.java
@@ -22,7 +22,6 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.Log;
-import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.SuppressFBWarnings;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Feature;
@@ -43,13 +42,12 @@
  * Test Network Quality Estimator.
  */
 @RunWith(BaseJUnit4ClassRunner.class)
-@JNINamespace("cronet")
 public class NQETest {
+    private static final String TAG = NQETest.class.getSimpleName();
+
     @Rule
     public final CronetTestRule mTestRule = new CronetTestRule();
 
-    private static final String TAG = NQETest.class.getSimpleName();
-
     private EmbeddedTestServer mTestServer;
     private String mUrl;
 
@@ -93,6 +91,7 @@
     @Test
     @SmallTest
     @Feature({"Cronet"})
+    @OnlyRunNativeCronet
     public void testNotEnabled() throws Exception {
         ExperimentalCronetEngine.Builder cronetEngineBuilder =
                 new ExperimentalCronetEngine.Builder(getContext());
@@ -127,6 +126,7 @@
     @Test
     @SmallTest
     @Feature({"Cronet"})
+    @OnlyRunNativeCronet
     public void testListenerRemoved() throws Exception {
         ExperimentalCronetEngine.Builder cronetEngineBuilder =
                 new ExperimentalCronetEngine.Builder(getContext());
@@ -164,6 +164,7 @@
     @Test
     @SmallTest
     @Feature({"Cronet"})
+    @OnlyRunNativeCronet
     public void testQuicDisabled() throws Exception {
         ExperimentalCronetEngine.Builder cronetEngineBuilder =
                 new ExperimentalCronetEngine.Builder(getContext());
@@ -334,6 +335,7 @@
     @Test
     @SmallTest
     @Feature({"Cronet"})
+    @OnlyRunNativeCronet
     public void testQuicDisabledWithParams() throws Exception {
         ExperimentalCronetEngine.Builder cronetEngineBuilder =
                 new ExperimentalCronetEngine.Builder(getContext());
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/PkpTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/PkpTest.java
index 22d899d..af3ee454 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/PkpTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/PkpTest.java
@@ -4,10 +4,24 @@
 
 package org.chromium.net;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import static org.chromium.net.CronetTestRule.getContext;
+import static org.chromium.net.CronetTestRule.getTestStorage;
+
 import android.support.test.filters.SmallTest;
 
 import org.json.JSONObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
+import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.net.CronetTestRule.OnlyRunNativeCronet;
 import org.chromium.net.test.util.CertTestUtil;
@@ -24,7 +38,8 @@
 /**
  * Public-Key-Pinning tests of Cronet Java API.
  */
-public class PkpTest extends CronetTestBase {
+@RunWith(BaseJUnit4ClassRunner.class)
+public class PkpTest {
     private static final String CERT_USED = "quic_test.example.com.crt";
     private static final String[] CERTS_USED = {CERT_USED};
     private static final int DISTANT_FUTURE = Integer.MAX_VALUE;
@@ -35,6 +50,9 @@
     private static final boolean ENABLE_PINNING_BYPASS_FOR_LOCAL_ANCHORS = true;
     private static final boolean DISABLE_PINNING_BYPASS_FOR_LOCAL_ANCHORS = false;
 
+    @Rule
+    public final CronetTestRule mTestRule = new CronetTestRule();
+
     private CronetEngine mCronetEngine;
     private ExperimentalCronetEngine.Builder mBuilder;
     private TestUrlRequestCallback mListener;
@@ -42,9 +60,11 @@
     private String mServerHost; // test.example.com
     private String mDomain; // example.com
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    @Before
+    public void setUp() throws Exception {
+        if (mTestRule.testingJavaImpl()) {
+            return;
+        }
         // Start QUIC Test Server
         System.loadLibrary("cronet_tests");
         QuicTestServer.startQuicTestServer(getContext());
@@ -53,11 +73,10 @@
         mDomain = mServerHost.substring(mServerHost.indexOf('.') + 1, mServerHost.length());
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         QuicTestServer.shutdownQuicTestServer();
         shutdownCronetEngine();
-        super.tearDown();
     }
 
     /**
@@ -66,6 +85,7 @@
      *
      * @throws Exception
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet
@@ -85,6 +105,7 @@
      *
      * @throws Exception
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet
@@ -108,6 +129,7 @@
      *
      * @throws Exception
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet
@@ -128,6 +150,7 @@
      *
      * @throws Exception
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet
@@ -148,6 +171,7 @@
      *
      * @throws Exception
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet
@@ -167,6 +191,7 @@
      *
      * @throws Exception
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet
@@ -187,6 +212,7 @@
      *
      * @throws Exception
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet
@@ -207,8 +233,10 @@
      *
      * @throws Exception
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
+    @OnlyRunNativeCronet
     public void testLocalTrustAnchorPinningEnforced() throws Exception {
         createCronetEngineBuilder(DISABLE_PINNING_BYPASS_FOR_LOCAL_ANCHORS, UNKNOWN_ROOT);
         byte[] nonMatchingHash = generateSomeSha256();
@@ -226,8 +254,10 @@
      *
      * @throws Exception
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
+    @OnlyRunNativeCronet
     public void testLocalTrustAnchorPinningNotEnforced() throws Exception {
         createCronetEngineBuilder(ENABLE_PINNING_BYPASS_FOR_LOCAL_ANCHORS, UNKNOWN_ROOT);
         byte[] nonMatchingHash = generateSomeSha256();
@@ -244,6 +274,7 @@
      *
      * @throws Exception
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunNativeCronet
@@ -270,6 +301,7 @@
      *
      * @throws Exception
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testHostNameArgumentValidation() throws Exception {
@@ -327,6 +359,7 @@
      *
      * @throws Exception
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testNullArguments() throws Exception {
@@ -342,6 +375,7 @@
      *
      * @throws Exception
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     public void testIllegalArgumentExceptionWhenPinValueIsSHA1() throws Exception {
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/UploadDataProvidersTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/UploadDataProvidersTest.java
index 778f671..cc270ea 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/UploadDataProvidersTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/UploadDataProvidersTest.java
@@ -21,7 +21,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.annotations.SuppressFBWarnings;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.net.CronetTestRule.CronetTestFramework;
@@ -37,7 +36,6 @@
     private static final String LOREM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
             + "Proin elementum, libero laoreet fringilla faucibus, metus tortor vehicula ante, "
             + "lacinia lorem eros vel sapien.";
-    @SuppressFBWarnings("URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
     @Rule
     public final CronetTestRule mTestRule = new CronetTestRule();
     private CronetTestFramework mTestFramework;
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetBufferedOutputStreamTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetBufferedOutputStreamTest.java
index ae63e4b..8ca050e1 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetBufferedOutputStreamTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetBufferedOutputStreamTest.java
@@ -4,11 +4,24 @@
 
 package org.chromium.net.urlconnection;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import static org.chromium.net.CronetTestRule.getContext;
+
 import android.support.test.filters.SmallTest;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.net.CronetEngine;
-import org.chromium.net.CronetTestBase;
+import org.chromium.net.CronetTestRule;
 import org.chromium.net.CronetTestRule.CompareDefaultWithCronet;
 import org.chromium.net.CronetTestRule.OnlyRunCronetHttpURLConnection;
 import org.chromium.net.NativeTestServer;
@@ -21,20 +34,23 @@
 /**
  * Tests the CronetBufferedOutputStream implementation.
  */
-public class CronetBufferedOutputStreamTest extends CronetTestBase {
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        setStreamHandlerFactory(new CronetEngine.Builder(getContext()).build());
+@RunWith(BaseJUnit4ClassRunner.class)
+public class CronetBufferedOutputStreamTest {
+    @Rule
+    public final CronetTestRule mTestRule = new CronetTestRule();
+
+    @Before
+    public void setUp() throws Exception {
+        mTestRule.setStreamHandlerFactory(new CronetEngine.Builder(getContext()).build());
         assertTrue(NativeTestServer.startNativeTestServer(getContext()));
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         NativeTestServer.shutdownNativeTestServer();
-        super.tearDown();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -58,6 +74,7 @@
      * writing after being connected, so this test only runs against Cronet's
      * implementation.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunCronetHttpURLConnection
@@ -79,6 +96,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -100,6 +118,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -132,6 +151,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -152,6 +172,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -174,6 +195,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -190,6 +212,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -217,6 +240,7 @@
         connection2.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -234,6 +258,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -264,6 +289,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -282,6 +308,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -302,11 +329,11 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
-    public void testWriteLessThanContentLength()
-            throws Exception {
+    public void testWriteLessThanContentLength() throws Exception {
         URL url = new URL(NativeTestServer.getEchoBodyURL());
         HttpURLConnection connection =
                 (HttpURLConnection) url.openConnection();
@@ -330,6 +357,7 @@
      * Tests that if caller writes more than the content length provided,
      * an exception should occur.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -363,6 +391,7 @@
      * Same as {@code testWriteMoreThanContentLength()}, but it only writes one byte
      * at a time.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -397,6 +426,7 @@
      * Use {@code OnlyRunCronetHttpURLConnection} as the default implementation
      * does not pass this test.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunCronetHttpURLConnection
@@ -417,6 +447,7 @@
     /**
      * Like {@link #testRewind} but does not set Content-Length header.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunCronetHttpURLConnection
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetChunkedOutputStreamTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetChunkedOutputStreamTest.java
index a56e641..60af887 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetChunkedOutputStreamTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetChunkedOutputStreamTest.java
@@ -21,7 +21,6 @@
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.net.CronetEngine;
-import org.chromium.net.CronetTestBase;
 import org.chromium.net.CronetTestRule;
 import org.chromium.net.CronetTestRule.CompareDefaultWithCronet;
 import org.chromium.net.CronetTestRule.OnlyRunCronetHttpURLConnection;
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java
index 7855dc2..d237188 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java
@@ -21,7 +21,6 @@
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.net.CronetEngine;
-import org.chromium.net.CronetTestBase;
 import org.chromium.net.CronetTestRule;
 import org.chromium.net.CronetTestRule.CompareDefaultWithCronet;
 import org.chromium.net.CronetTestRule.OnlyRunCronetHttpURLConnection;
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLConnectionTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLConnectionTest.java
index 8c9d81d..2c9863f7 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLConnectionTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLConnectionTest.java
@@ -4,14 +4,29 @@
 
 package org.chromium.net.urlconnection;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import static org.chromium.net.CronetTestRule.getContext;
+
 import android.os.Build;
 import android.support.test.filters.SmallTest;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import org.chromium.base.annotations.SuppressFBWarnings;
+import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.net.CronetEngine;
 import org.chromium.net.CronetException;
-import org.chromium.net.CronetTestBase;
+import org.chromium.net.CronetTestRule;
 import org.chromium.net.CronetTestRule.CompareDefaultWithCronet;
 import org.chromium.net.CronetTestRule.OnlyRunCronetHttpURLConnection;
 import org.chromium.net.MockUrlRequestJobFactory;
@@ -46,23 +61,27 @@
  * {@code OnlyRunCronetHttpURLConnection} only run Cronet's implementation.
  * See {@link CronetTestBase#runTest()} for details.
  */
-public class CronetHttpURLConnectionTest extends CronetTestBase {
+@RunWith(BaseJUnit4ClassRunner.class)
+public class CronetHttpURLConnectionTest {
+    @Rule
+    public final CronetTestRule mTestRule = new CronetTestRule();
+
     private CronetEngine mCronetEngine;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mCronetEngine = enableDiskCache(new CronetEngine.Builder(getContext())).build();
-        setStreamHandlerFactory(mCronetEngine);
+    @Before
+    public void setUp() throws Exception {
+        mCronetEngine = mTestRule.enableDiskCache(new CronetEngine.Builder(getContext())).build();
+        mTestRule.setStreamHandlerFactory(mCronetEngine);
         assertTrue(NativeTestServer.startNativeTestServer(getContext()));
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         NativeTestServer.shutdownNativeTestServer();
-        super.tearDown();
+        mCronetEngine.shutdown();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -76,6 +95,7 @@
         urlConnection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -93,6 +113,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunCronetHttpURLConnection
@@ -107,6 +128,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunCronetHttpURLConnection
@@ -125,9 +147,11 @@
         } catch (SocketTimeoutException e) {
             // Expected
         }
+        connection.disconnect();
         mockUrlRequestJobFactory.shutdown();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -150,6 +174,7 @@
      * {@code setFixedLengthStreamingMode} is called.
      * Regression test for crbug.com/582975.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -175,6 +200,7 @@
      * {@code setChunkedStreamingMode} is called.
      * Regression test for crbug.com/582975.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -197,6 +223,7 @@
     /**
      * Tests that using reflection to find {@code fixedContentLengthLong} works.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunCronetHttpURLConnection
@@ -222,6 +249,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -250,6 +278,7 @@
         urlConnection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -282,6 +311,7 @@
         assertTrue(NativeTestServer.startNativeTestServer(getContext()));
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -305,6 +335,7 @@
         checkExceptionsAreThrown(urlConnection);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -327,6 +358,7 @@
         checkExceptionsAreThrown(urlConnection);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -339,6 +371,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -354,6 +387,7 @@
         assertEquals("GET", TestUtil.getResponseAsString(urlConnection));
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     // TODO(xunjieli): Currently the wrapper does not throw an exception.
@@ -380,6 +414,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -396,6 +431,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -433,6 +469,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunCronetHttpURLConnection
@@ -451,6 +488,7 @@
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -504,6 +542,7 @@
         conn.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -531,6 +570,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -555,6 +595,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -581,6 +622,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -607,10 +649,8 @@
         connection.disconnect();
     }
 
-    @SuppressFBWarnings({
-            "RANGE_ARRAY_OFFSET",
-            "RANGE_ARRAY_LENGTH"
-            })
+    @SuppressFBWarnings({"RANGE_ARRAY_OFFSET", "RANGE_ARRAY_LENGTH"})
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -647,6 +687,7 @@
         urlConnection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -675,6 +716,7 @@
         TestUtil.checkLargeData(responseData);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -704,6 +746,7 @@
      * Tests batch reading on CronetInputStream when
      * {@link CronetHttpURLConnection#getMoreData} is called multiple times.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunCronetHttpURLConnection
@@ -746,6 +789,7 @@
         mockUrlRequestJobFactory.shutdown();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -766,6 +810,7 @@
         assertTrue(Arrays.equals(testInputBytes, actualOutput));
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -798,6 +843,7 @@
      * Makes sure that disconnect while reading from InputStream, the message
      * loop does not block. Regression test for crbug.com/550605.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -828,7 +874,7 @@
             fail();
         } catch (IOException e) {
             // Expected.
-            if (!testingSystemHttpURLConnection()) {
+            if (!mTestRule.testingSystemHttpURLConnection()) {
                 assertEquals("disconnect() called", e.getMessage());
             }
         }
@@ -838,7 +884,7 @@
             fail();
         } catch (IOException e) {
             // Expected.
-            if (!testingSystemHttpURLConnection()) {
+            if (!mTestRule.testingSystemHttpURLConnection()) {
                 assertEquals("disconnect() called", e.getMessage());
             }
         }
@@ -848,6 +894,7 @@
      * Makes sure that {@link UrlRequest.Callback#onFailed} exception is
      * propagated when calling read on the input stream.
      */
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -865,7 +912,7 @@
                 b = in.read();
             }
             // On KitKat, the default implementation doesn't throw an error.
-            if (!testingSystemHttpURLConnection()) {
+            if (!mTestRule.testingSystemHttpURLConnection()) {
                 // Server closes the connection before EOF can be received.
                 fail();
             }
@@ -881,7 +928,7 @@
         try {
             in.read();
             // On KitKat, the default implementation doesn't throw an error.
-            if (!testingSystemHttpURLConnection()) {
+            if (!mTestRule.testingSystemHttpURLConnection()) {
                 fail();
             }
         } catch (IOException e) {
@@ -895,6 +942,7 @@
         assertTrue(NativeTestServer.startNativeTestServer(getContext()));
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -911,6 +959,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -921,7 +970,8 @@
         connection.setInstanceFollowRedirects(false);
         // Redirect following control broken in Android Marshmallow:
         // https://code.google.com/p/android/issues/detail?id=194495
-        if (!testingSystemHttpURLConnection() || Build.VERSION.SDK_INT != Build.VERSION_CODES.M) {
+        if (!mTestRule.testingSystemHttpURLConnection()
+                || Build.VERSION.SDK_INT != Build.VERSION_CODES.M) {
             assertEquals(302, connection.getResponseCode());
             assertEquals("Found", connection.getResponseMessage());
             assertEquals("/success.txt", connection.getHeaderField("Location"));
@@ -931,6 +981,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -941,7 +992,8 @@
                 (HttpURLConnection) url.openConnection();
         // Redirect following control broken in Android Marshmallow:
         // https://code.google.com/p/android/issues/detail?id=194495
-        if (!testingSystemHttpURLConnection() || Build.VERSION.SDK_INT != Build.VERSION_CODES.M) {
+        if (!mTestRule.testingSystemHttpURLConnection()
+                || Build.VERSION.SDK_INT != Build.VERSION_CODES.M) {
             assertEquals(302, connection.getResponseCode());
             assertEquals("Found", connection.getResponseMessage());
             assertEquals("/success.txt", connection.getHeaderField("Location"));
@@ -951,11 +1003,11 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
-    public void testDisableRedirectsGlobalAfterConnectionIsCreated()
-            throws Exception {
+    public void testDisableRedirectsGlobalAfterConnectionIsCreated() throws Exception {
         HttpURLConnection.setFollowRedirects(true);
         URL url = new URL(NativeTestServer.getFileURL("/redirect.html"));
         HttpURLConnection connection =
@@ -971,6 +1023,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunCronetHttpURLConnection
@@ -990,6 +1043,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -1001,7 +1055,8 @@
         assertEquals(302, connection.getResponseCode());
         assertEquals("Found", connection.getResponseMessage());
         // Behavior changed in Android Marshmallow to not update the URL.
-        if (testingSystemHttpURLConnection() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+        if (mTestRule.testingSystemHttpURLConnection()
+                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
             // Redirected port is randomized, verify everything but port.
             assertEquals(url.getProtocol(), connection.getURL().getProtocol());
             assertEquals(url.getHost(), connection.getURL().getHost());
@@ -1013,6 +1068,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -1058,6 +1114,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -1079,6 +1136,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
@@ -1102,6 +1160,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunCronetHttpURLConnection
@@ -1119,6 +1178,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunCronetHttpURLConnection
@@ -1170,6 +1230,7 @@
         connection.disconnect();
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunCronetHttpURLConnection
@@ -1186,6 +1247,7 @@
                 CacheSetting.USE_CACHE, ExpectedOutcome.SUCCESS);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunCronetHttpURLConnection
@@ -1199,6 +1261,7 @@
                 CacheSetting.DONT_USE_CACHE, ExpectedOutcome.FAILURE);
     }
 
+    @Test
     @SmallTest
     @Feature({"Cronet"})
     @OnlyRunCronetHttpURLConnection
diff --git a/components/cronet/ios/BUILD.gn b/components/cronet/ios/BUILD.gn
index b77ddf3..cde8473 100644
--- a/components/cronet/ios/BUILD.gn
+++ b/components/cronet/ios/BUILD.gn
@@ -259,7 +259,7 @@
     visibility = [ ":$_bundle_target_name" ]
     sources = invoker.public_headers
     outputs = [
-      "{{bundle_root_dir}}/Headers/{{source_file_part}}",
+      "{{bundle_contents_dir}}/Headers/{{source_file_part}}",
     ]
   }
 
@@ -270,7 +270,7 @@
     visibility = [ ":$_bundle_target_name" ]
     sources = get_target_outputs(_static_library_target)
     outputs = [
-      "{{bundle_root_dir}}/$_framework_name",
+      "{{bundle_executable_dir}}/$_framework_name",
     ]
     public_deps = [
       _static_library_target,
@@ -280,9 +280,10 @@
   create_bundle(_bundle_target_name) {
     product_type = "com.apple.product-type.framework"
     bundle_root_dir = "$root_out_dir/Static/$_output_name"
-    bundle_executable_dir = bundle_root_dir
-    bundle_resources_dir = bundle_root_dir
-    bundle_plugins_dir = bundle_root_dir
+    bundle_contents_dir = bundle_root_dir
+    bundle_executable_dir = bundle_contents_dir
+    bundle_resources_dir = bundle_contents_dir
+    bundle_plugins_dir = bundle_contents_dir
     deps = [
       ":$_framework_binary_target",
       ":$_framework_headers_target",
diff --git a/components/domain_reliability/util.cc b/components/domain_reliability/util.cc
index ea25394..f1a1fd8c 100644
--- a/components/domain_reliability/util.cc
+++ b/components/domain_reliability/util.cc
@@ -129,6 +129,7 @@
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_39:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_40:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_41:
+    case net::HttpResponseInfo::CONNECTION_INFO_QUIC_42:
       return "QUIC";
     case net::HttpResponseInfo::NUM_OF_CONNECTION_INFOS:
       NOTREACHED();
diff --git a/components/gcm_driver/crypto/gcm_crypto_test_helpers.cc b/components/gcm_driver/crypto/gcm_crypto_test_helpers.cc
index 3b11cd3..76e860c 100644
--- a/components/gcm_driver/crypto/gcm_crypto_test_helpers.cc
+++ b/components/gcm_driver/crypto/gcm_crypto_test_helpers.cc
@@ -24,10 +24,10 @@
                                       IncomingMessage* message) {
   DCHECK(message);
 
-  std::string private_key, public_key_x509, public_key;
+  std::string private_key, public_key;
 
   // Create an ephemeral key-pair for the sender.
-  if (!CreateP256KeyPair(&private_key, &public_key_x509, &public_key))
+  if (!CreateP256KeyPair(&private_key, &public_key))
     return false;
 
   std::string shared_secret;
diff --git a/components/gcm_driver/crypto/gcm_key_store.cc b/components/gcm_driver/crypto/gcm_key_store.cc
index d13a189..dca5c830 100644
--- a/components/gcm_driver/crypto/gcm_key_store.cc
+++ b/components/gcm_driver/crypto/gcm_key_store.cc
@@ -130,8 +130,8 @@
       << "Instance ID tokens cannot share an app_id with a non-InstanceID GCM "
          "registration";
 
-  std::string private_key, public_key_x509, public_key;
-  if (!CreateP256KeyPair(&private_key, &public_key_x509, &public_key)) {
+  std::string private_key, public_key;
+  if (!CreateP256KeyPair(&private_key, &public_key)) {
     NOTREACHED() << "Unable to initialize a P-256 key pair.";
 
     callback.Run(KeyPair(), std::string() /* auth_secret */);
@@ -155,7 +155,6 @@
   KeyPair* pair = encryption_data.add_keys();
   pair->set_type(KeyPair::ECDH_P256);
   pair->set_private_key(private_key);
-  pair->set_public_key_x509(public_key_x509);
   pair->set_public_key(public_key);
 
   // Write them immediately to our cache, so subsequent calls to
diff --git a/components/gcm_driver/crypto/p256_key_util.cc b/components/gcm_driver/crypto/p256_key_util.cc
index 3d48413..89016cd 100644
--- a/components/gcm_driver/crypto/p256_key_util.cc
+++ b/components/gcm_driver/crypto/p256_key_util.cc
@@ -34,7 +34,6 @@
 }  // namespace
 
 bool CreateP256KeyPair(std::string* out_private_key,
-                       std::string* out_public_key_x509,
                        std::string* out_public_key) {
   DCHECK(out_private_key);
   DCHECK(out_public_key);
@@ -67,21 +66,8 @@
     return false;
   }
 
-  std::vector<uint8_t> public_key_x509;
-
-  // Export the public key to an X.509 SubjectPublicKeyInfo for enabling NSS to
-  // import the key material when computing a shared secret.
-  if (!key_pair->ExportPublicKey(&public_key_x509)) {
-    DLOG(ERROR) << "Unable to export the public key as an X.509 "
-                << "SubjectPublicKeyInfo block.";
-    return false;
-  }
-
   out_private_key->assign(reinterpret_cast<const char*>(private_key.data()),
                           private_key.size());
-  out_public_key_x509->assign(
-      reinterpret_cast<const char*>(public_key_x509.data()),
-      public_key_x509.size());
 
   // Concatenate the leading 0x04 byte and the two uncompressed points.
   out_public_key->reserve(kUncompressedPointBytes);
diff --git a/components/gcm_driver/crypto/p256_key_util.h b/components/gcm_driver/crypto/p256_key_util.h
index aa3bfe1..4e7316f 100644
--- a/components/gcm_driver/crypto/p256_key_util.h
+++ b/components/gcm_driver/crypto/p256_key_util.h
@@ -17,10 +17,8 @@
 // created successfully, and was written to the out arguments.
 //
 // The |out_private_key| will be an ASN.1-encoded PKCS#8 EncryptedPrivateKeyInfo
-// block, |out_public_key_x509| an X.509 SubjectPublicKeyInfo block and
-// |out_public_key| an octet string in uncompressed form per SEC1 2.3.3.
+// block, |out_public_key| an octet string in uncompressed form per SEC1 2.3.3.
 bool CreateP256KeyPair(std::string* out_private_key,
-                       std::string* out_public_key_x509,
                        std::string* out_public_key) WARN_UNUSED_RESULT;
 
 // Computes the shared secret between |private_key| and |peer_public_key|. The
diff --git a/components/gcm_driver/crypto/p256_key_util_unittest.cc b/components/gcm_driver/crypto/p256_key_util_unittest.cc
index 6144a00c..985efe0 100644
--- a/components/gcm_driver/crypto/p256_key_util_unittest.cc
+++ b/components/gcm_driver/crypto/p256_key_util_unittest.cc
@@ -46,38 +46,32 @@
 TEST(P256KeyUtilTest, UniqueKeyPairGeneration) {
   // Canary for determining that no key repetitions are found in few iterations.
   std::set<std::string> seen_private_keys;
-  std::set<std::string> seen_public_keys_x509;
   std::set<std::string> seen_public_keys;
 
   for (int iteration = 0; iteration < 10; ++iteration) {
     SCOPED_TRACE(iteration);
 
-    std::string private_key, public_key_x509, public_key;
-    ASSERT_TRUE(CreateP256KeyPair(&private_key, &public_key_x509, &public_key));
+    std::string private_key, public_key;
+    ASSERT_TRUE(CreateP256KeyPair(&private_key, &public_key));
 
     EXPECT_NE(private_key, public_key);
     EXPECT_GT(private_key.size(), 0u);
-    EXPECT_GT(public_key_x509.size(), 0u);
     EXPECT_EQ(public_key.size(), kUncompressedPointBytes);
 
     EXPECT_EQ(0u, seen_private_keys.count(private_key));
-    EXPECT_EQ(0u, seen_public_keys_x509.count(public_key_x509));
     EXPECT_EQ(0u, seen_public_keys.count(public_key));
 
     seen_private_keys.insert(private_key);
-    seen_public_keys_x509.insert(public_key_x509);
     seen_public_keys.insert(public_key);
   }
 }
 
 TEST(P256KeyUtilTest, SharedSecretCalculation) {
-  std::string bob_private_key, bob_public_key_x509, bob_public_key;
-  std::string alice_private_key, alice_public_key_x509, alice_public_key;
+  std::string bob_private_key, bob_public_key;
+  std::string alice_private_key, alice_public_key;
 
-  ASSERT_TRUE(CreateP256KeyPair(
-      &bob_private_key, &bob_public_key_x509, &bob_public_key));
-  ASSERT_TRUE(CreateP256KeyPair(
-      &alice_private_key, &alice_public_key_x509, &alice_public_key));
+  ASSERT_TRUE(CreateP256KeyPair(&bob_private_key, &bob_public_key));
+  ASSERT_TRUE(CreateP256KeyPair(&alice_private_key, &alice_public_key));
   ASSERT_NE(bob_private_key, alice_private_key);
 
   std::string bob_shared_secret, alice_shared_secret;
@@ -100,14 +94,13 @@
 
 TEST(P256KeyUtilTest, SharedSecretWithPreExistingKey) {
   std::string bob_private_key, bob_public_key;
-  std::string alice_private_key, alice_public_key_x509, alice_public_key;
+  std::string alice_private_key, alice_public_key;
 
   ASSERT_TRUE(base::Base64Decode(kBobPrivateKey, &bob_private_key));
   ASSERT_TRUE(base::Base64Decode(kBobPublicKey, &bob_public_key));
 
   // First verify against a newly created, ephemeral key-pair.
-  ASSERT_TRUE(CreateP256KeyPair(
-      &alice_private_key, &alice_public_key_x509, &alice_public_key));
+  ASSERT_TRUE(CreateP256KeyPair(&alice_private_key, &alice_public_key));
 
   std::string bob_shared_secret, alice_shared_secret;
   ASSERT_TRUE(ComputeSharedP256Secret(bob_private_key, alice_public_key,
diff --git a/components/gcm_driver/crypto/proto/gcm_encryption_data.proto b/components/gcm_driver/crypto/proto/gcm_encryption_data.proto
index 63eecba..9fdc1079 100644
--- a/components/gcm_driver/crypto/proto/gcm_encryption_data.proto
+++ b/components/gcm_driver/crypto/proto/gcm_encryption_data.proto
@@ -22,8 +22,7 @@
   // The private key matching the size requirements of |type|.
   optional bytes private_key = 2;
 
-  // The public key as an X.509 SubjectPublicKeyInfo block.
-  optional bytes public_key_x509 = 3;
+  reserved 3;  // public_key_x509, now deleted.
 
   // The public key as an uncompressed EC point according to SEC 2.3.3.
   optional bytes public_key = 4;
diff --git a/components/metrics/call_stack_profile_metrics_provider.cc b/components/metrics/call_stack_profile_metrics_provider.cc
index 9e34470..cf859488 100644
--- a/components/metrics/call_stack_profile_metrics_provider.cc
+++ b/components/metrics/call_stack_profile_metrics_provider.cc
@@ -469,16 +469,10 @@
       return UI_THREAD;
     case CallStackProfileParams::FILE_THREAD:
       return FILE_THREAD;
-    case CallStackProfileParams::FILE_USER_BLOCKING_THREAD:
-      return FILE_USER_BLOCKING_THREAD;
     case CallStackProfileParams::PROCESS_LAUNCHER_THREAD:
       return PROCESS_LAUNCHER_THREAD;
-    case CallStackProfileParams::CACHE_THREAD:
-      return CACHE_THREAD;
     case CallStackProfileParams::IO_THREAD:
       return IO_THREAD;
-    case CallStackProfileParams::DB_THREAD:
-      return DB_THREAD;
     case CallStackProfileParams::GPU_MAIN_THREAD:
       return GPU_MAIN_THREAD;
     case CallStackProfileParams::RENDER_THREAD:
diff --git a/components/metrics/call_stack_profile_params.h b/components/metrics/call_stack_profile_params.h
index fad2116..c16e6ac 100644
--- a/components/metrics/call_stack_profile_params.h
+++ b/components/metrics/call_stack_profile_params.h
@@ -31,11 +31,8 @@
     // Browser process threads, some of which occur in other processes as well.
     UI_THREAD,
     FILE_THREAD,
-    FILE_USER_BLOCKING_THREAD,
     PROCESS_LAUNCHER_THREAD,
-    CACHE_THREAD,
     IO_THREAD,
-    DB_THREAD,
 
     // GPU process thread.
     GPU_MAIN_THREAD,
diff --git a/components/metrics/proto/execution_context.proto b/components/metrics/proto/execution_context.proto
index 64dfa6e..9a32fa4 100644
--- a/components/metrics/proto/execution_context.proto
+++ b/components/metrics/proto/execution_context.proto
@@ -34,11 +34,11 @@
   // some of which occur in other processes as well.
   UI_THREAD = 1;
   FILE_THREAD = 2;
-  FILE_USER_BLOCKING_THREAD = 3;
+  FILE_USER_BLOCKING_THREAD = 3;  // Deprecated.
   PROCESS_LAUNCHER_THREAD = 4;
-  CACHE_THREAD = 5;
+  CACHE_THREAD = 5;  // Deprecated.
   IO_THREAD = 6;
-  DB_THREAD = 7;
+  DB_THREAD = 7;  // Deprecated.
 
   // GPU process thread.
   GPU_MAIN_THREAD = 8;
diff --git a/components/metrics/public/cpp/call_stack_profile_struct_traits.h b/components/metrics/public/cpp/call_stack_profile_struct_traits.h
index 6a3af7cf..47860c4d 100644
--- a/components/metrics/public/cpp/call_stack_profile_struct_traits.h
+++ b/components/metrics/public/cpp/call_stack_profile_struct_traits.h
@@ -232,16 +232,10 @@
         return metrics::mojom::Thread::UI_THREAD;
       case metrics::CallStackProfileParams::Thread::FILE_THREAD:
         return metrics::mojom::Thread::FILE_THREAD;
-      case metrics::CallStackProfileParams::Thread::FILE_USER_BLOCKING_THREAD:
-        return metrics::mojom::Thread::FILE_USER_BLOCKING_THREAD;
       case metrics::CallStackProfileParams::Thread::PROCESS_LAUNCHER_THREAD:
         return metrics::mojom::Thread::PROCESS_LAUNCHER_THREAD;
-      case metrics::CallStackProfileParams::Thread::CACHE_THREAD:
-        return metrics::mojom::Thread::CACHE_THREAD;
       case metrics::CallStackProfileParams::Thread::IO_THREAD:
         return metrics::mojom::Thread::IO_THREAD;
-      case metrics::CallStackProfileParams::Thread::DB_THREAD:
-        return metrics::mojom::Thread::DB_THREAD;
       case metrics::CallStackProfileParams::Thread::GPU_MAIN_THREAD:
         return metrics::mojom::Thread::GPU_MAIN_THREAD;
       case metrics::CallStackProfileParams::Thread::RENDER_THREAD:
@@ -265,22 +259,12 @@
       case metrics::mojom::Thread::FILE_THREAD:
         *out = metrics::CallStackProfileParams::Thread::FILE_THREAD;
         return true;
-      case metrics::mojom::Thread::FILE_USER_BLOCKING_THREAD:
-        *out =
-            metrics::CallStackProfileParams::Thread::FILE_USER_BLOCKING_THREAD;
-        return true;
       case metrics::mojom::Thread::PROCESS_LAUNCHER_THREAD:
         *out = metrics::CallStackProfileParams::Thread::PROCESS_LAUNCHER_THREAD;
         return true;
-      case metrics::mojom::Thread::CACHE_THREAD:
-        *out = metrics::CallStackProfileParams::Thread::CACHE_THREAD;
-        return true;
       case metrics::mojom::Thread::IO_THREAD:
         *out = metrics::CallStackProfileParams::Thread::IO_THREAD;
         return true;
-      case metrics::mojom::Thread::DB_THREAD:
-        *out = metrics::CallStackProfileParams::Thread::DB_THREAD;
-        return true;
       case metrics::mojom::Thread::GPU_MAIN_THREAD:
         *out = metrics::CallStackProfileParams::Thread::GPU_MAIN_THREAD;
         return true;
diff --git a/components/metrics/public/cpp/call_stack_profile_struct_traits_unittest.cc b/components/metrics/public/cpp/call_stack_profile_struct_traits_unittest.cc
index b6802d3..3d697d4 100644
--- a/components/metrics/public/cpp/call_stack_profile_struct_traits_unittest.cc
+++ b/components/metrics/public/cpp/call_stack_profile_struct_traits_unittest.cc
@@ -309,21 +309,12 @@
   EXPECT_TRUE(proxy_->BounceThread(Thread::FILE_THREAD, &out));
   EXPECT_EQ(Thread::FILE_THREAD, out);
 
-  EXPECT_TRUE(proxy_->BounceThread(Thread::FILE_USER_BLOCKING_THREAD, &out));
-  EXPECT_EQ(Thread::FILE_USER_BLOCKING_THREAD, out);
-
   EXPECT_TRUE(proxy_->BounceThread(Thread::PROCESS_LAUNCHER_THREAD, &out));
   EXPECT_EQ(Thread::PROCESS_LAUNCHER_THREAD, out);
 
-  EXPECT_TRUE(proxy_->BounceThread(Thread::CACHE_THREAD, &out));
-  EXPECT_EQ(Thread::CACHE_THREAD, out);
-
   EXPECT_TRUE(proxy_->BounceThread(Thread::IO_THREAD, &out));
   EXPECT_EQ(Thread::IO_THREAD, out);
 
-  EXPECT_TRUE(proxy_->BounceThread(Thread::DB_THREAD, &out));
-  EXPECT_EQ(Thread::DB_THREAD, out);
-
   EXPECT_TRUE(proxy_->BounceThread(Thread::GPU_MAIN_THREAD, &out));
   EXPECT_EQ(Thread::GPU_MAIN_THREAD, out);
 
diff --git a/components/metrics/public/interfaces/call_stack_profile_collector.mojom b/components/metrics/public/interfaces/call_stack_profile_collector.mojom
index 3573f90..66fbb42f 100644
--- a/components/metrics/public/interfaces/call_stack_profile_collector.mojom
+++ b/components/metrics/public/interfaces/call_stack_profile_collector.mojom
@@ -49,11 +49,8 @@
 
   UI_THREAD,
   FILE_THREAD,
-  FILE_USER_BLOCKING_THREAD,
   PROCESS_LAUNCHER_THREAD,
-  CACHE_THREAD,
   IO_THREAD,
-  DB_THREAD,
 
   GPU_MAIN_THREAD,
 
diff --git a/components/offline_pages/core/downloads/download_ui_adapter.cc b/components/offline_pages/core/downloads/download_ui_adapter.cc
index 8d96015..097c4f48 100644
--- a/components/offline_pages/core/downloads/download_ui_adapter.cc
+++ b/components/offline_pages/core/downloads/download_ui_adapter.cc
@@ -60,9 +60,10 @@
 }
 
 DownloadUIAdapter::ItemInfo::ItemInfo(const OfflinePageItem& page,
-                                      bool temporarily_hidden)
+                                      bool temporarily_hidden,
+                                      bool is_suggested)
     : ui_item(base::MakeUnique<OfflineItem>(
-          OfflineItemConversions::CreateOfflineItem(page))),
+          OfflineItemConversions::CreateOfflineItem(page, is_suggested))),
       is_request(false),
       offline_id(page.offline_id),
       client_id(page.client_id),
@@ -143,7 +144,10 @@
 
   bool temporarily_hidden =
       delegate_->IsTemporarilyHiddenInUI(added_page.client_id);
-  AddItemHelper(base::MakeUnique<ItemInfo>(added_page, temporarily_hidden));
+  bool is_suggested = model->GetPolicyController()->IsSuggested(
+      added_page.client_id.name_space);
+  AddItemHelper(
+      base::MakeUnique<ItemInfo>(added_page, temporarily_hidden, is_suggested));
 }
 
 void DownloadUIAdapter::OfflinePageDeleted(
@@ -370,8 +374,10 @@
       DCHECK(items_.find(guid) == items_.end());
       bool temporarily_hidden =
           delegate_->IsTemporarilyHiddenInUI(page.client_id);
+      bool is_suggested =
+          model_->GetPolicyController()->IsSuggested(page.client_id.name_space);
       std::unique_ptr<ItemInfo> item =
-          base::MakeUnique<ItemInfo>(page, temporarily_hidden);
+          base::MakeUnique<ItemInfo>(page, temporarily_hidden, is_suggested);
       items_[guid] = std::move(item);
     }
   }
diff --git a/components/offline_pages/core/downloads/download_ui_adapter.h b/components/offline_pages/core/downloads/download_ui_adapter.h
index b2b3cf0..c6d3777 100644
--- a/components/offline_pages/core/downloads/download_ui_adapter.h
+++ b/components/offline_pages/core/downloads/download_ui_adapter.h
@@ -116,7 +116,9 @@
   enum class State { NOT_LOADED, LOADING_PAGES, LOADING_REQUESTS, LOADED };
 
   struct ItemInfo {
-    ItemInfo(const OfflinePageItem& page, bool temporarily_hidden);
+    ItemInfo(const OfflinePageItem& page,
+             bool temporarily_hidden,
+             bool is_suggested);
     ItemInfo(const SavePageRequest& request, bool temporarily_hidden);
     ~ItemInfo();
 
diff --git a/components/offline_pages/core/downloads/offline_item_conversions.cc b/components/offline_pages/core/downloads/offline_item_conversions.cc
index a086a3a0..29e463ea 100644
--- a/components/offline_pages/core/downloads/offline_item_conversions.cc
+++ b/components/offline_pages/core/downloads/offline_item_conversions.cc
@@ -16,7 +16,8 @@
 namespace offline_pages {
 
 OfflineItem OfflineItemConversions::CreateOfflineItem(
-    const OfflinePageItem& page) {
+    const OfflinePageItem& page,
+    bool is_suggested) {
   OfflineItem item;
   item.id = ContentId(kOfflinePageNamespace, page.client_id.id);
   item.title = base::UTF16ToUTF8(page.title);
@@ -32,6 +33,7 @@
   item.progress.value = 100;
   item.progress.max = 100;
   item.progress.unit = OfflineItemProgressUnit::PERCENTAGE;
+  item.is_suggested = is_suggested;
 
   return item;
 }
diff --git a/components/offline_pages/core/downloads/offline_item_conversions.h b/components/offline_pages/core/downloads/offline_item_conversions.h
index d34d60a9..47742b7 100644
--- a/components/offline_pages/core/downloads/offline_item_conversions.h
+++ b/components/offline_pages/core/downloads/offline_item_conversions.h
@@ -21,7 +21,8 @@
 // collection representation (for displaying in UI).
 class OfflineItemConversions {
  public:
-  static OfflineItem CreateOfflineItem(const OfflinePageItem& page);
+  static OfflineItem CreateOfflineItem(const OfflinePageItem& page,
+                                       bool is_suggested);
   static OfflineItem CreateOfflineItem(const SavePageRequest& request);
 
  private:
diff --git a/components/offline_pages/core/downloads/offline_item_conversions_unittest.cc b/components/offline_pages/core/downloads/offline_item_conversions_unittest.cc
index 91dc5d4..ab97457 100644
--- a/components/offline_pages/core/downloads/offline_item_conversions_unittest.cc
+++ b/components/offline_pages/core/downloads/offline_item_conversions_unittest.cc
@@ -36,7 +36,7 @@
   offline_page_item.last_access_time = last_access_time;
 
   OfflineItem offline_item =
-      OfflineItemConversions::CreateOfflineItem(offline_page_item);
+      OfflineItemConversions::CreateOfflineItem(offline_page_item, true);
 
   EXPECT_EQ(ContentId(kOfflinePageNamespace, guid), offline_item.id);
   EXPECT_EQ(url, offline_item.page_url);
@@ -52,6 +52,7 @@
   EXPECT_TRUE(offline_item.progress.max.has_value());
   EXPECT_EQ(100, offline_item.progress.max.value());
   EXPECT_EQ(OfflineItemProgressUnit::PERCENTAGE, offline_item.progress.unit);
+  EXPECT_TRUE(offline_item.is_suggested);
 }
 
 TEST(OfflineItemConversionsTest, SavePageRequestConversion) {
@@ -81,6 +82,7 @@
   EXPECT_EQ(0, offline_item.progress.value);
   EXPECT_FALSE(offline_item.progress.max.has_value());
   EXPECT_EQ(OfflineItemProgressUnit::PERCENTAGE, offline_item.progress.unit);
+  EXPECT_FALSE(offline_item.is_suggested);
 }
 
 }  // namespace offline_pages
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index 9d467009..3daef43 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -17,6 +17,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/metrics/proto/omnibox_event.pb.h"
 #include "components/omnibox/browser/autocomplete_classifier.h"
@@ -41,6 +42,10 @@
 #include "ui/gfx/image/image.h"
 #include "url/url_util.h"
 
+#if defined(OS_WIN)
+#include "ui/base/win/osk_display_manager.h"
+#endif
+
 using bookmarks::BookmarkModel;
 using metrics::OmniboxEventProto;
 
@@ -901,6 +906,9 @@
   last_omnibox_focus_ = base::TimeTicks();
   paste_state_ = NONE;
   control_key_state_ = UP;
+#if defined(OS_WIN)
+  ui::OnScreenKeyboardDisplayManager::GetInstance()->DismissVirtualKeyboard();
+#endif
 }
 
 bool OmniboxEditModel::WillHandleEscapeKey() const {
diff --git a/components/password_manager/core/common/password_manager_features.cc b/components/password_manager/core/common/password_manager_features.cc
index b1973e4..44d20f2 100644
--- a/components/password_manager/core/common/password_manager_features.cc
+++ b/components/password_manager/core/common/password_manager_features.cc
@@ -20,6 +20,10 @@
 const base::Feature kDropSyncCredential = {"drop-sync-credential",
                                            base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Use HTML based username detector.
+const base::Feature kEnableHtmlBasedUsernameDetector = {
+    "EnableHtmlBaseUsernameDetector", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enable additional elements in the form popup UI, which will allow the user to
 // view all saved passwords.
 const base::Feature kEnableManualFallbacksGeneration = {
@@ -57,7 +61,7 @@
 
 // Enables password selection while saving username and password details.
 extern const base::Feature kEnablePasswordSelection{
-    "EnablePasswordSelection", base::FEATURE_DISABLED_BY_DEFAULT};
+    "EnablePasswordSelection", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Disallow autofilling of the sync credential.
 const base::Feature kProtectSyncCredential = {
diff --git a/components/password_manager/core/common/password_manager_features.h b/components/password_manager/core/common/password_manager_features.h
index 0e3a673..d762a7b 100644
--- a/components/password_manager/core/common/password_manager_features.h
+++ b/components/password_manager/core/common/password_manager_features.h
@@ -19,6 +19,7 @@
 
 extern const base::Feature kAffiliationBasedMatching;
 extern const base::Feature kDropSyncCredential;
+extern const base::Feature kEnableHtmlBasedUsernameDetector;
 extern const base::Feature kEnableManualFallbacksFilling;
 extern const base::Feature kEnableManualFallbacksFillingStandalone;
 extern const base::Feature kEnableManualFallbacksGeneration;
diff --git a/components/password_manager_strings.grdp b/components/password_manager_strings.grdp
index 7f988e7..2067bcea 100644
--- a/components/password_manager_strings.grdp
+++ b/components/password_manager_strings.grdp
@@ -1,6 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
 <grit-part>
 
+  <message name="IDS_MANAGE_PASSWORDS_AUTO_SIGNIN_TITLE" desc="The title of the auto-signin toast.">
+    Signing in as <ph name="username">$1<ex>chef@google.com</ex></ph>
+  </message>
   <message name="IDS_PASSWORD_MANAGER_EMPTY_LOGIN" desc="A placeholder for the 'Manage Passwords' bubble's empty username label">
     (No username)
   </message>
diff --git a/components/policy/BUILD.gn b/components/policy/BUILD.gn
index 42758e8..3939de0 100644
--- a/components/policy/BUILD.gn
+++ b/components/policy/BUILD.gn
@@ -434,8 +434,9 @@
   }
 
   create_bundle("chrome_manifest_bundle") {
-    bundle_root_dir = "$root_out_dir/$chrome_mac_bundle_id.manifest/Contents"
-    bundle_resources_dir = "$bundle_root_dir/Resources"
+    bundle_root_dir = "$root_out_dir/$chrome_mac_bundle_id.manifest"
+    bundle_contents_dir = "$bundle_root_dir/Contents"
+    bundle_resources_dir = "$bundle_contents_dir/Resources"
 
     deps = [
       ":manifest_bundle_data",
diff --git a/components/safe_browsing/db/v4_local_database_manager.cc b/components/safe_browsing/db/v4_local_database_manager.cc
index da446ab..b504923 100644
--- a/components/safe_browsing/db/v4_local_database_manager.cc
+++ b/components/safe_browsing/db/v4_local_database_manager.cc
@@ -16,6 +16,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/strings/string_util.h"
 #include "base/task_scheduler/post_task.h"
 #include "components/safe_browsing/db/v4_feature_list.h"
 #include "components/safe_browsing/db/v4_protocol_manager_util.h"
@@ -32,6 +33,10 @@
 const ThreatSeverity kLeastSeverity =
     std::numeric_limits<ThreatSeverity>::max();
 
+const char* const kV4UnusedStoreFileExists =
+    "SafeBrowsing.V4UnusedStoreFileExists";
+const char* const kV3Suffix = ".V3.";
+
 // The list of the name of any store files that are no longer used and can be
 // safely deleted from the disk. There's no overlap allowed between the files
 // on this list and the list returned by GetListInfos().
@@ -138,6 +143,19 @@
   return stores_to_check;
 }
 
+const char* const kPVer3FileNameSuffixesToDelete[] = {
+    "Bloom",        "Bloom Prefix Set",   "Csd Whitelist",
+    "Download",     "Download Whitelist", "Extension Blacklist",
+    "IP Blacklist", "Module Whitelist",   "Resource Blacklist",
+    "UwS List",     "UwS List Prefix Set"};
+
+std::string GetUmaSuffixForPVer3FileNameSuffix(const std::string& suffix) {
+  DCHECK(!suffix.empty());
+  std::string uma_suffix;
+  base::RemoveChars(suffix, base::kWhitespaceASCII, &uma_suffix);
+  return uma_suffix;
+}
+
 }  // namespace
 
 V4LocalDatabaseManager::PendingCheck::PendingCheck(
@@ -218,6 +236,7 @@
   DCHECK(!list_infos_.empty());
 
   DeleteUnusedStoreFiles();
+  DeletePVer3StoreFiles();
 
   DVLOG(1) << "V4LocalDatabaseManager::V4LocalDatabaseManager: "
            << "base_path_: " << base_path_.AsUTF8Unsafe();
@@ -568,6 +587,27 @@
   }
 }
 
+void V4LocalDatabaseManager::DeletePVer3StoreFiles() {
+  // PVer3 files are directly in the profile directory, whereas PVer4 files are
+  // under "Safe Browsing" directory, so we need to look in the DirName() of
+  // base_path_.
+  for (auto* const pver3_store_suffix : kPVer3FileNameSuffixesToDelete) {
+    const base::FilePath store_path = base_path_.DirName().AppendASCII(
+        std::string("Safe Browsing ") + pver3_store_suffix);
+    bool path_exists = base::PathExists(store_path);
+    base::UmaHistogramBoolean(
+        std::string(kV4UnusedStoreFileExists) + kV3Suffix +
+            GetUmaSuffixForPVer3FileNameSuffix(pver3_store_suffix),
+        path_exists);
+    if (!path_exists) {
+      continue;
+    }
+    task_runner_->PostTask(
+        FROM_HERE, base::Bind(base::IgnoreResult(&base::DeleteFile), store_path,
+                              false /* recursive */));
+  }
+}
+
 void V4LocalDatabaseManager::DeleteUnusedStoreFiles() {
   for (auto* const store_filename_to_delete : kStoreFileNamesToDelete) {
     // Is the file marked for deletion also being used for a valid V4Store?
@@ -579,9 +619,9 @@
       const base::FilePath store_path =
           base_path_.AppendASCII(store_filename_to_delete);
       bool path_exists = base::PathExists(store_path);
-      base::UmaHistogramBoolean("SafeBrowsing.V4UnusedStoreFileExists" +
-                                    GetUmaSuffixForStore(store_path),
-                                path_exists);
+      base::UmaHistogramBoolean(
+          kV4UnusedStoreFileExists + GetUmaSuffixForStore(store_path),
+          path_exists);
       if (!path_exists) {
         continue;
       }
diff --git a/components/safe_browsing/db/v4_local_database_manager.h b/components/safe_browsing/db/v4_local_database_manager.h
index bb96c6ba..79bcf4a6 100644
--- a/components/safe_browsing/db/v4_local_database_manager.h
+++ b/components/safe_browsing/db/v4_local_database_manager.h
@@ -208,6 +208,11 @@
   // Called when the database has been updated and schedules the next update.
   void DatabaseUpdated();
 
+  // Delete any PVer3 list files from disk because PVer3 has been deprecated.
+  // This method can be removed after the UMA metrics for the following prefix
+  // go down to 0 in Stable: "SafeBrowsing.V4UnusedStoreFileExists.V3."
+  void DeletePVer3StoreFiles();
+
   // Delete any *.store files from disk that are no longer used.
   void DeleteUnusedStoreFiles();
 
diff --git a/components/safe_browsing/web_ui/safe_browsing_ui.cc b/components/safe_browsing/web_ui/safe_browsing_ui.cc
index 2fdb069..85f5b758 100644
--- a/components/safe_browsing/web_ui/safe_browsing_ui.cc
+++ b/components/safe_browsing/web_ui/safe_browsing_ui.cc
@@ -233,28 +233,30 @@
 #endif
 
 std::string ParseThreatDetailsInfo(
-    ClientSafeBrowsingReportRequest* client_safe_browsing_report_request) {
+    const ClientSafeBrowsingReportRequest& report) {
   std::string report_request_parsed;
   base::DictionaryValue report_request;
-  if (client_safe_browsing_report_request->has_type()) {
-    report_request.SetInteger(
-        "type", static_cast<int>(client_safe_browsing_report_request->type()));
+  if (report.has_type()) {
+    report_request.SetInteger("type", static_cast<int>(report.type()));
   }
-  if (client_safe_browsing_report_request->has_page_url())
-    report_request.SetString("page_url",
-                             client_safe_browsing_report_request->page_url());
-  if (client_safe_browsing_report_request->has_client_country()) {
-    report_request.SetString(
-        "client_country",
-        client_safe_browsing_report_request->client_country());
+  if (report.has_page_url())
+    report_request.SetString("page_url", report.page_url());
+  if (report.has_client_country()) {
+    report_request.SetString("client_country", report.client_country());
   }
-  if (client_safe_browsing_report_request->has_repeat_visit()) {
-    report_request.SetInteger(
-        "repeat_visit", client_safe_browsing_report_request->repeat_visit());
+  if (report.has_repeat_visit()) {
+    report_request.SetInteger("repeat_visit", report.repeat_visit());
   }
-  if (client_safe_browsing_report_request->has_did_proceed()) {
-    report_request.SetInteger(
-        "did_proceed", client_safe_browsing_report_request->did_proceed());
+  if (report.has_did_proceed()) {
+    report_request.SetInteger("did_proceed", report.did_proceed());
+  }
+  std::string serialized;
+  if (report.SerializeToString(&serialized)) {
+    std::string base64_encoded;
+    base::Base64UrlEncode(serialized,
+                          base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+                          &base64_encoded);
+    report_request.SetString("base64(serialized)", base64_encoded);
   }
 
   base::Value* report_request_tree = &report_request;
@@ -363,7 +365,7 @@
 
   for (const auto& report : reports) {
     sent_reports.GetList().push_back(
-        base::Value(ParseThreatDetailsInfo(report.get())));
+        base::Value(ParseThreatDetailsInfo(*report.get())));
 
     AllowJavascript();
     std::string callback_id;
@@ -376,7 +378,7 @@
     ClientSafeBrowsingReportRequest* threat_detail) {
   AllowJavascript();
   FireWebUIListener("threat-details-update",
-                    base::Value(ParseThreatDetailsInfo(threat_detail)));
+                    base::Value(ParseThreatDetailsInfo(*threat_detail)));
 }
 
 void SafeBrowsingUIHandler::RegisterMessages() {
diff --git a/components/signin/core/browser/account_reconcilor.h b/components/signin/core/browser/account_reconcilor.h
index 519f7ee5..57463776 100644
--- a/components/signin/core/browser/account_reconcilor.h
+++ b/components/signin/core/browser/account_reconcilor.h
@@ -111,6 +111,7 @@
   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, SigninManagerRegistration);
   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, Reauth);
   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, ProfileAlreadyConnected);
+  FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTestDice, TableRowTest);
   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, EnabledWithDice);
   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, DiceReconcileWhithoutSignin);
   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, DiceReconcileNoop);
diff --git a/components/signin/ios/browser/BUILD.gn b/components/signin/ios/browser/BUILD.gn
index f34f92ee..e08dc16 100644
--- a/components/signin/ios/browser/BUILD.gn
+++ b/components/signin/ios/browser/BUILD.gn
@@ -19,6 +19,7 @@
   ]
 
   deps = [
+    ":active_state_manager",
     "//base",
     "//components/content_settings/core/browser",
     "//components/google/core/browser",
@@ -33,6 +34,20 @@
   ]
 }
 
+source_set("active_state_manager") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "active_state_manager.h",
+    "active_state_manager_impl.h",
+    "active_state_manager_impl.mm",
+  ]
+
+  deps = [
+    "//base",
+    "//ios/web",
+  ]
+}
+
 source_set("test_support") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
@@ -53,10 +68,12 @@
   testonly = true
   sources = [
     "account_consistency_service_unittest.mm",
+    "active_state_manager_impl_unittest.mm",
     "profile_oauth2_token_service_ios_delegate_unittest.mm",
   ]
 
   deps = [
+    ":active_state_manager",
     ":test_support",
     "//components/prefs:test_support",
     "//components/signin/core/browser",
diff --git a/components/signin/ios/browser/account_consistency_service.h b/components/signin/ios/browser/account_consistency_service.h
index e05b4c7..67a79db8 100644
--- a/components/signin/ios/browser/account_consistency_service.h
+++ b/components/signin/ios/browser/account_consistency_service.h
@@ -18,8 +18,8 @@
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/signin/core/browser/gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/signin_manager.h"
+#include "components/signin/ios/browser/active_state_manager.h"
 #import "components/signin/ios/browser/manage_accounts_delegate.h"
-#include "ios/web/public/active_state_manager.h"
 
 namespace web {
 class BrowserState;
@@ -41,7 +41,7 @@
 class AccountConsistencyService : public KeyedService,
                                   public GaiaCookieManagerService::Observer,
                                   public SigninManagerBase::Observer,
-                                  public web::ActiveStateManager::Observer {
+                                  public ActiveStateManager::Observer {
  public:
   // Name of the preference property that persists the domains that have a
   // CHROME_CONNECTED cookie set by this service.
diff --git a/components/signin/ios/browser/account_consistency_service.mm b/components/signin/ios/browser/account_consistency_service.mm
index cf4ab48..8c8306c 100644
--- a/components/signin/ios/browser/account_consistency_service.mm
+++ b/components/signin/ios/browser/account_consistency_service.mm
@@ -221,7 +221,7 @@
       applying_cookie_requests_(false) {
   gaia_cookie_manager_service_->AddObserver(this);
   signin_manager_->AddObserver(this);
-  web::BrowserState::GetActiveStateManager(browser_state_)->AddObserver(this);
+  ActiveStateManager::FromBrowserState(browser_state_)->AddObserver(this);
   LoadFromPrefs();
   if (signin_manager_->IsAuthenticated()) {
     AddChromeConnectedCookies();
@@ -307,8 +307,7 @@
 void AccountConsistencyService::Shutdown() {
   gaia_cookie_manager_service_->RemoveObserver(this);
   signin_manager_->RemoveObserver(this);
-  web::BrowserState::GetActiveStateManager(browser_state_)
-      ->RemoveObserver(this);
+  ActiveStateManager::FromBrowserState(browser_state_)->RemoveObserver(this);
   ResetWKWebView();
   web_state_handlers_.clear();
 }
@@ -322,7 +321,7 @@
   if (cookie_requests_.empty()) {
     return;
   }
-  if (!web::BrowserState::GetActiveStateManager(browser_state_)->IsActive()) {
+  if (!ActiveStateManager::FromBrowserState(browser_state_)->IsActive()) {
     // Web view usage isn't active for now, ignore cookie requests for now and
     // wait to be notified that it became active again.
     return;
@@ -389,7 +388,7 @@
 }
 
 WKWebView* AccountConsistencyService::GetWKWebView() {
-  if (!web::BrowserState::GetActiveStateManager(browser_state_)->IsActive()) {
+  if (!ActiveStateManager::FromBrowserState(browser_state_)->IsActive()) {
     // |browser_state_| is not active, WKWebView linked to this browser state
     // should not exist or be created.
     return nil;
diff --git a/components/signin/ios/browser/account_consistency_service_unittest.mm b/components/signin/ios/browser/account_consistency_service_unittest.mm
index 7b940c0..c916953 100644
--- a/components/signin/ios/browser/account_consistency_service_unittest.mm
+++ b/components/signin/ios/browser/account_consistency_service_unittest.mm
@@ -123,7 +123,7 @@
  protected:
   void SetUp() override {
     PlatformTest::SetUp();
-    web::BrowserState::GetActiveStateManager(&browser_state_)->SetActive(true);
+    ActiveStateManager::FromBrowserState(&browser_state_)->SetActive(true);
     AccountConsistencyService::RegisterPrefs(prefs_.registry());
     AccountTrackerService::RegisterPrefs(prefs_.registry());
     content_settings::CookieSettings::RegisterProfilePrefs(prefs_.registry());
@@ -146,7 +146,7 @@
   void TearDown() override {
     account_consistency_service_->Shutdown();
     settings_map_->ShutdownOnUIThread();
-    web::BrowserState::GetActiveStateManager(&browser_state_)->SetActive(false);
+    ActiveStateManager::FromBrowserState(&browser_state_)->SetActive(false);
     PlatformTest::TearDown();
   }
 
@@ -222,7 +222,7 @@
 // inactive.
 TEST_F(AccountConsistencyServiceTest, OnInactive) {
   [[GetMockWKWebView() expect] stopLoading];
-  web::BrowserState::GetActiveStateManager(&browser_state_)->SetActive(false);
+  ActiveStateManager::FromBrowserState(&browser_state_)->SetActive(false);
   EXPECT_OCMOCK_VERIFY(GetMockWKWebView());
 }
 
@@ -265,9 +265,9 @@
   [[GetMockWKWebView() expect] setNavigationDelegate:[OCMArg isNotNil]];
   AddPageLoadedExpectation(kGoogleUrl, true /* continue_navigation */);
   AddPageLoadedExpectation(kYoutubeUrl, true /* continue_navigation */);
-  web::BrowserState::GetActiveStateManager(&browser_state_)->SetActive(false);
+  ActiveStateManager::FromBrowserState(&browser_state_)->SetActive(false);
   SignIn();
-  web::BrowserState::GetActiveStateManager(&browser_state_)->SetActive(true);
+  ActiveStateManager::FromBrowserState(&browser_state_)->SetActive(true);
   EXPECT_OCMOCK_VERIFY(GetMockWKWebView());
 }
 
@@ -282,8 +282,8 @@
   AddPageLoadedExpectation(kGoogleUrl, true /* continue_navigation */);
   AddPageLoadedExpectation(kYoutubeUrl, true /* continue_navigation */);
   SignIn();
-  web::BrowserState::GetActiveStateManager(&browser_state_)->SetActive(false);
-  web::BrowserState::GetActiveStateManager(&browser_state_)->SetActive(true);
+  ActiveStateManager::FromBrowserState(&browser_state_)->SetActive(false);
+  ActiveStateManager::FromBrowserState(&browser_state_)->SetActive(true);
   EXPECT_OCMOCK_VERIFY(GetMockWKWebView());
 }
 
diff --git a/ios/web/public/active_state_manager.h b/components/signin/ios/browser/active_state_manager.h
similarity index 68%
rename from ios/web/public/active_state_manager.h
rename to components/signin/ios/browser/active_state_manager.h
index 325e936d..f4bf4e3 100644
--- a/ios/web/public/active_state_manager.h
+++ b/components/signin/ios/browser/active_state_manager.h
@@ -2,19 +2,29 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_PUBLIC_ACTIVE_STATE_MANAGER_H_
-#define IOS_WEB_PUBLIC_ACTIVE_STATE_MANAGER_H_
+#ifndef COMPONENTS_SIGNIN_IOS_BROWSER_ACTIVE_STATE_MANAGER_H_
+#define COMPONENTS_SIGNIN_IOS_BROWSER_ACTIVE_STATE_MANAGER_H_
 
 #include "base/macros.h"
 
 namespace web {
-
 class BrowserState;
+}  // namespace web
 
 // Manages the active state associated with a particular BrowserState. Not
 // thread safe. Must be used only on the main thread.
 class ActiveStateManager {
  public:
+  // Returns whether |browser_state| has an associated ActiveStateManager.
+  // Must only be accessed from main thread.
+  static bool ExistsForBrowserState(web::BrowserState* browser_state);
+
+  // Returns the ActiveStateManager associated with |browser_state.|
+  // Lazily creates one if an ActiveStateManager is not already associated with
+  // the |browser_state|. |browser_state| cannot be a nullptr.  Must be accessed
+  // only from the main thread.
+  static ActiveStateManager* FromBrowserState(web::BrowserState* browser_state);
+
   // Sets the active state of the ActiveStateManager. At most one
   // ActiveStateManager can be active at any given time in the app. A
   // ActiveStateManager must be made inactive before it is destroyed. It is
@@ -44,6 +54,4 @@
   virtual ~ActiveStateManager(){};
 };
 
-}  // namespace web
-
-#endif  // IOS_WEB_PUBLIC_ACTIVE_STATE_MANAGER_H_
+#endif  // COMPONENTS_SIGNIN_IOS_BROWSER_ACTIVE_STATE_MANAGER_H_
diff --git a/ios/web/active_state_manager_impl.h b/components/signin/ios/browser/active_state_manager_impl.h
similarity index 72%
rename from ios/web/active_state_manager_impl.h
rename to components/signin/ios/browser/active_state_manager_impl.h
index 3a383384..d25e5ae 100644
--- a/ios/web/active_state_manager_impl.h
+++ b/components/signin/ios/browser/active_state_manager_impl.h
@@ -2,24 +2,24 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_ACTIVE_STATE_MANAGER_IMPL_H_
-#define IOS_WEB_ACTIVE_STATE_MANAGER_IMPL_H_
+#ifndef COMPONENTS_SIGNIN_IOS_BROWSER_ACTIVE_STATE_MANAGER_IMPL_H_
+#define COMPONENTS_SIGNIN_IOS_BROWSER_ACTIVE_STATE_MANAGER_IMPL_H_
 
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "base/supports_user_data.h"
-#include "ios/web/public/active_state_manager.h"
+#include "components/signin/ios/browser/active_state_manager.h"
 
 namespace web {
-
 class BrowserState;
+}  // namespace web
 
 // Concrete subclass of web::ActiveStateManager. Informs observers when an
 // ActiveStateManager becomes active/inactive.
 class ActiveStateManagerImpl : public ActiveStateManager,
                                public base::SupportsUserData::Data {
  public:
-  explicit ActiveStateManagerImpl(BrowserState* browser_state);
+  explicit ActiveStateManagerImpl(web::BrowserState* browser_state);
   ~ActiveStateManagerImpl() override;
 
   // ActiveStateManager methods.
@@ -29,7 +29,7 @@
   void RemoveObserver(ActiveStateManager::Observer* observer) override;
 
  private:
-  BrowserState* browser_state_;  // weak, owns this object.
+  web::BrowserState* browser_state_;  // weak, owns this object.
   // true if the ActiveStateManager is active.
   bool active_;
   // The list of observers.
@@ -38,6 +38,4 @@
   DISALLOW_COPY_AND_ASSIGN(ActiveStateManagerImpl);
 };
 
-}  // namespace web
-
-#endif  // IOS_WEB_ACTIVE_STATE_MANAGER_IMPL_H_
+#endif  // COMPONENTS_SIGNIN_IOS_BROWSER_ACTIVE_STATE_MANAGER_IMPL_H_
diff --git a/components/signin/ios/browser/active_state_manager_impl.mm b/components/signin/ios/browser/active_state_manager_impl.mm
new file mode 100644
index 0000000..26a7fc57
--- /dev/null
+++ b/components/signin/ios/browser/active_state_manager_impl.mm
@@ -0,0 +1,86 @@
+// Copyright 2015 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/signin/ios/browser/active_state_manager_impl.h"
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "ios/web/public/browser_state.h"
+#include "ios/web/public/web_thread.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+const char kActiveStateManagerKeyName[] = "active_state_manager";
+}  // namespace
+
+// static
+bool ActiveStateManager::ExistsForBrowserState(
+    web::BrowserState* browser_state) {
+  DCHECK_CURRENTLY_ON(web::WebThread::UI);
+  return browser_state->GetUserData(kActiveStateManagerKeyName) != nullptr;
+}
+
+// static
+ActiveStateManager* ActiveStateManager::FromBrowserState(
+    web::BrowserState* browser_state) {
+  DCHECK_CURRENTLY_ON(web::WebThread::UI);
+  DCHECK(browser_state);
+
+  ActiveStateManagerImpl* active_state_manager =
+      static_cast<ActiveStateManagerImpl*>(
+          browser_state->GetUserData(kActiveStateManagerKeyName));
+  if (!active_state_manager) {
+    active_state_manager = new ActiveStateManagerImpl(browser_state);
+    browser_state->SetUserData(kActiveStateManagerKeyName,
+                               base::WrapUnique(active_state_manager));
+  }
+  return active_state_manager;
+}
+
+ActiveStateManagerImpl::ActiveStateManagerImpl(web::BrowserState* browser_state)
+    : browser_state_(browser_state), active_(false) {
+  DCHECK_CURRENTLY_ON(web::WebThread::UI);
+  DCHECK(browser_state_);
+}
+
+ActiveStateManagerImpl::~ActiveStateManagerImpl() {
+  for (auto& observer : observer_list_)
+    observer.WillBeDestroyed();
+  DCHECK(!IsActive());
+}
+
+void ActiveStateManagerImpl::SetActive(bool active) {
+  DCHECK_CURRENTLY_ON(web::WebThread::UI);
+
+  if (active == active_) {
+    return;
+  }
+  active_ = active;
+
+  if (active) {
+    for (auto& observer : observer_list_)
+      observer.OnActive();
+  } else {
+    for (auto& observer : observer_list_)
+      observer.OnInactive();
+  }
+}
+
+bool ActiveStateManagerImpl::IsActive() {
+  DCHECK_CURRENTLY_ON(web::WebThread::UI);
+  return active_;
+}
+
+void ActiveStateManagerImpl::AddObserver(ActiveStateManager::Observer* obs) {
+  DCHECK_CURRENTLY_ON(web::WebThread::UI);
+  observer_list_.AddObserver(obs);
+}
+
+void ActiveStateManagerImpl::RemoveObserver(ActiveStateManager::Observer* obs) {
+  DCHECK_CURRENTLY_ON(web::WebThread::UI);
+  observer_list_.RemoveObserver(obs);
+}
diff --git a/ios/web/active_state_manager_impl_unittest.mm b/components/signin/ios/browser/active_state_manager_impl_unittest.mm
similarity index 84%
rename from ios/web/active_state_manager_impl_unittest.mm
rename to components/signin/ios/browser/active_state_manager_impl_unittest.mm
index fd6562e..5863e7b 100644
--- a/ios/web/active_state_manager_impl_unittest.mm
+++ b/components/signin/ios/browser/active_state_manager_impl_unittest.mm
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/web/active_state_manager_impl.h"
+#include "components/signin/ios/browser/active_state_manager_impl.h"
 
-#include "ios/web/public/active_state_manager.h"
+#include "components/signin/ios/browser/active_state_manager.h"
 #include "ios/web/public/browser_state.h"
 #include "ios/web/public/test/fakes/test_browser_state.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
@@ -17,11 +17,8 @@
 #error "This file requires ARC support."
 #endif
 
-namespace web {
-namespace {
-
 // A test fixture to test ActiveStateManagerImpl.
-typedef WebTest ActiveStateManagerImplTest;
+typedef web::WebTest ActiveStateManagerImplTest;
 
 // An ActiveStateManager::Observer used for testing purposes.
 class ActiveStateManagerObserver : public ActiveStateManager::Observer {
@@ -35,13 +32,11 @@
   MOCK_METHOD0(WillBeDestroyed, void());
 };
 
-}  // namespace
-
 // Tests that an ActiveStateManagerImpl is successfully created with a
 // BrowserState and that it can be made active/inactive.
 TEST_F(ActiveStateManagerImplTest, ActiveState) {
   ActiveStateManager* active_state_manager =
-      BrowserState::GetActiveStateManager(GetBrowserState());
+      ActiveStateManager::FromBrowserState(GetBrowserState());
   ASSERT_TRUE(active_state_manager);
 
   active_state_manager->SetActive(true);
@@ -59,12 +54,12 @@
 // Tests that ActiveStateManager::Observer are notified correctly.
 TEST_F(ActiveStateManagerImplTest, ObserverMethod) {
   // |GetBrowserState()| already has its ActiveStateManager be active.
-  BrowserState::GetActiveStateManager(GetBrowserState())->SetActive(false);
+  ActiveStateManager::FromBrowserState(GetBrowserState())->SetActive(false);
 
   ActiveStateManagerObserver observer;
-  TestBrowserState browser_state;
+  web::TestBrowserState browser_state;
   ActiveStateManager* active_state_manager =
-      BrowserState::GetActiveStateManager(&browser_state);
+      ActiveStateManager::FromBrowserState(&browser_state);
 
   active_state_manager->AddObserver(&observer);
 
@@ -78,5 +73,3 @@
   // |active_state_manager| goes away -- which happens when |browser_state| goes
   // away.
 }
-
-}  // namespace web
diff --git a/components/test/data/payments/payment_request_shipping_address_instance_test.html b/components/test/data/payments/payment_request_shipping_address_instance_test.html
new file mode 100644
index 0000000..977b742b
--- /dev/null
+++ b/components/test/data/payments/payment_request_shipping_address_instance_test.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!--
+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.
+-->
+<html>
+<head>
+<title>Shipping Address Instance Test</title>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
+<link rel="stylesheet" type="text/css" href="style.css">
+</head>
+<body>
+<button onclick="buy()" id="buy">Shipping Address Instance Test</button>
+<pre id="result"></pre>
+<script src="util.js"></script>
+<script src="shipping_address_instance.js"></script>
+</body>
+</html>
diff --git a/components/test/data/payments/shipping_address_instance.js b/components/test/data/payments/shipping_address_instance.js
new file mode 100644
index 0000000..903e02f
--- /dev/null
+++ b/components/test/data/payments/shipping_address_instance.js
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+/**
+ * Launches PaymentRequest to check whether PaymentRequest.shippingAddress is
+ * the same instance as PaymentResponse.shippingAddress.
+ */
+function buy() { // eslint-disable-line no-unused-vars
+    try {
+        var details = {
+            total: {
+                label: 'Total',
+                amount: {
+                    currency: 'USD',
+                    value: '5.00',
+                },
+            },
+            shippingOptions: [{
+                id: 'freeShippingOption',
+                label: 'Free global shipping',
+                amount: {
+                    currency: 'USD',
+                    value: '0',
+                },
+                selected: true,
+            }],
+        };
+        var request = new PaymentRequest(
+            [{
+                supportedMethods: 'basic-card',
+            }],
+            details, {
+                requestShipping: true,
+            });
+        request.show()
+            .then(function(resp) {
+                print('Same instance: ' +
+                    (request.shippingAddress === resp.shippingAddress)
+                    .toString());
+                resp.complete('success');
+            })
+            .catch(function(error) {
+                print('User did not authorized transaction: ' + error);
+            });
+    } catch (error) {
+        print('Developer mistake ' + error);
+    }
+}
diff --git a/components/ui_devtools/string_util.cc b/components/ui_devtools/string_util.cc
index ade4b6f3..fde02a7 100644
--- a/components/ui_devtools/string_util.cc
+++ b/components/ui_devtools/string_util.cc
@@ -5,6 +5,7 @@
 #include "components/ui_devtools/string_util.h"
 
 #include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "components/ui_devtools/Protocol.h"
 
 namespace ui_devtools {
@@ -18,5 +19,15 @@
                              string.length());
 };
 
+// static
+void StringUtil::builderAppendQuotedString(StringBuilder& builder,
+                                           const String& str) {
+  builder.append('"');
+  base::string16 str16 = base::UTF8ToUTF16(str);
+  escapeWideStringForJSON(reinterpret_cast<const uint16_t*>(&str16[0]),
+                          str16.length(), &builder);
+  builder.append('"');
+}
+
 }  // namespace protocol
 }  // namespace ui_devtools
diff --git a/components/ui_devtools/string_util.h b/components/ui_devtools/string_util.h
index 8f12102..6efd379 100644
--- a/components/ui_devtools/string_util.h
+++ b/components/ui_devtools/string_util.h
@@ -56,6 +56,8 @@
   static void builderAppend(StringBuilder& builder, const char* s, size_t len) {
     builder.append(s, len);
   }
+  static void builderAppendQuotedString(StringBuilder& builder,
+                                        const String& str);
   static void builderReserve(StringBuilder& builder, unsigned capacity) {
     builder.reserveCapacity(capacity);
   }
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index d3c4218..5464fa2 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -387,10 +387,6 @@
     "background_fetch/background_fetch_cross_origin_filter.h",
     "background_fetch/background_fetch_data_manager.cc",
     "background_fetch/background_fetch_data_manager.h",
-    "background_fetch/background_fetch_delegate.cc",
-    "background_fetch/background_fetch_delegate.h",
-    "background_fetch/background_fetch_delegate_impl.cc",
-    "background_fetch/background_fetch_delegate_impl.h",
     "background_fetch/background_fetch_delegate_proxy.cc",
     "background_fetch/background_fetch_delegate_proxy.h",
     "background_fetch/background_fetch_event_dispatcher.cc",
@@ -401,8 +397,6 @@
     "background_fetch/background_fetch_registration_id.h",
     "background_fetch/background_fetch_request_info.cc",
     "background_fetch/background_fetch_request_info.h",
-    "background_fetch/background_fetch_response.cc",
-    "background_fetch/background_fetch_response.h",
     "background_fetch/background_fetch_service_impl.cc",
     "background_fetch/background_fetch_service_impl.h",
     "background_sync/background_sync_context.cc",
diff --git a/content/browser/accessibility/browser_accessibility_com_win.cc b/content/browser/accessibility/browser_accessibility_com_win.cc
index 383197a..c546a71 100644
--- a/content/browser/accessibility/browser_accessibility_com_win.cc
+++ b/content/browser/accessibility/browser_accessibility_com_win.cc
@@ -238,25 +238,7 @@
 }
 
 STDMETHODIMP BrowserAccessibilityComWin::get_caretOffset(LONG* offset) {
-  WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_CARET_OFFSET);
-  AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes);
-  if (!owner())
-    return E_FAIL;
-
-  if (!offset)
-    return E_INVALIDARG;
-
-  if (!owner()->HasCaret())
-    return S_FALSE;
-
-  int selection_start, selection_end;
-  GetSelectionOffsets(&selection_start, &selection_end);
-  // The caret is always at the end of the selection.
-  *offset = selection_end;
-  if (*offset < 0)
-    return S_FALSE;
-
-  return S_OK;
+  return AXPlatformNodeWin::get_caretOffset(offset);
 }
 
 STDMETHODIMP BrowserAccessibilityComWin::get_characterExtents(
diff --git a/content/browser/background_fetch/background_fetch_context.cc b/content/browser/background_fetch/background_fetch_context.cc
index d98ffc30..e6f4daf 100644
--- a/content/browser/background_fetch/background_fetch_context.cc
+++ b/content/browser/background_fetch/background_fetch_context.cc
@@ -7,10 +7,10 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
-#include "content/browser/background_fetch/background_fetch_delegate_impl.h"
 #include "content/browser/background_fetch/background_fetch_job_controller.h"
 #include "content/browser/background_fetch/background_fetch_registration_id.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/public/browser/background_fetch_delegate.h"
 #include "content/public/browser/blob_handle.h"
 #include "content/public/browser/browser_context.h"
 #include "net/url_request/url_request_context_getter.h"
@@ -39,18 +39,10 @@
     : browser_context_(browser_context),
       data_manager_(browser_context, service_worker_context),
       event_dispatcher_(service_worker_context),
+      delegate_proxy_(browser_context_->GetBackgroundFetchDelegate()),
       weak_factory_(this) {
   // Although this lives only on the IO thread, it is constructed on UI thread.
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  // These are constructed out of the initializer because delegate's real type
-  // is required to get the WeakPtr.
-  std::unique_ptr<BackgroundFetchDelegateImpl, BrowserThread::DeleteOnUIThread>
-      delegate;
-  delegate.reset(new BackgroundFetchDelegateImpl(browser_context_));
-  delegate_proxy_ =
-      std::make_unique<BackgroundFetchDelegateProxy>(delegate->GetWeakPtr());
-  delegate_ = std::move(delegate);
 }
 
 BackgroundFetchContext::~BackgroundFetchContext() {
@@ -122,8 +114,8 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   std::unique_ptr<BackgroundFetchJobController> controller =
-      base::MakeUnique<BackgroundFetchJobController>(
-          delegate_proxy_.get(), registration_id, options, &data_manager_,
+      std::make_unique<BackgroundFetchJobController>(
+          &delegate_proxy_, registration_id, options, &data_manager_,
           base::BindOnce(&BackgroundFetchContext::DidCompleteJob,
                          weak_factory_.GetWeakPtr()));
 
diff --git a/content/browser/background_fetch/background_fetch_context.h b/content/browser/background_fetch/background_fetch_context.h
index a8041c13..5348d174 100644
--- a/content/browser/background_fetch/background_fetch_context.h
+++ b/content/browser/background_fetch/background_fetch_context.h
@@ -14,6 +14,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "content/browser/background_fetch/background_fetch_data_manager.h"
+#include "content/browser/background_fetch/background_fetch_delegate_proxy.h"
 #include "content/browser/background_fetch/background_fetch_event_dispatcher.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/browser_thread.h"
@@ -21,8 +22,6 @@
 
 namespace content {
 
-class BackgroundFetchDelegate;
-class BackgroundFetchDelegateProxy;
 class BackgroundFetchJobController;
 struct BackgroundFetchOptions;
 class BackgroundFetchRegistrationId;
@@ -103,10 +102,7 @@
 
   BackgroundFetchDataManager data_manager_;
   BackgroundFetchEventDispatcher event_dispatcher_;
-
-  std::unique_ptr<BackgroundFetchDelegate, BrowserThread::DeleteOnUIThread>
-      delegate_;
-  std::unique_ptr<BackgroundFetchDelegateProxy> delegate_proxy_;
+  BackgroundFetchDelegateProxy delegate_proxy_;
 
   // Map of the Background Fetch fetches that are currently in-progress. Must
   // be destroyed before |data_manager_|.
diff --git a/content/browser/background_fetch/background_fetch_delegate_impl.h b/content/browser/background_fetch/background_fetch_delegate_impl.h
deleted file mode 100644
index 030d027..0000000
--- a/content/browser/background_fetch/background_fetch_delegate_impl.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_DELEGATE_IMPL_H_
-#define CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_DELEGATE_IMPL_H_
-
-#include <memory>
-#include <string>
-
-#include "base/memory/weak_ptr.h"
-#include "content/browser/background_fetch/background_fetch_delegate.h"
-#include "content/public/browser/download_interrupt_reasons.h"
-
-namespace content {
-
-class BrowserContext;
-class DownloadItem;
-
-// Implementation of BackgroundFetchDelegate using the legacy DownloadManager.
-class CONTENT_EXPORT BackgroundFetchDelegateImpl
-    : public BackgroundFetchDelegate {
- public:
-  explicit BackgroundFetchDelegateImpl(BrowserContext* browser_context);
-
-  ~BackgroundFetchDelegateImpl() override;
-
-  // BackgroundFetchDelegate implementation:
-  void DownloadUrl(const std::string& guid,
-                   const std::string& method,
-                   const GURL& url,
-                   const net::NetworkTrafficAnnotationTag& traffic_annotation,
-                   const net::HttpRequestHeaders& headers) override;
-
-  base::WeakPtr<BackgroundFetchDelegateImpl> GetWeakPtr();
-
- private:
-  void DidStartRequest(DownloadItem* download_item,
-                       DownloadInterruptReason interrupt_reason);
-
-  BrowserContext* browser_context_;
-
-  base::WeakPtrFactory<BackgroundFetchDelegateImpl> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(BackgroundFetchDelegateImpl);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_DELEGATE_IMPL_H_
diff --git a/content/browser/background_fetch/background_fetch_delegate_proxy.cc b/content/browser/background_fetch/background_fetch_delegate_proxy.cc
index 8d453293..bdca8fd 100644
--- a/content/browser/background_fetch/background_fetch_delegate_proxy.cc
+++ b/content/browser/background_fetch/background_fetch_delegate_proxy.cc
@@ -8,9 +8,9 @@
 
 #include "base/guid.h"
 #include "base/memory/ptr_util.h"
-#include "content/browser/background_fetch/background_fetch_delegate.h"
 #include "content/browser/background_fetch/background_fetch_job_controller.h"
-#include "content/browser/background_fetch/background_fetch_response.h"
+#include "content/public/browser/background_fetch_delegate.h"
+#include "content/public/browser/background_fetch_response.h"
 #include "content/public/browser/download_item.h"
 #include "content/public/browser/download_manager.h"
 #include "content/public/browser/download_url_parameters.h"
@@ -23,11 +23,14 @@
     : public BackgroundFetchDelegate::Client {
  public:
   Core(const base::WeakPtr<BackgroundFetchDelegateProxy>& io_parent,
-       base::WeakPtr<BackgroundFetchDelegate> delegate)
+       BackgroundFetchDelegate* delegate)
       : io_parent_(io_parent), delegate_(delegate), weak_ptr_factory_(this) {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-    delegate_->SetDelegateClient(weak_ptr_factory_.GetWeakPtr());
+    // Some BrowserContext implementations return nullptr for their delegate
+    // implementation and the feature should be disabled in that case.
+    if (delegate_)
+      delegate_->SetDelegateClient(GetWeakPtrOnUI());
   }
 
   ~Core() override { DCHECK_CURRENTLY_ON(BrowserThread::UI); }
@@ -43,6 +46,12 @@
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     DCHECK(request);
 
+    // TODO(crbug/757760): This can be nullptr, is if the delegate has shut
+    // down, in which case we need to make sure this is retried when the browser
+    // restarts.
+    if (!delegate_)
+      return;
+
     const ServiceWorkerFetchRequest& fetch_request = request->fetch_request();
 
     const net::NetworkTrafficAnnotationTag traffic_annotation(
@@ -102,12 +111,15 @@
   void OnDownloadStarted(
       const std::string& guid,
       std::unique_ptr<content::BackgroundFetchResponse> response) override;
+  void OnDelegateShutdown() override;
 
  private:
   // Weak reference to the IO thread outer class that owns us.
   base::WeakPtr<BackgroundFetchDelegateProxy> io_parent_;
 
-  base::WeakPtr<BackgroundFetchDelegate> delegate_;
+  // Delegate is owned elsewhere and is valid from construction until
+  // OnDelegateShutDown (if not initially nullptr).
+  BackgroundFetchDelegate* delegate_;
 
   base::WeakPtrFactory<Core> weak_ptr_factory_;
 
@@ -141,8 +153,12 @@
                      guid, std::move(response)));
 }
 
+void BackgroundFetchDelegateProxy::Core::OnDelegateShutdown() {
+  delegate_ = nullptr;
+}
+
 BackgroundFetchDelegateProxy::BackgroundFetchDelegateProxy(
-    base::WeakPtr<BackgroundFetchDelegate> delegate)
+    BackgroundFetchDelegate* delegate)
     : weak_ptr_factory_(this) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
diff --git a/content/browser/background_fetch/background_fetch_delegate_proxy.h b/content/browser/background_fetch/background_fetch_delegate_proxy.h
index 311fb73..fdd4491f 100644
--- a/content/browser/background_fetch/background_fetch_delegate_proxy.h
+++ b/content/browser/background_fetch/background_fetch_delegate_proxy.h
@@ -42,8 +42,7 @@
     virtual ~Controller() {}
   };
 
-  explicit BackgroundFetchDelegateProxy(
-      base::WeakPtr<BackgroundFetchDelegate> delegate);
+  explicit BackgroundFetchDelegateProxy(BackgroundFetchDelegate* delegate);
 
   ~BackgroundFetchDelegateProxy();
 
diff --git a/content/browser/background_fetch/background_fetch_delegate_proxy_unittest.cc b/content/browser/background_fetch/background_fetch_delegate_proxy_unittest.cc
index a246a83..40dec76 100644
--- a/content/browser/background_fetch/background_fetch_delegate_proxy_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_delegate_proxy_unittest.cc
@@ -8,15 +8,18 @@
 
 #include "base/memory/weak_ptr.h"
 #include "base/run_loop.h"
-#include "content/browser/background_fetch/background_fetch_delegate.h"
 #include "content/browser/background_fetch/background_fetch_test_base.h"
+#include "content/public/browser/background_fetch_delegate.h"
+#include "content/public/browser/background_fetch_response.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
 
+namespace {
+
 class FakeBackgroundFetchDelegate : public BackgroundFetchDelegate {
  public:
-  FakeBackgroundFetchDelegate() : weak_ptr_factory_(this) {}
+  FakeBackgroundFetchDelegate() {}
 
   void DownloadUrl(const std::string& guid,
                    const std::string& method,
@@ -26,13 +29,13 @@
     if (!client())
       return;
 
-    auto response = base::MakeUnique<BackgroundFetchResponse>(
+    auto response = std::make_unique<BackgroundFetchResponse>(
         std::vector<GURL>({url}),
         base::MakeRefCounted<net::HttpResponseHeaders>("200 OK"));
 
     client()->OnDownloadStarted(guid, std::move(response));
     if (complete_downloads_) {
-      auto result = base::MakeUnique<BackgroundFetchResult>(
+      auto result = std::make_unique<BackgroundFetchResult>(
           base::Time::Now(), base::FilePath(), 10u);
       BrowserThread::PostTask(
           BrowserThread::IO, FROM_HERE,
@@ -45,14 +48,8 @@
     complete_downloads_ = complete_downloads;
   }
 
-  base::WeakPtr<BackgroundFetchDelegate> GetWeakPtr() {
-    return weak_ptr_factory_.GetWeakPtr();
-  }
-
  private:
   bool complete_downloads_ = true;
-
-  base::WeakPtrFactory<FakeBackgroundFetchDelegate> weak_ptr_factory_;
 };
 
 class FakeController : public BackgroundFetchDelegateProxy::Controller {
@@ -77,14 +74,15 @@
 
 class BackgroundFetchDelegateProxyTest : public BackgroundFetchTestBase {
  public:
-  BackgroundFetchDelegateProxyTest()
-      : delegate_proxy_(delegate_.GetWeakPtr()) {}
+  BackgroundFetchDelegateProxyTest() : delegate_proxy_(&delegate_) {}
 
  protected:
   FakeBackgroundFetchDelegate delegate_;
   BackgroundFetchDelegateProxy delegate_proxy_;
 };
 
+}  // namespace
+
 TEST_F(BackgroundFetchDelegateProxyTest, SetDelegate) {
   EXPECT_TRUE(delegate_.client().get());
 }
diff --git a/content/browser/background_fetch/background_fetch_job_controller.cc b/content/browser/background_fetch/background_fetch_job_controller.cc
index f78506a..d489f1a 100644
--- a/content/browser/background_fetch/background_fetch_job_controller.cc
+++ b/content/browser/background_fetch/background_fetch_job_controller.cc
@@ -75,6 +75,12 @@
 void BackgroundFetchJobController::DidCompleteRequest(
     const scoped_refptr<BackgroundFetchRequestInfo>& request) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(state_ == State::FETCHING || state_ == State::ABORTED);
+
+  // TODO(delphick): When ABORT is implemented correctly we should hopefully
+  // never get here and the DCHECK above should only allow FETCHING.
+  if (state_ == State::ABORTED)
+    return;
 
   // The DataManager must acknowledge that it stored the data and that there are
   // no more pending requests to avoid marking this job as completed too early.
diff --git a/content/browser/background_fetch/background_fetch_job_controller_unittest.cc b/content/browser/background_fetch/background_fetch_job_controller_unittest.cc
index 4b131097..4702c59 100644
--- a/content/browser/background_fetch/background_fetch_job_controller_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_job_controller_unittest.cc
@@ -16,13 +16,12 @@
 #include "base/run_loop.h"
 #include "content/browser/background_fetch/background_fetch_constants.h"
 #include "content/browser/background_fetch/background_fetch_data_manager.h"
-#include "content/browser/background_fetch/background_fetch_delegate_impl.h"
 #include "content/browser/background_fetch/background_fetch_registration_id.h"
 #include "content/browser/background_fetch/background_fetch_test_base.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/public/browser/background_fetch_delegate.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_item.h"
-#include "content/public/browser/storage_partition.h"
 #include "content/public/test/fake_download_item.h"
 #include "content/public/test/mock_download_manager.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -46,7 +45,7 @@
   // included |request_data|. Should be wrapped in ASSERT_NO_FATAL_FAILURE().
   void CreateRegistrationForRequests(
       BackgroundFetchRegistrationId* registration_id,
-      std::map<std::string /* url */, std::string /* method */> request_data) {
+      std::map<GURL, std::string /* method */> request_data) {
     DCHECK(registration_id);
 
     ASSERT_TRUE(CreateRegistrationId(kExampleTag, registration_id));
@@ -55,7 +54,7 @@
     requests.reserve(request_data.size());
 
     for (const auto& pair : request_data) {
-      requests.emplace_back(GURL(pair.first), pair.second /* method */,
+      requests.emplace_back(pair.first, pair.second /* method */,
                             ServiceWorkerHeaderMap(), Referrer(),
                             false /* is_reload */);
     }
@@ -82,12 +81,11 @@
   // Creates a new BackgroundFetchJobController instance.
   std::unique_ptr<BackgroundFetchJobController> CreateJobController(
       const BackgroundFetchRegistrationId& registration_id) {
-    delegate_ =
-        base::MakeUnique<BackgroundFetchDelegateImpl>(browser_context());
-    delegate_proxy_ =
-        base::MakeUnique<BackgroundFetchDelegateProxy>(delegate_->GetWeakPtr());
+    delegate_ = browser_context()->GetBackgroundFetchDelegate();
+    DCHECK(delegate_);
+    delegate_proxy_ = std::make_unique<BackgroundFetchDelegateProxy>(delegate_);
 
-    return base::MakeUnique<BackgroundFetchJobController>(
+    return std::make_unique<BackgroundFetchJobController>(
         delegate_proxy_.get(), registration_id, BackgroundFetchOptions(),
         &data_manager_,
         base::BindOnce(&BackgroundFetchJobControllerTest::DidCompleteJob,
@@ -103,7 +101,7 @@
   base::OnceClosure job_completed_closure_;
 
   std::unique_ptr<BackgroundFetchDelegateProxy> delegate_proxy_;
-  std::unique_ptr<BackgroundFetchDelegateImpl> delegate_;
+  BackgroundFetchDelegate* delegate_;
 
  private:
   void DidCreateRegistration(blink::mojom::BackgroundFetchError* out_error,
@@ -135,7 +133,7 @@
   BackgroundFetchRegistrationId registration_id;
 
   ASSERT_NO_FATAL_FAILURE(CreateRegistrationForRequests(
-      &registration_id, {{"https://example.com/funny_cat.png", "GET"}}));
+      &registration_id, {{GURL("https://example.com/funny_cat.png"), "GET"}}));
 
   std::unique_ptr<BackgroundFetchJobController> controller =
       CreateJobController(registration_id);
@@ -167,11 +165,11 @@
   ASSERT_GT(5u, kMaximumBackgroundFetchParallelRequests);
 
   ASSERT_NO_FATAL_FAILURE(CreateRegistrationForRequests(
-      &registration_id, {{"https://example.com/funny_cat.png", "GET"},
-                         {"https://example.com/scary_cat.png", "GET"},
-                         {"https://example.com/crazy_cat.png", "GET"},
-                         {"https://example.com/silly_cat.png", "GET"},
-                         {"https://example.com/happy_cat.png", "GET"}}));
+      &registration_id, {{GURL("https://example.com/funny_cat.png"), "GET"},
+                         {GURL("https://example.com/scary_cat.png"), "GET"},
+                         {GURL("https://example.com/crazy_cat.png"), "GET"},
+                         {GURL("https://example.com/silly_cat.png"), "GET"},
+                         {GURL("https://example.com/happy_cat.png"), "GET"}}));
 
   std::unique_ptr<BackgroundFetchJobController> controller =
       CreateJobController(registration_id);
@@ -201,7 +199,7 @@
   BackgroundFetchRegistrationId registration_id;
 
   ASSERT_NO_FATAL_FAILURE(CreateRegistrationForRequests(
-      &registration_id, {{"https://example.com/sad_cat.png", "GET"}}));
+      &registration_id, {{GURL("https://example.com/sad_cat.png"), "GET"}}));
 
   std::unique_ptr<BackgroundFetchJobController> controller =
       CreateJobController(registration_id);
diff --git a/content/browser/background_fetch/background_fetch_request_info.cc b/content/browser/background_fetch/background_fetch_request_info.cc
index 35da819..05d56e1 100644
--- a/content/browser/background_fetch/background_fetch_request_info.cc
+++ b/content/browser/background_fetch/background_fetch_request_info.cc
@@ -8,7 +8,7 @@
 
 #include "base/strings/string_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "content/browser/background_fetch/background_fetch_response.h"
+#include "content/public/browser/background_fetch_response.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_item.h"
 #include "net/http/http_response_headers.h"
diff --git a/content/browser/background_fetch/background_fetch_request_info.h b/content/browser/background_fetch/background_fetch_request_info.h
index ea1610d..01132c1 100644
--- a/content/browser/background_fetch/background_fetch_request_info.h
+++ b/content/browser/background_fetch/background_fetch_request_info.h
@@ -16,7 +16,6 @@
 #include "base/sequence_checker.h"
 #include "base/time/time.h"
 #include "content/browser/background_fetch/background_fetch_constants.h"
-#include "content/browser/background_fetch/background_fetch_response.h"
 #include "content/common/content_export.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/browser/download_item.h"
@@ -24,6 +23,9 @@
 
 namespace content {
 
+struct BackgroundFetchResponse;
+struct BackgroundFetchResult;
+
 // Simple class to encapsulate the components of a fetch request.
 // TODO(peter): This can likely change to have a single owner, and thus become
 // an std::unique_ptr<>, when persistent storage has been implemented.
diff --git a/content/browser/background_fetch/background_fetch_service_unittest.cc b/content/browser/background_fetch/background_fetch_service_unittest.cc
index 713d46b..4335b75f 100644
--- a/content/browser/background_fetch/background_fetch_service_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_service_unittest.cc
@@ -335,7 +335,7 @@
   constexpr int kThirdResponseCode = 200;
 
   requests.push_back(CreateRequestWithProvidedResponse(
-      "GET", "https://example.com/funny_cat.txt",
+      "GET", GURL("https://example.com/funny_cat.txt"),
       TestResponseBuilder(kFirstResponseCode)
           .SetResponseData(
               "This text describes a scenario involving a funny cat.")
@@ -344,7 +344,7 @@
           .Build()));
 
   requests.push_back(CreateRequestWithProvidedResponse(
-      "GET", "https://example.com/crazy_cat.txt",
+      "GET", GURL("https://example.com/crazy_cat.txt"),
       TestResponseBuilder(kSecondResponseCode)
           .SetResponseData(
               "This text describes another scenario that involves a crazy cat.")
@@ -352,7 +352,7 @@
           .Build()));
 
   requests.push_back(CreateRequestWithProvidedResponse(
-      "GET", "https://chrome.com/accessible_cross_origin_cat.txt",
+      "GET", GURL("https://chrome.com/accessible_cross_origin_cat.txt"),
       TestResponseBuilder(kThirdResponseCode)
           .SetResponseData("This cat originates from another origin.")
           .AddResponseHeader("Access-Control-Allow-Origin", "*")
@@ -444,11 +444,11 @@
   constexpr int kSecondResponseCode = 200;
 
   requests.push_back(CreateRequestWithProvidedResponse(
-      "GET", "https://example.com/not_existing_cat.txt",
+      "GET", GURL("https://example.com/not_existing_cat.txt"),
       TestResponseBuilder(kFirstResponseCode).Build()));
 
   requests.push_back(CreateRequestWithProvidedResponse(
-      "GET", "https://chrome.com/inaccessible_cross_origin_cat.txt",
+      "GET", GURL("https://chrome.com/inaccessible_cross_origin_cat.txt"),
       TestResponseBuilder(kSecondResponseCode)
           .SetResponseData(
               "This is a cross-origin response not accessible to the reader.")
@@ -631,7 +631,7 @@
 
   std::vector<ServiceWorkerFetchRequest> requests;
   requests.push_back(CreateRequestWithProvidedResponse(
-      "GET", "https://example.com/funny_cat.txt",
+      "GET", GURL("https://example.com/funny_cat.txt"),
       TestResponseBuilder(kResponseCode)
           .SetResponseData("Random data about a funny cat.")
           .Build()));
diff --git a/content/browser/background_fetch/background_fetch_test_base.cc b/content/browser/background_fetch/background_fetch_test_base.cc
index a3671456..af80792 100644
--- a/content/browser/background_fetch/background_fetch_test_base.cc
+++ b/content/browser/background_fetch/background_fetch_test_base.cc
@@ -25,11 +25,6 @@
 #include "content/common/service_worker/service_worker_status_code.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/download_item.h"
-#include "content/public/browser/download_url_parameters.h"
-#include "content/public/test/fake_download_item.h"
-#include "content/public/test/mock_download_manager.h"
-#include "third_party/WebKit/public/platform/modules/serviceworker/service_worker_registration.mojom.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -67,147 +62,11 @@
 
 }  // namespace
 
-// -----------------------------------------------------------------------------
-// TestResponse
-
-BackgroundFetchTestBase::TestResponse::TestResponse() = default;
-
-BackgroundFetchTestBase::TestResponse::~TestResponse() = default;
-
-// -----------------------------------------------------------------------------
-// TestResponseBuilder
-
-BackgroundFetchTestBase::TestResponseBuilder::TestResponseBuilder(
-    int response_code)
-    : response_(base::MakeUnique<BackgroundFetchTestBase::TestResponse>()) {
-  response_->headers = make_scoped_refptr(new net::HttpResponseHeaders(
-      "HTTP/1.1 " + std::to_string(response_code)));
-}
-
-BackgroundFetchTestBase::TestResponseBuilder::~TestResponseBuilder() = default;
-
-BackgroundFetchTestBase::TestResponseBuilder&
-BackgroundFetchTestBase::TestResponseBuilder::AddResponseHeader(
-    const std::string& name,
-    const std::string& value) {
-  DCHECK(response_);
-  response_->headers->AddHeader(name + ": " + value);
-  return *this;
-}
-
-BackgroundFetchTestBase::TestResponseBuilder&
-BackgroundFetchTestBase::TestResponseBuilder::SetResponseData(
-    std::string data) {
-  DCHECK(response_);
-  response_->data.swap(data);
-  return *this;
-}
-
-std::unique_ptr<BackgroundFetchTestBase::TestResponse>
-BackgroundFetchTestBase::TestResponseBuilder::Build() {
-  return std::move(response_);
-}
-
-// -----------------------------------------------------------------------------
-// RespondingDownloadManager
-
-// Faked download manager that will respond to known HTTP requests with a test-
-// defined response. See CreateRequestWithProvidedResponse().
-class BackgroundFetchTestBase::RespondingDownloadManager
-    : public MockDownloadManager {
- public:
-  RespondingDownloadManager() : weak_ptr_factory_(this) {}
-  ~RespondingDownloadManager() override = default;
-
-  // Responds to requests to |url| with the given |response|.
-  void RegisterResponse(const GURL& url,
-                        std::unique_ptr<TestResponse> response) {
-    DCHECK_EQ(registered_responses_.count(url), 0u);
-    registered_responses_[url] = std::move(response);
-  }
-
-  // Called when the Background Fetch system starts a download, all information
-  // for which is contained in the |params|.
-  void DownloadUrl(std::unique_ptr<DownloadUrlParameters> params) override {
-    auto iter = registered_responses_.find(params->url());
-    if (iter == registered_responses_.end())
-      return;
-
-    TestResponse* response = iter->second.get();
-
-    std::unique_ptr<FakeDownloadItem> download_item =
-        base::MakeUnique<FakeDownloadItem>();
-
-    download_item->SetURL(params->url());
-    download_item->SetUrlChain({params->url()});
-    download_item->SetState(DownloadItem::DownloadState::IN_PROGRESS);
-    download_item->SetGuid(params->guid().empty() ? base::GenerateGUID()
-                                                  : params->guid());
-    download_item->SetStartTime(base::Time::Now());
-    download_item->SetResponseHeaders(response->headers);
-
-    // Asynchronously invoke the callback set on the |params|, and then continue
-    // dealing with the response in this class.
-    BrowserThread::PostTaskAndReply(
-        BrowserThread::UI, FROM_HERE,
-        base::BindOnce(params->callback(), download_item.get(),
-                       DOWNLOAD_INTERRUPT_REASON_NONE),
-        base::BindOnce(&RespondingDownloadManager::DidStartDownload,
-                       weak_ptr_factory_.GetWeakPtr(), download_item.get()));
-
-    download_items_.push_back(std::move(download_item));
-  }
-
- private:
-  // Called when the download has been "started" by the download manager. This
-  // is where we finish the download by sending a single update.
-  void DidStartDownload(FakeDownloadItem* download_item) {
-    auto iter = registered_responses_.find(download_item->GetURL());
-    DCHECK(iter != registered_responses_.end());
-
-    TestResponse* response = iter->second.get();
-
-    download_item->SetState(DownloadItem::DownloadState::COMPLETE);
-    download_item->SetEndTime(base::Time::Now());
-
-    base::FilePath response_path;
-    if (!temp_directory_.IsValid())
-      ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
-
-    // Write the |response|'s data to a temporary file.
-    ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_directory_.GetPath(),
-                                               &response_path));
-
-    ASSERT_NE(-1 /* error */,
-              base::WriteFile(response_path, response->data.c_str(),
-                              response->data.size()));
-
-    download_item->SetTargetFilePath(response_path);
-    download_item->SetReceivedBytes(response->data.size());
-    download_item->SetMimeType("text/plain");
-
-    // Notify the Job Controller about the download having been updated.
-    download_item->NotifyDownloadUpdated();
-  }
-
-  // Map of URL to the response information associated with that URL.
-  std::map<GURL, std::unique_ptr<TestResponse>> registered_responses_;
-
-  // Only used to guarantee the lifetime of the created FakeDownloadItems.
-  std::vector<std::unique_ptr<FakeDownloadItem>> download_items_;
-
-  // Temporary directory in which successfully downloaded files will be stored.
-  base::ScopedTempDir temp_directory_;
-
-  base::WeakPtrFactory<RespondingDownloadManager> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(RespondingDownloadManager);
-};
-
 BackgroundFetchTestBase::BackgroundFetchTestBase()
     // Using REAL_IO_THREAD would give better coverage for thread safety, but
     // at time of writing EmbeddedWorkerTestHelper didn't seem to support that.
     : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
+      delegate_(browser_context_.GetBackgroundFetchDelegate()),
       origin_(GURL(kTestOrigin)) {}
 
 BackgroundFetchTestBase::~BackgroundFetchTestBase() {
@@ -216,19 +75,10 @@
 }
 
 void BackgroundFetchTestBase::SetUp() {
-  download_manager_ = new RespondingDownloadManager();
-
-  // The |download_manager_| ownership is given to the BrowserContext, and the
-  // BrowserContext will take care of deallocating it.
-  BrowserContext::SetDownloadManagerForTesting(
-      browser_context(), base::WrapUnique(download_manager_));
-
   set_up_called_ = true;
 }
 
 void BackgroundFetchTestBase::TearDown() {
-  EXPECT_CALL(*download_manager_, Shutdown()).Times(1);
-
   service_worker_registrations_.clear();
   tear_down_called_ = true;
 }
@@ -293,20 +143,16 @@
 ServiceWorkerFetchRequest
 BackgroundFetchTestBase::CreateRequestWithProvidedResponse(
     const std::string& method,
-    const std::string& url,
+    const GURL& url,
     std::unique_ptr<TestResponse> response) {
   GURL gurl(url);
 
-  // Register the |response| with the faked download manager.
-  download_manager_->RegisterResponse(gurl, std::move(response));
+  // Register the |response| with the faked delegate.
+  delegate_->RegisterResponse(url, std::move(response));
 
   // Create a ServiceWorkerFetchRequest request with the same information.
   return ServiceWorkerFetchRequest(gurl, method, ServiceWorkerHeaderMap(),
                                    Referrer(), false /* is_reload */);
 }
 
-MockDownloadManager* BackgroundFetchTestBase::download_manager() {
-  return download_manager_;
-}
-
 }  // namespace content
diff --git a/content/browser/background_fetch/background_fetch_test_base.h b/content/browser/background_fetch/background_fetch_test_base.h
index 2bc8f683..7ced56f 100644
--- a/content/browser/background_fetch/background_fetch_test_base.h
+++ b/content/browser/background_fetch/background_fetch_test_base.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_TEST_BASE_H_
 #define CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_TEST_BASE_H_
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -12,26 +13,25 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "content/browser/background_fetch/background_fetch_embedded_worker_test_helper.h"
+#include "content/browser/background_fetch/background_fetch_test_browser_context.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/origin.h"
 
-namespace net {
-class HttpResponseHeaders;
-}
-
 namespace content {
 
 class BackgroundFetchRegistrationId;
-class MockDownloadManager;
 class ServiceWorkerRegistration;
 
 // Base class containing common functionality needed in unit tests written for
 // the Background Fetch feature.
 class BackgroundFetchTestBase : public ::testing::Test {
  public:
+  using TestResponse = MockBackgroundFetchDelegate::TestResponse;
+  using TestResponseBuilder = MockBackgroundFetchDelegate::TestResponseBuilder;
+
   BackgroundFetchTestBase();
   ~BackgroundFetchTestBase() override;
 
@@ -39,36 +39,6 @@
   void SetUp() override;
   void TearDown() override;
 
-  // Structure encapsulating the data for a injected response. Should only be
-  // created by the builder, which also defines the ownership semantics.
-  struct TestResponse {
-    TestResponse();
-    ~TestResponse();
-
-    scoped_refptr<net::HttpResponseHeaders> headers;
-    std::string data;
-  };
-
-  // Builder for creating a TestResponse object with the given data. The faked
-  // download manager will respond to the corresponding request based on this.
-  class TestResponseBuilder {
-   public:
-    explicit TestResponseBuilder(int response_code);
-    ~TestResponseBuilder();
-
-    TestResponseBuilder& AddResponseHeader(const std::string& name,
-                                           const std::string& value);
-    TestResponseBuilder& SetResponseData(std::string data);
-
-    // Finalizes the builder and invalidates the underlying response.
-    std::unique_ptr<TestResponse> Build();
-
-   private:
-    std::unique_ptr<TestResponse> response_;
-
-    DISALLOW_COPY_AND_ASSIGN(TestResponseBuilder);
-  };
-
   // Creates a Background Fetch registration backed by a Service Worker
   // registration for the testing origin. The resulting registration will be
   // stored in |*registration_id|. Returns whether creation was successful,
@@ -79,14 +49,14 @@
       WARN_UNUSED_RESULT;
 
   // Creates a ServiceWorkerFetchRequest instance for the given details and
-  // provides a faked |response| with the faked download manager.
+  // provides a faked |response|.
   ServiceWorkerFetchRequest CreateRequestWithProvidedResponse(
       const std::string& method,
-      const std::string& url,
+      const GURL& url,
       std::unique_ptr<TestResponse> response);
 
   // Returns the embedded worker test helper instance, which can be used to
-  // influence the behaviour of the Service Worker events.
+  // influence the behavior of the Service Worker events.
   BackgroundFetchEmbeddedWorkerTestHelper* embedded_worker_test_helper() {
     return &embedded_worker_test_helper_;
   }
@@ -94,9 +64,6 @@
   // Returns the browser context that should be used for the tests.
   BrowserContext* browser_context() { return &browser_context_; }
 
-  // Returns the download manager used for the tests.
-  MockDownloadManager* download_manager();
-
   // Returns the origin that should be used for Background Fetch tests.
   const url::Origin& origin() const { return origin_; }
 
@@ -104,11 +71,9 @@
   TestBrowserThreadBundle thread_bundle_;  // Must be first member.
 
  private:
-  class RespondingDownloadManager;
+  BackgroundFetchTestBrowserContext browser_context_;
 
-  TestBrowserContext browser_context_;
-
-  RespondingDownloadManager* download_manager_;  // owned by |browser_context_|
+  MockBackgroundFetchDelegate* delegate_;
 
   BackgroundFetchEmbeddedWorkerTestHelper embedded_worker_test_helper_;
 
diff --git a/content/browser/background_fetch/background_fetch_test_browser_context.cc b/content/browser/background_fetch/background_fetch_test_browser_context.cc
new file mode 100644
index 0000000..9296d2e
--- /dev/null
+++ b/content/browser/background_fetch/background_fetch_test_browser_context.cc
@@ -0,0 +1,23 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/background_fetch/background_fetch_test_browser_context.h"
+
+#include <utility>
+
+namespace content {
+
+BackgroundFetchTestBrowserContext::BackgroundFetchTestBrowserContext() {}
+
+BackgroundFetchTestBrowserContext::~BackgroundFetchTestBrowserContext() {}
+
+MockBackgroundFetchDelegate*
+BackgroundFetchTestBrowserContext::GetBackgroundFetchDelegate() {
+  if (!delegate_)
+    delegate_ = std::make_unique<MockBackgroundFetchDelegate>();
+
+  return delegate_.get();
+}
+
+}  // namespace content
diff --git a/content/browser/background_fetch/background_fetch_test_browser_context.h b/content/browser/background_fetch/background_fetch_test_browser_context.h
new file mode 100644
index 0000000..7766739
--- /dev/null
+++ b/content/browser/background_fetch/background_fetch_test_browser_context.h
@@ -0,0 +1,28 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_TEST_BROWSER_CONTEXT_H_
+#define CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_TEST_BROWSER_CONTEXT_H_
+
+#include <memory>
+
+#include "content/browser/background_fetch/mock_background_fetch_delegate.h"
+#include "content/public/test/test_browser_context.h"
+
+namespace content {
+
+class BackgroundFetchTestBrowserContext : public TestBrowserContext {
+ public:
+  BackgroundFetchTestBrowserContext();
+  ~BackgroundFetchTestBrowserContext() override;
+
+  MockBackgroundFetchDelegate* GetBackgroundFetchDelegate() override;
+
+ private:
+  std::unique_ptr<MockBackgroundFetchDelegate> delegate_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_TEST_BROWSER_CONTEXT_H_
diff --git a/content/browser/background_fetch/mock_background_fetch_delegate.cc b/content/browser/background_fetch/mock_background_fetch_delegate.cc
new file mode 100644
index 0000000..29f3a85
--- /dev/null
+++ b/content/browser/background_fetch/mock_background_fetch_delegate.cc
@@ -0,0 +1,132 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <utility>
+#include <vector>
+
+#include "base/files/file_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "content/browser/background_fetch/mock_background_fetch_delegate.h"
+#include "content/public/browser/background_fetch_response.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/http/http_response_headers.h"
+
+namespace content {
+
+MockBackgroundFetchDelegate::TestResponse::TestResponse() = default;
+
+MockBackgroundFetchDelegate::TestResponse::~TestResponse() = default;
+
+MockBackgroundFetchDelegate::TestResponseBuilder::TestResponseBuilder(
+    int response_code)
+    : response_(base::MakeUnique<TestResponse>()) {
+  response_->succeeded_ = (response_code >= 200 && response_code < 300);
+  response_->headers = make_scoped_refptr(new net::HttpResponseHeaders(
+      "HTTP/1.1 " + std::to_string(response_code)));
+}
+
+MockBackgroundFetchDelegate::TestResponseBuilder::~TestResponseBuilder() =
+    default;
+
+MockBackgroundFetchDelegate::TestResponseBuilder&
+MockBackgroundFetchDelegate::TestResponseBuilder::AddResponseHeader(
+    const std::string& name,
+    const std::string& value) {
+  DCHECK(response_);
+  response_->headers->AddHeader(name + ": " + value);
+  return *this;
+}
+
+MockBackgroundFetchDelegate::TestResponseBuilder&
+MockBackgroundFetchDelegate::TestResponseBuilder::SetResponseData(
+    std::string data) {
+  DCHECK(response_);
+  response_->data.swap(data);
+  return *this;
+}
+
+std::unique_ptr<MockBackgroundFetchDelegate::TestResponse>
+MockBackgroundFetchDelegate::TestResponseBuilder::Build() {
+  return std::move(response_);
+}
+
+MockBackgroundFetchDelegate::MockBackgroundFetchDelegate() {}
+
+MockBackgroundFetchDelegate::~MockBackgroundFetchDelegate() {}
+
+void MockBackgroundFetchDelegate::DownloadUrl(
+    const std::string& guid,
+    const std::string& method,
+    const GURL& url,
+    const net::NetworkTrafficAnnotationTag& traffic_annotation,
+    const net::HttpRequestHeaders& headers) {
+  // TODO(delphick): Currently we just disallow re-using GUIDs but later when we
+  // use the DownloadService, we should signal StartResult::UNEXPECTED_GUID.
+  DCHECK(seen_guids_.find(guid) == seen_guids_.end());
+
+  std::unique_ptr<TestResponse> test_response;
+  scoped_refptr<const net::HttpResponseHeaders> response_headers;
+
+  auto url_iter = url_responses_.find(url);
+  if (url_iter != url_responses_.end()) {
+    test_response = std::move(url_iter->second);
+    url_responses_.erase(url_iter);
+  } else {
+    // TODO(delphick): When we use the DownloadService, we should signal
+    // StartResult::INTERNAL_ERROR to say the URL wasn't registered rather than
+    // assuming 404.
+    test_response = TestResponseBuilder(404).Build();
+  }
+
+  response_headers = test_response->headers;
+
+  std::unique_ptr<BackgroundFetchResponse> response =
+      std::make_unique<BackgroundFetchResponse>(std::vector<GURL>({url}),
+                                                response_headers);
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&BackgroundFetchDelegate::Client::OnDownloadStarted,
+                     client(), guid, std::move(response)));
+
+  if (test_response->succeeded_) {
+    base::FilePath response_path;
+    if (!temp_directory_.IsValid()) {
+      CHECK(temp_directory_.CreateUniqueTempDir());
+    }
+
+    // Write the |response|'s data to a temporary file.
+    CHECK(base::CreateTemporaryFileInDir(temp_directory_.GetPath(),
+                                         &response_path));
+
+    CHECK_NE(-1 /* error */,
+             base::WriteFile(response_path, test_response->data.c_str(),
+                             test_response->data.size()));
+
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            &BackgroundFetchDelegate::Client::OnDownloadComplete, client(),
+            guid,
+            std::make_unique<BackgroundFetchResult>(
+                base::Time::Now(), response_path, test_response->data.size())));
+  } else {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            &BackgroundFetchDelegate::Client::OnDownloadComplete, client(),
+            guid, std::make_unique<BackgroundFetchResult>(base::Time::Now())));
+  }
+
+  seen_guids_.insert(guid);
+}
+
+void MockBackgroundFetchDelegate::RegisterResponse(
+    const GURL& url,
+    std::unique_ptr<TestResponse> response) {
+  DCHECK_EQ(0u, url_responses_.count(url));
+  url_responses_[url] = std::move(response);
+}
+
+}  // namespace content
diff --git a/content/browser/background_fetch/mock_background_fetch_delegate.h b/content/browser/background_fetch/mock_background_fetch_delegate.h
new file mode 100644
index 0000000..d05bce10
--- /dev/null
+++ b/content/browser/background_fetch/mock_background_fetch_delegate.h
@@ -0,0 +1,89 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_BACKGROUND_FETCH_MOCK_BACKGROUND_FETCH_DELEGATE_H_
+#define CONTENT_BROWSER_BACKGROUND_FETCH_MOCK_BACKGROUND_FETCH_DELEGATE_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/files/scoped_temp_dir.h"
+#include "content/public/browser/background_fetch_delegate.h"
+#include "url/gurl.h"
+
+namespace net {
+class HttpResponseHeaders;
+}
+
+namespace content {
+
+class MockBackgroundFetchDelegate : public BackgroundFetchDelegate {
+ public:
+  // Structure encapsulating the data for a injected response. Should only be
+  // created by the builder, which also defines the ownership semantics.
+  struct TestResponse {
+    TestResponse();
+    ~TestResponse();
+
+    bool succeeded_;
+    scoped_refptr<net::HttpResponseHeaders> headers;
+    std::string data;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(TestResponse);
+  };
+
+  // Builder for creating a TestResponse object with the given data.
+  // MockBackgroundFetchDelegate will respond to the corresponding request based
+  // on this.
+  class TestResponseBuilder {
+   public:
+    explicit TestResponseBuilder(int response_code);
+    ~TestResponseBuilder();
+
+    TestResponseBuilder& AddResponseHeader(const std::string& name,
+                                           const std::string& value);
+
+    TestResponseBuilder& SetResponseData(std::string data);
+
+    // Finalizes the builder and invalidates the underlying response.
+    std::unique_ptr<TestResponse> Build();
+
+   private:
+    std::unique_ptr<TestResponse> response_;
+
+    DISALLOW_COPY_AND_ASSIGN(TestResponseBuilder);
+  };
+
+  MockBackgroundFetchDelegate();
+  ~MockBackgroundFetchDelegate() override;
+
+  // BackgroundFetchDelegate implementation:
+  void DownloadUrl(const std::string& guid,
+                   const std::string& method,
+                   const GURL& url,
+                   const net::NetworkTrafficAnnotationTag& traffic_annotation,
+                   const net::HttpRequestHeaders& headers) override;
+
+  void RegisterResponse(const GURL& url,
+                        std::unique_ptr<TestResponse> response);
+
+ private:
+  // Single-use responses registered for specific URLs.
+  std::map<const GURL, std::unique_ptr<TestResponse>> url_responses_;
+
+  // GUIDs that have been registered via DownloadUrl and thus cannot be reused.
+  std::set<std::string> seen_guids_;
+
+  // Temporary directory in which successfully downloaded files will be stored.
+  base::ScopedTempDir temp_directory_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockBackgroundFetchDelegate);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_BACKGROUND_FETCH_MOCK_BACKGROUND_FETCH_DELEGATE_H_
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 03712d0..82b763f5 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -328,25 +328,12 @@
 MSVC_DISABLE_OPTIMIZE()
 MSVC_PUSH_DISABLE_WARNING(4748)
 
-NOINLINE void ResetThread_DB() {
-  volatile int inhibit_comdat = __LINE__;
-  ALLOW_UNUSED_LOCAL(inhibit_comdat);
-  BrowserThreadImpl::StopRedirectionOfThreadID(BrowserThread::DB);
-}
-
 NOINLINE void ResetThread_FILE() {
   volatile int inhibit_comdat = __LINE__;
   ALLOW_UNUSED_LOCAL(inhibit_comdat);
   BrowserThreadImpl::StopRedirectionOfThreadID(BrowserThread::FILE);
 }
 
-NOINLINE void ResetThread_FILE_USER_BLOCKING() {
-  volatile int inhibit_comdat = __LINE__;
-  ALLOW_UNUSED_LOCAL(inhibit_comdat);
-  BrowserThreadImpl::StopRedirectionOfThreadID(
-      BrowserThread::FILE_USER_BLOCKING);
-}
-
 #if defined(OS_ANDROID)
 NOINLINE void ResetThread_PROCESS_LAUNCHER(
     std::unique_ptr<BrowserProcessSubThread> thread) {
@@ -362,21 +349,6 @@
 }
 #endif  // defined(OS_ANDROID)
 
-#if defined(OS_WIN)
-NOINLINE void ResetThread_CACHE(
-    std::unique_ptr<BrowserProcessSubThread> thread) {
-  volatile int inhibit_comdat = __LINE__;
-  ALLOW_UNUSED_LOCAL(inhibit_comdat);
-    thread.reset();
-}
-#else   // defined(OS_WIN)
-NOINLINE void ResetThread_CACHE() {
-  volatile int inhibit_comdat = __LINE__;
-  ALLOW_UNUSED_LOCAL(inhibit_comdat);
-  BrowserThreadImpl::StopRedirectionOfThreadID(BrowserThread::CACHE);
-}
-#endif  // defined(OS_WIN)
-
 NOINLINE void ResetThread_IO(std::unique_ptr<BrowserProcessSubThread> thread) {
   volatile int inhibit_comdat = __LINE__;
   ALLOW_UNUSED_LOCAL(inhibit_comdat);
@@ -1051,18 +1023,6 @@
         base::TaskShutdownBehavior::BLOCK_SHUTDOWN};
 
     switch (thread_id) {
-      case BrowserThread::DB:
-        TRACE_EVENT_BEGIN1("startup",
-            "BrowserMainLoop::CreateThreads:start",
-            "Thread", "BrowserThread::DB");
-        non_ui_non_io_task_runner_traits = kUserVisibleTraits;
-        break;
-      case BrowserThread::FILE_USER_BLOCKING:
-        TRACE_EVENT_BEGIN1("startup",
-            "BrowserMainLoop::CreateThreads:start",
-            "Thread", "BrowserThread::FILE_USER_BLOCKING");
-        non_ui_non_io_task_runner_traits = kUserBlockingTraits;
-        break;
       case BrowserThread::FILE:
         TRACE_EVENT_BEGIN1("startup",
             "BrowserMainLoop::CreateThreads:start",
@@ -1084,23 +1044,6 @@
         non_ui_non_io_task_runner_traits = kUserBlockingTraits;
 #endif  // defined(OS_ANDROID)
         break;
-      case BrowserThread::CACHE:
-        TRACE_EVENT_BEGIN1("startup",
-            "BrowserMainLoop::CreateThreads:start",
-            "Thread", "BrowserThread::CACHE");
-#if defined(OS_WIN)
-        // TaskScheduler doesn't support async I/O on Windows as CACHE thread is
-        // the only user and this use case is going away in
-        // https://codereview.chromium.org/2216583003/.
-        // TODO(gavinp): Remove this ifdef (and thus enable redirection of the
-        // CACHE thread on Windows) once that CL lands.
-        thread_to_start = &cache_thread_;
-        options = io_message_loop_options;
-        options.timer_slack = base::TIMER_SLACK_MAXIMUM;
-#else  // OS_WIN
-        non_ui_non_io_task_runner_traits = kUserBlockingTraits;
-#endif  // OS_WIN
-        break;
       case BrowserThread::IO:
         TRACE_EVENT_BEGIN1("startup",
             "BrowserMainLoop::CreateThreads:start",
@@ -1301,11 +1244,6 @@
       //
       // - (Not sure why DB stops last.)
       switch (thread_id) {
-        case BrowserThread::DB: {
-          TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:DBThread");
-          ResetThread_DB();
-          break;
-        }
         case BrowserThread::FILE: {
           TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:FileThread");
           // Clean up state that lives on or uses the FILE thread before it goes
@@ -1315,12 +1253,6 @@
           ResetThread_FILE();
           break;
         }
-        case BrowserThread::FILE_USER_BLOCKING: {
-          TRACE_EVENT0("shutdown",
-                       "BrowserMainLoop::Subsystem:FileUserBlockingThread");
-          ResetThread_FILE_USER_BLOCKING();
-          break;
-        }
         case BrowserThread::PROCESS_LAUNCHER: {
           TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:LauncherThread");
 #if defined(OS_ANDROID)
@@ -1330,15 +1262,6 @@
 #endif  // defined(OS_ANDROID)
           break;
         }
-        case BrowserThread::CACHE: {
-          TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:CacheThread");
-#if defined(OS_WIN)
-          ResetThread_CACHE(std::move(cache_thread_));
-#else   // defined(OS_WIN)
-          ResetThread_CACHE();
-#endif  // defined(OS_WIN)
-          break;
-        }
         case BrowserThread::IO: {
           TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:IOThread");
           ResetThread_IO(std::move(io_thread_));
diff --git a/content/browser/browser_thread_impl.cc b/content/browser/browser_thread_impl.cc
index 276ad382..5c5ada96 100644
--- a/content/browser/browser_thread_impl.cc
+++ b/content/browser/browser_thread_impl.cc
@@ -33,11 +33,8 @@
 // Friendly names for the well-known threads.
 static const char* const g_browser_thread_names[BrowserThread::ID_COUNT] = {
   "",  // UI (name assembled in browser_main.cc).
-  "Chrome_DBThread",  // DB
   "Chrome_FileThread",  // FILE
-  "Chrome_FileUserBlockingThread",  // FILE_USER_BLOCKING
   "Chrome_ProcessLauncherThread",  // PROCESS_LAUNCHER
-  "Chrome_CacheThread",  // CACHE
   "Chrome_IOThread",  // IO
 };
 
@@ -182,11 +179,8 @@
   }
 #endif  // DCHECK_IS_ON()
 
-  if (identifier_ == BrowserThread::DB ||
-      identifier_ == BrowserThread::FILE ||
-      identifier_ == BrowserThread::FILE_USER_BLOCKING ||
-      identifier_ == BrowserThread::PROCESS_LAUNCHER ||
-      identifier_ == BrowserThread::CACHE) {
+  if (identifier_ == BrowserThread::FILE ||
+      identifier_ == BrowserThread::PROCESS_LAUNCHER) {
     // Nesting and task observers are not allowed on redirected threads.
     base::RunLoop::DisallowNestingOnCurrentThread();
     message_loop()->DisallowTaskObservers();
@@ -211,25 +205,12 @@
   CHECK_GT(line_number, 0);
 }
 
-NOINLINE void BrowserThreadImpl::DBThreadRun(base::RunLoop* run_loop) {
-  volatile int line_number = __LINE__;
-  Thread::Run(run_loop);
-  CHECK_GT(line_number, 0);
-}
-
 NOINLINE void BrowserThreadImpl::FileThreadRun(base::RunLoop* run_loop) {
   volatile int line_number = __LINE__;
   Thread::Run(run_loop);
   CHECK_GT(line_number, 0);
 }
 
-NOINLINE void BrowserThreadImpl::FileUserBlockingThreadRun(
-    base::RunLoop* run_loop) {
-  volatile int line_number = __LINE__;
-  Thread::Run(run_loop);
-  CHECK_GT(line_number, 0);
-}
-
 NOINLINE void BrowserThreadImpl::ProcessLauncherThreadRun(
     base::RunLoop* run_loop) {
   volatile int line_number = __LINE__;
@@ -237,12 +218,6 @@
   CHECK_GT(line_number, 0);
 }
 
-NOINLINE void BrowserThreadImpl::CacheThreadRun(base::RunLoop* run_loop) {
-  volatile int line_number = __LINE__;
-  Thread::Run(run_loop);
-  CHECK_GT(line_number, 0);
-}
-
 NOINLINE void BrowserThreadImpl::IOThreadRun(base::RunLoop* run_loop) {
   volatile int line_number = __LINE__;
   Thread::Run(run_loop);
@@ -269,16 +244,10 @@
   switch (identifier_) {
     case BrowserThread::UI:
       return UIThreadRun(run_loop);
-    case BrowserThread::DB:
-      return DBThreadRun(run_loop);
     case BrowserThread::FILE:
       return FileThreadRun(run_loop);
-    case BrowserThread::FILE_USER_BLOCKING:
-      return FileUserBlockingThreadRun(run_loop);
     case BrowserThread::PROCESS_LAUNCHER:
       return ProcessLauncherThreadRun(run_loop);
-    case BrowserThread::CACHE:
-      return CacheThreadRun(run_loop);
     case BrowserThread::IO:
       return IOThreadRun(run_loop);
     case BrowserThread::ID_COUNT:
diff --git a/content/browser/browser_thread_impl.h b/content/browser/browser_thread_impl.h
index a9eca4c..91db6018d 100644
--- a/content/browser/browser_thread_impl.h
+++ b/content/browser/browser_thread_impl.h
@@ -76,11 +76,8 @@
   // The following are unique function names that makes it possible to tell
   // the thread id from the callstack alone in crash dumps.
   void UIThreadRun(base::RunLoop* run_loop);
-  void DBThreadRun(base::RunLoop* run_loop);
   void FileThreadRun(base::RunLoop* run_loop);
-  void FileUserBlockingThreadRun(base::RunLoop* run_loop);
   void ProcessLauncherThreadRun(base::RunLoop* run_loop);
-  void CacheThreadRun(base::RunLoop* run_loop);
   void IOThreadRun(base::RunLoop* run_loop);
 
   static bool PostTaskHelper(BrowserThread::ID identifier,
diff --git a/content/browser/devtools/protocol_string.cc b/content/browser/devtools/protocol_string.cc
index 3d24afdd..891e59c 100644
--- a/content/browser/devtools/protocol_string.cc
+++ b/content/browser/devtools/protocol_string.cc
@@ -6,6 +6,8 @@
 
 #include "base/json/json_reader.h"
 #include "base/memory/ptr_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "content/browser/devtools/protocol/protocol.h"
 
@@ -144,6 +146,16 @@
   string_.append(characters, length);
 }
 
+// static
+void StringUtil::builderAppendQuotedString(StringBuilder& builder,
+                                           const String& str) {
+  builder.append('"');
+  base::string16 str16 = base::UTF8ToUTF16(str);
+  escapeWideStringForJSON(reinterpret_cast<const uint16_t*>(&str16[0]),
+                          str16.length(), &builder);
+  builder.append('"');
+}
+
 std::string StringBuilder::toString() {
   return string_;
 }
diff --git a/content/browser/devtools/protocol_string.h b/content/browser/devtools/protocol_string.h
index 38dd03e4..71f4e2a4 100644
--- a/content/browser/devtools/protocol_string.h
+++ b/content/browser/devtools/protocol_string.h
@@ -28,10 +28,10 @@
  public:
   StringBuilder();
   ~StringBuilder();
-  void append(const std::string&);
+  void append(const String&);
   void append(char);
   void append(const char*, size_t);
-  std::string toString();
+  String toString();
   void reserveCapacity(size_t);
 
  private:
@@ -73,12 +73,15 @@
   static void builderAppend(StringBuilder& builder, const char* s, size_t len) {
     builder.append(s, len);
   }
+  static void builderAppendQuotedString(StringBuilder& builder,
+                                        const String& str);
   static void builderReserve(StringBuilder& builder, unsigned capacity) {
     builder.reserveCapacity(capacity);
   }
   static String builderToString(StringBuilder& builder) {
     return builder.toString();
   }
+
   static std::unique_ptr<protocol::Value> parseJSON(const String&);
 };
 
diff --git a/content/browser/download/download_manager_impl_unittest.cc b/content/browser/download/download_manager_impl_unittest.cc
index 7711f1e..9e4379af 100644
--- a/content/browser/download/download_manager_impl_unittest.cc
+++ b/content/browser/download/download_manager_impl_unittest.cc
@@ -324,6 +324,7 @@
   MOCK_METHOD0(GetPushMessagingService, PushMessagingService*());
   MOCK_METHOD0(GetSSLHostStateDelegate, SSLHostStateDelegate*());
   MOCK_METHOD0(GetPermissionManager, PermissionManager*());
+  MOCK_METHOD0(GetBackgroundFetchDelegate, BackgroundFetchDelegate*());
   MOCK_METHOD0(GetBackgroundSyncController, BackgroundSyncController*());
   MOCK_METHOD0(GetBrowsingDataRemoverDelegate, BrowsingDataRemoverDelegate*());
   MOCK_METHOD0(CreateMediaRequestContext,
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 5c392bd..3c19719 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -3041,8 +3041,8 @@
                                      GetProcess()->GetID(), GetRoutingID()));
 #endif  // BUILDFLAG(ENABLE_MEDIA_REMOTING)
 
-  registry_->AddInterface(
-      base::Bind(&KeyboardLockServiceImpl::CreateMojoService));
+  registry_->AddInterface(base::Bind(
+      &KeyboardLockServiceImpl::CreateMojoService, base::Unretained(this)));
 
   registry_->AddInterface(base::Bind(&ImageCaptureImpl::Create));
 
diff --git a/content/browser/gpu.sb b/content/browser/gpu.sb
index bf3c553..f9297fe 100644
--- a/content/browser/gpu.sb
+++ b/content/browser/gpu.sb
@@ -26,3 +26,7 @@
 ; https://crbug.com/515280
 (if (param-true? elcap-or-later)
   (allow file-read* (subpath "/System/Library/Extensions")))
+
+; Needed for VideoToolbox usage - https://crbug.com/767037
+(if (param-true? macos-1013)
+  (allow mach-lookup (global-name "com.apple.coremedia.videodecoder")))
diff --git a/content/browser/keyboard_lock/keyboard_lock_service_impl.cc b/content/browser/keyboard_lock/keyboard_lock_service_impl.cc
index 585c90a..f4e2127 100644
--- a/content/browser/keyboard_lock/keyboard_lock_service_impl.cc
+++ b/content/browser/keyboard_lock/keyboard_lock_service_impl.cc
@@ -9,18 +9,24 @@
 
 #include "base/memory/ptr_util.h"
 #include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
 
 namespace content {
 
-KeyboardLockServiceImpl::KeyboardLockServiceImpl() = default;
+KeyboardLockServiceImpl::KeyboardLockServiceImpl(
+    RenderFrameHost* render_frame_host)
+    : web_contents_(WebContents::FromRenderFrameHost(render_frame_host)) {
+  DCHECK(web_contents_);
+}
 
 KeyboardLockServiceImpl::~KeyboardLockServiceImpl() = default;
 
 // static
 void KeyboardLockServiceImpl::CreateMojoService(
+    RenderFrameHost* render_frame_host,
     blink::mojom::KeyboardLockServiceRequest request) {
   mojo::MakeStrongBinding(
-        base::MakeUnique<KeyboardLockServiceImpl>(),
+        base::MakeUnique<KeyboardLockServiceImpl>(render_frame_host),
         std::move(request));
 }
 
diff --git a/content/browser/keyboard_lock/keyboard_lock_service_impl.h b/content/browser/keyboard_lock/keyboard_lock_service_impl.h
index d5576bb..67c538a 100644
--- a/content/browser/keyboard_lock/keyboard_lock_service_impl.h
+++ b/content/browser/keyboard_lock/keyboard_lock_service_impl.h
@@ -11,19 +11,26 @@
 
 namespace content {
 
+class RenderFrameHost;
+class WebContents;
+
 class CONTENT_EXPORT KeyboardLockServiceImpl
     : public blink::mojom::KeyboardLockService {
  public:
-  KeyboardLockServiceImpl();
+  explicit KeyboardLockServiceImpl(RenderFrameHost* render_frame_host);
   ~KeyboardLockServiceImpl() override;
 
   static void CreateMojoService(
+      RenderFrameHost* render_frame_host,
       blink::mojom::KeyboardLockServiceRequest request);
 
   // blink::mojom::KeyboardLockService implementations.
   void RequestKeyboardLock(const std::vector<std::string>& key_codes,
                            RequestKeyboardLockCallback callback) override;
   void CancelKeyboardLock() override;
+
+ private:
+  WebContents* const web_contents_;
 };
 
 }  // namespace
diff --git a/content/browser/renderer_host/clipboard_message_filter.cc b/content/browser/renderer_host/clipboard_message_filter.cc
index 820d44c..cba632a 100644
--- a/content/browser/renderer_host/clipboard_message_filter.cc
+++ b/content/browser/renderer_host/clipboard_message_filter.cc
@@ -292,10 +292,10 @@
 
   // Make sure the size is representable as a signed 32-bit int, so
   // SkBitmap::getSize() won't be truncated.
-  if (!sk_64_isS32(bitmap.computeSize64()))
+  if (!bitmap.getSafeSize())
     return;
 
-  if (!bitmap_buffer->Map(bitmap.getSize()))
+  if (!bitmap_buffer->Map(bitmap.getSafeSize()))
     return;
 
   if (!bitmap.installPixels(bitmap.info(), bitmap_buffer->memory(),
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 7bcbc0b..617d202 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -2539,7 +2539,6 @@
     switches::kDisableWebGLImageChromium,
     switches::kDomAutomationController,
     switches::kEnableBrowserSideNavigation,
-    switches::kEnableCompositorImageAnimations,
     switches::kEnableDisplayList2dCanvas,
     switches::kEnableDistanceFieldText,
     switches::kEnableExperimentalCanvasFeatures,
@@ -2651,6 +2650,9 @@
 #if BUILDFLAG(ENABLE_PLUGINS)
     switches::kEnablePepperTesting,
 #endif
+#if BUILDFLAG(ENABLE_RUNTIME_MEDIA_RENDERER_SELECTION)
+    switches::kDisableMojoRenderer,
+#endif
 #if BUILDFLAG(ENABLE_WEBRTC)
     switches::kDisableWebRtcHWDecoding,
     switches::kDisableWebRtcHWEncoding,
diff --git a/content/browser/service_worker/service_worker_installed_scripts_sender.cc b/content/browser/service_worker/service_worker_installed_scripts_sender.cc
index 1a0615d..8108454 100644
--- a/content/browser/service_worker/service_worker_installed_scripts_sender.cc
+++ b/content/browser/service_worker/service_worker_installed_scripts_sender.cc
@@ -407,6 +407,7 @@
   TRACE_EVENT_NESTABLE_ASYNC_END1("ServiceWorker",
                                   "ServiceWorkerInstalledScriptsSender", this,
                                   "FinishedReason", static_cast<int>(reason));
+  running_sender_.reset();
   Finish(reason);
 
   switch (reason) {
diff --git a/content/browser/service_worker/service_worker_registration.cc b/content/browser/service_worker/service_worker_registration.cc
index e7a8b36..98c16b7 100644
--- a/content/browser/service_worker/service_worker_registration.cc
+++ b/content/browser/service_worker/service_worker_registration.cc
@@ -405,6 +405,11 @@
     const scoped_refptr<ServiceWorkerVersion>& version) {
   DCHECK_EQ(id(), version->registration_id());
 
+  // Protect the registration since version->Doom() can stop |version|, which
+  // destroys start worker callbacks, which might be the only things holding a
+  // reference to |this|.
+  scoped_refptr<ServiceWorkerRegistration> protect(this);
+
   UnsetVersion(version.get());
 
   for (std::unique_ptr<ServiceWorkerContextCore::ProviderHostIterator> it =
@@ -421,7 +426,7 @@
     // Delete the records from the db.
     context_->storage()->DeleteRegistration(
         id(), pattern().GetOrigin(),
-        base::Bind(&ServiceWorkerRegistration::OnDeleteFinished, this));
+        base::Bind(&ServiceWorkerRegistration::OnDeleteFinished, protect));
     // But not from memory if there is a version in the pipeline.
     // TODO(falken): Fix this logic. There could be a running register job for
     // this registration that hasn't set installing_version() yet.
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 16af78f..0020ff8 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -412,6 +412,9 @@
   WebRuntimeFeatures::EnableOverflowIconsForMediaControls(
       base::FeatureList::IsEnabled(media::kOverflowIconsForMediaControls));
 
+  WebRuntimeFeatures::EnableAllowActivationDelegationAttr(
+      base::FeatureList::IsEnabled(features::kAllowActivationDelegationAttr));
+
   // Enable explicitly enabled features, and then disable explicitly disabled
   // ones.
   for (const std::string& feature :
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index e5b104c8..0ed1169 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -40,6 +40,10 @@
     "appcache_service.h",
     "ax_event_notification_details.cc",
     "ax_event_notification_details.h",
+    "background_fetch_delegate.cc",
+    "background_fetch_delegate.h",
+    "background_fetch_response.cc",
+    "background_fetch_response.h",
     "background_sync_controller.h",
     "background_sync_parameters.cc",
     "background_sync_parameters.h",
diff --git a/content/browser/background_fetch/background_fetch_delegate.cc b/content/public/browser/background_fetch_delegate.cc
similarity index 81%
rename from content/browser/background_fetch/background_fetch_delegate.cc
rename to content/public/browser/background_fetch_delegate.cc
index a652b869..aeefdc9 100644
--- a/content/browser/background_fetch/background_fetch_delegate.cc
+++ b/content/public/browser/background_fetch_delegate.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/background_fetch/background_fetch_delegate.h"
+#include "content/public/browser/background_fetch_delegate.h"
 
 namespace content {
 
diff --git a/content/browser/background_fetch/background_fetch_delegate.h b/content/public/browser/background_fetch_delegate.h
similarity index 87%
rename from content/browser/background_fetch/background_fetch_delegate.h
rename to content/public/browser/background_fetch_delegate.h
index 07de2a5..597f5d8b9 100644
--- a/content/browser/background_fetch/background_fetch_delegate.h
+++ b/content/public/browser/background_fetch_delegate.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_DELEGATE_H_
-#define CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_DELEGATE_H_
+#ifndef CONTENT_PUBLIC_BROWSER_BACKGROUND_FETCH_DELEGATE_H_
+#define CONTENT_PUBLIC_BROWSER_BACKGROUND_FETCH_DELEGATE_H_
 
 #include <memory>
 #include <string>
@@ -54,6 +54,10 @@
     virtual void OnDownloadComplete(
         const std::string& guid,
         std::unique_ptr<BackgroundFetchResult> result) = 0;
+
+    // Called by the delegate when it's shutting down to signal that the
+    // delegate is no longer valid.
+    virtual void OnDelegateShutdown() = 0;
   };
 
   BackgroundFetchDelegate();
@@ -78,4 +82,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_DELEGATE_H_
+#endif  // CONTENT_PUBLIC_BROWSER_BACKGROUND_FETCH_DELEGATE_H_
diff --git a/content/browser/background_fetch/background_fetch_response.cc b/content/public/browser/background_fetch_response.cc
similarity index 82%
rename from content/browser/background_fetch/background_fetch_response.cc
rename to content/public/browser/background_fetch_response.cc
index 5a47a50..2b65eaf7 100644
--- a/content/browser/background_fetch/background_fetch_response.cc
+++ b/content/public/browser/background_fetch_response.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/background_fetch/background_fetch_response.h"
+#include "content/public/browser/background_fetch_response.h"
 
 namespace content {
 
@@ -13,6 +13,9 @@
 
 BackgroundFetchResponse::~BackgroundFetchResponse() {}
 
+BackgroundFetchResult::BackgroundFetchResult(base::Time response_time)
+    : response_time(response_time) {}
+
 BackgroundFetchResult::BackgroundFetchResult(base::Time response_time,
                                              const base::FilePath& path,
                                              uint64_t file_size)
diff --git a/content/browser/background_fetch/background_fetch_response.h b/content/public/browser/background_fetch_response.h
similarity index 77%
rename from content/browser/background_fetch/background_fetch_response.h
rename to content/public/browser/background_fetch_response.h
index 59a6df7..1debd98 100644
--- a/content/browser/background_fetch/background_fetch_response.h
+++ b/content/public/browser/background_fetch_response.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_RESPONSE_H_
-#define CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_RESPONSE_H_
+#ifndef CONTENT_PUBLIC_BROWSER_BACKGROUND_FETCH_RESPONSE_H_
+#define CONTENT_PUBLIC_BROWSER_BACKGROUND_FETCH_RESPONSE_H_
 
 #include <vector>
 
@@ -31,6 +31,10 @@
 };
 
 struct CONTENT_EXPORT BackgroundFetchResult {
+  // Constructor for failed downloads.
+  explicit BackgroundFetchResult(base::Time response_time);
+
+  // Constructor for successful downloads.
   BackgroundFetchResult(base::Time response_time,
                         const base::FilePath& path,
                         uint64_t file_size);
@@ -39,7 +43,7 @@
 
   const base::Time response_time;
   const base::FilePath file_path;
-  const uint64_t file_size;
+  const uint64_t file_size = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(BackgroundFetchResult);
@@ -47,4 +51,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_RESPONSE_H_
+#endif  // CONTENT_PUBLIC_BROWSER_BACKGROUND_FETCH_RESPONSE_H_
diff --git a/content/public/browser/browser_context.h b/content/public/browser/browser_context.h
index 4416a36..eff1498 100644
--- a/content/public/browser/browser_context.h
+++ b/content/public/browser/browser_context.h
@@ -55,6 +55,7 @@
 enum class PushDeliveryStatus;
 }
 
+class BackgroundFetchDelegate;
 class BackgroundSyncController;
 class BlobHandle;
 class BrowserPluginGuestManager;
@@ -245,6 +246,10 @@
   // otherwise.
   virtual PermissionManager* GetPermissionManager() = 0;
 
+  // Returns the BackgroundFetchDelegate associated with that context if any,
+  // nullptr otherwise.
+  virtual BackgroundFetchDelegate* GetBackgroundFetchDelegate() = 0;
+
   // Returns the BackgroundSyncController associated with that context if any,
   // nullptr otherwise.
   virtual BackgroundSyncController* GetBackgroundSyncController() = 0;
diff --git a/content/public/browser/browser_thread.h b/content/public/browser/browser_thread.h
index 472a713..75dfc0d 100644
--- a/content/public/browser/browser_thread.h
+++ b/content/public/browser/browser_thread.h
@@ -67,9 +67,6 @@
     // The main thread in the browser.
     UI,
 
-    // This is the thread that interacts with the database.
-    DB,
-
     // This is the thread that interacts with the file system.
     // DEPRECATED: prefer base/task_scheduler/post_task.h for new classes
     // requiring a background file I/O task runner, i.e.:
@@ -80,23 +77,11 @@
     //         is visible but non-blocking to the user.
     FILE,
 
-    // Used for file system operations that block user interactions.
-    // Responsiveness of this thread affect users.
-    // DEPRECATED: prefer base/task_scheduler/post_task.h for new classes
-    // requiring a user-blocking file I/O task runner, i.e.:
-    //   base::CreateSequencedTaskRunnerWithTraits(
-    //       {base::MayBlock(), base::TaskPriority::USER_BLOCKING})
-    FILE_USER_BLOCKING,
-
     // Used to launch and terminate Chrome processes.
     PROCESS_LAUNCHER,
 
-    // This is the thread to handle slow HTTP cache operations.
-    CACHE,
-
     // This is the thread that processes non-blocking IO, i.e. IPC and network.
-    // Blocking IO should happen on other threads like DB, FILE,
-    // FILE_USER_BLOCKING and CACHE depending on the usage.
+    // Blocking IO should happen in TaskScheduler.
     IO,
 
     // NOTE: do not add new threads here that are only used by a small number of
@@ -286,7 +271,6 @@
   struct DeleteOnUIThread : public DeleteOnThread<UI> { };
   struct DeleteOnIOThread : public DeleteOnThread<IO> { };
   struct DeleteOnFileThread : public DeleteOnThread<FILE> { };
-  struct DeleteOnDBThread : public DeleteOnThread<DB> { };
 
   // Returns an appropriate error message for when DCHECK_CURRENTLY_ON() fails.
   static std::string GetDCheckCurrentlyOnErrorMessage(ID expected);
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 11f65db..7747f55 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -9,6 +9,11 @@
 
 // All features in alphabetical order.
 
+// Enables the allowActivationDelegation attribute on iframes.
+// https://www.chromestatus.com/features/6025124331388928
+const base::Feature kAllowActivationDelegationAttr{
+    "AllowActivationDelegationAttr", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables content-initiated, main frame navigations to data URLs.
 // TODO(meacer): Remove when the deprecation is complete.
 //               https://www.chromestatus.com/feature/5669602927312896
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 1c10cb1c..149b27c 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -16,6 +16,7 @@
 
 // All features in alphabetical order. The features should be documented
 // alongside the definition of their values in the .cc file.
+CONTENT_EXPORT extern const base::Feature kAllowActivationDelegationAttr;
 CONTENT_EXPORT extern const base::Feature
     kAllowContentInitiatedDataUrlNavigations;
 CONTENT_EXPORT extern const base::Feature kAsmJsToWebAssembly;
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 6ad7f33..7005c8e 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -362,10 +362,6 @@
 const char kDisableBrowserSideNavigation[] = "disable-browser-side-navigation";
 const char kEnableBrowserSideNavigation[]   = "enable-browser-side-navigation";
 
-// Enable animating of images in the compositor instead of blink.
-const char kEnableCompositorImageAnimations[] =
-    "enable-compositor-image-animations";
-
 // Enables display list based 2d canvas implementation. Options:
 //  1. Enable: allow browser to use display list for 2d canvas (browser makes
 //     decision).
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index d11eab1..b1bdada 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -115,7 +115,6 @@
 CONTENT_EXPORT extern const char kEnableBlinkFeatures[];
 CONTENT_EXPORT extern const char kEnableBackgroundFetchPersistence[];
 CONTENT_EXPORT extern const char kEnableBrowserSideNavigation[];
-CONTENT_EXPORT extern const char kEnableCompositorImageAnimations[];
 CONTENT_EXPORT extern const char kEnableDisplayList2dCanvas[];
 CONTENT_EXPORT extern const char kEnableDistanceFieldText[];
 CONTENT_EXPORT extern const char kEnableExperimentalCanvasFeatures[];
diff --git a/content/public/test/test_browser_context.cc b/content/public/test/test_browser_context.cc
index dc647dfa..31b876bb 100644
--- a/content/public/test/test_browser_context.cc
+++ b/content/public/test/test_browser_context.cc
@@ -123,6 +123,10 @@
   return permission_manager_.get();
 }
 
+BackgroundFetchDelegate* TestBrowserContext::GetBackgroundFetchDelegate() {
+  return nullptr;
+}
+
 BackgroundSyncController* TestBrowserContext::GetBackgroundSyncController() {
   if (!background_sync_controller_)
     background_sync_controller_.reset(new MockBackgroundSyncController());
diff --git a/content/public/test/test_browser_context.h b/content/public/test/test_browser_context.h
index 0a878f5..061f69b 100644
--- a/content/public/test/test_browser_context.h
+++ b/content/public/test/test_browser_context.h
@@ -56,6 +56,7 @@
   PushMessagingService* GetPushMessagingService() override;
   SSLHostStateDelegate* GetSSLHostStateDelegate() override;
   PermissionManager* GetPermissionManager() override;
+  BackgroundFetchDelegate* GetBackgroundFetchDelegate() override;
   BackgroundSyncController* GetBackgroundSyncController() override;
   BrowsingDataRemoverDelegate* GetBrowsingDataRemoverDelegate() override;
   net::URLRequestContextGetter* CreateRequestContext(
diff --git a/content/public/test/test_browser_thread_bundle.cc b/content/public/test/test_browser_thread_bundle.cc
index ebe8ecc8..132419d 100644
--- a/content/public/test/test_browser_thread_bundle.cc
+++ b/content/public/test/test_browser_thread_bundle.cc
@@ -39,16 +39,10 @@
   base::RunLoop().RunUntilIdle();
   io_thread_->Stop();
   base::RunLoop().RunUntilIdle();
-  cache_thread_->Stop();
-  base::RunLoop().RunUntilIdle();
   process_launcher_thread_->Stop();
   base::RunLoop().RunUntilIdle();
-  file_user_blocking_thread_->Stop();
-  base::RunLoop().RunUntilIdle();
   file_thread_->Stop();
   base::RunLoop().RunUntilIdle();
-  db_thread_->Stop();
-  base::RunLoop().RunUntilIdle();
   ui_thread_->Stop();
   base::RunLoop().RunUntilIdle();
 
@@ -124,14 +118,6 @@
 void TestBrowserThreadBundle::CreateBrowserThreads() {
   CHECK(!threads_created_);
 
-  if (options_ & REAL_DB_THREAD) {
-    db_thread_ = base::MakeUnique<TestBrowserThread>(BrowserThread::DB);
-    db_thread_->Start();
-  } else {
-    db_thread_ = base::MakeUnique<TestBrowserThread>(
-        BrowserThread::DB, base::MessageLoop::current());
-  }
-
   if (options_ & REAL_FILE_THREAD) {
     file_thread_ = base::MakeUnique<TestBrowserThread>(BrowserThread::FILE);
     file_thread_->Start();
@@ -140,12 +126,8 @@
         BrowserThread::FILE, base::MessageLoop::current());
   }
 
-  file_user_blocking_thread_ = base::MakeUnique<TestBrowserThread>(
-      BrowserThread::FILE_USER_BLOCKING, base::MessageLoop::current());
   process_launcher_thread_ = base::MakeUnique<TestBrowserThread>(
       BrowserThread::PROCESS_LAUNCHER, base::MessageLoop::current());
-  cache_thread_ = base::MakeUnique<TestBrowserThread>(
-      BrowserThread::CACHE, base::MessageLoop::current());
 
   if (options_ & REAL_IO_THREAD) {
     io_thread_ = base::MakeUnique<TestBrowserThread>(BrowserThread::IO);
diff --git a/content/public/test/test_browser_thread_bundle.h b/content/public/test/test_browser_thread_bundle.h
index 8b72d65..26f5199 100644
--- a/content/public/test/test_browser_thread_bundle.h
+++ b/content/public/test/test_browser_thread_bundle.h
@@ -112,10 +112,9 @@
   enum Options {
     DEFAULT = 0,
     IO_MAINLOOP = 1 << 0,
-    REAL_DB_THREAD = 1 << 1,
-    REAL_FILE_THREAD = 1 << 2,
-    REAL_IO_THREAD = 1 << 3,
-    DONT_CREATE_BROWSER_THREADS = 1 << 4,
+    REAL_FILE_THREAD = 1 << 1,
+    REAL_IO_THREAD = 1 << 2,
+    DONT_CREATE_BROWSER_THREADS = 1 << 3,
   };
 
   TestBrowserThreadBundle();
@@ -132,11 +131,8 @@
 
   std::unique_ptr<base::test::ScopedTaskEnvironment> scoped_task_environment_;
   std::unique_ptr<TestBrowserThread> ui_thread_;
-  std::unique_ptr<TestBrowserThread> db_thread_;
   std::unique_ptr<TestBrowserThread> file_thread_;
-  std::unique_ptr<TestBrowserThread> file_user_blocking_thread_;
   std::unique_ptr<TestBrowserThread> process_launcher_thread_;
-  std::unique_ptr<TestBrowserThread> cache_thread_;
   std::unique_ptr<TestBrowserThread> io_thread_;
 
   int options_;
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc
index fbd0f388..aeb94bc 100644
--- a/content/renderer/gpu/render_widget_compositor.cc
+++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -595,9 +595,6 @@
   settings.wait_for_all_pipeline_stages_before_draw =
       cmd.HasSwitch(cc::switches::kRunAllCompositorStagesBeforeDraw);
 
-  settings.enable_image_animations =
-      cmd.HasSwitch(switches::kEnableCompositorImageAnimations);
-
   return settings;
 }
 
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 942d9c8..8d683097 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -3473,7 +3473,7 @@
 
   // The rest of RenderView assumes that a WebDocumentLoader will always have a
   // non-null NavigationState.
-  UpdateNavigationState(document_state, false /* was_within_same_page */,
+  UpdateNavigationState(document_state, false /* was_within_same_document */,
                         content_initiated);
 
   NavigationStateImpl* navigation_state = static_cast<NavigationStateImpl*>(
@@ -4106,7 +4106,7 @@
                "id", routing_id_);
   DocumentState* document_state =
       DocumentState::FromDocumentLoader(frame_->GetDocumentLoader());
-  UpdateNavigationState(document_state, true /* was_within_same_page */,
+  UpdateNavigationState(document_state, true /* was_within_same_document */,
                         content_initiated);
   static_cast<NavigationStateImpl*>(document_state->navigation_state())
       ->set_was_within_same_document(true);
@@ -6717,7 +6717,7 @@
 }
 
 void RenderFrameImpl::UpdateNavigationState(DocumentState* document_state,
-                                            bool was_within_same_page,
+                                            bool was_within_same_document,
                                             bool content_initiated) {
   // If this was a browser-initiated navigation, then there could be pending
   // navigation params, so use them. Otherwise, just reset the document state
@@ -6733,9 +6733,9 @@
   document_state->set_navigation_state(CreateNavigationStateFromPending());
 
   // The |set_was_load_data_with_base_url_request| state should not change for
-  // an in-page navigation, so skip updating it from the in-page navigation
-  // params in this case.
-  if (!was_within_same_page) {
+  // same document navigation, so skip updating it from the same document
+  // navigation params in this case.
+  if (!was_within_same_document) {
     const CommonNavigationParams& common_params =
         pending_navigation_params_->common_params;
     bool load_data = !common_params.base_url_for_data_url.is_empty() &&
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 4a94f63..858f11fe 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -1139,7 +1139,7 @@
   // Sets the NavigationState on the DocumentState based on
   // the value of |pending_navigation_params_|.
   void UpdateNavigationState(DocumentState* document_state,
-                             bool was_within_same_page,
+                             bool was_within_same_document,
                              bool content_initiated);
 
 #if BUILDFLAG(ENABLE_PLUGINS)
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 5dc03fa..4c342e71 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -857,9 +857,6 @@
     is_distance_field_text_enabled_ = false;
   }
 
-  WebRuntimeFeatures::EnableCompositorImageAnimations(
-      command_line.HasSwitch(switches::kEnableCompositorImageAnimations));
-
   // Note that under Linux, the media library will normally already have
   // been initialized by the Zygote before this instance became a Renderer.
   media::InitializeMediaLibrary();
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index b629409..012838c 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -653,7 +653,7 @@
       ]
 
       outputs = [
-        "{{bundle_root_dir}}/{{source_file_part}}",
+        "{{bundle_contents_dir}}/{{source_file_part}}",
       ]
 
       public_deps = [
@@ -769,7 +769,7 @@
       "$root_out_dir/$content_shell_helper_name.app",
     ]
     outputs = [
-      "{{bundle_root_dir}}/Frameworks/{{source_file_part}}",
+      "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}",
     ]
     public_deps = [
       ":content_shell_framework+link",
diff --git a/content/shell/browser/shell_browser_context.cc b/content/shell/browser/shell_browser_context.cc
index b3ad114a6..825f75d 100644
--- a/content/shell/browser/shell_browser_context.cc
+++ b/content/shell/browser/shell_browser_context.cc
@@ -223,6 +223,10 @@
   return permission_manager_.get();
 }
 
+BackgroundFetchDelegate* ShellBrowserContext::GetBackgroundFetchDelegate() {
+  return nullptr;
+}
+
 BackgroundSyncController* ShellBrowserContext::GetBackgroundSyncController() {
   if (!background_sync_controller_)
     background_sync_controller_.reset(new MockBackgroundSyncController());
diff --git a/content/shell/browser/shell_browser_context.h b/content/shell/browser/shell_browser_context.h
index d98a7b84..1ba415cf2 100644
--- a/content/shell/browser/shell_browser_context.h
+++ b/content/shell/browser/shell_browser_context.h
@@ -55,6 +55,7 @@
   PushMessagingService* GetPushMessagingService() override;
   SSLHostStateDelegate* GetSSLHostStateDelegate() override;
   PermissionManager* GetPermissionManager() override;
+  BackgroundFetchDelegate* GetBackgroundFetchDelegate() override;
   BackgroundSyncController* GetBackgroundSyncController() override;
   BrowsingDataRemoverDelegate* GetBrowsingDataRemoverDelegate() override;
   net::URLRequestContextGetter* CreateRequestContext(
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 2b25c8c..08b24f4 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -33,6 +33,10 @@
     "../browser/background_fetch/background_fetch_embedded_worker_test_helper.h",
     "../browser/background_fetch/background_fetch_test_base.cc",
     "../browser/background_fetch/background_fetch_test_base.h",
+    "../browser/background_fetch/background_fetch_test_browser_context.cc",
+    "../browser/background_fetch/background_fetch_test_browser_context.h",
+    "../browser/background_fetch/mock_background_fetch_delegate.cc",
+    "../browser/background_fetch/mock_background_fetch_delegate.h",
     "../browser/download/mock_download_file.cc",
     "../browser/download/mock_download_file.h",
     "../browser/download/mock_download_item_impl.cc",
diff --git a/content/test/renderer_audio_output_stream_factory_context_impl_unittest.cc b/content/test/renderer_audio_output_stream_factory_context_impl_unittest.cc
index 17a4f84..993a0bbc 100644
--- a/content/test/renderer_audio_output_stream_factory_context_impl_unittest.cc
+++ b/content/test/renderer_audio_output_stream_factory_context_impl_unittest.cc
@@ -26,7 +26,7 @@
 #include "media/audio/audio_output_device.h"
 #include "media/audio/audio_system_impl.h"
 #include "media/audio/audio_thread_impl.h"
-#include "media/audio/fake_audio_log_factory.h"
+#include "media/audio/mock_audio_manager.h"
 #include "media/audio/simple_sources.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/media_switches.h"
@@ -93,55 +93,19 @@
   }
 }
 
-class MockAudioManager : public media::AudioManagerBase {
- public:
-  MockAudioManager(std::unique_ptr<media::AudioThread> audio_thread,
-                   media::AudioLogFactory* audio_log_factory)
-      : media::AudioManagerBase(std::move(audio_thread), audio_log_factory) {
-    ON_CALL(*this, HasAudioOutputDevices()).WillByDefault(Return(true));
-  }
-
-  ~MockAudioManager() override = default;
-
-  MOCK_METHOD2(MakeLinearOutputStream,
-               media::AudioOutputStream*(const media::AudioParameters& params,
-                                         const LogCallback& log_callback));
-  MOCK_METHOD3(MakeLowLatencyOutputStream,
-               media::AudioOutputStream*(const media::AudioParameters& params,
-                                         const std::string& device_id,
-                                         const LogCallback& log_callback));
-  MOCK_METHOD3(MakeLinearInputStream,
-               media::AudioInputStream*(const media::AudioParameters& params,
-                                        const std::string& device_id,
-                                        const LogCallback& log_callback));
-  MOCK_METHOD3(MakeLowLatencyInputStream,
-               media::AudioInputStream*(const media::AudioParameters& params,
-                                        const std::string& device_id,
-                                        const LogCallback& log_callback));
-
- protected:
-  MOCK_METHOD0(HasAudioOutputDevices, bool());
-  MOCK_METHOD0(HasAudioInputDevices, bool());
-  MOCK_METHOD0(GetName, const char*());
-  media::AudioParameters GetPreferredOutputStreamParameters(
-      const std::string& device_id,
-      const media::AudioParameters& params) {
-    return GetTestAudioParameters();
-  }
-};
-
 class MockAudioOutputStream : public media::AudioOutputStream,
                               public base::PlatformThread::Delegate {
  public:
-  explicit MockAudioOutputStream(MockAudioManager* audio_manager)
+  MockAudioOutputStream()
       : done_(base::WaitableEvent::ResetPolicy::MANUAL,
-              base::WaitableEvent::InitialState::NOT_SIGNALED),
-        audio_manager_(audio_manager) {}
+              base::WaitableEvent::InitialState::NOT_SIGNALED) {}
 
   ~MockAudioOutputStream() override {
     base::PlatformThread::Join(thread_handle_);
   }
 
+  void Wait() { done_.Wait(); }
+
   void Start(AudioSourceCallback* callback) override {
     callback_ = callback;
     EXPECT_TRUE(base::PlatformThread::CreateWithPriority(
@@ -158,7 +122,7 @@
   void GetVolume(double* volume) override { *volume = 1; }
   void Close() override {
     Stop();
-    audio_manager_->ReleaseOutputStream(this);
+    delete this;
   }
 
   void ThreadMain() override {
@@ -189,7 +153,6 @@
   base::OnceClosure sync_closure_;
   base::PlatformThreadHandle thread_handle_;
   base::WaitableEvent done_;
-  MockAudioManager* audio_manager_;
   AudioSourceCallback* callback_;
 };
 
@@ -221,24 +184,20 @@
   RendererAudioOutputStreamFactoryIntegrationTest()
       : media_stream_manager_(),
         thread_bundle_(TestBrowserThreadBundle::Options::REAL_IO_THREAD),
-        log_factory_(),
-        audio_manager_(std::make_unique<MockAudioManager>(
-            std::make_unique<media::AudioThreadImpl>(),
-            &log_factory_)),
-        audio_system_(
-            std::make_unique<media::AudioSystemImpl>(audio_manager_.get())) {
+        audio_manager_(std::make_unique<media::AudioThreadImpl>()),
+        audio_system_(&audio_manager_) {
     media_stream_manager_ = std::make_unique<MediaStreamManager>(
-        audio_system_.get(), audio_manager_->GetTaskRunner());
+        &audio_system_, audio_manager_.GetTaskRunner());
   }
 
   ~RendererAudioOutputStreamFactoryIntegrationTest() override {
-    audio_manager_->Shutdown();
+    audio_manager_.Shutdown();
   }
 
   UniqueAudioOutputStreamFactoryPtr CreateAndBindFactory(
       mojom::RendererAudioOutputStreamFactoryRequest request) {
     factory_context_.reset(new RendererAudioOutputStreamFactoryContextImpl(
-        kRenderProcessId, audio_system_.get(), audio_manager_.get(),
+        kRenderProcessId, &audio_system_, &audio_manager_,
         media_stream_manager_.get(), kSalt));
     return RenderFrameAudioOutputStreamFactoryHandle::CreateFactory(
         factory_context_.get(), kRenderFrameId, std::move(request));
@@ -246,9 +205,8 @@
 
   std::unique_ptr<MediaStreamManager> media_stream_manager_;
   TestBrowserThreadBundle thread_bundle_;
-  media::FakeAudioLogFactory log_factory_;
-  std::unique_ptr<media::AudioManager> audio_manager_;
-  std::unique_ptr<media::AudioSystem> audio_system_;
+  media::MockAudioManager audio_manager_;
+  media::AudioSystemImpl audio_system_;
   std::unique_ptr<RendererAudioOutputStreamFactoryContextImpl,
                   BrowserThread::DeleteOnIOThread>
       factory_context_;
@@ -258,13 +216,23 @@
   // Sets up the factory on the IO thread and runs client code on the UI thread.
   // Send a sine wave from the client and makes sure it's received by the output
   // stream.
-  MockAudioOutputStream* stream = new MockAudioOutputStream(
-      static_cast<MockAudioManager*>(audio_manager_.get()));
+  MockAudioOutputStream* stream = new MockAudioOutputStream();
 
   // Make sure the mock audio manager uses our mock stream.
-  EXPECT_CALL(*static_cast<MockAudioManager*>(audio_manager_.get()),
-              MakeLowLatencyOutputStream(_, "", _))
-      .WillOnce(Return(stream));
+  bool create_stream_called = false;
+  audio_manager_.SetMakeOutputStreamCB(base::BindRepeating(
+      [](bool* create_stream_called, media::AudioOutputStream* stream,
+         const media::AudioParameters& params,
+         const std::string& name) -> media::AudioOutputStream* {
+        DCHECK(!*create_stream_called);
+        DCHECK(stream);
+        DCHECK_EQ(name, "default");
+        DCHECK_EQ(params.AsHumanReadableString(),
+                  GetTestAudioParameters().AsHumanReadableString());
+        *create_stream_called = true;
+        return stream;
+      },
+      &create_stream_called, stream));
 
   mojom::RendererAudioOutputStreamFactoryPtr stream_factory;
   auto factory_handle =
@@ -309,7 +277,7 @@
   // Wait for stream to start.
   SyncWithAllThreads();
   // Wait for stream to finish. Verifies data.
-  stream->Stop();
+  stream->Wait();
   device->Stop();
 
   // |stream_factory| must be destroyed on the correct thread.
diff --git a/docs/clang.md b/docs/clang.md
index 685362f..4ae603a 100644
--- a/docs/clang.md
+++ b/docs/clang.md
@@ -3,9 +3,9 @@
 [Clang](http://clang.llvm.org/) is a compiler with many desirable features
 (outlined on their website).
 
-Chrome can be built with Clang. It is now the default compiler on Mac and Linux
-for building Chrome, and it is currently useful for its warning and error
-messages on Android and Windows.
+Chrome can be built with Clang. It is now the default compiler on Android, Mac
+and Linux for building Chrome, and it is currently useful for its warning and
+error messages on Windows.
 
 See
 [the open bugs](http://code.google.com/p/chromium/issues/list?q=label:clang).
@@ -120,8 +120,6 @@
 
 LLD is a relatively new linker from LLVM. The current focus is on Windows and
 Linux support, where it can link Chrome approximately twice as fast as gold and
-MSVC's link.exe as of this writing. LLD does not yet support generating PDB
-files, which makes it hard to debug Chrome while using LLD.
+MSVC's link.exe as of this writing.
 
-on Windows.
 Set `use_lld = true` in args.gn.
diff --git a/docs/common_build_tasks.md b/docs/common_build_tasks.md
deleted file mode 100644
index c042169..0000000
--- a/docs/common_build_tasks.md
+++ /dev/null
@@ -1,159 +0,0 @@
-# Common Build Tasks
-
-The Chromium build system is a complicated beast of a system, and it is not very
-well documented beyond the basics of getting the source and building the
-Chromium product. This page has more advanced information about the build
-system.
-
-If you're new to Chromium development, read the
-[getting started guides](https://www.chromium.org/developers/how-tos/get-the-code).
-
-There is some additional documentation on
-[setting GYP build parameters](https://dev.chromium.org/developers/gyp-environment-variables).
-
-[TOC]
-
-## Faster Builds
-
-### Components Build
-
-A non-standard build configuration is to use dynamic linking instead of static
-linking for the various modules in the Chromium codebase. This results in
-significantly faster link times, but is a divergence from what is shipped and
-primarily tested. To enable the
-[component build](https://www.chromium.org/developers/how-tos/component-build):
-
-    $ GYP_DEFINES="component=shared_library" gclient runhooks
-
-or
-
-```
-C:\...\src>set GYP_DEFINES=component=shared_library
-C:\...\src>gclient runhooks
-```
-
-### Windows: Debug Builds Link Faster
-
-On Windows if using the components build, building in debug mode will generally
-link faster. This is because in debug mode, the linker works incrementally. In
-release mode, a full link is performed each time.
-
-### Mac: Disable Release Mode Stripping
-
-On Mac, if building in release mode, one of the final build steps will be to
-strip the build products and create dSYM files. This process can slow down your
-incremental builds, but it can be disabled with the following define:
-
-    $ GYP_DEFINES="mac_strip_release=0" gclient runhooks
-
-### Mac: DCHECKs in Release Mode
-
-DCHECKs are only designed to be run in debug builds. But building in release
-mode on Mac is significantly faster. You can have your cake and eat it too by
-building release mode with DCHECKs enabled using the following define:
-
-    $ GYP_DEFINES="dcheck_always_on=1" gclient runhooks
-
-### Linux
-
-The Linux build instructions page has its own section on [making the build
-faster](linux_build_instructions.md#faster-builds).
-
-## Configuring the Build
-
-### Environment Variables
-
-There are various environment variables that can be passed to the metabuild
-system GYP when generating project files. This is a summary of them:
-
-TODO(andybons): Convert to list.
-
-|:-------------|:--------------------------------------------------------------|
-| `GYP_DEFINES` | A set of key=value pairs separated by space that will set default values of variables used in .gyp and .gypi files |
-| `GYP_GENERATORS` | The specific generator that creates build-system specific files |
-| `GYP_GENERATOR_FLAGS` | Flags that are passed down to the tool that generates the build-system specific files |
-| `GYP_GENERATOR_OUTPUT` | The directory that the top-level build output directory is relative to |
-
-Note also that GYP uses CPPFLAGS, CFLAGS, and CXXFLAGS when generating ninja
-files (the values at build time = ninja run time are _not_ used); see
-[gyp/generator/ninja.py](https://code.google.com/p/chromium/codesearch#chromium/src/tools/gyp/pylib/gyp/generator/ninja.py&q=cxxflags).
-
-### Variable Files
-
-If you want to keep a set of variables established, there are a couple of magic
-files that GYP reads:
-
-#### chromium.gyp\_env
-
-Next to your top-level `/src/` directory, create a file called
-`chromium.gyp_env`. This holds a JSON dictionary, with the keys being any of the
-above environment variables. For the full list of supported keys, see
-[/src/build/gyp_helper.py](/build/gyp_helper.py).
-
-```
-{
-  'variables': {
-    'mac_strip_release': 0,
-  }, 'GYP_DEFINES':
-    'clang=1 ' 'component=shared_library ' 'dcheck_always_on=1 '
-}
-```
-
-#### include.gyp
-
-Or globally in your home directory, create a file `~/.gyp/include.gypi`.
-
-#### supplement.gypi
-
-The build system will also include any files named `/src/*/supplement.gypi`,
-which should be in the same format as include.gyp above.
-
-### Change the Build System
-
-Most platforms support multiple build systems (Windows: different Visual Studios
-versions and ninja, Mac: Xcode and ninja, etc.). A sensible default is selected,
-but it can be overridden:
-
-    $ GYP_GENERATORS=ninja gclient runhooks
-
-[Ninja](ninja_build.md) is generally the fastest way to build anything on any
-platform.
-
-### Change Build Output Directory
-
-If you need to change a compile-time flag and do not want to touch your current
-build output, you can re-run GYP and place output into a new directory, like so,
-assuming you are using ninja:
-
-```shell
-$ GYP_GENERATOR_FLAGS="output_dir=out_other_ninja" gclient runhooks
-$ ninja -C out_other_ninja/Release chrome
-```
-
-Alternatively, you can do the following, which should work with all GYP
-generators, but the out directory is nested as `out_other/out/`.
-
-```shell
-$ GYP_GENERATOR_OUTPUT="out_other" gclient runhooks
-$ ninja -C out_other/out/Release chrome
-```
-
-**Note:** If you wish to run the WebKit layout tests, make sure you specify the
-new directory using `--build-directory=out_other_ninja` (don't include the
-`Release` part).
-
-### Building Google Chrome
-
-To build Chrome, you need to be a Google employee and have access to the
-[src-internal](https://goto.google.com/src-internal) repository. Once your
-checkout is set up, you can run gclient like so:
-
-    $ GYP_DEFINES="branding=Chrome buildtype=Official" gclient runhooks
-
-Then building the `chrome` target will produce the official build. This tip can
-be used in conjunction with changing the output directory, since changing these
-defines will rebuild the world.
-
-Also note that some GYP\_DEFINES flags are incompatible with the official build.
-If you get an error when you try to build, try removing all your flags and start
-with just the above ones.
diff --git a/docs/design/threading.md b/docs/design/threading.md
index eb69f16e..0f3b8ea 100644
--- a/docs/design/threading.md
+++ b/docs/design/threading.md
@@ -1,511 +1,4 @@
 # Threading
 
-[TOC]
-
-## Overview
-
-Chromium is a very multithreaded product. We try to keep the UI as responsive as
-possible, and this means not blocking the UI thread with any blocking I/O or
-other expensive operations. Our approach is to use message passing as the way of
-communicating between threads. We discourage locking and threadsafe
-objects. Instead, objects live on only one thread, we pass messages between
-threads for communication, and we use callback interfaces (implemented by
-message passing) for most cross-thread requests.
-
-The `Thread` object is defined in
-[`base/threading/thread.h`](https://cs.chromium.org/chromium/src/base/threading/thread.h).
-In general you should probably use one of the existing threads described below
-rather than make new ones. We already have a lot of threads that are difficult
-to keep track of. Each thread has a `MessageLoop` (see
-[`base/message_loop/message_loop.h`](https://cs.chromium.org/chromium/src/base/message_loop/message_loop.h)
-that processes messages for that thread. You can get the message loop for a
-thread using the `Thread.message_loop()` function.  More details about
-`MessageLoop` can be found in
-[Anatomy of Chromium MessageLoop](https://docs.google.com/document/d/1_pJUHO3f3VyRSQjEhKVvUU7NzCyuTCQshZvbWeQiCXU/view#).
-
-## Existing threads
-
-Most threads are managed by the BrowserProcess object, which acts as the service
-manager for the main "browser" process. By default, everything happens on the UI
-thread. We have pushed certain classes of processing into these other
-threads. It has getters for the following threads:
-
-*  **ui_thread**: Main thread where the application starts up.
-*  **io_thread**: This thread is somewhat mis-named. It is the dispatcher thread
-   that handles communication between the browser process and all the
-   sub-processes. It is also where all resource requests (web page loads) are
-   dispatched from (see
-   [Multi-process Architecture](https://www.chromium.org/developers/design-documents/multi-process-architecture)).
-*  **file_thread**: A general process thread for file operations. When you want to
-   do blocking filesystem operations (for example, requesting an icon for a file
-   type, or writing downloaded files to disk), dispatch to this thread.
-*  **db_thread**: A thread for database operations. For example, the cookie
-   service does sqlite operations on this thread. Note that the history database
-   doesn't use this thread yet.
-*  **safe_browsing_thread**
-
-Several components have their own threads:
-
-*  **History**: The history service object has its own thread. This might be
-   merged with the db_thread above. However, we need to be sure that things
-   happen in the correct order -- for example, that cookies are loaded before
-   history since cookies are needed for the first load, and history
-   initialization is long and will block it.
-*  **Proxy service**: See
-   [`net/http/http_proxy_service.cc`](https://cs.chromium.org/chromium/src/net/http/http_proxy_service.cc).
-*  **Automation proxy**: This thread is used to communicate with the UI test
-   program driving the app.
-
-## Keeping the browser responsive
-
-As hinted in the overview, we avoid doing any blocking I/O on the UI thread to
-keep the UI responsive.  Less apparent is that we also need to avoid blocking
-I/O on the IO thread.  The reason is that if we block it for an expensive
-operation, say disk access, then IPC messages don't get processed.  The effect
-is that the user can't interact with a page.  Note that asynchronous/overlapped
-I/O are fine.
-
-Another thing to watch out for is to not block threads on one another.  Locks
-should only be used to swap in a shared data structure that can be accessed on
-multiple threads.  If one thread updates it based on expensive computation or
-through disk access, then that slow work should be done without holding on to
-the lock.  Only when the result is available should the lock be used to swap in
-the new data.  An example of this is in PluginList::LoadPlugins
-([`content/common/plugin_list.cc`](https://cs.chromium.org/chromium/src/content/common/plugin_list.cc). If
-you must use locks,
-[here](https://www.chromium.org/developers/lock-and-condition-variable)
-are some best practices and pitfalls to avoid.
-
-In order to write non-blocking code, many APIs in Chromium are
-asynchronous. Usually this means that they either need to be executed on a
-particular thread and will return results via a custom delegate interface, or
-they take a `base::Callback<>` object that is called when the requested
-operation is completed.  Executing work on a specific thread is covered in the
-PostTask section below.
-
-## Getting stuff to other threads
-
-### `base::Callback<>`, Async APIs and Currying
-
-
-A `base::Callback<>` (see the docs in
-[`base/callback.h`](https://cs.chromium.org/chromium/src/base/callback.h) is
-a templated class with a `Run()` method.  It is a generalization of a function
-pointer and is created by a call to `base::Bind`.  Async APIs often will take a
-`base::Callback<>` as a means to asynchronously return the results of an
-operation.  Here is an example of a hypothetical FileRead API.
-
-    void ReadToString(const std::string& filename, const base::Callback<void(const std::string&)>& on_read);
-
-    void DisplayString(const std::string& result) {
-      LOG(INFO) << result;
-    }
-
-    void SomeFunc(const std::string& file) {
-      ReadToString(file, base::Bind(&DisplayString));
-    };
-
-In the example above, `base::Bind` takes the function pointer `&DisplayString`
-and turns it into a `base::Callback<void(const std::string& result)>`. The type
-of the generated `base::Callback<>` is inferred from the arguments.  Why not
-just pass the function pointer directly?  The reason is `base::Bind` allows the
-caller to adapt function interfaces and/or attach extra context
-via [Currying](http://en.wikipedia.org/wiki/Currying).  For instance, if we had
-a utility function `DisplayStringWithPrefix` that took an extra argument with
-the prefix, we use `base::Bind` to adapt the interface as follows.
-
-    void DisplayStringWithPrefix(const std::string& prefix, const std::string& result) {
-        LOG(INFO) << prefix << result;
-    }
-
-    void AnotherFunc(const std::string& file) {
-      ReadToString(file, base::Bind(&DisplayStringWithPrefix, "MyPrefix: "));
-    };
-
-This can be used in lieu of creating an adapter functions a small classes that
-holds prefix as a member variable.  Notice also that the `"MyPrefix: "` argument
-is actually a `const char*`, while `DisplayStringWithPrefix` actually wants a
-`const std::string&`.  Like normal function dispatch, `base::Bind`, will coerce
-parameters types if possible.
-
-See [How arguments are handled by base::Bind()](#how_arguments_are_handled)
-below for more details about argument storage, copying, and special handling of
-references.
-
-### PostTask
-
-The lowest level of dispatching to another thread is to use the
-`MessageLoop.PostTask` and `MessageLoop.PostDelayedTask`
-(see
-[`base/message_loop/message_loop.h`](https://cs.chromium.org/chromium/src/base/message_loop/message_loop.h)).
-PostTask schedules a task to be run on a particular thread.  A task is defined
-as a `base::Closure`, which is a typedef for a
-`base::Callback<void(void)>`. `PostDelayedTask` schedules a task to be run after
-a delay on a particular thread. A task is represented by the `base::Closure`
-typedef, which contains a `Run()` function, and is created by calling
-`base::Bind()`.  To process a task, the message loop eventually calls
-`base::Closure`'s `Run` function, and then drops the reference to the task
-object. Both `PostTask` and `PostDelayedTask` take a `base::Location`
-parameter, which is used for lightweight debugging purposes (counts and
-primitive profiling of pending and completed tasks can be monitored in a debug
-build via the url about:objects). Generally the macro value `FROM_HERE` is the
-appropriate value to use in this parameter.
-
-Note that new tasks go on the message loop's queue, and any delay that is
-specified is subject to the operating system's timer resolutions. This means
-that under Windows, very small timeouts (under 10ms) will likely not be honored
-(and will be longer). Using a timeout of 0 in `PostDelayedTask` is equivalent to
-calling `PostTask`, and adds no delay beyond queuing delay. `PostTask` is also
-used to do something on the current thread "sometime after the current
-processing returns to the message loop." Such a continuation on the current
-thread can be used to assure that other time critical tasks are not starved on
-this thread.
-
-The following is an example of a creating a task for a function and posting it
-to another thread (in this example, the file thread):
-
-    void WriteToFile(const std::string& filename, const std::string& data);
-    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
-                            base::Bind(&WriteToFile, "foo.txt", "hello world!"));
-
-You should always use `BrowserThread` to post tasks between threads.  Never
-cache `MessageLoop` pointers as it can cause bugs such as the pointers being
-deleted while you're still holding on to them.  More information can be
-found
-[here](https://www.chromium.org/developers/design-documents/threading/suble-threading-bugs-and-patterns-to-avoid-them).
-
-
-### base::Bind() and class methods.
-
-The `base::Bind()` API also supports invoking class methods as well.  The syntax
-is very similar to calling `base::Bind()` on a function, except the first
-argument should be the object the method belongs to. By default, the object that
-`PostTask` uses must be a thread-safe reference-counted object. Reference
-counting ensures that the object invoked on another thread will stay alive until
-the task completes.
-
-    class MyObject : public base::RefCountedThreadSafe<MyObject> {
-     public:
-      void DoSomething(const std::string16& name) {
-        thread_->message_loop()->PostTask(
-           FROM_HERE, base::Bind(&MyObject::DoSomethingOnAnotherThread, this, name));
-      }
-
-      void DoSomethingOnAnotherThread(const std::string16& name) {
-        ...
-      }
-     private:
-      // Always good form to make the destructor private so that only RefCounted
-      // ThreadSafe can access it.
-      // This avoids bugs with double deletes.
-      friend class base::RefCountedThreadSafe<MyObject>;
-
-      ~MyObject();
-      Thread* thread_;
-    };
-
-If you have external synchronization structures that can completely ensure that
-an object will always be alive while the task is waiting to execute, you can
-wrap the object pointer with `base::Unretained()` when calling `base::Bind()` to
-disable the refcounting.  This will also allow using `base::Bind()` on classes
-that are not refcounted.  Be careful when doing this!
-
-
-
-### How arguments are handled by `base::Bind()`
-<a id="how_arguments_are_handled"></a>
-
-The arguments given to `base::Bind()` are copied into an internal
-`InvokerStorage` structure object (defined in
-[`base/bind_internal.h`](http://cs.chromium.org/chromium/src/base/bind_internal.h).
-When the function is finally executed, it will see copies of the arguments.  This is important if your target function or method takes a const reference; the
-reference will be to a copy of the argument.  If you need a reference to the
-original argument, you can wrap the argument with `base::ConstRef()`.  Use this
-carefully as it is likely dangerous if target of the reference cannot be
-guaranteed to live past when the task is executed.  In particular, it is almost
-never safe to use `base::ConstRef()` to a variable on the stack unless you can
-guarantee the stack frame will not be invalidated until the asynchronous task
-finishes.
-
-Sometimes, you will want to pass reference-counted objects as parameters (be
-sure to use `RefCountedThreadSafe` and not plain `RefCounted` as the base class
-for these objects). To ensure that the object lives throughout the entire
-request, the Closure generated by `base::Bind` must keep a reference to it. This
-can be done by passing scoped_refptr as the parameter type, or by wrapping the
-raw pointer with `make_scoped_refptr()`:
-
-    class SomeParamObject : public base::RefCountedThreadSafe<SomeParamObject> {
-     ...
-    };
-
-    class MyObject : public base::RefCountedThreadSafe<MyObject> {
-     public:
-      void DoSomething() {
-        scoped_refptr<SomeParamObject> param(new SomeParamObject);
-
-        // Without RetainedRef, the scoped_refptr would try to implicitly
-        // convert to a raw pointer and fail compilation.
-        thread_->message_loop()->PostTask(FROM_HERE,
-           base::Bind(&MyObject::DoSomethingOnAnotherThread, this,
-           base::RetainedRef(param)));
-      }
-
-      void DoSomething2() {
-        SomeParamObject* param = new SomeParamObject;
-        thread_->message_loop()->PostTask(FROM_HERE,
-           base::Bind(&MyObject::DoSomethingOnAnotherThread, this,
-                             base::RetainedRef(make_scoped_refptr(param))));
-      }
-
-      void DoSomething3() {
-        scoped_refptr<SomeParamObject> param(new SomeParamObject);
-
-        // Moving |param| prevents an extra AddRef()/Release() pair.
-        thread_->message_loop()->PostTask(FROM_HERE,
-           base::Bind(&MyObject::DoSomethingOnAnotherThread, this,
-           base::RetainedRef(std::move(param))));
-      }
-
-      // Note how this takes a raw pointer. The important part is that
-      // base::Bind() was passed a scoped_refptr; using a scoped_refptr
-      // here would result in an extra AddRef()/Release() pair.
-      void DoSomethingOnAnotherThread(SomeParamObject* param) {
-        ...
-      }
-    };
-
-If you want to pass the object without taking a reference on it, wrap the
-argument with `base::Unretained()`. Again, using this means there are external
-guarantees on the lifetime of the object, so tread carefully!
-
-If your object has a non-trivial destructor that needs to run on a specific
-thread, you can use the following trait. This is needed since timing races could
-lead to a task completing execution before the code that posted it has unwound
-the stack.
-
-    class MyObject : public base::RefCountedThreadSafe<MyObject, BrowserThread::DeleteOnIOThread> {
-
-## Callback cancellation
-
-There are 2 major reasons to cancel a task (in the form of a Callback):
-*  You want to do something later on your object, but at the time your callback
-   runs, your object may have been destroyed.
-*  When input changes (e.g. user input), old tasks become unnecessary. For
-   performance considerations, you should cancel them.
-See following about different approaches for cancellation.
-
-### Important notes about cancellation
-
-It's dangerous to cancel a task with owned parameters. See the following
-example. (The example uses `base::WeakPtr` for cancellation, but the problem
-applies to all approaches).
-
-    class MyClass {
-     public:
-      // Owns |p|.
-      void DoSomething(AnotherClass* p) {
-        ...
-      }
-      WeakPtr<MyClass> AsWeakPtr() {
-        return weak_factory_.GetWeakPtr();
-      }
-     private:
-      base::WeakPtrFactory<MyClass> weak_factory_;
-    };
-
-    ...
-    Closure cancelable_closure = Bind(&MyClass::DoSomething, object->AsWeakPtr(), p);
-    Callback<void(AnotherClass*)> cancelable_callback = Bind(&MyClass::DoSomething, object->AsWeakPtr());
-    ...
-
-    void FunctionRunLater(const Closure& cancelable_closure,
-                          const Callback<void(AnotherClass*)>& cancelable_callback) {
-      ...
-      // Leak memory!
-      cancelable_closure.Run();
-      cancelable_callback.Run(p);
-    }
-
-In `FunctionRunLater`, both `Run()` calls will leak `p` when object is already
-destructed. Using `scoped_ptr` can fix the bug:
-
-    class MyClass {
-     public:
-      void DoSomething(scoped_ptr<AnotherClass> p) {
-        ...
-      }
-      ...
-    };
-
-### base::WeakPtr and Cancellation __[NOT THREAD SAFE]__
-
-You can use a `base::WeakPtr` and `base::WeakPtrFactory`
-(in
-[base/memory/weak_ptr.h](https://cs.chromium.org/chromium/src/base/memory/weak_ptr.h))
-to ensure that any invokes can not outlive the object they are being invoked on,
-without using reference counting. The `base::Bind` mechanism has special
-understanding for `base::WeakPtr` that will disable the task's execution if the
-`base::WeakPtr` has been invalidated. The `base::WeakPtrFactory` object can be
-used to generate `base::WeakPtr` instances that know about the factory
-object. When the factory is destroyed, all the `base::WeakPtr` will have their
-internal "invalidated" flag set, which will make any tasks bound to them to not
-dispatch. By putting the factory as a member of the object being dispatched to,
-you can get automatic cancellation.
-
-__NOTE__: This only works when the task is posted to the same thread. Currently
-there is not a general solution that works for tasks posted to other
-threads. See
-the [next section about CancelableTaskTracker](#cancelable_task_tracker) for an
-alternative solution.
-
-    class MyObject {
-     public:
-      MyObject() : weak_factory_(this) {}
-
-      void DoSomething() {
-        const int kDelayMS = 100;
-        MessageLoop::current()->PostDelayedTask(FROM_HERE,
-            base::Bind(&MyObject::DoSomethingLater, weak_factory_.GetWeakPtr()),
-            kDelayMS);
-      }
-
-      void DoSomethingLater() {
-        ...
-      }
-
-     private:
-      base::WeakPtrFactory<MyObject> weak_factory_;
-    };
-
-### CancelableTaskTracker
-<a id="cancelable_task_tracker"></a>
-
-While `base::WeakPtr` is very helpful to cancel a task, it is not thread safe so
-can not be used to cancel tasks running on another thread. This is sometimes a
-performance critical requirement. E.g. We need to cancel database lookup task on
-DB thread when user changes inputed text. In this kind of situation
-`CancelableTaskTracker` is appropriate.
-
-With `CancelableTaskTracker` you can cancel a single task with returned
-`TaskId`. This is another reason to use `CancelableTaskTracker` instead of
-`base::WeakPtr`, even in a single thread context.
-
-`CancelableTaskTracker` has 2 `Post` methods doing the same thing as the ones in
-`base::TaskRunner`, with additional cancellation support.
-
-    class UserInputHandler : public base::RefCountedThreadSafe<UserInputHandler> {
-      // Runs on UI thread.
-      void OnUserInput(Input input) {
-        CancelPreviousTask();
-        DBResult* result = new DBResult();
-        task_id_ = tracker_->PostTaskAndReply(
-            BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB).get(),
-            FROM_HERE,
-            base::Bind(&LookupHistoryOnDBThread, this, input, result),
-            base::Bind(&ShowHistoryOnUIThread, this, base::Owned(result)));
-      }
-
-      void CancelPreviousTask() {
-        tracker_->TryCancel(task_id_);
-      }
-
-      ...
-
-     private:
-      CancelableTaskTracker tracker_;  // Cancels all pending tasks while destruction.
-      CancelableTaskTracker::TaskId task_id_;
-      ...
-    };
-
-Since task runs on other threads, there's no guarantee it can be successfully
-canceled.
-
-When `TryCancel()` is called:
-
-*  If neither task nor reply has started running, both will be canceled.
-*  If task is already running or has finished running, reply will be canceled.
-*  If reply is running or has finished running, cancelation is a noop.
-
-Like `base::WeakPtrFactory`, `CancelableTaskTracker` will cancel all tasks on
-destruction.
-
-### Cancelable request __(DEPRECATED)__
-
-Note. Cancelable request is deprecated. Please do not use it in new code. For
-canceling tasks running on the same thread, use WeakPtr. For canceling tasks
-running on a different thread, use `CancelableTaskTracker`.
-
-A cancelable request makes it easier to make requests to another thread with
-that thread returning some data to you asynchronously. Like the revokable store
-system, it uses objects that track whether the originating object is alive. When
-the calling object is deleted, the request will be canceled to prevent invalid
-callbacks.
-
-Like the revokable store system, a user of a cancelable request has
-an object (here, called a _Consumer_) that tracks whether it is alive and will
-auto-cancel any outstanding requests on deleting.
-
-    class MyClass {
-      void MakeRequest() {
-        frontend_service->StartRequest(some_input1, some_input2, this,
-            // Use base::Unretained(this) if this may cause a refcount cycle.
-            base::Bind(&MyClass:RequestComplete, this));
-      }
-      void RequestComplete(int status) {
-        ...
-      }
-
-     private:
-      CancelableRequestConsumer consumer_;
-    };
-
-Note that the `MyClass::RequestComplete`, is bounded with
-`base::Unretained(this)` here.
-
-The consumer also allows you to associate extra data with a request. Use
-`CancelableRequestConsumer` which will allow you to associate arbitrary data
-with the handle returned by the provider service when you invoke the
-request. The data will be automatically destroyed when the request is canceled.
-
-A service handling requests inherits from `CancelableRequestProvider`. This
-object provides methods for canceling in-flight requests, and will work with the
-consumers to make sure everything is cleaned up properly on cancel. This
-frontend service just tracks the request and sends it to a backend service on
-another thread for actual processing. It would look like this:
-
-    class FrontendService : public CancelableRequestProvider {
-      typedef base::Callback<void(int)> RequestCallbackType;
-
-      Handle StartRequest(int some_input1, int some_input2,
-          CallbackConsumer* consumer,
-          const RequestCallbackType& callback) {
-        scoped_refptr<CancelableRequest<FrontendService::RequestCallbackType>>
-            request(new CancelableRequest(callback));
-        AddRequest(request, consumer);
-
-        // Send the parameters and the request to the backend thread.
-        backend_thread_->PostTask(FROM_HERE,
-            base::Bind(&BackendService::DoRequest, backend_, request,
-                       some_input1, some_input2), 0);
-        // The handle will have been set by AddRequest.
-        return request->handle();
-      }
-    };
-
-The backend service runs on another thread. It does processing and forwards the
-result back to the original caller. It would look like this:
-
-    class BackendService : public base::RefCountedThreadSafe<BackendService> {
-      void DoRequest(
-          scoped_refptr<CancelableRequest<FrontendService::RequestCallbackType>>
-              request,
-          int some_input1, int some_input2) {
-        if (request->canceled())
-          return;
-
-        ... do your processing ...
-
-        // Execute ForwardResult() like you would do Run() on the base::Callback<>.
-        request->ForwardResult(return_value);
-      }
-    };
+Superseded by new [`Threading and Tasks in Chrome`](../threading_and_tasks.md)
+and [`Callback<> and Bind()`](../callback.md) documentation.
diff --git a/docs/shift_based_development.md b/docs/shift_based_development.md
deleted file mode 100644
index 43b718e..0000000
--- a/docs/shift_based_development.md
+++ /dev/null
@@ -1,150 +0,0 @@
-# Introduction
-
-Kai Wang (kaiwang@) and I (Jói Sigurðsson, joi@) experimented with something we called “shift-based development” in the first half of Q1 2013 as a way to work closely together on a componentization that was hard to do in parallel.
-
-I work from Iceland, which is 7 or 8 hours ahead of Kai’s location (Mountain View, California), depending on the time of year (daylight savings time is not observed in Iceland). Kai and I were working on componentizing the Prefs subsystem of Chromium, and it was obvious early on that if we tried to develop in parallel, we would step on each others’ toes very regularly and be forced to do often difficult merges (due to e.g. moving files, renaming things, and so on). The idea came up, since my normal work day ends right around the time Kai’s starts, why not do the development in serial instead. Although I often work an extra hour or two later in the evening, that’s normally for responding to code review requests and email and not so much for coding, so this way we’d be completely free of getting in each others’ way.
-
-The way we implemented this was we set up a bare git repository, and at the end of the day, we would push whatever we were working on to this repository, and let the other know the status of things. This could be a single working branch, or (more often) a pipeline of branches, plus a branch representing the SVN revision we were based off of. To make this work, we were both using the unmanaged git workflow.
-
-The idea was, the next “shift” would pull down the pipeline of branches and continue where the last left off, doing development on the last change in the pipeline if it was incomplete, landing whatever changes could be landed, or starting a new change in the pipeline if all of them were ready for review or ready to land.
-
-One limitation we ran into was that only the owner of an issue in Rietveld could upload a new patch set. To work around this limitation, I added a feature to Rietveld where you can add a COLLABORATOR=xyz@chromium.org line to your change description, which will allow that person to also upload patches and edit the change description (see [Rietveld patch](https://code.google.com/p/rietveld/source/detail?r=a37a6b2495b43e5fdd38292602d933714b7e8ddd)).
-
-In my opinion this was moderately successful. We were probably less productive than we would have been if each of us had been working on completely unrelated things, but certainly more productive than if we had tried to work together on componentizing Prefs in parallel.
-
-With more practice, I think this way of working together could be quite successful. It was also challenging and fun and could be a worthwhile thing to try for folks separated by close to a full working day or more. See details below if you'd like to try.
-
-# Details
-
-The following instructions assume Linux is being used, but should be easily adaptable to other OSes. If you find mistakes in the instructions, please feel free to correct and clarify this Wiki page.
-
-## Setup
-
-On one of our Linux boxes, we set up a bare git repository using [these instructions](http://git-scm.com/book/en/Git-on-the-Server-Setting-Up-the-Server). I'm sure you could also use an existing git service such as github.
-
-Let's assume the IP address of the Linux box hosting the bare
-repository is `12.34.56.78`, the Linux user that provides access to the
-bare repository is named `gitshift`, and the repository is located at
-`/home/gitshift/git/gitshift.git`.
-
-Each developer participating in the shift-based development provides
-their RSA public key (e.g. `~/.ssh/id_rsa.pub`) and we add it to
-`/home/gitshift/.ssh/authorized_keys`; that way as long as you are using
-`ssh-agent` and have run `ssh-add`, you won't need to type in a password
-every time you issue a git command that affects the repository.
-
-Issue these commands to add a remote for this repository to your local
-git repo, and to mirror its initial state.  You can call it whatever
-you like, shiftrepo is just an example.
-
-```
-$ git remote add shiftrepo gitshift@12.34.56.78:/home/gitshift/git/gitshift.git
-$ git fetch shiftrepo
-```
-
-You should now be able to do a `git push` of some dummy branch; try
-it out, e.g.
-
-```
-$ git checkout -b shifttest master
-$ echo boo > boo.txt
-$ git add boo.txt && git commit -m .
-$ git push shiftrepo
-```
-
-The shared repository is just a place where you share your branches;
-it is not a place where you do actual work.  The actual work should be
-done in your separate local repositories, and you still use e.g. git
-pull (which in our git svn repositories behind the scenes does a `git svn fetch` etc.
-
-Shift-based collaboration won't work well (at least not with a
-pipeline of branches) unless you are using an "unmanaged" git checkout.
-
-## Example Working Rules
-
-For the branches we collaborated on, we set up some working
-rules. This may be a good starting set and it worked well enough for us,
-but others could adapt these rules:
-
-a) We had a naming convention for pipelines of branches. E.g. you
-might have branches named shiftrepo/p0-movemore and shiftrepo/p1-sync,
-where p1-sync's upstream branch is p0-movemore, and p0-movemore's
-upstream branch is an SVN revision, generally a version that was LKGR
-at some point.  You can find the git commit hash of this revision by
-running
-
-```
-git log -1 --grep=src@ | head -1 | cut -d " " -f2
-```
-
-and the SVN revision number by running
-
-```
-git log -1 --grep=src@ | grep git-svn-id | cut -d@ -f2 | cut -d " "  -f1
-```
-
-We followed a naming convention of one or two alphabetic characters
-followed by the sequence number of the branch, followed by a dash and
-a descriptive name for what's going on in that particular branch. The
-one or two alphabetic characters indicated the rough over-arching
-topic (e.g. p for Prefs), and the stuff after the dash can be more
-descriptive.
-
-b) When pushing a pipeline of branches to shiftrepo (where branch A
-depends on branch B and so forth) we made sure to first git pull in
-each dependent branch in sequence, so that each branch in shiftrepo is
-building straight on top of the previous branch.
-
-c) At the end of our shift, we did `git push shiftrepo branchname`
-for each branch.
-
-d) At the start of our shift, we did `git fetch shiftrepo` and then
-for each branch we were collaborating on we did `git checkout branchname && git merge shiftrepo/branchname`. Note that the first command checks
-out the local branch, and the second merges the shiftrepo/ branch into
-it. This does not make the shiftrepo/ branch a parent of the local
-branch.
-
-e) Also at the start of each shift, we updated the local upstream
-branches for each branch to match the upstream relationships that the
-person ending their shift had on their end.  One case is if the oldest
-branch in the pipeline has been merged to a new LKGR, then we did this:
-
-```
-git branch --set-upstream oldestBranchName `git log -1 --grep=src@ oldestBranchName | head -1 | cut -d " " -f2`
-```
-
-and the other case is if new branches were created during the last shift, e.g. p4-foo was added, then for each we need to do like this:
-
-```
-git branch --set-upstream p4-foo p3-bar
-```
-
-f) For managing old branches, we removed the oldest branch in a
-pipeline when several conditions were met:
-
-> i) The old branch has been checked in.
-
-> ii) The SVN revision of the check-in of the old branch is equal to
-> or older than LKGR, i.e. that change in SVN is included when you
-> sync to LKGR.
-
-> iii) That LKGR has been merged into the old branch, and we've done
-> `git pull` in the next branch after it.
-
-g) We only used the CQ to commit stuff. The fear was (and this hasn't
-really been validated as true or false) that there might be some
-gotchas if we used `git cl dcommit` instead.
-
-h) At the end of our shift, we communicated by email/IM/Hangout to let
-the other know the status of the work, next steps remaining for any
-currently-open branches, and to discuss what might make sense for the
-next branches to work on.
-
-## Random Commands
-
-To push a local branch to shiftrepo: `git push shiftrepo localbranchname`
-
-To push all "matching" branches (i.e. push the latest copy of
-any local branch that has previously been pushed to shiftrepo): `git push shiftrepo`
-
-To delete a branch from shiftrepo, it's weird: `git push shiftrepo :branchname`
diff --git a/docs/testing/layout_tests.md b/docs/testing/layout_tests.md
index 9040fb3ad..79d8b21 100644
--- a/docs/testing/layout_tests.md
+++ b/docs/testing/layout_tests.md
@@ -538,9 +538,6 @@
 [bugs with the component Blink>Infra](https://bugs.chromium.org/p/chromium/issues/list?can=2&q=component%3ABlink%3EInfra)
 for issues related to Blink tools, include the layout test runner.
 
-* Windows and Linux: Do not copy and paste while the layout tests are running,
-  as it may interfere with the editing/pasteboard and other clipboard-related
-  tests. (Mac tests swizzle NSClipboard to avoid any conflicts).
 * If QuickTime is not installed, the plugin tests
   `fast/dom/object-embed-plugin-scripting.html` and
   `plugins/embed-attributes-setting.html` are expected to fail.
diff --git a/docs/use_find_bugs_for_android.md b/docs/use_find_bugs_for_android.md
index b9a3450..1245d92c 100644
--- a/docs/use_find_bugs_for_android.md
+++ b/docs/use_find_bugs_for_android.md
@@ -6,10 +6,6 @@
 
 ## How To Run
 
-For gyp builds, add `run_findbugs=1` to your `GYP_DEFINES`.
-
-For gn builds, add `run_findbugs=true` to the args you pass to `gn gen`:
-
     gn gen --args='target_os="android" run_findbugs=true'
 
 Note that running findbugs will add time to your build. The amount of additional
diff --git a/docs/working_remotely_with_android.md b/docs/working_remotely_with_android.md
index f804fc68..eb98447 100644
--- a/docs/working_remotely_with_android.md
+++ b/docs/working_remotely_with_android.md
@@ -5,12 +5,6 @@
 
 ## Introduction
 
-When you call /build/android/run_tests.py or
-/build/android/run_instrumentation_tests.py it assumes an Android device
-is attached to the local host.
-
-TODO: these scripts do not exist.
-
 If you want to work remotely from your laptop with an Android device attached to
 it, while keeping an ssh connection to a remote desktop machine where you have
 your build environment setup, you will have to use one of the two alternatives
@@ -18,19 +12,6 @@
 
 ## Option 1: SSHFS - Mounting the out/Debug directory
 
-### On your remote host machine
-
-You can open a regular ssh to your host.
-
-    # build it
-    desktop$ cd $SRC;
-    desktop$ . build/android/envsetup.sh
-    desktop$ build/gyp_chromium -DOS=android
-    desktop$ ninja -C out/Debug
-
-See also
-[Android Build Instructions](https://www.chromium.org/developers/how-tos/android-build-instructions).
-
 ### On your laptop
 
 You have to have an Android device attached to it.
@@ -52,13 +33,9 @@
 laptop$ adb devices
 laptop$ adb root
 
-# Install APK (which was previously built in the host machine).
-
-laptop$ python build/android/adb_install_apk.py --apk ContentShell.apk --apk_package org.chromium.content_shell
-
 # Run tests.
 
-laptop$ python build/android/run_instrumentation_tests.py -I --test-apk ContentShellTest -vvv
+laptop$ out/Default/bin/run_$YOUR_TEST
 ```
 
 *** note
@@ -76,14 +53,11 @@
 
 # Rebuild the needed binaries on your laptop.
 
-laptop$ build/gyp_chromium -DOS=android
 laptop$ ninja -C out/Debug md5sum host_forwarder
 ```
 
 ## Option 2: SSH Tunneling
 
-### Option 2a: Use a script
-
 Copy /tools/android/adb_remote_setup.sh to your laptop, then run it.
 adb_remote_setup.sh updates itself, so you only need to copy it once.
 
@@ -93,29 +67,8 @@
 laptop$ ./adb_remote_setup.sh <desktop_hostname> <path_to_adb_on_desktop>
 ```
 
-### Option 2b: Manual tunneling
-
-You have to make sure that ports 5037, 10000, ad 10201 are not being used on
-either your laptop or your desktop. Try the command: `netstat -nap | grep 10000`
-to see
-
-Kill the pids that are using those ports.
-
-#### On your host machine
+### On your host machine
 
 ```shell
-desktop$ killall adb
-desktop$ killall host_forwarder
-```
-
-#### On your laptop
-
-```shell
-laptop$ ssh -C -R 5037:localhost:5037 -R 10000:localhost:10000 -R 10201:localhost:10201 <desktop_host_name>
-```
-
-#### On your host machine
-
-```shell
-desktop$ python build/android/run_instrumentation_tests.py -I --test-apk ContentShellTest -vvv
+desktop$ out/Default/bin/run_$YOUR_TEST
 ```
diff --git a/extensions/shell/BUILD.gn b/extensions/shell/BUILD.gn
index 743cd31..08feb083 100644
--- a/extensions/shell/BUILD.gn
+++ b/extensions/shell/BUILD.gn
@@ -433,7 +433,7 @@
       "$root_out_dir/App Shell Helper.app",
     ]
     outputs = [
-      "{{bundle_root_dir}}/Frameworks/{{source_file_part}}",
+      "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}",
     ]
     public_deps = [
       ":app_shell_framework+link",
diff --git a/headless/lib/browser/devtools_api/devtools_connection.js b/headless/lib/browser/devtools_api/devtools_connection.js
index 07b9741a..461bd76 100644
--- a/headless/lib/browser/devtools_api/devtools_connection.js
+++ b/headless/lib/browser/devtools_api/devtools_connection.js
@@ -21,11 +21,17 @@
   /**
    * @param {!Object} transport The API providing transport for devtools
    *     commands.
+   * @param {function(string, Object=): undefined =} opt_validator An
+   *     optional function which performs checks before sending any DevTools
+   *     messages.  It should throw an error if validation fails.
    */
-  constructor(transport) {
+  constructor(transport, opt_validator) {
     /** @private {!Object} */
     this.transport_ = transport;
 
+    /** @private {function(string, Object=): undefined|undefined} */
+    this.validator_ = opt_validator;
+
     /** @private {number} */
     this.commandId_ = 1;
 
@@ -148,6 +154,9 @@
     // OnProtocolMessage easily distinguish between C++ and JS generated
     // commands and route the response accordingly.
     this.commandId_ += 2;
+    if (this.validator_) {
+      this.validator_(method, params);
+    }
     // Note the names are in quotes to prevent closure compiler name mangling.
     this.transport_.send(
         JSON.stringify({'method': method, 'id': id, 'params': params}));
diff --git a/headless/lib/browser/headless_browser_context_impl.cc b/headless/lib/browser/headless_browser_context_impl.cc
index a4a71bf4..bf7e1d1 100644
--- a/headless/lib/browser/headless_browser_context_impl.cc
+++ b/headless/lib/browser/headless_browser_context_impl.cc
@@ -252,6 +252,11 @@
   return permission_manager_.get();
 }
 
+content::BackgroundFetchDelegate*
+HeadlessBrowserContextImpl::GetBackgroundFetchDelegate() {
+  return nullptr;
+}
+
 content::BackgroundSyncController*
 HeadlessBrowserContextImpl::GetBackgroundSyncController() {
   return nullptr;
diff --git a/headless/lib/browser/headless_browser_context_impl.h b/headless/lib/browser/headless_browser_context_impl.h
index 11e800a..2c25fe7c 100644
--- a/headless/lib/browser/headless_browser_context_impl.h
+++ b/headless/lib/browser/headless_browser_context_impl.h
@@ -65,6 +65,7 @@
   content::PushMessagingService* GetPushMessagingService() override;
   content::SSLHostStateDelegate* GetSSLHostStateDelegate() override;
   content::PermissionManager* GetPermissionManager() override;
+  content::BackgroundFetchDelegate* GetBackgroundFetchDelegate() override;
   content::BackgroundSyncController* GetBackgroundSyncController() override;
   content::BrowsingDataRemoverDelegate* GetBrowsingDataRemoverDelegate()
       override;
diff --git a/ios/build/tools/setup-gn.py b/ios/build/tools/setup-gn.py
index 27a2451..ec0d7785 100755
--- a/ios/build/tools/setup-gn.py
+++ b/ios/build/tools/setup-gn.py
@@ -95,8 +95,15 @@
     args.append(('enable_stripping', 'enable_dsyms'))
     args.append(('is_official_build', self._config == 'Official'))
     args.append(('is_chrome_branded', 'is_official_build'))
-    args.append(('use_xcode_clang', 'is_official_build'))
     args.append(('ios_enable_coverage', self._config == 'Coverage'))
+
+    # TODO(crbug.com/75794): the version of llvm-cov used for code coverage is
+    # tied to the version of clang used. As no copy of llvm-cov is shipped with
+    # the Chrome's clang, the version shipped with Xcode is used, thus code
+    # needs to be compiled with Xcode's clang when enabling code coverage.
+    # Remove this once llvm-cov is shipped with Chrome's clang.
+    args.append(('use_xcode_clang', 'is_official_build || ios_enable_coverage'))
+
     if os.environ.get('FORCE_MAC_TOOLCHAIN', '0') == '1':
       args.append(('use_system_xcode', False))
 
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index d7f1bb0..e50abd1 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -6,6 +6,7 @@
 import("//build/config/mac/base_rules.gni")
 import("//build/mac/tweak_info_plist.gni")
 import("//ios/build/chrome_build.gni")
+import("//ios/public/provider/chrome/browser/build_config.gni")
 
 source_set("app") {
   configs += [ "//build/config/compiler:enable_arc" ]
@@ -253,13 +254,18 @@
   entitlements_target = ":entitlements"
   info_plist_target = ":info_plist"
 
+  bundle_deps = [ "//ios/chrome/app/resources" ]
+  if (!is_chrome_branded || ios_chrome_app_variants == []) {
+    bundle_deps += [ ios_application_icons_target ]
+  } else {
+    variants = ios_chrome_app_variants
+  }
+
   deps = [
     ":main",
     ":tests_fake_hook",
   ]
 
-  bundle_deps = [ "//ios/chrome/app/resources" ]
-
   if (current_toolchain == default_toolchain) {
     if (ios_enable_search_widget_extension) {
       deps += [ ":search_widget_extension_bundle" ]
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 4cda094..3863f75 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -113,6 +113,8 @@
 #import "ios/chrome/browser/ui/commands/show_signin_command.h"
 #import "ios/chrome/browser/ui/commands/start_voice_search_command.h"
 #import "ios/chrome/browser/ui/downloads/download_manager_controller.h"
+#import "ios/chrome/browser/ui/external_file_remover_factory.h"
+#import "ios/chrome/browser/ui/external_file_remover_impl.h"
 #import "ios/chrome/browser/ui/first_run/first_run_util.h"
 #import "ios/chrome/browser/ui/first_run/welcome_to_chrome_view_controller.h"
 #import "ios/chrome/browser/ui/fullscreen_controller.h"
@@ -225,10 +227,12 @@
 // can be set to |NONE| when not in use.
 enum class StackViewDismissalMode { NONE, NORMAL, INCOGNITO };
 
+// The delay, in seconds, for cleaning external files.
+const int kExternalFilesCleanupDelaySeconds = 60;
+
 }  // namespace
 
 @interface MainController ()<BrowserStateStorageSwitching,
-                             BrowsingDataRemovalControllerDelegate,
                              PrefObserverDelegate,
                              SettingsNavigationControllerDelegate,
                              TabModelObserver,
@@ -819,7 +823,7 @@
 - (BrowsingDataRemovalController*)browsingDataRemovalController {
   if (!_browsingDataRemovalController) {
     _browsingDataRemovalController =
-        [[BrowsingDataRemovalController alloc] initWithDelegate:self];
+        [[BrowsingDataRemovalController alloc] init];
   }
   return _browsingDataRemovalController;
 }
@@ -920,22 +924,6 @@
   [_browserViewWrangler cleanDeviceSharingManager];
 }
 
-#pragma mark - BrowsingDataRemovalControllerDelegate methods
-
-- (void)removeExternalFilesForBrowserState:
-            (ios::ChromeBrowserState*)browserState
-                         completionHandler:(ProceduralBlock)completionHandler {
-  // TODO(crbug.com/648940): Move this logic from BVC into
-  // BrowsingDataRemovalController thereby eliminating the need for
-  // BrowsingDataRemovalControllerDelegate. .
-  if (_mainBrowserState == browserState) {
-    [self.mainBVC removeExternalFilesImmediately:YES
-                               completionHandler:completionHandler];
-  } else if (completionHandler) {
-    dispatch_async(dispatch_get_main_queue(), completionHandler);
-  }
-}
-
 #pragma mark - Startup tasks
 
 - (void)sendQueuedFeedback {
@@ -1133,9 +1121,14 @@
 }
 
 - (void)scheduleTasksRequiringBVCWithBrowserState {
-  if (GetApplicationContext()->WasLastShutdownClean())
-    [self.mainBVC removeExternalFilesImmediately:NO completionHandler:nil];
-
+  if (GetApplicationContext()->WasLastShutdownClean()) {
+    // Delay the cleanup of the unreferenced files to not impact startup
+    // performance.
+    ExternalFileRemoverFactory::GetForBrowserState(_mainBrowserState)
+        ->RemoveAfterDelay(
+            base::TimeDelta::FromSeconds(kExternalFilesCleanupDelaySeconds),
+            base::OnceClosure());
+  }
   [self scheduleShowPromo];
 }
 
diff --git a/ios/chrome/app/resources/BUILD.gn b/ios/chrome/app/resources/BUILD.gn
index aef0092..f270ee36 100644
--- a/ios/chrome/app/resources/BUILD.gn
+++ b/ios/chrome/app/resources/BUILD.gn
@@ -16,7 +16,6 @@
     ":launchscreen_xib",
     ":quick_action_icons",
     ":system_strings",
-    ios_application_icons_target,
     ios_packed_resources_target,
   ]
 }
@@ -73,21 +72,19 @@
   copy_data_to_bundle = true
 }
 
-bundle_data("chromium_icons") {
+appiconset("chromium_icons") {
   sources = [
-    "chromium/Icon-120.png",
-    "chromium/Icon-152.png",
-    "chromium/Icon-167.png",
-    "chromium/Icon-180.png",
-    "chromium/Icon-29.png",
-    "chromium/Icon-40.png",
-    "chromium/Icon-58.png",
-    "chromium/Icon-76.png",
-    "chromium/Icon-80.png",
-    "chromium/Icon-87.png",
-  ]
-  outputs = [
-    "{{bundle_resources_dir}}/{{source_file_part}}",
+    "chromium/AppIcon.appiconset/Contents.json",
+    "chromium/AppIcon.appiconset/Icon-120.png",
+    "chromium/AppIcon.appiconset/Icon-152.png",
+    "chromium/AppIcon.appiconset/Icon-167.png",
+    "chromium/AppIcon.appiconset/Icon-180.png",
+    "chromium/AppIcon.appiconset/Icon-29.png",
+    "chromium/AppIcon.appiconset/Icon-40.png",
+    "chromium/AppIcon.appiconset/Icon-58.png",
+    "chromium/AppIcon.appiconset/Icon-76.png",
+    "chromium/AppIcon.appiconset/Icon-80.png",
+    "chromium/AppIcon.appiconset/Icon-87.png",
   ]
 }
 
diff --git a/ios/chrome/app/resources/Info.plist b/ios/chrome/app/resources/Info.plist
index e044db2d..45f3512 100644
--- a/ios/chrome/app/resources/Info.plist
+++ b/ios/chrome/app/resources/Info.plist
@@ -29,16 +29,17 @@
 		<dict>
 			<key>CFBundleIconFiles</key>
 			<array>
-				<string>Icon-120.png</string>
-				<string>Icon-152.png</string>
-				<string>Icon-167.png</string>
-				<string>Icon-180.png</string>
-				<string>Icon-29.png</string>
-				<string>Icon-40.png</string>
-				<string>Icon-58.png</string>
-				<string>Icon-76.png</string>
-				<string>Icon-80.png</string>
-				<string>Icon-87.png</string>
+			</array>
+			<key>UIPrerenderedIcon</key>
+			<true/>
+		</dict>
+	</dict>
+	<key>CFBundleIcons~ipad</key>
+	<dict>
+		<key>CFBundlePrimaryIcon</key>
+		<dict>
+			<key>CFBundleIconFiles</key>
+			<array>
 			</array>
 			<key>UIPrerenderedIcon</key>
 			<true/>
diff --git a/ios/chrome/app/resources/chromium/AppIcon.appiconset/Contents.json b/ios/chrome/app/resources/chromium/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..77b86e8
--- /dev/null
+++ b/ios/chrome/app/resources/chromium/AppIcon.appiconset/Contents.json
@@ -0,0 +1,111 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "size" : "20x20",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "20x20",
+      "scale" : "3x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "iphone",
+      "filename" : "Icon-58.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "iphone",
+      "filename" : "Icon-87.png",
+      "scale" : "3x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "iphone",
+      "filename" : "Icon-80.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "iphone",
+      "filename" : "Icon-120.png",
+      "scale" : "3x"
+    },
+    {
+      "size" : "60x60",
+      "idiom" : "iphone",
+      "filename" : "Icon-120.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "60x60",
+      "idiom" : "iphone",
+      "filename" : "Icon-180.png",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "20x20",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "20x20",
+      "scale" : "2x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "ipad",
+      "filename" : "Icon-29.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "ipad",
+      "filename" : "Icon-58.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "ipad",
+      "filename" : "Icon-40.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "ipad",
+      "filename" : "Icon-80.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "76x76",
+      "idiom" : "ipad",
+      "filename" : "Icon-76.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "76x76",
+      "idiom" : "ipad",
+      "filename" : "Icon-152.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "83.5x83.5",
+      "idiom" : "ipad",
+      "filename" : "Icon-167.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ios-marketing",
+      "size" : "1024x1024",
+      "scale" : "1x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
diff --git a/ios/chrome/app/resources/chromium/Icon-120.png b/ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-120.png
similarity index 100%
rename from ios/chrome/app/resources/chromium/Icon-120.png
rename to ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-120.png
Binary files differ
diff --git a/ios/chrome/app/resources/chromium/Icon-152.png b/ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-152.png
similarity index 100%
rename from ios/chrome/app/resources/chromium/Icon-152.png
rename to ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-152.png
Binary files differ
diff --git a/ios/chrome/app/resources/chromium/Icon-167.png b/ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-167.png
similarity index 100%
rename from ios/chrome/app/resources/chromium/Icon-167.png
rename to ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-167.png
Binary files differ
diff --git a/ios/chrome/app/resources/chromium/Icon-180.png b/ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-180.png
similarity index 100%
rename from ios/chrome/app/resources/chromium/Icon-180.png
rename to ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-180.png
Binary files differ
diff --git a/ios/chrome/app/resources/chromium/Icon-29.png b/ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-29.png
similarity index 100%
rename from ios/chrome/app/resources/chromium/Icon-29.png
rename to ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-29.png
Binary files differ
diff --git a/ios/chrome/app/resources/chromium/Icon-40.png b/ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-40.png
similarity index 100%
rename from ios/chrome/app/resources/chromium/Icon-40.png
rename to ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-40.png
Binary files differ
diff --git a/ios/chrome/app/resources/chromium/Icon-58.png b/ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-58.png
similarity index 100%
rename from ios/chrome/app/resources/chromium/Icon-58.png
rename to ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-58.png
Binary files differ
diff --git a/ios/chrome/app/resources/chromium/Icon-76.png b/ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-76.png
similarity index 100%
rename from ios/chrome/app/resources/chromium/Icon-76.png
rename to ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-76.png
Binary files differ
diff --git a/ios/chrome/app/resources/chromium/Icon-80.png b/ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-80.png
similarity index 100%
rename from ios/chrome/app/resources/chromium/Icon-80.png
rename to ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-80.png
Binary files differ
diff --git a/ios/chrome/app/resources/chromium/Icon-87.png b/ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-87.png
similarity index 100%
rename from ios/chrome/app/resources/chromium/Icon-87.png
rename to ios/chrome/app/resources/chromium/AppIcon.appiconset/Icon-87.png
Binary files differ
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 2bb2281..17a54c58 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -183,6 +183,9 @@
       <message name="IDS_IOS_APPEX_PASSWORD_FORM_FILLED_SUCCESS" desc="Message displayed when an App Extension has successfully filled a sign-in form with username and password. [Length: 30em] [iOS only]">
         Forms filled.
       </message>
+      <message name="IDS_IOS_SHOW_ALL_PASSWORDS" desc="Button title in the keyboard accessory bar to open the list of all saved passwords. [Length: 20em] [iOS only]">
+        Show All...
+      </message>
       <message name="IDS_IOS_APPLICATION_SHORTCUT_NEWINCOGNITOTAB_TITLE" desc="Message when opening a New Incognito Tab from springboard force touch static shortcuts. [Length: unlimited] [iOS only]." meaning="3D Touch entry to create a new Incognito tab.">
         New Incognito Tab
       </message>
diff --git a/ios/chrome/browser/browser_state/BUILD.gn b/ios/chrome/browser/browser_state/BUILD.gn
index a78e474..b83c6c3b 100644
--- a/ios/chrome/browser/browser_state/BUILD.gn
+++ b/ios/chrome/browser/browser_state/BUILD.gn
@@ -72,6 +72,7 @@
     "//components/proxy_config/ios",
     "//components/signin/core/browser",
     "//components/signin/core/common",
+    "//components/signin/ios/browser:active_state_manager",
     "//components/sync",
     "//components/sync_preferences",
     "//components/user_prefs",
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_manager_impl.cc b/ios/chrome/browser/browser_state/chrome_browser_state_manager_impl.cc
index fa47a66..2e86e5f 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state_manager_impl.cc
+++ b/ios/chrome/browser/browser_state/chrome_browser_state_manager_impl.cc
@@ -22,6 +22,7 @@
 #include "components/signin/core/browser/account_tracker_service.h"
 #include "components/signin/core/browser/gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/signin_manager.h"
+#include "components/signin/ios/browser/active_state_manager.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/browser_state_info_cache.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state_impl.h"
@@ -39,7 +40,6 @@
 #include "ios/chrome/browser/signin/gaia_cookie_manager_service_factory.h"
 #include "ios/chrome/browser/signin/signin_manager_factory.h"
 #include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h"
-#include "ios/web/public/active_state_manager.h"
 
 namespace {
 
@@ -114,16 +114,15 @@
 ChromeBrowserStateManagerImpl::~ChromeBrowserStateManagerImpl() {
   for (const auto& pair : browser_states_) {
     ChromeBrowserStateImpl* browser_state = pair.second.get();
-    web::BrowserState::GetActiveStateManager(browser_state)->SetActive(false);
+    ActiveStateManager::FromBrowserState(browser_state)->SetActive(false);
     if (!browser_state->HasOffTheRecordChromeBrowserState())
       continue;
 
     web::BrowserState* otr_browser_state =
         browser_state->GetOffTheRecordChromeBrowserState();
-    if (!web::BrowserState::HasActiveStateManager(otr_browser_state))
+    if (!ActiveStateManager::ExistsForBrowserState(otr_browser_state))
       continue;
-    web::BrowserState::GetActiveStateManager(otr_browser_state)
-        ->SetActive(false);
+    ActiveStateManager::FromBrowserState(otr_browser_state)->SetActive(false);
   }
 }
 
diff --git a/ios/chrome/browser/browsing_data/browsing_data_removal_controller.h b/ios/chrome/browser/browsing_data/browsing_data_removal_controller.h
index fec87263..3e0259ec 100644
--- a/ios/chrome/browser/browsing_data/browsing_data_removal_controller.h
+++ b/ios/chrome/browser/browsing_data/browsing_data_removal_controller.h
@@ -16,28 +16,10 @@
 class ChromeBrowserState;
 }  // namespace ios
 
-// A protocol required by delegates of the BrowsingDataRemovalController.
-@protocol BrowsingDataRemovalControllerDelegate
-@required
-// Removes files received from other applications by |browserState|.
-// |completionHandler| is called when the files have been removed.
-- (void)removeExternalFilesForBrowserState:
-            (ios::ChromeBrowserState*)browserState
-                         completionHandler:(ProceduralBlock)completionHandler;
-// TODO(crbug.com/543213): Add a method here to inform the delegate when there
-// are no pending removal operations associated with a BrowserState.
-@end
-
 // This class is responsible for removing browsing data associated with a
 // ChromeBrowserState.
 @interface BrowsingDataRemovalController : NSObject
 
-- (instancetype)initWithDelegate:
-    (id<BrowsingDataRemovalControllerDelegate>)delegate
-    NS_DESIGNATED_INITIALIZER;
-
-- (instancetype)init NS_UNAVAILABLE;
-
 // Removes browsing data from |browserState| for datatypes in |mask|.
 // |mask| is obtained from IOSChromeBrowsingDataRemover::RemoveDataMask.
 // |browserState| cannot be null and must not be off the record.
diff --git a/ios/chrome/browser/browsing_data/browsing_data_removal_controller.mm b/ios/chrome/browser/browsing_data/browsing_data_removal_controller.mm
index ba09317..0f29f4dc 100644
--- a/ios/chrome/browser/browsing_data/browsing_data_removal_controller.mm
+++ b/ios/chrome/browser/browsing_data/browsing_data_removal_controller.mm
@@ -26,6 +26,8 @@
 #include "ios/chrome/browser/signin/account_consistency_service_factory.h"
 #import "ios/chrome/browser/snapshots/snapshots_util.h"
 #import "ios/chrome/browser/ui/browser_view_controller.h"
+#import "ios/chrome/browser/ui/external_file_remover.h"
+#import "ios/chrome/browser/ui/external_file_remover_factory.h"
 #include "ios/web/public/web_thread.h"
 #import "ios/web/public/web_view_creation_util.h"
 #import "ios/web/web_state/ui/wk_web_view_configuration_provider.h"
@@ -104,34 +106,30 @@
 // Called when a removal operation for |browserState| finishes.
 - (void)decrementPendingRemovalCountForBrowserState:
     (ios::ChromeBrowserState*)browserState;
+
+// Removes files received from other applications by |browserState|.
+// |completionHandler| is called when the files have been removed.
+- (void)removeExternalFilesForBrowserState:
+            (ios::ChromeBrowserState*)browserState
+                         completionHandler:(ProceduralBlock)completionHandler;
 @end
 
 @implementation BrowsingDataRemovalController {
   // Wrapper around IOSChromeBrowsingDataRemover that serializes removal
   // operations.
   std::unique_ptr<BrowsingDataRemoverHelper> _browsingDataRemoverHelper;
-  // The delegate.
-  __weak id<BrowsingDataRemovalControllerDelegate> _delegate;
   // A map that tracks the number of pending removals for a given
   // ChromeBrowserState.
   base::hash_map<ios::ChromeBrowserState*, int> _pendingRemovalCount;
 }
 
-- (instancetype)initWithDelegate:
-    (id<BrowsingDataRemovalControllerDelegate>)delegate {
+- (instancetype)init {
   if ((self = [super init])) {
-    DCHECK(delegate);
     _browsingDataRemoverHelper.reset(new BrowsingDataRemoverHelper());
-    _delegate = delegate;
   }
   return self;
 }
 
-- (instancetype)init {
-  NOTREACHED();
-  return nil;
-}
-
 - (void)removeBrowsingDataFromBrowserState:
             (ios::ChromeBrowserState*)browserState
                                       mask:(int)mask
@@ -175,10 +173,11 @@
   if (mask & IOSChromeBrowsingDataRemover::REMOVE_DOWNLOADS) {
     DCHECK_EQ(browsing_data::TimePeriod::ALL_TIME, timePeriod)
         << "Partial clearing not supported";
-    callbackCounter->IncrementCount();
-    [_delegate
-        removeExternalFilesForBrowserState:browserState
-                         completionHandler:decrementCallbackCounterCount];
+    if (!browserState->IsOffTheRecord()) {
+      callbackCounter->IncrementCount();
+      [self removeExternalFilesForBrowserState:browserState
+                             completionHandler:decrementCallbackCounterCount];
+    }
   }
 
   if (!browserState->IsOffTheRecord()) {
@@ -433,4 +432,16 @@
   _pendingRemovalCount.erase(browserState);
 }
 
+- (void)removeExternalFilesForBrowserState:
+            (ios::ChromeBrowserState*)browserState
+                         completionHandler:(ProceduralBlock)completionHandler {
+  DCHECK(!browserState->IsOffTheRecord());
+  base::OnceClosure callback;
+  if (completionHandler)
+    callback = base::BindBlockArc(completionHandler);
+
+  ExternalFileRemoverFactory::GetForBrowserState(browserState)
+      ->RemoveAfterDelay(base::TimeDelta::FromSeconds(0), std::move(callback));
+}
+
 @end
diff --git a/ios/chrome/browser/browsing_data/browsing_data_removal_controller_unittest.mm b/ios/chrome/browser/browsing_data/browsing_data_removal_controller_unittest.mm
index a96c084f..067676ca6 100644
--- a/ios/chrome/browser/browsing_data/browsing_data_removal_controller_unittest.mm
+++ b/ios/chrome/browser/browsing_data/browsing_data_removal_controller_unittest.mm
@@ -23,10 +23,8 @@
 // finish performing its operation even when a BrowserState is destroyed.
 TEST_F(BrowsingDataRemovalControllerTest, PerformAfterBrowserStateDestruction) {
   __block BOOL block_was_called = NO;
-  id mock_delegate = [OCMockObject
-      mockForProtocol:@protocol(BrowsingDataRemovalControllerDelegate)];
   BrowsingDataRemovalController* removal_controller =
-      [[BrowsingDataRemovalController alloc] initWithDelegate:mock_delegate];
+      [[BrowsingDataRemovalController alloc] init];
 
   TestChromeBrowserState::Builder builder;
   std::unique_ptr<TestChromeBrowserState> browser_state = builder.Build();
diff --git a/ios/chrome/browser/passwords/password_controller.mm b/ios/chrome/browser/passwords/password_controller.mm
index 3365d05..83654051 100644
--- a/ios/chrome/browser/passwords/password_controller.mm
+++ b/ios/chrome/browser/passwords/password_controller.mm
@@ -14,6 +14,7 @@
 
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
+#import "base/mac/bind_objc_block.h"
 #include "base/mac/foundation_util.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string16.h"
@@ -61,6 +62,10 @@
 namespace {
 // Types of password infobars to display.
 enum class PasswordInfoBarType { SAVE, UPDATE };
+
+// Script command prefix for form changes. Possible command to be sent from
+// injected JS is 'form.buttonClicked'.
+constexpr char kCommandPrefix[] = "passwordForm";
 }
 
 @interface PasswordController ()
@@ -131,6 +136,9 @@
 - (void)showInfoBarForForm:(std::unique_ptr<PasswordFormManager>)form
                infoBarType:(PasswordInfoBarType)type;
 
+// Handler for injected JavaScript callbacks.
+- (BOOL)handleScriptCommand:(const base::DictionaryValue&)JSONCommand;
+
 @end
 
 namespace {
@@ -326,6 +334,15 @@
       credentialManager_ = base::MakeUnique<CredentialManager>(
           passwordManagerClient_.get(), webState_);
     }
+
+    __weak PasswordController* weakSelf = self;
+    auto callback = base::BindBlockArc(^bool(const base::DictionaryValue& JSON,
+                                             const GURL& originURL,
+                                             bool userIsInteracting) {
+      // |originURL| and |isInteracting| aren't used.
+      return [weakSelf handleScriptCommand:JSON];
+    });
+    webState->AddScriptCommandCallback(callback, kCommandPrefix);
   }
   return self;
 }
@@ -352,6 +369,8 @@
 }
 
 - (void)detach {
+  if (webState_)
+    webState_->RemoveScriptCommandCallback(kCommandPrefix);
   webState_ = nullptr;
   webStateObserverBridge_.reset();
   passwordGenerationAgent_ = nil;
@@ -873,6 +892,29 @@
        completionHandler:completionHandler];
 }
 
+- (BOOL)handleScriptCommand:(const base::DictionaryValue&)JSONCommand {
+  std::string command;
+  if (!JSONCommand.GetString("command", &command))
+    return NO;
+
+  if (command != "passwordForm.submitButtonClick")
+    return NO;
+
+  GURL pageURL;
+  if (!GetPageURLAndCheckTrustLevel(webState_, &pageURL))
+    return NO;
+  autofill::PasswordForm form;
+  BOOL formParsedFromJSON =
+      [self getPasswordForm:&form fromDictionary:&JSONCommand pageURL:pageURL];
+  if (formParsedFromJSON && ![self isWebStateDestroyed]) {
+    self.passwordManager->OnPasswordFormSubmitted(self.passwordManagerDriver,
+                                                  form);
+    return YES;
+  }
+
+  return NO;
+}
+
 - (PasswordGenerationAgent*)passwordGenerationAgent {
   return passwordGenerationAgent_;
 }
diff --git a/ios/chrome/browser/passwords/password_controller_js_unittest.mm b/ios/chrome/browser/passwords/password_controller_js_unittest.mm
index 671bf85..5a09f1a 100644
--- a/ios/chrome/browser/passwords/password_controller_js_unittest.mm
+++ b/ios/chrome/browser/passwords/password_controller_js_unittest.mm
@@ -331,4 +331,60 @@
               ExecuteJavaScriptWithFormat(@"__gCrWeb.findPasswordForms()"));
 };
 
+// Checks that a touchend event from a button which contains in a password form
+// works as a submission indicator for this password form.
+TEST_F(PasswordControllerJsTest, TouchendAsSubmissionIndicator) {
+  LoadHtmlAndInject(
+      @"<html><body>"
+       "<form name='login_form' id='login_form'>"
+       "  Name: <input type='text' name='username'>"
+       "  Password: <input type='password' name='password'>"
+       "  <button id='submit_button' value='Submit'>"
+       "</form>"
+       "</body></html>");
+
+  // Call __gCrWeb.findPasswordForms in order to set an event handler on the
+  // button touchend event.
+  ExecuteJavaScriptWithFormat(@"__gCrWeb.findPasswordForms()");
+
+  // Replace __gCrWeb.message.invokeOnHost with mock method for checking of call
+  // arguments.
+  ExecuteJavaScriptWithFormat(
+      @"var invokeOnHostArgument = null;"
+       "var invokeOnHostCalls = 0;"
+       "__gCrWeb.message.invokeOnHost = function(command) {"
+       "  invokeOnHostArgument = command;"
+       "  invokeOnHostCalls++;"
+       "}");
+
+  // Simulate touchend event on the button.
+  ExecuteJavaScriptWithFormat(
+      @"document.getElementsByName('username')[0].value = 'user1';"
+       "document.getElementsByName('password')[0].value = 'password1';"
+       "var e = new UIEvent('touchend');"
+       "document.getElementsByTagName('button')[0].dispatchEvent(e);");
+
+  // Check that there was only 1 call for invokeOnHost.
+  EXPECT_NSEQ(@1, ExecuteJavaScriptWithFormat(@"invokeOnHostCalls"));
+
+  NSString* expected_command = [NSString
+      stringWithFormat:
+          @"{\"action\":\"%s\","
+           "\"method\":null,"
+           "\"name\":\"login_form\","
+           "\"origin\":\"%s\","
+           "\"fields\":[{\"element\":\"username\",\"type\":\"text\"},"
+           "{\"element\":\"password\",\"type\":\"password\"}],"
+           "\"usernameElement\":\"username\","
+           "\"usernameValue\":\"user1\","
+           "\"passwords\":[{\"element\":\"password\",\"value\":\"password1\"}],"
+           "\"command\":\"passwordForm.submitButtonClick\"}",
+          BaseUrl().c_str(), BaseUrl().c_str()];
+
+  // Check that invokeOnHost was called with the correct argument.
+  EXPECT_NSEQ(
+      expected_command,
+      ExecuteJavaScriptWithFormat(@"__gCrWeb.stringify(invokeOnHostArgument)"));
+};
+
 }  // namespace
diff --git a/ios/chrome/browser/passwords/password_controller_unittest.mm b/ios/chrome/browser/passwords/password_controller_unittest.mm
index 3675c0f..a529927 100644
--- a/ios/chrome/browser/passwords/password_controller_unittest.mm
+++ b/ios/chrome/browser/passwords/password_controller_unittest.mm
@@ -161,7 +161,7 @@
   void SetUp() override {
     web::WebTestWithWebState::SetUp();
     passwordController_ =
-        CreatePasswordController(web_state(), store_.get(), nullptr);
+        CreatePasswordController(web_state(), store_.get(), &weak_client_);
     @autoreleasepool {
       // Make sure the temporary array is released after SetUp finishes,
       // otherwise [passwordController_ suggestionProvider] will be retained
@@ -247,6 +247,8 @@
   PasswordController* passwordController_;
 
   scoped_refptr<password_manager::MockPasswordStore> store_;
+
+  MockPasswordManagerClient* weak_client_;
 };
 
 struct PasswordFormTestData {
@@ -1421,3 +1423,35 @@
     return *p_get_logins_called;
   });
 }
+
+// Tests that a touchend event from a button which contains in a password form
+// works as a submission indicator for this password form.
+TEST_F(PasswordControllerTest, TouchendAsSubmissionIndicator) {
+  LoadHtml(
+      @"<html><body>"
+       "<form name='login_form' id='login_form'>"
+       "  <input type='text' name='username'>"
+       "  <input type='password' name='password'>"
+       "  <button id='submit_button' value='Submit'>"
+       "</form>"
+       "</body></html>");
+  // Use a mock LogManager to detect that OnPasswordFormSubmitted has been
+  // called. TODO(crbug.com/598672): this is a hack, we should modularize the
+  // code better to allow proper unit-testing.
+  MockLogManager log_manager;
+  EXPECT_CALL(log_manager, IsLoggingActive()).WillRepeatedly(Return(true));
+  const char kExpectedMessage[] =
+      "Message: \"PasswordManager::ProvisionallySavePassword\"\n";
+  EXPECT_CALL(log_manager, LogSavePasswordProgress(kExpectedMessage));
+  EXPECT_CALL(log_manager,
+              LogSavePasswordProgress(testing::Ne(kExpectedMessage)))
+      .Times(testing::AnyNumber());
+  EXPECT_CALL(*weak_client_, GetLogManager())
+      .WillRepeatedly(Return(&log_manager));
+
+  ExecuteJavaScript(
+      @"document.getElementsByName('username')[0].value = 'user1';"
+       "document.getElementsByName('password')[0].value = 'password1';"
+       "var e = new UIEvent('touchend');"
+       "document.getElementsByTagName('button')[0].dispatchEvent(e);");
+}
diff --git a/ios/chrome/browser/passwords/resources/password_controller.js b/ios/chrome/browser/passwords/resources/password_controller.js
index e537dcc..3ba6e338 100644
--- a/ios/chrome/browser/passwords/resources/password_controller.js
+++ b/ios/chrome/browser/passwords/resources/password_controller.js
@@ -93,6 +93,35 @@
   };
 
   /**
+   * If |form| has no submit elements and exactly 1 button that button
+   * is assumed to be a submit button. This function adds onSubmitButtonClick_
+   * as a handler for touchend event of this button. Touchend event is used as
+   * a proxy for onclick event because onclick handling might be prevented by
+   * the site JavaScript.
+   */
+  var addSubmitButtonTouchEndHandler_ = function(form) {
+    if (form.querySelector('input[type=submit]'))
+      return;
+    var buttons = form.querySelectorAll('button');
+    if (buttons.length != 1)
+      return;
+    buttons[0].addEventListener('touchend', onSubmitButtonTouchEnd_);
+   };
+
+   /**
+    * Click handler for the submit button. It sends to the host
+    * form.submitButtonClick command.
+    */
+   var onSubmitButtonTouchEnd_ = function(evt) {
+       var form = evt.currentTarget.form;
+       var formData = __gCrWeb.getPasswordFormData(form);
+       if (!formData)
+         return;
+       formData["command"] = 'passwordForm.submitButtonClick';
+       __gCrWeb.message.invokeOnHost(formData);
+    };
+
+  /**
    * Returns the password form with the given |name| as a JSON string.
    * @param {string} name The name of the form to extract.
    * @return {string} The password form.
@@ -412,6 +441,7 @@
       var formData = __gCrWeb.getPasswordFormData(forms[i]);
       if (formData) {
         formDataList.push(formData);
+        addSubmitButtonTouchEndHandler_(forms[i]);
       }
     }
 
diff --git a/ios/chrome/browser/signin/BUILD.gn b/ios/chrome/browser/signin/BUILD.gn
index 6134059..9da95a54 100644
--- a/ios/chrome/browser/signin/BUILD.gn
+++ b/ios/chrome/browser/signin/BUILD.gn
@@ -59,6 +59,7 @@
     "//components/prefs",
     "//components/signin/core/browser",
     "//components/signin/ios/browser",
+    "//components/signin/ios/browser:active_state_manager",
     "//components/sync",
     "//google_apis",
     "//ios/chrome/browser",
diff --git a/ios/chrome/browser/signin/gaia_auth_fetcher_ios.mm b/ios/chrome/browser/signin/gaia_auth_fetcher_ios.mm
index ea6c0e1b..e0a309e 100644
--- a/ios/chrome/browser/signin/gaia_auth_fetcher_ios.mm
+++ b/ios/chrome/browser/signin/gaia_auth_fetcher_ios.mm
@@ -216,12 +216,11 @@
     GaiaAuthFetcherIOS* fetcher,
     web::BrowserState* browser_state)
     : browser_state_(browser_state), fetcher_(fetcher), request_() {
-  web::BrowserState::GetActiveStateManager(browser_state_)->AddObserver(this);
+  ActiveStateManager::FromBrowserState(browser_state_)->AddObserver(this);
 }
 
 GaiaAuthFetcherIOSBridge::~GaiaAuthFetcherIOSBridge() {
-  web::BrowserState::GetActiveStateManager(browser_state_)
-      ->RemoveObserver(this);
+  ActiveStateManager::FromBrowserState(browser_state_)->RemoveObserver(this);
   ResetWKWebView();
 }
 
@@ -282,7 +281,7 @@
 }
 
 WKWebView* GaiaAuthFetcherIOSBridge::GetWKWebView() {
-  if (!web::BrowserState::GetActiveStateManager(browser_state_)->IsActive()) {
+  if (!ActiveStateManager::FromBrowserState(browser_state_)->IsActive()) {
     // |browser_state_| is not active, WKWebView linked to this browser state
     // should not exist or be created.
     return nil;
diff --git a/ios/chrome/browser/signin/gaia_auth_fetcher_ios_private.h b/ios/chrome/browser/signin/gaia_auth_fetcher_ios_private.h
index ad1e6c5..a009310 100644
--- a/ios/chrome/browser/signin/gaia_auth_fetcher_ios_private.h
+++ b/ios/chrome/browser/signin/gaia_auth_fetcher_ios_private.h
@@ -9,7 +9,7 @@
 
 #import "base/mac/scoped_nsobject.h"
 #include "base/macros.h"
-#include "ios/web/public/active_state_manager.h"
+#include "components/signin/ios/browser/active_state_manager.h"
 
 class GaiaAuthFetcherIOS;
 class GURL;
@@ -24,7 +24,7 @@
 
 // Bridge between the GaiaAuthFetcherIOS and the webview (and its navigation
 // delegate) used to actually do the network fetch.
-class GaiaAuthFetcherIOSBridge : web::ActiveStateManager::Observer {
+class GaiaAuthFetcherIOSBridge : ActiveStateManager::Observer {
  public:
   GaiaAuthFetcherIOSBridge(GaiaAuthFetcherIOS* fetcher,
                            web::BrowserState* browser_state);
diff --git a/ios/chrome/browser/signin/gaia_auth_fetcher_ios_unittest.mm b/ios/chrome/browser/signin/gaia_auth_fetcher_ios_unittest.mm
index 6eaaa80..d64643e 100644
--- a/ios/chrome/browser/signin/gaia_auth_fetcher_ios_unittest.mm
+++ b/ios/chrome/browser/signin/gaia_auth_fetcher_ios_unittest.mm
@@ -60,7 +60,7 @@
   GaiaAuthFetcherIOSTest() {
     browser_state_ = TestChromeBrowserState::Builder().Build();
 
-    web::BrowserState::GetActiveStateManager(browser_state())->SetActive(true);
+    ActiveStateManager::FromBrowserState(browser_state())->SetActive(true);
     gaia_auth_fetcher_.reset(new GaiaAuthFetcherIOS(&consumer_, std::string(),
                                                     nullptr, browser_state()));
     gaia_auth_fetcher_->bridge_.reset(new FakeGaiaAuthFetcherIOSBridge(
@@ -69,7 +69,7 @@
 
   ~GaiaAuthFetcherIOSTest() override {
     gaia_auth_fetcher_.reset();
-    web::BrowserState::GetActiveStateManager(browser_state())->SetActive(false);
+    ActiveStateManager::FromBrowserState(browser_state())->SetActive(false);
   }
 
   GaiaAuthFetcherIOSBridge* GetBridge() {
@@ -162,7 +162,7 @@
 // inactive.
 TEST_F(GaiaAuthFetcherIOSTest, OnInactive) {
   [[GetMockWKWebView() expect] stopLoading];
-  web::BrowserState::GetActiveStateManager(browser_state())->SetActive(false);
+  ActiveStateManager::FromBrowserState(browser_state())->SetActive(false);
   EXPECT_OCMOCK_VERIFY(GetMockWKWebView());
 }
 
@@ -178,9 +178,9 @@
     GetBridge()->URLFetchSuccess("data");
   }]) loadRequest:[OCMArg any]];
 
-  web::BrowserState::GetActiveStateManager(browser_state())->SetActive(false);
+  ActiveStateManager::FromBrowserState(browser_state())->SetActive(false);
   gaia_auth_fetcher_->StartMergeSession("uber_token", "");
-  web::BrowserState::GetActiveStateManager(browser_state())->SetActive(true);
+  ActiveStateManager::FromBrowserState(browser_state())->SetActive(true);
   EXPECT_OCMOCK_VERIFY(GetMockWKWebView());
 }
 
@@ -198,7 +198,7 @@
   }]) loadRequest:[OCMArg any]];
 
   gaia_auth_fetcher_->StartMergeSession("uber_token", "");
-  web::BrowserState::GetActiveStateManager(browser_state())->SetActive(false);
-  web::BrowserState::GetActiveStateManager(browser_state())->SetActive(true);
+  ActiveStateManager::FromBrowserState(browser_state())->SetActive(false);
+  ActiveStateManager::FromBrowserState(browser_state())->SetActive(true);
   EXPECT_OCMOCK_VERIFY(GetMockWKWebView());
 }
diff --git a/ios/chrome/browser/ui/BUILD.gn b/ios/chrome/browser/ui/BUILD.gn
index 89d418e..aef7f3da 100644
--- a/ios/chrome/browser/ui/BUILD.gn
+++ b/ios/chrome/browser/ui/BUILD.gn
@@ -222,7 +222,10 @@
     "external_file_controller.h",
     "external_file_controller.mm",
     "external_file_remover.h",
-    "external_file_remover.mm",
+    "external_file_remover_factory.h",
+    "external_file_remover_factory.mm",
+    "external_file_remover_impl.h",
+    "external_file_remover_impl.mm",
     "fade_truncated_label.h",
     "fade_truncated_label.mm",
     "fullscreen_controller.h",
@@ -254,11 +257,13 @@
     "//components/feature_engagement",
     "//components/image_fetcher/ios",
     "//components/infobars/core",
+    "//components/keyed_service/ios",
     "//components/payments/core",
     "//components/prefs",
     "//components/reading_list/core",
     "//components/search_engines",
     "//components/sessions",
+    "//components/signin/ios/browser:active_state_manager",
     "//components/strings",
     "//components/toolbar",
     "//components/url_formatter",
diff --git a/ios/chrome/browser/ui/bookmarks/bars/bookmark_context_bar.mm b/ios/chrome/browser/ui/bookmarks/bars/bookmark_context_bar.mm
index b7459441..ab5048bc 100644
--- a/ios/chrome/browser/ui/bookmarks/bars/bookmark_context_bar.mm
+++ b/ios/chrome/browser/ui/bookmarks/bars/bookmark_context_bar.mm
@@ -24,8 +24,8 @@
 const CGFloat kToolbarHeight = 48.0f;
 }  // namespace
 
-@interface BookmarkContextBar () {
-}
+@interface BookmarkContextBar ()
+
 // Button at the leading position.
 @property(nonatomic, strong) UIButton* leadingButton;
 // Button at the center position.
@@ -76,7 +76,7 @@
   [button setTitleColor:disabledColor forState:UIControlStateDisabled];
 }
 
-- (void)initStyleForButton:(UIButton*)button {
+- (void)configureStyleForButton:(UIButton*)button {
   [button setBackgroundColor:[UIColor whiteColor]];
   [button setTitleColor:[[MDCPalette bluePalette] tint500]
                forState:UIControlStateNormal];
@@ -94,7 +94,7 @@
   if (self) {
     self.accessibilityIdentifier = @"context_bar";
     _leadingButton = [UIButton buttonWithType:UIButtonTypeCustom];
-    [self initStyleForButton:_leadingButton];
+    [self configureStyleForButton:_leadingButton];
     _leadingButton.contentHorizontalAlignment =
         UseRTLLayout() ? UIControlContentHorizontalAlignmentRight
                        : UIControlContentHorizontalAlignmentLeft;
@@ -104,7 +104,7 @@
     _leadingButton.accessibilityIdentifier = @"context_bar_leading_button";
 
     _centerButton = [UIButton buttonWithType:UIButtonTypeCustom];
-    [self initStyleForButton:_centerButton];
+    [self configureStyleForButton:_centerButton];
     _centerButton.contentHorizontalAlignment =
         UIControlContentHorizontalAlignmentCenter;
     [_centerButton addTarget:self
@@ -113,7 +113,7 @@
     _centerButton.accessibilityIdentifier = @"context_bar_center_button";
 
     _trailingButton = [UIButton buttonWithType:UIButtonTypeCustom];
-    [self initStyleForButton:_trailingButton];
+    [self configureStyleForButton:_trailingButton];
     _trailingButton.contentHorizontalAlignment =
         UseRTLLayout() ? UIControlContentHorizontalAlignmentLeft
                        : UIControlContentHorizontalAlignmentRight;
@@ -132,20 +132,7 @@
     [self addSubview:_stackView];
     _stackView.translatesAutoresizingMaskIntoConstraints = NO;
     _stackView.layoutMarginsRelativeArrangement = YES;
-    if (@available(iOS 11.0, *)) {
-      [NSLayoutConstraint activateConstraints:@[
-        [self.safeAreaLayoutGuide.topAnchor
-            constraintEqualToAnchor:_stackView.topAnchor],
-        [self.safeAreaLayoutGuide.leadingAnchor
-            constraintEqualToAnchor:_stackView.leadingAnchor],
-        [self.safeAreaLayoutGuide.trailingAnchor
-            constraintEqualToAnchor:_stackView.trailingAnchor],
-        [self.safeAreaLayoutGuide.bottomAnchor
-            constraintEqualToAnchor:_stackView.bottomAnchor],
-      ]];
-    } else {
-      AddSameConstraints(_stackView, self);
-    }
+    PinToSafeArea(_stackView, self);
     [_stackView.heightAnchor constraintEqualToConstant:kToolbarHeight].active =
         YES;
 
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
index a3e3d23..97de915 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
@@ -36,12 +36,15 @@
 #import "ios/chrome/browser/ui/bookmarks/bookmark_table_view.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h"
 #import "ios/chrome/browser/ui/commands/application_commands.h"
+#import "ios/chrome/browser/ui/icons/chrome_icon.h"
+#import "ios/chrome/browser/ui/material_components/utils.h"
 #import "ios/chrome/browser/ui/rtl_geometry.h"
 #import "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 #import "ios/chrome/browser/ui/url_loader.h"
 #import "ios/chrome/browser/ui/util/constraints_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
+#import "ios/third_party/material_components_ios/src/components/AppBar/src/MaterialAppBar.h"
 #import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
 #include "ios/web/public/referrer.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -53,9 +56,6 @@
 // The width of the bookmark menu, displaying the different sections.
 const CGFloat kMenuWidth = 264;
 
-// The spacer between title and done button on the navigation bar.
-const CGFloat kSpacer = 50;
-
 // Returns a vector of all URLs in |nodes|.
 std::vector<GURL> GetUrlsToOpen(const std::vector<const BookmarkNode*>& nodes) {
   std::vector<GURL> urls;
@@ -76,12 +76,17 @@
     BookmarkModelBridgeObserver,
     BookmarkPromoControllerDelegate,
     BookmarkTableViewDelegate,
-    ContextBarDelegate>
+    ContextBarDelegate,
+    UIGestureRecognizerDelegate>
+
+// The app bar for the bookmarks.
+@property(nonatomic, strong) MDCAppBar* appBar;
 
 @end
 
 @implementation BookmarkHomeViewController
 
+@synthesize appBar = _appBar;
 @synthesize actionSheetCoordinator = _actionSheetCoordinator;
 @synthesize bookmarkPromoController = _bookmarkPromoController;
 @synthesize bookmarks = _bookmarks;
@@ -157,28 +162,6 @@
                                action:@selector(navigationBarWantsEditing:)];
     [self.navigationBar setBackTarget:self
                                action:@selector(navigationBarBack:)];
-  } else {
-    [self setupNavigationBar];
-  }
-}
-
-- (void)viewWillLayoutSubviews {
-  [super viewWillLayoutSubviews];
-  if (base::FeatureList::IsEnabled(kBookmarkNewGeneration)) {
-    // Resize the custom title view (placed as leftBarButtonItem) on the
-    // navigation bar according to the space available, so that we truncate it
-    // correctly.
-    CGRect frame = self.navigationItem.leftBarButtonItem.customView.frame;
-    NSDictionary* attributes = [self.navigationItem.rightBarButtonItem
-        titleTextAttributesForState:UIControlStateNormal];
-    CGFloat rightButtonWidth =
-        attributes ? [self.navigationItem.rightBarButtonItem.title
-                         sizeWithAttributes:attributes]
-                         .width
-                   : 0;
-    frame.size.width = self.view.bounds.size.width - frame.origin.x -
-                       rightButtonWidth - kSpacer;
-    self.navigationItem.leftBarButtonItem.customView.frame = frame;
   }
 }
 
@@ -995,6 +978,9 @@
   [self.bookmarksTableView setTranslatesAutoresizingMaskIntoConstraints:NO];
   [self.view addSubview:self.bookmarksTableView];
 
+  // After the table view has been added.
+  [self setupNavigationBar];
+
   if (_rootNode != self.bookmarks->root_node()) {
     [self setupContextBar];
   }
@@ -1053,75 +1039,47 @@
 
 // Set up navigation bar for the new UI.
 - (void)setupNavigationBar {
-  // Add custom back button.
-  self.navigationItem.backBarButtonItem = [self customizedBackButton];
+  self.navigationController.navigationBarHidden = YES;
+
+  self.navigationController.interactivePopGestureRecognizer.delegate = self;
+
+  self.appBar = [[MDCAppBar alloc] init];
+  [self addChildViewController:_appBar.headerViewController];
+  ConfigureAppBarWithCardStyle(self.appBar);
+  // Set the header view's tracking scroll view.
+  self.appBar.headerViewController.headerView.trackingScrollView =
+      self.bookmarksTableView.tableView;
+  self.bookmarksTableView.headerView =
+      self.appBar.headerViewController.headerView;
+
+  [self.appBar addSubviewsToParent];
+
+  if (self.navigationController.viewControllers.count > 1) {
+    // Add custom back button.
+    UIBarButtonItem* backButton =
+        [ChromeIcon templateBarButtonItemWithImage:[ChromeIcon backIcon]
+                                            target:self
+                                            action:@selector(back)];
+    self.navigationItem.leftBarButtonItem = backButton;
+  }
 
   // Add custom title.
-  self.navigationItem.leftBarButtonItem = [self customizedNavigationTitle];
+  self.title = bookmark_utils_ios::TitleForBookmarkNode(_rootNode);
 
   // Add custom done button.
   self.navigationItem.rightBarButtonItem = [self customizedDoneButton];
-
-  self.navigationController.navigationBar.tintColor = UIColor.blackColor;
-  self.navigationController.navigationBar.barTintColor =
-      bookmark_utils_ios::mainBackgroundColor();
-  self.navigationController.navigationBar.translucent = NO;
 }
 
-- (UIBarButtonItem*)customizedBackButton {
-  UIImage* backImage = [UIImage imageNamed:@"bookmark_gray_back"];
-  // Set these two properties to customize the back button.
-  [UINavigationBar appearance].backIndicatorImage = backImage;
-  [UINavigationBar appearance].backIndicatorTransitionMaskImage = backImage;
-  // Removes back button label.
-  UIBarButtonItem* backButton =
-      [[UIBarButtonItem alloc] initWithTitle:@""
-                                       style:UIBarButtonItemStylePlain
-                                      target:nil
-                                      action:nil];
-  backImage.accessibilityLabel =
-      l10n_util::GetNSString(IDS_IOS_BOOKMARK_NEW_BACK_LABEL);
-  return backButton;
-}
-
-- (UIBarButtonItem*)customizedNavigationTitle {
-  NSDictionary* titleAttributes = @{
-    NSForegroundColorAttributeName :
-        [UIColor colorWithWhite:68 / 255.0 alpha:1.0],
-    NSFontAttributeName : [MDCTypography titleFont]
-  };
-  UILabel* titleView = [[UILabel alloc] initWithFrame:CGRectZero];
-  [titleView
-      setAttributedText:
-          [[NSAttributedString alloc]
-              initWithString:bookmark_utils_ios::TitleForBookmarkNode(_rootNode)
-                  attributes:titleAttributes]];
-  [titleView sizeToFit];
-  // iOS11 navigation bar uses auto layout, hence explicitly set this to NO.
-  if (base::ios::IsRunningOnIOS11OrLater()) {
-    titleView.translatesAutoresizingMaskIntoConstraints = NO;
-  }
-  self.navigationItem.leftItemsSupplementBackButton = YES;
-  return [[UIBarButtonItem alloc] initWithCustomView:titleView];
+- (void)back {
+  [self.navigationController popViewControllerAnimated:YES];
 }
 
 - (UIBarButtonItem*)customizedDoneButton {
   UIBarButtonItem* doneButton = [[UIBarButtonItem alloc]
       initWithTitle:l10n_util::GetNSString(IDS_IOS_NAVIGATION_BAR_DONE_BUTTON)
-                        .localizedUppercaseString
-              style:UIBarButtonItemStylePlain
+              style:UIBarButtonItemStyleDone
              target:self
              action:@selector(navigationBarCancel:)];
-  NSDictionary* attributes = @{
-    NSForegroundColorAttributeName :
-        [UIColor colorWithWhite:68 / 255.0 alpha:1.0],
-    NSFontAttributeName : [MDCTypography buttonFont]
-  };
-  [doneButton setTitleTextAttributes:attributes forState:UIControlStateNormal];
-  // iOS11 bug requires us to set the text attribute for every state used.
-  // See http://www.openradar.me/34276265.
-  [doneButton setTitleTextAttributes:attributes
-                            forState:UIControlStateSelected];
   doneButton.accessibilityLabel =
       l10n_util::GetNSString(IDS_IOS_NAVIGATION_BAR_DONE_BUTTON);
   return doneButton;
@@ -1186,10 +1144,9 @@
       NSDictionary* views = @{
         @"tableView" : self.bookmarksTableView,
         @"contextBar" : self.contextBar,
-        @"topGuide" : self.topLayoutGuide,
       };
       NSArray* constraints = @[
-        @"V:|[topGuide][tableView][contextBar]|",
+        @"V:|[tableView][contextBar]|",
         @"H:|[tableView]|",
         @"H:|[contextBar]|",
       ];
@@ -1197,10 +1154,9 @@
     } else if (self.bookmarksTableView) {
       NSDictionary* views = @{
         @"tableView" : self.bookmarksTableView,
-        @"topGuide" : self.topLayoutGuide,
       };
       NSArray* constraints = @[
-        @"V:|[topGuide][tableView]|",
+        @"V:|[tableView]|",
         @"H:|[tableView]|",
       ];
       ApplyVisualConstraints(constraints, views);
@@ -1546,6 +1502,14 @@
   return alert;
 }
 
+#pragma mark - UIGestureRecognizerDelegate
+
+- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer*)gestureRecognizer {
+  DCHECK(gestureRecognizer ==
+         self.navigationController.interactivePopGestureRecognizer);
+  return self.navigationController.viewControllers.count > 1;
+}
+
 @end
 
 @implementation BookmarkHomeViewController (ExposedForTesting)
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_table_view.h b/ios/chrome/browser/ui/bookmarks/bookmark_table_view.h
index ccdbba8..342e6d7983 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_table_view.h
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_table_view.h
@@ -24,6 +24,7 @@
 }  // namespace user_prefs
 
 @class BookmarkTableView;
+@class MDCFlexibleHeaderView;
 
 // Delegate to handle actions on the table.
 @protocol BookmarkTableViewDelegate<NSObject>
@@ -68,6 +69,11 @@
 @interface BookmarkTableView : UIView
 // If the table is in edit mode.
 @property(nonatomic, assign) BOOL editing;
+// The UITableView to show bookmarks.
+@property(nonatomic, strong, readonly) UITableView* tableView;
+// Header view to display the shadow below the app bar. It must be tracking the
+// |tableView|.
+@property(nonatomic, weak) MDCFlexibleHeaderView* headerView;
 
 // Shows all sub-folders and sub-urls of a folder node (that is set as the root
 // node) in a UITableView. Note: This class intentionally does not try to
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_table_view.mm b/ios/chrome/browser/ui/bookmarks/bookmark_table_view.mm
index 793b69ed..36297a7 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_table_view.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_table_view.mm
@@ -30,6 +30,7 @@
 #import "ios/chrome/browser/ui/bookmarks/cells/bookmark_table_signin_promo_cell.h"
 #import "ios/chrome/browser/ui/commands/application_commands.h"
 #include "ios/chrome/grit/ios_strings.h"
+#import "ios/third_party/material_components_ios/src/components/FlexibleHeader/src/MDCFlexibleHeaderView.h"
 #include "skia/ext/skia_utils_ios.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
@@ -91,8 +92,6 @@
   std::set<const bookmarks::BookmarkNode*> _editNodes;
 }
 
-// The UITableView to show bookmarks.
-@property(nonatomic, strong) UITableView* tableView;
 // The model holding bookmark data.
 @property(nonatomic, assign) bookmarks::BookmarkModel* bookmarkModel;
 // The browser state.
@@ -121,9 +120,10 @@
 
 @implementation BookmarkTableView
 
+@synthesize tableView = _tableView;
+@synthesize headerView = _headerView;
 @synthesize bookmarkModel = _bookmarkModel;
 @synthesize browserState = _browserState;
-@synthesize tableView = _tableView;
 @synthesize delegate = _delegate;
 @synthesize emptyTableBackgroundView = _emptyTableBackgroundView;
 @synthesize spinnerView = _spinnerView;
@@ -166,10 +166,15 @@
     [self promoStateChangedAnimated:NO];
 
     // Create and setup tableview.
-    self.tableView =
+    _tableView =
         [[UITableView alloc] initWithFrame:frame style:UITableViewStylePlain];
+    self.tableView.accessibilityIdentifier = @"bookmarksTableView";
     self.tableView.dataSource = self;
     self.tableView.delegate = self;
+    if (@available(iOS 11.0, *)) {
+      self.tableView.contentInsetAdjustmentBehavior =
+          UIScrollViewContentInsetAdjustmentNever;
+    }
     // Use iOS8's self sizing feature to compute row height. However,
     // this reduces the row height of bookmarks section from 56 to 45
     // TODO(crbug.com/695749): Fix the bookmark section row height to 56.
@@ -967,4 +972,35 @@
   [self refreshContents];
 }
 
+#pragma mark UIScrollViewDelegate
+
+- (void)scrollViewDidScroll:(UIScrollView*)scrollView {
+  if (scrollView == self.headerView.trackingScrollView) {
+    [self.headerView trackingScrollViewDidScroll];
+  }
+}
+
+- (void)scrollViewDidEndDecelerating:(UIScrollView*)scrollView {
+  if (scrollView == self.headerView.trackingScrollView) {
+    [self.headerView trackingScrollViewDidEndDecelerating];
+  }
+}
+
+- (void)scrollViewDidEndDragging:(UIScrollView*)scrollView
+                  willDecelerate:(BOOL)decelerate {
+  if (scrollView == self.headerView.trackingScrollView) {
+    [self.headerView trackingScrollViewDidEndDraggingWillDecelerate:decelerate];
+  }
+}
+
+- (void)scrollViewWillEndDragging:(UIScrollView*)scrollView
+                     withVelocity:(CGPoint)velocity
+              targetContentOffset:(inout CGPoint*)targetContentOffset {
+  if (scrollView == self.headerView.trackingScrollView) {
+    [self.headerView
+        trackingScrollViewWillEndDraggingWithVelocity:velocity
+                                  targetContentOffset:targetContentOffset];
+  }
+}
+
 @end
diff --git a/ios/chrome/browser/ui/bookmarks/bookmarks_new_generation_egtest.mm b/ios/chrome/browser/ui/bookmarks/bookmarks_new_generation_egtest.mm
index cc5bad0..a8b8565 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmarks_new_generation_egtest.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmarks_new_generation_egtest.mm
@@ -152,6 +152,35 @@
 
 #pragma mark - Tests
 
+// Test that swiping left to right navigate back.
+- (void)testNavigateBackWithGesture {
+  // Disabled on iPad as there is not "navigate back" gesture.
+  if (IsIPadIdiom()) {
+    EARL_GREY_TEST_SKIPPED(@"Test not applicable for iPad");
+  }
+
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(kBookmarkNewGeneration);
+
+  [BookmarksNewGenTestCase setupStandardBookmarks];
+  [BookmarksNewGenTestCase openBookmarks];
+  [BookmarksNewGenTestCase openMobileBookmarks];
+
+  // Make sure the Mobile Bookmarks is not present.
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"Mobile Bookmarks")]
+      assertWithMatcher:grey_nil()];
+
+  // Back using swipe left gesture.
+  [[EarlGrey
+      selectElementWithMatcher:grey_accessibilityID(@"bookmarksTableView")]
+      performAction:grey_swipeFastInDirectionWithStartPoint(kGREYDirectionRight,
+                                                            0.01, 0.5)];
+
+  // Check we navigated back to the Mobile Bookmarks.
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"Mobile Bookmarks")]
+      assertWithMatcher:grey_sufficientlyVisible()];
+}
+
 - (void)testUndoDeleteBookmarkFromSwipe {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(kBookmarkNewGeneration);
diff --git a/ios/chrome/browser/ui/browser_view_controller.h b/ios/chrome/browser/ui/browser_view_controller.h
index e2fab8e7..b25de169 100644
--- a/ios/chrome/browser/ui/browser_view_controller.h
+++ b/ios/chrome/browser/ui/browser_view_controller.h
@@ -143,12 +143,6 @@
 // Dismisses all presented views then calls |completion|.
 - (void)clearPresentedStateWithCompletion:(ProceduralBlock)completion;
 
-// Removes files received from other applications. If |immediately| is YES,
-// initiates the removal of files immediately. |completionHandler| is called
-// when files have been removed.
-- (void)removeExternalFilesImmediately:(BOOL)immediately
-                     completionHandler:(ProceduralBlock)completionHandler;
-
 // Returns a tab strip placeholder view created from the current state of the
 // tab strip. It is used to animate the transition from the browser view
 // controller to the tab switcher.
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index e48a382..4f91041 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -51,6 +51,7 @@
 #include "components/search_engines/template_url_service.h"
 #include "components/sessions/core/session_types.h"
 #include "components/sessions/core/tab_restore_service_helper.h"
+#include "components/signin/ios/browser/active_state_manager.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/toolbar/toolbar_model_impl.h"
 #include "ios/chrome/app/tests_hook.h"
@@ -125,7 +126,6 @@
 #import "ios/chrome/browser/ui/dialogs/java_script_dialog_presenter_impl.h"
 #import "ios/chrome/browser/ui/elements/activity_overlay_coordinator.h"
 #import "ios/chrome/browser/ui/external_file_controller.h"
-#import "ios/chrome/browser/ui/external_file_remover.h"
 #import "ios/chrome/browser/ui/find_bar/find_bar_controller_ios.h"
 #import "ios/chrome/browser/ui/first_run/welcome_to_chrome_view_controller.h"
 #import "ios/chrome/browser/ui/fullscreen_controller.h"
@@ -193,7 +193,6 @@
 #include "ios/public/provider/chrome/browser/voice/voice_search_controller.h"
 #include "ios/public/provider/chrome/browser/voice/voice_search_controller_delegate.h"
 #include "ios/public/provider/chrome/browser/voice/voice_search_provider.h"
-#include "ios/web/public/active_state_manager.h"
 #include "ios/web/public/navigation_item.h"
 #import "ios/web/public/navigation_manager.h"
 #include "ios/web/public/referrer_util.h"
@@ -280,11 +279,6 @@
 const CGFloat kSearchByImageMaxImageWidth = 600.0;
 const CGFloat kSearchByImageMaxImageHeight = 400.0;
 
-// The delay, in seconds, after startup before cleaning up the files received
-// from other applications that are not bookmarked nor referenced by an open or
-// recently closed tab.
-const int kExternalFilesCleanupDelaySeconds = 60;
-
 enum HeaderBehaviour {
   // The header moves completely out of the screen.
   Hideable = 0,
@@ -522,9 +516,6 @@
   // button.
   BookmarkInteractionController* _bookmarkInteractionController;
 
-  // Used to remove unreferenced external files.
-  std::unique_ptr<ExternalFileRemover> _externalFileRemover;
-
   // The currently displayed "Rate This App" dialog, if one exists.
   id<AppRatingPrompt> _rateThisAppDialog;
 
@@ -1084,8 +1075,8 @@
   }
 
   if (_browserState) {
-    web::ActiveStateManager* active_state_manager =
-        web::BrowserState::GetActiveStateManager(_browserState);
+    ActiveStateManager* active_state_manager =
+        ActiveStateManager::FromBrowserState(_browserState);
     active_state_manager->SetActive(active);
   }
 
@@ -1800,8 +1791,6 @@
   self.tabStripCoordinator = nil;
   self.tabStripView = nil;
 
-  // The file remover needs the browser state, so needs to be destroyed now.
-  _externalFileRemover = nil;
   _browserState = nullptr;
   [_dispatcher stopDispatchingToTarget:self];
   _dispatcher = nil;
@@ -2443,24 +2432,6 @@
   }
 }
 
-#pragma mark - External files
-
-- (void)removeExternalFilesImmediately:(BOOL)immediately
-                     completionHandler:(ProceduralBlock)completionHandler {
-  DCHECK_CURRENTLY_ON(web::WebThread::UI);
-  DCHECK(!_isOffTheRecord);
-  _externalFileRemover =
-      std::make_unique<ExternalFileRemover>(self.browserState);
-  // Delay the cleanup of the unreferenced files received from other apps
-  // to not impact startup performance.
-  int delay = immediately ? 0 : kExternalFilesCleanupDelaySeconds;
-  _externalFileRemover->RemoveAfterDelay(
-      base::TimeDelta::FromSeconds(delay),
-      base::BindBlockArc(completionHandler ? completionHandler
-                                           : ^{
-                                             }));
-}
-
 - (UIView<TabStripFoldAnimation>*)tabStripPlaceholderView {
   return [self.tabStripCoordinator placeholderView];
 }
diff --git a/ios/chrome/browser/ui/external_file_remover.h b/ios/chrome/browser/ui/external_file_remover.h
index bea1f78..b6c432b 100644
--- a/ios/chrome/browser/ui/external_file_remover.h
+++ b/ios/chrome/browser/ui/external_file_remover.h
@@ -1,63 +1,25 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef IOS_CHROME_BROWSER_UI_EXTERNAL_FILE_REMOVER_H_
 #define IOS_CHROME_BROWSER_UI_EXTERNAL_FILE_REMOVER_H_
 
-#include "base/callback.h"
-#include "base/memory/weak_ptr.h"
-#include "base/time/time.h"
-#include "components/sessions/core/tab_restore_service_observer.h"
+#include "components/keyed_service/core/keyed_service.h"
 
-namespace ios {
-class ChromeBrowserState;
-}
-namespace sessions {
-class TabRestoreService;
-}
-
-// ExternalFileRemover is responsible for removing documents received from other
-// applications that are not in the list of recently closed tabs, open tabs or
-// bookmarks.
-class ExternalFileRemover : public sessions::TabRestoreServiceObserver {
+// ExternalFileRemover is responsible for removing documents received from
+// other applications that are not in the list of recently closed tabs, open
+// tabs or bookmarks.
+class ExternalFileRemover : public KeyedService {
  public:
-  // Creates an ExternalFileRemover to remove external documents not referenced
-  // by the specified BrowserViewController. Use Remove to initiate the removal.
-  explicit ExternalFileRemover(ios::ChromeBrowserState* browser_state);
-  ~ExternalFileRemover() override;
-
-  // sessions::TabRestoreServiceObserver methods
-  void TabRestoreServiceChanged(sessions::TabRestoreService* service) override;
-  void TabRestoreServiceDestroyed(
-      sessions::TabRestoreService* service) override;
-
+  ExternalFileRemover() = default;
+  ~ExternalFileRemover() override = default;
   // Post a delayed task to clean up the files received from other applications.
-  // |callback| is called when the clean up has finished.
-  void RemoveAfterDelay(const base::TimeDelta& delay,
-                        const base::Closure& callback);
+  // |callback| is called when the clean up has finished; it may be null.
+  virtual void RemoveAfterDelay(base::TimeDelta delay,
+                                base::OnceClosure callback) = 0;
 
  private:
-  // Loads the |tabRestoreService_| if necessary. Removes all files received
-  // from other apps if |all_files| is true. Otherwise, removes the unreferenced
-  // files only. |callback| is called when the removal finishes.
-  void Remove(bool all_files, const base::Closure& callback);
-  // Removes files received from other apps. If |all_files| is true, then
-  // all files including files that may be referenced by tabs through restore
-  // service or history. Otherwise, only the unreferenced files are removed.
-  // |callback| is called when the removal finishes.
-  void RemoveFiles(bool all_files, const base::Closure& callback);
-  // Returns all Referenced External files.
-  NSSet* GetReferencedExternalFiles();
-  // Pointer to the tab restore service in the browser state associated with
-  // |bvc_|.
-  sessions::TabRestoreService* tab_restore_service_ = nullptr;
-  // ChromeBrowserState used to get the referenced files. Must outlive this
-  // object.
-  ios::ChromeBrowserState* browser_state_ = nullptr;
-  // Used to ensure |Remove()| is not run when this object is destroyed.
-  base::WeakPtrFactory<ExternalFileRemover> weak_ptr_factory_;
-
   DISALLOW_COPY_AND_ASSIGN(ExternalFileRemover);
 };
 
diff --git a/ios/chrome/browser/ui/external_file_remover.mm b/ios/chrome/browser/ui/external_file_remover.mm
deleted file mode 100644
index 5a913bb7..0000000
--- a/ios/chrome/browser/ui/external_file_remover.mm
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/external_file_remover.h"
-
-#include "base/logging.h"
-#import "base/mac/bind_objc_block.h"
-#include "base/strings/sys_string_conversions.h"
-#include "base/task_scheduler/post_task.h"
-#include "components/bookmarks/browser/bookmark_model.h"
-#include "components/sessions/core/tab_restore_service.h"
-#include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/chrome_url_util.h"
-#include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
-#import "ios/chrome/browser/tabs/tab_model.h"
-#import "ios/chrome/browser/tabs/tab_model_list.h"
-#import "ios/chrome/browser/ui/external_file_controller.h"
-#include "ios/web/public/web_thread.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-ExternalFileRemover::ExternalFileRemover(ios::ChromeBrowserState* browser_state)
-    : browser_state_(browser_state), weak_ptr_factory_(this) {}
-
-ExternalFileRemover::~ExternalFileRemover() {
-  if (tab_restore_service_)
-    tab_restore_service_->RemoveObserver(this);
-}
-
-void ExternalFileRemover::TabRestoreServiceChanged(
-    sessions::TabRestoreService* service) {
-  if (service->IsLoaded()) {
-    tab_restore_service_->RemoveObserver(this);
-    RemoveFiles(false, base::Closure());
-  }
-}
-
-void ExternalFileRemover::TabRestoreServiceDestroyed(
-    sessions::TabRestoreService* service) {
-  // This happens during shutdown and RemoveFiles() cannot be safely called,
-  // so it is a no-op.
-}
-
-void ExternalFileRemover::Remove(bool all_files,
-                                 const base::Closure& callback) {
-  // |IOSChromeTabRestoreServiceFactory::GetForBrowserState| has to be called on
-  // the UI thread.
-  DCHECK_CURRENTLY_ON(web::WebThread::UI);
-  tab_restore_service_ =
-      IOSChromeTabRestoreServiceFactory::GetForBrowserState(browser_state_);
-  DCHECK(tab_restore_service_);
-  if (!tab_restore_service_->IsLoaded()) {
-    // TODO(crbug.com/430902): In the case of the presence of tab restore
-    // service, only unreferenced files are removed. This can be addressed with
-    // the larger problem of Clear All browsing data not clearing Tab Restore.
-    tab_restore_service_->AddObserver(this);
-    tab_restore_service_->LoadTabsFromLastSession();
-    if (!callback.is_null()) {
-      web::WebThread::PostTask(web::WebThread::UI, FROM_HERE, callback);
-    }
-  } else {
-    RemoveFiles(all_files, callback);
-  }
-}
-
-void ExternalFileRemover::RemoveFiles(bool all_files,
-                                      const base::Closure& callback) {
-  NSSet* referenced_files = nil;
-  if (all_files) {
-    referenced_files = GetReferencedExternalFiles();
-  }
-
-  const NSInteger kMinimumAgeInDays = 30;
-  NSInteger age_in_days = all_files ? 0 : kMinimumAgeInDays;
-
-  base::Closure callback_wrapper = callback;
-  if (callback_wrapper.is_null()) {
-    callback_wrapper = base::Bind(&base::DoNothing);
-  }
-  base::PostTaskWithTraitsAndReply(
-      FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
-      base::BindBlockArc(^{
-        [ExternalFileController removeFilesExcluding:referenced_files
-                                           olderThan:age_in_days];
-      }),
-      callback_wrapper);
-}
-
-void ExternalFileRemover::RemoveAfterDelay(const base::TimeDelta& delay,
-                                           const base::Closure& callback) {
-  bool remove_all_files = delay == base::TimeDelta::FromSeconds(0);
-  // Creating a copy so it can be used from the block underneath.
-  base::Closure callback_copy = callback;
-  // Since a method on |this| is called from a block, this dance is necessary to
-  // make sure a method on |this| is not called when the object has gone away.
-  base::WeakPtr<ExternalFileRemover> weak_this = weak_ptr_factory_.GetWeakPtr();
-  web::WebThread::PostDelayedTask(
-      web::WebThread::UI, FROM_HERE, base::BindBlockArc(^{
-        if (weak_this) {
-          weak_this->Remove(remove_all_files, callback_copy);
-        } else if (!callback_copy.is_null()) {
-          callback_copy.Run();
-        }
-      }),
-      delay);
-}
-
-NSSet* ExternalFileRemover::GetReferencedExternalFiles() {
-  // Add files from all TabModels.
-  NSMutableSet* referenced_external_files = [NSMutableSet set];
-  for (TabModel* tab_model in GetTabModelsForChromeBrowserState(
-           browser_state_)) {
-    NSSet* tab_model_files = [tab_model currentlyReferencedExternalFiles];
-    if (tab_model_files) {
-      [referenced_external_files unionSet:tab_model_files];
-    }
-  }
-
-  bookmarks::BookmarkModel* bookmark_model =
-      ios::BookmarkModelFactory::GetForBrowserState(browser_state_);
-  // Check if the bookmark model is loaded.
-  if (!bookmark_model || !bookmark_model->loaded())
-    return referenced_external_files;
-
-  // Add files from Bookmarks.
-  std::vector<bookmarks::BookmarkModel::URLAndTitle> bookmarks;
-  bookmark_model->GetBookmarks(&bookmarks);
-  for (const auto& bookmark : bookmarks) {
-    GURL bookmark_url = bookmark.url;
-    if (UrlIsExternalFileReference(bookmark_url)) {
-      [referenced_external_files
-          addObject:base::SysUTF8ToNSString(bookmark_url.ExtractFileName())];
-    }
-  }
-  return referenced_external_files;
-}
diff --git a/ios/chrome/browser/ui/external_file_remover_factory.h b/ios/chrome/browser/ui/external_file_remover_factory.h
new file mode 100644
index 0000000..48b7faf
--- /dev/null
+++ b/ios/chrome/browser/ui/external_file_remover_factory.h
@@ -0,0 +1,45 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_EXTERNAL_FILE_REMOVER_FACTORY_H_
+#define IOS_CHROME_BROWSER_UI_EXTERNAL_FILE_REMOVER_FACTORY_H_
+
+#include <memory>
+
+#include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+}  // namespace base
+
+namespace ios {
+class ChromeBrowserState;
+}
+
+class ExternalFileRemover;
+
+// Singleton that owns all |ExternalFileRemover| and associates them with
+// browser states. Listens for the |BrowserState|'s destruction notification and
+// cleans up the associated |ExternalFileRemover|.
+class ExternalFileRemoverFactory : public BrowserStateKeyedServiceFactory {
+ public:
+  static ExternalFileRemover* GetForBrowserState(
+      ios::ChromeBrowserState* browser_state);
+  static ExternalFileRemoverFactory* GetInstance();
+
+ private:
+  friend struct base::DefaultSingletonTraits<ExternalFileRemoverFactory>;
+
+  ExternalFileRemoverFactory();
+  ~ExternalFileRemoverFactory() override;
+
+  // BrowserStateKeyedServiceFactory implementation.
+  std::unique_ptr<KeyedService> BuildServiceInstanceFor(
+      web::BrowserState* context) const override;
+
+  DISALLOW_COPY_AND_ASSIGN(ExternalFileRemoverFactory);
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_EXTERNAL_FILE_REMOVER_FACTORY_H_
diff --git a/ios/chrome/browser/ui/external_file_remover_factory.mm b/ios/chrome/browser/ui/external_file_remover_factory.mm
new file mode 100644
index 0000000..8773b65
--- /dev/null
+++ b/ios/chrome/browser/ui/external_file_remover_factory.mm
@@ -0,0 +1,49 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/external_file_remover_factory.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/singleton.h"
+#include "components/keyed_service/ios/browser_state_dependency_manager.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
+#import "ios/chrome/browser/ui/external_file_remover_impl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// static
+ExternalFileRemover* ExternalFileRemoverFactory::GetForBrowserState(
+    ios::ChromeBrowserState* browser_state) {
+  return static_cast<ExternalFileRemover*>(
+      GetInstance()->GetServiceForBrowserState(browser_state, true));
+}
+
+// static
+ExternalFileRemoverFactory* ExternalFileRemoverFactory::GetInstance() {
+  return base::Singleton<ExternalFileRemoverFactory>::get();
+}
+
+ExternalFileRemoverFactory::ExternalFileRemoverFactory()
+    : BrowserStateKeyedServiceFactory(
+          "ExternalFileRemoverService",
+          BrowserStateDependencyManager::GetInstance()) {
+  DependsOn(IOSChromeTabRestoreServiceFactory::GetInstance());
+}
+
+ExternalFileRemoverFactory::~ExternalFileRemoverFactory() {}
+
+std::unique_ptr<KeyedService>
+ExternalFileRemoverFactory::BuildServiceInstanceFor(
+    web::BrowserState* context) const {
+  ios::ChromeBrowserState* browser_state =
+      ios::ChromeBrowserState::FromBrowserState(context);
+  return std::make_unique<ExternalFileRemoverImpl>(
+      browser_state,
+      IOSChromeTabRestoreServiceFactory::GetForBrowserState(browser_state));
+}
diff --git a/ios/chrome/browser/ui/external_file_remover_impl.h b/ios/chrome/browser/ui/external_file_remover_impl.h
new file mode 100644
index 0000000..70e1a399
--- /dev/null
+++ b/ios/chrome/browser/ui/external_file_remover_impl.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_EXTERNAL_FILE_REMOVER_IMPL_H_
+#define IOS_CHROME_BROWSER_UI_EXTERNAL_FILE_REMOVER_IMPL_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "components/sessions/core/tab_restore_service_observer.h"
+#import "ios/chrome/browser/ui/external_file_remover.h"
+
+namespace ios {
+class ChromeBrowserState;
+}
+namespace sessions {
+class TabRestoreService;
+}
+
+// Concrete implementation of ExternalFileRemover.
+class ExternalFileRemoverImpl : public ExternalFileRemover,
+                                public sessions::TabRestoreServiceObserver {
+ public:
+  // Creates an ExternalFileRemoverImpl to remove external documents not
+  // referenced by the specified BrowserViewController. Use Remove to initiate
+  // the removal.
+  ExternalFileRemoverImpl(ios::ChromeBrowserState* browser_state,
+                          sessions::TabRestoreService* tab_restore_service);
+  ~ExternalFileRemoverImpl() override;
+
+  // ExternalFileRemover methods.
+  void RemoveAfterDelay(base::TimeDelta delay,
+                        base::OnceClosure callback) override;
+
+  // KeyedService methods
+  void Shutdown() override;
+
+  // sessions::TabRestoreServiceObserver methods
+  void TabRestoreServiceChanged(sessions::TabRestoreService* service) override;
+  void TabRestoreServiceDestroyed(
+      sessions::TabRestoreService* service) override;
+
+ private:
+  // Struct used to save information for delayed requests.
+  struct DelayedFileRemoveRequest {
+    bool remove_all_files;
+    base::ScopedClosureRunner closure_runner;
+  };
+  // Removes all files received from other apps if |all_files| is true.
+  // Otherwise, removes the unreferenced files only. |closure_runner| is called
+  // when the removal finishes.
+  void Remove(bool all_files, base::ScopedClosureRunner closure_runner);
+  // Removes files received from other apps. If |all_files| is true, then
+  // all files including files that may be referenced by tabs through restore
+  // service or history. Otherwise, only the unreferenced files are removed.
+  // |closure_runner| is called when the removal finishes.
+  void RemoveFiles(bool all_files, base::ScopedClosureRunner closure_runner);
+  // Returns all Referenced External files.
+  NSSet* GetReferencedExternalFiles();
+  // Vector used to store delayed requests.
+  std::vector<DelayedFileRemoveRequest> delayed_file_remove_requests_;
+  // Pointer to the tab restore service.
+  sessions::TabRestoreService* tab_restore_service_ = nullptr;
+  // ChromeBrowserState used to get the referenced files. Must outlive this
+  // object.
+  ios::ChromeBrowserState* browser_state_ = nullptr;
+  // Used to ensure that this class' methods are called on the correct sequence.
+  SEQUENCE_CHECKER(sequence_checker_);
+  // Used to ensure |Remove()| is not run when this object is destroyed.
+  base::WeakPtrFactory<ExternalFileRemoverImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExternalFileRemoverImpl);
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_EXTERNAL_FILE_REMOVER_IMPL_H_
diff --git a/ios/chrome/browser/ui/external_file_remover_impl.mm b/ios/chrome/browser/ui/external_file_remover_impl.mm
new file mode 100644
index 0000000..3bf6a9d
--- /dev/null
+++ b/ios/chrome/browser/ui/external_file_remover_impl.mm
@@ -0,0 +1,155 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/external_file_remover_impl.h"
+
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#import "base/mac/bind_objc_block.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/task_scheduler/post_task.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/sessions/core/tab_restore_service.h"
+#include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/chrome_url_util.h"
+#include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
+#import "ios/chrome/browser/tabs/tab_model.h"
+#import "ios/chrome/browser/tabs/tab_model_list.h"
+#import "ios/chrome/browser/ui/external_file_controller.h"
+#include "ios/web/public/web_thread.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+// Empty callback. The closure owned by |closure_runner| will be invoked as
+// part of the destructor of base::ScopedClosureRunner (which takes care of
+// checking for null closure).
+void RunCallback(base::ScopedClosureRunner closure_runner) {}
+}  // namespace
+
+ExternalFileRemoverImpl::ExternalFileRemoverImpl(
+    ios::ChromeBrowserState* browser_state,
+    sessions::TabRestoreService* tab_restore_service)
+    : tab_restore_service_(tab_restore_service),
+      browser_state_(browser_state),
+      weak_ptr_factory_(this) {
+  DCHECK(tab_restore_service_);
+  tab_restore_service_->AddObserver(this);
+}
+
+ExternalFileRemoverImpl::~ExternalFileRemoverImpl() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void ExternalFileRemoverImpl::RemoveAfterDelay(base::TimeDelta delay,
+                                               base::OnceClosure callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  base::ScopedClosureRunner closure_runner =
+      base::ScopedClosureRunner(std::move(callback));
+  bool remove_all_files = delay == base::TimeDelta::FromSeconds(0);
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&ExternalFileRemoverImpl::RemoveFiles,
+                 weak_ptr_factory_.GetWeakPtr(), remove_all_files,
+                 base::Passed(&closure_runner)),
+      delay);
+}
+
+void ExternalFileRemoverImpl::Shutdown() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (tab_restore_service_) {
+    tab_restore_service_->RemoveObserver(this);
+    tab_restore_service_ = nullptr;
+  }
+  delayed_file_remove_requests_.clear();
+}
+
+void ExternalFileRemoverImpl::TabRestoreServiceChanged(
+    sessions::TabRestoreService* service) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (service->IsLoaded())
+    return;
+
+  tab_restore_service_->RemoveObserver(this);
+  tab_restore_service_ = nullptr;
+
+  std::vector<DelayedFileRemoveRequest> delayed_file_remove_requests;
+  delayed_file_remove_requests = std::move(delayed_file_remove_requests_);
+  for (DelayedFileRemoveRequest& request : delayed_file_remove_requests) {
+    RemoveFiles(request.remove_all_files, std::move(request.closure_runner));
+  }
+}
+
+void ExternalFileRemoverImpl::TabRestoreServiceDestroyed(
+    sessions::TabRestoreService* service) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTREACHED() << "Should never happen as unregistration happen in Shutdown";
+}
+
+void ExternalFileRemoverImpl::Remove(bool all_files,
+                                     base::ScopedClosureRunner closure_runner) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!tab_restore_service_) {
+    RemoveFiles(all_files, std::move(closure_runner));
+    return;
+  }
+  // Removal is delayed until tab restore loading completes.
+  DCHECK(!tab_restore_service_->IsLoaded());
+  DelayedFileRemoveRequest request = {all_files, std::move(closure_runner)};
+  delayed_file_remove_requests_.push_back(std::move(request));
+  if (delayed_file_remove_requests_.size() == 1)
+    tab_restore_service_->LoadTabsFromLastSession();
+}
+
+void ExternalFileRemoverImpl::RemoveFiles(
+    bool all_files,
+    base::ScopedClosureRunner closure_runner) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NSSet* referenced_files = all_files ? GetReferencedExternalFiles() : nil;
+
+  const NSInteger kMinimumAgeInDays = 30;
+  NSInteger age_in_days = all_files ? 0 : kMinimumAgeInDays;
+
+  base::PostTaskWithTraitsAndReply(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+      base::BindBlockArc(^{
+        [ExternalFileController removeFilesExcluding:referenced_files
+                                           olderThan:age_in_days];
+      }),
+      base::Bind(&RunCallback, base::Passed(&closure_runner)));
+}
+
+NSSet* ExternalFileRemoverImpl::GetReferencedExternalFiles() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Add files from all TabModels.
+  NSMutableSet* referenced_external_files = [NSMutableSet set];
+  for (TabModel* tab_model in GetTabModelsForChromeBrowserState(
+           browser_state_)) {
+    NSSet* tab_model_files = [tab_model currentlyReferencedExternalFiles];
+    if (tab_model_files) {
+      [referenced_external_files unionSet:tab_model_files];
+    }
+  }
+
+  bookmarks::BookmarkModel* bookmark_model =
+      ios::BookmarkModelFactory::GetForBrowserState(browser_state_);
+  // Check if the bookmark model is loaded.
+  if (!bookmark_model || !bookmark_model->loaded())
+    return referenced_external_files;
+
+  // Add files from Bookmarks.
+  std::vector<bookmarks::BookmarkModel::URLAndTitle> bookmarks;
+  bookmark_model->GetBookmarks(&bookmarks);
+  for (const auto& bookmark : bookmarks) {
+    GURL bookmark_url = bookmark.url;
+    if (UrlIsExternalFileReference(bookmark_url)) {
+      [referenced_external_files
+          addObject:base::SysUTF8ToNSString(bookmark_url.ExtractFileName())];
+    }
+  }
+  return referenced_external_files;
+}
diff --git a/ios/chrome/browser/ui/history/clear_browsing_bar.mm b/ios/chrome/browser/ui/history/clear_browsing_bar.mm
index 142a0484..ac9a540f 100644
--- a/ios/chrome/browser/ui/history/clear_browsing_bar.mm
+++ b/ios/chrome/browser/ui/history/clear_browsing_bar.mm
@@ -95,20 +95,7 @@
     [self addSubview:_stackView];
     _stackView.translatesAutoresizingMaskIntoConstraints = NO;
 
-    if (@available(iOS 11.0, *)) {
-      [NSLayoutConstraint activateConstraints:@[
-        [self.safeAreaLayoutGuide.topAnchor
-            constraintEqualToAnchor:_stackView.topAnchor],
-        [self.safeAreaLayoutGuide.leadingAnchor
-            constraintEqualToAnchor:_stackView.leadingAnchor],
-        [self.safeAreaLayoutGuide.trailingAnchor
-            constraintEqualToAnchor:_stackView.trailingAnchor],
-        [self.safeAreaLayoutGuide.bottomAnchor
-            constraintEqualToAnchor:_stackView.bottomAnchor],
-      ]];
-    } else {
-      AddSameConstraints(_stackView, self);
-    }
+    PinToSafeArea(_stackView, self);
     [_stackView.heightAnchor constraintEqualToConstant:kToolbarHeight].active =
         YES;
 
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.mm b/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.mm
index fd7e7744..0fcea39 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.mm
@@ -60,13 +60,13 @@
         [[OmniboxPopupTruncatingLabel alloc] initWithFrame:CGRectZero];
     _textTruncatingLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
     _textTruncatingLabel.userInteractionEnabled = NO;
-    [self addSubview:_textTruncatingLabel];
+    [self.contentView addSubview:_textTruncatingLabel];
 
     _detailTruncatingLabel =
         [[OmniboxPopupTruncatingLabel alloc] initWithFrame:CGRectZero];
     _detailTruncatingLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
     _detailTruncatingLabel.userInteractionEnabled = NO;
-    [self addSubview:_detailTruncatingLabel];
+    [self.contentView addSubview:_detailTruncatingLabel];
 
     // Answers use a UILabel with NSLineBreakByTruncatingTail to produce a
     // truncation with an ellipse instead of fading on multi-line text.
@@ -74,14 +74,14 @@
     _detailAnswerLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
     _detailAnswerLabel.userInteractionEnabled = NO;
     _detailAnswerLabel.lineBreakMode = NSLineBreakByTruncatingTail;
-    [self addSubview:_detailAnswerLabel];
+    [self.contentView addSubview:_detailAnswerLabel];
 
     _appendButton = [UIButton buttonWithType:UIButtonTypeCustom];
     [_appendButton setContentMode:UIViewContentModeRight];
     [self updateAppendButtonImages];
     // TODO(justincohen): Consider using the UITableViewCell's accessory view.
     // The current implementation is from before using a UITableViewCell.
-    [self addSubview:_appendButton];
+    [self.contentView addSubview:_appendButton];
 
     // Leading icon is only displayed on iPad.
     if (IsIPadIdiom()) {
@@ -91,13 +91,13 @@
 
       // TODO(justincohen): Consider using the UITableViewCell's image view.
       // The current implementation is from before using a UITableViewCell.
-      [self addSubview:_imageView];
+      [self.contentView addSubview:_imageView];
     }
 
     _answerImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
     _answerImageView.userInteractionEnabled = NO;
     _answerImageView.contentMode = UIViewContentModeScaleAspectFit;
-    [self addSubview:_answerImageView];
+    [self.contentView addSubview:_answerImageView];
   }
   return self;
 }
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_popup_material_view_controller.mm b/ios/chrome/browser/ui/omnibox/omnibox_popup_material_view_controller.mm
index 8d44405..8a1e8ad 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_popup_material_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_popup_material_view_controller.mm
@@ -96,6 +96,11 @@
 - (void)viewDidLoad {
   [super viewDidLoad];
 
+  // Respect the safe area on iOS 11 to support iPhone X.
+  if (@available(iOS 11, *)) {
+    self.tableView.insetsContentViewsToSafeArea = YES;
+  }
+
   // Initialize the same size as the parent view, autoresize will correct this.
   [self.view setFrame:CGRectZero];
   if (_incognito) {
diff --git a/ios/chrome/browser/ui/payments/BUILD.gn b/ios/chrome/browser/ui/payments/BUILD.gn
index 801faee..a4e1709 100644
--- a/ios/chrome/browser/ui/payments/BUILD.gn
+++ b/ios/chrome/browser/ui/payments/BUILD.gn
@@ -140,8 +140,10 @@
     "//ios/chrome/browser/ui/collection_view",
     "//ios/chrome/browser/ui/colors",
     "//ios/chrome/browser/ui/icons",
+    "//ios/chrome/browser/ui/material_components",
     "//ios/chrome/browser/ui/payments/cells",
     "//ios/third_party/material_components_ios",
+    "//third_party/libaddressinput:strings_grit",
     "//ui/base",
   ]
   libs = [ "UIKit.framework" ]
diff --git a/ios/chrome/browser/ui/payments/country_selection_coordinator.mm b/ios/chrome/browser/ui/payments/country_selection_coordinator.mm
index c4db48a6..35c9666 100644
--- a/ios/chrome/browser/ui/payments/country_selection_coordinator.mm
+++ b/ios/chrome/browser/ui/payments/country_selection_coordinator.mm
@@ -7,8 +7,6 @@
 #include "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/ui/payments/payment_request_picker_row.h"
 #include "ios/chrome/browser/ui/payments/payment_request_picker_view_controller.h"
-#include "ios/chrome/grit/ios_strings.h"
-#include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -55,7 +53,6 @@
   self.viewController =
       [[PaymentRequestPickerViewController alloc] initWithRows:rows
                                                       selected:selectedRow];
-  self.viewController.title = l10n_util::GetNSString(IDS_IOS_AUTOFILL_COUNTRY);
   self.viewController.delegate = self;
 
   DCHECK(self.baseViewController.navigationController);
diff --git a/ios/chrome/browser/ui/payments/payment_request_picker_view_controller.h b/ios/chrome/browser/ui/payments/payment_request_picker_view_controller.h
index aacfa08f..ae692de 100644
--- a/ios/chrome/browser/ui/payments/payment_request_picker_view_controller.h
+++ b/ios/chrome/browser/ui/payments/payment_request_picker_view_controller.h
@@ -7,7 +7,10 @@
 
 #import <UIKit/UIKit.h>
 
+#import "ios/chrome/browser/ui/material_components/app_bar_presenting.h"
+
 extern NSString* const kPaymentRequestPickerRowAccessibilityID;
+extern NSString* const kPaymentRequestPickerViewControllerAccessibilityID;
 extern NSString* const kPaymentRequestPickerSearchBarAccessibilityID;
 
 @class PaymentRequestPickerViewController;
@@ -25,7 +28,8 @@
 
 // TableViewController that displays a searchable list of rows featuring a
 // selected row as well as an index list.
-@interface PaymentRequestPickerViewController : UITableViewController
+@interface PaymentRequestPickerViewController
+    : UITableViewController<AppBarPresenting>
 
 // The delegate to be notified when the user selects a row.
 @property(nonatomic, weak) id<PaymentRequestPickerViewControllerDelegate>
diff --git a/ios/chrome/browser/ui/payments/payment_request_picker_view_controller.mm b/ios/chrome/browser/ui/payments/payment_request_picker_view_controller.mm
index 1c544e2..4d435e3 100644
--- a/ios/chrome/browser/ui/payments/payment_request_picker_view_controller.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_picker_view_controller.mm
@@ -6,8 +6,13 @@
 
 #import "base/logging.h"
 #import "base/mac/foundation_util.h"
+#import "ios/chrome/browser/ui/material_components/utils.h"
 #import "ios/chrome/browser/ui/payments/payment_request_picker_row.h"
+#include "ios/chrome/browser/ui/ui_util.h"
+#import "ios/third_party/material_components_ios/src/components/AppBar/src/MaterialAppBar.h"
 #import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
+#include "third_party/libaddressinput/messages.h"
+#include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -15,13 +20,10 @@
 
 NSString* const kPaymentRequestPickerRowAccessibilityID =
     @"kPaymentRequestPickerRowAccessibilityID";
-NSString* const kPaymentRequestPickerSearchBarAccessibilityID =
-    @"kPaymentRequestPickerSearchBarAccessibilityID";
-
-namespace {
 NSString* const kPaymentRequestPickerViewControllerAccessibilityID =
     @"kPaymentRequestPickerViewControllerAccessibilityID";
-}  // namespace
+NSString* const kPaymentRequestPickerSearchBarAccessibilityID =
+    @"kPaymentRequestPickerSearchBarAccessibilityID";
 
 @interface PaymentRequestPickerViewController ()<UISearchResultsUpdating>
 
@@ -43,6 +45,8 @@
 @end
 
 @implementation PaymentRequestPickerViewController
+
+@synthesize appBar = _appBar;
 @synthesize searchController = _searchController;
 @synthesize allRows = _allRows;
 @synthesize displayedRows = _displayedRows;
@@ -54,6 +58,8 @@
                     selected:(PickerRow*)selectedRow {
   self = [super initWithStyle:UITableViewStylePlain];
   if (self) {
+    self.title =
+        l10n_util::GetNSString(IDS_LIBADDRESSINPUT_COUNTRY_OR_REGION_LABEL);
     self.allRows = [rows sortedArrayUsingComparator:^NSComparisonResult(
                              PickerRow* row1, PickerRow* row2) {
       return [row1.label localizedCaseInsensitiveCompare:row2.label];
@@ -61,6 +67,10 @@
     self.selectedRow = selectedRow;
     // Default to displaying all the rows.
     self.displayedRows = self.allRows;
+
+    _appBar = [[MDCAppBar alloc] init];
+    [self addChildViewController:_appBar.headerViewController];
+    ConfigureAppBarWithCardStyle(_appBar);
   }
   return self;
 }
@@ -78,10 +88,34 @@
 - (void)viewDidLoad {
   [super viewDidLoad];
 
+  self.tableView.delegate = self;
+  self.tableView.layoutMargins = UIEdgeInsetsZero;
+  self.tableView.separatorInset = UIEdgeInsetsZero;
+
+  const bool isFullScreen =
+      !IsIPadIdiom() || ([self navigationController].modalPresentationStyle !=
+                         UIModalPresentationFormSheet);
+
+  if (isFullScreen && [self navigationController].navigationBarHidden) {
+    // TODO(crbug.com/767428): When shown full screen, the UITableViewController
+    // uses the full screen even when the status bar is present, but insets the
+    // section headers by the size of the status bar. This will inset the
+    // content by the same amount, to ensure they line up properly. Also insets
+    // by one more pixel to hide the one pixel gap left in between the
+    // navigation bar and the UITableView.
+    const UIEdgeInsets statusBarInset =
+        UIEdgeInsetsMake(-1 - StatusBarHeight(), 0, 0, 0);
+    self.tableView.contentInset = statusBarInset;
+    self.tableView.scrollIndicatorInsets = statusBarInset;
+  }
+
   self.tableView.rowHeight = MDCCellDefaultOneLineHeight;
   self.tableView.accessibilityIdentifier =
       kPaymentRequestPickerViewControllerAccessibilityID;
 
+  self.tableView.sectionIndexBackgroundColor = [UIColor clearColor];
+  self.tableView.sectionIndexTrackingBackgroundColor = [UIColor clearColor];
+
   self.searchController =
       [[UISearchController alloc] initWithSearchResultsController:nil];
   self.searchController.searchResultsUpdater = self;
@@ -96,6 +130,18 @@
   // context. Make this class the presentation context so that the search
   // controller does not present on top of the navigation controller.
   self.definesPresentationContext = YES;
+
+  self.appBar.headerViewController.headerView.trackingScrollView =
+      self.tableView;
+  [self.appBar addSubviewsToParent];
+}
+
+- (UIViewController*)childViewControllerForStatusBarHidden {
+  return self.appBar.headerViewController;
+}
+
+- (UIViewController*)childViewControllerForStatusBarStyle {
+  return self.appBar.headerViewController;
 }
 
 #pragma mark - UITableViewDataSource
@@ -141,25 +187,51 @@
   return cell;
 }
 
+#pragma mark - UIScrollViewDelegate
+
+- (void)scrollViewDidScroll:(UIScrollView*)scrollView {
+  MDCFlexibleHeaderView* headerView =
+      self.appBar.headerViewController.headerView;
+  if (scrollView == headerView.trackingScrollView) {
+    [headerView trackingScrollViewDidScroll];
+  }
+}
+
+- (void)scrollViewDidEndDecelerating:(UIScrollView*)scrollView {
+  MDCFlexibleHeaderView* headerView =
+      self.appBar.headerViewController.headerView;
+  if (scrollView == headerView.trackingScrollView) {
+    [headerView trackingScrollViewDidEndDecelerating];
+  }
+}
+
+- (void)scrollViewDidEndDragging:(UIScrollView*)scrollView
+                  willDecelerate:(BOOL)decelerate {
+  MDCFlexibleHeaderView* headerView =
+      self.appBar.headerViewController.headerView;
+  if (scrollView == headerView.trackingScrollView) {
+    [headerView trackingScrollViewDidEndDraggingWillDecelerate:decelerate];
+  }
+}
+
+- (void)scrollViewWillEndDragging:(UIScrollView*)scrollView
+                     withVelocity:(CGPoint)velocity
+              targetContentOffset:(inout CGPoint*)targetContentOffset {
+  MDCFlexibleHeaderView* headerView =
+      self.appBar.headerViewController.headerView;
+  if (scrollView == headerView.trackingScrollView) {
+    [headerView
+        trackingScrollViewWillEndDraggingWithVelocity:velocity
+                                  targetContentOffset:targetContentOffset];
+  }
+}
+
 #pragma mark - UITableViewDelegate
 
 - (void)tableView:(UITableView*)tableView
     didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
-  if (self.selectedRow) {
-    NSIndexPath* oldSelectedIndexPath = [self indexPathForRow:self.selectedRow];
-    self.selectedRow = nil;
-    // Reload the previously selected row if it is displaying.
-    if (oldSelectedIndexPath) {
-      [self.tableView reloadRowsAtIndexPaths:@[ oldSelectedIndexPath ]
-                            withRowAnimation:UITableViewRowAnimationFade];
-    }
-  }
-
   self.selectedRow =
       [[self rowsInSection:indexPath.section] objectAtIndex:indexPath.row];
-  // Reload the newly selected row.
-  [self.tableView reloadRowsAtIndexPaths:@[ indexPath ]
-                        withRowAnimation:UITableViewRowAnimationFade];
 
   [_delegate paymentRequestPickerViewController:self
                                    didSelectRow:self.selectedRow];
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_toolbar.mm b/ios/chrome/browser/ui/reading_list/reading_list_toolbar.mm
index d83b989c..8b3ff0f 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_toolbar.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_toolbar.mm
@@ -196,20 +196,8 @@
 
     [self addSubview:_stackView];
     _stackView.translatesAutoresizingMaskIntoConstraints = NO;
-    if (@available(iOS 11.0, *)) {
-      [NSLayoutConstraint activateConstraints:@[
-        [_stackView.topAnchor
-            constraintEqualToAnchor:self.safeAreaLayoutGuide.topAnchor],
-        [_stackView.leadingAnchor
-            constraintEqualToAnchor:self.safeAreaLayoutGuide.leadingAnchor],
-        [_stackView.trailingAnchor
-            constraintEqualToAnchor:self.safeAreaLayoutGuide.trailingAnchor],
-        [_stackView.bottomAnchor
-            constraintEqualToAnchor:self.safeAreaLayoutGuide.bottomAnchor],
-      ]];
-    } else {
-      AddSameConstraints(_stackView, self);
-    }
+
+    PinToSafeArea(_stackView, self);
     _heightConstraint = [_stackView.heightAnchor
         constraintEqualToConstant:kToolbarNormalHeight];
     _heightConstraint.active = YES;
diff --git a/ios/chrome/browser/ui/settings/password_details_collection_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/password_details_collection_view_controller_unittest.mm
index 1d975f9..1aa3bf5 100644
--- a/ios/chrome/browser/ui/settings/password_details_collection_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/password_details_collection_view_controller_unittest.mm
@@ -86,8 +86,7 @@
 class PasswordDetailsCollectionViewControllerTest
     : public CollectionViewControllerTest {
  protected:
-  PasswordDetailsCollectionViewControllerTest()
-      : thread_bundle_(web::TestWebThreadBundle::REAL_DB_THREAD) {
+  PasswordDetailsCollectionViewControllerTest() {
     origin_ = kSite;
     form_.username_value = base::SysNSStringToUTF16(kUsername);
     form_.password_value = base::SysNSStringToUTF16(kPassword);
diff --git a/ios/chrome/browser/ui/settings/save_passwords_collection_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/save_passwords_collection_view_controller_unittest.mm
index a6581138..8317db8 100644
--- a/ios/chrome/browser/ui/settings/save_passwords_collection_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/save_passwords_collection_view_controller_unittest.mm
@@ -34,8 +34,7 @@
 class SavePasswordsCollectionViewControllerTest
     : public CollectionViewControllerTest {
  protected:
-  SavePasswordsCollectionViewControllerTest()
-      : thread_bundle_(web::TestWebThreadBundle::REAL_DB_THREAD) {}
+  SavePasswordsCollectionViewControllerTest() = default;
 
   void SetUp() override {
     TestChromeBrowserState::Builder test_cbs_builder;
diff --git a/ios/chrome/browser/ui/util/constraints_ui_util.h b/ios/chrome/browser/ui/util/constraints_ui_util.h
index 349fbe57..d743a83 100644
--- a/ios/chrome/browser/ui/util/constraints_ui_util.h
+++ b/ios/chrome/browser/ui/util/constraints_ui_util.h
@@ -97,6 +97,10 @@
 // trailing, top and bottom anchors.
 void AddSameConstraints(UIView* view1, UIView* view2);
 
+// Adds constraints to make |innerView| leading, trailing, top and bottom
+// anchors equals to |outerView| safe area (or view bounds) anchors.
+void PinToSafeArea(UIView* innerView, UIView* outerView);
+
 // Returns a safeAreaLayoutGuide for a given view.
 UILayoutGuide* SafeAreaLayoutGuideForView(UIView* view);
 
diff --git a/ios/chrome/browser/ui/util/constraints_ui_util.mm b/ios/chrome/browser/ui/util/constraints_ui_util.mm
index d4a99e58..dfd0e7bd 100644
--- a/ios/chrome/browser/ui/util/constraints_ui_util.mm
+++ b/ios/chrome/browser/ui/util/constraints_ui_util.mm
@@ -133,6 +133,23 @@
   ]];
 }
 
+void PinToSafeArea(UIView* innerView, UIView* outerView) {
+  if (@available(iOS 11.0, *)) {
+    [NSLayoutConstraint activateConstraints:@[
+      [innerView.topAnchor
+          constraintEqualToAnchor:outerView.safeAreaLayoutGuide.topAnchor],
+      [innerView.leadingAnchor
+          constraintEqualToAnchor:outerView.safeAreaLayoutGuide.leadingAnchor],
+      [innerView.trailingAnchor
+          constraintEqualToAnchor:outerView.safeAreaLayoutGuide.trailingAnchor],
+      [innerView.bottomAnchor
+          constraintEqualToAnchor:outerView.safeAreaLayoutGuide.bottomAnchor],
+    ]];
+  } else {
+    AddSameConstraints(innerView, outerView);
+  }
+}
+
 UILayoutGuide* SafeAreaLayoutGuideForView(UIView* view) {
   if (@available(iOS 11, *)) {
     return view.safeAreaLayoutGuide;
diff --git a/ios/chrome/test/earl_grey/chrome_ios_eg_test.gni b/ios/chrome/test/earl_grey/chrome_ios_eg_test.gni
index f0972f9..ee51dfd 100644
--- a/ios/chrome/test/earl_grey/chrome_ios_eg_test.gni
+++ b/ios/chrome/test/earl_grey/chrome_ios_eg_test.gni
@@ -125,7 +125,10 @@
     if (!defined(bundle_deps)) {
       bundle_deps = []
     }
-    bundle_deps += [ "//ios/chrome/app/resources" ]
+    bundle_deps += [
+      "//ios/chrome/app/resources",
+      ios_application_icons_target,
+    ]
 
     if (!defined(extra_substitutions)) {
       extra_substitutions = []
diff --git a/ios/clean/chrome/app/BUILD.gn b/ios/clean/chrome/app/BUILD.gn
index ef91046..300a03e 100644
--- a/ios/clean/chrome/app/BUILD.gn
+++ b/ios/clean/chrome/app/BUILD.gn
@@ -50,7 +50,10 @@
     "//ios/chrome/app:tests_fake_hook",
   ]
 
-  bundle_deps = [ "//ios/chrome/app/resources" ]
+  bundle_deps = [
+    "//ios/chrome/app/resources",
+    ios_application_icons_target,
+  ]
 
   extra_substitutions = [
     "CONTENT_WIDGET_EXTENSION_BUNDLE_ID=$chromium_bundle_id.ContentTodayExtension",
diff --git a/ios/clean/chrome/test/perf/BUILD.gn b/ios/clean/chrome/test/perf/BUILD.gn
index 0ead211c..e83caa8 100644
--- a/ios/clean/chrome/test/perf/BUILD.gn
+++ b/ios/clean/chrome/test/perf/BUILD.gn
@@ -6,6 +6,7 @@
 import("//build/config/mac/base_rules.gni")
 import("//build/mac/tweak_info_plist.gni")
 import("//ios/build/chrome_build.gni")
+import("//ios/public/provider/chrome/browser/build_config.gni")
 import("//ios/third_party/earl_grey/ios_eg_test.gni")
 
 tweak_info_plist("info_plist") {
@@ -52,7 +53,10 @@
     "//ios/testing/perf:startup",
   ]
 
-  bundle_deps = [ "//ios/chrome/app/resources" ]
+  bundle_deps = [
+    "//ios/chrome/app/resources",
+    ios_application_icons_target,
+  ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
 
diff --git a/ios/public/provider/chrome/browser/build_config.gni b/ios/public/provider/chrome/browser/build_config.gni
index aaf2ee9..321634db 100644
--- a/ios/public/provider/chrome/browser/build_config.gni
+++ b/ios/public/provider/chrome/browser/build_config.gni
@@ -3,6 +3,11 @@
 # found in the LICENSE file.
 
 declare_args() {
+  # List of variants of "//ios/chrome/app:chrome" to build. Each variant will
+  # have the same binary but can add deps to customizes their resources. The
+  # application bundles will be found in $root_out_dir/variants/$variant.name.
+  ios_chrome_app_variants = []
+
   # Label of the target providing application icons.  This target must be a
   # bundle_data target that copy Icon-*.png files in the application bundle.
   ios_application_icons_target = "//ios/chrome/app/resources:chromium_icons"
diff --git a/ios/showcase/payments/sc_payments_picker_egtest.mm b/ios/showcase/payments/sc_payments_picker_egtest.mm
index 9fc268a..775c6ee 100644
--- a/ios/showcase/payments/sc_payments_picker_egtest.mm
+++ b/ios/showcase/payments/sc_payments_picker_egtest.mm
@@ -81,42 +81,58 @@
   [super tearDown];
 }
 
+- (void)scrollToTop {
+  // Scroll to the top, starting at the center of the screen.
+  [[EarlGrey selectElementWithMatcher:grey_kindOfClass([UITableView class])]
+      performAction:grey_scrollToContentEdgeWithStartPoint(kGREYContentEdgeTop,
+                                                           0.5f, 0.5f)];
+}
+
+- (void)assertSection:(NSString*)label visible:(BOOL)visible {
+  [[[EarlGrey selectElementWithMatcher:SectionWithTitle(label)]
+         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 200)
+      onElementWithMatcher:grey_kindOfClass([UITableView class])]
+      assertWithMatcher:visible ? grey_notNil() : grey_nil()];
+
+  // Return to top, to ensure we are in the same state before every search,
+  // as each search action may scroll down, thereby making it impossible for
+  // the next search action to work properly, as we only scroll down when
+  // searching.
+  [self scrollToTop];
+}
+
+- (void)assertRow:(NSString*)label
+         selected:(BOOL)selected
+          visible:(BOOL)visible {
+  [[[EarlGrey selectElementWithMatcher:RowWithLabel(label, selected)]
+         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 200)
+      onElementWithMatcher:grey_kindOfClass([UITableView class])]
+      assertWithMatcher:visible ? grey_notNil() : grey_nil()];
+
+  // Return to top, to ensure we are in the same state before every search,
+  // as each search action may scroll down, thereby making it impossible for
+  // the next search action to work properly, as we only scroll down when
+  // searching.
+  [self scrollToTop];
+}
+
 // Tests if all the expected rows and sections are present and the expected row
 // is selected.
 - (void)testVerifyRowsAndSection {
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"B")]
-      assertWithMatcher:grey_notNil()];
+  [self assertSection:@"B" visible:YES];
+  [self assertRow:@"Belgium" selected:NO visible:YES];
+  [self assertRow:@"Brazil" selected:NO visible:YES];
 
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Belgium", NO)]
-      assertWithMatcher:grey_notNil()];
+  [self assertSection:@"C" visible:YES];
+  [self assertRow:@"Canada" selected:NO visible:YES];
+  [self assertRow:@"Chile" selected:NO visible:YES];
+  [self assertRow:@"China" selected:YES visible:YES];
 
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Brazil", NO)]
-      assertWithMatcher:grey_notNil()];
+  [self assertSection:@"E" visible:YES];
+  [self assertRow:@"España" selected:NO visible:YES];
 
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"C")]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Canada", NO)]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Chile", NO)]
-      assertWithMatcher:grey_notNil()];
-
-  // 'China' is selected.
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"China", YES)]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"E")]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"España", NO)]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"M")]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"México", NO)]
-      assertWithMatcher:grey_notNil()];
+  [self assertSection:@"M" visible:YES];
+  [self assertRow:@"México" selected:NO visible:YES];
 }
 
 // Tests if filtering works.
@@ -127,49 +143,28 @@
     EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 11.");
   }
 
+  [self scrollToTop];
+
   // Type 'c' in the search bar.
   [[EarlGrey
       selectElementWithMatcher:
           grey_accessibilityID(kPaymentRequestPickerSearchBarAccessibilityID)]
       performAction:grey_typeText(@"c")];
 
-  // Section 'B' should not be visible.
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"B")]
-      assertWithMatcher:grey_nil()];
+  [self assertSection:@"B" visible:NO];
+  [self assertRow:@"Belgium" selected:NO visible:NO];
+  [self assertRow:@"Brazil" selected:NO visible:NO];
 
-  // 'Belgium' should not be visible.
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Belgium", NO)]
-      assertWithMatcher:grey_nil()];
+  [self assertSection:@"C" visible:YES];
+  [self assertRow:@"Canada" selected:NO visible:YES];
+  [self assertRow:@"Chile" selected:NO visible:YES];
+  [self assertRow:@"China" selected:YES visible:YES];
 
-  // 'Brazil' should not be visible.
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Brazil", NO)]
-      assertWithMatcher:grey_nil()];
+  [self assertSection:@"E" visible:NO];
+  [self assertRow:@"España" selected:NO visible:NO];
 
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"C")]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Canada", NO)]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Chile", NO)]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"China", YES)]
-      assertWithMatcher:grey_notNil()];
-
-  // Section 'E' should not be visible.
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"E")]
-      assertWithMatcher:grey_nil()];
-
-  // 'España' should not be visible.
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"España", NO)]
-      assertWithMatcher:grey_nil()];
-
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"M")]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"México", NO)]
-      assertWithMatcher:grey_notNil()];
+  [self assertSection:@"M" visible:YES];
+  [self assertRow:@"México" selected:NO visible:YES];
 
   // Type 'hi' in the search bar. So far we have typed "chi".
   [[EarlGrey
@@ -177,46 +172,20 @@
           grey_accessibilityID(kPaymentRequestPickerSearchBarAccessibilityID)]
       performAction:grey_typeText(@"hi")];
 
-  // Section 'B' should not be visible.
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"B")]
-      assertWithMatcher:grey_nil()];
+  [self assertSection:@"B" visible:NO];
+  [self assertRow:@"Belgium" selected:NO visible:NO];
+  [self assertRow:@"Brazil" selected:NO visible:NO];
 
-  // 'Belgium' should not be visible.
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Belgium", NO)]
-      assertWithMatcher:grey_nil()];
+  [self assertSection:@"C" visible:YES];
+  [self assertRow:@"Canada" selected:NO visible:NO];
+  [self assertRow:@"Chile" selected:NO visible:YES];
+  [self assertRow:@"China" selected:YES visible:YES];
 
-  // 'Brazil' should not be visible.
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Brazil", NO)]
-      assertWithMatcher:grey_nil()];
+  [self assertSection:@"E" visible:NO];
+  [self assertRow:@"España" selected:NO visible:NO];
 
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"C")]
-      assertWithMatcher:grey_notNil()];
-
-  // 'Canada' should not be visible.
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Canada", NO)]
-      assertWithMatcher:grey_nil()];
-
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Chile", NO)]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"China", YES)]
-      assertWithMatcher:grey_notNil()];
-
-  // Section 'E' should not be visible.
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"E")]
-      assertWithMatcher:grey_nil()];
-
-  // 'España' should not be visible.
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"España", NO)]
-      assertWithMatcher:grey_nil()];
-
-  // Section 'M' should not be visible.
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"M")]
-      assertWithMatcher:grey_nil()];
-
-  // 'México' should not be visible.
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"México", NO)]
-      assertWithMatcher:grey_nil()];
+  [self assertSection:@"M" visible:NO];
+  [self assertRow:@"México" selected:NO visible:NO];
 
   // Type 'l' in the search bar. So far we have typed "chil".
   [[EarlGrey
@@ -224,90 +193,41 @@
           grey_accessibilityID(kPaymentRequestPickerSearchBarAccessibilityID)]
       performAction:grey_typeText(@"l")];
 
-  // Section 'B' should not be visible.
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"B")]
-      assertWithMatcher:grey_nil()];
+  [self assertSection:@"B" visible:NO];
+  [self assertRow:@"Belgium" selected:NO visible:NO];
+  [self assertRow:@"Brazil" selected:NO visible:NO];
 
-  // 'Belgium' should not be visible.
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Belgium", NO)]
-      assertWithMatcher:grey_nil()];
+  [self assertSection:@"C" visible:YES];
+  [self assertRow:@"Canada" selected:NO visible:NO];
+  [self assertRow:@"Chile" selected:NO visible:YES];
+  [self assertRow:@"China" selected:YES visible:NO];
 
-  // 'Brazil' should not be visible.
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Brazil", NO)]
-      assertWithMatcher:grey_nil()];
+  [self assertSection:@"E" visible:NO];
+  [self assertRow:@"España" selected:NO visible:NO];
 
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"C")]
-      assertWithMatcher:grey_notNil()];
-
-  // 'Canada' should not be visible.
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Canada", NO)]
-      assertWithMatcher:grey_nil()];
-
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Chile", NO)]
-      assertWithMatcher:grey_notNil()];
-
-  // 'China' should not be visible.
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"China", YES)]
-      assertWithMatcher:grey_nil()];
-
-  // Section 'E' should not be visible.
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"E")]
-      assertWithMatcher:grey_nil()];
-
-  // 'España' should not be visible.
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"España", NO)]
-      assertWithMatcher:grey_nil()];
-
-  // Section 'M' should not be visible.
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"M")]
-      assertWithMatcher:grey_nil()];
-
-  // 'México' should not be visible.
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"México", NO)]
-      assertWithMatcher:grey_nil()];
+  [self assertSection:@"M" visible:NO];
+  [self assertRow:@"México" selected:NO visible:NO];
 
   // Cancel filtering the text in the search bar.
   [[EarlGrey selectElementWithMatcher:CancelButton()] performAction:grey_tap()];
 
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"B")]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Belgium", NO)]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Brazil", NO)]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"C")]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Canada", NO)]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Chile", NO)]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"China", YES)]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"E")]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"España", NO)]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:SectionWithTitle(@"M")]
-      assertWithMatcher:grey_notNil()];
-
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"México", NO)]
-      assertWithMatcher:grey_notNil()];
+  [self assertSection:@"B" visible:YES];
+  [self assertRow:@"Belgium" selected:NO visible:YES];
+  [self assertRow:@"Brazil" selected:NO visible:YES];
+  [self assertSection:@"C" visible:YES];
+  [self assertRow:@"Canada" selected:NO visible:YES];
+  [self assertRow:@"Chile" selected:NO visible:YES];
+  [self assertRow:@"China" selected:YES visible:YES];
+  [self assertSection:@"E" visible:YES];
+  [self assertRow:@"España" selected:NO visible:YES];
+  [self assertSection:@"M" visible:YES];
+  [self assertRow:@"México" selected:NO visible:YES];
 }
 
 // Tests that tapping a row should make it the selected row.
 - (void)testVerifySelection {
   // 'China' is selected.
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"China", YES)]
-      assertWithMatcher:grey_notNil()];
+  [self assertRow:@"China" selected:YES visible:YES];
 
   // 'Canada' is not selected. Tap it.
   [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Canada", NO)]
@@ -323,8 +243,7 @@
       performAction:grey_tap()];
 
   // 'China' is not selected anymore.
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"China", NO)]
-      assertWithMatcher:grey_notNil()];
+  [self assertRow:@"China" selected:NO visible:YES];
 
   // Now 'Canada' is selected. Tap it again.
   [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Canada", YES)]
@@ -340,12 +259,10 @@
       performAction:grey_tap()];
 
   // 'China' is still not selected.
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"China", NO)]
-      assertWithMatcher:grey_notNil()];
+  [self assertRow:@"China" selected:NO visible:YES];
 
   // 'Canada' is still selected.
-  [[EarlGrey selectElementWithMatcher:RowWithLabel(@"Canada", YES)]
-      assertWithMatcher:grey_notNil()];
+  [self assertRow:@"Canada" selected:YES visible:YES];
 }
 
 @end
diff --git a/ios/third_party/material_components_ios/BUILD.gn b/ios/third_party/material_components_ios/BUILD.gn
index 86cdf06..37035ed 100644
--- a/ios/third_party/material_components_ios/BUILD.gn
+++ b/ios/third_party/material_components_ios/BUILD.gn
@@ -298,7 +298,7 @@
       "src/components/Collections/src/MaterialCollections.bundle/Resources/$locale.lproj/MaterialCollections.strings",
     ]
     outputs = [
-      "{{bundle_root_dir}}/MaterialCollections.bundle/Resources/$locale.lproj/{{source_file_part}}",
+      "{{bundle_resources_dir}}/MaterialCollections.bundle/Resources/$locale.lproj/{{source_file_part}}",
     ]
   }
 
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index d0e8f90..bb547db 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -49,8 +49,6 @@
   ]
 
   sources = [
-    "active_state_manager_impl.h",
-    "active_state_manager_impl.mm",
     "browser_state.mm",
     "browser_url_rewriter_impl.h",
     "browser_url_rewriter_impl.mm",
@@ -114,7 +112,6 @@
     "net/web_http_protocol_handler_delegate.h",
     "net/web_http_protocol_handler_delegate.mm",
     "payments/payment_request.cc",
-    "public/active_state_manager.h",
     "public/block_types.h",
     "public/browser_state.h",
     "public/browser_url_rewriter.h",
@@ -453,7 +450,6 @@
   ]
 
   sources = [
-    "active_state_manager_impl_unittest.mm",
     "browser_state_unittest.cc",
     "history_state_util_unittest.mm",
     "payments/payment_request_unittest.cc",
diff --git a/ios/web/active_state_manager_impl.mm b/ios/web/active_state_manager_impl.mm
deleted file mode 100644
index 8dc052d..0000000
--- a/ios/web/active_state_manager_impl.mm
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2015 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 "ios/web/active_state_manager_impl.h"
-
-#include "base/logging.h"
-#include "ios/web/public/browser_state.h"
-#include "ios/web/public/web_thread.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace web {
-
-ActiveStateManagerImpl::ActiveStateManagerImpl(BrowserState* browser_state)
-    : browser_state_(browser_state), active_(false) {
-  DCHECK_CURRENTLY_ON(WebThread::UI);
-  DCHECK(browser_state_);
-}
-
-ActiveStateManagerImpl::~ActiveStateManagerImpl() {
-  for (auto& observer : observer_list_)
-    observer.WillBeDestroyed();
-  DCHECK(!IsActive());
-}
-
-void ActiveStateManagerImpl::SetActive(bool active) {
-  DCHECK_CURRENTLY_ON(WebThread::UI);
-
-  if (active == active_) {
-    return;
-  }
-  active_ = active;
-
-  if (active) {
-    for (auto& observer : observer_list_)
-      observer.OnActive();
-  } else {
-    for (auto& observer : observer_list_)
-      observer.OnInactive();
-  }
-}
-
-bool ActiveStateManagerImpl::IsActive() {
-  DCHECK_CURRENTLY_ON(WebThread::UI);
-  return active_;
-}
-
-void ActiveStateManagerImpl::AddObserver(ActiveStateManager::Observer* obs) {
-  DCHECK_CURRENTLY_ON(WebThread::UI);
-  observer_list_.AddObserver(obs);
-}
-
-void ActiveStateManagerImpl::RemoveObserver(ActiveStateManager::Observer* obs) {
-  DCHECK_CURRENTLY_ON(WebThread::UI);
-  observer_list_.RemoveObserver(obs);
-}
-
-}  // namespace web
diff --git a/ios/web/app/web_main_loop.h b/ios/web/app/web_main_loop.h
index ca18981..89882be 100644
--- a/ios/web/app/web_main_loop.h
+++ b/ios/web/app/web_main_loop.h
@@ -85,7 +85,6 @@
   std::unique_ptr<WebThreadImpl> main_thread_;
 
   // Members initialized in |RunMainMessageLoopParts()| ------------------------
-  std::unique_ptr<WebThreadImpl> db_thread_;
   std::unique_ptr<WebThreadImpl> io_thread_;
 
   // Members initialized in |WebThreadsStarted()| --------------------------
diff --git a/ios/web/app/web_main_loop.mm b/ios/web/app/web_main_loop.mm
index 779fcbf1..7d9c08d 100644
--- a/ios/web/app/web_main_loop.mm
+++ b/ios/web/app/web_main_loop.mm
@@ -133,44 +133,13 @@
 
   base::Thread::Options io_message_loop_options;
   io_message_loop_options.message_loop_type = base::MessageLoop::TYPE_IO;
+  io_thread_ = std::make_unique<WebThreadImpl>(WebThread::IO);
+  io_thread_->StartWithOptions(io_message_loop_options);
 
-  // Start threads in the order they occur in the WebThread::ID
-  // enumeration, except for WebThread::UI which is the main
-  // thread.
-  //
-  // Must be size_t so we can increment it.
-  for (size_t thread_id = WebThread::UI + 1; thread_id < WebThread::ID_COUNT;
-       ++thread_id) {
-    std::unique_ptr<WebThreadImpl>* thread_to_start = nullptr;
-    base::Thread::Options options;
+  // Only start IO thread above as this is the only WebThread besides UI (which
+  // is the main thread).
+  static_assert(WebThread::ID_COUNT == 2, "Unhandled WebThread");
 
-    switch (thread_id) {
-      // TODO(rohitrao): We probably do not need all of these threads.  Remove
-      // the ones that serve no purpose.  http://crbug.com/365909
-      case WebThread::DB:
-        thread_to_start = &db_thread_;
-        options.timer_slack = base::TIMER_SLACK_MAXIMUM;
-        break;
-      case WebThread::IO:
-        thread_to_start = &io_thread_;
-        options = io_message_loop_options;
-        break;
-      case WebThread::UI:
-      case WebThread::ID_COUNT:
-      default:
-        NOTREACHED();
-        break;
-    }
-
-    WebThread::ID id = static_cast<WebThread::ID>(thread_id);
-
-    if (thread_to_start) {
-      (*thread_to_start).reset(new WebThreadImpl(id));
-      (*thread_to_start)->StartWithOptions(options);
-    } else {
-      NOTREACHED();
-    }
-  }
   created_threads_ = true;
   return result_code_;
 }
@@ -207,34 +176,11 @@
 
   service_manager_context_.reset();
 
-  // Must be size_t so we can subtract from it.
-  for (size_t thread_id = WebThread::ID_COUNT - 1;
-       thread_id >= (WebThread::UI + 1); --thread_id) {
-    // Find the thread object we want to stop. Looping over all valid
-    // WebThread IDs and DCHECKing on a missing case in the switch
-    // statement helps avoid a mismatch between this code and the
-    // WebThread::ID enumeration.
-    //
-    // The destruction order is the reverse order of occurrence in the
-    // WebThread::ID list. The rationale for the order is as
-    // follows (need to be filled in a bit):
-    //
-    //
-    // - (Not sure why DB stops last.)
-    switch (thread_id) {
-      case WebThread::DB:
-        db_thread_.reset();
-        break;
-      case WebThread::IO:
-        io_thread_.reset();
-        break;
-      case WebThread::UI:
-      case WebThread::ID_COUNT:
-      default:
-        NOTREACHED();
-        break;
-    }
-  }
+  io_thread_.reset();
+
+  // Only stop IO thread above as this is the only WebThread besides UI (which
+  // is the main thread).
+  static_assert(WebThread::ID_COUNT == 2, "Unhandled WebThread");
 
   // Shutdown TaskScheduler after the other threads. Other threads such as the
   // I/O thread may need to schedule work like closing files or flushing data
diff --git a/ios/web/browser_state.mm b/ios/web/browser_state.mm
index 01c9fa40..cdf1418 100644
--- a/ios/web/browser_state.mm
+++ b/ios/web/browser_state.mm
@@ -10,7 +10,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/process/process_handle.h"
-#include "ios/web/active_state_manager_impl.h"
 #include "ios/web/public/certificate_policy_cache.h"
 #include "ios/web/public/service_manager_connection.h"
 #include "ios/web/public/service_names.mojom.h"
@@ -37,7 +36,6 @@
 
 // Data key names.
 const char kCertificatePolicyCacheKeyName[] = "cert_policy_cache";
-const char kActiveStateManagerKeyName[] = "active_state_manager";
 const char kMojoWasInitialized[] = "mojo-was-initialized";
 const char kServiceManagerConnection[] = "service-manager-connection";
 const char kServiceUserId[] = "service-user-id";
@@ -118,29 +116,6 @@
   return handle->policy_cache;
 }
 
-// static
-bool BrowserState::HasActiveStateManager(BrowserState* browser_state) {
-  DCHECK_CURRENTLY_ON(WebThread::UI);
-  return browser_state->GetUserData(kActiveStateManagerKeyName) != nullptr;
-}
-
-// static
-ActiveStateManager* BrowserState::GetActiveStateManager(
-    BrowserState* browser_state) {
-  DCHECK_CURRENTLY_ON(WebThread::UI);
-  DCHECK(browser_state);
-
-  ActiveStateManagerImpl* active_state_manager =
-      static_cast<ActiveStateManagerImpl*>(
-          browser_state->GetUserData(kActiveStateManagerKeyName));
-  if (!active_state_manager) {
-    active_state_manager = new ActiveStateManagerImpl(browser_state);
-    browser_state->SetUserData(kActiveStateManagerKeyName,
-                               base::WrapUnique(active_state_manager));
-  }
-  return active_state_manager;
-}
-
 BrowserState::BrowserState() : url_data_manager_ios_backend_(nullptr) {
   // (Refcounted)?BrowserStateKeyedServiceFactories needs to be able to convert
   // a base::SupportsUserData to a BrowserState. Moreover, since the factories
diff --git a/ios/web/public/browser_state.h b/ios/web/public/browser_state.h
index e5571a1..2ffc93f 100644
--- a/ios/web/public/browser_state.h
+++ b/ios/web/public/browser_state.h
@@ -21,7 +21,6 @@
 }
 
 namespace web {
-class ActiveStateManager;
 class CertificatePolicyCache;
 class ServiceManagerConnection;
 class URLDataManagerIOS;
@@ -39,16 +38,6 @@
   static scoped_refptr<CertificatePolicyCache> GetCertificatePolicyCache(
       BrowserState* browser_state);
 
-  // Returns whether |browser_state| has an associated ActiveStateManager.
-  // Must only be accessed from main thread.
-  static bool HasActiveStateManager(BrowserState* browser_state);
-
-  // Returns the ActiveStateManager associated with |browser_state.|
-  // Lazily creates one if an ActiveStateManager is not already associated with
-  // the |browser_state|. |browser_state| cannot be a nullptr.  Must be accessed
-  // only from the main thread.
-  static ActiveStateManager* GetActiveStateManager(BrowserState* browser_state);
-
   // Returns whether this BrowserState is incognito. Default is false.
   virtual bool IsOffTheRecord() const = 0;
 
diff --git a/ios/web/public/test/test_web_thread_bundle.h b/ios/web/public/test/test_web_thread_bundle.h
index 1785dd7..11173102 100644
--- a/ios/web/public/test/test_web_thread_bundle.h
+++ b/ios/web/public/test/test_web_thread_bundle.h
@@ -49,8 +49,7 @@
   enum Options {
     DEFAULT = 0,
     IO_MAINLOOP = 1 << 0,
-    REAL_DB_THREAD = 1 << 1,
-    REAL_IO_THREAD = 1 << 2,
+    REAL_IO_THREAD = 1 << 1,
   };
 
   TestWebThreadBundle();
@@ -63,7 +62,6 @@
 
   std::unique_ptr<base::test::ScopedTaskEnvironment> scoped_task_environment_;
   std::unique_ptr<TestWebThread> ui_thread_;
-  std::unique_ptr<TestWebThread> db_thread_;
   std::unique_ptr<TestWebThread> io_thread_;
 
   DISALLOW_COPY_AND_ASSIGN(TestWebThreadBundle);
diff --git a/ios/web/public/web_state/web_state_policy_decider.h b/ios/web/public/web_state/web_state_policy_decider.h
index 852b726..6b00225 100644
--- a/ios/web/public/web_state/web_state_policy_decider.h
+++ b/ios/web/public/web_state/web_state_policy_decider.h
@@ -21,14 +21,23 @@
   virtual ~WebStatePolicyDecider();
 
   // Asks the decider whether the navigation corresponding to |request| should
-  // be allowed to continue. Defaults to true if not overriden.
+  // be allowed to continue. Defaults to true if not overriden. Called before
+  // WebStateObserver::DidStartNavigation.
+  // Never called in the following cases:
+  //  - same-document back-forward and state change navigations
+  //  - CRWNativeContent navigations
   virtual bool ShouldAllowRequest(NSURLRequest* request,
                                   ui::PageTransition transition);
 
   // Asks the decider whether the navigation corresponding to |response| should
   // be allowed to continue. Defaults to true if not overriden.
   // |for_main_frame| indicates whether the frame being navigated is the main
-  // frame.
+  // frame. Called before WebStateObserver::DidFinishNavigation.
+  // Never called in the following cases:
+  //  - same-document navigations (unless ititiated via LoadURLWithParams)
+  //  - CRWNativeContent navigations
+  //  - going back after form submission navigation (except iOS 9)
+  //  - user-initiated POST navigation on iOS 9 and 10
   virtual bool ShouldAllowResponse(NSURLResponse* response,
                                    bool for_main_frame);
 
diff --git a/ios/web/public/web_thread.h b/ios/web/public/web_thread.h
index c2b8684..60c2642 100644
--- a/ios/web/public/web_thread.h
+++ b/ios/web/public/web_thread.h
@@ -65,9 +65,6 @@
     // The main thread in the browser.
     UI,
 
-    // This is the thread that interacts with the database.
-    DB,
-
     // This is the thread that processes non-blocking IO, i.e. IPC and network.
     // Blocking IO should happen in TaskScheduler.
     IO,
@@ -205,7 +202,6 @@
   // std::unique_ptr<Foo, web::WebThread::DeleteOnIOThread> ptr;
   struct DeleteOnUIThread : public DeleteOnThread<UI> {};
   struct DeleteOnIOThread : public DeleteOnThread<IO> {};
-  struct DeleteOnDBThread : public DeleteOnThread<DB> {};
 
  private:
   friend class WebThreadImpl;
diff --git a/ios/web/test/test_web_thread_bundle.cc b/ios/web/test/test_web_thread_bundle.cc
index 0a36aa9..22883ff 100644
--- a/ios/web/test/test_web_thread_bundle.cc
+++ b/ios/web/test/test_web_thread_bundle.cc
@@ -36,8 +36,6 @@
   base::RunLoop().RunUntilIdle();
   io_thread_.reset();
   base::RunLoop().RunUntilIdle();
-  db_thread_.reset();
-  base::RunLoop().RunUntilIdle();
   // This is the point at which the cache pool is normally shut down. So flush
   // it again in case any shutdown tasks have been posted to the pool from the
   // threads above.
@@ -59,14 +57,6 @@
   ui_thread_.reset(
       new TestWebThread(WebThread::UI, base::MessageLoop::current()));
 
-  if (options & TestWebThreadBundle::REAL_DB_THREAD) {
-    db_thread_.reset(new TestWebThread(WebThread::DB));
-    db_thread_->Start();
-  } else {
-    db_thread_.reset(
-        new TestWebThread(WebThread::DB, base::MessageLoop::current()));
-  }
-
   if (options & TestWebThreadBundle::REAL_IO_THREAD) {
     io_thread_.reset(new TestWebThread(WebThread::IO));
     io_thread_->StartIOThread();
diff --git a/ios/web/web_state/navigation_callbacks_inttest.mm b/ios/web/web_state/navigation_callbacks_inttest.mm
index 3f54caf6..751d5dc 100644
--- a/ios/web/web_state/navigation_callbacks_inttest.mm
+++ b/ios/web/web_state/navigation_callbacks_inttest.mm
@@ -337,9 +337,9 @@
   // Perform new page navigation.
   NavigationContext* context = nullptr;
   EXPECT_CALL(*observer_, DidStartLoading());
+  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidStartNavigation(_))
       .WillOnce(VerifyNewPageStartedContext(web_state(), url, &context));
-  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
   EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true))
       .WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidFinishNavigation(_))
@@ -357,8 +357,8 @@
 
   // Perform new page navigation.
   EXPECT_CALL(*observer_, DidStartLoading());
-  EXPECT_CALL(*observer_, DidStartNavigation(_));
   EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
+  EXPECT_CALL(*observer_, DidStartNavigation(_));
   EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true))
       .WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidFinishNavigation(_));
@@ -368,9 +368,9 @@
   // Reload web page.
   NavigationContext* context = nullptr;
   EXPECT_CALL(*observer_, DidStartLoading());
+  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidStartNavigation(_))
       .WillOnce(VerifyReloadStartedContext(web_state(), url, &context));
-  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
   EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true))
       .WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidFinishNavigation(_))
@@ -398,9 +398,9 @@
   // Perform new page navigation.
   NavigationContext* context = nullptr;
   EXPECT_CALL(*observer_, DidStartLoading());
+  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidStartNavigation(_))
       .WillOnce(VerifyNewPageStartedContext(web_state(), url, &context));
-  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
   EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true))
       .WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidFinishNavigation(_))
@@ -411,12 +411,12 @@
   // Perform same-document navigation.
   const GURL hash_url = HttpServer::MakeUrl("http://chromium.test#1");
   EXPECT_CALL(*observer_, DidStartLoading());
+  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidStartNavigation(_))
       .WillOnce(VerifySameDocumentStartedContext(
           web_state(), hash_url, &context,
           ui::PageTransition::PAGE_TRANSITION_TYPED,
           /*renderer_initiated=*/false));
-  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
   // No ShouldAllowResponse callback for same-document navigations.
   EXPECT_CALL(*observer_, DidFinishNavigation(_))
       .WillOnce(VerifySameDocumentFinishedContext(
@@ -427,14 +427,15 @@
   LoadUrl(hash_url);
 
   // Perform same-document navigation by going back.
+  // No ShouldAllowRequest callback for same-document back-forward navigations.
   EXPECT_CALL(*observer_, DidStartLoading());
   EXPECT_CALL(*observer_, DidStartNavigation(_))
       .WillOnce(VerifySameDocumentStartedContext(
           web_state(), url, &context,
           ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
           /*renderer_initiated=*/false));
-  // No ShouldAllowRequest/ShouldAllowResponse callbacks for same-document
-  // back-forward navigations.
+  // No ShouldAllowResponse callbacks for same-document back-forward
+  // navigations.
   EXPECT_CALL(*observer_, DidFinishNavigation(_))
       .WillOnce(VerifySameDocumentFinishedContext(
           web_state(), url, &context,
@@ -456,9 +457,9 @@
   // Perform new page navigation.
   NavigationContext* context = nullptr;
   EXPECT_CALL(*observer_, DidStartLoading());
+  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidStartNavigation(_))
       .WillOnce(VerifyNewPageStartedContext(web_state(), url, &context));
-  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
   EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true))
       .WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidFinishNavigation(_))
@@ -495,9 +496,9 @@
   // Perform new page navigation.
   NavigationContext* context = nullptr;
   EXPECT_CALL(*observer_, DidStartLoading());
+  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidStartNavigation(_))
       .WillOnce(VerifyNewPageStartedContext(web_state(), url, &context));
-  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
   EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true))
       .WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidFinishNavigation(_))
@@ -523,13 +524,13 @@
 
   // Perform replace state using JavaScript.
   const GURL replace_url = HttpServer::MakeUrl("http://chromium.test/1.html");
+  // No ShouldAllowRequest callbacks for same-document push state navigations.
   EXPECT_CALL(*observer_, DidStartNavigation(_))
       .WillOnce(VerifySameDocumentStartedContext(
           web_state(), replace_url, &context,
           ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
           /*renderer_initiated=*/true));
-  // No ShouldAllowRequest/ShouldAllowResponse callbacks for same-document push
-  // state navigations.
+  // No ShouldAllowResponse callbacks for same-document push state navigations.
   EXPECT_CALL(*observer_, DidFinishNavigation(_))
       .WillOnce(VerifySameDocumentFinishedContext(
           web_state(), replace_url, &context,
@@ -569,10 +570,10 @@
   // Reload native content.
   NavigationContext* context = nullptr;
   EXPECT_CALL(*observer_, DidStartLoading());
+  // No ShouldAllowRequest callbacks for native content navigations.
   EXPECT_CALL(*observer_, DidStartNavigation(_))
       .WillOnce(VerifyReloadStartedContext(web_state(), url, &context));
-  // No ShouldAllowRequest/ShouldAllowResponse callbacks for native content
-  // navigations.
+  // No ShouldAllowResponse callbacks for native content navigations.
   EXPECT_CALL(*observer_, DidFinishNavigation(_))
       .WillOnce(VerifyReloadFinishedContext(web_state(), url, &context,
                                             false /* is_web_page */));
@@ -590,10 +591,10 @@
   // Perform new page navigation.
   NavigationContext* context = nullptr;
   EXPECT_CALL(*observer_, DidStartLoading());
+  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidStartNavigation(_))
       .WillOnce(VerifyPostStartedContext(web_state(), url, &context,
                                          /*renderer_initiated=*/false));
-  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
   if (@available(iOS 11, *)) {
     EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true))
         .WillOnce(Return(true));
@@ -624,8 +625,8 @@
 
   // Perform new page navigation.
   EXPECT_CALL(*observer_, DidStartLoading());
-  EXPECT_CALL(*observer_, DidStartNavigation(_));
   EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
+  EXPECT_CALL(*observer_, DidStartNavigation(_));
   EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true))
       .WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidFinishNavigation(_));
@@ -663,8 +664,8 @@
 
   // Perform new page navigation.
   EXPECT_CALL(*observer_, DidStartLoading());
-  EXPECT_CALL(*observer_, DidStartNavigation(_));
   EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
+  EXPECT_CALL(*observer_, DidStartNavigation(_));
   EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true))
       .WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidFinishNavigation(_));
@@ -686,10 +687,10 @@
   // Reload the page.
   NavigationContext* context = nullptr;
   EXPECT_CALL(*observer_, DidStartLoading());
+  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidStartNavigation(_))
       .WillOnce(VerifyPostStartedContext(web_state(), action, &context,
                                          /*renderer_initiated=*/true));
-  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
   EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true))
       .WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidFinishNavigation(_))
@@ -720,8 +721,8 @@
 
   // Perform new page navigation.
   EXPECT_CALL(*observer_, DidStartLoading());
-  EXPECT_CALL(*observer_, DidStartNavigation(_));
   EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
+  EXPECT_CALL(*observer_, DidStartNavigation(_));
   EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true))
       .WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidFinishNavigation(_));
@@ -742,8 +743,8 @@
 
   // Go Back.
   EXPECT_CALL(*observer_, DidStartLoading());
-  EXPECT_CALL(*observer_, DidStartNavigation(_));
   EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
+  EXPECT_CALL(*observer_, DidStartNavigation(_));
   if (@available(iOS 10, *)) {
     // Starting from iOS10, ShouldAllowResponse is not called when going back
     // after form submission.
@@ -760,10 +761,10 @@
   // Go forward.
   NavigationContext* context = nullptr;
   EXPECT_CALL(*observer_, DidStartLoading());
+  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidStartNavigation(_))
       .WillOnce(VerifyPostStartedContext(web_state(), action, &context,
                                          /*renderer_initiated=*/false));
-  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).WillOnce(Return(true));
   EXPECT_CALL(*observer_, DidFinishNavigation(_))
       .WillOnce(VerifyPostFinishedContext(web_state(), action, &context,
                                           /*renderer_initiated=*/false));
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index b72f6b9..0bbfe75 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -1474,7 +1474,7 @@
   context->SetIsSameDocument(sameDocumentNavigation);
 
   _webStateImpl->SetIsLoading(true);
-  _webStateImpl->OnNavigationStarted(context.get());
+  [_webUIManager loadWebUIForURL:requestURL];
   return context;
 }
 
@@ -1721,6 +1721,7 @@
 
 - (void)loadNativeViewWithSuccess:(BOOL)loadSuccess
                 navigationContext:(web::NavigationContextImpl*)context {
+  _webStateImpl->OnNavigationStarted(context);
   const GURL currentURL([self currentURL]);
   [self didStartLoadingURL:currentURL];
   _loadPhase = web::PAGE_LOADED;
@@ -1953,6 +1954,7 @@
                          referrer:self.currentNavItemReferrer
                        transition:ui::PageTransition::PAGE_TRANSITION_RELOAD
            sameDocumentNavigation:NO];
+    _webStateImpl->OnNavigationStarted(navigationContext.get());
     [self didStartLoadingURL:url];
     [self.nativeController reload];
     _webStateImpl->OnNavigationFinished(navigationContext.get());
@@ -4383,6 +4385,7 @@
 
       _lastRegisteredRequestURL = webViewURL;
     }
+    _webStateImpl->OnNavigationStarted(context);
     return;
   }
 
@@ -4419,6 +4422,7 @@
 
   std::unique_ptr<web::NavigationContextImpl> navigationContext =
       [self registerLoadRequestForURL:webViewURL sameDocumentNavigation:NO];
+  _webStateImpl->OnNavigationStarted(navigationContext.get());
   [_navigationStates setContext:std::move(navigationContext)
                   forNavigation:navigation];
   DCHECK(self.loadPhase == web::LOAD_REQUESTED);
@@ -5034,13 +5038,14 @@
   [self setDocumentURL:newURL];
 
   if (!_changingHistoryState) {
-    [self didStartLoadingURL:_documentURL];
-
     // Pass either newly created context (if it exists) or context that already
     // existed before.
     web::NavigationContextImpl* navigationContext = newNavigationContext.get();
     if (!navigationContext)
       navigationContext = [self contextForPendingNavigationWithURL:newURL];
+    DCHECK(navigationContext->IsSameDocument());
+    _webStateImpl->OnNavigationStarted(navigationContext);
+    [self didStartLoadingURL:_documentURL];
     _webStateImpl->OnNavigationFinished(navigationContext);
 
     [self updateSSLStatusForCurrentNavigationItem];
diff --git a/ios/web/web_thread_impl.cc b/ios/web/web_thread_impl.cc
index d42f6a7f..a5175f9 100644
--- a/ios/web/web_thread_impl.cc
+++ b/ios/web/web_thread_impl.cc
@@ -26,7 +26,6 @@
 // Friendly names for the well-known threads.
 const char* const g_web_thread_names[WebThread::ID_COUNT] = {
     "Web_UIThread",                // UI
-    "Web_DBThread",                // DB
     "Web_IOThread",                // IO
 };
 
@@ -149,12 +148,6 @@
   CHECK_GT(line_number, 0);
 }
 
-NOINLINE void WebThreadImpl::DBThreadRun(base::RunLoop* run_loop) {
-  volatile int line_number = __LINE__;
-  Thread::Run(run_loop);
-  CHECK_GT(line_number, 0);
-}
-
 NOINLINE void WebThreadImpl::IOThreadRun(base::RunLoop* run_loop) {
   volatile int line_number = __LINE__;
   Thread::Run(run_loop);
@@ -169,8 +162,6 @@
   switch (thread_id) {
     case WebThread::UI:
       return UIThreadRun(run_loop);
-    case WebThread::DB:
-      return DBThreadRun(run_loop);
     case WebThread::IO:
       return IOThreadRun(run_loop);
     case WebThread::ID_COUNT:
diff --git a/ios/web/web_thread_impl.h b/ios/web/web_thread_impl.h
index ee383766..002f3ba6 100644
--- a/ios/web/web_thread_impl.h
+++ b/ios/web/web_thread_impl.h
@@ -39,7 +39,6 @@
   // The following are unique function names that makes it possible to tell
   // the thread id from the callstack alone in crash dumps.
   void UIThreadRun(base::RunLoop* run_loop);
-  void DBThreadRun(base::RunLoop* run_loop);
   void IOThreadRun(base::RunLoop* run_loop);
 
   static bool PostTaskHelper(WebThread::ID identifier,
diff --git a/ios/web/webui/crw_web_ui_manager.h b/ios/web/webui/crw_web_ui_manager.h
index b9550d2..88edae8d 100644
--- a/ios/web/webui/crw_web_ui_manager.h
+++ b/ios/web/webui/crw_web_ui_manager.h
@@ -27,6 +27,9 @@
 // Designated initializer.
 - (instancetype)initWithWebState:(web::WebStateImpl*)webState;
 
+// Starts loading WebUI for the given URL.
+- (void)loadWebUIForURL:(const GURL&)URL;
+
 @end
 
 @interface CRWWebUIManager (UsedOnlyForTesting)  // Testing API.
diff --git a/ios/web/webui/crw_web_ui_manager.mm b/ios/web/webui/crw_web_ui_manager.mm
index d49cdf3b8..7f1eec2 100644
--- a/ios/web/webui/crw_web_ui_manager.mm
+++ b/ios/web/webui/crw_web_ui_manager.mm
@@ -94,25 +94,23 @@
   [self resetWebState];
 }
 
-#pragma mark - CRWWebStateObserver Methods
-
-- (void)webState:(web::WebState*)webState
-    didStartNavigation:(web::NavigationContext*)navigation {
-  DCHECK(webState == _webState);
+- (void)loadWebUIForURL:(const GURL&)URL {
   // If URL is not an application specific URL, ignore the navigation.
-  GURL URL(navigation->GetUrl());
-  if (!web::GetWebClient()->IsAppSpecificURL(URL))
+  GURL URLCopy(URL);
+  if (!web::GetWebClient()->IsAppSpecificURL(URLCopy))
     return;
 
   __weak CRWWebUIManager* weakSelf = self;
-  [self loadWebUIPageForURL:URL completionHandler:^(NSString* HTML) {
+  [self loadWebUIPageForURL:URLCopy completionHandler:^(NSString* HTML) {
     web::WebStateImpl* webState = [weakSelf webState];
     if (webState) {
-      webState->LoadWebUIHtml(base::SysNSStringToUTF16(HTML), URL);
+      webState->LoadWebUIHtml(base::SysNSStringToUTF16(HTML), URLCopy);
     }
   }];
 }
 
+#pragma mark - CRWWebStateObserver Methods
+
 - (void)webState:(web::WebState*)webState didLoadPageWithSuccess:(BOOL)success {
   DCHECK_EQ(webState, _webState);
   // All WebUI pages are HTML based.
diff --git a/ios/web/webui/crw_web_ui_manager_unittest.mm b/ios/web/webui/crw_web_ui_manager_unittest.mm
index 99bfa65..76f3a13 100644
--- a/ios/web/webui/crw_web_ui_manager_unittest.mm
+++ b/ios/web/webui/crw_web_ui_manager_unittest.mm
@@ -20,7 +20,6 @@
 #import "ios/web/public/test/fakes/test_web_client.h"
 #include "ios/web/public/test/scoped_testing_web_client.h"
 #include "ios/web/public/test/web_test.h"
-#import "ios/web/web_state/navigation_context_impl.h"
 #import "ios/web/web_state/web_state_impl.h"
 #import "ios/web/webui/crw_web_ui_page_builder.h"
 #import "ios/web/webui/url_fetcher_block_adapter.h"
@@ -141,11 +140,7 @@
   base::string16 html(base::SysNSStringToUTF16(kHtml));
   GURL url(kTestWebUIUrl);
   EXPECT_CALL(*web_state_impl_, LoadWebUIHtml(html, url));
-  std::unique_ptr<web::NavigationContext> context =
-      NavigationContextImpl::CreateNavigationContext(
-          web_state_impl_.get(), url,
-          ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK, true);
-  web_state_impl_->OnNavigationStarted(context.get());
+  [web_ui_manager_ loadWebUIForURL:url];
 }
 
 }  // namespace web
diff --git a/media/audio/mock_audio_manager.cc b/media/audio/mock_audio_manager.cc
index 22b2811..d9d79aaa 100644
--- a/media/audio/mock_audio_manager.cc
+++ b/media/audio/mock_audio_manager.cc
@@ -8,7 +8,6 @@
 
 #include "base/callback.h"
 #include "base/logging.h"
-#include "base/single_thread_task_runner.h"
 #include "media/base/audio_parameters.h"
 
 namespace media {
@@ -51,23 +50,26 @@
     const media::AudioParameters& params,
     const std::string& device_id,
     const LogCallback& log_callback) {
-  NOTREACHED();
-  return NULL;
+  return MakeAudioOutputStreamProxy(params, device_id);
 }
 
 media::AudioOutputStream* MockAudioManager::MakeAudioOutputStreamProxy(
     const media::AudioParameters& params,
     const std::string& device_id) {
-  NOTREACHED();
-  return NULL;
+  if (make_output_stream_cb_) {
+    return make_output_stream_cb_.Run(params, device_id);
+  }
+  return nullptr;
 }
 
 media::AudioInputStream* MockAudioManager::MakeAudioInputStream(
     const media::AudioParameters& params,
     const std::string& device_id,
     const LogCallback& log_callback) {
-  NOTREACHED();
-  return NULL;
+  if (make_input_stream_cb_) {
+    return make_input_stream_cb_.Run(params, device_id);
+  }
+  return nullptr;
 }
 
 void MockAudioManager::AddOutputDeviceChangeListener(
@@ -118,6 +120,14 @@
   return nullptr;
 }
 
+void MockAudioManager::SetMakeOutputStreamCB(MakeOutputStreamCallback cb) {
+  make_output_stream_cb_ = std::move(cb);
+}
+
+void MockAudioManager::SetMakeInputStreamCB(MakeInputStreamCallback cb) {
+  make_input_stream_cb_ = std::move(cb);
+}
+
 void MockAudioManager::SetInputStreamParameters(const AudioParameters& params) {
   input_params_ = params;
 }
diff --git a/media/audio/mock_audio_manager.h b/media/audio/mock_audio_manager.h
index f3cd33e..030ac42 100644
--- a/media/audio/mock_audio_manager.h
+++ b/media/audio/mock_audio_manager.h
@@ -8,7 +8,7 @@
 #include <memory>
 #include <string>
 
-#include "base/callback.h"
+#include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/sequenced_task_runner_helpers.h"
 #include "base/single_thread_task_runner.h"
@@ -26,6 +26,14 @@
       base::RepeatingCallback<void(AudioDeviceDescriptions*)>;
   using GetAssociatedOutputDeviceIDCallback =
       base::RepeatingCallback<std::string(const std::string&)>;
+  using MakeOutputStreamCallback =
+      base::RepeatingCallback<media::AudioOutputStream*(
+          const media::AudioParameters& params,
+          const std::string& device_id)>;
+  using MakeInputStreamCallback =
+      base::RepeatingCallback<media::AudioInputStream*(
+          const media::AudioParameters& params,
+          const std::string& device_id)>;
 
   explicit MockAudioManager(std::unique_ptr<AudioThread> audio_thread);
   ~MockAudioManager() override;
@@ -58,6 +66,8 @@
   const char* GetName() override;
 
   // Setters to emulate desired in-test behavior.
+  void SetMakeOutputStreamCB(MakeOutputStreamCallback cb);
+  void SetMakeInputStreamCB(MakeInputStreamCallback cb);
   void SetInputStreamParameters(const AudioParameters& params);
   void SetOutputStreamParameters(const AudioParameters& params);
   void SetDefaultOutputStreamParameters(const AudioParameters& params);
@@ -97,6 +107,8 @@
   AudioParameters default_output_params_;
   bool has_input_devices_ = true;
   bool has_output_devices_ = true;
+  MakeOutputStreamCallback make_output_stream_cb_;
+  MakeInputStreamCallback make_input_stream_cb_;
   GetDeviceDescriptionsCallback get_input_device_descriptions_cb_;
   GetDeviceDescriptionsCallback get_output_device_descriptions_cb_;
   GetAssociatedOutputDeviceIDCallback get_associated_output_device_id_cb_;
diff --git a/media/filters/video_renderer_algorithm_unittest.cc b/media/filters/video_renderer_algorithm_unittest.cc
index da84d097..4e8ca341 100644
--- a/media/filters/video_renderer_algorithm_unittest.cc
+++ b/media/filters/video_renderer_algorithm_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/simple_test_tick_clock.h"
+#include "build/build_config.h"
 #include "media/base/media_log.h"
 #include "media/base/timestamp_constants.h"
 #include "media/base/video_frame_pool.h"
@@ -1334,7 +1335,9 @@
 }
 
 // Test runs too slowly on debug builds.
-#if defined(NDEBUG)
+// TODO(fuchsia): Also runs too slowly on Fuchsia, this should be investigated,
+// see https://crbug.com/767166.
+#if defined(NDEBUG) && !defined(OS_FUCHSIA)
 TEST_F(VideoRendererAlgorithmTest, CadenceBasedTest) {
   // Common display rates.
   const double kDisplayRates[] = {
@@ -1362,7 +1365,7 @@
     }
   }
 }
-#endif
+#endif  // defined(NDEBUG) && !defined(OS_FUCHSIA)
 
 // Rotate through various playback rates and ensure algorithm adapts correctly.
 TEST_F(VideoRendererAlgorithmTest, VariablePlaybackRateCadence) {
diff --git a/net/dns/host_resolver_impl_fuzzer.cc b/net/dns/host_resolver_impl_fuzzer.cc
index dad559fac..4afd51a 100644
--- a/net/dns/host_resolver_impl_fuzzer.cc
+++ b/net/dns/host_resolver_impl_fuzzer.cc
@@ -151,10 +151,13 @@
     }
 
     info.set_allow_cached_response(data_provider_->ConsumeBool());
-    return host_resolver_->Resolve(
+    int rv = host_resolver_->Resolve(
         info, priority, &address_list_,
         base::Bind(&DnsRequest::OnCallback, base::Unretained(this)), &request_,
         net::NetLogWithSource());
+    if (rv == net::ERR_IO_PENDING)
+      is_running_ = true;
+    return rv;
   }
 
   // Waits until the request is done, if it isn't done already.
@@ -209,11 +212,13 @@
     host_resolver.SetDnsClientEnabled(data_provider.ConsumeBool());
 
     std::vector<std::unique_ptr<DnsRequest>> dns_requests;
-    while (true) {
+    bool done = false;
+    while (!done) {
       switch (data_provider.ConsumeInt32InRange(0, 3)) {
         case 0:
           // Quit on 0, or when no data is left.
-          return 0;
+          done = true;
+          break;
         case 1:
           DnsRequest::CreateRequest(&host_resolver, &data_provider,
                                     &dns_requests);
diff --git a/net/http/http_response_info.cc b/net/http/http_response_info.cc
index 5611777..e2fe9a5 100644
--- a/net/http/http_response_info.cc
+++ b/net/http/http_response_info.cc
@@ -446,6 +446,7 @@
     case CONNECTION_INFO_QUIC_39:
     case CONNECTION_INFO_QUIC_40:
     case CONNECTION_INFO_QUIC_41:
+    case CONNECTION_INFO_QUIC_42:
       return true;
     case NUM_OF_CONNECTION_INFOS:
       NOTREACHED();
@@ -498,6 +499,8 @@
       return "http/2+quic/40";
     case CONNECTION_INFO_QUIC_41:
       return "http/2+quic/41";
+    case CONNECTION_INFO_QUIC_42:
+      return "http/2+quic/42";
     case CONNECTION_INFO_HTTP0_9:
       return "http/0.9";
     case CONNECTION_INFO_HTTP1_0:
diff --git a/net/http/http_response_info.h b/net/http/http_response_info.h
index ef4d4274..7b4eb273 100644
--- a/net/http/http_response_info.h
+++ b/net/http/http_response_info.h
@@ -53,6 +53,7 @@
     CONNECTION_INFO_QUIC_39 = 17,
     CONNECTION_INFO_QUIC_40 = 18,
     CONNECTION_INFO_QUIC_41 = 19,
+    CONNECTION_INFO_QUIC_42 = 20,
     NUM_OF_CONNECTION_INFOS,
   };
 
diff --git a/net/http/transport_security_state.cc b/net/http/transport_security_state.cc
index b8d57e4..9c62fb8 100644
--- a/net/http/transport_security_state.cc
+++ b/net/http/transport_security_state.cc
@@ -394,19 +394,19 @@
 // PreloadResult is the result of resolving a specific name in the preloaded
 // data.
 struct PreloadResult {
-  uint32_t pinset_id;
+  uint32_t pinset_id = 0;
   // hostname_offset contains the number of bytes from the start of the given
   // hostname where the name of the matching entry starts.
-  size_t hostname_offset;
-  bool sts_include_subdomains;
-  bool pkp_include_subdomains;
-  bool force_https;
-  bool has_pins;
-  bool expect_ct;
-  uint32_t expect_ct_report_uri_id;
-  bool expect_staple;
-  bool expect_staple_include_subdomains;
-  uint32_t expect_staple_report_uri_id;
+  size_t hostname_offset = 0;
+  bool sts_include_subdomains = false;
+  bool pkp_include_subdomains = false;
+  bool force_https = false;
+  bool has_pins = false;
+  bool expect_ct = false;
+  uint32_t expect_ct_report_uri_id = 0;
+  bool expect_staple = false;
+  bool expect_staple_include_subdomains = false;
+  uint32_t expect_staple_report_uri_id = 0;
 };
 
 // DecodeHSTSPreloadRaw resolves |hostname| in the preloaded data. It returns
@@ -520,37 +520,51 @@
 
       if (c == kEndOfString) {
         PreloadResult tmp;
-        if (!reader.Next(&tmp.sts_include_subdomains) ||
-            !reader.Next(&tmp.force_https) || !reader.Next(&tmp.has_pins)) {
+        bool is_simple_entry;
+        if (!reader.Next(&is_simple_entry)) {
           return false;
         }
 
-        tmp.pkp_include_subdomains = tmp.sts_include_subdomains;
-
-        if (tmp.has_pins) {
-          if (!reader.Read(4, &tmp.pinset_id) ||
-              (!tmp.sts_include_subdomains &&
-               !reader.Next(&tmp.pkp_include_subdomains))) {
+        // Simple entries only configure HSTS with IncludeSubdomains and use a
+        // compact serialization format where the other policy flags are
+        // omitted. The omitted flags are assumed to be 0 and the associated
+        // policies are disabled.
+        if (is_simple_entry) {
+          tmp.force_https = true;
+          tmp.sts_include_subdomains = true;
+        } else {
+          if (!reader.Next(&tmp.sts_include_subdomains) ||
+              !reader.Next(&tmp.force_https) || !reader.Next(&tmp.has_pins)) {
             return false;
           }
-        }
 
-        if (!reader.Next(&tmp.expect_ct))
-          return false;
+          tmp.pkp_include_subdomains = tmp.sts_include_subdomains;
 
-        if (tmp.expect_ct) {
-          if (!reader.Read(4, &tmp.expect_ct_report_uri_id))
-            return false;
-        }
+          if (tmp.has_pins) {
+            if (!reader.Read(4, &tmp.pinset_id) ||
+                (!tmp.sts_include_subdomains &&
+                 !reader.Next(&tmp.pkp_include_subdomains))) {
+              return false;
+            }
+          }
 
-        if (!reader.Next(&tmp.expect_staple))
-          return false;
-        tmp.expect_staple_include_subdomains = false;
-        if (tmp.expect_staple) {
-          if (!reader.Next(&tmp.expect_staple_include_subdomains))
+          if (!reader.Next(&tmp.expect_ct))
             return false;
-          if (!reader.Read(4, &tmp.expect_staple_report_uri_id))
+
+          if (tmp.expect_ct) {
+            if (!reader.Read(4, &tmp.expect_ct_report_uri_id))
+              return false;
+          }
+
+          if (!reader.Next(&tmp.expect_staple))
             return false;
+          tmp.expect_staple_include_subdomains = false;
+          if (tmp.expect_staple) {
+            if (!reader.Next(&tmp.expect_staple_include_subdomains))
+              return false;
+            if (!reader.Read(4, &tmp.expect_staple_report_uri_id))
+              return false;
+          }
         }
 
         tmp.hostname_offset = hostname_offset;
diff --git a/net/http/transport_security_state_static_unittest3.json b/net/http/transport_security_state_static_unittest3.json
index b1a1a69b..1c740e1 100644
--- a/net/http/transport_security_state_static_unittest3.json
+++ b/net/http/transport_security_state_static_unittest3.json
@@ -58,6 +58,11 @@
       "expect_staple": true,
       "include_subdomains_for_expect_staple": true,
       "expect_staple_report_uri": "https://report.badssl.com/staple-upload"
+    }, {
+      // Keep this a simple entry in the context of TrieWriter::IsSimpleEntry().
+      "name": "simple-entry.example.com",
+      "mode": "force-https",
+      "include_subdomains": true
     }
   ]
 }
diff --git a/net/http/transport_security_state_unittest.cc b/net/http/transport_security_state_unittest.cc
index f650ec5..9f14b2cf 100644
--- a/net/http/transport_security_state_unittest.cc
+++ b/net/http/transport_security_state_unittest.cc
@@ -2442,6 +2442,23 @@
   EXPECT_TRUE(staple_state.include_subdomains);
   EXPECT_EQ(GURL("https://report.badssl.com/staple-upload"),
             staple_state.report_uri);
+
+  sts_state = TransportSecurityState::STSState();
+  pkp_state = TransportSecurityState::PKPState();
+  ct_state = TransportSecurityState::ExpectCTState();
+  staple_state = TransportSecurityState::ExpectStapleState();
+
+  // This should be a simple entry in the context of
+  // TrieWriter::IsSimpleEntry().
+  EXPECT_TRUE(GetStaticDomainState(&state, "simple-entry.example.com",
+                                   &sts_state, &pkp_state));
+  EXPECT_TRUE(sts_state.include_subdomains);
+  EXPECT_EQ(TransportSecurityState::STSState::MODE_FORCE_HTTPS,
+            sts_state.upgrade_mode);
+  EXPECT_FALSE(pkp_state.include_subdomains);
+  EXPECT_FALSE(GetExpectCTState(&state, "simple-entry.example.com", &ct_state));
+  EXPECT_FALSE(
+      GetExpectStapleState(&state, "simple-entry.example.com", &staple_state));
 }
 
 static const struct ExpectStapleErrorResponseData {
diff --git a/net/quic/chromium/quic_http_stream.cc b/net/quic/chromium/quic_http_stream.cc
index 05f25d7..4e7ac30 100644
--- a/net/quic/chromium/quic_http_stream.cc
+++ b/net/quic/chromium/quic_http_stream.cc
@@ -88,8 +88,8 @@
       return HttpResponseInfo::CONNECTION_INFO_QUIC_39;
     case QUIC_VERSION_40:
       return HttpResponseInfo::CONNECTION_INFO_QUIC_40;
-    case QUIC_VERSION_41:
-      return HttpResponseInfo::CONNECTION_INFO_QUIC_41;
+    case QUIC_VERSION_42:
+      return HttpResponseInfo::CONNECTION_INFO_QUIC_42;
   }
   NOTREACHED();
   return HttpResponseInfo::CONNECTION_INFO_QUIC_UNKNOWN_VERSION;
diff --git a/net/quic/chromium/quic_stream_factory.cc b/net/quic/chromium/quic_stream_factory.cc
index 68882ac..92dcfa5d 100644
--- a/net/quic/chromium/quic_stream_factory.cc
+++ b/net/quic/chromium/quic_stream_factory.cc
@@ -8,6 +8,7 @@
 #include <tuple>
 #include <utility>
 
+#include "base/auto_reset.h"
 #include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
@@ -359,12 +360,13 @@
   size_t EstimateMemoryUsage() const;
 
   void AddRequest(QuicStreamRequest* request) {
+    CHECK(request->server_id() == key_.server_id());
     stream_requests_.insert(request);
   }
 
   void RemoveRequest(QuicStreamRequest* request) {
     auto request_iter = stream_requests_.find(request);
-    DCHECK(request_iter != stream_requests_.end());
+    CHECK(request_iter != stream_requests_.end());
     stream_requests_.erase(request_iter);
   }
 
@@ -380,8 +382,9 @@
     STATE_CONNECT,
     STATE_CONNECT_COMPLETE,
   };
-  IoState io_state_;
 
+  bool in_loop_;  // Temporary to investigate crbug.com/750271.
+  IoState io_state_;
   QuicStreamFactory* factory_;
   QuicVersion quic_version_;
   HostResolver* host_resolver_;
@@ -409,7 +412,8 @@
                             bool was_alternative_service_recently_broken,
                             int cert_verify_flags,
                             const NetLogWithSource& net_log)
-    : io_state_(STATE_RESOLVE_HOST),
+    : in_loop_(false),
+      io_state_(STATE_RESOLVE_HOST),
       factory_(factory),
       quic_version_(quic_version),
       host_resolver_(host_resolver),
@@ -437,7 +441,8 @@
 
 QuicStreamFactory::Job::~Job() {
   net_log_.EndEvent(NetLogEventType::QUIC_STREAM_FACTORY_JOB);
-  DCHECK(callback_.is_null());
+  CHECK(!in_loop_);
+  CHECK(callback_.is_null());
 }
 
 int QuicStreamFactory::Job::Run(const CompletionCallback& callback) {
@@ -450,6 +455,9 @@
 
 int QuicStreamFactory::Job::DoLoop(int rv) {
   TRACE_EVENT0(kNetTracingCategory, "QuicStreamFactory::Job::DoLoop");
+  CHECK(!in_loop_);
+  base::AutoReset<bool> auto_reset_in_loop(&in_loop_, true);
+
   do {
     IoState state = io_state_;
     io_state_ = STATE_NONE;
@@ -478,8 +486,10 @@
 
 void QuicStreamFactory::Job::OnIOComplete(int rv) {
   rv = DoLoop(rv);
-  if (rv != ERR_IO_PENDING && !callback_.is_null())
+  if (rv != ERR_IO_PENDING && !callback_.is_null()) {
+    CHECK(!in_loop_);
     base::ResetAndReturn(&callback_).Run(rv);
+  }
 }
 
 void QuicStreamFactory::Job::PopulateNetErrorDetails(
@@ -1109,6 +1119,7 @@
 
 void QuicStreamFactory::CancelRequest(QuicStreamRequest* request) {
   auto job_iter = active_jobs_.find(request->server_id());
+  CHECK(job_iter != active_jobs_.end());
   job_iter->second->RemoveRequest(request);
 }
 
diff --git a/net/quic/core/quic_flags_list.h b/net/quic/core/quic_flags_list.h
index 87db034..e62f241 100644
--- a/net/quic/core/quic_flags_list.h
+++ b/net/quic/core/quic_flags_list.h
@@ -121,8 +121,8 @@
 // When enabled, ack frame uses a deque internally instead of a set.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_frames_deque2, false)
 
-// If true, enable QUIC v41.
-QUIC_FLAG(bool, FLAGS_quic_enable_version_41, false)
+// If true, enable QUIC v42.
+QUIC_FLAG(bool, FLAGS_quic_enable_version_42, false)
 
 // Small optimization for QuicSentPacketManager::HandleAckForSentPackets.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_handle_acks, true)
diff --git a/net/quic/core/quic_version_manager.cc b/net/quic/core/quic_version_manager.cc
index 95813d5..e3e88a5 100644
--- a/net/quic/core/quic_version_manager.cc
+++ b/net/quic/core/quic_version_manager.cc
@@ -10,7 +10,7 @@
 namespace net {
 
 QuicVersionManager::QuicVersionManager(QuicVersionVector supported_versions)
-    : enable_version_41_(GetQuicFlag(FLAGS_quic_enable_version_41)),
+    : enable_version_42_(GetQuicFlag(FLAGS_quic_enable_version_42)),
       enable_version_40_(FLAGS_quic_reloadable_flag_quic_enable_version_40),
       enable_version_39_(FLAGS_quic_reloadable_flag_quic_enable_version_39),
       enable_version_38_(FLAGS_quic_reloadable_flag_quic_enable_version_38),
@@ -26,11 +26,11 @@
 }
 
 void QuicVersionManager::MaybeRefilterSupportedVersions() {
-  if (enable_version_41_ != GetQuicFlag(FLAGS_quic_enable_version_41) ||
+  if (enable_version_42_ != GetQuicFlag(FLAGS_quic_enable_version_42) ||
       enable_version_40_ != FLAGS_quic_reloadable_flag_quic_enable_version_40 ||
       enable_version_39_ != FLAGS_quic_reloadable_flag_quic_enable_version_39 ||
       enable_version_38_ != FLAGS_quic_reloadable_flag_quic_enable_version_38) {
-    enable_version_41_ = GetQuicFlag(FLAGS_quic_enable_version_41);
+    enable_version_42_ = GetQuicFlag(FLAGS_quic_enable_version_42);
     enable_version_40_ = FLAGS_quic_reloadable_flag_quic_enable_version_40;
     enable_version_39_ = FLAGS_quic_reloadable_flag_quic_enable_version_39;
     enable_version_38_ = FLAGS_quic_reloadable_flag_quic_enable_version_38;
diff --git a/net/quic/core/quic_version_manager.h b/net/quic/core/quic_version_manager.h
index 7728cac..118b745e 100644
--- a/net/quic/core/quic_version_manager.h
+++ b/net/quic/core/quic_version_manager.h
@@ -31,8 +31,8 @@
   }
 
  private:
-  // FLAGS_quic_enable_version_41
-  bool enable_version_41_;
+  // FLAGS_quic_enable_version_42
+  bool enable_version_42_;
   // FLAGS_quic_reloadable_flag_quic_enable_version_40
   bool enable_version_40_;
   // FLAGS_quic_reloadable_flag_quic_enable_version_39
diff --git a/net/quic/core/quic_version_manager_test.cc b/net/quic/core/quic_version_manager_test.cc
index 52ad8a99..6880bdf 100644
--- a/net/quic/core/quic_version_manager_test.cc
+++ b/net/quic/core/quic_version_manager_test.cc
@@ -16,7 +16,7 @@
 class QuicVersionManagerTest : public QuicTest {};
 
 TEST_F(QuicVersionManagerTest, QuicVersionManager) {
-  SetQuicFlag(&FLAGS_quic_enable_version_41, false);
+  SetQuicFlag(&FLAGS_quic_enable_version_42, false);
   FLAGS_quic_reloadable_flag_quic_enable_version_40 = false;
   FLAGS_quic_reloadable_flag_quic_enable_version_39 = false;
   FLAGS_quic_reloadable_flag_quic_enable_version_38 = false;
@@ -55,9 +55,9 @@
   EXPECT_EQ(QUIC_VERSION_37, manager.GetSupportedVersions()[3]);
   EXPECT_EQ(QUIC_VERSION_35, manager.GetSupportedVersions()[4]);
 
-  SetQuicFlag(&FLAGS_quic_enable_version_41, true);
+  SetQuicFlag(&FLAGS_quic_enable_version_42, true);
   ASSERT_EQ(6u, manager.GetSupportedVersions().size());
-  EXPECT_EQ(QUIC_VERSION_41, manager.GetSupportedVersions()[0]);
+  EXPECT_EQ(QUIC_VERSION_42, manager.GetSupportedVersions()[0]);
   EXPECT_EQ(QUIC_VERSION_40, manager.GetSupportedVersions()[1]);
   EXPECT_EQ(QUIC_VERSION_39, manager.GetSupportedVersions()[2]);
   EXPECT_EQ(QUIC_VERSION_38, manager.GetSupportedVersions()[3]);
@@ -67,7 +67,7 @@
   EXPECT_EQ(FilterSupportedVersions(AllSupportedVersions()),
             manager.GetSupportedVersions());
   ASSERT_EQ(6u, manager.GetSupportedVersions().size());
-  EXPECT_EQ(QUIC_VERSION_41, manager.GetSupportedVersions()[0]);
+  EXPECT_EQ(QUIC_VERSION_42, manager.GetSupportedVersions()[0]);
   EXPECT_EQ(QUIC_VERSION_40, manager.GetSupportedVersions()[1]);
   EXPECT_EQ(QUIC_VERSION_39, manager.GetSupportedVersions()[2]);
   EXPECT_EQ(QUIC_VERSION_38, manager.GetSupportedVersions()[3]);
diff --git a/net/quic/core/quic_versions.cc b/net/quic/core/quic_versions.cc
index 65519aa..8dd121c 100644
--- a/net/quic/core/quic_versions.cc
+++ b/net/quic/core/quic_versions.cc
@@ -30,8 +30,8 @@
   QuicVersionVector filtered_versions(versions.size());
   filtered_versions.clear();  // Guaranteed by spec not to change capacity.
   for (QuicVersion version : versions) {
-    if (version == QUIC_VERSION_41) {
-      if (GetQuicFlag(FLAGS_quic_enable_version_41) &&
+    if (version == QUIC_VERSION_42) {
+      if (GetQuicFlag(FLAGS_quic_enable_version_42) &&
           FLAGS_quic_reloadable_flag_quic_enable_version_40 &&
           FLAGS_quic_reloadable_flag_quic_enable_version_39 &&
           FLAGS_quic_reloadable_flag_quic_enable_version_38) {
@@ -82,8 +82,8 @@
       return MakeQuicTag('Q', '0', '3', '9');
     case QUIC_VERSION_40:
       return MakeQuicTag('Q', '0', '4', '0');
-    case QUIC_VERSION_41:
-      return MakeQuicTag('Q', '0', '4', '1');
+    case QUIC_VERSION_42:
+      return MakeQuicTag('Q', '0', '4', '2');
     default:
       // This shold be an ERROR because we should never attempt to convert an
       // invalid QuicVersion to be written to the wire.
@@ -115,7 +115,7 @@
     RETURN_STRING_LITERAL(QUIC_VERSION_38);
     RETURN_STRING_LITERAL(QUIC_VERSION_39);
     RETURN_STRING_LITERAL(QUIC_VERSION_40);
-    RETURN_STRING_LITERAL(QUIC_VERSION_41);
+    RETURN_STRING_LITERAL(QUIC_VERSION_42);
     default:
       return "QUIC_VERSION_UNSUPPORTED";
   }
diff --git a/net/quic/core/quic_versions.h b/net/quic/core/quic_versions.h
index e6a6a0c..e2517253 100644
--- a/net/quic/core/quic_versions.h
+++ b/net/quic/core/quic_versions.h
@@ -33,7 +33,7 @@
                          // WINDOW_UPDATE every 20 sent packets which do not
                          // contain retransmittable frames.
   QUIC_VERSION_40 = 40,  // RST_STREAM, ACK and STREAM frames match IETF format.
-  QUIC_VERSION_41 = 41,  // Use IETF packet header format.
+  QUIC_VERSION_42 = 42,  // Use IETF packet header format.
 
   // IMPORTANT: if you are adding to this list, follow the instructions at
   // http://sites/quic/adding-and-removing-versions
@@ -47,7 +47,7 @@
 // IMPORTANT: if you are adding to this list, follow the instructions at
 // http://sites/quic/adding-and-removing-versions
 static const QuicVersion kSupportedQuicVersions[] = {
-    QUIC_VERSION_41, QUIC_VERSION_40, QUIC_VERSION_39,
+    QUIC_VERSION_42, QUIC_VERSION_40, QUIC_VERSION_39,
     QUIC_VERSION_38, QUIC_VERSION_37, QUIC_VERSION_35};
 
 typedef std::vector<QuicVersion> QuicVersionVector;
diff --git a/net/tools/quic/quic_dispatcher_test.cc b/net/tools/quic/quic_dispatcher_test.cc
index cadb9e9..4dcfc90 100644
--- a/net/tools/quic/quic_dispatcher_test.cc
+++ b/net/tools/quic/quic_dispatcher_test.cc
@@ -551,7 +551,7 @@
   FLAGS_quic_reloadable_flag_quic_enable_version_38 = true;
   FLAGS_quic_reloadable_flag_quic_enable_version_39 = true;
   FLAGS_quic_reloadable_flag_quic_enable_version_40 = true;
-  SetQuicFlag(&FLAGS_quic_enable_version_41, true);
+  SetQuicFlag(&FLAGS_quic_enable_version_42, true);
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
   server_address_ = QuicSocketAddress(QuicIpAddress::Any4(), 5);
   QuicConnectionId connection_id = 1;
@@ -597,17 +597,17 @@
                 SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
                 PACKET_6BYTE_PACKET_NUMBER, 1);
   // Turn off version 41.
-  SetQuicFlag(&FLAGS_quic_enable_version_41, false);
+  SetQuicFlag(&FLAGS_quic_enable_version_42, false);
   ++connection_id;
   EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
                                               QuicStringPiece("hq")))
       .Times(0);
-  ProcessPacket(client_address, connection_id, true, QUIC_VERSION_41,
+  ProcessPacket(client_address, connection_id, true, QUIC_VERSION_42,
                 SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
                 PACKET_6BYTE_PACKET_NUMBER, 1);
 
   // Turn on version 41.
-  SetQuicFlag(&FLAGS_quic_enable_version_41, true);
+  SetQuicFlag(&FLAGS_quic_enable_version_42, true);
   ++connection_id;
   EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
                                               QuicStringPiece("hq")))
@@ -622,7 +622,7 @@
                                base::Unretained(this), connection_id))));
   EXPECT_CALL(*dispatcher_,
               ShouldCreateOrBufferPacketForConnection(connection_id));
-  ProcessPacket(client_address, connection_id, true, QUIC_VERSION_41,
+  ProcessPacket(client_address, connection_id, true, QUIC_VERSION_42,
                 SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
                 PACKET_6BYTE_PACKET_NUMBER, 1);
   // Turn off version 40.
diff --git a/net/tools/transport_security_state_generator/trie/trie_writer.cc b/net/tools/transport_security_state_generator/trie/trie_writer.cc
index 3dd8163..0f92d5b 100644
--- a/net/tools/transport_security_state_generator/trie/trie_writer.cc
+++ b/net/tools/transport_security_state_generator/trie/trie_writer.cc
@@ -20,6 +20,14 @@
   return lhs->reversed_name < rhs->reversed_name;
 }
 
+// Returns true if the entry only configures HSTS with includeSubdomains.
+// Such entries, when written, can be represented more compactly, and thus
+// reduce the overall size of the trie.
+bool IsSimpleEntry(const TransportSecurityStateEntry* entry) {
+  return entry->force_https && entry->include_subdomains &&
+         entry->pinset.empty() && !entry->expect_ct && !entry->expect_staple;
+}
+
 }  // namespace
 
 ReversedEntry::ReversedEntry(std::vector<uint8_t> reversed_name,
@@ -124,6 +132,13 @@
 
 bool TrieWriter::WriteEntry(const TransportSecurityStateEntry* entry,
                             TrieBitBuffer* writer) {
+  if (IsSimpleEntry(entry)) {
+    writer->WriteBit(1);
+    return true;
+  } else {
+    writer->WriteBit(0);
+  }
+
   uint8_t include_subdomains = 0;
   if (entry->include_subdomains) {
     include_subdomains = 1;
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc
index 62c09006..59e9142 100644
--- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc
@@ -75,15 +75,17 @@
     case __NR_flock:
     case __NR_fsync:
     case __NR_ftruncate:
-#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+#if defined(__i386__) || defined(__arm__) || defined(__mips32__)
     case __NR_ftruncate64:
 #endif
 #if defined(__x86_64__) || defined(__aarch64__)
     case __NR_newfstatat:
     case __NR_fstatfs:
-#elif defined(__i386__) || defined(__arm__) || defined(__mips__)
+#elif defined(__i386__) || defined(__arm__) || defined(__mips32__)
     case __NR_fstatat64:
     case __NR_fstatfs64:
+#endif
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
     case __NR_getdents:
 #endif
     case __NR_getdents64:
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 2fe2275..a916ed13 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -4269,6 +4269,335 @@
       }
     ]
   },
+  "ClangToTLinuxMSan": {
+    "gtest_tests": [
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "accessibility_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "aura_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "base_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "cacheinvalidation_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "capture_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "cast_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "cc_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "chromedriver_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "components_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "compositor_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "content_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "crypto_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "dbus_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "device_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "display_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "events_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "gcm_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "gfx_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "google_apis_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "gpu_ipc_service_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "gpu_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "ipc_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "jingle_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "latency_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "media_blink_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "media_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "midi_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "mojo_common_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "mojo_public_bindings_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "mojo_public_system_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "mojo_system_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "nacl_loader_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "native_theme_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "net_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "pdf_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "ppapi_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "printing_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "remoting_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "sandbox_linux_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "skia_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "sql_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "storage_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "ui_base_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "ui_touch_selection_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "url_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "views_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "viz_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "wm_unittests"
+      }
+    ]
+  },
   "ClangToTLinuxThinLTO": {
     "gtest_tests": [
       {
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 5bae4dd1..7a60b7d 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1753,6 +1753,8 @@
 crbug.com/751952 virtual/origin-trials-runtimeflags-disabled/http/tests/origin_trials/webexposed/budget-api-origin-trial-interfaces.html [ Pass Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/css/css-text-3/line-break/line-break-anywhere-001.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-text-3/line-break/line-break-anywhere-002.html [ Failure ]
 crbug.com/626703 external/wpt/payment-request/PaymentAddress/attributes-and-toJSON-method-manual.https.html [ Skip ]
 crbug.com/626703 external/wpt/payment-request/PaymentRequestUpdateEvent/updateWith-state-checks-manual.https.html [ Skip ]
 crbug.com/626703 external/wpt/payment-request/user-accepts-payment-request-algo-manual.https.html [ Skip ]
@@ -2721,7 +2723,7 @@
 
 # ====== End of display: contents tests ======
 
-crbug.com/676229 [ Win7 Linux Mac ] plugins/mouse-click-plugin-clears-selection.html [ Failure Pass ]
+crbug.com/676229 [ Win Linux Mac ] plugins/mouse-click-plugin-clears-selection.html [ Failure Pass ]
 crbug.com/736333 [ Win7 Linux ] plugins/iframe-plugin-bgcolor.html [ Failure Pass ]
 crbug.com/742670 [ Mac ] plugins/iframe-plugin-bgcolor.html [ Failure Pass ]
 
@@ -3749,3 +3751,6 @@
 # Sheriff failures 2017-09-21
 crbug.com/767012 [ Win10 ] http/tests/devtools/startup/console-log-before-frame-navigation.html [ Pass Timeout ]
 crbug.com/767469 [ Linux Mac ] virtual/stable/http/tests/navigation/start-load-during-provisional-loader-detach.html [ Pass Failure ]
+
+# Sheriff failures 2017-09-22
+crbug.com/767892 [ Win Linux Mac ] virtual/mojo-loading/http/tests/devtools/startup/database-open.html [ Pass Timeout ]
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index a6c362c..4360a8e91 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -44189,6 +44189,30 @@
      {}
     ]
    ],
+   "css/css-text-3/line-break/line-break-anywhere-001.html": [
+    [
+     "/css/css-text-3/line-break/line-break-anywhere-001.html",
+     [
+      [
+       "/css/css-text-3/line-break/reference/line-break-anywhere-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text-3/line-break/line-break-anywhere-002.html": [
+    [
+     "/css/css-text-3/line-break/line-break-anywhere-002.html",
+     [
+      [
+       "/css/css-text-3/line-break/reference/line-break-anywhere-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-text-3/line-break/line-break-normal-021.xht": [
     [
      "/css/css-text-3/line-break/line-break-normal-021.xht",
@@ -91253,6 +91277,11 @@
      {}
     ]
    ],
+   "css/css-text-3/line-break/reference/line-break-anywhere-001-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-text-3/line-break/reference/line-break-normal-021-ref.xht": [
     [
      {}
@@ -111378,6 +111407,11 @@
      {}
     ]
    ],
+   "html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external.js": [
+    [
+     {}
+    ]
+   ],
    "html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-common.js": [
     [
      {}
@@ -120693,6 +120727,31 @@
      {}
     ]
    ],
+   "streams/readable-byte-streams/detached-buffers-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "streams/readable-byte-streams/detached-buffers.dedicatedworker-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "streams/readable-byte-streams/detached-buffers.js": [
+    [
+     {}
+    ]
+   ],
+   "streams/readable-byte-streams/detached-buffers.serviceworker.https-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "streams/readable-byte-streams/detached-buffers.sharedworker-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "streams/readable-byte-streams/general-expected.txt": [
     [
      {}
@@ -153999,6 +154058,30 @@
      {}
     ]
    ],
+   "html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external-classic.html": [
+    [
+     "/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external-classic.html",
+     {}
+    ]
+   ],
+   "html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external-module.html": [
+    [
+     "/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external-module.html",
+     {}
+    ]
+   ],
+   "html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-inline-classic.html": [
+    [
+     "/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-inline-classic.html",
+     {}
+    ]
+   ],
+   "html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-inline-module.html": [
+    [
+     "/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-inline-module.html",
+     {}
+    ]
+   ],
    "html/semantics/scripting-1/the-script-element/module/error-and-slow-dependency.html": [
     [
      "/html/semantics/scripting-1/the-script-element/module/error-and-slow-dependency.html",
@@ -180009,6 +180092,30 @@
      {}
     ]
    ],
+   "streams/readable-byte-streams/detached-buffers.dedicatedworker.html": [
+    [
+     "/streams/readable-byte-streams/detached-buffers.dedicatedworker.html",
+     {}
+    ]
+   ],
+   "streams/readable-byte-streams/detached-buffers.html": [
+    [
+     "/streams/readable-byte-streams/detached-buffers.html",
+     {}
+    ]
+   ],
+   "streams/readable-byte-streams/detached-buffers.serviceworker.https.html": [
+    [
+     "/streams/readable-byte-streams/detached-buffers.serviceworker.https.html",
+     {}
+    ]
+   ],
+   "streams/readable-byte-streams/detached-buffers.sharedworker.html": [
+    [
+     "/streams/readable-byte-streams/detached-buffers.sharedworker.html",
+     {}
+    ]
+   ],
    "streams/readable-byte-streams/general.dedicatedworker.html": [
     [
      "/streams/readable-byte-streams/general.dedicatedworker.html",
@@ -235424,6 +235531,14 @@
    "5498c55b57270bb257281f8f2076a29a8af28fc4",
    "support"
   ],
+  "css/css-text-3/line-break/line-break-anywhere-001.html": [
+   "1df8077c37fe3330623ee3ea12eb0efb6ef5f733",
+   "reftest"
+  ],
+  "css/css-text-3/line-break/line-break-anywhere-002.html": [
+   "639f9030e99b2cd0d6b42acc7a3c0da54b3fb420",
+   "reftest"
+  ],
   "css/css-text-3/line-break/line-break-normal-021.xht": [
    "6490bab71e762e042c5d37e7ad2b9852a53e50e6",
    "reftest"
@@ -235476,6 +235591,10 @@
    "8f124452720b5b1be387b72f721aff36111c01d6",
    "reftest"
   ],
+  "css/css-text-3/line-break/reference/line-break-anywhere-001-ref.html": [
+   "6224337b8fabfa02acd3e742a65d2c7905043648",
+   "support"
+  ],
   "css/css-text-3/line-break/reference/line-break-normal-021-ref.xht": [
    "cc36420439dfb604810d5238e587bdb8d4a58a0e",
    "support"
@@ -268884,6 +269003,26 @@
    "fab2384255a9cccad102dd1f7e3ccec2d9f5f34a",
    "testharness"
   ],
+  "html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external-classic.html": [
+   "b69fecbbf7254070d2f7a651a6429f829b65e275",
+   "testharness"
+  ],
+  "html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external-module.html": [
+   "e8fe7c798834fcd1c3f084a7344ed1f063c09831",
+   "testharness"
+  ],
+  "html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external.js": [
+   "3b18d95251c1f5470f1269cf42847c2ac257b16f",
+   "support"
+  ],
+  "html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-inline-classic.html": [
+   "41aa6634b6ab394eea6eaf9dc4fe621d2ee14b9d",
+   "testharness"
+  ],
+  "html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-inline-module.html": [
+   "41aa6634b6ab394eea6eaf9dc4fe621d2ee14b9d",
+   "testharness"
+  ],
   "html/semantics/scripting-1/the-script-element/module/error-and-slow-dependency.html": [
    "32455a1418d94fa68368bae3b1c0291204f6b4e3",
    "testharness"
@@ -291677,7 +291816,7 @@
    "testharness"
   ],
   "service-workers/service-worker/link-element-register-mime-types.https-expected.txt": [
-   "8ed38c31f09d6ae605ae470ddfd63d180f41387b",
+   "ac763c885ae2978c8e4da5484ae69ecbf2bd1ca1",
    "support"
   ],
   "service-workers/service-worker/link-element-register-mime-types.https.html": [
@@ -291965,7 +292104,7 @@
    "testharness"
   ],
   "service-workers/service-worker/registration-mime-types.https-expected.txt": [
-   "5e00df5587f5f058ce6c2bcb9a6d9ef875caff4a",
+   "65d1dbf598b983581bf13cfdf781b8d933a069a2",
    "support"
   ],
   "service-workers/service-worker/registration-mime-types.https.html": [
@@ -294040,6 +294179,42 @@
    "6f3911baf77e26af2d7e7d7472caae4df6d5a27e",
    "testharness"
   ],
+  "streams/readable-byte-streams/detached-buffers-expected.txt": [
+   "f2ddef38a426a9afa6a7b3c83fdd6cc041eba645",
+   "support"
+  ],
+  "streams/readable-byte-streams/detached-buffers.dedicatedworker-expected.txt": [
+   "f2ddef38a426a9afa6a7b3c83fdd6cc041eba645",
+   "support"
+  ],
+  "streams/readable-byte-streams/detached-buffers.dedicatedworker.html": [
+   "8742d541924382fb06a3258d6723f22ddf299045",
+   "testharness"
+  ],
+  "streams/readable-byte-streams/detached-buffers.html": [
+   "9eb042e92c7592d9a5a233469d491d61619a9045",
+   "testharness"
+  ],
+  "streams/readable-byte-streams/detached-buffers.js": [
+   "d593ac7499f206c20f8735e7b8c62cfed421bd8f",
+   "support"
+  ],
+  "streams/readable-byte-streams/detached-buffers.serviceworker.https-expected.txt": [
+   "1be53317af5fa90abbf92f0e322aec92e5ded984",
+   "support"
+  ],
+  "streams/readable-byte-streams/detached-buffers.serviceworker.https.html": [
+   "dbc50df2acf0108c88437262608314d1417f6a40",
+   "testharness"
+  ],
+  "streams/readable-byte-streams/detached-buffers.sharedworker-expected.txt": [
+   "f2ddef38a426a9afa6a7b3c83fdd6cc041eba645",
+   "support"
+  ],
+  "streams/readable-byte-streams/detached-buffers.sharedworker.html": [
+   "eb113a11109828edf35885b21e4e57fd3ef84368",
+   "testharness"
+  ],
   "streams/readable-byte-streams/general-expected.txt": [
    "73f76f622b4b6b4aff498525f1732e7d675c90b3",
    "support"
@@ -294057,7 +294232,7 @@
    "testharness"
   ],
   "streams/readable-byte-streams/general.js": [
-   "e8d971fb6491da06f9ceedb1b6cd6a5c06b6f3b4",
+   "4617fdb8c1ef68600f476dadcf47090100fc9ce8",
    "support"
   ],
   "streams/readable-byte-streams/general.serviceworker.https-expected.txt": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-text-3/line-break/line-break-anywhere-001.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-text-3/line-break/line-break-anywhere-001.html
new file mode 100644
index 0000000..8c8252e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-text-3/line-break/line-break-anywhere-001.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-001-ref.html">
+<meta name="flags" content="">
+<meta name="assert" content="line-break:anywhere puts a soft wrap opportunity around every typographic character unit,
+                             including around punctuation or in the middle of words,
+                             disregarding any prohibition against line breaks introduced by characters with the GL, JW, or ZJW character class.">
+<style>
+#green {
+  position: absolute;
+  background: green;
+  font-family: monospace;
+  width: 1ch;
+  height: 20em;
+}
+#test {
+  width: 1ch;
+  line-height: 1;
+  color: red;
+  font-family: monospace;
+  line-break: anywhere;
+}
+</style>
+
+<p>Test passes if there is a green rectangle below and no red.</p>
+<div id=green></div>
+<!-- with line breaks everywhere, none of the following characters should stick out from under the green div -->
+<div id=test>aa-a.a)a,a)a&nbsp;a&#xfeff;a&#x2060;a&#x200d;a・a</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-text-3/line-break/line-break-anywhere-002.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-text-3/line-break/line-break-anywhere-002.html
new file mode 100644
index 0000000..79d9dab
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-text-3/line-break/line-break-anywhere-002.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-001-ref.html">
+<meta name="flags" content="">
+<meta name="assert" content="line-break:anywhere puts a soft wrap opportunity betwwen letters in the middle of words and hyphenation is not applied.">
+<style>
+#green {
+  position: absolute;
+  background: green;
+  font-family: monospace;
+  width: 1ch;
+  height: 20em;
+}
+#test {
+  width: 1ch;
+  line-height: 1;
+  color: red;
+  font-family: monospace;
+  line-break: anywhere;
+  hyphens: auto;
+}
+</style>
+
+<p>Test passes if there is a green rectangle below and no red.</p>
+<div id=green></div>
+<!-- Hyphenation, if it occurs, will produce a hyphen that sticks out from under the green rectangle.
+     Also, if the words fails to be wrapped between all letters, letters will also stick out from under the green rectangle -->
+<div id=test>no hyphenation</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-text-3/line-break/reference/line-break-anywhere-001-ref.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-text-3/line-break/reference/line-break-anywhere-001-ref.html
new file mode 100644
index 0000000..ff74b3bce
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-text-3/line-break/reference/line-break-anywhere-001-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test Reference</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<style>
+#green {
+  position: absolute;
+  background: green;
+  font-family: monospace;
+  width: 1ch;
+  height: 20em;
+}
+</style>
+
+<p>Test passes if there is a green rectangle below and no red.</p>
+<div id=green></div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers-expected.txt
new file mode 100644
index 0000000..6a3e32b4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+FAIL ReadableStream with byte source: read()ing from a closed stream still transfers the buffer bytes type is not yet implemented
+FAIL ReadableStream with byte source: read()ing from a stream with queued chunks still transfers the buffer bytes type is not yet implemented
+FAIL ReadableStream with byte source: enqueuing an already-detached buffer throws bytes type is not yet implemented
+FAIL ReadableStream with byte source: reading into an already-detached buffer rejects bytes type is not yet implemented
+FAIL ReadableStream with byte source: respond() throws if the BYOB request's buffer has been detached (in the readable state) bytes type is not yet implemented
+FAIL ReadableStream with byte source: respond() throws if the BYOB request's buffer has been detached (in the closed state) bytes type is not yet implemented
+FAIL ReadableStream with byte source: respondWithNewView() throws if the supplied view's buffer has been detached (in the readable state) bytes type is not yet implemented
+FAIL ReadableStream with byte source: respondWithNewView() throws if the supplied view's buffer has been detached (in the closed state) bytes type is not yet implemented
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.dedicatedworker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.dedicatedworker-expected.txt
new file mode 100644
index 0000000..6a3e32b4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.dedicatedworker-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+FAIL ReadableStream with byte source: read()ing from a closed stream still transfers the buffer bytes type is not yet implemented
+FAIL ReadableStream with byte source: read()ing from a stream with queued chunks still transfers the buffer bytes type is not yet implemented
+FAIL ReadableStream with byte source: enqueuing an already-detached buffer throws bytes type is not yet implemented
+FAIL ReadableStream with byte source: reading into an already-detached buffer rejects bytes type is not yet implemented
+FAIL ReadableStream with byte source: respond() throws if the BYOB request's buffer has been detached (in the readable state) bytes type is not yet implemented
+FAIL ReadableStream with byte source: respond() throws if the BYOB request's buffer has been detached (in the closed state) bytes type is not yet implemented
+FAIL ReadableStream with byte source: respondWithNewView() throws if the supplied view's buffer has been detached (in the readable state) bytes type is not yet implemented
+FAIL ReadableStream with byte source: respondWithNewView() throws if the supplied view's buffer has been detached (in the closed state) bytes type is not yet implemented
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.dedicatedworker.html b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.dedicatedworker.html
new file mode 100644
index 0000000..d721081a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.dedicatedworker.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>detached-buffers.js dedicated worker wrapper file</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+'use strict';
+fetch_tests_from_worker(new Worker('detached-buffers.js'));
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.html b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.html
new file mode 100644
index 0000000..9cdc29b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>detached-buffers.js browser context wrapper file</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+
+
+<script src="detached-buffers.js"></script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.js b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.js
new file mode 100644
index 0000000..b1b47f0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.js
@@ -0,0 +1,156 @@
+'use strict';
+
+if (self.importScripts) {
+  self.importScripts('/resources/testharness.js');
+}
+
+promise_test(() => {
+  const stream = new ReadableStream({
+    start(c) {
+      c.close();
+    },
+    type: 'bytes'
+  });
+
+  const reader = stream.getReader({ mode: 'byob' });
+  const view = new Uint8Array([1, 2, 3]);
+  return reader.read(view).then(({ value, done }) => {
+    // Sanity checks
+    assert_true(value instanceof Uint8Array, 'The value read must be a Uint8Array');
+    assert_not_equals(value, view, 'The value read must not be the *same* Uint8Array');
+    assert_array_equals(value, [], 'The value read must be an empty Uint8Array, since the stream is closed');
+    assert_true(done, 'done must be true, since the stream is closed');
+
+    // The important assertions
+    assert_not_equals(value.buffer, view.buffer, 'a different ArrayBuffer must underlie the value');
+    assert_equals(view.buffer.byteLength, 0, 'the original buffer must be detached');
+  });
+}, 'ReadableStream with byte source: read()ing from a closed stream still transfers the buffer');
+
+promise_test(() => {
+  const stream = new ReadableStream({
+    start(c) {
+      c.enqueue(new Uint8Array([1, 2, 3]));
+    },
+    type: 'bytes'
+  });
+
+  const reader = stream.getReader({ mode: 'byob' });
+  const view = new Uint8Array([4, 5, 6]);
+  return reader.read(view).then(({ value, done }) => {
+    // Sanity checks
+    assert_true(value instanceof Uint8Array, 'The value read must be a Uint8Array');
+    assert_not_equals(value, view, 'The value read must not be the *same* Uint8Array');
+    assert_array_equals(value, [1, 2, 3], 'The value read must be the enqueued Uint8Array, not the original values');
+    assert_false(done, 'done must be false, since the stream is not closed');
+
+    // The important assertions
+    assert_not_equals(value.buffer, view.buffer, 'a different ArrayBuffer must underlie the value');
+    assert_equals(view.buffer.byteLength, 0, 'the original buffer must be detached');
+  });
+}, 'ReadableStream with byte source: read()ing from a stream with queued chunks still transfers the buffer');
+
+test(() => {
+  const stream = new ReadableStream({
+    start(c) {
+      const view = new Uint8Array([1, 2, 3]);
+      c.enqueue(view);
+      assert_throws(new TypeError(), () => c.enqueue(view), 'enqueuing an already-detached buffer must throw');
+    },
+    type: 'bytes'
+  });
+}, 'ReadableStream with byte source: enqueuing an already-detached buffer throws');
+
+promise_test(t => {
+  const stream = new ReadableStream({
+    start(c) {
+      c.enqueue(new Uint8Array([1, 2, 3]));
+    },
+    type: 'bytes'
+  });
+  const reader = stream.getReader({ mode: 'byob' });
+
+  const view = new Uint8Array([4, 5, 6]);
+  return reader.read(view).then(() => {
+    // view is now detached
+    return promise_rejects(t, new TypeError(), reader.read(view),
+      'read(view) must reject when given an already-detached buffer');
+  });
+}, 'ReadableStream with byte source: reading into an already-detached buffer rejects');
+
+async_test(t => {
+  const stream = new ReadableStream({
+    pull: t.step_func_done(c => {
+      // Detach it by reading into it
+      reader.read(c.byobRequest.view);
+
+      assert_throws(new TypeError(), () => c.byobRequest.respond(1),
+        'respond() must throw if the corresponding view has become detached');
+    }),
+    type: 'bytes'
+  });
+  const reader = stream.getReader({ mode: 'byob' });
+
+  reader.read(new Uint8Array([4, 5, 6]));
+}, 'ReadableStream with byte source: respond() throws if the BYOB request\'s buffer has been detached (in the ' +
+   'readable state)');
+
+async_test(t => {
+  const stream = new ReadableStream({
+    pull: t.step_func_done(c => {
+      // Detach it by reading into it
+      reader.read(c.byobRequest.view);
+
+      c.close();
+
+      assert_throws(new TypeError(), () => c.byobRequest.respond(0),
+        'respond() must throw if the corresponding view has become detached');
+    }),
+    type: 'bytes'
+  });
+  const reader = stream.getReader({ mode: 'byob' });
+
+  reader.read(new Uint8Array([4, 5, 6]));
+}, 'ReadableStream with byte source: respond() throws if the BYOB request\'s buffer has been detached (in the ' +
+   'closed state)');
+
+async_test(t => {
+  const stream = new ReadableStream({
+    pull: t.step_func_done(c => {
+      // Detach it by reading into it
+      const view = new Uint8Array([1, 2, 3]);
+      reader.read(view);
+
+      assert_throws(new TypeError(), () => c.byobRequest.respondWithNewView(view),
+        'respondWithNewView() must throw if passed a detached view');
+    }),
+    type: 'bytes'
+  });
+  const reader = stream.getReader({ mode: 'byob' });
+
+  reader.read(new Uint8Array([4, 5, 6]));
+}, 'ReadableStream with byte source: respondWithNewView() throws if the supplied view\'s buffer has been detached ' +
+    '(in the readable state)');
+
+async_test(t => {
+  const stream = new ReadableStream({
+    pull: t.step_func_done(c => {
+      // Detach it by reading into it
+      const view = new Uint8Array([1, 2, 3]);
+      reader.read(view);
+
+      c.close();
+
+      const zeroLengthView = new Uint8Array(view.buffer, 0, 0);
+      assert_throws(new TypeError(), () => c.byobRequest.respondWithNewView(zeroLengthView),
+        'respondWithNewView() must throw if passed a (zero-length) view whose buffer has been detached');
+    }),
+    type: 'bytes'
+  });
+  const reader = stream.getReader({ mode: 'byob' });
+
+  reader.read(new Uint8Array([4, 5, 6]));
+}, 'ReadableStream with byte source: respondWithNewView() throws if the supplied view\'s buffer has been detached ' +
+   '(in the closed state)');
+
+done();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.serviceworker.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.serviceworker.https-expected.txt
new file mode 100644
index 0000000..3307db6d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.serviceworker.https-expected.txt
@@ -0,0 +1,12 @@
+This is a testharness.js-based test.
+PASS Service worker test setup
+FAIL ReadableStream with byte source: read()ing from a closed stream still transfers the buffer bytes type is not yet implemented
+FAIL ReadableStream with byte source: read()ing from a stream with queued chunks still transfers the buffer bytes type is not yet implemented
+FAIL ReadableStream with byte source: enqueuing an already-detached buffer throws bytes type is not yet implemented
+FAIL ReadableStream with byte source: reading into an already-detached buffer rejects bytes type is not yet implemented
+FAIL ReadableStream with byte source: respond() throws if the BYOB request's buffer has been detached (in the readable state) bytes type is not yet implemented
+FAIL ReadableStream with byte source: respond() throws if the BYOB request's buffer has been detached (in the closed state) bytes type is not yet implemented
+FAIL ReadableStream with byte source: respondWithNewView() throws if the supplied view's buffer has been detached (in the readable state) bytes type is not yet implemented
+FAIL ReadableStream with byte source: respondWithNewView() throws if the supplied view's buffer has been detached (in the closed state) bytes type is not yet implemented
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.serviceworker.https.html b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.serviceworker.https.html
new file mode 100644
index 0000000..db7d2c54
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.serviceworker.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>detached-buffers.js service worker wrapper file</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+
+<script>
+'use strict';
+service_worker_test('detached-buffers.js', 'Service worker test setup');
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.sharedworker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.sharedworker-expected.txt
new file mode 100644
index 0000000..6a3e32b4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.sharedworker-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+FAIL ReadableStream with byte source: read()ing from a closed stream still transfers the buffer bytes type is not yet implemented
+FAIL ReadableStream with byte source: read()ing from a stream with queued chunks still transfers the buffer bytes type is not yet implemented
+FAIL ReadableStream with byte source: enqueuing an already-detached buffer throws bytes type is not yet implemented
+FAIL ReadableStream with byte source: reading into an already-detached buffer rejects bytes type is not yet implemented
+FAIL ReadableStream with byte source: respond() throws if the BYOB request's buffer has been detached (in the readable state) bytes type is not yet implemented
+FAIL ReadableStream with byte source: respond() throws if the BYOB request's buffer has been detached (in the closed state) bytes type is not yet implemented
+FAIL ReadableStream with byte source: respondWithNewView() throws if the supplied view's buffer has been detached (in the readable state) bytes type is not yet implemented
+FAIL ReadableStream with byte source: respondWithNewView() throws if the supplied view's buffer has been detached (in the closed state) bytes type is not yet implemented
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.sharedworker.html b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.sharedworker.html
new file mode 100644
index 0000000..dac6917d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/detached-buffers.sharedworker.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>detached-buffers.js shared worker wrapper file</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+'use strict';
+fetch_tests_from_worker(new SharedWorker('detached-buffers.js'));
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.js b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.js
index 239891135..1ec9fbca 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.js
@@ -1906,7 +1906,7 @@
     start(c) {
       controller = c;
     },
-    type: "bytes"
+    type: 'bytes'
   });
 
   const readPromise = rs.getReader().read();
diff --git a/third_party/WebKit/LayoutTests/fast/inline/inline-offsetParent-continuation.html b/third_party/WebKit/LayoutTests/fast/inline/inline-offsetParent-continuation.html
new file mode 100644
index 0000000..894dfbc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/inline/inline-offsetParent-continuation.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+
+<span id="expectedOffsetParent" style="position:relative;">
+    <div id="elm"></div>
+</span>
+
+<script>
+test(function() {
+    var expectedOffsetParent = document.getElementById("expectedOffsetParent");
+    var actualOffsetParent = document.getElementById("elm").offsetParent;
+
+    assert_true(actualOffsetParent == expectedOffsetParent);
+}, 'offsetParent is correct for a block inside an inline.');
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/cookies-protocol-test-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/cookies-protocol-test-expected.txt
index eacabbb..3d629743 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/cookies-protocol-test-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/cookies-protocol-test-expected.txt
@@ -155,3 +155,10 @@
 
 Running test: deleteAllCookies
 
+Running test: nonUnicodeCookie
+Adding multiple cookies
+Num of cookies 1
+name: cookie1, value: привет, domain: .chromium.org, path: /path, session
+
+Running test: deleteAllCookies
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/cookies-protocol-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/cookies-protocol-test.js
index 8a4ffe2..432f8ed 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/cookies-protocol-test.js
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/cookies-protocol-test.js
@@ -172,5 +172,11 @@
 
     deleteAllCookies,
 
+    async function nonUnicodeCookie() {
+      await setCookies([{name: 'cookie1', value: 'привет', domain: '.chromium.org', path: '/path' }]);
+    },
+
+    deleteAllCookies,
+
   ]);
 })
diff --git a/third_party/WebKit/LayoutTests/scrollbars/scrollbar-layout-viewport-scrollwidth.html b/third_party/WebKit/LayoutTests/scrollbars/scrollbar-layout-viewport-scrollwidth.html
new file mode 100644
index 0000000..5432443
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/scrollbars/scrollbar-layout-viewport-scrollwidth.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<title>Verify scrollWidth for LayoutViewportScrollableArea</title>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<style>
+    body, p{
+        margin:0;
+    }
+    div{
+        width:3000px;
+    }
+</style>
+<body>
+    <div>This page should be scrollable</div>
+</body>
+<script>
+async_test(function(t) {
+    window.onload = t.step_func_done(function() {
+        assert_equals(document.scrollingElement.scrollWidth, 3000);
+    });
+});
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/vr/latest/events_deviceconnect.html b/third_party/WebKit/LayoutTests/vr/latest/events_deviceconnect.html
new file mode 100644
index 0000000..ce73eb00
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/vr/latest/events_deviceconnect.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../resources/fake-vr-displays.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/device/vr/vr_service.mojom.js"></script>
+<script src="../resources/mock-vr-service.js"></script>
+<canvas id="webgl-canvas"></canvas>
+<script>
+let fakeDisplays = fakeVRDisplays();
+
+vr_test((t, mockService) => {
+  let watcherDone = new Event("watcherdone");
+  let eventWatcher = new EventWatcher(t, navigator.vr, ["deviceconnect",
+                                                        "watcherdone"]);
+  eventWatcher.wait_for(["deviceconnect", "watcherdone"])
+    .then( () => {
+      t.done();
+    });
+
+  // The event should fire when a listener is added even if the devices are not
+  // explicity queried with navigator.vr.getDevices().
+  function onDeviceConnect(event) {
+    t.step( () => {
+      assert_equals(event.device.deviceName, 'Google, Inc. Daydream View');
+      navigator.vr.dispatchEvent(watcherDone);
+    });
+  }
+  navigator.vr.addEventListener("deviceconnect", onDeviceConnect, false);
+
+}, [fakeDisplays["Pixel"]],
+"Test deviceconnect fires when devices are connected.");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/vr/latest/getDevices_one_device.html b/third_party/WebKit/LayoutTests/vr/latest/getDevices_one_device.html
new file mode 100644
index 0000000..da338a45
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/vr/latest/getDevices_one_device.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../resources/fake-vr-displays.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/device/vr/vr_service.mojom.js"></script>
+<script src="../resources/mock-vr-service.js"></script>
+<script src="../resources/test-constants.js"></script>
+<script>
+let fakeDisplays = fakeVRDisplays();
+
+vr_test( (t) => {
+  return navigator.vr.getDevices().then( (devices) => {
+    t.step( () => {
+      assert_true(devices != null);
+      assert_equals(devices.length, 1);
+      let device = devices[0];
+      assert_equals(device.deviceName, 'Google, Inc. Daydream View');
+      assert_false(device.isExternal);
+    }, "getDevices returned correct results");
+  }, (err) => {
+    t.step( () => {
+      assert_unreached("getDevices rejected");
+    });
+  }).then( () => {
+    t.done();
+  });
+}, [fakeDisplays["Pixel"]],
+"navigator.vr.getDevices properly returns a single device");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/vr/latest/getDevices_two_devices.html b/third_party/WebKit/LayoutTests/vr/latest/getDevices_two_devices.html
new file mode 100644
index 0000000..7d6db3e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/vr/latest/getDevices_two_devices.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../resources/fake-vr-displays.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/device/vr/vr_service.mojom.js"></script>
+<script src="../resources/mock-vr-service.js"></script>
+<script src="../resources/test-constants.js"></script>
+<script>
+let fakeDisplays = fakeVRDisplays();
+vr_test( (t) => {
+  return navigator.vr.getDevices().then( (devices) => {
+    t.step( () => {
+      assert_true(devices != null);
+      assert_equals(devices.length, 2);
+    }, "getVRDisplays returned correct results");
+    let pixel = devices[0];
+    let fake = devices[1];
+    t.step( () => {
+      assert_equals(pixel.deviceName, 'Google, Inc. Daydream View');
+      assert_false(pixel.isExternal);
+    }, "Pixel attributes are correct");
+    t.step( () => {
+      assert_equals(fake.deviceName, 'FakeVRDisplay');
+      assert_false(fake.isExternal);
+    }, "Fake device attributes are correct");
+  }, (err) => {
+    t.step( () => {
+      assert_unreached("getDevices rejected");
+    });
+  }).then( () => {
+    t.done();
+  });
+}, [fakeDisplays["Pixel"], fakeDisplays["FakeMagicWindowOnly"]],
+"navigator.vr.getDevices properly returns two devices");
+</script>
+
diff --git a/third_party/WebKit/LayoutTests/vr/latest/getDevices_zero_devices.html b/third_party/WebKit/LayoutTests/vr/latest/getDevices_zero_devices.html
new file mode 100644
index 0000000..eed1d6f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/vr/latest/getDevices_zero_devices.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../resources/fake-vr-displays.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/device/vr/vr_service.mojom.js"></script>
+<script src="../resources/mock-vr-service.js"></script>
+<script src="../resources/test-constants.js"></script>
+<script>
+let fakeDisplays = fakeVRDisplays();
+
+vr_test( (t) => {
+  return navigator.vr.getDevices().then( (devices) => {
+    t.step( () => {
+      assert_true(devices != null);
+      assert_equals(devices.length, 0);
+    }, "getDevices returned correct results");
+  }, (err) => {
+    t.step( () => {
+      assert_unreached("getDevices rejected");
+    });
+  }).then( () => {
+    t.done();
+  });
+}, [],
+"navigator.vr.getDevices properly returns zero devices");
+
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/vr/latest/vrDevice_requestSession_exclusive.html b/third_party/WebKit/LayoutTests/vr/latest/vrDevice_requestSession_exclusive.html
new file mode 100644
index 0000000..4de31e7c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/vr/latest/vrDevice_requestSession_exclusive.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../resources/fake-vr-displays.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/device/vr/vr_service.mojom.js"></script>
+<script src="../resources/mock-vr-service.js"></script>
+<script src="../resources/test-constants.js"></script>
+<canvas id="webgl-canvas"></canvas>
+<script src="../resources/presentation-setup.js"></script>
+<script>
+let fakeDisplays = fakeVRDisplays();
+
+vr_test( (t) => {
+  return navigator.vr.getDevices().then( (devices) => {
+    let pixel = devices[0];
+    let magicWindowOnly = devices[1];
+
+    // Test that requests for an exclusive session outside of a user gesture
+    // are rejected.
+    promise_test( function() {
+      return promise_rejects(this, "InvalidStateError",
+          pixel.requestSession({ exclusive: true }));
+    }, "requestSession rejected outside of a user gesture");
+
+    runWithUserGesture( () => {
+      // Test that a device which only supports non-exclusive sessions rejects
+      // requests for an exclusive context.
+      promise_test( function() {
+        return promise_rejects(this, "NotSupportedError",
+            magicWindowOnly.requestSession({ exclusive: true }));
+      }, "requestSession rejected for device that only supports non-exclusive sessions.");
+
+      // TODO: Once implemented, ensure that the pixel device can create an
+      // exclusive session.
+
+      t.done();
+    });
+  }, (err) => {
+    t.step( () => {
+      assert_unreached("getDevices rejected");
+    });
+  });
+}, [fakeDisplays["Pixel"], fakeDisplays["FakeMagicWindowOnly"]],
+"supportsSession properly creates or rejects exclusive sessions");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/vr/latest/vrDevice_supportsSession_exclusive.html b/third_party/WebKit/LayoutTests/vr/latest/vrDevice_supportsSession_exclusive.html
new file mode 100644
index 0000000..6d51f0b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/vr/latest/vrDevice_supportsSession_exclusive.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../resources/fake-vr-displays.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/device/vr/vr_service.mojom.js"></script>
+<script src="../resources/mock-vr-service.js"></script>
+<script src="../resources/test-constants.js"></script>
+<script>
+let fakeDisplays = fakeVRDisplays();
+
+vr_test( (t) => {
+  return navigator.vr.getDevices().then( (devices) => {
+    let pixel = devices[0];
+    let magicWindowOnly = devices[1];
+
+    return pixel.supportsSession({ exclusive: true }).then( () => {
+      // Expected result
+    }).catch( () => {
+      t.step( () => {
+        assert_unreached("supportsSession unexpectedly rejected exclusive");
+      });
+    }).then( () => {
+      return magicWindowOnly.supportsSession({ exclusive: true }).then( () => {
+        t.step( () => {
+          assert_unreached("supportsSession unexpectedly allowed exclusive");
+        });
+      }).catch( () => {
+        // Expected result
+      });
+    });
+  }, (err) => {
+    t.step( () => {
+      assert_unreached("getDevices rejected");
+    });
+  }).then( () => {
+    t.done();
+  });
+}, [fakeDisplays["Pixel"], fakeDisplays["FakeMagicWindowOnly"]],
+"supportsSession properly identifies supported exclusive sessions");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/vr/latest/vrDevice_supportsSession_non_exclusive.html b/third_party/WebKit/LayoutTests/vr/latest/vrDevice_supportsSession_non_exclusive.html
new file mode 100644
index 0000000..75ccb18
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/vr/latest/vrDevice_supportsSession_non_exclusive.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../resources/fake-vr-displays.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/device/vr/vr_service.mojom.js"></script>
+<script src="../resources/mock-vr-service.js"></script>
+<script src="../resources/test-constants.js"></script>
+<script>
+let fakeDisplays = fakeVRDisplays();
+
+vr_test( (t) => {
+  return navigator.vr.getDevices().then( (devices) => {
+    let pixel = devices[0];
+
+    return pixel.supportsSession().then( () => {
+      t.step( () => {
+        assert_unreached("supportsSession unexpectedly allowed non-exclusive session without an output context");
+      });
+    }).catch( () => {
+      // Expected result
+    });
+    // TODO: Once implemented ensure that non-exclusive sessions with an output
+    // context are supported.
+  }, (err) => {
+    t.step( () => {
+      assert_unreached("getDevices rejected");
+    });
+  }).then( () => {
+    t.done();
+  });
+}, [fakeDisplays["Pixel"]],
+"supportsSession properly identifies supported non-exclusive sessions");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index e4a1461..cfaa7438 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -7173,6 +7173,17 @@
     method getDevices
     setter ondeviceconnect
     setter ondevicedisconnect
+interface VRDevice : EventTarget
+    attribute @@toStringTag
+    getter deviceName
+    getter isExternal
+    method constructor
+    method requestSession
+    method supportsSession
+interface VRDeviceEvent : Event
+    attribute @@toStringTag
+    getter device
+    method constructor
 interface VRDisplay : EventTarget
     attribute @@toStringTag
     getter capabilities
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptModule.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptModule.cpp
index d117d2f6..7b6734f 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptModule.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptModule.cpp
@@ -38,13 +38,6 @@
   CHECK(RuntimeEnabledFeatures::ModuleScriptsEnabled());
 }
 
-ScriptModule::ScriptModule(WTF::HashTableDeletedValueType)
-    : module_(WTF::kHashTableDeletedValue) {
-  // We ensure module-related code is not executed without the flag.
-  // https://crbug.com/715376
-  CHECK(RuntimeEnabledFeatures::ModuleScriptsEnabled());
-}
-
 ScriptModule::ScriptModule(v8::Isolate* isolate, v8::Local<v8::Module> module)
     : module_(SharedPersistent<v8::Module>::Create(module, isolate)),
       identity_hash_(static_cast<unsigned>(module->GetIdentityHash())) {
@@ -106,14 +99,18 @@
   return ScriptValue();
 }
 
-void ScriptModule::Evaluate(ScriptState* script_state) const {
+ScriptValue ScriptModule::Evaluate(ScriptState* script_state,
+                                   CaptureEvalErrorFlag capture_error) const {
   v8::Isolate* isolate = script_state->GetIsolate();
 
   // Isolate exceptions that occur when executing the code. These exceptions
   // should not interfere with javascript code we might evaluate from C++ when
   // returning from here.
   v8::TryCatch try_catch(isolate);
-  try_catch.SetVerbose(true);
+
+  // "If rethrow errors is true, .... Otherwise, report the exception
+  // given by evaluationStatus.[[Value]]." [spec text]
+  try_catch.SetVerbose(capture_error == CaptureEvalErrorFlag::kReport);
 
   probe::ExecuteScript probe(ExecutionContext::From(script_state));
   // TODO(kouhei): We currently don't have a code-path which use return value of
@@ -122,8 +119,16 @@
   if (!V8ScriptRunner::EvaluateModule(module_->NewLocal(isolate),
                                       script_state->GetContext(), isolate)
            .ToLocal(&result)) {
-    return;
+    DCHECK(try_catch.HasCaught());
+    switch (capture_error) {
+      case CaptureEvalErrorFlag::kCapture:
+        return ScriptValue(script_state, try_catch.Exception());
+      case CaptureEvalErrorFlag::kReport:
+        return ScriptValue();
+    }
   }
+
+  return ScriptValue();
 }
 
 void ScriptModule::ReportException(ScriptState* script_state,
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptModule.h b/third_party/WebKit/Source/bindings/core/v8/ScriptModule.h
index 0cf127a..c78f716 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptModule.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptModule.h
@@ -27,6 +27,14 @@
 using ScriptModuleState = v8::Module::Status;
 const char* ScriptModuleStateToString(ScriptModuleState);
 
+// CaptureEvalErrorFlag is used to implement "rethrow errors" parameter in
+// run-a-module-script. When "rethrow errors" is to be set, use kCapture for
+// ScriptModule::Evaluate()/Modulator::EvaluateModule(), and rethrow the
+// returned exception (if any) in the caller of these methods. When "rethrow
+// errors" is not to be set, use kReport, and Evaluate()/EvaluateModule()
+// "report the error".
+enum class CaptureEvalErrorFlag : bool { kReport, kCapture };
+
 // ScriptModule wraps a handle to a v8::Module for use in core.
 //
 // Using ScriptModules needs a ScriptState and its scope to operate in. You
@@ -50,13 +58,14 @@
 
   // TODO(kouhei): Remove copy ctor
   ScriptModule();
-  ScriptModule(WTF::HashTableDeletedValueType);
   ~ScriptModule();
 
   // Returns exception, if any.
   ScriptValue Instantiate(ScriptState*);
 
-  void Evaluate(ScriptState*) const;
+  // Returns exception if CaptureEvalErrorFlag::kCapture is specified.
+  // Otherwise, "report the error" to console.
+  ScriptValue Evaluate(ScriptState*, CaptureEvalErrorFlag) const;
   static void ReportException(ScriptState*, v8::Local<v8::Value> exception);
 
   Vector<String> ModuleRequests(ScriptState*);
@@ -67,26 +76,7 @@
   // Should only be used via ModulatorImpl::GetError()
   v8::Local<v8::Value> ErrorCompletion(ScriptState*);
 
-  bool IsHashTableDeletedValue() const {
-    return module_.IsHashTableDeletedValue();
-  }
-
-  bool operator==(const blink::ScriptModule& other) const {
-    if (IsHashTableDeletedValue() && other.IsHashTableDeletedValue())
-      return true;
-
-    if (IsHashTableDeletedValue() || other.IsHashTableDeletedValue())
-      return false;
-
-    blink::SharedPersistent<v8::Module>* left = module_.Get();
-    blink::SharedPersistent<v8::Module>* right = other.module_.Get();
-    if (left == right)
-      return true;
-    if (!left || !right)
-      return false;
-    return *left == *right;
-  }
-
+  inline bool operator==(const blink::ScriptModule& other) const;
   bool operator!=(const blink::ScriptModule& other) const {
     return !(*this == other);
   }
@@ -115,6 +105,7 @@
   unsigned identity_hash_ = 0;
 
   friend struct ScriptModuleHash;
+  friend struct WTF::HashTraits<blink::ScriptModule>;
 };
 
 struct ScriptModuleHash {
@@ -144,8 +135,41 @@
 
 template <>
 struct HashTraits<blink::ScriptModule>
-    : public SimpleClassHashTraits<blink::ScriptModule> {};
+    : public SimpleClassHashTraits<blink::ScriptModule> {
+  static bool IsDeletedValue(const blink::ScriptModule& value) {
+    return HashTraits<RefPtr<blink::SharedPersistent<v8::Module>>>::
+        IsDeletedValue(value.module_);
+  }
+
+  static void ConstructDeletedValue(blink::ScriptModule& slot,
+                                    bool zero_value) {
+    HashTraits<RefPtr<blink::SharedPersistent<v8::Module>>>::
+        ConstructDeletedValue(slot.module_, zero_value);
+  }
+};
 
 }  // namespace WTF
 
+namespace blink {
+
+inline bool ScriptModule::operator==(const ScriptModule& other) const {
+  if (HashTraits<ScriptModule>::IsDeletedValue(*this) &&
+      HashTraits<ScriptModule>::IsDeletedValue(other))
+    return true;
+
+  if (HashTraits<ScriptModule>::IsDeletedValue(*this) ||
+      HashTraits<ScriptModule>::IsDeletedValue(other))
+    return false;
+
+  blink::SharedPersistent<v8::Module>* left = module_.Get();
+  blink::SharedPersistent<v8::Module>* right = other.module_.Get();
+  if (left == right)
+    return true;
+  if (!left || !right)
+    return false;
+  return *left == *right;
+}
+
+}  // namespace blink
+
 #endif  // ScriptModule_h
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptModuleTest.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptModuleTest.cpp
index 446ea9e5..e85350a 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptModuleTest.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptModuleTest.cpp
@@ -236,7 +236,8 @@
   module_failure.Instantiate(scope.GetScriptState());
   ASSERT_EQ(ScriptModuleState::kInstantiated,
             module_failure.Status(scope.GetScriptState()));
-  module_failure.Evaluate(scope.GetScriptState());
+  module_failure.Evaluate(scope.GetScriptState(),
+                          CaptureEvalErrorFlag::kReport);
   ASSERT_EQ(ScriptModuleState::kErrored,
             module_failure.Status(scope.GetScriptState()));
   v8::Local<v8::Value> error =
@@ -276,7 +277,7 @@
   ScriptValue exception = module.Instantiate(scope.GetScriptState());
   ASSERT_TRUE(exception.IsEmpty());
 
-  module.Evaluate(scope.GetScriptState());
+  module.Evaluate(scope.GetScriptState(), CaptureEvalErrorFlag::kReport);
   v8::Local<v8::Value> value = scope.GetFrame()
                                    .GetScriptController()
                                    .ExecuteScriptInMainWorldAndReturnValue(
@@ -296,6 +297,27 @@
   EXPECT_EQ(42.0, exported_value->NumberValue(scope.GetContext()).ToChecked());
 }
 
+TEST(ScriptModuleTest, EvaluateCaptureError) {
+  V8TestingScope scope;
+
+  auto modulator = new ScriptModuleTestModulator();
+  Modulator::SetModulator(scope.GetScriptState(), modulator);
+
+  ScriptModule module = ScriptModule::Compile(
+      scope.GetIsolate(), "throw 'bar';", "foo.js", kSharableCrossOrigin,
+      WebURLRequest::kFetchCredentialsModeOmit, "", kParserInserted,
+      TextPosition::MinimumPosition(), ASSERT_NO_EXCEPTION);
+  ASSERT_FALSE(module.IsNull());
+  ScriptValue exception = module.Instantiate(scope.GetScriptState());
+  ASSERT_TRUE(exception.IsEmpty());
+
+  ScriptValue error =
+      module.Evaluate(scope.GetScriptState(), CaptureEvalErrorFlag::kCapture);
+  ASSERT_TRUE(error.V8Value()->IsString());
+  EXPECT_EQ("bar", ToCoreString(v8::Local<v8::String>::Cast(error.V8Value())));
+  EXPECT_EQ(ScriptModuleState::kErrored, module.Status(scope.GetScriptState()));
+}
+
 }  // namespace
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/core/v8/ToV8Test.cpp b/third_party/WebKit/Source/bindings/core/v8/ToV8Test.cpp
index fdffde0..3a43a3f 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ToV8Test.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ToV8Test.cpp
@@ -41,9 +41,11 @@
   }
 }
 
-class GarbageCollectedHolder : public GarbageCollected<GarbageCollectedHolder> {
+class GarbageCollectedHolderForToV8Test
+    : public GarbageCollected<GarbageCollectedHolderForToV8Test> {
  public:
-  GarbageCollectedHolder(GarbageCollectedScriptWrappable* script_wrappable)
+  GarbageCollectedHolderForToV8Test(
+      GarbageCollectedScriptWrappable* script_wrappable)
       : script_wrappable_(script_wrappable) {}
 
   DEFINE_INLINE_TRACE() { visitor->Trace(script_wrappable_); }
@@ -66,7 +68,7 @@
   V8TestingScope scope;
   GarbageCollectedScriptWrappable* object =
       new GarbageCollectedScriptWrappable("world");
-  GarbageCollectedHolder holder(object);
+  GarbageCollectedHolderForToV8Test holder(object);
   OffHeapGarbageCollectedHolder off_heap_holder(object);
 
   TEST_TOV8("world", object);
diff --git a/third_party/WebKit/Source/bindings/modules/BUILD.gn b/third_party/WebKit/Source/bindings/modules/BUILD.gn
index 620c18c7..ba19c803 100644
--- a/third_party/WebKit/Source/bindings/modules/BUILD.gn
+++ b/third_party/WebKit/Source/bindings/modules/BUILD.gn
@@ -55,6 +55,7 @@
     "//third_party/WebKit/Source/modules/speech/SpeechSynthesisEvent.idl",
     "//third_party/WebKit/Source/modules/storage/StorageEvent.idl",
     "//third_party/WebKit/Source/modules/vr/VRDisplayEvent.idl",
+    "//third_party/WebKit/Source/modules/vr/latest/VRDeviceEvent.idl",
     "//third_party/WebKit/Source/modules/webaudio/AudioProcessingEvent.idl",
     "//third_party/WebKit/Source/modules/webaudio/OfflineAudioCompletionEvent.idl",
     "//third_party/WebKit/Source/modules/webgl/WebGLContextEvent.idl",
diff --git a/third_party/WebKit/Source/core/dom/Element.cpp b/third_party/WebKit/Source/core/dom/Element.cpp
index 85b8a921..e5e0d401 100644
--- a/third_party/WebKit/Source/core/dom/Element.cpp
+++ b/third_party/WebKit/Source/core/dom/Element.cpp
@@ -946,7 +946,11 @@
 
   if (GetDocument().ScrollingElementNoLayout() == this) {
     if (GetDocument().View())
-      return AdjustForAbsoluteZoom(GetDocument().View()->ContentsWidth(),
+      return AdjustForAbsoluteZoom(GetDocument()
+                                       .View()
+                                       ->LayoutViewportScrollableArea()
+                                       ->ContentsSize()
+                                       .Width(),
                                    GetDocument().GetFrame()->PageZoomFactor());
     return 0;
   }
@@ -964,7 +968,11 @@
 
   if (GetDocument().ScrollingElementNoLayout() == this) {
     if (GetDocument().View())
-      return AdjustForAbsoluteZoom(GetDocument().View()->ContentsHeight(),
+      return AdjustForAbsoluteZoom(GetDocument()
+                                       .View()
+                                       ->LayoutViewportScrollableArea()
+                                       ->ContentsSize()
+                                       .Height(),
                                    GetDocument().GetFrame()->PageZoomFactor());
     return 0;
   }
diff --git a/third_party/WebKit/Source/core/dom/Modulator.h b/third_party/WebKit/Source/core/dom/Modulator.h
index 72d2b93..0003b4bd 100644
--- a/third_party/WebKit/Source/core/dom/Modulator.h
+++ b/third_party/WebKit/Source/core/dom/Modulator.h
@@ -142,7 +142,17 @@
   virtual Vector<ModuleRequest> ModuleRequestsFromScriptModule(
       ScriptModule) = 0;
 
-  virtual void ExecuteModule(const ModuleScript*) = 0;
+  // ExecuteModule implements #run-a-module-script HTML spec algorithm.
+  // https://html.spec.whatwg.org/multipage/webappapis.html#run-a-module-script
+  // Note: "rethrow errors" flag in the spec corresponds to capture_error being
+  // CaptureEvalErrorFlag::kCapture. Here we rely on caller to handle the
+  // exception. The current only caller is
+  // DynamicImportTreeClinet::NotifyModuleTreeLoadFinished, which catches
+  // the exception immediately, so just returning the exception value here is
+  // more convenient and optimal.
+  virtual ScriptValue ExecuteModule(
+      const ModuleScript*,
+      CaptureEvalErrorFlag = CaptureEvalErrorFlag::kReport) = 0;
 
   virtual ModuleScriptFetcher* CreateModuleScriptFetcher() = 0;
 
diff --git a/third_party/WebKit/Source/core/dom/ModulatorImplBase.cpp b/third_party/WebKit/Source/core/dom/ModulatorImplBase.cpp
index d1f324f..160518a 100644
--- a/third_party/WebKit/Source/core/dom/ModulatorImplBase.cpp
+++ b/third_party/WebKit/Source/core/dom/ModulatorImplBase.cpp
@@ -186,7 +186,9 @@
   return requests;
 }
 
-void ModulatorImplBase::ExecuteModule(const ModuleScript* module_script) {
+ScriptValue ModulatorImplBase::ExecuteModule(
+    const ModuleScript* module_script,
+    CaptureEvalErrorFlag capture_error) {
   // https://html.spec.whatwg.org/#run-a-module-script
 
   // We ensure module-related code is not executed without the flag.
@@ -199,7 +201,7 @@
   // Step 2. "Check if we can run script with settings.
   //          If this returns "do not run" then abort these steps." [spec text]
   if (!GetExecutionContext()->CanExecuteScripts(kAboutToExecuteScript))
-    return;
+    return ScriptValue();
 
   // Step 4. "Prepare to run script given settings." [spec text]
   // This is placed here to also cover ScriptModule::ReportException().
@@ -210,7 +212,7 @@
   if (module_script->IsErrored()) {
     ScriptValue error = GetError(module_script);
     ScriptModule::ReportException(script_state_.Get(), error.V8Value());
-    return;
+    return ScriptValue();
   }
 
   // Step 5. "Let record be s's module record." [spec text]
@@ -218,11 +220,21 @@
   CHECK(!record.IsNull());
 
   // Step 6. "Let evaluationStatus be record.ModuleEvaluation()." [spec text]
-  record.Evaluate(script_state_.Get());
+  ScriptValue eval_error = record.Evaluate(script_state_.Get(), capture_error);
+  // Step 7. "If evaluationStatus is an abrupt completion, then:" [spec text]
+  if (capture_error == CaptureEvalErrorFlag::kCapture) {
+    // Step 7.1. "If rethrow errors is true, rethrow the exception given by
+    // evaluationStatus.[[Value]] for s." [spec text]
+    return eval_error;
+  }
+  DCHECK(eval_error.IsEmpty());
 
-  // Step 7. "If evaluationStatus is an abrupt completion, then report the
-  // exception given by evaluationStatus.[[Value]] for s." [spec text]
-  // TODO(kouhei): Implement this.
+  // Step 7.2. "Otherwise, report the exception given by
+  // evaluationStatus.[[Value]] for script." [spec text]
+  // This is done inside ScriptModule::EvaluateScript if capture_error ==
+  // kReport.
+
+  return ScriptValue();
 
   // Step 8. "Clean up after running script with settings." [spec text]
   // Implemented as the ScriptState::Scope destructor.
diff --git a/third_party/WebKit/Source/core/dom/ModulatorImplBase.h b/third_party/WebKit/Source/core/dom/ModulatorImplBase.h
index c839a9e..8070b18 100644
--- a/third_party/WebKit/Source/core/dom/ModulatorImplBase.h
+++ b/third_party/WebKit/Source/core/dom/ModulatorImplBase.h
@@ -74,7 +74,7 @@
   ScriptModuleState GetRecordStatus(ScriptModule) override;
   ScriptValue GetError(const ModuleScript*) override;
   Vector<ModuleRequest> ModuleRequestsFromScriptModule(ScriptModule) override;
-  void ExecuteModule(const ModuleScript*) override;
+  ScriptValue ExecuteModule(const ModuleScript*, CaptureEvalErrorFlag) override;
 
   RefPtr<ScriptState> script_state_;
   RefPtr<WebTaskRunner> task_runner_;
diff --git a/third_party/WebKit/Source/core/dom/SpaceSplitString.cpp b/third_party/WebKit/Source/core/dom/SpaceSplitString.cpp
index 97a1bda..c80dd05a 100644
--- a/third_party/WebKit/Source/core/dom/SpaceSplitString.cpp
+++ b/third_party/WebKit/Source/core/dom/SpaceSplitString.cpp
@@ -23,6 +23,7 @@
 #include "core/html/parser/HTMLParserIdioms.h"
 #include "platform/wtf/HashSet.h"
 #include "platform/wtf/text/AtomicStringHash.h"
+#include "platform/wtf/text/StringHash.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/core/exported/WebFrameTest.cpp b/third_party/WebKit/Source/core/exported/WebFrameTest.cpp
index b5748c1..d708c92 100644
--- a/third_party/WebKit/Source/core/exported/WebFrameTest.cpp
+++ b/third_party/WebKit/Source/core/exported/WebFrameTest.cpp
@@ -1482,13 +1482,16 @@
   EXPECT_EQ(980, web_view_helper.WebView()
                      ->MainFrameImpl()
                      ->GetFrameView()
+                     ->LayoutViewportScrollableArea()
                      ->ContentsSize()
                      .Width());
-  EXPECT_EQ(980.0 / viewport_width * viewport_height, web_view_helper.WebView()
-                                                          ->MainFrameImpl()
-                                                          ->GetFrameView()
-                                                          ->ContentsSize()
-                                                          .Height());
+  EXPECT_EQ(980.0 / viewport_width * viewport_height,
+            web_view_helper.WebView()
+                ->MainFrameImpl()
+                ->GetFrameView()
+                ->LayoutViewportScrollableArea()
+                ->ContentsSize()
+                .Height());
 }
 
 TEST_P(ParameterizedWebFrameTest, WideViewportSetsTo980WithXhtmlMp) {
@@ -2537,6 +2540,7 @@
   EXPECT_EQ(980, web_view_helper.WebView()
                      ->MainFrameImpl()
                      ->GetFrameView()
+                     ->LayoutViewportScrollableArea()
                      ->ContentsSize()
                      .Width());
 }
@@ -3132,19 +3136,22 @@
   web_view_helper.Resize(WebSize(viewport_width, viewport_height));
 
   LocalFrameView* view = web_view_helper.LocalMainFrame()->GetFrameView();
-  EXPECT_EQ(view->ScrollSize(kHorizontalScrollbar),
-            view->ContentsSize().Width() - view->VisibleContentRect().Width());
-  EXPECT_EQ(
-      view->ScrollSize(kVerticalScrollbar),
-      view->ContentsSize().Height() - view->VisibleContentRect().Height());
+  ScrollableArea* scrollable_area = view->LayoutViewportScrollableArea();
+  EXPECT_EQ(scrollable_area->ScrollSize(kHorizontalScrollbar),
+            scrollable_area->ContentsSize().Width() -
+                view->VisibleContentRect().Width());
+  EXPECT_EQ(scrollable_area->ScrollSize(kVerticalScrollbar),
+            scrollable_area->ContentsSize().Height() -
+                view->VisibleContentRect().Height());
 
   web_view_helper.WebView()->SetPageScaleFactor(10);
 
-  EXPECT_EQ(view->ScrollSize(kHorizontalScrollbar),
-            view->ContentsSize().Width() - view->VisibleContentRect().Width());
-  EXPECT_EQ(
-      view->ScrollSize(kVerticalScrollbar),
-      view->ContentsSize().Height() - view->VisibleContentRect().Height());
+  EXPECT_EQ(scrollable_area->ScrollSize(kHorizontalScrollbar),
+            scrollable_area->ContentsSize().Width() -
+                view->VisibleContentRect().Width());
+  EXPECT_EQ(scrollable_area->ScrollSize(kVerticalScrollbar),
+            scrollable_area->ContentsSize().Height() -
+                view->VisibleContentRect().Height());
 }
 
 TEST_P(ParameterizedWebFrameTest, CanOverrideScaleLimits) {
@@ -7961,7 +7968,9 @@
   // and allowing it to scroll an additional 20px.
   web_view->ApplyViewportDeltas(WebFloatSize(), WebFloatSize(), WebFloatSize(),
                                 1.0f, 20.0f / browser_controls_height);
-  EXPECT_SIZE_EQ(ScrollOffset(0, 1920), frame_view->MaximumScrollOffset());
+  EXPECT_SIZE_EQ(
+      ScrollOffset(0, 1920),
+      frame_view->LayoutViewportScrollableArea()->MaximumScrollOffset());
 
   // Show more, make sure the scroll actually gets clamped.
   web_view->ApplyViewportDeltas(WebFloatSize(), WebFloatSize(), WebFloatSize(),
@@ -7973,7 +7982,9 @@
   // Hide until there's 10px showing.
   web_view->ApplyViewportDeltas(WebFloatSize(), WebFloatSize(), WebFloatSize(),
                                 1.0f, -30.0f / browser_controls_height);
-  EXPECT_SIZE_EQ(ScrollOffset(0, 1910), frame_view->MaximumScrollOffset());
+  EXPECT_SIZE_EQ(
+      ScrollOffset(0, 1910),
+      frame_view->LayoutViewportScrollableArea()->MaximumScrollOffset());
 
   // Simulate a LayoutEmbeddedContent::resize. The frame is resized to
   // accomodate the browser controls and Blink's view of the browser controls
@@ -7982,12 +7993,16 @@
                                 1.0f, 30.0f / browser_controls_height);
   web_view->ResizeWithBrowserControls(WebSize(100, 60), 40.0f, 0, true);
   web_view->UpdateAllLifecyclePhases();
-  EXPECT_SIZE_EQ(ScrollOffset(0, 1940), frame_view->MaximumScrollOffset());
+  EXPECT_SIZE_EQ(
+      ScrollOffset(0, 1940),
+      frame_view->LayoutViewportScrollableArea()->MaximumScrollOffset());
 
   // Now simulate hiding.
   web_view->ApplyViewportDeltas(WebFloatSize(), WebFloatSize(), WebFloatSize(),
                                 1.0f, -10.0f / browser_controls_height);
-  EXPECT_SIZE_EQ(ScrollOffset(0, 1930), frame_view->MaximumScrollOffset());
+  EXPECT_SIZE_EQ(
+      ScrollOffset(0, 1930),
+      frame_view->LayoutViewportScrollableArea()->MaximumScrollOffset());
 
   // Reset to original state: 100px widget height, browser controls fully
   // hidden.
@@ -7996,7 +8011,9 @@
   web_view->ResizeWithBrowserControls(WebSize(100, 100),
                                       browser_controls_height, 0, false);
   web_view->UpdateAllLifecyclePhases();
-  EXPECT_SIZE_EQ(ScrollOffset(0, 1900), frame_view->MaximumScrollOffset());
+  EXPECT_SIZE_EQ(
+      ScrollOffset(0, 1900),
+      frame_view->LayoutViewportScrollableArea()->MaximumScrollOffset());
 
   // Show the browser controls by just 1px, since we're zoomed in to 2X, that
   // should allow an extra 0.5px of scrolling in the visual viewport. Make
@@ -8004,11 +8021,15 @@
   // main frame.
   web_view->ApplyViewportDeltas(WebFloatSize(), WebFloatSize(), WebFloatSize(),
                                 1.0f, 1.0f / browser_controls_height);
-  EXPECT_SIZE_EQ(ScrollOffset(0, 1901), frame_view->MaximumScrollOffset());
+  EXPECT_SIZE_EQ(
+      ScrollOffset(0, 1901),
+      frame_view->LayoutViewportScrollableArea()->MaximumScrollOffset());
 
   web_view->ApplyViewportDeltas(WebFloatSize(), WebFloatSize(), WebFloatSize(),
                                 1.0f, 2.0f / browser_controls_height);
-  EXPECT_SIZE_EQ(ScrollOffset(0, 1903), frame_view->MaximumScrollOffset());
+  EXPECT_SIZE_EQ(
+      ScrollOffset(0, 1903),
+      frame_view->LayoutViewportScrollableArea()->MaximumScrollOffset());
 }
 
 TEST_P(ParameterizedWebFrameTest, MaximumScrollPositionCanBeNegative) {
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
index 7092543..2139712f 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
@@ -714,11 +714,11 @@
   return scrollbar_manager_.CreateScrollbar(orientation);
 }
 
-void LocalFrameView::SetContentsSize(const IntSize& size) {
-  if (size == ContentsSize())
+void LocalFrameView::SetLayoutOverflowSize(const IntSize& size) {
+  if (size == layout_overflow_size_)
     return;
 
-  contents_size_ = size;
+  layout_overflow_size_ = size;
   needs_scrollbars_update_ = true;
   ScrollableArea::ContentsResized();
 
@@ -753,7 +753,7 @@
       SetScrollOrigin(origin);
   }
 
-  SetContentsSize(size);
+  SetLayoutOverflowSize(size);
 }
 
 void LocalFrameView::AdjustViewSizeAndLayout() {
@@ -2736,7 +2736,12 @@
   // 4) scrolling: no;
 
   // Covers #1
-  IntSize contents_size = this->ContentsSize();
+  IntSize contents_size;
+  if (GetLayoutView())
+    contents_size = GetLayoutView()->DocumentRect().Size();
+  else
+    contents_size = ContentsSize();
+
   IntSize visible_content_size = VisibleContentRect().Size();
   if ((contents_size.Height() <= visible_content_size.Height() &&
        contents_size.Width() <= visible_content_size.Width()))
@@ -4088,7 +4093,9 @@
 }
 
 IntSize LocalFrameView::ContentsSize() const {
-  return contents_size_;
+  if (RuntimeEnabledFeatures::RootLayerScrollingEnabled())
+    return Size();
+  return layout_overflow_size_;
 }
 
 void LocalFrameView::ClipPaintRect(FloatRect* paint_rect) const {
@@ -4124,7 +4131,7 @@
 
   // If no scrollbars are present, the content may still be scrollable.
   if (!scrollbar) {
-    IntSize scroll_size = contents_size_ - VisibleContentRect().Size();
+    IntSize scroll_size = ContentsSize() - VisibleContentRect().Size();
     scroll_size.ClampNegativeToZero();
     return orientation == kHorizontalScrollbar ? scroll_size.Width()
                                                : scroll_size.Height();
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameView.h b/third_party/WebKit/Source/core/frame/LocalFrameView.h
index 6296e2c..3b6a772 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameView.h
+++ b/third_party/WebKit/Source/core/frame/LocalFrameView.h
@@ -154,7 +154,7 @@
 
   Scrollbar* CreateScrollbar(ScrollbarOrientation);
 
-  void SetContentsSize(const IntSize&);
+  void SetLayoutOverflowSize(const IntSize&);
 
   void UpdateLayout();
   bool DidFirstLayout() const;
@@ -1165,7 +1165,7 @@
 
   ScrollOffset pending_scroll_delta_;
   ScrollOffset scroll_offset_;
-  IntSize contents_size_;
+  IntSize layout_overflow_size_;
 
   bool scrollbars_suppressed_;
 
diff --git a/third_party/WebKit/Source/core/frame/VisualViewport.cpp b/third_party/WebKit/Source/core/frame/VisualViewport.cpp
index 83c6edb0..314525b 100644
--- a/third_party/WebKit/Source/core/frame/VisualViewport.cpp
+++ b/third_party/WebKit/Source/core/frame/VisualViewport.cpp
@@ -595,9 +595,11 @@
 
   IntSize visual_viewport_max =
       FlooredIntSize(FloatSize(ContentsSize()) - scaled_size);
-  IntSize max = view->MaximumScrollOffsetInt() + visual_viewport_max;
+  IntSize max = view->LayoutViewportScrollableArea()->MaximumScrollOffsetInt() +
+                visual_viewport_max;
   IntSize min =
-      view->MinimumScrollOffsetInt();  // VisualViewportMin should be (0, 0)
+      view->LayoutViewportScrollableArea()
+          ->MinimumScrollOffsetInt();  // VisualViewportMin should be (0, 0)
 
   IntSize clamped = ToIntSize(offset);
   clamped = clamped.ShrunkTo(max);
diff --git a/third_party/WebKit/Source/core/frame/VisualViewportTest.cpp b/third_party/WebKit/Source/core/frame/VisualViewportTest.cpp
index 43cc623e..38d5209 100644
--- a/third_party/WebKit/Source/core/frame/VisualViewportTest.cpp
+++ b/third_party/WebKit/Source/core/frame/VisualViewportTest.cpp
@@ -1243,15 +1243,16 @@
   EXPECT_SIZE_EQ(FloatSize(250.5f, 100.5f), visual_viewport.GetScrollOffset());
 }
 
-static ScrollOffset expectedMaxFrameViewScrollOffset(
+static ScrollOffset expectedMaxLayoutViewportScrollOffset(
     VisualViewport& visual_viewport,
     LocalFrameView& frame_view) {
   float aspect_ratio = visual_viewport.VisibleRect().Width() /
                        visual_viewport.VisibleRect().Height();
   float new_height = frame_view.FrameRect().Width() / aspect_ratio;
-  return ScrollOffset(
-      frame_view.ContentsSize().Width() - frame_view.FrameRect().Width(),
-      frame_view.ContentsSize().Height() - new_height);
+  IntSize contents_size =
+      frame_view.LayoutViewportScrollableArea()->ContentsSize();
+  return ScrollOffset(contents_size.Width() - frame_view.FrameRect().Width(),
+                      contents_size.Height() - new_height);
 }
 
 TEST_P(VisualViewportTest, TestBrowserControlsAdjustment) {
@@ -1283,8 +1284,9 @@
   // The outer viewport (LocalFrameView) should be affected as well.
   frame_view.LayoutViewportScrollableArea()->ScrollBy(
       ScrollOffset(10000, 10000), kUserScroll);
-  EXPECT_SIZE_EQ(expectedMaxFrameViewScrollOffset(visual_viewport, frame_view),
-                 frame_view.LayoutViewportScrollableArea()->GetScrollOffset());
+  EXPECT_SIZE_EQ(
+      expectedMaxLayoutViewportScrollOffset(visual_viewport, frame_view),
+      frame_view.LayoutViewportScrollableArea()->GetScrollOffset());
 
   // Simulate bringing up the browser controls by 10.5px.
   WebView()->ApplyViewportDeltas(WebFloatSize(), WebFloatSize(), WebFloatSize(),
@@ -1300,8 +1302,9 @@
   // The outer viewport (LocalFrameView) should be affected as well.
   frame_view.LayoutViewportScrollableArea()->ScrollBy(
       ScrollOffset(10000, 10000), kUserScroll);
-  EXPECT_SIZE_EQ(expectedMaxFrameViewScrollOffset(visual_viewport, frame_view),
-                 frame_view.LayoutViewportScrollableArea()->GetScrollOffset());
+  EXPECT_SIZE_EQ(
+      expectedMaxLayoutViewportScrollOffset(visual_viewport, frame_view),
+      frame_view.LayoutViewportScrollableArea()->GetScrollOffset());
 }
 
 TEST_P(VisualViewportTest, TestBrowserControlsAdjustmentWithScale) {
@@ -1333,7 +1336,7 @@
   frame_view.LayoutViewportScrollableArea()->ScrollBy(
       ScrollOffset(10000, 10000), kUserScroll);
   ScrollOffset expected =
-      expectedMaxFrameViewScrollOffset(visual_viewport, frame_view);
+      expectedMaxLayoutViewportScrollOffset(visual_viewport, frame_view);
   EXPECT_SIZE_EQ(expected,
                  frame_view.LayoutViewportScrollableArea()->GetScrollOffset());
 
@@ -1370,8 +1373,9 @@
 
   frame_view.LayoutViewportScrollableArea()->ScrollBy(
       ScrollOffset(10000, 10000), kUserScroll);
-  EXPECT_SIZE_EQ(expectedMaxFrameViewScrollOffset(visual_viewport, frame_view),
-                 frame_view.LayoutViewportScrollableArea()->GetScrollOffset());
+  EXPECT_SIZE_EQ(
+      expectedMaxLayoutViewportScrollOffset(visual_viewport, frame_view),
+      frame_view.LayoutViewportScrollableArea()->GetScrollOffset());
 }
 
 // Tests that a scroll all the way to the bottom of the page, while hiding the
@@ -1420,7 +1424,7 @@
                  visual_viewport.VisibleRect().Size());
 
   ScrollOffset frame_view_expected =
-      expectedMaxFrameViewScrollOffset(visual_viewport, frame_view);
+      expectedMaxLayoutViewportScrollOffset(visual_viewport, frame_view);
   ScrollOffset visual_viewport_expected = ScrollOffset(
       750, layout_viewport_height - visual_viewport_height / page_scale);
 
diff --git a/third_party/WebKit/Source/core/input/EventHandler.cpp b/third_party/WebKit/Source/core/input/EventHandler.cpp
index 488e152..8d194970 100644
--- a/third_party/WebKit/Source/core/input/EventHandler.cpp
+++ b/third_party/WebKit/Source/core/input/EventHandler.cpp
@@ -405,7 +405,7 @@
   if (!node)
     return false;
 
-  if (node->IsTextNode() && node->CanStartSelection())
+  if (node->IsTextNode() && (node->CanStartSelection() || result.IsOverLink()))
     return true;
 
   return HasEditableStyle(*node);
diff --git a/third_party/WebKit/Source/core/input/EventHandler.h b/third_party/WebKit/Source/core/input/EventHandler.h
index 0a940ab..6853c06 100644
--- a/third_party/WebKit/Source/core/input/EventHandler.h
+++ b/third_party/WebKit/Source/core/input/EventHandler.h
@@ -413,6 +413,7 @@
                            ShadowChildCanOverrideUserSelectText);
   FRIEND_TEST_ALL_PREFIXES(EventHandlerTest, InputFieldsCanStartSelection);
   FRIEND_TEST_ALL_PREFIXES(EventHandlerTest, ImagesCannotStartSelection);
+  FRIEND_TEST_ALL_PREFIXES(EventHandlerTest, AnchorTextCannotStartSelection);
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/input/EventHandlerTest.cpp b/third_party/WebKit/Source/core/input/EventHandlerTest.cpp
index dd999122..173463d6 100644
--- a/third_party/WebKit/Source/core/input/EventHandlerTest.cpp
+++ b/third_party/WebKit/Source/core/input/EventHandlerTest.cpp
@@ -432,6 +432,28 @@
                                                                          hit));
 }
 
+TEST_F(EventHandlerTest, ReadOnlyInputDoesNotInheritUserSelect) {
+  SetHtmlInnerHTML(
+      "<div style='user-select: none'>"
+      "<input readonly value='blabla'>"
+      "</div>");
+  Element* const field =
+      ToElement(GetDocument().body()->firstChild()->firstChild());
+  ShadowRoot* const shadow_root = field->UserAgentShadowRoot();
+
+  Element* const text = shadow_root->getElementById("inner-editor");
+  LayoutPoint location = text->GetLayoutObject()->VisualRect().Center();
+  HitTestResult hit =
+      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtPoint(
+          location);
+  EXPECT_TRUE(text->CanStartSelection());
+
+  // TODO(crbug.com/764661): Show I-beam because field is selectable.
+  // EXPECT_TRUE(
+  //   GetDocument().GetFrame()->GetEventHandler().ShouldShowIBeamForNode(field,
+  //                                                                      hit));
+}
+
 TEST_F(EventHandlerTest, ImagesCannotStartSelection) {
   SetHtmlInnerHTML("<img>");
   Element* const img = ToElement(GetDocument().body()->firstChild());
@@ -445,6 +467,21 @@
                                                                          hit));
 }
 
+TEST_F(EventHandlerTest, AnchorTextCannotStartSelection) {
+  SetHtmlInnerHTML("<a id='id' href=bala>Anchor Text</a>");
+  Node* const link = GetDocument().body()->firstChild();
+  LayoutPoint location = link->GetLayoutObject()->VisualRect().Center();
+  HitTestResult hit =
+      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtPoint(
+          location);
+  Node* const text = link->firstChild();
+  EXPECT_FALSE(text->CanStartSelection());
+  EXPECT_TRUE(hit.IsOverLink());
+  EXPECT_TRUE(
+      GetDocument().GetFrame()->GetEventHandler().ShouldShowIBeamForNode(text,
+                                                                         hit));
+}
+
 // Regression test for http://crbug.com/641403 to verify we use up-to-date
 // layout tree for dispatching "contextmenu" event.
 TEST_F(EventHandlerTest, sendContextMenuEventWithHover) {
diff --git a/third_party/WebKit/Source/core/inspector/InspectorDOMSnapshotAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorDOMSnapshotAgent.cpp
index e03496ac..32d0973 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorDOMSnapshotAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorDOMSnapshotAgent.cpp
@@ -70,12 +70,11 @@
   }
 
   static void ConstructDeletedValue(Vector<String>& vec, bool) {
-    vec.clear();
-    vec.push_back(String(WTF::kHashTableDeletedValue));
+    new (NotNull, &vec) Vector<String>(WTF::kHashTableDeletedValue);
   }
 
   static bool IsDeletedValue(const Vector<String>& vec) {
-    return !vec.IsEmpty() && vec[0].IsHashTableDeletedValue();
+    return vec.IsHashTableDeletedValue();
   }
 
   static bool IsEmptyValue(const Vector<String>& vec) { return vec.IsEmpty(); }
diff --git a/third_party/WebKit/Source/core/inspector/V8InspectorString.cpp b/third_party/WebKit/Source/core/inspector/V8InspectorString.cpp
index f9c7254..71fac42 100644
--- a/third_party/WebKit/Source/core/inspector/V8InspectorString.cpp
+++ b/third_party/WebKit/Source/core/inspector/V8InspectorString.cpp
@@ -55,6 +55,24 @@
       string.length());
 }
 
+// static
+void StringUtil::builderAppendQuotedString(StringBuilder& builder,
+                                           const String& str) {
+  builder.Append('"');
+  if (!str.IsEmpty()) {
+    if (str.Is8Bit()) {
+      escapeLatinStringForJSON(
+          reinterpret_cast<const uint8_t*>(str.Characters8()), str.length(),
+          &builder);
+    } else {
+      escapeWideStringForJSON(
+          reinterpret_cast<const uint16_t*>(str.Characters16()), str.length(),
+          &builder);
+    }
+  }
+  builder.Append('"');
+}
+
 }  // namespace protocol
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/inspector/V8InspectorString.h b/third_party/WebKit/Source/core/inspector/V8InspectorString.h
index 08bbb3c..12bdc42 100644
--- a/third_party/WebKit/Source/core/inspector/V8InspectorString.h
+++ b/third_party/WebKit/Source/core/inspector/V8InspectorString.h
@@ -64,6 +64,7 @@
   static void builderAppend(StringBuilder& builder, const char* s, size_t len) {
     builder.Append(s, len);
   }
+  static void builderAppendQuotedString(StringBuilder&, const String&);
   static void builderReserve(StringBuilder& builder, unsigned capacity) {
     builder.ReserveCapacity(capacity);
   }
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.cpp b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
index 089b786..1c34cb8 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
@@ -3115,8 +3115,20 @@
 
     node = ancestor->GetNode();
 
-    if (!node)
-      continue;
+    if (!node) {
+      if (!ancestor->VirtualContinuation())
+        continue;
+
+      // This is an anonymous continuation; ie. our ancestor is really the split
+      // inline. Find it by spooling to the end of the continuation chain.
+
+      while (LayoutBoxModelObject* cont = ancestor->VirtualContinuation())
+        ancestor = cont;
+
+      node = ancestor->GetNode();
+
+      DCHECK(node);
+    }
 
     // TODO(kochi): If |base| or |node| is nested deep in shadow roots, this
     // loop may get expensive, as isUnclosedNodeOf() can take up to O(N+M) time
diff --git a/third_party/WebKit/Source/core/layout/LayoutTextControl.cpp b/third_party/WebKit/Source/core/layout/LayoutTextControl.cpp
index 3ca9443..50ce95a2 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTextControl.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTextControl.cpp
@@ -79,6 +79,7 @@
   // element.
   text_block_style.SetDirection(Style()->Direction());
   text_block_style.SetUnicodeBidi(Style()->GetUnicodeBidi());
+  text_block_style.SetUserSelect(EUserSelect::kText);
 
   UpdateUserModifyProperty(*GetTextControlElement(), text_block_style);
 }
diff --git a/third_party/WebKit/Source/core/page/scrolling/RootScrollerTest.cpp b/third_party/WebKit/Source/core/page/scrolling/RootScrollerTest.cpp
index 664e24d..7640162 100644
--- a/third_party/WebKit/Source/core/page/scrolling/RootScrollerTest.cpp
+++ b/third_party/WebKit/Source/core/page/scrolling/RootScrollerTest.cpp
@@ -927,7 +927,8 @@
 
   MainFrameView()->UpdateAllLifecyclePhases();
 
-  ScrollableArea* container_scroller = child_frame->View();
+  ScrollableArea* container_scroller =
+      child_frame->View()->LayoutViewportScrollableArea();
 
   EXPECT_FALSE(container_scroller->HorizontalScrollbar());
   EXPECT_FALSE(container_scroller->VerticalScrollbar());
diff --git a/third_party/WebKit/Source/core/paint/PaintInvalidationTest.cpp b/third_party/WebKit/Source/core/paint/PaintInvalidationTest.cpp
index 3b15c04f..030682c1 100644
--- a/third_party/WebKit/Source/core/paint/PaintInvalidationTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintInvalidationTest.cpp
@@ -58,7 +58,8 @@
 
   GetDocument().View()->UpdateAllLifecyclePhases();
 
-  ScrollableArea* scrollable_area = GetDocument().View();
+  ScrollableArea* scrollable_area =
+      GetDocument().View()->LayoutViewportScrollableArea();
   ASSERT_EQ(scrollable_area->MaximumScrollOffset().Height(), 0);
   EXPECT_FALSE(GetDocument().GetLayoutView()->MayNeedPaintInvalidation());
 
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp b/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp
index ec1c736..d788d3a 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp
@@ -1120,4 +1120,12 @@
   GetDocument().View()->UpdateAllLifecyclePhases();
 }
 
+TEST_P(PaintLayerTest, FrameViewContentSize) {
+  bool rls = RuntimeEnabledFeatures::RootLayerScrollingEnabled();
+  SetBodyInnerHTML(
+      "<style> body { width: 1200px; height: 900px; margin: 0 } </style>");
+  EXPECT_EQ(rls ? IntSize(800, 600) : IntSize(1200, 900),
+            GetDocument().View()->ContentsSize());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/testing/DummyModulator.cpp b/third_party/WebKit/Source/core/testing/DummyModulator.cpp
index 94d63e7..d2fb721 100644
--- a/third_party/WebKit/Source/core/testing/DummyModulator.cpp
+++ b/third_party/WebKit/Source/core/testing/DummyModulator.cpp
@@ -134,8 +134,10 @@
   return Vector<ModuleRequest>();
 }
 
-void DummyModulator::ExecuteModule(const ModuleScript*) {
+ScriptValue DummyModulator::ExecuteModule(const ModuleScript*,
+                                          CaptureEvalErrorFlag) {
   NOTREACHED();
+  return ScriptValue();
 }
 
 ModuleScriptFetcher* DummyModulator::CreateModuleScriptFetcher() {
diff --git a/third_party/WebKit/Source/core/testing/DummyModulator.h b/third_party/WebKit/Source/core/testing/DummyModulator.h
index 953b93f..d9c235e 100644
--- a/third_party/WebKit/Source/core/testing/DummyModulator.h
+++ b/third_party/WebKit/Source/core/testing/DummyModulator.h
@@ -64,7 +64,7 @@
   ScriptModuleState GetRecordStatus(ScriptModule) override;
   ScriptValue GetError(const ModuleScript*) override;
   Vector<ModuleRequest> ModuleRequestsFromScriptModule(ScriptModule) override;
-  void ExecuteModule(const ModuleScript*) override;
+  ScriptValue ExecuteModule(const ModuleScript*, CaptureEvalErrorFlag) override;
   ModuleScriptFetcher* CreateModuleScriptFetcher() override;
 
   Member<ScriptModuleResolver> resolver_;
diff --git a/third_party/WebKit/Source/modules/EventTargetModulesFactory.json5 b/third_party/WebKit/Source/modules/EventTargetModulesFactory.json5
index 5309097..ab7c8415 100644
--- a/third_party/WebKit/Source/modules/EventTargetModulesFactory.json5
+++ b/third_party/WebKit/Source/modules/EventTargetModulesFactory.json5
@@ -47,6 +47,7 @@
     "modules/speech/SpeechSynthesisUtterance",
     "modules/vr/VRDisplay",
     "modules/vr/latest/VR",
+    "modules/vr/latest/VRDevice",
     "modules/webaudio/AudioContext",
     "modules/webaudio/AudioNode",
     "modules/webmidi/MIDIAccess",
diff --git a/third_party/WebKit/Source/modules/modules_idl_files.gni b/third_party/WebKit/Source/modules/modules_idl_files.gni
index c1d9d4d..2243172 100644
--- a/third_party/WebKit/Source/modules/modules_idl_files.gni
+++ b/third_party/WebKit/Source/modules/modules_idl_files.gni
@@ -308,6 +308,8 @@
                     "vr/VRPose.idl",
                     "vr/VRStageParameters.idl",
                     "vr/latest/VR.idl",
+                    "vr/latest/VRDevice.idl",
+                    "vr/latest/VRDeviceEvent.idl",
                     "webaudio/AnalyserNode.idl",
                     "webaudio/AudioBuffer.idl",
                     "webaudio/AudioBufferSourceNode.idl",
@@ -569,6 +571,8 @@
                     "storage/StorageEventInit.idl",
                     "vr/VRDisplayEventInit.idl",
                     "vr/VRLayerInit.idl",
+                    "vr/latest/VRDeviceEventInit.idl",
+                    "vr/latest/VRSessionCreationOptions.idl",
                     "webaudio/AnalyserOptions.idl",
                     "webaudio/AudioBufferOptions.idl",
                     "webaudio/AudioBufferSourceOptions.idl",
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp b/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
index 2ffb07d..7f9550c 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
@@ -1172,7 +1172,8 @@
       return;
     }
 
-    shipping_address_ = new PaymentAddress(response->shipping_address.Clone());
+    shipping_address_ =
+        new PaymentAddress(std::move(response->shipping_address));
     shipping_option_ = response->shipping_option;
   } else {
     if (response->shipping_address || !response->shipping_option.IsNull()) {
@@ -1195,7 +1196,8 @@
 
   complete_timer_.StartOneShot(kCompleteTimeoutSeconds, BLINK_FROM_HERE);
 
-  show_resolver_->Resolve(new PaymentResponse(std::move(response), this, id_));
+  show_resolver_->Resolve(new PaymentResponse(
+      std::move(response), shipping_address_.Get(), this, id_));
 
   // Do not close the mojo connection here. The merchant website should call
   // PaymentResponse::complete(String), which will be forwarded over the mojo
diff --git a/third_party/WebKit/Source/modules/payments/PaymentResponse.cpp b/third_party/WebKit/Source/modules/payments/PaymentResponse.cpp
index f28abe3..72a9547 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentResponse.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentResponse.cpp
@@ -15,15 +15,13 @@
 
 PaymentResponse::PaymentResponse(
     payments::mojom::blink::PaymentResponsePtr response,
+    PaymentAddress* shipping_address,
     PaymentCompleter* payment_completer,
     const String& requestId)
     : requestId_(requestId),
       method_name_(response->method_name),
       stringified_details_(response->stringified_details),
-      shipping_address_(
-          response->shipping_address
-              ? new PaymentAddress(std::move(response->shipping_address))
-              : nullptr),
+      shipping_address_(shipping_address),
       shipping_option_(response->shipping_option),
       payer_name_(response->payer_name),
       payer_email_(response->payer_email),
diff --git a/third_party/WebKit/Source/modules/payments/PaymentResponse.h b/third_party/WebKit/Source/modules/payments/PaymentResponse.h
index ca0b9f7e..6f6a9f8 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentResponse.h
+++ b/third_party/WebKit/Source/modules/payments/PaymentResponse.h
@@ -30,6 +30,7 @@
 
  public:
   PaymentResponse(payments::mojom::blink::PaymentResponsePtr,
+                  PaymentAddress* shipping_address_,
                   PaymentCompleter*,
                   const String& requestId);
   virtual ~PaymentResponse();
diff --git a/third_party/WebKit/Source/modules/payments/PaymentResponseTest.cpp b/third_party/WebKit/Source/modules/payments/PaymentResponseTest.cpp
index 757b9cb..7385aac9 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentResponseTest.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentResponseTest.cpp
@@ -55,13 +55,14 @@
   MockPaymentCompleter* complete_callback = new MockPaymentCompleter;
 
   PaymentResponse* output =
-      new PaymentResponse(std::move(input), complete_callback, "");
+      new PaymentResponse(std::move(input), nullptr, complete_callback, "id");
 
   EXPECT_EQ("foo", output->methodName());
   EXPECT_EQ("standardShippingOption", output->shippingOption());
   EXPECT_EQ("Jon Doe", output->payerName());
   EXPECT_EQ("abc@gmail.com", output->payerEmail());
   EXPECT_EQ("0123", output->payerPhone());
+  EXPECT_EQ("id", output->requestId());
 
   ScriptValue details =
       output->details(scope.GetScriptState(), scope.GetExceptionState());
@@ -85,7 +86,7 @@
   input->stringified_details = "transactionId";
   MockPaymentCompleter* complete_callback = new MockPaymentCompleter;
   PaymentResponse* output =
-      new PaymentResponse(std::move(input), complete_callback, "");
+      new PaymentResponse(std::move(input), nullptr, complete_callback, "id");
 
   ScriptValue details =
       output->details(scope.GetScriptState(), scope.GetExceptionState());
@@ -101,7 +102,7 @@
   input->stringified_details = "{\"transactionId\": 123}";
   MockPaymentCompleter* complete_callback = new MockPaymentCompleter;
   PaymentResponse* output =
-      new PaymentResponse(std::move(input), complete_callback, "");
+      new PaymentResponse(std::move(input), nullptr, complete_callback, "id");
 
   EXPECT_CALL(*complete_callback,
               Complete(scope.GetScriptState(), PaymentCompleter::kSuccess));
@@ -117,7 +118,7 @@
   input->stringified_details = "{\"transactionId\": 123}";
   MockPaymentCompleter* complete_callback = new MockPaymentCompleter;
   PaymentResponse* output =
-      new PaymentResponse(std::move(input), complete_callback, "");
+      new PaymentResponse(std::move(input), nullptr, complete_callback, "id");
 
   EXPECT_CALL(*complete_callback,
               Complete(scope.GetScriptState(), PaymentCompleter::kFail));
@@ -142,9 +143,11 @@
   input->shipping_address->address_line.push_back("340 Main St");
   input->shipping_address->address_line.push_back("BIN1");
   input->shipping_address->address_line.push_back("First floor");
+  PaymentAddress* address =
+      new PaymentAddress(std::move(input->shipping_address));
 
-  PaymentResponse* output =
-      new PaymentResponse(std::move(input), new MockPaymentCompleter, "");
+  PaymentResponse* output = new PaymentResponse(std::move(input), address,
+                                                new MockPaymentCompleter, "id");
   ScriptValue json_object = output->toJSONForBinding(scope.GetScriptState());
   EXPECT_TRUE(json_object.IsObject());
 
@@ -154,7 +157,7 @@
           .ToLocalChecked(),
       kDoNotExternalize);
   String expected =
-      "{\"requestId\":\"\",\"methodName\":\"foo\",\"details\":{"
+      "{\"requestId\":\"id\",\"methodName\":\"foo\",\"details\":{"
       "\"transactionId\":123},"
       "\"shippingAddress\":{\"country\":\"US\",\"addressLine\":[\"340 Main "
       "St\","
diff --git a/third_party/WebKit/Source/modules/vr/BUILD.gn b/third_party/WebKit/Source/modules/vr/BUILD.gn
index 0b897b8..a395560 100644
--- a/third_party/WebKit/Source/modules/vr/BUILD.gn
+++ b/third_party/WebKit/Source/modules/vr/BUILD.gn
@@ -29,6 +29,10 @@
     "VRStageParameters.h",
     "latest/VR.cpp",
     "latest/VR.h",
+    "latest/VRDevice.cpp",
+    "latest/VRDevice.h",
+    "latest/VRDeviceEvent.cpp",
+    "latest/VRDeviceEvent.h",
   ]
 
   deps = [
diff --git a/third_party/WebKit/Source/modules/vr/latest/VR.cpp b/third_party/WebKit/Source/modules/vr/latest/VR.cpp
index f0ad820..fd3789d 100644
--- a/third_party/WebKit/Source/modules/vr/latest/VR.cpp
+++ b/third_party/WebKit/Source/modules/vr/latest/VR.cpp
@@ -9,7 +9,11 @@
 #include "core/dom/Document.h"
 #include "core/frame/LocalFrame.h"
 #include "modules/EventTargetModules.h"
+#include "modules/vr/latest/VRDevice.h"
+#include "modules/vr/latest/VRDeviceEvent.h"
 #include "platform/feature_policy/FeaturePolicy.h"
+#include "public/platform/InterfaceProvider.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
 
 namespace blink {
 
@@ -27,7 +31,23 @@
 
 }  // namespace
 
-VR::VR(LocalFrame& frame) : ContextLifecycleObserver(frame.GetDocument()) {}
+VR::VR(LocalFrame& frame)
+    : ContextLifecycleObserver(frame.GetDocument()),
+      devices_synced_(false),
+      binding_(this) {
+  frame.GetInterfaceProvider().GetInterface(mojo::MakeRequest(&service_));
+  service_.set_connection_error_handler(
+      ConvertToBaseCallback(WTF::Bind(&VR::Dispose, WrapWeakPersistent(this))));
+
+  device::mojom::blink::VRServiceClientPtr client;
+  binding_.Bind(mojo::MakeRequest(&client));
+
+  // Setting the client kicks off a request for the details of any connected
+  // VRDevices.
+  service_->SetClient(std::move(client),
+                      ConvertToBaseCallback(WTF::Bind(&VR::OnDevicesSynced,
+                                                      WrapPersistent(this))));
+}
 
 ExecutionContext* VR::GetExecutionContext() const {
   return ContextLifecycleObserver::GetExecutionContext();
@@ -62,21 +82,86 @@
         DOMException::Create(kSecurityError, kCrossOriginSubframeBlocked));
   }
 
+  // If we're still waiting for a previous call to resolve return that promise
+  // again.
+  if (pending_devices_resolver_) {
+    return pending_devices_resolver_->Promise();
+  }
+
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
 
-  // Until we can return an accurate list of devices arbitrarily resolve with an
-  // empty array.
-  v8::Local<v8::Array> empty_array =
-      v8::Array::New(script_state->GetIsolate(), 0);
-  resolver->Resolve(empty_array);
+  // If we've previously synced the VRDevices or no longer have a valid service
+  // connection just return the current list. In the case of the service being
+  // disconnected this will be an empty array.
+  if (!service_ || devices_synced_) {
+    resolver->Resolve(devices_);
+    return promise;
+  }
+
+  // Otherwise wait for the full list of devices to be populated and resolve
+  // when onDevicesSynced is called.
+  pending_devices_resolver_ = resolver;
 
   return promise;
 }
 
-void VR::ContextDestroyed(ExecutionContext*) {}
+// Each time a new VRDevice is connected we'll recieve a VRDisplayPtr for it
+// here. Upon calling SetClient in the constructor we should receive one call
+// for each VRDevice that was already connected at the time.
+void VR::OnDisplayConnected(
+    device::mojom::blink::VRDisplayPtr display,
+    device::mojom::blink::VRDisplayClientRequest client_request,
+    device::mojom::blink::VRDisplayInfoPtr display_info) {
+  VRDevice* vr_device =
+      new VRDevice(this, std::move(display), std::move(client_request),
+                   std::move(display_info));
+
+  devices_.push_back(vr_device);
+
+  DispatchEvent(
+      VRDeviceEvent::Create(EventTypeNames::deviceconnect, vr_device));
+}
+
+// Called when the VRService has called OnDevicesConnected for all active
+// VRDevices.
+void VR::OnDevicesSynced() {
+  devices_synced_ = true;
+  OnGetDevices();
+}
+
+// Called when details for every connected VRDevice has been received.
+void VR::OnGetDevices() {
+  if (pending_devices_resolver_) {
+    pending_devices_resolver_->Resolve(devices_);
+    pending_devices_resolver_ = nullptr;
+  }
+}
+
+void VR::ContextDestroyed(ExecutionContext*) {
+  Dispose();
+}
+
+void VR::Dispose() {
+  // If the document context was destroyed, shut down the client connection
+  // and never call the mojo service again.
+  service_.reset();
+  binding_.Close();
+
+  // Shutdown all devices' message pipe
+  for (const auto& device : devices_)
+    device->Dispose();
+
+  devices_.clear();
+
+  // Ensure that any outstanding getDevices promises are resolved. They will
+  // receive an empty array of devices.
+  OnGetDevices();
+}
 
 DEFINE_TRACE(VR) {
+  visitor->Trace(devices_);
+  visitor->Trace(pending_devices_resolver_);
   ContextLifecycleObserver::Trace(visitor);
   EventTargetWithInlineData::Trace(visitor);
 }
diff --git a/third_party/WebKit/Source/modules/vr/latest/VR.h b/third_party/WebKit/Source/modules/vr/latest/VR.h
index 64f4639..9f7a564 100644
--- a/third_party/WebKit/Source/modules/vr/latest/VR.h
+++ b/third_party/WebKit/Source/modules/vr/latest/VR.h
@@ -8,13 +8,20 @@
 #include "bindings/core/v8/ScriptPromise.h"
 #include "core/dom/ContextLifecycleObserver.h"
 #include "core/dom/events/EventTarget.h"
+#include "device/vr/vr_service.mojom-blink.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "platform/heap/Handle.h"
 #include "platform/wtf/Forward.h"
+#include "public/platform/WebCallbacks.h"
 
 namespace blink {
 
+class ScriptPromiseResolver;
+class VRDevice;
+
 class VR final : public EventTargetWithInlineData,
-                 public ContextLifecycleObserver {
+                 public ContextLifecycleObserver,
+                 public device::mojom::blink::VRServiceClient {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(VR);
 
@@ -26,6 +33,11 @@
 
   ScriptPromise getDevices(ScriptState*);
 
+  // VRServiceClient overrides.
+  void OnDisplayConnected(device::mojom::blink::VRDisplayPtr,
+                          device::mojom::blink::VRDisplayClientRequest,
+                          device::mojom::blink::VRDisplayInfoPtr) override;
+
   // EventTarget overrides.
   ExecutionContext* GetExecutionContext() const override;
   const AtomicString& InterfaceName() const override;
@@ -33,10 +45,22 @@
   // ContextLifecycleObserver overrides.
   void ContextDestroyed(ExecutionContext*) override;
 
+  void Dispose();
+
   DECLARE_VIRTUAL_TRACE();
 
  private:
   explicit VR(LocalFrame& frame);
+
+  void OnDevicesSynced();
+  void OnGetDevices();
+
+  bool devices_synced_;
+
+  HeapVector<Member<VRDevice>> devices_;
+  Member<ScriptPromiseResolver> pending_devices_resolver_;
+  device::mojom::blink::VRServicePtr service_;
+  mojo::Binding<device::mojom::blink::VRServiceClient> binding_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/vr/latest/VR.idl b/third_party/WebKit/Source/modules/vr/latest/VR.idl
index d8ee612..da58f9c 100644
--- a/third_party/WebKit/Source/modules/vr/latest/VR.idl
+++ b/third_party/WebKit/Source/modules/vr/latest/VR.idl
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// https://w3c.github.io/webvr/spec/latest/#vr-interface
 [
     SecureContext,
     RuntimeEnabled=WebVR2
diff --git a/third_party/WebKit/Source/modules/vr/latest/VRDevice.cpp b/third_party/WebKit/Source/modules/vr/latest/VRDevice.cpp
new file mode 100644
index 0000000..ced8849
--- /dev/null
+++ b/third_party/WebKit/Source/modules/vr/latest/VRDevice.cpp
@@ -0,0 +1,142 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "modules/vr/latest/VRDevice.h"
+
+#include "bindings/core/v8/ScriptPromiseResolver.h"
+#include "core/dom/DOMException.h"
+#include "core/dom/UserGestureIndicator.h"
+#include "modules/EventTargetModules.h"
+#include "modules/vr/latest/VR.h"
+
+namespace blink {
+
+namespace {
+
+const char kExclusiveNotSupported[] =
+    "VRDevice does not support the creation of exclusive sessions.";
+
+const char kNonExclusiveNotSupported[] =
+    "VRDevice does not support the creation of non-exclusive sessions.";
+
+const char kRequestNotInUserGesture[] =
+    "Exclusive sessions can only be requested during a user gesture.";
+
+const char kSessionCreationNotImplemented[] =
+    "Session creation not implemented.";
+
+}  // namespace
+
+VRDevice::VRDevice(VR* vr,
+                   device::mojom::blink::VRDisplayPtr display,
+                   device::mojom::blink::VRDisplayClientRequest client_request,
+                   device::mojom::blink::VRDisplayInfoPtr display_info)
+    : vr_(vr),
+      display_(std::move(display)),
+      display_client_binding_(this, std::move(client_request)) {
+  SetVRDisplayInfo(std::move(display_info));
+}
+
+ExecutionContext* VRDevice::GetExecutionContext() const {
+  return vr_->GetExecutionContext();
+}
+
+const AtomicString& VRDevice::InterfaceName() const {
+  return EventTargetNames::VRDevice;
+}
+
+const char* VRDevice::checkSessionSupport(
+    const VRSessionCreationOptions& options) const {
+  if (options.exclusive()) {
+    // Validation for exclusive sessions.
+    if (!supports_exclusive_) {
+      return kExclusiveNotSupported;
+    }
+  } else {
+    // Validation for non-exclusive sessions.
+    // TODO: Add support for non-exclusive sessions in a follow up CL.
+    return kNonExclusiveNotSupported;
+  }
+
+  return nullptr;
+}
+
+ScriptPromise VRDevice::supportsSession(
+    ScriptState* script_state,
+    const VRSessionCreationOptions& options) const {
+  // Check to see if the device is capable of supporting the requested session
+  // options. Note that reporting support here does not guarantee that creating
+  // a session with those options will succeed, as other external and
+  // time-sensitve factors (focus state, existance of another exclusive session,
+  // etc.) may prevent the creation of a session as well.
+  const char* reject_reason = checkSessionSupport(options);
+  if (reject_reason) {
+    return ScriptPromise::RejectWithDOMException(
+        script_state, DOMException::Create(kNotSupportedError, reject_reason));
+  }
+
+  // If the above checks pass, resolve without a value. Future API iterations
+  // may specify a value to be returned here.
+  ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
+  ScriptPromise promise = resolver->Promise();
+  resolver->Resolve();
+  return promise;
+}
+
+ScriptPromise VRDevice::requestSession(
+    ScriptState* script_state,
+    const VRSessionCreationOptions& options) {
+  // Check first to see if the device is capable of supporting the requested
+  // options.
+  const char* reject_reason = checkSessionSupport(options);
+  if (reject_reason) {
+    return ScriptPromise::RejectWithDOMException(
+        script_state, DOMException::Create(kNotSupportedError, reject_reason));
+  }
+
+  // Check if the current page state prevents the requested session from being
+  // created.
+  if (options.exclusive()) {
+    if (!UserGestureIndicator::ProcessingUserGesture()) {
+      return ScriptPromise::RejectWithDOMException(
+          script_state,
+          DOMException::Create(kInvalidStateError, kRequestNotInUserGesture));
+    }
+  }
+
+  // TODO: Session creation will be implemented in a follow up CL.
+  return ScriptPromise::RejectWithDOMException(
+      script_state,
+      DOMException::Create(kNotSupportedError, kSessionCreationNotImplemented));
+}
+
+// TODO: Forward these calls on to the sessions once they've been implemented.
+void VRDevice::OnChanged(device::mojom::blink::VRDisplayInfoPtr display_info) {
+  SetVRDisplayInfo(std::move(display_info));
+}
+void VRDevice::OnExitPresent() {}
+void VRDevice::OnBlur() {}
+void VRDevice::OnFocus() {}
+void VRDevice::OnActivate(device::mojom::blink::VRDisplayEventReason,
+                          OnActivateCallback on_handled) {}
+void VRDevice::OnDeactivate(device::mojom::blink::VRDisplayEventReason) {}
+
+void VRDevice::Dispose() {
+  display_client_binding_.Close();
+}
+
+void VRDevice::SetVRDisplayInfo(
+    device::mojom::blink::VRDisplayInfoPtr display_info) {
+  display_info_ = std::move(display_info);
+  device_name_ = display_info_->displayName;
+  is_external_ = display_info_->capabilities->hasExternalDisplay;
+  supports_exclusive_ = (display_info_->capabilities->canPresent);
+}
+
+DEFINE_TRACE(VRDevice) {
+  visitor->Trace(vr_);
+  EventTargetWithInlineData::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/modules/vr/latest/VRDevice.h b/third_party/WebKit/Source/modules/vr/latest/VRDevice.h
new file mode 100644
index 0000000..74a9c6b3
--- /dev/null
+++ b/third_party/WebKit/Source/modules/vr/latest/VRDevice.h
@@ -0,0 +1,81 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VRDevice_h
+#define VRDevice_h
+
+#include "bindings/core/v8/ScriptPromise.h"
+#include "core/dom/events/EventTarget.h"
+#include "device/vr/vr_service.mojom-blink.h"
+#include "modules/vr/latest/VRSessionCreationOptions.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "platform/heap/Handle.h"
+#include "platform/wtf/Forward.h"
+#include "platform/wtf/text/WTFString.h"
+
+namespace blink {
+
+class VR;
+
+class VRDevice final : public EventTargetWithInlineData,
+                       public device::mojom::blink::VRDisplayClient {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  VRDevice(VR*,
+           device::mojom::blink::VRDisplayPtr,
+           device::mojom::blink::VRDisplayClientRequest,
+           device::mojom::blink::VRDisplayInfoPtr);
+  VR* vr() const { return vr_; }
+
+  const String& deviceName() const { return device_name_; }
+  bool isExternal() const { return is_external_; }
+
+  ScriptPromise supportsSession(ScriptState*,
+                                const VRSessionCreationOptions&) const;
+  ScriptPromise requestSession(ScriptState*, const VRSessionCreationOptions&);
+
+  // EventTarget overrides.
+  ExecutionContext* GetExecutionContext() const override;
+  const AtomicString& InterfaceName() const override;
+
+  // VRDisplayClient
+  void OnChanged(device::mojom::blink::VRDisplayInfoPtr) override;
+  void OnExitPresent() override;
+  void OnBlur() override;
+  void OnFocus() override;
+  void OnActivate(device::mojom::blink::VRDisplayEventReason,
+                  OnActivateCallback on_handled) override;
+  void OnDeactivate(device::mojom::blink::VRDisplayEventReason) override;
+
+  void Dispose();
+
+  const device::mojom::blink::VRDisplayPtr& vrDisplayPtr() const {
+    return display_;
+  }
+  const device::mojom::blink::VRDisplayInfoPtr& vrDisplayInfoPtr() const {
+    return display_info_;
+  }
+
+  DECLARE_VIRTUAL_TRACE();
+
+ private:
+  void SetVRDisplayInfo(device::mojom::blink::VRDisplayInfoPtr);
+
+  const char* checkSessionSupport(const VRSessionCreationOptions&) const;
+
+  Member<VR> vr_;
+  String device_name_;
+  bool is_external_;
+  bool supports_exclusive_;
+
+  device::mojom::blink::VRDisplayPtr display_;
+  device::mojom::blink::VRDisplayInfoPtr display_info_;
+
+  mojo::Binding<device::mojom::blink::VRDisplayClient> display_client_binding_;
+};
+
+}  // namespace blink
+
+#endif  // VRDevice_h
diff --git a/third_party/WebKit/Source/modules/vr/latest/VRDevice.idl b/third_party/WebKit/Source/modules/vr/latest/VRDevice.idl
new file mode 100644
index 0000000..1843d0ee
--- /dev/null
+++ b/third_party/WebKit/Source/modules/vr/latest/VRDevice.idl
@@ -0,0 +1,15 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://w3c.github.io/webvr/spec/latest/#vrdevice-interface
+[
+    SecureContext,
+    RuntimeEnabled=WebVR2
+] interface VRDevice : EventTarget {
+  readonly attribute DOMString deviceName;
+  readonly attribute boolean isExternal;
+
+  [CallWith=ScriptState] Promise supportsSession([PermissiveDictionaryConversion] optional VRSessionCreationOptions options);
+  [CallWith=ScriptState] Promise requestSession([PermissiveDictionaryConversion] optional VRSessionCreationOptions options);
+};
diff --git a/third_party/WebKit/Source/modules/vr/latest/VRDeviceEvent.cpp b/third_party/WebKit/Source/modules/vr/latest/VRDeviceEvent.cpp
new file mode 100644
index 0000000..f610c2c1
--- /dev/null
+++ b/third_party/WebKit/Source/modules/vr/latest/VRDeviceEvent.cpp
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "modules/vr/latest/VRDeviceEvent.h"
+
+namespace blink {
+
+VRDeviceEvent::VRDeviceEvent() {}
+
+VRDeviceEvent::VRDeviceEvent(const AtomicString& type, VRDevice* device)
+    : Event(type, true, false), device_(device) {}
+
+VRDeviceEvent::VRDeviceEvent(const AtomicString& type,
+                             const VRDeviceEventInit& initializer)
+    : Event(type, initializer) {
+  if (initializer.hasDevice())
+    device_ = initializer.device();
+}
+
+VRDeviceEvent::~VRDeviceEvent() {}
+
+const AtomicString& VRDeviceEvent::InterfaceName() const {
+  return EventNames::VRDeviceEvent;
+}
+
+DEFINE_TRACE(VRDeviceEvent) {
+  visitor->Trace(device_);
+  Event::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/modules/vr/latest/VRDeviceEvent.h b/third_party/WebKit/Source/modules/vr/latest/VRDeviceEvent.h
new file mode 100644
index 0000000..69b43397
--- /dev/null
+++ b/third_party/WebKit/Source/modules/vr/latest/VRDeviceEvent.h
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VRDeviceEvent_h
+#define VRDeviceEvent_h
+
+#include "modules/EventModules.h"
+#include "modules/vr/latest/VRDevice.h"
+#include "modules/vr/latest/VRDeviceEventInit.h"
+
+namespace blink {
+
+class VRDeviceEvent final : public Event {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  static VRDeviceEvent* Create() { return new VRDeviceEvent; }
+  static VRDeviceEvent* Create(const AtomicString& type, VRDevice* device) {
+    return new VRDeviceEvent(type, device);
+  }
+
+  static VRDeviceEvent* Create(const AtomicString& type,
+                               const VRDeviceEventInit& initializer) {
+    return new VRDeviceEvent(type, initializer);
+  }
+
+  ~VRDeviceEvent() override;
+
+  VRDevice* device() const { return device_.Get(); }
+
+  const AtomicString& InterfaceName() const override;
+
+  DECLARE_VIRTUAL_TRACE();
+
+ private:
+  VRDeviceEvent();
+  VRDeviceEvent(const AtomicString& type, VRDevice*);
+  VRDeviceEvent(const AtomicString&, const VRDeviceEventInit&);
+
+  Member<VRDevice> device_;
+};
+
+}  // namespace blink
+
+#endif  // VRDisplayEvent_h
diff --git a/third_party/WebKit/Source/modules/vr/latest/VRDeviceEvent.idl b/third_party/WebKit/Source/modules/vr/latest/VRDeviceEvent.idl
new file mode 100644
index 0000000..f6bb7ee
--- /dev/null
+++ b/third_party/WebKit/Source/modules/vr/latest/VRDeviceEvent.idl
@@ -0,0 +1,12 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://w3c.github.io/webvr/spec/latest/#vrdeviceevent-interface
+[
+    SecureContext,
+    RuntimeEnabled=WebVR2,
+    Constructor(DOMString type, VRDeviceEventInit eventInitDict)
+] interface VRDeviceEvent : Event {
+  readonly attribute VRDevice device;
+};
\ No newline at end of file
diff --git a/third_party/WebKit/Source/modules/vr/latest/VRDeviceEventInit.idl b/third_party/WebKit/Source/modules/vr/latest/VRDeviceEventInit.idl
new file mode 100644
index 0000000..9d005753
--- /dev/null
+++ b/third_party/WebKit/Source/modules/vr/latest/VRDeviceEventInit.idl
@@ -0,0 +1,10 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://w3c.github.io/webvr/spec/latest/#vrdeviceevent-interface
+[
+    SecureContext
+] dictionary VRDeviceEventInit : EventInit {
+  required VRDevice device;
+};
\ No newline at end of file
diff --git a/third_party/WebKit/Source/modules/vr/latest/VRSessionCreationOptions.idl b/third_party/WebKit/Source/modules/vr/latest/VRSessionCreationOptions.idl
new file mode 100644
index 0000000..786d630b
--- /dev/null
+++ b/third_party/WebKit/Source/modules/vr/latest/VRSessionCreationOptions.idl
@@ -0,0 +1,10 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://w3c.github.io/webvr/spec/latest/#vrsessioncreationoptions-interface
+[
+    SecureContext
+] dictionary VRSessionCreationOptions {
+  boolean exclusive = false;
+};
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletObjectProxy.h b/third_party/WebKit/Source/modules/webaudio/AudioWorkletObjectProxy.h
index d98c501..b4a05cf 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletObjectProxy.h
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletObjectProxy.h
@@ -6,14 +6,13 @@
 #define AudioWorkletObjectProxy_h
 
 #include "core/workers/ThreadedWorkletObjectProxy.h"
-#include "modules/ModulesExport.h"
 
 namespace blink {
 
 class AudioWorkletGlobalScope;
 class AudioWorkletMessagingProxy;
 
-class MODULES_EXPORT AudioWorkletObjectProxy final
+class AudioWorkletObjectProxy final
     : public ThreadedWorkletObjectProxy {
  public:
   AudioWorkletObjectProxy(AudioWorkletMessagingProxy*,
diff --git a/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp b/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp
index 73839bf..4ed47e35 100644
--- a/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp
+++ b/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp
@@ -76,6 +76,10 @@
   RuntimeEnabledFeatures::SetAccelerated2dCanvasEnabled(enable);
 }
 
+void WebRuntimeFeatures::EnableAllowActivationDelegationAttr(bool enable) {
+  RuntimeEnabledFeatures::SetAllowActivationDelegationAttrEnabled(enable);
+}
+
 void WebRuntimeFeatures::EnableAudioOutputDevices(bool enable) {
   RuntimeEnabledFeatures::SetAudioOutputDevicesEnabled(enable);
 }
@@ -96,10 +100,6 @@
   RuntimeEnabledFeatures::SetCompositorTouchActionEnabled(enable);
 }
 
-void WebRuntimeFeatures::EnableCompositorImageAnimations(bool enable) {
-  RuntimeEnabledFeatures::SetCompositorImageAnimationsEnabled(enable);
-}
-
 void WebRuntimeFeatures::EnableCSSHexAlphaColor(bool enable) {
   RuntimeEnabledFeatures::SetCSSHexAlphaColorEnabled(enable);
 }
diff --git a/third_party/WebKit/Source/platform/graphics/BitmapImage.cpp b/third_party/WebKit/Source/platform/graphics/BitmapImage.cpp
index 722e42e..18f0ecd7 100644
--- a/third_party/WebKit/Source/platform/graphics/BitmapImage.cpp
+++ b/third_party/WebKit/Source/platform/graphics/BitmapImage.cpp
@@ -257,14 +257,6 @@
     }
   }
 
-  // If the image is being animated by the compositor, clear the cached_frame_
-  // on a data update to push it to the compositor. Since we never advance the
-  // animation here, the |cached_frame_index_| is always the first frame and the
-  // |cached_frame_| might have not have been cleared in the loop above.
-  if (RuntimeEnabledFeatures::CompositorImageAnimationsEnabled()
-      && MaybeAnimated())
-    cached_frame_ = PaintImage();
-
   // Feed all the data we've seen so far to the image decoder.
   all_data_received_ = all_data_received;
 
@@ -484,9 +476,6 @@
 }
 
 bool BitmapImage::ShouldAnimate() {
-  if (RuntimeEnabledFeatures::CompositorImageAnimationsEnabled())
-    return false;
-
   bool animated = RepetitionCount() != kAnimationNone && !animation_finished_ &&
                   GetImageObserver();
   if (animated && animation_policy_ == kImageAnimationPolicyNoAnimation)
diff --git a/third_party/WebKit/Source/platform/graphics/LoggingCanvas.cpp b/third_party/WebKit/Source/platform/graphics/LoggingCanvas.cpp
index 6801aa8..bca81ca 100644
--- a/third_party/WebKit/Source/platform/graphics/LoggingCanvas.cpp
+++ b/third_party/WebKit/Source/platform/graphics/LoggingCanvas.cpp
@@ -925,7 +925,7 @@
 }
 
 void ShowPaintRecord(const PaintRecord* record, const SkRect& bounds) {
-  DLOG(INFO) << RecordAsDebugString(record, bounds);
+  DLOG(INFO) << RecordAsDebugString(record, bounds).Utf8().data();
 }
 #endif
 
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintControllerDebugData.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintControllerDebugData.cpp
index c951979..17e5ec0 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintControllerDebugData.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintControllerDebugData.cpp
@@ -170,7 +170,9 @@
                     current_paint_artifact_.GetDisplayItemList(),
                     current_cached_subsequences_,
                     current_paint_artifact_.PaintChunks(), flags)
-                    .ToString();
+                    .ToString()
+                    .Utf8()
+                    .data();
 
   // DebugName() and ClientCacheIsValid() can only be called on a live client,
   // so only output it for new_display_item_list_, in which we are sure the
@@ -180,7 +182,9 @@
                     new_display_item_list_, new_cached_subsequences_,
                     new_paint_chunks_.PaintChunks(),
                     flags | DisplayItemList::kShowClientDebugName)
-                    .ToString();
+                    .ToString()
+                    .Utf8()
+                    .data();
 }
 
 void PaintController::ShowDebugData() const {
diff --git a/third_party/WebKit/Source/platform/loader/fetch/PreloadKey.h b/third_party/WebKit/Source/platform/loader/fetch/PreloadKey.h
index 03ef735d..8c1b80dd 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/PreloadKey.h
+++ b/third_party/WebKit/Source/platform/loader/fetch/PreloadKey.h
@@ -30,13 +30,9 @@
   };
 
   PreloadKey() = default;
-  PreloadKey(WTF::HashTableDeletedValueType)
-      : url(WTF::kHashTableDeletedValue) {}
   PreloadKey(const KURL& url, Resource::Type type)
       : url(RemoveFragmentFromUrl(url)), type(type) {}
 
-  bool IsHashTableDeletedValue() const { return url.IsHashTableDeletedValue(); }
-
   bool operator==(const PreloadKey& x) const {
     return url == x.url && type == x.type;
   }
@@ -64,7 +60,15 @@
 
 template <>
 struct HashTraits<blink::PreloadKey>
-    : public SimpleClassHashTraits<blink::PreloadKey> {};
+    : public SimpleClassHashTraits<blink::PreloadKey> {
+  static bool IsDeletedValue(const blink::PreloadKey& value) {
+    return HashTraits<blink::KURL>::IsDeletedValue(value.url);
+  }
+
+  static void ConstructDeletedValue(blink::PreloadKey& slot, bool zero_value) {
+    HashTraits<blink::KURL>::ConstructDeletedValue(slot.url, zero_value);
+  }
+};
 
 }  // namespace WTF
 
diff --git a/third_party/WebKit/Source/platform/runtime_enabled_features.json5 b/third_party/WebKit/Source/platform/runtime_enabled_features.json5
index 8d4b8a2..4e4b40f 100644
--- a/third_party/WebKit/Source/platform/runtime_enabled_features.json5
+++ b/third_party/WebKit/Source/platform/runtime_enabled_features.json5
@@ -76,6 +76,9 @@
       status: "experimental",
     },
     {
+      name: "AllowActivationDelegationAttr",
+    },
+    {
       name: "AllowContentInitiatedDataUrlNavigations",
       status: "stable",
     },
@@ -166,10 +169,6 @@
       status: "stable",
     },
     {
-      name: "CompositorImageAnimations",
-      status: "test",
-    },
-    {
       name: "CompositorTouchAction",
       status: "test",
     },
diff --git a/third_party/WebKit/Source/platform/weborigin/KURL.cpp b/third_party/WebKit/Source/platform/weborigin/KURL.cpp
index 40c7887..5df88888 100644
--- a/third_party/WebKit/Source/platform/weborigin/KURL.cpp
+++ b/third_party/WebKit/Source/platform/weborigin/KURL.cpp
@@ -253,11 +253,6 @@
   InitInnerURL();
 }
 
-KURL::KURL(WTF::HashTableDeletedValueType)
-    : is_valid_(false),
-      protocol_is_in_http_family_(false),
-      string_(WTF::kHashTableDeletedValue) {}
-
 KURL::KURL(const KURL& other)
     : is_valid_(other.is_valid_),
       protocol_is_in_http_family_(other.protocol_is_in_http_family_),
diff --git a/third_party/WebKit/Source/platform/weborigin/KURL.h b/third_party/WebKit/Source/platform/weborigin/KURL.h
index 26e4d1e..3905842 100644
--- a/third_party/WebKit/Source/platform/weborigin/KURL.h
+++ b/third_party/WebKit/Source/platform/weborigin/KURL.h
@@ -31,7 +31,6 @@
 #include "platform/PlatformExport.h"
 #include "platform/wtf/Allocator.h"
 #include "platform/wtf/Forward.h"
-#include "platform/wtf/HashTableDeletedValueType.h"
 #include "platform/wtf/text/WTFString.h"
 #include "url/third_party/mozilla/url_parse.h"
 #include "url/url_canon.h"
@@ -85,15 +84,10 @@
   // from such. It is usually best to avoid repeatedly parsing a string,
   // unless memory saving outweigh the possible slow-downs.
   KURL(ParsedURLStringTag, const String&);
-  explicit KURL(WTF::HashTableDeletedValueType);
 
   // Creates an isolated URL object suitable for sending to another thread.
   static KURL CreateIsolated(ParsedURLStringTag, const String&);
 
-  bool IsHashTableDeletedValue() const {
-    return GetString().IsHashTableDeletedValue();
-  }
-
   // Resolves the relative URL with the given base URL. If provided, the
   // TextEncoding is used to encode non-ASCII characers. The base URL can be
   // null or empty, in which case the relative URL will be interpreted as
@@ -219,6 +213,8 @@
   }
 
  private:
+  friend struct WTF::HashTraits<blink::KURL>;
+
   void Init(const KURL& base,
             const String& relative,
             const WTF::TextEncoding* query_encoding);
diff --git a/third_party/WebKit/Source/platform/weborigin/KURLHash.h b/third_party/WebKit/Source/platform/weborigin/KURLHash.h
index 26c83bd..55c59e63 100644
--- a/third_party/WebKit/Source/platform/weborigin/KURLHash.h
+++ b/third_party/WebKit/Source/platform/weborigin/KURLHash.h
@@ -55,7 +55,15 @@
 namespace WTF {
 
 template <>
-struct HashTraits<blink::KURL> : SimpleClassHashTraits<blink::KURL> {};
+struct HashTraits<blink::KURL> : SimpleClassHashTraits<blink::KURL> {
+  static bool IsDeletedValue(const blink::KURL& value) {
+    return HashTraits<String>::IsDeletedValue(value.GetString());
+  }
+
+  static void ConstructDeletedValue(blink::KURL& slot, bool zero_value) {
+    HashTraits<String>::ConstructDeletedValue(slot.string_, zero_value);
+  }
+};
 
 }  // namespace WTF
 
diff --git a/third_party/WebKit/Source/platform/wtf/HashTraits.h b/third_party/WebKit/Source/platform/wtf/HashTraits.h
index bbcbc94..c4d75a75 100644
--- a/third_party/WebKit/Source/platform/wtf/HashTraits.h
+++ b/third_party/WebKit/Source/platform/wtf/HashTraits.h
@@ -22,16 +22,16 @@
 #ifndef WTF_HashTraits_h
 #define WTF_HashTraits_h
 
+#include <string.h>  // For memset.
+#include <limits>
+#include <memory>
+#include <type_traits>
+#include <utility>
 #include "platform/wtf/Forward.h"
 #include "platform/wtf/HashFunctions.h"
 #include "platform/wtf/HashTableDeletedValueType.h"
 #include "platform/wtf/StdLibExtras.h"
 #include "platform/wtf/TypeTraits.h"
-#include <limits>
-#include <memory>
-#include <string.h>  // For memset.
-#include <type_traits>
-#include <utility>
 
 namespace WTF {
 
@@ -215,12 +215,25 @@
 
 template <typename P>
 struct HashTraits<RefPtr<P>> : SimpleClassHashTraits<RefPtr<P>> {
+  static_assert(sizeof(void*) == sizeof(RefPtr<P>),
+                "Unexpected RefPtr size."
+                " RefPtr needs to be single pointer to support deleted value.");
+
   typedef std::nullptr_t EmptyValueType;
   static EmptyValueType EmptyValue() { return nullptr; }
 
   static const bool kHasIsEmptyValueFunction = true;
   static bool IsEmptyValue(const RefPtr<P>& value) { return !value; }
 
+  static bool IsDeletedValue(const RefPtr<P>& value) {
+    return *reinterpret_cast<void* const*>(&value) ==
+           reinterpret_cast<const void*>(-1);
+  }
+
+  static void ConstructDeletedValue(RefPtr<P>& slot, bool zero_value) {
+    *reinterpret_cast<void**>(&slot) = reinterpret_cast<void*>(-1);
+  }
+
   typedef RefPtrValuePeeker<P> PeekInType;
   typedef RefPtr<P>* IteratorGetType;
   typedef const RefPtr<P>* IteratorConstGetType;
@@ -276,6 +289,8 @@
 struct HashTraits<String> : SimpleClassHashTraits<String> {
   static const bool kHasIsEmptyValueFunction = true;
   static bool IsEmptyValue(const String&);
+  static bool IsDeletedValue(const String& value);
+  static void ConstructDeletedValue(String& slot, bool zero_value);
 };
 
 // This struct template is an implementation detail of the
diff --git a/third_party/WebKit/Source/platform/wtf/RefPtr.h b/third_party/WebKit/Source/platform/wtf/RefPtr.h
index 53ecc8bbd..164c76a4 100644
--- a/third_party/WebKit/Source/platform/wtf/RefPtr.h
+++ b/third_party/WebKit/Source/platform/wtf/RefPtr.h
@@ -84,6 +84,8 @@
 
   // Hash table deleted values, which are only constructed and never copied or
   // destroyed.
+  // TODO(tzik): Remove this after updating callsites of this to use
+  // HashTraits<RefPtr<>>::{ConstructDeletedValue,IsHashTableDeletedValue}.
   RefPtr(HashTableDeletedValueType) : ptr_(HashTableDeletedValue()) {}
   bool IsHashTableDeletedValue() const {
     return ptr_ == HashTableDeletedValue();
diff --git a/third_party/WebKit/Source/platform/wtf/Vector.h b/third_party/WebKit/Source/platform/wtf/Vector.h
index d7fefe1..181e93db 100644
--- a/third_party/WebKit/Source/platform/wtf/Vector.h
+++ b/third_party/WebKit/Source/platform/wtf/Vector.h
@@ -437,6 +437,12 @@
   VectorBufferBase(T* buffer, size_t capacity)
       : buffer_(buffer), capacity_(capacity) {}
 
+  VectorBufferBase(HashTableDeletedValueType value)
+      : buffer_(reinterpret_cast<T*>(-1)) {}
+  bool IsHashTableDeletedValue() const {
+    return buffer_ == reinterpret_cast<T*>(-1);
+  }
+
   T* buffer_;
   unsigned capacity_;
   unsigned size_;
@@ -549,6 +555,11 @@
 
   VectorBuffer() : Base(InlineBuffer(), inlineCapacity) {}
 
+  VectorBuffer(HashTableDeletedValueType value) : Base(value) {}
+  bool IsHashTableDeletedValue() const {
+    return Base::IsHashTableDeletedValue();
+  }
+
   explicit VectorBuffer(size_t capacity)
       : Base(InlineBuffer(), inlineCapacity) {
     if (capacity > inlineCapacity)
@@ -964,6 +975,12 @@
   // is copy initialized from the specified value.
   inline Vector(size_t, const T&);
 
+  // HashTable support
+  Vector(HashTableDeletedValueType value) : Base(value) {}
+  bool IsHashTableDeletedValue() const {
+    return Base::IsHashTableDeletedValue();
+  }
+
   // Copying.
   Vector(const Vector&);
   template <size_t otherCapacity>
diff --git a/third_party/WebKit/Source/platform/wtf/text/AtomicString.h b/third_party/WebKit/Source/platform/wtf/text/AtomicString.h
index 62775081..7b2069b 100644
--- a/third_party/WebKit/Source/platform/wtf/text/AtomicString.h
+++ b/third_party/WebKit/Source/platform/wtf/text/AtomicString.h
@@ -80,6 +80,8 @@
 
   // Hash table deleted values, which are only constructed and never copied or
   // destroyed.
+  // TODO(tzik): Remove this after updating callsites of this to use
+  // HashTraits<AtomicString>::{ConstructDeletedValue,IsHashTableDeletedValue}.
   AtomicString(WTF::HashTableDeletedValueType)
       : string_(WTF::kHashTableDeletedValue) {}
   bool IsHashTableDeletedValue() const {
@@ -231,6 +233,8 @@
 #endif
 
  private:
+  friend struct HashTraits<AtomicString>;
+
   String string_;
 
   ALWAYS_INLINE static RefPtr<StringImpl> Add(StringImpl* r) {
diff --git a/third_party/WebKit/Source/platform/wtf/text/AtomicStringHash.h b/third_party/WebKit/Source/platform/wtf/text/AtomicStringHash.h
index 32eaf1e..807b6b55 100644
--- a/third_party/WebKit/Source/platform/wtf/text/AtomicStringHash.h
+++ b/third_party/WebKit/Source/platform/wtf/text/AtomicStringHash.h
@@ -58,6 +58,14 @@
 
   static const bool kHasIsEmptyValueFunction = true;
   static bool IsEmptyValue(const AtomicString& value) { return value.IsNull(); }
+
+  static bool IsDeletedValue(const AtomicString& value) {
+    return HashTraits<String>::IsDeletedValue(value.string_);
+  }
+
+  static void ConstructDeletedValue(AtomicString& slot, bool zero_value) {
+    HashTraits<String>::ConstructDeletedValue(slot.string_, zero_value);
+  }
 };
 
 }  // namespace WTF
diff --git a/third_party/WebKit/Source/platform/wtf/text/StringHash.h b/third_party/WebKit/Source/platform/wtf/text/StringHash.h
index 486e546..a9dfaff 100644
--- a/third_party/WebKit/Source/platform/wtf/text/StringHash.h
+++ b/third_party/WebKit/Source/platform/wtf/text/StringHash.h
@@ -33,6 +33,15 @@
   return value.IsNull();
 }
 
+inline bool HashTraits<String>::IsDeletedValue(const String& value) {
+  return HashTraits<RefPtr<StringImpl>>::IsDeletedValue(value.impl_);
+}
+
+inline void HashTraits<String>::ConstructDeletedValue(String& slot,
+                                                      bool zero_value) {
+  HashTraits<RefPtr<StringImpl>>::ConstructDeletedValue(slot.impl_, zero_value);
+}
+
 // The hash() functions on StringHash and CaseFoldingHash do not support null
 // strings. get(), contains(), and add() on HashMap<String,..., StringHash>
 // cause a null-pointer dereference when passed null strings.
diff --git a/third_party/WebKit/Source/platform/wtf/text/WTFString.h b/third_party/WebKit/Source/platform/wtf/text/WTFString.h
index 7f4f86e..b7918c71 100644
--- a/third_party/WebKit/Source/platform/wtf/text/WTFString.h
+++ b/third_party/WebKit/Source/platform/wtf/text/WTFString.h
@@ -498,6 +498,8 @@
 
   // Hash table deleted values, which are only constructed and never copied or
   // destroyed.
+  // TODO(tzik): Remove this after updating callsites of this to use
+  // HashTraits<String>::{ConstructDeletedValue,IsHashTableDeletedValue}.
   String(WTF::HashTableDeletedValueType) : impl_(WTF::kHashTableDeletedValue) {}
   bool IsHashTableDeletedValue() const {
     return impl_.IsHashTableDeletedValue();
@@ -509,6 +511,8 @@
 #endif
 
  private:
+  friend struct HashTraits<String>;
+
   template <typename CharacterType>
   void AppendInternal(CharacterType);
 
diff --git a/third_party/WebKit/public/platform/WebRuntimeFeatures.h b/third_party/WebKit/public/platform/WebRuntimeFeatures.h
index 6080d09..29fcd502 100644
--- a/third_party/WebKit/public/platform/WebRuntimeFeatures.h
+++ b/third_party/WebKit/public/platform/WebRuntimeFeatures.h
@@ -67,8 +67,6 @@
 
   BLINK_PLATFORM_EXPORT static void EnableCompositorTouchAction(bool);
 
-  BLINK_PLATFORM_EXPORT static void EnableCompositorImageAnimations(bool);
-
   BLINK_PLATFORM_EXPORT static void EnableDisplayList2dCanvas(bool);
   BLINK_PLATFORM_EXPORT static void ForceDisplayList2dCanvas(bool);
 
@@ -76,6 +74,7 @@
   BLINK_PLATFORM_EXPORT static bool IsOriginTrialsEnabled();
 
   BLINK_PLATFORM_EXPORT static void EnableAccelerated2dCanvas(bool);
+  BLINK_PLATFORM_EXPORT static void EnableAllowActivationDelegationAttr(bool);
   BLINK_PLATFORM_EXPORT static void EnableAudioOutputDevices(bool);
   BLINK_PLATFORM_EXPORT static void EnableCanvas2dImageChromium(bool);
   BLINK_PLATFORM_EXPORT static void EnableCSSHexAlphaColor(bool);
diff --git a/third_party/brotli/README.chromium b/third_party/brotli/README.chromium
index ce8435e..4b565b62 100644
--- a/third_party/brotli/README.chromium
+++ b/third_party/brotli/README.chromium
@@ -1,6 +1,6 @@
 Name: Brotli
 URL: https://github.com/google/brotli
-Version: 19dc934e391752b78f78eca60c2c1ca5f6ac647b
+Version: c60563591a9a86196f19987c81dde4384a088861
 License: MIT
 License File: LICENSE
 Security Critical: yes
@@ -14,8 +14,7 @@
 - This only includes the contents of c/ directory, the README.md and the LICENSE
   files.
 - Auxiliary fuzzer runners removed from fuzz/
-- Line 19 of enc/cluster_inc.h was modified to eliminate build error on Windows.
-- Line 700 of tools/brotli.c was modified to eliminate build error on Windows.
 - common/dictionary.bin: Removed.
+- enc/hash_longest_match_quickly_inc.h:223: calm down MSVC
 - BUILD.gn: Added.
 - brotli.gni: Added.
diff --git a/third_party/brotli/README.md b/third_party/brotli/README.md
index 637d52b..26ab21f 100644
--- a/third_party/brotli/README.md
+++ b/third_party/brotli/README.md
@@ -8,21 +8,22 @@
 currently available general-purpose compression methods. It is similar in speed
 with deflate but offers more dense compression.
 
-The specification of the Brotli Compressed Data Format is defined in [RFC 7932](https://www.ietf.org/rfc/rfc7932.txt).
+The specification of the Brotli Compressed Data Format is defined in [RFC 7932](https://tools.ietf.org/html/rfc7932).
 
 Brotli is open-sourced under the MIT License, see the LICENSE file.
 
 Brotli mailing list:
 https://groups.google.com/forum/#!forum/brotli
 
-[![Build Status](https://travis-ci.org/google/brotli.svg?branch=master)](https://travis-ci.org/google/brotli)
+[![TravisCI Build Status](https://travis-ci.org/google/brotli.svg?branch=master)](https://travis-ci.org/google/brotli)
+[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/google/brotli?branch=master&svg=true)](https://ci.appveyor.com/project/szabadka/brotli)
 
 ### Build instructions
 
 #### Autotools-style CMake
 
 [configure-cmake](https://github.com/nemequ/configure-cmake) is an
-autotools-style configure script for CMake-based projects.
+autotools-style configure script for CMake-based projects (not supported on Windows).
 
 The basic commands to build, test and install brotli are:
 
@@ -32,32 +33,19 @@
     $ make test
     $ make install
 
-To build static libraries use `--disable-shared-libs` argument:
-
-    $ mkdir out-static && cd out-static
-    $ ../configure-cmake --disable-shared-libs
-    $ make install
-
 #### Bazel
 
 See [Bazel](http://www.bazel.build/)
 
 #### CMake
 
-The basic commands to build, test and install brotli are:
+The basic commands to build and install brotli are:
 
     $ mkdir out && cd out
-    $ cmake ..
-    $ make
-    $ make test
-    $ make install
+    $ cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./installed ..
+    $ cmake --build . --config Release --target install
 
-You can use other [CMake](https://cmake.org/) configuration. For example, to
-build static libraries:
-
-    $ mkdir out-static && cd out-static
-    $ cmake .. -DBUILD_SHARED_LIBS=OFF
-    $ make
+You can use other [CMake](https://cmake.org/) configuration.
 
 #### Premake5
 
@@ -65,12 +53,16 @@
 
 #### Python
 
-To install the Python module from source, run the following:
+To install the latest release of the Python module, run the following:
 
-    $ python setup.py install
+    $ pip install brotli
 
-See the [Python readme](python/README.md) for more details on testing
-and development.
+To install the tip-of-the-tree version, run:
+
+    $ pip install --upgrade git+https://github.com/google/brotli
+
+See the [Python readme](python/README.md) for more details on installing
+from source, development, and testing.
 
 ### Benchmarks
 * [Squash Compression Benchmark](https://quixdb.github.io/squash-benchmark/) / [Unstable Squash Compression Benchmark](https://quixdb.github.io/squash-benchmark/unstable/)
@@ -78,6 +70,12 @@
 * [Lzturbo Benchmark](https://sites.google.com/site/powturbo/home/benchmark)
 
 ### Related projects
+> **Disclaimer:** Brotli authors take no responsibility for the third party projects mentioned in this section.
+
 Independent [decoder](https://github.com/madler/brotli) implementation by Mark Adler, based entirely on format specification.
 
 JavaScript port of brotli [decoder](https://github.com/devongovett/brotli.js). Could be used directly via `npm install brotli`
+
+Hand ported [decoder / encoder](https://github.com/dominikhlbg/BrotliHaxe) in haxe by Dominik Homberger. Output source code: JavaScript, PHP, Python, Java and C#
+
+7Zip [plugin](https://github.com/mcmilk/7-Zip-Zstd)
diff --git a/third_party/brotli/common/constants.h b/third_party/brotli/common/constants.h
index 7b3d6a5..416ec55 100644
--- a/third_party/brotli/common/constants.h
+++ b/third_party/brotli/common/constants.h
@@ -38,6 +38,8 @@
                                      BROTLI_MAX_NDIRECT +              \
                                      (BROTLI_MAX_DISTANCE_BITS <<      \
                                       (BROTLI_MAX_NPOSTFIX + 1)))
+/* Distance that is guaranteed to be representable in any stream. */
+#define BROTLI_MAX_DISTANCE 0x3FFFFFC
 
 /* 7.1. Context modes and context ID lookup for literals */
 /* "context IDs for literals are in the range of 0..63" */
diff --git a/third_party/brotli/dec/decode.c b/third_party/brotli/dec/decode.c
index d3951f8..be8de42 100644
--- a/third_party/brotli/dec/decode.c
+++ b/third_party/brotli/dec/decode.c
@@ -39,6 +39,11 @@
 #define HUFFMAN_TABLE_BITS 8U
 #define HUFFMAN_TABLE_MASK 0xff
 
+/* We need the slack region for the following reasons:
+    - doing up to two 16-byte copies for fast backward copying
+    - inserting transformed dictionary word (5 prefix + 24 base + 8 suffix) */
+static const uint32_t kRingBufferWriteAheadSlack = 42;
+
 static const uint8_t kCodeLengthCodeOrder[BROTLI_CODE_LENGTH_CODES] = {
   1, 2, 3, 4, 0, 5, 17, 6, 16, 7, 8, 9, 10, 11, 12, 13, 14, 15,
 };
@@ -52,6 +57,17 @@
   0, 4, 3, 2, 0, 4, 3, 1, 0, 4, 3, 2, 0, 4, 3, 5,
 };
 
+BROTLI_BOOL BrotliDecoderSetParameter(
+    BrotliDecoderState* state, BrotliDecoderParameter p, uint32_t value) {
+  switch (p) {
+    case BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION:
+      state->canny_ringbuffer_allocation = !!value ? 0 : 1;
+      return BROTLI_TRUE;
+
+    default: return BROTLI_FALSE;
+  }
+}
+
 BrotliDecoderState* BrotliDecoderCreateInstance(
     brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) {
   BrotliDecoderState* state = 0;
@@ -1210,7 +1226,9 @@
   BROTLI_LOG_UINT(to_write);
   BROTLI_LOG_UINT(num_written);
   s->partial_pos_out += num_written;
-  if (total_out) *total_out = s->partial_pos_out - (size_t)s->custom_dict_size;
+  if (total_out) {
+    *total_out = s->partial_pos_out;
+  }
   if (num_written < to_write) {
     if (s->ringbuffer_size == (1 << s->window_bits) || force) {
       return BROTLI_DECODER_NEEDS_MORE_OUTPUT;
@@ -1242,22 +1260,16 @@
 
    Last two bytes of ring-buffer are initialized to 0, so context calculation
    could be done uniformly for the first two and all other positions.
-
-   Custom dictionary, if any, is copied to the end of ring-buffer.
 */
 static BROTLI_BOOL BROTLI_NOINLINE BrotliEnsureRingBuffer(
     BrotliDecoderState* s) {
-  /* We need the slack region for the following reasons:
-      - doing up to two 16-byte copies for fast backward copying
-      - inserting transformed dictionary word (5 prefix + 24 base + 8 suffix) */
-  static const int kRingBufferWriteAheadSlack = 42;
   uint8_t* old_ringbuffer = s->ringbuffer;
   if (s->ringbuffer_size == s->new_ringbuffer_size) {
     return BROTLI_TRUE;
   }
 
-  s->ringbuffer = (uint8_t*)BROTLI_ALLOC(s, (size_t)(s->new_ringbuffer_size +
-      kRingBufferWriteAheadSlack));
+  s->ringbuffer = (uint8_t*)BROTLI_ALLOC(s, (size_t)(s->new_ringbuffer_size) +
+      kRingBufferWriteAheadSlack);
   if (s->ringbuffer == 0) {
     /* Restore previous value. */
     s->ringbuffer = old_ringbuffer;
@@ -1266,13 +1278,7 @@
   s->ringbuffer[s->new_ringbuffer_size - 2] = 0;
   s->ringbuffer[s->new_ringbuffer_size - 1] = 0;
 
-  if (!old_ringbuffer) {
-    if (s->custom_dict) {
-      memcpy(s->ringbuffer, s->custom_dict, (size_t)s->custom_dict_size);
-      s->partial_pos_out = (size_t)s->custom_dict_size;
-      s->pos = s->custom_dict_size;
-    }
-  } else {
+  if (!!old_ringbuffer) {
     memcpy(s->ringbuffer, old_ringbuffer, (size_t)s->pos);
     BROTLI_FREE(s, old_ringbuffer);
   }
@@ -1361,16 +1367,20 @@
   }
 
   if (!s->ringbuffer) {
-    /* Custom dictionary counts as a "virtual" output. */
-    output_size = s->custom_dict_size;
+    output_size = 0;
   } else {
     output_size = s->pos;
   }
   output_size += s->meta_block_remaining_len;
   min_size = min_size < output_size ? output_size : min_size;
 
-  while ((new_ringbuffer_size >> 1) >= min_size) {
-    new_ringbuffer_size >>= 1;
+  if (!!s->canny_ringbuffer_allocation) {
+    /* Reduce ring buffer size to save memory when server is unscrupulous.
+       In worst case memory usage might be 1.5x bigger for a short period of
+       ring buffer reallocation.*/
+    while ((new_ringbuffer_size >> 1) >= min_size) {
+      new_ringbuffer_size >>= 1;
+    }
   }
 
   s->new_ringbuffer_size = new_ringbuffer_size;
@@ -1735,14 +1745,14 @@
   /* Apply copy of LZ77 back-reference, or static dictionary reference if
   the distance is larger than the max LZ77 distance */
   if (s->distance_code > s->max_distance) {
+    int address = s->distance_code - s->max_distance - 1;
     if (i >= BROTLI_MIN_DICTIONARY_WORD_LENGTH &&
         i <= BROTLI_MAX_DICTIONARY_WORD_LENGTH) {
       int offset = (int)s->dictionary->offsets_by_length[i];
-      int word_id = s->distance_code - s->max_distance - 1;
       uint32_t shift = s->dictionary->size_bits_by_length[i];
       int mask = (int)BitMask(shift);
-      int word_idx = word_id & mask;
-      int transform_idx = word_id >> shift;
+      int word_idx = address & mask;
+      int transform_idx = address >> shift;
       /* Compensate double distance-ring-buffer roll. */
       s->dist_rb_idx += s->distance_context;
       offset += word_idx * i;
@@ -1925,7 +1935,13 @@
     if (result != BROTLI_DECODER_SUCCESS) { /* Error, needs more input/output */
       if (result == BROTLI_DECODER_NEEDS_MORE_INPUT) {
         if (s->ringbuffer != 0) { /* Pro-actively push output. */
-          WriteRingBuffer(s, available_out, next_out, total_out, BROTLI_TRUE);
+          BrotliDecoderErrorCode intermediate_result = WriteRingBuffer(s,
+              available_out, next_out, total_out, BROTLI_TRUE);
+          /* WriteRingBuffer checks s->meta_block_remaining_len validity. */
+          if ((int)intermediate_result < 0) {
+            result = intermediate_result;
+            break;
+          }
         }
         if (s->buffer_length != 0) { /* Used with internal buffer. */
           if (br->avail_in == 0) { /* Successfully finished read transaction. */
@@ -1999,11 +2015,6 @@
         }
         /* Maximum distance, see section 9.1. of the spec. */
         s->max_backward_distance = (1 << s->window_bits) - BROTLI_WINDOW_GAP;
-        /* Limit custom dictionary size. */
-        if (s->custom_dict_size >= s->max_backward_distance) {
-          s->custom_dict += s->custom_dict_size - s->max_backward_distance;
-          s->custom_dict_size = s->max_backward_distance;
-        }
 
         /* Allocate memory for both block_type_trees and block_len_trees. */
         s->block_type_trees = (HuffmanCode*)BROTLI_ALLOC(s,
@@ -2300,16 +2311,11 @@
   return SaveErrorCode(s, result);
 }
 
-void BrotliDecoderSetCustomDictionary(
-    BrotliDecoderState* s, size_t size, const uint8_t* dict) {
-  if (size > (1u << 24)) {
-    return;
-  }
-  s->custom_dict = dict;
-  s->custom_dict_size = (int)size;
-}
-
 BROTLI_BOOL BrotliDecoderHasMoreOutput(const BrotliDecoderState* s) {
+  /* After unrecoverable error remaining output is considered nonsensical. */
+  if ((int)s->error_code < 0) {
+    return BROTLI_FALSE;
+  }
   return TO_BROTLI_BOOL(
       s->ringbuffer != 0 && UnwrittenBytes(s, BROTLI_FALSE) != 0);
 }
@@ -2319,17 +2325,20 @@
   size_t available_out = *size ? *size : 1u << 24;
   size_t requested_out = available_out;
   BrotliDecoderErrorCode status;
-  if (s->ringbuffer == 0) {
+  if ((s->ringbuffer == 0) || ((int)s->error_code < 0)) {
     *size = 0;
     return 0;
   }
   WrapRingBuffer(s);
   status = WriteRingBuffer(s, &available_out, &result, 0, BROTLI_TRUE);
+  /* Either WriteRingBuffer returns those "success" codes... */
   if (status == BROTLI_DECODER_SUCCESS ||
       status == BROTLI_DECODER_NEEDS_MORE_OUTPUT) {
     *size = requested_out - available_out;
   } else {
-    /* This might happen if previous decoder error code was ignored. */
+    /* ... or stream is broken. Normally this should be caught by
+       BrotliDecoderDecompressStream, this is just a safeguard. */
+    if ((int)status < 0) SaveErrorCode(s, status);
     *size = 0;
     result = 0;
   }
diff --git a/third_party/brotli/dec/state.c b/third_party/brotli/dec/state.c
index b7431f2..554313d 100644
--- a/third_party/brotli/dec/state.c
+++ b/third_party/brotli/dec/state.c
@@ -83,11 +83,12 @@
   s->distance_hgroup.codes = NULL;
   s->distance_hgroup.htrees = NULL;
 
-  s->custom_dict = NULL;
-  s->custom_dict_size = 0;
-
   s->is_last_metablock = 0;
+  s->is_uncompressed = 0;
+  s->is_metadata = 0;
   s->should_wrap_ringbuffer = 0;
+  s->canny_ringbuffer_allocation = 1;
+
   s->window_bits = 0;
   s->max_distance = 0;
   s->dist_rb[0] = 16;
diff --git a/third_party/brotli/dec/state.h b/third_party/brotli/dec/state.h
index 5946124..1d2773b 100644
--- a/third_party/brotli/dec/state.h
+++ b/third_party/brotli/dec/state.h
@@ -172,7 +172,7 @@
   uint32_t space;
 
   HuffmanCode table[32];
-  /* List of of symbol chains. */
+  /* List of heads of symbol chains. */
   uint16_t* symbol_lists;
   /* Storage from symbol_lists. */
   uint16_t symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1 +
@@ -197,10 +197,6 @@
   uint32_t mtf_upper_bound;
   uint32_t mtf[64 + 1];
 
-  /* For custom dictionaries */
-  const uint8_t* custom_dict;
-  int custom_dict_size;
-
   /* less used attributes are in the end of this struct */
   /* States inside function calls */
   BrotliRunningMetablockHeaderState substate_metablock_header;
@@ -215,6 +211,7 @@
   unsigned int is_uncompressed : 1;
   unsigned int is_metadata : 1;
   unsigned int should_wrap_ringbuffer : 1;
+  unsigned int canny_ringbuffer_allocation : 1;
   unsigned int size_nibbles : 8;
   uint32_t window_bits;
 
diff --git a/third_party/brotli/enc/backward_references.c b/third_party/brotli/enc/backward_references.c
index 39a74c2c..3ac7f2f 100644
--- a/third_party/brotli/enc/backward_references.c
+++ b/third_party/brotli/enc/backward_references.c
@@ -48,6 +48,8 @@
 #define EXPAND_CAT(a, b) CAT(a, b)
 #define CAT(a, b) a ## b
 #define FN(X) EXPAND_CAT(X, HASHER())
+#define EXPORT_FN(X) EXPAND_CAT(X, EXPAND_CAT(PREFIX(), HASHER()))
+#define PREFIX() N
 
 #define HASHER() H2
 /* NOLINTNEXTLINE(build/include) */
@@ -94,6 +96,8 @@
 #include "./backward_references_inc.h"
 #undef HASHER
 
+#undef PREFIX
+#undef EXPORT_FN
 #undef FN
 #undef CAT
 #undef EXPAND_CAT
@@ -113,11 +117,11 @@
   switch (params->hasher.type) {
 #define CASE_(N)                                                  \
     case N:                                                       \
-      CreateBackwardReferencesH ## N(dictionary,                  \
+      CreateBackwardReferencesNH ## N(dictionary,                 \
           kStaticDictionaryHash, num_bytes, position, ringbuffer, \
           ringbuffer_mask, params, hasher, dist_cache,            \
           last_insert_len, commands, num_commands, num_literals); \
-      break;
+      return;
     FOR_GENERIC_HASHERS(CASE_)
 #undef CASE_
     default:
diff --git a/third_party/brotli/enc/backward_references_hq.c b/third_party/brotli/enc/backward_references_hq.c
index 5150ae1..335b55c 100644
--- a/third_party/brotli/enc/backward_references_hq.c
+++ b/third_party/brotli/enc/backward_references_hq.c
@@ -300,6 +300,7 @@
 static uint32_t ComputeDistanceShortcut(const size_t block_start,
                                         const size_t pos,
                                         const size_t max_backward,
+                                        const size_t gap,
                                         const ZopfliNode* nodes) {
   const size_t clen = ZopfliNodeCopyLength(&nodes[pos]);
   const size_t ilen = nodes[pos].insert_length;
@@ -311,8 +312,8 @@
      does not update the last distances. */
   if (pos == 0) {
     return 0;
-  } else if (dist + clen <= block_start + pos &&
-             dist <= max_backward &&
+  } else if (dist + clen <= block_start + pos + gap &&
+             dist <= max_backward + gap &&
              ZopfliNodeDistanceCode(&nodes[pos]) > 0) {
     return (uint32_t)pos;
   } else {
@@ -350,12 +351,12 @@
    is eligible. */
 static void EvaluateNode(
     const size_t block_start, const size_t pos, const size_t max_backward_limit,
-    const int* starting_dist_cache, const ZopfliCostModel* model,
-    StartPosQueue* queue, ZopfliNode* nodes) {
+    const size_t gap, const int* starting_dist_cache,
+    const ZopfliCostModel* model, StartPosQueue* queue, ZopfliNode* nodes) {
   /* Save cost, because ComputeDistanceCache invalidates it. */
   float node_cost = nodes[pos].u.cost;
   nodes[pos].u.shortcut = ComputeDistanceShortcut(
-      block_start, pos, max_backward_limit, nodes);
+      block_start, pos, max_backward_limit, gap, nodes);
   if (node_cost <= ZopfliCostModelGetLiteralCosts(model, 0, pos)) {
     PosData posdata;
     posdata.pos = pos;
@@ -385,9 +386,10 @@
   size_t min_len;
   size_t result = 0;
   size_t k;
+  size_t gap = 0;
 
-  EvaluateNode(block_start, pos, max_backward_limit, starting_dist_cache, model,
-               queue, nodes);
+  EvaluateNode(block_start, pos, max_backward_limit, gap, starting_dist_cache,
+      model, queue, nodes);
 
   {
     const PosData* posdata = StartPosQueueAt(queue, 0);
@@ -415,25 +417,31 @@
       const size_t backward =
           (size_t)(posdata->distance_cache[idx] + kDistanceCacheOffset[j]);
       size_t prev_ix = cur_ix - backward;
-      if (prev_ix >= cur_ix) {
+      size_t len = 0;
+      uint8_t continuation = ringbuffer[cur_ix_masked + best_len];
+      if (cur_ix_masked + best_len > ringbuffer_mask) {
+        break;
+      }
+      if (BROTLI_PREDICT_FALSE(backward > max_distance + gap)) {
         continue;
       }
-      if (BROTLI_PREDICT_FALSE(backward > max_distance)) {
-        continue;
-      }
-      prev_ix &= ringbuffer_mask;
+      if (backward <= max_distance) {
+        if (prev_ix >= cur_ix) {
+          continue;
+        }
 
-      if (cur_ix_masked + best_len > ringbuffer_mask ||
-          prev_ix + best_len > ringbuffer_mask ||
-          ringbuffer[cur_ix_masked + best_len] !=
-              ringbuffer[prev_ix + best_len]) {
+        prev_ix &= ringbuffer_mask;
+        if (prev_ix + best_len > ringbuffer_mask ||
+            continuation != ringbuffer[prev_ix + best_len]) {
+          continue;
+        }
+        len = FindMatchLengthWithLimit(&ringbuffer[prev_ix],
+                                       &ringbuffer[cur_ix_masked],
+                                       max_len);
+      } else {
         continue;
       }
       {
-        const size_t len =
-            FindMatchLengthWithLimit(&ringbuffer[prev_ix],
-                                     &ringbuffer[cur_ix_masked],
-                                     max_len);
         const float dist_cost = base_cost +
             ZopfliCostModelGetDistanceCost(model, j);
         size_t l;
@@ -464,7 +472,8 @@
       for (j = 0; j < num_matches; ++j) {
         BackwardMatch match = matches[j];
         size_t dist = match.distance;
-        BROTLI_BOOL is_dictionary_match = TO_BROTLI_BOOL(dist > max_distance);
+        BROTLI_BOOL is_dictionary_match =
+            TO_BROTLI_BOOL(dist > max_distance + gap);
         /* We already tried all possible last distance matches, so we can use
            normal distance code here. */
         size_t dist_code = dist + BROTLI_NUM_DISTANCE_SHORT_CODES - 1;
@@ -526,11 +535,14 @@
                                 const ZopfliNode* nodes,
                                 int* dist_cache,
                                 size_t* last_insert_len,
+                                const BrotliEncoderParams* params,
                                 Command* commands,
                                 size_t* num_literals) {
   size_t pos = 0;
   uint32_t offset = nodes[0].u.next;
   size_t i;
+  size_t gap = 0;
+  BROTLI_UNUSED(params);
   for (i = 0; offset != BROTLI_UINT32_MAX; i++) {
     const ZopfliNode* next = &nodes[pos + offset];
     size_t copy_length = ZopfliNodeCopyLength(next);
@@ -546,11 +558,11 @@
       size_t len_code = ZopfliNodeLengthCode(next);
       size_t max_distance =
           BROTLI_MIN(size_t, block_start + pos, max_backward_limit);
-      BROTLI_BOOL is_dictionary = TO_BROTLI_BOOL(distance > max_distance);
+      BROTLI_BOOL is_dictionary = TO_BROTLI_BOOL(distance > max_distance + gap);
       size_t dist_code = ZopfliNodeDistanceCode(next);
 
-      InitCommand(
-          &commands[i], insert_length, copy_length, len_code, dist_code);
+      InitCommand(&commands[i], insert_length,
+          copy_length, (int)len_code - (int)copy_length, dist_code);
 
       if (!is_dictionary && dist_code > 0) {
         dist_cache[3] = dist_cache[2];
@@ -572,6 +584,7 @@
                             size_t ringbuffer_mask,
                             const BrotliEncoderParams* params,
                             const size_t max_backward_limit,
+                            const size_t gap,
                             const int* dist_cache,
                             const ZopfliCostModel* model,
                             const uint32_t* num_matches,
@@ -600,8 +613,8 @@
       while (skip) {
         i++;
         if (i + 3 >= num_bytes) break;
-        EvaluateNode(
-            position, i, max_backward_limit, dist_cache, model, &queue, nodes);
+        EvaluateNode(position, i, max_backward_limit, gap, dist_cache, model,
+            &queue, nodes);
         cur_match_pos += num_matches[i];
         skip--;
       }
@@ -629,6 +642,7 @@
   const size_t store_end = num_bytes >= StoreLookaheadH10() ?
       position + num_bytes - StoreLookaheadH10() + 1 : position;
   size_t i;
+  size_t gap = 0;
   nodes[0].length = 0;
   nodes[0].u.cost = 0;
   InitZopfliCostModel(m, &model, num_bytes);
@@ -640,7 +654,8 @@
     const size_t pos = position + i;
     const size_t max_distance = BROTLI_MIN(size_t, pos, max_backward_limit);
     size_t num_matches = FindAllMatchesH10(hasher, dictionary, ringbuffer,
-        ringbuffer_mask, pos, num_bytes - i, max_distance, params, matches);
+        ringbuffer_mask, pos, num_bytes - i, max_distance, gap, params,
+        matches);
     size_t skip;
     if (num_matches > 0 &&
         BackwardMatchLength(&matches[num_matches - 1]) > max_zopfli_len) {
@@ -662,8 +677,8 @@
       while (skip) {
         i++;
         if (i + HashTypeLengthH10() - 1 >= num_bytes) break;
-        EvaluateNode(
-            position, i, max_backward_limit, dist_cache, &model, &queue, nodes);
+        EvaluateNode(position, i, max_backward_limit, gap, dist_cache, &model,
+            &queue, nodes);
         skip--;
       }
     }
@@ -688,7 +703,7 @@
       dist_cache, hasher, nodes);
   if (BROTLI_IS_OOM(m)) return;
   BrotliZopfliCreateCommands(num_bytes, position, max_backward_limit, nodes,
-      dist_cache, last_insert_len, commands, num_literals);
+      dist_cache, last_insert_len, params, commands, num_literals);
   BROTLI_FREE(m, nodes);
 }
 
@@ -712,6 +727,7 @@
   ZopfliCostModel model;
   ZopfliNode* nodes;
   BackwardMatch* matches = BROTLI_ALLOC(m, BackwardMatch, matches_size);
+  size_t gap = 0;
   if (BROTLI_IS_OOM(m)) return;
   for (i = 0; i + HashTypeLengthH10() - 1 < num_bytes; ++i) {
     const size_t pos = position + i;
@@ -725,14 +741,12 @@
         cur_match_pos + MAX_NUM_MATCHES_H10);
     if (BROTLI_IS_OOM(m)) return;
     num_found_matches = FindAllMatchesH10(hasher, dictionary, ringbuffer,
-        ringbuffer_mask, pos, max_length, max_distance, params,
+        ringbuffer_mask, pos, max_length, max_distance, gap, params,
         &matches[cur_match_pos]);
     cur_match_end = cur_match_pos + num_found_matches;
     for (j = cur_match_pos; j + 1 < cur_match_end; ++j) {
-      assert(BackwardMatchLength(&matches[j]) <
+      assert(BackwardMatchLength(&matches[j]) <=
           BackwardMatchLength(&matches[j + 1]));
-      assert(matches[j].distance > max_distance ||
-             matches[j].distance <= matches[j + 1].distance);
     }
     num_matches[i] = (uint32_t)num_found_matches;
     if (num_found_matches > 0) {
@@ -774,10 +788,10 @@
     *last_insert_len = orig_last_insert_len;
     memcpy(dist_cache, orig_dist_cache, 4 * sizeof(dist_cache[0]));
     *num_commands += ZopfliIterate(num_bytes, position, ringbuffer,
-        ringbuffer_mask, params, max_backward_limit, dist_cache,
+        ringbuffer_mask, params, max_backward_limit, gap, dist_cache,
         &model, num_matches, matches, nodes);
     BrotliZopfliCreateCommands(num_bytes, position, max_backward_limit,
-        nodes, dist_cache, last_insert_len, commands, num_literals);
+        nodes, dist_cache, last_insert_len, params, commands, num_literals);
   }
   CleanupZopfliCostModel(m, &model);
   BROTLI_FREE(m, nodes);
diff --git a/third_party/brotli/enc/backward_references_hq.h b/third_party/brotli/enc/backward_references_hq.h
index 0a768cd..7e3bd7e 100644
--- a/third_party/brotli/enc/backward_references_hq.h
+++ b/third_party/brotli/enc/backward_references_hq.h
@@ -83,14 +83,11 @@
     const BrotliEncoderParams* params, const size_t max_backward_limit,
     const int* dist_cache, HasherHandle hasher, ZopfliNode* nodes);
 
-BROTLI_INTERNAL void BrotliZopfliCreateCommands(const size_t num_bytes,
-                                                const size_t block_start,
-                                                const size_t max_backward_limit,
-                                                const ZopfliNode* nodes,
-                                                int* dist_cache,
-                                                size_t* last_insert_len,
-                                                Command* commands,
-                                                size_t* num_literals);
+BROTLI_INTERNAL void BrotliZopfliCreateCommands(
+    const size_t num_bytes, const size_t block_start,
+    const size_t max_backward_limit, const ZopfliNode* nodes,
+    int* dist_cache, size_t* last_insert_len, const BrotliEncoderParams* params,
+    Command* commands, size_t* num_literals);
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }  /* extern "C" */
diff --git a/third_party/brotli/enc/backward_references_inc.h b/third_party/brotli/enc/backward_references_inc.h
index 0479dfd..81c783c0 100644
--- a/third_party/brotli/enc/backward_references_inc.h
+++ b/third_party/brotli/enc/backward_references_inc.h
@@ -5,11 +5,11 @@
    See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
 */
 
-/* template parameters: FN */
+/* template parameters: EXPORT_FN, FN */
 
-static BROTLI_NOINLINE void FN(CreateBackwardReferences)(
-    const BrotliDictionary* dictionary, const uint16_t* dictionary_hash,
-    size_t num_bytes, size_t position,
+static BROTLI_NOINLINE void EXPORT_FN(CreateBackwardReferences)(
+    const BrotliDictionary* dictionary,
+    const uint16_t* dictionary_hash, size_t num_bytes, size_t position,
     const uint8_t* ringbuffer, size_t ringbuffer_mask,
     const BrotliEncoderParams* params, HasherHandle hasher, int* dist_cache,
     size_t* last_insert_len, Command* commands, size_t* num_commands,
@@ -27,6 +27,7 @@
   const size_t random_heuristics_window_size =
       LiteralSpreeLengthForSparseSearch(params);
   size_t apply_random_heuristics = position + random_heuristics_window_size;
+  const size_t gap = 0;
 
   /* Minimum score to accept a backward reference. */
   const score_t kMinScore = BROTLI_SCORE_BASE + 100;
@@ -38,29 +39,29 @@
     size_t max_distance = BROTLI_MIN(size_t, position, max_backward_limit);
     HasherSearchResult sr;
     sr.len = 0;
-    sr.len_x_code = 0;
+    sr.len_code_delta = 0;
     sr.distance = 0;
     sr.score = kMinScore;
-    if (FN(FindLongestMatch)(hasher, dictionary, dictionary_hash,
-                             ringbuffer, ringbuffer_mask, dist_cache,
-                             position, max_length, max_distance, &sr)) {
+    FN(FindLongestMatch)(hasher, dictionary, dictionary_hash, ringbuffer,
+                         ringbuffer_mask, dist_cache, position,
+                         max_length, max_distance, gap, &sr);
+    if (sr.score > kMinScore) {
       /* Found a match. Let's look for something even better ahead. */
       int delayed_backward_references_in_row = 0;
       --max_length;
       for (;; --max_length) {
         const score_t cost_diff_lazy = 175;
-        BROTLI_BOOL is_match_found;
         HasherSearchResult sr2;
         sr2.len = params->quality < MIN_QUALITY_FOR_EXTENSIVE_REFERENCE_SEARCH ?
             BROTLI_MIN(size_t, sr.len - 1, max_length) : 0;
-        sr2.len_x_code = 0;
+        sr2.len_code_delta = 0;
         sr2.distance = 0;
         sr2.score = kMinScore;
         max_distance = BROTLI_MIN(size_t, position + 1, max_backward_limit);
-        is_match_found = FN(FindLongestMatch)(hasher, dictionary,
-            dictionary_hash, ringbuffer, ringbuffer_mask, dist_cache,
-            position + 1, max_length, max_distance, &sr2);
-        if (is_match_found && sr2.score >= sr.score + cost_diff_lazy) {
+        FN(FindLongestMatch)(hasher, dictionary, dictionary_hash, ringbuffer,
+                             ringbuffer_mask, dist_cache, position + 1,
+                             max_length, max_distance, gap, &sr2);
+        if (sr2.score >= sr.score + cost_diff_lazy) {
           /* Ok, let's just write one byte for now and start a match from the
              next byte. */
           ++position;
@@ -80,24 +81,33 @@
         /* The first 16 codes are special short-codes,
            and the minimum offset is 1. */
         size_t distance_code =
-            ComputeDistanceCode(sr.distance, max_distance, dist_cache);
-        if (sr.distance <= max_distance && distance_code > 0) {
+            ComputeDistanceCode(sr.distance, max_distance + gap, dist_cache);
+        if ((sr.distance <= (max_distance + gap)) && distance_code > 0) {
           dist_cache[3] = dist_cache[2];
           dist_cache[2] = dist_cache[1];
           dist_cache[1] = dist_cache[0];
           dist_cache[0] = (int)sr.distance;
           FN(PrepareDistanceCache)(hasher, dist_cache);
         }
-        InitCommand(commands++, insert_length, sr.len, sr.len ^ sr.len_x_code,
+        InitCommand(commands++, insert_length, sr.len, sr.len_code_delta,
             distance_code);
       }
       *num_literals += insert_length;
       insert_length = 0;
       /* Put the hash keys into the table, if there are enough bytes left.
          Depending on the hasher implementation, it can push all positions
-         in the given range or only a subset of them. */
-      FN(StoreRange)(hasher, ringbuffer, ringbuffer_mask, position + 2,
-                     BROTLI_MIN(size_t, position + sr.len, store_end));
+         in the given range or only a subset of them.
+         Avoid hash poisoning with RLE data. */
+      {
+        size_t range_start = position + 2;
+        size_t range_end = BROTLI_MIN(size_t, position + sr.len, store_end);
+        if (sr.distance < (sr.len >> 2)) {
+          range_start = BROTLI_MIN(size_t, range_end, BROTLI_MAX(size_t,
+              range_start, position + sr.len - (sr.distance << 2)));
+        }
+        FN(StoreRange)(hasher, ringbuffer, ringbuffer_mask, range_start,
+                       range_end);
+      }
       position += sr.len;
     } else {
       ++insert_length;
diff --git a/third_party/brotli/enc/block_splitter.c b/third_party/brotli/enc/block_splitter.c
index a870885..0331146 100644
--- a/third_party/brotli/enc/block_splitter.c
+++ b/third_party/brotli/enc/block_splitter.c
@@ -74,11 +74,9 @@
   }
 }
 
-static BROTLI_INLINE unsigned int MyRand(unsigned int* seed) {
+static BROTLI_INLINE uint32_t MyRand(uint32_t* seed) {
+  /* Initial seed should be 7. In this case, loop length is (1 << 29). */
   *seed *= 16807U;
-  if (*seed == 0) {
-    *seed = 1;
-  }
   return *seed;
 }
 
diff --git a/third_party/brotli/enc/block_splitter_inc.h b/third_party/brotli/enc/block_splitter_inc.h
index f932740d..4884478 100644
--- a/third_party/brotli/enc/block_splitter_inc.h
+++ b/third_party/brotli/enc/block_splitter_inc.h
@@ -13,7 +13,7 @@
                                     size_t stride,
                                     size_t num_histograms,
                                     HistogramType* histograms) {
-  unsigned int seed = 7;
+  uint32_t seed = 7;
   size_t block_length = length / num_histograms;
   size_t i;
   FN(ClearHistograms)(histograms, num_histograms);
@@ -29,14 +29,13 @@
   }
 }
 
-static void FN(RandomSample)(unsigned int* seed,
+static void FN(RandomSample)(uint32_t* seed,
                              const DataType* data,
                              size_t length,
                              size_t stride,
                              HistogramType* sample) {
   size_t pos = 0;
   if (stride >= length) {
-    pos = 0;
     stride = length;
   } else {
     pos = MyRand(seed) % (length - stride + 1);
@@ -50,7 +49,7 @@
                                    HistogramType* histograms) {
   size_t iters =
       kIterMulForRefining * length / stride + kMinItersForRefining;
-  unsigned int seed = 7;
+  uint32_t seed = 7;
   size_t iter;
   iters = ((iters + num_histograms - 1) / num_histograms) * num_histograms;
   for (iter = 0; iter < iters; ++iter) {
diff --git a/third_party/brotli/enc/brotli_bit_stream.c b/third_party/brotli/enc/brotli_bit_stream.c
index 4874695..2907510 100644
--- a/third_party/brotli/enc/brotli_bit_stream.c
+++ b/third_party/brotli/enc/brotli_bit_stream.c
@@ -1325,17 +1325,6 @@
   }
 }
 
-void BrotliStoreSyncMetaBlock(size_t* BROTLI_RESTRICT storage_ix,
-                              uint8_t* BROTLI_RESTRICT storage) {
-  /* Empty metadata meta-block bit pattern:
-       1 bit:  is_last (0)
-       2 bits: num nibbles (3)
-       1 bit:  reserved (0)
-       2 bits: metadata length bytes (0) */
-  BrotliWriteBits(6, 6, storage_ix, storage);
-  JumpToByteBoundary(storage_ix, storage);
-}
-
 #if defined(__cplusplus) || defined(c_plusplus)
 }  /* extern "C" */
 #endif
diff --git a/third_party/brotli/enc/brotli_bit_stream.h b/third_party/brotli/enc/brotli_bit_stream.h
index a98f98f5..2c8bfed 100644
--- a/third_party/brotli/enc/brotli_bit_stream.h
+++ b/third_party/brotli/enc/brotli_bit_stream.h
@@ -96,10 +96,6 @@
     BROTLI_BOOL is_final_block, const uint8_t* input, size_t position,
     size_t mask, size_t len, size_t* storage_ix, uint8_t* storage);
 
-/* Stores an empty metadata meta-block and syncs to a byte boundary. */
-BROTLI_INTERNAL void BrotliStoreSyncMetaBlock(size_t* storage_ix,
-                                              uint8_t* storage);
-
 #if defined(__cplusplus) || defined(c_plusplus)
 }  /* extern "C" */
 #endif
diff --git a/third_party/brotli/enc/cluster_inc.h b/third_party/brotli/enc/cluster_inc.h
index 8e69d81..22ecb3cc 100644
--- a/third_party/brotli/enc/cluster_inc.h
+++ b/third_party/brotli/enc/cluster_inc.h
@@ -16,7 +16,9 @@
     uint32_t idx2, size_t max_num_pairs, HistogramPair* pairs,
     size_t* num_pairs) CODE({
   BROTLI_BOOL is_good_pair = BROTLI_FALSE;
-  HistogramPair p = {0};
+  HistogramPair p;
+  p.idx1 = p.idx2 = 0;
+  p.cost_diff = p.cost_combo = 0;
   if (idx1 == idx2) {
     return;
   }
diff --git a/third_party/brotli/enc/command.h b/third_party/brotli/enc/command.h
index 67ac9817..632318e 100644
--- a/third_party/brotli/enc/command.h
+++ b/third_party/brotli/enc/command.h
@@ -114,17 +114,19 @@
 
 /* distance_code is e.g. 0 for same-as-last short code, or 16 for offset 1. */
 static BROTLI_INLINE void InitCommand(Command* self, size_t insertlen,
-    size_t copylen, size_t copylen_code, size_t distance_code) {
+    size_t copylen, int copylen_code_delta, size_t distance_code) {
+  /* Don't rely on signed int representation, use honest casts. */
+  uint32_t delta = (uint8_t)((int8_t)copylen_code_delta);
   self->insert_len_ = (uint32_t)insertlen;
-  self->copy_len_ = (uint32_t)(copylen | ((copylen_code ^ copylen) << 24));
+  self->copy_len_ = (uint32_t)(copylen | (delta << 24));
   /* The distance prefix and extra bits are stored in this Command as if
      npostfix and ndirect were 0, they are only recomputed later after the
      clustering if needed. */
   PrefixEncodeCopyDistance(
       distance_code, 0, 0, &self->dist_prefix_, &self->dist_extra_);
   GetLengthCode(
-      insertlen, copylen_code, TO_BROTLI_BOOL(self->dist_prefix_ == 0),
-      &self->cmd_prefix_);
+      insertlen, (size_t)((int)copylen + copylen_code_delta),
+      TO_BROTLI_BOOL(self->dist_prefix_ == 0), &self->cmd_prefix_);
 }
 
 static BROTLI_INLINE void InitInsertCommand(Command* self, size_t insertlen) {
@@ -167,7 +169,8 @@
 }
 
 static BROTLI_INLINE uint32_t CommandCopyLenCode(const Command* self) {
-  return (self->copy_len_ & 0xFFFFFF) ^ (self->copy_len_ >> 24);
+  int32_t delta = (int8_t)((uint8_t)(self->copy_len_ >> 24));
+  return (uint32_t)((int32_t)(self->copy_len_ & 0xFFFFFF) + delta);
 }
 
 #if defined(__cplusplus) || defined(c_plusplus)
diff --git a/third_party/brotli/enc/compress_fragment.c b/third_party/brotli/enc/compress_fragment.c
index 96b590fa..b4ca810 100644
--- a/third_party/brotli/enc/compress_fragment.c
+++ b/third_party/brotli/enc/compress_fragment.c
@@ -42,7 +42,7 @@
 static const uint32_t kHashMul32 = 0x1e35a7bd;
 
 static BROTLI_INLINE uint32_t Hash(const uint8_t* p, size_t shift) {
-  const uint64_t h = (BROTLI_UNALIGNED_LOAD64(p) << 24) * kHashMul32;
+  const uint64_t h = (BROTLI_UNALIGNED_LOAD64LE(p) << 24) * kHashMul32;
   return (uint32_t)(h >> shift);
 }
 
@@ -603,7 +603,7 @@
            compression we first update "table" with the hashes of some positions
            within the last copy. */
         {
-          uint64_t input_bytes = BROTLI_UNALIGNED_LOAD64(ip - 3);
+          uint64_t input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 3);
           uint32_t prev_hash = HashBytesAtOffset(input_bytes, 0, shift);
           uint32_t cur_hash = HashBytesAtOffset(input_bytes, 3, shift);
           table[prev_hash] = (int)(ip - base_ip - 3);
@@ -640,7 +640,7 @@
            compression we first update "table" with the hashes of some positions
            within the last copy. */
         {
-          uint64_t input_bytes = BROTLI_UNALIGNED_LOAD64(ip - 3);
+          uint64_t input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 3);
           uint32_t prev_hash = HashBytesAtOffset(input_bytes, 0, shift);
           uint32_t cur_hash = HashBytesAtOffset(input_bytes, 3, shift);
           table[prev_hash] = (int)(ip - base_ip - 3);
diff --git a/third_party/brotli/enc/compress_fragment_two_pass.c b/third_party/brotli/enc/compress_fragment_two_pass.c
index cc549ed0..e6611a0 100644
--- a/third_party/brotli/enc/compress_fragment_two_pass.c
+++ b/third_party/brotli/enc/compress_fragment_two_pass.c
@@ -41,7 +41,7 @@
 static const uint32_t kHashMul32 = 0x1e35a7bd;
 
 static BROTLI_INLINE uint32_t Hash(const uint8_t* p, size_t shift) {
-  const uint64_t h = (BROTLI_UNALIGNED_LOAD64(p) << 16) * kHashMul32;
+  const uint64_t h = (BROTLI_UNALIGNED_LOAD64LE(p) << 16) * kHashMul32;
   return (uint32_t)(h >> shift);
 }
 
@@ -346,7 +346,7 @@
           /* We could immediately start working at ip now, but to improve
              compression we first update "table" with the hashes of some
              positions within the last copy. */
-          uint64_t input_bytes = BROTLI_UNALIGNED_LOAD64(ip - 5);
+          uint64_t input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 5);
           uint32_t prev_hash = HashBytesAtOffset(input_bytes, 0, shift);
           uint32_t cur_hash;
           table[prev_hash] = (int)(ip - base_ip - 5);
@@ -354,7 +354,7 @@
           table[prev_hash] = (int)(ip - base_ip - 4);
           prev_hash = HashBytesAtOffset(input_bytes, 2, shift);
           table[prev_hash] = (int)(ip - base_ip - 3);
-          input_bytes = BROTLI_UNALIGNED_LOAD64(ip - 2);
+          input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 2);
           cur_hash = HashBytesAtOffset(input_bytes, 2, shift);
           prev_hash = HashBytesAtOffset(input_bytes, 0, shift);
           table[prev_hash] = (int)(ip - base_ip - 2);
@@ -386,7 +386,7 @@
           /* We could immediately start working at ip now, but to improve
              compression we first update "table" with the hashes of some
              positions within the last copy. */
-          uint64_t input_bytes = BROTLI_UNALIGNED_LOAD64(ip - 5);
+          uint64_t input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 5);
           uint32_t prev_hash = HashBytesAtOffset(input_bytes, 0, shift);
           uint32_t cur_hash;
           table[prev_hash] = (int)(ip - base_ip - 5);
@@ -394,7 +394,7 @@
           table[prev_hash] = (int)(ip - base_ip - 4);
           prev_hash = HashBytesAtOffset(input_bytes, 2, shift);
           table[prev_hash] = (int)(ip - base_ip - 3);
-          input_bytes = BROTLI_UNALIGNED_LOAD64(ip - 2);
+          input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 2);
           cur_hash = HashBytesAtOffset(input_bytes, 2, shift);
           prev_hash = HashBytesAtOffset(input_bytes, 0, shift);
           table[prev_hash] = (int)(ip - base_ip - 2);
diff --git a/third_party/brotli/enc/encode.c b/third_party/brotli/enc/encode.c
index d1115739..0695210 100644
--- a/third_party/brotli/enc/encode.c
+++ b/third_party/brotli/enc/encode.c
@@ -385,8 +385,7 @@
    context values, based on the entropy reduction of histograms over the
    first 5 bits of literals. */
 static BROTLI_BOOL ShouldUseComplexStaticContextMap(const uint8_t* input,
-    size_t start_pos, size_t length, size_t mask, int quality,
-    size_t size_hint, ContextType* literal_context_mode,
+    size_t start_pos, size_t length, size_t mask, int quality, size_t size_hint,
     size_t* num_literal_contexts, const uint32_t** literal_context_map) {
   static const uint32_t kStaticContextMapComplexUTF8[64] = {
     11, 11, 12, 12, /* 0 special */
@@ -457,7 +456,6 @@
     if (entropy[2] > 3.0 || entropy[1] - entropy[2] < 0.2) {
       return BROTLI_FALSE;
     } else {
-      *literal_context_mode = CONTEXT_UTF8;
       *num_literal_contexts = 13;
       *literal_context_map = kStaticContextMapComplexUTF8;
       return BROTLI_TRUE;
@@ -466,13 +464,12 @@
 }
 
 static void DecideOverLiteralContextModeling(const uint8_t* input,
-    size_t start_pos, size_t length, size_t mask, int quality,
-    size_t size_hint, ContextType* literal_context_mode,
+    size_t start_pos, size_t length, size_t mask, int quality, size_t size_hint,
     size_t* num_literal_contexts, const uint32_t** literal_context_map) {
   if (quality < MIN_QUALITY_FOR_CONTEXT_MODELING || length < 64) {
     return;
   } else if (ShouldUseComplexStaticContextMap(
-      input, start_pos, length, mask, quality, size_hint, literal_context_mode,
+      input, start_pos, length, mask, quality, size_hint,
       num_literal_contexts, literal_context_map)) {
     /* Context map was already set, nothing else to do. */
   } else {
@@ -492,7 +489,6 @@
         prev = lut[literal >> 6] * 3;
       }
     }
-    *literal_context_mode = CONTEXT_UTF8;
     ChooseContextMap(quality, &bigram_prefix_histo[0], num_literal_contexts,
                      literal_context_map);
   }
@@ -596,7 +592,7 @@
       if (!params->disable_literal_context_modeling) {
         DecideOverLiteralContextModeling(
             data, wrapped_last_flush_pos, bytes, mask, params->quality,
-            params->size_hint, &literal_context_mode, &num_literal_contexts,
+            params->size_hint, &num_literal_contexts,
             &literal_context_map);
       }
       BrotliBuildMetaBlockGreedy(m, data, wrapped_last_flush_pos, mask,
@@ -832,36 +828,6 @@
   }
 }
 
-void BrotliEncoderSetCustomDictionary(BrotliEncoderState* s, size_t size,
-                                      const uint8_t* dict) {
-  size_t max_dict_size = BROTLI_MAX_BACKWARD_LIMIT(s->params.lgwin);
-  size_t dict_size = size;
-  MemoryManager* m = &s->memory_manager_;
-
-  if (!EnsureInitialized(s)) return;
-
-  if (dict_size == 0 ||
-      s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY ||
-      s->params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY) {
-    return;
-  }
-  if (size > max_dict_size) {
-    dict += size - max_dict_size;
-    dict_size = max_dict_size;
-  }
-  CopyInputToRingBuffer(s, dict_size, dict);
-  s->last_flush_pos_ = dict_size;
-  s->last_processed_pos_ = dict_size;
-  if (dict_size > 0) {
-    s->prev_byte_ = dict[dict_size - 1];
-  }
-  if (dict_size > 1) {
-    s->prev_byte2_ = dict[dict_size - 2];
-  }
-  HasherPrependCustomDictionary(m, &s->hasher_, &s->params, dict_size, dict);
-  if (BROTLI_IS_OOM(m)) return;
-}
-
 /* Marks all input as processed.
    Returns true if position wrapping occurs. */
 static BROTLI_BOOL UpdateLastProcessedPos(BrotliEncoderState* s) {
@@ -1208,7 +1174,8 @@
       }
       BrotliZopfliCreateCommands(block_size, block_start, max_backward_limit,
                                  &nodes[0], dist_cache, &last_insert_len,
-                                 &commands[num_commands], &num_literals);
+                                 &params, &commands[num_commands],
+                                 &num_literals);
       num_commands += path_size;
       block_start += block_size;
       metablock_size += block_size;
diff --git a/third_party/brotli/enc/find_match_length.h b/third_party/brotli/enc/find_match_length.h
index b3e3d80..41845312 100644
--- a/third_party/brotli/enc/find_match_length.h
+++ b/third_party/brotli/enc/find_match_length.h
@@ -17,7 +17,7 @@
 #endif
 
 /* Separate implementation for little-endian 64-bit targets, for speed. */
-#if defined(__GNUC__) && defined(_LP64) && defined(IS_LITTLE_ENDIAN)
+#if defined(__GNUC__) && defined(_LP64) && defined(BROTLI_LITTLE_ENDIAN)
 
 static BROTLI_INLINE size_t FindMatchLengthWithLimit(const uint8_t* s1,
                                                      const uint8_t* s2,
@@ -25,13 +25,13 @@
   size_t matched = 0;
   size_t limit2 = (limit >> 3) + 1;  /* + 1 is for pre-decrement in while */
   while (BROTLI_PREDICT_TRUE(--limit2)) {
-    if (BROTLI_PREDICT_FALSE(BROTLI_UNALIGNED_LOAD64(s2) ==
-                      BROTLI_UNALIGNED_LOAD64(s1 + matched))) {
+    if (BROTLI_PREDICT_FALSE(BROTLI_UNALIGNED_LOAD64LE(s2) ==
+                      BROTLI_UNALIGNED_LOAD64LE(s1 + matched))) {
       s2 += 8;
       matched += 8;
     } else {
-      uint64_t x =
-          BROTLI_UNALIGNED_LOAD64(s2) ^ BROTLI_UNALIGNED_LOAD64(s1 + matched);
+      uint64_t x = BROTLI_UNALIGNED_LOAD64LE(s2) ^
+          BROTLI_UNALIGNED_LOAD64LE(s1 + matched);
       size_t matching_bits = (size_t)__builtin_ctzll(x);
       matched += matching_bits >> 3;
       return matched;
diff --git a/third_party/brotli/enc/hash.h b/third_party/brotli/enc/hash.h
index 4c94cda..c94edd33 100644
--- a/third_party/brotli/enc/hash.h
+++ b/third_party/brotli/enc/hash.h
@@ -62,9 +62,9 @@
 
 typedef struct HasherSearchResult {
   size_t len;
-  size_t len_x_code; /* == len ^ len_code */
   size_t distance;
   score_t score;
+  int len_code_delta; /* == len_code - len */
 } HasherSearchResult;
 
 /* kHashMul32 multiplier has these properties:
@@ -173,27 +173,29 @@
     backward = max_backward + dist + 1 +
         (transform_id << dictionary->size_bits_by_length[len]);
   }
+  if (backward >= BROTLI_MAX_DISTANCE) {
+    return BROTLI_FALSE;
+  }
   score = BackwardReferenceScore(matchlen, backward);
   if (score < out->score) {
     return BROTLI_FALSE;
   }
   out->len = matchlen;
-  out->len_x_code = len ^ matchlen;
+  out->len_code_delta = (int)len - (int)matchlen;
   out->distance = backward;
   out->score = score;
   return BROTLI_TRUE;
 }
 
-static BROTLI_INLINE BROTLI_BOOL SearchInStaticDictionary(
+static BROTLI_INLINE void SearchInStaticDictionary(
     const BrotliDictionary* dictionary, const uint16_t* dictionary_hash,
     HasherHandle handle, const uint8_t* data, size_t max_length,
     size_t max_backward, HasherSearchResult* out, BROTLI_BOOL shallow) {
   size_t key;
   size_t i;
-  BROTLI_BOOL is_match_found = BROTLI_FALSE;
   HasherCommon* self = GetHasherCommon(handle);
   if (self->dict_num_matches < (self->dict_num_lookups >> 7)) {
-    return BROTLI_FALSE;
+    return;
   }
   key = Hash14(data) << 1;
   for (i = 0; i < (shallow ? 1u : 2u); ++i, ++key) {
@@ -204,11 +206,9 @@
           dictionary, item, data, max_length, max_backward, out);
       if (item_matches) {
         self->dict_num_matches++;
-        is_match_found = BROTLI_TRUE;
       }
     }
   }
-  return is_match_found;
 }
 
 typedef struct BackwardMatch {
@@ -420,30 +420,6 @@
   }
 }
 
-/* Custom LZ77 window. */
-static BROTLI_INLINE void HasherPrependCustomDictionary(
-    MemoryManager* m, HasherHandle* handle, BrotliEncoderParams* params,
-    const size_t size, const uint8_t* dict) {
-  size_t overlap;
-  size_t i;
-  HasherHandle self;
-  HasherSetup(m, handle, params, dict, 0, size, BROTLI_FALSE);
-  if (BROTLI_IS_OOM(m)) return;
-  self = *handle;
-  switch (GetHasherCommon(self)->params.type) {
-#define PREPEND_(N)                             \
-    case N:                                     \
-      overlap = (StoreLookaheadH ## N()) - 1;   \
-      for (i = 0; i + overlap < size; i++) {    \
-        StoreH ## N(self, dict, ~(size_t)0, i); \
-      }                                         \
-      break;
-    FOR_ALL_HASHERS(PREPEND_)
-#undef PREPEND_
-    default: break;
-  }
-}
-
 static BROTLI_INLINE void InitOrStitchToPreviousBlock(
     MemoryManager* m, HasherHandle* handle, const uint8_t* data, size_t mask,
     BrotliEncoderParams* params, size_t position, size_t input_size,
diff --git a/third_party/brotli/enc/hash_forgetful_chain_inc.h b/third_party/brotli/enc/hash_forgetful_chain_inc.h
index a49fa6d..8f9ee73 100644
--- a/third_party/brotli/enc/hash_forgetful_chain_inc.h
+++ b/third_party/brotli/enc/hash_forgetful_chain_inc.h
@@ -152,24 +152,24 @@
    Does not look for matches longer than max_length.
    Does not look for matches further away than max_backward.
    Writes the best match into |out|.
-   Returns 1 when match is found, otherwise 0. */
-static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HasherHandle handle,
+   |out|->score is updated only if a better match is found. */
+static BROTLI_INLINE void FN(FindLongestMatch)(HasherHandle handle,
     const BrotliDictionary* dictionary, const uint16_t* dictionary_hash,
     const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask,
     const int* BROTLI_RESTRICT distance_cache,
     const size_t cur_ix, const size_t max_length, const size_t max_backward,
-    HasherSearchResult* BROTLI_RESTRICT out) {
+    const size_t gap, HasherSearchResult* BROTLI_RESTRICT out) {
   HashForgetfulChain* self = FN(Self)(handle);
   const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
-  BROTLI_BOOL is_match_found = BROTLI_FALSE;
   /* Don't accept a short copy from far away. */
+  score_t min_score = out->score;
   score_t best_score = out->score;
   size_t best_len = out->len;
   size_t i;
   const size_t key = FN(HashBytes)(&data[cur_ix_masked]);
   const uint8_t tiny_hash = (uint8_t)(key);
   out->len = 0;
-  out->len_x_code = 0;
+  out->len_code_delta = 0;
   /* Try last distance first. */
   for (i = 0; i < NUM_LAST_DISTANCES_TO_CHECK; ++i) {
     const size_t backward = (size_t)distance_cache[i];
@@ -194,7 +194,6 @@
             out->len = best_len;
             out->distance = backward;
             out->score = best_score;
-            is_match_found = BROTLI_TRUE;
           }
         }
       }
@@ -234,19 +233,17 @@
             out->len = best_len;
             out->distance = backward;
             out->score = best_score;
-            is_match_found = BROTLI_TRUE;
           }
         }
       }
     }
     FN(Store)(handle, data, ring_buffer_mask, cur_ix);
   }
-  if (!is_match_found) {
-    is_match_found = SearchInStaticDictionary(dictionary, dictionary_hash,
-        handle, &data[cur_ix_masked], max_length, max_backward, out,
+  if (out->score == min_score) {
+    SearchInStaticDictionary(dictionary, dictionary_hash,
+        handle, &data[cur_ix_masked], max_length, max_backward + gap, out,
         BROTLI_FALSE);
   }
-  return is_match_found;
 }
 
 #undef BANK_SIZE
diff --git a/third_party/brotli/enc/hash_longest_match64_inc.h b/third_party/brotli/enc/hash_longest_match64_inc.h
index 7d4199f..6b0697b2 100644
--- a/third_party/brotli/enc/hash_longest_match64_inc.h
+++ b/third_party/brotli/enc/hash_longest_match64_inc.h
@@ -23,7 +23,7 @@
 static BROTLI_INLINE uint32_t FN(HashBytes)(const uint8_t *data,
                                             const uint64_t mask,
                                             const int shift) {
-  const uint64_t h = (BROTLI_UNALIGNED_LOAD64(data) & mask) * kHashMul64Long;
+  const uint64_t h = (BROTLI_UNALIGNED_LOAD64LE(data) & mask) * kHashMul64Long;
   /* The higher bits contain more mixture from the multiplication,
      so we take our results from there. */
   return (uint32_t)(h >> shift);
@@ -156,25 +156,25 @@
    Does not look for matches longer than max_length.
    Does not look for matches further away than max_backward.
    Writes the best match into |out|.
-   Returns true when match is found, otherwise false. */
-static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HasherHandle handle,
+   |out|->score is updated only if a better match is found. */
+static BROTLI_INLINE void FN(FindLongestMatch)(HasherHandle handle,
     const BrotliDictionary* dictionary, const uint16_t* dictionary_hash,
     const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask,
     const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix,
-    const size_t max_length, const size_t max_backward,
+    const size_t max_length, const size_t max_backward, const size_t gap,
     HasherSearchResult* BROTLI_RESTRICT out) {
   HasherCommon* common = GetHasherCommon(handle);
   HashLongestMatch* self = FN(Self)(handle);
   uint16_t* num = FN(Num)(self);
   uint32_t* buckets = FN(Buckets)(self);
   const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
-  BROTLI_BOOL is_match_found = BROTLI_FALSE;
   /* Don't accept a short copy from far away. */
+  score_t min_score = out->score;
   score_t best_score = out->score;
   size_t best_len = out->len;
   size_t i;
   out->len = 0;
-  out->len_x_code = 0;
+  out->len_code_delta = 0;
   /* Try last distance first. */
   for (i = 0; i < (size_t)common->params.num_last_distances_to_check; ++i) {
     const size_t backward = (size_t)distance_cache[i];
@@ -209,7 +209,6 @@
             out->len = best_len;
             out->distance = backward;
             out->score = best_score;
-            is_match_found = BROTLI_TRUE;
           }
         }
       }
@@ -250,7 +249,6 @@
             out->len = best_len;
             out->distance = backward;
             out->score = best_score;
-            is_match_found = BROTLI_TRUE;
           }
         }
       }
@@ -258,12 +256,11 @@
     bucket[num[key] & self->block_mask_] = (uint32_t)cur_ix;
     ++num[key];
   }
-  if (!is_match_found) {
-    is_match_found = SearchInStaticDictionary(dictionary, dictionary_hash,
-        handle, &data[cur_ix_masked], max_length, max_backward, out,
+  if (min_score == out->score) {
+    SearchInStaticDictionary(dictionary, dictionary_hash,
+        handle, &data[cur_ix_masked], max_length, max_backward + gap, out,
         BROTLI_FALSE);
   }
-  return is_match_found;
 }
 
 #undef HashLongestMatch
diff --git a/third_party/brotli/enc/hash_longest_match_inc.h b/third_party/brotli/enc/hash_longest_match_inc.h
index 6913c73..dc5335f 100644
--- a/third_party/brotli/enc/hash_longest_match_inc.h
+++ b/third_party/brotli/enc/hash_longest_match_inc.h
@@ -149,25 +149,25 @@
    Does not look for matches longer than max_length.
    Does not look for matches further away than max_backward.
    Writes the best match into |out|.
-   Returns true when match is found, otherwise false. */
-static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HasherHandle handle,
+   |out|->score is updated only if a better match is found. */
+static BROTLI_INLINE void FN(FindLongestMatch)(HasherHandle handle,
     const BrotliDictionary* dictionary, const uint16_t* dictionary_hash,
     const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask,
     const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix,
-    const size_t max_length, const size_t max_backward,
+    const size_t max_length, const size_t max_backward, const size_t gap,
     HasherSearchResult* BROTLI_RESTRICT out) {
   HasherCommon* common = GetHasherCommon(handle);
   HashLongestMatch* self = FN(Self)(handle);
   uint16_t* num = FN(Num)(self);
   uint32_t* buckets = FN(Buckets)(self);
   const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
-  BROTLI_BOOL is_match_found = BROTLI_FALSE;
   /* Don't accept a short copy from far away. */
+  score_t min_score = out->score;
   score_t best_score = out->score;
   size_t best_len = out->len;
   size_t i;
   out->len = 0;
-  out->len_x_code = 0;
+  out->len_code_delta = 0;
   /* Try last distance first. */
   for (i = 0; i < (size_t)common->params.num_last_distances_to_check; ++i) {
     const size_t backward = (size_t)distance_cache[i];
@@ -202,7 +202,6 @@
             out->len = best_len;
             out->distance = backward;
             out->score = best_score;
-            is_match_found = BROTLI_TRUE;
           }
         }
       }
@@ -242,7 +241,6 @@
             out->len = best_len;
             out->distance = backward;
             out->score = best_score;
-            is_match_found = BROTLI_TRUE;
           }
         }
       }
@@ -250,12 +248,11 @@
     bucket[num[key] & self->block_mask_] = (uint32_t)cur_ix;
     ++num[key];
   }
-  if (!is_match_found) {
-    is_match_found = SearchInStaticDictionary(dictionary, dictionary_hash,
-        handle, &data[cur_ix_masked], max_length, max_backward, out,
+  if (min_score == out->score) {
+    SearchInStaticDictionary(dictionary, dictionary_hash,
+        handle, &data[cur_ix_masked], max_length, max_backward + gap, out,
         BROTLI_FALSE);
   }
-  return is_match_found;
 }
 
 #undef HashLongestMatch
diff --git a/third_party/brotli/enc/hash_longest_match_quickly_inc.h b/third_party/brotli/enc/hash_longest_match_quickly_inc.h
index ec1553e..d29567a 100644
--- a/third_party/brotli/enc/hash_longest_match_quickly_inc.h
+++ b/third_party/brotli/enc/hash_longest_match_quickly_inc.h
@@ -22,7 +22,7 @@
    the address in. The HashLongestMatch and HashLongestMatchQuickly
    classes have separate, different implementations of hashing. */
 static uint32_t FN(HashBytes)(const uint8_t* data) {
-  const uint64_t h = ((BROTLI_UNALIGNED_LOAD64(data) << (64 - 8 * HASH_LEN)) *
+  const uint64_t h = ((BROTLI_UNALIGNED_LOAD64LE(data) << (64 - 8 * HASH_LEN)) *
                       kHashMul64);
   /* The higher bits contain more mixture from the multiplication,
      so we take our results from there. */
@@ -123,24 +123,24 @@
    Does not look for matches longer than max_length.
    Does not look for matches further away than max_backward.
    Writes the best match into |out|.
-   Returns true if match is found, otherwise false. */
-static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(
+   |out|->score is updated only if a better match is found. */
+static BROTLI_INLINE void FN(FindLongestMatch)(
     HasherHandle handle, const BrotliDictionary* dictionary,
     const uint16_t* dictionary_hash, const uint8_t* BROTLI_RESTRICT data,
     const size_t ring_buffer_mask, const int* BROTLI_RESTRICT distance_cache,
     const size_t cur_ix, const size_t max_length, const size_t max_backward,
-    HasherSearchResult* BROTLI_RESTRICT out) {
+    const size_t gap, HasherSearchResult* BROTLI_RESTRICT out) {
   HashLongestMatchQuickly* self = FN(Self)(handle);
   const size_t best_len_in = out->len;
   const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
   const uint32_t key = FN(HashBytes)(&data[cur_ix_masked]);
   int compare_char = data[cur_ix_masked + best_len_in];
+  score_t min_score = out->score;
   score_t best_score = out->score;
   size_t best_len = best_len_in;
   size_t cached_backward = (size_t)distance_cache[0];
   size_t prev_ix = cur_ix - cached_backward;
-  BROTLI_BOOL is_match_found = BROTLI_FALSE;
-  out->len_x_code = 0;
+  out->len_code_delta = 0;
   if (prev_ix < cur_ix) {
     prev_ix &= (uint32_t)ring_buffer_mask;
     if (compare_char == data[prev_ix + best_len]) {
@@ -148,17 +148,18 @@
                                             &data[cur_ix_masked],
                                             max_length);
       if (len >= 4) {
-        best_score = BackwardReferenceScoreUsingLastDistance(len);
-        best_len = len;
-        out->len = len;
-        out->distance = cached_backward;
-        out->score = best_score;
-        compare_char = data[cur_ix_masked + best_len];
-        if (BUCKET_SWEEP == 1) {
-          self->buckets_[key] = (uint32_t)cur_ix;
-          return BROTLI_TRUE;
-        } else {
-          is_match_found = BROTLI_TRUE;
+        const score_t score = BackwardReferenceScoreUsingLastDistance(len);
+        if (best_score < score) {
+          best_score = score;
+          best_len = len;
+          out->len = len;
+          out->distance = cached_backward;
+          out->score = best_score;
+          compare_char = data[cur_ix_masked + best_len];
+          if (BUCKET_SWEEP == 1) {
+            self->buckets_[key] = (uint32_t)cur_ix;
+            return;
+          }
         }
       }
     }
@@ -172,19 +173,22 @@
     backward = cur_ix - prev_ix;
     prev_ix &= (uint32_t)ring_buffer_mask;
     if (compare_char != data[prev_ix + best_len_in]) {
-      return BROTLI_FALSE;
+      return;
     }
     if (BROTLI_PREDICT_FALSE(backward == 0 || backward > max_backward)) {
-      return BROTLI_FALSE;
+      return;
     }
     len = FindMatchLengthWithLimit(&data[prev_ix],
                                    &data[cur_ix_masked],
                                    max_length);
     if (len >= 4) {
-      out->len = len;
-      out->distance = backward;
-      out->score = BackwardReferenceScore(len, backward);
-      return BROTLI_TRUE;
+      const score_t score = BackwardReferenceScore(len, backward);
+      if (best_score < score) {
+        out->len = len;
+        out->distance = backward;
+        out->score = score;
+        return;
+      }
     }
   } else {
     uint32_t *bucket = self->buckets_ + key;
@@ -212,18 +216,17 @@
           out->distance = backward;
           out->score = score;
           compare_char = data[cur_ix_masked + best_len];
-          is_match_found = BROTLI_TRUE;
         }
       }
     }
   }
-  if (USE_DICTIONARY && !is_match_found) {
-    is_match_found = SearchInStaticDictionary(dictionary, dictionary_hash,
-        handle, &data[cur_ix_masked], max_length, max_backward, out,
+  BROTLI_UNUSED(min_score);  /* Calm down MSVC. */
+  if (USE_DICTIONARY && min_score == out->score) {
+    SearchInStaticDictionary(dictionary, dictionary_hash,
+        handle, &data[cur_ix_masked], max_length, max_backward + gap, out,
         BROTLI_TRUE);
   }
   self->buckets_[key + ((cur_ix >> 3) % BUCKET_SWEEP)] = (uint32_t)cur_ix;
-  return is_match_found;
 }
 
 #undef HASH_MAP_SIZE
diff --git a/third_party/brotli/enc/hash_to_binary_tree_inc.h b/third_party/brotli/enc/hash_to_binary_tree_inc.h
index 0b2554c..30c71b52 100644
--- a/third_party/brotli/enc/hash_to_binary_tree_inc.h
+++ b/third_party/brotli/enc/hash_to_binary_tree_inc.h
@@ -19,8 +19,10 @@
 
 #define BUCKET_SIZE (1 << BUCKET_BITS)
 
-static size_t FN(HashTypeLength)(void) { return 4; }
-static size_t FN(StoreLookahead)(void) { return MAX_TREE_COMP_LENGTH; }
+static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 4; }
+static BROTLI_INLINE size_t FN(StoreLookahead)(void) {
+  return MAX_TREE_COMP_LENGTH;
+}
 
 static uint32_t FN(HashBytes)(const uint8_t *data) {
   uint32_t h = BROTLI_UNALIGNED_LOAD32(data) * kHashMul32;
@@ -199,7 +201,7 @@
 static BROTLI_INLINE size_t FN(FindAllMatches)(HasherHandle handle,
     const BrotliDictionary* dictionary, const uint8_t* data,
     const size_t ring_buffer_mask, const size_t cur_ix,
-    const size_t max_length, const size_t max_backward,
+    const size_t max_length, const size_t max_backward, const size_t gap,
     const BrotliEncoderParams* params, BackwardMatch* matches) {
   BackwardMatch* const orig_matches = matches;
   const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
@@ -248,8 +250,10 @@
       for (l = minlen; l <= maxlen; ++l) {
         uint32_t dict_id = dict_matches[l];
         if (dict_id < kInvalidMatch) {
-          InitDictionaryBackwardMatch(matches++,
-              max_backward + (dict_id >> 5) + 1, l, dict_id & 31);
+          size_t distance = max_backward + gap + (dict_id >> 5) + 1;
+          if (distance < BROTLI_MAX_DISTANCE) {
+            InitDictionaryBackwardMatch(matches++, distance, l, dict_id & 31);
+          }
         }
       }
     }
diff --git a/third_party/brotli/enc/memory.h b/third_party/brotli/enc/memory.h
index f68c0355..babf1f8 100644
--- a/third_party/brotli/enc/memory.h
+++ b/third_party/brotli/enc/memory.h
@@ -40,7 +40,7 @@
 
 BROTLI_INTERNAL void* BrotliAllocate(MemoryManager* m, size_t n);
 #define BROTLI_ALLOC(M, T, N)                               \
-  ((N) ? ((T*)BrotliAllocate((M), (N) * sizeof(T))) : NULL)
+  ((N) > 0 ? ((T*)BrotliAllocate((M), (N) * sizeof(T))) : NULL)
 
 BROTLI_INTERNAL void BrotliFree(MemoryManager* m, void* p);
 #define BROTLI_FREE(M, P) { \
diff --git a/third_party/brotli/enc/port.h b/third_party/brotli/enc/port.h
index 0d5f24c..0e5f0e7 100644
--- a/third_party/brotli/enc/port.h
+++ b/third_party/brotli/enc/port.h
@@ -26,36 +26,37 @@
 #define __LITTLE_ENDIAN LITTLE_ENDIAN
 #endif
 
-/* define the macro IS_LITTLE_ENDIAN
+/* define the macro BROTLI_LITTLE_ENDIAN
    using the above endian definitions from endian.h if
    endian.h was included */
 #ifdef __BYTE_ORDER
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-#define IS_LITTLE_ENDIAN
+#define BROTLI_LITTLE_ENDIAN
 #endif
 
 #else
 
 #if defined(__LITTLE_ENDIAN__)
-#define IS_LITTLE_ENDIAN
+#define BROTLI_LITTLE_ENDIAN
 #endif
 #endif  /* __BYTE_ORDER */
 
 #if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
-#define IS_LITTLE_ENDIAN
+#define BROTLI_LITTLE_ENDIAN
 #endif
 
 /* Enable little-endian optimization for x64 architecture on Windows. */
 #if (defined(_WIN32) || defined(_WIN64)) && defined(_M_X64)
-#define IS_LITTLE_ENDIAN
+#define BROTLI_LITTLE_ENDIAN
 #endif
 
 /* Portable handling of unaligned loads, stores, and copies.
    On some platforms, like ARM, the copy functions can be more efficient
    then a load and a store. */
 
-#if defined(ARCH_PIII) || \
-  defined(ARCH_ATHLON) || defined(ARCH_K8) || defined(_ARCH_PPC)
+#if defined(BROTLI_LITTLE_ENDIAN) && (\
+    defined(ARCH_PIII) || defined(ARCH_ATHLON) || \
+    defined(ARCH_K8) || defined(_ARCH_PPC))
 
 /* x86 and x86-64 can perform unaligned loads/stores directly;
    modern PowerPC hardware can also do unaligned integer loads and stores;
@@ -63,14 +64,12 @@
 */
 
 #define BROTLI_UNALIGNED_LOAD32(_p) (*(const uint32_t *)(_p))
-#define BROTLI_UNALIGNED_LOAD64(_p) (*(const uint64_t *)(_p))
+#define BROTLI_UNALIGNED_LOAD64LE(_p) (*(const uint64_t *)(_p))
 
-#define BROTLI_UNALIGNED_STORE32(_p, _val) \
-  (*(uint32_t *)(_p) = (_val))
-#define BROTLI_UNALIGNED_STORE64(_p, _val) \
+#define BROTLI_UNALIGNED_STORE64LE(_p, _val) \
   (*(uint64_t *)(_p) = (_val))
 
-#elif defined(__arm__) && \
+#elif defined(BROTLI_LITTLE_ENDIAN) && defined(__arm__) && \
   !defined(__ARM_ARCH_5__) && \
   !defined(__ARM_ARCH_5T__) && \
   !defined(__ARM_ARCH_5TE__) && \
@@ -88,16 +87,14 @@
    slowly (trip through kernel mode). */
 
 #define BROTLI_UNALIGNED_LOAD32(_p) (*(const uint32_t *)(_p))
-#define BROTLI_UNALIGNED_STORE32(_p, _val) \
-  (*(uint32_t *)(_p) = (_val))
 
-static BROTLI_INLINE uint64_t BROTLI_UNALIGNED_LOAD64(const void *p) {
+static BROTLI_INLINE uint64_t BROTLI_UNALIGNED_LOAD64LE(const void *p) {
   uint64_t t;
   memcpy(&t, p, sizeof t);
   return t;
 }
 
-static BROTLI_INLINE void BROTLI_UNALIGNED_STORE64(void *p, uint64_t v) {
+static BROTLI_INLINE void BROTLI_UNALIGNED_STORE64LE(void *p, uint64_t v) {
   memcpy(p, &v, sizeof v);
 }
 
@@ -112,20 +109,47 @@
   return t;
 }
 
-static BROTLI_INLINE uint64_t BROTLI_UNALIGNED_LOAD64(const void *p) {
+#if defined(BROTLI_LITTLE_ENDIAN)
+
+static BROTLI_INLINE uint64_t BROTLI_UNALIGNED_LOAD64LE(const void *p) {
   uint64_t t;
   memcpy(&t, p, sizeof t);
   return t;
 }
 
-static BROTLI_INLINE void BROTLI_UNALIGNED_STORE32(void *p, uint32_t v) {
+static BROTLI_INLINE void BROTLI_UNALIGNED_STORE64LE(void *p, uint64_t v) {
   memcpy(p, &v, sizeof v);
 }
 
-static BROTLI_INLINE void BROTLI_UNALIGNED_STORE64(void *p, uint64_t v) {
-  memcpy(p, &v, sizeof v);
+#else  /* BROTLI_LITTLE_ENDIAN */
+
+static BROTLI_INLINE uint64_t BROTLI_UNALIGNED_LOAD64LE(const void *p) {
+  const uint8_t* in = (const uint8_t*)p;
+  uint64_t value = (uint64_t)(in[0]);
+  value |= (uint64_t)(in[1]) << 8;
+  value |= (uint64_t)(in[2]) << 16;
+  value |= (uint64_t)(in[3]) << 24;
+  value |= (uint64_t)(in[4]) << 32;
+  value |= (uint64_t)(in[5]) << 40;
+  value |= (uint64_t)(in[6]) << 48;
+  value |= (uint64_t)(in[7]) << 56;
+  return value;
 }
 
+static BROTLI_INLINE void BROTLI_UNALIGNED_STORE64LE(void *p, uint64_t v) {
+  uint8_t* out = (uint8_t*)p;
+  out[0] = (uint8_t)v;
+  out[1] = (uint8_t)(v >> 8);
+  out[2] = (uint8_t)(v >> 16);
+  out[3] = (uint8_t)(v >> 24);
+  out[4] = (uint8_t)(v >> 32);
+  out[5] = (uint8_t)(v >> 40);
+  out[6] = (uint8_t)(v >> 48);
+  out[7] = (uint8_t)(v >> 56);
+}
+
+#endif  /* BROTLI_LITTLE_ENDIAN */
+
 #endif
 
 #define TEMPLATE_(T)                                                           \
diff --git a/third_party/brotli/enc/write_bits.h b/third_party/brotli/enc/write_bits.h
index 3999c917..83fdddc 100644
--- a/third_party/brotli/enc/write_bits.h
+++ b/third_party/brotli/enc/write_bits.h
@@ -40,7 +40,7 @@
                                           uint64_t bits,
                                           size_t * BROTLI_RESTRICT pos,
                                           uint8_t * BROTLI_RESTRICT array) {
-#ifdef IS_LITTLE_ENDIAN
+#ifdef BROTLI_LITTLE_ENDIAN
   /* This branch of the code can write up to 56 bits at a time,
      7 bits are lost by being perhaps already in *p and at least
      1 bit is needed to initialize the bit-stream ahead (i.e. if 7
@@ -54,7 +54,7 @@
   assert((bits >> n_bits) == 0);
   assert(n_bits <= 56);
   v |= bits << (*pos & 7);
-  BROTLI_UNALIGNED_STORE64(p, v);  /* Set some bits. */
+  BROTLI_UNALIGNED_STORE64LE(p, v);  /* Set some bits. */
   *pos += n_bits;
 #else
   /* implicit & 0xff is assumed for uint8_t arithmetics */
diff --git a/third_party/brotli/include/brotli/decode.h b/third_party/brotli/include/brotli/decode.h
index 93cbe38..d1d21c42d 100644
--- a/third_party/brotli/include/brotli/decode.h
+++ b/third_party/brotli/include/brotli/decode.h
@@ -84,8 +84,9 @@
   BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_1, -14) SEPARATOR              \
   BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_2, -15) SEPARATOR              \
                                                                            \
-  /* -16..-18 codes are reserved */                                        \
+  /* -16..-17 codes are reserved */                                        \
                                                                            \
+  BROTLI_ERROR_CODE(_ERROR_, COMPOUND_DICTIONARY, -18) SEPARATOR           \
   BROTLI_ERROR_CODE(_ERROR_, DICTIONARY_NOT_SET, -19) SEPARATOR            \
   BROTLI_ERROR_CODE(_ERROR_, INVALID_ARGUMENTS, -20) SEPARATOR             \
                                                                            \
@@ -126,6 +127,29 @@
  */
 #define BROTLI_LAST_ERROR_CODE BROTLI_DECODER_ERROR_UNREACHABLE
 
+/** Options to be used with ::BrotliDecoderSetParameter. */
+typedef enum BrotliDecoderParameter {
+  /**
+   * Disable "canny" ring buffer allocation strategy.
+   *
+   * Ring buffer is allocated according to window size, despite the real size of
+   * the content.
+   */
+  BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION = 0
+} BrotliDecoderParameter;
+
+/**
+ * Sets the specified parameter to the given decoder instance.
+ *
+ * @param state decoder instance
+ * @param param parameter to set
+ * @param value new parameter value
+ * @returns ::BROTLI_FALSE if parameter is unrecognized, or value is invalid
+ * @returns ::BROTLI_TRUE if value is accepted
+ */
+BROTLI_DEC_API BROTLI_BOOL BrotliDecoderSetParameter(
+    BrotliDecoderState* state, BrotliDecoderParameter param, uint32_t value);
+
 /**
  * Creates an instance of ::BrotliDecoderState and initializes it.
  *
@@ -219,31 +243,6 @@
   size_t* available_out, uint8_t** next_out, size_t* total_out);
 
 /**
- * Prepends LZ77 dictionary.
- *
- * Fills the fresh ::BrotliDecoderState with additional data corpus for LZ77
- * backward references.
- *
- * @note Not to be confused with the static dictionary (see RFC7932 section 8).
- * @warning The dictionary must exist in memory until decoding is done and
- *          is owned by the caller.
- *
- * Workflow:
- *  -# Allocate and initialize state with ::BrotliDecoderCreateInstance
- *  -# Invoke ::BrotliDecoderSetCustomDictionary
- *  -# Use ::BrotliDecoderDecompressStream
- *  -# Clean up and free state with ::BrotliDecoderDestroyInstance
- *
- * @param state decoder instance
- * @param size length of @p dict; should be less or equal to 2^24 (16MiB),
- *        otherwise the dictionary will be ignored
- * @param dict "dictionary"; @b MUST be the same as used during compression
- */
-BROTLI_DEC_API void BrotliDecoderSetCustomDictionary(
-    BrotliDecoderState* state, size_t size,
-    const uint8_t dict[BROTLI_ARRAY_PARAM(size)]);
-
-/**
  * Checks if decoder has more output.
  *
  * @param state decoder instance
@@ -304,7 +303,8 @@
  *          the input and produced all of the output
  * @returns ::BROTLI_FALSE otherwise
  */
-BROTLI_BOOL BrotliDecoderIsFinished(const BrotliDecoderState* state);
+BROTLI_DEC_API BROTLI_BOOL BrotliDecoderIsFinished(
+    const BrotliDecoderState* state);
 
 /**
  * Acquires a detailed error code.
@@ -317,7 +317,7 @@
  * @param state decoder instance
  * @returns last saved error code
  */
-BrotliDecoderErrorCode BrotliDecoderGetErrorCode(
+BROTLI_DEC_API BrotliDecoderErrorCode BrotliDecoderGetErrorCode(
     const BrotliDecoderState* state);
 
 /**
diff --git a/third_party/brotli/include/brotli/encode.h b/third_party/brotli/include/brotli/encode.h
index 98a5ff8..e4cf18b7 100644
--- a/third_party/brotli/include/brotli/encode.h
+++ b/third_party/brotli/include/brotli/encode.h
@@ -72,7 +72,9 @@
    * Actual flush is performed when input stream is depleted and there is enough
    * space in output stream. This means that client should repeat
    * ::BROTLI_OPERATION_FLUSH operation until @p available_in becomes @c 0, and
-   * ::BrotliEncoderHasMoreOutput returns ::BROTLI_FALSE.
+   * ::BrotliEncoderHasMoreOutput returns ::BROTLI_FALSE. If output is acquired
+   * via ::BrotliEncoderTakeOutput, then operation should be repeated after
+   * output buffer is drained.
    *
    * @warning Until flush is complete, client @b SHOULD @b NOT swap,
    *          reduce or extend input stream.
@@ -86,8 +88,10 @@
    *
    * Actual finalization is performed when input stream is depleted and there is
    * enough space in output stream. This means that client should repeat
-   * ::BROTLI_OPERATION_FLUSH operation until @p available_in becomes @c 0, and
-   * ::BrotliEncoderHasMoreOutput returns ::BROTLI_FALSE.
+   * ::BROTLI_OPERATION_FINISH operation until @p available_in becomes @c 0, and
+   * ::BrotliEncoderHasMoreOutput returns ::BROTLI_FALSE. If output is acquired
+   * via ::BrotliEncoderTakeOutput, then operation should be repeated after
+   * output buffer is drained.
    *
    * @warning Until finalization is complete, client @b SHOULD @b NOT swap,
    *          reduce or extend input stream.
@@ -224,29 +228,6 @@
 BROTLI_ENC_API void BrotliEncoderDestroyInstance(BrotliEncoderState* state);
 
 /**
- * Prepends imaginary LZ77 dictionary.
- *
- * Fills the fresh ::BrotliEncoderState with additional data corpus for LZ77
- * backward references.
- *
- * @note Not to be confused with the static dictionary (see RFC7932 section 8).
- *
- * Workflow:
- *  -# Allocate and initialize state with ::BrotliEncoderCreateInstance
- *  -# Set ::BROTLI_PARAM_LGWIN parameter
- *  -# Invoke ::BrotliEncoderSetCustomDictionary
- *  -# Use ::BrotliEncoderCompressStream
- *  -# Clean up and free state with ::BrotliEncoderDestroyInstance
- *
- * @param state encoder instance
- * @param size length of @p dict; at most "window size" bytes are used
- * @param dict "dictionary"; @b MUST use same dictionary during decompression
- */
-BROTLI_ENC_API void BrotliEncoderSetCustomDictionary(
-    BrotliEncoderState* state, size_t size,
-    const uint8_t dict[BROTLI_ARRAY_PARAM(size)]);
-
-/**
  * Calculates the output size bound for the given @p input_size.
  *
  * @warning Result is not applicable to ::BrotliEncoderCompressStream output,
diff --git a/third_party/brotli/tools/brotli.c b/third_party/brotli/tools/brotli.c
index 418f90a..497ae65 100644
--- a/third_party/brotli/tools/brotli.c
+++ b/third_party/brotli/tools/brotli.c
@@ -15,6 +15,7 @@
 #include <sys/types.h>
 #include <time.h>
 
+#include "../common/constants.h"
 #include "../common/version.h"
 #include <brotli/decode.h>
 #include <brotli/encode.h>
@@ -22,6 +23,7 @@
 #if !defined(_WIN32)
 #include <unistd.h>
 #include <utime.h>
+#define MAKE_BINARY(FILENO) (FILENO)
 #else
 #include <io.h>
 #include <share.h>
@@ -30,13 +32,14 @@
 #define MAKE_BINARY(FILENO) (_setmode((FILENO), _O_BINARY), (FILENO))
 
 #if !defined(__MINGW32__)
-#define STDIN_FILENO MAKE_BINARY(_fileno(stdin))
-#define STDOUT_FILENO MAKE_BINARY(_fileno(stdout))
+#define STDIN_FILENO _fileno(stdin)
+#define STDOUT_FILENO _fileno(stdout)
 #define S_IRUSR S_IREAD
 #define S_IWUSR S_IWRITE
 #endif
 
 #define fdopen _fdopen
+#define isatty _isatty
 #define unlink _unlink
 #define utimbuf _utimbuf
 #define utime _utime
@@ -91,7 +94,6 @@
   BROTLI_BOOL test_integrity;
   BROTLI_BOOL decompress;
   const char* output_path;
-  const char* dictionary_path;
   const char* suffix;
   int not_input_indices[MAX_OPTIONS];
   size_t longest_path_len;
@@ -100,8 +102,6 @@
   /* Inner state */
   int argc;
   char** argv;
-  uint8_t* dictionary;
-  size_t dictionary_size;
   char* modified_path;  /* Storage for path with appended / cut suffix */
   int iterator;
   int ignore;
@@ -149,7 +149,7 @@
   size_t unbrotli_len = strlen(unbrotli);
   name = FileName(name);
   /* Partial comparison. On Windows there could be ".exe" suffix. */
-  if (strncmp(name, unbrotli, unbrotli_len)) {
+  if (strncmp(name, unbrotli, unbrotli_len) == 0) {
     char terminator = name[unbrotli_len];
     if (terminator == 0 || terminator == '.') return COMMAND_DECOMPRESS;
   }
@@ -174,11 +174,11 @@
 
   for (i = 1; i < argc; ++i) {
     const char* arg = argv[i];
-    size_t arg_len = strlen(arg);
-
     /* C99 5.1.2.2.1: "members argv[0] through argv[argc-1] inclusive shall
        contain pointers to strings"; NULL and 0-length are not forbidden. */
-    if (!arg || arg_len == 0) {
+    size_t arg_len = arg ? strlen(arg) : 0;
+
+    if (arg_len == 0) {
       params->not_input_indices[next_option_index++] = i;
       continue;
     }
@@ -285,9 +285,6 @@
           if (params->lgwin != 0 && params->lgwin < BROTLI_MIN_WINDOW_BITS) {
             return COMMAND_INVALID;
           }
-        } else if (c == 'D') {
-          if (params->dictionary_path) return COMMAND_INVALID;
-          params->dictionary_path = argv[i];
         } else if (c == 'S') {
           if (suffix_set) return COMMAND_INVALID;
           suffix_set = BROTLI_TRUE;
@@ -342,10 +339,7 @@
         if (!value || value[1] == 0) return COMMAND_INVALID;
         key_len = (size_t)(value - arg);
         value++;
-        if (strncmp("dictionary", arg, key_len) == 0) {
-          if (params->dictionary_path) return COMMAND_INVALID;
-          params->dictionary_path = value;
-        } else if (strncmp("lgwin", arg, key_len) == 0) {
+        if (strncmp("lgwin", arg, key_len) == 0) {
           if (lgwin_set) return COMMAND_INVALID;
           lgwin_set = ParseInt(value, 0,
                                BROTLI_MAX_WINDOW_BITS, &params->lgwin);
@@ -393,50 +387,47 @@
   int major = BROTLI_VERSION >> 24;
   int minor = (BROTLI_VERSION >> 12) & 0xFFF;
   int patch = BROTLI_VERSION & 0xFFF;
-  fprintf(stdout, "\
-brotli %d.%d.%d\n",
-          major, minor, patch);
+  fprintf(stdout, "brotli %d.%d.%d\n", major, minor, patch);
 }
 
 static void PrintHelp(const char* name) {
   /* String is cut to pieces with length less than 509, to conform C90 spec. */
-  fprintf(stdout, "\
-Usage: %s [OPTION]... [FILE]...\n",
+  fprintf(stdout,
+"Usage: %s [OPTION]... [FILE]...\n",
           name);
-  fprintf(stdout, "\
-Options:\n\
-  -#                          compression level (0-9)\n\
-  -c, --stdout                write on standard output\n\
-  -d, --decompress            decompress\n\
-  -f, --force                 force output file overwrite\n\
-  -h, --help                  display this help and exit\n");
-  fprintf(stdout, "\
-  -j, --rm                    remove source file(s)\n\
-  -k, --keep                  keep source file(s) (default)\n\
-  -n, --no-copy-stat          do not copy source file(s) attributes\n\
-  -o FILE, --output=FILE      output file (only if 1 input file)\n");
-  fprintf(stdout, "\
-  -q NUM, --quality=NUM       compression level (%d-%d)\n",
+  fprintf(stdout,
+"Options:\n"
+"  -#                          compression level (0-9)\n"
+"  -c, --stdout                write on standard output\n"
+"  -d, --decompress            decompress\n"
+"  -f, --force                 force output file overwrite\n"
+"  -h, --help                  display this help and exit\n");
+  fprintf(stdout,
+"  -j, --rm                    remove source file(s)\n"
+"  -k, --keep                  keep source file(s) (default)\n"
+"  -n, --no-copy-stat          do not copy source file(s) attributes\n"
+"  -o FILE, --output=FILE      output file (only if 1 input file)\n");
+  fprintf(stdout,
+"  -q NUM, --quality=NUM       compression level (%d-%d)\n",
           BROTLI_MIN_QUALITY, BROTLI_MAX_QUALITY);
-  fprintf(stdout, "\
-  -t, --test                  test compressed file integrity\n\
-  -v, --verbose               verbose mode\n");
-  fprintf(stdout, "\
-  -w NUM, --lgwin=NUM         set LZ77 window size (0, %d-%d) (default:%d)\n",
+  fprintf(stdout,
+"  -t, --test                  test compressed file integrity\n"
+"  -v, --verbose               verbose mode\n");
+  fprintf(stdout,
+"  -w NUM, --lgwin=NUM         set LZ77 window size (0, %d-%d) (default:%d)\n",
           BROTLI_MIN_WINDOW_BITS, BROTLI_MAX_WINDOW_BITS, DEFAULT_LGWIN);
-  fprintf(stdout, "\
-                              window size = 2**NUM - 16\n\
-                              0 lets compressor decide over the optimal value\n\
-  -D FILE, --dictionary=FILE  use FILE as LZ77 dictionary\n");
-  fprintf(stdout, "\
-  -S SUF, --suffix=SUF        output file suffix (default:'%s')\n",
+  fprintf(stdout,
+"                              window size = 2**NUM - 16\n"
+"                              0 lets compressor choose the optimal value\n");
+  fprintf(stdout,
+"  -S SUF, --suffix=SUF        output file suffix (default:'%s')\n",
           DEFAULT_SUFFIX);
-  fprintf(stdout, "\
-  -V, --version               display version and exit\n\
-  -Z, --best                  use best compression level (11) (default)\n\
-Simple options could be coalesced, i.e. '-9kf' is equivalent to '-9 -k -f'.\n\
-With no FILE, or when FILE is -, read standard input.\n\
-All arguments after '--' are treated as files.\n");
+  fprintf(stdout,
+"  -V, --version               display version and exit\n"
+"  -Z, --best                  use best compression level (11) (default)\n"
+"Simple options could be coalesced, i.e. '-9kf' is equivalent to '-9 -k -f'.\n"
+"With no FILE, or when FILE is -, read standard input.\n"
+"All arguments after '--' are treated as files.\n");
 }
 
 static const char* PrintablePath(const char* path) {
@@ -446,7 +437,7 @@
 static BROTLI_BOOL OpenInputFile(const char* input_path, FILE** f) {
   *f = NULL;
   if (!input_path) {
-    *f = fdopen(STDIN_FILENO, "rb");
+    *f = fdopen(MAKE_BINARY(STDIN_FILENO), "rb");
     return BROTLI_TRUE;
   }
   *f = fopen(input_path, "rb");
@@ -463,7 +454,7 @@
   int fd;
   *f = NULL;
   if (!output_path) {
-    *f = fdopen(STDOUT_FILENO, "wb");
+    *f = fdopen(MAKE_BINARY(STDOUT_FILENO), "wb");
     return BROTLI_TRUE;
   }
   fd = open(output_path, O_CREAT | (force ? 0 : O_EXCL) | O_WRONLY | O_TRUNC,
@@ -482,25 +473,8 @@
   return BROTLI_TRUE;
 }
 
-static int64_t FileSize(const char* path) {
-  FILE* f = fopen(path, "rb");
-  int64_t retval;
-  if (f == NULL) {
-    return -1;
-  }
-  if (fseek(f, 0L, SEEK_END) != 0) {
-    fclose(f);
-    return -1;
-  }
-  retval = ftell(f);
-  if (fclose(f) != 0) {
-    return -1;
-  }
-  return retval;
-}
-
 /* Copy file times and permissions.
-   TODO(eustas): this is a "best effort" implementation; honest cross-platform
+   TODO: this is a "best effort" implementation; honest cross-platform
    fully featured implementation is way too hacky; add more hacks by request. */
 static void CopyStat(const char* input_path, const char* output_path) {
   struct stat statbuf;
@@ -532,54 +506,6 @@
   }
 }
 
-/* Result ownership is passed to caller.
-   |*dictionary_size| is set to resulting buffer size. */
-static BROTLI_BOOL ReadDictionary(Context* context) {
-  static const int kMaxDictionarySize = (1 << 24) - 16;
-  FILE* f;
-  int64_t file_size_64;
-  uint8_t* buffer;
-  size_t bytes_read;
-
-  if (context->dictionary_path == NULL) return BROTLI_TRUE;
-  f = fopen(context->dictionary_path, "rb");
-  if (f == NULL) {
-    fprintf(stderr, "failed to open dictionary file [%s]: %s\n",
-            PrintablePath(context->dictionary_path), strerror(errno));
-    return BROTLI_FALSE;
-  }
-
-  file_size_64 = FileSize(context->dictionary_path);
-  if (file_size_64 == -1) {
-    fprintf(stderr, "could not get size of dictionary file [%s]",
-            PrintablePath(context->dictionary_path));
-    return BROTLI_FALSE;
-  }
-
-  if (file_size_64 > kMaxDictionarySize) {
-    fprintf(stderr, "dictionary [%s] is larger than maximum allowed: %d\n",
-            PrintablePath(context->dictionary_path), kMaxDictionarySize);
-    return BROTLI_FALSE;
-  }
-  context->dictionary_size = (size_t)file_size_64;
-
-  buffer = (uint8_t*)malloc(context->dictionary_size);
-  if (!buffer) {
-    fprintf(stderr, "could not read dictionary: out of memory\n");
-    return BROTLI_FALSE;
-  }
-  bytes_read = fread(buffer, sizeof(uint8_t), context->dictionary_size, f);
-  if (bytes_read != context->dictionary_size) {
-    free(buffer);
-    fprintf(stderr, "failed to read dictionary [%s]: %s\n",
-            PrintablePath(context->dictionary_path), strerror(errno));
-    return BROTLI_FALSE;
-  }
-  fclose(f);
-  context->dictionary = buffer;
-  return BROTLI_TRUE;
-}
-
 static BROTLI_BOOL NextFile(Context* context) {
   const char* arg;
   size_t arg_len;
@@ -662,7 +588,9 @@
 static BROTLI_BOOL CloseFiles(Context* context, BROTLI_BOOL success) {
   BROTLI_BOOL is_ok = BROTLI_TRUE;
   if (!context->test_integrity && context->fout) {
-    if (!success) unlink(context->current_output_path);
+    if (!success && context->current_output_path) {
+      unlink(context->current_output_path);
+    }
     if (fclose(context->fout) != 0) {
       if (success) {
         fprintf(stderr, "fclose failed [%s]: %s\n",
@@ -677,14 +605,16 @@
     }
   }
 
-  if (fclose(context->fin) != 0) {
-    if (is_ok) {
-      fprintf(stderr, "fclose failed [%s]: %s\n",
-              PrintablePath(context->current_input_path), strerror(errno));
+  if (context->fin) {
+    if (fclose(context->fin) != 0) {
+      if (is_ok) {
+        fprintf(stderr, "fclose failed [%s]: %s\n",
+                PrintablePath(context->current_input_path), strerror(errno));
+      }
+      is_ok = BROTLI_FALSE;
     }
-    is_ok = BROTLI_FALSE;
   }
-  if (success && context->junk_source) {
+  if (success && context->junk_source && context->current_input_path) {
     unlink(context->current_input_path);
   }
 
@@ -698,7 +628,7 @@
 
 static BROTLI_BOOL DecompressFile(Context* context, BrotliDecoderState* s) {
   size_t available_in = 0;
-  const uint8_t* next_in;
+  const uint8_t* next_in = NULL;
   size_t available_out = kFileBufferSize;
   uint8_t* next_out = context->output;
   BrotliDecoderResult result = BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
@@ -720,7 +650,7 @@
     if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) {
       if (feof(context->fin)) {
         fprintf(stderr, "corrupt input [%s]\n",
-                PrintablePath(context->current_output_path));
+                PrintablePath(context->current_input_path));
         return BROTLI_FALSE;
       }
       available_in = fread(context->input, 1, kFileBufferSize, context->fin);
@@ -735,13 +665,13 @@
     } else if (result == BROTLI_DECODER_RESULT_SUCCESS) {
       if (available_in != 0 || !feof(context->fin)) {
         fprintf(stderr, "corrupt input [%s]\n",
-                PrintablePath(context->current_output_path));
+                PrintablePath(context->current_input_path));
         return BROTLI_FALSE;
       }
       return BROTLI_TRUE;
     } else {
       fprintf(stderr, "corrupt input [%s]\n",
-              PrintablePath(context->current_output_path));
+              PrintablePath(context->current_input_path));
       return BROTLI_FALSE;
     }
 
@@ -758,11 +688,12 @@
       fprintf(stderr, "out of memory\n");
       return BROTLI_FALSE;
     }
-    if (context->dictionary) {
-      BrotliDecoderSetCustomDictionary(s,
-          context->dictionary_size, context->dictionary);
-    }
     is_ok = OpenFiles(context);
+    if (is_ok && !context->current_input_path &&
+        !context->force_overwrite && isatty(STDIN_FILENO)) {
+      fprintf(stderr, "Use -h help. Use -f to force input from a terminal.\n");
+      is_ok = BROTLI_FALSE;
+    }
     if (is_ok) is_ok = DecompressFile(context, s);
     BrotliDecoderDestroyInstance(s);
     if (!CloseFiles(context, is_ok)) is_ok = BROTLI_FALSE;
@@ -827,11 +758,12 @@
         BROTLI_PARAM_QUALITY, (uint32_t)context->quality);
     BrotliEncoderSetParameter(s,
         BROTLI_PARAM_LGWIN, (uint32_t)context->lgwin);
-    if (context->dictionary) {
-      BrotliEncoderSetCustomDictionary(s,
-          context->dictionary_size, context->dictionary);
-    }
     is_ok = OpenFiles(context);
+    if (is_ok && !context->current_output_path &&
+        !context->force_overwrite && isatty(STDOUT_FILENO)) {
+      fprintf(stderr, "Use -h help. Use -f to force output to a terminal.\n");
+      is_ok = BROTLI_FALSE;
+    }
     if (is_ok) is_ok = CompressFile(context, s);
     BrotliEncoderDestroyInstance(s);
     if (!CloseFiles(context, is_ok)) is_ok = BROTLI_FALSE;
@@ -856,7 +788,6 @@
   context.write_to_stdout = BROTLI_FALSE;
   context.decompress = BROTLI_FALSE;
   context.output_path = NULL;
-  context.dictionary_path = NULL;
   context.suffix = DEFAULT_SUFFIX;
   for (i = 0; i < MAX_OPTIONS; ++i) context.not_input_indices[i] = 0;
   context.longest_path_len = 1;
@@ -864,8 +795,6 @@
 
   context.argc = argc;
   context.argv = argv;
-  context.dictionary = NULL;
-  context.dictionary_size = 0;
   context.modified_path = NULL;
   context.iterator = 0;
   context.ignore = 0;
@@ -880,7 +809,6 @@
 
   if (command == COMMAND_COMPRESS || command == COMMAND_DECOMPRESS ||
       command == COMMAND_TEST_INTEGRITY) {
-    if (!ReadDictionary(&context)) is_ok = BROTLI_FALSE;
     if (is_ok) {
       size_t modified_path_len =
           context.longest_path_len + strlen(context.suffix) + 1;
@@ -925,7 +853,6 @@
 
   if (context.iterator_error) is_ok = BROTLI_FALSE;
 
-  free(context.dictionary);
   free(context.modified_path);
   free(context.buffer);
 
diff --git a/third_party/brotli/tools/brotli.md b/third_party/brotli/tools/brotli.md
index fc6d9bff..c029869 100644
--- a/third_party/brotli/tools/brotli.md
+++ b/third_party/brotli/tools/brotli.md
@@ -84,9 +84,6 @@
     `(2**NUM - 16)`; 0 lets compressor decide over the optimal value; bigger
     windows size improve density; decoder might require up to window size
     memory to operate
-* `-D FILE`, `--dictionary=FILE`:
-    use FILE as LZ77 dictionary; same dictionary MUST be used both for
-    compression and decompression
 * `-S SUF`, `--suffix=SUF`:
     output file suffix (default: `.br`)
 * `-V`, `--version`:
diff --git a/third_party/freetype/BUILD.gn b/third_party/freetype/BUILD.gn
index 48de03f..0dbb1c6 100644
--- a/third_party/freetype/BUILD.gn
+++ b/third_party/freetype/BUILD.gn
@@ -40,6 +40,7 @@
   defines = []
 
   sources = [
+    "include/freetype-custom-config/ftconfig.h",
     "include/freetype-custom-config/ftmodule.h",
     "include/freetype-custom-config/ftoption.h",
     "src/src/base/ftbase.c",
@@ -82,6 +83,7 @@
     # GN currently does not escape '<' and '>' when generating xml based Visual
     # Studio project files. As a result, use quotes instead of pointy brackets
     # in these defines.
+    "FT_CONFIG_CONFIG_H=\"freetype-custom-config/ftconfig.h\"",
     "FT_CONFIG_MODULES_H=\"freetype-custom-config/ftmodule.h\"",
     "FT_CONFIG_OPTIONS_H=\"freetype-custom-config/ftoption.h\"",
   ]
@@ -107,6 +109,7 @@
   defines = []
 
   sources = [
+    "include/freetype-custom-config/ftconfig.h",
     "include/freetype-custom-config/ftmodule.h",
     "include/freetype-custom-config/ftoption.h",
     "src/src/autofit/autofit.c",
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index b16f7df..2511f8e2 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-8-1
-Revision: 39ce3ac499d4cd7371031a062f410953c8ecce29
+Version: VER-2-8-1-9
+Revision: 1ad07c1c79841e54ff3d5c37e28bfb91f402ee84
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
 License File: src/docs/FTL.TXT
diff --git a/third_party/freetype/include/freetype-custom-config/ftconfig.h b/third_party/freetype/include/freetype-custom-config/ftconfig.h
index 889aebf..320bce1 100644
--- a/third_party/freetype/include/freetype-custom-config/ftconfig.h
+++ b/third_party/freetype/include/freetype-custom-config/ftconfig.h
@@ -408,9 +408,62 @@
 #endif /* !FT_BASE_DEF */
 
 
+  /*   When compiling FreeType as a DLL, some systems/compilers need a     */
+  /*   special attribute in front OR after the return type of function     */
+  /*   declarations.                                                       */
+  /*                                                                       */
+  /*   Two macros are used within the FreeType source code to define       */
+  /*   exported library functions: FT_EXPORT and FT_EXPORT_DEF.            */
+  /*                                                                       */
+  /*     FT_EXPORT( return_type )                                          */
+  /*                                                                       */
+  /*       is used in a function declaration, as in                        */
+  /*                                                                       */
+  /*         FT_EXPORT( FT_Error )                                         */
+  /*         FT_Init_FreeType( FT_Library*  alibrary );                    */
+  /*                                                                       */
+  /*                                                                       */
+  /*     FT_EXPORT_DEF( return_type )                                      */
+  /*                                                                       */
+  /*       is used in a function definition, as in                         */
+  /*                                                                       */
+  /*         FT_EXPORT_DEF( FT_Error )                                     */
+  /*         FT_Init_FreeType( FT_Library*  alibrary )                     */
+  /*         {                                                             */
+  /*           ... some code ...                                           */
+  /*           return FT_Err_Ok;                                           */
+  /*         }                                                             */
+  /*                                                                       */
+  /*   You can provide your own implementation of FT_EXPORT and            */
+  /*   FT_EXPORT_DEF here if you want.                                     */
+  /*                                                                       */
+#if defined(_WIN32)
+
+#if defined(FT2_BUILD_DLL)
+#if defined(FT2_BUILD_LIBRARY)
+#define FT_EXPORT(x)     __declspec(dllexport) x
+#define FT_EXPORT_DEF(x) __declspec(dllexport) x
+#else
+#define FT_EXPORT(x)     __declspec(dllimport) x
+#define FT_EXPORT_DEF(x) __declspec(dllimport) x
+#endif
+#endif
+
+#else
+#if !defined(MAC_RESTRICT_VISIBILITY)
+#define FT_EXPORT(x)     __attribute__((visibility ("default"))) x
+#define FT_EXPORT_DEF(x) __attribute__((visibility ("default"))) x
+#else
+#define FT_EXPORT(x)     x
+#define FT_EXPORT_DEF(x) x
+#endif
+#endif
+
 #ifndef FT_EXPORT
 
-#ifdef __cplusplus
+#if defined( _DLL )
+#define FT_EXPORT( x )  __declspec(dllexport)  x
+#elif defined( __cplusplus )
 #define FT_EXPORT( x )  extern "C"  x
 #else
 #define FT_EXPORT( x )  extern  x
@@ -421,7 +474,9 @@
 
 #ifndef FT_EXPORT_DEF
 
-#ifdef __cplusplus
+#if defined( _DLL )
+#define FT_EXPORT_DEF( x )  __declspec(dllexport)  x
+#elif defined( __cplusplus )
 #define FT_EXPORT_DEF( x )  extern "C"  x
 #else
 #define FT_EXPORT_DEF( x )  extern  x
diff --git a/third_party/freetype/include/freetype-custom-config/ftoption.h b/third_party/freetype/include/freetype-custom-config/ftoption.h
index 34f64d9..0810991 100644
--- a/third_party/freetype/include/freetype-custom-config/ftoption.h
+++ b/third_party/freetype/include/freetype-custom-config/ftoption.h
@@ -272,67 +272,6 @@
 
   /*************************************************************************/
   /*                                                                       */
-  /* DLL export compilation                                                */
-  /*                                                                       */
-  /*   When compiling FreeType as a DLL, some systems/compilers need a     */
-  /*   special keyword in front OR after the return type of function       */
-  /*   declarations.                                                       */
-  /*                                                                       */
-  /*   Two macros are used within the FreeType source code to define       */
-  /*   exported library functions: FT_EXPORT and FT_EXPORT_DEF.            */
-  /*                                                                       */
-  /*     FT_EXPORT( return_type )                                          */
-  /*                                                                       */
-  /*       is used in a function declaration, as in                        */
-  /*                                                                       */
-  /*         FT_EXPORT( FT_Error )                                         */
-  /*         FT_Init_FreeType( FT_Library*  alibrary );                    */
-  /*                                                                       */
-  /*                                                                       */
-  /*     FT_EXPORT_DEF( return_type )                                      */
-  /*                                                                       */
-  /*       is used in a function definition, as in                         */
-  /*                                                                       */
-  /*         FT_EXPORT_DEF( FT_Error )                                     */
-  /*         FT_Init_FreeType( FT_Library*  alibrary )                     */
-  /*         {                                                             */
-  /*           ... some code ...                                           */
-  /*           return FT_Err_Ok;                                           */
-  /*         }                                                             */
-  /*                                                                       */
-  /*   You can provide your own implementation of FT_EXPORT and            */
-  /*   FT_EXPORT_DEF here if you want.  If you leave them undefined, they  */
-  /*   will be later automatically defined as `extern return_type' to      */
-  /*   allow normal compilation.                                           */
-  /*                                                                       */
-  /*   Do not #undef these macros here since the build system might define */
-  /*   them for certain configurations only.                               */
-  /*                                                                       */
-
-#if defined(_WIN32)
-
-#if defined(FT2_BUILD_DLL)
-#if defined(FT2_BUILD_LIBRARY)
-#define FT_EXPORT(x)     __declspec(dllexport) x
-#define FT_EXPORT_DEF(x) __declspec(dllexport) x
-#else
-#define FT_EXPORT(x)     __declspec(dllimport) x
-#define FT_EXPORT_DEF(x) __declspec(dllimport) x
-#endif
-#endif
-
-#else
-#if !defined(MAC_RESTRICT_VISIBILITY)
-#define FT_EXPORT(x)     __attribute__((visibility ("default"))) x
-#define FT_EXPORT_DEF(x) __attribute__((visibility ("default"))) x
-#else
-#define FT_EXPORT(x)     x
-#define FT_EXPORT_DEF(x) x
-#endif
-#endif
-
-  /*************************************************************************/
-  /*                                                                       */
   /* Glyph Postscript Names handling                                       */
   /*                                                                       */
   /*   By default, FreeType 2 is compiled with the `psnames' module.  This */
diff --git a/third_party/inspector_protocol/README.chromium b/third_party/inspector_protocol/README.chromium
index b1863f9..ebf89594 100644
--- a/third_party/inspector_protocol/README.chromium
+++ b/third_party/inspector_protocol/README.chromium
@@ -2,7 +2,7 @@
 Short Name: inspector_protocol
 URL: https://chromium.googlesource.com/deps/inspector_protocol/
 Version: 0
-Revision: efefa86c3183d307f0a0e53bf568fe57c5b58849
+Revision: 66273318ce891a49d516d8d7540abf6c1a42121f
 License: BSD
 License File: LICENSE
 Security Critical: no
diff --git a/third_party/inspector_protocol/lib/Values_cpp.template b/third_party/inspector_protocol/lib/Values_cpp.template
index 6f8b14c..b9f0613 100644
--- a/third_party/inspector_protocol/lib/Values_cpp.template
+++ b/third_party/inspector_protocol/lib/Values_cpp.template
@@ -42,30 +42,22 @@
     }
 }
 
-void escapeStringForJSON(const String& str, StringBuilder* dst)
+template <typename Char>
+void escapeStringForJSONInternal(const Char* str, unsigned len,
+                                 StringBuilder* dst)
 {
-    for (unsigned i = 0; i < str.length(); ++i) {
-        uint16_t c = str[i];
-        if (!escapeChar(c, dst)) {
-            if (c < 32 || c > 126 || c == '<' || c == '>') {
-                // 1. Escaping <, > to prevent script execution.
-                // 2. Technically, we could also pass through c > 126 as UTF8, but this
-                //    is also optional. It would also be a pain to implement here.
-                appendUnsignedAsHex(c, dst);
-            } else {
-                StringUtil::builderAppend(*dst, c);
-            }
+    for (unsigned i = 0; i < len; ++i) {
+        Char c = str[i];
+        if (escapeChar(c, dst))
+            continue;
+        if (c < 32 || c > 126) {
+            appendUnsignedAsHex(c, dst);
+        } else {
+            StringUtil::builderAppend(*dst, c);
         }
     }
 }
 
-void doubleQuoteStringForJSON(const String& str, StringBuilder* dst)
-{
-    StringUtil::builderAppend(*dst, '"');
-    escapeStringForJSON(str, dst);
-    StringUtil::builderAppend(*dst, '"');
-}
-
 } // anonymous namespace
 
 bool Value::asBoolean(bool*) const
@@ -181,7 +173,7 @@
 void StringValue::writeJSON(StringBuilder* output) const
 {
     DCHECK(type() == TypeString);
-    doubleQuoteStringForJSON(m_stringValue, output);
+    StringUtil::builderAppendQuotedString(*output, m_stringValue);
 }
 
 std::unique_ptr<Value> StringValue::clone() const
@@ -336,7 +328,7 @@
         CHECK(it != m_data.end());
         if (i)
             StringUtil::builderAppend(*output, ',');
-        doubleQuoteStringForJSON(it->first, output);
+        StringUtil::builderAppendQuotedString(*output, it->first);
         StringUtil::builderAppend(*output, ':');
         it->second->writeJSON(output);
     }
@@ -402,6 +394,16 @@
     return m_data[index].get();
 }
 
+void escapeLatinStringForJSON(const uint8_t* str, unsigned len, StringBuilder* dst)
+{
+    escapeStringForJSONInternal<uint8_t>(str, len, dst);
+}
+
+void escapeWideStringForJSON(const uint16_t* str, unsigned len, StringBuilder* dst)
+{
+    escapeStringForJSONInternal<uint16_t>(str, len, dst);
+}
+
 {% for namespace in config.protocol.namespace %}
 } // namespace {{namespace}}
 {% endfor %}
diff --git a/third_party/inspector_protocol/lib/Values_h.template b/third_party/inspector_protocol/lib/Values_h.template
index 646543a..3638b34 100644
--- a/third_party/inspector_protocol/lib/Values_h.template
+++ b/third_party/inspector_protocol/lib/Values_h.template
@@ -239,6 +239,9 @@
     std::vector<std::unique_ptr<Value>> m_data;
 };
 
+void escapeLatinStringForJSON(const uint8_t* str, unsigned len, StringBuilder* dst);
+void escapeWideStringForJSON(const uint16_t* str, unsigned len, StringBuilder* dst);
+
 {% for namespace in config.protocol.namespace %}
 } // namespace {{namespace}}
 {% endfor %}
diff --git a/third_party/webrtc_overrides/rtc_base/task_queue.h b/third_party/webrtc_overrides/rtc_base/task_queue.h
deleted file mode 100644
index 04864bb4..0000000
--- a/third_party/webrtc_overrides/rtc_base/task_queue.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
- *
- *  Use of this source code is governed by a BSD-style license
- *  that can be found in the LICENSE file in the root of the source
- *  tree. An additional intellectual property rights grant can be found
- *  in the file PATENTS.  All contributing project authors may
- *  be found in the AUTHORS file in the root of the source tree.
- */
-
-// TODO(nisse): This file is obsolete, but still referenced by
-// webrtc's rtc_base/BUILD.gn. Update that, then delete this file.
-#include "third_party/webrtc/rtc_base/task_queue.h"
diff --git a/tools/android/eclipse/.classpath b/tools/android/eclipse/.classpath
index dc88dca9..6b91efc 100644
--- a/tools/android/eclipse/.classpath
+++ b/tools/android/eclipse/.classpath
@@ -30,8 +30,9 @@
     <classpathentry kind="src" path="base/test/android/junit/src"/>
     <classpathentry kind="src" path="chrome/android/java/src"/>
     <classpathentry kind="src" path="chrome/android/javatests/src"/>
-    <classpathentry kind="src" path="chrome/android/sync_shell/javatests/src"/>
     <classpathentry kind="src" path="chrome/android/junit/src"/>
+    <classpathentry kind="src" path="chrome/android/sync_shell/javatests/src"/>
+    <classpathentry kind="src" path="chrome/android/third_party/widget_bottomsheet_base/java/src/" />
     <classpathentry kind="src" path="chrome/android/webapk/libs/client/junit/src"/>
     <classpathentry kind="src" path="chrome/android/webapk/libs/client/src"/>
     <classpathentry kind="src" path="chrome/android/webapk/libs/common/src"/>
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index ba581ef2..1b9fdeb3 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -792,6 +792,7 @@
   parser = argparse.ArgumentParser(description='Build Clang.')
   parser.add_argument('--bootstrap', action='store_true',
                       help='first build clang with CC, then with itself.')
+  # TODO(phajdan.jr): remove --if-needed after fixing callers. It's no-op.
   parser.add_argument('--if-needed', action='store_true',
                       help="run only if the script thinks clang is needed")
   parser.add_argument('--force-local-build', action='store_true',
@@ -825,12 +826,6 @@
     print '--lto-lld is only effective on Linux. Ignoring the option.'
     args.lto_lld = False
 
-  if args.if_needed:
-    # TODO(thakis): Can probably remove this and --if-needed altogether.
-    if re.search(r'\b(make_clang_dir)=', os.environ.get('GYP_DEFINES', '')):
-      print 'Skipping Clang update (make_clang_dir= was set in GYP_DEFINES).'
-      return 0
-
   # Get svn if we're going to use it to check the revision or do a local build.
   if (use_head_revision or args.llvm_force_head_revision or
       args.force_local_build):
diff --git a/tools/cygprofile/profile_android_startup.py b/tools/cygprofile/profile_android_startup.py
index c33b60b3..d906ad9 100755
--- a/tools/cygprofile/profile_android_startup.py
+++ b/tools/cygprofile/profile_android_startup.py
@@ -203,8 +203,7 @@
   # TEST_URL must be a url in the WPR_ARCHIVE.
   _TEST_URL = 'https://www.google.com/#hl=en&q=science'
   _WPR_ARCHIVE = os.path.join(
-      constants.DIR_SOURCE_ROOT, 'tools', 'perf', 'page_sets', 'data',
-      'top_10_mobile_002.wpr')
+      os.path.dirname(__file__), 'top_10_mobile_002.wpr')
 
   # TODO(jbudorick): Make host_cyglog_dir mandatory after updating
   # downstream clients. See crbug.com/639831 for context.
diff --git a/tools/cygprofile/top_10_mobile_002.wpr.sha1 b/tools/cygprofile/top_10_mobile_002.wpr.sha1
new file mode 100644
index 0000000..caa6d52
--- /dev/null
+++ b/tools/cygprofile/top_10_mobile_002.wpr.sha1
@@ -0,0 +1 @@
+8bcbec66f88136691a49f02e15e7cf8ccf9a9d7c
\ No newline at end of file
diff --git a/tools/json_schema_compiler/cc_generator.py b/tools/json_schema_compiler/cc_generator.py
index 44a0988..63016eb 100644
--- a/tools/json_schema_compiler/cc_generator.py
+++ b/tools/json_schema_compiler/cc_generator.py
@@ -43,7 +43,6 @@
       .Append()
       .Append(self._util_cc_helper.GetIncludePath())
       .Append('#include "base/logging.h"')
-      .Append('#include "base/memory/ptr_util.h"')
       .Append('#include "base/strings/string_number_conversions.h"')
       .Append('#include "base/strings/utf_string_conversions.h"')
       .Append('#include "base/values.h"')
@@ -632,12 +631,12 @@
       maybe_namespace = ''
       if type_.property_type == PropertyType.REF:
         maybe_namespace = '%s::' % underlying_type.namespace.unix_name
-      return 'base::MakeUnique<base::Value>(%sToString(%s))' % (
+      return 'std::make_unique<base::Value>(%sToString(%s))' % (
           maybe_namespace, var)
     elif underlying_type.property_type == PropertyType.BINARY:
       if is_ptr:
         var = '*%s' % var
-      return 'base::MakeUnique<base::Value>(%s)' % var
+      return 'std::make_unique<base::Value>(%s)' % var
     elif underlying_type.property_type == PropertyType.ARRAY:
       return '%s' % self._util_cc_helper.CreateValueFromArray(
           var,
@@ -646,9 +645,9 @@
       if is_ptr:
         var = '*%s' % var
       if underlying_type.property_type == PropertyType.STRING:
-        return 'base::MakeUnique<base::Value>(%s)' % var
+        return 'std::make_unique<base::Value>(%s)' % var
       else:
-        return 'base::MakeUnique<base::Value>(%s)' % var
+        return 'std::make_unique<base::Value>(%s)' % var
     else:
       raise NotImplementedError('Conversion of %s to base::Value not '
                                 'implemented' % repr(type_.type_))
diff --git a/tools/json_schema_compiler/test/arrays_unittest.cc b/tools/json_schema_compiler/test/arrays_unittest.cc
index 91112f2..aa00402c 100644
--- a/tools/json_schema_compiler/test/arrays_unittest.cc
+++ b/tools/json_schema_compiler/test/arrays_unittest.cc
@@ -10,7 +10,6 @@
 #include <utility>
 
 #include "base/macros.h"
-#include "base/memory/ptr_util.h"
 #include "base/values.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "tools/json_schema_compiler/test/enums.h"
@@ -21,20 +20,20 @@
 
 // TODO(calamity): Change to AppendString etc once kalman's patch goes through
 static std::unique_ptr<base::DictionaryValue> CreateBasicArrayTypeDictionary() {
-  auto value = base::MakeUnique<base::DictionaryValue>();
-  auto strings_value = base::MakeUnique<base::ListValue>();
+  auto value = std::make_unique<base::DictionaryValue>();
+  auto strings_value = std::make_unique<base::ListValue>();
   strings_value->AppendString("a");
   strings_value->AppendString("b");
   strings_value->AppendString("c");
   strings_value->AppendString("it's easy as");
-  auto integers_value = base::MakeUnique<base::ListValue>();
+  auto integers_value = std::make_unique<base::ListValue>();
   integers_value->AppendInteger(1);
   integers_value->AppendInteger(2);
   integers_value->AppendInteger(3);
-  auto booleans_value = base::MakeUnique<base::ListValue>();
+  auto booleans_value = std::make_unique<base::ListValue>();
   booleans_value->AppendBoolean(false);
   booleans_value->AppendBoolean(true);
-  auto numbers_value = base::MakeUnique<base::ListValue>();
+  auto numbers_value = std::make_unique<base::ListValue>();
   numbers_value->AppendDouble(6.1);
   value->Set("numbers", std::move(numbers_value));
   value->Set("booleans", std::move(booleans_value));
@@ -44,7 +43,7 @@
 }
 
 std::unique_ptr<base::DictionaryValue> CreateItemValue(int val) {
-  auto value = base::MakeUnique<base::DictionaryValue>();
+  auto value = std::make_unique<base::DictionaryValue>();
   value->SetInteger("val", val);
   return value;
 }
@@ -63,7 +62,7 @@
 
 TEST(JsonSchemaCompilerArrayTest, EnumArrayReference) {
   // { "types": ["one", "two", "three"] }
-  auto types = base::MakeUnique<base::ListValue>();
+  auto types = std::make_unique<base::ListValue>();
   types->AppendString("one");
   types->AppendString("two");
   types->AppendString("three");
@@ -88,12 +87,12 @@
 
 TEST(JsonSchemaCompilerArrayTest, EnumArrayMixed) {
   // { "types": ["one", "two", "three"] }
-  auto infile_enums = base::MakeUnique<base::ListValue>();
+  auto infile_enums = std::make_unique<base::ListValue>();
   infile_enums->AppendString("one");
   infile_enums->AppendString("two");
   infile_enums->AppendString("three");
 
-  auto external_enums = base::MakeUnique<base::ListValue>();
+  auto external_enums = std::make_unique<base::ListValue>();
   external_enums->AppendString("one");
   external_enums->AppendString("two");
   external_enums->AppendString("three");
@@ -134,7 +133,7 @@
     enums.push_back(ENUMERATION_TWO);
     enums.push_back(ENUMERATION_THREE);
 
-    auto types = base::MakeUnique<base::ListValue>();
+    auto types = std::make_unique<base::ListValue>();
     for (size_t i = 0; i < enums.size(); ++i)
       types->AppendString(ToString(enums[i]));
 
@@ -147,7 +146,7 @@
   }
   {
     base::DictionaryValue value;
-    auto enum_array = base::MakeUnique<base::ListValue>();
+    auto enum_array = std::make_unique<base::ListValue>();
     enum_array->AppendString("invalid");
 
     value.Set("types", std::move(enum_array));
@@ -159,13 +158,13 @@
 
 TEST(JsonSchemaCompilerArrayTest, RefArrayType) {
   {
-    auto value = base::MakeUnique<base::DictionaryValue>();
-    auto ref_array = base::MakeUnique<base::ListValue>();
+    auto value = std::make_unique<base::DictionaryValue>();
+    auto ref_array = std::make_unique<base::ListValue>();
     ref_array->Append(CreateItemValue(1));
     ref_array->Append(CreateItemValue(2));
     ref_array->Append(CreateItemValue(3));
     value->Set("refs", std::move(ref_array));
-    auto ref_array_type = base::MakeUnique<RefArrayType>();
+    auto ref_array_type = std::make_unique<RefArrayType>();
     EXPECT_TRUE(RefArrayType::Populate(*value, ref_array_type.get()));
     ASSERT_EQ(3u, ref_array_type->refs.size());
     EXPECT_EQ(1, ref_array_type->refs[0].val);
@@ -173,12 +172,12 @@
     EXPECT_EQ(3, ref_array_type->refs[2].val);
   }
   {
-    auto value = base::MakeUnique<base::DictionaryValue>();
-    auto not_ref_array = base::MakeUnique<base::ListValue>();
+    auto value = std::make_unique<base::DictionaryValue>();
+    auto not_ref_array = std::make_unique<base::ListValue>();
     not_ref_array->Append(CreateItemValue(1));
     not_ref_array->AppendInteger(3);
     value->Set("refs", std::move(not_ref_array));
-    auto ref_array_type = base::MakeUnique<RefArrayType>();
+    auto ref_array_type = std::make_unique<RefArrayType>();
     EXPECT_FALSE(RefArrayType::Populate(*value, ref_array_type.get()));
   }
 }
diff --git a/tools/json_schema_compiler/test/choices_unittest.cc b/tools/json_schema_compiler/test/choices_unittest.cc
index 2523a2e5..8c8102c 100644
--- a/tools/json_schema_compiler/test/choices_unittest.cc
+++ b/tools/json_schema_compiler/test/choices_unittest.cc
@@ -8,7 +8,6 @@
 
 #include <utility>
 
-#include "base/memory/ptr_util.h"
 #include "base/strings/string_piece.h"
 #include "base/values.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -25,12 +24,12 @@
 TEST(JsonSchemaCompilerChoicesTest, TakesIntegersParamsCreate) {
   {
     std::unique_ptr<TakesIntegers::Params> params(TakesIntegers::Params::Create(
-        *List(base::MakeUnique<base::Value>(true))));
+        *List(std::make_unique<base::Value>(true))));
     EXPECT_FALSE(params);
   }
   {
     std::unique_ptr<TakesIntegers::Params> params(
-        TakesIntegers::Params::Create(*List(base::MakeUnique<base::Value>(6))));
+        TakesIntegers::Params::Create(*List(std::make_unique<base::Value>(6))));
     ASSERT_TRUE(params);
     EXPECT_FALSE(params->nums.as_integers);
     EXPECT_EQ(6, *params->nums.as_integer);
@@ -38,8 +37,8 @@
   {
     std::unique_ptr<TakesIntegers::Params> params(
         TakesIntegers::Params::Create(*List(List(
-            base::MakeUnique<base::Value>(2), base::MakeUnique<base::Value>(6),
-            base::MakeUnique<base::Value>(8)))));
+            std::make_unique<base::Value>(2), std::make_unique<base::Value>(6),
+            std::make_unique<base::Value>(8)))));
     ASSERT_TRUE(params);
     ASSERT_TRUE(params->nums.as_integers);
     EXPECT_EQ(Vector(2, 6, 8), *params->nums.as_integers);
@@ -50,7 +49,7 @@
   {
     std::unique_ptr<ObjectWithChoices::Params> params(
         ObjectWithChoices::Params::Create(*List(
-            Dictionary("strings", base::MakeUnique<base::Value>("asdf")))));
+            Dictionary("strings", std::make_unique<base::Value>("asdf")))));
     ASSERT_TRUE(params);
     EXPECT_FALSE(params->string_info.strings.as_strings);
     EXPECT_EQ("asdf", *params->string_info.strings.as_string);
@@ -59,8 +58,8 @@
   {
     std::unique_ptr<ObjectWithChoices::Params> params(
         ObjectWithChoices::Params::Create(
-            *List(Dictionary("strings", base::MakeUnique<base::Value>("asdf"),
-                             "integers", base::MakeUnique<base::Value>(6)))));
+            *List(Dictionary("strings", std::make_unique<base::Value>("asdf"),
+                             "integers", std::make_unique<base::Value>(6)))));
     ASSERT_TRUE(params);
     EXPECT_FALSE(params->string_info.strings.as_strings);
     EXPECT_EQ("asdf", *params->string_info.strings.as_string);
@@ -112,7 +111,7 @@
                                             std::string("of"),
                                             std::string("strings"));
 
-  auto strings_value = base::MakeUnique<base::ListValue>();
+  auto strings_value = std::make_unique<base::ListValue>();
   for (size_t i = 0; i < strings.size(); ++i)
     strings_value->AppendString(strings[i]);
 
@@ -132,7 +131,7 @@
 }
 
 TEST(JsonSchemaCompilerChoicesTest, ChoiceTypeToValue) {
-  auto strings_value = base::MakeUnique<base::ListValue>();
+  auto strings_value = std::make_unique<base::ListValue>();
   strings_value->AppendString("list");
   strings_value->AppendString("of");
   strings_value->AppendString("strings");
diff --git a/tools/json_schema_compiler/test/crossref_unittest.cc b/tools/json_schema_compiler/test/crossref_unittest.cc
index 67c06d5..3f928db 100644
--- a/tools/json_schema_compiler/test/crossref_unittest.cc
+++ b/tools/json_schema_compiler/test/crossref_unittest.cc
@@ -7,7 +7,6 @@
 #include <memory>
 #include <utility>
 
-#include "base/memory/ptr_util.h"
 #include "base/values.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "tools/json_schema_compiler/test/simple_api.h"
@@ -17,7 +16,7 @@
 namespace {
 
 std::unique_ptr<base::DictionaryValue> CreateTestTypeValue() {
-  auto value = base::MakeUnique<base::DictionaryValue>();
+  auto value = std::make_unique<base::DictionaryValue>();
   value->SetDouble("number", 1.1);
   value->SetInteger("integer", 4);
   value->SetString("string", "bling");
@@ -87,8 +86,8 @@
 
 TEST(JsonSchemaCompilerCrossrefTest, TestTypeInObjectParamsCreate) {
   {
-    auto params_value = base::MakeUnique<base::ListValue>();
-    auto param_object_value = base::MakeUnique<base::DictionaryValue>();
+    auto params_value = std::make_unique<base::ListValue>();
+    auto param_object_value = std::make_unique<base::DictionaryValue>();
     param_object_value->Set("testType", CreateTestTypeValue());
     param_object_value->SetBoolean("boolean", true);
     params_value->Append(std::move(param_object_value));
@@ -101,8 +100,8 @@
         params->param_object.test_type->ToValue().get()));
   }
   {
-    auto params_value = base::MakeUnique<base::ListValue>();
-    auto param_object_value = base::MakeUnique<base::DictionaryValue>();
+    auto params_value = std::make_unique<base::ListValue>();
+    auto param_object_value = std::make_unique<base::DictionaryValue>();
     param_object_value->SetBoolean("boolean", true);
     params_value->Append(std::move(param_object_value));
     std::unique_ptr<crossref::TestTypeInObject::Params> params(
@@ -112,8 +111,8 @@
     EXPECT_TRUE(params->param_object.boolean);
   }
   {
-    auto params_value = base::MakeUnique<base::ListValue>();
-    auto param_object_value = base::MakeUnique<base::DictionaryValue>();
+    auto params_value = std::make_unique<base::ListValue>();
+    auto param_object_value = std::make_unique<base::DictionaryValue>();
     param_object_value->SetString("testType", "invalid");
     param_object_value->SetBoolean("boolean", true);
     params_value->Append(std::move(param_object_value));
@@ -122,8 +121,8 @@
     EXPECT_FALSE(params.get());
   }
   {
-    auto params_value = base::MakeUnique<base::ListValue>();
-    auto param_object_value = base::MakeUnique<base::DictionaryValue>();
+    auto params_value = std::make_unique<base::ListValue>();
+    auto param_object_value = std::make_unique<base::DictionaryValue>();
     param_object_value->Set("testType", CreateTestTypeValue());
     params_value->Append(std::move(param_object_value));
     std::unique_ptr<crossref::TestTypeInObject::Params> params(
diff --git a/tools/json_schema_compiler/test/enums_unittest.cc b/tools/json_schema_compiler/test/enums_unittest.cc
index fffe9b4..4522f0c4e 100644
--- a/tools/json_schema_compiler/test/enums_unittest.cc
+++ b/tools/json_schema_compiler/test/enums_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "tools/json_schema_compiler/test/enums.h"
 
-#include "base/memory/ptr_util.h"
 #include "base/values.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "tools/json_schema_compiler/test/test_util.h"
@@ -74,8 +73,8 @@
 TEST(JsonSchemaCompilerEnumsTest, EnumsArrayAsType) {
   {
     base::ListValue params_value;
-    params_value.Append(List(base::MakeUnique<base::Value>("one"),
-                             base::MakeUnique<base::Value>("two")));
+    params_value.Append(List(std::make_unique<base::Value>("one"),
+                             std::make_unique<base::Value>("two")));
     std::unique_ptr<TakesEnumArrayAsType::Params> params(
         TakesEnumArrayAsType::Params::Create(params_value));
     ASSERT_TRUE(params);
@@ -85,7 +84,7 @@
   }
   {
     base::ListValue params_value;
-    params_value.Append(List(base::MakeUnique<base::Value>("invalid")));
+    params_value.Append(List(std::make_unique<base::Value>("invalid")));
     std::unique_ptr<TakesEnumArrayAsType::Params> params(
         TakesEnumArrayAsType::Params::Create(params_value));
     EXPECT_FALSE(params);
@@ -165,8 +164,8 @@
 TEST(JsonSchemaCompilerEnumsTest, TakesEnumArrayParamsCreate) {
   {
     base::ListValue params_value;
-    params_value.Append(List(base::MakeUnique<base::Value>("one"),
-                             base::MakeUnique<base::Value>("two")));
+    params_value.Append(List(std::make_unique<base::Value>("one"),
+                             std::make_unique<base::Value>("two")));
     std::unique_ptr<TakesEnumArray::Params> params(
         TakesEnumArray::Params::Create(params_value));
     ASSERT_TRUE(params);
@@ -176,7 +175,7 @@
   }
   {
     base::ListValue params_value;
-    params_value.Append(List(base::MakeUnique<base::Value>("invalid")));
+    params_value.Append(List(std::make_unique<base::Value>("invalid")));
     std::unique_ptr<TakesEnumArray::Params> params(
         TakesEnumArray::Params::Create(params_value));
     EXPECT_FALSE(params);
diff --git a/tools/json_schema_compiler/test/error_generation_unittest.cc b/tools/json_schema_compiler/test/error_generation_unittest.cc
index 06a3a9c..596c0b1 100644
--- a/tools/json_schema_compiler/test/error_generation_unittest.cc
+++ b/tools/json_schema_compiler/test/error_generation_unittest.cc
@@ -4,8 +4,9 @@
 
 #include "tools/json_schema_compiler/test/error_generation.h"
 
+#include <memory>
+
 #include "base/json/json_writer.h"
-#include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "tools/json_schema_compiler/test/test_util.h"
@@ -36,11 +37,11 @@
 TEST(JsonSchemaCompilerErrorTest, RequiredPropertyPopulate) {
   {
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("string", base::MakeUnique<base::Value>("bling"));
+        Dictionary("string", std::make_unique<base::Value>("bling"));
     EXPECT_TRUE(EqualsUtf16("", GetPopulateError<TestType>(*value)));
   }
   {
-    auto value = base::MakeUnique<base::Value>(base::Value::Type::BINARY);
+    auto value = std::make_unique<base::Value>(base::Value::Type::BINARY);
     EXPECT_TRUE(EqualsUtf16("expected dictionary, got binary",
         GetPopulateError<TestType>(*value)));
   }
@@ -53,7 +54,7 @@
         GetPopulateError<ChoiceType::Integers>(*value)));
   }
   {
-    auto value = base::MakeUnique<base::Value>(base::Value::Type::BINARY);
+    auto value = std::make_unique<base::Value>(base::Value::Type::BINARY);
     EXPECT_TRUE(EqualsUtf16("expected integers or integer, got binary",
         GetPopulateError<ChoiceType::Integers>(*value)));
   }
@@ -64,7 +65,7 @@
 TEST(JsonSchemaCompilerErrorTest, TypeIsRequired) {
   {
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("integers", base::MakeUnique<Value>(5));
+        Dictionary("integers", std::make_unique<Value>(5));
     EXPECT_TRUE(EqualsUtf16("", GetPopulateError<ChoiceType>(*value)));
   }
   {
@@ -79,13 +80,13 @@
 TEST(JsonSchemaCompilerErrorTest, TooManyParameters) {
   {
     std::unique_ptr<base::ListValue> params_value =
-        List(base::MakeUnique<Value>(5));
+        List(std::make_unique<Value>(5));
     base::string16 error;
     EXPECT_TRUE(TestFunction::Params::Create(*params_value, &error));
   }
   {
     std::unique_ptr<base::ListValue> params_value =
-        List(base::MakeUnique<Value>(5), base::MakeUnique<Value>(5));
+        List(std::make_unique<Value>(5), std::make_unique<Value>(5));
     base::string16 error;
     EXPECT_FALSE(TestFunction::Params::Create(*params_value, &error));
     EXPECT_TRUE(EqualsUtf16("expected 1 arguments, got 2", error));
@@ -97,13 +98,13 @@
 TEST(JsonSchemaCompilerErrorTest, ParamIsRequired) {
   {
     std::unique_ptr<base::ListValue> params_value =
-        List(base::MakeUnique<Value>(5));
+        List(std::make_unique<Value>(5));
     base::string16 error;
     EXPECT_TRUE(TestFunction::Params::Create(*params_value, &error));
   }
   {
     std::unique_ptr<base::ListValue> params_value =
-        List(base::MakeUnique<Value>());
+        List(std::make_unique<Value>());
     base::string16 error;
     EXPECT_FALSE(TestFunction::Params::Create(*params_value, &error));
     EXPECT_TRUE(EqualsUtf16("'num' is required", error));
@@ -115,12 +116,12 @@
 TEST(JsonSchemaCompilerErrorTest, WrongPropertyValueType) {
   {
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("string", base::MakeUnique<base::Value>("yes"));
+        Dictionary("string", std::make_unique<base::Value>("yes"));
     EXPECT_TRUE(EqualsUtf16("", GetPopulateError<TestType>(*value)));
   }
   {
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("string", base::MakeUnique<Value>(1.1));
+        Dictionary("string", std::make_unique<Value>(1.1));
     EXPECT_TRUE(EqualsUtf16("'string': expected string, got double",
         GetPopulateError<TestType>(*value)));
   }
@@ -130,12 +131,12 @@
   {
     base::string16 error;
     std::unique_ptr<base::ListValue> params_value =
-        List(base::MakeUnique<base::Value>("Yeah!"));
+        List(std::make_unique<base::Value>("Yeah!"));
     EXPECT_TRUE(TestString::Params::Create(*params_value, &error));
   }
   {
     std::unique_ptr<base::ListValue> params_value =
-        List(base::MakeUnique<Value>(5));
+        List(std::make_unique<Value>(5));
     base::string16 error;
     EXPECT_FALSE(TestTypeInObject::Params::Create(*params_value, &error));
     EXPECT_TRUE(EqualsUtf16("'paramObject': expected dictionary, got integer",
@@ -150,7 +151,7 @@
   }
   {
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("otherType", base::MakeUnique<Value>(1.1));
+        Dictionary("otherType", std::make_unique<Value>(1.1));
     ObjectType out;
     base::string16 error;
     EXPECT_TRUE(ObjectType::Populate(*value, &out, &error));
@@ -163,13 +164,13 @@
 TEST(JsonSchemaCompilerErrorTest, UnableToPopulateArray) {
   {
     std::unique_ptr<base::ListValue> params_value =
-        List(base::MakeUnique<Value>(5));
+        List(std::make_unique<Value>(5));
     EXPECT_TRUE(EqualsUtf16("",
         GetPopulateError<ChoiceType::Integers>(*params_value)));
   }
   {
     std::unique_ptr<base::ListValue> params_value =
-        List(base::MakeUnique<Value>(5), base::MakeUnique<Value>(false));
+        List(std::make_unique<Value>(5), std::make_unique<Value>(false));
     EXPECT_TRUE(EqualsUtf16(
         "expected integer, got boolean; unable to populate array 'integers'",
         GetPopulateError<ChoiceType::Integers>(*params_value)));
@@ -179,12 +180,12 @@
 TEST(JsonSchemaCompilerErrorTest, BinaryTypeExpected) {
   {
     std::unique_ptr<base::DictionaryValue> value = Dictionary(
-        "data", base::MakeUnique<base::Value>(base::Value::Type::BINARY));
+        "data", std::make_unique<base::Value>(base::Value::Type::BINARY));
     EXPECT_TRUE(EqualsUtf16("", GetPopulateError<BinaryData>(*value)));
   }
   {
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("data", base::MakeUnique<Value>(1.1));
+        Dictionary("data", std::make_unique<Value>(1.1));
     EXPECT_TRUE(EqualsUtf16("'data': expected binary, got double",
         GetPopulateError<BinaryData>(*value)));
   }
@@ -193,12 +194,12 @@
 TEST(JsonSchemaCompilerErrorTest, ListExpected) {
   {
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("TheArray", base::MakeUnique<base::ListValue>());
+        Dictionary("TheArray", std::make_unique<base::ListValue>());
     EXPECT_TRUE(EqualsUtf16("", GetPopulateError<ArrayObject>(*value)));
   }
   {
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("TheArray", base::MakeUnique<Value>(5));
+        Dictionary("TheArray", std::make_unique<Value>(5));
     EXPECT_TRUE(EqualsUtf16("'TheArray': expected list, got integer",
         GetPopulateError<ArrayObject>(*value)));
   }
@@ -209,12 +210,12 @@
 TEST(JsonSchemaCompilerErrorTest, BadEnumValue) {
   {
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("enumeration", base::MakeUnique<base::Value>("one"));
+        Dictionary("enumeration", std::make_unique<base::Value>("one"));
     EXPECT_TRUE(EqualsUtf16("", GetPopulateError<HasEnumeration>(*value)));
   }
   {
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("enumeration", base::MakeUnique<base::Value>("bad sauce"));
+        Dictionary("enumeration", std::make_unique<base::Value>("bad sauce"));
     EXPECT_TRUE(EqualsUtf16("'Enumeration': expected \"one\" or \"two\" "
               "or \"three\", got \"bad sauce\"",
         GetPopulateError<HasEnumeration>(*value)));
@@ -226,12 +227,12 @@
 TEST(JsonSchemaCompilerErrorTest, WarnOnOptionalFailure) {
   {
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("string", base::MakeUnique<base::Value>("bling"));
+        Dictionary("string", std::make_unique<base::Value>("bling"));
     EXPECT_TRUE(EqualsUtf16("", GetPopulateError<OptionalTestType>(*value)));
   }
   {
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("string", base::MakeUnique<base::Value>(1));
+        Dictionary("string", std::make_unique<base::Value>(1));
 
     OptionalTestType out;
     base::string16 error;
@@ -245,13 +246,13 @@
 TEST(JsonSchemaCompilerErrorTest, OptionalBinaryTypeFailure) {
   {
     std::unique_ptr<base::DictionaryValue> value = Dictionary(
-        "data", base::MakeUnique<base::Value>(base::Value::Type::BINARY));
+        "data", std::make_unique<base::Value>(base::Value::Type::BINARY));
     EXPECT_TRUE(EqualsUtf16("", GetPopulateError<OptionalBinaryData>(*value)));
   }
   {
     // There's a bug with silent failures if the key doesn't exist.
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("data", base::MakeUnique<base::Value>(1));
+        Dictionary("data", std::make_unique<base::Value>(1));
 
     OptionalBinaryData out;
     base::string16 error;
@@ -265,12 +266,12 @@
 TEST(JsonSchemaCompilerErrorTest, OptionalArrayTypeFailure) {
   {
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("TheArray", base::MakeUnique<base::ListValue>());
+        Dictionary("TheArray", std::make_unique<base::ListValue>());
     EXPECT_TRUE(EqualsUtf16("", GetPopulateError<ArrayObject>(*value)));
   }
   {
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("TheArray", base::MakeUnique<Value>(5));
+        Dictionary("TheArray", std::make_unique<Value>(5));
     ArrayObject out;
     base::string16 error;
     EXPECT_TRUE(ArrayObject::Populate(*value, &out, &error));
@@ -283,13 +284,13 @@
 TEST(JsonSchemaCompilerErrorTest, OptionalUnableToPopulateArray) {
   {
     std::unique_ptr<base::ListValue> params_value =
-        List(base::MakeUnique<Value>(5));
+        List(std::make_unique<Value>(5));
     EXPECT_TRUE(EqualsUtf16("",
         GetPopulateError<OptionalChoiceType::Integers>(*params_value)));
   }
   {
     std::unique_ptr<base::ListValue> params_value =
-        List(base::MakeUnique<Value>(5), base::MakeUnique<Value>(false));
+        List(std::make_unique<Value>(5), std::make_unique<Value>(false));
     OptionalChoiceType::Integers out;
     base::string16 error;
     EXPECT_TRUE(OptionalChoiceType::Integers::Populate(*params_value, &out,
@@ -304,7 +305,7 @@
 TEST(JsonSchemaCompilerErrorTest, MultiplePopulationErrors) {
   {
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("TheArray", base::MakeUnique<Value>(5));
+        Dictionary("TheArray", std::make_unique<Value>(5));
     ArrayObject out;
     base::string16 error;
     EXPECT_TRUE(ArrayObject::Populate(*value, &out, &error));
@@ -323,13 +324,13 @@
 TEST(JsonSchemaCompilerErrorTest, TooManyKeys) {
   {
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("string", base::MakeUnique<base::Value>("yes"));
+        Dictionary("string", std::make_unique<base::Value>("yes"));
     EXPECT_TRUE(EqualsUtf16("", GetPopulateError<TestType>(*value)));
   }
   {
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("string", base::MakeUnique<base::Value>("yes"), "ohno",
-                   base::MakeUnique<base::Value>("many values"));
+        Dictionary("string", std::make_unique<base::Value>("yes"), "ohno",
+                   std::make_unique<base::Value>("many values"));
     EXPECT_TRUE(EqualsUtf16("found unexpected key 'ohno'",
         GetPopulateError<TestType>(*value)));
   }
diff --git a/tools/json_schema_compiler/test/functions_as_parameters_unittest.cc b/tools/json_schema_compiler/test/functions_as_parameters_unittest.cc
index ac0462d..9762898 100644
--- a/tools/json_schema_compiler/test/functions_as_parameters_unittest.cc
+++ b/tools/json_schema_compiler/test/functions_as_parameters_unittest.cc
@@ -4,7 +4,8 @@
 
 #include "tools/json_schema_compiler/test/functions_as_parameters.h"
 
-#include "base/memory/ptr_util.h"
+#include <memory>
+
 #include "base/values.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/tools/json_schema_compiler/test/idl_schemas_unittest.cc b/tools/json_schema_compiler/test/idl_schemas_unittest.cc
index 8ca738d..751b743 100644
--- a/tools/json_schema_compiler/test/idl_schemas_unittest.cc
+++ b/tools/json_schema_compiler/test/idl_schemas_unittest.cc
@@ -5,7 +5,6 @@
 #include <memory>
 #include <utility>
 
-#include "base/memory/ptr_util.h"
 #include "base/values.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "tools/json_schema_compiler/test/idl_basics.h"
@@ -130,7 +129,7 @@
   // use an empty array.
   base::ListValue list;
   list.AppendInteger(33);
-  list.Append(base::MakeUnique<base::ListValue>());
+  list.Append(std::make_unique<base::ListValue>());
   std::unique_ptr<Function10::Params> f10_params =
       Function10::Params::Create(list);
   ASSERT_TRUE(f10_params != NULL);
diff --git a/tools/json_schema_compiler/test/objects_unittest.cc b/tools/json_schema_compiler/test/objects_unittest.cc
index 9bc8e5e..07db0bd 100644
--- a/tools/json_schema_compiler/test/objects_unittest.cc
+++ b/tools/json_schema_compiler/test/objects_unittest.cc
@@ -6,10 +6,10 @@
 
 #include <stddef.h>
 
+#include <memory>
 #include <utility>
 
 #include "base/json/json_writer.h"
-#include "base/memory/ptr_util.h"
 #include "base/values.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "tools/json_schema_compiler/test/objects_movable.h"
@@ -21,15 +21,15 @@
 
 TEST(JsonSchemaCompilerObjectsTest, ObjectParamParamsCreate) {
   {
-    auto strings = base::MakeUnique<base::ListValue>();
+    auto strings = std::make_unique<base::ListValue>();
     strings->AppendString("one");
     strings->AppendString("two");
-    auto info_value = base::MakeUnique<base::DictionaryValue>();
+    auto info_value = std::make_unique<base::DictionaryValue>();
     info_value->Set("strings", std::move(strings));
     info_value->SetInteger("integer", 5);
     info_value->SetBoolean("boolean", true);
 
-    auto params_value = base::MakeUnique<base::ListValue>();
+    auto params_value = std::make_unique<base::ListValue>();
     params_value->Append(std::move(info_value));
     std::unique_ptr<ObjectParam::Params> params(
         ObjectParam::Params::Create(*params_value));
@@ -41,14 +41,14 @@
     EXPECT_TRUE(params->info.boolean);
   }
   {
-    auto strings = base::MakeUnique<base::ListValue>();
+    auto strings = std::make_unique<base::ListValue>();
     strings->AppendString("one");
     strings->AppendString("two");
-    auto info_value = base::MakeUnique<base::DictionaryValue>();
+    auto info_value = std::make_unique<base::DictionaryValue>();
     info_value->Set("strings", std::move(strings));
     info_value->SetInteger("integer", 5);
 
-    auto params_value = base::MakeUnique<base::ListValue>();
+    auto params_value = std::make_unique<base::ListValue>();
     params_value->Append(std::move(info_value));
     std::unique_ptr<ObjectParam::Params> params(
         ObjectParam::Params::Create(*params_value));
diff --git a/tools/json_schema_compiler/test/simple_api_unittest.cc b/tools/json_schema_compiler/test/simple_api_unittest.cc
index 707268d3..d8409d0 100644
--- a/tools/json_schema_compiler/test/simple_api_unittest.cc
+++ b/tools/json_schema_compiler/test/simple_api_unittest.cc
@@ -4,7 +4,8 @@
 
 #include "tools/json_schema_compiler/test/simple_api.h"
 
-#include "base/memory/ptr_util.h"
+#include <memory>
+
 #include "base/values.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -79,7 +80,7 @@
 TEST(JsonSchemaCompilerSimpleTest, OptionalParamsTakingNull) {
   {
     std::unique_ptr<base::ListValue> params_value(new base::ListValue());
-    params_value->Append(base::MakeUnique<base::Value>());
+    params_value->Append(std::make_unique<base::Value>());
     std::unique_ptr<OptionalString::Params> params(
         OptionalString::Params::Create(*params_value));
     EXPECT_TRUE(params.get());
@@ -100,7 +101,7 @@
 TEST(JsonSchemaCompilerSimpleTest, OptionalBeforeRequired) {
   {
     std::unique_ptr<base::ListValue> params_value(new base::ListValue());
-    params_value->Append(base::MakeUnique<base::Value>());
+    params_value->Append(std::make_unique<base::Value>());
     params_value->AppendString("asdf");
     std::unique_ptr<OptionalBeforeRequired::Params> params(
         OptionalBeforeRequired::Params::Create(*params_value));
diff --git a/tools/json_schema_compiler/test/test_util.cc b/tools/json_schema_compiler/test/test_util.cc
index d6665fcb..fd22ae6 100644
--- a/tools/json_schema_compiler/test/test_util.cc
+++ b/tools/json_schema_compiler/test/test_util.cc
@@ -9,7 +9,6 @@
 
 #include "base/json/json_reader.h"
 #include "base/logging.h"
-#include "base/memory/ptr_util.h"
 
 namespace json_schema_compiler {
 namespace test_util {
@@ -25,13 +24,13 @@
 }
 
 std::unique_ptr<base::ListValue> List(std::unique_ptr<base::Value> a) {
-  auto list = base::MakeUnique<base::ListValue>();
+  auto list = std::make_unique<base::ListValue>();
   list->Append(std::move(a));
   return list;
 }
 std::unique_ptr<base::ListValue> List(std::unique_ptr<base::Value> a,
                                       std::unique_ptr<base::Value> b) {
-  auto list = base::MakeUnique<base::ListValue>();
+  auto list = std::make_unique<base::ListValue>();
   list->Append(std::move(a));
   list->Append(std::move(b));
   return list;
@@ -39,7 +38,7 @@
 std::unique_ptr<base::ListValue> List(std::unique_ptr<base::Value> a,
                                       std::unique_ptr<base::Value> b,
                                       std::unique_ptr<base::Value> c) {
-  auto list = base::MakeUnique<base::ListValue>();
+  auto list = std::make_unique<base::ListValue>();
   list->Append(std::move(a));
   list->Append(std::move(b));
   list->Append(std::move(c));
@@ -49,7 +48,7 @@
 std::unique_ptr<base::DictionaryValue> Dictionary(
     const std::string& ak,
     std::unique_ptr<base::Value> av) {
-  auto dict = base::MakeUnique<base::DictionaryValue>();
+  auto dict = std::make_unique<base::DictionaryValue>();
   dict->SetWithoutPathExpansion(ak, std::move(av));
   return dict;
 }
@@ -58,7 +57,7 @@
     std::unique_ptr<base::Value> av,
     const std::string& bk,
     std::unique_ptr<base::Value> bv) {
-  auto dict = base::MakeUnique<base::DictionaryValue>();
+  auto dict = std::make_unique<base::DictionaryValue>();
   dict->SetWithoutPathExpansion(ak, std::move(av));
   dict->SetWithoutPathExpansion(bk, std::move(bv));
   return dict;
@@ -70,7 +69,7 @@
     std::unique_ptr<base::Value> bv,
     const std::string& ck,
     std::unique_ptr<base::Value> cv) {
-  auto dict = base::MakeUnique<base::DictionaryValue>();
+  auto dict = std::make_unique<base::DictionaryValue>();
   dict->SetWithoutPathExpansion(ak, std::move(av));
   dict->SetWithoutPathExpansion(bk, std::move(bv));
   dict->SetWithoutPathExpansion(ck, std::move(cv));
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index fafaf33..1578278 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -158,6 +158,7 @@
       'ClangToTLinux (dbg)': 'clang_tot_shared_debug',
       'ClangToTLinuxASan': 'clang_tot_asan_lsan_static_release',
       'ClangToTLinuxASanLibfuzzer': 'release_libfuzzer_asan_clang_tot',
+      'ClangToTLinuxMSan': 'clang_tot_msan_release_bot',
       'ClangToTLinuxLLD': 'clang_tot_lld_release_shared',
       'ClangToTLinuxThinLTO': 'clang_tot_release_full_symbols_thin_lto_static_use_lld',
       'ClangToTLinuxUBSanVptr': 'clang_tot_edge_ubsan_no_recover_hack_static_release',
@@ -1137,6 +1138,10 @@
       'clang_tot', 'full_symbols', 'shared', 'release',
     ],
 
+    'clang_tot_msan_release_bot': [
+      'clang_tot', 'msan', 'release_bot',
+    ],
+
     'clang_tot_shared_debug': [
       'clang_tot', 'shared', 'debug',
     ],
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 676720a..7084925 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -492,6 +492,20 @@
       label="The user saw the dialog but closed it without adding an engine"/>
 </enum>
 
+<enum name="AddToHomescreenTimeoutStatus">
+  <int value="0" label="No timeout, site is not a PWA"/>
+  <int value="1" label="No timeout, site is a PWA"/>
+  <int value="2" label="Timeout during manifest/icon fetch, site is not a PWA"/>
+  <int value="3" label="Timeout during manifest/icon fetch, site is a PWA"/>
+  <int value="4"
+      label="Timeout during manifest/icon fetch, site status unknown"/>
+  <int value="5"
+      label="Timeout during installability check, site is not a PWA"/>
+  <int value="6" label="Timeout during installability check , site is a PWA"/>
+  <int value="7"
+      label="Timeout during installability check, site status unknown"/>
+</enum>
+
 <enum name="AdSamplerTriggerAction">
   <int value="0" label="The trigger was activated"/>
   <int value="1" label="An ad was found and data was collected"/>
@@ -23193,6 +23207,7 @@
   <int value="-1887053262"
       label="enable-zero-suggest-most-visited-without-serp"/>
   <int value="-1884385890" label="AutofillUpstreamShowGoogleLogo:enabled"/>
+  <int value="-1883170077" label="EnableHtmlBaseUsernameDetector:disabled"/>
   <int value="-1882330924" label="NTPArticleSuggestions:enabled"/>
   <int value="-1880355454" label="disable-topchrome-md"/>
   <int value="-1876881908"
@@ -24436,6 +24451,7 @@
   <int value="1891210939" label="enable-blink-features"/>
   <int value="1892201400" label="enable-password-separated-signin-flow"/>
   <int value="1893317228" label="WebPayments:enabled"/>
+  <int value="1893463366" label="EnableHtmlBaseUsernameDetector:enabled"/>
   <int value="1896456311" label="enable-password-save-in-page-navigation"/>
   <int value="1898231011" label="enable-native-notifications"/>
   <int value="1900529524" label="disable-touch-drag-drop"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index e07e909..b0e60fb 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -67906,6 +67906,14 @@
   </summary>
 </histogram>
 
+<histogram name="SafeBrowsing.V4UnusedStoreFileExists.V3" enum="BooleanExists">
+  <owner>vakh@chromium.org</owner>
+  <summary>
+    Track the presence of Safe Browsing list files used for PVer3, which has
+    been deprecated. Logged once per startup.
+  </summary>
+</histogram>
+
 <histogram name="SafeBrowsing.V4Update.Network.Result"
     enum="CombinedHttpResponseAndNetErrorCode">
   <owner>vakh@chromium.org</owner>
@@ -88592,6 +88600,16 @@
   </summary>
 </histogram>
 
+<histogram name="Webapp.InstallabilityCheckStatus.AddToHomescreenTimeout"
+    enum="AddToHomescreenTimeoutStatus">
+  <owner>dominickn@chromium.org</owner>
+  <summary>
+    Records whether or not the check that a site is or isn't a Progressive Web
+    App (PWA) completes before the add to homescreen dialog on Android forcibly
+    times out and falls back to generating an icon.
+  </summary>
+</histogram>
+
 <histogram name="Webapp.InstallabilityCheckStatus.MenuItemAddToHomescreen"
     enum="InstallabilityCheckStatus">
   <owner>dominickn@chromium.org</owner>
@@ -101998,6 +102016,21 @@
   <affected-histogram name="SafeBrowsing.V4UnusedStoreFileExists"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="SafeBrowsing.V4Store.V3.Metrics" separator=".">
+  <suffix name="Bloom"/>
+  <suffix name="BloomPrefixSet"/>
+  <suffix name="CsdWhitelist"/>
+  <suffix name="Download"/>
+  <suffix name="DownloadWhitelist"/>
+  <suffix name="ExtensionBlacklist"/>
+  <suffix name="IPBlacklist"/>
+  <suffix name="ModuleWhitelist"/>
+  <suffix name="ResourceBlacklist"/>
+  <suffix name="UwSList"/>
+  <suffix name="UwSListPrefixSet"/>
+  <affected-histogram name="SafeBrowsing.V4UnusedStoreFileExists.V3"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="SafeBrowsingLists" separator=".">
   <suffix name="Browse" label="Browse"/>
   <suffix name="Download" label="Download"/>
diff --git a/tools/perf/page_sets/data/typical_10_mobile.json b/tools/perf/page_sets/data/typical_10_mobile.json
index e2aad23..40b1439 100644
--- a/tools/perf/page_sets/data/typical_10_mobile.json
+++ b/tools/perf/page_sets/data/typical_10_mobile.json
@@ -1,34 +1,34 @@
 {
     "archives": {
         "http://de.m.wikipedia.org/wiki/K%C3%B6lner_Dom": {
-            "DEFAULT": "typical_10_mobile_001.wpr"
+            "DEFAULT": "typical_10_mobile_001.wprgo"
         },
         "http://m.chiebukuro.yahoo.co.jp/detail/q10136829180": {
-            "DEFAULT": "typical_10_mobile_000.wpr"
+            "DEFAULT": "typical_10_mobile_000.wprgo"
         },
         "http://m.ebay.com/itm/351157205404": {
-            "DEFAULT": "typical_10_mobile_000.wpr"
+            "DEFAULT": "typical_10_mobile_000.wprgo"
         },
         "http://m.facebook.com/barackobama": {
-            "DEFAULT": "typical_10_mobile_000.wpr"
+            "DEFAULT": "typical_10_mobile_000.wprgo"
         },
         "http://m.huffpost.com/us/entry/6004486": {
-            "DEFAULT": "typical_10_mobile_000.wpr"
+            "DEFAULT": "typical_10_mobile_000.wprgo"
         },
         "http://m.ynet.co.il": {
-            "DEFAULT": "typical_10_mobile_000.wpr"
+            "DEFAULT": "typical_10_mobile_000.wprgo"
         },
         "http://siriuslymeg.tumblr.com/": {
-            "DEFAULT": "typical_10_mobile_000.wpr"
+            "DEFAULT": "typical_10_mobile_000.wprgo"
         },
         "http://wapbaike.baidu.com/": {
-            "DEFAULT": "typical_10_mobile_000.wpr"
+            "DEFAULT": "typical_10_mobile_000.wprgo"
         },
         "http://www.cnn.com/2014/03/31/showbiz/tv/himym-finale/index.html": {
-            "DEFAULT": "typical_10_mobile_000.wpr"
+            "DEFAULT": "typical_10_mobile_000.wprgo"
         },
         "http://www.rg.ru/2014/10/21/cska-site.html": {
-            "DEFAULT": "typical_10_mobile_000.wpr"
+            "DEFAULT": "typical_10_mobile_000.wprgo"
         }
     },
     "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.",
diff --git a/tools/perf/page_sets/data/typical_10_mobile_000.wpr.sha1 b/tools/perf/page_sets/data/typical_10_mobile_000.wpr.sha1
deleted file mode 100644
index 4fc7168..0000000
--- a/tools/perf/page_sets/data/typical_10_mobile_000.wpr.sha1
+++ /dev/null
@@ -1 +0,0 @@
-88abc57bafa161d3536f7daf5b792a4a9b7ef1fc
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/typical_10_mobile_000.wprgo.sha1 b/tools/perf/page_sets/data/typical_10_mobile_000.wprgo.sha1
new file mode 100644
index 0000000..3c43c49
--- /dev/null
+++ b/tools/perf/page_sets/data/typical_10_mobile_000.wprgo.sha1
@@ -0,0 +1 @@
+675bce83c65987b425305731ea4dcc346cf31735
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/typical_10_mobile_001.wpr.sha1 b/tools/perf/page_sets/data/typical_10_mobile_001.wpr.sha1
deleted file mode 100644
index 43b9f24..0000000
--- a/tools/perf/page_sets/data/typical_10_mobile_001.wpr.sha1
+++ /dev/null
@@ -1 +0,0 @@
-f7b8be4bccbd200712f599c1f652a78f6d4ea67b
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/typical_10_mobile_001.wprgo.sha1 b/tools/perf/page_sets/data/typical_10_mobile_001.wprgo.sha1
new file mode 100644
index 0000000..54060189
--- /dev/null
+++ b/tools/perf/page_sets/data/typical_10_mobile_001.wprgo.sha1
@@ -0,0 +1 @@
+4e370dd68389ac7a48acc76ed12599794010e565
\ No newline at end of file
diff --git a/tools/perf/page_sets/system_health/expectations.py b/tools/perf/page_sets/system_health/expectations.py
index 9dffd5d..606e36ce 100644
--- a/tools/perf/page_sets/system_health/expectations.py
+++ b/tools/perf/page_sets/system_health/expectations.py
@@ -125,8 +125,8 @@
                       'crbug.com/676336')
     self.DisableStory('browse:news:reddit', [expectations.ALL_DESKTOP],
                       'crbug.com/759777')
-    self.DisableStory('browse:news:cnn',
-                      [expectations.ALL_MAC], 'crbug.com/728576')
+    self.DisableStory('browse:news:cnn', [expectations.ALL_DESKTOP],
+                      'mac:crbug.com/728576, all:crbug.com/759777')
     self.DisableStory('browse:tools:earth', [expectations.ALL_DESKTOP],
                       'crbug.com/708590')
     self.DisableStory('browse:tools:maps', [expectations.ALL_DESKTOP],
diff --git a/tools/win/ShowThreadNames/ReadMe.txt b/tools/win/ShowThreadNames/ReadMe.txt
index c4bf66ab..e322aa3c 100644
--- a/tools/win/ShowThreadNames/ReadMe.txt
+++ b/tools/win/ShowThreadNames/ReadMe.txt
@@ -2,11 +2,11 @@
 This tool is designed to test the usage of the SetThreadDescription WinAPI in
 Chrome. In Chrome, the SetThreadDescription API has been enabled to set thread
 names. However, since there is no tool support to retrieve thread names set by
-GetThreadDescription, we'll still rely on SetNameInternal function in 
+GetThreadDescription, we'll still rely on SetNameInternal function in
 platform_thread_win.cc to set thread names. Despite this, we need a tool to demo
 the SetThreadDescription API works, even without the debugger to be present.
 
-The problem setting can be referred to 
+The problem setting can be referred to
 https://bugs.chromium.org/p/chromium/issues/detail?id=684203
 
 This tool incorporates the GetThreadDescription API trying to get names of all
@@ -20,11 +20,11 @@
 later ones.
 
 [How to use it]
-Please download the three files (.cc, .sln, .vcxproj) and compile the code in 
-Visual Studio. Run "ShowThreadNames.exe" either from the build directory or from 
-Visual Studio. No parameters are needed. This tool allows interaction with the 
-user. Once launched, it will show "Please enter the process Id, or "quit" to 
-end the program :" on the terminal. Simply type in the ID of any Chrome process 
+Please download the three files (.cc, .sln, .vcxproj) and compile the code in
+Visual Studio. Run "ShowThreadNames.exe" either from the build directory or from
+Visual Studio. No parameters are needed. This tool allows interaction with the
+user. Once launched, it will show "Please enter the process Id, or "quit" to
+end the program :" on the terminal. Simply type in the ID of any Chrome process
 you are interested in, and you will get output like below:
 
 thread_ID   thread_name
@@ -37,10 +37,7 @@
 2256    AudioThread
 9308    BrokerEvent
 5668    BrowserWatchdog
-4352    Chrome_CacheThread
-12268   Chrome_DBThread
 8616    Chrome_FileThread
-1072    Chrome_FileUserBlockingThread
 8280    Chrome_HistoryThread
 7472    Chrome_IOThread
 6336    Chrome_ProcessLauncherThread
@@ -55,6 +52,6 @@
 8216    TaskSchedulerServiceThread
 11088   VideoCaptureThread
 
-The threads are sorted by their names. Note that some threads have no names in 
-this example. If checking them using Visual Studio debugger, it is found that 
+The threads are sorted by their names. Note that some threads have no names in
+this example. If checking them using Visual Studio debugger, it is found that
 they are ntdll.dll!WorkerThreads.
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index 4c004d18..1812039 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -2269,8 +2269,21 @@
 }
 
 STDMETHODIMP AXPlatformNodeWin::get_caretOffset(LONG* offset) {
+  WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_CARET_OFFSET);
   COM_OBJECT_VALIDATE_1_ARG(offset);
-  *offset = static_cast<LONG>(GetIntAttribute(AX_ATTR_TEXT_SEL_END));
+  AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
+  *offset = 0;
+
+  if (!HasCaret())
+    return S_FALSE;
+
+  int selection_start, selection_end;
+  GetSelectionOffsets(&selection_start, &selection_end);
+  // The caret is always at the end of the selection.
+  *offset = selection_end;
+  if (*offset < 0)
+    return S_FALSE;
+
   return S_OK;
 }
 
@@ -3638,7 +3651,7 @@
   if (*offset == IA2_TEXT_OFFSET_LENGTH) {
     *offset = static_cast<LONG>(GetText().length());
   } else if (*offset == IA2_TEXT_OFFSET_CARET) {
-    get_caretOffset(offset);
+    *offset = static_cast<LONG>(GetIntAttribute(AX_ATTR_TEXT_SEL_END));
   }
 }
 
diff --git a/ui/accessibility/platform/ax_platform_node_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
index afa8066..799a36c 100644
--- a/ui/accessibility/platform/ax_platform_node_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
@@ -2695,4 +2695,64 @@
   EXPECT_EQ(1, selections);
 }
 
-}  // namespace ui
\ No newline at end of file
+TEST_F(AXPlatformNodeWinTest,
+       TestIAccessibleTextTextFieldGetCaretOffsetNoCaret) {
+  Init(BuildTextField());
+
+  ScopedComPtr<IAccessible2> ia2_text_field =
+      ToIAccessible2(GetRootIAccessible());
+  ScopedComPtr<IAccessibleText> text_field;
+  ia2_text_field.CopyTo(text_field.GetAddressOf());
+  ASSERT_NE(nullptr, text_field.Get());
+
+  LONG offset;
+  EXPECT_EQ(S_FALSE, text_field->get_caretOffset(&offset));
+  EXPECT_EQ(0, offset);
+}
+
+TEST_F(AXPlatformNodeWinTest,
+       TestIAccessibleTextTextFieldGetCaretOffsetHasCaret) {
+  Init(BuildTextFieldWithSelectionRange(1, 2));
+
+  ScopedComPtr<IAccessible2> ia2_text_field =
+      ToIAccessible2(GetRootIAccessible());
+  ScopedComPtr<IAccessibleText> text_field;
+  ia2_text_field.CopyTo(text_field.GetAddressOf());
+  ASSERT_NE(nullptr, text_field.Get());
+
+  LONG offset;
+  EXPECT_HRESULT_SUCCEEDED(text_field->get_caretOffset(&offset));
+  EXPECT_EQ(2, offset);
+}
+
+TEST_F(AXPlatformNodeWinTest,
+       TestIAccessibleTextContextEditableGetCaretOffsetNoCaret) {
+  Init(BuildContentEditable());
+
+  ScopedComPtr<IAccessible2> ia2_text_field =
+      ToIAccessible2(GetRootIAccessible());
+  ScopedComPtr<IAccessibleText> text_field;
+  ia2_text_field.CopyTo(text_field.GetAddressOf());
+  ASSERT_NE(nullptr, text_field.Get());
+
+  LONG offset;
+  EXPECT_EQ(S_FALSE, text_field->get_caretOffset(&offset));
+  EXPECT_EQ(0, offset);
+}
+
+TEST_F(AXPlatformNodeWinTest,
+       TestIAccessibleTextContentEditableGetCaretOffsetHasCaret) {
+  Init(BuildContentEditableWithSelectionRange(1, 2));
+
+  ScopedComPtr<IAccessible2> ia2_text_field =
+      ToIAccessible2(GetRootIAccessible());
+  ScopedComPtr<IAccessibleText> text_field;
+  ia2_text_field.CopyTo(text_field.GetAddressOf());
+  ASSERT_NE(nullptr, text_field.Get());
+
+  LONG offset;
+  EXPECT_HRESULT_SUCCEEDED(text_field->get_caretOffset(&offset));
+  EXPECT_EQ(2, offset);
+}
+
+}  // namespace ui
diff --git a/ui/android/java/src/org/chromium/ui/base/EventForwarder.java b/ui/android/java/src/org/chromium/ui/base/EventForwarder.java
index 3736b081..6dae114 100644
--- a/ui/android/java/src/org/chromium/ui/base/EventForwarder.java
+++ b/ui/android/java/src/org/chromium/ui/base/EventForwarder.java
@@ -53,9 +53,17 @@
         // TODO(mustaq): Should we include MotionEvent.TOOL_TYPE_STYLUS here?
         // crbug.com/592082
         if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
-            // Mouse button info is incomplete on L and below
-            int apiVersion = Build.VERSION.SDK_INT;
-            if (apiVersion >= android.os.Build.VERSION_CODES.M) {
+            // Skip firing mouse events in the follwoing cases:
+            // - In Android L and below, where mouse button info is incomplete.
+            // - A move w/o a button press, which represents a trackpad scroll. Real mouse moves w/o
+            //   buttons goes to onHoverEvent.
+            final int apiVersion = Build.VERSION.SDK_INT;
+            final boolean isTouchpadScroll = event.getButtonState() == 0
+                    && (event.getActionMasked() == MotionEvent.ACTION_DOWN
+                               || event.getActionMasked() == MotionEvent.ACTION_MOVE
+                               || event.getActionMasked() == MotionEvent.ACTION_UP);
+
+            if (apiVersion >= android.os.Build.VERSION_CODES.M && !isTouchpadScroll) {
                 return onMouseEvent(event);
             }
         }
diff --git a/ui/app_list/app_list_features.cc b/ui/app_list/app_list_features.cc
index c866006..3ba8d12 100644
--- a/ui/app_list/app_list_features.cc
+++ b/ui/app_list/app_list_features.cc
@@ -25,6 +25,8 @@
     "EnablePlayStoreAppSearch", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kEnablePlayStoreAppSearchDefaultOn{
     "EnablePlayStoreAppSearch", base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kEnableAppListFocus{"EnableAppListFocus",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
 namespace {
 
@@ -79,6 +81,10 @@
   return base::FeatureList::IsEnabled(GetPlaystoreAppSearchFeature());
 }
 
+bool IsAppListFocusEnabled() {
+  return base::FeatureList::IsEnabled(kEnableAppListFocus);
+}
+
 std::string AnswerServerUrl() {
   const std::string experiment_url = base::GetFieldTrialParamValueByFeature(
       GetAnswerCardFeature(), "ServerUrl");
diff --git a/ui/app_list/app_list_features.h b/ui/app_list/app_list_features.h
index 4b7e0153..834414e 100644
--- a/ui/app_list/app_list_features.h
+++ b/ui/app_list/app_list_features.h
@@ -38,12 +38,20 @@
 APP_LIST_EXPORT extern const base::Feature kEnablePlayStoreAppSearchDefaultOff;
 APP_LIST_EXPORT extern const base::Feature kEnablePlayStoreAppSearchDefaultOn;
 
+// Enables the app list focus. In this mode, many views become focusable. Focus
+// transition are handled by FocusManager and accessibility focus transition can
+// be triggered properly on search+arrow key as standard.
+// TODO(weidongg/766807) Remove this flag when the related changes become
+// stable.
+APP_LIST_EXPORT extern const base::Feature kEnableAppListFocus;
+
 bool APP_LIST_EXPORT IsAnswerCardEnabled();
 bool APP_LIST_EXPORT IsAnswerCardDarkRunEnabled();
 bool APP_LIST_EXPORT IsBackgroundBlurEnabled();
 bool APP_LIST_EXPORT IsFullscreenAppListEnabled();
 bool APP_LIST_EXPORT IsTouchFriendlySearchResultsPageEnabled();
 bool APP_LIST_EXPORT IsPlayStoreAppSearchEnabled();
+bool APP_LIST_EXPORT IsAppListFocusEnabled();
 std::string APP_LIST_EXPORT AnswerServerUrl();
 std::string APP_LIST_EXPORT AnswerServerQuerySuffix();
 
diff --git a/ui/app_list/views/app_list_folder_view.cc b/ui/app_list/views/app_list_folder_view.cc
index 5278349bf..13717ae 100644
--- a/ui/app_list/views/app_list_folder_view.cc
+++ b/ui/app_list/views/app_list_folder_view.cc
@@ -8,6 +8,7 @@
 
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/app_list/app_list_constants.h"
+#include "ui/app_list/app_list_features.h"
 #include "ui/app_list/app_list_folder_item.h"
 #include "ui/app_list/app_list_model.h"
 #include "ui/app_list/views/app_list_item_view.h"
@@ -50,7 +51,8 @@
       view_model_(new views::ViewModel),
       model_(model),
       folder_item_(NULL),
-      hide_for_reparent_(false) {
+      hide_for_reparent_(false),
+      is_app_list_focus_enabled_(features::IsAppListFocusEnabled()) {
   AddChildView(folder_header_view_);
   view_model_->Add(folder_header_view_, kIndexFolderHeader);
 
@@ -128,6 +130,11 @@
 }
 
 bool AppListFolderView::OnKeyPressed(const ui::KeyEvent& event) {
+  if (is_app_list_focus_enabled_) {
+    // TODO(weidongg/766807) Remove this function when the flag is enabled by
+    // default.
+    return false;
+  }
   // Process TAB if focus should go to header; otherwise, AppsGridView will do
   // the right thing.
   if (event.key_code() == ui::VKEY_TAB) {
diff --git a/ui/app_list/views/app_list_folder_view.h b/ui/app_list/views/app_list_folder_view.h
index cacbb9e..6da2b8c 100644
--- a/ui/app_list/views/app_list_folder_view.h
+++ b/ui/app_list/views/app_list_folder_view.h
@@ -120,6 +120,9 @@
 
   base::string16 accessible_name_;
 
+  // Whether the app list focus is enabled.
+  const bool is_app_list_focus_enabled_;
+
   DISALLOW_COPY_AND_ASSIGN(AppListFolderView);
 };
 
diff --git a/ui/app_list/views/app_list_item_view.cc b/ui/app_list/views/app_list_item_view.cc
index 4a53331..e7a49902 100644
--- a/ui/app_list/views/app_list_item_view.cc
+++ b/ui/app_list/views/app_list_item_view.cc
@@ -77,6 +77,8 @@
       title_(new views::Label),
       progress_bar_(new views::ProgressBar),
       is_fullscreen_app_list_enabled_(features::IsFullscreenAppListEnabled()) {
+  if (features::IsAppListFocusEnabled())
+    SetFocusBehavior(FocusBehavior::ALWAYS);
   if (!is_fullscreen_app_list_enabled_) {
     shadow_animator_.reset(new ImageShadowAnimator(this));
     shadow_animator_->animation()->SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN);
@@ -481,6 +483,14 @@
   return true;
 }
 
+void AppListItemView::OnFocus() {
+  apps_grid_view_->SetSelectedView(this);
+}
+
+void AppListItemView::OnBlur() {
+  apps_grid_view_->ClearSelectedView(this);
+}
+
 void AppListItemView::OnGestureEvent(ui::GestureEvent* event) {
   switch (event->type()) {
     case ui::ET_GESTURE_SCROLL_BEGIN:
diff --git a/ui/app_list/views/app_list_item_view.h b/ui/app_list/views/app_list_item_view.h
index 2f51be2..ffb6587 100644
--- a/ui/app_list/views/app_list_item_view.h
+++ b/ui/app_list/views/app_list_item_view.h
@@ -142,6 +142,8 @@
   bool OnMousePressed(const ui::MouseEvent& event) override;
   void OnMouseReleased(const ui::MouseEvent& event) override;
   bool OnMouseDragged(const ui::MouseEvent& event) override;
+  void OnFocus() override;
+  void OnBlur() override;
 
   // AppListItemObserver overrides:
   void ItemIconChanged() override;
diff --git a/ui/app_list/views/app_list_view.cc b/ui/app_list/views/app_list_view.cc
index 186e24d..cd6494a0 100644
--- a/ui/app_list/views/app_list_view.cc
+++ b/ui/app_list/views/app_list_view.cc
@@ -1158,6 +1158,13 @@
   if (new_state_override == CLOSED) {
     return;
   }
+
+  // Reset the focus to initially focused view. This should be done before
+  // updating visibility of views, because setting focused view invisible
+  // automatically moves focus to next focusable view, which potentially causes
+  // bugs.
+  GetInitiallyFocusedView()->RequestFocus();
+
   // Updates the visibility of app list items according to the change of
   // |app_list_state_|.
   app_list_main_view_->contents_view()
diff --git a/ui/app_list/views/app_list_view_unittest.cc b/ui/app_list/views/app_list_view_unittest.cc
index ef9b702..6d539dfa 100644
--- a/ui/app_list/views/app_list_view_unittest.cc
+++ b/ui/app_list/views/app_list_view_unittest.cc
@@ -24,20 +24,28 @@
 #include "ui/app_list/test/app_list_test_view_delegate.h"
 #include "ui/app_list/test/test_search_result.h"
 #include "ui/app_list/views/app_list_folder_view.h"
+#include "ui/app_list/views/app_list_item_view.h"
 #include "ui/app_list/views/app_list_main_view.h"
 #include "ui/app_list/views/apps_container_view.h"
 #include "ui/app_list/views/apps_grid_view.h"
 #include "ui/app_list/views/contents_view.h"
+#include "ui/app_list/views/expand_arrow_view.h"
 #include "ui/app_list/views/search_box_view.h"
+#include "ui/app_list/views/search_result_list_view.h"
 #include "ui/app_list/views/search_result_page_view.h"
+#include "ui/app_list/views/search_result_tile_item_list_view.h"
 #include "ui/app_list/views/search_result_tile_item_view.h"
+#include "ui/app_list/views/search_result_view.h"
 #include "ui/app_list/views/start_page_view.h"
+#include "ui/app_list/views/suggestions_container_view.h"
 #include "ui/app_list/views/test/app_list_view_test_api.h"
 #include "ui/app_list/views/test/apps_grid_view_test_api.h"
 #include "ui/app_list/views/tile_item_view.h"
+#include "ui/compositor/layer_animator.h"
 #include "ui/events/event_utils.h"
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/test/views_test_base.h"
+#include "ui/views/view_model.h"
 
 namespace app_list {
 namespace test {
@@ -296,8 +304,250 @@
   DISALLOW_COPY_AND_ASSIGN(AppListViewFullscreenTest);
 };
 
+// TODO(weidongg/766807) Remove all old focus tests after the flag is enabled
+// by default.
+class AppListViewFocusTest : public views::ViewsTestBase {
+ public:
+  AppListViewFocusTest() = default;
+  ~AppListViewFocusTest() override = default;
+
+  // testing::Test
+  void SetUp() override {
+    views::ViewsTestBase::SetUp();
+
+    // Enable app list focus.
+    scoped_feature_list_.InitAndEnableFeature(features::kEnableAppListFocus);
+
+    // Initialize app list view.
+    delegate_.reset(new AppListTestViewDelegate);
+    view_ = new AppListView(delegate_.get());
+    view_->Initialize(GetContext(), 0, false, false);
+    test_api_.reset(new AppsGridViewTestApi(apps_grid_view()));
+
+    // Add suggestion apps and app list items.
+    const int kSuggestionAppNum = 5;
+    const int kAppListItemNum = test_api_->TilesPerPage(0) + 1;
+    AppListTestModel* model = delegate_->GetTestModel();
+    for (size_t i = 0; i < kSuggestionAppNum; i++)
+      model->results()->Add(base::MakeUnique<TestStartPageSearchResult>());
+    model->PopulateApps(kAppListItemNum);
+    apps_grid_view()->ResetForShowApps();
+
+    // Disable animation timer.
+    view_->GetWidget()->GetLayer()->GetAnimator()->set_disable_timer_for_test(
+        true);
+  }
+
+  void TearDown() override {
+    view_->GetWidget()->Close();
+    views::ViewsTestBase::TearDown();
+  }
+
+  void SetAppListState(AppListView::AppListState state) {
+    if (state == AppListView::CLOSED) {
+      view_->Dismiss();
+      return;
+    }
+    view_->SetState(state);
+  }
+
+  void Show() { view_->ShowWhenReady(); }
+
+  void SimulateKeyPress(ui::KeyboardCode key_code, bool shift_down) {
+    ui::KeyEvent key_event(ui::ET_KEY_PRESSED, key_code,
+                           shift_down ? ui::EF_SHIFT_DOWN : ui::EF_NONE);
+    view_->GetWidget()->OnKeyEvent(&key_event);
+  }
+
+  // Add search results for test on focus movement.
+  void SetUpSearchResults(int tile_results_num, int list_results_num) {
+    std::vector<std::pair<SearchResult::DisplayType, int>> result_types;
+    result_types.push_back(
+        std::make_pair(SearchResult::DISPLAY_TILE, tile_results_num));
+    result_types.push_back(
+        std::make_pair(SearchResult::DISPLAY_LIST, list_results_num));
+
+    AppListModel::SearchResults* results = delegate_->GetTestModel()->results();
+    results->DeleteAll();
+    double relevance = result_types.size();
+    for (const auto& data : result_types) {
+      // Set the relevance of the results in each group in decreasing order (so
+      // the earlier groups have higher relevance, and therefore appear first).
+      relevance -= 1.0;
+      for (int i = 0; i < data.second; ++i) {
+        std::unique_ptr<TestSearchResult> result =
+            base::MakeUnique<TestSearchResult>();
+        result->set_display_type(data.first);
+        result->set_relevance(relevance);
+        results->Add(std::move(result));
+      }
+    }
+
+    // Adding results will schedule Update().
+    RunPendingMessages();
+  }
+
+  AppListView* app_list_view() { return view_; }
+
+  AppListMainView* main_view() { return view_->app_list_main_view(); }
+
+  ContentsView* contents_view() {
+    return view_->app_list_main_view()->contents_view();
+  }
+
+  AppsGridView* apps_grid_view() {
+    return main_view()
+        ->contents_view()
+        ->apps_container_view()
+        ->apps_grid_view();
+  }
+
+  SuggestionsContainerView* suggestions_container_view() {
+    return apps_grid_view()->suggestions_container_for_test();
+  }
+
+  SearchBoxView* search_box_view() { return main_view()->search_box_view(); }
+
+  views::View* focused_view() {
+    return view_->GetWidget()->GetFocusManager()->GetFocusedView();
+  }
+
+ private:
+  AppListView* view_ = nullptr;  // Owned by native widget.
+  std::unique_ptr<AppListTestViewDelegate> delegate_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+  std::unique_ptr<AppsGridViewTestApi> test_api_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppListViewFocusTest);
+};
+
 }  // namespace
 
+// Tests that the initial focus is on search box.
+TEST_F(AppListViewFocusTest, InitialFocus) {
+  Show();
+  EXPECT_EQ(search_box_view()->search_box(), focused_view());
+}
+
+// Tests the focus traversal triggered by tab in peeking state.
+TEST_F(AppListViewFocusTest, FocusTraversalInPeekingState) {
+  Show();
+  SetAppListState(AppListView::PEEKING);
+
+  // Move focus along suggestion apps.
+  for (views::View* v : suggestions_container_view()->tile_views()) {
+    SimulateKeyPress(ui::VKEY_TAB, false);
+    EXPECT_EQ(v, focused_view());
+  }
+
+  // Move focus on expand arrow.
+  SimulateKeyPress(ui::VKEY_TAB, false);
+  EXPECT_EQ(apps_grid_view()->expand_arrow_view_for_test(), focused_view());
+
+  // Move focus back to search box.
+  SimulateKeyPress(ui::VKEY_TAB, false);
+  EXPECT_EQ(search_box_view()->search_box(), focused_view());
+}
+
+// Tests the focus traversal triggered by tab in fullscreen all apps state.
+TEST_F(AppListViewFocusTest, FocusTraversalInFullscreenAllAppsState) {
+  Show();
+  SetAppListState(AppListView::FULLSCREEN_ALL_APPS);
+
+  // Move focus along suggestion apps.
+  for (views::View* v : suggestions_container_view()->tile_views()) {
+    SimulateKeyPress(ui::VKEY_TAB, false);
+    EXPECT_EQ(v, focused_view());
+  }
+
+  // Move focus along app list items.
+  const views::ViewModelT<AppListItemView>* view_model =
+      apps_grid_view()->view_model_for_test();
+  for (int i = 0; i < view_model->view_size(); ++i) {
+    SimulateKeyPress(ui::VKEY_TAB, false);
+    EXPECT_EQ(view_model->view_at(i), focused_view());
+  }
+
+  // Move focus back to search box.
+  SimulateKeyPress(ui::VKEY_TAB, false);
+  EXPECT_EQ(search_box_view()->search_box(), focused_view());
+}
+
+// Tests the focus traversal triggered by tab in half state with opened search
+// box.
+TEST_F(AppListViewFocusTest, FocusTraversalInHalfState) {
+  Show();
+
+  // Type something in search box to transition to HALF state and populate
+  // fake search results.
+  search_box_view()->search_box()->InsertText(base::UTF8ToUTF16("test"));
+  EXPECT_EQ(app_list_view()->app_list_state(), AppListView::HALF);
+  const int kTileResults = 3;
+  const int kListResults = 2;
+  SetUpSearchResults(kTileResults, kListResults);
+
+  // Move focus to close button.
+  SimulateKeyPress(ui::VKEY_TAB, false);
+  EXPECT_EQ(search_box_view()->close_button(), focused_view());
+
+  // Move focus along the search results.
+  const std::vector<SearchResultTileItemView*>& tile_views =
+      contents_view()
+          ->search_result_tile_item_list_view_for_test()
+          ->tile_views_for_test();
+  for (int i = 0; i < kTileResults; ++i) {
+    SimulateKeyPress(ui::VKEY_TAB, false);
+    EXPECT_EQ(tile_views[i], focused_view());
+  }
+  const views::View* results_container =
+      contents_view()
+          ->search_result_list_view_for_test()
+          ->results_container_for_test();
+  for (int i = 0; i < kListResults; ++i) {
+    SimulateKeyPress(ui::VKEY_TAB, false);
+    EXPECT_EQ(results_container->child_at(i), focused_view());
+  }
+
+  // Move focus back to search box.
+  SimulateKeyPress(ui::VKEY_TAB, false);
+  EXPECT_EQ(search_box_view()->search_box(), focused_view());
+}
+
+// Tests that the focus is set back onto search box after state transition.
+TEST_F(AppListViewFocusTest, FocusResetAfterStateTransition) {
+  Show();
+
+  // Type something in search box to transition to HALF state and populate
+  // fake search results.
+  search_box_view()->search_box()->InsertText(base::UTF8ToUTF16("test"));
+  const int kTileResults = 3;
+  const int kListResults = 2;
+  SetUpSearchResults(kTileResults, kListResults);
+  EXPECT_EQ(app_list_view()->app_list_state(), AppListView::HALF);
+  EXPECT_EQ(search_box_view()->search_box(), focused_view());
+
+  // Move focus to the first search result, then transition to PEEKING state.
+  SimulateKeyPress(ui::VKEY_TAB, false);
+  SimulateKeyPress(ui::VKEY_TAB, false);
+  SetAppListState(AppListView::PEEKING);
+  EXPECT_EQ(app_list_view()->app_list_state(), AppListView::PEEKING);
+  EXPECT_EQ(search_box_view()->search_box(), focused_view());
+
+  // Move focus to the first suggestion app, then transition to
+  // FULLSCREEN_ALL_APPS state.
+  SimulateKeyPress(ui::VKEY_TAB, false);
+  SetAppListState(AppListView::FULLSCREEN_ALL_APPS);
+  EXPECT_EQ(app_list_view()->app_list_state(),
+            AppListView::FULLSCREEN_ALL_APPS);
+  EXPECT_EQ(search_box_view()->search_box(), focused_view());
+
+  // Move focus to the first suggestion app, then transition to PEEKING state.
+  SimulateKeyPress(ui::VKEY_TAB, false);
+  SetAppListState(AppListView::PEEKING);
+  EXPECT_EQ(app_list_view()->app_list_state(), AppListView::PEEKING);
+  EXPECT_EQ(search_box_view()->search_box(), focused_view());
+}
+
 // Tests that opening the app list opens in peeking mode by default.
 TEST_F(AppListViewFullscreenTest, ShowPeekingByDefault) {
   Initialize(0, false, false);
diff --git a/ui/app_list/views/apps_grid_view.cc b/ui/app_list/views/apps_grid_view.cc
index d728dfc..9c1e1334 100644
--- a/ui/app_list/views/apps_grid_view.cc
+++ b/ui/app_list/views/apps_grid_view.cc
@@ -306,6 +306,7 @@
       contents_view_(contents_view),
       bounds_animator_(this),
       is_fullscreen_app_list_enabled_(features::IsFullscreenAppListEnabled()),
+      is_app_list_focus_enabled_(features::IsAppListFocusEnabled()),
       page_flip_delay_in_ms_(is_fullscreen_app_list_enabled_
                                  ? kPageFlipDelayInMsFullscreen
                                  : kPageFlipDelayInMs) {
@@ -317,8 +318,8 @@
   layer()->SetFillsBoundsOpaquely(false);
 
   if (is_fullscreen_app_list_enabled_ && !folder_delegate_) {
-    suggestions_container_ =
-        new SuggestionsContainerView(contents_view_, nullptr);
+    suggestions_container_ = new SuggestionsContainerView(
+        contents_view_, nullptr, &pagination_model_);
     AddChildView(suggestions_container_);
     UpdateSuggestions();
 
@@ -943,6 +944,12 @@
 }
 
 bool AppsGridView::OnKeyPressed(const ui::KeyEvent& event) {
+  if (is_app_list_focus_enabled_) {
+    // TODO(weidongg/766810) Add handling of arrow up and down here.
+    return false;
+  }
+  // TODO(weidongg/766807) Remove everything below when the flag is enabled by
+  // default.
   bool handled = false;
   if (suggestions_container_ &&
       suggestions_container_->selected_index() != -1) {
@@ -1016,6 +1023,11 @@
 }
 
 bool AppsGridView::OnKeyReleased(const ui::KeyEvent& event) {
+  if (is_app_list_focus_enabled_) {
+    // TODO(weidongg/766807) Remove this function when the flag is enabled by
+    // default.
+    return false;
+  }
   bool handled = false;
   if (selected_view_)
     handled = selected_view_->OnKeyReleased(event);
diff --git a/ui/app_list/views/apps_grid_view.h b/ui/app_list/views/apps_grid_view.h
index b7257fb3..96716ac9 100644
--- a/ui/app_list/views/apps_grid_view.h
+++ b/ui/app_list/views/apps_grid_view.h
@@ -625,6 +625,9 @@
   // True if the fullscreen app list feature is enabled.
   const bool is_fullscreen_app_list_enabled_;
 
+  // Whether the app list focus is enabled.
+  const bool is_app_list_focus_enabled_;
+
   // Delay in milliseconds of when |page_flip_timer_| should fire after user
   // drags an item near the edges.
   int page_flip_delay_in_ms_;
diff --git a/ui/app_list/views/contents_view.cc b/ui/app_list/views/contents_view.cc
index d7bc2010..44e87016 100644
--- a/ui/app_list/views/contents_view.cc
+++ b/ui/app_list/views/contents_view.cc
@@ -484,6 +484,10 @@
 }
 
 bool ContentsView::OnKeyPressed(const ui::KeyEvent& event) {
+  if (features::IsAppListFocusEnabled())
+    return false;
+  // TODO(weidongg/766807) Remove this function when the flag is enabled by
+  // default.
   if (app_list_pages_[GetActivePageIndex()]->OnKeyPressed(event))
     return true;
   if (event.key_code() != ui::VKEY_TAB &&
diff --git a/ui/app_list/views/expand_arrow_view.cc b/ui/app_list/views/expand_arrow_view.cc
index 8d6d93a..1270a5a 100644
--- a/ui/app_list/views/expand_arrow_view.cc
+++ b/ui/app_list/views/expand_arrow_view.cc
@@ -8,6 +8,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "ui/app_list/app_list_constants.h"
+#include "ui/app_list/app_list_features.h"
 #include "ui/app_list/vector_icons/vector_icons.h"
 #include "ui/app_list/views/app_list_view.h"
 #include "ui/app_list/views/contents_view.h"
@@ -67,6 +68,8 @@
       contents_view_(contents_view),
       app_list_view_(app_list_view),
       weak_ptr_factory_(this) {
+  if (features::IsAppListFocusEnabled())
+    SetFocusBehavior(FocusBehavior::ALWAYS);
   SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
 
@@ -152,6 +155,14 @@
   return true;
 }
 
+void ExpandArrowView::OnFocus() {
+  SetSelected(true);
+}
+
+void ExpandArrowView::OnBlur() {
+  SetSelected(false);
+}
+
 std::unique_ptr<views::InkDrop> ExpandArrowView::CreateInkDrop() {
   std::unique_ptr<views::InkDropImpl> ink_drop =
       Button::CreateDefaultInkDropImpl();
diff --git a/ui/app_list/views/expand_arrow_view.h b/ui/app_list/views/expand_arrow_view.h
index efab5bf1..6ea9baec 100644
--- a/ui/app_list/views/expand_arrow_view.h
+++ b/ui/app_list/views/expand_arrow_view.h
@@ -45,6 +45,8 @@
   gfx::Size CalculatePreferredSize() const override;
   void Layout() override;
   bool OnKeyPressed(const ui::KeyEvent& event) override;
+  void OnFocus() override;
+  void OnBlur() override;
 
   // Overridden from views::InkDropHost:
   std::unique_ptr<views::InkDrop> CreateInkDrop() override;
diff --git a/ui/app_list/views/search_box_view.cc b/ui/app_list/views/search_box_view.cc
index a4f3552..05a22b7eb 100644
--- a/ui/app_list/views/search_box_view.cc
+++ b/ui/app_list/views/search_box_view.cc
@@ -126,7 +126,10 @@
 class SearchBoxImageButton : public views::ImageButton {
  public:
   explicit SearchBoxImageButton(views::ButtonListener* listener)
-      : ImageButton(listener), selected_(false) {}
+      : ImageButton(listener), selected_(false) {
+    if (features::IsAppListFocusEnabled())
+      SetFocusBehavior(FocusBehavior::ALWAYS);
+  }
   ~SearchBoxImageButton() override {}
 
   bool selected() { return selected_; }
@@ -173,7 +176,8 @@
 class SearchBoxTextfield : public views::Textfield {
  public:
   explicit SearchBoxTextfield(SearchBoxView* search_box_view)
-      : search_box_view_(search_box_view) {}
+      : search_box_view_(search_box_view),
+        is_app_list_focus_enabled_(features::IsAppListFocusEnabled()) {}
   ~SearchBoxTextfield() override = default;
 
   // Overridden from views::View:
@@ -190,9 +194,24 @@
         ui::MENU_SOURCE_KEYBOARD);
   }
 
+  void OnFocus() override {
+    if (is_app_list_focus_enabled_)
+      search_box_view_->SetSelected(true);
+    Textfield::OnFocus();
+  }
+
+  void OnBlur() override {
+    if (is_app_list_focus_enabled_)
+      search_box_view_->SetSelected(false);
+    Textfield::OnBlur();
+  }
+
  private:
   SearchBoxView* const search_box_view_;
 
+  // Whether the app list focus is enabled.
+  const bool is_app_list_focus_enabled_;
+
   DISALLOW_COPY_AND_ASSIGN(SearchBoxTextfield);
 };
 
@@ -205,6 +224,7 @@
       search_box_(new SearchBoxTextfield(this)),
       app_list_view_(app_list_view),
       is_fullscreen_app_list_enabled_(features::IsFullscreenAppListEnabled()),
+      is_app_list_focus_enabled_(features::IsAppListFocusEnabled()),
       focused_view_(is_fullscreen_app_list_enabled_ ? FOCUS_NONE
                                                     : FOCUS_SEARCH_BOX) {
   SetPaintToLayer();
@@ -736,6 +756,35 @@
   return static_cast<ContentsView*>(contents_view_)->GetSelectedView();
 }
 
+void SearchBoxView::SetSelected(bool selected) {
+  if (!is_fullscreen_app_list_enabled_)
+    return;
+  if (selected_ == selected)
+    return;
+  selected_ = selected;
+  if (selected) {
+    // Set the ChromeVox focus to the search box.
+    search_box_->NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true);
+    if (IsSearchBoxTrimmedQueryEmpty()) {
+      // This includes two situations: query is empty or query is a string of
+      // spaces. In both situations, opened search box is hidden and we need to
+      // show a ring around search box to indicate that it is selected.
+      SetBorder(views::CreateRoundedRectBorder(
+          kSearchBoxBorderWidth, kSearchBoxFocusBorderCornerRadius,
+          kSearchBoxBorderColor));
+    }
+    if (!search_box_->text().empty()) {
+      // If query is not empty (including a string of spaces), we need to select
+      // the entire text range.
+      search_box_->SelectAll(false);
+    }
+  } else {
+    SetDefaultBorder();
+  }
+  Layout();
+  SchedulePaint();
+}
+
 void SearchBoxView::UpdateModel() {
   // Temporarily remove from observer to ignore notifications caused by us.
   model_->search_box()->RemoveObserver(this);
@@ -769,6 +818,11 @@
 
 bool SearchBoxView::HandleKeyEvent(views::Textfield* sender,
                                    const ui::KeyEvent& key_event) {
+  if (is_app_list_focus_enabled_) {
+    // TODO(weidongg/766807) Remove this function when the flag is enabled by
+    // default.
+    return false;
+  }
   if (key_event.type() == ui::ET_KEY_PRESSED) {
     if (key_event.key_code() == ui::VKEY_TAB &&
         focused_view_ != FOCUS_CONTENTS_VIEW &&
@@ -1021,33 +1075,4 @@
                                kSearchBoxBorderWidth, kSearchBoxBorderWidth));
 }
 
-void SearchBoxView::SetSelected(bool selected) {
-  if (!is_fullscreen_app_list_enabled_)
-    return;
-  if (selected_ == selected)
-    return;
-  selected_ = selected;
-  if (selected) {
-    // Set the ChromeVox focus to the search box.
-    search_box_->NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true);
-    if (IsSearchBoxTrimmedQueryEmpty()) {
-      // This includes two situations: query is empty or query is a string of
-      // spaces. In both situations, opened search box is hidden and we need to
-      // show a ring around search box to indicate that it is selected.
-      SetBorder(views::CreateRoundedRectBorder(
-          kSearchBoxBorderWidth, kSearchBoxFocusBorderCornerRadius,
-          kSearchBoxBorderColor));
-    }
-    if (!search_box_->text().empty()) {
-      // If query is not empty (including a string of spaces), we need to select
-      // the entire text range.
-      search_box_->SelectAll(false);
-    }
-  } else {
-    SetDefaultBorder();
-  }
-  Layout();
-  SchedulePaint();
-}
-
 }  // namespace app_list
diff --git a/ui/app_list/views/search_box_view.h b/ui/app_list/views/search_box_view.h
index 19d901f..a4ef1914 100644
--- a/ui/app_list/views/search_box_view.h
+++ b/ui/app_list/views/search_box_view.h
@@ -160,6 +160,9 @@
   // Returns selected view in contents view.
   views::View* GetSelectedViewInContentsView() const;
 
+  bool selected() { return selected_; }
+  void SetSelected(bool selected);
+
  private:
   // Updates model text and selection model with current Textfield info.
   void UpdateModel();
@@ -217,9 +220,6 @@
 
   void SetDefaultBorder();
 
-  bool selected() { return selected_; }
-  void SetSelected(bool selected);
-
   SearchBoxViewDelegate* delegate_;     // Not owned.
   AppListViewDelegate* view_delegate_;  // Not owned.
   AppListModel* model_ = nullptr;       // Owned by the profile-keyed service.
@@ -241,6 +241,9 @@
   // Whether the fullscreen app list feature is enabled.
   const bool is_fullscreen_app_list_enabled_;
 
+  // Whether the app list focus is enabled.
+  const bool is_app_list_focus_enabled_;
+
   SearchBoxFocus focused_view_;  // Which element has TAB'd focus.
 
   // Whether the search box is active.
diff --git a/ui/app_list/views/search_result_answer_card_view.cc b/ui/app_list/views/search_result_answer_card_view.cc
index 4d123cd1..7aa2228 100644
--- a/ui/app_list/views/search_result_answer_card_view.cc
+++ b/ui/app_list/views/search_result_answer_card_view.cc
@@ -30,6 +30,8 @@
  public:
   explicit SearchAnswerContainerView(AppListViewDelegate* view_delegate)
       : Button(this), view_delegate_(view_delegate) {
+    if (features::IsAppListFocusEnabled())
+      SetFocusBehavior(FocusBehavior::ALWAYS);
     // Center the card horizontally in the container.
     views::BoxLayout* answer_container_layout =
         new views::BoxLayout(views::BoxLayout::kHorizontal,
diff --git a/ui/app_list/views/search_result_list_view.cc b/ui/app_list/views/search_result_list_view.cc
index 6cfaa9c..71626c7 100644
--- a/ui/app_list/views/search_result_list_view.cc
+++ b/ui/app_list/views/search_result_list_view.cc
@@ -74,6 +74,11 @@
 }
 
 bool SearchResultListView::OnKeyPressed(const ui::KeyEvent& event) {
+  if (features::IsAppListFocusEnabled()) {
+    // TODO(weidongg/766807) Remove this function when the flag is enabled by
+    // default.
+    return false;
+  }
   if (selected_index() >= 0 &&
       results_container_->child_at(selected_index())->OnKeyPressed(event)) {
     return true;
diff --git a/ui/app_list/views/search_result_list_view.h b/ui/app_list/views/search_result_list_view.h
index fc56b8cd9..17e135bc 100644
--- a/ui/app_list/views/search_result_list_view.h
+++ b/ui/app_list/views/search_result_list_view.h
@@ -61,6 +61,8 @@
   int GetYSize() override;
   views::View* GetSelectedView() const override;
 
+  views::View* results_container_for_test() const { return results_container_; }
+
  private:
   friend class test::SearchResultListViewTest;
 
diff --git a/ui/app_list/views/search_result_page_view.cc b/ui/app_list/views/search_result_page_view.cc
index c1e32d4..d66dc289 100644
--- a/ui/app_list/views/search_result_page_view.cc
+++ b/ui/app_list/views/search_result_page_view.cc
@@ -143,6 +143,7 @@
 SearchResultPageView::SearchResultPageView()
     : selected_index_(0),
       is_fullscreen_app_list_enabled_(features::IsFullscreenAppListEnabled()),
+      is_app_list_focus_enabled_(features::IsAppListFocusEnabled()),
       contents_view_(new views::View) {
   SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
@@ -213,6 +214,12 @@
 }
 
 bool SearchResultPageView::OnKeyPressed(const ui::KeyEvent& event) {
+  if (is_app_list_focus_enabled_) {
+    // TODO(weidongg/766810) Add handling of arrow up and down here.
+    return false;
+  }
+  // TODO(weidongg/766807) Remove everything below when the flag is enabled by
+  // default.
   if (HasSelection() &&
       result_container_views_.at(selected_index_)->OnKeyPressed(event)) {
     return true;
diff --git a/ui/app_list/views/search_result_page_view.h b/ui/app_list/views/search_result_page_view.h
index 3215199..09e2e3d 100644
--- a/ui/app_list/views/search_result_page_view.h
+++ b/ui/app_list/views/search_result_page_view.h
@@ -78,6 +78,9 @@
 
   const bool is_fullscreen_app_list_enabled_;
 
+  // Whether the app list focus is enabled.
+  const bool is_app_list_focus_enabled_;
+
   // View containing SearchCardView instances. Owned by view hierarchy.
   views::View* const contents_view_;
 
diff --git a/ui/app_list/views/search_result_tile_item_list_view.cc b/ui/app_list/views/search_result_tile_item_list_view.cc
index fd57b36..ed9e92e4 100644
--- a/ui/app_list/views/search_result_tile_item_list_view.cc
+++ b/ui/app_list/views/search_result_tile_item_list_view.cc
@@ -67,7 +67,7 @@
       }
 
       SearchResultTileItemView* tile_item = new SearchResultTileItemView(
-          this, view_delegate, false /* Not a suggested app */,
+          this, view_delegate, nullptr, false /* Not a suggested app */,
           is_fullscreen_app_list_enabled_, is_play_store_app_search_enabled_);
       tile_item->SetParentBackgroundColor(kCardBackgroundColorFullscreen);
       tile_views_.push_back(tile_item);
@@ -79,7 +79,7 @@
         kBetweenTileSpacing));
     for (size_t i = 0; i < kNumSearchResultTiles; ++i) {
       SearchResultTileItemView* tile_item = new SearchResultTileItemView(
-          this, view_delegate, false /* Not a suggested app */,
+          this, view_delegate, nullptr, false /* Not a suggested app */,
           is_fullscreen_app_list_enabled_, is_play_store_app_search_enabled_);
       tile_item->SetParentBackgroundColor(kCardBackgroundColor);
       tile_item->SetBorder(
@@ -181,6 +181,11 @@
 }
 
 bool SearchResultTileItemListView::OnKeyPressed(const ui::KeyEvent& event) {
+  if (features::IsAppListFocusEnabled()) {
+    // TODO(weidongg/766807) Remove this function when the flag is enabled by
+    // default.
+    return false;
+  }
   int selection_index = selected_index();
   // Also count the separator when Play Store app search feature is enabled.
   const int child_index = is_play_store_app_search_enabled_
diff --git a/ui/app_list/views/search_result_tile_item_list_view.h b/ui/app_list/views/search_result_tile_item_list_view.h
index 595707d9..82dd4158 100644
--- a/ui/app_list/views/search_result_tile_item_list_view.h
+++ b/ui/app_list/views/search_result_tile_item_list_view.h
@@ -38,6 +38,10 @@
   // Overridden from views::View:
   bool OnKeyPressed(const ui::KeyEvent& event) override;
 
+  const std::vector<SearchResultTileItemView*>& tile_views_for_test() const {
+    return tile_views_;
+  }
+
  private:
   // Overridden from SearchResultContainerView:
   int DoUpdate() override;
diff --git a/ui/app_list/views/search_result_tile_item_view.cc b/ui/app_list/views/search_result_tile_item_view.cc
index babbca6..fb1f4a98 100644
--- a/ui/app_list/views/search_result_tile_item_view.cc
+++ b/ui/app_list/views/search_result_tile_item_view.cc
@@ -9,6 +9,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "ui/app_list/app_list_constants.h"
 #include "ui/app_list/app_list_view_delegate.h"
+#include "ui/app_list/pagination_model.h"
 #include "ui/app_list/search_result.h"
 #include "ui/app_list/vector_icons/vector_icons.h"
 #include "ui/app_list/views/search_result_container_view.h"
@@ -45,12 +46,14 @@
 SearchResultTileItemView::SearchResultTileItemView(
     SearchResultContainerView* result_container,
     AppListViewDelegate* view_delegate,
+    PaginationModel* pagination_model,
     bool is_suggested_app,
     bool is_fullscreen_app_list_enabled,
     bool is_play_store_search_enabled)
     : is_suggested_app_(is_suggested_app),
       result_container_(result_container),
       view_delegate_(view_delegate),
+      pagination_model_(pagination_model),
       is_fullscreen_app_list_enabled_(is_fullscreen_app_list_enabled) {
   // When |item_| is null, the tile is invisible. Calling SetSearchResult with a
   // non-null item makes the tile visible.
@@ -220,6 +223,15 @@
   return false;
 }
 
+void SearchResultTileItemView::OnFocus() {
+  if (pagination_model_ && is_recommendation() &&
+      view_delegate_->GetModel()->state() == AppListModel::STATE_APPS) {
+    // Go back to first page when app in suggestions container is focused.
+    pagination_model_->SelectPage(0, false);
+  }
+  TileItemView::OnFocus();
+}
+
 void SearchResultTileItemView::OnIconChanged() {
   SetIcon(item_->icon());
   Layout();
diff --git a/ui/app_list/views/search_result_tile_item_view.h b/ui/app_list/views/search_result_tile_item_view.h
index 62b3f53..4c72921b 100644
--- a/ui/app_list/views/search_result_tile_item_view.h
+++ b/ui/app_list/views/search_result_tile_item_view.h
@@ -22,6 +22,7 @@
 class AppListViewDelegate;
 class SearchResult;
 class SearchResultContainerView;
+class PaginationModel;
 
 // A TileItemView that displays a search result.
 class APP_LIST_EXPORT SearchResultTileItemView
@@ -31,6 +32,7 @@
  public:
   SearchResultTileItemView(SearchResultContainerView* result_container,
                            AppListViewDelegate* view_delegate,
+                           PaginationModel* pagination_model,
                            bool is_suggested_app,
                            bool is_fullscreen_app_list_enabled,
                            bool is_play_store_search_enabled);
@@ -42,6 +44,7 @@
   // Overridden from TileItemView:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
   bool OnKeyPressed(const ui::KeyEvent& event) override;
+  void OnFocus() override;
 
   // Overridden from SearchResultObserver:
   void OnIconChanged() override;
@@ -84,6 +87,8 @@
 
   AppListViewDelegate* view_delegate_;
 
+  PaginationModel* const pagination_model_;  // Owned by AppsGridView.
+
   std::unique_ptr<views::MenuRunner> context_menu_runner_;
 
   const bool is_fullscreen_app_list_enabled_;
diff --git a/ui/app_list/views/search_result_view.cc b/ui/app_list/views/search_result_view.cc
index deff6be..2fccad89 100644
--- a/ui/app_list/views/search_result_view.cc
+++ b/ui/app_list/views/search_result_view.cc
@@ -95,7 +95,10 @@
       badge_icon_(new views::ImageView),
       actions_view_(new SearchResultActionsView(this)),
       progress_bar_(new views::ProgressBar),
-      is_fullscreen_app_list_enabled_(features::IsFullscreenAppListEnabled()) {
+      is_fullscreen_app_list_enabled_(features::IsFullscreenAppListEnabled()),
+      is_app_list_focus_enabled_(features::IsAppListFocusEnabled()) {
+  if (is_app_list_focus_enabled_)
+    SetFocusBehavior(FocusBehavior::ALWAYS);
   icon_->set_can_process_events_within_subtree(false);
   badge_icon_->set_can_process_events_within_subtree(false);
 
@@ -333,7 +336,6 @@
     return;
 
   gfx::Rect content_rect(rect);
-  const bool selected = list_view_->IsResultViewSelected(this);
   const bool hover = state() == STATE_HOVERED || state() == STATE_PRESSED;
 
   if (!is_fullscreen_app_list_enabled_)
@@ -362,7 +364,8 @@
 
   // Possibly call FillRect a second time (these colours are partially
   // transparent, so the previous FillRect is not redundant).
-  if (selected) {
+  if (is_app_list_focus_enabled_ ? selected()
+                                 : list_view_->IsResultViewSelected(this)) {
     canvas->FillRect(content_rect, is_fullscreen_app_list_enabled_
                                        ? kRowSelectedColor
                                        : kSelectedColor);
@@ -407,6 +410,14 @@
   }
 }
 
+void SearchResultView::OnFocus() {
+  SetSelected(true);
+}
+
+void SearchResultView::OnBlur() {
+  SetSelected(false);
+}
+
 void SearchResultView::ButtonPressed(views::Button* sender,
                                      const ui::Event& event) {
   DCHECK(sender == this);
@@ -496,6 +507,13 @@
   list_view_->SearchResultActionActivated(this, index, event_flags);
 }
 
+void SearchResultView::SetSelected(bool selected) {
+  if (selected_ == selected)
+    return;
+  selected_ = selected;
+  SchedulePaint();
+}
+
 void SearchResultView::ShowContextMenuForView(views::View* source,
                                               const gfx::Point& point,
                                               ui::MenuSourceType source_type) {
diff --git a/ui/app_list/views/search_result_view.h b/ui/app_list/views/search_result_view.h
index e76787fe..49129ee 100644
--- a/ui/app_list/views/search_result_view.h
+++ b/ui/app_list/views/search_result_view.h
@@ -85,6 +85,8 @@
   bool OnKeyPressed(const ui::KeyEvent& event) override;
   void ChildPreferredSizeChanged(views::View* child) override;
   void PaintButtonContents(gfx::Canvas* canvas) override;
+  void OnFocus() override;
+  void OnBlur() override;
 
   // views::ButtonListener overrides:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
@@ -109,6 +111,9 @@
   // SearchResultActionsViewDelegate overrides:
   void OnSearchResultActionActivated(size_t index, int event_flags) override;
 
+  void SetSelected(bool selected);
+  bool selected() const { return selected_; }
+
   SearchResult* result_ = nullptr;  // Owned by AppListModel::SearchResults.
 
   bool is_last_result_ = false;
@@ -125,8 +130,14 @@
 
   std::unique_ptr<views::MenuRunner> context_menu_runner_;
 
+  // Whether this view is selected.
+  bool selected_ = false;
+
   const bool is_fullscreen_app_list_enabled_;
 
+  // Whether the app list focus is enabled.
+  const bool is_app_list_focus_enabled_;
+
   DISALLOW_COPY_AND_ASSIGN(SearchResultView);
 };
 
diff --git a/ui/app_list/views/start_page_view.cc b/ui/app_list/views/start_page_view.cc
index c542268..e26e8a18 100644
--- a/ui/app_list/views/start_page_view.cc
+++ b/ui/app_list/views/start_page_view.cc
@@ -73,7 +73,8 @@
       is_fullscreen_app_list_enabled_
           ? nullptr
           : new AllAppsTileItemView(app_list_main_view_->contents_view(),
-                                    app_list_view));
+                                    app_list_view),
+      nullptr);
 
   search_box_spacer_view_->SetPreferredSize(gfx::Size(
       is_fullscreen_app_list_enabled_ ? kStartPageSearchBoxWidthFullscreen
diff --git a/ui/app_list/views/suggestions_container_view.cc b/ui/app_list/views/suggestions_container_view.cc
index 4a47fa4..e2c2908 100644
--- a/ui/app_list/views/suggestions_container_view.cc
+++ b/ui/app_list/views/suggestions_container_view.cc
@@ -25,9 +25,11 @@
 
 SuggestionsContainerView::SuggestionsContainerView(
     ContentsView* contents_view,
-    AllAppsTileItemView* all_apps_button)
+    AllAppsTileItemView* all_apps_button,
+    PaginationModel* pagination_model)
     : contents_view_(contents_view),
       all_apps_button_(all_apps_button),
+      pagination_model_(pagination_model),
       is_fullscreen_app_list_enabled_(features::IsFullscreenAppListEnabled()) {
   SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
@@ -161,8 +163,8 @@
       features::IsPlayStoreAppSearchEnabled();
   for (; i < apps_num; ++i) {
     SearchResultTileItemView* tile_item = new SearchResultTileItemView(
-        this, view_delegate_, true, is_fullscreen_app_list_enabled,
-        is_play_store_app_search_enabled);
+        this, view_delegate_, pagination_model_, true,
+        is_fullscreen_app_list_enabled, is_play_store_app_search_enabled);
     if (i % kNumTilesCols == 0)
       tiles_layout_manager->StartRow(0, 0);
     tiles_layout_manager->AddView(tile_item);
diff --git a/ui/app_list/views/suggestions_container_view.h b/ui/app_list/views/suggestions_container_view.h
index 15053f5a..8f62aa9 100644
--- a/ui/app_list/views/suggestions_container_view.h
+++ b/ui/app_list/views/suggestions_container_view.h
@@ -23,7 +23,8 @@
 class SuggestionsContainerView : public SearchResultContainerView {
  public:
   SuggestionsContainerView(ContentsView* contents_view,
-                           AllAppsTileItemView* all_apps_button);
+                           AllAppsTileItemView* all_apps_button,
+                           PaginationModel* pagination_model);
   ~SuggestionsContainerView() override;
 
   TileItemView* GetTileItemView(int index);
@@ -52,6 +53,8 @@
   std::vector<SearchResultTileItemView*> search_result_tile_views_;
   AllAppsTileItemView* all_apps_button_ = nullptr;
 
+  PaginationModel* const pagination_model_;  // Owned by AppsGridView.
+
   const bool is_fullscreen_app_list_enabled_;
 
   DISALLOW_COPY_AND_ASSIGN(SuggestionsContainerView);
diff --git a/ui/app_list/views/tile_item_view.cc b/ui/app_list/views/tile_item_view.cc
index 054f574..656a5a0 100644
--- a/ui/app_list/views/tile_item_view.cc
+++ b/ui/app_list/views/tile_item_view.cc
@@ -63,6 +63,8 @@
       badge_(nullptr),
       title_(new views::Label),
       is_fullscreen_app_list_enabled_(features::IsFullscreenAppListEnabled()) {
+  if (features::IsAppListFocusEnabled())
+    SetFocusBehavior(FocusBehavior::ALWAYS);
   // Prevent the icon view from interfering with our mouse events.
   icon_->set_can_process_events_within_subtree(false);
   icon_->SetVerticalAlignment(views::ImageView::LEADING);
@@ -196,6 +198,14 @@
   return "TileItemView";
 }
 
+void TileItemView::OnFocus() {
+  SetSelected(true);
+}
+
+void TileItemView::OnBlur() {
+  SetSelected(false);
+}
+
 void TileItemView::ImageShadowAnimationProgressed(
     ImageShadowAnimator* animator) {
   icon_->SetImage(animator->shadow_image());
diff --git a/ui/app_list/views/tile_item_view.h b/ui/app_list/views/tile_item_view.h
index 3ddd293..83aa4ab 100644
--- a/ui/app_list/views/tile_item_view.h
+++ b/ui/app_list/views/tile_item_view.h
@@ -58,6 +58,8 @@
   // Overridden from views::View:
   void Layout() override;
   const char* GetClassName() const override;
+  void OnFocus() override;
+  void OnBlur() override;
 
   // Overridden from ImageShadowAnimator::Delegate:
   void ImageShadowAnimationProgressed(ImageShadowAnimator* animator) override;
@@ -80,6 +82,7 @@
   void set_is_recommendation(bool is_recommendation) {
     is_recommendation_ = is_recommendation;
   }
+  bool is_recommendation() const { return is_recommendation_; }
 
  private:
   void UpdateBackgroundColor();
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index 30921e7..e5e65cac 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -250,6 +250,18 @@
   // is necessary because of shutdown ordering (WindowTreeClient is destroyed
   // before windows).
   in_shutdown_ = true;
+
+  // Windows of type WindowMusType::OTHER were implicitly created from the
+  // server and may not have been destroyed. Delete them to ensure we don't
+  // leak.
+  WindowTracker windows_to_destroy;
+  for (auto& pair : windows_) {
+    if (pair.second->window_mus_type() == WindowMusType::OTHER)
+      windows_to_destroy.Add(pair.second->GetWindow());
+  }
+  while (!windows_to_destroy.windows().empty())
+    delete windows_to_destroy.Pop();
+
   IdToWindowMap windows;
   std::swap(windows, windows_);
   for (auto& pair : windows)
diff --git a/ui/aura/mus/window_tree_client_unittest.cc b/ui/aura/mus/window_tree_client_unittest.cc
index 4b91afb..d95fc18 100644
--- a/ui/aura/mus/window_tree_client_unittest.cc
+++ b/ui/aura/mus/window_tree_client_unittest.cc
@@ -2744,6 +2744,18 @@
   window2.Init(ui::LAYER_NOT_DRAWN);
 }
 
+TEST_F(WindowTreeClientDestructionTest, WindowsFromOtherConnectionsDeleted) {
+  std::unique_ptr<Window> other_client_window =
+      CreateWindowUsingId(window_tree_client_impl(), 10, nullptr);
+  WindowTracker window_tracker;
+  window_tracker.Add(other_client_window.get());
+  other_client_window.release();
+  DeleteWindowTreeClient();
+  // Deleting WindowTreeClient should delete the Window that was in
+  // |window_tracker|.
+  EXPECT_TRUE(window_tracker.windows().empty());
+}
+
 TEST_F(WindowTreeClientWmTest, ObservedPointerEvents) {
   const gfx::Rect bounds(1, 2, 101, 102);
   std::unique_ptr<DisplayInitParams> display_params =
diff --git a/ui/aura/window_tree_host.cc b/ui/aura/window_tree_host.cc
index 64d96e7..d73bbda0 100644
--- a/ui/aura/window_tree_host.cc
+++ b/ui/aura/window_tree_host.cc
@@ -346,6 +346,14 @@
     observer.OnHostWorkspaceChanged(this);
 }
 
+void WindowTreeHost::OnHostDisplayChanged() {
+  if (!compositor_)
+    return;
+  display::Display display =
+      display::Screen::GetScreen()->GetDisplayNearestWindow(window());
+  compositor_->SetDisplayColorSpace(display.color_space());
+}
+
 void WindowTreeHost::OnHostCloseRequested() {
   for (WindowTreeHostObserver& observer : observers_)
     observer.OnHostCloseRequested(this);
diff --git a/ui/aura/window_tree_host.h b/ui/aura/window_tree_host.h
index e1aeec5c..8267956 100644
--- a/ui/aura/window_tree_host.h
+++ b/ui/aura/window_tree_host.h
@@ -214,6 +214,7 @@
   void OnHostMovedInPixels(const gfx::Point& new_location_in_pixels);
   void OnHostResizedInPixels(const gfx::Size& new_size_in_pixels);
   void OnHostWorkspaceChanged();
+  void OnHostDisplayChanged();
   void OnHostCloseRequested();
   void OnHostActivated();
   void OnHostLostWindowCapture();
diff --git a/ui/views/cocoa/bridged_native_widget.h b/ui/views/cocoa/bridged_native_widget.h
index f5d2309..9bb2c2b 100644
--- a/ui/views/cocoa/bridged_native_widget.h
+++ b/ui/views/cocoa/bridged_native_widget.h
@@ -22,6 +22,7 @@
 #include "ui/views/widget/widget.h"
 
 @class BridgedContentView;
+@class ModalShowAnimationWithLayer;
 @class ViewsNSWindowDelegate;
 
 namespace ui {
@@ -169,6 +170,10 @@
   // Called by NativeWidgetMac when the window size constraints change.
   void OnSizeConstraintsChanged();
 
+  // Called by the window show animation when it completes and wants to destroy
+  // itself.
+  void OnShowAnimationComplete();
+
   // See widget.h for documentation.
   ui::InputMethod* GetInputMethod();
 
@@ -212,6 +217,9 @@
   bool window_visible() const { return window_visible_; }
   bool wants_to_be_visible() const { return wants_to_be_visible_; }
 
+  bool animate() const { return animate_; }
+  void set_animate(bool animate) { animate_ = animate; }
+
   // Overridden from ui::internal::InputMethodDelegate:
   ui::EventDispatchDetails DispatchKeyEventPostIME(ui::KeyEvent* key) override;
 
@@ -294,6 +302,7 @@
   base::scoped_nsobject<NSWindow> window_;
   base::scoped_nsobject<ViewsNSWindowDelegate> window_delegate_;
   base::scoped_nsobject<BridgedContentView> bridged_view_;
+  base::scoped_nsobject<ModalShowAnimationWithLayer> show_animation_;
   std::unique_ptr<ui::InputMethod> input_method_;
   std::unique_ptr<CocoaMouseCapture> mouse_capture_;
   std::unique_ptr<CocoaWindowMoveLoop> window_move_loop_;
@@ -330,6 +339,9 @@
   // currently hidden due to having a hidden parent.
   bool wants_to_be_visible_;
 
+  // Whether to animate the window (when it is appropriate to do so).
+  bool animate_ = true;
+
   // If true, the window has been made visible or changed shape and the window
   // shadow needs to be invalidated when a frame is received for the new shape.
   bool invalidate_shadow_on_frame_swap_ = false;
diff --git a/ui/views/cocoa/bridged_native_widget.mm b/ui/views/cocoa/bridged_native_widget.mm
index 54f89f3..53e17cd 100644
--- a/ui/views/cocoa/bridged_native_widget.mm
+++ b/ui/views/cocoa/bridged_native_widget.mm
@@ -72,10 +72,33 @@
 // of the window stationary (e.g. a scale). It's also not required for the hide
 // animation: in that case, the shadow is never invalidated so retains the
 // shadow calculated before a translate is applied.
-@interface ModalShowAnimationWithLayer : ConstrainedWindowAnimationShow
+@interface ModalShowAnimationWithLayer
+    : ConstrainedWindowAnimationShow<NSAnimationDelegate>
 @end
 
-@implementation ModalShowAnimationWithLayer
+@implementation ModalShowAnimationWithLayer {
+  // This is the "real" delegate, but this class acts as the NSAnimationDelegate
+  // to avoid a separate object.
+  views::BridgedNativeWidget* bridgedNativeWidget_;
+}
+- (instancetype)initWithBridgedNativeWidget:
+    (views::BridgedNativeWidget*)widget {
+  if ((self = [super initWithWindow:widget->ns_window()])) {
+    bridgedNativeWidget_ = widget;
+    [self setDelegate:self];
+  }
+  return self;
+}
+- (void)dealloc {
+  DCHECK(!bridgedNativeWidget_);
+  [super dealloc];
+}
+- (void)animationDidEnd:(NSAnimation*)animation {
+  DCHECK(bridgedNativeWidget_);
+  bridgedNativeWidget_->OnShowAnimationComplete();
+  bridgedNativeWidget_ = nullptr;
+  [self setDelegate:nil];
+}
 - (void)stopAnimation {
   [super stopAnimation];
   [window_ invalidateShadow];
@@ -589,6 +612,9 @@
   //      NSWindow API, or changes propagating out from here.
   wants_to_be_visible_ = new_state != HIDE_WINDOW;
 
+  [show_animation_ stopAnimation];
+  DCHECK(!show_animation_);
+
   if (new_state == HIDE_WINDOW) {
     // Calling -orderOut: on a window with an attached sheet encounters broken
     // AppKit behavior. The sheet effectively becomes "lost".
@@ -648,17 +674,18 @@
 
   // For non-sheet modal types, use the constrained window animations to make
   // the window appear.
-  if (native_widget_mac_->GetWidget()->IsModal()) {
-    base::scoped_nsobject<NSAnimation> show_animation(
-        [[ModalShowAnimationWithLayer alloc] initWithWindow:window_]);
+  if (animate_ && native_widget_mac_->GetWidget()->IsModal()) {
+    show_animation_.reset(
+        [[ModalShowAnimationWithLayer alloc] initWithBridgedNativeWidget:this]);
+
     // The default mode is blocking, which would block the UI thread for the
     // duration of the animation, but would keep it smooth. The window also
     // hasn't yet received a frame from the compositor at this stage, so it is
     // fully transparent until the GPU sends a frame swap IPC. For the blocking
     // option, the animation needs to wait until AcceleratedWidgetSwapCompleted
     // has been called at least once, otherwise it will animate nothing.
-    [show_animation setAnimationBlockingMode:NSAnimationNonblocking];
-    [show_animation startAnimation];
+    [show_animation_ setAnimationBlockingMode:NSAnimationNonblocking];
+    [show_animation_ startAnimation];
   }
 }
 
@@ -759,6 +786,10 @@
     [NSEvent removeMonitor:mouse_down_monitor_];
     mouse_down_monitor_ = nullptr;
   }
+
+  [show_animation_ stopAnimation];  // If set, calls OnShowAnimationComplete().
+  DCHECK(!show_animation_);
+
   [window_ setDelegate:nil];
   native_widget_mac_->OnWindowDestroyed();
   // Note: |this| is deleted here.
@@ -1012,6 +1043,10 @@
                                     shows_fullscreen_controls);
 }
 
+void BridgedNativeWidget::OnShowAnimationComplete() {
+  show_animation_.reset();
+}
+
 ui::InputMethod* BridgedNativeWidget::GetInputMethod() {
   if (!input_method_) {
     input_method_ = ui::CreateInputMethod(this, nil);
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
index 26bfbd3..8e0c55fb 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
@@ -796,11 +796,13 @@
 }
 
 void DesktopWindowTreeHostWin::HandleMove() {
+  CheckForMonitorChange();
   native_widget_delegate_->OnNativeWidgetMove();
   OnHostMovedInPixels(GetBoundsInPixels().origin());
 }
 
 void DesktopWindowTreeHostWin::HandleWorkAreaChanged() {
+  CheckForMonitorChange();
   GetWidget()->widget_delegate()->OnWorkAreaChanged();
 }
 
@@ -814,11 +816,13 @@
 
 void DesktopWindowTreeHostWin::HandleClientSizeChanged(
     const gfx::Size& new_size) {
+  CheckForMonitorChange();
   if (dispatcher())
     OnHostResizedInPixels(new_size);
 }
 
 void DesktopWindowTreeHostWin::HandleFrameChanged() {
+  CheckForMonitorChange();
   SetWindowTransparency();
   // Replace the frame and layout the contents.
   GetWidget()->non_client_view()->UpdateFrame();
@@ -997,6 +1001,15 @@
   return false;
 }
 
+void DesktopWindowTreeHostWin::CheckForMonitorChange() {
+  HMONITOR monitor_from_window =
+      ::MonitorFromWindow(GetHWND(), MONITOR_DEFAULTTOPRIMARY);
+  if (monitor_from_window == last_monitor_from_window_)
+    return;
+  last_monitor_from_window_ = monitor_from_window;
+  OnHostDisplayChanged();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // DesktopWindowTreeHost, public:
 
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
index 3c69d75e..db66147 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
@@ -222,6 +222,12 @@
   // Returns true if a modal window is active in the current root window chain.
   bool IsModalWindowActive() const;
 
+  // Called whenever the HWND resizes or moves, to see if the nearest HMONITOR
+  // has changed, and, if so, inform the aura::WindowTreeHost.
+  void CheckForMonitorChange();
+
+  HMONITOR last_monitor_from_window_ = nullptr;
+
   std::unique_ptr<HWNDMessageHandler> message_handler_;
   std::unique_ptr<aura::client::FocusClient> focus_client_;
 
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm
index b3fd155..bf01303 100644
--- a/ui/views/widget/native_widget_mac.mm
+++ b/ui/views/widget/native_widget_mac.mm
@@ -363,7 +363,7 @@
   }
 
   // For other modal types, animate the close.
-  if (delegate_->IsModal()) {
+  if (bridge_->animate() && delegate_->IsModal()) {
     [ViewsNSWindowCloseAnimator closeWindowWithAnimation:window];
     return;
   }
@@ -587,7 +587,8 @@
 }
 
 void NativeWidgetMac::SetVisibilityChangedAnimationsEnabled(bool value) {
-  NOTIMPLEMENTED();
+  if (bridge_)
+    bridge_->set_animate(value);
 }
 
 void NativeWidgetMac::SetVisibilityAnimationDuration(
diff --git a/ui/views/widget/native_widget_mac_unittest.mm b/ui/views/widget/native_widget_mac_unittest.mm
index 5a2188cb..ad3d330 100644
--- a/ui/views/widget/native_widget_mac_unittest.mm
+++ b/ui/views/widget/native_widget_mac_unittest.mm
@@ -105,6 +105,11 @@
     bridge_->AcceleratedWidgetSwapCompleted();
   }
 
+  NSAnimation* show_animation() {
+    return base::mac::ObjCCastStrict<NSAnimation>(
+        bridge_->show_animation_.get());
+  }
+
  private:
   BridgedNativeWidget* bridge_;
 
@@ -994,14 +999,23 @@
   EXPECT_FALSE(modal_dialog_widget->IsVisible());
   ScopedSwizzleWaiter show_waiter([ConstrainedWindowAnimationShow class]);
 
+  BridgedNativeWidgetTestApi test_api(modal_dialog_widget->GetNativeWindow());
+  EXPECT_FALSE(test_api.show_animation());
+
   modal_dialog_widget->Show();
   // Visible immediately (although it animates from transparent).
   EXPECT_TRUE(modal_dialog_widget->IsVisible());
+  base::scoped_nsobject<NSAnimation> retained_animation(
+      test_api.show_animation(), base::scoped_policy::RETAIN);
+  EXPECT_TRUE(retained_animation);
+  EXPECT_TRUE([retained_animation isAnimating]);
 
   // Run the animation.
   show_waiter.WaitForMethod();
   EXPECT_TRUE(modal_dialog_widget->IsVisible());
   EXPECT_TRUE(show_waiter.method_called());
+  EXPECT_FALSE([retained_animation isAnimating]);
+  EXPECT_FALSE(test_api.show_animation());
   return modal_dialog_widget;
 }
 
@@ -1070,6 +1084,53 @@
   }
 }
 
+// Tests that calls to Hide() a Widget cancel any in-progress show animation,
+// and that clients can control the triggering of the animation.
+TEST_F(NativeWidgetMacTest, ShowAnimationControl) {
+  NSWindow* native_parent = MakeNativeParent();
+  Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
+      new ModalDialogDelegate(ui::MODAL_TYPE_CHILD), nullptr,
+      [native_parent contentView]);
+
+  modal_dialog_widget->SetBounds(gfx::Rect(50, 50, 200, 150));
+  EXPECT_FALSE(modal_dialog_widget->IsVisible());
+
+  BridgedNativeWidgetTestApi test_api(modal_dialog_widget->GetNativeWindow());
+  EXPECT_FALSE(test_api.show_animation());
+  modal_dialog_widget->Show();
+
+  EXPECT_TRUE(modal_dialog_widget->IsVisible());
+  base::scoped_nsobject<NSAnimation> retained_animation(
+      test_api.show_animation(), base::scoped_policy::RETAIN);
+  EXPECT_TRUE(retained_animation);
+  EXPECT_TRUE([retained_animation isAnimating]);
+
+  // Hide without waiting for the animation to complete. Animation should cancel
+  // and clear references from BridgedNativeWidget.
+  modal_dialog_widget->Hide();
+  EXPECT_FALSE([retained_animation isAnimating]);
+  EXPECT_FALSE(test_api.show_animation());
+  retained_animation.reset();
+
+  // Disable animations and show again.
+  modal_dialog_widget->SetVisibilityChangedAnimationsEnabled(false);
+  modal_dialog_widget->Show();
+  EXPECT_FALSE(test_api.show_animation());  // No animation this time.
+  modal_dialog_widget->Hide();
+
+  // Test after re-enabling.
+  modal_dialog_widget->SetVisibilityChangedAnimationsEnabled(true);
+  modal_dialog_widget->Show();
+  EXPECT_TRUE(test_api.show_animation());
+  retained_animation.reset(test_api.show_animation(),
+                           base::scoped_policy::RETAIN);
+
+  // Closing should also cancel the animation.
+  EXPECT_TRUE([retained_animation isAnimating]);
+  [native_parent close];
+  EXPECT_FALSE([retained_animation isAnimating]);
+}
+
 // Tests behavior of window-modal dialogs, displayed as sheets.
 TEST_F(NativeWidgetMacTest, WindowModalSheet) {
   NSWindow* native_parent =
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js b/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js
index 0488269..519367b 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js
@@ -85,22 +85,20 @@
     if (!this.networkProperties)
       return;
 
+    var properties = this.networkProperties;
     if (newValue.GUID != (oldValue && oldValue.GUID))
       this.savedStaticIp_ = undefined;
 
     // Update the 'automatic' property.
-    if (this.networkProperties.IPAddressConfigType) {
-      var ipConfigType =
-          CrOnc.getActiveValue(this.networkProperties.IPAddressConfigType);
+    if (properties.IPAddressConfigType) {
+      var ipConfigType = CrOnc.getActiveValue(properties.IPAddressConfigType);
       this.automatic_ = (ipConfigType != CrOnc.IPConfigType.STATIC);
     }
 
-    if (this.networkProperties.IPConfigs) {
+    if (properties.IPConfigs || properties.StaticIPConfig) {
       // Update the 'ipConfig' property.
-      var ipv4 =
-          CrOnc.getIPConfigForType(this.networkProperties, CrOnc.IPType.IPV4);
-      var ipv6 =
-          CrOnc.getIPConfigForType(this.networkProperties, CrOnc.IPType.IPV6);
+      var ipv4 = CrOnc.getIPConfigForType(properties, CrOnc.IPType.IPV4);
+      var ipv6 = CrOnc.getIPConfigForType(properties, CrOnc.IPType.IPV6);
       this.ipConfig_ = {
         ipv4: this.getIPConfigUIProperties_(ipv4),
         ipv6: this.getIPConfigUIProperties_(ipv6)