diff --git a/AUTHORS b/AUTHORS
index b35adca..037ab29 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -445,6 +445,7 @@
 Jun Jiang <jun.a.jiang@intel.com>
 Junchao Han <junchao.han@intel.com>
 Junghoon Lee <sjh836@gmail.com>
+Junghyuk Yoo <wjdgurdl272@gmail.com>
 JungJik Lee <jungjik.lee@samsung.com>
 Jungkee Song <jungkee.song@samsung.com>
 Junmin Zhu <junmin.zhu@intel.com>
@@ -750,6 +751,7 @@
 Sanghyun Park <sh919.park@samsung.com>
 Sanghyup Lee <sh53.lee@samsung.com>
 Sangjoon Je <htamop@gmail.com>
+Sangseok Jang <sangseok.jang@navercorp.com>
 Sangwoo Ko <sangwoo.ko@navercorp.com>
 Sangwoo Ko <sangwoo108@gmail.com>
 Sanjoy Pal <ncj674@motorola.com>
diff --git a/DEPS b/DEPS
index 34418bf..b70d9ee 100644
--- a/DEPS
+++ b/DEPS
@@ -111,11 +111,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': '92694bea2840317e62de361d965f11f68cccd009',
+  'skia_revision': '603c5da17491267cd58ef2ba59600d2ed2ad3a5c',
   # 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': '14d9c29330036d87823b15706a4ebcf076b3df83',
+  'v8_revision': 'bb69b9888af945b411c7a507975c0aeeabf8bfc3',
   # 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.
@@ -135,7 +135,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': '7bcba336e4b83c44d5331a0f988a9d062851c47e',
+  'pdfium_revision': '6fe32f898af3eea875fd01a6d18f719d17dd72f3',
   # 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.
@@ -167,11 +167,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling HarfBuzz
   # and whatever else without interference from each other.
-  'harfbuzz_revision': '54d332dd9b0263821376161cdffb60ffb3c7847f',
+  'harfbuzz_revision': '1f14107f71a6c3da8270ed21c3588f945fa91733',
   # 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': '38911d8ac8b22226b63f465e5bdb8cadebeed786',
+  'catapult_revision': '687f318e303c48b791fa608da923c23b31cd6afe',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -219,11 +219,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': '18fe6d59e5e8dd8c5ccf8baa40f57bda838e7dfa',
+  'spv_tools_revision': '1c1e749f0b51603032ed573acb5ee4cd6fee8d01',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_headers_revision': '801cca8104245c07e8cc53292da87ee1b76946fe',
+  'spv_headers_revision': 'a2c529b5dda18838ab4b52f816acfebd774eaab3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -342,7 +342,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '3543693ca80a183aab49f5fe89e0c93888fb50a8',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'ec1a04b2e9872b2b96aa294004b58c0a097b88cc',
       'condition': 'checkout_ios',
   },
 
@@ -650,7 +650,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '7100fff13dd034bce875aeb1e3c3a28a2831bada',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'f74d12074e1988ffc5095a6310a82cd0549256c7',
       'condition': 'checkout_linux',
   },
 
@@ -909,7 +909,7 @@
     Var('chromium_git') + '/webm/libwebm.git' + '@' + 'e4931ebc0a816458c18a6734e91a4d1b5acd5c56',
 
   'src/third_party/libyuv':
-    Var('chromium_git') + '/libyuv/libyuv.git' + '@' + '9a07219dc8fbf2b77e390d16bd24809444838a91',  # from r1714
+    Var('chromium_git') + '/libyuv/libyuv.git' + '@' + 'b36c86fdfe746d7be904c3a565b047b24d58087e',  # from r1714
 
   'src/third_party/lighttpd': {
       'url': Var('chromium_git') + '/chromium/deps/lighttpd.git' + '@' + Var('lighttpd_revision'),
@@ -1004,7 +1004,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'd612e27088b86490416758a002b3b21a345be3fb',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '4fa2f815257c8b3256a3dbca4095e0ebe2437836',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1156,7 +1156,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '0d55c887e92b645f6effe753528323ab2ffd94c2',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '1803bb247055669e9da4a082077078960de62a7f',
+    Var('webrtc_git') + '/src.git' + '@' + '95dfa5223ac6c680f2138255d8b7ab122a3fd86d',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1187,7 +1187,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@9322d68816d4a9341db3a51f0568c7885f6adb6c',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@4f6d8fb43536c1743f6eea37c3e3d4f39c6b745b',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 2391c996..ccfebcf3 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -2991,6 +2991,50 @@
   return []
 
 
+def _CheckCorrectProductNameInMessages(input_api, output_api):
+  """Check that Chromium-branded strings don't include "Chrome" or vice versa.
+
+  This assumes we won't intentionally reference one product from the other
+  product.
+  """
+  all_problems = []
+  test_cases = [{
+    "filename_postfix": "google_chrome_strings.grd",
+    "correct_name": "Chrome",
+    "incorrect_name": "Chromium",
+  }, {
+    "filename_postfix": "chromium_strings.grd",
+    "correct_name": "Chromium",
+    "incorrect_name": "Chrome",
+  }]
+
+  for test_case in test_cases:
+    problems = []
+    filename_filter = lambda x: x.LocalPath().endswith(
+        test_case["filename_postfix"])
+
+    # Check each new line. Can yield false positives in multiline comments, but
+    # easier than trying to parse the XML because messages can have nested
+    # children, and associating message elements with affected lines is hard.
+    for f in input_api.AffectedSourceFiles(filename_filter):
+      for line_num, line in f.ChangedContents():
+        if "<message" in line or "<!--" in line or "-->" in line:
+          continue
+        if test_case["incorrect_name"] in line:
+          problems.append(
+              "Incorrect product name in %s:%d" % (f.LocalPath(), line_num))
+
+    if problems:
+      message = (
+        "Strings in %s-branded string files should reference \"%s\", not \"%s\""
+            % (test_case["correct_name"], test_case["correct_name"],
+               test_case["incorrect_name"]))
+      all_problems.append(
+          output_api.PresubmitPromptWarning(message, items=problems))
+
+  return all_problems
+
+
 def _AndroidSpecificOnUploadChecks(input_api, output_api):
   """Groups checks that target android code."""
   results = []
@@ -3068,6 +3112,7 @@
   results.extend(input_api.RunTests(
     input_api.canned_checks.CheckVPythonSpec(input_api, output_api)))
   results.extend(_CheckTranslationScreenshots(input_api, output_api))
+  results.extend(_CheckCorrectProductNameInMessages(input_api, output_api))
 
   for f in input_api.AffectedFiles():
     path, name = input_api.os_path.split(f.LocalPath())
diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py
index 0fbda0c..0c23bb96d 100755
--- a/PRESUBMIT_test.py
+++ b/PRESUBMIT_test.py
@@ -1611,6 +1611,125 @@
     self.assertTrue('base/another.h' in warnings[0].items)
 
 
+class CorrectProductNameInMessagesTest(unittest.TestCase):
+  def testProductNameInDesc(self):
+    mock_input_api = MockInputApi()
+    mock_input_api.files = [
+      MockAffectedFile('chrome/app/google_chrome_strings.grd', [
+        '<message name="Foo" desc="Welcome to Chrome">',
+        '  Welcome to Chrome!',
+        '</message>',
+      ]),
+      MockAffectedFile('chrome/app/chromium_strings.grd', [
+        '<message name="Bar" desc="Welcome to Chrome">',
+        '  Welcome to Chromium!',
+        '</message>',
+      ]),
+    ]
+    warnings = PRESUBMIT._CheckCorrectProductNameInMessages(
+        mock_input_api, MockOutputApi())
+    self.assertEqual(0, len(warnings))
+
+  def testChromeInChromium(self):
+    mock_input_api = MockInputApi()
+    mock_input_api.files = [
+      MockAffectedFile('chrome/app/google_chrome_strings.grd', [
+        '<message name="Foo" desc="Welcome to Chrome">',
+        '  Welcome to Chrome!',
+        '</message>',
+      ]),
+      MockAffectedFile('chrome/app/chromium_strings.grd', [
+        '<message name="Bar" desc="Welcome to Chrome">',
+        '  Welcome to Chrome!',
+        '</message>',
+      ]),
+    ]
+    warnings = PRESUBMIT._CheckCorrectProductNameInMessages(
+        mock_input_api, MockOutputApi())
+    self.assertEqual(1, len(warnings))
+    self.assertTrue('chrome/app/chromium_strings.grd' in warnings[0].items[0])
+
+  def testChromiumInChrome(self):
+    mock_input_api = MockInputApi()
+    mock_input_api.files = [
+      MockAffectedFile('chrome/app/google_chrome_strings.grd', [
+        '<message name="Foo" desc="Welcome to Chrome">',
+        '  Welcome to Chromium!',
+        '</message>',
+      ]),
+      MockAffectedFile('chrome/app/chromium_strings.grd', [
+        '<message name="Bar" desc="Welcome to Chrome">',
+        '  Welcome to Chromium!',
+        '</message>',
+      ]),
+    ]
+    warnings = PRESUBMIT._CheckCorrectProductNameInMessages(
+        mock_input_api, MockOutputApi())
+    self.assertEqual(1, len(warnings))
+    self.assertTrue(
+        'chrome/app/google_chrome_strings.grd:2' in warnings[0].items[0])
+
+  def testMultipleInstances(self):
+    mock_input_api = MockInputApi()
+    mock_input_api.files = [
+      MockAffectedFile('chrome/app/chromium_strings.grd', [
+        '<message name="Bar" desc="Welcome to Chrome">',
+        '  Welcome to Chrome!',
+        '</message>',
+        '<message name="Baz" desc="A correct message">',
+        '  Chromium is the software you are using.',
+        '</message>',
+        '<message name="Bat" desc="An incorrect message">',
+        '  Google Chrome is the software you are using.',
+        '</message>',
+      ]),
+    ]
+    warnings = PRESUBMIT._CheckCorrectProductNameInMessages(
+        mock_input_api, MockOutputApi())
+    self.assertEqual(1, len(warnings))
+    self.assertTrue(
+        'chrome/app/chromium_strings.grd:2' in warnings[0].items[0])
+    self.assertTrue(
+        'chrome/app/chromium_strings.grd:8' in warnings[0].items[1])
+
+  def testMultipleWarnings(self):
+    mock_input_api = MockInputApi()
+    mock_input_api.files = [
+      MockAffectedFile('chrome/app/chromium_strings.grd', [
+        '<message name="Bar" desc="Welcome to Chrome">',
+        '  Welcome to Chrome!',
+        '</message>',
+        '<message name="Baz" desc="A correct message">',
+        '  Chromium is the software you are using.',
+        '</message>',
+        '<message name="Bat" desc="An incorrect message">',
+        '  Google Chrome is the software you are using.',
+        '</message>',
+      ]),
+      MockAffectedFile('components/components_google_chrome_strings.grd', [
+        '<message name="Bar" desc="Welcome to Chrome">',
+        '  Welcome to Chrome!',
+        '</message>',
+        '<message name="Baz" desc="A correct message">',
+        '  Chromium is the software you are using.',
+        '</message>',
+        '<message name="Bat" desc="An incorrect message">',
+        '  Google Chrome is the software you are using.',
+        '</message>',
+      ]),
+    ]
+    warnings = PRESUBMIT._CheckCorrectProductNameInMessages(
+        mock_input_api, MockOutputApi())
+    self.assertEqual(2, len(warnings))
+    self.assertTrue(
+        'components/components_google_chrome_strings.grd:5'
+             in warnings[0].items[0])
+    self.assertTrue(
+        'chrome/app/chromium_strings.grd:2' in warnings[1].items[0])
+    self.assertTrue(
+        'chrome/app/chromium_strings.grd:8' in warnings[1].items[1])
+
+
 class MojoManifestOwnerTest(unittest.TestCase):
   def testMojoManifestChangeNeedsSecurityOwner(self):
     mock_input_api = MockInputApi()
diff --git a/WATCHLISTS b/WATCHLISTS
index 6f3eee1..1a66c20f 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1988,8 +1988,7 @@
                              'shimazu+serviceworker@chromium.org'],
     'blink_service_worker_tests': ['kenjibaheux+watch@chromium.org'],
     'blink_shadow_dom': ['hayato@chromium.org'],
-    'blink_spellcheck' : ['groby+blinkspell@chromium.org',
-                          'timvolodine@chromium.org',
+    'blink_spellcheck' : ['timvolodine@chromium.org',
                           'xiaochengh+watch@chromium.org'],
     'blink_spv2_layout_tests': ['pdr+virtualspv2watchlist@chromium.org'],
     'blink_streams': ['ricea+watch@chromium.org'],
@@ -2310,8 +2309,7 @@
                       'romax+watch@chromium.org'],
     'omnibox': ['jdonnelly+watch@chromium.org'],
     'optimization_guide': ['dougarnett+watch-optguide@chromium.org',
-                           'sophiechang+watch-optguide@chromium.org',
-                           'maniscalco+watch-optguide@chromium.org'],
+                           'sophiechang+watch-optguide@chromium.org'],
     'origin_trials': ['chasej+watch@chromium.org',
                       'iclelland+watch@chromium.org'],
     'ozone': ['kalyan.kondapally@intel.com',
@@ -2387,8 +2385,7 @@
                   'nohle+watch-smartlock@chromium.org'],
     'smb': ['cros-enterprise-lax+smbwatch@chromium.org'],
     'source_idls': ['jmedley+watch@chromium.org'],
-    'spellcheck': ['groby+spellwatch@chromium.org',
-                   'rlp+watch@chromium.org',
+    'spellcheck': ['rlp+watch@chromium.org',
                    'rouslan+spell@chromium.org',
                    'timvolodine@chromium.org'],
     'stack_sampling_profiler': ['chengx+watch@chromium.org'],
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index c86d537..8c9daa6 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1187,6 +1187,8 @@
     "wm/overview/scoped_overview_animation_settings.h",
     "wm/overview/scoped_transform_overview_window.cc",
     "wm/overview/scoped_transform_overview_window.h",
+    "wm/overview/start_animation_observer.cc",
+    "wm/overview/start_animation_observer.h",
     "wm/overview/window_grid.cc",
     "wm/overview/window_grid.h",
     "wm/overview/window_selector.cc",
@@ -1366,6 +1368,7 @@
     "//ui/wm",
   ]
   deps = [
+    "//ash/app_list/presenter",
     "//ash/app_menu",
     "//ash/assistant/ui:constants",
     "//ash/components/cursor",
@@ -1422,8 +1425,7 @@
     "//services/ws/public/mojom/input_devices",
     "//services/ws/remote_view_host",
 
-    # TODO(msw): Remove this; only ash_with_content should depend on webkit.
-    "//ash/app_list/presenter",
+    # TODO(msw): Remove this; ash should not depend on blink/webkit.
     "//third_party/blink/public:blink_headers",
     "//third_party/icu",
     "//third_party/qcms",
@@ -1489,47 +1491,6 @@
   }
 }
 
-component("ash_with_content") {
-  sources = [
-    "content/ash_with_content_export.h",
-    "content/keyboard_overlay/keyboard_overlay_delegate.cc",
-    "content/keyboard_overlay/keyboard_overlay_delegate.h",
-    "content/keyboard_overlay/keyboard_overlay_view.cc",
-    "content/keyboard_overlay/keyboard_overlay_view.h",
-  ]
-
-  defines = [ "ASH_WITH_CONTENT_IMPLEMENTATION" ]
-
-  public_deps = [
-    ":ash",
-  ]
-  deps = [
-    "//ash/public/cpp",
-    "//base",
-    "//base/third_party/dynamic_annotations",
-    "//components/discardable_memory/public/interfaces",
-    "//content/public/browser",
-    "//gpu/config",
-    "//ipc",
-    "//mojo/public/cpp/bindings",
-    "//services/ws/public/mojom",
-    "//skia",
-    "//ui/aura",
-    "//ui/base",
-    "//ui/compositor",
-    "//ui/events",
-    "//ui/gfx",
-    "//ui/gfx/geometry",
-    "//ui/keyboard",
-    "//ui/resources",
-    "//ui/strings",
-    "//ui/views",
-    "//ui/views/controls/webview",
-    "//ui/web_dialogs",
-    "//url",
-  ]
-}
-
 static_library("ash_shell_lib") {
   testonly = true
   sources = [
@@ -1595,7 +1556,6 @@
 
   deps = [
     ":ash_shell_lib",
-    ":ash_with_content",
     ":test_support_without_content",
     "//ash/components/quick_launch:lib",
     "//ash/components/quick_launch/public/mojom",
@@ -1648,8 +1608,6 @@
 test("ash_content_unittests") {
   sources = [
     "content/display/screen_orientation_controller_unittest.cc",
-    "content/keyboard_overlay/keyboard_overlay_delegate_unittest.cc",
-    "content/keyboard_overlay/keyboard_overlay_view_unittest.cc",
     "test/ash_unittests.cc",
   ]
   configs += [
@@ -1659,7 +1617,6 @@
 
   deps = [
     ":ash",
-    ":ash_with_content",
     ":test_support_with_content",
     "//ash/public/cpp",
     "//base",
@@ -1942,6 +1899,7 @@
     "system/update/tray_update_unittest.cc",
     "system/update/update_notification_controller_unittest.cc",
     "system/user/tray_user_unittest.cc",
+    "system/virtual_keyboard/virtual_keyboard_tray_unittest.cc",
     "test/ash_test_helper_unittest.cc",
     "test/ash_unittests.cc",
     "tooltips/tooltip_controller_unittest.cc",
@@ -1975,6 +1933,7 @@
     "wm/overlay_event_filter_unittest.cc",
     "wm/overlay_layout_manager_unittest.cc",
     "wm/overview/cleanup_animation_observer_unittest.cc",
+    "wm/overview/start_animation_observer_unittest.cc",
     "wm/overview/window_selector_controller_unittest.cc",
     "wm/overview/window_selector_unittest.cc",
     "wm/pip/pip_positioner_unittest.cc",
@@ -2126,7 +2085,7 @@
     "display/test_data/",
   ]
 
-  # Usage of content should be in ash_with_content.
+  # Disallow depending directly on content.
   assert_no_deps = [
     "//content/public/browser",
     "//content/public/common",
@@ -2202,7 +2161,6 @@
 
   public_deps = [
     ":test_support_common",
-    "//ash:ash_with_content",
     "//content/public/browser",
     "//content/test:test_support",
     "//services/network:test_support",
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc
index e55f4686..fd4b0e6 100644
--- a/ash/accelerators/accelerator_controller.cc
+++ b/ash/accelerators/accelerator_controller.cc
@@ -348,12 +348,7 @@
   return display::Display::ROTATE_0;
 }
 
-// Rotates the screen.
-void HandleRotateScreen() {
-  if (Shell::Get()->display_manager()->IsInUnifiedMode())
-    return;
-
-  base::RecordAction(UserMetricsAction("Accel_Rotate_Screen"));
+void RotateScreen() {
   gfx::Point point = display::Screen::GetScreen()->GetCursorScreenPoint();
   display::Display display =
       display::Screen::GetScreen()->GetDisplayNearestPoint(point);
@@ -364,6 +359,31 @@
       display::Display::RotationSource::USER);
 }
 
+// Rotates the screen.
+void HandleRotateScreen() {
+  if (Shell::Get()->display_manager()->IsInUnifiedMode())
+    return;
+
+  base::RecordAction(UserMetricsAction("Accel_Rotate_Screen"));
+  const bool dialog_ever_accepted =
+      Shell::Get()
+          ->accessibility_controller()
+          ->HasDisplayRotationAcceleratorDialogBeenAccepted();
+
+  if (!dialog_ever_accepted) {
+    Shell::Get()->accelerator_controller()->MaybeShowConfirmationDialog(
+        IDS_ASH_ROTATE_SCREEN_TITLE, IDS_ASH_ROTATE_SCREEN_BODY,
+        base::BindOnce([]() {
+          RotateScreen();
+          Shell::Get()
+              ->accessibility_controller()
+              ->SetDisplayRotationAcceleratorDialogBeenAccepted();
+        }));
+  } else {
+    RotateScreen();
+  }
+}
+
 void HandleRestoreTab() {
   base::RecordAction(UserMetricsAction("Accel_Restore_Tab"));
   Shell::Get()->new_window_controller()->RestoreTab();
@@ -386,9 +406,8 @@
           std::make_unique<WindowRotation>(360, active_window->layer())));
 }
 
-void HandleShowKeyboardOverlay() {
-  base::RecordAction(UserMetricsAction("Accel_Show_Keyboard_Overlay"));
-  Shell::Get()->new_window_controller()->ShowKeyboardOverlay();
+void HandleShowKeyboardShortcutViewer() {
+  Shell::Get()->new_window_controller()->ShowKeyboardShortcutViewer();
 }
 
 void HandleTakeWindowScreenshot() {
@@ -1380,7 +1399,7 @@
     case RESTORE_TAB:
     case ROTATE_WINDOW:
     case SHOW_IME_MENU_BUBBLE:
-    case SHOW_KEYBOARD_OVERLAY:
+    case SHOW_SHORTCUT_VIEWER:
     case SHOW_TASK_MANAGER:
     case SUSPEND:
     case TAKE_PARTIAL_SCREENSHOT:
@@ -1609,8 +1628,8 @@
     case SHOW_IME_MENU_BUBBLE:
       HandleShowImeMenuBubble();
       break;
-    case SHOW_KEYBOARD_OVERLAY:
-      HandleShowKeyboardOverlay();
+    case SHOW_SHORTCUT_VIEWER:
+      HandleShowKeyboardShortcutViewer();
       break;
     case SHOW_STYLUS_TOOLS:
       HandleShowStylusTools();
diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc
index d8a662c..5644931 100644
--- a/ash/accelerators/accelerator_controller_unittest.cc
+++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -512,14 +512,45 @@
   display::Display::Rotation initial_rotation =
       GetActiveDisplayRotation(display.id());
   ui::test::EventGenerator* generator = GetEventGenerator();
+  AccessibilityController* accessibility_controller =
+      Shell::Get()->accessibility_controller();
+
+  EXPECT_FALSE(accessibility_controller
+                   ->HasDisplayRotationAcceleratorDialogBeenAccepted());
   generator->PressKey(ui::VKEY_BROWSER_REFRESH,
                       ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
   generator->ReleaseKey(ui::VKEY_BROWSER_REFRESH,
                         ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
-  display::Display::Rotation new_rotation =
+  // Dialog should be open.
+  EXPECT_TRUE(IsConfirmationDialogOpen());
+  // Cancel on the dialog should have no effect.
+  CancelConfirmationDialog();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(accessibility_controller
+                   ->HasDisplayRotationAcceleratorDialogBeenAccepted());
+
+  display::Display::Rotation rotation_after_cancel =
+      GetActiveDisplayRotation(display.id());
+  // Screen rotation should not have been triggered.
+  EXPECT_EQ(initial_rotation, rotation_after_cancel);
+
+  // Use short cut again.
+  generator->PressKey(ui::VKEY_BROWSER_REFRESH,
+                      ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
+  generator->ReleaseKey(ui::VKEY_BROWSER_REFRESH,
+                        ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
+  EXPECT_TRUE(IsConfirmationDialogOpen());
+  AcceptConfirmationDialog();
+  base::RunLoop().RunUntilIdle();
+
+  // Dialog should be closed.
+  EXPECT_FALSE(IsConfirmationDialogOpen());
+  EXPECT_TRUE(accessibility_controller
+                  ->HasDisplayRotationAcceleratorDialogBeenAccepted());
+  display::Display::Rotation rotation_after_accept =
       GetActiveDisplayRotation(display.id());
   // |new_rotation| is determined by the AcceleratorController.
-  EXPECT_NE(initial_rotation, new_rotation);
+  EXPECT_NE(initial_rotation, rotation_after_accept);
 }
 
 TEST_F(AcceleratorControllerTest, AutoRepeat) {
diff --git a/ash/accelerators/accelerator_table.cc b/ash/accelerators/accelerator_table.cc
index 816c5b5..8449a0e 100644
--- a/ash/accelerators/accelerator_table.cc
+++ b/ash/accelerators/accelerator_table.cc
@@ -29,8 +29,8 @@
 //      the notification text. Also found in |ash_strings.grd|.
 //    - {true or false} whether the deprecated accelerator is still enabled (we
 //      don't disable a deprecated accelerator abruptly).
-// 5- Don't forget to update the keyboard overlay. Find 'shortcut' in the file
-//    keyboard_overlay_data.js.
+// 5- Don't forget to update the keyboard_shortcut_viewer_metadata.cc and
+//    shortcut_viewer_strings.grdp.
 const AcceleratorData kDeprecatedAccelerators[] = {
     {true, ui::VKEY_L, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, LOCK_SCREEN},
     {true, ui::VKEY_ESCAPE, ui::EF_SHIFT_DOWN, SHOW_TASK_MANAGER},
@@ -227,7 +227,7 @@
     SCALE_UI_RESET,
     SCALE_UI_UP,
     SHOW_IME_MENU_BUBBLE,
-    SHOW_KEYBOARD_OVERLAY,
+    SHOW_SHORTCUT_VIEWER,
     SUSPEND,
     SWAP_PRIMARY_DISPLAY,
     TAKE_PARTIAL_SCREENSHOT,
diff --git a/ash/accessibility/accessibility_controller.cc b/ash/accessibility/accessibility_controller.cc
index bfa85a2..ac02fd1 100644
--- a/ash/accessibility/accessibility_controller.cc
+++ b/ash/accessibility/accessibility_controller.cc
@@ -87,6 +87,7 @@
     prefs::kScreenMagnifierAcceleratorDialogHasBeenAccepted,
     prefs::kDockedMagnifierAcceleratorDialogHasBeenAccepted,
     prefs::kDictationAcceleratorDialogHasBeenAccepted,
+    prefs::kDisplayRotationAcceleratorDialogHasBeenAccepted,
 };
 
 // Returns true if |pref_service| is the one used for the signin screen.
@@ -259,6 +260,8 @@
             AutoclickController::GetDefaultAutoclickDelay().InMilliseconds()));
     registry->RegisterIntegerPref(prefs::kAccessibilityAutoclickEventType,
                                   static_cast<int>(kDefaultAutoclickEventType));
+    registry->RegisterBooleanPref(
+        prefs::kAccessibilityAutoclickRevertToLeftClick, true);
     registry->RegisterBooleanPref(prefs::kAccessibilityCaretHighlightEnabled,
                                   false);
     registry->RegisterBooleanPref(prefs::kAccessibilityCursorHighlightEnabled,
@@ -293,6 +296,8 @@
         prefs::kDockedMagnifierAcceleratorDialogHasBeenAccepted, false);
     registry->RegisterBooleanPref(
         prefs::kDictationAcceleratorDialogHasBeenAccepted, false);
+    registry->RegisterBooleanPref(
+        prefs::kDisplayRotationAcceleratorDialogHasBeenAccepted, false);
     return;
   }
 
@@ -301,6 +306,8 @@
   registry->RegisterForeignPref(prefs::kAccessibilityAutoclickEnabled);
   registry->RegisterForeignPref(prefs::kAccessibilityAutoclickDelayMs);
   registry->RegisterForeignPref(prefs::kAccessibilityAutoclickEventType);
+  registry->RegisterForeignPref(
+      prefs::kAccessibilityAutoclickRevertToLeftClick);
   registry->RegisterForeignPref(prefs::kAccessibilityCaretHighlightEnabled);
   registry->RegisterForeignPref(prefs::kAccessibilityCursorHighlightEnabled);
   registry->RegisterForeignPref(prefs::kAccessibilityDictationEnabled);
@@ -323,6 +330,8 @@
       prefs::kDockedMagnifierAcceleratorDialogHasBeenAccepted);
   registry->RegisterForeignPref(
       prefs::kDictationAcceleratorDialogHasBeenAccepted);
+  registry->RegisterForeignPref(
+      prefs::kDisplayRotationAcceleratorDialogHasBeenAccepted);
 }
 
 void AccessibilityController::SetHighContrastAcceleratorDialogAccepted() {
@@ -363,6 +372,22 @@
   active_user_prefs_->CommitPendingWrite();
 }
 
+bool AccessibilityController::HasDisplayRotationAcceleratorDialogBeenAccepted()
+    const {
+  return active_user_prefs_ &&
+         active_user_prefs_->GetBoolean(
+             prefs::kDisplayRotationAcceleratorDialogHasBeenAccepted);
+}
+
+void AccessibilityController::
+    SetDisplayRotationAcceleratorDialogBeenAccepted() {
+  if (!active_user_prefs_)
+    return;
+  active_user_prefs_->SetBoolean(
+      prefs::kDisplayRotationAcceleratorDialogHasBeenAccepted, true);
+  active_user_prefs_->CommitPendingWrite();
+}
+
 bool AccessibilityController::HasDockedMagnifierAcceleratorDialogBeenAccepted()
     const {
   return active_user_prefs_ &&
@@ -807,6 +832,11 @@
           &AccessibilityController::UpdateAutoclickEventTypeFromPref,
           base::Unretained(this)));
   pref_change_registrar_->Add(
+      prefs::kAccessibilityAutoclickRevertToLeftClick,
+      base::BindRepeating(
+          &AccessibilityController::UpdateAutoclickRevertToLeftClickFromPref,
+          base::Unretained(this)));
+  pref_change_registrar_->Add(
       prefs::kAccessibilityCaretHighlightEnabled,
       base::BindRepeating(
           &AccessibilityController::UpdateCaretHighlightFromPref,
@@ -864,6 +894,7 @@
   UpdateAutoclickFromPref();
   UpdateAutoclickDelayFromPref();
   UpdateAutoclickEventTypeFromPref();
+  UpdateAutoclickRevertToLeftClickFromPref();
   UpdateCaretHighlightFromPref();
   UpdateCursorHighlightFromPref();
   UpdateDictationFromPref();
@@ -912,6 +943,24 @@
   Shell::Get()->autoclick_controller()->SetAutoclickEventType(event_type);
 }
 
+void AccessibilityController::SetAutoclickEventType(
+    mojom::AutoclickEventType event_type) {
+  if (!active_user_prefs_)
+    return;
+  active_user_prefs_->SetInteger(prefs::kAccessibilityAutoclickEventType,
+                                 static_cast<int>(event_type));
+  active_user_prefs_->CommitPendingWrite();
+}
+
+void AccessibilityController::UpdateAutoclickRevertToLeftClickFromPref() {
+  DCHECK(active_user_prefs_);
+  bool revert_to_left_click = active_user_prefs_->GetBoolean(
+      prefs::kAccessibilityAutoclickRevertToLeftClick);
+
+  Shell::Get()->autoclick_controller()->set_revert_to_left_click(
+      revert_to_left_click);
+}
+
 void AccessibilityController::UpdateCaretHighlightFromPref() {
   DCHECK(active_user_prefs_);
   const bool enabled = active_user_prefs_->GetBoolean(
diff --git a/ash/accessibility/accessibility_controller.h b/ash/accessibility/accessibility_controller.h
index 71b15ab..2a483362 100644
--- a/ash/accessibility/accessibility_controller.h
+++ b/ash/accessibility/accessibility_controller.h
@@ -65,9 +65,12 @@
   bool HasDockedMagnifierAcceleratorDialogBeenAccepted() const;
   void SetDictationAcceleratorDialogAccepted();
   bool HasDictationAcceleratorDialogBeenAccepted() const;
+  void SetDisplayRotationAcceleratorDialogBeenAccepted();
+  bool HasDisplayRotationAcceleratorDialogBeenAccepted() const;
 
   void SetAutoclickEnabled(bool enabled);
   bool IsAutoclickEnabled() const;
+  void SetAutoclickEventType(mojom::AutoclickEventType event_type);
 
   void SetCaretHighlightEnabled(bool enabled);
   bool IsCaretHighlightEnabled() const;
@@ -188,6 +191,7 @@
   void UpdateAutoclickFromPref();
   void UpdateAutoclickDelayFromPref();
   void UpdateAutoclickEventTypeFromPref();
+  void UpdateAutoclickRevertToLeftClickFromPref();
   void UpdateCaretHighlightFromPref();
   void UpdateCursorHighlightFromPref();
   void UpdateDictationFromPref();
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index edf03f3..62becee 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -521,8 +521,9 @@
       WindowSelector::EnterExitOverviewType::kWindowsMinimized;
 }
 
-void AppListControllerImpl::OnOverviewModeEndingAnimationComplete() {
-  if (!IsHomeLauncherEnabledInTabletMode())
+void AppListControllerImpl::OnOverviewModeEndingAnimationComplete(
+    bool canceled) {
+  if (!IsHomeLauncherEnabledInTabletMode() || canceled)
     return;
 
   presenter_.ScheduleOverviewModeAnimation(/*start=*/false,
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h
index d57d0b1..6e4ae13 100644
--- a/ash/app_list/app_list_controller_impl.h
+++ b/ash/app_list/app_list_controller_impl.h
@@ -197,7 +197,7 @@
   // ShellObserver:
   void OnOverviewModeStarting() override;
   void OnOverviewModeEnding() override;
-  void OnOverviewModeEndingAnimationComplete() override;
+  void OnOverviewModeEndingAnimationComplete(bool canceled) override;
 
   // TabletModeObserver:
   void OnTabletModeStarted() override;
diff --git a/ash/app_list/home_launcher_gesture_handler.cc b/ash/app_list/home_launcher_gesture_handler.cc
index b8655ce..e76ac3e 100644
--- a/ash/app_list/home_launcher_gesture_handler.cc
+++ b/ash/app_list/home_launcher_gesture_handler.cc
@@ -209,8 +209,16 @@
 
 bool HomeLauncherGestureHandler::OnReleaseEvent(const gfx::Point& location,
                                                 bool* out_dragged_down) {
-  if (!IsDragInProgress())
+  if (!IsDragInProgress()) {
+    if (window_) {
+      // |window_| may not be nullptr when this release event is triggered by
+      // opening |window_| with modal dialog in OnPressEvent(). In that case,
+      // just leave the |window_| in show state and stop tracking.
+      RemoveObserversAndStopTracking();
+      return true;
+    }
     return false;
+  }
 
   last_event_location_ = base::make_optional(location);
   if (out_dragged_down) {
@@ -657,6 +665,13 @@
   if (mode == Mode::kSlideDownToHide) {
     ScopedAnimationDisabler disable(window_);
     window_->Show();
+
+    // When |window_| has a modal dialog child, window_->Show() above would
+    // cancel the current gesture and trigger OnReleaseEvent() to reset
+    // |window_|.
+    if (!window_)
+      return false;
+
     wm::ActivateWindow(window_);
     window_->layer()->SetOpacity(1.f);
   }
diff --git a/ash/app_list/views/search_result_tile_item_view.cc b/ash/app_list/views/search_result_tile_item_view.cc
index b221106..0683cb83f 100644
--- a/ash/app_list/views/search_result_tile_item_view.cc
+++ b/ash/app_list/views/search_result_tile_item_view.cc
@@ -26,7 +26,6 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/gfx/canvas.h"
-#include "ui/gfx/image/canvas_image_source.h"
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/strings/grit/ui_strings.h"
@@ -66,29 +65,6 @@
 constexpr SkColor kSearchRatingStarColor =
     SkColorSetARGB(0x8F, 0x00, 0x00, 0x00);
 
-// The background image source for badge.
-class BadgeBackgroundImageSource : public gfx::CanvasImageSource {
- public:
-  explicit BadgeBackgroundImageSource(int size)
-      : CanvasImageSource(gfx::Size(size, size), false),
-        radius_(static_cast<float>(size / 2)) {}
-  ~BadgeBackgroundImageSource() override = default;
-
- private:
-  // gfx::CanvasImageSource overrides:
-  void Draw(gfx::Canvas* canvas) override {
-    cc::PaintFlags flags;
-    flags.setColor(SK_ColorWHITE);
-    flags.setAntiAlias(true);
-    flags.setStyle(cc::PaintFlags::kFill_Style);
-    canvas->DrawCircle(gfx::PointF(radius_, radius_), radius_, flags);
-  }
-
-  const float radius_;
-
-  DISALLOW_COPY_AND_ASSIGN(BadgeBackgroundImageSource);
-};
-
 }  // namespace
 
 SearchResultTileItemView::SearchResultTileItemView(
@@ -458,13 +434,10 @@
     return;
   }
 
-  const int size = app_list::AppListConfig::instance()
-                       .search_tile_badge_background_radius() *
-                   2;
-  gfx::ImageSkia background(std::make_unique<BadgeBackgroundImageSource>(size),
-                            gfx::Size(size, size));
-  gfx::ImageSkia icon_with_background =
-      gfx::ImageSkiaOperations::CreateSuperimposedImage(background, badge_icon);
+  gfx::ImageSkia resized_badge_icon(
+      gfx::ImageSkiaOperations::CreateResizedImage(
+          badge_icon, skia::ImageOperations::RESIZE_BEST,
+          AppListConfig::instance().search_tile_badge_icon_size()));
 
   gfx::ShadowValues shadow_values;
   shadow_values.push_back(
@@ -472,7 +445,7 @@
   shadow_values.push_back(
       gfx::ShadowValue(gfx::Vector2d(0, 1), 2, SkColorSetARGB(0x33, 0, 0, 0)));
   badge_->SetImage(gfx::ImageSkiaOperations::CreateImageWithDropShadow(
-      icon_with_background, shadow_values));
+      resized_badge_icon, shadow_values));
   badge_->SetVisible(true);
 }
 
@@ -569,26 +542,24 @@
     title_->SetBoundsRect(AppListItemView::GetTitleBoundsForTargetViewBounds(
         rect, title_->GetPreferredSize()));
   } else {
-    rect.Inset(0, kSearchTileTopPadding, 0, 0);
-    icon_->SetBoundsRect(rect);
+    gfx::Rect icon_rect(rect);
+    icon_rect.ClampToCenteredSize(icon_->GetImage().size());
+    icon_rect.set_y(kSearchTileTopPadding);
+    icon_->SetBoundsRect(icon_rect);
 
     if (badge_) {
-      gfx::Rect badge_rect(rect);
-      const gfx::Size icon_size = icon_->GetImage().size();
       const int badge_icon_dimension =
           AppListConfig::instance().search_tile_badge_icon_dimension();
-      const int badge_background_radius =
-          AppListConfig::instance().search_tile_badge_background_radius();
-      badge_rect.Offset((icon_size.width() - badge_icon_dimension) / 2,
-                        icon_size.height() - badge_background_radius -
-                            badge_icon_dimension / 2);
+      const int badge_icon_offset =
+          AppListConfig::instance().search_tile_badge_icon_offset();
+      const gfx::Rect badge_rect(
+          icon_rect.right() - badge_icon_dimension + badge_icon_offset,
+          icon_rect.bottom() - badge_icon_dimension + badge_icon_offset,
+          badge_icon_dimension, badge_icon_dimension);
       badge_->SetBoundsRect(badge_rect);
     }
 
-    rect.Inset(0,
-               AppListConfig::instance().search_tile_icon_dimension() +
-                   kSearchTitleSpacing,
-               0, 0);
+    rect.set_y(icon_rect.bottom() + kSearchTitleSpacing);
     rect.set_height(title_->GetPreferredSize().height());
     title_->SetBoundsRect(rect);
 
diff --git a/ash/app_menu/notification_menu_controller.cc b/ash/app_menu/notification_menu_controller.cc
index 33f8ffc..15b3345 100644
--- a/ash/app_menu/notification_menu_controller.cc
+++ b/ash/app_menu/notification_menu_controller.cc
@@ -93,7 +93,7 @@
                                  : nullptr;
 }
 
-void NotificationMenuController::OnSlideChanged() {}
+void NotificationMenuController::OnSlideChanged(bool in_progress) {}
 
 void NotificationMenuController::OnSlideOut() {
   // Results in |this| being deleted if there are no more notifications to show.
diff --git a/ash/app_menu/notification_menu_controller.h b/ash/app_menu/notification_menu_controller.h
index fa9bdf0..4714773 100644
--- a/ash/app_menu/notification_menu_controller.h
+++ b/ash/app_menu/notification_menu_controller.h
@@ -42,7 +42,7 @@
 
   // message_center::SlideOutController::Delegate overrides:
   ui::Layer* GetSlideOutLayer() override;
-  void OnSlideChanged() override;
+  void OnSlideChanged(bool in_progress) override;
   void OnSlideOut() override;
 
   // NotificationMenuView::Delegate overrides:
diff --git a/ash/app_menu/notification_menu_view_unittest.cc b/ash/app_menu/notification_menu_view_unittest.cc
index f62080d..390f294f 100644
--- a/ash/app_menu/notification_menu_view_unittest.cc
+++ b/ash/app_menu/notification_menu_view_unittest.cc
@@ -46,7 +46,7 @@
     return notification_menu_view_->GetSlideOutLayer();
   }
 
-  void OnSlideChanged() override {}
+  void OnSlideChanged(bool in_progress) override {}
 
   void OnSlideOut() override { slide_out_count_++; }
 
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 6cc3045..aeaf24b 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -157,10 +157,6 @@
         Shelf
       </message>
 
-      <message name="IDS_ASH_KEYBOARD_OVERLAY_TITLE" desc="The title of the keyboard overlay.">
-        Keyboard overlay
-      </message>
-
       <message name="IDS_ASH_LEARN_MORE" desc="Text of Learn more links.">
         Learn more
       </message>
@@ -1284,6 +1280,12 @@
       <message name="IDS_ASH_SCREEN_MAGNIFIER_BODY" desc="The text for the screen magnifier dialog.">
        You pressed the shortcut for the full-screen magnifier. Do you want to turn it on?
       </message>
+      <message name="IDS_ASH_ROTATE_SCREEN_TITLE" desc="Dialog title for when the screen has been rotated.">
+        Rotate Screen
+      </message>
+      <message name="IDS_ASH_ROTATE_SCREEN_BODY" desc="The text for the screen rotation dialog.">
+        You pressed the shortcut for screen rotation. Do you want to rotate the screen?
+      </message>
       <message name="IDS_ASH_CONTINUE_BUTTON" desc="The text for continue button on accessibility confirmation dialogs.">
         Continue
       </message>
diff --git a/ash/autoclick/autoclick_controller.cc b/ash/autoclick/autoclick_controller.cc
index fbd31a4b..fa3cacef 100644
--- a/ash/autoclick/autoclick_controller.cc
+++ b/ash/autoclick/autoclick_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/autoclick/autoclick_controller.h"
 
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/autoclick/autoclick_drag_event_rewriter.h"
 #include "ash/autoclick/autoclick_ring_handler.h"
 #include "ash/public/cpp/ash_constants.h"
@@ -69,6 +70,7 @@
 AutoclickController::AutoclickController()
     : enabled_(false),
       event_type_(kDefaultAutoclickEventType),
+      revert_to_left_click_(true),
       tap_down_target_(nullptr),
       delay_(GetDefaultAutoclickDelay()),
       mouse_event_flags_(ui::EF_NONE),
@@ -204,8 +206,10 @@
         drag_event_rewriter_->SetEnabled(true);
         return;
       }
-      if (details.dispatcher_destroyed)
+      if (details.dispatcher_destroyed) {
+        OnActionCompleted();
         return;
+      }
     }
     if (drag_stop)
       drag_event_rewriter_->SetEnabled(false);
@@ -214,10 +218,12 @@
                                  mouse_event_flags_ | button, button);
     details = host->event_sink()->OnEventFromSource(&release_event);
 
-    // Now a single click has been completed.
+    // Now a single click, or half the drag & drop, has been completed.
     if (event_type_ != mojom::AutoclickEventType::kDoubleClick ||
-        details.dispatcher_destroyed)
+        details.dispatcher_destroyed) {
+      OnActionCompleted();
       return;
+    }
 
     ui::MouseEvent double_press_event(
         ui::ET_MOUSE_PRESSED, location_in_pixels, location_in_pixels,
@@ -228,9 +234,12 @@
         ui::EventTimeForNow(),
         mouse_event_flags_ | button | ui::EF_IS_DOUBLE_CLICK, button);
     details = host->event_sink()->OnEventFromSource(&double_press_event);
-    if (details.dispatcher_destroyed)
+    if (details.dispatcher_destroyed) {
+      OnActionCompleted();
       return;
+    }
     details = host->event_sink()->OnEventFromSource(&double_release_event);
+    OnActionCompleted();
   }
 }
 
@@ -243,6 +252,16 @@
   SetTapDownTarget(nullptr);
 }
 
+void AutoclickController::OnActionCompleted() {
+  if (!revert_to_left_click_ ||
+      event_type_ == mojom::AutoclickEventType::kLeftClick)
+    return;
+  // Change the preference, but set it locally so we do not reset any state when
+  // AutoclickController::SetAutoclickEventType is called.
+  event_type_ = mojom::AutoclickEventType::kLeftClick;
+  Shell::Get()->accessibility_controller()->SetAutoclickEventType(event_type_);
+}
+
 void AutoclickController::InitClickTimer() {
   CancelAutoclickAction();
   autoclick_timer_ = std::make_unique<base::RetainingOneShotTimer>(
diff --git a/ash/autoclick/autoclick_controller.h b/ash/autoclick/autoclick_controller.h
index ef22251..01eda9f 100644
--- a/ash/autoclick/autoclick_controller.h
+++ b/ash/autoclick/autoclick_controller.h
@@ -52,6 +52,11 @@
   // Sets the event type.
   void SetAutoclickEventType(mojom::AutoclickEventType type);
 
+  // Sets whether to revert to a left click after any other event type.
+  void set_revert_to_left_click(bool revert_to_left_click) {
+    revert_to_left_click_ = revert_to_left_click;
+  }
+
  private:
   void SetTapDownTarget(aura::Window* target);
   void CreateAutoclickRingWidget(const gfx::Point& point_in_screen);
@@ -59,6 +64,7 @@
                                  const gfx::Point& point_in_screen);
   void DoAutoclickAction();
   void CancelAutoclickAction();
+  void OnActionCompleted();
   void InitClickTimer();
   void UpdateRingWidget(const gfx::Point& mouse_location);
 
@@ -74,6 +80,7 @@
 
   bool enabled_;
   mojom::AutoclickEventType event_type_;
+  bool revert_to_left_click_;
   // The target window is observed by AutoclickController for the duration
   // of a autoclick gesture.
   aura::Window* tap_down_target_;
diff --git a/ash/autoclick/autoclick_drag_event_rewriter.cc b/ash/autoclick/autoclick_drag_event_rewriter.cc
index 90368eb..19855b7 100644
--- a/ash/autoclick/autoclick_drag_event_rewriter.cc
+++ b/ash/autoclick/autoclick_drag_event_rewriter.cc
@@ -38,4 +38,4 @@
   return ui::EVENT_REWRITE_CONTINUE;
 }
 
-}  // namespace ash
\ No newline at end of file
+}  // namespace ash
diff --git a/ash/autoclick/autoclick_unittest.cc b/ash/autoclick/autoclick_unittest.cc
index bd0d942..6c002bc3 100644
--- a/ash/autoclick/autoclick_unittest.cc
+++ b/ash/autoclick/autoclick_unittest.cc
@@ -313,6 +313,7 @@
 
 TEST_F(AutoclickTest, AutoclickChangeEventTypes) {
   GetAutoclickController()->SetEnabled(true);
+  GetAutoclickController()->set_revert_to_left_click(false);
   GetAutoclickController()->SetAutoclickEventType(
       mojom::AutoclickEventType::kRightClick);
   std::vector<ui::MouseEvent> events;
@@ -376,6 +377,7 @@
 
 TEST_F(AutoclickTest, AutoclickDragAndDropEvents) {
   GetAutoclickController()->SetEnabled(true);
+  GetAutoclickController()->set_revert_to_left_click(false);
   GetAutoclickController()->SetAutoclickEventType(
       mojom::AutoclickEventType::kDragAndDrop);
   std::vector<ui::MouseEvent> events;
@@ -405,4 +407,63 @@
   EXPECT_EQ(ui::ET_MOUSE_RELEASED, events[0].type());
 }
 
+TEST_F(AutoclickTest, AutoclickRevertsToLeftClick) {
+  GetAutoclickController()->SetEnabled(true);
+  GetAutoclickController()->set_revert_to_left_click(true);
+  GetAutoclickController()->SetAutoclickEventType(
+      mojom::AutoclickEventType::kRightClick);
+  std::vector<ui::MouseEvent> events;
+
+  GetEventGenerator()->MoveMouseTo(30, 30);
+  events = WaitForMouseEvents();
+  ASSERT_EQ(2u, events.size());
+  EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & events[0].flags());
+  EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & events[1].flags());
+
+  // Another event is now left-click; we've reverted to left click.
+  GetEventGenerator()->MoveMouseTo(90, 90);
+  GetAutoclickController()->SetAutoclickEventType(
+      mojom::AutoclickEventType::kLeftClick);
+  events = WaitForMouseEvents();
+  ASSERT_EQ(2u, events.size());
+  EXPECT_TRUE(ui::EF_LEFT_MOUSE_BUTTON & events[0].flags());
+  EXPECT_TRUE(ui::EF_LEFT_MOUSE_BUTTON & events[1].flags());
+
+  // The next event is also a left click.
+  GetEventGenerator()->MoveMouseTo(120, 120);
+  GetAutoclickController()->SetAutoclickEventType(
+      mojom::AutoclickEventType::kLeftClick);
+  events = WaitForMouseEvents();
+  ASSERT_EQ(2u, events.size());
+  EXPECT_TRUE(ui::EF_LEFT_MOUSE_BUTTON & events[0].flags());
+  EXPECT_TRUE(ui::EF_LEFT_MOUSE_BUTTON & events[1].flags());
+
+  // Changing revert to false doesn't change that we are on left click at
+  // present.
+  GetAutoclickController()->set_revert_to_left_click(false);
+  GetEventGenerator()->MoveMouseTo(150, 150);
+  GetAutoclickController()->SetAutoclickEventType(
+      mojom::AutoclickEventType::kLeftClick);
+  events = WaitForMouseEvents();
+  ASSERT_EQ(2u, events.size());
+  EXPECT_TRUE(ui::EF_LEFT_MOUSE_BUTTON & events[0].flags());
+  EXPECT_TRUE(ui::EF_LEFT_MOUSE_BUTTON & events[1].flags());
+
+  // But we should no longer revert to left click if the type is something else.
+  GetAutoclickController()->SetAutoclickEventType(
+      mojom::AutoclickEventType::kRightClick);
+  GetEventGenerator()->MoveMouseTo(180, 180);
+  events = WaitForMouseEvents();
+  ASSERT_EQ(2u, events.size());
+  EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & events[0].flags());
+  EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & events[1].flags());
+
+  // Should still be right click.
+  GetEventGenerator()->MoveMouseTo(210, 210);
+  events = WaitForMouseEvents();
+  ASSERT_EQ(2u, events.size());
+  EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & events[0].flags());
+  EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & events[1].flags());
+}
+
 }  // namespace ash
diff --git a/ash/components/shortcut_viewer/keyboard_shortcut_item.h b/ash/components/shortcut_viewer/keyboard_shortcut_item.h
index 5ce4871..bd5f831 100644
--- a/ash/components/shortcut_viewer/keyboard_shortcut_item.h
+++ b/ash/components/shortcut_viewer/keyboard_shortcut_item.h
@@ -84,7 +84,7 @@
   //     E.g. shortcuts for "CTRL + 1 through 8.", we will provide
   //     shortcut_string " + 1 through 8", and auto-generate {ui::VKEY_CONTROL}.
   //  3. For grouped |accelerator_ids| with different modifiers, e.g.
-  //     SHOW_KEYBOARD_OVERLAY, we can not auto-generate it and we will provide
+  //     TOGGLE_FULLSCREEN, we can not auto-generate it and we will provide
   //     the |shortcut_key_codes|.
   //  4. For ksv items not in the two accelerator_tables, we will provide the
   //     |shortcut_key_codes| and |accelerator_ids| will be empty.
diff --git a/ash/content/DEPS b/ash/content/DEPS
deleted file mode 100644
index ee6cf24..0000000
--- a/ash/content/DEPS
+++ /dev/null
@@ -1,13 +0,0 @@
-specific_include_rules = {
-  "content_gpu_interface_provider.*": [
-    "+components/discardable_memory/public/interfaces",
-    "+content/public/browser",
-  ],
-  "screen_orientation_delegate_chromeos.cc": [
-    "+content/public/browser/screen_orientation_provider.h",
-    "+content/public/browser/web_contents.h",
-  ],
-  "screen_orientation_delegate_chromeos.h": [
-    "+content/public/browser/screen_orientation_delegate.h",
-  ],
-}
diff --git a/ash/content/ash_with_content_export.h b/ash/content/ash_with_content_export.h
deleted file mode 100644
index f234cc20..0000000
--- a/ash/content/ash_with_content_export.h
+++ /dev/null
@@ -1,32 +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.
-
-#ifndef ASH_CONTENT_ASH_WITH_CONTENT_EXPORT_H_
-#define ASH_CONTENT_ASH_WITH_CONTENT_EXPORT_H_
-
-// Defines ASH_WITH_CONTENT_EXPORT so that functionality implemented by the Ash
-// module can be exported to consumers.
-
-#if defined(COMPONENT_BUILD)
-#if defined(WIN32)
-
-#if defined(ASH_WITH_CONTENT_IMPLEMENTATION)
-#define ASH_WITH_CONTENT_EXPORT __declspec(dllexport)
-#else
-#define ASH_WITH_CONTENT_EXPORT __declspec(dllimport)
-#endif  // defined(ASH_WITH_CONTENT_IMPLEMENTATION)
-
-#else  // defined(WIN32)
-#if defined(ASH_WITH_CONTENT_IMPLEMENTATION)
-#define ASH_WITH_CONTENT_EXPORT __attribute__((visibility("default")))
-#else
-#define ASH_WITH_CONTENT_EXPORT
-#endif
-#endif
-
-#else  // defined(COMPONENT_BUILD)
-#define ASH_WITH_CONTENT_EXPORT
-#endif
-
-#endif  // ASH_CONTENT_ASH_WITH_CONTENT_EXPORT_H_
diff --git a/ash/content/keyboard_overlay/DEPS b/ash/content/keyboard_overlay/DEPS
deleted file mode 100644
index 275939a..0000000
--- a/ash/content/keyboard_overlay/DEPS
+++ /dev/null
@@ -1,8 +0,0 @@
-include_rules = [
-  "+content/public/browser",
-]
-specific_include_rules = {
-  ".*test\.cc": [
-    "+content/public/test",
-  ],
-}
diff --git a/ash/content/keyboard_overlay/keyboard_overlay_delegate.cc b/ash/content/keyboard_overlay/keyboard_overlay_delegate.cc
deleted file mode 100644
index ab1c28c..0000000
--- a/ash/content/keyboard_overlay/keyboard_overlay_delegate.cc
+++ /dev/null
@@ -1,144 +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.
-
-#include "ash/content/keyboard_overlay/keyboard_overlay_delegate.h"
-
-#include <algorithm>
-
-#include "ash/shell.h"
-#include "base/bind.h"
-#include "base/memory/weak_ptr.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/values.h"
-#include "content/public/browser/web_ui.h"
-#include "content/public/browser/web_ui_message_handler.h"
-#include "ui/aura/window_event_dispatcher.h"
-#include "ui/display/display.h"
-#include "ui/display/screen.h"
-#include "ui/views/controls/webview/web_dialog_view.h"
-#include "ui/views/widget/widget.h"
-
-using content::WebContents;
-using content::WebUIMessageHandler;
-
-namespace {
-
-const int kBaseWidth = 1252;
-const int kBaseHeight = 516;
-const int kHorizontalMargin = 28;
-
-// A message handler for detecting the timing when the web contents is painted.
-class PaintMessageHandler : public WebUIMessageHandler,
-                            public base::SupportsWeakPtr<PaintMessageHandler> {
- public:
-  explicit PaintMessageHandler(views::Widget* widget) : widget_(widget) {}
-  ~PaintMessageHandler() override = default;
-
-  // WebUIMessageHandler implementation.
-  void RegisterMessages() override;
-
- private:
-  void DidPaint(const base::ListValue* args);
-
-  views::Widget* widget_;
-
-  DISALLOW_COPY_AND_ASSIGN(PaintMessageHandler);
-};
-
-void PaintMessageHandler::RegisterMessages() {
-  web_ui()->RegisterMessageCallback(
-      "didPaint",
-      base::BindRepeating(&PaintMessageHandler::DidPaint, AsWeakPtr()));
-}
-
-void PaintMessageHandler::DidPaint(const base::ListValue* args) {
-  // Show the widget after the web content has been painted.
-  widget_->Show();
-  web_ui()->CallJavascriptFunctionUnsafe("onWidgetShown");
-}
-
-}  // namespace
-
-namespace ash {
-
-KeyboardOverlayDelegate::KeyboardOverlayDelegate(const base::string16& title,
-                                                 const GURL& url)
-    : title_(title), url_(url), widget_(NULL) {}
-
-KeyboardOverlayDelegate::~KeyboardOverlayDelegate() = default;
-
-views::Widget* KeyboardOverlayDelegate::Show(views::WebDialogView* view) {
-  widget_ = new views::Widget;
-  views::Widget::InitParams params(
-      views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
-  params.context = Shell::GetPrimaryRootWindow();
-  params.delegate = view;
-  widget_->Init(params);
-
-  // Show the widget at the bottom of the work area.
-  gfx::Size size;
-  GetDialogSize(&size);
-  const gfx::Rect rect =
-      display::Screen::GetScreen()
-          ->GetDisplayNearestWindow(widget_->GetNativeWindow())
-          .work_area();
-  gfx::Rect bounds(rect.x() + (rect.width() - size.width()) / 2,
-                   rect.y() + (rect.height() - size.height()) / 2, size.width(),
-                   size.height());
-  widget_->SetBounds(bounds);
-
-  // The widget will be shown when the web contents gets ready to display.
-  return widget_;
-}
-
-ui::ModalType KeyboardOverlayDelegate::GetDialogModalType() const {
-  return ui::MODAL_TYPE_SYSTEM;
-}
-
-base::string16 KeyboardOverlayDelegate::GetDialogTitle() const {
-  return title_;
-}
-
-GURL KeyboardOverlayDelegate::GetDialogContentURL() const {
-  return url_;
-}
-
-void KeyboardOverlayDelegate::GetWebUIMessageHandlers(
-    std::vector<WebUIMessageHandler*>* handlers) const {
-  handlers->push_back(new PaintMessageHandler(widget_));
-}
-
-void KeyboardOverlayDelegate::GetDialogSize(gfx::Size* size) const {
-  using std::min;
-  DCHECK(widget_);
-  gfx::Rect rect = display::Screen::GetScreen()
-                       ->GetDisplayNearestWindow(widget_->GetNativeWindow())
-                       .work_area();
-  const int width = min(kBaseWidth, rect.width() - kHorizontalMargin);
-  const int height = width * kBaseHeight / kBaseWidth;
-  size->SetSize(width, height);
-}
-
-std::string KeyboardOverlayDelegate::GetDialogArgs() const {
-  return "[]";
-}
-
-void KeyboardOverlayDelegate::OnDialogClosed(const std::string& json_retval) {
-  delete this;
-  return;
-}
-
-void KeyboardOverlayDelegate::OnCloseContents(WebContents* source,
-                                              bool* out_close_dialog) {}
-
-bool KeyboardOverlayDelegate::ShouldShowDialogTitle() const {
-  return false;
-}
-
-bool KeyboardOverlayDelegate::HandleContextMenu(
-    const content::ContextMenuParams& params) {
-  return true;
-}
-
-}  // namespace ash
diff --git a/ash/content/keyboard_overlay/keyboard_overlay_delegate.h b/ash/content/keyboard_overlay/keyboard_overlay_delegate.h
deleted file mode 100644
index 0a7edf4..0000000
--- a/ash/content/keyboard_overlay/keyboard_overlay_delegate.h
+++ /dev/null
@@ -1,66 +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.
-
-#ifndef ASH_KEYBOARD_OVERLAY_KEYBOARD_OVERLAY_DELEGATE_H_
-#define ASH_KEYBOARD_OVERLAY_KEYBOARD_OVERLAY_DELEGATE_H_
-
-#include "ash/content/ash_with_content_export.h"
-#include "base/compiler_specific.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "ui/web_dialogs/web_dialog_delegate.h"
-#include "url/gurl.h"
-
-namespace views {
-class WebDialogView;
-class Widget;
-}
-
-namespace ash {
-
-// Delegate to handle showing the keyboard overlay drawing. Exported for test.
-class ASH_WITH_CONTENT_EXPORT KeyboardOverlayDelegate
-    : public ui::WebDialogDelegate {
- public:
-  KeyboardOverlayDelegate(const base::string16& title, const GURL& url);
-
-  // Shows the keyboard overlay widget. Returns the widget for testing.
-  views::Widget* Show(views::WebDialogView* view);
-
-  // Overridden from ui::WebDialogDelegate:
-  void GetDialogSize(gfx::Size* size) const override;
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(KeyboardOverlayDelegateTest, ShowAndClose);
-
-  ~KeyboardOverlayDelegate() override;
-
-  // Overridden from ui::WebDialogDelegate:
-  ui::ModalType GetDialogModalType() const override;
-  base::string16 GetDialogTitle() const override;
-  GURL GetDialogContentURL() const override;
-  void GetWebUIMessageHandlers(
-      std::vector<content::WebUIMessageHandler*>* handlers) const override;
-  std::string GetDialogArgs() const override;
-  void OnDialogClosed(const std::string& json_retval) override;
-  void OnCloseContents(content::WebContents* source,
-                       bool* out_close_dialog) override;
-  bool ShouldShowDialogTitle() const override;
-  bool HandleContextMenu(const content::ContextMenuParams& params) override;
-
-  // The dialog title.
-  base::string16 title_;
-
-  // The URL of the keyboard overlay.
-  GURL url_;
-
-  // The widget associated with this delegate. Not owned.
-  views::Widget* widget_;
-
-  DISALLOW_COPY_AND_ASSIGN(KeyboardOverlayDelegate);
-};
-
-}  // namespace ash
-
-#endif  // ASH_KEYBOARD_OVERLAY_KEYBOARD_OVERLAY_DELEGATE_H_
diff --git a/ash/content/keyboard_overlay/keyboard_overlay_delegate_unittest.cc b/ash/content/keyboard_overlay/keyboard_overlay_delegate_unittest.cc
deleted file mode 100644
index 7e503ff..0000000
--- a/ash/content/keyboard_overlay/keyboard_overlay_delegate_unittest.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/content/keyboard_overlay/keyboard_overlay_delegate.h"
-
-#include "ash/public/cpp/shelf_types.h"
-#include "ash/shelf/shelf.h"
-#include "ash/shell.h"
-#include "ash/test/ash_test_base.h"
-#include "base/strings/utf_string_conversions.h"
-#include "ui/aura/window.h"
-#include "ui/display/display.h"
-#include "ui/display/screen.h"
-#include "ui/views/widget/widget.h"
-
-namespace ash {
-
-class KeyboardOverlayDelegateTest
-    : public AshTestBase,
-      public testing::WithParamInterface<ShelfAlignment> {
- public:
-  KeyboardOverlayDelegateTest() : shelf_alignment_(GetParam()) {}
-  virtual ~KeyboardOverlayDelegateTest() = default;
-  ShelfAlignment shelf_alignment() const { return shelf_alignment_; }
-
- private:
-  ShelfAlignment shelf_alignment_;
-
-  DISALLOW_COPY_AND_ASSIGN(KeyboardOverlayDelegateTest);
-};
-
-// Verifies we can show and close the widget for the overlay dialog.
-TEST_P(KeyboardOverlayDelegateTest, ShowAndClose) {
-  UpdateDisplay("500x400,300x200");
-  GetPrimaryShelf()->SetAlignment(shelf_alignment());
-  KeyboardOverlayDelegate delegate(base::ASCIIToUTF16("Title"),
-                                   GURL("chrome://keyboardoverlay/"));
-  // Showing the dialog creates a widget.
-  views::Widget* widget = delegate.Show(nullptr);
-  EXPECT_TRUE(widget);
-
-  // The widget is on the primary root window.
-  EXPECT_EQ(Shell::GetPrimaryRootWindow(),
-            widget->GetNativeWindow()->GetRootWindow());
-
-  // The widget is horizontally and vertically centered in the work area.
-  gfx::Rect work_area =
-      display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
-  gfx::Rect bounds = widget->GetRestoredBounds();
-  EXPECT_EQ(work_area.CenterPoint().x(), bounds.CenterPoint().x());
-  EXPECT_EQ(work_area.y() + (work_area.height() - bounds.height()) / 2,
-            bounds.y());
-
-  // Clean up.
-  widget->CloseNow();
-}
-
-// Tests run four times - for all possible values of shelf alignment
-INSTANTIATE_TEST_CASE_P(ShelfAlignmentAny,
-                        KeyboardOverlayDelegateTest,
-                        testing::Values(SHELF_ALIGNMENT_BOTTOM,
-                                        SHELF_ALIGNMENT_LEFT,
-                                        SHELF_ALIGNMENT_RIGHT,
-                                        SHELF_ALIGNMENT_BOTTOM_LOCKED));
-
-}  // namespace ash
diff --git a/ash/content/keyboard_overlay/keyboard_overlay_view.cc b/ash/content/keyboard_overlay/keyboard_overlay_view.cc
deleted file mode 100644
index a7543e3..0000000
--- a/ash/content/keyboard_overlay/keyboard_overlay_view.cc
+++ /dev/null
@@ -1,94 +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.
-
-#include "ash/content/keyboard_overlay/keyboard_overlay_view.h"
-
-#include "ash/content/keyboard_overlay/keyboard_overlay_delegate.h"
-#include "ash/shell.h"
-#include "ash/strings/grit/ash_strings.h"
-#include "base/strings/utf_string_conversions.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/events/event.h"
-#include "ui/views/widget/widget.h"
-#include "ui/web_dialogs/web_dialog_delegate.h"
-
-using ui::WebDialogDelegate;
-
-namespace {
-
-// Keys to invoke Cancel (Escape, Ctrl+Alt+/, or Shift+Ctrl+Alt+/, Help, F14).
-const ash::KeyboardOverlayView::KeyEventData kCancelKeys[] = {
-    {ui::VKEY_ESCAPE, ui::EF_NONE},
-    {ui::VKEY_OEM_2, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN},
-    {ui::VKEY_OEM_2, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN},
-    {ui::VKEY_HELP, ui::EF_NONE},
-    {ui::VKEY_F14, ui::EF_NONE},
-};
-}
-
-namespace ash {
-
-KeyboardOverlayView::KeyboardOverlayView(content::BrowserContext* context,
-                                         WebDialogDelegate* delegate,
-                                         WebContentsHandler* handler)
-    : views::WebDialogView(context, delegate, handler) {}
-
-KeyboardOverlayView::~KeyboardOverlayView() = default;
-
-void KeyboardOverlayView::Cancel() {
-  Shell::Get()->overlay_filter()->Deactivate(this);
-  views::Widget* widget = GetWidget();
-  if (widget)
-    widget->Close();
-}
-
-bool KeyboardOverlayView::IsCancelingKeyEvent(ui::KeyEvent* event) {
-  if (event->type() != ui::ET_KEY_PRESSED)
-    return false;
-
-  // Ignore the non-modifiers flags in order to treat it the same way
-  // Accelerators are generated and compared. crbug.com/535008.
-  const int flags = ui::Accelerator::MaskOutKeyEventFlags(event->flags());
-  for (size_t i = 0; i < arraysize(kCancelKeys); ++i) {
-    if ((kCancelKeys[i].key_code == event->key_code()) &&
-        (kCancelKeys[i].flags == flags))
-      return true;
-  }
-  return false;
-}
-
-aura::Window* KeyboardOverlayView::GetWindow() {
-  return GetWidget()->GetNativeWindow();
-}
-
-// static
-void KeyboardOverlayView::ShowDialog(content::BrowserContext* context,
-                                     WebContentsHandler* handler,
-                                     const GURL& url) {
-  if (Shell::Get()->overlay_filter()->IsActive())
-    return;
-
-  KeyboardOverlayDelegate* delegate = new KeyboardOverlayDelegate(
-      l10n_util::GetStringUTF16(IDS_ASH_KEYBOARD_OVERLAY_TITLE), url);
-  KeyboardOverlayView* view =
-      new KeyboardOverlayView(context, delegate, handler);
-  delegate->Show(view);
-
-  Shell::Get()->overlay_filter()->Activate(view);
-}
-
-void KeyboardOverlayView::WindowClosing() {
-  Cancel();
-}
-
-// static
-void KeyboardOverlayView::GetCancelingKeysForTesting(
-    std::vector<KeyboardOverlayView::KeyEventData>* canceling_keys) {
-  CHECK(canceling_keys);
-  canceling_keys->clear();
-  for (size_t i = 0; i < arraysize(kCancelKeys); ++i)
-    canceling_keys->push_back(kCancelKeys[i]);
-}
-
-}  // namespace ash
diff --git a/ash/content/keyboard_overlay/keyboard_overlay_view.h b/ash/content/keyboard_overlay/keyboard_overlay_view.h
deleted file mode 100644
index 2f709c2..0000000
--- a/ash/content/keyboard_overlay/keyboard_overlay_view.h
+++ /dev/null
@@ -1,71 +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.
-
-#ifndef ASH_KEYBOARD_OVERLAY_KEYBOARD_OVERLAY_VIEW_H_
-#define ASH_KEYBOARD_OVERLAY_KEYBOARD_OVERLAY_VIEW_H_
-
-#include <vector>
-
-#include "ash/content/ash_with_content_export.h"
-#include "ash/wm/overlay_event_filter.h"
-#include "base/compiler_specific.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "ui/views/controls/webview/web_dialog_view.h"
-
-class GURL;
-
-namespace content {
-class BrowserContext;
-}
-
-namespace ui {
-class WebDialogDelegate;
-}
-
-namespace ash {
-
-// A customized dialog view for the keyboard overlay.
-class ASH_WITH_CONTENT_EXPORT KeyboardOverlayView
-    : public views::WebDialogView,
-      public ash::OverlayEventFilter::Delegate {
- public:
-  struct KeyEventData {
-    ui::KeyboardCode key_code;
-    int flags;
-  };
-
-  KeyboardOverlayView(content::BrowserContext* context,
-                      ui::WebDialogDelegate* delegate,
-                      WebContentsHandler* handler);
-  ~KeyboardOverlayView() override;
-
-  // Overridden from ash::OverlayEventFilter::Delegate:
-  void Cancel() override;
-  bool IsCancelingKeyEvent(ui::KeyEvent* event) override;
-  aura::Window* GetWindow() override;
-
-  // Shows the keyboard overlay.
-  static void ShowDialog(content::BrowserContext* context,
-                         WebContentsHandler* handler,
-                         const GURL& url);
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(KeyboardOverlayViewTest, OpenAcceleratorsClose);
-  FRIEND_TEST_ALL_PREFIXES(KeyboardOverlayViewTest,
-                           TestCancelingKeysWithNonModifierFlags);
-  FRIEND_TEST_ALL_PREFIXES(KeyboardOverlayViewTest, NoRedundantCancelingKeys);
-
-  // Overridden from views::WidgetDelegate:
-  void WindowClosing() override;
-
-  static void GetCancelingKeysForTesting(
-      std::vector<KeyEventData>* canceling_keys);
-
-  DISALLOW_COPY_AND_ASSIGN(KeyboardOverlayView);
-};
-
-}  // namespace ash
-
-#endif  // ASH_KEYBOARD_OVERLAY_KEYBOARD_OVERLAY_VIEW_H_
diff --git a/ash/content/keyboard_overlay/keyboard_overlay_view_unittest.cc b/ash/content/keyboard_overlay/keyboard_overlay_view_unittest.cc
deleted file mode 100644
index e39258e..0000000
--- a/ash/content/keyboard_overlay/keyboard_overlay_view_unittest.cc
+++ /dev/null
@@ -1,93 +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.
-
-#include "ash/content/keyboard_overlay/keyboard_overlay_view.h"
-
-#include <algorithm>
-
-#include "ash/content/keyboard_overlay/keyboard_overlay_delegate.h"
-#include "ash/public/cpp/accelerators.h"
-#include "ash/test/ash_test_base.h"
-#include "base/stl_util.h"
-#include "content/public/test/test_browser_context.h"
-#include "ui/web_dialogs/test/test_web_contents_handler.h"
-#include "ui/web_dialogs/test/test_web_dialog_delegate.h"
-
-namespace ash {
-
-using KeyboardOverlayViewTest = AshTestBase;
-
-bool operator==(const KeyboardOverlayView::KeyEventData& lhs,
-                const KeyboardOverlayView::KeyEventData& rhs) {
-  return (lhs.key_code == rhs.key_code) && (lhs.flags == rhs.flags);
-}
-
-// Verifies that the accelerators that open the keyboard overlay close it.
-TEST_F(KeyboardOverlayViewTest, OpenAcceleratorsClose) {
-  ui::test::TestWebDialogDelegate delegate(GURL("chrome://keyboardoverlay"));
-  content::TestBrowserContext browser_context;
-  KeyboardOverlayView view(&browser_context, &delegate,
-                           new ui::test::TestWebContentsHandler);
-  for (size_t i = 0; i < kAcceleratorDataLength; ++i) {
-    if (kAcceleratorData[i].action != SHOW_KEYBOARD_OVERLAY)
-      continue;
-    const AcceleratorData& open_key_data = kAcceleratorData[i];
-    ui::KeyEvent open_key(open_key_data.trigger_on_press ? ui::ET_KEY_PRESSED
-                                                         : ui::ET_KEY_RELEASED,
-                          open_key_data.keycode, open_key_data.modifiers);
-    EXPECT_TRUE(view.IsCancelingKeyEvent(&open_key));
-  }
-}
-
-// Test modifiers that might exist in a KeyEvent but they shouldn't be
-// considered in an accelerator comparison to determine if a KeyEvent is a
-// canceling key.
-TEST_F(KeyboardOverlayViewTest, TestCancelingKeysWithNonModifierFlags) {
-  ui::test::TestWebDialogDelegate delegate(GURL("chrome://keyboardoverlay"));
-  content::TestBrowserContext browser_context;
-  KeyboardOverlayView view(&browser_context, &delegate,
-                           new ui::test::TestWebContentsHandler);
-
-  const int kNonModifierFlags = ui::EF_IS_SYNTHESIZED | ui::EF_NUM_LOCK_ON |
-                                ui::EF_IME_FABRICATED_KEY | ui::EF_IS_REPEAT;
-
-  std::vector<KeyboardOverlayView::KeyEventData> canceling_keys;
-  KeyboardOverlayView::GetCancelingKeysForTesting(&canceling_keys);
-  for (const auto& key_data : canceling_keys) {
-    ui::KeyEvent key_event(ui::ET_KEY_PRESSED, key_data.key_code,
-                           key_data.flags | kNonModifierFlags);
-    EXPECT_TRUE(view.IsCancelingKeyEvent(&key_event));
-  }
-}
-
-// Verifies that there are no redunduant keys in the canceling keys.
-TEST_F(KeyboardOverlayViewTest, NoRedundantCancelingKeys) {
-  std::vector<KeyboardOverlayView::KeyEventData> open_keys;
-  for (size_t i = 0; i < kAcceleratorDataLength; ++i) {
-    if (kAcceleratorData[i].action != SHOW_KEYBOARD_OVERLAY)
-      continue;
-    // Escape is used just for canceling.
-    KeyboardOverlayView::KeyEventData open_key = {
-        kAcceleratorData[i].keycode, kAcceleratorData[i].modifiers,
-    };
-    open_keys.push_back(open_key);
-  }
-
-  std::vector<KeyboardOverlayView::KeyEventData> canceling_keys;
-  KeyboardOverlayView::GetCancelingKeysForTesting(&canceling_keys);
-
-  // Escape is used just for canceling, so exclude it from the comparison with
-  // open keys.
-  KeyboardOverlayView::KeyEventData escape = {ui::VKEY_ESCAPE, ui::EF_NONE};
-  std::vector<KeyboardOverlayView::KeyEventData>::iterator escape_itr =
-      std::find(canceling_keys.begin(), canceling_keys.end(), escape);
-  canceling_keys.erase(escape_itr);
-
-  // Other canceling keys should be same as opening keys.
-  EXPECT_EQ(open_keys.size(), canceling_keys.size());
-  for (size_t i = 0; i < canceling_keys.size(); ++i)
-    EXPECT_TRUE(base::ContainsValue(open_keys, canceling_keys[i]));
-}
-
-}  // namespace ash
diff --git a/ash/new_window_controller.cc b/ash/new_window_controller.cc
index a47fb17..cc08f6d 100644
--- a/ash/new_window_controller.cc
+++ b/ash/new_window_controller.cc
@@ -22,13 +22,6 @@
   client_.Bind(std::move(client));
 }
 
-// TODO(crbug.com/755448): Remove this when the new shortcut viewer is enabled.
-void NewWindowController::ShowKeyboardOverlay() {
-  // TODO(estade): implement this here rather than passing off to |client_|.
-  if (client_)
-    client_->ShowKeyboardOverlay();
-}
-
 void NewWindowController::NewTabWithUrl(const GURL& url,
                                         bool from_user_interaction) {
   if (client_)
diff --git a/ash/new_window_controller.h b/ash/new_window_controller.h
index c28d221c..dc0f5912 100644
--- a/ash/new_window_controller.h
+++ b/ash/new_window_controller.h
@@ -26,7 +26,6 @@
 
   // NewWindowController:
   void SetClient(mojom::NewWindowClientAssociatedPtrInfo client) override;
-  void ShowKeyboardOverlay() override;
 
   // Pass throughs for methods of the same name on |client_|.
   void NewTabWithUrl(const GURL& url, bool from_user_interaction);
diff --git a/ash/public/cpp/accelerators.cc b/ash/public/cpp/accelerators.cc
index 2b664123..d06cd9d 100644
--- a/ash/public/cpp/accelerators.cc
+++ b/ash/public/cpp/accelerators.cc
@@ -109,13 +109,13 @@
     {true, ui::VKEY_MEDIA_LAUNCH_APP2, ui::EF_SHIFT_DOWN, TOGGLE_FULLSCREEN},
     {true, ui::VKEY_ESCAPE, ui::EF_SHIFT_DOWN | ui::EF_COMMAND_DOWN, UNPIN},
     {true, ui::VKEY_L, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, FOCUS_SHELF},
-    {true, ui::VKEY_HELP, ui::EF_NONE, SHOW_KEYBOARD_OVERLAY},
+    {true, ui::VKEY_HELP, ui::EF_NONE, SHOW_SHORTCUT_VIEWER},
     {true, ui::VKEY_OEM_2, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
-     SHOW_KEYBOARD_OVERLAY},
+     SHOW_SHORTCUT_VIEWER},
     {true, ui::VKEY_OEM_2,
      ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
-     SHOW_KEYBOARD_OVERLAY},
-    {true, ui::VKEY_F14, ui::EF_NONE, SHOW_KEYBOARD_OVERLAY},
+     SHOW_SHORTCUT_VIEWER},
+    {true, ui::VKEY_F14, ui::EF_NONE, SHOW_SHORTCUT_VIEWER},
     {true, ui::VKEY_N, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
      TOGGLE_MESSAGE_CENTER_BUBBLE},
     {true, ui::VKEY_P, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, SHOW_STYLUS_TOOLS},
diff --git a/ash/public/cpp/accelerators.h b/ash/public/cpp/accelerators.h
index 8f17065..fa7250ea 100644
--- a/ash/public/cpp/accelerators.h
+++ b/ash/public/cpp/accelerators.h
@@ -80,7 +80,7 @@
   SCALE_UI_RESET,
   SCALE_UI_UP,
   SHOW_IME_MENU_BUBBLE,
-  SHOW_KEYBOARD_OVERLAY,
+  SHOW_SHORTCUT_VIEWER,
   SHOW_STYLUS_TOOLS,
   SHOW_TASK_MANAGER,
   START_VOICE_INTERACTION,
diff --git a/ash/public/cpp/app_list/app_list_config.cc b/ash/public/cpp/app_list/app_list_config.cc
index 08c0391..452b59a 100644
--- a/ash/public/cpp/app_list/app_list_config.cc
+++ b/ash/public/cpp/app_list/app_list_config.cc
@@ -24,8 +24,8 @@
       grid_focus_dimension_(64),
       grid_focus_corner_radius_(8),
       search_tile_icon_dimension_(48),
-      search_tile_badge_icon_dimension_(12),
-      search_tile_badge_background_radius_(10),
+      search_tile_badge_icon_dimension_(22),
+      search_tile_badge_icon_offset_(5),
       search_list_icon_dimension_(18),
       search_list_badge_icon_dimension_(14),
       suggestion_chip_icon_dimension_(48),
diff --git a/ash/public/cpp/app_list/app_list_config.h b/ash/public/cpp/app_list/app_list_config.h
index b78df49c..bb0f806 100644
--- a/ash/public/cpp/app_list/app_list_config.h
+++ b/ash/public/cpp/app_list/app_list_config.h
@@ -42,8 +42,8 @@
   int search_tile_badge_icon_dimension() const {
     return search_tile_badge_icon_dimension_;
   }
-  int search_tile_badge_background_radius() const {
-    return search_tile_badge_background_radius_;
+  int search_tile_badge_icon_offset() const {
+    return search_tile_badge_icon_offset_;
   }
   int search_list_icon_dimension() const { return search_list_icon_dimension_; }
   int search_list_badge_icon_dimension() const {
@@ -175,9 +175,8 @@
   // The badge icon dimension of tile views in search result page view.
   int search_tile_badge_icon_dimension_;
 
-  // The badge background corner radius of tile views in search result page
-  // view.
-  int search_tile_badge_background_radius_;
+  // The badge icon offset of tile views in search result page view.
+  int search_tile_badge_icon_offset_;
 
   // The icon dimension of list views in search result page view.
   int search_list_icon_dimension_;
diff --git a/ash/public/cpp/ash_features.cc b/ash/public/cpp/ash_features.cc
index e61fd16..32efa1d1 100644
--- a/ash/public/cpp/ash_features.cc
+++ b/ash/public/cpp/ash_features.cc
@@ -19,9 +19,6 @@
 const base::Feature kDragTabsInTabletMode{"DragTabsInTabletMode",
                                           base::FEATURE_ENABLED_BY_DEFAULT};
 
-const base::Feature kKeyboardShortcutViewer{"KeyboardShortcutViewer",
-                                            base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kKeyboardShortcutViewerApp{
     "KeyboardShortcutViewerApp", base::FEATURE_ENABLED_BY_DEFAULT};
 
@@ -64,10 +61,6 @@
   return base::FeatureList::IsEnabled(kDockedMagnifier);
 }
 
-bool IsKeyboardShortcutViewerEnabled() {
-  return base::FeatureList::IsEnabled(kKeyboardShortcutViewer);
-}
-
 bool IsKeyboardShortcutViewerAppEnabled() {
   return base::FeatureList::IsEnabled(kKeyboardShortcutViewerApp);
 }
diff --git a/ash/public/cpp/ash_features.h b/ash/public/cpp/ash_features.h
index 4db82ae..0053904 100644
--- a/ash/public/cpp/ash_features.h
+++ b/ash/public/cpp/ash_features.h
@@ -26,11 +26,6 @@
 // https://crbug.com/823769.
 ASH_PUBLIC_EXPORT extern const base::Feature kDragTabsInTabletMode;
 
-// Enables the keyboard shortcut viewer.
-// TODO(wutao): Remove this after the feature is fully launched.
-// https://crbug.com/755448.
-ASH_PUBLIC_EXPORT extern const base::Feature kKeyboardShortcutViewer;
-
 // Enables the keyboard shortcut viewer mojo app.
 // TODO(msw): Remove this after the feature is fully launched.
 // https://crbug.com/841020.
@@ -84,8 +79,6 @@
 
 ASH_PUBLIC_EXPORT bool IsDockedMagnifierEnabled();
 
-ASH_PUBLIC_EXPORT bool IsKeyboardShortcutViewerEnabled();
-
 ASH_PUBLIC_EXPORT bool IsKeyboardShortcutViewerAppEnabled();
 
 ASH_PUBLIC_EXPORT bool IsLockScreenNotificationsEnabled();
diff --git a/ash/public/cpp/ash_pref_names.cc b/ash/public/cpp/ash_pref_names.cc
index d83b0ed..4a3ed9d 100644
--- a/ash/public/cpp/ash_pref_names.cc
+++ b/ash/public/cpp/ash_pref_names.cc
@@ -53,6 +53,10 @@
 // maps to mojom::AccessibilityController::AutoclickEventType.
 const char kAccessibilityAutoclickEventType[] =
     "settings.a11y.autoclick_event_type";
+// Whether Autoclick should immediately return to left click after performing
+// another event type action, or whether it should stay as the other event type.
+const char kAccessibilityAutoclickRevertToLeftClick[] =
+    "settings.a11y.autoclick_revert_to_left_click";
 // A boolean pref which determines whether caret highlighting is enabled.
 const char kAccessibilityCaretHighlightEnabled[] =
     "settings.a11y.caret_highlight";
@@ -95,6 +99,10 @@
 // ever been shown.
 const char kDictationAcceleratorDialogHasBeenAccepted[] =
     "settings.a11y.dictation_accelerator_dialog_has_been_accepted";
+// A boolean pref which indicates whether the display rotation confirmation
+// dialog has ever been shown.
+const char kDisplayRotationAcceleratorDialogHasBeenAccepted[] =
+    "settings.a11y.display_rotation_accelerator_dialog_has_been_accepted";
 
 // A dictionary pref that stores the mixed mirror mode parameters.
 const char kDisplayMixedMirrorModeParams[] =
diff --git a/ash/public/cpp/ash_pref_names.h b/ash/public/cpp/ash_pref_names.h
index eb5303f..d0edd495 100644
--- a/ash/public/cpp/ash_pref_names.h
+++ b/ash/public/cpp/ash_pref_names.h
@@ -24,6 +24,7 @@
 ASH_PUBLIC_EXPORT extern const char kAccessibilityAutoclickEnabled[];
 ASH_PUBLIC_EXPORT extern const char kAccessibilityAutoclickDelayMs[];
 ASH_PUBLIC_EXPORT extern const char kAccessibilityAutoclickEventType[];
+ASH_PUBLIC_EXPORT extern const char kAccessibilityAutoclickRevertToLeftClick[];
 ASH_PUBLIC_EXPORT extern const char kAccessibilityCaretHighlightEnabled[];
 ASH_PUBLIC_EXPORT extern const char kAccessibilityCursorHighlightEnabled[];
 ASH_PUBLIC_EXPORT extern const char kAccessibilityFocusHighlightEnabled[];
@@ -42,6 +43,8 @@
     kScreenMagnifierAcceleratorDialogHasBeenAccepted[];
 ASH_PUBLIC_EXPORT extern const char
     kDictationAcceleratorDialogHasBeenAccepted[];
+ASH_PUBLIC_EXPORT extern const char
+    kDisplayRotationAcceleratorDialogHasBeenAccepted[];
 
 ASH_PUBLIC_EXPORT extern const char kDisplayMixedMirrorModeParams[];
 ASH_PUBLIC_EXPORT extern const char kDisplayPowerState[];
diff --git a/ash/public/interfaces/new_window.mojom b/ash/public/interfaces/new_window.mojom
index 2c1231a..6a25b7a 100644
--- a/ash/public/interfaces/new_window.mojom
+++ b/ash/public/interfaces/new_window.mojom
@@ -10,12 +10,6 @@
 // An exported object in ash which lets an ash consumer set a client interface.
 interface NewWindowController {
   SetClient(associated NewWindowClient client);
-
-  // TODO(crbug.com/755448): Remove this when the new shortcut viewer is
-  // enabled.
-  // Shows the keyboard shortcut overlay. TODO(mash): this calls the client
-  // function of the same name below, but it should be implemented inside ash.
-  ShowKeyboardOverlay();
 };
 
 // A delegate interface that an ash user sends to ash to handle certain window
@@ -44,11 +38,6 @@
   // Invoked when the user uses Shift+Ctrl+T to restore the closed tab.
   RestoreTab();
 
-  // TODO(crbug.com/755448): Remove this when the new shortcut viewer is
-  // enabled.
-  // Shows the keyboard shortcut overlay.
-  ShowKeyboardOverlay();
-
   // Show the keyboard shortcut viewer.
   ShowKeyboardShortcutViewer();
 
diff --git a/ash/public/interfaces/shell_test_api.mojom b/ash/public/interfaces/shell_test_api.mojom
index 34168265..c4c0b17 100644
--- a/ash/public/interfaces/shell_test_api.mojom
+++ b/ash/public/interfaces/shell_test_api.mojom
@@ -19,4 +19,8 @@
   // Tells the SplitViewController to snap the given window to the left.
   // The client name is used to find the client's WindowTree.
   SnapWindowInSplitView(string client_name, uint64 window_id) => ();
+
+  // Fullscreens the active window, as if the user had pressed the hardware
+  // fullscreen button.
+  ToggleFullscreen() => ();
 };
diff --git a/ash/shelf/shelf_constants.h b/ash/shelf/shelf_constants.h
index 1f886a2..cbc09e0 100644
--- a/ash/shelf/shelf_constants.h
+++ b/ash/shelf/shelf_constants.h
@@ -69,7 +69,7 @@
 // The alpha value for the shelf background.
 ASH_EXPORT constexpr int kShelfTranslucentOverAppList = 51;            // 20%
 ASH_EXPORT constexpr int kShelfTranslucentAlpha = 153;                 // 60%
-ASH_EXPORT constexpr int kShelfTranslucentMaximizedWindow = 230;       // 90%
+ASH_EXPORT constexpr int kShelfTranslucentMaximizedWindow = 255;       // 100%
 
 // The alpha value used to darken a colorized shelf when the shelf is
 // translucent.
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index ef7dc28..79a81bc 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -1173,9 +1173,10 @@
 }
 
 bool ShelfLayoutManager::ShouldBlurShelfBackground() {
-  if (!IsBackgroundBlurEnabled())
-    return false;
-  return state_.session_state == session_manager::SessionState::ACTIVE;
+  return IsBackgroundBlurEnabled() &&
+         (shelf_background_type_ == SHELF_BACKGROUND_DEFAULT ||
+          shelf_background_type_ == SHELF_BACKGROUND_OVERLAP) &&
+         state_.session_state == session_manager::SessionState::ACTIVE;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index 7559708..0338a39 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -289,7 +289,7 @@
   views::Widget::InitParams params(
       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
   params.name = "ShelfWidget";
-  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
+  params.layer_type = ui::LAYER_NOT_DRAWN;
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   params.delegate = delegate_view_;
   params.parent = shelf_container;
diff --git a/ash/shell.cc b/ash/shell.cc
index 4e5295366..78abc8540 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -585,6 +585,11 @@
     observer.OnOverviewModeStarting();
 }
 
+void Shell::NotifyOverviewModeStartingAnimationComplete(bool canceled) {
+  for (auto& observer : shell_observers_)
+    observer.OnOverviewModeStartingAnimationComplete(canceled);
+}
+
 void Shell::NotifyOverviewModeEnding() {
   for (auto& observer : shell_observers_)
     observer.OnOverviewModeEnding();
@@ -595,9 +600,9 @@
     observer.OnOverviewModeEnded();
 }
 
-void Shell::NotifyOverviewModeEndingAnimationComplete() {
+void Shell::NotifyOverviewModeEndingAnimationComplete(bool canceled) {
   for (auto& observer : shell_observers_)
-    observer.OnOverviewModeEndingAnimationComplete();
+    observer.OnOverviewModeEndingAnimationComplete(canceled);
 }
 
 void Shell::NotifySplitViewModeStarting() {
diff --git a/ash/shell.h b/ash/shell.h
index f301648b..687d853e 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -602,15 +602,18 @@
   // windows get re-arranged).
   void NotifyOverviewModeStarting();
 
-  // Notifies observers that overview mode is about to end (bofore the windows
+  // Notifies observers that the start overview mode animation has completed.
+  void NotifyOverviewModeStartingAnimationComplete(bool canceled);
+
+  // Notifies observers that overview mode is about to end (before the windows
   // restore themselves).
   void NotifyOverviewModeEnding();
 
   // Notifies observers that overview mode has ended.
   void NotifyOverviewModeEnded();
 
-  // Notifies observers that the end overivew mode animation has completed.
-  void NotifyOverviewModeEndingAnimationComplete();
+  // Notifies observers that the end overview mode animation has completed.
+  void NotifyOverviewModeEndingAnimationComplete(bool canceled);
 
   // Notifies observers that split view mode is about to be started (before the
   // window gets snapped and activated).
diff --git a/ash/shell_observer.h b/ash/shell_observer.h
index b9d9bc3..615b7cd 100644
--- a/ash/shell_observer.h
+++ b/ash/shell_observer.h
@@ -45,6 +45,11 @@
   // get re-arranged).
   virtual void OnOverviewModeStarting() {}
 
+  // Called after the animations that happen when overview mode is started are
+  // complete. If |canceled| it means overview was quit before the start
+  // animations were finished.
+  virtual void OnOverviewModeStartingAnimationComplete(bool canceled) {}
+
   // Called when the overview mode is about to end (bofore the windows restore
   // themselves).
   virtual void OnOverviewModeEnding() {}
@@ -53,8 +58,9 @@
   virtual void OnOverviewModeEnded() {}
 
   // Called after the animations that happen when overview mode is ended are
-  // complete.
-  virtual void OnOverviewModeEndingAnimationComplete() {}
+  // complete. If |canceled| it means overview was reentered before the exit
+  // animations were finished.
+  virtual void OnOverviewModeEndingAnimationComplete(bool canceled) {}
 
   // Called when the split view mode is about to be started before the window
   // gets snapped and activated).
diff --git a/ash/shell_test_api.cc b/ash/shell_test_api.cc
index 4b1047e..b23260c 100644
--- a/ash/shell_test_api.cc
+++ b/ash/shell_test_api.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/accelerators/accelerator_commands.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
 #include "ash/system/power/backlights_forced_off_setter.h"
@@ -103,4 +104,9 @@
   std::move(cb).Run();
 }
 
+void ShellTestApi::ToggleFullscreen(ToggleFullscreenCallback cb) {
+  ash::accelerators::ToggleFullscreen();
+  std::move(cb).Run();
+}
+
 }  // namespace ash
diff --git a/ash/shell_test_api.h b/ash/shell_test_api.h
index e1c56bfa..44a6a879 100644
--- a/ash/shell_test_api.h
+++ b/ash/shell_test_api.h
@@ -58,6 +58,7 @@
   void SnapWindowInSplitView(const std::string& client_name,
                              ws::Id window_id,
                              SnapWindowInSplitViewCallback cb) override;
+  void ToggleFullscreen(ToggleFullscreenCallback cb) override;
 
  private:
   Shell* shell_;  // not owned
diff --git a/ash/system/message_center/arc/arc_notification_content_view.cc b/ash/system/message_center/arc/arc_notification_content_view.cc
index 9f1a68f..4c4e7193 100644
--- a/ash/system/message_center/arc/arc_notification_content_view.cc
+++ b/ash/system/message_center/arc/arc_notification_content_view.cc
@@ -185,12 +185,9 @@
   DISALLOW_COPY_AND_ASSIGN(EventForwarder);
 };
 
-class ArcNotificationContentView::SlideHelper
-    : public ui::LayerAnimationObserver {
+class ArcNotificationContentView::SlideHelper {
  public:
   explicit SlideHelper(ArcNotificationContentView* owner) : owner_(owner) {
-    GetSlideOutLayer()->GetAnimator()->AddObserver(this);
-
     // Reset opacity to 1 to handle to case when the surface is sliding before
     // getting managed by this class, e.g. sliding in a popup before showing
     // in a message center view.
@@ -199,25 +196,25 @@
       owner_->surface_->GetWindow()->layer()->SetOpacity(1.0f);
     }
   }
-  ~SlideHelper() override {
-    if (GetSlideOutLayer())
-      GetSlideOutLayer()->GetAnimator()->RemoveObserver(this);
-  }
+  virtual ~SlideHelper() = default;
 
-  void Update() {
+  void Update(base::Optional<bool> slide_in_progress) {
+    if (slide_in_progress.has_value())
+      slide_in_progress_ = slide_in_progress.value();
+
     const bool has_animation =
         GetSlideOutLayer()->GetAnimator()->is_animating();
     const bool has_transform = !GetSlideOutLayer()->transform().IsIdentity();
-    const bool sliding = has_transform || has_animation;
-    if (sliding_ == sliding)
+    const bool moving = (slide_in_progress_ && has_transform) || has_animation;
+
+    if (moving_ == moving)
       return;
+    moving_ = moving;
 
-    sliding_ = sliding;
-
-    if (sliding_)
-      OnSlideStart();
+    if (moving_)
+      owner_->ShowCopiedSurface();
     else
-      OnSlideEnd();
+      owner_->HideCopiedSurface();
   }
 
  private:
@@ -227,21 +224,9 @@
     return layer ? layer : owner_->GetWidget()->GetLayer();
   }
 
-  void OnSlideStart() { owner_->ShowCopiedSurface(); }
-
-  void OnSlideEnd() { owner_->HideCopiedSurface(); }
-
-  // ui::LayerAnimationObserver
-  void OnLayerAnimationEnded(ui::LayerAnimationSequence* seq) override {
-    Update();
-  }
-  void OnLayerAnimationAborted(ui::LayerAnimationSequence* seq) override {
-    Update();
-  }
-  void OnLayerAnimationScheduled(ui::LayerAnimationSequence* seq) override {}
-
   ArcNotificationContentView* const owner_;
-  bool sliding_ = false;
+  bool slide_in_progress_ = false;
+  bool moving_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(SlideHelper);
 };
@@ -368,9 +353,9 @@
     UpdateMask();
 }
 
-void ArcNotificationContentView::OnSlideChanged() {
+void ArcNotificationContentView::OnSlideChanged(bool in_progress) {
   if (slide_helper_)
-    slide_helper_->Update();
+    slide_helper_->Update(in_progress);
 }
 
 void ArcNotificationContentView::OnContainerAnimationStarted() {
@@ -508,7 +493,7 @@
   slide_helper_.reset(new SlideHelper(this));
 
   // Invokes Update() in case surface is attached during a slide.
-  slide_helper_->Update();
+  slide_helper_->Update(base::nullopt);
 
   // (Re-)create the floating buttons after |surface_| is attached to a widget.
   MaybeCreateFloatingControlButtons();
diff --git a/ash/system/message_center/arc/arc_notification_content_view.h b/ash/system/message_center/arc/arc_notification_content_view.h
index 40c5b4b..76e3cca1 100644
--- a/ash/system/message_center/arc/arc_notification_content_view.h
+++ b/ash/system/message_center/arc/arc_notification_content_view.h
@@ -58,7 +58,7 @@
   message_center::NotificationControlButtonsView* GetControlButtonsView();
   void UpdateControlButtonsVisibility();
   void UpdateCornerRadius(int top_radius, int bottom_radius);
-  void OnSlideChanged();
+  void OnSlideChanged(bool in_progress);
   void OnContainerAnimationStarted();
   void OnContainerAnimationEnded();
   void ActivateWidget(bool activate);
diff --git a/ash/system/message_center/arc/arc_notification_view.cc b/ash/system/message_center/arc/arc_notification_view.cc
index cdb050c..e03fa64 100644
--- a/ash/system/message_center/arc/arc_notification_view.cc
+++ b/ash/system/message_center/arc/arc_notification_view.cc
@@ -161,9 +161,9 @@
   content_view_->OnContainerAnimationEnded();
 }
 
-void ArcNotificationView::OnSlideChanged() {
-  MessageView::OnSlideChanged();
-  content_view_->OnSlideChanged();
+void ArcNotificationView::OnSlideChanged(bool in_progress) {
+  MessageView::OnSlideChanged(in_progress);
+  content_view_->OnSlideChanged(in_progress);
 }
 
 gfx::Size ArcNotificationView::CalculatePreferredSize() const {
diff --git a/ash/system/message_center/arc/arc_notification_view.h b/ash/system/message_center/arc/arc_notification_view.h
index 71a13dc..14ccfe7 100644
--- a/ash/system/message_center/arc/arc_notification_view.h
+++ b/ash/system/message_center/arc/arc_notification_view.h
@@ -56,8 +56,8 @@
   void OnSnoozeButtonPressed(const ui::Event& event) override;
   void UpdateCornerRadius(int top_radius, int bottom_radius) override;
 
-  // views::SlideOutController::Delegate:
-  void OnSlideChanged() override;
+  // message_center::SlideOutController::Delegate:
+  void OnSlideChanged(bool in_progress) override;
 
   // Overridden from views::View:
   gfx::Size CalculatePreferredSize() const override;
diff --git a/ash/system/status_area_widget.cc b/ash/system/status_area_widget.cc
index a67a61ae..8e8cb2c 100644
--- a/ash/system/status_area_widget.cc
+++ b/ash/system/status_area_widget.cc
@@ -270,14 +270,26 @@
 }
 
 void StatusAreaWidget::OnMouseEvent(ui::MouseEvent* event) {
-  if (event->type() == ui::ET_MOUSE_PRESSED)
+  // Clicking anywhere except the virtual keyboard tray icon should hide the
+  // virtual keyboard.
+  gfx::Point location = event->location();
+  views::View::ConvertPointFromWidget(virtual_keyboard_tray_.get(), &location);
+  if (event->type() == ui::ET_MOUSE_PRESSED &&
+      !virtual_keyboard_tray_->HitTestPoint(location)) {
     keyboard::KeyboardController::Get()->HideKeyboardImplicitlyByUser();
+  }
   views::Widget::OnMouseEvent(event);
 }
 
 void StatusAreaWidget::OnGestureEvent(ui::GestureEvent* event) {
-  if (event->type() == ui::ET_GESTURE_TAP_DOWN)
+  // Tapping anywhere except the virtual keyboard tray icon should hide the
+  // virtual keyboard.
+  gfx::Point location = event->location();
+  views::View::ConvertPointFromWidget(virtual_keyboard_tray_.get(), &location);
+  if (event->type() == ui::ET_GESTURE_TAP_DOWN &&
+      !virtual_keyboard_tray_->HitTestPoint(location)) {
     keyboard::KeyboardController::Get()->HideKeyboardImplicitlyByUser();
+  }
   views::Widget::OnGestureEvent(event);
 }
 
diff --git a/ash/system/status_area_widget_unittest.cc b/ash/system/status_area_widget_unittest.cc
index 533677e..1f21bf39 100644
--- a/ash/system/status_area_widget_unittest.cc
+++ b/ash/system/status_area_widget_unittest.cc
@@ -340,6 +340,8 @@
     // only case where both the virtual keyboard and the shelf are visible.
     keyboard_controller()->SetContainerType(keyboard::ContainerType::FLOATING,
                                             base::nullopt, base::DoNothing());
+    keyboard_controller()->GetKeyboardWindow()->SetBounds(
+        gfx::Rect(0, 0, 10, 10));
   }
 
   keyboard::KeyboardController* keyboard_controller() {
@@ -347,6 +349,47 @@
   }
 };
 
+// See https://crbug.com/897672.
+TEST_F(StatusAreaWidgetVirtualKeyboardTest,
+       ClickingVirtualKeyboardTrayHidesShownKeyboard) {
+  // Set up the virtual keyboard tray icon along with some other tray icons.
+  StatusAreaWidget* status = StatusAreaWidgetTestHelper::GetStatusAreaWidget();
+  status->virtual_keyboard_tray_for_testing()->SetVisible(true);
+  status->ime_menu_tray()->SetVisible(true);
+
+  keyboard_controller()->ShowKeyboard(false /* locked */);
+  keyboard_controller()->NotifyKeyboardWindowLoaded();
+  ASSERT_TRUE(keyboard::WaitUntilShown());
+
+  // The keyboard should hide when clicked.
+  ui::test::EventGenerator* generator = GetEventGenerator();
+  generator->set_current_location(status->virtual_keyboard_tray_for_testing()
+                                      ->GetBoundsInScreen()
+                                      .CenterPoint());
+  generator->ClickLeftButton();
+  ASSERT_TRUE(keyboard::WaitUntilHidden());
+}
+
+// See https://crbug.com/897672.
+TEST_F(StatusAreaWidgetVirtualKeyboardTest,
+       TappingVirtualKeyboardTrayHidesShownKeyboard) {
+  // Set up the virtual keyboard tray icon along with some other tray icons.
+  StatusAreaWidget* status = StatusAreaWidgetTestHelper::GetStatusAreaWidget();
+  status->virtual_keyboard_tray_for_testing()->SetVisible(true);
+  status->ime_menu_tray()->SetVisible(true);
+
+  keyboard_controller()->ShowKeyboard(false /* locked */);
+  keyboard_controller()->NotifyKeyboardWindowLoaded();
+  ASSERT_TRUE(keyboard::WaitUntilShown());
+
+  // The keyboard should hide when tapped.
+  ui::test::EventGenerator* generator = GetEventGenerator();
+  generator->GestureTapAt(status->virtual_keyboard_tray_for_testing()
+                              ->GetBoundsInScreen()
+                              .CenterPoint());
+  ASSERT_TRUE(keyboard::WaitUntilHidden());
+}
+
 TEST_F(StatusAreaWidgetVirtualKeyboardTest, ClickingHidesVirtualKeyboard) {
   keyboard_controller()->ShowKeyboard(false /* locked */);
   keyboard_controller()->NotifyKeyboardWindowLoaded();
diff --git a/ash/system/unified/unified_system_tray.cc b/ash/system/unified/unified_system_tray.cc
index 3e36ca09..818a780 100644
--- a/ash/system/unified/unified_system_tray.cc
+++ b/ash/system/unified/unified_system_tray.cc
@@ -27,6 +27,8 @@
 #include "ash/system/unified/unified_slider_bubble_controller.h"
 #include "ash/system/unified/unified_system_tray_bubble.h"
 #include "ash/system/unified/unified_system_tray_model.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "chromeos/network/network_handler.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/display/display.h"
@@ -65,6 +67,9 @@
   DISALLOW_COPY_AND_ASSIGN(UiDelegate);
 };
 
+const base::TimeDelta UnifiedSystemTray::kNotificationCountUpdateDelay =
+    base::TimeDelta::FromMilliseconds(100);
+
 UnifiedSystemTray::UiDelegate::UiDelegate(UnifiedSystemTray* owner)
     : owner_(owner) {
   ui_controller_ = std::make_unique<MessageCenterUiController>(this);
@@ -327,6 +332,16 @@
 }
 
 void UnifiedSystemTray::UpdateNotificationInternal() {
+  // Limit update frequency in order to avoid flashing when 2 updates are
+  // incoming in a very short period of time. It happens when ARC++ apps
+  // creating bundled notifications.
+  if (!timer_.IsRunning()) {
+    timer_.Start(FROM_HERE, kNotificationCountUpdateDelay, this,
+                 &UnifiedSystemTray::UpdateNotificationAfterDelay);
+  }
+}
+
+void UnifiedSystemTray::UpdateNotificationAfterDelay() {
   notification_counter_item_->Update();
   quiet_mode_view_->Update();
 }
diff --git a/ash/system/unified/unified_system_tray.h b/ash/system/unified/unified_system_tray.h
index c1a4e0f..0747124 100644
--- a/ash/system/unified/unified_system_tray.h
+++ b/ash/system/unified/unified_system_tray.h
@@ -9,6 +9,8 @@
 
 #include "ash/ash_export.h"
 #include "ash/system/tray/tray_background_view.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
 
 namespace ash {
 
@@ -97,6 +99,8 @@
   UnifiedSystemTrayModel* model() { return model_.get(); }
 
  private:
+  const static base::TimeDelta kNotificationCountUpdateDelay;
+
   friend class UnifiedSystemTrayTest;
   friend class UnifiedSystemTrayTestApi;
 
@@ -110,6 +114,7 @@
   void ShowBubbleInternal(bool show_by_click);
   void HideBubbleInternal();
   void UpdateNotificationInternal();
+  void UpdateNotificationAfterDelay();
 
   const std::unique_ptr<UiDelegate> ui_delegate_;
 
@@ -130,6 +135,7 @@
   tray::TimeTrayItemView* const time_view_;
 
   ui::Layer* ink_drop_layer_ = nullptr;
+  base::OneShotTimer timer_;
 
   DISALLOW_COPY_AND_ASSIGN(UnifiedSystemTray);
 };
diff --git a/ash/system/virtual_keyboard/virtual_keyboard_tray.cc b/ash/system/virtual_keyboard/virtual_keyboard_tray.cc
index 23b0ab0..999e779 100644
--- a/ash/system/virtual_keyboard/virtual_keyboard_tray.cc
+++ b/ash/system/virtual_keyboard/virtual_keyboard_tray.cc
@@ -67,18 +67,26 @@
       LoginMetricsRecorder::TrayClickTarget::kVirtualKeyboardTray);
 
   auto* keyboard_controller = keyboard::KeyboardController::Get();
+
   // Keyboard may not always be enabled. https://crbug.com/749989
-  if (keyboard_controller->IsEnabled()) {
-    keyboard_controller->ShowKeyboardInDisplay(
-        display::Screen::GetScreen()->GetDisplayNearestWindow(
-            shelf_->GetWindow()));
-  }
+  if (!keyboard_controller->IsEnabled())
+    return true;
+
   // Normally, active status is set when virtual keyboard is shown/hidden,
   // however, showing virtual keyboard happens asynchronously and, especially
   // the first time, takes some time. We need to set active status here to
   // prevent bad things happening if user clicked the button before keyboard is
   // shown.
-  SetIsActive(true);
+  if (is_active()) {
+    keyboard_controller->HideKeyboardByUser();
+    SetIsActive(false);
+  } else {
+    keyboard_controller->ShowKeyboardInDisplay(
+        display::Screen::GetScreen()->GetDisplayNearestWindow(
+            shelf_->GetWindow()));
+    SetIsActive(true);
+  }
+
   return true;
 }
 
diff --git a/ash/system/virtual_keyboard/virtual_keyboard_tray_unittest.cc b/ash/system/virtual_keyboard/virtual_keyboard_tray_unittest.cc
new file mode 100644
index 0000000..27854b6
--- /dev/null
+++ b/ash/system/virtual_keyboard/virtual_keyboard_tray_unittest.cc
@@ -0,0 +1,59 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/virtual_keyboard/virtual_keyboard_tray.h"
+
+#include "ash/system/status_area_widget.h"
+#include "ash/system/status_area_widget_test_helper.h"
+#include "ash/test/ash_test_base.h"
+#include "base/command_line.h"
+#include "ui/keyboard/keyboard_controller.h"
+#include "ui/keyboard/keyboard_switches.h"
+#include "ui/keyboard/keyboard_util.h"
+#include "ui/keyboard/test/keyboard_test_util.h"
+
+namespace ash {
+
+class VirtualKeyboardTrayTest : public AshTestBase {
+ protected:
+  void SetUp() override {
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        keyboard::switches::kEnableVirtualKeyboard);
+    AshTestBase::SetUp();
+    ASSERT_TRUE(keyboard::IsKeyboardEnabled());
+
+    // These tests only apply to the floating virtual keyboard, as it is the
+    // only case where both the virtual keyboard and the shelf are visible.
+    keyboard_controller()->LoadKeyboardWindowInBackground();
+    keyboard_controller()->NotifyKeyboardWindowLoaded();
+    keyboard_controller()->SetContainerType(keyboard::ContainerType::FLOATING,
+                                            base::nullopt, base::DoNothing());
+  }
+
+  keyboard::KeyboardController* keyboard_controller() {
+    return keyboard::KeyboardController::Get();
+  }
+};
+
+// Tests that the tray action toggles the virtual keyboard.
+TEST_F(VirtualKeyboardTrayTest, PerformActionTogglesVirtualKeyboard) {
+  StatusAreaWidget* status = StatusAreaWidgetTestHelper::GetStatusAreaWidget();
+  VirtualKeyboardTray* tray = status->virtual_keyboard_tray_for_testing();
+  tray->SetVisible(true);
+  ASSERT_TRUE(tray->visible());
+
+  // First tap should show the virtual keyboard.
+  tray->PerformAction(ui::GestureEvent(
+      0, 0, 0, base::TimeTicks(), ui::GestureEventDetails(ui::ET_GESTURE_TAP)));
+  EXPECT_TRUE(tray->is_active());
+  ASSERT_TRUE(keyboard::WaitUntilShown());
+
+  // Second tap should hide the virtual keyboard.
+  tray->PerformAction(ui::GestureEvent(
+      0, 0, 0, base::TimeTicks(), ui::GestureEventDetails(ui::ET_GESTURE_TAP)));
+  EXPECT_FALSE(tray->is_active());
+  ASSERT_TRUE(keyboard::WaitUntilHidden());
+}
+
+}  // namespace ash
diff --git a/ash/wm/overview/cleanup_animation_observer.h b/ash/wm/overview/cleanup_animation_observer.h
index 06e5757e..80def4c 100644
--- a/ash/wm/overview/cleanup_animation_observer.h
+++ b/ash/wm/overview/cleanup_animation_observer.h
@@ -37,6 +37,8 @@
   void Shutdown() override;
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(WindowSelectorTest, OverviewExitAnimationObserver);
+
   std::unique_ptr<views::Widget> widget_;
   WindowSelectorDelegate* owner_;
 
diff --git a/ash/wm/overview/cleanup_animation_observer_unittest.cc b/ash/wm/overview/cleanup_animation_observer_unittest.cc
index a0f84de4..b04a8b8 100644
--- a/ash/wm/overview/cleanup_animation_observer_unittest.cc
+++ b/ash/wm/overview/cleanup_animation_observer_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/overview/window_selector_delegate.h"
+#include "base/containers/unique_ptr_adapters.h"
 #include "ui/aura/window.h"
 #include "ui/compositor/layer_animation_observer.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
@@ -41,22 +42,14 @@
 
   void RemoveAndDestroyAnimationObserver(
       DelayedAnimationObserver* animation_observer) override {
-    class IsEqual {
-     public:
-      explicit IsEqual(DelayedAnimationObserver* animation_observer)
-          : animation_observer_(animation_observer) {}
-      bool operator()(const std::unique_ptr<DelayedAnimationObserver>& other) {
-        return (other.get() == animation_observer_);
-      }
-
-     private:
-      const DelayedAnimationObserver* animation_observer_;
-    };
-    observers_.erase(std::remove_if(observers_.begin(), observers_.end(),
-                                    IsEqual(animation_observer)),
-                     observers_.end());
+    base::EraseIf(observers_, base::MatchesUniquePtr(animation_observer));
   }
 
+  void AddStartAnimationObserver(
+      std::unique_ptr<DelayedAnimationObserver> animation_observer) override {}
+  void RemoveAndDestroyStartAnimationObserver(
+      DelayedAnimationObserver* animation_observer) override {}
+
  private:
   std::vector<std::unique_ptr<DelayedAnimationObserver>> observers_;
 
@@ -78,7 +71,7 @@
   // cause a window to be closed via
   // views::Widget::GetWidgetForNativeView(window)->Close().
   std::unique_ptr<views::Widget> CreateWindowWidget(const gfx::Rect& bounds) {
-    std::unique_ptr<views::Widget> widget(new views::Widget);
+    auto widget = std::make_unique<views::Widget>();
     views::Widget::InitParams params;
     params.bounds = bounds;
     params.type = views::Widget::InitParams::TYPE_WINDOW;
@@ -110,10 +103,8 @@
 // Tests that basic create-destroy sequence does not crash.
 TEST_F(CleanupAnimationObserverTest, CreateDestroy) {
   TestWindowSelectorDelegate delegate;
-  std::unique_ptr<views::Widget> widget(
-      CreateWindowWidget(gfx::Rect(0, 0, 40, 40)));
-  std::unique_ptr<CleanupAnimationObserver> observer(
-      new CleanupAnimationObserver(std::move(widget)));
+  std::unique_ptr<views::Widget> widget = CreateWindowWidget(gfx::Rect(40, 40));
+  auto observer = std::make_unique<CleanupAnimationObserver>(std::move(widget));
   delegate.AddDelayedAnimationObserver(std::move(observer));
 }
 
@@ -122,8 +113,7 @@
 // owns the observer does not crash.
 TEST_F(CleanupAnimationObserverTest, CreateAnimateComplete) {
   TestWindowSelectorDelegate delegate;
-  std::unique_ptr<views::Widget> widget(
-      CreateWindowWidget(gfx::Rect(0, 0, 40, 40)));
+  std::unique_ptr<views::Widget> widget = CreateWindowWidget(gfx::Rect(40, 40));
   aura::Window* widget_window = widget->GetNativeWindow();
   {
     ui::ScopedLayerAnimationSettings animation_settings(
@@ -133,8 +123,8 @@
     animation_settings.SetPreemptionStrategy(
         ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
 
-    std::unique_ptr<CleanupAnimationObserver> observer(
-        new CleanupAnimationObserver(std::move(widget)));
+    auto observer =
+        std::make_unique<CleanupAnimationObserver>(std::move(widget));
     animation_settings.AddObserver(observer.get());
     delegate.AddDelayedAnimationObserver(std::move(observer));
 
@@ -153,8 +143,7 @@
 // instance in destructor, this test would have crashed.
 TEST_F(CleanupAnimationObserverTest, CreateAnimateShutdown) {
   TestWindowSelectorDelegate delegate;
-  std::unique_ptr<views::Widget> widget(
-      CreateWindowWidget(gfx::Rect(0, 0, 40, 40)));
+  std::unique_ptr<views::Widget> widget = CreateWindowWidget(gfx::Rect(40, 40));
   aura::Window* widget_window = widget->GetNativeWindow();
   {
     // Normal animations for tests have ZERO_DURATION, make sure we are actually
@@ -168,8 +157,8 @@
     animation_settings.SetPreemptionStrategy(
         ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
 
-    std::unique_ptr<CleanupAnimationObserver> observer(
-        new CleanupAnimationObserver(std::move(widget)));
+    auto observer =
+        std::make_unique<CleanupAnimationObserver>(std::move(widget));
     animation_settings.AddObserver(observer.get());
     delegate.AddDelayedAnimationObserver(std::move(observer));
 
diff --git a/ash/wm/overview/scoped_transform_overview_window.cc b/ash/wm/overview/scoped_transform_overview_window.cc
index d2449837..2ce0906 100644
--- a/ash/wm/overview/scoped_transform_overview_window.cc
+++ b/ash/wm/overview/scoped_transform_overview_window.cc
@@ -13,8 +13,10 @@
 #include "ash/wm/overview/drop_target_view.h"
 #include "ash/wm/overview/overview_utils.h"
 #include "ash/wm/overview/scoped_overview_animation_settings.h"
+#include "ash/wm/overview/start_animation_observer.h"
 #include "ash/wm/overview/window_grid.h"
 #include "ash/wm/overview/window_selector.h"
+#include "ash/wm/overview/window_selector_controller.h"
 #include "ash/wm/overview/window_selector_item.h"
 #include "ash/wm/window_mirror_view.h"
 #include "ash/wm/window_state.h"
@@ -269,6 +271,16 @@
     auto settings = std::make_unique<ScopedOverviewAnimationSettings>(
         animation_type, window);
     settings->DeferPaint();
+
+    // Create a start animation observer if this is an enter overview layout
+    // animation.
+    if (animation_type == OVERVIEW_ANIMATION_LAY_OUT_SELECTOR_ITEMS_ON_ENTER) {
+      auto start_observer = std::make_unique<StartAnimationObserver>();
+      settings->AddObserver(start_observer.get());
+      Shell::Get()->window_selector_controller()->AddStartAnimationObserver(
+          std::move(start_observer));
+    }
+
     animation_settings->push_back(std::move(settings));
   }
 
diff --git a/ash/wm/overview/start_animation_observer.cc b/ash/wm/overview/start_animation_observer.cc
new file mode 100644
index 0000000..640a80c5
--- /dev/null
+++ b/ash/wm/overview/start_animation_observer.cc
@@ -0,0 +1,27 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/wm/overview/start_animation_observer.h"
+
+namespace ash {
+
+StartAnimationObserver::StartAnimationObserver() = default;
+
+StartAnimationObserver::~StartAnimationObserver() = default;
+
+void StartAnimationObserver::OnImplicitAnimationsCompleted() {
+  if (owner_)
+    owner_->RemoveAndDestroyStartAnimationObserver(this);
+}
+
+void StartAnimationObserver::SetOwner(WindowSelectorDelegate* owner) {
+  DCHECK(!owner_);
+  owner_ = owner;
+}
+
+void StartAnimationObserver::Shutdown() {
+  owner_ = nullptr;
+}
+
+}  // namespace ash
\ No newline at end of file
diff --git a/ash/wm/overview/start_animation_observer.h b/ash/wm/overview/start_animation_observer.h
new file mode 100644
index 0000000..7780e96
--- /dev/null
+++ b/ash/wm/overview/start_animation_observer.h
@@ -0,0 +1,38 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_WM_OVERVIEW_START_ANIMATION_OBSERVER_H_
+#define ASH_WM_OVERVIEW_START_ANIMATION_OBSERVER_H_
+
+#include "ash/ash_export.h"
+#include "ash/wm/overview/window_selector_delegate.h"
+#include "base/macros.h"
+#include "ui/compositor/layer_animation_observer.h"
+
+namespace ash {
+
+// An observer which watches a overview start animation and signals its owner
+// when the animation it is watching finishes.
+class ASH_EXPORT StartAnimationObserver : public ui::ImplicitAnimationObserver,
+                                          public DelayedAnimationObserver {
+ public:
+  StartAnimationObserver();
+  ~StartAnimationObserver() override;
+
+  // ui::ImplicitAnimationObserver:
+  void OnImplicitAnimationsCompleted() override;
+
+  // DelayedAnimationObserver:
+  void SetOwner(WindowSelectorDelegate* owner) override;
+  void Shutdown() override;
+
+ private:
+  WindowSelectorDelegate* owner_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(StartAnimationObserver);
+};
+
+}  // namespace ash
+
+#endif  // ASH_WM_OVERVIEW_START_ANIMATION_OBSERVER_H_
diff --git a/ash/wm/overview/start_animation_observer_unittest.cc b/ash/wm/overview/start_animation_observer_unittest.cc
new file mode 100644
index 0000000..d6ede9c
--- /dev/null
+++ b/ash/wm/overview/start_animation_observer_unittest.cc
@@ -0,0 +1,79 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/wm/overview/start_animation_observer.h"
+
+#include <vector>
+
+#include "ash/test/ash_test_base.h"
+#include "ash/wm/overview/window_selector_delegate.h"
+#include "base/containers/unique_ptr_adapters.h"
+#include "ui/aura/window.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/gfx/transform.h"
+
+namespace ash {
+
+namespace {
+
+class TestWindowSelectorDelegate : public WindowSelectorDelegate {
+ public:
+  TestWindowSelectorDelegate() = default;
+
+  ~TestWindowSelectorDelegate() override = default;
+
+  // WindowSelectorDelegate:
+  void OnSelectionEnded() override {}
+  void AddDelayedAnimationObserver(
+      std::unique_ptr<DelayedAnimationObserver> animation_observer) override {}
+  void RemoveAndDestroyAnimationObserver(
+      DelayedAnimationObserver* animation_observer) override {}
+  void AddStartAnimationObserver(
+      std::unique_ptr<DelayedAnimationObserver> animation_observer) override {
+    animation_observer->SetOwner(this);
+    observers_.push_back(std::move(animation_observer));
+  }
+  void RemoveAndDestroyStartAnimationObserver(
+      DelayedAnimationObserver* animation_observer) override {
+    base::EraseIf(observers_, base::MatchesUniquePtr(animation_observer));
+  }
+
+  size_t NumObservers() const { return observers_.size(); }
+
+ private:
+  std::vector<std::unique_ptr<DelayedAnimationObserver>> observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestWindowSelectorDelegate);
+};
+
+}  // namespace
+
+using StartAnimationObserverTest = AshTestBase;
+
+// Tests that adding a StartAnimationObserver works as intended.
+TEST_F(StartAnimationObserverTest, Basic) {
+  TestWindowSelectorDelegate delegate;
+  std::unique_ptr<aura::Window> window = CreateTestWindow();
+
+  {
+    ui::ScopedLayerAnimationSettings animation_settings(
+        window->layer()->GetAnimator());
+    animation_settings.SetTransitionDuration(
+        base::TimeDelta::FromMilliseconds(1000));
+    animation_settings.SetPreemptionStrategy(
+        ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+
+    auto observer = std::make_unique<StartAnimationObserver>();
+    animation_settings.AddObserver(observer.get());
+    delegate.AddStartAnimationObserver(std::move(observer));
+    window->SetTransform(gfx::Transform(1.f, 0.f, 0.f, 1.f, 100.f, 0.f));
+    EXPECT_EQ(1u, delegate.NumObservers());
+  }
+
+  // Tests that when done animating, the observer count is zero.
+  window->layer()->GetAnimator()->StopAnimating();
+  EXPECT_EQ(0u, delegate.NumObservers());
+}
+
+}  // namespace ash
\ No newline at end of file
diff --git a/ash/wm/overview/window_selector_controller.cc b/ash/wm/overview/window_selector_controller.cc
index c96ec8a..0e81821 100644
--- a/ash/wm/overview/window_selector_controller.cc
+++ b/ash/wm/overview/window_selector_controller.cc
@@ -325,6 +325,10 @@
     // Clear any animations that may be running from last overview end.
     for (const auto& animation : delayed_animations_)
       animation->Shutdown();
+    if (!delayed_animations_.empty()) {
+      Shell::Get()->NotifyOverviewModeStartingAnimationComplete(
+          /*canceled=*/true);
+    }
     delayed_animations_.clear();
 
     window_selector_ = std::make_unique<WindowSelector>(this);
@@ -475,6 +479,12 @@
 void WindowSelectorController::OnSelectionEnded() {
   if (is_shutting_down_)
     return;
+
+  if (!start_animations_.empty()) {
+    Shell::Get()->NotifyOverviewModeEndingAnimationComplete(
+        /*canceled=*/true);
+  }
+  start_animations_.clear();
   is_shutting_down_ = true;
   Shell::Get()->NotifyOverviewModeEnding();
   auto* window_selector = window_selector_.release();
@@ -508,7 +518,24 @@
   if (!window_selector_ && !previous_empty && delayed_animations_.empty()) {
     if (IsBlurAllowed())
       overview_blur_controller_->Unblur();
-    Shell::Get()->NotifyOverviewModeEndingAnimationComplete();
+    Shell::Get()->NotifyOverviewModeEndingAnimationComplete(/*canceled=*/false);
+  }
+}
+
+void WindowSelectorController::AddStartAnimationObserver(
+    std::unique_ptr<DelayedAnimationObserver> animation_observer) {
+  animation_observer->SetOwner(this);
+  start_animations_.push_back(std::move(animation_observer));
+}
+
+void WindowSelectorController::RemoveAndDestroyStartAnimationObserver(
+    DelayedAnimationObserver* animation_observer) {
+  const bool previous_empty = start_animations_.empty();
+  base::EraseIf(start_animations_, base::MatchesUniquePtr(animation_observer));
+
+  if (!previous_empty && start_animations_.empty()) {
+    Shell::Get()->NotifyOverviewModeStartingAnimationComplete(
+        /*canceled=*/false);
   }
 }
 
diff --git a/ash/wm/overview/window_selector_controller.h b/ash/wm/overview/window_selector_controller.h
index 7415f89..72491bd 100644
--- a/ash/wm/overview/window_selector_controller.h
+++ b/ash/wm/overview/window_selector_controller.h
@@ -22,6 +22,11 @@
 // allows selecting a window to activate it.
 class ASH_EXPORT WindowSelectorController : public WindowSelectorDelegate {
  public:
+  enum class AnimationCompleteReason {
+    kCompleted,
+    kCanceled,
+  };
+
   WindowSelectorController();
   ~WindowSelectorController() override;
 
@@ -72,6 +77,10 @@
       std::unique_ptr<DelayedAnimationObserver> animation) override;
   void RemoveAndDestroyAnimationObserver(
       DelayedAnimationObserver* animation) override;
+  void AddStartAnimationObserver(
+      std::unique_ptr<DelayedAnimationObserver> animation_observer) override;
+  void RemoveAndDestroyStartAnimationObserver(
+      DelayedAnimationObserver* animation_observer) override;
 
   WindowSelector* window_selector() { return window_selector_.get(); }
 
@@ -80,6 +89,7 @@
   friend class WindowSelectorTest;
   FRIEND_TEST_ALL_PREFIXES(TabletModeControllerTest,
                            DisplayDisconnectionDuringOverview);
+  FRIEND_TEST_ALL_PREFIXES(WindowSelectorTest, OverviewExitAnimationObserver);
 
   // There is no need to blur or unblur the wallpaper for tests.
   static void SetDoNotChangeWallpaperBlurForTests();
@@ -92,6 +102,10 @@
   // those animations are in progress, the animations are shut down and the
   // widgets destroyed.
   std::vector<std::unique_ptr<DelayedAnimationObserver>> delayed_animations_;
+  // Collection of DelayedAnimationObserver objects. When this becomes empty,
+  // notify shell that the starting animations have been completed.
+  std::vector<std::unique_ptr<DelayedAnimationObserver>> start_animations_;
+
   std::unique_ptr<WindowSelector> window_selector_;
   base::Time last_selection_time_;
 
diff --git a/ash/wm/overview/window_selector_delegate.h b/ash/wm/overview/window_selector_delegate.h
index 021cede..8534cab9 100644
--- a/ash/wm/overview/window_selector_delegate.h
+++ b/ash/wm/overview/window_selector_delegate.h
@@ -5,6 +5,8 @@
 #ifndef ASH_WM_OVERVIEW_WINDOW_SELECTOR_DELEGATE_H_
 #define ASH_WM_OVERVIEW_WINDOW_SELECTOR_DELEGATE_H_
 
+#include <memory>
+
 #include "ash/ash_export.h"
 #include "base/compiler_specific.h"
 
@@ -37,14 +39,24 @@
       std::unique_ptr<DelayedAnimationObserver> animation_observer) = 0;
 
   // Finds and erases |animation_observer| from the list deleting the widget
-  // owned by the |animation_observer|.
-  // This method should be called when a scheduled animation completes.
-  // If the animation completion callback is a result of a window getting
-  // destroyed then the DelayedAnimationObserver::Shutdown() should be called
-  // first before destroying the window.
+  // owned by the |animation_observer|. This method should be called when a
+  // scheduled animation completes. If the animation completion callback is a
+  // result of a window getting destroyed then the
+  // DelayedAnimationObserver::Shutdown() should be called first before
+  // destroying the window.
   virtual void RemoveAndDestroyAnimationObserver(
       DelayedAnimationObserver* animation_observer) = 0;
 
+  // Passes ownership of |animation_observer| to |this| delegate.
+  virtual void AddStartAnimationObserver(
+      std::unique_ptr<DelayedAnimationObserver> animation_observer) = 0;
+
+  // Finds and erases |animation_observer| from the list which tracks the start
+  // animations. This method should be called when a scheduled start overview
+  // animation completes.
+  virtual void RemoveAndDestroyStartAnimationObserver(
+      DelayedAnimationObserver* animation_observer) = 0;
+
  protected:
   virtual ~WindowSelectorDelegate() {}
 };
diff --git a/ash/wm/overview/window_selector_item.cc b/ash/wm/overview/window_selector_item.cc
index d81cbe5..f2cc56e 100644
--- a/ash/wm/overview/window_selector_item.cc
+++ b/ash/wm/overview/window_selector_item.cc
@@ -18,6 +18,7 @@
 #include "ash/wm/overview/rounded_rect_view.h"
 #include "ash/wm/overview/scoped_overview_animation_settings.h"
 #include "ash/wm/overview/scoped_transform_overview_window.h"
+#include "ash/wm/overview/start_animation_observer.h"
 #include "ash/wm/overview/window_grid.h"
 #include "ash/wm/overview/window_selector_controller.h"
 #include "ash/wm/splitview/split_view_constants.h"
@@ -96,6 +97,11 @@
 // title.
 constexpr gfx::Size kIconSize{24, 24};
 
+// The amount we need to offset the close button so that the icon, which is
+// smaller than the actual button is lined up with the right side of the window
+// preview.
+constexpr int kCloseButtonOffsetDp = 8;
+
 constexpr int kCloseButtonInkDropInsetDp = 2;
 
 // Close button color.
@@ -325,7 +331,7 @@
       header_view_->AddChildView(image_view_);
     header_view_->AddChildView(title_label_);
     AddChildWithLayer(header_view_, close_button_);
-    AddChildWithLayer(this, header_view_);
+    AddChildWithLayer(listener_button_, header_view_);
     layout->SetFlexForView(title_label_, 1);
   }
 
@@ -417,11 +423,11 @@
       cannot_snap_container_->SetBoundsRect(cannot_snap_bounds);
     }
 
-    // Position the header at the top. The left should be indented to match
-    // the transformed window, but not the right because the close button hit
-    // radius should extend past the transformed window's rightmost bounds.
+    // Position the header at the top. The right side of the header should be
+    // positioned so that the rightmost of the close icon matches the right side
+    // of the window preview.
     gfx::Rect header_bounds = GetLocalBounds();
-    header_bounds.Inset(kWindowSelectorMargin, kWindowSelectorMargin, 0, 0);
+    header_bounds.Inset(0, 0, kCloseButtonOffsetDp, 0);
     header_bounds.set_height(visible_height);
     header_view_->SetBoundsRect(header_bounds);
   }
@@ -1174,6 +1180,16 @@
       mode == HeaderFadeInMode::kFirstUpdate ? OVERVIEW_ANIMATION_NONE
                                              : animation_type,
       widget_window);
+
+  // Create a start animation observer if this is an enter overview layout
+  // animation.
+  if (animation_type == OVERVIEW_ANIMATION_LAY_OUT_SELECTOR_ITEMS_ON_ENTER) {
+    auto start_observer = std::make_unique<StartAnimationObserver>();
+    animation_settings.AddObserver(start_observer.get());
+    Shell::Get()->window_selector_controller()->AddStartAnimationObserver(
+        std::move(start_observer));
+  }
+
   // |widget_window| covers both the transformed window and the header
   // as well as the gap between the windows to prevent events from reaching
   // the window including its sizing borders.
diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc
index c61b554..98bb2de 100644
--- a/ash/wm/overview/window_selector_unittest.cc
+++ b/ash/wm/overview/window_selector_unittest.cc
@@ -28,6 +28,7 @@
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/overview/cleanup_animation_observer.h"
 #include "ash/wm/overview/overview_utils.h"
 #include "ash/wm/overview/overview_window_drag_controller.h"
 #include "ash/wm/overview/window_grid.h"
@@ -3108,19 +3109,25 @@
 // Test class that allows us to check what whether the last overview enter or
 // exit was using a slide animation. This is needed because the cached slide
 // animation variable may be reset or the WindowSelector object may not be
-// available after a toggle has completed.
-class TestOverviewAnimationTypeObserver : public ShellObserver {
+// available after a toggle has completed. Also stores whether the animation
+// complete observers fired because an animation completed or was canceled.
+class TestOverviewObserver : public ShellObserver {
  public:
-  TestOverviewAnimationTypeObserver() { Shell::Get()->AddShellObserver(this); }
-  ~TestOverviewAnimationTypeObserver() override {
-    Shell::Get()->RemoveShellObserver(this);
-  }
+  TestOverviewObserver() { Shell::Get()->AddShellObserver(this); }
+  ~TestOverviewObserver() override { Shell::Get()->RemoveShellObserver(this); }
 
   // ShellObserver:
   void OnOverviewModeStarting() override { UpdateLastAnimationWasSlide(); }
   void OnOverviewModeEnding() override { UpdateLastAnimationWasSlide(); }
+  void OnOverviewModeStartingAnimationComplete(bool canceled) override {
+    animation_canceled_ = canceled;
+  }
+  void OnOverviewModeEndingAnimationComplete(bool canceled) override {
+    animation_canceled_ = canceled;
+  }
 
   bool last_animation_was_slide() const { return last_animation_was_slide_; }
+  bool animation_canceled() const { return animation_canceled_; }
 
  private:
   void UpdateLastAnimationWasSlide() {
@@ -3133,18 +3140,19 @@
   }
 
   bool last_animation_was_slide_ = false;
+  bool animation_canceled_ = false;
 
-  DISALLOW_COPY_AND_ASSIGN(TestOverviewAnimationTypeObserver);
+  DISALLOW_COPY_AND_ASSIGN(TestOverviewObserver);
 };
 
 }  // namespace
 
 // Tests the slide animation for overview is never used in clamshell.
 TEST_F(WindowSelectorTest, OverviewEnterExitAnimation) {
-  TestOverviewAnimationTypeObserver observer;
+  TestOverviewObserver observer;
 
   const gfx::Rect bounds(200, 200);
-  std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
+  std::unique_ptr<aura::Window> window(CreateWindow(bounds));
 
   ToggleOverview();
   EXPECT_FALSE(observer.last_animation_was_slide());
@@ -3154,7 +3162,7 @@
 
   // Even with all window minimized, there should not be a slide animation.
   ASSERT_FALSE(IsSelecting());
-  wm::GetWindowState(window1.get())->Minimize();
+  wm::GetWindowState(window.get())->Minimize();
   ToggleOverview();
   EXPECT_FALSE(observer.last_animation_was_slide());
 }
@@ -3163,7 +3171,7 @@
 // are minimized, and that if overview is exited from the home launcher all
 // windows are minimized.
 TEST_F(WindowSelectorTest, OverviewEnterExitAnimationTablet) {
-  TestOverviewAnimationTypeObserver observer;
+  TestOverviewObserver observer;
 
   // Ensure calls to EnableTabletModeWindowManager complete.
   base::RunLoop().RunUntilIdle();
@@ -3171,7 +3179,7 @@
   base::RunLoop().RunUntilIdle();
 
   const gfx::Rect bounds(200, 200);
-  std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
+  std::unique_ptr<aura::Window> window(CreateWindow(bounds));
 
   ToggleOverview();
   EXPECT_FALSE(observer.last_animation_was_slide());
@@ -3181,13 +3189,77 @@
   ToggleOverview(WindowSelector::EnterExitOverviewType::kWindowsMinimized);
   EXPECT_TRUE(observer.last_animation_was_slide());
   ASSERT_FALSE(IsSelecting());
-  EXPECT_TRUE(wm::GetWindowState(window1.get())->IsMinimized());
+  EXPECT_TRUE(wm::GetWindowState(window.get())->IsMinimized());
 
   // All windows are minimized, so we should use the slide animation.
   ToggleOverview();
   EXPECT_TRUE(observer.last_animation_was_slide());
 }
 
+// Tests that the overview enter animation observer works as expected.
+TEST_F(WindowSelectorTest, OverviewEnterAnimationObserver) {
+  TestOverviewObserver observer;
+
+  ui::ScopedAnimationDurationScaleMode animation_scale_mode(
+      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
+
+  const gfx::Rect bounds(200, 200);
+  std::unique_ptr<aura::Window> window(CreateWindow(bounds));
+
+  // Test that if the animations are allowed to run out on enter the observer
+  // will be notified of complete animations.
+  ToggleOverview();
+  WindowSelectorItem* item = GetWindowItemForWindow(0, window.get());
+  window->layer()->GetAnimator()->StopAnimating();
+  item_widget(item)->GetNativeWindow()->layer()->GetAnimator()->StopAnimating();
+  EXPECT_FALSE(observer.animation_canceled());
+
+  ToggleOverview();
+
+  // Test that if the animations are canceled after entering by exiting overview
+  // right away, the observer will be notified of incomplete animations.
+  ToggleOverview();
+  ToggleOverview();
+  EXPECT_TRUE(observer.animation_canceled());
+}
+
+// Tests that the overview exit animation observer works as expected.
+TEST_F(WindowSelectorTest, OverviewExitAnimationObserver) {
+  TestOverviewObserver observer;
+
+  ui::ScopedAnimationDurationScaleMode animation_scale_mode(
+      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
+
+  const gfx::Rect bounds(200, 200);
+  std::unique_ptr<aura::Window> window(CreateWindow(bounds));
+
+  ToggleOverview();
+
+  // Test that if the animations are allowed to run out on exit the observer
+  // will be notified of complete animations.
+  ToggleOverview();
+  window->layer()->GetAnimator()->StopAnimating();
+  std::vector<std::unique_ptr<DelayedAnimationObserver>>& delayed_animations =
+      window_selector_controller()->delayed_animations_;
+  // On animation complete |delayed_animations| will erase its own members, so
+  // use a while loop to avoid indexing errors.
+  ASSERT_FALSE(delayed_animations.empty());
+  while (!delayed_animations.empty()) {
+    views::Widget* item_widget =
+        static_cast<CleanupAnimationObserver*>(delayed_animations.back().get())
+            ->widget_.get();
+    item_widget->GetNativeWindow()->layer()->GetAnimator()->StopAnimating();
+  }
+  EXPECT_FALSE(observer.animation_canceled());
+
+  // Test that if the animations are canceled after exiting by reentering
+  // overview right away, the observer will be notified of incomplete
+  // animations.
+  ToggleOverview();
+  ToggleOverview();
+  EXPECT_TRUE(observer.animation_canceled());
+}
+
 // Tests that overview mode is entered with kWindowDragged mode when an app is
 // dragged from the top of the screen.
 TEST_F(WindowSelectorTest, DraggingFromTopAnimation) {
diff --git a/ash/wm/pip/pip_window_resizer_unittest.cc b/ash/wm/pip/pip_window_resizer_unittest.cc
index e098e2f..f9015041 100644
--- a/ash/wm/pip/pip_window_resizer_unittest.cc
+++ b/ash/wm/pip/pip_window_resizer_unittest.cc
@@ -127,7 +127,7 @@
   ASSERT_TRUE(resizer.get());
 
   resizer->Drag(CalculateDragPoint(*resizer, 0, 10), 0);
-  EXPECT_EQ("200,210 100x100", test_state()->last_bounds().ToString());
+  EXPECT_EQ(gfx::Rect(200, 210, 100, 100), test_state()->last_bounds());
 }
 
 TEST_F(PipWindowResizerTest, PipWindowCanResize) {
@@ -136,7 +136,7 @@
   ASSERT_TRUE(resizer.get());
 
   resizer->Drag(CalculateDragPoint(*resizer, 0, 10), 0);
-  EXPECT_EQ("200,200 100x110", test_state()->last_bounds().ToString());
+  EXPECT_EQ(gfx::Rect(200, 200, 100, 110), test_state()->last_bounds());
 }
 
 TEST_F(PipWindowResizerTest, PipWindowDragIsRestrictedToWorkArea) {
@@ -146,19 +146,19 @@
 
   // Drag to the right.
   resizer->Drag(CalculateDragPoint(*resizer, 800, 0), 0);
-  EXPECT_EQ("292,200 100x100", test_state()->last_bounds().ToString());
+  EXPECT_EQ(gfx::Rect(292, 200, 100, 100), test_state()->last_bounds());
 
   // Drag down.
   resizer->Drag(CalculateDragPoint(*resizer, 0, 800), 0);
-  EXPECT_EQ("200,292 100x100", test_state()->last_bounds().ToString());
+  EXPECT_EQ(gfx::Rect(200, 292, 100, 100), test_state()->last_bounds());
 
   // Drag to the left.
   resizer->Drag(CalculateDragPoint(*resizer, -800, 0), 0);
-  EXPECT_EQ("8,200 100x100", test_state()->last_bounds().ToString());
+  EXPECT_EQ(gfx::Rect(8, 200, 100, 100), test_state()->last_bounds());
 
   // Drag up.
   resizer->Drag(CalculateDragPoint(*resizer, 0, -800), 0);
-  EXPECT_EQ("200,8 100x100", test_state()->last_bounds().ToString());
+  EXPECT_EQ(gfx::Rect(200, 8, 100, 100), test_state()->last_bounds());
 }
 
 TEST_F(PipWindowResizerTest, PipWindowCanBeDraggedInTabletMode) {
@@ -169,7 +169,7 @@
   ASSERT_TRUE(resizer.get());
 
   resizer->Drag(CalculateDragPoint(*resizer, 0, 10), 0);
-  EXPECT_EQ("200,210 100x100", test_state()->last_bounds().ToString());
+  EXPECT_EQ(gfx::Rect(200, 210, 100, 100), test_state()->last_bounds());
 }
 
 TEST_F(PipWindowResizerTest, PipWindowCanBeResizedInTabletMode) {
@@ -180,7 +180,7 @@
   ASSERT_TRUE(resizer.get());
 
   resizer->Drag(CalculateDragPoint(*resizer, 0, 10), 0);
-  EXPECT_EQ("200,200 100x110", test_state()->last_bounds().ToString());
+  EXPECT_EQ(gfx::Rect(200, 200, 100, 110), test_state()->last_bounds());
 }
 
 }  // namespace wm
diff --git a/ash/ws/window_service_delegate_impl_unittest.cc b/ash/ws/window_service_delegate_impl_unittest.cc
index 8de2501..e6a17b6 100644
--- a/ash/ws/window_service_delegate_impl_unittest.cc
+++ b/ash/ws/window_service_delegate_impl_unittest.cc
@@ -115,6 +115,46 @@
   EXPECT_EQ(gfx::Point(105, 106), top_level_->bounds().origin());
 }
 
+TEST_F(WindowServiceDelegateImplTest, RunWindowMoveWithMultipleDisplays) {
+  UpdateDisplay("500x500,500x500");
+  GetWindowTreeTestHelper()->window_tree()->PerformWindowMove(
+      21, GetTopLevelWindowId(), ws::mojom::MoveLoopSource::MOUSE,
+      top_level_->GetBoundsInScreen().origin());
+  GetEventGenerator()->MoveMouseTo(gfx::Point(501, 1));
+  GetWindowTreeClientChanges()->clear();
+  GetEventGenerator()->ReleaseLeftButton();
+
+  EXPECT_EQ(Shell::GetRootWindowForDisplayId(GetSecondaryDisplay().id()),
+            top_level_->GetRootWindow());
+  EXPECT_TRUE(
+      ContainsChange(*GetWindowTreeClientChanges(),
+                     "DisplayChanged window_id=0,1 display_id=2200000001"));
+  EXPECT_TRUE(ContainsChange(
+      *GetWindowTreeClientChanges(),
+      std::string("BoundsChanged window=0,1 old_bounds=500,0 104x100 "
+                  "new_bounds=500,0 104x100 local_surface_id=*")));
+}
+
+TEST_F(WindowServiceDelegateImplTest, SetWindowBoundsToDifferentDisplay) {
+  UpdateDisplay("500x500,500x500");
+  EXPECT_EQ(gfx::Point(100, 100), top_level_->GetBoundsInScreen().origin());
+
+  GetWindowTreeClientChanges()->clear();
+  GetWindowTreeTestHelper()->window_tree()->SetWindowBounds(
+      21, GetTopLevelWindowId(), gfx::Rect(600, 100, 100, 100),
+      base::Optional<viz::LocalSurfaceId>());
+  EXPECT_EQ(gfx::Point(600, 100), top_level_->GetBoundsInScreen().origin());
+  EXPECT_EQ(Shell::GetRootWindowForDisplayId(GetSecondaryDisplay().id()),
+            top_level_->GetRootWindow());
+  EXPECT_TRUE(
+      ContainsChange(*GetWindowTreeClientChanges(),
+                     "DisplayChanged window_id=0,1 display_id=2200000001"));
+  EXPECT_TRUE(ContainsChange(
+      *GetWindowTreeClientChanges(),
+      std::string("BoundsChanged window=0,1 old_bounds=100,100 100x100 "
+                  "new_bounds=600,100 104x100 local_surface_id=*")));
+}
+
 TEST_F(WindowServiceDelegateImplTest, DeleteWindowWithInProgressRunLoop) {
   GetWindowTreeTestHelper()->window_tree()->PerformWindowMove(
       29, GetTopLevelWindowId(), ws::mojom::MoveLoopSource::MOUSE,
diff --git a/base/BUILD.gn b/base/BUILD.gn
index a536aecb..634b4c7 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -731,6 +731,7 @@
     "system/system_monitor.h",
     "task/cancelable_task_tracker.cc",
     "task/cancelable_task_tracker.h",
+    "task/common/intrusive_heap.h",
     "task/lazy_task_runner.cc",
     "task/lazy_task_runner.h",
     "task/post_task.cc",
@@ -740,7 +741,6 @@
     "task/sequence_manager/associated_thread_id.h",
     "task/sequence_manager/enqueue_order.cc",
     "task/sequence_manager/enqueue_order.h",
-    "task/sequence_manager/intrusive_heap.h",
     "task/sequence_manager/lazily_deallocated_deque.h",
     "task/sequence_manager/lazy_now.cc",
     "task/sequence_manager/lazy_now.h",
@@ -779,6 +779,7 @@
     "task/single_thread_task_runner_thread_mode.h",
     "task/task_executor.cc",
     "task/task_executor.h",
+    "task/task_observer.h",
     "task/task_scheduler/can_schedule_sequence_observer.h",
     "task/task_scheduler/delayed_task_manager.cc",
     "task/task_scheduler/delayed_task_manager.h",
@@ -949,8 +950,6 @@
     "trace_event/trace_config_category_filter.h",
     "trace_event/trace_event.h",
     "trace_event/trace_event_android.cc",
-    "trace_event/trace_event_argument.cc",
-    "trace_event/trace_event_argument.h",
     "trace_event/trace_event_etw_export_win.cc",
     "trace_event/trace_event_etw_export_win.h",
     "trace_event/trace_event_filter.cc",
@@ -964,6 +963,8 @@
     "trace_event/trace_log.cc",
     "trace_event/trace_log.h",
     "trace_event/trace_log_constants.cc",
+    "trace_event/traced_value.cc",
+    "trace_event/traced_value.h",
     "trace_event/tracing_agent.cc",
     "trace_event/tracing_agent.h",
     "tuple.h",
@@ -1400,8 +1401,6 @@
       "files/file_posix.cc",
       "files/file_util_posix.cc",
       "files/memory_mapped_file_posix.cc",
-      "fuchsia/async_dispatcher.cc",
-      "fuchsia/async_dispatcher.h",
       "fuchsia/component_context.cc",
       "fuchsia/component_context.h",
       "fuchsia/default_job.cc",
@@ -1474,6 +1473,7 @@
 
     deps += [
       "//third_party/fuchsia-sdk/sdk:async_default",
+      "//third_party/fuchsia-sdk/sdk:async_loop_cpp",
       "//third_party/fuchsia-sdk/sdk:fidl",
       "//third_party/fuchsia-sdk/sdk:svc",
     ]
@@ -2436,10 +2436,10 @@
     "sys_info_unittest.cc",
     "system/system_monitor_unittest.cc",
     "task/cancelable_task_tracker_unittest.cc",
+    "task/common/intrusive_heap_unittest.cc",
     "task/lazy_task_runner_unittest.cc",
     "task/post_task_unittest.cc",
     "task/scoped_set_task_priority_for_current_thread_unittest.cc",
-    "task/sequence_manager/intrusive_heap_unittest.cc",
     "task/sequence_manager/lazily_deallocated_deque_unittest.cc",
     "task/sequence_manager/sequence_manager_impl_unittest.cc",
     "task/sequence_manager/task_queue_selector_unittest.cc",
@@ -2519,11 +2519,11 @@
     "trace_event/process_memory_dump_unittest.cc",
     "trace_event/trace_category_unittest.cc",
     "trace_event/trace_config_unittest.cc",
-    "trace_event/trace_event_argument_unittest.cc",
     "trace_event/trace_event_filter_test_utils.cc",
     "trace_event/trace_event_filter_test_utils.h",
     "trace_event/trace_event_system_stats_monitor_unittest.cc",
     "trace_event/trace_event_unittest.cc",
+    "trace_event/traced_value_unittest.cc",
     "tuple_unittest.cc",
     "unguessable_token_unittest.cc",
     "value_iterators_unittest.cc",
@@ -2717,7 +2717,6 @@
     sources += [
       "files/dir_reader_posix_unittest.cc",
       "files/file_descriptor_watcher_posix_unittest.cc",
-      "fuchsia/async_dispatcher_unittest.cc",
       "fuchsia/filtered_service_directory_unittest.cc",
       "fuchsia/service_directory_test_base.cc",
       "fuchsia/service_directory_test_base.h",
diff --git a/base/fuchsia/async_dispatcher.cc b/base/fuchsia/async_dispatcher.cc
deleted file mode 100644
index ac01930e..0000000
--- a/base/fuchsia/async_dispatcher.cc
+++ /dev/null
@@ -1,411 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/fuchsia/async_dispatcher.h"
-
-#include <lib/async/default.h>
-#include <lib/async/task.h>
-#include <lib/async/wait.h>
-#include <lib/zx/handle.h>
-#include <lib/zx/time.h>
-#include <zircon/syscalls.h>
-
-#include "base/fuchsia/fuchsia_logging.h"
-
-namespace base {
-
-namespace {
-
-template <typename T>
-uintptr_t key_from_ptr(T* ptr) {
-  return reinterpret_cast<uintptr_t>(ptr);
-};
-
-}  // namespace
-
-class AsyncDispatcher::ExceptionState : public LinkNode<ExceptionState> {
- public:
-  explicit ExceptionState(AsyncDispatcher* async_dispatcher) {
-    async_dispatcher->exception_list_.Append(this);
-  }
-  ~ExceptionState() { RemoveFromList(); }
-
-  async_exception_t* exception() {
-    // ExceptionState objects are allocated in-place in the |state| field of an
-    // enclosing async_exceptionwait_t, so async_exception_t address can be
-    // calculated by subtracting state offset in async_exception_t from |this|.
-    static_assert(std::is_standard_layout<async_exception_t>(),
-                  "async_wait_t is expected to have standard layout.");
-    return reinterpret_cast<async_exception_t*>(
-        reinterpret_cast<uint8_t*>(this) - offsetof(async_exception_t, state));
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ExceptionState);
-};
-
-class AsyncDispatcher::WaitState : public LinkNode<WaitState> {
- public:
-  explicit WaitState(AsyncDispatcher* async_dispatcher) {
-    async_dispatcher->wait_list_.Append(this);
-  }
-  ~WaitState() { RemoveFromList(); }
-
-  async_wait_t* wait() {
-    // WaitState objects are allocated in-place in the |state| field of an
-    // enclosing async_wait_t, so async_wait_t address can be calculated by
-    // subtracting state offset in async_wait_t from |this|.
-    static_assert(std::is_standard_layout<async_wait_t>(),
-                  "async_wait_t is expected to have standard layout.");
-    return reinterpret_cast<async_wait_t*>(reinterpret_cast<uint8_t*>(this) -
-                                           offsetof(async_wait_t, state));
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(WaitState);
-};
-
-class AsyncDispatcher::TaskState : public LinkNode<TaskState> {
- public:
-  explicit TaskState(LinkNode<TaskState>* previous_task) {
-    InsertAfter(previous_task);
-  }
-  ~TaskState() { RemoveFromList(); }
-
-  async_task_t* task() {
-    // TaskState objects are allocated in-place in the |state| field of an
-    // enclosing async_task_t, so async_task_t address can be calculated by
-    // subtracting state offset in async_task_t from |this|.
-    static_assert(std::is_standard_layout<async_task_t>(),
-                  "async_task_t is expected to have standard layout.");
-    return reinterpret_cast<async_task_t*>(reinterpret_cast<uint8_t*>(this) -
-                                           offsetof(async_task_t, state));
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TaskState);
-};
-
-AsyncDispatcher::AsyncDispatcher() : ops_storage_({}) {
-  zx_status_t status = zx::port::create(0u, &port_);
-  ZX_DCHECK(status == ZX_OK, status);
-
-  status = zx::timer::create(0u, ZX_CLOCK_MONOTONIC, &timer_);
-  ZX_DCHECK(status == ZX_OK, status);
-  status = timer_.wait_async(port_, key_from_ptr(&timer_), ZX_TIMER_SIGNALED,
-                             ZX_WAIT_ASYNC_REPEATING);
-  ZX_DCHECK(status == ZX_OK, status);
-
-  status = zx::event::create(0, &stop_event_);
-  ZX_DCHECK(status == ZX_OK, status);
-  status = stop_event_.wait_async(port_, key_from_ptr(&stop_event_),
-                                  ZX_EVENT_SIGNALED, ZX_WAIT_ASYNC_REPEATING);
-  ZX_DCHECK(status == ZX_OK, status);
-
-  ops_storage_.version = ASYNC_OPS_V2;
-  ops_storage_.v1.now = NowOp;
-  ops_storage_.v1.begin_wait = BeginWaitOp;
-  ops_storage_.v1.cancel_wait = CancelWaitOp;
-  ops_storage_.v1.post_task = PostTaskOp;
-  ops_storage_.v1.cancel_task = CancelTaskOp;
-  ops_storage_.v1.queue_packet = QueuePacketOp;
-  ops_storage_.v1.set_guest_bell_trap = SetGuestBellTrapOp;
-  ops_storage_.v2.bind_exception_port = BindExceptionPortOp;
-  ops_storage_.v2.unbind_exception_port = UnbindExceptionPortOp;
-  ops = &ops_storage_;
-
-  DCHECK(!async_get_default_dispatcher());
-  async_set_default_dispatcher(this);
-}
-
-AsyncDispatcher::~AsyncDispatcher() {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK_EQ(async_get_default_dispatcher(), this);
-
-  // Some waits and tasks may be canceled while the dispatcher is being
-  // destroyed, so pop-from-head until none remain.
-
-  while (!exception_list_.empty()) {
-    ExceptionState* state = exception_list_.head()->value();
-    async_exception_t* exception = state->exception();
-    state->~ExceptionState();
-    exception->handler(this, exception, ZX_ERR_CANCELED, nullptr);
-  }
-
-  while (!wait_list_.empty()) {
-    WaitState* state = wait_list_.head()->value();
-    async_wait_t* wait = state->wait();
-    state->~WaitState();
-    wait->handler(this, wait, ZX_ERR_CANCELED, nullptr);
-  }
-
-  while (!task_list_.empty()) {
-    TaskState* state = task_list_.head()->value();
-    async_task_t* task = state->task();
-    state->~TaskState();
-    task->handler(this, task, ZX_ERR_CANCELED);
-  }
-
-  async_set_default_dispatcher(nullptr);
-}
-
-zx_status_t AsyncDispatcher::DispatchOrWaitUntil(zx_time_t deadline) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  zx_port_packet_t packet = {};
-  zx_status_t status = port_.wait(zx::time(deadline), &packet);
-  if (status != ZX_OK)
-    return status;
-
-  if (ZX_PKT_IS_SIGNAL_ONE(packet.type) || ZX_PKT_IS_SIGNAL_REP(packet.type)) {
-    if (packet.key == key_from_ptr(&timer_)) {
-      // |timer_| has expired.
-      DCHECK(packet.signal.observed & ZX_TIMER_SIGNALED);
-      DispatchTasks();
-      return ZX_OK;
-    } else if (packet.key == key_from_ptr(&stop_event_)) {
-      // Stop() was called.
-      DCHECK(packet.signal.observed & ZX_EVENT_SIGNALED);
-      status = zx_object_signal(stop_event_.get(), ZX_EVENT_SIGNALED, 0);
-      ZX_DCHECK(status == ZX_OK, status);
-      return ZX_ERR_CANCELED;
-    } else {
-      DCHECK_EQ(packet.type, ZX_PKT_TYPE_SIGNAL_ONE);
-      auto* wait = reinterpret_cast<async_wait_t*>(packet.key);
-
-      // Clean the state before invoking the handler: it may destroy |*wait|.
-      auto* state = reinterpret_cast<WaitState*>(&wait->state);
-      state->~WaitState();
-
-      wait->handler(this, wait, packet.status, &packet.signal);
-
-      return ZX_OK;
-    }
-  } else if (ZX_PKT_IS_EXCEPTION(packet.type)) {
-    auto* exception = reinterpret_cast<async_exception_t*>(packet.key);
-
-    // |exception| may have been deleted by the time |handler| returns.
-    exception->handler(this, exception, packet.status, &packet);
-
-    return ZX_OK;
-  }
-
-  NOTREACHED();
-  return ZX_ERR_INTERNAL;
-}
-
-void AsyncDispatcher::Stop() {
-  // Can be called on any thread.
-  zx_status_t status =
-      zx_object_signal(stop_event_.get(), 0, ZX_EVENT_SIGNALED);
-  ZX_DCHECK(status == ZX_OK, status);
-}
-
-zx_time_t AsyncDispatcher::NowOp(async_dispatcher_t* async) {
-  DCHECK(async);
-  return zx_clock_get(ZX_CLOCK_MONOTONIC);
-}
-
-zx_status_t AsyncDispatcher::BeginWaitOp(async_dispatcher_t* async,
-                                         async_wait_t* wait) {
-  return static_cast<AsyncDispatcher*>(async)->BeginWait(wait);
-}
-
-zx_status_t AsyncDispatcher::CancelWaitOp(async_dispatcher_t* async,
-                                          async_wait_t* wait) {
-  return static_cast<AsyncDispatcher*>(async)->CancelWait(wait);
-}
-
-zx_status_t AsyncDispatcher::PostTaskOp(async_dispatcher_t* async,
-                                        async_task_t* task) {
-  return static_cast<AsyncDispatcher*>(async)->PostTask(task);
-}
-
-zx_status_t AsyncDispatcher::CancelTaskOp(async_dispatcher_t* async,
-                                          async_task_t* task) {
-  return static_cast<AsyncDispatcher*>(async)->CancelTask(task);
-}
-
-zx_status_t AsyncDispatcher::QueuePacketOp(async_dispatcher_t* async,
-                                           async_receiver_t* receiver,
-                                           const zx_packet_user_t* data) {
-  return ZX_ERR_NOT_SUPPORTED;
-}
-
-zx_status_t AsyncDispatcher::SetGuestBellTrapOp(async_dispatcher_t* async,
-                                                async_guest_bell_trap_t* trap,
-                                                zx_handle_t guest,
-                                                zx_vaddr_t addr,
-                                                size_t length) {
-  return ZX_ERR_NOT_SUPPORTED;
-}
-
-zx_status_t AsyncDispatcher::BindExceptionPortOp(async_dispatcher_t* async,
-                                                 async_exception_t* exception) {
-  return static_cast<AsyncDispatcher*>(async)->BindExceptionPort(exception);
-}
-
-zx_status_t AsyncDispatcher::UnbindExceptionPortOp(
-    async_dispatcher_t* async,
-    async_exception_t* exception) {
-  return static_cast<AsyncDispatcher*>(async)->UnbindExceptionPort(exception);
-}
-
-zx_status_t AsyncDispatcher::BeginWait(async_wait_t* wait) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  static_assert(sizeof(AsyncDispatcher::WaitState) <= sizeof(async_state_t),
-                "WaitState is too big");
-  WaitState* state = new (&wait->state) WaitState(this);
-  zx_status_t status =
-      zx::unowned_handle(wait->object)
-          ->wait_async(port_, reinterpret_cast<uintptr_t>(wait), wait->trigger,
-                       ZX_WAIT_ASYNC_ONCE);
-
-  if (status != ZX_OK)
-    state->~WaitState();
-
-  return status;
-}
-
-zx_status_t AsyncDispatcher::CancelWait(async_wait_t* wait) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  if (!wait->state.reserved[0])
-    return ZX_ERR_NOT_FOUND;
-
-  zx_status_t status = port_.cancel(*zx::unowned_handle(wait->object),
-                                    reinterpret_cast<uintptr_t>(wait));
-  if (status == ZX_OK) {
-    auto* state = reinterpret_cast<WaitState*>(&(wait->state));
-    state->~WaitState();
-  }
-
-  return status;
-}
-
-zx_status_t AsyncDispatcher::PostTask(async_task_t* task) {
-  // Can be called on any thread.
-  AutoLock lock(lock_);
-
-  // Find correct position for the new task in |task_list_| to keep the list
-  // sorted by deadline. This implementation has O(N) complexity, but it's
-  // acceptable - async task are not expected to be used frequently.
-  // TODO(sergeyu): Consider using a more efficient data structure if tasks
-  // performance becomes important.
-  LinkNode<TaskState>* node;
-  for (node = task_list_.head(); node != task_list_.end();
-       node = node->previous()) {
-    if (task->deadline >= node->value()->task()->deadline)
-      break;
-  }
-
-  static_assert(sizeof(AsyncDispatcher::TaskState) <= sizeof(async_state_t),
-                "TaskState is too big");
-
-  // Will insert new task after |node|.
-  new (&task->state) TaskState(node);
-
-  if (reinterpret_cast<TaskState*>(&task->state) == task_list_.head()) {
-    // Task inserted at head. Earliest deadline changed.
-    RestartTimerLocked();
-  }
-
-  return ZX_OK;
-}
-
-zx_status_t AsyncDispatcher::CancelTask(async_task_t* task) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  AutoLock lock(lock_);
-
-  if (!task->state.reserved[0])
-    return ZX_ERR_NOT_FOUND;
-
-  auto* state = reinterpret_cast<TaskState*>(&task->state);
-  state->~TaskState();
-
-  return ZX_OK;
-}
-
-zx_status_t AsyncDispatcher::BindExceptionPort(async_exception_t* exception) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  static_assert(
-      sizeof(AsyncDispatcher::ExceptionState) <= sizeof(async_state_t),
-      "ExceptionState is too big");
-  ExceptionState* state = new (&exception->state) ExceptionState(this);
-
-  zx_status_t status = zx_task_bind_exception_port(
-      exception->task, port_.get(), reinterpret_cast<uintptr_t>(exception),
-      exception->options);
-  if (status != ZX_OK)
-    state->~ExceptionState();
-
-  return status;
-}
-
-zx_status_t AsyncDispatcher::UnbindExceptionPort(async_exception_t* exception) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  if (!exception->state.reserved[0])
-    return ZX_ERR_NOT_FOUND;
-
-  zx_status_t status = zx_task_bind_exception_port(
-      exception->task, ZX_HANDLE_INVALID,
-      reinterpret_cast<uintptr_t>(exception), exception->options);
-  if (status == ZX_OK) {
-    auto* state = reinterpret_cast<ExceptionState*>(&exception->state);
-    state->~ExceptionState();
-  }
-
-  return status;
-}
-
-void AsyncDispatcher::DispatchTasks() {
-  // Snapshot now value to set implicit bound for the tasks that will run before
-  // DispatchTasks() returns. This also helps to avoid calling zx_clock_get()
-  // more than necessary.
-  zx_time_t now = zx_clock_get(ZX_CLOCK_MONOTONIC);
-
-  while (true) {
-    async_task_t* task;
-    {
-      AutoLock lock(lock_);
-      if (task_list_.empty())
-        break;
-
-      TaskState* task_state = task_list_.head()->value();
-      task = task_state->task();
-
-      if (task->deadline > now) {
-        RestartTimerLocked();
-        break;
-      }
-
-      task_state->~TaskState();
-
-      // ~TaskState() is expected to reset the state to 0. The destructor
-      // removes the task from the |task_list_| and LinkNode::RemoveFromList()
-      // sets both its fields to nullptr, which is equivalent to resetting the
-      // state to 0.
-      DCHECK_EQ(task->state.reserved[0], 0u);
-    }
-
-    // The handler is responsible for freeing the |task| or it may reuse it.
-    task->handler(this, task, ZX_OK);
-  }
-}
-
-void AsyncDispatcher::RestartTimerLocked() {
-  lock_.AssertAcquired();
-
-  if (task_list_.empty())
-    return;
-  zx_time_t deadline = task_list_.head()->value()->task()->deadline;
-  zx_status_t status = timer_.set(zx::time(deadline), zx::duration());
-  ZX_DCHECK(status == ZX_OK, status);
-}
-
-}  // namespace base
diff --git a/base/fuchsia/async_dispatcher.h b/base/fuchsia/async_dispatcher.h
deleted file mode 100644
index 9f1d610..0000000
--- a/base/fuchsia/async_dispatcher.h
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_FUCHSIA_ASYNC_DISPATCHER_H_
-#define BASE_FUCHSIA_ASYNC_DISPATCHER_H_
-
-#include <lib/async/dispatcher.h>
-#include <lib/async/exception.h>
-#include <lib/zx/event.h>
-#include <lib/zx/port.h>
-#include <lib/zx/timer.h>
-
-#include "base/containers/linked_list.h"
-#include "base/macros.h"
-#include "base/synchronization/lock.h"
-#include "base/threading/thread_checker.h"
-
-namespace base {
-
-// Implementation of dispatcher for Fuchsia's async library. It's necessary to
-// run Fuchsia's library on chromium threads.
-class BASE_EXPORT AsyncDispatcher : public async_dispatcher_t {
- public:
-  AsyncDispatcher();
-  ~AsyncDispatcher();
-
-  // Returns after running one or more tasks or waits until |deadline|.
-  // Returns |ZX_OK| if some tasks were executed, |ZX_ERR_TIMED_OUT| - the
-  // deadline expired, |ZX_ERR_CANCELED| - Stop() was called.
-  zx_status_t DispatchOrWaitUntil(zx_time_t deadline);
-
-  // If Run() is being executed then it will return as soon as possible (e.g.
-  // finishing running the current task), otherwise the following Run() call
-  // will quit immediately instead of waiting until deadline expires.
-  void Stop();
-
- private:
-  class ExceptionState;
-  class WaitState;
-  class TaskState;
-
-  // ASYNC_OPS_V1 operations.
-  static zx_time_t NowOp(async_dispatcher_t* async);
-  static zx_status_t BeginWaitOp(async_dispatcher_t* async, async_wait_t* wait);
-  static zx_status_t CancelWaitOp(async_dispatcher_t* async,
-                                  async_wait_t* wait);
-  static zx_status_t PostTaskOp(async_dispatcher_t* async, async_task_t* task);
-  static zx_status_t CancelTaskOp(async_dispatcher_t* async,
-                                  async_task_t* task);
-  static zx_status_t QueuePacketOp(async_dispatcher_t* async,
-                                   async_receiver_t* receiver,
-                                   const zx_packet_user_t* data);
-  static zx_status_t SetGuestBellTrapOp(async_dispatcher_t* async,
-                                        async_guest_bell_trap_t* trap,
-                                        zx_handle_t guest,
-                                        zx_vaddr_t addr,
-                                        size_t length);
-
-  // ASYNC_OPS_V2 operations.
-  static zx_status_t BindExceptionPortOp(async_dispatcher_t* dispatcher,
-                                         async_exception_t* exception);
-  static zx_status_t UnbindExceptionPortOp(async_dispatcher_t* dispatcher,
-                                           async_exception_t* exception);
-
-  // async_ops_t implementation. Called by corresponding *Op() methods above.
-  zx_status_t BeginWait(async_wait_t* wait);
-  zx_status_t CancelWait(async_wait_t* wait);
-  zx_status_t PostTask(async_task_t* task);
-  zx_status_t CancelTask(async_task_t* task);
-  zx_status_t BindExceptionPort(async_exception_t* exception);
-  zx_status_t UnbindExceptionPort(async_exception_t* exception);
-
-  // Runs tasks in |task_list_| that have deadline in the past.
-  void DispatchTasks();
-
-  // Must be called while |lock_| is held.
-  void RestartTimerLocked();
-
-  THREAD_CHECKER(thread_checker_);
-
-  zx::port port_;
-  zx::timer timer_;
-  zx::event stop_event_;
-
-  LinkedList<WaitState> wait_list_;
-  LinkedList<ExceptionState> exception_list_;
-
-  async_ops_t ops_storage_;
-
-  // |lock_| must be held when accessing |task_list_|.
-  base::Lock lock_;
-
-  LinkedList<TaskState> task_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(AsyncDispatcher);
-};
-
-}  // namespace base
-
-#endif  // BASE_FUCHSIA_ASYNC_DISPATCHER_H_
diff --git a/base/fuchsia/async_dispatcher_unittest.cc b/base/fuchsia/async_dispatcher_unittest.cc
deleted file mode 100644
index 1b368a7..0000000
--- a/base/fuchsia/async_dispatcher_unittest.cc
+++ /dev/null
@@ -1,306 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/fuchsia/async_dispatcher.h"
-
-#include <lib/async/default.h>
-#include <lib/async/task.h>
-#include <lib/async/wait.h>
-#include <lib/zx/job.h>
-#include <lib/zx/socket.h>
-
-#include "base/callback.h"
-#include "base/process/launch.h"
-#include "base/test/multiprocess_test.h"
-#include "base/test/test_timeouts.h"
-#include "base/time/time.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/multiprocess_func_list.h"
-
-namespace base {
-
-namespace {
-
-struct TestTask : public async_task_t {
-  explicit TestTask() {
-    state = ASYNC_STATE_INIT;
-    handler = &TaskProc;
-    deadline = 0;
-  }
-
-  static void TaskProc(async_dispatcher_t* async,
-                       async_task_t* task,
-                       zx_status_t status);
-
-  int num_calls = 0;
-  int repeats = 1;
-  OnceClosure on_call;
-  zx_status_t last_status = ZX_OK;
-};
-
-// static
-void TestTask::TaskProc(async_dispatcher_t* async,
-                        async_task_t* task,
-                        zx_status_t status) {
-  EXPECT_EQ(async, async_get_default_dispatcher());
-  EXPECT_TRUE(status == ZX_OK || status == ZX_ERR_CANCELED)
-      << "status: " << status;
-
-  auto* test_task = static_cast<TestTask*>(task);
-  test_task->num_calls++;
-  test_task->last_status = status;
-
-  if (test_task->on_call)
-    std::move(test_task->on_call).Run();
-
-  if (test_task->num_calls < test_task->repeats)
-    async_post_task(async, task);
-};
-
-struct TestWait : public async_wait_t {
-  TestWait(zx_handle_t handle,
-           zx_signals_t signals) {
-    state = ASYNC_STATE_INIT;
-    handler = &HandleProc;
-    object = handle;
-    trigger = signals;
-  }
-
-  static void HandleProc(async_dispatcher_t* async,
-                         async_wait_t* wait,
-                         zx_status_t status,
-                         const zx_packet_signal_t* signal);
-  int num_calls = 0;
-  OnceClosure on_call;
-  zx_status_t last_status = ZX_OK;
-};
-
-// static
-void TestWait::HandleProc(async_dispatcher_t* async,
-                          async_wait_t* wait,
-                          zx_status_t status,
-                          const zx_packet_signal_t* signal) {
-  EXPECT_EQ(async, async_get_default_dispatcher());
-  EXPECT_TRUE(status == ZX_OK || status == ZX_ERR_CANCELED)
-      << "status: " << status;
-
-  auto* test_wait = static_cast<TestWait*>(wait);
-
-  test_wait->num_calls++;
-  test_wait->last_status = status;
-
-  if (test_wait->on_call)
-    std::move(test_wait->on_call).Run();
-}
-
-struct TestException : public async_exception_t {
-  TestException(zx_handle_t handle) {
-    state = ASYNC_STATE_INIT;
-    handler = &HandleProc;
-    task = handle;
-    options = 0;
-  }
-
-  static void HandleProc(async_dispatcher_t* async,
-                         async_exception_t* wait,
-                         zx_status_t status,
-                         const zx_port_packet_t* packet);
-  int num_calls = 0;
-  OnceClosure on_call;
-  zx_status_t last_status = ZX_OK;
-};
-
-// static
-void TestException::HandleProc(async_dispatcher_t* async,
-                               async_exception_t* wait,
-                               zx_status_t status,
-                               const zx_port_packet_t* packet) {
-  EXPECT_EQ(async, async_get_default_dispatcher());
-
-  auto* test_wait = static_cast<TestException*>(wait);
-
-  test_wait->num_calls++;
-  test_wait->last_status = status;
-
-  if (test_wait->on_call)
-    std::move(test_wait->on_call).Run();
-}
-
-}  // namespace
-
-class AsyncDispatcherTest : public MultiProcessTest {
- public:
-  AsyncDispatcherTest() {
-    dispatcher_ = std::make_unique<AsyncDispatcher>();
-
-    async_ = async_get_default_dispatcher();
-    EXPECT_TRUE(async_);
-
-    EXPECT_EQ(zx::socket::create(ZX_SOCKET_DATAGRAM, &socket1_, &socket2_),
-              ZX_OK);
-  }
-
-  ~AsyncDispatcherTest() override = default;
-
-  void RunUntilIdle() {
-    while (true) {
-      zx_status_t status = dispatcher_->DispatchOrWaitUntil(0);
-      if (status != ZX_OK) {
-        EXPECT_EQ(status, ZX_ERR_TIMED_OUT);
-        break;
-      }
-    }
-  }
-
- protected:
-  std::unique_ptr<AsyncDispatcher> dispatcher_;
-
-  async_dispatcher_t* async_ = nullptr;
-
-  zx::socket socket1_;
-  zx::socket socket2_;
-};
-
-TEST_F(AsyncDispatcherTest, PostTask) {
-  TestTask task;
-  ASSERT_EQ(async_post_task(async_, &task), ZX_OK);
-  dispatcher_->DispatchOrWaitUntil(0);
-  EXPECT_EQ(task.num_calls, 1);
-  EXPECT_EQ(task.last_status, ZX_OK);
-}
-
-TEST_F(AsyncDispatcherTest, TaskRepeat) {
-  TestTask task;
-  task.repeats = 2;
-  ASSERT_EQ(async_post_task(async_, &task), ZX_OK);
-  RunUntilIdle();
-  EXPECT_EQ(task.num_calls, 2);
-  EXPECT_EQ(task.last_status, ZX_OK);
-}
-
-TEST_F(AsyncDispatcherTest, DelayedTask) {
-  TestTask task;
-  constexpr auto kDelay = TimeDelta::FromMilliseconds(5);
-  TimeTicks started = TimeTicks::Now();
-  task.deadline = zx_deadline_after(kDelay.InNanoseconds());
-  ASSERT_EQ(async_post_task(async_, &task), ZX_OK);
-  zx_status_t status = dispatcher_->DispatchOrWaitUntil(zx_deadline_after(
-      (kDelay + TestTimeouts::tiny_timeout()).InNanoseconds()));
-  EXPECT_EQ(status, ZX_OK);
-  EXPECT_GE(TimeTicks::Now() - started, kDelay);
-}
-
-TEST_F(AsyncDispatcherTest, CancelTask) {
-  TestTask task;
-  ASSERT_EQ(async_post_task(async_, &task), ZX_OK);
-  ASSERT_EQ(async_cancel_task(async_, &task), ZX_OK);
-  RunUntilIdle();
-  EXPECT_EQ(task.num_calls, 0);
-}
-
-TEST_F(AsyncDispatcherTest, TaskObserveShutdown) {
-  TestTask task;
-  ASSERT_EQ(async_post_task(async_, &task), ZX_OK);
-  dispatcher_.reset();
-
-  EXPECT_EQ(task.num_calls, 1);
-  EXPECT_EQ(task.last_status, ZX_ERR_CANCELED);
-}
-
-TEST_F(AsyncDispatcherTest, Wait) {
-  TestWait wait(socket1_.get(), ZX_SOCKET_READABLE);
-  EXPECT_EQ(async_begin_wait(async_, &wait), ZX_OK);
-
-  // Handler shouldn't be called because the event wasn't signaled.
-  RunUntilIdle();
-  EXPECT_EQ(wait.num_calls, 0);
-
-  char byte = 0;
-  EXPECT_EQ(socket2_.write(/*options=*/0, &byte, sizeof(byte),
-                           /*actual=*/nullptr),
-            ZX_OK);
-
-  zx_status_t status = dispatcher_->DispatchOrWaitUntil(
-      zx_deadline_after(TestTimeouts::tiny_timeout().InNanoseconds()));
-  EXPECT_EQ(status, ZX_OK);
-
-  EXPECT_EQ(wait.num_calls, 1);
-  EXPECT_EQ(wait.last_status, ZX_OK);
-}
-
-TEST_F(AsyncDispatcherTest, CancelWait) {
-  TestWait wait(socket1_.get(), ZX_SOCKET_READABLE);
-  EXPECT_EQ(async_begin_wait(async_, &wait), ZX_OK);
-
-  char byte = 0;
-  EXPECT_EQ(socket2_.write(/*options=*/0, &byte, sizeof(byte),
-                           /*actual=*/nullptr),
-            ZX_OK);
-
-  EXPECT_EQ(async_cancel_wait(async_, &wait), ZX_OK);
-
-  RunUntilIdle();
-  EXPECT_EQ(wait.num_calls, 0);
-}
-
-TEST_F(AsyncDispatcherTest, WaitShutdown) {
-  TestWait wait(socket1_.get(), ZX_SOCKET_READABLE);
-  EXPECT_EQ(async_begin_wait(async_, &wait), ZX_OK);
-  RunUntilIdle();
-  dispatcher_.reset();
-
-  EXPECT_EQ(wait.num_calls, 1);
-  EXPECT_EQ(wait.last_status, ZX_ERR_CANCELED);
-}
-
-// Sub-process which crashes itself, to generate an exception-port event.
-MULTIPROCESS_TEST_MAIN(AsyncDispatcherCrashingChild) {
-  IMMEDIATE_CRASH();
-  return 0;
-}
-
-TEST_F(AsyncDispatcherTest, BindExceptionPort) {
-  zx::job child_job;
-  ASSERT_EQ(zx::job::create(*zx::job::default_job(), 0, &child_job), ZX_OK);
-
-  // Bind |child_job|'s exception port to the dispatcher.
-  TestException exception(child_job.get());
-  EXPECT_EQ(async_bind_exception_port(async_, &exception), ZX_OK);
-
-  // Launch a child process in the job, that will immediately crash.
-  LaunchOptions options;
-  options.job_handle = child_job.get();
-  Process child =
-      SpawnChildWithOptions("AsyncDispatcherCrashingChild", options);
-  ASSERT_TRUE(child.IsValid());
-
-  // Wait for the exception event to be handled.
-  EXPECT_EQ(
-      dispatcher_->DispatchOrWaitUntil(
-          (TimeTicks::Now() + TestTimeouts::action_max_timeout()).ToZxTime()),
-      ZX_OK);
-  EXPECT_EQ(exception.num_calls, 1);
-  EXPECT_EQ(exception.last_status, ZX_OK);
-
-  EXPECT_EQ(async_unbind_exception_port(async_, &exception), ZX_OK);
-  ASSERT_EQ(child_job.kill(), ZX_OK);
-}
-
-TEST_F(AsyncDispatcherTest, CancelExceptionPort) {
-  zx::job child_job;
-  ASSERT_EQ(zx::job::create(*zx::job::default_job(), 0, &child_job), ZX_OK);
-
-  // Bind |child_job|'s exception port to the dispatcher.
-  TestException exception(child_job.get());
-  EXPECT_EQ(async_bind_exception_port(async_, &exception), ZX_OK);
-
-  // Tear-down the dispatcher, and verify that the |exception| is cancelled.
-  dispatcher_ = nullptr;
-  EXPECT_EQ(exception.num_calls, 1);
-  EXPECT_EQ(exception.last_status, ZX_ERR_CANCELED);
-
-  ASSERT_EQ(child_job.kill(), ZX_OK);
-}
-
-}  // namespace base
diff --git a/base/message_loop/message_loop.h b/base/message_loop/message_loop.h
index 4b0c2c52..f1461af 100644
--- a/base/message_loop/message_loop.h
+++ b/base/message_loop/message_loop.h
@@ -175,6 +175,9 @@
 
   // TODO(https://crbug.com/825327): Remove users of TaskObservers through
   // MessageLoop::current() and migrate the type back here.
+  //
+  // This alias is deprecated. Use base::TaskObserver instead.
+  // TODO(yutak): Replace all the use sites with base::TaskObserver.
   using TaskObserver = MessageLoopCurrent::TaskObserver;
 
   // These functions can only be called on the same thread that |this| is
diff --git a/base/message_loop/message_loop_current.h b/base/message_loop/message_loop_current.h
index 403d0dcc..3a61e3d 100644
--- a/base/message_loop/message_loop_current.h
+++ b/base/message_loop/message_loop_current.h
@@ -12,6 +12,7 @@
 #include "base/message_loop/message_pump_for_ui.h"
 #include "base/pending_task.h"
 #include "base/single_thread_task_runner.h"
+#include "base/task/task_observer.h"
 #include "build/build_config.h"
 
 namespace base {
@@ -98,21 +99,9 @@
   // instance should replace its TaskRunner.
   void SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner);
 
-  // A TaskObserver is an object that receives task notifications from the
-  // MessageLoop.
-  //
-  // NOTE: A TaskObserver implementation should be extremely fast!
-  class BASE_EXPORT TaskObserver {
-   public:
-    // This method is called before processing a task.
-    virtual void WillProcessTask(const PendingTask& pending_task) = 0;
-
-    // This method is called after processing a task.
-    virtual void DidProcessTask(const PendingTask& pending_task) = 0;
-
-   protected:
-    virtual ~TaskObserver() = default;
-  };
+  // This alias is deprecated. Use base::TaskObserver instead.
+  // TODO(yutak): Replace all the use sites with base::TaskObserver.
+  using TaskObserver = base::TaskObserver;
 
   // Forwards to MessageLoop::(Add|Remove)TaskObserver.
   // DEPRECATED(https://crbug.com/825327): only owners of the MessageLoop
diff --git a/base/message_loop/message_pump_fuchsia.cc b/base/message_loop/message_pump_fuchsia.cc
index 0b9cbf3..5413f61 100644
--- a/base/message_loop/message_pump_fuchsia.cc
+++ b/base/message_loop/message_pump_fuchsia.cc
@@ -4,8 +4,10 @@
 
 #include "base/message_loop/message_pump_fuchsia.h"
 
+#include <lib/async-loop/cpp/loop.h>
 #include <lib/fdio/io.h>
 #include <lib/fdio/unsafe.h>
+#include <lib/zx/time.h>
 #include <zircon/status.h>
 #include <zircon/syscalls.h>
 
@@ -28,7 +30,8 @@
   DCHECK(!handler);
   async_wait_t::handler = &HandleSignal;
 
-  zx_status_t status = async_begin_wait(&weak_pump_->async_dispatcher_, this);
+  zx_status_t status =
+      async_begin_wait(weak_pump_->async_loop_->dispatcher(), this);
   if (status != ZX_OK) {
     ZX_DLOG(ERROR, status) << "async_begin_wait() failed";
     async_wait_t::handler = nullptr;
@@ -60,7 +63,8 @@
 
   async_wait_t::handler = nullptr;
 
-  zx_status_t result = async_cancel_wait(&weak_pump_->async_dispatcher_, this);
+  zx_status_t result =
+      async_cancel_wait(weak_pump_->async_loop_->dispatcher(), this);
   ZX_DLOG_IF(ERROR, result != ZX_OK, result) << "async_cancel_wait failed";
   return result == ZX_OK;
 }
@@ -159,8 +163,9 @@
   return success;
 }
 
-MessagePumpFuchsia::MessagePumpFuchsia() : weak_factory_(this) {}
-
+MessagePumpFuchsia::MessagePumpFuchsia()
+    : async_loop_(new async::Loop(&kAsyncLoopConfigAttachToThread)),
+      weak_factory_(this) {}
 MessagePumpFuchsia::~MessagePumpFuchsia() = default;
 
 bool MessagePumpFuchsia::WatchFileDescriptor(int fd,
@@ -233,12 +238,15 @@
 }
 
 bool MessagePumpFuchsia::HandleEvents(zx_time_t deadline) {
-  zx_status_t status = async_dispatcher_.DispatchOrWaitUntil(deadline);
+  zx_status_t status = async_loop_->Run(zx::time(deadline), /*once=*/true);
   switch (status) {
     // Return true if some tasks or events were dispatched or if the dispatcher
     // was stopped by ScheduleWork().
     case ZX_OK:
+      return true;
+
     case ZX_ERR_CANCELED:
+      async_loop_->ResetQuit();
       return true;
 
     case ZX_ERR_TIMED_OUT:
@@ -279,6 +287,7 @@
     zx_time_t deadline = delayed_work_time_.is_null()
                              ? ZX_TIME_INFINITE
                              : delayed_work_time_.ToZxTime();
+
     HandleEvents(deadline);
   }
 }
@@ -288,9 +297,8 @@
 }
 
 void MessagePumpFuchsia::ScheduleWork() {
-  // Stop AsyncDispatcher to let MessagePumpFuchsia::Run() handle message loop
-  // tasks.
-  async_dispatcher_.Stop();
+  // Stop async_loop to let MessagePumpFuchsia::Run() handle message loop tasks.
+  async_loop_->Quit();
 }
 
 void MessagePumpFuchsia::ScheduleDelayedWork(
diff --git a/base/message_loop/message_pump_fuchsia.h b/base/message_loop/message_pump_fuchsia.h
index ebd56e5c..1c2381b 100644
--- a/base/message_loop/message_pump_fuchsia.h
+++ b/base/message_loop/message_pump_fuchsia.h
@@ -8,7 +8,6 @@
 #include <lib/async/wait.h>
 
 #include "base/base_export.h"
-#include "base/fuchsia/async_dispatcher.h"
 #include "base/location.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -17,6 +16,10 @@
 
 typedef struct fdio fdio_t;
 
+namespace async {
+class Loop;
+}  // namespace async
+
 namespace base {
 
 class BASE_EXPORT MessagePumpFuchsia : public MessagePump,
@@ -142,7 +145,7 @@
   // This flag is set to false when Run should return.
   bool keep_running_ = true;
 
-  AsyncDispatcher async_dispatcher_;
+  std::unique_ptr<async::Loop> async_loop_;
 
   // The time at which we should call DoDelayedWork.
   TimeTicks delayed_work_time_;
diff --git a/base/metrics/histogram_flattener.h b/base/metrics/histogram_flattener.h
index 6a5e3f4..ed81154 100644
--- a/base/metrics/histogram_flattener.h
+++ b/base/metrics/histogram_flattener.h
@@ -19,12 +19,13 @@
 // handles the logistics of gathering up available histograms for recording.
 class BASE_EXPORT HistogramFlattener {
  public:
+  virtual ~HistogramFlattener() = default;
+
   virtual void RecordDelta(const HistogramBase& histogram,
                            const HistogramSamples& snapshot) = 0;
 
  protected:
   HistogramFlattener() = default;
-  virtual ~HistogramFlattener() = default;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(HistogramFlattener);
diff --git a/base/task/sequence_manager/intrusive_heap.h b/base/task/common/intrusive_heap.h
similarity index 96%
rename from base/task/sequence_manager/intrusive_heap.h
rename to base/task/common/intrusive_heap.h
index c49215b..084afcaf 100644
--- a/base/task/sequence_manager/intrusive_heap.h
+++ b/base/task/common/intrusive_heap.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 BASE_TASK_SEQUENCE_MANAGER_INTRUSIVE_HEAP_H_
-#define BASE_TASK_SEQUENCE_MANAGER_INTRUSIVE_HEAP_H_
+#ifndef BASE_TASK_COMMON_INTRUSIVE_HEAP_H_
+#define BASE_TASK_COMMON_INTRUSIVE_HEAP_H_
 
 #include <algorithm>
 #include <vector>
@@ -11,7 +11,6 @@
 #include "base/logging.h"
 
 namespace base {
-namespace sequence_manager {
 namespace internal {
 
 template <typename T>
@@ -228,7 +227,6 @@
 };
 
 }  // namespace internal
-}  // namespace sequence_manager
 }  // namespace base
 
-#endif  // BASE_TASK_SEQUENCE_MANAGER_INTRUSIVE_HEAP_H_
+#endif  // BASE_TASK_COMMON_INTRUSIVE_HEAP_H_
diff --git a/base/task/sequence_manager/intrusive_heap_unittest.cc b/base/task/common/intrusive_heap_unittest.cc
similarity index 98%
rename from base/task/sequence_manager/intrusive_heap_unittest.cc
rename to base/task/common/intrusive_heap_unittest.cc
index 8ec5514..0d2add6 100644
--- a/base/task/sequence_manager/intrusive_heap_unittest.cc
+++ b/base/task/common/intrusive_heap_unittest.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/task/sequence_manager/intrusive_heap.h"
+#include "base/task/common/intrusive_heap.h"
+
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
-namespace sequence_manager {
 namespace internal {
 
 namespace {
@@ -386,5 +386,4 @@
 }
 
 }  // namespace internal
-}  // namespace sequence_manager
 }  // namespace base
diff --git a/base/task/sequence_manager/task_queue_impl.h b/base/task/sequence_manager/task_queue_impl.h
index 62db3e3..899b8fe9 100644
--- a/base/task/sequence_manager/task_queue_impl.h
+++ b/base/task/sequence_manager/task_queue_impl.h
@@ -16,15 +16,15 @@
 #include "base/memory/weak_ptr.h"
 #include "base/message_loop/message_loop.h"
 #include "base/pending_task.h"
+#include "base/task/common/intrusive_heap.h"
 #include "base/task/sequence_manager/associated_thread_id.h"
 #include "base/task/sequence_manager/enqueue_order.h"
-#include "base/task/sequence_manager/intrusive_heap.h"
 #include "base/task/sequence_manager/lazily_deallocated_deque.h"
 #include "base/task/sequence_manager/sequenced_task_source.h"
 #include "base/task/sequence_manager/task_queue.h"
 #include "base/threading/thread_checker.h"
 #include "base/trace_event/trace_event.h"
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 
 namespace base {
 namespace sequence_manager {
@@ -181,9 +181,11 @@
   // Must be called from the main thread.
   void WakeUpForDelayedWork(LazyNow* lazy_now);
 
-  HeapHandle heap_handle() const { return main_thread_only().heap_handle; }
+  base::internal::HeapHandle heap_handle() const {
+    return main_thread_only().heap_handle;
+  }
 
-  void set_heap_handle(HeapHandle heap_handle) {
+  void set_heap_handle(base::internal::HeapHandle heap_handle) {
     main_thread_only().heap_handle = heap_handle;
   }
 
@@ -310,7 +312,7 @@
     DelayedIncomingQueue delayed_incoming_queue;
     ObserverList<MessageLoop::TaskObserver>::Unchecked task_observers;
     size_t set_index;
-    HeapHandle heap_handle;
+    base::internal::HeapHandle heap_handle;
     int is_enabled_refcount;
     int voter_refcount;
     trace_event::BlameContext* blame_context;  // Not owned.
diff --git a/base/task/sequence_manager/task_queue_selector.cc b/base/task/sequence_manager/task_queue_selector.cc
index 03b08a9..0f3aca60 100644
--- a/base/task/sequence_manager/task_queue_selector.cc
+++ b/base/task/sequence_manager/task_queue_selector.cc
@@ -11,7 +11,7 @@
 #include "base/task/sequence_manager/task_queue_impl.h"
 #include "base/task/sequence_manager/work_queue.h"
 #include "base/threading/thread_checker.h"
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 
 namespace base {
 namespace sequence_manager {
diff --git a/base/task/sequence_manager/time_domain.h b/base/task/sequence_manager/time_domain.h
index 928abd0..150495c 100644
--- a/base/task/sequence_manager/time_domain.h
+++ b/base/task/sequence_manager/time_domain.h
@@ -10,7 +10,7 @@
 #include "base/callback.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/task/sequence_manager/intrusive_heap.h"
+#include "base/task/common/intrusive_heap.h"
 #include "base/task/sequence_manager/lazy_now.h"
 #include "base/task/sequence_manager/task_queue_impl.h"
 #include "base/time/time.h"
@@ -122,19 +122,19 @@
       return wake_up <= other.wake_up;
     }
 
-    void SetHeapHandle(internal::HeapHandle handle) {
+    void SetHeapHandle(base::internal::HeapHandle handle) {
       DCHECK(handle.IsValid());
       queue->set_heap_handle(handle);
     }
 
     void ClearHeapHandle() {
       DCHECK(queue->heap_handle().IsValid());
-      queue->set_heap_handle(internal::HeapHandle());
+      queue->set_heap_handle(base::internal::HeapHandle());
     }
   };
 
   internal::SequenceManagerImpl* sequence_manager_;  // Not owned.
-  internal::IntrusiveHeap<ScheduledDelayedWakeUp> delayed_wake_up_queue_;
+  base::internal::IntrusiveHeap<ScheduledDelayedWakeUp> delayed_wake_up_queue_;
   int pending_high_res_wake_up_count_ = 0;
 
   scoped_refptr<internal::AssociatedThreadId> associated_thread_;
diff --git a/base/task/sequence_manager/work_queue.h b/base/task/sequence_manager/work_queue.h
index b273fc05..d727f82 100644
--- a/base/task/sequence_manager/work_queue.h
+++ b/base/task/sequence_manager/work_queue.h
@@ -6,12 +6,12 @@
 #define BASE_TASK_SEQUENCE_MANAGER_WORK_QUEUE_H_
 
 #include "base/base_export.h"
+#include "base/task/common/intrusive_heap.h"
 #include "base/task/sequence_manager/enqueue_order.h"
-#include "base/task/sequence_manager/intrusive_heap.h"
 #include "base/task/sequence_manager/sequenced_task_source.h"
 #include "base/task/sequence_manager/task_queue_impl.h"
 #include "base/trace_event/trace_event.h"
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 
 namespace base {
 namespace sequence_manager {
@@ -96,9 +96,11 @@
 
   size_t work_queue_set_index() const { return work_queue_set_index_; }
 
-  HeapHandle heap_handle() const { return heap_handle_; }
+  base::internal::HeapHandle heap_handle() const { return heap_handle_; }
 
-  void set_heap_handle(HeapHandle handle) { heap_handle_ = handle; }
+  void set_heap_handle(base::internal::HeapHandle handle) {
+    heap_handle_ = handle;
+  }
 
   QueueType queue_type() const { return queue_type_; }
 
@@ -142,7 +144,7 @@
   WorkQueueSets* work_queue_sets_ = nullptr;  // NOT OWNED.
   TaskQueueImpl* const task_queue_;           // NOT OWNED.
   size_t work_queue_set_index_ = 0;
-  HeapHandle heap_handle_;
+  base::internal::HeapHandle heap_handle_;
   const char* const name_;
   EnqueueOrder fence_;
   const QueueType queue_type_;
diff --git a/base/task/sequence_manager/work_queue_sets.cc b/base/task/sequence_manager/work_queue_sets.cc
index e56fc82e..d7b315b 100644
--- a/base/task/sequence_manager/work_queue_sets.cc
+++ b/base/task/sequence_manager/work_queue_sets.cc
@@ -30,7 +30,7 @@
 void WorkQueueSets::RemoveQueue(WorkQueue* work_queue) {
   DCHECK_EQ(this, work_queue->work_queue_sets());
   work_queue->AssignToWorkQueueSets(nullptr);
-  HeapHandle heap_handle = work_queue->heap_handle();
+  base::internal::HeapHandle heap_handle = work_queue->heap_handle();
   if (!heap_handle.IsValid())
     return;
   size_t set_index = work_queue->work_queue_set_index();
@@ -100,7 +100,7 @@
 
 void WorkQueueSets::OnQueueBlocked(WorkQueue* work_queue) {
   DCHECK_EQ(this, work_queue->work_queue_sets());
-  HeapHandle heap_handle = work_queue->heap_handle();
+  base::internal::HeapHandle heap_handle = work_queue->heap_handle();
   if (!heap_handle.IsValid())
     return;
   size_t set_index = work_queue->work_queue_set_index();
@@ -147,7 +147,8 @@
   EnqueueOrder enqueue_order;
   bool has_enqueue_order = work_queue->GetFrontTaskEnqueueOrder(&enqueue_order);
 
-  for (const IntrusiveHeap<OldestTaskEnqueueOrder>& heap : work_queue_heaps_) {
+  for (const base::internal::IntrusiveHeap<OldestTaskEnqueueOrder>& heap :
+       work_queue_heaps_) {
     for (const OldestTaskEnqueueOrder& heap_value_pair : heap) {
       if (heap_value_pair.value == work_queue) {
         DCHECK(has_enqueue_order);
diff --git a/base/task/sequence_manager/work_queue_sets.h b/base/task/sequence_manager/work_queue_sets.h
index 01db040..a0ff05c2 100644
--- a/base/task/sequence_manager/work_queue_sets.h
+++ b/base/task/sequence_manager/work_queue_sets.h
@@ -11,10 +11,10 @@
 #include "base/base_export.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/task/sequence_manager/intrusive_heap.h"
+#include "base/task/common/intrusive_heap.h"
 #include "base/task/sequence_manager/task_queue_impl.h"
 #include "base/task/sequence_manager/work_queue.h"
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 
 namespace base {
 namespace sequence_manager {
@@ -82,14 +82,19 @@
       return key <= other.key;
     }
 
-    void SetHeapHandle(HeapHandle handle) { value->set_heap_handle(handle); }
+    void SetHeapHandle(base::internal::HeapHandle handle) {
+      value->set_heap_handle(handle);
+    }
 
-    void ClearHeapHandle() { value->set_heap_handle(HeapHandle()); }
+    void ClearHeapHandle() {
+      value->set_heap_handle(base::internal::HeapHandle());
+    }
   };
 
   // For each set |work_queue_heaps_| has a queue of WorkQueue ordered by the
   // oldest task in each WorkQueue.
-  std::vector<IntrusiveHeap<OldestTaskEnqueueOrder>> work_queue_heaps_;
+  std::vector<base::internal::IntrusiveHeap<OldestTaskEnqueueOrder>>
+      work_queue_heaps_;
   const char* const name_;
 
   DISALLOW_COPY_AND_ASSIGN(WorkQueueSets);
diff --git a/base/task/task_observer.h b/base/task/task_observer.h
new file mode 100644
index 0000000..4a8d06ff
--- /dev/null
+++ b/base/task/task_observer.h
@@ -0,0 +1,32 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TASK_TASK_OBSERVER_H_
+#define BASE_TASK_TASK_OBSERVER_H_
+
+#include "base/base_export.h"
+#include "base/pending_task.h"
+
+namespace base {
+
+// A TaskObserver is an object that receives notifications about tasks being
+// processed on the thread it's associated with.
+//
+// NOTE: A TaskObserver implementation should be extremely fast!
+
+class BASE_EXPORT TaskObserver {
+ public:
+  // This method is called before processing a task.
+  virtual void WillProcessTask(const PendingTask& pending_task) = 0;
+
+  // This method is called after processing a task.
+  virtual void DidProcessTask(const PendingTask& pending_task) = 0;
+
+ protected:
+  virtual ~TaskObserver() = default;
+};
+
+}  // namespace base
+
+#endif  // BASE_TASK_TASK_OBSERVER_H_
diff --git a/base/test/test_pending_task.h b/base/test/test_pending_task.h
index 460de0e..dc8eea1 100644
--- a/base/test/test_pending_task.h
+++ b/base/test/test_pending_task.h
@@ -10,7 +10,7 @@
 #include "base/callback.h"
 #include "base/location.h"
 #include "base/time/time.h"
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 
 namespace base {
 
diff --git a/base/test/trace_event_analyzer_unittest.cc b/base/test/trace_event_analyzer_unittest.cc
index c5732c3..a528d1c 100644
--- a/base/test/trace_event_analyzer_unittest.cc
+++ b/base/test/trace_event_analyzer_unittest.cc
@@ -12,7 +12,7 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/platform_thread.h"
 #include "base/trace_event/trace_buffer.h"
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index ff90ab2..bf42d1a 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -10,6 +10,90 @@
 #include "base/logging.h"
 #include "base/macros.h"
 
+// -----------------------------------------------------------------------------
+// Usage documentation
+// -----------------------------------------------------------------------------
+//
+// Overview:
+// This file exposes functions to ban and allow certain slow operations
+// on a per-thread basis. To annotate *usage* of such slow operations, refer to
+// scoped_blocking_call.h instead.
+//
+// Specific allowances that can be controlled in this file are:
+// - Blocking call: Refers to any call that causes the calling thread to wait
+//   off-CPU. It includes but is not limited to calls that wait on synchronous
+//   file I/O operations: read or write a file from disk, interact with a pipe
+//   or a socket, rename or delete a file, enumerate files in a directory, etc.
+//   Acquiring a low contention lock is not considered a blocking call.
+//
+// - Waiting on a //base sync primitive: Refers to calling one of these methods:
+//   - base::WaitableEvent::*Wait*
+//   - base::ConditionVariable::*Wait*
+//   - base::Process::WaitForExit*
+//
+// - Long CPU work: Refers to any code that takes more than 100 ms to
+//   run when there is no CPU contention and no hard page faults and therefore,
+//   is not suitable to run on a thread required to keep the browser responsive
+//   (where jank could be visible to the user).
+//
+// The following disallowance functions are offered:
+//  - DisallowBlocking(): Disallows blocking calls on the current thread.
+//  - DisallowBaseSyncPrimitives(): Disallows waiting on a //base sync primitive
+//    on the current thread.
+//  - DisallowUnresponsiveTasks() Disallows blocking calls, waiting on a //base
+//    sync primitive, and long cpu work on the current thread.
+//
+// In addition, scoped-allowance mechanisms are offered to make an exception
+// within a scope for a behavior that is normally disallowed.
+//  - ScopedAllowBlocking(ForTesting): Allows blocking calls.
+//  - ScopedAllowBaseSyncPrimitives(ForTesting)(OutsideBlockingScope): Allow
+//    waiting on a //base sync primitive. The OutsideBlockingScope suffix allows
+//    uses in a scope where blocking is also disallowed.
+//
+// Avoid using allowances outside of unit tests. In unit tests, use allowances
+// with the suffix "ForTesting".
+//
+// Prefer making blocking calls from tasks posted to base::TaskScheduler with
+// base::MayBlock().
+//
+// Instead of waiting on a WaitableEvent or a ConditionVariable, prefer putting
+// the work that should happen after the wait in a continuation callback and
+// post it from where the WaitableEvent or ConditionVariable would have been
+// signaled. If something needs to be scheduled after many tasks have executed,
+// use base::BarrierClosure.
+//
+// On Windows, join processes asynchronously using base::win::ObjectWatcher.
+//
+// Where unavoidable, put ScopedAllow* instances in the narrowest scope possible
+// in the caller making the blocking call but no further down. For example: if a
+// Cleanup() method needs to do a blocking call, document Cleanup() as blocking
+// and add a ScopedAllowBlocking instance in callers that can't avoid making
+// this call from a context where blocking is banned, as such:
+//
+//   void Client::MyMethod() {
+//     (...)
+//     {
+//       // Blocking is okay here because XYZ.
+//       ScopedAllowBlocking allow_blocking;
+//       my_foo_->Cleanup();
+//     }
+//     (...)
+//   }
+//
+//   // This method can block.
+//   void Foo::Cleanup() {
+//     // Do NOT add the ScopedAllowBlocking in Cleanup() directly as that hides
+//     // its blocking nature from unknowing callers and defeats the purpose of
+//     // these checks.
+//     FlushStateToDisk();
+//   }
+//
+// Note: In rare situations where the blocking call is an implementation detail
+// (i.e. the impl makes a call that invokes AssertBlockingAllowed() but it
+// somehow knows that in practice this will not block), it might be okay to hide
+// the ScopedAllowBlocking instance in the impl with a comment explaining why
+// that's okay.
+
 class BrowserProcessImpl;
 class HistogramSynchronizer;
 class NativeBackendKWallet;
@@ -164,14 +248,6 @@
   {}
 #endif
 
-// TODO(etiennep): Add a meta-comment which collapses all of the comments below.
-
-// A "blocking call" refers to any call that causes the calling thread to wait
-// off-CPU. It includes but is not limited to calls that wait on synchronous
-// file I/O operations: read or write a file from disk, interact with a pipe or
-// a socket, rename or delete a file, enumerate files in a directory, etc.
-// Acquiring a low contention lock is not considered a blocking call.
-
 namespace internal {
 
 // Asserts that blocking calls are allowed in the current scope. This is an
@@ -207,40 +283,6 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedDisallowBlocking);
 };
 
-// ScopedAllowBlocking(ForTesting) allow blocking calls within a scope where
-// they are normally disallowed.
-//
-// Avoid using this. Prefer making blocking calls from tasks posted to
-// base::TaskScheduler with base::MayBlock().
-//
-// Where unavoidable, put ScopedAllow* instances in the narrowest scope possible
-// in the caller making the blocking call but no further down. That is: if a
-// Cleanup() method needs to do a blocking call, document Cleanup() as blocking
-// and add a ScopedAllowBlocking instance in callers that can't avoid making
-// this call from a context where blocking is banned, as such:
-//   void Client::MyMethod() {
-//     (...)
-//     {
-//       // Blocking is okay here because XYZ.
-//       ScopedAllowBlocking allow_blocking;
-//       my_foo_->Cleanup();
-//     }
-//     (...)
-//   }
-//
-//   // This method can block.
-//   void Foo::Cleanup() {
-//     // Do NOT add the ScopedAllowBlocking in Cleanup() directly as that hides
-//     // its blocking nature from unknowing callers and defeats the purpose of
-//     // these checks.
-//     FlushStateToDisk();
-//   }
-//
-// Note: In rare situations where the blocking call is an implementation detail
-// (i.e. the impl makes a call that invokes AssertBlockingAllowed() but it
-// somehow knows that in practice this will not block), it might be okay to hide
-// the ScopedAllowBlocking instance in the impl with a comment explaining why
-// that's okay.
 class BASE_EXPORT ScopedAllowBlocking {
  private:
   // This can only be instantiated by friends. Use ScopedAllowBlockingForTesting
@@ -283,29 +325,9 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedAllowBlockingForTesting);
 };
 
-// "Waiting on a //base sync primitive" refers to calling one of these methods:
-// - base::WaitableEvent::*Wait*
-// - base::ConditionVariable::*Wait*
-// - base::Process::WaitForExit*
-
-// Disallows waiting on a //base sync primitive on the current thread.
 INLINE_IF_DCHECK_IS_OFF void DisallowBaseSyncPrimitives()
     EMPTY_BODY_IF_DCHECK_IS_OFF;
 
-// ScopedAllowBaseSyncPrimitives(ForTesting)(OutsideBlockingScope) allow waiting
-// on a //base sync primitive within a scope where this is normally disallowed.
-//
-// Avoid using this.
-//
-// Instead of waiting on a WaitableEvent or a ConditionVariable, put the work
-// that should happen after the wait in a callback and post that callback from
-// where the WaitableEvent or ConditionVariable would have been signaled. If
-// something needs to be scheduled after many tasks have executed, use
-// base::BarrierClosure.
-//
-// On Windows, join processes asynchronously using base::win::ObjectWatcher.
-
-// This can only be used in a scope where blocking is allowed.
 class BASE_EXPORT ScopedAllowBaseSyncPrimitives {
  private:
   // This can only be instantiated by friends. Use
@@ -342,7 +364,6 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedAllowBaseSyncPrimitives);
 };
 
-// This can be used in a scope where blocking is disallowed.
 class BASE_EXPORT ScopedAllowBaseSyncPrimitivesOutsideBlockingScope {
  private:
   // This can only be instantiated by friends. Use
@@ -375,8 +396,6 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedAllowBaseSyncPrimitivesOutsideBlockingScope);
 };
 
-// This can be used in tests without being a friend of
-// ScopedAllowBaseSyncPrimitives(OutsideBlockingScope).
 class BASE_EXPORT ScopedAllowBaseSyncPrimitivesForTesting {
  public:
   ScopedAllowBaseSyncPrimitivesForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF;
@@ -403,18 +422,10 @@
 
 }  // namespace internal
 
-// "Long CPU" work refers to any code that takes more than 100 ms to
-// run when there is no CPU contention and no hard page faults and therefore,
-// is not suitable to run on a thread required to keep the browser responsive
-// (where jank could be visible to the user).
-
 // Asserts that running long CPU work is allowed in the current scope.
 INLINE_IF_DCHECK_IS_OFF void AssertLongCPUWorkAllowed()
     EMPTY_BODY_IF_DCHECK_IS_OFF;
 
-// Disallows all tasks that may be unresponsive on the current thread. This
-// disallows blocking calls, waiting on a //base sync primitive and long CPU
-// work.
 INLINE_IF_DCHECK_IS_OFF void DisallowUnresponsiveTasks()
     EMPTY_BODY_IF_DCHECK_IS_OFF;
 
diff --git a/base/trace_event/blame_context.cc b/base/trace_event/blame_context.cc
index ae0b7182..0ed9aa7 100644
--- a/base/trace_event/blame_context.cc
+++ b/base/trace_event/blame_context.cc
@@ -6,7 +6,7 @@
 
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event.h"
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 
 namespace base {
 namespace trace_event {
diff --git a/base/trace_event/blame_context_unittest.cc b/base/trace_event/blame_context_unittest.cc
index 12e7857..f628115 100644
--- a/base/trace_event/blame_context_unittest.cc
+++ b/base/trace_event/blame_context_unittest.cc
@@ -7,7 +7,7 @@
 #include "base/json/json_writer.h"
 #include "base/message_loop/message_loop.h"
 #include "base/test/trace_event_analyzer.h"
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
diff --git a/base/trace_event/malloc_dump_provider.cc b/base/trace_event/malloc_dump_provider.cc
index 46fdb3e2..0077d8b 100644
--- a/base/trace_event/malloc_dump_provider.cc
+++ b/base/trace_event/malloc_dump_provider.cc
@@ -12,7 +12,7 @@
 #include "base/allocator/buildflags.h"
 #include "base/debug/profiler.h"
 #include "base/trace_event/process_memory_dump.h"
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 #include "build/build_config.h"
 
 #if defined(OS_MACOSX)
diff --git a/base/trace_event/memory_allocator_dump.cc b/base/trace_event/memory_allocator_dump.cc
index 5260a73..f9c8315 100644
--- a/base/trace_event/memory_allocator_dump.cc
+++ b/base/trace_event/memory_allocator_dump.cc
@@ -12,7 +12,7 @@
 #include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/memory_dump_provider.h"
 #include "base/trace_event/process_memory_dump.h"
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 #include "base/values.h"
 
 namespace base {
diff --git a/base/trace_event/memory_allocator_dump.h b/base/trace_event/memory_allocator_dump.h
index de38afd..4999e85 100644
--- a/base/trace_event/memory_allocator_dump.h
+++ b/base/trace_event/memory_allocator_dump.h
@@ -18,7 +18,7 @@
 #include "base/optional.h"
 #include "base/trace_event/memory_allocator_dump_guid.h"
 #include "base/trace_event/memory_dump_request_args.h"
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 #include "base/unguessable_token.h"
 #include "base/values.h"
 
diff --git a/base/trace_event/memory_allocator_dump_unittest.cc b/base/trace_event/memory_allocator_dump_unittest.cc
index 78a545f..67ea455 100644
--- a/base/trace_event/memory_allocator_dump_unittest.cc
+++ b/base/trace_event/memory_allocator_dump_unittest.cc
@@ -11,7 +11,7 @@
 #include "base/trace_event/memory_allocator_dump_guid.h"
 #include "base/trace_event/memory_dump_provider.h"
 #include "base/trace_event/process_memory_dump.h"
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index d61528a..d3b7287a 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -32,7 +32,7 @@
 #include "base/trace_event/memory_infra_background_whitelist.h"
 #include "base/trace_event/process_memory_dump.h"
 #include "base/trace_event/trace_event.h"
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 #include "build/build_config.h"
 
 #if defined(OS_ANDROID)
diff --git a/base/trace_event/process_memory_dump.cc b/base/trace_event/process_memory_dump.cc
index 362641c..8f851f4 100644
--- a/base/trace_event/process_memory_dump.cc
+++ b/base/trace_event/process_memory_dump.cc
@@ -13,7 +13,7 @@
 #include "base/process/process_metrics.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/memory_infra_background_whitelist.h"
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 #include "base/unguessable_token.h"
 #include "build/build_config.h"
 
diff --git a/base/trace_event/process_memory_dump_unittest.cc b/base/trace_event/process_memory_dump_unittest.cc
index d5f771d..8e334e0 100644
--- a/base/trace_event/process_memory_dump_unittest.cc
+++ b/base/trace_event/process_memory_dump_unittest.cc
@@ -12,8 +12,8 @@
 #include "base/process/process_metrics.h"
 #include "base/trace_event/memory_allocator_dump_guid.h"
 #include "base/trace_event/memory_infra_background_whitelist.h"
-#include "base/trace_event/trace_event_argument.h"
 #include "base/trace_event/trace_log.h"
+#include "base/trace_event/traced_value.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/base/trace_event/trace_event_argument.h b/base/trace_event/trace_event_argument.h
index 81d8c01..2d2eb54 100644
--- a/base/trace_event/trace_event_argument.h
+++ b/base/trace_event/trace_event_argument.h
@@ -1,92 +1,11 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef BASE_TRACE_EVENT_TRACE_EVENT_ARGUMENT_H_
 #define BASE_TRACE_EVENT_TRACE_EVENT_ARGUMENT_H_
 
-#include <stddef.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/pickle.h"
-#include "base/strings/string_piece.h"
-#include "base/trace_event/trace_event_impl.h"
-
-namespace base {
-
-class Value;
-
-namespace trace_event {
-
-class BASE_EXPORT TracedValue : public ConvertableToTraceFormat {
- public:
-  TracedValue();
-  explicit TracedValue(size_t capacity);
-  ~TracedValue() override;
-
-  void EndDictionary();
-  void EndArray();
-
-  // These methods assume that |name| is a long lived "quoted" string.
-  void SetInteger(const char* name, int value);
-  void SetDouble(const char* name, double value);
-  void SetBoolean(const char* name, bool value);
-  void SetString(const char* name, base::StringPiece value);
-  void SetValue(const char* name, const TracedValue& value);
-  void BeginDictionary(const char* name);
-  void BeginArray(const char* name);
-
-  // These, instead, can be safely passed a temporary string.
-  void SetIntegerWithCopiedName(base::StringPiece name, int value);
-  void SetDoubleWithCopiedName(base::StringPiece name, double value);
-  void SetBooleanWithCopiedName(base::StringPiece name, bool value);
-  void SetStringWithCopiedName(base::StringPiece name,
-                               base::StringPiece value);
-  void SetValueWithCopiedName(base::StringPiece name,
-                              const TracedValue& value);
-  void BeginDictionaryWithCopiedName(base::StringPiece name);
-  void BeginArrayWithCopiedName(base::StringPiece name);
-
-  void AppendInteger(int);
-  void AppendDouble(double);
-  void AppendBoolean(bool);
-  void AppendString(base::StringPiece);
-  void BeginArray();
-  void BeginDictionary();
-
-  // ConvertableToTraceFormat implementation.
-  void AppendAsTraceFormat(std::string* out) const override;
-
-  void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead) override;
-
-  // DEPRECATED: do not use, here only for legacy reasons. These methods causes
-  // a copy-and-translation of the base::Value into the equivalent TracedValue.
-  // TODO(primiano): migrate the (three) existing clients to the cheaper
-  // SetValue(TracedValue) API. crbug.com/495628.
-  void SetValue(const char* name, std::unique_ptr<base::Value> value);
-  void SetBaseValueWithCopiedName(base::StringPiece name,
-                                  const base::Value& value);
-  void AppendBaseValue(const base::Value& value);
-
-  // Public for tests only.
-  std::unique_ptr<base::Value> ToBaseValue() const;
-
- private:
-  Pickle pickle_;
-
-#ifndef NDEBUG
-  // In debug builds checks the pairings of {Start,End}{Dictionary,Array}
-  std::vector<bool> nesting_stack_;
-#endif
-
-  DISALLOW_COPY_AND_ASSIGN(TracedValue);
-};
-
-}  // namespace trace_event
-}  // namespace base
+// TODO(crbug/898787): Update callers to use traced_value.h instead.
+#include "base/trace_event/traced_value.h"
 
 #endif  // BASE_TRACE_EVENT_TRACE_EVENT_ARGUMENT_H_
diff --git a/base/trace_event/trace_event_impl.cc b/base/trace_event/trace_event_impl.cc
index c72e1fc..3458115 100644
--- a/base/trace_event/trace_event_impl.cc
+++ b/base/trace_event/trace_event_impl.cc
@@ -16,8 +16,8 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
-#include "base/trace_event/trace_event_argument.h"
 #include "base/trace_event/trace_log.h"
+#include "base/trace_event/traced_value.h"
 
 namespace base {
 namespace trace_event {
diff --git a/base/trace_event/trace_event_argument.cc b/base/trace_event/traced_value.cc
similarity index 96%
rename from base/trace_event/trace_event_argument.cc
rename to base/trace_event/traced_value.cc
index e614b27..3f4c743 100644
--- a/base/trace_event/trace_event_argument.cc
+++ b/base/trace_event/traced_value.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 "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 
 #include <stdint.h>
 
@@ -39,10 +39,18 @@
 #define DEBUG_PUSH_CONTAINER(x) nesting_stack_.push_back(x)
 #define DEBUG_POP_CONTAINER() nesting_stack_.pop_back()
 #else
-#define DCHECK_CURRENT_CONTAINER_IS(x) do {} while (0)
-#define DCHECK_CONTAINER_STACK_DEPTH_EQ(x) do {} while (0)
-#define DEBUG_PUSH_CONTAINER(x) do {} while (0)
-#define DEBUG_POP_CONTAINER() do {} while (0)
+#define DCHECK_CURRENT_CONTAINER_IS(x) \
+  do {                                 \
+  } while (0)
+#define DCHECK_CONTAINER_STACK_DEPTH_EQ(x) \
+  do {                                     \
+  } while (0)
+#define DEBUG_PUSH_CONTAINER(x) \
+  do {                          \
+  } while (0)
+#define DEBUG_POP_CONTAINER() \
+  do {                        \
+  } while (0)
 #endif
 
 inline void WriteKeyNameAsRawPtr(Pickle& pickle, const char* ptr) {
@@ -71,8 +79,7 @@
 }
 }  // namespace
 
-TracedValue::TracedValue() : TracedValue(0) {
-}
+TracedValue::TracedValue() : TracedValue(0) {}
 
 TracedValue::TracedValue(size_t capacity) {
   DEBUG_PUSH_CONTAINER(kStackTypeDict);
@@ -122,8 +129,7 @@
   WriteKeyNameAsRawPtr(pickle_, name);
 }
 
-void TracedValue::SetBooleanWithCopiedName(base::StringPiece name,
-                                           bool value) {
+void TracedValue::SetBooleanWithCopiedName(base::StringPiece name, bool value) {
   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
   pickle_.WriteBytes(&kTypeBool, 1);
   pickle_.WriteBool(value);
diff --git a/base/trace_event/traced_value.h b/base/trace_event/traced_value.h
new file mode 100644
index 0000000..97c52a076
--- /dev/null
+++ b/base/trace_event/traced_value.h
@@ -0,0 +1,90 @@
+// 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.
+
+#ifndef BASE_TRACE_EVENT_TRACED_VALUE_H_
+#define BASE_TRACE_EVENT_TRACED_VALUE_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/pickle.h"
+#include "base/strings/string_piece.h"
+#include "base/trace_event/trace_event_impl.h"
+
+namespace base {
+
+class Value;
+
+namespace trace_event {
+
+class BASE_EXPORT TracedValue : public ConvertableToTraceFormat {
+ public:
+  TracedValue();
+  explicit TracedValue(size_t capacity);
+  ~TracedValue() override;
+
+  void EndDictionary();
+  void EndArray();
+
+  // These methods assume that |name| is a long lived "quoted" string.
+  void SetInteger(const char* name, int value);
+  void SetDouble(const char* name, double value);
+  void SetBoolean(const char* name, bool value);
+  void SetString(const char* name, base::StringPiece value);
+  void SetValue(const char* name, const TracedValue& value);
+  void BeginDictionary(const char* name);
+  void BeginArray(const char* name);
+
+  // These, instead, can be safely passed a temporary string.
+  void SetIntegerWithCopiedName(base::StringPiece name, int value);
+  void SetDoubleWithCopiedName(base::StringPiece name, double value);
+  void SetBooleanWithCopiedName(base::StringPiece name, bool value);
+  void SetStringWithCopiedName(base::StringPiece name, base::StringPiece value);
+  void SetValueWithCopiedName(base::StringPiece name, const TracedValue& value);
+  void BeginDictionaryWithCopiedName(base::StringPiece name);
+  void BeginArrayWithCopiedName(base::StringPiece name);
+
+  void AppendInteger(int);
+  void AppendDouble(double);
+  void AppendBoolean(bool);
+  void AppendString(base::StringPiece);
+  void BeginArray();
+  void BeginDictionary();
+
+  // ConvertableToTraceFormat implementation.
+  void AppendAsTraceFormat(std::string* out) const override;
+
+  void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead) override;
+
+  // DEPRECATED: do not use, here only for legacy reasons. These methods causes
+  // a copy-and-translation of the base::Value into the equivalent TracedValue.
+  // TODO(primiano): migrate the (three) existing clients to the cheaper
+  // SetValue(TracedValue) API. crbug.com/495628.
+  void SetValue(const char* name, std::unique_ptr<base::Value> value);
+  void SetBaseValueWithCopiedName(base::StringPiece name,
+                                  const base::Value& value);
+  void AppendBaseValue(const base::Value& value);
+
+  // Public for tests only.
+  std::unique_ptr<base::Value> ToBaseValue() const;
+
+ private:
+  Pickle pickle_;
+
+#ifndef NDEBUG
+  // In debug builds checks the pairings of {Start,End}{Dictionary,Array}
+  std::vector<bool> nesting_stack_;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(TracedValue);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_TRACED_VALUE_H_
diff --git a/base/trace_event/trace_event_argument_unittest.cc b/base/trace_event/traced_value_unittest.cc
similarity index 98%
rename from base/trace_event/trace_event_argument_unittest.cc
rename to base/trace_event/traced_value_unittest.cc
index 69d203a..21f58c66 100644
--- a/base/trace_event/trace_event_argument_unittest.cc
+++ b/base/trace_event/traced_value_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 
 #include <stddef.h>
 
diff --git a/build/android/apk_operations.py b/build/android/apk_operations.py
index fb19445..26c56e9 100755
--- a/build/android/apk_operations.py
+++ b/build/android/apk_operations.py
@@ -306,8 +306,8 @@
         run as root.
 
   Returns:
-    A dict of path->size in kb containing all paths in |path_spec| that exist on
-    device. Paths that do not exist are silently ignored.
+    A dict of path->size in KiB containing all paths in |path_spec| that exist
+    on device. Paths that do not exist are silently ignored.
   """
   # Example output for: du -s -k /data/data/org.chromium.chrome/{*,.*}
   # 144     /data/data/org.chromium.chrome/cache
@@ -341,7 +341,7 @@
     raise
 
 
-def _RunDiskUsage(devices, package_name, verbose):
+def _RunDiskUsage(devices, package_name):
   # Measuring dex size is a bit complicated:
   # https://source.android.com/devices/tech/dalvik/jit-compiler
   #
@@ -463,10 +463,9 @@
             compilation_filter)
 
   def print_sizes(desc, sizes):
-    print '%s: %dkb' % (desc, sum(sizes.itervalues()))
-    if verbose:
-      for path, size in sorted(sizes.iteritems()):
-        print '    %s: %skb' % (path, size)
+    print '%s: %d KiB' % (desc, sum(sizes.itervalues()))
+    for path, size in sorted(sizes.iteritems()):
+      print '    %s: %s KiB' % (path, size)
 
   parallel_devices = device_utils.DeviceUtils.parallel(devices)
   all_results = parallel_devices.pMap(disk_usage_helper).pGet(None)
@@ -489,7 +488,7 @@
     if show_warning:
       logging.warning('For a more realistic odex size, run:')
       logging.warning('    %s compile-dex [speed|speed-profile]', sys.argv[0])
-    print 'Total: %skb (%.1fmb)' % (total, total / 1024.0)
+    print 'Total: %s KiB (%.1f MiB)' % (total, total / 1024.0)
 
 
 class _LogcatProcessor(object):
@@ -1186,8 +1185,7 @@
   all_devices_by_default = True
 
   def Run(self):
-    _RunDiskUsage(self.devices, self.args.package_name,
-                  bool(self.args.verbose_count))
+    _RunDiskUsage(self.devices, self.args.package_name)
 
 
 class _MemUsageCommand(_Command):
diff --git a/build/fuchsia/fidlgen_js/test/fidlgen_js_unittest.cc b/build/fuchsia/fidlgen_js/test/fidlgen_js_unittest.cc
index b4fef3d..8ca5644 100644
--- a/build/fuchsia/fidlgen_js/test/fidlgen_js_unittest.cc
+++ b/build/fuchsia/fidlgen_js/test/fidlgen_js_unittest.cc
@@ -7,7 +7,7 @@
 #include <lib/fidl/cpp/internal/weak_stub_controller.h>
 
 #include "base/bind.h"
-#include "base/fuchsia/async_dispatcher.h"
+#include "base/run_loop.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/launcher/unit_test_launcher.h"
@@ -154,8 +154,12 @@
     various_stuff_ = stuff_as_vec;
   }
 
-  void WithResponse(int32_t a, int32_t b, WithResponseCallback sum) override {
-    sum(a + b);
+  void WithResponse(int32_t a,
+                    int32_t b,
+                    WithResponseCallback callback) override {
+    response_callbacks_.push_back(base::BindOnce(
+        [](WithResponseCallback callback, int32_t result) { callback(result); },
+        std::move(callback), a + b));
   }
 
   void SendAStruct(fidljstest::BasicStruct basic_struct) override {
@@ -196,6 +200,13 @@
 
   fidljstest::BasicStruct GetReceivedStruct() const { return basic_struct_; }
 
+  void CallResponseCallbacks() {
+    for (auto& cb : response_callbacks_) {
+      std::move(cb).Run();
+    }
+    response_callbacks_.clear();
+  }
+
  private:
   bool was_do_something_called_ = false;
   int32_t received_int_ = -1;
@@ -204,6 +215,7 @@
   std::string various_msg_;
   std::vector<uint32_t> various_stuff_;
   fidljstest::BasicStruct basic_struct_;
+  std::vector<base::OnceClosure> response_callbacks_;
 
   DISALLOW_COPY_AND_ASSIGN(TestolaImpl);
 };
@@ -341,14 +353,12 @@
 }
 
 TEST_F(FidlGenJsTest, RawWithResponse) {
-  base::AsyncDispatcher dispatcher;
-
   v8::Isolate* isolate = instance_->isolate();
   BindingsSetupHelper helper(isolate);
 
   TestolaImpl testola_impl;
   fidl::Binding<fidljstest::Testola> binding(&testola_impl);
-  binding.Bind(std::move(helper.server()), &dispatcher);
+  binding.Bind(std::move(helper.server()));
 
   // Send the data from the JS side into the channel.
   std::string source = R"(
@@ -363,12 +373,11 @@
     )";
   helper.runner().Run(source, "test.js");
 
-  // Run the dispatcher until the response or timeout.
-  while (helper.Get<int>("sum_result") == -1) {
-    ASSERT_EQ(dispatcher.DispatchOrWaitUntil(zx_deadline_after(
-                  ZX_MSEC(TestTimeouts::action_timeout().InMilliseconds()))),
-              ZX_OK);
-  }
+  base::RunLoop().RunUntilIdle();
+
+  testola_impl.CallResponseCallbacks();
+
+  base::RunLoop().RunUntilIdle();
 
   // Confirm that the response was received with the correct value.
   auto sum_result = helper.Get<int>("sum_result");
@@ -376,15 +385,13 @@
 }
 
 TEST_F(FidlGenJsTest, NoResponseBeforeTearDown) {
-  base::AsyncDispatcher dispatcher;
-
   v8::Isolate* isolate = instance_->isolate();
 
   BindingsSetupHelper helper(isolate);
 
   TestolaImpl testola_impl;
   fidl::Binding<fidljstest::Testola> binding(&testola_impl);
-  binding.Bind(std::move(helper.server()), &dispatcher);
+  binding.Bind(std::move(helper.server()));
 
   // Send the data from the JS side into the channel.
   std::string source = R"(
@@ -406,8 +413,9 @@
     )";
   helper.runner().Run(source, "test.js");
 
-  // Run the dispatcher, to read and queue the response.
-  EXPECT_EQ(dispatcher.DispatchOrWaitUntil(ZX_TIME_INFINITE), ZX_OK);
+  // Run the message loop to read and queue the request, but don't send the
+  // response.
+  base::RunLoop().RunUntilIdle();
 
   // This causes outstanding waits to be canceled.
   helper.DestroyBindingsForTesting();
@@ -418,14 +426,12 @@
 }
 
 TEST_F(FidlGenJsTest, RawReceiveFidlStructMessage) {
-  base::AsyncDispatcher dispatcher;
-
   v8::Isolate* isolate = instance_->isolate();
   BindingsSetupHelper helper(isolate);
 
   TestolaImpl testola_impl;
   fidl::Binding<fidljstest::Testola> binding(&testola_impl);
-  binding.Bind(std::move(helper.server()), &dispatcher);
+  binding.Bind(std::move(helper.server()));
 
   // Send the data from the JS side into the channel.
   std::string source = R"(
@@ -438,9 +444,7 @@
   helper.runner().Run(source, "test.js");
 
   // Run the dispatcher to read and dispatch the response.
-  ASSERT_EQ(dispatcher.DispatchOrWaitUntil(zx_deadline_after(
-                ZX_MSEC(TestTimeouts::action_timeout().InMilliseconds()))),
-            ZX_OK);
+  base::RunLoop().RunUntilIdle();
 
   fidljstest::BasicStruct received_struct = testola_impl.GetReceivedStruct();
   EXPECT_EQ(received_struct.b, true);
@@ -454,14 +458,12 @@
 }
 
 TEST_F(FidlGenJsTest, RawReceiveFidlNestedStructsAndRespond) {
-  base::AsyncDispatcher dispatcher;
-
   v8::Isolate* isolate = instance_->isolate();
   BindingsSetupHelper helper(isolate);
 
   TestolaImpl testola_impl;
   fidl::Binding<fidljstest::Testola> binding(&testola_impl);
-  binding.Bind(std::move(helper.server()), &dispatcher);
+  binding.Bind(std::move(helper.server()));
 
   // Send the data from the JS side into the channel.
   std::string source = R"(
@@ -486,11 +488,8 @@
     )";
   helper.runner().Run(source, "test.js");
 
-  // Run the dispatcher to read the request.
-  EXPECT_EQ(dispatcher.DispatchOrWaitUntil(ZX_TIME_INFINITE), ZX_OK);
-
-  // Run the dispatcher again to write the response.
-  EXPECT_EQ(dispatcher.DispatchOrWaitUntil(ZX_TIME_INFINITE), ZX_OK);
+  // Run the message loop to read the request and write the response.
+  base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(helper.Get<int>("result_count"), 123);
   EXPECT_EQ(helper.Get<std::string>("result_id"), "here is my id");
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index b58860247..c202c68 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-b188ffb05d7f4a23fea52ada32477d8c4a10c646
\ No newline at end of file
+544e4b9339ce00bc63a3616a78783d975949b38d
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index b94c8a63..5b3aaab 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-7fd50dd381471743c99dcf7d0ee6ea73e62c55aa
\ No newline at end of file
+2f3f1bf9489ae7f53df3203849b26b756743cadd
\ No newline at end of file
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index ee4a8ff..1c4e204 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -511,8 +511,17 @@
   paint->setAntiAlias(true);
 
   paint->setTextSize(size);
-  paint->setTextAlign(align);
   paint->setTypeface(typeface_);
+
+  if (align != SkPaint::kLeft_Align) {
+    SkScalar width = paint->measureText(text.c_str(), text.length(), nullptr);
+    if (align == SkPaint::kCenter_Align) {
+      x -= width * 0.5f;
+    } else {
+      DCHECK_EQ(align, SkPaint::kRight_Align);
+      x -= width;
+    }
+  }
   canvas->drawText(text.c_str(), text.length(), x, y, *paint);
 
   paint->setAntiAlias(anti_alias);
diff --git a/cc/tiles/gpu_image_decode_cache.cc b/cc/tiles/gpu_image_decode_cache.cc
index 7155080..480a9bb 100644
--- a/cc/tiles/gpu_image_decode_cache.cc
+++ b/cc/tiles/gpu_image_decode_cache.cc
@@ -734,6 +734,7 @@
     DecodeTaskType task_type) {
   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
                "GpuImageDecodeCache::GetTaskForImageAndRef");
+
   if (SkipImage(draw_image))
     return TaskResult(false);
 
@@ -791,10 +792,12 @@
     // Ref image and create a upload and decode tasks. We will release this ref
     // in UploadTaskCompleted.
     RefImage(draw_image, cache_key);
+    auto decode_task =
+        image_data->is_bitmap_backed
+            ? nullptr
+            : GetImageDecodeTaskAndRef(draw_image, tracing_info, task_type);
     task = base::MakeRefCounted<ImageUploadTaskImpl>(
-        this, draw_image,
-        GetImageDecodeTaskAndRef(draw_image, tracing_info, task_type),
-        tracing_info);
+        this, draw_image, std::move(decode_task), tracing_info);
     image_data->upload.task = task;
   } else {
     task = GetImageDecodeTaskAndRef(draw_image, tracing_info, task_type);
@@ -1114,10 +1117,16 @@
     gr_context_access.emplace(context_);
   base::AutoLock lock(lock_);
 
-  ImageData* image_data = GetImageDataForDrawImage(
-      draw_image, InUseCacheKey::FromDrawImage(draw_image));
+  auto cache_key = InUseCacheKey::FromDrawImage(draw_image);
+  ImageData* image_data = GetImageDataForDrawImage(draw_image, cache_key);
   DCHECK(image_data);
   DCHECK(image_data->is_budgeted) << "Must budget an image for pre-decoding";
+
+  if (image_data->is_bitmap_backed) {
+    RefImageDecode(draw_image, cache_key);
+    DecodeImageIfNecessary(draw_image, image_data, TaskType::kInRaster);
+  }
+
   UploadImageIfNecessary(draw_image, image_data);
 }
 
@@ -1183,6 +1192,7 @@
 
   ImageData* image_data = GetImageDataForDrawImage(draw_image, cache_key);
   DCHECK(image_data);
+  DCHECK(!image_data->is_bitmap_backed);
   if (image_data->decode.is_locked()) {
     // We should never be creating a decode task for a not budgeted image.
     DCHECK(image_data->is_budgeted);
diff --git a/cc/tiles/gpu_image_decode_cache_unittest.cc b/cc/tiles/gpu_image_decode_cache_unittest.cc
index fc570cc..25758ca 100644
--- a/cc/tiles/gpu_image_decode_cache_unittest.cc
+++ b/cc/tiles/gpu_image_decode_cache_unittest.cc
@@ -2251,7 +2251,7 @@
   EXPECT_FALSE(cache->GetSWImageDecodeForTesting(draw_image));
 }
 
-TEST_P(GpuImageDecodeCacheTest, NonLazyImageUploadNoScaleTask) {
+TEST_P(GpuImageDecodeCacheTest, NonLazyImageUploadTaskHasNoDeps) {
   auto cache = CreateCache();
   bool is_decomposable = true;
   SkFilterQuality quality = kHigh_SkFilterQuality;
@@ -2265,7 +2265,7 @@
       cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
   EXPECT_TRUE(result.need_unref);
   EXPECT_TRUE(result.task);
-  TestTileTaskRunner::ProcessTask(result.task->dependencies()[0].get());
+  EXPECT_TRUE(result.task->dependencies().empty());
   TestTileTaskRunner::ProcessTask(result.task.get());
 
   cache->UnrefImage(draw_image);
diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc
index 7c9b86cb..ef8086d0 100644
--- a/cc/trees/draw_property_utils.cc
+++ b/cc/trees/draw_property_utils.cc
@@ -457,9 +457,14 @@
     LayerType* layer,
     const TransformTree& transform_tree,
     const EffectTree& effect_tree) {
+  // TODO(enne): remove temporary CHECKs once http://crbug.com/898668 is fixed.
+  CHECK_NE(layer->transform_tree_index(), TransformTree::kInvalidNodeId);
   const TransformNode* transform_node =
       transform_tree.Node(layer->transform_tree_index());
+  CHECK(transform_node);
+  CHECK_NE(layer->effect_tree_index(), EffectTree::kInvalidNodeId);
   const EffectNode* effect_node = effect_tree.Node(layer->effect_tree_index());
+  CHECK(effect_node);
 
   if (effect_node->has_render_surface && effect_node->subtree_has_copy_request)
     return false;
@@ -725,6 +730,9 @@
     LayerImpl* layer,
     const TransformTree& transform_tree,
     const EffectTree& effect_tree) {
+  // TODO(enne): remove temporary CHECKs once http://crbug.com/898668 is fixed.
+  CHECK(layer);
+  CHECK(layer->layer_tree_impl());
   return LayerShouldBeSkippedInternal(layer, transform_tree, effect_tree);
 }
 
@@ -732,6 +740,11 @@
     Layer* layer,
     const TransformTree& transform_tree,
     const EffectTree& effect_tree) {
+  // TODO(enne): remove temporary CHECKs once http://crbug.com/898668 is fixed.
+  CHECK(layer);
+  CHECK(layer->layer_tree_host());
+  CHECK_EQ(layer->layer_tree_host()->property_trees()->sequence_number,
+           layer->property_tree_sequence_number());
   return LayerShouldBeSkippedInternal(layer, transform_tree, effect_tree);
 }
 
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 4d2e7de..31c8a2a 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -748,8 +748,6 @@
   // and SlimmingPaintV2.
   if (!IsUsingLayerLists()) {
     TRACE_EVENT0("cc", "LayerTreeHost::UpdateLayers::BuildPropertyTrees");
-    TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"),
-                 "LayerTreeHostCommon::ComputeVisibleRectsWithPropertyTrees");
     Layer* root_scroll =
         PropertyTreeBuilder::FindFirstScrollableLayer(root_layer);
     Layer* page_scale_layer = viewport_layers_.page_scale.get();
diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc
index 3e7e96e..7ce5bae 100644
--- a/cc/trees/layer_tree_host_common.cc
+++ b/cc/trees/layer_tree_host_common.cc
@@ -202,12 +202,6 @@
     mask_layer->set_contributes_to_drawn_render_surface(false);
 }
 
-static bool CdpPerfTracingEnabled() {
-  bool tracing_enabled;
-  TRACE_EVENT_CATEGORY_GROUP_ENABLED("cdp.perf", &tracing_enabled);
-  return tracing_enabled;
-}
-
 static float TranslationFromActiveTreeLayerScreenSpaceTransform(
     LayerImpl* pending_tree_layer) {
   LayerTreeImpl* layer_tree_impl = pending_tree_layer->layer_tree_impl();
@@ -498,21 +492,12 @@
     PropertyTreeOption property_tree_option) {
   inputs->render_surface_list->clear();
 
-  const bool should_measure_property_tree_performance =
-      property_tree_option == BUILD_PROPERTY_TREES;
-
   LayerImplList visible_layer_list;
   switch (property_tree_option) {
     case BUILD_PROPERTY_TREES: {
       // The translation from layer to property trees is an intermediate
       // state. We will eventually get these data passed directly to the
       // compositor.
-      if (should_measure_property_tree_performance) {
-        TRACE_EVENT_BEGIN0(
-            TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"),
-            "LayerTreeHostCommon::ComputeVisibleRectsWithPropertyTrees");
-      }
-
       PropertyTreeBuilder::BuildPropertyTrees(
           inputs->root_layer, inputs->page_scale_layer,
           inputs->inner_viewport_scroll_layer,
@@ -532,18 +517,9 @@
       // updates when they are built on compositor thread.
       inputs->property_trees->transform_tree
           .set_source_to_parent_updates_allowed(false);
-      if (should_measure_property_tree_performance) {
-        TRACE_EVENT_END0(
-            TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"),
-            "LayerTreeHostCommon::ComputeVisibleRectsWithPropertyTrees");
-      }
-
       break;
     }
     case DONT_BUILD_PROPERTY_TREES: {
-      TRACE_EVENT0(
-          TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"),
-          "LayerTreeHostCommon::ComputeJustVisibleRectsWithPropertyTrees");
       // Since page scale and elastic overscroll are SyncedProperties, changes
       // on the active tree immediately affect the pending tree, so instead of
       // trying to update property trees whenever these values change, we
@@ -605,11 +581,6 @@
     }
   }
 
-  if (should_measure_property_tree_performance) {
-    TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"),
-                       "LayerTreeHostCommon::CalculateDrawProperties");
-  }
-
   {
     TRACE_EVENT0("cc", "draw_property_utils::FindLayersThatNeedUpdates");
     draw_property_utils::FindLayersThatNeedUpdates(
@@ -632,11 +603,6 @@
         inputs->render_surface_list, inputs->max_texture_size);
   }
 
-  if (should_measure_property_tree_performance) {
-    TRACE_EVENT_END0(TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"),
-                     "LayerTreeHostCommon::CalculateDrawProperties");
-  }
-
   // A root layer render_surface should always exist after
   // CalculateDrawProperties.
   DCHECK(inputs->property_trees->effect_tree.GetRenderSurface(
@@ -665,48 +631,6 @@
 void LayerTreeHostCommon::CalculateDrawProperties(
     CalcDrawPropsImplInputs* inputs) {
   CalculateDrawPropertiesInternal(inputs, DONT_BUILD_PROPERTY_TREES);
-
-  if (CdpPerfTracingEnabled()) {
-    LayerTreeImpl* layer_tree_impl = inputs->root_layer->layer_tree_impl();
-    if (layer_tree_impl->IsPendingTree() &&
-        layer_tree_impl->is_first_frame_after_commit()) {
-      LayerImpl* active_tree_root =
-          layer_tree_impl->FindActiveTreeLayerById(inputs->root_layer->id());
-      float jitter = 0.f;
-      if (active_tree_root) {
-        int last_scrolled_node_index =
-            active_tree_root->layer_tree_impl()->LastScrolledScrollNodeIndex();
-        if (last_scrolled_node_index != ScrollTree::kInvalidNodeId) {
-          std::unordered_set<int> jitter_nodes;
-          for (auto* layer : *layer_tree_impl) {
-            // Layers that have the same scroll tree index jitter together. So,
-            // it is enough to calculate jitter on one of these layers. So,
-            // after we find a jittering layer, we need not consider other
-            // layers with the same scroll tree index.
-            int scroll_tree_index = layer->scroll_tree_index();
-            if (last_scrolled_node_index <= scroll_tree_index &&
-                jitter_nodes.find(scroll_tree_index) == jitter_nodes.end()) {
-              float layer_jitter = CalculateLayerJitter(layer);
-              if (layer_jitter > 0.f) {
-                jitter_nodes.insert(layer->scroll_tree_index());
-                jitter += layer_jitter;
-              }
-            }
-          }
-        }
-      }
-      TRACE_EVENT_ASYNC_BEGIN1(
-          "cdp.perf", "jitter",
-          inputs->root_layer->layer_tree_impl()->source_frame_number(), "value",
-          jitter);
-      inputs->root_layer->layer_tree_impl()->set_is_first_frame_after_commit(
-          false);
-      TRACE_EVENT_ASYNC_END1(
-          "cdp.perf", "jitter",
-          inputs->root_layer->layer_tree_impl()->source_frame_number(), "value",
-          jitter);
-    }
-  }
 }
 
 void LayerTreeHostCommon::CalculateDrawPropertiesForTesting(
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 0d1659f1..d35b911 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -4746,7 +4746,8 @@
                                 : ElementId();
 
   active_tree_->property_trees()->scroll_tree.CollectScrollDeltas(
-      scroll_info, inner_viewport_scroll_element_id);
+      scroll_info, inner_viewport_scroll_element_id,
+      active_tree_->settings().commit_fractional_scroll_deltas);
 }
 
 void LayerTreeHostImpl::CollectScrollbarUpdates(
diff --git a/cc/trees/layer_tree_settings.h b/cc/trees/layer_tree_settings.h
index 389e25b..7f074dd9 100644
--- a/cc/trees/layer_tree_settings.h
+++ b/cc/trees/layer_tree_settings.h
@@ -165,6 +165,12 @@
   // DidReceiveCompositorFrameAck, used by the Compositor but not the
   // LayerTreeView.
   bool send_compositor_frame_ack = true;
+
+  // When false, scroll deltas accumulated on the impl thread are rounded to
+  // integer values when sent to Blink on commit. This flag should eventually
+  // go away and CC should send Blink fractional values:
+  // https://crbug.com/414283.
+  bool commit_fractional_scroll_deltas = false;
 };
 
 }  // namespace cc
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index 13e7b3e0..84cdffaaf 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -1472,10 +1472,17 @@
 }
 
 gfx::ScrollOffset ScrollTree::PullDeltaForMainThread(
-    SyncedScrollOffset* scroll_offset) {
+    SyncedScrollOffset* scroll_offset,
+    bool use_fractional_deltas) {
   DCHECK(property_trees()->is_active);
+
+  // Once this setting is enabled, all the complicated rounding logic below can
+  // go away.
+  if (use_fractional_deltas)
+    return scroll_offset->PullDeltaForMainThread();
+
   // TODO(flackr): We should pass the fractional scroll deltas when Blink fully
-  // supports fractional scrolls.
+  // supports fractional scrolls. crbug.com/414283.
   // TODO(flackr): We should ideally round the fractional scrolls in the same
   // direction as the scroll will be snapped but for common cases this is
   // equivalent to rounding to the nearest integer offset.
@@ -1495,13 +1502,13 @@
   return delta;
 }
 
-void ScrollTree::CollectScrollDeltas(
-    ScrollAndScaleSet* scroll_info,
-    ElementId inner_viewport_scroll_element_id) {
+void ScrollTree::CollectScrollDeltas(ScrollAndScaleSet* scroll_info,
+                                     ElementId inner_viewport_scroll_element_id,
+                                     bool use_fractional_deltas) {
   DCHECK(!property_trees()->is_main_thread);
   for (auto map_entry : synced_scroll_offset_map_) {
     gfx::ScrollOffset scroll_delta =
-        PullDeltaForMainThread(map_entry.second.get());
+        PullDeltaForMainThread(map_entry.second.get(), use_fractional_deltas);
 
     ElementId id = map_entry.first;
 
@@ -1521,8 +1528,11 @@
 }
 
 void ScrollTree::CollectScrollDeltasForTesting() {
+  LayerTreeSettings settings;
+  bool use_fractional_deltas = settings.commit_fractional_scroll_deltas;
+
   for (auto map_entry : synced_scroll_offset_map_) {
-    PullDeltaForMainThread(map_entry.second.get());
+    PullDeltaForMainThread(map_entry.second.get(), use_fractional_deltas);
   }
 }
 
diff --git a/cc/trees/property_tree.h b/cc/trees/property_tree.h
index 85abe497..4f704bff 100644
--- a/cc/trees/property_tree.h
+++ b/cc/trees/property_tree.h
@@ -437,7 +437,8 @@
   // reported to the main thread during the main frame. As such, should only be
   // called on the impl thread side PropertyTrees.
   void CollectScrollDeltas(ScrollAndScaleSet* scroll_info,
-                           ElementId inner_viewport_scroll_element_id);
+                           ElementId inner_viewport_scroll_element_id,
+                           bool use_fractional_deltas);
 
   // Applies deltas sent in the previous main frame onto the impl thread state.
   // Should only be called on the impl thread side PropertyTrees.
@@ -500,7 +501,8 @@
   SyncedScrollOffsetMap synced_scroll_offset_map_;
 
   SyncedScrollOffset* GetOrCreateSyncedScrollOffset(ElementId id);
-  gfx::ScrollOffset PullDeltaForMainThread(SyncedScrollOffset* scroll_offset);
+  gfx::ScrollOffset PullDeltaForMainThread(SyncedScrollOffset* scroll_offset,
+                                           bool use_fractional_deltas);
 };
 
 struct AnimationScaleData {
diff --git a/cc/trees/tree_synchronizer_unittest.cc b/cc/trees/tree_synchronizer_unittest.cc
index c49b0f0..547dccae 100644
--- a/cc/trees/tree_synchronizer_unittest.cc
+++ b/cc/trees/tree_synchronizer_unittest.cc
@@ -131,13 +131,44 @@
 }
 
 class TreeSynchronizerTest : public testing::Test {
+ public:
+  void ResetLayerTreeHost(const LayerTreeSettings& settings) {
+    host_ = FakeLayerTreeHost::Create(&client_, &task_graph_runner_,
+                                      animation_host_.get(), settings);
+    host_->host_impl()->CreatePendingTree();
+  }
+
+  scoped_refptr<Layer> SetupScrollLayer() {
+    FakeLayerTreeHostImpl* host_impl = host_->host_impl();
+
+    scoped_refptr<Layer> layer_tree_root = Layer::Create();
+    scoped_refptr<Layer> scroll_layer = Layer::Create();
+
+    ElementId scroll_element_id = ElementId(5);
+    scroll_layer->SetElementId(scroll_element_id);
+
+    layer_tree_root->AddChild(scroll_layer);
+
+    scroll_layer->SetScrollable(gfx::Size(1, 1));
+    scroll_layer->SetBounds(gfx::Size(10, 10));
+
+    host_->SetRootLayer(layer_tree_root);
+    host_->BuildPropertyTreesForTesting();
+    host_->CommitAndCreatePendingTree();
+    host_impl->ActivateSyncTree();
+
+    ExpectTreesAreIdentical(layer_tree_root.get(),
+                            host_impl->active_tree()->root_layer_for_testing(),
+                            host_impl->active_tree());
+
+    return scroll_layer;
+  }
+
  protected:
   TreeSynchronizerTest()
-      : animation_host_(AnimationHost::CreateForTesting(ThreadInstance::MAIN)),
-        host_(FakeLayerTreeHost::Create(&client_,
-                                        &task_graph_runner_,
-                                        animation_host_.get())) {
-    host_->host_impl()->CreatePendingTree();
+      : animation_host_(AnimationHost::CreateForTesting(ThreadInstance::MAIN)) {
+    LayerTreeSettings settings;
+    ResetLayerTreeHost(settings);
   }
 
   FakeLayerTreeHostClient client_;
@@ -658,7 +689,8 @@
 
   // Pull ScrollOffset delta for main thread, and change offset on main thread
   std::unique_ptr<ScrollAndScaleSet> scroll_info(new ScrollAndScaleSet());
-  scroll_tree.CollectScrollDeltas(scroll_info.get(), ElementId());
+  scroll_tree.CollectScrollDeltas(scroll_info.get(), ElementId(),
+                                  settings.commit_fractional_scroll_deltas);
   host_->proxy()->SetNeedsCommit();
   host_->ApplyScrollAndScale(scroll_info.get());
   EXPECT_EQ(gfx::ScrollOffset(20, 30), scroll_layer->CurrentScrollOffset());
@@ -741,5 +773,53 @@
           transform_layer->transform_tree_index(), host_impl->active_tree()));
 }
 
+TEST_F(TreeSynchronizerTest, RoundedScrollDeltasOnCommit) {
+  LayerTreeSettings settings;
+  settings.commit_fractional_scroll_deltas = false;
+  ResetLayerTreeHost(settings);
+
+  host_->InitializeSingleThreaded(&single_thread_client_,
+                                  base::ThreadTaskRunnerHandle::Get());
+
+  scoped_refptr<Layer> scroll_layer = SetupScrollLayer();
+
+  // Scroll the layer by a fractional amount.
+  FakeLayerTreeHostImpl* host_impl = host_->host_impl();
+  LayerImpl* scroll_layer_impl =
+      host_impl->active_tree()->LayerById(scroll_layer->id());
+  scroll_layer_impl->ScrollBy(gfx::Vector2dF(0, 1.75f));
+
+  // When we collect the scroll deltas, we should have truncated the fractional
+  // part because the commit_fractional_scroll_deltas setting is enabled.
+  std::unique_ptr<ScrollAndScaleSet> scroll_info =
+      host_impl->ProcessScrollDeltas();
+  ASSERT_EQ(1u, scroll_info->scrolls.size());
+  EXPECT_EQ(2.f, scroll_info->scrolls[0].scroll_delta.y());
+}
+
+TEST_F(TreeSynchronizerTest, PreserveFractionalScrollDeltasOnCommit) {
+  LayerTreeSettings settings;
+  settings.commit_fractional_scroll_deltas = true;
+  ResetLayerTreeHost(settings);
+
+  host_->InitializeSingleThreaded(&single_thread_client_,
+                                  base::ThreadTaskRunnerHandle::Get());
+
+  scoped_refptr<Layer> scroll_layer = SetupScrollLayer();
+
+  // Scroll the layer by a fractional amount.
+  FakeLayerTreeHostImpl* host_impl = host_->host_impl();
+  LayerImpl* scroll_layer_impl =
+      host_impl->active_tree()->LayerById(scroll_layer->id());
+  scroll_layer_impl->ScrollBy(gfx::Vector2dF(0, 1.75f));
+
+  // When we collect the scroll deltas, we should keep the fractional part
+  // because the commit_fractional_scroll_deltas setting is disabled.
+  std::unique_ptr<ScrollAndScaleSet> scroll_info =
+      host_impl->ProcessScrollDeltas();
+  ASSERT_EQ(1u, scroll_info->scrolls.size());
+  EXPECT_EQ(1.75f, scroll_info->scrolls[0].scroll_delta.y());
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/chrome/VERSION b/chrome/VERSION
index fba884e..459ef3c 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=72
 MINOR=0
-BUILD=3592
+BUILD=3593
 PATCH=0
diff --git a/chrome/android/java/res/color/flush_footer_button_color.xml b/chrome/android/java/res/color/flush_footer_button_color.xml
deleted file mode 100644
index 1af3db42..0000000
--- a/chrome/android/java/res/color/flush_footer_button_color.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2018 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true" android:color="@color/modern_grey_300" />
-    <item android:color="@color/modern_grey_100" />
-</selector>
diff --git a/ui/android/java/res/drawable-v21/button_borderless_compat.xml b/chrome/android/java/res/drawable-v21/button_borderless_compat.xml
similarity index 100%
rename from ui/android/java/res/drawable-v21/button_borderless_compat.xml
rename to chrome/android/java/res/drawable-v21/button_borderless_compat.xml
diff --git a/ui/android/java/res/drawable/button_compat_shape.xml b/chrome/android/java/res/drawable/button_compat_shape.xml
similarity index 87%
rename from ui/android/java/res/drawable/button_compat_shape.xml
rename to chrome/android/java/res/drawable/button_compat_shape.xml
index ca6dc64..e996d87 100644
--- a/ui/android/java/res/drawable/button_compat_shape.xml
+++ b/chrome/android/java/res/drawable/button_compat_shape.xml
@@ -4,6 +4,6 @@
      found in the LICENSE file. -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="#fff" />
+    <solid android:color="@android:color/white" />
     <corners android:radius="@dimen/button_compat_corner_radius" />
 </shape>
diff --git a/chrome/android/java/res/drawable/flush_footer_button.xml b/chrome/android/java/res/drawable/flush_footer_button.xml
deleted file mode 100644
index 1630b0ed..0000000
--- a/chrome/android/java/res/drawable/flush_footer_button.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2016 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file. -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-    <solid android:color="@color/flush_footer_button_color" />
-    <stroke android:width="1dp" android:color="@color/hairline_stroke_color"/>
-</shape>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/accessibility_tab_switcher_list_item.xml b/chrome/android/java/res/layout/accessibility_tab_switcher_list_item.xml
index 62e6b35..b31d17dd 100644
--- a/chrome/android/java/res/layout/accessibility_tab_switcher_list_item.xml
+++ b/chrome/android/java/res/layout/accessibility_tab_switcher_list_item.xml
@@ -35,7 +35,7 @@
         android:layout_height="match_parent"
         android:visibility="invisible" >
 
-        <Button
+        <org.chromium.ui.widget.ButtonCompat
             android:id="@+id/undo_button"
             android:layout_height="match_parent"
             android:layout_width="0dp"
@@ -45,8 +45,7 @@
             android:textAlignment="viewStart"
             android:text="@string/accessibility_tab_switcher_undo_tab_closed"
             android:contentDescription="@string/accessibility_tab_switcher_undo_tab_closed"
-            android:textAppearance="@style/BlueButtonText2"
-            android:background="?attr/selectableItemBackground" />
+            style="@style/TextButton" />
     </LinearLayout>
 
 </org.chromium.chrome.browser.widget.accessibility.AccessibilityTabModelListItem>
diff --git a/chrome/android/java/res/layout/account_signin_view.xml b/chrome/android/java/res/layout/account_signin_view.xml
index 7e3632a8..c6e80c5 100644
--- a/chrome/android/java/res/layout/account_signin_view.xml
+++ b/chrome/android/java/res/layout/account_signin_view.xml
@@ -6,7 +6,6 @@
 -->
 <org.chromium.chrome.browser.signin.AccountSigninView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
@@ -184,12 +183,13 @@
         android:orientation="horizontal" >
 
         <!--suppress ButtonStyle -->
-        <Button
+        <org.chromium.ui.widget.ButtonCompat
             android:id="@+id/negative_button"
             android:layout_width="wrap_content"
             android:layout_height="36dp"
             android:paddingStart="@dimen/fre_button_padding"
-            android:paddingEnd="@dimen/fre_button_padding" />
+            android:paddingEnd="@dimen/fre_button_padding"
+            style="@style/TextButton" />
 
         <View
             android:layout_width="0dp"
@@ -204,17 +204,16 @@
             android:layout_height="36dp"
             android:paddingStart="@dimen/fre_button_padding"
             android:paddingEnd="@dimen/fre_button_padding"
-            android:textAppearance="@style/WhiteButtonText"
-            app:buttonColor="@color/light_active_color"
-            app:buttonRaised="false"/>
+            style="@style/FilledButton.Flat" />
 
-        <Button
+        <org.chromium.ui.widget.ButtonCompat
             android:id="@+id/more_button"
             android:layout_width="wrap_content"
             android:layout_height="36dp"
             android:drawableEnd="@drawable/down_arrow"
             android:drawablePadding="8dp"
-            android:visibility="gone"/>
+            android:visibility="gone"
+            style="@style/TextButton" />
 
         <View
             android:id="@+id/positive_button_end_padding"
diff --git a/chrome/android/java/res/layout/button_preference_button.xml b/chrome/android/java/res/layout/button_preference_button.xml
index 46284baa..29a5807 100644
--- a/chrome/android/java/res/layout/button_preference_button.xml
+++ b/chrome/android/java/res/layout/button_preference_button.xml
@@ -8,11 +8,9 @@
      See crbug.com/674736 -->
 <org.chromium.ui.widget.ButtonCompat
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/button_preference"
     android:layout_height="wrap_content"
     android:layout_width="wrap_content"
     android:minHeight="40dp"
     android:focusable="false"
-    android:textAppearance="@style/WhiteButtonText"
-    app:buttonColor="@color/pref_accent_color" />
+    style="@style/FilledButton" />
diff --git a/chrome/android/java/res/layout/clear_browsing_data_tab_content.xml b/chrome/android/java/res/layout/clear_browsing_data_tab_content.xml
index afc065d..2d46965 100644
--- a/chrome/android/java/res/layout/clear_browsing_data_tab_content.xml
+++ b/chrome/android/java/res/layout/clear_browsing_data_tab_content.xml
@@ -17,16 +17,16 @@
         android:layout_weight="1"
         android:layout_height="0dp"/>
 
-    <Button
+    <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/clear_button"
         android:layout_gravity="end"
-        style="?android:attr/buttonBarButtonStyle"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:padding="8dp"
         android:minHeight="48dp"
         android:layout_marginEnd="@dimen/clear_browsing_data_button_margin"
         android:focusable="true"
-        android:text="@string/clear_data_delete" />
+        android:text="@string/clear_data_delete"
+        style="@style/TextButton" />
 
 </LinearLayout>
diff --git a/chrome/android/java/res/layout/consent_bump_more_options_view.xml b/chrome/android/java/res/layout/consent_bump_more_options_view.xml
index 5bc2525b..ece7170 100644
--- a/chrome/android/java/res/layout/consent_bump_more_options_view.xml
+++ b/chrome/android/java/res/layout/consent_bump_more_options_view.xml
@@ -83,13 +83,14 @@
         android:orientation="horizontal"
         android:padding="16dp">
 
-        <Button
+        <org.chromium.ui.widget.ButtonCompat
             android:id="@+id/back_button"
             android:layout_width="wrap_content"
             android:layout_height="36dp"
             android:paddingStart="@dimen/fre_button_padding"
             android:paddingEnd="@dimen/fre_button_padding"
-            android:text="@string/back"/>
+            android:text="@string/back"
+            style="@style/TextButton" />
         <View
             android:layout_width="0dp"
             android:layout_height="0dp"
@@ -102,8 +103,6 @@
             android:paddingStart="@dimen/fre_button_padding"
             android:paddingEnd="@dimen/fre_button_padding"
             android:text="@string/ok"
-            android:textAppearance="@style/WhiteButtonText"
-            app:buttonColor="@color/light_active_color"
-            app:buttonRaised="false"/>
+            style="@style/FilledButton.Flat" />
     </LinearLayout>
 </LinearLayout>
diff --git a/chrome/android/java/res/layout/contacts_picker_toolbar.xml b/chrome/android/java/res/layout/contacts_picker_toolbar.xml
index 9ffc881..c82d23e 100644
--- a/chrome/android/java/res/layout/contacts_picker_toolbar.xml
+++ b/chrome/android/java/res/layout/contacts_picker_toolbar.xml
@@ -37,12 +37,12 @@
             android:background="@drawable/ic_search"
             android:contentDescription="@string/search" />
 
-        <Button
+        <org.chromium.ui.widget.ButtonCompat
             android:id="@+id/done"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:enabled="false"
             android:text="@string/done"
-            style="@style/MainButtonStyle" />
+            style="@style/TextButton" />
     </LinearLayout>
 </org.chromium.chrome.browser.contacts_picker.ContactsPickerToolbar>
diff --git a/chrome/android/java/res/layout/content_suggestions_action_card_modern.xml b/chrome/android/java/res/layout/content_suggestions_action_card_modern.xml
index 1fa764d..3d0784f 100644
--- a/chrome/android/java/res/layout/content_suggestions_action_card_modern.xml
+++ b/chrome/android/java/res/layout/content_suggestions_action_card_modern.xml
@@ -6,7 +6,7 @@
     android:layout_height="wrap_content"
     android:layout_marginTop="@dimen/content_suggestions_action_modern_margin_top" >
 
-    <Button
+    <org.chromium.ui.widget.ButtonCompat
         style="@style/SuggestionCardAction"
         android:id="@+id/action_button"
         android:layout_width="wrap_content"
diff --git a/chrome/android/java/res/layout/content_suggestions_all_dismissed_card_modern.xml b/chrome/android/java/res/layout/content_suggestions_all_dismissed_card_modern.xml
index d5d56dc6..d7c3622 100644
--- a/chrome/android/java/res/layout/content_suggestions_all_dismissed_card_modern.xml
+++ b/chrome/android/java/res/layout/content_suggestions_all_dismissed_card_modern.xml
@@ -49,7 +49,7 @@
         android:textAppearance="@style/BlackBody"
         app:leading="20dp" />
 
-    <Button
+    <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/action_button"
         style="@style/SuggestionCardAction"
         android:layout_width="wrap_content"
diff --git a/chrome/android/java/res/layout/content_suggestions_status_card_modern.xml b/chrome/android/java/res/layout/content_suggestions_status_card_modern.xml
index 2d29d8e2..e54b48cd 100644
--- a/chrome/android/java/res/layout/content_suggestions_status_card_modern.xml
+++ b/chrome/android/java/res/layout/content_suggestions_status_card_modern.xml
@@ -28,7 +28,7 @@
         android:layout_height="wrap_content"
         tools:text="Status card body can be over multiple lines and quite wide" />
 
-    <Button
+    <org.chromium.ui.widget.ButtonCompat
         style="@style/SuggestionCardAction"
         android:id="@+id/status_action_button"
         android:layout_width="wrap_content"
diff --git a/chrome/android/java/res/layout/contextual_search_promo_view.xml b/chrome/android/java/res/layout/contextual_search_promo_view.xml
index 3bdd62f..4406a03 100644
--- a/chrome/android/java/res/layout/contextual_search_promo_view.xml
+++ b/chrome/android/java/res/layout/contextual_search_promo_view.xml
@@ -5,7 +5,6 @@
 
 <RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/contextual_search_promo"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
@@ -41,10 +40,9 @@
         android:paddingStart="8dp"
         android:paddingEnd="8dp"
         android:text="@string/contextual_search_allow_button"
-        android:textAppearance="@style/WhiteButtonText"
-        app:buttonColor="@color/light_active_color" />
+        style="@style/FilledButton" />
 
-    <Button
+    <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/contextual_search_no_thanks_button"
         android:layout_below="@id/contextual_search_promo_text"
         android:layout_toStartOf="@id/contextual_search_allow_button"
@@ -56,7 +54,7 @@
         android:paddingStart="8dp"
         android:paddingEnd="8dp"
         android:text="@string/contextual_search_no_thanks_button"
-        style="@style/MainButtonStyle" />
+        style="@style/TextButton" />
 
     <View
         android:layout_below="@id/contextual_search_allow_button"
diff --git a/chrome/android/java/res/layout/data_reduction_stats_layout.xml b/chrome/android/java/res/layout/data_reduction_stats_layout.xml
index 0ef83a2..87b6e0c 100644
--- a/chrome/android/java/res/layout/data_reduction_stats_layout.xml
+++ b/chrome/android/java/res/layout/data_reduction_stats_layout.xml
@@ -94,13 +94,14 @@
 
         <include layout="@layout/data_usage_breakdown" />
 
-        <Button
+        <org.chromium.ui.widget.ButtonCompat
             android:id="@+id/data_reduction_reset_statistics"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_gravity="center"
             android:layout_marginTop="24dp"
-            android:text="@string/data_reduction_usage_reset_statistics_button" />
+            android:text="@string/data_reduction_usage_reset_statistics_button"
+            style="@style/TextButton" />
 
     </LinearLayout>
 
diff --git a/chrome/android/java/res/layout/default_search_engine_first_run_fragment.xml b/chrome/android/java/res/layout/default_search_engine_first_run_fragment.xml
index dc06574..2158027 100644
--- a/chrome/android/java/res/layout/default_search_engine_first_run_fragment.xml
+++ b/chrome/android/java/res/layout/default_search_engine_first_run_fragment.xml
@@ -7,7 +7,6 @@
 <!-- Layout used to present a set of default search engines to the user. -->
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@color/signin_body_background"
@@ -78,8 +77,6 @@
         android:paddingStart="@dimen/fre_button_padding"
         android:paddingEnd="@dimen/fre_button_padding"
         android:text="@string/ok"
-        android:textAppearance="@style/WhiteButtonText"
-        app:buttonColor="@color/light_active_color"
-        app:buttonRaised="false" />
+        style="@style/FilledButton.Flat" />
 
 </LinearLayout>
diff --git a/chrome/android/java/res/layout/experimental_explore_sites_section.xml b/chrome/android/java/res/layout/experimental_explore_sites_section.xml
index 76586bb..caf602a 100644
--- a/chrome/android/java/res/layout/experimental_explore_sites_section.xml
+++ b/chrome/android/java/res/layout/experimental_explore_sites_section.xml
@@ -27,7 +27,7 @@
         android:paddingHorizontal="@dimen/experimental_explore_sites_padding"
         android:orientation="horizontal" />
 
-    <Button
+    <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/experimental_explore_sites_more_button"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -35,6 +35,7 @@
         android:layout_marginTop="@dimen/experimental_explore_sites_spacing"
         android:layout_marginHorizontal="@dimen/experimental_explore_sites_margin"
         android:layout_marginBottom="@dimen/experimental_explore_sites_spacing"
-        android:text="@string/ntp_explore_sites_more" />
+        android:text="@string/ntp_explore_sites_more"
+        style="@style/TextButton" />
 
 </LinearLayout>
diff --git a/chrome/android/java/res/layout/fre_data_reduction_proxy.xml b/chrome/android/java/res/layout/fre_data_reduction_proxy.xml
index 2a6449f..082857c 100644
--- a/chrome/android/java/res/layout/fre_data_reduction_proxy.xml
+++ b/chrome/android/java/res/layout/fre_data_reduction_proxy.xml
@@ -6,7 +6,6 @@
 -->
 <org.chromium.chrome.browser.firstrun.FirstRunView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
@@ -89,7 +88,5 @@
         android:paddingStart="@dimen/fre_button_padding"
         android:paddingEnd="@dimen/fre_button_padding"
         android:text="@string/next"
-        android:textAppearance="@style/WhiteButtonText"
-        app:buttonColor="@color/light_active_color"
-        app:buttonRaised="false"/>
+        style="@style/FilledButton.Flat" />
 </org.chromium.chrome.browser.firstrun.FirstRunView>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/fre_tosanduma.xml b/chrome/android/java/res/layout/fre_tosanduma.xml
index 30917c7..d6c30f0 100644
--- a/chrome/android/java/res/layout/fre_tosanduma.xml
+++ b/chrome/android/java/res/layout/fre_tosanduma.xml
@@ -6,7 +6,6 @@
 -->
 <org.chromium.chrome.browser.firstrun.FirstRunView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
@@ -87,9 +86,7 @@
         android:paddingStart="@dimen/fre_button_padding"
         android:paddingEnd="@dimen/fre_button_padding"
         android:text="@string/fre_accept_continue"
-        android:textAppearance="@style/WhiteButtonText"
-        app:buttonColor="@color/light_active_color"
-        app:buttonRaised="false"/>
+        style="@style/FilledButton.Flat" />
 
     <!-- Same location as the button; marginButtom is adjusted for the different size. -->
     <ProgressBar
diff --git a/chrome/android/java/res/layout/homepage_editor.xml b/chrome/android/java/res/layout/homepage_editor.xml
index 3dad103..c9c61f8 100644
--- a/chrome/android/java/res/layout/homepage_editor.xml
+++ b/chrome/android/java/res/layout/homepage_editor.xml
@@ -39,17 +39,17 @@
             <View style="@style/ButtonBarTopDivider" />
 
             <LinearLayout style="@style/ButtonBar" >
-                <Button
+                <org.chromium.ui.widget.ButtonCompat
                     android:id="@+id/homepage_reset"
                     style="@style/ButtonBarButton"
                     android:text="@string/reset" />
 
-                <Button
+                <org.chromium.ui.widget.ButtonCompat
                     android:id="@+id/homepage_cancel"
                     style="@style/ButtonBarButton"
                     android:text="@string/cancel" />
 
-                <Button
+                <org.chromium.ui.widget.ButtonCompat
                     android:id="@+id/homepage_save"
                     style="@style/ButtonBarButton"
                     android:text="@string/save" />
diff --git a/chrome/android/java/res/layout/item_chooser_dialog.xml b/chrome/android/java/res/layout/item_chooser_dialog.xml
index c0496a6..0a6d21e 100644
--- a/chrome/android/java/res/layout/item_chooser_dialog.xml
+++ b/chrome/android/java/res/layout/item_chooser_dialog.xml
@@ -5,7 +5,6 @@
  -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:orientation="vertical"
@@ -78,6 +77,5 @@
         android:layout_marginEnd="12dp"
         android:paddingStart="16dp"
         android:paddingEnd="16dp"
-        android:textAppearance="@style/WhiteButtonText"
-        app:buttonColor="@color/pref_accent_color" />
+        style="@style/FilledButton" />
 </LinearLayout>
diff --git a/chrome/android/java/res/layout/keyboard_accessory_action.xml b/chrome/android/java/res/layout/keyboard_accessory_action.xml
index 3635972a..34a7e59 100644
--- a/chrome/android/java/res/layout/keyboard_accessory_action.xml
+++ b/chrome/android/java/res/layout/keyboard_accessory_action.xml
@@ -5,7 +5,6 @@
 
 <org.chromium.ui.widget.ButtonCompat
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:gravity="center"
     android:layout_height="@dimen/keyboard_accessory_action_height"
     android:layout_width="wrap_content"
@@ -18,6 +17,4 @@
     android:layout_marginBottom="@dimen/keyboard_accessory_half_padding"
     android:layout_marginTop="@dimen/keyboard_accessory_half_padding"
     android:textAlignment="center"
-    android:textAppearance="@style/WhiteButtonText"
-    app:buttonColor="@color/light_active_color"
-    app:buttonRaised="false"/>
+    style="@style/FilledButton.Flat" />
diff --git a/chrome/android/java/res/layout/location_bar_status.xml b/chrome/android/java/res/layout/location_bar_status.xml
index 173fe7c..2ca2613 100644
--- a/chrome/android/java/res/layout/location_bar_status.xml
+++ b/chrome/android/java/res/layout/location_bar_status.xml
@@ -13,6 +13,8 @@
         android:gravity="center_vertical"
         android:textAlignment="viewStart"
         android:textAppearance="@style/TextAppearance.OmniboxVerboseStatus"
+        android:ellipsize="end"
+        android:maxLines="1"
         android:visibility="gone" />
 
     <!-- Separator is going to be shown or hidden together with the status defined above. -->
@@ -26,7 +28,7 @@
     <!-- This space follows the verbose status and compliments the space that follows
          the status. -->
     <Space android:id="@+id/location_bar_verbose_status_extra_space"
-        android:layout_width="8dp"
+        android:layout_width="@dimen/location_bar_status_separator_spacer"
         android:layout_height="match_parent"
         android:visibility="gone" />
 
diff --git a/chrome/android/java/res/layout/page_info.xml b/chrome/android/java/res/layout/page_info.xml
index 594c84e..bb78e48 100644
--- a/chrome/android/java/res/layout/page_info.xml
+++ b/chrome/android/java/res/layout/page_info.xml
@@ -92,9 +92,8 @@
         android:paddingEnd="@dimen/page_info_popup_button_padding_sides"
         android:paddingStart="@dimen/page_info_popup_button_padding_sides"
         android:text="@string/page_info_instant_app_button"
-        android:textAppearance="@style/WhiteButtonText"
-        app:buttonRaised="false"
-        app:buttonColor="@color/app_banner_install_button_bg" />
+        app:buttonColor="@color/app_banner_install_button_bg"
+        style="@style/FilledButton" />
 
     <LinearLayout
         android:id="@+id/page_info_permissions_list"
@@ -105,7 +104,7 @@
         android:paddingEnd="@dimen/page_info_popup_padding_sides" >
     </LinearLayout>
 
-    <Button
+    <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/page_info_site_settings_button"
         android:layout_width="wrap_content"
         android:layout_height="@dimen/page_info_popup_button_height"
@@ -115,9 +114,10 @@
         android:layout_marginTop="8dp"
         android:paddingEnd="@dimen/page_info_popup_button_padding_sides"
         android:paddingStart="@dimen/page_info_popup_button_padding_sides"
-        android:text="@string/page_info_site_settings_button" />
+        android:text="@string/page_info_site_settings_button"
+        style="@style/TextButton" />
 
-    <Button
+    <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/page_info_open_online_button"
         android:layout_width="wrap_content"
         android:layout_height="@dimen/page_info_popup_button_height"
@@ -127,5 +127,6 @@
         android:layout_marginTop="8dp"
         android:paddingEnd="@dimen/page_info_popup_button_padding_sides"
         android:paddingStart="@dimen/page_info_popup_button_padding_sides"
-        android:text="@string/page_info_open_online_button" />
+        android:text="@string/page_info_open_online_button"
+        style="@style/TextButton" />
 </LinearLayout>
diff --git a/chrome/android/java/res/layout/payment_request_bottom_bar.xml b/chrome/android/java/res/layout/payment_request_bottom_bar.xml
index 152292d2..a14323948 100644
--- a/chrome/android/java/res/layout/payment_request_bottom_bar.xml
+++ b/chrome/android/java/res/layout/payment_request_bottom_bar.xml
@@ -6,7 +6,6 @@
 <!-- Sits at the bottom of the payment request UI. -->
 <org.chromium.chrome.browser.payments.ui.PaymentRequestBottomBar
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/bottom_bar"
     android:layout_height="wrap_content"
@@ -34,18 +33,17 @@
         android:layout_width="0dp"
         android:layout_height="0dp" />
 
-    <Button
+    <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/button_secondary"
         android:layout_width="wrap_content"
         android:layout_height="36dp"
-        android:text="@string/payments_edit_button" />
+        android:text="@string/payments_edit_button"
+        style="@style/TextButton" />
 
     <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/button_primary"
         android:layout_width="wrap_content"
         android:layout_height="36dp"
         android:text="@string/payments_pay_button"
-        android:textAppearance="@style/WhiteButtonText"
-        app:buttonColor="@color/light_active_color"
-        app:buttonRaised="false" />
+        style="@style/FilledButton.Flat" />
 </org.chromium.chrome.browser.payments.ui.PaymentRequestBottomBar>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/payment_request_error.xml b/chrome/android/java/res/layout/payment_request_error.xml
index e5720e0..19a7a92 100644
--- a/chrome/android/java/res/layout/payment_request_error.xml
+++ b/chrome/android/java/res/layout/payment_request_error.xml
@@ -29,18 +29,17 @@
         android:layout_marginEnd="@dimen/editor_dialog_section_large_spacing"
         android:layout_marginBottom="@dimen/payments_section_largest_spacing"
         android:text="@string/payments_error_message"
-        android:textColor="@color/error_text_color"
-        android:textSize="16sp" />
+        android:textAppearance="@style/TextAppearance.PaymentRequestErrorText" />
 
     <!-- Dismisses the dialog. -->
-    <Button
+    <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/ok_button"
-        android:background="?android:attr/selectableItemBackground"
         android:text="@string/ok"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="end"
         android:layout_marginEnd="@dimen/editor_dialog_section_small_spacing"
-        android:layout_marginBottom="@dimen/editor_dialog_section_small_spacing" />
+        android:layout_marginBottom="@dimen/editor_dialog_section_small_spacing"
+        style="@style/TextButton" />
 
 </org.chromium.chrome.browser.payments.ui.PaymentRequestUiErrorView>
diff --git a/chrome/android/java/res/layout/personalized_signin_promo_view_body.xml b/chrome/android/java/res/layout/personalized_signin_promo_view_body.xml
index cd689ed..2969c66 100644
--- a/chrome/android/java/res/layout/personalized_signin_promo_view_body.xml
+++ b/chrome/android/java/res/layout/personalized_signin_promo_view_body.xml
@@ -21,7 +21,6 @@
 
     <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/signin_promo_signin_button"
-        style="@style/FilledButton.Flat"
         android:layout_width="match_parent"
         android:layout_height="36dp"
         android:layout_marginBottom="6dp"
@@ -30,9 +29,10 @@
         android:layout_marginTop="6dp"
         android:ellipsize="end"
         android:singleLine="true"
-        android:text="@string/signin_promo_continue_as"/>
+        android:text="@string/signin_promo_continue_as"
+        style="@style/FilledButton.Flat" />
 
-    <Button
+    <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/signin_promo_choose_account_button"
         style="@style/SigninButtonBorderlessRegular"
         android:layout_width="match_parent"
diff --git a/chrome/android/java/res/layout/photo_picker_toolbar.xml b/chrome/android/java/res/layout/photo_picker_toolbar.xml
index 207bcdc..c04d8c9 100644
--- a/chrome/android/java/res/layout/photo_picker_toolbar.xml
+++ b/chrome/android/java/res/layout/photo_picker_toolbar.xml
@@ -7,18 +7,17 @@
 
 <org.chromium.chrome.browser.photo_picker.PhotoPickerToolbar
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:paddingStart="0dp"
     android:paddingEnd="0dp">
-    <Button
+    <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/done"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="end"
         android:enabled="false"
         android:text="@string/done"
-        style="@style/MainButtonStyle" />
+        style="@style/TextButton" />
 </org.chromium.chrome.browser.photo_picker.PhotoPickerToolbar>
 
diff --git a/chrome/android/java/res/layout/sad_tab.xml b/chrome/android/java/res/layout/sad_tab.xml
index fe6cdd0..9fa91538 100644
--- a/chrome/android/java/res/layout/sad_tab.xml
+++ b/chrome/android/java/res/layout/sad_tab.xml
@@ -8,7 +8,6 @@
 
 <org.chromium.chrome.browser.tab.SadTabView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     tools:ignore="Overdraw"
     android:background="#f7f7f7"
@@ -72,9 +71,8 @@
             android:id="@+id/sad_tab_button"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:textAppearance="@style/WhiteButtonText"
             android:minWidth="222dp"
-            app:buttonColor="@color/light_active_color"
-            android:layout_gravity="end" />
+            android:layout_gravity="end"
+            style="@style/FilledButton" />
     </LinearLayout>
 </org.chromium.chrome.browser.tab.SadTabView>
diff --git a/chrome/android/java/res/layout/signin_view.xml b/chrome/android/java/res/layout/signin_view.xml
index f8e9982c..aed47c1 100644
--- a/chrome/android/java/res/layout/signin_view.xml
+++ b/chrome/android/java/res/layout/signin_view.xml
@@ -202,13 +202,14 @@
         android:layout_height="wrap_content"
         android:orientation="horizontal"
         android:padding="16dp">
-        <Button
+        <org.chromium.ui.widget.ButtonCompat
             android:id="@+id/negative_button"
             android:layout_width="wrap_content"
             android:layout_height="36dp"
             android:paddingStart="@dimen/fre_button_padding"
             android:paddingEnd="@dimen/fre_button_padding"
-            tools:text="@string/no_thanks"/>
+            tools:text="@string/no_thanks"
+            style="@style/TextButton" />
         <View
             android:layout_width="0dp"
             android:layout_height="0dp"
@@ -220,18 +221,17 @@
             android:layout_height="36dp"
             android:paddingStart="@dimen/fre_button_padding"
             android:paddingEnd="@dimen/fre_button_padding"
-            android:textAppearance="@style/WhiteButtonText"
-            app:buttonColor="@color/light_active_color"
-            app:buttonRaised="false"
-            tools:text="@string/signin_accept_button"/>
-        <Button
+            tools:text="@string/signin_accept_button"
+            style="@style/FilledButton.Flat" />
+        <org.chromium.ui.widget.ButtonCompat
             android:id="@+id/more_button"
             android:layout_width="wrap_content"
             android:layout_height="36dp"
             android:drawableEnd="@drawable/down_arrow"
             android:drawablePadding="8dp"
             android:visibility="gone"
-            tools:text="@string/more"/>
+            tools:text="@string/more"
+            style="@style/TextButton" />
         <View
             android:id="@+id/positive_button_end_padding"
             android:layout_width="0dp"
diff --git a/chrome/android/java/res/layout/snackbar.xml b/chrome/android/java/res/layout/snackbar.xml
index e3a6426..f397a0a 100644
--- a/chrome/android/java/res/layout/snackbar.xml
+++ b/chrome/android/java/res/layout/snackbar.xml
@@ -70,13 +70,13 @@
             android:textAlignment="viewStart"
             android:textAppearance="@style/WhiteBody" />
 
-        <Button
-            style="@style/MainButtonStyle"
+        <org.chromium.ui.widget.ButtonCompat
             android:id="@+id/snackbar_button"
             android:layout_width="wrap_content"
             android:layout_height="@dimen/snackbar_min_height"
             android:layout_gravity="center_vertical"
             android:paddingEnd="24dp"
-            android:paddingStart="24dp" />
+            android:paddingStart="24dp"
+            style="@style/TextButton" />
     </LinearLayout>
 </RelativeLayout>
diff --git a/chrome/android/java/res/layout/storage_preferences.xml b/chrome/android/java/res/layout/storage_preferences.xml
index 1ae8046..03c3511 100644
--- a/chrome/android/java/res/layout/storage_preferences.xml
+++ b/chrome/android/java/res/layout/storage_preferences.xml
@@ -4,26 +4,44 @@
      found in the LICENSE file. -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     android:orientation="vertical"
     android:layout_height="match_parent"
     android:layout_width="match_parent">
 
-    <ListView android:id="@android:id/list"
+    <org.chromium.ui.widget.OptimizedFrameLayout
         android:layout_width="match_parent"
         android:layout_height="0dp"
-        android:layout_weight="1"/>
+        android:layout_weight="1">
 
-    <TextView android:id="@android:id/empty"
+        <ListView android:id="@android:id/list"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/toolbar_shadow_height"
+            android:layout_gravity="bottom"
+            android:background="@drawable/modern_toolbar_shadow"
+            android:scaleY="-1"
+            tools:ignore="ContentDescription" />
+    </org.chromium.ui.widget.OptimizedFrameLayout>
+
+    <TextView
+        android:id="@android:id/empty"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:padding="8dp"
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:gravity="center"
-        android:visibility="gone"/>
+        android:visibility="gone" />
 
-    <Button android:id="@+id/clear_button"
+    <org.chromium.ui.widget.ButtonCompat
+        android:id="@+id/clear_button"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp"
         android:text="@string/storage_clear_button_title"
-        style="@style/FlushFooterButton"/>
+        style="@style/TextButton" />
 </LinearLayout>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/sync_promo_view.xml b/chrome/android/java/res/layout/sync_promo_view.xml
index 50cfb8c..9133991 100644
--- a/chrome/android/java/res/layout/sync_promo_view.xml
+++ b/chrome/android/java/res/layout/sync_promo_view.xml
@@ -4,7 +4,6 @@
      found in the LICENSE file. -->
 <org.chromium.chrome.browser.signin.SyncPromoView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="wrap_content" >
 
@@ -44,8 +43,7 @@
                 android:id="@+id/sign_in"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:textAppearance="@style/WhiteButtonText"
-                app:buttonColor="@color/light_active_color" />
+                style="@style/FilledButton" />
         </LinearLayout>
 
     </LinearLayout>
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml
index 86e4165..2486261 100644
--- a/chrome/android/java/res/values-v17/styles.xml
+++ b/chrome/android/java/res/values-v17/styles.xml
@@ -30,7 +30,6 @@
         <!-- TODO(huayinz): Change this when the color for checkboxes are updated to modern. -->
         <item name="colorControlNormal">@color/light_normal_color</item>
         <item name="colorControlActivated">@color/light_active_color</item>
-        <item name="buttonStyle">@style/MainButtonStyle</item>
 
         <!-- Navigation Transitions, requires API level 21 -->
         <item name="android:windowAllowEnterTransitionOverlap" tools:targetApi="21">false</item>
@@ -47,10 +46,6 @@
         <item name="android:windowIsTranslucent">true</item>
     </style>
 
-    <style name="MainButtonStyle" parent="ButtonCompatBorderless">
-        <item name="android:textAppearance">@style/BlueButtonText2</item>
-    </style>
-
     <!-- Extend MainThemeBase rather than MainTheme to avoid values-v27 navigation bar colors from
          being applied -->
     <style name="SearchActivityTheme" parent="MainThemeBase">
@@ -225,20 +220,9 @@
         <item name="colorPrimaryDark">@android:color/black</item>
         <!-- Color of checkboxes, switches, buttons, etc. -->
         <item name="colorAccent">@color/pref_accent_color</item>
-        <!-- Button style -->
-        <item name="buttonStyle">@style/MainButtonStyle</item>
     </style>
     <style name="ThemeWithActionBar" parent="ThemeWithActionBarBase" />
 
-    <style name="FlushFooterButton">
-        <item name="android:background">@drawable/flush_footer_button</item>
-        <item name="android:textAppearance">@style/TextAppearance.FlushFooterButton</item>
-    </style>
-    <style name="TextAppearance.FlushFooterButton"
-        parent="android:TextAppearance.DeviceDefault.Widget.Button">
-        <item name="android:textColor">@color/light_normal_color</item>
-    </style>
-
     <!-- Manage Space Activity -->
     <style name="ManageSpaceTheme" parent="PreferencesTheme">
         <!-- Action bar color. This is intentionally a dark color. See crbug.com/871193. -->
@@ -346,11 +330,10 @@
         <item name="android:dividerPadding">0dp</item>
         <item name="android:showDividers">middle</item>
     </style>
-    <style name="ButtonBarButton" parent="@android:style/Widget.Holo.Light.Button">
+    <style name="ButtonBarButton" parent="@style/TextButton">
         <item name="android:layout_width">0dp</item>
         <item name="android:layout_height">match_parent</item>
         <item name="android:layout_weight">1</item>
-        <item name="android:background">?attr/selectableItemBackground</item>
         <item name="android:paddingStart">4dp</item>
         <item name="android:paddingEnd">4dp</item>
     </style>
@@ -469,7 +452,6 @@
         <item name="android:textColorLink">@color/default_text_color_link</item>
         <item name="colorPrimaryDark">@android:color/black</item>
         <item name="colorAccent">@color/light_active_color</item>
-        <item name="buttonStyle">@style/MainButtonStyle</item>
 
         <!-- Remove ActionBar -->
         <item name="windowNoTitle">true</item>
@@ -554,6 +536,10 @@
     <style name="TextAppearance.PaymentRequestHeaderTitle" parent="BlackTitle1">
         <item name="android:textStyle">bold</item>
     </style>
+    <style name="TextAppearance.PaymentRequestErrorText">
+        <item name="android:textColor">@color/error_text_color</item>
+        <item name="android:textSize">@dimen/text_size_large</item>
+    </style>
 
     <!-- Misc styles -->
     <style name="LocationBarButton">
@@ -600,8 +586,7 @@
         <item name="android:textAppearance">@style/BlackBody</item>
         <item name="android:layout_gravity">start</item>
     </style>
-    <style name="SigninButtonBorderlessRegular">
-        <item name="android:background">?android:attr/selectableItemBackground</item>
+    <style name="SigninButtonBorderlessRegular" parent="@style/TextButton">
         <item name="android:paddingStart">0dp</item>
         <item name="android:paddingEnd">0dp</item>
         <item name="android:textAppearance">@style/BlueLink2</item>
@@ -739,7 +724,7 @@
         <item name="android:ellipsize">end</item>
         <item name="leading">16sp</item>
     </style>
-    <style name="SuggestionCardAction">
+    <style name="SuggestionCardAction" parent="@style/TextButton">
         <item name="android:minHeight">48dp</item>
     </style>
 
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index c1b3157d..f7a34a9 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -38,6 +38,8 @@
     <dimen name="toolbar_swipe_commit_distance">90dp</dimen>
     <!-- TabSwitcher - Maximum amount to slide the tabs down or right in over scroll. -->
     <dimen name="over_scroll_slide">10dp</dimen>
+     <!-- TabSwitcher - The size of the close button on each tab. -->
+    <dimen name="tab_switcher_close_button_size">20dp</dimen>
 
     <!-- TabSwitcher -->
     <dimen name="tabswitcher_border_frame_padding_left">6dp</dimen>
@@ -261,10 +263,13 @@
     <dimen name="location_bar_icon_width">28dp</dimen>
     <dimen name="location_bar_start_icon_width">28dp</dimen>
     <dimen name="location_bar_lateral_padding">10dp</dimen>
+    <dimen name="location_bar_status_separator_width">1dp</dimen>
+    <dimen name="location_bar_status_separator_spacer">8dp</dimen>
+    <dimen name="location_bar_min_url_width">68dp</dimen>
+    <dimen name="location_bar_min_verbose_status_text_width">48dp</dimen>
 
     <dimen name="tablet_toolbar_start_padding">4dp</dimen>
     <dimen name="tablet_toolbar_end_padding">6dp</dimen>
-    <dimen name="location_bar_status_separator_width">1dp</dimen>
     <dimen name="toolbar_optional_button_animation_translation">10dp</dimen>
 
     <!-- Modern toolbar dimensions -->
diff --git a/chrome/android/java/res/values/ids.xml b/chrome/android/java/res/values/ids.xml
index 2d7ab74..47286b4 100644
--- a/chrome/android/java/res/values/ids.xml
+++ b/chrome/android/java/res/values/ids.xml
@@ -131,4 +131,5 @@
     <item type="id" name="highlight_state" />
     <item type="id" name="item_animator" />
     <item type="id" name="highlight_color" />
+    <item type="id" name="view_model" />
 </resources>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
index a1c4e18..21691b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
@@ -349,12 +349,14 @@
             }
         }
 
-        // If the previous caller was not Chrome, but added EXTRA_IS_OPENED_BY_CHROME for malicious
-        // purpose, remove it. The new intent will be sent by Chrome, but was not sent by Chrome
-        // initially.
+        // If the previous caller was not Chrome, but added EXTRA_IS_OPENED_BY_CHROME or
+        // EXTRA_IS_OPENED_BY_WEBAPK for malicious purpose, remove it. The new intent will be sent
+        // by Chrome, but was not sent by Chrome initially.
         if (!IntentHandler.wasIntentSenderChrome(intent)) {
             IntentUtils.safeRemoveExtra(
                     newIntent, CustomTabIntentDataProvider.EXTRA_IS_OPENED_BY_CHROME);
+            IntentUtils.safeRemoveExtra(
+                    newIntent, CustomTabIntentDataProvider.EXTRA_IS_OPENED_BY_WEBAPK);
         }
 
         return newIntent;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java
index 13b8a399..4309d3e5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java
@@ -6,6 +6,7 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Color;
 import android.graphics.RectF;
 
 import org.chromium.base.ApiCompatibilityUtils;
@@ -82,13 +83,17 @@
             // In the modern design, the text box is always drawn opaque in the compositor.
             float textBoxAlpha = 1.f;
 
-            int closeButtonColor = ColorUtils.getThemedAssetColor(defaultThemeColor, useLightIcon);
+            int closeButtonColor = useLightIcon
+                    ? Color.WHITE
+                    : ApiCompatibilityUtils.getColor(res, R.color.light_icon_color);
+            float closeButtonSizePx =
+                    res.getDimensionPixelSize(R.dimen.tab_switcher_close_button_size);
 
             int borderColorResource =
                     t.isIncognito() ? R.color.tab_back_incognito : R.color.tab_back;
             // TODO(dtrainor, clholgat): remove "* dpToPx" once the native part fully supports dp.
             nativePutTabLayer(mNativePtr, t.getId(), R.id.control_container,
-                    R.drawable.btn_close_white, R.drawable.tabswitcher_border_frame_shadow,
+                    R.drawable.btn_delete_24dp, R.drawable.tabswitcher_border_frame_shadow,
                     R.drawable.tabswitcher_border_frame_decoration, R.drawable.logo_card_back,
                     R.drawable.tabswitcher_border_frame,
                     R.drawable.tabswitcher_border_frame_inner_shadow, t.canUseLiveTexture(),
@@ -104,13 +109,13 @@
                     t.getTiltX(), t.getTiltY(), t.getAlpha(), t.getBorderAlpha() * decoration,
                     t.getBorderInnerShadowAlpha() * decoration, decoration, shadowAlpha,
                     t.getBorderCloseButtonAlpha() * decoration,
-                    LayoutTab.CLOSE_BUTTON_WIDTH_DP * dpToPx, t.getStaticToViewBlend(),
-                    t.getBorderScale(), t.getSaturation(), t.getBrightness(), t.showToolbar(),
-                    defaultThemeColor, t.getToolbarBackgroundColor(), closeButtonColor,
-                    t.anonymizeToolbar(), t.isTitleNeeded(), urlBarBackgroundId,
-                    t.getTextBoxBackgroundColor(), textBoxAlpha, t.getToolbarAlpha(),
-                    t.getToolbarYOffset() * dpToPx, t.getSideBorderScale(),
-                    t.insetBorderVertical());
+                    LayoutTab.CLOSE_BUTTON_WIDTH_DP * dpToPx, closeButtonSizePx,
+                    t.getStaticToViewBlend(), t.getBorderScale(), t.getSaturation(),
+                    t.getBrightness(), t.showToolbar(), defaultThemeColor,
+                    t.getToolbarBackgroundColor(), closeButtonColor, t.anonymizeToolbar(),
+                    t.isTitleNeeded(), urlBarBackgroundId, t.getTextBoxBackgroundColor(),
+                    textBoxAlpha, t.getToolbarAlpha(), t.getToolbarYOffset() * dpToPx,
+                    t.getSideBorderScale(), t.insetBorderVertical());
         }
         nativeFinishBuildingFrame(mNativePtr);
     }
@@ -169,10 +174,10 @@
             float shadowX, float shadowY, float shadowWidth, float shadowHeight, float pivotX,
             float pivotY, float rotationX, float rotationY, float alpha, float borderAlpha,
             float borderInnerShadowAlpha, float contourAlpha, float shadowAlpha, float closeAlpha,
-            float closeBtnWidth, float staticToViewBlend, float borderScale, float saturation,
-            float brightness, boolean showToolbar, int defaultThemeColor,
-            int toolbarBackgroundColor, int closeButtonColor, boolean anonymizeToolbar,
-            boolean showTabTitle, int toolbarTextBoxResource, int toolbarTextBoxBackgroundColor,
-            float toolbarTextBoxAlpha, float toolbarAlpha, float toolbarYOffset,
-            float sideBorderScale, boolean insetVerticalBorder);
+            float closeBtnWidth, float closeBtnAssetSize, float staticToViewBlend,
+            float borderScale, float saturation, float brightness, boolean showToolbar,
+            int defaultThemeColor, int toolbarBackgroundColor, int closeButtonColor,
+            boolean anonymizeToolbar, boolean showTabTitle, int toolbarTextBoxResource,
+            int toolbarTextBoxBackgroundColor, float toolbarTextBoxAlpha, float toolbarAlpha,
+            float toolbarYOffset, float sideBorderScale, boolean insetVerticalBorder);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index a8760c93..187739f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -1059,10 +1059,18 @@
         if (params.getReferrer() == null) {
             params.setReferrer(mConnection.getReferrerForSession(mSession));
         }
-        // See ChromeTabCreator#getTransitionType(). This marks the navigation chain as starting
-        // from an external intent (unless otherwise specified by an extra in the intent).
-        params.setTransitionType(IntentHandler.getTransitionTypeFromIntent(intent,
-                PageTransition.LINK | PageTransition.FROM_API));
+
+        // See ChromeTabCreator#getTransitionType(). If the sender of the intent was a WebAPK, mark
+        // the intent as a standard link navigation. Pass the user gesture along since one must have
+        // been active to open a new tab and reach here. Otherwise, mark the navigation chain as
+        // starting from an external intent. See crbug.com/792990.
+        int defaultTransition = PageTransition.LINK | PageTransition.FROM_API;
+        if (mIntentDataProvider != null && mIntentDataProvider.isOpenedByWebApk()) {
+            params.setHasUserGesture(true);
+            defaultTransition = PageTransition.LINK;
+        }
+        params.setTransitionType(
+                IntentHandler.getTransitionTypeFromIntent(intent, defaultTransition));
         tab.loadUrl(params);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
index ed43ad9..73bbcfc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
@@ -109,6 +109,10 @@
     public static final String EXTRA_DISABLE_DOWNLOAD_BUTTON =
             "org.chromium.chrome.browser.customtabs.EXTRA_DISABLE_DOWNLOAD_BUTTON";
 
+    /** Extra that indicates whether the client is a WebAPK. */
+    public static final String EXTRA_IS_OPENED_BY_WEBAPK =
+            "org.chromium.chrome.browser.customtabs.EXTRA_IS_OPENED_BY_WEBAPK";
+
     /**
      * Indicates the source where the Custom Tab is launched. This is only used for
      * WebApp/WebAPK/TrustedWebActivity. The value is defined as
@@ -147,6 +151,7 @@
     private final int mInitialBackgroundColor;
     private final boolean mDisableStar;
     private final boolean mDisableDownload;
+    private final boolean mIsOpenedByWebApk;
     private final boolean mIsTrustedWebActivity;
     @Nullable
     private final ComponentName mModuleComponentName;
@@ -266,6 +271,8 @@
         mDisableStar = IntentUtils.safeGetBooleanExtra(intent, EXTRA_DISABLE_STAR_BUTTON, false);
         mDisableDownload =
                 IntentUtils.safeGetBooleanExtra(intent, EXTRA_DISABLE_DOWNLOAD_BUTTON, false);
+        mIsOpenedByWebApk =
+                IntentUtils.safeGetBooleanExtra(intent, EXTRA_IS_OPENED_BY_WEBAPK, false);
 
         String modulePackageName =
                 IntentUtils.safeGetStringExtra(intent, EXTRA_MODULE_PACKAGE_NAME);
@@ -672,6 +679,13 @@
     }
 
     /**
+     * @return Whether the Custom Tab was opened from a WebAPK.
+     */
+    boolean isOpenedByWebApk() {
+        return mIsOpenedByWebApk;
+    }
+
+    /**
      * @return Whether the Custom Tab is opened for payment request.
      */
     boolean isForPaymentRequest() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastReceiver.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastReceiver.java
deleted file mode 100644
index 0042857..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastReceiver.java
+++ /dev/null
@@ -1,35 +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.
-
-package org.chromium.chrome.browser.download;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-/**
- * This {@link BroadcastReceiver} handles clicks to download notifications and their action buttons.
- * Clicking on an in-progress or failed download will open the download manager. Clicking on
- * a complete, successful download will open the file. Clicking on the resume button of a paused
- * download will relaunch the browser process and try to resume the download from where it is
- * stopped.
- */
-public class DownloadBroadcastReceiver extends BroadcastReceiver {
-    @Override
-    public void onReceive(final Context context, Intent intent) {
-        performDownloadOperation(context, intent);
-    }
-
-    /**
-     * Called to perform a download operation. This will call the DownloadNotificationService
-     * to start the browser process asynchronously, and resume or cancel the download afterwards.
-     * @param context Context of the receiver.
-     * @param intent Intent retrieved from the notification.
-     */
-    private void performDownloadOperation(final Context context, Intent intent) {
-        if (DownloadNotificationService.isDownloadOperationIntent(intent)) {
-            DownloadNotificationService.startDownloadNotificationService(context, intent);
-        }
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
index a25f9f8..bbb0a4b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -31,7 +31,6 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.task.AsyncTask;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.download.DownloadMetrics.DownloadOpenSource;
 import org.chromium.chrome.browser.download.ui.BackendProvider;
 import org.chromium.chrome.browser.externalnav.ExternalNavigationDelegateImpl;
@@ -234,13 +233,7 @@
     public static DownloadManagerService getDownloadManagerService() {
         ThreadUtils.assertOnUiThread();
         if (sDownloadManagerService == null) {
-            // TODO(crbug.com/765327): Remove temporary fix after flag is no longer being used.
-            DownloadNotifier downloadNotifier =
-                    (!BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER)
-                                    .isStartupSuccessfullyCompleted()
-                            || !ChromeFeatureList.isEnabled(ChromeFeatureList.DOWNLOADS_FOREGROUND))
-                    ? new SystemDownloadNotifier()
-                    : new SystemDownloadNotifier2();
+            DownloadNotifier downloadNotifier = new SystemDownloadNotifier2();
             sDownloadManagerService = new DownloadManagerService(
                     downloadNotifier, new Handler(), UPDATE_DELAY_MILLIS);
         }
@@ -417,7 +410,7 @@
             mInfoBarController = new DownloadInfoBarController(false);
             mIncognitoInfoBarController = new DownloadInfoBarController(true);
 
-            DownloadNotificationService.clearResumptionAttemptLeft();
+            DownloadNotificationService2.clearResumptionAttemptLeft();
 
             DownloadManagerService.getDownloadManagerService().checkForExternallyRemovedDownloads(
                     /*isOffTheRecord=*/false);
@@ -1511,21 +1504,11 @@
     @Override
     public void broadcastDownloadAction(DownloadItem downloadItem, String action) {
         Context appContext = ContextUtils.getApplicationContext();
-        if (!BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER)
-                        .isStartupSuccessfullyCompleted()
-                || !ChromeFeatureList.isEnabled(ChromeFeatureList.DOWNLOADS_FOREGROUND)) {
-            Intent intent = DownloadNotificationService.buildActionIntent(appContext, action,
-                    LegacyHelpers.buildLegacyContentId(false, downloadItem.getId()),
-                    downloadItem.getDownloadInfo().isOffTheRecord());
-            addCancelExtra(intent, downloadItem);
-            appContext.sendBroadcast(intent);
-        } else {
             Intent intent = DownloadNotificationFactory.buildActionIntent(appContext, action,
                     LegacyHelpers.buildLegacyContentId(false, downloadItem.getId()),
                     downloadItem.getDownloadInfo().isOffTheRecord());
             addCancelExtra(intent, downloadItem);
             appContext.startService(intent);
-        }
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
deleted file mode 100644
index 6d92e63..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
+++ /dev/null
@@ -1,1477 +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.
-
-package org.chromium.chrome.browser.download;
-
-import static android.app.DownloadManager.ACTION_NOTIFICATION_CLICKED;
-
-import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
-import android.app.DownloadManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.drawable.shapes.OvalShape;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.service.notification.StatusBarNotification;
-import android.support.v4.app.NotificationCompat;
-import android.text.TextUtils;
-import android.util.Pair;
-
-import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.base.ApplicationStatus;
-import org.chromium.base.ContextUtils;
-import org.chromium.base.Log;
-import org.chromium.base.ObserverList;
-import org.chromium.base.StrictModeContext;
-import org.chromium.base.VisibleForTesting;
-import org.chromium.base.library_loader.LibraryProcessType;
-import org.chromium.base.library_loader.ProcessInitException;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.AppHooks;
-import org.chromium.chrome.browser.ChromeApplication;
-import org.chromium.chrome.browser.ChromeFeatureList;
-import org.chromium.chrome.browser.download.items.OfflineContentAggregatorNotificationBridgeUiFactory;
-import org.chromium.chrome.browser.init.BrowserParts;
-import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
-import org.chromium.chrome.browser.init.EmptyBrowserParts;
-import org.chromium.chrome.browser.init.ServiceManagerStartupUtils;
-import org.chromium.chrome.browser.media.MediaViewerUtils;
-import org.chromium.chrome.browser.notifications.ChromeNotificationBuilder;
-import org.chromium.chrome.browser.notifications.NotificationBuilderFactory;
-import org.chromium.chrome.browser.notifications.NotificationConstants;
-import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
-import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.util.IntentUtils;
-import org.chromium.components.offline_items_collection.ContentId;
-import org.chromium.components.offline_items_collection.FailState;
-import org.chromium.components.offline_items_collection.LegacyHelpers;
-import org.chromium.components.offline_items_collection.OfflineItem.Progress;
-import org.chromium.content_public.browser.BrowserStartupController;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Service responsible for creating and updating download notifications even after
- * Chrome gets killed.
- *
- * On O and above, this service will receive {@link Service#startForeground(int, Notification)}
- * calls when containing active downloads.  The foreground notification will be the summary
- * notification generated by {@link DownloadNotificationService#buildSummaryNotification(Context)}.
- * The service will receive a {@link Service#stopForeground(boolean)} call when all active downloads
- * are paused.  The summary notification will be hidden when there are no other notifications in the
- * {@link NotificationConstants#GROUP_DOWNLOADS} group.  This gets checked after every notification
- * gets removed from the {@link NotificationManager}.
- */
-public class DownloadNotificationService extends Service {
-    static final String EXTRA_DOWNLOAD_CONTENTID_ID =
-            "org.chromium.chrome.browser.download.DownloadContentId_Id";
-    static final String EXTRA_DOWNLOAD_CONTENTID_NAMESPACE =
-            "org.chromium.chrome.browser.download.DownloadContentId_Namespace";
-    static final String EXTRA_DOWNLOAD_FILE_PATH = "DownloadFilePath";
-    static final String EXTRA_NOTIFICATION_DISMISSED = "NotificationDismissed";
-    static final String EXTRA_IS_SUPPORTED_MIME_TYPE = "IsSupportedMimeType";
-    static final String EXTRA_IS_OFF_THE_RECORD =
-            "org.chromium.chrome.browser.download.IS_OFF_THE_RECORD";
-
-    public static final String ACTION_DOWNLOAD_CANCEL =
-            "org.chromium.chrome.browser.download.DOWNLOAD_CANCEL";
-    public static final String ACTION_DOWNLOAD_PAUSE =
-            "org.chromium.chrome.browser.download.DOWNLOAD_PAUSE";
-    public static final String ACTION_DOWNLOAD_RESUME =
-            "org.chromium.chrome.browser.download.DOWNLOAD_RESUME";
-    static final String ACTION_DOWNLOAD_RESUME_ALL =
-            "org.chromium.chrome.browser.download.DOWNLOAD_RESUME_ALL";
-    public static final String ACTION_DOWNLOAD_OPEN =
-            "org.chromium.chrome.browser.download.DOWNLOAD_OPEN";
-    public static final String ACTION_DOWNLOAD_UPDATE_SUMMARY_ICON =
-            "org.chromium.chrome.browser.download.DOWNLOAD_UPDATE_SUMMARY_ICON";
-    public static final String ACTION_DOWNLOAD_FAIL_SAFE =
-            "org.chromium.chrome.browser.download.ACTION_SUMMARY_FAIL_SAFE";
-
-    static final String NOTIFICATION_NAMESPACE = "DownloadNotificationService";
-    private static final String TAG = "DownloadNotification";
-    // Limit file name to 25 characters. TODO(qinmin): use different limit for different devices?
-    private static final int MAX_FILE_NAME_LENGTH = 25;
-
-    /** Notification Id starting value, to avoid conflicts from IDs used in prior versions. */
-
-    private static final String EXTRA_NOTIFICATION_BUNDLE_ICON_ID =
-            "Chrome.NotificationBundleIconIdExtra";
-    private static final int STARTING_NOTIFICATION_ID = 1000000;
-    private static final int MAX_RESUMPTION_ATTEMPT_LEFT = 5;
-
-    private static final String KEY_AUTO_RESUMPTION_ATTEMPT_LEFT = "ResumptionAttemptLeft";
-    private static final String KEY_NEXT_DOWNLOAD_NOTIFICATION_ID = "NextDownloadNotificationId";
-
-    /**
-     * An Observer interface that allows other classes to know when this class is canceling
-     * downloads.
-     */
-    public interface Observer {
-        /**
-         * Called when a download was canceled from the notification.  The implementer is not
-         * responsible for canceling the actual download (that should be triggered internally from
-         * this class).  The implementer is responsible for using this to do their own tracking
-         * related to which downloads might be active in this service.  File downloads don't trigger
-         * a cancel event when they are told to cancel downloads, so classes might have no idea that
-         * a download stopped otherwise.
-         * @param id The {@link ContentId} of the download that was canceled.
-         */
-        void onDownloadCanceled(ContentId id);
-    }
-
-    private final ObserverList<Observer> mObservers = new ObserverList<>();
-    private final IBinder mBinder = new LocalBinder();
-    private final List<ContentId> mDownloadsInProgress = new ArrayList<ContentId>();
-
-    private NotificationManager mNotificationManager;
-    private SharedPreferences mSharedPrefs;
-    private int mNextNotificationId;
-    private int mNumAutoResumptionAttemptLeft;
-    private Bitmap mDownloadSuccessLargeIcon;
-    private DownloadSharedPreferenceHelper mDownloadSharedPreferenceHelper;
-
-    /**
-     * @return Whether or not this service should be made a foreground service if there are active
-     * downloads.
-     */
-    @VisibleForTesting
-    static boolean useForegroundService() {
-        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
-    }
-
-    /**
-     * Start this service with a summary {@link Notification}.  This will start the service in the
-     * foreground.
-     * @param context The context used to build the notification and to start the service.
-     * @param source The {@link Intent} that should be used to build on to start the service.
-     */
-    public static void startDownloadNotificationService(Context context, Intent source) {
-        Intent intent = source != null ? new Intent(source) : new Intent();
-        intent.setComponent(new ComponentName(context, DownloadNotificationService.class));
-
-        if (useForegroundService()) {
-            NotificationManager manager =
-                    (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
-            // Attempt to update the notification summary icon without starting the service.
-            if (ACTION_DOWNLOAD_UPDATE_SUMMARY_ICON.equals(intent.getAction())) {
-                // updateSummaryIcon should be a noop if the notification isn't showing or if the
-                // icon won't change anyway.
-                updateSummaryIcon(context, manager, -1, null);
-                return;
-            }
-
-            AppHooks.get().startForegroundService(intent);
-        } else {
-            context.startService(intent);
-        }
-    }
-
-    /**
-     * Updates the notification summary with a new icon, if necessary.
-     * @param removedNotificationId The id of a notification that is currently closing and should be
-     *                              ignored.  -1 if no notifications are being closed.
-     * @param addedNotification     A {@link Pair} of <id, Notification> of a notification that is
-     *                              currently being added and should be used in addition to or in
-     *                              place of the existing icons.
-     */
-    private static void updateSummaryIcon(Context context, NotificationManager manager,
-            int removedNotificationId, Pair<Integer, Notification> addedNotification) {
-        if (!useForegroundService()) return;
-
-        Pair<Boolean, Integer> icon =
-                getSummaryIcon(context, manager, removedNotificationId, addedNotification);
-        if (!icon.first || !hasDownloadNotifications(manager, removedNotificationId)) return;
-
-        manager.notify(NotificationConstants.NOTIFICATION_ID_DOWNLOAD_SUMMARY,
-                buildSummaryNotificationWithIcon(context, icon.second));
-    }
-
-    /**
-     * Returns whether or not there are any download notifications showing that aren't the summary
-     * notification.
-     * @param notificationIdToIgnore If not -1, the id of a notification to ignore and
-     *                               assume is closing or about to be closed.
-     * @return Whether or not there are valid download notifications currently visible.
-     */
-    @TargetApi(Build.VERSION_CODES.M)
-    private static boolean hasDownloadNotifications(
-            NotificationManager manager, int notificationIdToIgnore) {
-        if (!useForegroundService()) return false;
-
-        StatusBarNotification[] notifications = manager.getActiveNotifications();
-        for (StatusBarNotification notification : notifications) {
-            boolean isDownloadsGroup = TextUtils.equals(notification.getNotification().getGroup(),
-                    NotificationConstants.GROUP_DOWNLOADS);
-            boolean isSummaryNotification =
-                    notification.getId() == NotificationConstants.NOTIFICATION_ID_DOWNLOAD_SUMMARY;
-            boolean isIgnoredNotification =
-                    notificationIdToIgnore != -1 && notificationIdToIgnore == notification.getId();
-            if (isDownloadsGroup && !isSummaryNotification && !isIgnoredNotification) return true;
-        }
-
-        return false;
-    }
-
-    /**
-     * Calculates the suggested icon for the summary notification based on the other notifications
-     * currently showing.
-     * @param context A context to use to query Android-specific information (NotificationManager).
-     * @param removedNotificationId The id of a notification that is currently closing and should be
-     *                              ignored.  -1 if no notifications are being closed.
-     * @param addedNotification     A {@link Pair} of <id, Notification> of a notification that is
-     *                              currently being added and should be used in addition to or in
-     *                              place of the existing icons.
-     * @return                      A {@link Pair} that represents both whether or not the new icon
-     *                              is different from the old one and the icon id itself.
-     */
-    @TargetApi(Build.VERSION_CODES.M)
-    private static Pair<Boolean, Integer> getSummaryIcon(Context context,
-            NotificationManager manager, int removedNotificationId,
-            Pair<Integer, Notification> addedNotification) {
-        if (!useForegroundService()) return new Pair<Boolean, Integer>(false, -1);
-        boolean progress = false;
-        boolean paused = false;
-        boolean pending = false;
-        boolean completed = false;
-        boolean failed = false;
-
-        final int progressIcon = android.R.drawable.stat_sys_download;
-        final int pausedIcon = R.drawable.ic_download_pause;
-        final int pendingIcon = R.drawable.ic_download_pending;
-        final int completedIcon = R.drawable.offline_pin;
-        final int failedIcon = android.R.drawable.stat_sys_download_done;
-
-        StatusBarNotification[] notifications = manager.getActiveNotifications();
-
-        int oldIcon = -1;
-        for (StatusBarNotification notification : notifications) {
-            boolean isDownloadsGroup = TextUtils.equals(notification.getNotification().getGroup(),
-                    NotificationConstants.GROUP_DOWNLOADS);
-            if (!isDownloadsGroup) continue;
-            if (notification.getId() == removedNotificationId) continue;
-
-            boolean isSummaryNotification =
-                    notification.getId() == NotificationConstants.NOTIFICATION_ID_DOWNLOAD_SUMMARY;
-
-            if (addedNotification != null && addedNotification.first == notification.getId()) {
-                continue;
-            }
-
-            int icon =
-                    notification.getNotification().extras.getInt(EXTRA_NOTIFICATION_BUNDLE_ICON_ID);
-            if (isSummaryNotification) {
-                oldIcon = icon;
-                continue;
-            }
-
-            progress |= icon == progressIcon;
-            paused |= icon == pausedIcon;
-            pending |= icon == pendingIcon;
-            completed |= icon == completedIcon;
-            failed |= icon == failedIcon;
-        }
-
-        if (addedNotification != null) {
-            int icon = addedNotification.second.extras.getInt(EXTRA_NOTIFICATION_BUNDLE_ICON_ID);
-
-            progress |= icon == progressIcon;
-            paused |= icon == pausedIcon;
-            pending |= icon == pendingIcon;
-            completed |= icon == completedIcon;
-            failed |= icon == failedIcon;
-        }
-
-        int newIcon = android.R.drawable.stat_sys_download_done;
-        if (progress) {
-            newIcon = android.R.drawable.stat_sys_download;
-        } else if (pending) {
-            newIcon = R.drawable.ic_download_pending;
-        } else if (failed) {
-            newIcon = android.R.drawable.stat_sys_download_done;
-        } else if (paused) {
-            newIcon = R.drawable.ic_download_pause;
-        } else if (completed) {
-            newIcon = R.drawable.offline_pin;
-        }
-
-        return new Pair<Boolean, Integer>(newIcon != oldIcon, newIcon);
-    }
-
-    /**
-     * Builds a summary notification that represents all downloads.
-     * {@see #buildSummaryNotification(Context)}.
-     * @param context A context used to query Android strings and resources.
-     * @param iconId  The id of an icon to use for the notification.
-     * @return        a {@link Notification} that represents the summary icon for all downloads.
-     */
-    private static Notification buildSummaryNotificationWithIcon(Context context, int iconId) {
-        ChromeNotificationBuilder builder =
-                NotificationBuilderFactory
-                        .createChromeNotificationBuilder(
-                                true /* preferCompat */, ChannelDefinitions.ChannelId.DOWNLOADS)
-                        .setContentTitle(
-                                context.getString(R.string.download_notification_summary_title))
-                        .setSubText(context.getString(R.string.menu_downloads))
-                        .setSmallIcon(iconId)
-                        .setLocalOnly(true)
-                        .setGroup(NotificationConstants.GROUP_DOWNLOADS)
-                        .setGroupSummary(true);
-        Bundle extras = new Bundle();
-        extras.putInt(EXTRA_NOTIFICATION_BUNDLE_ICON_ID, iconId);
-        builder.addExtras(extras);
-
-        // This notification should not actually be shown.  But if it is, set the click intent to
-        // open downloads home.
-        // TODO(dtrainor): Only do this if we have no transient downloads.
-        Intent downloadHomeIntent =
-                buildActionIntent(context, ACTION_NOTIFICATION_CLICKED, null, false);
-        builder.setContentIntent(PendingIntent.getBroadcast(context,
-                NotificationConstants.NOTIFICATION_ID_DOWNLOAD_SUMMARY, downloadHomeIntent,
-                PendingIntent.FLAG_UPDATE_CURRENT));
-
-        return builder.build();
-    }
-
-    /**
-     * Builds a summary notification that represents downloads.  This is the notification passed to
-     * {@link #startForeground(int, Notification)}, which keeps this service in the foreground.
-     * @param context The context used to build the notification and pull specific resources.
-     * @return The {@link Notification} to show for the summary.  Meant to be used by
-     *         {@link NotificationManager#notify(int, Notification)}.
-     */
-    private static Notification buildSummaryNotification(
-            Context context, NotificationManager manager) {
-        Pair<Boolean, Integer> icon = getSummaryIcon(context, manager, -1, null);
-        return buildSummaryNotificationWithIcon(context, icon.second);
-    }
-
-    /**
-     * @return Whether or not there are any current resumable downloads being tracked.  These
-     *         tracked downloads may not currently be showing notifications.
-     */
-    public static boolean isTrackingResumableDownloads(Context context) {
-        List<DownloadSharedPreferenceEntry> entries =
-                DownloadSharedPreferenceHelper.getInstance().getEntries();
-        for (DownloadSharedPreferenceEntry entry : entries) {
-            if (canResumeDownload(context, entry)) return true;
-        }
-        return false;
-    }
-
-    /**
-     * Class for clients to access.
-     */
-    public class LocalBinder extends Binder {
-        DownloadNotificationService getService() {
-            return DownloadNotificationService.this;
-        }
-    }
-
-    @Override
-    public void onTaskRemoved(Intent rootIntent) {
-        // Record instance of task removed.
-        DownloadNotificationUmaHelper.recordServiceStoppedHistogram(
-                DownloadNotificationUmaHelper.ServiceStopped.TASK_REMOVED, false);
-
-        super.onTaskRemoved(rootIntent);
-        // If we've lost all Activities, cancel the off the record downloads and validate that we
-        // should still be showing any download notifications at all.
-        if (ApplicationStatus.isEveryActivityDestroyed()) {
-            cancelOffTheRecordDownloads();
-            hideSummaryNotificationIfNecessary(-1);
-        }
-    }
-
-    @Override
-    public void onLowMemory() {
-        // Record instance of service with low memory.
-        DownloadNotificationUmaHelper.recordServiceStoppedHistogram(
-                DownloadNotificationUmaHelper.ServiceStopped.LOW_MEMORY, false /*withForeground*/);
-        super.onLowMemory();
-    }
-
-    @Override
-    public void onCreate() {
-        mNotificationManager =
-                (NotificationManager) ContextUtils.getApplicationContext().getSystemService(
-                        Context.NOTIFICATION_SERVICE);
-        mSharedPrefs = ContextUtils.getAppSharedPreferences();
-        mNumAutoResumptionAttemptLeft =
-                mSharedPrefs.getInt(KEY_AUTO_RESUMPTION_ATTEMPT_LEFT, MAX_RESUMPTION_ATTEMPT_LEFT);
-        mDownloadSharedPreferenceHelper = DownloadSharedPreferenceHelper.getInstance();
-        mNextNotificationId =
-                mSharedPrefs.getInt(KEY_NEXT_DOWNLOAD_NOTIFICATION_ID, STARTING_NOTIFICATION_ID);
-    }
-
-    @Override
-    public void onDestroy() {
-        // Record instance of service being destroyed.
-        DownloadNotificationUmaHelper.recordServiceStoppedHistogram(
-                DownloadNotificationUmaHelper.ServiceStopped.DESTROYED, false /* withForeground */);
-
-        updateNotificationsForShutdown();
-        rescheduleDownloads();
-        super.onDestroy();
-    }
-
-    @Override
-    public int onStartCommand(final Intent intent, int flags, int startId) {
-        // Start a foreground service every time we process a valid intent.  This makes sure we
-        // honor the promise that we'll be in the foreground when we start, even if we immediately
-        // drop ourselves back.
-        if (useForegroundService() && intent != null) startForegroundInternal();
-
-        if (intent == null) {
-            // Record instance of service restarting.
-            DownloadNotificationUmaHelper.recordServiceStoppedHistogram(
-                    DownloadNotificationUmaHelper.ServiceStopped.START_STICKY, false);
-
-            // Intent is only null during a process restart because of returning START_STICKY.  In
-            // this case cancel the off the record notifications and put the normal notifications
-            // into a pending state, then try to restart.  Finally validate that we are actually
-            // showing something.
-            updateNotificationsForShutdown();
-            handleDownloadOperation(
-                    new Intent(DownloadNotificationService.ACTION_DOWNLOAD_RESUME_ALL));
-            hideSummaryNotificationIfNecessary(-1);
-        } else if (TextUtils.equals(intent.getAction(),
-                           DownloadNotificationService.ACTION_DOWNLOAD_FAIL_SAFE)) {
-            hideSummaryNotificationIfNecessary(-1);
-        } else if (isDownloadOperationIntent(intent)) {
-            handleDownloadOperation(intent);
-            DownloadResumptionScheduler.getDownloadResumptionScheduler().cancel();
-            // Limit the number of auto resumption attempts in case Chrome falls into a vicious
-            // cycle.
-            if (ACTION_DOWNLOAD_RESUME_ALL.equals(intent.getAction())) {
-                if (mNumAutoResumptionAttemptLeft > 0) {
-                    mNumAutoResumptionAttemptLeft--;
-                    updateResumptionAttemptLeft();
-                }
-            } else {
-                // Reset number of attempts left if the action is triggered by user.
-                mNumAutoResumptionAttemptLeft = MAX_RESUMPTION_ATTEMPT_LEFT;
-                clearResumptionAttemptLeft();
-            }
-        }
-        // This should restart the service after Chrome gets killed. However, this
-        // doesn't work on Android 4.4.2.
-        return START_STICKY;
-    }
-
-    /**
-     * Adds an {@link Observer}, which will be notified when this service attempts to
-     * start stopping itself.
-     */
-    public void addObserver(Observer observer) {
-        mObservers.addObserver(observer);
-    }
-
-    /**
-     * Removes {@code observer}, which will no longer be notified when this class decides to start
-     * stopping itself.
-     */
-    public void removeObserver(Observer observer) {
-        mObservers.removeObserver(observer);
-    }
-
-    /**
-     * On >= O Android releases, puts this service into a background state.
-     * @param killNotification Whether or not this call should kill the summary notification or not.
-     *                         Not killing it puts the service into the background, but leaves the
-     *                         download notifications visible.
-     */
-    @VisibleForTesting
-    @TargetApi(Build.VERSION_CODES.N)
-    void stopForegroundInternal(boolean killNotification) {
-        Log.w(TAG, "stopForegroundInternal killNotification: " + killNotification);
-        if (!useForegroundService()) return;
-        stopForeground(killNotification ? STOP_FOREGROUND_REMOVE : STOP_FOREGROUND_DETACH);
-    }
-
-    /**
-     * On >= O Android releases, puts this service into a foreground state, binding it to the
-     * {@link Notification} generated by {@link #buildSummaryNotification(Context)}.
-     */
-    @VisibleForTesting
-    void startForegroundInternal() {
-        Log.w(TAG, "startForegroundInternal");
-        if (!useForegroundService()) return;
-        Notification notification = buildSummaryNotification(
-                ContextUtils.getApplicationContext(), mNotificationManager);
-        startForeground(NotificationConstants.NOTIFICATION_ID_DOWNLOAD_SUMMARY, notification);
-    }
-
-    @VisibleForTesting
-    boolean hasDownloadNotificationsInternal(int notificationIdToIgnore) {
-        return hasDownloadNotifications(mNotificationManager, notificationIdToIgnore);
-    }
-
-    private void rescheduleDownloads() {
-        if (mNumAutoResumptionAttemptLeft <= 0) return;
-        DownloadResumptionScheduler.getDownloadResumptionScheduler().scheduleIfNecessary();
-    }
-
-    @VisibleForTesting
-    void updateNotificationsForShutdown() {
-        cancelOffTheRecordDownloads();
-        List<DownloadSharedPreferenceEntry> entries = mDownloadSharedPreferenceHelper.getEntries();
-        for (DownloadSharedPreferenceEntry entry : entries) {
-            if (entry.isOffTheRecord) continue;
-            // Move all regular downloads to pending.  Don't propagate the pause because
-            // if native is still working and it triggers an update, then the service will be
-            // restarted.
-            notifyDownloadPaused(entry.id, entry.fileName, !entry.isOffTheRecord, true,
-                    entry.isOffTheRecord, entry.isTransient, null);
-        }
-    }
-
-    @VisibleForTesting
-    void cancelOffTheRecordDownloads() {
-        boolean cancelActualDownload =
-                BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER)
-                        .isStartupSuccessfullyCompleted()
-                && Profile.getLastUsedProfile().hasOffTheRecordProfile();
-
-        List<DownloadSharedPreferenceEntry> entries = mDownloadSharedPreferenceHelper.getEntries();
-        List<DownloadSharedPreferenceEntry> copies =
-                new ArrayList<DownloadSharedPreferenceEntry>(entries);
-        for (DownloadSharedPreferenceEntry entry : copies) {
-            if (!entry.isOffTheRecord) continue;
-            ContentId id = entry.id;
-            notifyDownloadCanceled(id);
-            if (cancelActualDownload) {
-                DownloadServiceDelegate delegate = getServiceDelegate(id);
-                delegate.cancelDownload(id, true);
-                delegate.destroyServiceDelegate();
-            }
-            for (Observer observer : mObservers) observer.onDownloadCanceled(id);
-        }
-    }
-
-    /**
-     * Track in-progress downloads here and, if on an Android version >= O, make
-     * this a foreground service.
-     * @param id The {@link ContentId} of the download that has been started and should be tracked.
-     */
-    private void startTrackingInProgressDownload(ContentId id) {
-        Log.w(TAG, "startTrackingInProgressDownload");
-        if (mDownloadsInProgress.size() == 0) startForegroundInternal();
-        if (!mDownloadsInProgress.contains(id)) mDownloadsInProgress.add(id);
-    }
-
-    /**
-     * Stop tracking the download represented by {@code id}.  If on an Android version >= O, stop
-     * making this a foreground service.
-     * @param id                  The {@link ContentId} of the download that has been paused or
-     *                            canceled and shouldn't be tracked.
-     * @param allowStopForeground Whether or not this should check internal state and stop the
-     *                            foreground notification from showing.  This could be false if we
-     *                            plan on removing the notification in the near future.  We don't
-     *                            want to just detach here, because that will put us in a
-     *                            potentially bad state where we cannot dismiss the notification.
-     */
-    private void stopTrackingInProgressDownload(ContentId id, boolean allowStopForeground) {
-        Log.w(TAG, "stopTrackingInProgressDownload");
-        mDownloadsInProgress.remove(id);
-        if (allowStopForeground && mDownloadsInProgress.size() == 0) stopForegroundInternal(false);
-    }
-
-    /**
-     * @return The summary {@link StatusBarNotification} if one is showing.
-     */
-    @TargetApi(Build.VERSION_CODES.M)
-    private static StatusBarNotification getSummaryNotification(NotificationManager manager) {
-        if (!useForegroundService()) return null;
-
-        StatusBarNotification[] notifications = manager.getActiveNotifications();
-        for (StatusBarNotification notification : notifications) {
-            boolean isDownloadsGroup = TextUtils.equals(notification.getNotification().getGroup(),
-                    NotificationConstants.GROUP_DOWNLOADS);
-            boolean isSummaryNotification =
-                    notification.getId() == NotificationConstants.NOTIFICATION_ID_DOWNLOAD_SUMMARY;
-            if (isDownloadsGroup && isSummaryNotification) return notification;
-        }
-
-        return null;
-    }
-
-    /**
-     * Cancels the existing summary notification.  Moved to a helper method for test mocking.
-     */
-    @VisibleForTesting
-    void cancelSummaryNotification() {
-        mNotificationManager.cancel(NotificationConstants.NOTIFICATION_ID_DOWNLOAD_SUMMARY);
-    }
-
-    /**
-     * Check all current notifications and hide the summary notification if we have no downloads
-     * notifications left.  On Android if the user swipes away the last download notification the
-     * summary will be dismissed.  But if the last downloads notification is dismissed via
-     * {@link NotificationManager#cancel(int)}, the summary will remain, so we need to check and
-     * manually remove it ourselves.
-     * @param notificationIdToIgnore Canceling a notification and querying for the current list of
-     *                               active notifications isn't synchronous.  Pass a notification id
-     *                               here if there is a notification that should be assumed gone.
-     *                               Or pass -1 if no notification fits that criteria.
-     */
-    @SuppressLint("NewApi") // useForegroundService guards StatusBarNotification.getNotification
-    boolean hideSummaryNotificationIfNecessary(int notificationIdToIgnore) {
-        Log.w(TAG, "hideSummaryNotificationIfNecessary id: " + notificationIdToIgnore);
-        if (mDownloadsInProgress.size() > 0) return false;
-
-        if (useForegroundService()) {
-            if (hasDownloadNotificationsInternal(notificationIdToIgnore)) return false;
-
-            StatusBarNotification notification = getSummaryNotification(mNotificationManager);
-            if (notification != null) {
-                // We have a valid summary notification, but how we dismiss it depends on whether or
-                // not it is currently bound to this service via startForeground(...).
-                if ((notification.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE)
-                        != 0) {
-                    // If we are a foreground service and we are hiding the notification, we have no
-                    // other downloads notifications showing, so we need to remove the notification
-                    // and unregister it from this service at the same time.
-                    stopForegroundInternal(true);
-                } else {
-                    // If we are not a foreground service, remove the notification via the
-                    // NotificationManager.  The notification is not bound to this service, so any
-                    // call to stopForeground() won't affect the notification.
-                    cancelSummaryNotification();
-                }
-            } else {
-                // If we don't have a valid summary, just guarantee that we aren't in the foreground
-                // for safety.  Still try to remove the summary notification to make sure it's gone.
-                // This is because querying for it might fail if we have just recently started up
-                // and began showing it.  This might leave us in a bad state if the cancel request
-                // fails inside the framework.
-                // TODO(dtrainor): Add a way to attempt to automatically clean up the notification
-                // shortly after this.
-                stopForegroundInternal(true);
-            }
-        } else {
-            // If we're not using a foreground service, just shut down after we are no longer
-            // tracking any downloads.
-            if (mDownloadSharedPreferenceHelper.getEntries().size() > 0) return false;
-        }
-
-        // Stop the service which should start the destruction process.  At this point we should be
-        // a background service.  We might not be unbound from any clients.  When they unbind we
-        // will shut down.  That is okay because they will only unbind from us when they are ok with
-        // us going away (e.g. we shouldn't be unbound while in the foreground).
-        stopSelf();
-
-        // Record instance of service being stopped intentionally.
-        DownloadNotificationUmaHelper.recordServiceStoppedHistogram(
-                DownloadNotificationUmaHelper.ServiceStopped.STOPPED, false /* withForeground */);
-        return true;
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return mBinder;
-    }
-
-    /**
-     * Helper method to update the remaining number of background resumption attempts left.
-     */
-    private void updateResumptionAttemptLeft() {
-        SharedPreferences.Editor editor = mSharedPrefs.edit();
-        editor.putInt(KEY_AUTO_RESUMPTION_ATTEMPT_LEFT, mNumAutoResumptionAttemptLeft);
-        editor.apply();
-    }
-
-    /**
-     * Helper method to clear the remaining number of background resumption attempts left.
-     */
-    static void clearResumptionAttemptLeft() {
-        SharedPreferences SharedPrefs = ContextUtils.getAppSharedPreferences();
-        SharedPreferences.Editor editor = SharedPrefs.edit();
-        editor.remove(KEY_AUTO_RESUMPTION_ATTEMPT_LEFT);
-        editor.apply();
-    }
-
-    /**
-     * Adds or updates an in-progress download notification.
-     * @param id                      The {@link ContentId} of the download.
-     * @param fileName                File name of the download.
-     * @param progress                The current download progress.
-     * @param bytesReceived           Total number of bytes received.
-     * @param timeRemainingInMillis   Remaining download time in milliseconds.
-     * @param startTime               Time when download started.
-     * @param isOffTheRecord          Whether the download is off the record.
-     * @param canDownloadWhileMetered Whether the download can happen in metered network.
-     * @param isTransient             Whether or not clicking on the download should launch
-     *                                downloads home.
-     * @param icon                    A {@link Bitmap} to be used as the large icon for display.
-     */
-    @VisibleForTesting
-    public void notifyDownloadProgress(ContentId id, String fileName, Progress progress,
-            long bytesReceived, long timeRemainingInMillis, long startTime, boolean isOffTheRecord,
-            boolean canDownloadWhileMetered, boolean isTransient, Bitmap icon) {
-        updateActiveDownloadNotification(id, fileName, progress, bytesReceived,
-                timeRemainingInMillis, startTime, isOffTheRecord, canDownloadWhileMetered, false,
-                isTransient, icon);
-    }
-
-    /**
-     * Adds or updates a pending download notification.
-     * @param id                      The {@link ContentId} of the download.
-     * @param fileName                File name of the download.
-     * @param isOffTheRecord          Whether the download is off the record.
-     * @param canDownloadWhileMetered Whether the download can happen in metered network.
-     * @param isTransient             Whether or not clicking on the download should launch
-     *                                downloads home.
-     * @param icon                    A {@link Bitmap} to be used as the large icon for display.
-     */
-    private void notifyDownloadPending(ContentId id, String fileName, boolean isOffTheRecord,
-            boolean canDownloadWhileMetered, boolean isTransient, Bitmap icon) {
-        updateActiveDownloadNotification(id, fileName, Progress.createIndeterminateProgress(), 0, 0,
-                0, isOffTheRecord, canDownloadWhileMetered, true, isTransient, icon);
-    }
-
-    /**
-     * Helper method to update the notification for an active download, the download is either in
-     * progress or pending.
-     * @param id                      The {@link ContentId} of the download.
-     * @param fileName                File name of the download.
-     * @param progress                The current download progress.
-     * @param bytesReceived           Total number of bytes received.
-     * @param timeRemainingInMillis   Remaining download time in milliseconds or -1 if it is
-     *                                unknown.
-     * @param startTime               Time when download started.
-     * @param isOffTheRecord          Whether the download is off the record.
-     * @param canDownloadWhileMetered Whether the download can happen in metered network.
-     * @param isDownloadPending       Whether the download is pending.
-     * @param isTransient             Whether or not clicking on the download should launch
-     *                                downloads home.
-     * @param icon                    A {@link Bitmap} to be used as the large icon for display.
-     */
-    private void updateActiveDownloadNotification(ContentId id, String fileName, Progress progress,
-            long bytesReceived, long timeRemainingInMillis, long startTime, boolean isOffTheRecord,
-            boolean canDownloadWhileMetered, boolean isDownloadPending, boolean isTransient,
-            Bitmap icon) {
-        boolean indeterminate = (progress.isIndeterminate() || isDownloadPending);
-        String contentText = null;
-        if (isDownloadPending) {
-            contentText = ContextUtils.getApplicationContext().getResources().getString(
-                    R.string.download_notification_pending);
-        } else if (indeterminate || timeRemainingInMillis < 0) {
-            // TODO(dimich): Enable the byte count back in M59. See bug 704049 for more info and
-            // details of what was temporarily reverted (for M58).
-            contentText = ContextUtils.getApplicationContext().getResources().getString(
-                    R.string.download_started);
-        } else {
-            contentText = DownloadUtils.getTimeOrFilesLeftString(
-                    ContextUtils.getApplicationContext(), progress, timeRemainingInMillis);
-        }
-        int resId = isDownloadPending ? R.drawable.ic_download_pending
-                                      : android.R.drawable.stat_sys_download;
-        ChromeNotificationBuilder builder = buildNotification(resId, fileName, contentText);
-        builder.setOngoing(true);
-        builder.setPriorityBeforeO(NotificationCompat.PRIORITY_HIGH);
-
-        // Avoid animations while the download isn't progressing.
-        if (!isDownloadPending) {
-            builder.setProgress(100, indeterminate ? -1 : progress.getPercentage(), indeterminate);
-        }
-
-        if (!indeterminate && !LegacyHelpers.isLegacyOfflinePage(id)) {
-            String percentText = DownloadUtils.getPercentageString(progress.getPercentage());
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
-                builder.setSubText(percentText);
-            } else {
-                builder.setContentInfo(percentText);
-            }
-        }
-        int notificationId = getNotificationId(id);
-        if (startTime > 0) builder.setWhen(startTime);
-
-        if (!isTransient) {
-            // Clicking on an in-progress download sends the user to see all their downloads.
-            Intent downloadHomeIntent = buildActionIntent(ContextUtils.getApplicationContext(),
-                    ACTION_NOTIFICATION_CLICKED, null, isOffTheRecord);
-            builder.setContentIntent(
-                    PendingIntent.getBroadcast(ContextUtils.getApplicationContext(), notificationId,
-                            downloadHomeIntent, PendingIntent.FLAG_UPDATE_CURRENT));
-        }
-        builder.setAutoCancel(false);
-        if (icon != null && !isOffTheRecord) builder.setLargeIcon(icon);
-
-        Intent pauseIntent = buildActionIntent(
-                ContextUtils.getApplicationContext(), ACTION_DOWNLOAD_PAUSE, id, isOffTheRecord);
-        builder.addAction(R.drawable.ic_pause_white_24dp,
-                ContextUtils.getApplicationContext().getResources().getString(
-                        R.string.download_notification_pause_button),
-                buildPendingIntent(pauseIntent, notificationId));
-
-        Intent cancelIntent = buildActionIntent(
-                ContextUtils.getApplicationContext(), ACTION_DOWNLOAD_CANCEL, id, isOffTheRecord);
-        builder.addAction(R.drawable.btn_close_white,
-                ContextUtils.getApplicationContext().getResources().getString(
-                        R.string.download_notification_cancel_button),
-                buildPendingIntent(cancelIntent, notificationId));
-
-        updateNotification(notificationId, builder.build(), id,
-                new DownloadSharedPreferenceEntry(id, notificationId, isOffTheRecord,
-                        canDownloadWhileMetered, fileName, true, isTransient));
-        startTrackingInProgressDownload(id);
-    }
-
-    /**
-     * Removes a download notification and all associated tracking.  This method relies on the
-     * caller to provide the notification id, which is useful in the case where the internal
-     * tracking doesn't exist (e.g. in the case of a successful download, where we show the download
-     * completed notification and remove our internal state tracking).
-     * @param notificationId Notification ID of the download
-     * @param id The {@link ContentId} of the download.
-     */
-    public void cancelNotification(int notificationId, ContentId id) {
-        mNotificationManager.cancel(NOTIFICATION_NAMESPACE, notificationId);
-        mDownloadSharedPreferenceHelper.removeSharedPreferenceEntry(id);
-
-        // Since we are about to go through the process of validating whether or not we can shut
-        // down, don't stop foreground if we have no download notifications left to show.  Hiding
-        // the summary will take care of that for us.
-        stopTrackingInProgressDownload(id, hasDownloadNotificationsInternal(notificationId));
-        if (!hideSummaryNotificationIfNecessary(notificationId)) {
-            updateSummaryIcon(ContextUtils.getApplicationContext(), mNotificationManager,
-                    notificationId, null);
-        }
-    }
-
-    /**
-     * Called when a download is canceled.  This method uses internal tracking to try to find the
-     * notification id to cancel.
-     * @param id The {@link ContentId} of the download.
-     */
-    @VisibleForTesting
-    public void notifyDownloadCanceled(ContentId id) {
-        DownloadSharedPreferenceEntry entry =
-                mDownloadSharedPreferenceHelper.getDownloadSharedPreferenceEntry(id);
-        if (entry == null) {
-            // In case notifyDownloadCanceled was called after the entry has already been removed.
-            stopTrackingInProgressDownload(id, hasDownloadNotificationsInternal(-1));
-            return;
-        }
-        cancelNotification(entry.notificationId, id);
-    }
-
-    /**
-     * Change a download notification to paused state.
-     * @param id              The {@link ContentId} of the download.
-     * @param fileName        File name of the download.
-     * @param isResumable     Whether download can be resumed.
-     * @param isAutoResumable Whether download is can be resumed automatically.
-     * @param isOffTheRecord  Whether the download is off the record.
-     * @param isTransient     Whether or not clicking on the download should launch downloads home.
-     * @param icon            A {@link Bitmap} to be used as the large icon for display.
-     */
-    public void notifyDownloadPaused(ContentId id, String fileName, boolean isResumable,
-            boolean isAutoResumable, boolean isOffTheRecord, boolean isTransient, Bitmap icon) {
-        DownloadSharedPreferenceEntry entry =
-                mDownloadSharedPreferenceHelper.getDownloadSharedPreferenceEntry(id);
-        if (!isResumable) {
-            notifyDownloadFailed(
-                    id, fileName, isOffTheRecord ? null : icon, FailState.CANNOT_DOWNLOAD);
-            return;
-        }
-        // Download is already paused.
-        if (entry != null && !entry.isAutoResumable) {
-            // Shutdown the service in case it was restarted unnecessarily.
-            stopTrackingInProgressDownload(id, true);
-            return;
-        }
-        boolean canDownloadWhileMetered = entry == null ? false : entry.canDownloadWhileMetered;
-        // If download is interrupted due to network disconnection, show download pending state.
-        if (isAutoResumable) {
-            notifyDownloadPending(
-                    id, fileName, isOffTheRecord, canDownloadWhileMetered, isTransient, icon);
-            stopTrackingInProgressDownload(id, true);
-            return;
-        }
-
-        String contentText = ContextUtils.getApplicationContext().getResources().getString(
-                R.string.download_notification_paused);
-        ChromeNotificationBuilder builder =
-                buildNotification(R.drawable.ic_download_pause, fileName, contentText);
-        int notificationId = entry == null ? getNotificationId(id) : entry.notificationId;
-        if (!isTransient) {
-            // Clicking on an in-progress download sends the user to see all their downloads.
-            Intent downloadHomeIntent = buildActionIntent(
-                    ContextUtils.getApplicationContext(), ACTION_NOTIFICATION_CLICKED, null, false);
-            builder.setContentIntent(
-                    PendingIntent.getBroadcast(ContextUtils.getApplicationContext(), notificationId,
-                            downloadHomeIntent, PendingIntent.FLAG_UPDATE_CURRENT));
-        }
-        builder.setAutoCancel(false);
-        if (icon != null && !isOffTheRecord) builder.setLargeIcon(icon);
-
-        Intent resumeIntent = buildActionIntent(
-                ContextUtils.getApplicationContext(), ACTION_DOWNLOAD_RESUME, id, isOffTheRecord);
-        builder.addAction(R.drawable.ic_file_download_white_24dp,
-                ContextUtils.getApplicationContext().getResources().getString(
-                        R.string.download_notification_resume_button),
-                buildPendingIntent(resumeIntent, notificationId));
-
-        Intent cancelIntent = buildActionIntent(
-                ContextUtils.getApplicationContext(), ACTION_DOWNLOAD_CANCEL, id, isOffTheRecord);
-        builder.addAction(R.drawable.btn_close_white,
-                ContextUtils.getApplicationContext().getResources().getString(
-                        R.string.download_notification_cancel_button),
-                buildPendingIntent(cancelIntent, notificationId));
-        PendingIntent deleteIntent = isTransient ? buildPendingIntent(cancelIntent, notificationId)
-                                                 : buildSummaryIconIntent(notificationId);
-        builder.setDeleteIntent(deleteIntent);
-
-        updateNotification(notificationId, builder.build(), id,
-                new DownloadSharedPreferenceEntry(id, notificationId, isOffTheRecord,
-                        canDownloadWhileMetered, fileName, isAutoResumable, isTransient));
-        stopTrackingInProgressDownload(id, true);
-    }
-
-    /**
-     * Add a download successful notification.
-     * @param id                  The {@link ContentId} of the download.
-     * @param filePath            Full path to the download.
-     * @param fileName            Filename of the download.
-     * @param systemDownloadId    Download ID assigned by system DownloadManager.
-     * @param isSupportedMimeType Whether the MIME type can be viewed inside browser.
-     * @param isOpenable          Whether or not this download can be opened.
-     * @param icon                A {@link Bitmap} to be used as the large icon for display.
-     * @param originalUrl         The original url of the downloaded file.
-     * @param referrer            Referrer of the downloaded file.
-     * @return                    ID of the successful download notification. Used for removing the
-     *                            notification when user click on the snackbar.
-     */
-    @VisibleForTesting
-    public int notifyDownloadSuccessful(ContentId id, String filePath, String fileName,
-            long systemDownloadId, boolean isOffTheRecord, boolean isSupportedMimeType,
-            boolean isOpenable, Bitmap icon, String originalUrl, String referrer) {
-        int notificationId = getNotificationId(id);
-        ChromeNotificationBuilder builder = buildNotification(R.drawable.offline_pin, fileName,
-                ContextUtils.getApplicationContext().getResources().getString(
-                        R.string.download_notification_completed));
-        ComponentName component =
-                new ComponentName(ContextUtils.getApplicationContext().getPackageName(),
-                        DownloadBroadcastReceiver.class.getName());
-
-        if (isOpenable) {
-            Intent intent = null;
-            if (LegacyHelpers.isLegacyDownload(id)) {
-                intent = new Intent(ACTION_NOTIFICATION_CLICKED);
-                long[] idArray = {systemDownloadId};
-                intent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS, idArray);
-                intent.putExtra(EXTRA_DOWNLOAD_FILE_PATH, filePath);
-                intent.putExtra(EXTRA_IS_SUPPORTED_MIME_TYPE, isSupportedMimeType);
-                intent.putExtra(EXTRA_IS_OFF_THE_RECORD, isOffTheRecord);
-                intent.putExtra(EXTRA_DOWNLOAD_CONTENTID_ID, id.id);
-                intent.putExtra(EXTRA_DOWNLOAD_CONTENTID_NAMESPACE, id.namespace);
-                intent.putExtra(NotificationConstants.EXTRA_NOTIFICATION_ID, notificationId);
-                MediaViewerUtils.setOriginalUrlAndReferralExtraToIntent(
-                        intent, originalUrl, referrer);
-            } else {
-                intent = buildActionIntent(
-                        ContextUtils.getApplicationContext(), ACTION_DOWNLOAD_OPEN, id, false);
-            }
-
-            intent.setComponent(component);
-            builder.setContentIntent(
-                    PendingIntent.getBroadcast(ContextUtils.getApplicationContext(), notificationId,
-                            intent, PendingIntent.FLAG_UPDATE_CURRENT));
-        }
-        if (icon == null && mDownloadSuccessLargeIcon == null) {
-            Bitmap bitmap = BitmapFactory.decodeResource(
-                    ContextUtils.getApplicationContext().getResources(), R.drawable.offline_pin);
-            mDownloadSuccessLargeIcon = getLargeNotificationIcon(bitmap);
-        }
-        builder.setDeleteIntent(buildSummaryIconIntent(notificationId));
-        builder.setLargeIcon(icon != null && !isOffTheRecord ? icon : mDownloadSuccessLargeIcon);
-        updateNotification(notificationId, builder.build(), id, null);
-        stopTrackingInProgressDownload(id, true);
-        return notificationId;
-    }
-
-    /**
-     * Add a download failed notification.
-     * @param id       The {@link ContentId} of the download.
-     * @param fileName Filename of the download.
-     * @param icon     A {@link Bitmap} to be used as the large icon for display.
-     * @param failState Reason of the failure.
-     */
-    @VisibleForTesting
-    public void notifyDownloadFailed(
-            ContentId id, String fileName, Bitmap icon, @FailState int failState) {
-        // If the download is not in history db, fileName could be empty. Get it from
-        // SharedPreferences.
-        if (TextUtils.isEmpty(fileName)) {
-            DownloadSharedPreferenceEntry entry =
-                    mDownloadSharedPreferenceHelper.getDownloadSharedPreferenceEntry(id);
-            if (entry == null) return;
-            fileName = entry.fileName;
-        }
-
-        int notificationId = getNotificationId(id);
-        String message = DownloadUtils.getFailStatusString(failState);
-        ChromeNotificationBuilder builder =
-                buildNotification(android.R.drawable.stat_sys_download_done, fileName, message);
-        if (icon != null) builder.setLargeIcon(icon);
-        builder.setDeleteIntent(buildSummaryIconIntent(notificationId));
-        updateNotification(notificationId, builder.build(), id, null);
-        stopTrackingInProgressDownload(id, true);
-    }
-
-    /**
-     * Helper method to build a PendingIntent from the provided intent.
-     * @param intent Intent to broadcast.
-     * @param notificationId ID of the notification.
-     */
-    private PendingIntent buildPendingIntent(Intent intent, int notificationId) {
-        return PendingIntent.getBroadcast(ContextUtils.getApplicationContext(), notificationId,
-                intent, PendingIntent.FLAG_UPDATE_CURRENT);
-    }
-
-    private PendingIntent buildSummaryIconIntent(int notificationId) {
-        Intent intent =
-                new Intent(ContextUtils.getApplicationContext(), DownloadBroadcastReceiver.class);
-        intent.setAction(ACTION_DOWNLOAD_UPDATE_SUMMARY_ICON);
-        return buildPendingIntent(intent, notificationId);
-    }
-
-    /**
-     * Helper method to build an download action Intent from the provided information.
-     * @param context {@link Context} to pull resources from.
-     * @param action Download action to perform.
-     * @param id The {@link ContentId} of the download.
-     * @param isOffTheRecord Whether the download is incognito.
-     */
-    static Intent buildActionIntent(
-            Context context, String action, ContentId id, boolean isOffTheRecord) {
-        ComponentName component = new ComponentName(
-                context.getPackageName(), DownloadBroadcastReceiver.class.getName());
-        Intent intent = new Intent(action);
-        intent.setComponent(component);
-        intent.putExtra(EXTRA_DOWNLOAD_CONTENTID_ID, id != null ? id.id : "");
-        intent.putExtra(EXTRA_DOWNLOAD_CONTENTID_NAMESPACE, id != null ? id.namespace : "");
-        intent.putExtra(EXTRA_IS_OFF_THE_RECORD, isOffTheRecord);
-        return intent;
-    }
-
-    /**
-     * Builds a notification to be displayed.
-     * @param iconId Id of the notification icon.
-     * @param title Title of the notification.
-     * @param contentText Notification content text to be displayed.
-     * @return notification builder that builds the notification to be displayed
-     */
-    private ChromeNotificationBuilder buildNotification(
-            int iconId, String title, String contentText) {
-        Bundle extras = new Bundle();
-        extras.putInt(EXTRA_NOTIFICATION_BUNDLE_ICON_ID, iconId);
-
-        ChromeNotificationBuilder builder =
-                NotificationBuilderFactory
-                        .createChromeNotificationBuilder(
-                                true /* preferCompat */, ChannelDefinitions.ChannelId.DOWNLOADS)
-                        .setContentTitle(
-                                DownloadUtils.getAbbreviatedFileName(title, MAX_FILE_NAME_LENGTH))
-                        .setSmallIcon(iconId)
-                        .setLocalOnly(true)
-                        .setAutoCancel(true)
-                        .setContentText(contentText)
-                        .setGroup(NotificationConstants.GROUP_DOWNLOADS)
-                        .addExtras(extras);
-        return builder;
-    }
-
-    private Bitmap getLargeNotificationIcon(Bitmap bitmap) {
-        Resources resources = ContextUtils.getApplicationContext().getResources();
-        int height = (int) resources.getDimension(android.R.dimen.notification_large_icon_height);
-        int width = (int) resources.getDimension(android.R.dimen.notification_large_icon_width);
-        final OvalShape circle = new OvalShape();
-        circle.resize(width, height);
-        final Paint paint = new Paint();
-        paint.setColor(ApiCompatibilityUtils.getColor(resources, R.color.google_blue_grey_500));
-
-        final Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(result);
-        circle.draw(canvas, paint);
-        float leftOffset = (width - bitmap.getWidth()) / 2f;
-        float topOffset = (height - bitmap.getHeight()) / 2f;
-        if (leftOffset >= 0 && topOffset >= 0) {
-            canvas.drawBitmap(bitmap, leftOffset, topOffset, null);
-        } else {
-            // Scale down the icon into the notification icon dimensions
-            canvas.drawBitmap(bitmap,
-                    new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()),
-                    new Rect(0, 0, width, height),
-                    null);
-        }
-        return result;
-    }
-
-    /**
-     * Retrieves DownloadSharedPreferenceEntry from a download action intent.
-     * @param intent Intent that contains the download action.
-     */
-    private DownloadSharedPreferenceEntry getDownloadEntryFromIntent(Intent intent) {
-        if (ACTION_DOWNLOAD_RESUME_ALL.equals(intent.getAction())) return null;
-        return mDownloadSharedPreferenceHelper.getDownloadSharedPreferenceEntry(
-                getContentIdFromIntent(intent));
-    }
-
-    /**
-     * Helper method to launch the browser process and handle a download operation that is included
-     * in the given intent.
-     * @param intent Intent with the download operation.
-     */
-    private void handleDownloadOperation(final Intent intent) {
-        // Process updating the summary notification first.  This has no impact on a specific
-        // download.
-        if (ACTION_DOWNLOAD_UPDATE_SUMMARY_ICON.equals(intent.getAction())) {
-            updateSummaryIcon(ContextUtils.getApplicationContext(), mNotificationManager, -1, null);
-            hideSummaryNotificationIfNecessary(-1);
-            return;
-        }
-
-        // TODO(qinmin): Figure out how to properly handle this case.
-        final ContentId id = getContentIdFromIntent(intent);
-        final DownloadSharedPreferenceEntry entry = getDownloadEntryFromIntent(intent);
-        if (entry == null
-                && !(id != null && LegacyHelpers.isLegacyOfflinePage(id)
-                           && TextUtils.equals(intent.getAction(), ACTION_DOWNLOAD_OPEN))
-                && !(TextUtils.equals(intent.getAction(), ACTION_NOTIFICATION_CLICKED))
-                && !(TextUtils.equals(intent.getAction(), ACTION_DOWNLOAD_RESUME_ALL))) {
-            handleDownloadOperationForMissingNotification(intent);
-            hideSummaryNotificationIfNecessary(-1);
-            return;
-        }
-
-        if (ACTION_DOWNLOAD_PAUSE.equals(intent.getAction())) {
-            // If browser process already goes away, the download should have already paused. Do
-            // nothing in that case.
-            if (!DownloadManagerService.hasDownloadManagerService()) {
-                // TODO(dtrainor): Should we spin up native to make sure we have the icon?  Or maybe
-                // build a Java cache for easy access.
-                notifyDownloadPaused(entry.id, entry.fileName, !entry.isOffTheRecord, false,
-                        entry.isOffTheRecord, entry.isTransient, null);
-                hideSummaryNotificationIfNecessary(-1);
-                return;
-            }
-        } else if (ACTION_DOWNLOAD_RESUME.equals(intent.getAction())) {
-            // If user manually resumes a download, update the network type if it
-            // is not metered previously.
-            boolean canDownloadWhileMetered = entry.canDownloadWhileMetered
-                    || DownloadManagerService.isActiveNetworkMetered(
-                               ContextUtils.getApplicationContext());
-            // Update the SharedPreference entry.
-            mDownloadSharedPreferenceHelper.addOrReplaceSharedPreferenceEntry(
-                    new DownloadSharedPreferenceEntry(entry.id, entry.notificationId,
-                            entry.isOffTheRecord, canDownloadWhileMetered, entry.fileName, true,
-                            entry.isTransient));
-        } else if (ACTION_DOWNLOAD_RESUME_ALL.equals(intent.getAction())
-                && (mDownloadSharedPreferenceHelper.getEntries().isEmpty()
-                           || DownloadManagerService.hasDownloadManagerService())) {
-            hideSummaryNotificationIfNecessary(-1);
-            return;
-        } else if (ACTION_DOWNLOAD_OPEN.equals(intent.getAction())) {
-            // TODO(fgorski): Do we even need to do anything special here, before we launch Chrome?
-        } else if (ACTION_DOWNLOAD_CANCEL.equals(intent.getAction())
-                && IntentUtils.safeGetBooleanExtra(intent, EXTRA_NOTIFICATION_DISMISSED, false)) {
-            // User canceled a download by dismissing its notification from earlier versions, ignore
-            // it. TODO(qinmin): remove this else-if block after M60.
-            return;
-        }
-
-        BrowserParts parts = new EmptyBrowserParts() {
-            @Override
-            public void finishNativeInitialization() {
-                DownloadServiceDelegate downloadServiceDelegate =
-                        isDownloadOpenOrNotificationClickedAction(intent) ? null
-                                                                          : getServiceDelegate(id);
-                if (ACTION_DOWNLOAD_CANCEL.equals(intent.getAction())) {
-                    // TODO(qinmin): Alternatively, we can delete the downloaded content on
-                    // SD card, and remove the download ID from the SharedPreferences so we
-                    // don't need to restart the browser process. http://crbug.com/579643.
-                    cancelNotification(entry.notificationId, entry.id);
-                    downloadServiceDelegate.cancelDownload(entry.id, entry.isOffTheRecord);
-                    for (Observer observer : mObservers) {
-                        observer.onDownloadCanceled(entry.id);
-                    }
-                } else if (ACTION_DOWNLOAD_PAUSE.equals(intent.getAction())) {
-                    // TODO(dtrainor): Consider hitting the delegate and rely on that to update the
-                    // state.
-                    notifyDownloadPaused(entry.id, entry.fileName, true, false,
-                            entry.isOffTheRecord, entry.isTransient, null);
-                    downloadServiceDelegate.pauseDownload(entry.id, entry.isOffTheRecord);
-                } else if (ACTION_DOWNLOAD_RESUME.equals(intent.getAction())) {
-                    // TODO(dtrainor): Consider hitting the delegate and rely on that to update the
-                    // state.
-                    notifyDownloadPending(entry.id, entry.fileName, entry.isOffTheRecord,
-                            entry.canDownloadWhileMetered, entry.isTransient, null);
-                    downloadServiceDelegate.resumeDownload(
-                            entry.id, entry.buildDownloadItem(), true);
-                } else if (ACTION_DOWNLOAD_RESUME_ALL.equals(intent.getAction())) {
-                    assert entry == null;
-                    resumeAllPendingDownloads();
-                } else if (ACTION_DOWNLOAD_OPEN.equals(intent.getAction())) {
-                    ContentId id = getContentIdFromIntent(intent);
-                    if (id != null) {
-                        OfflineContentAggregatorNotificationBridgeUiFactory.instance().openItem(id);
-                    }
-                } else if (ACTION_NOTIFICATION_CLICKED.equals(intent.getAction())) {
-                    openDownload(ContextUtils.getApplicationContext(), intent);
-                } else {
-                    Log.e(TAG, "Unrecognized intent action.", intent);
-                }
-                if (!isDownloadOpenOrNotificationClickedAction(intent)) {
-                    downloadServiceDelegate.destroyServiceDelegate();
-                }
-
-                hideSummaryNotificationIfNecessary(ACTION_DOWNLOAD_CANCEL.equals(intent.getAction())
-                                ? entry.notificationId
-                                : -1);
-            }
-
-            @Override
-            public boolean startServiceManagerOnly() {
-                return ServiceManagerStartupUtils.canStartServiceManager(
-                               ChromeFeatureList.SERVICE_MANAGER_FOR_DOWNLOAD)
-                        && !ACTION_DOWNLOAD_OPEN.equals(intent.getAction());
-            }
-        };
-        try {
-            ChromeBrowserInitializer.getInstance(ContextUtils.getApplicationContext())
-                    .handlePreNativeStartup(parts);
-            ChromeBrowserInitializer.getInstance(ContextUtils.getApplicationContext())
-                    .handlePostNativeStartup(true, parts);
-        } catch (ProcessInitException e) {
-            Log.e(TAG, "Unable to load native library.", e);
-            ChromeApplication.reportStartupErrorAndExit(e);
-        }
-    }
-
-    private boolean isDownloadOpenOrNotificationClickedAction(Intent intent) {
-        return ACTION_DOWNLOAD_OPEN.equals(intent.getAction())
-                || ACTION_NOTIFICATION_CLICKED.equals(intent.getAction());
-    }
-
-    /**
-     * Called to open a particular download item.  Falls back to opening Download Home.
-     * @param context Context of the receiver.
-     * @param intent Intent from the android DownloadManager.
-     */
-    private void openDownload(final Context context, Intent intent) {
-        long ids[] =
-                intent.getLongArrayExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS);
-        if (ids == null || ids.length == 0) {
-            DownloadManagerService.openDownloadsPage(context);
-            return;
-        }
-
-        long id = ids[0];
-        Uri uri = DownloadManagerDelegate.getContentUriFromDownloadManager(context, id);
-        if (uri == null) {
-            DownloadManagerService.openDownloadsPage(context);
-            return;
-        }
-
-        String downloadFilename = IntentUtils.safeGetStringExtra(
-                intent, DownloadNotificationService.EXTRA_DOWNLOAD_FILE_PATH);
-        boolean isSupportedMimeType = IntentUtils.safeGetBooleanExtra(
-                intent, DownloadNotificationService.EXTRA_IS_SUPPORTED_MIME_TYPE, false);
-        boolean isOffTheRecord = IntentUtils.safeGetBooleanExtra(
-                intent, DownloadNotificationService.EXTRA_IS_OFF_THE_RECORD, false);
-        String originalUrl = IntentUtils.safeGetStringExtra(intent, Intent.EXTRA_ORIGINATING_URI);
-        String referrer = IntentUtils.safeGetStringExtra(intent, Intent.EXTRA_REFERRER);
-        ContentId contentId = DownloadNotificationService.getContentIdFromIntent(intent);
-        DownloadManagerService.openDownloadedContent(context, downloadFilename, isSupportedMimeType,
-                isOffTheRecord, contentId.id, id, originalUrl, referrer,
-                DownloadMetrics.DownloadOpenSource.NOTIFICATION);
-    }
-
-    /**
-     * Handles operations for downloads that the DownloadNotificationService is unaware of.
-     *
-     * This can happen because the DownloadNotificationService learn about downloads later than
-     * Download Home does, and may not yet have a DownloadSharedPreferenceEntry for the item.
-     *
-     * TODO(qinmin): Figure out how to fix the SharedPreferences so that it properly tracks entries.
-     */
-    private void handleDownloadOperationForMissingNotification(Intent intent) {
-        // This function should only be called via Download Home, but catch this case to be safe.
-        if (!DownloadManagerService.hasDownloadManagerService()) return;
-
-        String action = intent.getAction();
-        ContentId id = getContentIdFromIntent(intent);
-        boolean isOffTheRecord =
-                IntentUtils.safeGetBooleanExtra(intent, EXTRA_IS_OFF_THE_RECORD, false);
-        if (!LegacyHelpers.isLegacyDownload(id)) return;
-
-        // Pass information directly to the DownloadManagerService.
-        if (TextUtils.equals(action, ACTION_DOWNLOAD_CANCEL)) {
-            getServiceDelegate(id).cancelDownload(id, isOffTheRecord);
-        } else if (TextUtils.equals(action, ACTION_DOWNLOAD_PAUSE)) {
-            getServiceDelegate(id).pauseDownload(id, isOffTheRecord);
-        } else if (TextUtils.equals(action, ACTION_DOWNLOAD_RESUME)) {
-            DownloadInfo info = new DownloadInfo.Builder()
-                                        .setDownloadGuid(id.id)
-                                        .setIsOffTheRecord(isOffTheRecord)
-                                        .build();
-            getServiceDelegate(id).resumeDownload(id, new DownloadItem(false, info), true);
-        }
-    }
-
-    /**
-     * Gets appropriate download delegate that can handle interactions with download item referred
-     * to by the entry.
-     * @param id The {@link ContentId} to grab the delegate for.
-     * @return delegate for interactions with the entry
-     */
-    DownloadServiceDelegate getServiceDelegate(ContentId id) {
-        return LegacyHelpers.isLegacyDownload(id)
-                ? DownloadManagerService.getDownloadManagerService()
-                : OfflineContentAggregatorNotificationBridgeUiFactory.instance();
-    }
-
-    @VisibleForTesting
-    void updateNotification(int id, Notification notification) {
-        // Disabling StrictMode to avoid violations (crbug.com/809864).
-        try (StrictModeContext unused = StrictModeContext.allowDiskReads()) {
-            mNotificationManager.notify(NOTIFICATION_NAMESPACE, id, notification);
-        }
-    }
-
-    private void updateNotification(int notificationId, Notification notification, ContentId id,
-            DownloadSharedPreferenceEntry entry) {
-        updateNotification(notificationId, notification);
-        trackNotificationUma(id, notification);
-
-        if (entry != null) {
-            mDownloadSharedPreferenceHelper.addOrReplaceSharedPreferenceEntry(entry);
-        } else {
-            mDownloadSharedPreferenceHelper.removeSharedPreferenceEntry(id);
-        }
-        updateSummaryIcon(ContextUtils.getApplicationContext(), mNotificationManager, -1,
-                new Pair<Integer, Notification>(notificationId, notification));
-    }
-
-    private void trackNotificationUma(ContentId id, Notification notification) {
-        // Check if we already have an entry in the DownloadSharedPreferenceHelper.  This is a
-        // reasonable indicator for whether or not a notification is already showing (or at least if
-        // we had built one for this download before.
-        if (mDownloadSharedPreferenceHelper.hasEntry(id)) return;
-        NotificationUmaTracker.getInstance().onNotificationShown(
-                LegacyHelpers.isLegacyOfflinePage(id)
-                        ? NotificationUmaTracker.SystemNotificationType.DOWNLOAD_PAGES
-                        : NotificationUmaTracker.SystemNotificationType.DOWNLOAD_FILES,
-                notification);
-
-        // Record number of other notifications when there's a new notification.
-        DownloadNotificationUmaHelper.recordExistingNotificationsCountHistogram(
-                mDownloadSharedPreferenceHelper.getEntries().size(), false /* withForeground */);
-    }
-
-    /**
-     * Checks if an intent requires operations on a download.
-     * @param intent An intent to validate.
-     * @return true if the intent requires actions, or false otherwise.
-     */
-    static boolean isDownloadOperationIntent(Intent intent) {
-        if (intent == null) return false;
-        if (ACTION_DOWNLOAD_UPDATE_SUMMARY_ICON.equals(intent.getAction())) return true;
-        if (ACTION_DOWNLOAD_RESUME_ALL.equals(intent.getAction())) return true;
-        if (!ACTION_DOWNLOAD_CANCEL.equals(intent.getAction())
-                && !ACTION_DOWNLOAD_RESUME.equals(intent.getAction())
-                && !ACTION_DOWNLOAD_PAUSE.equals(intent.getAction())
-                && !ACTION_DOWNLOAD_OPEN.equals(intent.getAction())
-                && !ACTION_NOTIFICATION_CLICKED.equals(intent.getAction())) {
-            return false;
-        }
-
-        ContentId id = getContentIdFromIntent(intent);
-        if (id == null) return false;
-        return true;
-    }
-
-    private static boolean canResumeDownload(Context context, DownloadSharedPreferenceEntry entry) {
-        if (entry == null) return false;
-        if (!entry.isAutoResumable) return false;
-
-        boolean isNetworkMetered = DownloadManagerService.isActiveNetworkMetered(context);
-        return entry.canDownloadWhileMetered || !isNetworkMetered;
-    }
-
-    /**
-     * @param intent The {@link Intent} to pull from and build a {@link ContentId}.
-     * @return A {@link ContentId} built by pulling extras from {@code intent}.  This will be
-     *         {@code null} if {@code intent} is missing any required extras.
-     */
-    public static ContentId getContentIdFromIntent(Intent intent) {
-        if (!intent.hasExtra(EXTRA_DOWNLOAD_CONTENTID_ID)
-                || !intent.hasExtra(EXTRA_DOWNLOAD_CONTENTID_NAMESPACE)) {
-            return null;
-        }
-
-        return new ContentId(
-                IntentUtils.safeGetStringExtra(intent, EXTRA_DOWNLOAD_CONTENTID_NAMESPACE),
-                IntentUtils.safeGetStringExtra(intent, EXTRA_DOWNLOAD_CONTENTID_ID));
-    }
-
-    /**
-     * Resumes all pending downloads from SharedPreferences. If a download is
-     * already in progress, do nothing.
-     */
-    public void resumeAllPendingDownloads() {
-        if (!DownloadManagerService.hasDownloadManagerService()) return;
-        List<DownloadSharedPreferenceEntry> entries = mDownloadSharedPreferenceHelper.getEntries();
-        for (int i = 0; i < entries.size(); ++i) {
-            DownloadSharedPreferenceEntry entry = entries.get(i);
-            if (!canResumeDownload(ContextUtils.getApplicationContext(), entry)) continue;
-            if (mDownloadsInProgress.contains(entry.id)) continue;
-
-            notifyDownloadPending(entry.id, entry.fileName, entry.isOffTheRecord,
-                    entry.canDownloadWhileMetered, entry.isTransient, null);
-            DownloadServiceDelegate downloadServiceDelegate = getServiceDelegate(entry.id);
-            downloadServiceDelegate.resumeDownload(entry.id, entry.buildDownloadItem(), false);
-            downloadServiceDelegate.destroyServiceDelegate();
-        }
-    }
-
-    /**
-     * Return the notification ID for the given download {@link ContentId}.
-     * @param id the {@link ContentId} of the download.
-     * @return notification ID to be used.
-     */
-    private int getNotificationId(ContentId id) {
-        DownloadSharedPreferenceEntry entry =
-                mDownloadSharedPreferenceHelper.getDownloadSharedPreferenceEntry(id);
-        if (entry != null) return entry.notificationId;
-        int notificationId = mNextNotificationId;
-        mNextNotificationId = mNextNotificationId == Integer.MAX_VALUE
-                ? STARTING_NOTIFICATION_ID : mNextNotificationId + 1;
-        SharedPreferences.Editor editor = mSharedPrefs.edit();
-        editor.putInt(KEY_NEXT_DOWNLOAD_NOTIFICATION_ID, mNextNotificationId);
-        editor.apply();
-        return notificationId;
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService2.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService2.java
index e6b8107..a6dce94e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService2.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService2.java
@@ -61,6 +61,14 @@
         int FAILED = 4;
     }
 
+    public static final String ACTION_DOWNLOAD_CANCEL =
+            "org.chromium.chrome.browser.download.DOWNLOAD_CANCEL";
+    public static final String ACTION_DOWNLOAD_PAUSE =
+            "org.chromium.chrome.browser.download.DOWNLOAD_PAUSE";
+    public static final String ACTION_DOWNLOAD_RESUME =
+            "org.chromium.chrome.browser.download.DOWNLOAD_RESUME";
+    static final String ACTION_DOWNLOAD_OPEN = "org.chromium.chrome.browser.download.DOWNLOAD_OPEN";
+
     static final String EXTRA_DOWNLOAD_CONTENTID_ID =
             "org.chromium.chrome.browser.download.DownloadContentId_Id";
     static final String EXTRA_DOWNLOAD_CONTENTID_NAMESPACE =
@@ -73,14 +81,6 @@
     static final String EXTRA_DOWNLOAD_STATE_AT_CANCEL =
             "org.chromium.chrome.browser.download.OfflineItemsStateAtCancel";
 
-    static final String ACTION_DOWNLOAD_CANCEL =
-            "org.chromium.chrome.browser.download.DOWNLOAD_CANCEL";
-    static final String ACTION_DOWNLOAD_PAUSE =
-            "org.chromium.chrome.browser.download.DOWNLOAD_PAUSE";
-    static final String ACTION_DOWNLOAD_RESUME =
-            "org.chromium.chrome.browser.download.DOWNLOAD_RESUME";
-    static final String ACTION_DOWNLOAD_OPEN = "org.chromium.chrome.browser.download.DOWNLOAD_OPEN";
-
     static final String EXTRA_NOTIFICATION_BUNDLE_ICON_ID = "Chrome.NotificationBundleIconIdExtra";
     /** Notification Id starting value, to avoid conflicts from IDs used in prior versions. */
     private static final int STARTING_NOTIFICATION_ID = 1000000;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadResumptionScheduler.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadResumptionScheduler.java
index 51f5bb1..6405ba5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadResumptionScheduler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadResumptionScheduler.java
@@ -5,10 +5,8 @@
 package org.chromium.chrome.browser.download;
 
 import android.annotation.SuppressLint;
-import android.content.Intent;
 
 import org.chromium.base.ContextUtils;
-import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
 import org.chromium.components.background_task_scheduler.TaskIds;
 import org.chromium.components.background_task_scheduler.TaskInfo;
@@ -84,21 +82,12 @@
     }
 
     /**
-     * Kicks off the download resumption process through either {@link DownloadNotificationService}
-     * or {@link DownloadNotificationService2}, which handles actually resuming the individual
-     * downloads.
+     * Kicks off the download resumption process through {@link DownloadNotificationService2},
+     * which handles actually resuming the individual downloads.
      *
      * It is assumed that native is loaded at the time of this call.
      */
     public void resume() {
-        if (ChromeFeatureList.isEnabled(ChromeFeatureList.DOWNLOADS_FOREGROUND)) {
             DownloadNotificationService2.getInstance().resumeAllPendingDownloads();
-        } else {
-            // Start the DownloadNotificationService and allow that to manage the download life
-            // cycle. Shut down the task right away after starting the service
-            DownloadNotificationService.startDownloadNotificationService(
-                    ContextUtils.getApplicationContext(),
-                    new Intent(DownloadNotificationService.ACTION_DOWNLOAD_RESUME_ALL));
-        }
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/SystemDownloadNotifier.java b/chrome/android/java/src/org/chromium/chrome/browser/download/SystemDownloadNotifier.java
deleted file mode 100644
index 60c350d..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/SystemDownloadNotifier.java
+++ /dev/null
@@ -1,338 +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.
-
-package org.chromium.chrome.browser.download;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-import android.support.annotation.IntDef;
-
-import org.chromium.base.ContextUtils;
-import org.chromium.base.Log;
-import org.chromium.base.ThreadUtils;
-import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.browser.download.DownloadNotificationService.Observer;
-import org.chromium.components.offline_items_collection.ContentId;
-import org.chromium.components.offline_items_collection.PendingState;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-import javax.annotation.Nullable;
-
-/**
- * DownloadNotifier implementation that creates and updates download notifications.
- * This class creates the {@link DownloadNotificationService} when needed, and binds
- * to the latter to issue calls to show and update notifications.
- */
-public class SystemDownloadNotifier implements DownloadNotifier, Observer {
-    private static final String TAG = "DownloadNotifier";
-
-    @IntDef({DownloadNotificationType.PROGRESS, DownloadNotificationType.SUCCESS,
-            DownloadNotificationType.FAILURE, DownloadNotificationType.CANCEL,
-            DownloadNotificationType.RESUME_ALL, DownloadNotificationType.PAUSE,
-            DownloadNotificationType.INTERRUPT, DownloadNotificationType.REMOVE_NOTIFICATION})
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface DownloadNotificationType {
-        int PROGRESS = 0;
-        int SUCCESS = 1;
-        int FAILURE = 2;
-        int CANCEL = 3;
-        int RESUME_ALL = 4;
-        int PAUSE = 5;
-        int INTERRUPT = 6;
-        int REMOVE_NOTIFICATION = 7;
-    }
-
-    @Nullable
-    private DownloadNotificationService mBoundService;
-    private Set<String> mActiveDownloads = new HashSet<String>();
-    private ArrayList<PendingNotificationInfo> mPendingNotifications = new ArrayList<>();
-
-    private boolean mIsServiceBound;
-
-    /**
-     * Pending download notifications to be posted.
-     */
-    static class PendingNotificationInfo {
-        // Pending download notifications to be posted.
-        public final int type;
-        public final DownloadInfo downloadInfo;
-        public long startTime;
-        public boolean isAutoResumable;
-        public boolean canDownloadWhileMetered;
-        public boolean canResolve;
-        public long systemDownloadId;
-        public boolean isSupportedMimeType;
-        public int notificationId;
-
-        public PendingNotificationInfo(int type, DownloadInfo downloadInfo) {
-            this.type = type;
-            this.downloadInfo = downloadInfo;
-        }
-    }
-
-    /**
-     * Object to receive information as the service is started and stopped.
-     */
-    private final ServiceConnection mConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (!(service instanceof DownloadNotificationService.LocalBinder)) {
-                Log.w(TAG,
-                        "Not from DownloadNotificationService, do not connect."
-                                + " Component name: " + className);
-                assert false;
-                return;
-            }
-            mBoundService = ((DownloadNotificationService.LocalBinder) service).getService();
-            mBoundService.addObserver(SystemDownloadNotifier.this);
-            // updateDownloadNotification() may leave some outstanding notifications
-            // before the service is connected, handle them now.
-            handlePendingNotifications();
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName className) {}
-    };
-
-    /**
-     * For tests only: sets the DownloadNotificationService.
-     * @param service An instance of DownloadNotificationService.
-     */
-    @VisibleForTesting
-    void setDownloadNotificationService(DownloadNotificationService service) {
-        mBoundService = service;
-    }
-
-    /**
-     * Handles all the pending notifications that hasn't been processed.
-     */
-    @VisibleForTesting
-    void handlePendingNotifications() {
-        if (mPendingNotifications.isEmpty()) return;
-        Iterator<PendingNotificationInfo> iter = mPendingNotifications.iterator();
-        while (iter.hasNext()) {
-            // Get the next PendingNotification, set autoRelease to be true for the last element.
-            PendingNotificationInfo nextPendingNotification = iter.next();
-            boolean autoRelease = !iter.hasNext();
-            updateDownloadNotification(nextPendingNotification, autoRelease);
-        }
-        mPendingNotifications.clear();
-    }
-
-    /**
-     * Starts and binds to the download notification service if needed.
-     */
-    private void startAndBindToServiceIfNeeded() {
-        if (mIsServiceBound) return;
-        startAndBindService();
-        mIsServiceBound = true;
-    }
-
-    /**
-     * Stops the download notification service if there are no download in progress.
-     */
-    private void unbindServiceIfNeeded() {
-        if (!mActiveDownloads.isEmpty() || !mIsServiceBound) return;
-        if (mBoundService != null) mBoundService.removeObserver(this);
-        unbindService();
-        mBoundService = null;
-        mIsServiceBound = false;
-    }
-
-    @VisibleForTesting
-    void startAndBindService() {
-        Context applicationContext = ContextUtils.getApplicationContext();
-        DownloadNotificationService.startDownloadNotificationService(applicationContext, null);
-        applicationContext.bindService(
-                new Intent(applicationContext, DownloadNotificationService.class), mConnection,
-                Context.BIND_AUTO_CREATE);
-    }
-
-    @VisibleForTesting
-    void unbindService() {
-        ContextUtils.getApplicationContext().unbindService(mConnection);
-    }
-
-    @Override
-    public void onDownloadCanceled(ContentId id) {
-        mActiveDownloads.remove(id.id);
-        if (mActiveDownloads.isEmpty()) unbindServiceIfNeeded();
-    }
-
-    @Override
-    public void notifyDownloadCanceled(ContentId id) {
-        DownloadInfo downloadInfo = new DownloadInfo.Builder().setContentId(id).build();
-        updateDownloadNotification(
-                new PendingNotificationInfo(DownloadNotificationType.CANCEL, downloadInfo), true);
-    }
-
-    @Override
-    public void notifyDownloadSuccessful(DownloadInfo downloadInfo, long systemDownloadId,
-            boolean canResolve, boolean isSupportedMimeType) {
-        PendingNotificationInfo info =
-                new PendingNotificationInfo(DownloadNotificationType.SUCCESS, downloadInfo);
-        info.canResolve = canResolve;
-        info.systemDownloadId = systemDownloadId;
-        info.isSupportedMimeType = isSupportedMimeType;
-        updateDownloadNotification(info, true);
-    }
-
-    @Override
-    public void notifyDownloadFailed(DownloadInfo downloadInfo) {
-        updateDownloadNotification(
-                new PendingNotificationInfo(DownloadNotificationType.FAILURE, downloadInfo), true);
-    }
-
-    @Override
-    public void notifyDownloadProgress(
-            DownloadInfo downloadInfo, long startTime, boolean canDownloadWhileMetered) {
-        PendingNotificationInfo info =
-                new PendingNotificationInfo(DownloadNotificationType.PROGRESS, downloadInfo);
-        info.startTime = startTime;
-        info.canDownloadWhileMetered = canDownloadWhileMetered;
-        updateDownloadNotification(info, true);
-    }
-
-    @Override
-    public void notifyDownloadPaused(DownloadInfo downloadInfo) {
-        PendingNotificationInfo info =
-                new PendingNotificationInfo(DownloadNotificationType.PAUSE, downloadInfo);
-        updateDownloadNotification(info, true);
-    }
-
-    @Override
-    public void notifyDownloadInterrupted(
-            DownloadInfo downloadInfo, boolean isAutoResumable, @PendingState int notUsed) {
-        PendingNotificationInfo info =
-                new PendingNotificationInfo(DownloadNotificationType.INTERRUPT, downloadInfo);
-        info.isAutoResumable = isAutoResumable;
-        updateDownloadNotification(info, true);
-    }
-
-    @Override
-    public void removeDownloadNotification(int notificationId, DownloadInfo downloadInfo) {
-        PendingNotificationInfo info = new PendingNotificationInfo(
-                DownloadNotificationType.REMOVE_NOTIFICATION, downloadInfo);
-        info.notificationId = notificationId;
-        updateDownloadNotification(info, true);
-    }
-
-    @Override
-    public void resumePendingDownloads() {
-        if (DownloadNotificationService.isTrackingResumableDownloads(
-                    ContextUtils.getApplicationContext())) {
-            updateDownloadNotification(
-                    new PendingNotificationInfo(DownloadNotificationType.RESUME_ALL, null), true);
-        }
-    }
-
-    /**
-     * Called when a successful notification is shown.
-     * @param notificationInfo Pending notification information to be handled.
-     * @param notificationId ID of the notification.
-     */
-    @VisibleForTesting
-    void onSuccessNotificationShown(
-            final PendingNotificationInfo notificationInfo, final int notificationId) {
-        if (notificationInfo.downloadInfo == null
-                || !notificationInfo.downloadInfo.getIsOpenable()) {
-            return;
-        }
-        DownloadManagerService.getDownloadManagerService().onSuccessNotificationShown(
-                notificationInfo.downloadInfo, notificationInfo.canResolve, notificationId,
-                notificationInfo.systemDownloadId);
-    }
-
-    /**
-     * Helper method to schedule download notification updates.
-     * @param notificationInfo Pending notification information to be handled.
-     * @param autoRelease Whether or not to allow unbinding the service after processing the action.
-     */
-    @VisibleForTesting
-    void updateDownloadNotification(
-            final PendingNotificationInfo notificationInfo, boolean autoRelease) {
-        assert ThreadUtils.runningOnUiThread();
-        startAndBindToServiceIfNeeded();
-
-        if (mBoundService == null) {
-            if (notificationInfo == null) return;
-            if (notificationInfo.downloadInfo != null) {
-                for (PendingNotificationInfo pendingNotification : mPendingNotifications) {
-                    if (pendingNotification.downloadInfo != null
-                            && pendingNotification.downloadInfo.getContentId().equals(
-                                       notificationInfo.downloadInfo.getContentId())) {
-                        mPendingNotifications.remove(pendingNotification);
-                        break;
-                    }
-                }
-            }
-            mPendingNotifications.add(notificationInfo);
-            return;
-        }
-
-        DownloadInfo info = notificationInfo.downloadInfo;
-        if (notificationInfo.type == DownloadNotificationType.PROGRESS) {
-            mActiveDownloads.add(info.getDownloadGuid());
-        } else if (notificationInfo.type != DownloadNotificationType.RESUME_ALL) {
-            mActiveDownloads.remove(info.getDownloadGuid());
-        }
-
-        switch (notificationInfo.type) {
-            case DownloadNotificationType.PROGRESS:
-                mBoundService.notifyDownloadProgress(info.getContentId(), info.getFileName(),
-                        info.getProgress(), info.getBytesReceived(),
-                        info.getTimeRemainingInMillis(), notificationInfo.startTime,
-                        info.isOffTheRecord(), notificationInfo.canDownloadWhileMetered,
-                        info.getIsTransient(), info.getIcon());
-                break;
-            case DownloadNotificationType.PAUSE:
-                mBoundService.notifyDownloadPaused(info.getContentId(), info.getFileName(), true,
-                        false, info.isOffTheRecord(), info.getIsTransient(), info.getIcon());
-                break;
-            case DownloadNotificationType.INTERRUPT:
-                mBoundService.notifyDownloadPaused(info.getContentId(), info.getFileName(),
-                        info.isResumable(), notificationInfo.isAutoResumable, info.isOffTheRecord(),
-                        info.getIsTransient(), info.getIcon());
-                break;
-            case DownloadNotificationType.SUCCESS:
-                final int notificationId = mBoundService.notifyDownloadSuccessful(
-                        info.getContentId(), info.getFilePath(), info.getFileName(),
-                        notificationInfo.systemDownloadId, info.isOffTheRecord(),
-                        notificationInfo.isSupportedMimeType, info.getIsOpenable(), info.getIcon(),
-                        info.getOriginalUrl(), info.getReferrer());
-                onSuccessNotificationShown(notificationInfo, notificationId);
-                break;
-            case DownloadNotificationType.FAILURE:
-                mBoundService.notifyDownloadFailed(info.getContentId(), info.getFileName(),
-                        info.getIcon(), info.getFailState());
-                break;
-            case DownloadNotificationType.CANCEL:
-                mBoundService.notifyDownloadCanceled(info.getContentId());
-                break;
-            case DownloadNotificationType.RESUME_ALL:
-                mBoundService.resumeAllPendingDownloads();
-                break;
-            case DownloadNotificationType.REMOVE_NOTIFICATION:
-                mBoundService.cancelNotification(
-                        notificationInfo.notificationId, info.getContentId());
-                break;
-            default:
-                assert false;
-        }
-
-        // Don't need to expose the notification id to ignore.  Cancel will automatically call this
-        // method as well and pass it in.
-        if (mBoundService != null) mBoundService.hideSummaryNotificationIfNecessary(-1);
-        if (autoRelease) unbindServiceIfNeeded();
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/PrefetchStatusProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/PrefetchStatusProvider.java
deleted file mode 100644
index d27c19f..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/PrefetchStatusProvider.java
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.download.home;
-
-import org.chromium.chrome.browser.offlinepages.prefetch.PrefetchConfiguration;
-
-/** Helper class to expose the status of the offline prefetch feature. */
-public class PrefetchStatusProvider {
-    /** @return Whether or not the offline prefetch feature is enabled. */
-    public boolean enabled() {
-        return PrefetchConfiguration.isPrefetchingFlagEnabled();
-    }
-}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyCoordinator.java
index a506a11..86c2cbb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyCoordinator.java
@@ -10,7 +10,6 @@
 import android.view.View;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.download.home.PrefetchStatusProvider;
 import org.chromium.chrome.browser.download.home.empty.EmptyProperties.State;
 import org.chromium.chrome.browser.download.home.filter.FilterCoordinator;
 import org.chromium.chrome.browser.download.home.filter.Filters.FilterType;
@@ -18,13 +17,13 @@
 import org.chromium.chrome.browser.download.home.filter.OfflineItemFilterSource;
 import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
+import org.chromium.chrome.browser.offlinepages.prefetch.PrefetchConfiguration;
 import org.chromium.components.offline_items_collection.OfflineItem;
 
 import java.util.Collection;
 
 /** A class that determines whether an empty view should be shown and inserts into the model. */
 public class EmptyCoordinator implements OfflineItemFilterObserver, FilterCoordinator.Observer {
-    private final PrefetchStatusProvider mPrefetchStatusProvider;
     private final OfflineItemFilterSource mSource;
 
     private final PropertyModel mModel = new PropertyModel(EmptyProperties.ALL_KEYS);
@@ -34,9 +33,7 @@
     private boolean mInSearchMode;
 
     /** Creates a {@link EmptyCoordinator} instance that monitors {@code source}. */
-    public EmptyCoordinator(Context context, PrefetchStatusProvider prefetchStatusProvider,
-            OfflineItemFilterSource source) {
-        mPrefetchStatusProvider = prefetchStatusProvider;
+    public EmptyCoordinator(Context context, OfflineItemFilterSource source) {
         mSource = source;
 
         mSource.addObserver(this);
@@ -101,7 +98,7 @@
             if (mShowingPrefetch) {
                 iconId = R.drawable.ic_library_news_feed;
 
-                if (mPrefetchStatusProvider.enabled()) {
+                if (PrefetchConfiguration.isPrefetchingEnabled()) {
                     textId = mInSearchMode ? R.string.download_manager_prefetch_tab_no_results
                                            : R.string.download_manager_prefetch_tab_empty;
                 } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java
index 1a6a1c5..609aae0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java
@@ -9,11 +9,11 @@
 import android.view.View;
 
 import org.chromium.base.ObserverList;
-import org.chromium.chrome.browser.download.home.PrefetchStatusProvider;
 import org.chromium.chrome.browser.download.home.filter.Filters.FilterType;
 import org.chromium.chrome.browser.download.home.filter.chips.ChipsCoordinator;
 import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
+import org.chromium.chrome.browser.offlinepages.prefetch.PrefetchConfiguration;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -44,8 +44,7 @@
      * Builds a new FilterCoordinator.
      * @param context The context to build the views and pull parameters from.
      */
-    public FilterCoordinator(Context context, PrefetchStatusProvider prefetchStatusProvider,
-            OfflineItemFilterSource chipFilterSource) {
+    public FilterCoordinator(Context context, OfflineItemFilterSource chipFilterSource) {
         mChipsProvider = new FilterChipsProvider(type -> handleChipSelected(), chipFilterSource);
         mChipsCoordinator = new ChipsCoordinator(context, mChipsProvider);
 
@@ -55,7 +54,7 @@
         mModel.set(FilterProperties.CHANGE_LISTENER, this::handleTabSelected);
         selectTab(TabType.FILES);
 
-        mModel.set(FilterProperties.SHOW_TABS, prefetchStatusProvider.enabled());
+        mModel.set(FilterProperties.SHOW_TABS, PrefetchConfiguration.isPrefetchingFlagEnabled());
     }
 
     /** @return The {@link View} representing this widget. */
@@ -78,7 +77,8 @@
      * components might need to update the UI state.
      */
     public void setSelectedFilter(@FilterType int filter) {
-        if (filter == Filters.FilterType.PREFETCHED) {
+        if (filter == Filters.FilterType.PREFETCHED
+                && PrefetchConfiguration.isPrefetchingFlagEnabled()) {
             selectTab(TabType.PREFETCH);
         } else {
             mChipsProvider.setFilterSelected(filter);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/InvalidStateOfflineItemFilter.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/InvalidStateOfflineItemFilter.java
index a7d4bd6..100e65e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/InvalidStateOfflineItemFilter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/InvalidStateOfflineItemFilter.java
@@ -8,7 +8,7 @@
 import org.chromium.components.offline_items_collection.OfflineItemState;
 
 /** A {@link OfflineItemFilter} responsible for pruning out items that do not have the right state
- *  to show in the UI.
+ *  to show in the UI or have been externally deleted.
  */
 public class InvalidStateOfflineItemFilter extends OfflineItemFilter {
     /** Creates an instance of this filter and wraps {@code source}. */
@@ -20,6 +20,8 @@
     // OfflineItemFilter implementation.
     @Override
     protected boolean isFilteredOut(OfflineItem item) {
+        if (item.externallyRemoved) return true;
+
         switch (item.state) {
             case OfflineItemState.CANCELLED:
             case OfflineItemState.FAILED:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListCoordinator.java
index a2ea26b4..0e6c5c1d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListCoordinator.java
@@ -15,7 +15,6 @@
 import org.chromium.base.Callback;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.download.home.DownloadManagerUiConfig;
-import org.chromium.chrome.browser.download.home.PrefetchStatusProvider;
 import org.chromium.chrome.browser.download.home.StableIds;
 import org.chromium.chrome.browser.download.home.empty.EmptyCoordinator;
 import org.chromium.chrome.browser.download.home.filter.FilterCoordinator;
@@ -99,7 +98,6 @@
             FilterCoordinator.Observer filterObserver,
             DateOrderedListObserver dateOrderedListObserver) {
         mContext = context;
-        PrefetchStatusProvider prefetchProvider = new PrefetchStatusProvider();
 
         ListItemModel model = new ListItemModel();
         DecoratedListItemModel decoratedModel = new DecoratedListItemModel(model);
@@ -108,13 +106,11 @@
         mMediator = new DateOrderedListMediator(provider, this ::startShareIntent, deleteController,
                 selectionDelegate, config, dateOrderedListObserver, model);
 
-        mEmptyCoordinator =
-                new EmptyCoordinator(context, prefetchProvider, mMediator.getEmptySource());
+        mEmptyCoordinator = new EmptyCoordinator(context, mMediator.getEmptySource());
 
         mStorageCoordinator = new StorageCoordinator(context, mMediator.getFilterSource());
 
-        mFilterCoordinator =
-                new FilterCoordinator(context, prefetchProvider, mMediator.getFilterSource());
+        mFilterCoordinator = new FilterCoordinator(context, mMediator.getFilterSource());
         mFilterCoordinator.addObserver(mMediator::onFilterTypeSelected);
         mFilterCoordinator.addObserver(filterObserver);
         mFilterCoordinator.addObserver(mEmptyCoordinator);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java
index ab4a6fa..71635c7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java
@@ -106,6 +106,7 @@
                 isLocationEnabled ? R.id.with_settings_search_menu_id : R.id.search_menu_id;
         final int closeMenuId =
                 isLocationEnabled ? R.id.with_settings_close_menu_id : R.id.close_menu_id;
+        final int infoMenuId = R.id.info_menu_id;
 
         mView = (ViewGroup) LayoutInflater.from(context).inflate(
                 R.layout.download_home_toolbar, null);
@@ -132,6 +133,7 @@
                 FadingShadow.POSITION_TOP);
 
         if (!hasCloseButton) mToolbar.removeMenuItem(closeMenuId);
+        mToolbar.removeMenuItem(infoMenuId);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java
index 7c9753e..37d3ac8a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java
@@ -13,7 +13,7 @@
 import org.chromium.chrome.browser.download.DownloadInfo;
 import org.chromium.chrome.browser.download.DownloadItem;
 import org.chromium.chrome.browser.download.DownloadMetrics;
-import org.chromium.chrome.browser.download.DownloadNotificationService;
+import org.chromium.chrome.browser.download.DownloadNotificationService2;
 import org.chromium.chrome.browser.download.DownloadUtils;
 import org.chromium.chrome.browser.download.home.metrics.FileExtensions;
 import org.chromium.chrome.browser.download.items.OfflineContentAggregatorFactory;
@@ -321,19 +321,19 @@
         @Override
         public void cancel() {
             mBackendProvider.getDownloadDelegate().broadcastDownloadAction(
-                    mItem, DownloadNotificationService.ACTION_DOWNLOAD_CANCEL);
+                    mItem, DownloadNotificationService2.ACTION_DOWNLOAD_CANCEL);
         }
 
         @Override
         public void pause() {
             mBackendProvider.getDownloadDelegate().broadcastDownloadAction(
-                    mItem, DownloadNotificationService.ACTION_DOWNLOAD_PAUSE);
+                    mItem, DownloadNotificationService2.ACTION_DOWNLOAD_PAUSE);
         }
 
         @Override
         public void resume() {
             mBackendProvider.getDownloadDelegate().broadcastDownloadAction(
-                    mItem, DownloadNotificationService.ACTION_DOWNLOAD_RESUME);
+                    mItem, DownloadNotificationService2.ACTION_DOWNLOAD_RESUME);
         }
 
         @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/media/router/OWNERS
index bec5b85..cab983d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/OWNERS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/OWNERS
@@ -1,5 +1,9 @@
-imcheng@chromium.org
+# primary reviewer
 zqzhang@chromium.org
 
+# backup reviewers
+mfoltz@chromium.org
+mlamouri@chromium.org
+
 # TEAM: media-dev@chromium.org
-# COMPONENT: Blink>PresentationAPI
+# COMPONENT: Internals>Cast
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
index 9d3c780..87fca8d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
@@ -176,4 +176,18 @@
      * @param scrim The scrim for this location bar to use.
      */
     void setScrim(ScrimView scrim);
+
+    /**
+     * Called to set the width of the location bar when the url bar is not focused.
+     *
+     * Immediately after the animation to transition the URL bar from focused to unfocused finishes,
+     * the layout width returned from #getMeasuredWidth() can differ from the final unfocused width
+     * (e.g. this value) until the next layout pass is complete.
+     *
+     * This value may be used to determine whether optional child views should be visible in the
+     * unfocused location bar.
+     *
+     * @param unfocusedWidth The unfocused location bar width.
+     */
+    void setUnfocusedWidth(float unfocusedWidth);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index 0fd33469..6497299 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -99,7 +99,7 @@
 
     private final List<Runnable> mDeferredNativeRunnables = new ArrayList<Runnable>();
 
-    protected StatusViewCoordinator mStatusViewCoordiantor;
+    protected StatusViewCoordinator mStatusViewCoordinator;
 
     private String mOriginalUrl = "";
 
@@ -218,7 +218,7 @@
 
         setLayoutTransition(null);
 
-        mStatusViewCoordiantor = new StatusViewCoordinator(mIsTablet, this, this);
+        mStatusViewCoordinator = new StatusViewCoordinator(mIsTablet, this, this);
 
         mUrlBar.setOnKeyListener(new UrlBarKeyListener());
 
@@ -269,7 +269,7 @@
         mWindowAndroid = windowAndroid;
 
         mUrlCoordinator.setWindowDelegate(windowDelegate);
-        mStatusViewCoordiantor.setWindowAndroid(windowAndroid);
+        mStatusViewCoordinator.setWindowAndroid(windowAndroid);
     }
 
     /**
@@ -301,7 +301,7 @@
         mNativeInitialized = true;
 
         mAutocompleteCoordinator.onNativeInitialized();
-        mStatusViewCoordiantor.onNativeInitialized();
+        mStatusViewCoordinator.onNativeInitialized();
         updateMicButtonState();
         mDeleteButton.setOnClickListener(this);
         mMicButton.setOnClickListener(this);
@@ -445,7 +445,7 @@
 
         if (mToolbarDataProvider.isUsingBrandColor()) updateVisualsForState();
 
-        mStatusViewCoordiantor.onUrlFocusChange(mUrlHasFocus);
+        mStatusViewCoordinator.onUrlFocusChange(mUrlHasFocus);
 
         if (!mUrlFocusedWithoutAnimations) handleUrlFocusAnimation(hasFocus);
 
@@ -543,7 +543,7 @@
         updateButtonVisibility();
 
         mAutocompleteCoordinator.setToolbarDataProvider(toolbarDataProvider);
-        mStatusViewCoordiantor.setToolbarDataProvider(toolbarDataProvider);
+        mStatusViewCoordinator.setToolbarDataProvider(toolbarDataProvider);
         mUrlCoordinator.setOnFocusChangedCallback(this::onUrlFocusChange);
     }
 
@@ -583,7 +583,7 @@
             type = StatusViewCoordinator.NavigationButtonType.PAGE;
         }
 
-        mStatusViewCoordiantor.setNavigationButtonType(type);
+        mStatusViewCoordinator.setNavigationButtonType(type);
     }
 
     /**
@@ -591,7 +591,7 @@
      */
     @Override
     public void updateSecurityIcon() {
-        mStatusViewCoordiantor.updateSecurityIcon();
+        mStatusViewCoordinator.updateSecurityIcon();
         // Update the URL in case the scheme change triggers a URL emphasis change.
         setUrlToPageUrl();
     }
@@ -976,7 +976,7 @@
     public void updateLoadingState(boolean updateUrl) {
         if (updateUrl) setUrlToPageUrl();
         updateNavigationButton();
-        mStatusViewCoordiantor.updateSecurityIcon();
+        mStatusViewCoordinator.updateSecurityIcon();
     }
 
     /** @return The current active {@link Tab}. */
@@ -1062,6 +1062,11 @@
     }
 
     @Override
+    public void setUnfocusedWidth(float unfocusedWidth) {
+        mStatusViewCoordinator.setUnfocusedLocationBarWidth(unfocusedWidth);
+    }
+
+    @Override
     public void onWindowFocusChanged(boolean hasWindowFocus) {
         super.onWindowFocusChanged(hasWindowFocus);
         if (!hasWindowFocus && !mAutocompleteCoordinator.isSuggestionModalShown()) {
@@ -1119,7 +1124,7 @@
      */
     @Override
     public void updateVisualsForState() {
-        if (updateUseDarkColors()) mStatusViewCoordiantor.setUseDarkColors(mUseDarkColors);
+        if (updateUseDarkColors()) mStatusViewCoordinator.setUseDarkColors(mUseDarkColors);
         int id = mUseDarkColors ? R.color.dark_mode_tint : R.color.light_mode_tint;
         ColorStateList colorStateList = AppCompatResources.getColorStateList(getContext(), id);
         ApiCompatibilityUtils.setImageTintList(mMicButton, colorStateList);
@@ -1167,7 +1172,7 @@
 
     @Override
     public View getSecurityIconView() {
-        return mStatusViewCoordiantor.getSecurityIconView();
+        return mStatusViewCoordinator.getSecurityIconView();
     }
 
     @Override
@@ -1183,6 +1188,6 @@
 
     @VisibleForTesting
     public StatusViewCoordinator getStatusViewCoordinatorForTesting() {
-        return mStatusViewCoordiantor;
+        return mStatusViewCoordinator;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java
index 2ea54363..5bc8243c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java
@@ -198,7 +198,7 @@
     }
 
     private void finishUrlFocusChange(boolean hasFocus) {
-        mStatusViewCoordiantor.setSecurityButtonVisibility(!hasFocus);
+        mStatusViewCoordinator.setSecurityButtonVisibility(!hasFocus);
         if (hasFocus) {
             if (getWindowDelegate().getWindowSoftInputMode()
                     != WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java
index ac4c9c0..8fce56df 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java
@@ -11,6 +11,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.app.Activity;
+import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.DrawableRes;
 import android.support.annotation.IntDef;
@@ -76,6 +77,10 @@
     private final AppCompatImageButton mSecurityButton;
     private final TextView mVerboseStatusTextView;
 
+    private final float mUrlMinWidth;
+    private final float mVerboseStatusMinWidth;
+    private final float mStatusSeparatorWidth;
+
     private ToolbarDataProvider mToolbarDataProvider;
     private WindowAndroid mWindowAndroid;
 
@@ -95,6 +100,9 @@
 
     private boolean mUrlHasFocus;
     private boolean mUseDarkColors;
+    private float mUnfocusedLocationBarWidth;
+    private int mVerboseStatusTextMaxWidth;
+    private boolean mHasSpaceForVerboseStatus;
 
     /**
      * Creates a new StatusViewCoordinator.
@@ -154,6 +162,16 @@
                 ObjectAnimator.ofFloat(mSecurityButton, View.ALPHA, 0));
         mNavigationIconShowAnimator.setDuration(URL_FOCUS_CHANGE_ANIMATION_DURATION_MS);
         mNavigationIconShowAnimator.addListener(iconChangeAnimatorListener);
+
+        Resources res = mParentView.getResources();
+        mUrlMinWidth = res.getDimensionPixelSize(R.dimen.location_bar_min_url_width)
+                + res.getDimensionPixelSize(R.dimen.location_bar_start_icon_width)
+                + (res.getDimensionPixelSize(R.dimen.location_bar_lateral_padding) * 2);
+        mStatusSeparatorWidth =
+                res.getDimensionPixelSize(R.dimen.location_bar_status_separator_width)
+                + res.getDimensionPixelSize(R.dimen.location_bar_status_separator_spacer);
+        mVerboseStatusMinWidth = mStatusSeparatorWidth
+                + res.getDimensionPixelSize(R.dimen.location_bar_min_verbose_status_text_width);
     }
 
     /**
@@ -356,8 +374,8 @@
      * omnibox.
      */
     private void updateVerboseStatusVisibility() {
-        boolean verboseStatusVisible =
-                !mUrlHasFocus && mToolbarDataProvider.shouldShowVerboseStatus();
+        boolean verboseStatusVisible = !mUrlHasFocus
+                && mToolbarDataProvider.shouldShowVerboseStatus() && mHasSpaceForVerboseStatus;
 
         int verboseStatusVisibility = verboseStatusVisible ? View.VISIBLE : View.GONE;
 
@@ -418,4 +436,37 @@
                     PageInfoController.OpenedFromSource.TOOLBAR);
         }
     }
+
+    /**
+     * Called to set the width of the location bar when the url bar is not focused.
+     * This value is used to determine whether the verbose status text should be visible.
+     * @param unfocusedWidth The unfocused location bar width.
+     */
+    public void setUnfocusedLocationBarWidth(float unfocusedWidth) {
+        if (mUnfocusedLocationBarWidth == unfocusedWidth) return;
+
+        // This unfocused with is used rather than observing #onMeasure() to avoid showing the
+        // verbose status when the animation to unfocus the URL bar has finished. There is a call to
+        // LocationBarLayout#onMeasure() after the URL focus animation has finished and before the
+        // location bar has received its updated width layout param.
+        mUnfocusedLocationBarWidth = unfocusedWidth;
+
+        boolean previousHasSpace = mHasSpaceForVerboseStatus;
+        mHasSpaceForVerboseStatus =
+                mUnfocusedLocationBarWidth >= mUrlMinWidth + mVerboseStatusMinWidth;
+
+        if (mHasSpaceForVerboseStatus) {
+            int previousMaxWidth = mVerboseStatusTextMaxWidth;
+            mVerboseStatusTextMaxWidth =
+                    (int) (mUnfocusedLocationBarWidth - mUrlMinWidth - mStatusSeparatorWidth);
+
+            // Skip setting the max width if it hasn't changed since TextView#setMaxWidth
+            // invalidates the view and requests a layout.
+            if (previousMaxWidth != mVerboseStatusTextMaxWidth) {
+                mVerboseStatusTextView.setMaxWidth(mVerboseStatusTextMaxWidth);
+            }
+        }
+
+        if (previousHasSpace != mHasSpaceForVerboseStatus) updateVerboseStatusVisibility();
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
index a99cc15..71c1917 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
@@ -70,6 +70,8 @@
     private boolean mSuggestionsShown;
     private boolean mSuggestionModalShown;
     private ViewGroup mOmniboxResultsContainer;
+    private float mMaxRequiredWidth;
+    private float mMaxMatchContentsWidth;
 
     // The timestamp (using SystemClock.elapsedRealtime()) at the point when the user started
     // modifying the omnibox with new input.
@@ -397,22 +399,33 @@
 
             @Override
             public void onTextWidthsUpdated(float requiredWidth, float matchContentsWidth) {
-                mSuggestionList.updateMaxTextWidths(requiredWidth, matchContentsWidth);
+                mMaxRequiredWidth = Math.max(mMaxRequiredWidth, requiredWidth);
+                mMaxMatchContentsWidth = Math.max(mMaxMatchContentsWidth, matchContentsWidth);
             }
 
             @Override
             public float getMaxRequiredWidth() {
-                return mSuggestionList.getMaxRequiredWidth();
+                return mMaxRequiredWidth;
             }
 
             @Override
             public float getMaxMatchContentsWidth() {
-                return mSuggestionList.getMaxMatchContentsWidth();
+                return mMaxMatchContentsWidth;
             }
         });
     }
 
     /**
+     * Updates the maximum widths required to render the suggestions.
+     * This is needed for infinite suggestions where we try to vertically align the leading
+     * ellipsis.
+     */
+    private void resetMaxTextWidths() {
+        mMaxRequiredWidth = 0;
+        mMaxMatchContentsWidth = 0;
+    }
+
+    /**
      * Handles showing/hiding the suggestions list.
      * @param visible Whether the suggestions list should be visible.
      */
@@ -763,7 +776,7 @@
 
         // Show the suggestion list.
         initSuggestionList(); // It may not have been initialized yet.
-        mSuggestionList.resetMaxTextWidths();
+        resetMaxTextWidths();
 
         if (itemsChanged) mSuggestionListAdapter.notifySuggestionsChanged();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxResultsAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxResultsAdapter.java
index a180501..3c415c1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxResultsAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxResultsAdapter.java
@@ -6,17 +6,33 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.Paint;
 import android.support.annotation.Nullable;
 import android.support.v4.view.ViewCompat;
+import android.text.Spannable;
+import android.text.SpannableString;
 import android.text.TextUtils;
+import android.text.style.StyleSpan;
+import android.util.Pair;
+import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
+import org.chromium.chrome.browser.omnibox.MatchClassificationStyle;
+import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
+import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion.MatchClassification;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionViewProperties.SuggestionIcon;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionViewProperties.SuggestionTextContainer;
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
+import org.chromium.ui.base.DeviceFormFactor;
 
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -67,14 +83,273 @@
             suggestionView = new SuggestionView(mContext);
         }
         OmniboxResultItem item = mSuggestionItems.get(position);
+        // Always attempt to update the answer icon before updating the rest of the properties.  If
+        // the answer image is already cached, the response will be synchronous and thus we have
+        // the ability to push the answer image at the same time with all of the other state.
+        //
+        // Also, notifyDataSetChanged takes no effect if called within an update, so moving this
+        // after pushing the state results in cached icons not showing up.
         maybeFetchAnswerIcon(item);
+        updateView(suggestionView, item);
 
-        suggestionView.init(item, mSuggestionDelegate, position, mUseDarkColors);
+        // TODO(tedchoc): Remove the init function and push params to the model.
+        suggestionView.init(item, mSuggestionDelegate, position);
         ViewCompat.setLayoutDirection(suggestionView, mLayoutDirection);
 
         return suggestionView;
     }
 
+    private PropertyModel getModel(SuggestionView view) {
+        PropertyModel model = (PropertyModel) view.getTag(R.id.view_model);
+        if (model == null) {
+            model = new PropertyModel(SuggestionViewProperties.ALL_KEYS);
+            PropertyModelChangeProcessor.create(model, view, SuggestionViewViewBinder::bind);
+            model.set(SuggestionViewProperties.SUGGESTION_ICON_TYPE, SuggestionIcon.UNDEFINED);
+            view.setTag(R.id.view_model, model);
+        }
+        return model;
+    }
+
+    private void updateView(SuggestionView view, OmniboxResultItem item) {
+        PropertyModel model = getModel(view);
+        Paint textLine1Paint = view.getTextLine1().getPaint();
+        Paint textLine2Paint = view.getTextLine2().getPaint();
+        model.set(SuggestionViewProperties.USE_DARK_COLORS, mUseDarkColors);
+        model.set(SuggestionViewProperties.TEXT_LINE_1_ALIGNMENT_CONSTRAINTS, Pair.create(0f, 0f));
+
+        OmniboxSuggestion suggestion = item.getSuggestion();
+        // Suggestions with attached answers are rendered with rich results regardless of which
+        // suggestion type they are.
+        if (suggestion.hasAnswer()) {
+            setStateForAnswerSuggestion(model, suggestion.getAnswer(), item.getAnswerImage(),
+                    textLine1Paint, textLine2Paint);
+        } else {
+            setStateForTextSuggestion(model, item, textLine1Paint);
+        }
+    }
+
+    private boolean applyHighlightToMatchRegions(
+            Spannable str, List<MatchClassification> classifications) {
+        boolean hasMatch = false;
+        for (int i = 0; i < classifications.size(); i++) {
+            MatchClassification classification = classifications.get(i);
+            if ((classification.style & MatchClassificationStyle.MATCH)
+                    == MatchClassificationStyle.MATCH) {
+                int matchStartIndex = classification.offset;
+                int matchEndIndex;
+                if (i == classifications.size() - 1) {
+                    matchEndIndex = str.length();
+                } else {
+                    matchEndIndex = classifications.get(i + 1).offset;
+                }
+                matchStartIndex = Math.min(matchStartIndex, str.length());
+                matchEndIndex = Math.min(matchEndIndex, str.length());
+
+                hasMatch = true;
+                // Bold the part of the URL that matches the user query.
+                str.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), matchStartIndex,
+                        matchEndIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            }
+        }
+        return hasMatch;
+    }
+
+    /**
+     * Get the first line for a text based omnibox suggestion.
+     * @param suggestionItem The item containing the suggestion data.
+     * @param showDescriptionIfPresent Whether to show the description text of the suggestion if
+     *                                 the item contains valid data.
+     * @param shouldHighlight Whether the query should be highlighted.
+     * @param textLine1Paint The paint used for the first text line.
+     * @return The first line of text.
+     */
+    private Spannable getSuggestedQuery(PropertyModel model, OmniboxResultItem suggestionItem,
+            boolean showDescriptionIfPresent, boolean shouldHighlight, Paint textLine1Paint) {
+        String userQuery = suggestionItem.getMatchedQuery();
+        String suggestedQuery = null;
+        List<MatchClassification> classifications;
+        OmniboxSuggestion suggestion = suggestionItem.getSuggestion();
+        if (showDescriptionIfPresent && !TextUtils.isEmpty(suggestion.getUrl())
+                && !TextUtils.isEmpty(suggestion.getDescription())) {
+            suggestedQuery = suggestion.getDescription();
+            classifications = suggestion.getDescriptionClassifications();
+        } else {
+            suggestedQuery = suggestion.getDisplayText();
+            classifications = suggestion.getDisplayTextClassifications();
+        }
+        if (suggestedQuery == null) {
+            assert false : "Invalid suggestion sent with no displayable text";
+            suggestedQuery = "";
+            classifications = new ArrayList<MatchClassification>();
+            classifications.add(new MatchClassification(0, MatchClassificationStyle.NONE));
+        }
+
+        if (suggestion.getType() == OmniboxSuggestionType.SEARCH_SUGGEST_TAIL) {
+            String fillIntoEdit = suggestion.getFillIntoEdit();
+            // Data sanity checks.
+            if (fillIntoEdit.startsWith(userQuery) && fillIntoEdit.endsWith(suggestedQuery)
+                    && fillIntoEdit.length() < userQuery.length() + suggestedQuery.length()) {
+                final String ellipsisPrefix = "\u2026 ";
+                suggestedQuery = ellipsisPrefix + suggestedQuery;
+
+                // Offset the match classifications by the length of the ellipsis prefix to ensure
+                // the highlighting remains correct.
+                for (int i = 0; i < classifications.size(); i++) {
+                    classifications.set(i,
+                            new MatchClassification(
+                                    classifications.get(i).offset + ellipsisPrefix.length(),
+                                    classifications.get(i).style));
+                }
+                classifications.add(0, new MatchClassification(0, MatchClassificationStyle.NONE));
+
+                if (DeviceFormFactor.isNonMultiDisplayContextOnTablet(mContext)) {
+                    float requiredWidth =
+                            textLine1Paint.measureText(fillIntoEdit, 0, fillIntoEdit.length());
+                    float matchContentsWidth =
+                            textLine1Paint.measureText(suggestedQuery, 0, suggestedQuery.length());
+
+                    model.set(SuggestionViewProperties.TEXT_LINE_1_ALIGNMENT_CONSTRAINTS,
+                            Pair.create(requiredWidth, matchContentsWidth));
+                    // Update the max text widths values in SuggestionList. These will be passed to
+                    // the contents view on layout.
+                    mSuggestionDelegate.onTextWidthsUpdated(requiredWidth, matchContentsWidth);
+                }
+            }
+        }
+
+        Spannable str = SpannableString.valueOf(suggestedQuery);
+        if (shouldHighlight) applyHighlightToMatchRegions(str, classifications);
+        return str;
+    }
+
+    private void setStateForTextSuggestion(
+            PropertyModel model, OmniboxResultItem suggestionItem, Paint textLine1Paint) {
+        OmniboxSuggestion suggestion = suggestionItem.getSuggestion();
+        int suggestionType = suggestion.getType();
+        @SuggestionIcon
+        int suggestionIcon;
+        Spannable textLine1;
+
+        Spannable textLine2;
+        int textLine2Color = 0;
+        int textLine2Direction = View.TEXT_DIRECTION_INHERIT;
+        if (suggestion.isUrlSuggestion()) {
+            suggestionIcon = SuggestionIcon.GLOBE;
+            if (suggestion.isStarred()) {
+                suggestionIcon = SuggestionIcon.BOOKMARK;
+            } else if (suggestionType == OmniboxSuggestionType.HISTORY_URL) {
+                suggestionIcon = SuggestionIcon.HISTORY;
+            }
+            boolean urlHighlighted = false;
+            if (!TextUtils.isEmpty(suggestion.getUrl())) {
+                Spannable str = SpannableString.valueOf(suggestion.getDisplayText());
+                urlHighlighted = applyHighlightToMatchRegions(
+                        str, suggestion.getDisplayTextClassifications());
+                textLine2 = str;
+                textLine2Color = SuggestionViewViewBinder.getStandardUrlColor(
+                        mContext, model.get(SuggestionViewProperties.USE_DARK_COLORS));
+                textLine2Direction = View.TEXT_DIRECTION_LTR;
+            } else {
+                textLine2 = null;
+            }
+            textLine1 =
+                    getSuggestedQuery(model, suggestionItem, true, !urlHighlighted, textLine1Paint);
+        } else {
+            suggestionIcon = SuggestionIcon.MAGNIFIER;
+            if (suggestionType == OmniboxSuggestionType.VOICE_SUGGEST) {
+                suggestionIcon = SuggestionIcon.VOICE;
+            } else if ((suggestionType == OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED)
+                    || (suggestionType == OmniboxSuggestionType.SEARCH_HISTORY)) {
+                // Show history icon for suggestions based on user queries.
+                suggestionIcon = SuggestionIcon.HISTORY;
+            }
+            textLine1 = getSuggestedQuery(model, suggestionItem, false, true, textLine1Paint);
+            if ((suggestionType == OmniboxSuggestionType.SEARCH_SUGGEST_ENTITY)
+                    || (suggestionType == OmniboxSuggestionType.SEARCH_SUGGEST_PROFILE)) {
+                textLine2 = SpannableString.valueOf(suggestion.getDescription());
+                textLine2Color = SuggestionViewViewBinder.getStandardFontColor(
+                        mContext, model.get(SuggestionViewProperties.USE_DARK_COLORS));
+                textLine2Direction = View.TEXT_DIRECTION_INHERIT;
+            } else {
+                textLine2 = null;
+            }
+        }
+
+        model.set(SuggestionViewProperties.IS_ANSWER, false);
+        model.set(SuggestionViewProperties.HAS_ANSWER_IMAGE, false);
+        model.set(SuggestionViewProperties.ANSWER_IMAGE, null);
+
+        model.set(
+                SuggestionViewProperties.TEXT_LINE_1_TEXT, new SuggestionTextContainer(textLine1));
+        model.set(SuggestionViewProperties.TEXT_LINE_1_SIZING,
+                Pair.create(TypedValue.COMPLEX_UNIT_PX,
+                        mContext.getResources().getDimension(
+                                R.dimen.omnibox_suggestion_first_line_text_size)));
+
+        model.set(
+                SuggestionViewProperties.TEXT_LINE_2_TEXT, new SuggestionTextContainer(textLine2));
+        model.set(SuggestionViewProperties.TEXT_LINE_2_TEXT_COLOR, textLine2Color);
+        model.set(SuggestionViewProperties.TEXT_LINE_2_TEXT_DIRECTION, textLine2Direction);
+        model.set(SuggestionViewProperties.TEXT_LINE_2_SIZING,
+                Pair.create(TypedValue.COMPLEX_UNIT_PX,
+                        mContext.getResources().getDimension(
+                                R.dimen.omnibox_suggestion_second_line_text_size)));
+        model.set(SuggestionViewProperties.TEXT_LINE_2_MAX_LINES, 1);
+
+        boolean sameAsTyped = suggestionItem.getMatchedQuery().trim().equalsIgnoreCase(
+                suggestion.getDisplayText());
+        model.set(SuggestionViewProperties.REFINABLE, !sameAsTyped);
+
+        model.set(SuggestionViewProperties.SUGGESTION_ICON_TYPE, suggestionIcon);
+    }
+
+    private static int parseNumAnswerLines(List<SuggestionAnswer.TextField> textFields) {
+        for (int i = 0; i < textFields.size(); i++) {
+            if (textFields.get(i).hasNumLines()) {
+                return Math.min(3, textFields.get(i).getNumLines());
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Sets both lines of the Omnibox suggestion based on an Answers in Suggest result.
+     */
+    private void setStateForAnswerSuggestion(PropertyModel model, SuggestionAnswer answer,
+            Bitmap answerImage, Paint textLine1Paint, Paint textLine2Paint) {
+        float density = mContext.getResources().getDisplayMetrics().density;
+        SuggestionAnswer.ImageLine firstLine = answer.getFirstLine();
+        SuggestionAnswer.ImageLine secondLine = answer.getSecondLine();
+        int numAnswerLines = parseNumAnswerLines(secondLine.getTextFields());
+        if (numAnswerLines == -1) numAnswerLines = 1;
+        model.set(SuggestionViewProperties.IS_ANSWER, true);
+
+        model.set(SuggestionViewProperties.TEXT_LINE_1_SIZING,
+                Pair.create(TypedValue.COMPLEX_UNIT_SP,
+                        (float) AnswerTextBuilder.getMaxTextHeightSp(firstLine)));
+        model.set(SuggestionViewProperties.TEXT_LINE_1_TEXT,
+                new SuggestionTextContainer(AnswerTextBuilder.buildSpannable(
+                        firstLine, textLine1Paint.getFontMetrics(), density)));
+
+        model.set(SuggestionViewProperties.TEXT_LINE_2_SIZING,
+                Pair.create(TypedValue.COMPLEX_UNIT_SP,
+                        (float) AnswerTextBuilder.getMaxTextHeightSp(secondLine)));
+        model.set(SuggestionViewProperties.TEXT_LINE_2_TEXT,
+                new SuggestionTextContainer(AnswerTextBuilder.buildSpannable(
+                        secondLine, textLine2Paint.getFontMetrics(), density)));
+        model.set(SuggestionViewProperties.TEXT_LINE_2_MAX_LINES, numAnswerLines);
+        model.set(SuggestionViewProperties.TEXT_LINE_2_TEXT_COLOR,
+                SuggestionViewViewBinder.getStandardFontColor(
+                        mContext, model.get(SuggestionViewProperties.USE_DARK_COLORS)));
+        model.set(SuggestionViewProperties.TEXT_LINE_2_TEXT_DIRECTION, View.TEXT_DIRECTION_INHERIT);
+
+        model.set(SuggestionViewProperties.HAS_ANSWER_IMAGE, secondLine.hasImage());
+        model.set(SuggestionViewProperties.ANSWER_IMAGE, answerImage);
+
+        model.set(SuggestionViewProperties.REFINABLE, true);
+        model.set(SuggestionViewProperties.SUGGESTION_ICON_TYPE, SuggestionIcon.MAGNIFIER);
+    }
+
     private void maybeFetchAnswerIcon(OmniboxResultItem item) {
         ThreadUtils.assertOnUiThread();
 
@@ -119,9 +394,7 @@
                 didUpdateImage = true;
             }
         }
-        if (didUpdateImage) {
-            notifyDataSetChanged();
-        }
+        if (didUpdateImage) notifyDataSetChanged();
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsList.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsList.java
index c34c28e..6cdd9a66 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsList.java
@@ -36,9 +36,6 @@
     private final int[] mTempPosition = new int[2];
     private final Rect mTempRect = new Rect();
 
-    private float mMaxRequiredWidth;
-    private float mMaxMatchContentsWidth;
-
     /**
      * Provides the capabilities required to embed the omnibox suggestion list into the UI.
      */
@@ -158,40 +155,6 @@
         }
     }
 
-    /**
-     * Updates the maximum widths required to render the suggestions.
-     * This is needed for infinite suggestions where we try to vertically align the leading
-     * ellipsis.
-     */
-    void resetMaxTextWidths() {
-        mMaxRequiredWidth = 0;
-        mMaxMatchContentsWidth = 0;
-    }
-
-    /**
-     * Updates the max text width values for the suggestions.
-     * @param requiredWidth a new required width.
-     * @param matchContentsWidth a new match contents width.
-     */
-    void updateMaxTextWidths(float requiredWidth, float matchContentsWidth) {
-        mMaxRequiredWidth = Math.max(mMaxRequiredWidth, requiredWidth);
-        mMaxMatchContentsWidth = Math.max(mMaxMatchContentsWidth, matchContentsWidth);
-    }
-
-    /**
-     * @return max required width for the suggestions.
-     */
-    float getMaxRequiredWidth() {
-        return mMaxRequiredWidth;
-    }
-
-    /**
-     * @return max match contents width for the suggestions.
-     */
-    float getMaxMatchContentsWidth() {
-        return mMaxMatchContentsWidth;
-    }
-
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         View contentView =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionView.java
index dd553f7c..1cf1d91 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionView.java
@@ -6,9 +6,7 @@
 
 import android.annotation.SuppressLint;
 import android.content.Context;
-import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.ColorRes;
@@ -17,13 +15,6 @@
 import android.support.annotation.VisibleForTesting;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.content.res.AppCompatResources;
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.TextPaint;
-import android.text.TextUtils;
-import android.text.style.StyleSpan;
-import android.util.Pair;
-import android.util.TypedValue;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -32,24 +23,13 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableIntPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
-import org.chromium.chrome.browser.omnibox.MatchClassificationStyle;
-import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxResultsAdapter.OmniboxResultItem;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxResultsAdapter.OmniboxSuggestionDelegate;
-import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion.MatchClassification;
 import org.chromium.chrome.browser.widget.TintedDrawable;
 import org.chromium.ui.base.DeviceFormFactor;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * Container view for omnibox suggestions made very specific for omnibox suggestions to minimize
@@ -57,34 +37,24 @@
  */
 @VisibleForTesting
 public class SuggestionView extends ViewGroup {
-    @IntDef({SuggestionIcon.UNDEFINED, SuggestionIcon.BOOKMARK, SuggestionIcon.HISTORY,
-            SuggestionIcon.GLOBE, SuggestionIcon.MAGNIFIER, SuggestionIcon.VOICE})
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface SuggestionIcon {
-        int UNDEFINED = -1;
-        int BOOKMARK = 0;
-        int HISTORY = 1;
-        int GLOBE = 2;
-        int MAGNIFIER = 3;
-        int VOICE = 4;
-    }
-
     private static final float ANSWER_IMAGE_SCALING_FACTOR = 1.15f;
 
+    @IntDef({SuggestionLayoutType.TEXT_SUGGESTION, SuggestionLayoutType.ANSWER,
+            SuggestionLayoutType.MULTI_LINE_ANSWER})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SuggestionLayoutType {
+        int TEXT_SUGGESTION = 0;
+        int ANSWER = 1;
+        int MULTI_LINE_ANSWER = 2;
+    }
+
     private final int mSuggestionHeight;
     private final int mSuggestionAnswerHeight;
-    private int mNumAnswerLines = 1;
+    @SuggestionLayoutType
+    private int mSuggestionLayoutType;
 
-    private final int mDarkTitleColorStandardFont;
-    private final int mLightTitleColorStandardFont;
-    private final int mDarkUrlStandardColor;
-    private final int mLightUrlStandardColor;
-
-    private OmniboxResultItem mSuggestionItem;
     private OmniboxSuggestion mSuggestion;
     private OmniboxSuggestionDelegate mSuggestionDelegate;
-    // TODO(tedchoc): Remove the need for this.
-    private Boolean mUseDarkColors;
     private int mPosition;
     private int mRefineViewOffsetPx;
 
@@ -93,75 +63,11 @@
     private final int mRefineWidth;
     private final View mRefineView;
     private TintedDrawable mRefineIcon;
-    private final int mRefineViewModernEndPadding;
 
     // Pre-computed offsets in px.
     private final int mSuggestionStartOffsetPx;
     private final int mSuggestionIconWidthPx;
 
-    private static class SuggestionViewProperties {
-        /** Whether dark colors should be applied to text, icons */
-        public static final WritableBooleanPropertyKey USE_DARK_COLORS =
-                new WritableBooleanPropertyKey();
-
-        /** Whether an answer image will be shown. */
-        public static final WritableBooleanPropertyKey HAS_ANSWER_IMAGE =
-                new WritableBooleanPropertyKey();
-        /** The answer image to be shown. */
-        public static final WritableObjectPropertyKey<Bitmap> ANSWER_IMAGE =
-                new WritableObjectPropertyKey<>();
-
-        /** Whether the suggestion supports refinement. */
-        public static final WritableBooleanPropertyKey REFINABLE = new WritableBooleanPropertyKey();
-
-        /** The suggestion icon type shown. */
-        public static final WritableIntPropertyKey SUGGESTION_ICON_TYPE =
-                new WritableIntPropertyKey();
-
-        /**
-         * The sizing information for the first line of text.
-         *
-         * The first item is the unit of size (e.g. TypedValue.COMPLEX_UNIT_PX), and the second item
-         * is the size itself.
-         */
-        public static final WritableObjectPropertyKey<Pair<Integer, Float>> TEXT_LINE_1_SIZING =
-                new WritableObjectPropertyKey<>();
-        /** The actual text content for the first line of text. */
-        public static final WritableObjectPropertyKey<Spannable> TEXT_LINE_1_TEXT =
-                new WritableObjectPropertyKey<>();
-
-        /**
-         * The sizing information for the second line of text.
-         *
-         * The first item is the unit of size (e.g. TypedValue.COMPLEX_UNIT_PX), and the second item
-         * is the size itself.
-         */
-        public static final WritableObjectPropertyKey<Pair<Integer, Float>> TEXT_LINE_2_SIZING =
-                new WritableObjectPropertyKey<>();
-        /** The truncation policy for the second line of text. */
-        public static final WritableObjectPropertyKey<TextUtils.TruncateAt> TEXT_LINE_2_TRUNCATION =
-                new WritableObjectPropertyKey<>();
-        /** The maximum number of lines to be shown for the second line of text. */
-        public static final WritableIntPropertyKey TEXT_LINE_2_MAX_LINES =
-                new WritableIntPropertyKey();
-        /** The color to be applied to the second line of text. */
-        public static final WritableIntPropertyKey TEXT_LINE_2_TEXT_COLOR =
-                new WritableIntPropertyKey();
-        /** The direction the text should be laid out for the second line of text. */
-        public static final WritableIntPropertyKey TEXT_LINE_2_TEXT_DIRECTION =
-                new WritableIntPropertyKey();
-        /** The actual text content for the second line of text. */
-        public static final WritableObjectPropertyKey<Spannable> TEXT_LINE_2_TEXT =
-                new WritableObjectPropertyKey<>();
-
-        public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {USE_DARK_COLORS,
-                HAS_ANSWER_IMAGE, ANSWER_IMAGE, REFINABLE, SUGGESTION_ICON_TYPE, TEXT_LINE_1_SIZING,
-                TEXT_LINE_1_TEXT, TEXT_LINE_2_SIZING, TEXT_LINE_2_TRUNCATION, TEXT_LINE_2_MAX_LINES,
-                TEXT_LINE_2_TEXT_COLOR, TEXT_LINE_2_TEXT_DIRECTION, TEXT_LINE_2_TEXT};
-    }
-
-    private PropertyModel mModel;
-
     /**
      * Constructs a new omnibox suggestion view.
      *
@@ -175,16 +81,6 @@
         mSuggestionAnswerHeight = context.getResources().getDimensionPixelOffset(
                 R.dimen.omnibox_suggestion_answer_height);
 
-        Resources resources = getResources();
-        mDarkTitleColorStandardFont =
-                ApiCompatibilityUtils.getColor(resources, R.color.url_emphasis_default_text);
-        mLightTitleColorStandardFont =
-                ApiCompatibilityUtils.getColor(resources, R.color.url_emphasis_light_default_text);
-        mDarkUrlStandardColor =
-                ApiCompatibilityUtils.getColor(resources, R.color.suggestion_url_dark_modern);
-        mLightUrlStandardColor =
-                ApiCompatibilityUtils.getColor(resources, R.color.suggestion_url_light_modern);
-
         TypedArray a =
                 getContext().obtainStyledAttributes(new int[] {R.attr.selectableItemBackground});
         Drawable itemBackground = a.getDrawable(0);
@@ -244,108 +140,19 @@
         mRefineWidth =
                 getResources().getDimensionPixelSize(R.dimen.omnibox_suggestion_refine_width);
 
-        mRefineViewModernEndPadding = getResources().getDimensionPixelSize(
+        mRefineViewOffsetPx = getResources().getDimensionPixelSize(
                 R.dimen.omnibox_suggestion_refine_view_modern_end_padding);
 
         mSuggestionStartOffsetPx =
                 getResources().getDimensionPixelOffset(R.dimen.omnibox_suggestion_start_offset);
         mSuggestionIconWidthPx =
                 getResources().getDimensionPixelSize(R.dimen.location_bar_icon_width);
-
-        mModel = new PropertyModel(SuggestionViewProperties.ALL_KEYS);
-        PropertyModelChangeProcessor.create(mModel, this, SuggestionView::bind);
-        mModel.set(SuggestionViewProperties.SUGGESTION_ICON_TYPE, SuggestionIcon.UNDEFINED);
-    }
-
-    private static void bind(PropertyModel model, SuggestionView view, PropertyKey propertyKey) {
-        if (SuggestionViewProperties.USE_DARK_COLORS.equals(propertyKey)) {
-            boolean useDarkColors = model.get(SuggestionViewProperties.USE_DARK_COLORS);
-            view.mUseDarkColors = useDarkColors;
-            view.updateRefineIconTint(useDarkColors);
-            view.mContentsView.updateSuggestionIconTint(useDarkColors);
-            view.mContentsView.mTextLine1.setTextColor(view.getStandardFontColor());
-        } else if (SuggestionViewProperties.HAS_ANSWER_IMAGE.equals(propertyKey)) {
-            int visibility = model.get(SuggestionViewProperties.HAS_ANSWER_IMAGE) ? VISIBLE : GONE;
-            view.mContentsView.mAnswerImage.setVisibility(visibility);
-        } else if (SuggestionViewProperties.ANSWER_IMAGE.equals(propertyKey)) {
-            view.updateAnswerImage(model.get(SuggestionViewProperties.ANSWER_IMAGE));
-        } else if (SuggestionViewProperties.REFINABLE.equals(propertyKey)) {
-            boolean refinable = model.get(SuggestionViewProperties.REFINABLE);
-            view.setRefinable(refinable);
-            if (refinable) view.initRefineIcon(model.get(SuggestionViewProperties.USE_DARK_COLORS));
-        } else if (SuggestionViewProperties.SUGGESTION_ICON_TYPE.equals(propertyKey)) {
-            if (!DeviceFormFactor.isNonMultiDisplayContextOnTablet(view.getContext())) return;
-
-            @SuggestionIcon
-            int type = model.get(SuggestionViewProperties.SUGGESTION_ICON_TYPE);
-
-            if (type == SuggestionIcon.UNDEFINED) return;
-
-            int drawableId = R.drawable.ic_omnibox_page;
-            switch (type) {
-                case SuggestionIcon.BOOKMARK:
-                    drawableId = R.drawable.btn_star;
-                    break;
-                case SuggestionIcon.MAGNIFIER:
-                    drawableId = R.drawable.ic_suggestion_magnifier;
-                    break;
-                case SuggestionIcon.HISTORY:
-                    drawableId = R.drawable.ic_suggestion_history;
-                    break;
-                case SuggestionIcon.VOICE:
-                    drawableId = R.drawable.btn_mic;
-                    break;
-                default:
-                    break;
-            }
-            view.mContentsView.setSuggestionIconDrawable(
-                    drawableId, model.get(SuggestionViewProperties.USE_DARK_COLORS));
-        } else if (SuggestionViewProperties.TEXT_LINE_1_SIZING.equals(propertyKey)) {
-            Pair<Integer, Float> sizing = model.get(SuggestionViewProperties.TEXT_LINE_1_SIZING);
-            view.mContentsView.mTextLine1.setTextSize(sizing.first, sizing.second);
-        } else if (SuggestionViewProperties.TEXT_LINE_1_TEXT.equals(propertyKey)) {
-            view.mContentsView.mTextLine1.setText(
-                    model.get(SuggestionViewProperties.TEXT_LINE_1_TEXT));
-        } else if (SuggestionViewProperties.TEXT_LINE_2_SIZING.equals(propertyKey)) {
-            Pair<Integer, Float> sizing = model.get(SuggestionViewProperties.TEXT_LINE_2_SIZING);
-            view.mContentsView.mTextLine2.setTextSize(sizing.first, sizing.second);
-        } else if (SuggestionViewProperties.TEXT_LINE_2_TRUNCATION.equals(propertyKey)) {
-        } else if (SuggestionViewProperties.TEXT_LINE_2_MAX_LINES.equals(propertyKey)) {
-            int numberLines = model.get(SuggestionViewProperties.TEXT_LINE_2_MAX_LINES);
-            if (numberLines == 1) {
-                view.mContentsView.mTextLine2.setEllipsize(null);
-                view.mContentsView.mTextLine2.setSingleLine();
-            } else {
-                view.mContentsView.mTextLine2.setSingleLine(false);
-                view.mContentsView.mTextLine2.setEllipsize(TextUtils.TruncateAt.END);
-                view.mContentsView.mTextLine2.setMaxLines(numberLines);
-            }
-        } else if (SuggestionViewProperties.TEXT_LINE_2_TEXT_COLOR.equals(propertyKey)) {
-            view.mContentsView.mTextLine2.setTextColor(
-                    model.get(SuggestionViewProperties.TEXT_LINE_2_TEXT_COLOR));
-        } else if (SuggestionViewProperties.TEXT_LINE_2_TEXT_DIRECTION.equals(propertyKey)) {
-            ApiCompatibilityUtils.setTextDirection(view.mContentsView.mTextLine2,
-                    model.get(SuggestionViewProperties.TEXT_LINE_2_TEXT_DIRECTION));
-        } else if (SuggestionViewProperties.TEXT_LINE_2_TEXT.equals(propertyKey)) {
-            Spannable line2Text = model.get(SuggestionViewProperties.TEXT_LINE_2_TEXT);
-            if (TextUtils.isEmpty(line2Text)) {
-                view.mContentsView.mTextLine2.setVisibility(View.INVISIBLE);
-            } else {
-                view.mContentsView.mTextLine2.setVisibility(View.VISIBLE);
-                view.mContentsView.mTextLine2.setText(
-                        model.get(SuggestionViewProperties.TEXT_LINE_2_TEXT));
-            }
-        }
     }
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         if (getMeasuredWidth() == 0) return;
 
-        if (mSuggestion.getType() != OmniboxSuggestionType.SEARCH_SUGGEST_TAIL) {
-            mContentsView.resetTextWidths();
-        }
-
         boolean refineVisible = mRefineView.getVisibility() == VISIBLE;
         boolean isRtl = ApiCompatibilityUtils.isLayoutRtl(this);
         int contentsViewOffsetX = isRtl && refineVisible ? mRefineWidth : 0;
@@ -365,12 +172,12 @@
         int height = mSuggestionHeight;
         boolean refineVisible = mRefineView.getVisibility() == VISIBLE;
         int refineWidth = refineVisible ? mRefineWidth : 0;
-        if (mNumAnswerLines > 1) {
+        if (mSuggestionLayoutType == SuggestionLayoutType.MULTI_LINE_ANSWER) {
             mContentsView.measure(
                     MeasureSpec.makeMeasureSpec(width - refineWidth, MeasureSpec.EXACTLY),
                     MeasureSpec.makeMeasureSpec(mSuggestionAnswerHeight * 2, MeasureSpec.AT_MOST));
             height = mContentsView.getMeasuredHeight();
-        } else if (!TextUtils.isEmpty(mSuggestion.getAnswerContents())) {
+        } else if (mSuggestionLayoutType == SuggestionLayoutType.ANSWER) {
             height = mSuggestionAnswerHeight;
         }
         setMeasuredDimension(width, height);
@@ -379,7 +186,7 @@
         // after setting the height.
         if (width == 0) return;
 
-        if (mNumAnswerLines == 1) {
+        if (mSuggestionLayoutType != SuggestionLayoutType.MULTI_LINE_ANSWER) {
             mContentsView.measure(
                     MeasureSpec.makeMeasureSpec(width - refineWidth, MeasureSpec.EXACTLY),
                     MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
@@ -416,33 +223,39 @@
      * @param suggestionItem The omnibox suggestion item this view represents.
      * @param suggestionDelegate The suggestion delegate.
      * @param position Position of the suggestion in the dropdown list.
-     * @param useDarkColors Whether dark colors should be used for fonts and icons.
      */
+    // TODO(tedchoc): Remove this.
     public void init(OmniboxResultItem suggestionItem, OmniboxSuggestionDelegate suggestionDelegate,
-            int position, boolean useDarkColors) {
+            int position) {
         // Update the position unconditionally.
         mPosition = position;
         jumpDrawablesToCurrentState();
-        mUseDarkColors = useDarkColors;
-        mModel.set(SuggestionViewProperties.USE_DARK_COLORS, mUseDarkColors);
 
-        mSuggestionItem = suggestionItem;
         mSuggestion = suggestionItem.getSuggestion();
         mSuggestionDelegate = suggestionDelegate;
-        // Reset old computations.
-        mContentsView.resetTextWidths();
-        mRefineViewOffsetPx = mRefineViewModernEndPadding;
-
-        // Suggestions with attached answers are rendered with rich results regardless of which
-        // suggestion type they are.
-        if (mSuggestion.hasAnswer()) {
-            setStateForAnswerSuggestion(mSuggestion.getAnswer(), suggestionItem.getAnswerImage());
-        } else {
-            setStateForTextSuggestion(suggestionItem);
-        }
     }
 
-    private void setRefinable(boolean refinable) {
+    /** Set the type of layout this view is rendering. */
+    void setSuggestionLayoutType(@SuggestionLayoutType int type) {
+        mSuggestionLayoutType = type;
+    }
+
+    /** Get the View containing the first line of text. */
+    TextView getTextLine1() {
+        return mContentsView.mTextLine1;
+    }
+
+    /** Get the View containing the second line of text. */
+    TextView getTextLine2() {
+        return mContentsView.mTextLine2;
+    }
+
+    /** Get the View containing the answer image to be shown. */
+    ImageView getAnswerImageView() {
+        return mContentsView.mAnswerImage;
+    }
+
+    void setRefinable(boolean refinable) {
         if (refinable) {
             mRefineView.setVisibility(VISIBLE);
             mRefineView.setOnClickListener(new OnClickListener() {
@@ -460,25 +273,10 @@
         }
     }
 
-    private int getStandardFontColor() {
-        return (mUseDarkColors == null || mUseDarkColors) ? mDarkTitleColorStandardFont
-                                                          : mLightTitleColorStandardFont;
-    }
-
-    private int getStandardUrlColor() {
-        return (mUseDarkColors == null || mUseDarkColors) ? mDarkUrlStandardColor
-                                                          : mLightUrlStandardColor;
-    }
-
-    @Override
-    public void setSelected(boolean selected) {
-        super.setSelected(selected);
-        if (selected && !isInTouchMode()) {
-            mSuggestionDelegate.onSetUrlToSuggestion(mSuggestion);
-        }
-    }
-
-    private void initRefineIcon(boolean useDarkColors) {
+    /**
+     * Initializes the refine icon and sets it to the specified tint.
+     */
+    void initRefineIcon(boolean useDarkColors) {
         if (mRefineIcon != null) return;
         @ColorRes
         int tintId = useDarkColors ? R.color.dark_mode_tint : R.color.light_mode_tint;
@@ -490,7 +288,10 @@
         mRefineView.postInvalidateOnAnimation();
     }
 
-    private void updateRefineIconTint(boolean useDarkColors) {
+    /**
+     * Updates the refine icon (if present) to use the specified tint.
+     */
+    void updateRefineIconTint(boolean useDarkColors) {
         if (mRefineIcon == null) return;
         @ColorRes
         int tintId = useDarkColors ? R.color.dark_mode_tint : R.color.light_mode_tint;
@@ -498,222 +299,42 @@
         mRefineView.postInvalidateOnAnimation();
     }
 
-    private boolean applyHighlightToMatchRegions(
-            Spannable str, List<MatchClassification> classifications) {
-        boolean hasMatch = false;
-        for (int i = 0; i < classifications.size(); i++) {
-            MatchClassification classification = classifications.get(i);
-            if ((classification.style & MatchClassificationStyle.MATCH)
-                    == MatchClassificationStyle.MATCH) {
-                int matchStartIndex = classification.offset;
-                int matchEndIndex;
-                if (i == classifications.size() - 1) {
-                    matchEndIndex = str.length();
-                } else {
-                    matchEndIndex = classifications.get(i + 1).offset;
-                }
-                matchStartIndex = Math.min(matchStartIndex, str.length());
-                matchEndIndex = Math.min(matchEndIndex, str.length());
-
-                hasMatch = true;
-                // Bold the part of the URL that matches the user query.
-                str.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), matchStartIndex,
-                        matchEndIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-            }
-        }
-        return hasMatch;
+    /**
+     * Updates the suggestion icon to the specified drawable with the specified tint.
+     */
+    void setSuggestionIconDrawable(@DrawableRes int resId, boolean useDarkTint) {
+        mContentsView.mSuggestionIcon = TintedDrawable.constructTintedDrawable(getContext(), resId,
+                useDarkTint ? R.color.dark_mode_tint : R.color.white_mode_tint);
+        mContentsView.mSuggestionIcon.setBounds(0, 0,
+                mContentsView.mSuggestionIcon.getIntrinsicWidth(),
+                mContentsView.mSuggestionIcon.getIntrinsicHeight());
+        mContentsView.invalidate();
     }
 
     /**
-     * Get the first line for a text based omnibox suggestion.
-     * @param suggestionItem The item containing the suggestion data.
-     * @param showDescriptionIfPresent Whether to show the description text of the suggestion if
-     *                                 the item contains valid data.
-     * @param shouldHighlight Whether the query should be highlighted.
-     * @return The first line of text.
+     * Updates the suggestion icon (if present) to use the specified tint.
      */
-    private Spannable getSuggestedQuery(OmniboxResultItem suggestionItem,
-            boolean showDescriptionIfPresent, boolean shouldHighlight) {
-        String userQuery = suggestionItem.getMatchedQuery();
-        String suggestedQuery = null;
-        List<MatchClassification> classifications;
-        OmniboxSuggestion suggestion = suggestionItem.getSuggestion();
-        if (showDescriptionIfPresent && !TextUtils.isEmpty(suggestion.getUrl())
-                && !TextUtils.isEmpty(suggestion.getDescription())) {
-            suggestedQuery = suggestion.getDescription();
-            classifications = suggestion.getDescriptionClassifications();
-        } else {
-            suggestedQuery = suggestion.getDisplayText();
-            classifications = suggestion.getDisplayTextClassifications();
-        }
-        if (suggestedQuery == null) {
-            assert false : "Invalid suggestion sent with no displayable text";
-            suggestedQuery = "";
-            classifications = new ArrayList<MatchClassification>();
-            classifications.add(new MatchClassification(0, MatchClassificationStyle.NONE));
-        }
-
-        if (mSuggestion.getType() == OmniboxSuggestionType.SEARCH_SUGGEST_TAIL) {
-            String fillIntoEdit = mSuggestion.getFillIntoEdit();
-            // Data sanity checks.
-            if (fillIntoEdit.startsWith(userQuery) && fillIntoEdit.endsWith(suggestedQuery)
-                    && fillIntoEdit.length() < userQuery.length() + suggestedQuery.length()) {
-                final String ellipsisPrefix = "\u2026 ";
-                suggestedQuery = ellipsisPrefix + suggestedQuery;
-
-                // Offset the match classifications by the length of the ellipsis prefix to ensure
-                // the highlighting remains correct.
-                for (int i = 0; i < classifications.size(); i++) {
-                    classifications.set(i,
-                            new MatchClassification(
-                                    classifications.get(i).offset + ellipsisPrefix.length(),
-                                    classifications.get(i).style));
-                }
-                classifications.add(0, new MatchClassification(0, MatchClassificationStyle.NONE));
-
-                if (DeviceFormFactor.isNonMultiDisplayContextOnTablet(getContext())) {
-                    TextPaint tp = mContentsView.mTextLine1.getPaint();
-                    mContentsView.mRequiredWidth =
-                            tp.measureText(fillIntoEdit, 0, fillIntoEdit.length());
-                    mContentsView.mMatchContentsWidth =
-                            tp.measureText(suggestedQuery, 0, suggestedQuery.length());
-
-                    // Update the max text widths values in SuggestionList. These will be passed to
-                    // the contents view on layout.
-                    mSuggestionDelegate.onTextWidthsUpdated(
-                            mContentsView.mRequiredWidth, mContentsView.mMatchContentsWidth);
-                }
-            }
-        }
-
-        Spannable str = SpannableString.valueOf(suggestedQuery);
-        if (shouldHighlight) applyHighlightToMatchRegions(str, classifications);
-        return str;
-    }
-
-    private void setStateForTextSuggestion(OmniboxResultItem suggestionItem) {
-        int suggestionType = mSuggestion.getType();
-        @SuggestionIcon
-        int suggestionIcon;
-        Spannable textLine1;
-
-        Spannable textLine2;
-        int textLine2Color = 0;
-        int textLine2Direction = TEXT_DIRECTION_INHERIT;
-        if (mSuggestion.isUrlSuggestion()) {
-            suggestionIcon = SuggestionIcon.GLOBE;
-            if (mSuggestion.isStarred()) {
-                suggestionIcon = SuggestionIcon.BOOKMARK;
-            } else if (suggestionType == OmniboxSuggestionType.HISTORY_URL) {
-                suggestionIcon = SuggestionIcon.HISTORY;
-            }
-            boolean urlHighlighted = false;
-            if (!TextUtils.isEmpty(mSuggestion.getUrl())) {
-                OmniboxSuggestion suggestion = suggestionItem.getSuggestion();
-                Spannable str = SpannableString.valueOf(suggestion.getDisplayText());
-                urlHighlighted = applyHighlightToMatchRegions(
-                        str, suggestion.getDisplayTextClassifications());
-                textLine2 = str;
-                textLine2Color = getStandardUrlColor();
-                textLine2Direction = TEXT_DIRECTION_LTR;
-            } else {
-                textLine2 = null;
-            }
-            textLine1 = getSuggestedQuery(suggestionItem, true, !urlHighlighted);
-        } else {
-            suggestionIcon = SuggestionIcon.MAGNIFIER;
-            if (suggestionType == OmniboxSuggestionType.VOICE_SUGGEST) {
-                suggestionIcon = SuggestionIcon.VOICE;
-            } else if ((suggestionType == OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED)
-                    || (suggestionType == OmniboxSuggestionType.SEARCH_HISTORY)) {
-                // Show history icon for suggestions based on user queries.
-                suggestionIcon = SuggestionIcon.HISTORY;
-            }
-            textLine1 = getSuggestedQuery(suggestionItem, false, true);
-            if ((suggestionType == OmniboxSuggestionType.SEARCH_SUGGEST_ENTITY)
-                    || (suggestionType == OmniboxSuggestionType.SEARCH_SUGGEST_PROFILE)) {
-                textLine2 = SpannableString.valueOf(mSuggestion.getDescription());
-                textLine2Color = getStandardFontColor();
-                textLine2Direction = TEXT_DIRECTION_INHERIT;
-            } else {
-                textLine2 = null;
-            }
-        }
-        mNumAnswerLines = 1;
-
-        mModel.set(SuggestionViewProperties.HAS_ANSWER_IMAGE, false);
-        mModel.set(SuggestionViewProperties.ANSWER_IMAGE, null);
-
-        mModel.set(SuggestionViewProperties.TEXT_LINE_1_TEXT, textLine1);
-        mModel.set(SuggestionViewProperties.TEXT_LINE_1_SIZING,
-                Pair.create(TypedValue.COMPLEX_UNIT_PX,
-                        getResources().getDimension(
-                                R.dimen.omnibox_suggestion_first_line_text_size)));
-
-        mModel.set(SuggestionViewProperties.TEXT_LINE_2_TEXT, textLine2);
-        mModel.set(SuggestionViewProperties.TEXT_LINE_2_TEXT_COLOR, textLine2Color);
-        mModel.set(SuggestionViewProperties.TEXT_LINE_2_TEXT_DIRECTION, textLine2Direction);
-        mModel.set(SuggestionViewProperties.TEXT_LINE_2_SIZING,
-                Pair.create(TypedValue.COMPLEX_UNIT_PX,
-                        getResources().getDimension(
-                                R.dimen.omnibox_suggestion_second_line_text_size)));
-        mModel.set(SuggestionViewProperties.TEXT_LINE_2_MAX_LINES, 1);
-
-        boolean sameAsTyped = suggestionItem.getMatchedQuery().trim().equalsIgnoreCase(
-                mSuggestion.getDisplayText());
-        mModel.set(SuggestionViewProperties.REFINABLE, !sameAsTyped);
-
-        mModel.set(SuggestionViewProperties.SUGGESTION_ICON_TYPE, suggestionIcon);
-    }
-
-    static int parseNumAnswerLines(List<SuggestionAnswer.TextField> textFields) {
-        for (int i = 0; i < textFields.size(); i++) {
-            if (textFields.get(i).hasNumLines()) {
-                return Math.min(3, textFields.get(i).getNumLines());
-            }
-        }
-        return -1;
+    void updateSuggestionIconTint(boolean useDarkTint) {
+        if (mContentsView.mSuggestionIcon == null) return;
+        mContentsView.mSuggestionIcon.setTint(AppCompatResources.getColorStateList(
+                getContext(), useDarkTint ? R.color.dark_mode_tint : R.color.white_mode_tint));
+        mContentsView.invalidate();
     }
 
     /**
-     * Sets both lines of the Omnibox suggestion based on an Answers in Suggest result.
-     *
-     * @param answer The answer to be displayed.
-     * @param answerImage The image associated with the answer.
+     * Updates the text alignment constraints to be applied when positioning the text.
      */
-    private void setStateForAnswerSuggestion(SuggestionAnswer answer, Bitmap answerImage) {
-        float density = getResources().getDisplayMetrics().density;
-        SuggestionAnswer.ImageLine firstLine = answer.getFirstLine();
-        SuggestionAnswer.ImageLine secondLine = answer.getSecondLine();
-        mNumAnswerLines = parseNumAnswerLines(secondLine.getTextFields());
-        if (mNumAnswerLines == -1) mNumAnswerLines = 1;
-
-        mModel.set(SuggestionViewProperties.TEXT_LINE_1_SIZING,
-                Pair.create(TypedValue.COMPLEX_UNIT_SP,
-                        (float) AnswerTextBuilder.getMaxTextHeightSp(firstLine)));
-        mModel.set(SuggestionViewProperties.TEXT_LINE_1_TEXT,
-                AnswerTextBuilder.buildSpannable(
-                        firstLine, mContentsView.mTextLine1.getPaint().getFontMetrics(), density));
-
-        mModel.set(SuggestionViewProperties.TEXT_LINE_2_SIZING,
-                Pair.create(TypedValue.COMPLEX_UNIT_SP,
-                        (float) AnswerTextBuilder.getMaxTextHeightSp(secondLine)));
-        mModel.set(SuggestionViewProperties.TEXT_LINE_2_TEXT,
-                AnswerTextBuilder.buildSpannable(
-                        secondLine, mContentsView.mTextLine2.getPaint().getFontMetrics(), density));
-        mModel.set(SuggestionViewProperties.TEXT_LINE_2_MAX_LINES, mNumAnswerLines);
-        mModel.set(SuggestionViewProperties.TEXT_LINE_2_TEXT_COLOR, getStandardFontColor());
-        mModel.set(SuggestionViewProperties.TEXT_LINE_2_TEXT_DIRECTION, TEXT_DIRECTION_INHERIT);
-
-        mModel.set(SuggestionViewProperties.HAS_ANSWER_IMAGE, secondLine.hasImage());
-        mModel.set(SuggestionViewProperties.ANSWER_IMAGE, answerImage);
-
-        mModel.set(SuggestionViewProperties.REFINABLE, true);
-        mModel.set(SuggestionViewProperties.SUGGESTION_ICON_TYPE, SuggestionIcon.MAGNIFIER);
+    void updateTextAlignmentConstraintWidths(float requiredWidth, float matchContentWidth) {
+        mContentsView.mRequiredWidth = requiredWidth;
+        mContentsView.mMatchContentsWidth = matchContentWidth;
     }
 
-    private void updateAnswerImage(Bitmap bitmap) {
-        mContentsView.mAnswerImage.setImageBitmap(bitmap);
+    @Override
+    public void setSelected(boolean selected) {
+        super.setSelected(selected);
+        if (selected && !isInTouchMode()) {
+            mSuggestionDelegate.onSetUrlToSuggestion(mSuggestion);
+        }
     }
 
     /**
@@ -801,11 +422,6 @@
             addView(mAnswerImage);
         }
 
-        private void resetTextWidths() {
-            mRequiredWidth = 0;
-            mMatchContentsWidth = 0;
-        }
-
         @Override
         protected void onDraw(Canvas canvas) {
             super.onDraw(canvas);
@@ -867,7 +483,7 @@
 
             int line2VerticalOffset = line1VerticalOffset + line1Height;
             int answerVerticalOffset = line2VerticalOffset;
-            if (mSuggestion.hasAnswer() && mSuggestion.getAnswer().getSecondLine().hasImage()) {
+            if (mAnswerImage.getVisibility() == VISIBLE) {
                 // The image is positioned vertically aligned with the second text line but
                 // requires a small additional offset to align with the ascent of the text
                 // instead of the top of the text which includes some whitespace.
@@ -936,7 +552,7 @@
                 int desiredHeight = mTextLine1.getMeasuredHeight() + mTextLine2.getMeasuredHeight();
                 int additionalPadding = (int) getResources().getDimension(
                         R.dimen.omnibox_suggestion_text_vertical_padding);
-                if (mSuggestion.hasAnswer()) {
+                if (mSuggestionLayoutType != SuggestionLayoutType.TEXT_SUGGESTION) {
                     additionalPadding += (int) getResources().getDimension(
                             R.dimen.omnibox_suggestion_multiline_text_vertical_padding);
                 }
@@ -966,20 +582,5 @@
             mForceIsFocused = false;
             return drawableState;
         }
-
-        private void setSuggestionIconDrawable(@DrawableRes int resId, boolean useDarkTint) {
-            mSuggestionIcon = TintedDrawable.constructTintedDrawable(getContext(), resId,
-                    useDarkTint ? R.color.dark_mode_tint : R.color.white_mode_tint);
-            mSuggestionIcon.setBounds(0, 0, mSuggestionIcon.getIntrinsicWidth(),
-                    mSuggestionIcon.getIntrinsicHeight());
-            invalidate();
-        }
-
-        private void updateSuggestionIconTint(boolean useDarkTint) {
-            if (mSuggestionIcon == null) return;
-            mSuggestionIcon.setTint(AppCompatResources.getColorStateList(
-                    getContext(), useDarkTint ? R.color.dark_mode_tint : R.color.white_mode_tint));
-            invalidate();
-        }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewProperties.java
new file mode 100644
index 0000000..d0d0f6c7
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewProperties.java
@@ -0,0 +1,147 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.omnibox.suggestions;
+
+import android.graphics.Bitmap;
+import android.support.annotation.IntDef;
+import android.text.Spannable;
+import android.text.TextUtils;
+import android.text.style.UpdateAppearance;
+import android.util.Pair;
+
+import org.chromium.chrome.browser.modelutil.PropertyKey;
+import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
+import org.chromium.chrome.browser.modelutil.PropertyModel.WritableIntPropertyKey;
+import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The properties associated with rendering the suggestion view.
+ */
+class SuggestionViewProperties {
+    @IntDef({SuggestionIcon.UNDEFINED, SuggestionIcon.BOOKMARK, SuggestionIcon.HISTORY,
+            SuggestionIcon.GLOBE, SuggestionIcon.MAGNIFIER, SuggestionIcon.VOICE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SuggestionIcon {
+        int UNDEFINED = -1;
+        int BOOKMARK = 0;
+        int HISTORY = 1;
+        int GLOBE = 2;
+        int MAGNIFIER = 3;
+        int VOICE = 4;
+    }
+
+    /**
+     * Container for suggestion text that prevents updates when the text/spans has not changed.
+     */
+    static class SuggestionTextContainer {
+        public final Spannable text;
+
+        public SuggestionTextContainer(Spannable text) {
+            this.text = text;
+        }
+
+        @Override
+        public String toString() {
+            return "SuggestionTextContainer: " + (text == null ? "null" : text.toString());
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof SuggestionTextContainer)) return false;
+            SuggestionTextContainer other = (SuggestionTextContainer) obj;
+            if (!TextUtils.equals(text, other.text)) return false;
+            if (text == null) return true;
+
+            UpdateAppearance[] thisSpans = text.getSpans(0, text.length(), UpdateAppearance.class);
+            UpdateAppearance[] otherSpans =
+                    other.text.getSpans(0, other.text.length(), UpdateAppearance.class);
+            if (thisSpans.length != otherSpans.length) return false;
+            for (int i = 0; i < thisSpans.length; i++) {
+                UpdateAppearance thisSpan = thisSpans[i];
+                UpdateAppearance otherSpan = otherSpans[i];
+                if (!thisSpan.getClass().equals(otherSpan.getClass())) return false;
+
+                if (text.getSpanStart(thisSpan) != other.text.getSpanStart(otherSpan)
+                        || text.getSpanEnd(thisSpan) != other.text.getSpanEnd(otherSpan)
+                        || text.getSpanFlags(thisSpan) != other.text.getSpanFlags(otherSpan)) {
+                    return false;
+                }
+
+                // TODO(tedchoc): This is a dangerous assumption.  We should actually update all
+                //                span types we use in suggestion text to implement .equals and
+                //                ensure the internal styles (e.g. color used in a foreground span)
+                //                is actually the same.  This "seems" safe for now, but not
+                //                particularly robust.
+                //
+                //                Once that happens, share this logic with
+                //                UrlBarMediator#isNewTextEquivalentToExistingText.
+            }
+            return true;
+        }
+    }
+
+    /** Whether dark colors should be applied to text, icons */
+    public static final WritableBooleanPropertyKey USE_DARK_COLORS =
+            new WritableBooleanPropertyKey();
+
+    /** Whether the suggestion is for an answer. */
+    public static final WritableBooleanPropertyKey IS_ANSWER = new WritableBooleanPropertyKey();
+    /** Whether an answer image will be shown. */
+    public static final WritableBooleanPropertyKey HAS_ANSWER_IMAGE =
+            new WritableBooleanPropertyKey();
+    /** The answer image to be shown. */
+    public static final WritableObjectPropertyKey<Bitmap> ANSWER_IMAGE =
+            new WritableObjectPropertyKey<>();
+
+    /** Whether the suggestion supports refinement. */
+    public static final WritableBooleanPropertyKey REFINABLE = new WritableBooleanPropertyKey();
+
+    /** The suggestion icon type shown. */
+    public static final WritableIntPropertyKey SUGGESTION_ICON_TYPE = new WritableIntPropertyKey();
+
+    /**
+     * The sizing information for the first line of text.
+     *
+     * The first item is the unit of size (e.g. TypedValue.COMPLEX_UNIT_PX), and the second item
+     * is the size itself.
+     */
+    public static final WritableObjectPropertyKey<Pair<Integer, Float>> TEXT_LINE_1_SIZING =
+            new WritableObjectPropertyKey<>();
+    /** The actual text content for the first line of text. */
+    public static final WritableObjectPropertyKey<SuggestionTextContainer> TEXT_LINE_1_TEXT =
+            new WritableObjectPropertyKey<>();
+    /** The alignment constraints for positioning the first line of text. */
+    public static final WritableObjectPropertyKey<Pair<Float, Float>>
+            TEXT_LINE_1_ALIGNMENT_CONSTRAINTS = new WritableObjectPropertyKey<>();
+
+    /**
+     * The sizing information for the second line of text.
+     *
+     * The first item is the unit of size (e.g. TypedValue.COMPLEX_UNIT_PX), and the second item
+     * is the size itself.
+     */
+    public static final WritableObjectPropertyKey<Pair<Integer, Float>> TEXT_LINE_2_SIZING =
+            new WritableObjectPropertyKey<>();
+    /** The maximum number of lines to be shown for the second line of text. */
+    public static final WritableIntPropertyKey TEXT_LINE_2_MAX_LINES = new WritableIntPropertyKey();
+    /** The color to be applied to the second line of text. */
+    public static final WritableIntPropertyKey TEXT_LINE_2_TEXT_COLOR =
+            new WritableIntPropertyKey();
+    /** The direction the text should be laid out for the second line of text. */
+    public static final WritableIntPropertyKey TEXT_LINE_2_TEXT_DIRECTION =
+            new WritableIntPropertyKey();
+    /** The actual text content for the second line of text. */
+    public static final WritableObjectPropertyKey<SuggestionTextContainer> TEXT_LINE_2_TEXT =
+            new WritableObjectPropertyKey<>();
+
+    public static final PropertyKey[] ALL_KEYS =
+            new PropertyKey[] {USE_DARK_COLORS, IS_ANSWER, HAS_ANSWER_IMAGE, ANSWER_IMAGE,
+                    REFINABLE, SUGGESTION_ICON_TYPE, TEXT_LINE_1_SIZING, TEXT_LINE_1_TEXT,
+                    TEXT_LINE_1_ALIGNMENT_CONSTRAINTS, TEXT_LINE_2_SIZING, TEXT_LINE_2_MAX_LINES,
+                    TEXT_LINE_2_TEXT_COLOR, TEXT_LINE_2_TEXT_DIRECTION, TEXT_LINE_2_TEXT};
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewViewBinder.java
new file mode 100644
index 0000000..be962049
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewViewBinder.java
@@ -0,0 +1,154 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.omnibox.suggestions;
+
+import android.content.Context;
+import android.support.annotation.ColorInt;
+import android.support.annotation.ColorRes;
+import android.text.Spannable;
+import android.text.TextUtils;
+import android.util.Pair;
+import android.view.View;
+
+import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.modelutil.PropertyKey;
+import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionViewProperties.SuggestionIcon;
+import org.chromium.ui.base.DeviceFormFactor;
+
+class SuggestionViewViewBinder {
+    /**
+     * @see
+     * org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder#bind(Object,
+     * Object, Object)
+     */
+    public static void bind(PropertyModel model, SuggestionView view, PropertyKey propertyKey) {
+        if (SuggestionViewProperties.USE_DARK_COLORS.equals(propertyKey)) {
+            boolean useDarkColors = model.get(SuggestionViewProperties.USE_DARK_COLORS);
+            view.updateRefineIconTint(useDarkColors);
+            view.updateSuggestionIconTint(useDarkColors);
+            view.getTextLine1().setTextColor(
+                    getStandardFontColor(view.getContext(), useDarkColors));
+        } else if (SuggestionViewProperties.IS_ANSWER.equals(propertyKey)) {
+            updateSuggestionLayoutType(view, model);
+        } else if (SuggestionViewProperties.HAS_ANSWER_IMAGE.equals(propertyKey)) {
+            int visibility =
+                    model.get(SuggestionViewProperties.HAS_ANSWER_IMAGE) ? View.VISIBLE : View.GONE;
+            view.getAnswerImageView().setVisibility(visibility);
+        } else if (SuggestionViewProperties.ANSWER_IMAGE.equals(propertyKey)) {
+            view.getAnswerImageView().setImageBitmap(
+                    model.get(SuggestionViewProperties.ANSWER_IMAGE));
+        } else if (SuggestionViewProperties.REFINABLE.equals(propertyKey)) {
+            boolean refinable = model.get(SuggestionViewProperties.REFINABLE);
+            view.setRefinable(refinable);
+            if (refinable) view.initRefineIcon(model.get(SuggestionViewProperties.USE_DARK_COLORS));
+        } else if (SuggestionViewProperties.SUGGESTION_ICON_TYPE.equals(propertyKey)) {
+            if (!DeviceFormFactor.isNonMultiDisplayContextOnTablet(view.getContext())) return;
+
+            @SuggestionIcon
+            int type = model.get(SuggestionViewProperties.SUGGESTION_ICON_TYPE);
+
+            if (type == SuggestionIcon.UNDEFINED) return;
+
+            int drawableId = R.drawable.ic_omnibox_page;
+            switch (type) {
+                case SuggestionIcon.BOOKMARK:
+                    drawableId = R.drawable.btn_star;
+                    break;
+                case SuggestionIcon.MAGNIFIER:
+                    drawableId = R.drawable.ic_suggestion_magnifier;
+                    break;
+                case SuggestionIcon.HISTORY:
+                    drawableId = R.drawable.ic_suggestion_history;
+                    break;
+                case SuggestionIcon.VOICE:
+                    drawableId = R.drawable.btn_mic;
+                    break;
+                default:
+                    break;
+            }
+            view.setSuggestionIconDrawable(
+                    drawableId, model.get(SuggestionViewProperties.USE_DARK_COLORS));
+        } else if (SuggestionViewProperties.TEXT_LINE_1_SIZING.equals(propertyKey)) {
+            Pair<Integer, Float> sizing = model.get(SuggestionViewProperties.TEXT_LINE_1_SIZING);
+            view.getTextLine1().setTextSize(sizing.first, sizing.second);
+        } else if (SuggestionViewProperties.TEXT_LINE_1_TEXT.equals(propertyKey)) {
+            view.getTextLine1().setText(model.get(SuggestionViewProperties.TEXT_LINE_1_TEXT).text);
+        } else if (SuggestionViewProperties.TEXT_LINE_1_ALIGNMENT_CONSTRAINTS.equals(propertyKey)) {
+            Pair<Float, Float> constraints =
+                    model.get(SuggestionViewProperties.TEXT_LINE_1_ALIGNMENT_CONSTRAINTS);
+            view.updateTextAlignmentConstraintWidths(constraints.first, constraints.second);
+        } else if (SuggestionViewProperties.TEXT_LINE_2_SIZING.equals(propertyKey)) {
+            Pair<Integer, Float> sizing = model.get(SuggestionViewProperties.TEXT_LINE_2_SIZING);
+            view.getTextLine2().setTextSize(sizing.first, sizing.second);
+        } else if (SuggestionViewProperties.TEXT_LINE_2_MAX_LINES.equals(propertyKey)) {
+            updateSuggestionLayoutType(view, model);
+            int numberLines = model.get(SuggestionViewProperties.TEXT_LINE_2_MAX_LINES);
+            if (numberLines == 1) {
+                view.getTextLine2().setEllipsize(null);
+                view.getTextLine2().setSingleLine();
+            } else {
+                view.getTextLine2().setSingleLine(false);
+                view.getTextLine2().setEllipsize(TextUtils.TruncateAt.END);
+                view.getTextLine2().setMaxLines(numberLines);
+            }
+        } else if (SuggestionViewProperties.TEXT_LINE_2_TEXT_COLOR.equals(propertyKey)) {
+            view.getTextLine2().setTextColor(
+                    model.get(SuggestionViewProperties.TEXT_LINE_2_TEXT_COLOR));
+        } else if (SuggestionViewProperties.TEXT_LINE_2_TEXT_DIRECTION.equals(propertyKey)) {
+            ApiCompatibilityUtils.setTextDirection(view.getTextLine2(),
+                    model.get(SuggestionViewProperties.TEXT_LINE_2_TEXT_DIRECTION));
+        } else if (SuggestionViewProperties.TEXT_LINE_2_TEXT.equals(propertyKey)) {
+            Spannable line2Text = model.get(SuggestionViewProperties.TEXT_LINE_2_TEXT).text;
+            if (TextUtils.isEmpty(line2Text)) {
+                view.getTextLine2().setVisibility(View.INVISIBLE);
+            } else {
+                view.getTextLine2().setVisibility(View.VISIBLE);
+                view.getTextLine2().setText(line2Text);
+            }
+        }
+    }
+
+    private static void updateSuggestionLayoutType(SuggestionView view, PropertyModel model) {
+        boolean isAnswer = model.get(SuggestionViewProperties.IS_ANSWER);
+        int numberLines = model.get(SuggestionViewProperties.TEXT_LINE_2_MAX_LINES);
+        if (!isAnswer) {
+            view.setSuggestionLayoutType(SuggestionView.SuggestionLayoutType.TEXT_SUGGESTION);
+        } else {
+            view.setSuggestionLayoutType(numberLines > 1
+                            ? SuggestionView.SuggestionLayoutType.MULTI_LINE_ANSWER
+                            : SuggestionView.SuggestionLayoutType.ANSWER);
+        }
+    }
+
+    /**
+     * Get the appropriate font color to be used for non-URL text in suggestions.
+     * @param context The context to load the color.
+     * @param useDarkColors Whether dark colors should be used.
+     * @return The font color to be used.
+     */
+    @ColorInt
+    public static int getStandardFontColor(Context context, boolean useDarkColors) {
+        @ColorRes
+        int res = useDarkColors ? R.color.url_emphasis_default_text
+                                : R.color.url_emphasis_light_default_text;
+        return ApiCompatibilityUtils.getColor(context.getResources(), res);
+    }
+
+    /**
+     * Get the appropriate font color to be used for URL text in suggestions.
+     * @param context The context to load the color.
+     * @param useDarkColors Whether dark colors should be used.
+     * @return The font color to be used.
+     */
+    @ColorInt
+    public static int getStandardUrlColor(Context context, boolean useDarkColors) {
+        @ColorRes
+        int res = useDarkColors ? R.color.suggestion_url_dark_modern
+                                : R.color.suggestion_url_light_modern;
+        return ApiCompatibilityUtils.getColor(context.getResources(), res);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/EmptyTabObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/EmptyTabObserver.java
index 9c732090..e524b1a2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/EmptyTabObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/EmptyTabObserver.java
@@ -134,7 +134,7 @@
     public void onInteractabilityChanged(boolean isInteractable) {}
 
     @Override
-    public void onRendererResponsiveStateChanged(boolean isResponsive) {}
+    public void onRendererResponsiveStateChanged(Tab tab, boolean isResponsive) {}
 
     @Override
     public void onNavigationEntriesDeleted(Tab tab) {}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
index 8147de6..b8439da 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -400,16 +400,12 @@
 
     // TODO(dtrainor): Port more methods to the observer.
     private final TabObserver mTabObserver = new EmptyTabObserver() {
-        /** A runnable to delay the enabling of fullscreen mode if necessary. */
-        private Runnable mEnterFullscreenRunnable;
-
         @Override
         public void onSSLStateUpdated(Tab tab) {
             PolicyAuditor auditor = AppHooks.get().getPolicyAuditor();
             auditor.notifyCertificateFailure(
                     PolicyAuditor.nativeGetCertificateFailure(getWebContents()),
                     getApplicationContext());
-            updateFullscreenEnabledState();
             updateThemeColorIfNeeded(false);
         }
 
@@ -431,62 +427,10 @@
                 didFinishPageLoad();
             }
         }
-
-        @Override
-        public void onInteractabilityChanged(boolean interactable) {
-            if (interactable && mEnterFullscreenRunnable != null) mEnterFullscreenRunnable.run();
-        }
-
-        @Override
-        public void onEnterFullscreenMode(Tab tab, final FullscreenOptions options) {
-            if (!isUserInteractable()) {
-                mEnterFullscreenRunnable = new Runnable() {
-                    @Override
-                    public void run() {
-                        enterFullscreenInternal(options);
-                        mEnterFullscreenRunnable = null;
-                    }
-                };
-                return;
-            }
-
-            enterFullscreenInternal(options);
-        }
-
-        @Override
-        public void onExitFullscreenMode(Tab tab) {
-            if (mEnterFullscreenRunnable != null) {
-                mEnterFullscreenRunnable = null;
-                return;
-            }
-
-            if (mFullscreenManager != null) {
-                mFullscreenManager.exitPersistentFullscreenMode();
-            }
-        }
-
-        /**
-         * Do the actual enter of fullscreen mode.
-         * @param options Options adjust fullscreen mode.
-         */
-        private void enterFullscreenInternal(FullscreenOptions options) {
-            if (mFullscreenManager != null) {
-                mFullscreenManager.enterPersistentFullscreenMode(options);
-            }
-
-            if (getWebContents() != null) {
-                SelectionPopupController controller =
-                        SelectionPopupController.fromWebContents(getWebContents());
-                controller.destroySelectActionMode();
-            }
-
-            // We want to remove any cached thumbnail of the Tab.
-            if (mNativeTabAndroid != 0) {
-                nativeClearThumbnailPlaceholder(mNativeTabAndroid);
-            }
-        }
     };
 
+    private final TabObserver mFullscreenHandler = new TabFullscreenHandler();
+
     private TabDelegateFactory mDelegateFactory;
 
     private BrowserControlsVisibilityDelegate mBrowserControlsVisibilityDelegate;
@@ -567,6 +511,7 @@
         }
 
         addObserver(mTabObserver);
+        addObserver(mFullscreenHandler);
 
         if (incognito) {
             CipherFactory.getInstance().triggerKeyGeneration();
@@ -1547,6 +1492,10 @@
         nativeAttachToTabContentManager(mNativeTabAndroid, tabContentManager);
     }
 
+    void clearThumbnailPlaceholder() {
+        if (mNativeTabAndroid != 0) nativeClearThumbnailPlaceholder(mNativeTabAndroid);
+    }
+
     /**
      * @return The delegate factory for testing purposes only.
      */
@@ -3025,13 +2974,7 @@
     void handleRendererResponsiveStateChanged(boolean isResponsive) {
         mIsRendererUnresponsive = !isResponsive;
         for (TabObserver observer : mObservers) {
-            observer.onRendererResponsiveStateChanged(isResponsive);
-        }
-        if (mFullscreenManager == null) return;
-        if (isResponsive) {
-            updateFullscreenEnabledState();
-        } else {
-            updateBrowserControlsState(BrowserControlsState.SHOWN, false);
+            observer.onRendererResponsiveStateChanged(this, isResponsive);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsOffsetHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsOffsetHelper.java
index 4a4979ec..4885719 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsOffsetHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsOffsetHelper.java
@@ -81,7 +81,7 @@
                 if (SadTab.isShowing(tab)) showAndroidControls(false);
             }
             @Override
-            public void onRendererResponsiveStateChanged(boolean isResponsive) {
+            public void onRendererResponsiveStateChanged(Tab tab, boolean isResponsive) {
                 if (!isResponsive) showAndroidControls(false);
             }
         };
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabFullscreenHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabFullscreenHandler.java
new file mode 100644
index 0000000..17ea187
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabFullscreenHandler.java
@@ -0,0 +1,84 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tab;
+
+import org.chromium.chrome.browser.fullscreen.FullscreenOptions;
+import org.chromium.content_public.browser.SelectionPopupController;
+import org.chromium.content_public.common.BrowserControlsState;
+
+/**
+ * {@link TabObserver} for basic fullscreen operations for {@link Tab}.
+ */
+public final class TabFullscreenHandler extends EmptyTabObserver {
+    /** A runnable to delay the enabling of fullscreen mode if necessary. */
+    private Runnable mEnterFullscreenRunnable;
+
+    @Override
+    public void onSSLStateUpdated(Tab tab) {
+        tab.updateFullscreenEnabledState();
+    }
+
+    @Override
+    public void onInteractabilityChanged(boolean interactable) {
+        if (interactable && mEnterFullscreenRunnable != null) mEnterFullscreenRunnable.run();
+    }
+
+    @Override
+    public void onEnterFullscreenMode(Tab tab, final FullscreenOptions options) {
+        if (!tab.isUserInteractable()) {
+            mEnterFullscreenRunnable = new Runnable() {
+                @Override
+                public void run() {
+                    enterFullscreenInternal(tab, options);
+                    mEnterFullscreenRunnable = null;
+                }
+            };
+            return;
+        }
+
+        enterFullscreenInternal(tab, options);
+    }
+
+    @Override
+    public void onExitFullscreenMode(Tab tab) {
+        if (mEnterFullscreenRunnable != null) {
+            mEnterFullscreenRunnable = null;
+            return;
+        }
+
+        if (tab.getFullscreenManager() != null) {
+            tab.getFullscreenManager().exitPersistentFullscreenMode();
+        }
+    }
+
+    /**
+     * Do the actual enter of fullscreen mode.
+     * @param options Options adjust fullscreen mode.
+     */
+    private void enterFullscreenInternal(Tab tab, FullscreenOptions options) {
+        if (tab.getFullscreenManager() != null) {
+            tab.getFullscreenManager().enterPersistentFullscreenMode(options);
+        }
+
+        if (tab.getWebContents() != null) {
+            SelectionPopupController controller =
+                    SelectionPopupController.fromWebContents(tab.getWebContents());
+            controller.destroySelectActionMode();
+        }
+
+        // We want to remove any cached thumbnail of the Tab.
+        tab.clearThumbnailPlaceholder();
+    }
+
+    @Override
+    public void onRendererResponsiveStateChanged(Tab tab, boolean isResponsive) {
+        if (tab.getFullscreenManager() == null) return;
+        if (isResponsive) {
+            tab.updateFullscreenEnabledState();
+        } else {
+            tab.updateBrowserControlsState(BrowserControlsState.SHOWN, false);
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabObserver.java
index 4c575cc5..81328503 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabObserver.java
@@ -315,9 +315,10 @@
 
     /**
      * Called when renderer changes its state about being responsive to requests.
+     * @param tab The notifying {@link Tab}.
      * @param {@code true} if the renderer becomes responsive, otherwise {@code false}.
      */
-    public void onRendererResponsiveStateChanged(boolean isResponsive);
+    public void onRendererResponsiveStateChanged(Tab tab, boolean isResponsive);
 
     /**
      * Called when navigation entries of a tab have been deleted.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
index dbbfe45..192f028 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
@@ -834,4 +834,7 @@
 
     @Override
     public void setScrim(ScrimView scrim) {}
+
+    @Override
+    public void setUnfocusedWidth(float unfocusedWidth) {}
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
index f9bce14..8a39aad5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
@@ -648,6 +648,7 @@
         mUnfocusedLocationBarLayoutWidth = rightViewBounds - leftViewBounds;
         mUnfocusedLocationBarLayoutLeft = leftViewBounds;
         mUnfocusedLocationBarLayoutRight = rightViewBounds;
+        mLocationBar.setUnfocusedWidth(mUnfocusedLocationBarLayoutWidth);
     }
 
     /**
@@ -2861,8 +2862,10 @@
     }
 
     private void setTabSwitcherAnimationMenuDrawable() {
-        mTabSwitcherAnimationMenuDrawable = ApiCompatibilityUtils.getDrawable(
-                getResources(), R.drawable.ic_more_vert_black_24dp);
+        mTabSwitcherAnimationMenuDrawable =
+                ApiCompatibilityUtils
+                        .getDrawable(getResources(), R.drawable.ic_more_vert_black_24dp)
+                        .mutate();
         ((BitmapDrawable) mTabSwitcherAnimationMenuDrawable).setGravity(Gravity.CENTER);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java
index 11dfd8d6..7227ad3e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java
@@ -57,6 +57,7 @@
         intent.setData(Uri.parse(url));
         intent.putExtra(CustomTabIntentDataProvider.EXTRA_SEND_TO_EXTERNAL_DEFAULT_HANDLER, true);
         intent.putExtra(CustomTabIntentDataProvider.EXTRA_IS_OPENED_BY_CHROME, true);
+        intent.putExtra(CustomTabIntentDataProvider.EXTRA_IS_OPENED_BY_WEBAPK, true);
         intent.putExtra(CustomTabIntentDataProvider.EXTRA_BROWSER_LAUNCH_SOURCE, mActivityType);
         intent.putExtra(Browser.EXTRA_APPLICATION_ID, mApkPackageName);
         addAsyncTabExtras(asyncParams, parentId, false /* isChromeUI */, assignedTabId, intent);
@@ -85,8 +86,8 @@
             for (String result : ExternalNavigationDelegateImpl.getSpecializedHandlersWithFilter(
                          handlers, null, null)) {
                 if (result.equals(mApkPackageName)) {
-                    // Current webapk matches, don't intercept so that we can launch a cct. See
-                    // http://crbug.com/831806 for more context.
+                    // Current WebAPK matches and this is a HTTP(s) link. Don't intercept so that we
+                    // can launch a CCT. See http://crbug.com/831806 for more context.
                     return false;
                 } else {
                     foundSpecializedHandler = true;
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index f649c4d..6502ae4b 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -2286,9 +2286,6 @@
       </message>
 
       <!-- Download UI -->
-      <message name="IDS_DOWNLOAD_NOTIFICATION_SUMMARY_TITLE" desc="Text that shows up for a summary notification for all downloads.">
-        Chrome Downloads
-      </message>
       <message name="IDS_DOWNLOAD_NOTIFICATION_CANCEL_BUTTON" desc="Text on the button that cancels a download.">
         Cancel
       </message>
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 03644f8..d1ed13ef 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -443,7 +443,6 @@
   "java/src/org/chromium/chrome/browser/download/DirectoryOption.java",
   "java/src/org/chromium/chrome/browser/download/DownloadActivity.java",
   "java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java",
-  "java/src/org/chromium/chrome/browser/download/DownloadBroadcastReceiver.java",
   "java/src/org/chromium/chrome/browser/download/DownloadController.java",
   "java/src/org/chromium/chrome/browser/download/DownloadDirectoryProvider.java",
   "java/src/org/chromium/chrome/browser/download/DownloadForegroundService.java",
@@ -460,7 +459,6 @@
   "java/src/org/chromium/chrome/browser/download/DownloadMediaParserBridge.java",
   "java/src/org/chromium/chrome/browser/download/DownloadMetrics.java",
   "java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java",
-  "java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java",
   "java/src/org/chromium/chrome/browser/download/DownloadNotificationService2.java",
   "java/src/org/chromium/chrome/browser/download/DownloadNotificationServiceObserver.java",
   "java/src/org/chromium/chrome/browser/download/DownloadNotificationUmaHelper.java",
@@ -476,7 +474,6 @@
   "java/src/org/chromium/chrome/browser/download/DownloadUpdate.java",
   "java/src/org/chromium/chrome/browser/download/DownloadUtils.java",
   "java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java",
-  "java/src/org/chromium/chrome/browser/download/SystemDownloadNotifier.java",
   "java/src/org/chromium/chrome/browser/download/SystemDownloadNotifier2.java",
   "java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinator.java",
   "java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorFactory.java",
@@ -484,7 +481,6 @@
   "java/src/org/chromium/chrome/browser/download/home/DownloadManagerUiConfig.java",
   "java/src/org/chromium/chrome/browser/download/home/JustNowProvider.java",
   "java/src/org/chromium/chrome/browser/download/home/OfflineItemSource.java",
-  "java/src/org/chromium/chrome/browser/download/home/PrefetchStatusProvider.java",
   "java/src/org/chromium/chrome/browser/download/home/StableIds.java",
   "java/src/org/chromium/chrome/browser/download/home/empty/EmptyCoordinator.java",
   "java/src/org/chromium/chrome/browser/download/home/empty/EmptyProperties.java",
@@ -1101,6 +1097,8 @@
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsList.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionAnswer.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionView.java",
+  "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewProperties.java",
+  "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewViewBinder.java",
   "java/src/org/chromium/chrome/browser/page_info/CertificateChainHelper.java",
   "java/src/org/chromium/chrome/browser/page_info/CertificateViewer.java",
   "java/src/org/chromium/chrome/browser/page_info/ConnectionInfoPopup.java",
@@ -1476,6 +1474,7 @@
   "java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java",
   "java/src/org/chromium/chrome/browser/tab/TabContextMenuPopulator.java",
   "java/src/org/chromium/chrome/browser/tab/TabDelegateFactory.java",
+  "java/src/org/chromium/chrome/browser/tab/TabFullscreenHandler.java",
   "java/src/org/chromium/chrome/browser/tab/TabGestureStateListener.java",
   "java/src/org/chromium/chrome/browser/tab/TabIdManager.java",
   "java/src/org/chromium/chrome/browser/tab/TabObserver.java",
@@ -1868,7 +1867,6 @@
   "javatests/src/org/chromium/chrome/browser/download/DownloadInfoBarControllerTest.java",
   "javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java",
   "javatests/src/org/chromium/chrome/browser/download/DownloadMediaParserTest.java",
-  "javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java",
   "javatests/src/org/chromium/chrome/browser/download/DownloadNotificationService2Test.java",
   "javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManagerTest.java",
   "javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceTest.java",
@@ -1876,7 +1874,6 @@
   "javatests/src/org/chromium/chrome/browser/download/DownloadTest.java",
   "javatests/src/org/chromium/chrome/browser/download/DownloadTestRule.java",
   "javatests/src/org/chromium/chrome/browser/download/DownloadUtilsTest.java",
-  "javatests/src/org/chromium/chrome/browser/download/MockDownloadNotificationService.java",
   "javatests/src/org/chromium/chrome/browser/download/MockDownloadNotificationService2.java",
   "javatests/src/org/chromium/chrome/browser/download/OMADownloadHandlerTest.java",
   "javatests/src/org/chromium/chrome/browser/download/SystemDownloadNotifier2Test.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java
index d18a8d85..e7cc97f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java
@@ -61,7 +61,7 @@
      * The MockDownloadNotifier. Currently there is no support for creating mock objects this is a
      * simple mock object that provides testing support for checking a sequence of calls.
      */
-    static class MockDownloadNotifier extends SystemDownloadNotifier {
+    static class MockDownloadNotifier extends SystemDownloadNotifier2 {
         /**
          * The Ids of different methods in this mock object.
          */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java
deleted file mode 100644
index 7f4fab6..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java
+++ /dev/null
@@ -1,644 +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.
-
-package org.chromium.chrome.browser.download;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.ServiceTestCase;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.ContextUtils;
-import org.chromium.base.ThreadUtils;
-import org.chromium.base.test.util.AdvancedMockContext;
-import org.chromium.base.test.util.DisabledTest;
-import org.chromium.base.test.util.Feature;
-import org.chromium.components.background_task_scheduler.BackgroundTaskScheduler;
-import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
-import org.chromium.components.background_task_scheduler.TaskInfo;
-import org.chromium.components.offline_items_collection.ContentId;
-import org.chromium.components.offline_items_collection.FailState;
-import org.chromium.components.offline_items_collection.LegacyHelpers;
-import org.chromium.components.offline_items_collection.OfflineItem.Progress;
-import org.chromium.components.offline_items_collection.OfflineItemProgressUnit;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.UUID;
-
-/**
- * Tests of {@link DownloadNotificationService}.
- */
-@RunWith(AndroidJUnit4.class)
-public class DownloadNotificationServiceTest
-        extends ServiceTestCase<MockDownloadNotificationService> {
-    private static class MockDownloadManagerService extends DownloadManagerService {
-        final List<DownloadItem> mDownloads = new ArrayList<DownloadItem>();
-
-        public MockDownloadManagerService() {
-            super(null, getTestHandler(), 1000);
-        }
-
-        @Override
-        protected void init() {}
-
-        @Override
-        public void resumeDownload(ContentId id, DownloadItem item, boolean hasUserGesture) {
-            mDownloads.add(item);
-        }
-    }
-
-    private static class MockBackgroundTaskScheduler implements BackgroundTaskScheduler {
-        boolean mScheduled;
-
-        @Override
-        public boolean schedule(Context context, TaskInfo taskInfo) {
-            mScheduled = true;
-            return true;
-        }
-
-        @Override
-        public void cancel(Context context, int taskId) {
-            mScheduled = false;
-        }
-
-        @Override
-        public void checkForOSUpgrade(Context context) {}
-
-        @Override
-        public void reschedule(Context context) {}
-    }
-
-    private static String buildEntryStringWithGuid(String guid, int notificationId, String fileName,
-            boolean metered, boolean autoResume, boolean offTheRecord) {
-        return new DownloadSharedPreferenceEntry(LegacyHelpers.buildLegacyContentId(false, guid),
-                notificationId, offTheRecord, metered, fileName, autoResume, false)
-                .getSharedPreferenceString();
-    }
-
-    private static String buildEntryStringWithGuid(
-            String guid, int notificationId, String fileName, boolean metered, boolean autoResume) {
-        return buildEntryStringWithGuid(guid, notificationId, fileName, metered, autoResume, false);
-    }
-
-    private static String buildEntryString(
-            int notificationId, String fileName, boolean metered, boolean autoResume) {
-        return buildEntryStringWithGuid(
-                UUID.randomUUID().toString(), notificationId, fileName, metered, autoResume);
-    }
-
-    public DownloadNotificationServiceTest() {
-        super(MockDownloadNotificationService.class);
-    }
-
-    @Override
-    protected void shutdownService() {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                DownloadNotificationServiceTest.super.shutdownService();
-            }
-        });
-    }
-
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        setContext(InstrumentationRegistry.getTargetContext());
-        SharedPreferences sharedPrefs = ContextUtils.getAppSharedPreferences();
-        // TODO(yolandyan): added for debugging reasons, remove if tests no longer flakes
-        Assert.assertNull(sharedPrefs.getStringSet(
-            DownloadSharedPreferenceHelper.KEY_PENDING_DOWNLOAD_NOTIFICATIONS, null));
-    }
-
-    @After
-    @Override
-    public void tearDown() throws Exception {
-        SharedPreferences sharedPrefs = ContextUtils.getAppSharedPreferences();
-        SharedPreferences.Editor editor = sharedPrefs.edit();
-        editor.remove(DownloadSharedPreferenceHelper.KEY_PENDING_DOWNLOAD_NOTIFICATIONS);
-        editor.apply();
-        super.tearDown();
-    }
-
-    private void startNotificationService() {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                Intent intent = new Intent(getService(), MockDownloadNotificationService.class);
-                startService(intent);
-            }
-        });
-    }
-
-    private DownloadNotificationService bindNotificationService() {
-        Intent intent = new Intent(getService(), MockDownloadNotificationService.class);
-        IBinder service = bindService(intent);
-        return ((DownloadNotificationService.LocalBinder) service).getService();
-    }
-
-    private static Handler getTestHandler() {
-        HandlerThread handlerThread = new HandlerThread("handlerThread");
-        handlerThread.start();
-        return new Handler(handlerThread.getLooper());
-    }
-
-    private void resumeAllDownloads(final DownloadNotificationService service) throws Exception {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                service.resumeAllPendingDownloads();
-            }
-        });
-    }
-
-    /**
-     * Tests that creating the service without launching chrome will do nothing if there is no
-     * ongoing download.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Download"})
-    @DisabledTest(message = "crbug.com/773346")
-    public void testPausingWithoutOngoingDownloads() {
-        setupService();
-        startNotificationService();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                getService().updateNotificationsForShutdown();
-            }
-        });
-        Assert.assertTrue(getService().isPaused());
-        Assert.assertTrue(getService().getNotificationIds().isEmpty());
-    }
-
-    /**
-     * Tests that download resumption task is scheduled when notification service is started
-     * without any download action.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Download"})
-    @DisabledTest(message = "crbug.com/773346")
-    public void testResumptionScheduledWithoutDownloadOperationIntent() throws Exception {
-        MockBackgroundTaskScheduler scheduler = new MockBackgroundTaskScheduler();
-        BackgroundTaskSchedulerFactory.setSchedulerForTesting(scheduler);
-        setupService();
-        Set<String> notifications = new HashSet<>();
-        notifications.add(buildEntryString(1, "test1", true, true));
-        SharedPreferences sharedPrefs = ContextUtils.getAppSharedPreferences();
-        SharedPreferences.Editor editor = sharedPrefs.edit();
-        editor.putStringSet(
-                DownloadSharedPreferenceHelper.KEY_PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
-        editor.apply();
-        startNotificationService();
-        shutdownService();
-        Assert.assertTrue(scheduler.mScheduled);
-    }
-
-    /**
-     * Tests that download resumption task is not scheduled when notification service is started
-     * with a download action.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Download"})
-    @DisabledTest(message = "crbug.com/653609")
-    public void testResumptionNotScheduledWithDownloadOperationIntent() {
-        MockBackgroundTaskScheduler scheduler = new MockBackgroundTaskScheduler();
-        BackgroundTaskSchedulerFactory.setSchedulerForTesting(scheduler);
-        setupService();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                Intent intent = new Intent(getService(), MockDownloadNotificationService.class);
-                intent.setAction(DownloadNotificationService.ACTION_DOWNLOAD_RESUME_ALL);
-                startService(intent);
-            }
-        });
-        Assert.assertFalse(scheduler.mScheduled);
-    }
-
-    /**
-     * Tests that download resumption task is not scheduled when there is no auto resumable
-     * download in SharedPreferences.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Download"})
-    @DisabledTest(message = "crbug.com/773346")
-    public void testResumptionNotScheduledWithoutAutoResumableDownload() throws Exception {
-        MockBackgroundTaskScheduler scheduler = new MockBackgroundTaskScheduler();
-        BackgroundTaskSchedulerFactory.setSchedulerForTesting(scheduler);
-        setupService();
-        Set<String> notifications = new HashSet<>();
-        notifications.add(buildEntryString(1, "test1", true, false));
-        SharedPreferences sharedPrefs = ContextUtils.getAppSharedPreferences();
-        SharedPreferences.Editor editor = sharedPrefs.edit();
-        editor.putStringSet(
-                DownloadSharedPreferenceHelper.KEY_PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
-        editor.apply();
-        startNotificationService();
-        Assert.assertFalse(scheduler.mScheduled);
-    }
-
-    /**
-     * Tests that creating the service without launching chrome will pause all ongoing downloads.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Download"})
-    @DisabledTest(message = "crbug.com/773346")
-    public void testPausingWithOngoingDownloads() {
-        setupService();
-        Context mockContext = new AdvancedMockContext(InstrumentationRegistry.getTargetContext());
-        getService().setContext(mockContext);
-        Set<String> notifications = new HashSet<>();
-        notifications.add(buildEntryString(1, "test1", true, true));
-        notifications.add(buildEntryString(2, "test2", true, true));
-        SharedPreferences sharedPrefs = ContextUtils.getAppSharedPreferences();
-        SharedPreferences.Editor editor = sharedPrefs.edit();
-        editor.putStringSet(
-                DownloadSharedPreferenceHelper.KEY_PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
-        editor.apply();
-        startNotificationService();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                getService().updateNotificationsForShutdown();
-            }
-        });
-        Assert.assertTrue(getService().isPaused());
-        Assert.assertEquals(2, getService().getNotificationIds().size());
-        Assert.assertTrue(getService().getNotificationIds().contains(1));
-        Assert.assertTrue(getService().getNotificationIds().contains(2));
-        Assert.assertTrue(sharedPrefs.contains(
-                DownloadSharedPreferenceHelper.KEY_PENDING_DOWNLOAD_NOTIFICATIONS));
-    }
-
-    /**
-     * Tests adding and cancelling notifications.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Download"})
-    @DisabledTest(message = "crbug.com/773346")
-    public void testAddingAndCancelingNotifications() {
-        setupService();
-        Context mockContext = new AdvancedMockContext(InstrumentationRegistry.getTargetContext());
-        getService().setContext(mockContext);
-        Set<String> notifications = new HashSet<>();
-        String guid1 = UUID.randomUUID().toString();
-        String guid2 = UUID.randomUUID().toString();
-        notifications.add(buildEntryStringWithGuid(guid1, 3, "success", true, true));
-        notifications.add(buildEntryStringWithGuid(guid2, 4, "failed", true, true));
-        SharedPreferences sharedPrefs = ContextUtils.getAppSharedPreferences();
-        SharedPreferences.Editor editor = sharedPrefs.edit();
-        editor.putStringSet(
-                DownloadSharedPreferenceHelper.KEY_PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
-        editor.apply();
-        startNotificationService();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                getService().updateNotificationsForShutdown();
-            }
-        });
-        Assert.assertEquals(2, getService().getNotificationIds().size());
-        Assert.assertTrue(getService().getNotificationIds().contains(3));
-        Assert.assertTrue(getService().getNotificationIds().contains(4));
-
-        DownloadNotificationService service = bindNotificationService();
-        ContentId id3 = LegacyHelpers.buildLegacyContentId(false, UUID.randomUUID().toString());
-        service.notifyDownloadProgress(id3, "test",
-                new Progress(1, 100L, OfflineItemProgressUnit.PERCENTAGE), 100L, 1L, 1L, true, true,
-                false, null);
-        Assert.assertEquals(3, getService().getNotificationIds().size());
-        int lastNotificationId = getService().getLastAddedNotificationId();
-        Set<String> entries = DownloadManagerService.getStoredDownloadInfo(
-                sharedPrefs, DownloadSharedPreferenceHelper.KEY_PENDING_DOWNLOAD_NOTIFICATIONS);
-        Assert.assertEquals(3, entries.size());
-
-        ContentId id1 = LegacyHelpers.buildLegacyContentId(false, guid1);
-        service.notifyDownloadSuccessful(
-                id1, "/path/to/success", "success", 100L, false, false, true, null, null, null);
-        entries = DownloadManagerService.getStoredDownloadInfo(
-                sharedPrefs, DownloadSharedPreferenceHelper.KEY_PENDING_DOWNLOAD_NOTIFICATIONS);
-        Assert.assertEquals(2, entries.size());
-
-        ContentId id2 = LegacyHelpers.buildLegacyContentId(false, guid2);
-        service.notifyDownloadFailed(id2, "failed", null, FailState.CANNOT_DOWNLOAD);
-        entries = DownloadManagerService.getStoredDownloadInfo(
-                sharedPrefs, DownloadSharedPreferenceHelper.KEY_PENDING_DOWNLOAD_NOTIFICATIONS);
-        Assert.assertEquals(1, entries.size());
-
-        service.notifyDownloadCanceled(id3);
-        Assert.assertEquals(2, getService().getNotificationIds().size());
-        Assert.assertFalse(getService().getNotificationIds().contains(lastNotificationId));
-
-        ContentId id4 = LegacyHelpers.buildLegacyContentId(false, UUID.randomUUID().toString());
-        service.notifyDownloadSuccessful(
-                id4, "/path/to/success", "success", 100L, false, false, true, null, null, null);
-        Assert.assertEquals(3, getService().getNotificationIds().size());
-        int nextNotificationId = getService().getLastAddedNotificationId();
-        service.cancelNotification(nextNotificationId, id4);
-        Assert.assertEquals(2, getService().getNotificationIds().size());
-        Assert.assertFalse(getService().getNotificationIds().contains(nextNotificationId));
-    }
-
-    /**
-     * Tests that notification is updated if download success comes without any prior progress.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Download"})
-    @DisabledTest(message = "crbug.com/773346")
-    public void testDownloadSuccessNotification() {
-        setupService();
-        startNotificationService();
-        DownloadNotificationService service = bindNotificationService();
-        ContentId id = LegacyHelpers.buildLegacyContentId(false, UUID.randomUUID().toString());
-        service.notifyDownloadSuccessful(
-                id, "/path/to/test", "test", 100L, false, false, true, null, null, null);
-        Assert.assertEquals(1, getService().getNotificationIds().size());
-    }
-
-    /**
-     * Tests resume all pending downloads. Only auto resumable downloads can resume.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Download"})
-    @DisabledTest(message = "crbug.com/773346")
-    public void testResumeAllPendingDownloads() throws Exception {
-        setupService();
-        Context mockContext = new AdvancedMockContext(InstrumentationRegistry.getTargetContext());
-        getService().setContext(mockContext);
-        Set<String> notifications = new HashSet<>();
-        String guid1 = UUID.randomUUID().toString();
-        String guid2 = UUID.randomUUID().toString();
-        String guid3 = UUID.randomUUID().toString();
-
-        notifications.add(buildEntryStringWithGuid(guid1, 3, "success", false, true));
-        notifications.add(buildEntryStringWithGuid(guid2, 4, "failed", true, true));
-        notifications.add(buildEntryStringWithGuid(guid3, 5, "nonresumable", true, false));
-
-        SharedPreferences sharedPrefs = ContextUtils.getAppSharedPreferences();
-        SharedPreferences.Editor editor = sharedPrefs.edit();
-        editor.putStringSet(
-                DownloadSharedPreferenceHelper.KEY_PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
-        editor.apply();
-        startNotificationService();
-        DownloadNotificationService service = bindNotificationService();
-        DownloadManagerService.disableNetworkListenerForTest();
-
-        final MockDownloadManagerService manager = new MockDownloadManagerService();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                DownloadManagerService.setDownloadManagerService(manager);
-            }
-        });
-        DownloadManagerService.setIsNetworkMeteredForTest(true);
-        resumeAllDownloads(service);
-        Assert.assertEquals(1, manager.mDownloads.size());
-        Assert.assertEquals(manager.mDownloads.get(0).getDownloadInfo().getDownloadGuid(), guid2);
-
-        manager.mDownloads.clear();
-        DownloadManagerService.setIsNetworkMeteredForTest(false);
-        resumeAllDownloads(service);
-        Assert.assertEquals(1, manager.mDownloads.size());
-        Assert.assertEquals(manager.mDownloads.get(0).getDownloadInfo().getDownloadGuid(), guid1);
-    }
-
-    /**
-     * Tests incognito download fails when browser gets killed.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Download"})
-    @DisabledTest(message = "crbug.com/773346")
-    public void testIncognitoDownloadCanceledOnServiceShutdown() throws Exception {
-        setupService();
-        Context mockContext = new AdvancedMockContext(InstrumentationRegistry.getTargetContext());
-        getService().setContext(mockContext);
-        Set<String> notifications = new HashSet<>();
-        String uuid = UUID.randomUUID().toString();
-        notifications.add(buildEntryStringWithGuid(uuid, 1, "test1", true, true, true));
-        SharedPreferences sharedPrefs = ContextUtils.getAppSharedPreferences();
-        SharedPreferences.Editor editor = sharedPrefs.edit();
-        editor.putStringSet(
-                DownloadSharedPreferenceHelper.KEY_PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
-        editor.apply();
-        startNotificationService();
-
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                getService().onTaskRemoved(new Intent());
-            }
-        });
-
-        Assert.assertTrue(getService().isPaused());
-        Assert.assertFalse(sharedPrefs.contains(
-                DownloadSharedPreferenceHelper.KEY_PENDING_DOWNLOAD_NOTIFICATIONS));
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Download"})
-    @DisabledTest(message = "crbug.com/773346")
-    public void testServiceWillStopOnCompletedDownload() throws Exception {
-        // On versions of Android that use a foreground service, the service will currently die with
-        // the notifications.
-        if (DownloadNotificationService.useForegroundService()) return;
-
-        setupService();
-        startNotificationService();
-        DownloadNotificationService service = bindNotificationService();
-        ContentId id = LegacyHelpers.buildLegacyContentId(false, UUID.randomUUID().toString());
-        service.notifyDownloadProgress(id, "/path/to/test", Progress.createIndeterminateProgress(),
-                10L, 1000L, 10L, false, false, false, null);
-        Assert.assertFalse(service.hideSummaryNotificationIfNecessary(-1));
-        service.notifyDownloadSuccessful(
-                id, "/path/to/test", "test", 100L, false, false, true, null, null, null);
-        Assert.assertTrue(service.hideSummaryNotificationIfNecessary(-1));
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Download"})
-    @DisabledTest(message = "crbug.com/773346")
-    public void testServiceWillStopOnFailedDownload() throws Exception {
-        // On versions of Android that use a foreground service, the service will currently die with
-        // the notifications.
-        if (DownloadNotificationService.useForegroundService()) return;
-
-        setupService();
-        startNotificationService();
-        DownloadNotificationService service = bindNotificationService();
-        ContentId id = LegacyHelpers.buildLegacyContentId(false, UUID.randomUUID().toString());
-        service.notifyDownloadProgress(id, "/path/to/test", Progress.createIndeterminateProgress(),
-                10L, 1000L, 10L, false, false, false, null);
-        Assert.assertFalse(service.hideSummaryNotificationIfNecessary(-1));
-        service.notifyDownloadFailed(id, "/path/to/test", null, FailState.CANNOT_DOWNLOAD);
-        Assert.assertTrue(service.hideSummaryNotificationIfNecessary(-1));
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Download"})
-    @DisabledTest(message = "crbug.com/773346")
-    public void testServiceWillStopOnCancelledDownload() throws Exception {
-        // On versions of Android that use a foreground service, the service will currently die with
-        // the notifications.
-        if (DownloadNotificationService.useForegroundService()) return;
-
-        setupService();
-        startNotificationService();
-        DownloadNotificationService service = bindNotificationService();
-        ContentId id = LegacyHelpers.buildLegacyContentId(false, UUID.randomUUID().toString());
-        service.notifyDownloadProgress(id, "/path/to/test", Progress.createIndeterminateProgress(),
-                10L, 1000L, 10L, false, false, false, null);
-        Assert.assertFalse(service.hideSummaryNotificationIfNecessary(-1));
-        service.notifyDownloadCanceled(id);
-        Assert.assertTrue(service.hideSummaryNotificationIfNecessary(-1));
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Download"})
-    public void testServiceWillNotStopOnInterruptedDownload() throws Exception {
-        // On versions of Android that use a foreground service, the service will currently die with
-        // the notifications.
-        if (DownloadNotificationService.useForegroundService()) return;
-
-        setupService();
-        startNotificationService();
-        DownloadNotificationService service = bindNotificationService();
-        ContentId id = LegacyHelpers.buildLegacyContentId(false, UUID.randomUUID().toString());
-        service.notifyDownloadProgress(id, "/path/to/test", Progress.createIndeterminateProgress(),
-                10L, 1000L, 10L, false, false, false, null);
-        Assert.assertFalse(service.hideSummaryNotificationIfNecessary(-1));
-        service.notifyDownloadPaused(id, "/path/to/test", true, true, false, false, null);
-        Assert.assertFalse(service.hideSummaryNotificationIfNecessary(-1));
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Download"})
-    @DisabledTest(message = "crbug.com/773346")
-    public void testServiceWillNotStopOnPausedDownload() throws Exception {
-        // On versions of Android that use a foreground service, the service will currently die with
-        // the notifications.
-        if (DownloadNotificationService.useForegroundService()) return;
-
-        setupService();
-        startNotificationService();
-        DownloadNotificationService service = bindNotificationService();
-        ContentId id = LegacyHelpers.buildLegacyContentId(false, UUID.randomUUID().toString());
-        service.notifyDownloadProgress(id, "/path/to/test", Progress.createIndeterminateProgress(),
-                10L, 1000L, 10L, false, false, false, null);
-        Assert.assertFalse(service.hideSummaryNotificationIfNecessary(-1));
-        service.notifyDownloadPaused(id, "/path/to/test", true, false, false, false, null);
-        Assert.assertFalse(service.hideSummaryNotificationIfNecessary(-1));
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Download"})
-    @DisabledTest(message = "crbug.com/773346")
-    public void testServiceWillNotStopWithOneOngoingDownload() throws Exception {
-        // On versions of Android that use a foreground service, the service will currently die with
-        // the notifications.
-        if (DownloadNotificationService.useForegroundService()) return;
-
-        setupService();
-        startNotificationService();
-        DownloadNotificationService service = bindNotificationService();
-        ContentId id1 = LegacyHelpers.buildLegacyContentId(false, UUID.randomUUID().toString());
-        ContentId id2 = LegacyHelpers.buildLegacyContentId(false, UUID.randomUUID().toString());
-
-        service.notifyDownloadProgress(id1, "/path/to/test", Progress.createIndeterminateProgress(),
-                10L, 1000L, 10L, false, false, false, null);
-        service.notifyDownloadProgress(id2, "/path/to/test", Progress.createIndeterminateProgress(),
-                10L, 1000L, 10L, false, false, false, null);
-        Assert.assertFalse(service.hideSummaryNotificationIfNecessary(-1));
-        service.notifyDownloadPaused(id1, "/path/to/test", true, false, false, false, null);
-        Assert.assertFalse(service.hideSummaryNotificationIfNecessary(-1));
-        service.notifyDownloadSuccessful(
-                id1, "/path/to/test", "test", 100L, false, false, true, null, null, null);
-        Assert.assertFalse(service.hideSummaryNotificationIfNecessary(-1));
-        service.notifyDownloadSuccessful(
-                id2, "/path/to/test", "test", 100L, false, false, true, null, null, null);
-        Assert.assertTrue(service.hideSummaryNotificationIfNecessary(-1));
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Download"})
-    @DisabledTest(message = "crbug.com/773346")
-    public void testForegroundServiceStopsIfCancelIsCalledWhenServiceIsStopped() {
-        // On versions of Android that use a foreground service, the service will currently die with
-        // the notifications.
-        if (!DownloadNotificationService.useForegroundService()) return;
-
-        // Make sure that when the download fails, the service stops.
-        setupService();
-        startNotificationService();
-        DownloadNotificationService service = bindNotificationService();
-        ContentId id = LegacyHelpers.buildLegacyContentId(false, UUID.randomUUID().toString());
-        service.notifyDownloadProgress(id, "/path/to/test", Progress.createIndeterminateProgress(),
-                10L, 1000L, 10L, false, false, false, null);
-        Assert.assertTrue(getService().isForegroundRunning());
-        service.notifyDownloadFailed(id, "/path/to/test", null, FailState.CANNOT_DOWNLOAD);
-        Assert.assertFalse(getService().isForegroundRunning());
-
-        // In the case of offline pages failures, cancel is called even after the download fails and
-        // the service stops. Confirm that if this happens, the service will still stop.
-        service.notifyDownloadCanceled(id);
-        Assert.assertFalse(getService().isForegroundRunning());
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Download"})
-    @DisabledTest(message = "crbug.com/773346")
-    public void testForegroundServiceStopsIfPauseIsCalledWhenServiceIsStopped() {
-        // This only applies to versions that uses the foreground service.
-        if (!DownloadNotificationService.useForegroundService()) return;
-
-        // Make sure that when the download fails, the service stops.
-        setupService();
-        startNotificationService();
-        DownloadNotificationService service = bindNotificationService();
-        ContentId id = LegacyHelpers.buildLegacyContentId(false, UUID.randomUUID().toString());
-        service.notifyDownloadProgress(id, "/path/to/test", Progress.createIndeterminateProgress(),
-                10L, 1000L, 10L, false, false, false, null);
-        Assert.assertTrue(getService().isForegroundRunning());
-        service.notifyDownloadPaused(id, "/path/to/test", true, false, false, false, null);
-        Assert.assertFalse(getService().isForegroundRunning());
-
-        // In the case of offline pages pause, pause is called even after the download pauses and
-        // the service stops. Confirm that if this happens, the service will still stop.
-        service.notifyDownloadPaused(id, "/path/to/test", true, false, false, false, null);
-        Assert.assertFalse(getService().isForegroundRunning());
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTestRule.java
index 8d4ba0aa..8f4b7fe 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTestRule.java
@@ -219,7 +219,7 @@
         ThreadUtils.runOnUiThreadBlocking(() -> {
             mSavedDownloadManagerService =
                     DownloadManagerService.setDownloadManagerService(new TestDownloadManagerService(
-                            new SystemDownloadNotifier(), new Handler(), UPDATE_DELAY_MILLIS));
+                            new SystemDownloadNotifier2(), new Handler(), UPDATE_DELAY_MILLIS));
             DownloadController.setDownloadNotificationService(
                     DownloadManagerService.getDownloadManagerService());
         });
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/MockDownloadNotificationService.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/MockDownloadNotificationService.java
deleted file mode 100644
index 15988c7..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/MockDownloadNotificationService.java
+++ /dev/null
@@ -1,153 +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.
-
-package org.chromium.chrome.browser.download;
-
-import android.app.Notification;
-import android.content.Context;
-import android.graphics.Bitmap;
-
-import org.chromium.base.ThreadUtils;
-import org.chromium.components.offline_items_collection.ContentId;
-import org.chromium.components.offline_items_collection.FailState;
-import org.chromium.components.offline_items_collection.OfflineItem.Progress;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Callable;
-
-/**
- * Mock class to DownloadNotificationService for testing purpose.
- */
-public class MockDownloadNotificationService extends DownloadNotificationService {
-    private final List<Integer> mNotificationIds = new ArrayList<Integer>();
-    private boolean mPaused;
-    private Context mContext;
-    private int mLastNotificationId;
-    private boolean mIsForegroundRunning;
-
-    void setContext(Context context) {
-        mContext = context;
-    }
-
-    @Override
-    public void stopForegroundInternal(boolean killNotification) {
-        if (!useForegroundService()) return;
-        if (killNotification) mNotificationIds.clear();
-        mIsForegroundRunning = false;
-    }
-
-    @Override
-    public void startForegroundInternal() {
-        mIsForegroundRunning = true;
-    }
-
-    public boolean isForegroundRunning() {
-        return mIsForegroundRunning;
-    }
-
-    @Override
-    public void cancelOffTheRecordDownloads() {
-        super.cancelOffTheRecordDownloads();
-        mPaused = true;
-    }
-
-    @Override
-    boolean hasDownloadNotificationsInternal(int notificationIdToIgnore) {
-        if (!useForegroundService()) return false;
-        // Cancelling notifications here is synchronous, so we don't really have to worry about
-        // {@code notificationIdToIgnore}, but address it properly anyway.
-        if (mNotificationIds.size() == 1 && notificationIdToIgnore != -1) {
-            return !mNotificationIds.contains(notificationIdToIgnore);
-        }
-
-        return !mNotificationIds.isEmpty();
-    }
-
-    @Override
-    void cancelSummaryNotification() {}
-
-    @Override
-    void updateNotification(int id, Notification notification) {
-        if (!mNotificationIds.contains(id)) {
-            mNotificationIds.add(id);
-            mLastNotificationId = id;
-        }
-    }
-
-    public boolean isPaused() {
-        return mPaused;
-    }
-
-    public List<Integer> getNotificationIds() {
-        return mNotificationIds;
-    }
-
-    @Override
-    public void cancelNotification(int notificationId, ContentId id) {
-        super.cancelNotification(notificationId, id);
-        mNotificationIds.remove(Integer.valueOf(notificationId));
-    }
-
-    public int getLastAddedNotificationId() {
-        return mLastNotificationId;
-    }
-
-    @Override
-    public Context getApplicationContext() {
-        return mContext == null ? super.getApplicationContext() : mContext;
-    }
-
-    @Override
-    public int notifyDownloadSuccessful(final ContentId id, final String filePath,
-            final String fileName, final long systemDownloadId, final boolean isOffTheRecord,
-            final boolean isSupportedMimeType, final boolean isOpenable, final Bitmap icon,
-            final String originalUrl, final String referrer) {
-        return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Integer>() {
-            @Override
-            public Integer call() throws Exception {
-                return MockDownloadNotificationService.super.notifyDownloadSuccessful(id, filePath,
-                        fileName, systemDownloadId, isOffTheRecord, isSupportedMimeType, isOpenable,
-                        icon, originalUrl, referrer);
-            }
-        });
-    }
-
-    @Override
-    public void notifyDownloadProgress(final ContentId id, final String fileName,
-            final Progress progress, final long bytesReceived, final long timeRemainingInMillis,
-            final long startTime, final boolean isOffTheRecord,
-            final boolean canDownloadWhileMetered, final boolean isTransient, final Bitmap icon) {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                MockDownloadNotificationService.super.notifyDownloadProgress(id, fileName, progress,
-                        bytesReceived, timeRemainingInMillis, startTime, isOffTheRecord,
-                        canDownloadWhileMetered, isTransient, icon);
-            }
-        });
-    }
-
-    @Override
-    public void notifyDownloadFailed(final ContentId id, final String fileName, final Bitmap icon,
-            @FailState int failState) {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                MockDownloadNotificationService.super.notifyDownloadFailed(
-                        id, fileName, icon, failState);
-            }
-        });
-    }
-
-    @Override
-    public void notifyDownloadCanceled(final ContentId id) {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                MockDownloadNotificationService.super.notifyDownloadCanceled(id);
-            }
-        });
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
index 53337ac..453fe37 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
@@ -6,10 +6,14 @@
 
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 
+import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.instanceOf;
 
+import static org.chromium.chrome.test.util.ViewUtils.waitForView;
+
 import android.content.ComponentCallbacks2;
 import android.graphics.Canvas;
 import android.support.test.InstrumentationRegistry;
@@ -231,7 +235,6 @@
         mRenderTestRule.render(mNtp.getSignInPromoViewForTesting(), "sign_in_promo");
     }
 
-    @DisabledTest(message = "https://crbug.com/898165")
     @Test
     @SmallTest
     @Feature({"NewTabPage", "FeedNewTabPage", "RenderTest"})
@@ -239,15 +242,14 @@
     public void testRender_ArticleSectionHeader(boolean interestFeedEnabled) throws Exception {
         // Scroll to the article section header in case it is not visible.
         onView(instanceOf(RecyclerView.class)).perform(RecyclerViewActions.scrollToPosition(2));
-        if (!interestFeedEnabled) {
-            RecyclerViewTestUtils.waitForStableRecyclerView(
-                    mNtp.getNewTabPageView().getRecyclerView());
-        }
+        waitForView((ViewGroup) mNtp.getView(), allOf(withId(R.id.header_title), isDisplayed()));
         View view = mNtp.getSectionHeaderViewForTesting();
-
         // Check header is expanded.
         mRenderTestRule.render(view, "expandable_header_expanded");
+
         // Toggle header on the current tab.
+        onView(instanceOf(RecyclerView.class)).perform(RecyclerViewActions.scrollToPosition(2));
+        waitForView((ViewGroup) mNtp.getView(), allOf(withId(R.id.header_title), isDisplayed()));
         onView(withId(R.id.header_title)).perform(click());
         // Check header is collapsed.
         mRenderTestRule.render(view, "expandable_header_collapsed");
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/OWNERS b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/OWNERS
index 9ee4ff4..6e368bb5 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/OWNERS
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/OWNERS
@@ -1,6 +1,6 @@
-imcheng@chromium.org
+mfoltz@chromium.org
 mlamouri@chromium.org
 zqzhang@chromium.org
 
 # TEAM: media-dev@chromium.org
-# COMPONENT: Blink>PresentationAPI
+# COMPONENT: Internals>Cast
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index c546a5ab..dc75928 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-72.0.3591.0_rc-r1.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-72.0.3592.0_rc-r1.afdo.bz2
\ No newline at end of file
diff --git a/chrome/app/OWNERS b/chrome/app/OWNERS
index d21d2c73..827fc3a 100644
--- a/chrome/app/OWNERS
+++ b/chrome/app/OWNERS
@@ -17,8 +17,7 @@
 per-file google_chrome_strings.grd=*
 per-file md_extensions_strings.grdp=*
 
-per-file media_router_strings.grdp*=imcheng@chromium.org
-per-file media_router_strings.grdp*=mfoltz@chromium.org
+per-file media_router_strings.grdp=file://chrome/browser/media/router/OWNERS
 
 per-file onboarding_welcome_strings.grdp=file://chrome/browser/ui/webui/welcome/nux/OWNERS
 
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index 101b0f89..b749e90 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -148,7 +148,6 @@
 #define IDC_MANAGE_EXTENSIONS           40022
 #define IDC_DEV_TOOLS_INSPECT           40023
 #define IDC_UPGRADE_DIALOG              40024
-#define IDC_SHOW_KEYBOARD_OVERLAY       40027
 #define IDC_PROFILING_ENABLED           40028
 #define IDC_BOOKMARKS_MENU              40029
 #define IDC_SHOW_SIGNIN                 40030
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index fa03b5e1..b3ba8c5 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -11,7 +11,6 @@
 #include "base/command_line.h"
 #include "base/cpu.h"
 #include "base/files/file_path.h"
-#include "base/files/file_util.h"
 #include "base/i18n/rtl.h"
 #include "base/lazy_instance.h"
 #include "base/macros.h"
@@ -116,7 +115,6 @@
 #include "base/android/java_exception_reporter.h"
 #include "chrome/browser/android/crash/pure_java_exception_handler.h"
 #include "chrome/common/descriptors_android.h"
-#include "ui/base/resource/resource_bundle_android.h"
 #else
 // Diagnostics is only available on non-android platforms.
 #include "chrome/browser/diagnostics/diagnostics_controller.h"
@@ -1070,13 +1068,11 @@
   return NULL;
 #else
   if (chrome_content_browser_client_ == nullptr) {
-    DCHECK(service_manifest_data_pack_);
     DCHECK(!chrome_feature_list_creator_);
     chrome_feature_list_creator_ = std::make_unique<ChromeFeatureListCreator>();
 
     chrome_content_browser_client_ =
         std::make_unique<ChromeContentBrowserClient>(
-            std::move(service_manifest_data_pack_),
             chrome_feature_list_creator_.get());
   }
   return chrome_content_browser_client_.get();
@@ -1109,29 +1105,6 @@
 #endif
 }
 
-ui::DataPack* ChromeMainDelegate::LoadServiceManifestDataPack() {
-  DCHECK(!service_manifest_data_pack_ && !chrome_content_browser_client_);
-  const base::CommandLine& command_line =
-      *base::CommandLine::ForCurrentProcess();
-  std::string process_type =
-      command_line.GetSwitchValueASCII(switches::kProcessType);
-  DCHECK(process_type.empty());
-
-  base::FilePath resources_pack_path;
-  base::PathService::Get(chrome::FILE_RESOURCES_PACK, &resources_pack_path);
-
-#if defined(OS_ANDROID)
-  service_manifest_data_pack_ =
-      ui::GetDataPackFromPackFile("assets/resources.pak", resources_pack_path);
-#else
-  if (base::PathExists(resources_pack_path)) {
-    service_manifest_data_pack_.reset(new ui::DataPack(ui::SCALE_FACTOR_NONE));
-    service_manifest_data_pack_->LoadFromPath(resources_pack_path);
-  }
-#endif  // defined(OS_ANDROID)
-  return service_manifest_data_pack_.get();
-}
-
 bool ChromeMainDelegate::ShouldEnableProfilerRecording() {
   switch (chrome::GetChannel()) {
     case version_info::Channel::UNKNOWN:
diff --git a/chrome/app/chrome_main_delegate.h b/chrome/app/chrome_main_delegate.h
index 34df18d..e8a5015e 100644
--- a/chrome/app/chrome_main_delegate.h
+++ b/chrome/app/chrome_main_delegate.h
@@ -13,7 +13,6 @@
 #include "build/build_config.h"
 #include "chrome/common/chrome_content_client.h"
 #include "content/public/app/content_main_delegate.h"
-#include "ui/base/resource/data_pack.h"
 
 #if !defined(CHROME_MULTIPLE_DLL_CHILD)
 #include "chrome/browser/metrics/chrome_feature_list_creator.h"
@@ -74,7 +73,6 @@
   content::ContentGpuClient* CreateContentGpuClient() override;
   content::ContentRendererClient* CreateContentRendererClient() override;
   content::ContentUtilityClient* CreateContentUtilityClient() override;
-  ui::DataPack* LoadServiceManifestDataPack() override;
 
 #if defined(OS_MACOSX)
   void InitMacCrashReporter(const base::CommandLine& command_line,
@@ -86,10 +84,6 @@
 
   std::unique_ptr<ChromeContentBrowserClient> chrome_content_browser_client_;
 
-  // This field is loaded by LoadServiceManifestDataPack() and passed to
-  // ContentBrowserClient in CreateContentBrowserClient()
-  std::unique_ptr<ui::DataPack> service_manifest_data_pack_;
-
 #if !defined(CHROME_MULTIPLE_DLL_CHILD)
   std::unique_ptr<ChromeFeatureListCreator> chrome_feature_list_creator_;
 #endif
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 9a91135..da6d638 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -1789,529 +1789,6 @@
     Aw, Snap! There was an error during renaming.
   </message>
 
-  <!-- Keyboard overlay UI -->
-  <message name="IDS_KEYBOARD_OVERLAY_TITLE" desc="The title of the keyboard overlay.">
-    Keyboard Overlay
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_INSTRUCTIONS" desc="The instructions for the keyboard overlay.">
-    Hold Control, Alt, Shift, or Search to see keyboard shortcuts for those modifiers.
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_INSTRUCTIONS_LAYOUT2" desc="The instructions for the keyboard overlay when device is using the 2017 keyboard layout.">
-    Hold Control, Alt, Shift, or Launcher to see keyboard shortcuts for those modifiers.
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_INSTRUCTIONS_HIDE" desc="The instruction for hiding the keyboard overlay.">
-    Type Ctrl+Alt+/ or Escape to hide
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ASSISTANT_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'assistant' key.">
-    assistant
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_PLAY_PAUSE_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'play / pause' key.">
-    play / pause
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_SYSTEM_MENU_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'system menu' key.">
-    system menu
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_LAUNCHER_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'launcher / search' key.">
-    launcher
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ESC_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'esc' (Escape) key.">
-    esc
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_BACK_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'back' key that when pressed the browser goes back to the previous page in the tab's browsing history.">
-    back
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_FORWARD_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'forward' key that when pressed the browser goes forward to the next page in the tab's browsing history.">
-    forward
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_RELOAD_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'reload' key that when pressed the browser reloads the current page.">
-    reload
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_FULL_SCREEN_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'full screen' key that when pressed the active window will become full screen.">
-    full screen
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_SWITCH_WIN_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'switch window' key that when pressed an overview of all windows will be shown on screen.">
-    switch window
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_BRIGHT_DOWN_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'bright down' key that turns the screen's brightness down.">
-    bright down
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_BRIGHT_UP_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'bright up' key that turns the screen's brightness up.">
-    bright up
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_MOVE_ACTIVE_WINDOW_BETWEEN_DISPLAYS" desc="The text in the keyboard overlay to explain the shortcut (move active window between displays).">
-    move window to another display
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_MUTE_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'mute' key that mutes the system's sound.">
-    mute
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_VOL_DOWN_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'volume down' key that turns the system's sound volume down.">
-    vol. down
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_VOL_UP_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'volume up' key that turns the system's sound volume up">
-    vol. up
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_POWER_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'power' key.">
-    power
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_BACKSPACE_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'backspace' key.">
-    backspace
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_TAB_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'tab' key.">
-    tab
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_SEARCH_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'search' key.">
-    search
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ENTER_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'enter' key.">
-    enter
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_SHIFT_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'shift' key.">
-    shift
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_CTRL_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'ctrl' key.">
-    ctrl
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ALT_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'alt' key.">
-    alt
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_LEFT_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'left' arrow key.">
-    left
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_RIGHT_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'right' arrow key.">
-    right
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_UP_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'up' arrow key.">
-    up
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_DOWN_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'down' arrow key.">
-    down
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_VOICE_INTERACTION" desc="The text in the keyboard overlay for voice interaction shortcut">
-    Start Assistant
-  </message>
-  <!-- BEGIN GENERATED KEYBOARD OVERLAY STRINGS -->
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_LAST_SHELF_ITEM" desc="The text in the keyboard overlay to explain the shortcut (Open the last shelf item).">
-    Last shelf item
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_LAST_TAB" desc="The text in the keyboard overlay to explain the shortcut.">
-    Last tab
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_1" desc="The text in the keyboard overlay to explain the shortcut (Open the shelf item at the specified position).">
-    Shelf item 1
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_2" desc="The text in the keyboard overlay to explain the shortcut (Open the shelf item at the specified position).">
-    Shelf item 2
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_3" desc="The text in the keyboard overlay to explain the shortcut (Open the shelf item at the specified position).">
-    Shelf item 3
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_4" desc="The text in the keyboard overlay to explain the shortcut (Open the shelf item at the specified position).">
-    Shelf item 4
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_5" desc="The text in the keyboard overlay to explain the shortcut (Open the shelf item at the specified position).">
-    Shelf item 5
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_6" desc="The text in the keyboard overlay to explain the shortcut (Open the shelf item at the specified position).">
-    Shelf item 6
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_7" desc="The text in the keyboard overlay to explain the shortcut (Open the shelf item at the specified position).">
-    Shelf item 7
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_8" desc="The text in the keyboard overlay to explain the shortcut (Open the shelf item at the specified position).">
-    Shelf item 8
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_NEXT_TAB" desc="The text in the keyboard overlay to explain the shortcut.">
-    Next tab
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_PREVIOUS_TAB" desc="The text in the keyboard overlay to explain the shortcut.">
-    Previous tab
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_1" desc="The text in the keyboard overlay to explain the shortcut.">
-    Tab 1
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_2" desc="The text in the keyboard overlay to explain the shortcut.">
-    Tab 2
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_3" desc="The text in the keyboard overlay to explain the shortcut.">
-    Tab 3
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_4" desc="The text in the keyboard overlay to explain the shortcut.">
-    Tab 4
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_5" desc="The text in the keyboard overlay to explain the shortcut.">
-    Tab 5
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_6" desc="The text in the keyboard overlay to explain the shortcut.">
-    Tab 6
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_7" desc="The text in the keyboard overlay to explain the shortcut.">
-    Tab 7
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_8" desc="The text in the keyboard overlay to explain the shortcut.">
-    Tab 8
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ADD_WWW_AND_COM_AND_OPEN_ADDRESS" desc="The text in the keyboard overlay to explain the shortcut.">
-    Add www. and .com and open address
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_BOOKMARK_ALL_TABS" desc="The text in the keyboard overlay to explain the shortcut.">
-    Bookmark all tabs
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_BOOKMARK_CURRENT_PAGE" desc="The text in the keyboard overlay to explain the shortcut.">
-    Add bookmark
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_BOOKMARK_MANAGER" desc="The text in the keyboard overlay to explain the shortcut (open the bookmark manager).">
-    Bookmark manager
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_CENTER_WINDOW" desc="The text in the keyboard overlay to explain the shortcut (center the window).">
-    Center window
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_CLEAR_BROWSING_DATA_DIALOG" desc="The text in the keyboard overlay to explain the shortcut.">
-    "Clear browsing data" dialog
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_CLOSE_TAB" desc="The text in the keyboard overlay to explain the shortcut.">
-    Close tab
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_CLOSE_WINDOW" desc="The text in the keyboard overlay to explain the shortcut.">
-    Close window
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_CONTEXT_MENU" desc="The text in the keyboard overlay to explain the shortcut.">
-    Context menu
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_COPY" desc="The text in the keyboard overlay to explain the shortcut.">
-    Copy
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_CUT" desc="The text in the keyboard overlay to explain the shortcut.">
-    Cut
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_CYCLE_THROUGH_INPUT_METHODS" desc="The text in the keyboard overlay to explain the shortcut.">
-    Next input method
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_DECREASE_KEY_BRIGHTNESS" desc="The text in the keyboard overlay to explain the shortcut (decrease the keyboard brightness).">
-    Decrease key brightness
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_DELETE" desc="The text in the keyboard overlay to explain the shortcut (Delete).">
-    Delete
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_DELETE_WORD" desc="The text in the keyboard overlay to explain the shortcut.">
-    Delete word
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_DEVELOPER_TOOLS" desc="The text in the keyboard overlay to explain the shortcut.">
-    Developer tools
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_DOCK_WINDOW_LEFT" desc="The text in the keyboard overlay to explain the shortcut (dock the window to the left).">
-    Dock window left
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_DOCK_WINDOW_RIGHT" desc="The text in the keyboard overlay to explain the shortcut (dock the window to the right).">
-    Dock window right
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_DOM_INSPECTOR" desc="The text in the keyboard overlay to explain the shortcut.">
-    DOM inspector
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_DOWNLOADS" desc="The text in the keyboard overlay to explain the shortcut.">
-    Downloads
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_END" desc="The text in the keyboard overlay to explain the shortcut (End).">
-    End
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_F1" desc="The text in the keyboard overlay to explain the shortcut (F1).">
-    F1
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_F10" desc="The text in the keyboard overlay to explain the shortcut (F10).">
-    F10
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_F11" desc="The text in the keyboard overlay to explain the shortcut (F11).">
-    F11
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_F12" desc="The text in the keyboard overlay to explain the shortcut (F12).">
-    F12
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_F2" desc="The text in the keyboard overlay to explain the shortcut (F2).">
-    F2
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_F3" desc="The text in the keyboard overlay to explain the shortcut (F3).">
-    F3
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_F4" desc="The text in the keyboard overlay to explain the shortcut (F4).">
-    F4
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_F5" desc="The text in the keyboard overlay to explain the shortcut (F5).">
-    F5
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_F6" desc="The text in the keyboard overlay to explain the shortcut (F6).">
-    F6
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_F7" desc="The text in the keyboard overlay to explain the shortcut (F7).">
-    F7
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_F8" desc="The text in the keyboard overlay to explain the shortcut (F8).">
-    F8
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_F9" desc="The text in the keyboard overlay to explain the shortcut (F9).">
-    F9
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_FIND_PREVIOUS_TEXT" desc="The text in the keyboard overlay to explain the shortcut.">
-    Find previous text
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_FIND_TEXT" desc="The text in the keyboard overlay to explain the shortcut.">
-    Find text
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_FIND_TEXT_AGAIN" desc="The text in the keyboard overlay to explain the shortcut.">
-    Find again
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_FOCUS_ADDRESS_BAR" desc="The text in the keyboard overlay to explain the shortcut.">
-    Focus address bar
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_FOCUS_ADDRESS_BAR_IN_SEARCH_MODE" desc="The text in the keyboard overlay to explain the shortcut.">
-    Focus address bar for search
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_FOCUS_BOOKMARKS" desc="The text in the keyboard overlay to explain the shortcut.">
-    Focus bookmarks
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_FOCUS_SHELF" desc="The text in the keyboard overlay to explain the shortcut (focus the application shelf).">
-    Focus shelf
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_FOCUS_NEXT_PANE" desc="The text in the keyboard overlay to explain the shortcut (switch focus to the next keyboard-accessible pane).">
-    Next pane
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_FOCUS_PREVIOUS_PANE" desc="The text in the keyboard overlay to explain the shortcut (switch focus to the previous keyboard-accessible pane).">
-    Previous pane
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_FOCUS_TOOLBAR" desc="The text in the keyboard overlay to explain the shortcut.">
-    Focus toolbar
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_GO_BACK" desc="The text in the keyboard overlay to explain the shortcut.">
-    Go back
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_GO_FORWARD" desc="The text in the keyboard overlay to explain the shortcut.">
-    Go forward
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_HELP" desc="The text in the keyboard overlay to explain the shortcut.">
-    Help
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_HISTORY" desc="The text in the keyboard overlay to explain the shortcut.">
-    History
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_HOME" desc="The text in the keyboard overlay to explain the shortcut (Home).">
-    Home
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_INCREASE_KEY_BRIGHTNESS" desc="The text in the keyboard overlay to explain the shortcut (Increase the keyboard brightness).">
-    Increase key brightness
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_INPUT_UNICODE_CHARACTERS" desc="The text in the keyboard overlay to explain the shortcut.">
-    Input Unicode characters
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_INSERT" desc="The text in the keyboard overlay to explain the shortcut (Insert).">
-    Insert
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_JAVASCRIPT_CONSOLE" desc="The text in the keyboard overlay to explain the shortcut.">
-    JavaScript console
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_LOCK_SCREEN" desc="The text in the keyboard overlay to explain the shortcut (lock the screen).">
-    Lock screen
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_LOCK_SCREEN_OR_POWER_OFF" desc="The text in the keyboard overlay to explain the shortcut.">
-    Lock / power
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_MAGNIFIER_DECREASE_ZOOM" desc="The text in the keyboard overlay to explain the shortcut.">
-    Demagnify
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_MAGNIFIER_INCREASE_ZOOM" desc="The text in the keyboard overlay to explain the shortcut.">
-    Magnify
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_MAXIMIZE_WINDOW" desc="The text in the keyboard overlay to explain the shortcut (maximize or restore the window).">
-    Maximize
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_MINIMIZE_WINDOW" desc="The text in the keyboard overlay to explain the shortcut (minimize the window).">
-    Minimize
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_MIRROR_MONITORS" desc="The text in the keyboard overlay to explain the shortcut (Mirror the monitors).">
-    Mirror monitors
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_NEW_INCOGNITO_WINDOW" desc="The text in the keyboard overlay to explain the shortcut.">
-    New incognito window
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_NEW_TAB" desc="The text in the keyboard overlay to explain the shortcut.">
-    New tab
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_NEW_TERMINAL" desc="The text in the keyboard overlay to explain the shortcut (Open a new terminal tab).">
-    New terminal
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_NEW_WINDOW" desc="The text in the keyboard overlay to explain the shortcut.">
-    New window
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_NEXT_USER" desc="The text in the keyboard overlay to explain the shortcut (Switch to next user).">
-    Next user
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_NEXT_WINDOW" desc="The text in the keyboard overlay to explain the shortcut (go to the next window).">
-    Next window
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_NEXT_WORD" desc="The text in the keyboard overlay to explain the shortcut (move the text cursor to the end of the next word).">
-    Next word
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_OPEN" desc="The text in the keyboard overlay to explain the shortcut (open a file).">
-    Open
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_OPEN_ADDRESS_IN_NEW_TAB" desc="The text in the keyboard overlay to explain the shortcut.">
-    Open in new tab
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_OPEN_FILE_MANAGER" desc="The text in the keyboard overlay to explain the shortcut (Open the file manager).">
-    File manager
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_PAGE_DOWN" desc="The text in the keyboard overlay to explain the shortcut (Page down).">
-    Page down
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_PAGE_UP" desc="The text in the keyboard overlay to explain the shortcut (Page up).">
-    Page up
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_PASTE" desc="The text in the keyboard overlay to explain the shortcut.">
-    Paste
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_PASTE_AS_PLAIN_TEXT" desc="The text in the keyboard overlay to explain the shortcut.">
-    Paste as plain text
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_PREVIOUS_USER" desc="The text in the keyboard overlay to explain the shortcut (Switch to previous user).">
-    Previous user
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_PREVIOUS_WINDOW" desc="The text in the keyboard overlay to explain the shortcut (go to the previous window).">
-    Previous window
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_PREVIOUS_WORD" desc="The text in the keyboard overlay to explain the shortcut (move the text cursor to the start of the previous word).">
-    Previous word
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_PRINT" desc="The text in the keyboard overlay to explain the shortcut.">
-    Print
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_RELOAD_CURRENT_PAGE" desc="The text in the keyboard overlay to explain the shortcut.">
-    Reload
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_RELOAD_BYPASSING_CACHE" desc="The text in the keyboard overlay to explain the shortcut.">
-    Reload bypassing cache
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_REOPEN_LAST_CLOSED_TAB" desc="The text in the keyboard overlay to explain the shortcut.">
-    Reopen last-closed tab
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_REPORT_ISSUE" desc="The text in the keyboard overlay to explain the shortcut (report an issue).">
-    Report issue
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_RESET_SCREEN_ZOOM" desc="The text in the keyboard overlay to explain the shortcut (reset the screen zoom).">
-    Reset screen zoom
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_RESET_ZOOM" desc="The text in the keyboard overlay to explain the shortcut.">
-    Reset zoom
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ROTATE_SCREEN" desc="The text in the keyboard overlay to explain the shortcut (rotate the screen).">
-    Rotate screen
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ROTATE_WINDOW" desc="The text in the keyboard overlay to explain the shortcut (rotate the window).">
-    Rotate window
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_SAVE" desc="The text in the keyboard overlay to explain the shortcut (save page as a file).">
-    Save
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_SCREENSHOT_REGION" desc="The text in the keyboard overlay to explain the shortcut (take a screenshot of the selected region).">
-    Screenshot region
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_SCREENSHOT_WINDOW" desc="The text in the keyboard overlay to explain the shortcut.">
-    Screenshot window
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_SCROLL_UP_ONE_PAGE" desc="The text in the keyboard overlay to explain the shortcut.">
-    Scroll up one page
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_SELECT_ALL" desc="The text in the keyboard overlay to explain the shortcut.">
-    Select all
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_SELECT_PREVIOUS_INPUT_METHOD" desc="The text in the keyboard overlay to explain the shortcut.">
-    Previous input method
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_SELECT_WORD_AT_A_TIME" desc="The text in the keyboard overlay to explain the shortcut.">
-    Select "word at a time"
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_SHOW_IME_BUBBLE" desc="The text in the keyboard overlay to explain the shortcut (Show the input options menu bubble).">
-    Input options bubble
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_SHOW_MESSAGE_CENTER" desc="The text in the keyboard overlay to explain the shortcut (Show the message center).">
-    Message center
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_SHOW_STATUS_MENU" desc="The text in the keyboard overlay to explain the shortcut (show the status menu).">
-    Status menu
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_SHOW_STYLUS_TOOLS" desc="The text in the keyboard overlay to explain the shortcut (show the stylus tools).">
-    Stylus tools
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_SIGN_OUT" desc="The text in the keyboard overlay to explain the shortcut.">
-    Sign out
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_SUSPEND" desc="The text in the keyboard overlay to explain the shortcut.">
-    Suspend
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_SWAP_PRIMARY_MONITOR" desc="The text in the keyboard overlay to explain the shortcut (swap primary monitor).">
-    Swap primary monitor
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_TAKE_SCREENSHOT" desc="The text in the keyboard overlay to explain the shortcut.">
-    Screenshot
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_TASK_MANAGER" desc="The text in the keyboard overlay to explain the shortcut.">
-    Task manager
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_TOGGLE_BOOKMARK_BAR" desc="The text in the keyboard overlay to explain the shortcut.">
-    Bookmark bar
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_TOGGLE_CAPS_LOCK" desc="The text in the keyboard overlay to explain the shortcut (enable or disable caps lock).">
-    Caps Lock
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_DISABLE_CAPS_LOCK" desc="The text in the keyboard overlay to explain the shortcut (disable caps lock when it's enabled).">
-    Disable Caps Lock
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_TOGGLE_CHROMEVOX_SPOKEN_FEEDBACK" desc="The text in the keyboard overlay to explain the shortcut (enable or disable spoken feedback).">
-    ChromeVox (spoken feedback)
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_TOGGLE_DICTATION" desc="The text in the keyboard overlay to explain the shortcut (start or stop speak to type).">
-    Dictation
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_TOGGLE_DOCKED_MAGNIFIER" desc="The text in the keyboard overlay to explain the shortcut (toggle the Docked Magnifier on/off).">
-    Toggle Docked Magnifier
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_TOGGLE_FULLSCREEN_MAGNIFIER" desc="The text in the keyboard overlay to explain the shortcut (toggle the Fullscreen Magnifier on/off).">
-    Toggle Fullscreen Magnifier
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_TOGGLE_HIGH_CONTRAST_MODE" desc="The text in the keyboard overlay to explain the shortcut (Toggle high contrast mode).">
-    Toggle High Contrast Mode
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_TOGGLE_PROJECTION_TOUCH_HUD" desc="The text in the keyboard overlay to explain the shortcut to show the points where a user interfaces with the touch screen. This would be useful for someone doing a presentation where they need the audience to see the points on the screen that are touched.">
-    Show touch points
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_TOUCH_HUD_MODE_CHANGE" desc="The text in the keyboard overlay to explain the shortcut to change the mode of the debug touch HUD. 'HUD' stands for 'heads-up display'. 'Touch' refers to the touch interface.">
-    Change touch HUD mode
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_UNDO" desc="The text in the keyboard overlay to explain the shortcut.">
-    Undo
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_UNPIN" desc="The text in the keyboard overlay to explain the shortcut.">
-    Unpin
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_VIEW_KEYBOARD_OVERLAY" desc="The text in the keyboard overlay to explain the shortcut.">
-    Keyboard overlay
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_VIEW_SOURCE" desc="The text in the keyboard overlay to explain the shortcut.">
-    View source
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_WORD_MOVE" desc="The text in the keyboard overlay to explain the shortcut.">
-    Word move
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ZOOM_IN" desc="The text in the keyboard overlay to explain the shortcut.">
-    Zoom in
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ZOOM_OUT" desc="The text in the keyboard overlay to explain the shortcut.">
-    Zoom out
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ZOOM_SCREEN_IN" desc="The text in the keyboard overlay to explain the shortcut (zoom in the screen).">
-    Zoom screen in
-  </message>
-  <message name="IDS_KEYBOARD_OVERLAY_ZOOM_SCREEN_OUT" desc="The text in the keyboard overlay to explain the shortcut (zoom out the screen).">
-    Zoom screen out
-  </message>
-  <!-- END GENERATED KEYBOARD OVERLAY STRINGS -->
-
   <message name="IDS_LOGIN_ERROR_WHITELIST" desc="Couldn't sign in because user is not whitelisted by the device owner.">
     You are not authorized to use this device. Please contact the device owner for sign-in permission.
   </message>
diff --git a/chrome/app/chromeos_strings_grdp/IDS_SETTINGS_AUTOCLICK_REVERT_TO_LEFT_CLICK.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_SETTINGS_AUTOCLICK_REVERT_TO_LEFT_CLICK.png.sha1
new file mode 100644
index 0000000..c14a61b
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_SETTINGS_AUTOCLICK_REVERT_TO_LEFT_CLICK.png.sha1
@@ -0,0 +1 @@
+5380150dea276e4e148e9d22e09a5e0e10be4e9c
\ No newline at end of file
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index a2ba4488..d383a0b 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -745,13 +745,6 @@
         Copied to Chromium
       </message>
 
-      <!-- Chrome OS keyboard overlay-->
-      <if expr="chromeos">
-        <message name="IDS_KEYBOARD_OVERLAY_SHOW_WRENCH_MENU" desc="The text in the keyboard overlay to explain the shortcut that opens the main Chrome menu.">
-          Show Chromium menu
-        </message>
-      </if>
-
       <!-- App list -->
       <if expr="enable_app_list">
         <message name="IDS_APP_LIST_SHORTCUT_NAME" desc="Name for the Chromium App List to appear in the taskbar and in any shortcuts to it.">
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 1e75781..42fcce87 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -754,13 +754,6 @@
         Copied to Chrome
       </message>
 
-      <!-- Chrome OS keyboard overlay-->
-      <if expr="chromeos">
-        <message name="IDS_KEYBOARD_OVERLAY_SHOW_WRENCH_MENU" desc="The text in the keyboard overlay to explain the shortcut that opens the main Chrome menu.">
-          Show Chrome menu
-        </message>
-      </if>
-
       <!-- App list -->
       <if expr="enable_app_list">
         <message name="IDS_APP_LIST_SHORTCUT_NAME" desc="Name for the Chrome App List to appear in the taskbar and in any shortcuts to it.">
diff --git a/chrome/app/media_router_strings_grdp/OWNERS b/chrome/app/media_router_strings_grdp/OWNERS
index e267f088..fda51ea 100644
--- a/chrome/app/media_router_strings_grdp/OWNERS
+++ b/chrome/app/media_router_strings_grdp/OWNERS
@@ -1,2 +1,3 @@
-imcheng@chromium.org
-mfoltz@chromium.org
\ No newline at end of file
+file://chrome/browser/media/router/OWNERS
+
+# COMPONENT: Internals>Cast>UI
diff --git a/chrome/app/onboarding_welcome_strings.grdp b/chrome/app/onboarding_welcome_strings.grdp
index b544062a..b0bb772 100644
--- a/chrome/app/onboarding_welcome_strings.grdp
+++ b/chrome/app/onboarding_welcome_strings.grdp
@@ -4,6 +4,12 @@
   <message name="IDS_ONBOARDING_WELCOME_GET_STARTED" desc="Label for a confirmation button to finish adding a bookmark and get the user started with using the browser.">
     Get started
   </message>
+  <message name="IDS_ONBOARDING_WELCOME_NEXT" desc="Label for a button to confirm and continue from the current onboarding step.">
+    Next
+  </message>
+  <message name="IDS_ONBOARDING_WELCOME_SKIP" desc="Label for a button to skip the current onboarding step.">
+    Skip
+  </message>
   <message name="IDS_ONBOARDING_WELCOME_BOOKMARK_ADDED" desc="String read for accessibility to inform the user a bookmark was added.">
     Bookmark added
   </message>
@@ -15,11 +21,8 @@
   </message>
 
   <!-- NUX email provider selection module -->
-  <message name="IDS_ONBOARDING_WELCOME_NUX_EMAIL_PROMPT" desc="Text shown to prompt the users to select an email service to add as bookmark. This is the subheader for IDS_ONBOARDING_WELCOME_NUX_EMAIL_WELCOME_TITLE.">
-    Choose your email service for quick access
-  </message>
-  <message name="IDS_ONBOARDING_WELCOME_NUX_EMAIL_WELCOME_TITLE" desc="Text shown to welcome the users to chrome and allow customization.">
-    Make Chrome your own
+  <message name="IDS_ONBOARDING_WELCOME_NUX_EMAIL_TITLE" desc="Text shown to prompt the users to select an email service to add as bookmark.">
+    Add a bookmark to your email
   </message>
 
   <!-- NUX Google apps selection module -->
@@ -60,7 +63,7 @@
     Save your progress
   </message>
   <message name="IDS_ONBOARDING_WELCOME_SIGNIN_VIEW_SUB_HEADER" desc="Sub-header for the page that prompts user to sign in to chrome.">
-    Sign in to get your bookmarks on all devices
+    Sign in and turn on sync to get your bookmarks, passwords and more on all devices
   </message>
   <message name="IDS_ONBOARDING_WELCOME_SIGNIN_VIEW_SIGNIN" desc="The label for a button to let users sign in to chrome.">
     Sign in
diff --git a/chrome/app/settings_chromium_strings.grdp b/chrome/app/settings_chromium_strings.grdp
index 7b263c0..b4a76cb 100644
--- a/chrome/app/settings_chromium_strings.grdp
+++ b/chrome/app/settings_chromium_strings.grdp
@@ -73,9 +73,6 @@
   </message>
 
   <!-- People Page -->
-  <message name="IDS_SETTINGS_SYNC_DISABLED_BY_ADMINISTRATOR" desc="The text to display when sync is disabled by administrator policy.">
-    Sign in to Chromium is disabled by the administrator of this device.
-  </message>
   <message name="IDS_SETTINGS_SYNC_DISCONNECT_DELETE_PROFILE_WARNING_WITH_COUNTS_SINGULAR" desc="Warning message displayed in the Sign out of Chrome dialog that indicates profile browsing data will be removed from the device.">
     This will delete 1 item from this device. To retrieve your data later, sign in to Chromium as <ph name="USER_EMAIL">$1<ex>foo@example.com</ex></ph>.
   </message>
diff --git a/chrome/app/settings_google_chrome_strings.grdp b/chrome/app/settings_google_chrome_strings.grdp
index 16563301..375e3196 100644
--- a/chrome/app/settings_google_chrome_strings.grdp
+++ b/chrome/app/settings_google_chrome_strings.grdp
@@ -73,9 +73,6 @@
   </message>
 
   <!-- People Page -->
-  <message name="IDS_SETTINGS_SYNC_DISABLED_BY_ADMINISTRATOR" desc="The text to display when sync is disabled by administrator policy.">
-    Sign in to Chrome is disabled by the administrator of this device.
-  </message>
   <message name="IDS_SETTINGS_SYNC_DISCONNECT_DELETE_PROFILE_WARNING_WITH_COUNTS_SINGULAR" desc="Warning message displayed in the Sign out of Chrome dialog that indicates profile browsing data will be removed from the device.">
     This will delete 1 item from this device. To retrieve your data later, sign in to Chrome as <ph name="USER_EMAIL">$1<ex>foo@example.com</ex></ph>.
   </message>
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 0de47eb..231e5538 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -292,6 +292,9 @@
     <message name="IDS_SETTINGS_AUTOCLICK_EVENT_TYPE_NO_ACTION" desc="Description of an option to take no action (pause mouse actions).">
       No action (pause)
     </message>
+    <message name="IDS_SETTINGS_AUTOCLICK_REVERT_TO_LEFT_CLICK" desc="Description of a checkbox that gives the option to return to the default left click action after taking another action.">
+      Revert to left click after action
+    </message>
     <message name="IDS_SETTINGS_ON_SCREEN_KEYBOARD_LABEL" desc="Label for checkbox which enables an on-screen keyboard.">
       Enable on-screen keyboard
     </message>
@@ -2488,12 +2491,6 @@
   <message name="IDS_SETTINGS_SPELLING_PREF_UNIFIED_CONSENT" desc="The documentation string of the 'Use Spelling' preference">
     Enhanced spell check
   </message>
-  <message name="IDS_SETTINGS_SPELLING_TURN_ON_HINT_UNIFIED_CONSENT" desc="Informs user how to change their settings to be able to turn on spell check.">
-    To turn this on, first turn on spell check in <ph name="BEGIN_LINK">&lt;a href="$1" target=&quot;_blank&quot;&gt;<ex>&lt;a href="$1" target=&quot;_blank&quot;&gt;</ex></ph>Languages and input<ph name="END_LINK">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph>
-  </message>
-  <message name="IDS_SETTINGS_SPELLING_TURN_ON_HINT_UNIFIED_CONSENT_MAC" desc="Informs user how to change their settings on Mac to be able to turn on spell check.">
-    To turn this on, first select Check Spelling While Typing in the Edit menu
-  </message>
   <message name="IDS_SETTINGS_ENABLE_LOGGING" desc="The label of the checkbox to enable/disable crash and user metrics logging">
     Automatically send usage statistics and crash reports to Google
   </message>
@@ -3973,7 +3970,7 @@
     <message name="IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_FAST" desc="The label for the end of the auto-repeat rate slider, representing a fast repeat rate.">
       Fast
     </message>
-    <message name="IDS_SETTINGS_KEYBOARD_SHOW_KEYBOARD_SHORTCUTS_OVERLAY" desc="The link to open the keyboard shortuts overlay.">
+    <message name="IDS_SETTINGS_KEYBOARD_SHOW_SHORTCUT_VIEWER" desc="The link to open the keyboard shortut viewer.">
       View keyboard shortcuts
     </message>
     <message name="IDS_SETTINGS_KEYBOARD_SHOW_LANGUAGE_AND_INPUT" desc="The link to navigate to the language and input method settings.">
diff --git a/chrome/app_shim/app_shim_controller.h b/chrome/app_shim/app_shim_controller.h
index d45db47..402bcac 100644
--- a/chrome/app_shim/app_shim_controller.h
+++ b/chrome/app_shim/app_shim_controller.h
@@ -31,7 +31,7 @@
   void OnPingChromeTimeout();
 
   // Connects to Chrome and sends a LaunchApp message.
-  void Init();
+  void InitBootstrapPipe();
 
   chrome::mojom::AppShimHost* host() const { return host_.get(); }
 
@@ -47,9 +47,12 @@
   // Builds main menu bar items.
   void SetUpMenu();
   void ChannelError(uint32_t custom_reason, const std::string& description);
+  void BootstrapChannelError(uint32_t custom_reason,
+                             const std::string& description);
+  void LaunchAppDone(apps::AppShimLaunchResult result,
+                     chrome::mojom::AppShimRequest app_shim_request);
 
   // chrome::mojom::AppShim implementation.
-  void LaunchAppDone(apps::AppShimLaunchResult result) override;
   void CreateViewsBridgeFactory(
       views_bridge_mac::mojom::BridgeFactoryAssociatedRequest request) override;
   void CreateContentNSViewBridgeFactory(
@@ -62,10 +65,14 @@
   void Close();
 
   const app_mode::ChromeAppModeInfo* const app_mode_info_;
-  base::FilePath user_data_dir_;
-  mojo::IsolatedConnection mojo_connection_;
+
+  mojo::IsolatedConnection bootstrap_mojo_connection_;
+  chrome::mojom::AppShimHostBootstrapPtr host_bootstrap_;
+
   mojo::Binding<chrome::mojom::AppShim> shim_binding_;
   chrome::mojom::AppShimHostPtr host_;
+  chrome::mojom::AppShimHostRequest host_request_;
+
   base::scoped_nsobject<AppShimDelegate> delegate_;
   bool launch_app_done_;
   bool ping_chrome_reply_received_;
diff --git a/chrome/app_shim/app_shim_controller.mm b/chrome/app_shim/app_shim_controller.mm
index e9d8ecf..120b5bd 100644
--- a/chrome/app_shim/app_shim_controller.mm
+++ b/chrome/app_shim/app_shim_controller.mm
@@ -5,6 +5,7 @@
 #include "chrome/app_shim/app_shim_controller.h"
 
 #import <Cocoa/Cocoa.h>
+#include <utility>
 
 #include "base/command_line.h"
 #include "base/files/file_util.h"
@@ -13,6 +14,7 @@
 #include "chrome/browser/ui/cocoa/main_menu_builder.h"
 #include "content/public/browser/ns_view_bridge_factory_impl.h"
 #include "content/public/common/ns_view_bridge_factory.mojom.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
 #include "mojo/public/cpp/platform/named_platform_channel.h"
 #include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -23,6 +25,7 @@
     const app_mode::ChromeAppModeInfo* app_mode_info)
     : app_mode_info_(app_mode_info),
       shim_binding_(this),
+      host_request_(mojo::MakeRequest(&host_)),
       delegate_([[AppShimDelegate alloc] init]),
       launch_app_done_(false),
       ping_chrome_reply_received_(false),
@@ -46,7 +49,7 @@
     return;
   }
 
-  Init();
+  InitBootstrapPipe();
 }
 
 void AppShimController::OnPingChromeTimeout() {
@@ -54,8 +57,7 @@
     [NSApp terminate:nil];
 }
 
-void AppShimController::Init() {
-  ui::WindowResizeHelperMac::Get()->Init(base::ThreadTaskRunnerHandle::Get());
+void AppShimController::InitBootstrapPipe() {
   SetUpMenu();
 
   // Chrome will relaunch shims when relaunching apps.
@@ -63,36 +65,31 @@
 
   // The user_data_dir for shims actually contains the app_data_path.
   // I.e. <user_data_dir>/<profile_dir>/Web Applications/_crx_extensionid/
-  user_data_dir_ = app_mode_info_->user_data_dir.DirName().DirName().DirName();
-  CHECK(!user_data_dir_.empty());
+  base::FilePath user_data_dir =
+      app_mode_info_->user_data_dir.DirName().DirName().DirName();
+  CHECK(!user_data_dir.empty());
 
   base::FilePath symlink_path =
-      user_data_dir_.Append(app_mode::kAppShimSocketSymlinkName);
-
+      user_data_dir.Append(app_mode::kAppShimSocketSymlinkName);
   base::FilePath socket_path;
-  if (!base::ReadSymbolicLink(symlink_path, &socket_path)) {
+  if (base::ReadSymbolicLink(symlink_path, &socket_path)) {
+    app_mode::VerifySocketPermissions(socket_path);
+    CreateChannelAndSendLaunchApp(socket_path);
+  } else {
     // The path in the user data dir is not a symlink, try connecting directly.
     CreateChannelAndSendLaunchApp(symlink_path);
-    return;
   }
-
-  app_mode::VerifySocketPermissions(socket_path);
-
-  CreateChannelAndSendLaunchApp(socket_path);
 }
 
 void AppShimController::CreateChannelAndSendLaunchApp(
     const base::FilePath& socket_path) {
-  mojo::ScopedMessagePipeHandle message_pipe = mojo_connection_.Connect(
-      mojo::NamedPlatformChannel::ConnectToServer(socket_path.value()));
-  host_ = chrome::mojom::AppShimHostPtr(
-      chrome::mojom::AppShimHostPtrInfo(std::move(message_pipe), 0));
-
-  chrome::mojom::AppShimPtr app_shim_ptr;
-  shim_binding_.Bind(mojo::MakeRequest(&app_shim_ptr),
-                     ui::WindowResizeHelperMac::Get()->task_runner());
-  shim_binding_.set_connection_error_with_reason_handler(
-      base::BindOnce(&AppShimController::ChannelError, base::Unretained(this)));
+  mojo::ScopedMessagePipeHandle message_pipe =
+      bootstrap_mojo_connection_.Connect(
+          mojo::NamedPlatformChannel::ConnectToServer(socket_path.value()));
+  host_bootstrap_ = chrome::mojom::AppShimHostBootstrapPtr(
+      chrome::mojom::AppShimHostBootstrapPtrInfo(std::move(message_pipe), 0));
+  host_bootstrap_.set_connection_error_with_reason_handler(base::BindOnce(
+      &AppShimController::BootstrapChannelError, base::Unretained(this)));
 
   bool launched_by_chrome = base::CommandLine::ForCurrentProcess()->HasSwitch(
       app_mode::kLaunchedByChromeProcessId);
@@ -105,14 +102,27 @@
   std::vector<base::FilePath> files;
   [delegate_ getFilesToOpenAtStartup:&files];
 
-  host_->LaunchApp(std::move(app_shim_ptr), app_mode_info_->profile_dir,
-                   app_mode_info_->app_mode_id, launch_type, files);
+  host_bootstrap_->LaunchApp(std::move(host_request_),
+                             app_mode_info_->profile_dir,
+                             app_mode_info_->app_mode_id, launch_type, files,
+                             base::BindOnce(&AppShimController::LaunchAppDone,
+                                            base::Unretained(this)));
 }
 
 void AppShimController::SetUpMenu() {
   chrome::BuildMainMenu(NSApp, delegate_, app_mode_info_->app_mode_name, true);
 }
 
+void AppShimController::BootstrapChannelError(uint32_t custom_reason,
+                                              const std::string& description) {
+  // The bootstrap channel is expected to close after sending LaunchAppDone.
+  if (launch_app_done_)
+    return;
+  LOG(ERROR) << "Channel error custom_reason:" << custom_reason
+             << " description: " << description;
+  Close();
+}
+
 void AppShimController::ChannelError(uint32_t custom_reason,
                                      const std::string& description) {
   LOG(ERROR) << "Channel error custom_reason:" << custom_reason
@@ -120,17 +130,24 @@
   Close();
 }
 
-void AppShimController::LaunchAppDone(apps::AppShimLaunchResult result) {
+void AppShimController::LaunchAppDone(
+    apps::AppShimLaunchResult result,
+    chrome::mojom::AppShimRequest app_shim_request) {
   if (result != apps::APP_SHIM_LAUNCH_SUCCESS) {
     Close();
     return;
   }
+  shim_binding_.Bind(std::move(app_shim_request),
+                     ui::WindowResizeHelperMac::Get()->task_runner());
+  shim_binding_.set_connection_error_with_reason_handler(
+      base::BindOnce(&AppShimController::ChannelError, base::Unretained(this)));
 
   std::vector<base::FilePath> files;
   if ([delegate_ getFilesToOpenAtStartup:&files])
     SendFocusApp(apps::APP_SHIM_FOCUS_OPEN_FILES, files);
 
   launch_app_done_ = true;
+  host_bootstrap_.reset();
 }
 
 void AppShimController::CreateViewsBridgeFactory(
diff --git a/chrome/app_shim/chrome_main_app_mode_mac.mm b/chrome/app_shim/chrome_main_app_mode_mac.mm
index 0a888eeb..894e8df 100644
--- a/chrome/app_shim/chrome_main_app_mode_mac.mm
+++ b/chrome/app_shim/chrome_main_app_mode_mac.mm
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "base/at_exit.h"
+#include "base/bind.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -32,6 +33,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/core/embedder/scoped_ipc_support.h"
+#include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 
@@ -230,9 +232,10 @@
       pid = [[existing_chrome objectAtIndex:0] processIdentifier];
   }
 
-  AppShimController controller(info);
   base::MessageLoopForUI main_message_loop;
+  ui::WindowResizeHelperMac::Get()->Init(main_message_loop.task_runner());
   base::PlatformThread::SetName("CrAppShimMain");
+  AppShimController controller(info);
 
   // In tests, launching Chrome does nothing, and we won't get a ping response,
   // so just assume the socket exists.
@@ -275,8 +278,8 @@
     // which is preferable to waiting for the Apple Event to timeout after one
     // minute.
     main_message_loop.task_runner()->PostTask(
-        FROM_HERE,
-        base::Bind(&AppShimController::Init, base::Unretained(&controller)));
+        FROM_HERE, base::BindOnce(&AppShimController::InitBootstrapPipe,
+                                  base::Unretained(&controller)));
   }
 
   base::RunLoop().Run();
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 3b32909..ebbf3b21 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -733,8 +733,6 @@
     "media/webrtc/window_icon_util_chromeos.cc",
     "media/webrtc/window_icon_util_mac.mm",
     "media/webrtc/window_icon_util_win.cc",
-    "memory/chrome_memory_coordinator_delegate.cc",
-    "memory/chrome_memory_coordinator_delegate.h",
     "memory_details.cc",
     "memory_details.h",
     "memory_details_android.cc",
@@ -778,6 +776,8 @@
     "metrics/oom/out_of_memory_reporter.h",
     "metrics/perf/perf_provider_chromeos.cc",
     "metrics/perf/perf_provider_chromeos.h",
+    "metrics/persistent_histograms.cc",
+    "metrics/persistent_histograms.h",
     "metrics/power_metrics_provider_mac.h",
     "metrics/power_metrics_provider_mac.mm",
     "metrics/process_memory_metrics_emitter.cc",
diff --git a/chrome/browser/OWNERS b/chrome/browser/OWNERS
index f91673e..a9ebea8f 100644
--- a/chrome/browser/OWNERS
+++ b/chrome/browser/OWNERS
@@ -83,8 +83,7 @@
 per-file *_mac.h=rsesek@chromium.org
 per-file *_mac.h=thakis@chromium.org
 
-per-file media_router_resources.grdp*=imcheng@chromium.org
-per-file media_router_resources.grdp*=mfoltz@chromium.org
+per-file media_router_resources.grdp=file://chrome/browser/media/router/OWNERS
 
 per-file shell_integration_win*=gab@chromium.org
 per-file shell_integration_win*=grt@chromium.org
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index afe4fbd..c7817b2c 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -217,13 +217,6 @@
      switches::kTouchEventFeatureDetectionAuto}};
 
 #if defined(USE_AURA)
-const FeatureEntry::Choice kOverscrollHistoryNavigationChoices[] = {
-    {flag_descriptions::kOverscrollHistoryNavigationSimpleUi, "", ""},
-    {flags_ui::kGenericExperimentChoiceDisabled,
-     switches::kOverscrollHistoryNavigation, "0"},
-    {flag_descriptions::kOverscrollHistoryNavigationParallaxUi,
-     switches::kOverscrollHistoryNavigation, "1"}};
-
 const FeatureEntry::Choice kOverscrollStartThresholdChoices[] = {
     {flags_ui::kGenericExperimentChoiceDefault, "", ""},
     {flag_descriptions::kOverscrollStartThreshold133Percent,
@@ -239,7 +232,6 @@
     {flags_ui::kGenericExperimentChoiceEnabled, switches::kPullToRefresh, "1"},
     {flag_descriptions::kPullToRefreshEnabledTouchscreen,
      switches::kPullToRefresh, "2"}};
-
 #endif  // USE_AURA
 
 const FeatureEntry::Choice kOverlayStrategiesChoices[] = {
@@ -1869,7 +1861,7 @@
     {"overscroll-history-navigation",
      flag_descriptions::kOverscrollHistoryNavigationName,
      flag_descriptions::kOverscrollHistoryNavigationDescription, kOsAura,
-     MULTI_VALUE_TYPE(kOverscrollHistoryNavigationChoices)},
+     FEATURE_VALUE_TYPE(features::kOverscrollHistoryNavigation)},
     {"overscroll-start-threshold",
      flag_descriptions::kOverscrollStartThresholdName,
      flag_descriptions::kOverscrollStartThresholdDescription, kOsAura,
@@ -3647,12 +3639,6 @@
      FEATURE_VALUE_TYPE(media::kOverflowIconsForMediaControls)},
 
 #if defined(OS_ANDROID)
-    {"enable-downloads-foreground", flag_descriptions::kDownloadsForegroundName,
-     flag_descriptions::kDownloadsForegroundDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(features::kDownloadsForeground)},
-#endif  // defined(OS_ANDROID)
-
-#if defined(OS_ANDROID)
     {"enable-downloads-location-change",
      flag_descriptions::kDownloadsLocationChangeName,
      flag_descriptions::kDownloadsLocationChangeDescription, kOsAndroid,
@@ -3713,11 +3699,6 @@
      FEATURE_VALUE_TYPE(blink::features::kStopInBackground)},
 
 #if defined(OS_CHROMEOS)
-    {"ash-enable-keyboard-shortcut-viewer",
-     flag_descriptions::kAshEnableKeyboardShortcutViewerName,
-     flag_descriptions::kAshEnableKeyboardShortcutViewerDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(ash::features::kKeyboardShortcutViewer)},
-
     {"ash-keyboard-shortcut-viewer-app",
      flag_descriptions::kAshKeyboardShortcutViewerAppName,
      flag_descriptions::kAshKeyboardShortcutViewerAppDescription, kOsCrOS,
diff --git a/chrome/browser/android/browsing_data/browsing_data_counter_bridge.cc b/chrome/browser/android/browsing_data/browsing_data_counter_bridge.cc
index ebdb3fc0..f822e89 100644
--- a/chrome/browser/android/browsing_data/browsing_data_counter_bridge.cc
+++ b/chrome/browser/android/browsing_data/browsing_data_counter_bridge.cc
@@ -66,12 +66,12 @@
 void BrowsingDataCounterBridge::onCounterFinished(
     std::unique_ptr<browsing_data::BrowsingDataCounter::Result> result) {
   JNIEnv* env = base::android::AttachCurrentThread();
+  Profile* profile =
+      ProfileManager::GetActiveUserProfile()->GetOriginalProfile();
   ScopedJavaLocalRef<jstring> result_string =
       base::android::ConvertUTF16ToJavaString(
-          env,
-          GetChromeCounterTextFromResult(
-              result.get(),
-              ProfileManager::GetActiveUserProfile()->GetOriginalProfile()));
+          env, browsing_data_counter_utils::GetChromeCounterTextFromResult(
+                   result.get(), profile));
   Java_BrowsingDataCounterBridge_onBrowsingDataCounterFinished(env, jobject_,
                                                                result_string);
 }
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index 907706ee..b101234 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -60,7 +60,6 @@
     &features::kAppNotificationStatusMessaging,
     &features::kClearOldBrowsingData,
     &features::kClipboardContentSetting,
-    &features::kDownloadsForeground,
     &features::kDownloadsLocationChange,
     &features::kExperimentalAppBanners,
     &features::kExperimentalUi,
diff --git a/chrome/browser/android/compositor/layer/tab_layer.cc b/chrome/browser/android/compositor/layer/tab_layer.cc
index 6559adf..8a101456 100644
--- a/chrome/browser/android/compositor/layer/tab_layer.cc
+++ b/chrome/browser/android/compositor/layer/tab_layer.cc
@@ -125,6 +125,7 @@
                              float saturation,
                              float brightness,
                              float close_btn_width,
+                             float close_btn_asset_size,
                              float static_to_view_blend,
                              float content_width,
                              float content_height,
@@ -327,10 +328,9 @@
   // Center Specific Assets in the Rects
   //----------------------------------------------------------------------------
   close_button_position.Offset(
-      (close_button_size.width() - close_btn_resource->size().width()) / 2.f,
-      (close_button_size.height() - close_btn_resource->size().height()) / 2.f);
-  close_button_size.SetSize(close_btn_resource->size().width(),
-                            close_btn_resource->size().height());
+      (close_button_size.width() - close_btn_asset_size) / 2.f,
+      (close_button_size.height() - close_btn_asset_size) / 2.f);
+  close_button_size.SetSize(close_btn_asset_size, close_btn_asset_size);
 
   //----------------------------------------------------------------------------
   // Handle Insetting the Top Border Component
diff --git a/chrome/browser/android/compositor/layer/tab_layer.h b/chrome/browser/android/compositor/layer/tab_layer.h
index 36686b8..ee31eda 100644
--- a/chrome/browser/android/compositor/layer/tab_layer.h
+++ b/chrome/browser/android/compositor/layer/tab_layer.h
@@ -76,6 +76,7 @@
                      float saturation,
                      float brightness,
                      float close_btn_width,
+                     float close_btn_asset_size,
                      float static_to_view_blend,
                      float content_width,
                      float content_height,
diff --git a/chrome/browser/android/compositor/scene_layer/tab_list_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/tab_list_scene_layer.cc
index 191368d..9fe1010 100644
--- a/chrome/browser/android/compositor/scene_layer/tab_list_scene_layer.cc
+++ b/chrome/browser/android/compositor/scene_layer/tab_list_scene_layer.cc
@@ -123,6 +123,7 @@
                                     jfloat shadow_alpha,
                                     jfloat close_alpha,
                                     jfloat close_btn_width,
+                                    jfloat close_btn_asset_size,
                                     jfloat static_to_view_blend,
                                     jfloat border_scale,
                                     jfloat saturation,
@@ -170,10 +171,10 @@
         pivot_y, rotation_x, rotation_y, alpha, border_alpha,
         border_inner_shadow_alpha, contour_alpha, shadow_alpha, close_alpha,
         border_scale, saturation, brightness, close_btn_width,
-        static_to_view_blend, content_width, content_height, content_width,
-        visible_content_height, show_toolbar, default_theme_color,
-        toolbar_background_color, close_button_color, anonymize_toolbar,
-        show_tab_title, toolbar_textbox_resource_id,
+        close_btn_asset_size, static_to_view_blend, content_width,
+        content_height, content_width, visible_content_height, show_toolbar,
+        default_theme_color, toolbar_background_color, close_button_color,
+        anonymize_toolbar, show_tab_title, toolbar_textbox_resource_id,
         toolbar_textbox_background_color, toolbar_textbox_alpha, toolbar_alpha,
         toolbar_y_offset, side_border_scale, inset_border);
   }
diff --git a/chrome/browser/android/compositor/scene_layer/tab_list_scene_layer.h b/chrome/browser/android/compositor/scene_layer/tab_list_scene_layer.h
index dc4b397..0334e5b 100644
--- a/chrome/browser/android/compositor/scene_layer/tab_list_scene_layer.h
+++ b/chrome/browser/android/compositor/scene_layer/tab_list_scene_layer.h
@@ -84,6 +84,7 @@
                    jfloat shadow_alpha,
                    jfloat close_alpha,
                    jfloat close_btn_width,
+                   jfloat close_btn_asset_size,
                    jfloat static_to_view_blend,
                    jfloat border_scale,
                    jfloat saturation,
diff --git a/chrome/browser/android/explore_sites/explore_sites_service_impl.cc b/chrome/browser/android/explore_sites/explore_sites_service_impl.cc
index bd17b8b1..d55e064f 100644
--- a/chrome/browser/android/explore_sites/explore_sites_service_impl.cc
+++ b/chrome/browser/android/explore_sites/explore_sites_service_impl.cc
@@ -23,6 +23,12 @@
 using chrome::android::explore_sites::ExploreSitesVariation;
 using chrome::android::explore_sites::GetExploreSitesVariation;
 
+namespace {
+void ReportCatalogError(explore_sites::ExploreSitesCatalogError error) {
+  UMA_HISTOGRAM_ENUMERATION("ExploreSites.CatalogError", error);
+}
+}  // namespace
+
 namespace explore_sites {
 
 ExploreSitesServiceImpl::ExploreSitesServiceImpl(
@@ -111,16 +117,83 @@
 }
 
 // Validate the catalog.  Note this does not take ownership of the pointer.
-void ValidateCatalog(Catalog* catalog) {
-  // TODO(https://crbug.com/895541): Validate that the category type is in the
-  // valid range.  Also make sure the site has a title, else remove it.
+// Since we can't modify the collection while iterating over it, we instead copy
+// the valid contents into a new validated catalog.
+std::unique_ptr<Catalog> ValidateCatalog(std::unique_ptr<Catalog> catalog) {
+  std::unique_ptr<Catalog> validated_catalog = std::make_unique<Catalog>();
 
-  // Convert the URLs to a standard format.
+  if (catalog == nullptr)
+    return validated_catalog;
+
+  // Check each category.
   for (auto& category : *catalog->mutable_categories()) {
-    for (auto& site : *category.mutable_sites()) {
-      site.set_site_url(GURL(site.site_url()).spec());
+    bool remove_category = false;
+    // Validate that the category type is within the known range.
+    if (!Category::CategoryType_IsValid(category.type())) {
+      remove_category = true;
+      ReportCatalogError(ExploreSitesCatalogError::kCategoryWithUnknownType);
     }
+    // Validate that the category has a title.
+    if (category.localized_title().empty()) {
+      remove_category = true;
+      ReportCatalogError(ExploreSitesCatalogError::kCategoryMissingTitle);
+    }
+
+    if (remove_category)
+      continue;
+
+    Category* new_category = nullptr;
+
+    // Check the individual sites in this category.
+    for (auto& site : *category.mutable_sites()) {
+      // Ensure the URL parses and is in a valid format.
+      GURL url(site.site_url());
+      if (!url.is_valid()) {
+        ReportCatalogError(ExploreSitesCatalogError::kSiteWithBadUrl);
+        continue;
+      }
+      if (site.title().empty()) {
+        ReportCatalogError(ExploreSitesCatalogError::kSiteMissingTitle);
+        continue;
+      }
+      if (site.icon().empty()) {
+        // We should report missing icons, but we will still include the site if
+        // only the icon is missing.
+        ReportCatalogError(ExploreSitesCatalogError::kSiteMissingIcon);
+      }
+
+      // If we have at least one valid site, we can safely create a category.
+      if (new_category == nullptr) {
+        // Create a new (empty) category.  We will fill it if we find at least
+        // one good site.
+        new_category = validated_catalog->add_categories();
+      }
+
+      // Add the site into the category we are working on.
+      Site* new_site = new_category->add_sites();
+      new_site->Swap(&site);
+    }
+
+    // Collect UMA if the last site was removed from the category, or there were
+    // none to start with.
+    if (new_category == nullptr) {
+      ReportCatalogError(ExploreSitesCatalogError::kCategoryWithNoSites);
+      continue;
+    }
+
+    // Now that sites have been copied in, copy over the other fields from the
+    // original category.
+    category.clear_sites();
+    new_category->MergeFrom(category);
   }
+
+  return validated_catalog;
+}
+
+void ExploreSitesServiceImpl::OnCatalogFetchedForTest(
+    ExploreSitesRequestStatus status,
+    std::unique_ptr<std::string> serialized_protobuf) {
+  OnCatalogFetched(status, std::move(serialized_protobuf));
 }
 
 void ExploreSitesServiceImpl::GotVersionToStartFetch(
@@ -156,31 +229,31 @@
   }
 
   // Convert the protobuf into a catalog object.
-  std::unique_ptr<explore_sites::GetCatalogResponse> catalog_response =
-      std::make_unique<explore_sites::GetCatalogResponse>();
-  if (!catalog_response->ParseFromString(*serialized_protobuf.get())) {
+  explore_sites::GetCatalogResponse catalog_response;
+  if (!catalog_response.ParseFromString(*serialized_protobuf.get())) {
     DVLOG(1) << "Failed to parse catalog";
     NotifyCatalogUpdated(std::move(update_catalog_callbacks_), false);
     update_catalog_callbacks_.clear();
+    ReportCatalogError(ExploreSitesCatalogError::kParseFailure);
     return;
   }
-  std::string catalog_version = catalog_response->version_token();
-  std::unique_ptr<Catalog> catalog(catalog_response->release_catalog());
+  std::string catalog_version = catalog_response.version_token();
 
   // Check the catalog, canonicalizing any URLs in it.
-  if (catalog) {
-    ValidateCatalog(catalog.get());
+  if (catalog_response.has_catalog()) {
+    std::unique_ptr<Catalog> validated_catalog = ValidateCatalog(
+        base::WrapUnique<Catalog>(catalog_response.release_catalog()));
 
     // Add the catalog to our internal database.
     task_queue_.AddTask(std::make_unique<ImportCatalogTask>(
-        explore_sites_store_.get(), catalog_version, std::move(catalog),
+        explore_sites_store_.get(), catalog_version,
+        std::move(validated_catalog),
         base::BindOnce(&ExploreSitesServiceImpl::NotifyCatalogUpdated,
                        weak_ptr_factory_.GetWeakPtr(),
                        std::move(update_catalog_callbacks_))));
   } else {
     NotifyCatalogUpdated(std::move(update_catalog_callbacks_), true);
   }
-  update_catalog_callbacks_.clear();
 }
 
 void ExploreSitesServiceImpl::ComposeSiteImage(BitmapCallback callback,
diff --git a/chrome/browser/android/explore_sites/explore_sites_service_impl.h b/chrome/browser/android/explore_sites/explore_sites_service_impl.h
index 40898f8..21bc9bc 100644
--- a/chrome/browser/android/explore_sites/explore_sites_service_impl.h
+++ b/chrome/browser/android/explore_sites/explore_sites_service_impl.h
@@ -51,6 +51,12 @@
   // Add the url to the blacklist.
   void BlacklistSite(const std::string& url) override;
 
+  // Test hook to call the OnCatalogFetched method since we can't pass nullptr
+  // through the test fetcher code.
+  void OnCatalogFetchedForTest(
+      ExploreSitesRequestStatus status,
+      std::unique_ptr<std::string> serialized_protobuf);
+
  private:
   // KeyedService implementation:
   void Shutdown() override;
diff --git a/chrome/browser/android/explore_sites/explore_sites_service_impl_unittest.cc b/chrome/browser/android/explore_sites/explore_sites_service_impl_unittest.cc
index 139e2a6f..9415d50 100644
--- a/chrome/browser/android/explore_sites/explore_sites_service_impl_unittest.cc
+++ b/chrome/browser/android/explore_sites/explore_sites_service_impl_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/feature_list.h"
 #include "base/message_loop/message_loop.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "chrome/browser/android/chrome_feature_list.h"
@@ -18,12 +19,16 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
-const char kCategoryName[] = "Technology";
+const char kTechnologyCategoryName[] = "Technology";
+const char kScienceCategoryName[] = "Science";
+const char kBooksCategoryName[] = "Books";
 const char kSite1UrlNoTrailingSlash[] = "https://example.com";
 const char kSite1Url[] = "https://example.com/";
 const char kSite2Url[] = "https://sample.com/";
+const char kSite4Url[] = "https://exemplar.com/";
 const char kSite1Name[] = "example";
 const char kSite2Name[] = "sample";
+const char kSite3Name[] = "exemplar";
 const char kAcceptLanguages[] = "en-US,en;q=0.5";
 }  // namespace
 
@@ -47,6 +52,9 @@
         std::move(history_stats_reporter));
     success_ = false;
     test_data_ = CreateTestDataProto();
+    mostly_valid_test_data_ = CreateMostlyValidTestDataProto();
+    bad_test_data_ = CreateBadTestDataProto();
+    histogram_tester_ = std::make_unique<base::HistogramTester>();
   }
 
   void UpdateCatalogDoneCallback(bool success) {
@@ -75,12 +83,22 @@
 
   std::string test_data() { return test_data_; }
 
+  std::string mostly_valid_test_data() { return mostly_valid_test_data_; }
+
+  std::string bad_test_data() { return bad_test_data_; }
+
   void PumpLoop() { task_runner_->RunUntilIdle(); }
 
   std::string CreateTestDataProto();
+  std::string CreateMostlyValidTestDataProto();
+  std::string CreateBadTestDataProto();
 
   void SimulateFetcherData(const std::string& response_data);
 
+  const base::HistogramTester* histograms() const {
+    return histogram_tester_.get();
+  }
+
   network::TestURLLoaderFactory::PendingRequest* GetLastPendingRequest();
 
   void ValidateTestCatalog();
@@ -92,10 +110,13 @@
   GetCatalogStatus database_status_;
   std::unique_ptr<std::vector<ExploreSitesCategory>> database_categories_;
   std::string test_data_;
+  std::string mostly_valid_test_data_;
+  std::string bad_test_data_;
   network::TestURLLoaderFactory test_url_loader_factory_;
   scoped_refptr<network::SharedURLLoaderFactory>
       test_shared_url_loader_factory_;
   network::ResourceRequest last_resource_request_;
+  std::unique_ptr<base::HistogramTester> histogram_tester_;
   base::MessageLoopForIO message_loop_;
   scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
 
@@ -139,7 +160,7 @@
 
   const ExploreSitesCategory& database_category = database_categories()->at(0);
   EXPECT_EQ(Category_CategoryType_TECHNOLOGY, database_category.category_type);
-  EXPECT_EQ(std::string(kCategoryName), database_category.label);
+  EXPECT_EQ(std::string(kTechnologyCategoryName), database_category.label);
   EXPECT_EQ(2U, database_category.sites.size());
 
   // Since the site name and url might come back in a different order than we
@@ -177,9 +198,9 @@
 
   // Create one category, technology.
   category->set_type(Category_CategoryType_TECHNOLOGY);
-  category->set_localized_title(kCategoryName);
+  category->set_localized_title(kTechnologyCategoryName);
 
-  // Serialize it into a string.
+  // Serialize the catalog into a string.
   catalog_response.SerializeToString(&serialized_protobuf);
 
   // Print out the string
@@ -188,6 +209,107 @@
   return serialized_protobuf;
 }
 
+// This is a helper to generate testing data to use in tests.
+std::string ExploreSitesServiceImplTest::CreateMostlyValidTestDataProto() {
+  std::string serialized_protobuf;
+  explore_sites::GetCatalogResponse catalog_response;
+  catalog_response.set_version_token("abcd");
+  explore_sites::Catalog* catalog = catalog_response.mutable_catalog();
+  explore_sites::Category* category = catalog->add_categories();
+  explore_sites::Site* site1 = category->add_sites();
+  explore_sites::Site* site2 = category->add_sites();
+  explore_sites::Site* site3 = category->add_sites();
+  explore_sites::Site* site4 = category->add_sites();
+
+  // Fill in fields we need to add to the EoS database.
+
+  // Create some sites.  The first two are valid, the third is missing a URL,
+  // the fourth is missing a title.
+  site1->set_site_url(kSite1UrlNoTrailingSlash);
+  site1->set_title(kSite1Name);
+  site2->set_site_url(kSite2Url);
+  site2->set_title(kSite2Name);
+  site3->set_title(kSite3Name);
+  site4->set_site_url(kSite4Url);
+
+  // Create one category, technology.
+  category->set_type(Category_CategoryType_TECHNOLOGY);
+  category->set_localized_title(kTechnologyCategoryName);
+
+  // Serialize the catalog into a string.
+  catalog_response.SerializeToString(&serialized_protobuf);
+
+  return serialized_protobuf;
+}
+
+// This is a helper to generate testing data to use in tests.  We intentionally
+// create catalogs and sites with problems to make the code emit histograms. We
+// create categories and sites with the following intentional defects:
+// A category with a type outside the known range.
+// A category with no sites.
+// A category with no title.
+// A category with only one site, which is invalid and gets removed.
+// A site with a malformed url.
+// A site with a missing title, as the only site in one category.
+std::string ExploreSitesServiceImplTest::CreateBadTestDataProto() {
+  std::string serialized_protobuf;
+  explore_sites::GetCatalogResponse catalog_response;
+  catalog_response.set_version_token("abcd");
+  explore_sites::Catalog* catalog = catalog_response.mutable_catalog();
+  explore_sites::Category* technology = catalog->add_categories();
+  explore_sites::Category* anime = catalog->add_categories();
+  explore_sites::Category* food = catalog->add_categories();
+  explore_sites::Category* books = catalog->add_categories();
+  explore_sites::Category* science = catalog->add_categories();
+  explore_sites::Site* site1 = technology->add_sites();
+  explore_sites::Site* site2 = technology->add_sites();
+  explore_sites::Site* site3 = books->add_sites();
+
+  // Site 1 will be a totally valid site.
+  site1->set_site_url(kSite1Url);
+  site1->set_title(kSite1Name);
+
+  // Site 2 will be missing a title.
+  site2->set_site_url(kSite2Url);
+  site2->set_title("");
+
+  // Site 3 will have a malformed URL.
+  site3->set_site_url("123456");
+  site3->set_title(kSite3Name);
+
+  // Fill out the technology category with valid data.
+  // It will get one good website, and one with a missing title.
+  // It should be left intact by validation, but with only one site.
+  technology->set_type(Category_CategoryType_TECHNOLOGY);
+  technology->set_localized_title(kTechnologyCategoryName);
+
+  // Fill out the anime category with a bad type value.
+  anime->set_type(static_cast<Category_CategoryType>(1000));
+  anime->set_localized_title("Anime");
+
+  // Don't add a title to the food category.
+  food->set_type(Category_CategoryType_FOOD);
+  food->set_localized_title("");
+
+  // The science category is valid, but has no web sites, so it should be
+  // removed by validation.
+  science->set_type(Category_CategoryType_SCIENCE);
+  science->set_localized_title(kScienceCategoryName);
+
+  // Fill out the books category.  It will get a website with a bad URL, and it
+  // should end up getting removed since all its sites are invalid.
+  books->set_type(Category_CategoryType_BOOKS);
+  books->set_localized_title(kBooksCategoryName);
+
+  // Serialize the catalog into a string.
+  catalog_response.SerializeToString(&serialized_protobuf);
+
+  // Print out the string
+  DVLOG(1) << "bad test data proto '" << serialized_protobuf << "'";
+
+  return serialized_protobuf;
+}
+
 TEST_F(ExploreSitesServiceImplTest, UpdateCatalogFromNetwork) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(chrome::android::kExploreSites);
@@ -211,7 +333,6 @@
   // never been called before in this session, it won't return anything, it will
   // just start the update process.  For our test, we've already put data into
   // the catalog, but GetCatalog doesn't know that.
-  // TODO(petewil): Fix get catalog so it always returns data if it has some.
   service()->GetCatalog(base::BindOnce(
       &ExploreSitesServiceImplTest::CatalogCallback, base::Unretained(this)));
   PumpLoop();
@@ -294,4 +415,81 @@
   ValidateTestCatalog();
 }
 
+TEST_F(ExploreSitesServiceImplTest, BadCatalogHistograms) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(chrome::android::kExploreSites);
+
+  service()->UpdateCatalogFromNetwork(
+      true /*is_immediate_fetch*/, kAcceptLanguages,
+      base::BindOnce(&ExploreSitesServiceImplTest::UpdateCatalogDoneCallback,
+                     base::Unretained(this)));
+
+  // Simulate fetching using the test loader factory and test data.
+  SimulateFetcherData(bad_test_data());
+
+  // Wait for callback to get called.
+  PumpLoop();
+
+  // Expect that we detected flaws in the data and reported the following
+  // histograms.
+  histograms()->ExpectTotalCount("ExploreSites.CatalogError", 7);
+  histograms()->ExpectBucketCount(
+      "ExploreSites.CatalogError",
+      ExploreSitesCatalogError::kCategoryMissingTitle, 1);
+  histograms()->ExpectBucketCount(
+      "ExploreSites.CatalogError",
+      ExploreSitesCatalogError::kCategoryWithUnknownType, 1);
+  histograms()->ExpectBucketCount(
+      "ExploreSites.CatalogError",
+      ExploreSitesCatalogError::kCategoryWithNoSites, 2);
+  histograms()->ExpectBucketCount("ExploreSites.CatalogError",
+                                  ExploreSitesCatalogError::kSiteWithBadUrl, 1);
+  histograms()->ExpectBucketCount("ExploreSites.CatalogError",
+                                  ExploreSitesCatalogError::kSiteMissingTitle,
+                                  1);
+}
+
+TEST_F(ExploreSitesServiceImplTest, MostlyValidCatalogHistograms) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(chrome::android::kExploreSites);
+
+  service()->UpdateCatalogFromNetwork(
+      true /*is_immediate_fetch*/, kAcceptLanguages,
+      base::BindOnce(&ExploreSitesServiceImplTest::UpdateCatalogDoneCallback,
+                     base::Unretained(this)));
+
+  // Simulate fetching using the test loader factory and test data.
+  SimulateFetcherData(mostly_valid_test_data());
+
+  // Wait for callback to get called.
+  PumpLoop();
+
+  // Expect that we detected flaws in the data and reported the following
+  // histograms.
+  histograms()->ExpectBucketCount("ExploreSites.CatalogError",
+                                  ExploreSitesCatalogError::kSiteWithBadUrl, 1);
+  histograms()->ExpectBucketCount("ExploreSites.CatalogError",
+                                  ExploreSitesCatalogError::kSiteMissingTitle,
+                                  1);
+}
+
+TEST_F(ExploreSitesServiceImplTest, UnparseableCatalogHistograms) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(chrome::android::kExploreSites);
+
+  service()->UpdateCatalogFromNetwork(
+      true /*is_immediate_fetch*/, kAcceptLanguages,
+      base::BindOnce(&ExploreSitesServiceImplTest::UpdateCatalogDoneCallback,
+                     base::Unretained(this)));
+
+  // Simulate fetching using a URL where the code expects a serialized protobuf.
+  SimulateFetcherData(kSite1Url);
+
+  // Wait for callback to get called.
+  PumpLoop();
+
+  histograms()->ExpectBucketCount("ExploreSites.CatalogError",
+                                  ExploreSitesCatalogError::kParseFailure, 1);
+}
+
 }  // namespace explore_sites
diff --git a/chrome/browser/android/explore_sites/explore_sites_types.h b/chrome/browser/android/explore_sites/explore_sites_types.h
index f9bc42d..3844da5 100644
--- a/chrome/browser/android/explore_sites/explore_sites_types.h
+++ b/chrome/browser/android/explore_sites/explore_sites_types.h
@@ -85,5 +85,25 @@
   // kMaxValue should always be the last type.
   kMaxValue = kShouldSuspendBlockedByAdministrator
 };
+
+// Must be kept in sync with ExploreSitesCatalogError enum in enums.xml.
+// This enum should be treated as append-only.
+enum class ExploreSitesCatalogError {
+  // Catalog parse from protobuf string failed.
+  kParseFailure = 0,
+  // Category with a missing title.
+  kCategoryMissingTitle = 1,
+  // Category with a type enum that this version does not support.
+  kCategoryWithUnknownType = 2,
+  // Category with no sites present.
+  kCategoryWithNoSites = 3,
+  // Site with a malformed or empty URL.
+  kSiteWithBadUrl = 4,
+  // Site with no title.
+  kSiteMissingTitle = 5,
+  // Site with a missing icon.
+  kSiteMissingIcon = 6,
+  kMaxValue = kSiteMissingIcon
+};
 }  // namespace explore_sites
 #endif  // CHROME_BROWSER_ANDROID_EXPLORE_SITES_EXPLORE_SITES_TYPES_H_
diff --git a/chrome/browser/android/vr/arcore_device/arcore_device.cc b/chrome/browser/android/vr/arcore_device/arcore_device.cc
index 04a5831..626d20e4 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_device.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_device.cc
@@ -97,7 +97,15 @@
                    std::make_unique<vr::ArCoreJavaUtils>(this),
                    std::make_unique<ArCorePermissionHelper>()) {}
 
-ArCoreDevice::~ArCoreDevice() {}
+ArCoreDevice::~ArCoreDevice() {
+  // The GL thread must be terminated since it uses our members. For example,
+  // there might still be a posted Initialize() call in flight that uses
+  // arcore_install_utils_ and arcore_factory_. Ensure that the thread is
+  // stopped before other members get destructed. Don't call Stop() here,
+  // destruction calls Stop() and doing so twice is illegal (null pointer
+  // dereference).
+  arcore_gl_thread_ = nullptr;
+}
 
 void ArCoreDevice::PauseTracking() {
   DCHECK(IsOnMainThread());
@@ -338,13 +346,13 @@
   }
 
   if (!is_arcore_gl_initialized_) {
-    // This won't happen twice because this method is called from the
-    // end of the permission sequence, which only happens once. We
-    // set is_arcore_gl_initialized in the callback so we don't
-    // allow operations that require its readiness to happen.
+    // We will only try to initialize ArCoreGl once, at the end of the
+    // permission sequence, and will resolve pending requests that have queued
+    // up once that initialization completes. We set is_arcore_gl_initialized_
+    // in the callback to block operations that require it to be ready.
     PostTaskToGlThread(base::BindOnce(
         &ArCoreGl::Initialize, arcore_gl_thread_->GetArCoreGl()->GetWeakPtr(),
-        std::move(arcore_install_utils_), std::move(arcore_factory_),
+        arcore_install_utils_.get(), arcore_factory_.get(),
         CreateMainThreadCallback(base::BindOnce(
             &ArCoreDevice::OnArCoreGlInitializationComplete, GetWeakPtr()))));
     return;
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.cc b/chrome/browser/android/vr/arcore_device/arcore_gl.cc
index 4c166c24..409b8cf 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_gl.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_gl.cc
@@ -96,8 +96,8 @@
 
 ArCoreGl::~ArCoreGl() {}
 
-void ArCoreGl::Initialize(std::unique_ptr<vr::ArCoreInstallUtils> install_utils,
-                          std::unique_ptr<ArCoreFactory> arcore_factory,
+void ArCoreGl::Initialize(vr::ArCoreInstallUtils* install_utils,
+                          ArCoreFactory* arcore_factory,
                           base::OnceCallback<void(bool)> callback) {
   DCHECK(IsOnGlThread());
 
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.h b/chrome/browser/android/vr/arcore_device/arcore_gl.h
index 81dcdef..1dd2d82 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_gl.h
+++ b/chrome/browser/android/vr/arcore_device/arcore_gl.h
@@ -46,8 +46,8 @@
   explicit ArCoreGl(std::unique_ptr<ArImageTransport> ar_image_transport);
   ~ArCoreGl();
 
-  void Initialize(std::unique_ptr<vr::ArCoreInstallUtils> install_utils,
-                  std::unique_ptr<ArCoreFactory> arcore_factory,
+  void Initialize(vr::ArCoreInstallUtils* install_utils,
+                  ArCoreFactory* arcore_factory,
                   base::OnceCallback<void(bool)> callback);
 
   void ProduceFrame(const gfx::Size& frame_size,
diff --git a/chrome/browser/app_controller_mac_browsertest.mm b/chrome/browser/app_controller_mac_browsertest.mm
index d880fa7..394c167 100644
--- a/chrome/browser/app_controller_mac_browsertest.mm
+++ b/chrome/browser/app_controller_mac_browsertest.mm
@@ -40,7 +40,6 @@
 #include "chrome/browser/ui/search/local_ntp_test_utils.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/user_manager.h"
-#include "chrome/browser/ui/views_mode_controller.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
diff --git a/chrome/browser/apps/app_shim/app_shim_host_mac.cc b/chrome/browser/apps/app_shim/app_shim_host_mac.cc
index f8ef148..2b73830 100644
--- a/chrome/browser/apps/app_shim/app_shim_host_mac.cc
+++ b/chrome/browser/apps/app_shim/app_shim_host_mac.cc
@@ -25,7 +25,9 @@
 }  // namespace
 
 AppShimHost::AppShimHost()
-    : host_binding_(this), initial_launch_finished_(false) {}
+    : host_bootstrap_binding_(this),
+      host_binding_(this),
+      app_shim_request_(mojo::MakeRequest(&app_shim_)) {}
 
 AppShimHost::~AppShimHost() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -37,15 +39,22 @@
 void AppShimHost::ServeChannel(mojo::PlatformChannelEndpoint endpoint) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   mojo::ScopedMessagePipeHandle message_pipe =
-      mojo_connection_.Connect(std::move(endpoint));
-  BindToRequest(chrome::mojom::AppShimHostRequest(std::move(message_pipe)));
+      bootstrap_mojo_connection_.Connect(std::move(endpoint));
+  host_bootstrap_binding_.Bind(
+      chrome::mojom::AppShimHostBootstrapRequest(std::move(message_pipe)));
+  host_bootstrap_binding_.set_connection_error_with_reason_handler(
+      base::BindOnce(&AppShimHost::BootstrapChannelError,
+                     base::Unretained(this)));
 }
 
-void AppShimHost::BindToRequest(
-    chrome::mojom::AppShimHostRequest host_request) {
-  host_binding_.Bind(std::move(host_request));
-  host_binding_.set_connection_error_with_reason_handler(
-      base::BindOnce(&AppShimHost::ChannelError, base::Unretained(this)));
+void AppShimHost::BootstrapChannelError(uint32_t custom_reason,
+                                        const std::string& description) {
+  // The bootstrap channel is expected to close after sending LaunchApp.
+  if (has_received_launch_app_)
+    return;
+  LOG(ERROR) << "Channel error custom_reason:" << custom_reason
+             << " description: " << description;
+  Close();
 }
 
 void AppShimHost::ChannelError(uint32_t custom_reason,
@@ -63,18 +72,28 @@
 ////////////////////////////////////////////////////////////////////////////////
 // AppShimHost, chrome::mojom::AppShimHost
 
-void AppShimHost::LaunchApp(chrome::mojom::AppShimPtr app_shim_ptr,
-                            const base::FilePath& profile_dir,
-                            const std::string& app_id,
-                            apps::AppShimLaunchType launch_type,
-                            const std::vector<base::FilePath>& files) {
+void AppShimHost::LaunchApp(
+    chrome::mojom::AppShimHostRequest app_shim_host_request,
+    const base::FilePath& profile_dir,
+    const std::string& app_id,
+    apps::AppShimLaunchType launch_type,
+    const std::vector<base::FilePath>& files,
+    LaunchAppCallback callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(profile_path_.empty());
+  DCHECK(!has_received_launch_app_);
   // Only one app launch message per channel.
-  if (!profile_path_.empty())
+  if (has_received_launch_app_)
     return;
+  has_received_launch_app_ = true;
 
-  app_shim_ = std::move(app_shim_ptr);
+  bootstrap_launch_app_callback_ = std::move(callback);
+  host_binding_.Bind(std::move(app_shim_host_request));
+  host_binding_.set_connection_error_with_reason_handler(
+      base::BindOnce(&AppShimHost::ChannelError, base::Unretained(this)));
+
+  // Create the interfaces used to host windows, so that browser windows may be
+  // created before the host process finishes launching.
+  // TODO(ccameron): Move earlier in initialization.
   if (features::HostWindowsInAppShimProcess()) {
     uint64_t host_id = g_next_host_id++;
 
@@ -133,9 +152,10 @@
 // AppShimHost, apps::AppShimHandler::Host
 
 void AppShimHost::OnAppLaunchComplete(apps::AppShimLaunchResult result) {
-  if (!initial_launch_finished_) {
-    app_shim_->LaunchAppDone(result);
-    initial_launch_finished_ = true;
+  if (!has_sent_on_launch_complete_) {
+    std::move(bootstrap_launch_app_callback_)
+        .Run(result, std::move(app_shim_request_));
+    has_sent_on_launch_complete_ = true;
   }
 }
 
diff --git a/chrome/browser/apps/app_shim/app_shim_host_mac.h b/chrome/browser/apps/app_shim/app_shim_host_mac.h
index 73a76d1..7ef6b7e1 100644
--- a/chrome/browser/apps/app_shim/app_shim_host_mac.h
+++ b/chrome/browser/apps/app_shim/app_shim_host_mac.h
@@ -29,7 +29,8 @@
 // chrome/app/chrome_main_app_mode_mac.mm. The AppShimHost owns itself, and is
 // destroyed when the app it corresponds to is closed or when the channel
 // connected to the app shim is closed.
-class AppShimHost : public chrome::mojom::AppShimHost,
+class AppShimHost : public chrome::mojom::AppShimHostBootstrap,
+                    public chrome::mojom::AppShimHost,
                     public apps::AppShimHandler::Host {
  public:
   AppShimHost();
@@ -41,18 +42,22 @@
   void ServeChannel(mojo::PlatformChannelEndpoint endpoint);
 
  protected:
-  void BindToRequest(chrome::mojom::AppShimHostRequest host_request);
+  void BootstrapChannelError(uint32_t custom_reason,
+                             const std::string& description);
   void ChannelError(uint32_t custom_reason, const std::string& description);
 
   // Closes the channel and destroys the AppShimHost.
   void Close();
 
-  // chrome::mojom::AppShimHost implementation.
-  void LaunchApp(chrome::mojom::AppShimPtr app_shim_ptr,
+  // chrome::mojom::AppShimHostBootstrap.
+  void LaunchApp(chrome::mojom::AppShimHostRequest app_shim_host_request,
                  const base::FilePath& profile_dir,
                  const std::string& app_id,
                  apps::AppShimLaunchType launch_type,
-                 const std::vector<base::FilePath>& files) override;
+                 const std::vector<base::FilePath>& files,
+                 LaunchAppCallback callback) override;
+
+  // chrome::mojom::AppShimHost.
   void FocusApp(apps::AppShimFocusType focus_type,
                 const std::vector<base::FilePath>& files) override;
   void SetAppHidden(bool hidden) override;
@@ -68,14 +73,21 @@
   std::string GetAppId() const override;
   views::BridgeFactoryHost* GetViewsBridgeFactoryHost() const override;
 
-  mojo::IsolatedConnection mojo_connection_;
+  mojo::IsolatedConnection bootstrap_mojo_connection_;
+  mojo::Binding<chrome::mojom::AppShimHostBootstrap> host_bootstrap_binding_;
+  LaunchAppCallback bootstrap_launch_app_callback_;
+
+  mojo::Binding<chrome::mojom::AppShimHost> host_binding_;
   chrome::mojom::AppShimPtr app_shim_;
+  chrome::mojom::AppShimRequest app_shim_request_;
+
   std::unique_ptr<views::BridgeFactoryHost> views_bridge_factory_host_;
   std::unique_ptr<content::NSViewBridgeFactoryHost> content_bridge_factory_;
-  mojo::Binding<chrome::mojom::AppShimHost> host_binding_;
+
   std::string app_id_;
   base::FilePath profile_path_;
-  bool initial_launch_finished_;
+  bool has_received_launch_app_ = false;
+  bool has_sent_on_launch_complete_ = false;
 
   THREAD_CHECKER(thread_checker_);
   DISALLOW_COPY_AND_ASSIGN(AppShimHost);
diff --git a/chrome/browser/apps/app_shim/app_shim_host_mac_unittest.cc b/chrome/browser/apps/app_shim/app_shim_host_mac_unittest.cc
index 2058694..df998b1 100644
--- a/chrome/browser/apps/app_shim/app_shim_host_mac_unittest.cc
+++ b/chrome/browser/apps/app_shim/app_shim_host_mac_unittest.cc
@@ -21,18 +21,15 @@
 
 class TestingAppShim : public chrome::mojom::AppShim {
  public:
-  TestingAppShim() : app_shim_binding_(this) {}
+  TestingAppShim() {}
 
-  chrome::mojom::AppShimPtr GetMojoPtr() {
-    chrome::mojom::AppShimPtr app_shim_ptr;
-    chrome::mojom::AppShimRequest app_shim_request =
-        mojo::MakeRequest(&app_shim_ptr);
-    app_shim_binding_.Bind(std::move(app_shim_request));
-    return app_shim_ptr;
+  chrome::mojom::AppShimHostBootstrap::LaunchAppCallback
+  GetLaunchAppCallback() {
+    return base::BindOnce(&TestingAppShim::LaunchAppDone,
+                          base::Unretained(this));
   }
-
-  chrome::mojom::AppShimHostRequest GetHostRequest() {
-    return mojo::MakeRequest(&host_ptr_);
+  chrome::mojom::AppShimHostBootstrapRequest GetHostBootstrapRequest() {
+    return mojo::MakeRequest(&host_bootstrap_ptr_);
   }
 
   apps::AppShimLaunchResult GetLaunchResult() const {
@@ -41,11 +38,13 @@
   }
 
  private:
-  // chrome::mojom::AppShim implementation.
-  void LaunchAppDone(apps::AppShimLaunchResult result) override {
+  void LaunchAppDone(apps::AppShimLaunchResult result,
+                     chrome::mojom::AppShimRequest app_shim_request) {
     received_launch_done_result_ = true;
     launch_done_result_ = result;
   }
+
+  // chrome::mojom::AppShim implementation.
   void CreateViewsBridgeFactory(
       views_bridge_mac::mojom::BridgeFactoryAssociatedRequest request)
       override {}
@@ -58,19 +57,18 @@
   bool received_launch_done_result_ = false;
   apps::AppShimLaunchResult launch_done_result_ = apps::APP_SHIM_LAUNCH_SUCCESS;
 
-  chrome::mojom::AppShimHostPtr host_ptr_;
-  mojo::Binding<chrome::mojom::AppShim> app_shim_binding_;
-
+  chrome::mojom::AppShimHostBootstrapPtr host_bootstrap_ptr_;
   DISALLOW_COPY_AND_ASSIGN(TestingAppShim);
 };
 
 class TestingAppShimHost : public AppShimHost {
  public:
-  explicit TestingAppShimHost(chrome::mojom::AppShimHostRequest host_request)
+  explicit TestingAppShimHost(
+      chrome::mojom::AppShimHostBootstrapRequest host_request)
       : test_weak_factory_(this) {
     // AppShimHost will bind to the request from ServeChannel. For testing
     // purposes, have this request passed in at creation.
-    BindToRequest(std::move(host_request));
+    host_bootstrap_binding_.Bind(std::move(host_request));
   }
 
   base::WeakPtr<TestingAppShimHost> GetWeakPtr() {
@@ -101,12 +99,16 @@
     return task_runner_;
   }
   TestingAppShimHost* host() { return host_.get(); }
-  chrome::mojom::AppShimHost* GetMojoHost() { return host_.get(); }
+  chrome::mojom::AppShimHostBootstrap* GetBootstrapMojoHost() {
+    return host_.get();
+  }
+  chrome::mojom::AppShimHost* GetMojoHost() { return host_ptr_.get(); }
 
   void LaunchApp(apps::AppShimLaunchType launch_type) {
-    GetMojoHost()->LaunchApp(shim_->GetMojoPtr(),
-                             base::FilePath(kTestProfileDir), kTestAppId,
-                             launch_type, std::vector<base::FilePath>());
+    GetBootstrapMojoHost()->LaunchApp(
+        mojo::MakeRequest(&host_ptr_), base::FilePath(kTestProfileDir),
+        kTestAppId, launch_type, std::vector<base::FilePath>(),
+        shim_->GetLaunchAppCallback());
   }
 
   apps::AppShimLaunchResult GetLaunchResult() {
@@ -114,7 +116,7 @@
     return shim_->GetLaunchResult();
   }
 
-  void SimulateDisconnect() { shim_.reset(); }
+  void SimulateDisconnect() { host_ptr_.reset(); }
 
  protected:
   void OnShimLaunch(Host* host,
@@ -149,7 +151,8 @@
   void SetUp() override {
     testing::Test::SetUp();
     shim_.reset(new TestingAppShim());
-    TestingAppShimHost* host = new TestingAppShimHost(shim_->GetHostRequest());
+    TestingAppShimHost* host =
+        new TestingAppShimHost(shim_->GetHostBootstrapRequest());
     host_ = host->GetWeakPtr();
   }
 
@@ -161,6 +164,7 @@
   // AppShimHost will destroy itself in AppShimHost::Close, so use a weak
   // pointer here to avoid lifetime issues.
   base::WeakPtr<TestingAppShimHost> host_;
+  chrome::mojom::AppShimHostPtr host_ptr_;
 
   DISALLOW_COPY_AND_ASSIGN(AppShimHostTest);
 };
diff --git a/chrome/browser/apps/app_shim/app_shim_host_manager_browsertest_mac.mm b/chrome/browser/apps/app_shim/app_shim_host_manager_browsertest_mac.mm
index 6bed870..325ca959 100644
--- a/chrome/browser/apps/app_shim/app_shim_host_manager_browsertest_mac.mm
+++ b/chrome/browser/apps/app_shim/app_shim_host_manager_browsertest_mac.mm
@@ -28,6 +28,9 @@
 #include "mojo/public/cpp/platform/named_platform_channel.h"
 #include "mojo/public/cpp/system/isolated_connection.h"
 
+using LaunchAppCallback =
+    chrome::mojom::AppShimHostBootstrap::LaunchAppCallback;
+
 namespace {
 
 const char kTestAppMode[] = "test_app";
@@ -37,18 +40,20 @@
  public:
   TestShimClient();
 
-  chrome::mojom::AppShimPtr GetMojoPtr() {
-    chrome::mojom::AppShimPtr app_shim_ptr;
-    chrome::mojom::AppShimRequest app_shim_request =
-        mojo::MakeRequest(&app_shim_ptr);
-    shim_binding_.Bind(std::move(app_shim_request));
-    return app_shim_ptr;
+  chrome::mojom::AppShimHostRequest GetHostRequest() {
+    return std::move(host_request_);
+  }
+  LaunchAppCallback GetLaunchAppCallback() {
+    return base::BindOnce(&TestShimClient::LaunchAppDone,
+                          base::Unretained(this));
   }
 
   chrome::mojom::AppShimHostPtr& host() { return host_; }
+  chrome::mojom::AppShimHostBootstrapPtr& host_bootstrap() {
+    return host_bootstrap_;
+  }
 
   // chrome::mojom::AppShim implementation (not used in testing, but can be).
-  void LaunchAppDone(apps::AppShimLaunchResult result) override {}
   void CreateViewsBridgeFactory(
       views_bridge_mac::mojom::BridgeFactoryAssociatedRequest request)
       override {}
@@ -59,14 +64,22 @@
   void SetUserAttention(apps::AppShimAttentionType attention_type) override {}
 
  private:
+  void LaunchAppDone(apps::AppShimLaunchResult result,
+                     chrome::mojom::AppShimRequest app_shim_request) {
+    shim_binding_.Bind(std::move(app_shim_request));
+  }
+
   mojo::IsolatedConnection mojo_connection_;
   mojo::Binding<chrome::mojom::AppShim> shim_binding_;
   chrome::mojom::AppShimHostPtr host_;
+  chrome::mojom::AppShimHostRequest host_request_;
+  chrome::mojom::AppShimHostBootstrapPtr host_bootstrap_;
 
   DISALLOW_COPY_AND_ASSIGN(TestShimClient);
 };
 
-TestShimClient::TestShimClient() : shim_binding_(this) {
+TestShimClient::TestShimClient()
+    : shim_binding_(this), host_request_(mojo::MakeRequest(&host_)) {
   base::FilePath user_data_dir;
   CHECK(base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
   base::FilePath symlink_path =
@@ -79,8 +92,8 @@
 
   mojo::ScopedMessagePipeHandle message_pipe = mojo_connection_.Connect(
       mojo::NamedPlatformChannel::ConnectToServer(socket_path.value()));
-  host_ = chrome::mojom::AppShimHostPtr(
-      chrome::mojom::AppShimHostPtrInfo(std::move(message_pipe), 0));
+  host_bootstrap_ = chrome::mojom::AppShimHostBootstrapPtr(
+      chrome::mojom::AppShimHostBootstrapPtrInfo(std::move(message_pipe), 0));
 }
 
 // Browser Test for AppShimHostManager to test IPC interactions across the
@@ -168,9 +181,10 @@
 // Test regular launch, which would ask Chrome to launch the app.
 IN_PROC_BROWSER_TEST_F(AppShimHostManagerBrowserTest, LaunchNormal) {
   test_client_.reset(new TestShimClient());
-  test_client_->host()->LaunchApp(
-      test_client_->GetMojoPtr(), browser()->profile()->GetPath(), kTestAppMode,
-      apps::APP_SHIM_LAUNCH_NORMAL, std::vector<base::FilePath>());
+  test_client_->host_bootstrap()->LaunchApp(
+      test_client_->GetHostRequest(), browser()->profile()->GetPath(),
+      kTestAppMode, apps::APP_SHIM_LAUNCH_NORMAL, std::vector<base::FilePath>(),
+      test_client_->GetLaunchAppCallback());
 
   RunAndExitGracefully();
   EXPECT_EQ(apps::APP_SHIM_LAUNCH_NORMAL, last_launch_type_);
@@ -180,9 +194,10 @@
 // Test register-only launch, used when Chrome has already launched the app.
 IN_PROC_BROWSER_TEST_F(AppShimHostManagerBrowserTest, LaunchRegisterOnly) {
   test_client_.reset(new TestShimClient());
-  test_client_->host()->LaunchApp(
-      test_client_->GetMojoPtr(), browser()->profile()->GetPath(), kTestAppMode,
-      apps::APP_SHIM_LAUNCH_REGISTER_ONLY, std::vector<base::FilePath>());
+  test_client_->host_bootstrap()->LaunchApp(
+      test_client_->GetHostRequest(), browser()->profile()->GetPath(),
+      kTestAppMode, apps::APP_SHIM_LAUNCH_REGISTER_ONLY,
+      std::vector<base::FilePath>(), test_client_->GetLaunchAppCallback());
 
   RunAndExitGracefully();
   EXPECT_EQ(apps::APP_SHIM_LAUNCH_REGISTER_ONLY, last_launch_type_);
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index b745568..5f13c2b 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -7,6 +7,7 @@
 
 #include "base/callback_helpers.h"
 #include "base/containers/queue.h"
+#include "base/feature_list.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/guid.h"
@@ -45,12 +46,14 @@
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/recently_audible_helper.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/guest_view/browser/guest_view_manager.h"
 #include "components/guest_view/browser/guest_view_manager_delegate.h"
 #include "components/guest_view/browser/guest_view_manager_factory.h"
 #include "components/guest_view/browser/test_guest_view_manager.h"
+#include "components/security_interstitials/content/security_interstitial_tab_helper.h"
 #include "components/version_info/channel.h"
 #include "components/version_info/version_info.h"
 #include "components/viz/common/features.h"
@@ -342,6 +345,26 @@
 
 #endif
 
+bool AreCommittedInterstitialsEnabled() {
+  return base::FeatureList::IsEnabled(features::kSSLCommittedInterstitials);
+}
+
+bool IsShowingInterstitial(content::WebContents* tab) {
+  if (AreCommittedInterstitialsEnabled()) {
+    security_interstitials::SecurityInterstitialTabHelper* helper =
+        security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
+            tab);
+    if (!helper) {
+      return false;
+    } else {
+      return helper
+                 ->GetBlockingPageForCurrentlyCommittedNavigationForTesting() !=
+             nullptr;
+    }
+  }
+  return tab->GetInterstitialPage() != nullptr;
+}
+
 }  // namespace
 
 // This class intercepts media access request from the embedder. The request
@@ -714,7 +737,12 @@
         GetGuestViewManager()->WaitForSingleGuestCreated();
     ASSERT_TRUE(
         guest_web_contents->GetMainFrame()->GetProcess()->IsForGuestsOnly());
-    content::WaitForInterstitialAttach(guest_web_contents);
+    if (AreCommittedInterstitialsEnabled()) {
+      content::TestNavigationObserver observer(guest_web_contents);
+      observer.Wait();
+    } else {
+      content::WaitForInterstitialAttach(guest_web_contents);
+    }
   }
 
   // Runs media_access/allow tests.
@@ -1835,14 +1863,17 @@
 
   content::WebContents* guest_web_contents =
       GetGuestViewManager()->WaitForSingleGuestCreated();
-
-  EXPECT_TRUE(guest_web_contents->ShowingInterstitialPage());
-  EXPECT_TRUE(guest_web_contents->GetInterstitialPage()
-                  ->GetMainFrame()
-                  ->GetView()
-                  ->IsShowing());
-  EXPECT_TRUE(content::IsInnerInterstitialPageConnected(
-      guest_web_contents->GetInterstitialPage()));
+  if (AreCommittedInterstitialsEnabled()) {
+    EXPECT_TRUE(IsShowingInterstitial(guest_web_contents));
+  } else {
+    EXPECT_TRUE(guest_web_contents->ShowingInterstitialPage());
+    EXPECT_TRUE(guest_web_contents->GetInterstitialPage()
+                    ->GetMainFrame()
+                    ->GetView()
+                    ->IsShowing());
+    EXPECT_TRUE(content::IsInnerInterstitialPageConnected(
+        guest_web_contents->GetInterstitialPage()));
+  }
 }
 
 // Test makes sure that interstitial pages are registered in the
@@ -1863,13 +1894,20 @@
   content::WebContents* outer_web_contents = GetFirstAppWindowWebContents();
   content::WebContents* guest_web_contents =
       GetGuestViewManager()->WaitForSingleGuestCreated();
-  content::InterstitialPage* interstitial_page =
-      guest_web_contents->GetInterstitialPage();
 
   std::vector<content::RenderWidgetHostView*> hosts =
       content::GetInputEventRouterRenderWidgetHostViews(outer_web_contents);
-  EXPECT_TRUE(
-      base::ContainsValue(hosts, interstitial_page->GetMainFrame()->GetView()));
+
+  if (AreCommittedInterstitialsEnabled()) {
+    // With committed interstitials, interstitials are no longer a special case
+    // so we can just use the main frame from the WebContents.
+    EXPECT_TRUE(base::ContainsValue(
+        hosts, outer_web_contents->GetMainFrame()->GetView()));
+  } else {
+    EXPECT_TRUE(base::ContainsValue(
+        hosts,
+        guest_web_contents->GetInterstitialPage()->GetMainFrame()->GetView()));
+  }
 }
 
 // Test makes sure that interstitial pages will receive input events and can be
@@ -1891,10 +1929,18 @@
   content::WebContents* outer_web_contents = GetFirstAppWindowWebContents();
   content::WebContents* guest_web_contents =
       GetGuestViewManager()->WaitForSingleGuestCreated();
-  content::InterstitialPage* interstitial_page =
-      guest_web_contents->GetInterstitialPage();
-  content::RenderFrameHost* interstitial_main_frame =
-      interstitial_page->GetMainFrame();
+
+  content::RenderFrameHost* interstitial_main_frame;
+
+  if (AreCommittedInterstitialsEnabled()) {
+    // With committed interstitials, interstitials are no longer a special case
+    // so we can just use the main frame from the WebContents.
+    interstitial_main_frame = guest_web_contents->GetMainFrame();
+  } else {
+    interstitial_main_frame =
+        guest_web_contents->GetInterstitialPage()->GetMainFrame();
+  }
+
   content::RenderWidgetHost* interstitial_widget =
       interstitial_main_frame->GetRenderViewHost()->GetWidget();
 
@@ -1944,7 +1990,7 @@
 
   content::WebContents* guest_web_contents =
       GetGuestViewManager()->WaitForSingleGuestCreated();
-  EXPECT_TRUE(guest_web_contents->ShowingInterstitialPage());
+  EXPECT_TRUE(IsShowingInterstitial(guest_web_contents));
 
   // Navigate to about:blank.
   content::TestNavigationObserver load_observer(guest_web_contents);
diff --git a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
index 7d561e70..3c87139d 100644
--- a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
@@ -22,7 +22,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h"
 #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
-#include "chrome/browser/ui/views_mode_controller.h"
 #include "chrome/test/base/interactive_test_utils.h"
 #include "chrome/test/base/test_launcher_utils.h"
 #include "chrome/test/base/ui_test_utils.h"
diff --git a/chrome/browser/banners/app_banner_manager_browsertest.cc b/chrome/browser/banners/app_banner_manager_browsertest.cc
index be88109..2c0d60b 100644
--- a/chrome/browser/banners/app_banner_manager_browsertest.cc
+++ b/chrome/browser/banners/app_banner_manager_browsertest.cc
@@ -251,9 +251,10 @@
     // with Bind.
     TriggerBannerFlow(
         browser, manager,
-        base::BindOnce(&ui_test_utils::NavigateToURLWithDisposition, browser,
-                       url, WindowOpenDisposition::CURRENT_TAB,
-                       ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION),
+        base::BindOnce(
+            base::IgnoreResult(&ui_test_utils::NavigateToURLWithDisposition),
+            browser, url, WindowOpenDisposition::CURRENT_TAB,
+            ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION),
         expected_will_show, expected_state);
   }
 
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 8ba39a1..e86e0af 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -489,9 +489,6 @@
         <include name="IDR_DRIVE_INTERNALS_HTML" file="resources\chromeos\drive_internals.html" flattenhtml="true" type="BINDATA" />
         <include name="IDR_DRIVE_INTERNALS_JS" file="resources\chromeos\drive_internals.js" type="BINDATA" />
          <include name="IDR_GUEST_SESSION_TAB_HTML" file="resources\chromeos\guest_session_tab.html" flattenhtml="true" type="BINDATA" />
-        <include name="IDR_KEYBOARD_OVERLAY_CSS" file="resources\chromeos\keyboard_overlay.css" flattenhtml="true" type="BINDATA" />
-        <include name="IDR_KEYBOARD_OVERLAY_HTML" file="resources\chromeos\keyboard_overlay.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
-        <include name="IDR_KEYBOARD_OVERLAY_JS" file="resources\chromeos\keyboard_overlay.js" flattenhtml="true" type="BINDATA" />
         <include name="IDR_MOBILE_MANIFEST" file="resources\chromeos\mobile_app\manifest.json" type="BINDATA" />
         <include name="IDR_MOBILE_SETUP_PAGE_HTML" file="resources\chromeos\mobile_setup.html" flattenhtml="true" type="BINDATA" />
         <include name="IDR_MOBILE_SETUP_PORTAL_PAGE_HTML" file="resources\chromeos\mobile_setup_portal.html" flattenhtml="true" type="BINDATA" />
@@ -672,6 +669,8 @@
         <include name="IDR_NUX_EMAIL_ICLOUD_2X" file="resources\welcome\onboarding_welcome\images\icloud_2x.png" type="BINDATA" />
         <include name="IDR_NUX_EMAIL_OUTLOOK_1X" file="resources\welcome\onboarding_welcome\images\outlook_1x.png" type="BINDATA" />
         <include name="IDR_NUX_EMAIL_OUTLOOK_2X" file="resources\welcome\onboarding_welcome\images\outlook_2x.png" type="BINDATA" />
+        <include name="IDR_NUX_EMAIL_PROVIDER_LOGO_1X" file="resources\welcome\onboarding_welcome\images\email_provider_1x.png" type="BINDATA" />
+        <include name="IDR_NUX_EMAIL_PROVIDER_LOGO_2X" file="resources\welcome\onboarding_welcome\images\email_provider_2x.png" type="BINDATA" />
         <include name="IDR_NUX_EMAIL_YAHOO_1X" file="resources\welcome\onboarding_welcome\images\yahoo_1x.png" type="BINDATA" />
         <include name="IDR_NUX_EMAIL_YAHOO_2X" file="resources\welcome\onboarding_welcome\images\yahoo_2x.png" type="BINDATA" />
         <include name="IDR_NUX_GOOGLE_APPS_CHROME_STORE_1X" file="resources\welcome\onboarding_welcome\images\chrome_store_1x.png" type="BINDATA" />
@@ -686,6 +685,10 @@
         <include name="IDR_NUX_GOOGLE_APPS_TRANSLATE_2X" file="resources\welcome\onboarding_welcome\images\translate_2x.png" type="BINDATA" />
         <include name="IDR_NUX_GOOGLE_APPS_YOUTUBE_1X" file="resources\welcome\onboarding_welcome\images\youtube_1x.png" type="BINDATA" />
         <include name="IDR_NUX_GOOGLE_APPS_YOUTUBE_2X" file="resources\welcome\onboarding_welcome\images\youtube_2x.png" type="BINDATA" />
+        <include name="IDR_NUX_SET_AS_DEFAULT_LOGO_1X" file="resources\welcome\onboarding_welcome\images\set_as_default_1x.png" type="BINDATA" />
+        <include name="IDR_NUX_SET_AS_DEFAULT_LOGO_2X" file="resources\welcome\onboarding_welcome\images\set_as_default_2x.png" type="BINDATA" />
+        <include name="IDR_NUX_SET_AS_DEFAULT_ILLUSTRATION_1X" file="resources\welcome\onboarding_welcome\images\set_as_default_illustration_1x.png" type="BINDATA" />
+        <include name="IDR_NUX_SET_AS_DEFAULT_ILLUSTRATION_2X" file="resources\welcome\onboarding_welcome\images\set_as_default_illustration_2x.png" type="BINDATA" />
         <include name="IDR_WELCOME_ONBOARDING_WELCOME_IMAGES_BACKGROUND_SVGS_BLUE_CIRCLE_SVG" file="resources\welcome\onboarding_welcome\images\background_svgs\blue_circle.svg" type="BINDATA" />
         <include name="IDR_WELCOME_ONBOARDING_WELCOME_IMAGES_BACKGROUND_SVGS_GREEN_RECTANGLE_SVG" file="resources\welcome\onboarding_welcome\images\background_svgs\green_rectangle.svg" type="BINDATA" />
         <include name="IDR_WELCOME_ONBOARDING_WELCOME_IMAGES_BACKGROUND_SVGS_GREY_OVAL_SVG" file="resources\welcome\onboarding_welcome\images\background_svgs\grey_oval.svg" type="BINDATA" />
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index d82cb74..32860c0 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -574,10 +574,8 @@
     // |previews_service| is null if |profile_| is off the record.
     PreviewsService* previews_service =
         PreviewsServiceFactory::GetForProfile(profile_);
-    if (previews_service && previews_service->previews_ui_service()) {
-      previews_service->previews_ui_service()->ClearBlackList(delete_begin_,
-                                                              delete_end_);
-    }
+    if (previews_service)
+      previews_service->ClearBlackList(delete_begin_, delete_end_);
 
     // |previews_service| is null if |profile_| is off the record.
     PageLoadCappingService* page_load_capping_service =
diff --git a/chrome/browser/browsing_data/counters/browsing_data_counter_utils.cc b/chrome/browser/browsing_data/counters/browsing_data_counter_utils.cc
index d8e40dc9d..8265b39 100644
--- a/chrome/browser/browsing_data/counters/browsing_data_counter_utils.cc
+++ b/chrome/browser/browsing_data/counters/browsing_data_counter_utils.cc
@@ -33,6 +33,9 @@
 #include "chrome/browser/sync/sync_ui_util.h"
 #endif
 
+namespace browsing_data_counter_utils {
+
+namespace {
 // A helper function to display the size of cache in units of MB or higher.
 // We need this, as 1 MB is the lowest nonzero cache size displayed by the
 // counter.
@@ -44,6 +47,7 @@
   return ui::FormatBytesWithUnits(
       bytes, ui::DataUnits::DATA_UNITS_MEBIBYTE, true);
 }
+}  // namespace
 
 bool ShouldShowCookieException(Profile* profile) {
   if (AccountConsistencyModeManager::IsMirrorEnabledForProfile(profile)) {
@@ -232,3 +236,5 @@
 
   return browsing_data::GetCounterTextFromResult(result);
 }
+
+}  // namespace browsing_data_counter_utils
diff --git a/chrome/browser/browsing_data/counters/browsing_data_counter_utils.h b/chrome/browser/browsing_data/counters/browsing_data_counter_utils.h
index f447d871..08582bd 100644
--- a/chrome/browser/browsing_data/counters/browsing_data_counter_utils.h
+++ b/chrome/browser/browsing_data/counters/browsing_data_counter_utils.h
@@ -11,6 +11,8 @@
 
 class Profile;
 
+namespace browsing_data_counter_utils {
+
 // Whether the exception about not being signed out of your Google profile
 // should be shown.
 bool ShouldShowCookieException(Profile* profile);
@@ -20,4 +22,6 @@
     const browsing_data::BrowsingDataCounter::Result* result,
     Profile* profile);
 
+}  // namespace browsing_data_counter_utils
+
 #endif  // CHROME_BROWSER_BROWSING_DATA_COUNTERS_BROWSING_DATA_COUNTER_UTILS_H_
diff --git a/chrome/browser/browsing_data/counters/browsing_data_counter_utils_browsertest.cc b/chrome/browser/browsing_data/counters/browsing_data_counter_utils_browsertest.cc
index eb4983e..221d74a 100644
--- a/chrome/browser/browsing_data/counters/browsing_data_counter_utils_browsertest.cc
+++ b/chrome/browser/browsing_data/counters/browsing_data_counter_utils_browsertest.cc
@@ -26,7 +26,7 @@
 #include "components/signin/core/browser/signin_manager_base.h"
 #endif
 
-namespace {
+namespace browsing_data_counter_utils {
 
 class BrowsingDataCounterUtilsBrowserTest
     : public SyncTest,
@@ -123,4 +123,4 @@
 #endif  // !defined(OS_CHROMEOS)
 }
 
-}  // namespace
+}  // namespace browsing_data_counter_utils
diff --git a/chrome/browser/browsing_data/counters/browsing_data_counter_utils_unittest.cc b/chrome/browser/browsing_data/counters/browsing_data_counter_utils_unittest.cc
index 49948f2..4bf2e64 100644
--- a/chrome/browser/browsing_data/counters/browsing_data_counter_utils_unittest.cc
+++ b/chrome/browser/browsing_data/counters/browsing_data_counter_utils_unittest.cc
@@ -30,6 +30,8 @@
 #include "chrome/browser/browsing_data/counters/media_licenses_counter.h"
 #endif
 
+namespace browsing_data_counter_utils {
+
 class BrowsingDataCounterUtilsTest : public testing::Test {
  public:
   BrowsingDataCounterUtilsTest() {}
@@ -221,3 +223,5 @@
 
   password_store->ShutdownOnUIThread();
 }
+
+}  // namespace browsing_data_counter_utils
diff --git a/chrome/browser/browsing_data/counters/site_data_counter.cc b/chrome/browser/browsing_data/counters/site_data_counter.cc
index 999e001..1bd8b2f0 100644
--- a/chrome/browser/browsing_data/counters/site_data_counter.cc
+++ b/chrome/browser/browsing_data/counters/site_data_counter.cc
@@ -15,7 +15,7 @@
 
 namespace {
 bool CheckSyncState(Profile* profile, const syncer::SyncService* sync_service) {
-  return ShouldShowCookieException(profile);
+  return browsing_data_counter_utils::ShouldShowCookieException(profile);
 }
 }  // namespace
 
diff --git a/chrome/browser/chrome_browser_field_trials.cc b/chrome/browser/chrome_browser_field_trials.cc
index 9a7ea1d..2ac704f 100644
--- a/chrome/browser/chrome_browser_field_trials.cc
+++ b/chrome/browser/chrome_browser_field_trials.cc
@@ -6,30 +6,20 @@
 
 #include <string>
 
-#include "base/bind.h"
-#include "base/bind_helpers.h"
 #include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/files/file_util.h"
 #include "base/metrics/field_trial.h"
-#include "base/metrics/histogram_base.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/metrics/persistent_histogram_allocator.h"
-#include "base/path_service.h"
 #include "base/strings/string_util.h"
-#include "base/sys_info.h"
-#include "base/task/post_task.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/metrics/chrome_metrics_service_client.h"
 #include "chrome/browser/metrics/chrome_metrics_services_manager_client.h"
+#include "chrome/browser/metrics/persistent_histograms.h"
 #include "chrome/common/channel_info.h"
-#include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "components/metrics/metrics_pref_names.h"
-#include "components/metrics/persistent_system_profile.h"
 #include "components/ukm/ukm_recorder_impl.h"
-#include "components/variations/variations_associated_data.h"
 #include "components/version_info/version_info.h"
 
 #if defined(OS_ANDROID)
@@ -44,159 +34,6 @@
 
 namespace {
 
-// Creating a "spare" file for persistent metrics involves a lot of I/O and
-// isn't important so delay the operation for a while after startup.
-#if defined(OS_ANDROID)
-// Android needs the spare file and also launches faster.
-constexpr bool kSpareFileRequired = true;
-constexpr int kSpareFileCreateDelaySeconds = 10;
-#else
-// Desktop may have to restore a lot of tabs so give it more time before doing
-// non-essential work. The spare file is still a performance boost but not as
-// significant of one so it's not required.
-constexpr bool kSpareFileRequired = false;
-constexpr int kSpareFileCreateDelaySeconds = 90;
-#endif
-
-// Check for feature enabling the use of persistent histogram storage and
-// enable the global allocator if so.
-// TODO(bcwhite): Move this and CreateInstallerFileMetricsProvider into a new
-// file and make kBrowserMetricsName local to that file.
-void InstantiatePersistentHistograms() {
-  base::FilePath metrics_dir;
-  if (!base::PathService::Get(chrome::DIR_USER_DATA, &metrics_dir))
-    return;
-
-  // Create a directory for storing completed metrics files. Files in this
-  // directory must have embedded system profiles. If the directory can't be
-  // created, the file will just be deleted below.
-  base::FilePath upload_dir =
-      metrics_dir.AppendASCII(ChromeMetricsServiceClient::kBrowserMetricsName);
-  base::CreateDirectory(upload_dir);
-
-  // Metrics files are typically created as a |spare_file| in the profile
-  // directory (e.g. "BrowserMetrics-spare.pma") and are then rotated into
-  // the |active_file| (e.g. "BrowserMetrics-active.pma") location for use
-  // during the browser run. It is then moved to a time-stamped file in a
-  // subdirectory (e.g. "BrowserMetrics/BrowserMetrics-1234ABCD.pma") for
-  // upload when convenient.
-  base::FilePath upload_file;
-  base::FilePath active_file;
-  base::FilePath spare_file;
-  base::GlobalHistogramAllocator::ConstructFilePathsForUploadDir(
-      metrics_dir, upload_dir, ChromeMetricsServiceClient::kBrowserMetricsName,
-      &upload_file, &active_file, &spare_file);
-
-  // This is used to report results to an UMA histogram.
-  enum InitResult {
-    LOCAL_MEMORY_SUCCESS,
-    LOCAL_MEMORY_FAILED,
-    MAPPED_FILE_SUCCESS,
-    MAPPED_FILE_FAILED,
-    MAPPED_FILE_EXISTS,
-    NO_SPARE_FILE,
-    NO_UPLOAD_DIR,
-    INIT_RESULT_MAX
-  };
-  InitResult result;
-
-  // Create persistent/shared memory and allow histograms to be stored in
-  // it. Memory that is not actualy used won't be physically mapped by the
-  // system. BrowserMetrics usage, as reported in UMA, has the 99.99
-  // percentile around 3MiB as of 2018-10-22.
-  const size_t kAllocSize = 4 << 20;     // 4 MiB
-  const uint32_t kAllocId = 0x935DDD43;  // SHA1(BrowserMetrics)
-  std::string storage = variations::GetVariationParamValueByFeature(
-      base::kPersistentHistogramsFeature, "storage");
-
-  static const char kMappedFile[] = "MappedFile";
-  static const char kLocalMemory[] = "LocalMemory";
-
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
-  // Linux kernel 4.4.0.* shows a huge number of SIGBUS crashes with persistent
-  // histograms enabled using a mapped file.  Change this to use local memory.
-  // https://bugs.chromium.org/p/chromium/issues/detail?id=753741
-  if (storage.empty() || storage == kMappedFile) {
-    int major, minor, bugfix;
-    base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix);
-    if (major == 4 && minor == 4 && bugfix == 0)
-      storage = kLocalMemory;
-  }
-#endif
-
-  // Don't use mapped-file memory by default on low-end devices, especially
-  // on Android. The extra disk consumption and/or extra disk access could
-  // have a significant performance impact. https://crbug.com/896394
-  if (storage.empty() && base::SysInfo::IsLowEndDevice())
-    storage = kLocalMemory;
-
-  if (storage.empty() || storage == kMappedFile) {
-    if (!base::PathExists(upload_dir)) {
-      // Handle failure to create the directory.
-      result = NO_UPLOAD_DIR;
-    } else if (base::PathExists(upload_file)) {
-      // "upload" filename is supposed to be unique so this shouldn't happen.
-      result = MAPPED_FILE_EXISTS;
-    } else {
-      // Move any sparse file into the upload position.
-      base::ReplaceFile(spare_file, upload_file, nullptr);
-      // Create global allocator using the "upload" file.
-      if (kSpareFileRequired && !base::PathExists(upload_file)) {
-        result = NO_SPARE_FILE;
-      } else if (base::GlobalHistogramAllocator::CreateWithFile(
-                     upload_file, kAllocSize, kAllocId,
-                     ChromeMetricsServiceClient::kBrowserMetricsName)) {
-        result = MAPPED_FILE_SUCCESS;
-      } else {
-        result = MAPPED_FILE_FAILED;
-      }
-    }
-    // Schedule the creation of a "spare" file for use on the next run.
-    base::PostDelayedTaskWithTraits(
-        FROM_HERE,
-        {base::MayBlock(), base::TaskPriority::LOWEST,
-         base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
-        base::BindOnce(base::IgnoreResult(
-                           &base::GlobalHistogramAllocator::CreateSpareFile),
-                       std::move(spare_file), kAllocSize),
-        base::TimeDelta::FromSeconds(kSpareFileCreateDelaySeconds));
-  } else if (storage == kLocalMemory) {
-    // Use local memory for storage even though it will not persist across
-    // an unclean shutdown. This sets the result but the actual creation is
-    // done below.
-    result = LOCAL_MEMORY_SUCCESS;
-  } else {
-    // Persistent metric storage is disabled. Must return here.
-    return;
-  }
-
-  // Get the allocator that was just created and report result. Exit if the
-  // allocator could not be created.
-  UMA_HISTOGRAM_ENUMERATION("UMA.PersistentHistograms.InitResult", result,
-                            INIT_RESULT_MAX);
-
-  base::GlobalHistogramAllocator* allocator =
-      base::GlobalHistogramAllocator::Get();
-  if (!allocator) {
-    // If no allocator was created above, try to create a LocalMemomory one
-    // here. This avoids repeating the call many times above. In the case where
-    // persistence is disabled, an early return is done above.
-    base::GlobalHistogramAllocator::CreateWithLocalMemory(
-        kAllocSize, kAllocId, ChromeMetricsServiceClient::kBrowserMetricsName);
-    allocator = base::GlobalHistogramAllocator::Get();
-    if (!allocator)
-      return;
-  }
-
-  // Store a copy of the system profile in this allocator.
-  metrics::GlobalPersistentSystemProfile::GetInstance()
-      ->RegisterPersistentAllocator(allocator->memory_allocator());
-
-  // Create tracking histograms for the allocator and record storage file.
-  allocator->CreateTrackingHistograms(
-      ChromeMetricsServiceClient::kBrowserMetricsName);
-}
-
 // Create a field trial to control metrics/crash sampling for Stable on
 // Windows/Android if no variations seed was applied.
 void CreateFallbackSamplingTrialIfNeeded(base::FeatureList* feature_list) {
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 3be69bf..1f6670c 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -188,7 +188,6 @@
 #include "net/url_request/url_request.h"
 #include "printing/buildflags/buildflags.h"
 #include "rlz/buildflags/buildflags.h"
-#include "services/service_manager/embedder/main_delegate.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "third_party/blink/public/common/experiments/memory_ablation_experiment.h"
 #include "third_party/widevine/cdm/buildflags.h"
@@ -683,8 +682,7 @@
 // Initializes the shared instance of ResourceBundle and returns the locale. An
 // empty |actual_locale| value indicates failure.
 ApplicationLocaleResult InitResourceBundleAndDetermineLocale(
-    const content::MainFunctionParams& params,
-    std::unique_ptr<ui::DataPack> data_pack) {
+    const content::MainFunctionParams& params) {
 #if defined(OS_MACOSX)
   // TODO(markusheintz): Read preference pref::kApplicationLocale in order
   // to enforce the application locale.
@@ -702,11 +700,22 @@
   std::string actual_locale = ui::ResourceBundle::InitSharedInstanceWithLocale(
       preferred_locale, nullptr, ui::ResourceBundle::LOAD_COMMON_RESOURCES);
 
-  if (data_pack) {
-    ui::ResourceBundle::GetSharedInstance().AddDataPack(std::move(data_pack));
-  } else {
-    LOG(ERROR) << "Failed to load resources.pak\n"
-               << "Some features may not be available.";
+  if (actual_locale.empty())
+    return {actual_locale, preferred_locale};
+
+  // First run prefs needs data from the ResourceBundle, so load it now.
+  {
+    TRACE_EVENT0("startup",
+                 "ChromeBrowserMainParts::InitResourceBundleAndDetermineLocale:"
+                 ":AddDataPack");
+    base::FilePath resources_pack_path;
+    base::PathService::Get(chrome::FILE_RESOURCES_PACK, &resources_pack_path);
+#if defined(OS_ANDROID)
+    ui::LoadMainAndroidPackFile("assets/resources.pak", resources_pack_path);
+#else
+    ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath(
+        resources_pack_path, ui::SCALE_FACTOR_NONE);
+#endif  // defined(OS_ANDROID)
   }
 
   return {actual_locale, preferred_locale};
@@ -782,7 +791,6 @@
 
 ChromeBrowserMainParts::ChromeBrowserMainParts(
     const content::MainFunctionParams& parameters,
-    std::unique_ptr<ui::DataPack> data_pack,
     ChromeFeatureListCreator* chrome_feature_list_creator)
     : parameters_(parameters),
       parsed_command_line_(parameters.command_line),
@@ -793,7 +801,6 @@
           !parameters.ui_task),
       profile_(NULL),
       run_message_loop_(true),
-      service_manifest_data_pack_(std::move(data_pack)),
       chrome_feature_list_creator_(chrome_feature_list_creator) {
   DCHECK(chrome_feature_list_creator_);
   // If we're running tests (ui_task is non-null).
@@ -1057,8 +1064,8 @@
 
   // First run prefs may use the ResourceBundle (and get data from it), so this
   // needs to be before ApplyFirstRunPrefs().
-  ApplicationLocaleResult locale_result = InitResourceBundleAndDetermineLocale(
-      parameters(), std::move(service_manifest_data_pack_));
+  ApplicationLocaleResult locale_result =
+      InitResourceBundleAndDetermineLocale(parameters());
 
   if (locale_result.actual_locale.empty()) {
     *failed_to_load_resource_bundle = true;
diff --git a/chrome/browser/chrome_browser_main.h b/chrome/browser/chrome_browser_main.h
index 52239138..37c9c68d 100644
--- a/chrome/browser/chrome_browser_main.h
+++ b/chrome/browser/chrome_browser_main.h
@@ -16,7 +16,6 @@
 #include "chrome/browser/ui/startup/startup_browser_creator.h"
 #include "content/public/browser/browser_main_parts.h"
 #include "content/public/common/main_function_params.h"
-#include "ui/base/resource/data_pack.h"
 
 class BrowserProcessImpl;
 class ChromeBrowserMainExtraParts;
@@ -57,7 +56,6 @@
 
  protected:
   ChromeBrowserMainParts(const content::MainFunctionParams& parameters,
-                         std::unique_ptr<ui::DataPack> data_pack,
                          ChromeFeatureListCreator* chrome_feature_list_creator);
 
   // content::BrowserMainParts overrides.
@@ -200,10 +198,6 @@
 
   base::FilePath user_data_dir_;
 
-  // This is used to store the ui data pack. The data pack is moved when
-  // resource bundle gets created.
-  std::unique_ptr<ui::DataPack> service_manifest_data_pack_;
-
   ChromeFeatureListCreator* chrome_feature_list_creator_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeBrowserMainParts);
diff --git a/chrome/browser/chrome_browser_main_android.cc b/chrome/browser/chrome_browser_main_android.cc
index 2c5148b..ac8971f 100644
--- a/chrome/browser/chrome_browser_main_android.cc
+++ b/chrome/browser/chrome_browser_main_android.cc
@@ -35,10 +35,8 @@
 
 ChromeBrowserMainPartsAndroid::ChromeBrowserMainPartsAndroid(
     const content::MainFunctionParams& parameters,
-    std::unique_ptr<ui::DataPack> data_pack,
     ChromeFeatureListCreator* chrome_feature_list_creator)
     : ChromeBrowserMainParts(parameters,
-                             std::move(data_pack),
                              chrome_feature_list_creator) {}
 
 ChromeBrowserMainPartsAndroid::~ChromeBrowserMainPartsAndroid() {
diff --git a/chrome/browser/chrome_browser_main_android.h b/chrome/browser/chrome_browser_main_android.h
index 74516a7c..fe11a5d 100644
--- a/chrome/browser/chrome_browser_main_android.h
+++ b/chrome/browser/chrome_browser_main_android.h
@@ -13,7 +13,6 @@
  public:
   ChromeBrowserMainPartsAndroid(
       const content::MainFunctionParams& parameters,
-      std::unique_ptr<ui::DataPack> data_pack,
       ChromeFeatureListCreator* chrome_feature_list_creator);
   ~ChromeBrowserMainPartsAndroid() override;
 
diff --git a/chrome/browser/chrome_browser_main_linux.cc b/chrome/browser/chrome_browser_main_linux.cc
index ab9016fd..10b5d11 100644
--- a/chrome/browser/chrome_browser_main_linux.cc
+++ b/chrome/browser/chrome_browser_main_linux.cc
@@ -36,10 +36,8 @@
 
 ChromeBrowserMainPartsLinux::ChromeBrowserMainPartsLinux(
     const content::MainFunctionParams& parameters,
-    std::unique_ptr<ui::DataPack> data_pack,
     ChromeFeatureListCreator* chrome_feature_list_creator)
     : ChromeBrowserMainPartsPosix(parameters,
-                                  std::move(data_pack),
                                   chrome_feature_list_creator) {}
 
 ChromeBrowserMainPartsLinux::~ChromeBrowserMainPartsLinux() {
diff --git a/chrome/browser/chrome_browser_main_linux.h b/chrome/browser/chrome_browser_main_linux.h
index a52adddd6..f6bf118 100644
--- a/chrome/browser/chrome_browser_main_linux.h
+++ b/chrome/browser/chrome_browser_main_linux.h
@@ -15,7 +15,6 @@
  public:
   ChromeBrowserMainPartsLinux(
       const content::MainFunctionParams& parameters,
-      std::unique_ptr<ui::DataPack> data_pack,
       ChromeFeatureListCreator* chrome_feature_list_creator);
   ~ChromeBrowserMainPartsLinux() override;
 
diff --git a/chrome/browser/chrome_browser_main_mac.h b/chrome/browser/chrome_browser_main_mac.h
index 8bc804c..584c61a 100644
--- a/chrome/browser/chrome_browser_main_mac.h
+++ b/chrome/browser/chrome_browser_main_mac.h
@@ -12,7 +12,6 @@
  public:
   ChromeBrowserMainPartsMac(
       const content::MainFunctionParams& parameters,
-      std::unique_ptr<ui::DataPack> data_pack,
       ChromeFeatureListCreator* chrome_feature_list_creator);
   ~ChromeBrowserMainPartsMac() override;
 
diff --git a/chrome/browser/chrome_browser_main_mac.mm b/chrome/browser/chrome_browser_main_mac.mm
index 3b588929..4fda3a7b 100644
--- a/chrome/browser/chrome_browser_main_mac.mm
+++ b/chrome/browser/chrome_browser_main_mac.mm
@@ -69,10 +69,8 @@
 
 ChromeBrowserMainPartsMac::ChromeBrowserMainPartsMac(
     const content::MainFunctionParams& parameters,
-    std::unique_ptr<ui::DataPack> data_pack,
     ChromeFeatureListCreator* chrome_feature_list_creator)
     : ChromeBrowserMainPartsPosix(parameters,
-                                  std::move(data_pack),
                                   chrome_feature_list_creator) {}
 
 ChromeBrowserMainPartsMac::~ChromeBrowserMainPartsMac() {
diff --git a/chrome/browser/chrome_browser_main_posix.cc b/chrome/browser/chrome_browser_main_posix.cc
index d25fa27..b278ec8 100644
--- a/chrome/browser/chrome_browser_main_posix.cc
+++ b/chrome/browser/chrome_browser_main_posix.cc
@@ -111,10 +111,8 @@
 
 ChromeBrowserMainPartsPosix::ChromeBrowserMainPartsPosix(
     const content::MainFunctionParams& parameters,
-    std::unique_ptr<ui::DataPack> data_pack,
     ChromeFeatureListCreator* chrome_feature_list_creator)
     : ChromeBrowserMainParts(parameters,
-                             std::move(data_pack),
                              chrome_feature_list_creator) {}
 
 int ChromeBrowserMainPartsPosix::PreEarlyInitialization() {
diff --git a/chrome/browser/chrome_browser_main_posix.h b/chrome/browser/chrome_browser_main_posix.h
index f5d497a..f1f4b25 100644
--- a/chrome/browser/chrome_browser_main_posix.h
+++ b/chrome/browser/chrome_browser_main_posix.h
@@ -12,7 +12,6 @@
  public:
   ChromeBrowserMainPartsPosix(
       const content::MainFunctionParams& parameters,
-      std::unique_ptr<ui::DataPack> data_pack,
       ChromeFeatureListCreator* chrome_feature_list_creator);
 
   // content::BrowserMainParts overrides.
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index f080e452..1953e123 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -455,10 +455,8 @@
 
 ChromeBrowserMainPartsWin::ChromeBrowserMainPartsWin(
     const content::MainFunctionParams& parameters,
-    std::unique_ptr<ui::DataPack> data_pack,
     ChromeFeatureListCreator* chrome_feature_list_creator)
     : ChromeBrowserMainParts(parameters,
-                             std::move(data_pack),
                              chrome_feature_list_creator) {}
 
 ChromeBrowserMainPartsWin::~ChromeBrowserMainPartsWin() {
diff --git a/chrome/browser/chrome_browser_main_win.h b/chrome/browser/chrome_browser_main_win.h
index 2865cdf4..7be504c 100644
--- a/chrome/browser/chrome_browser_main_win.h
+++ b/chrome/browser/chrome_browser_main_win.h
@@ -28,7 +28,6 @@
  public:
   ChromeBrowserMainPartsWin(
       const content::MainFunctionParams& parameters,
-      std::unique_ptr<ui::DataPack> data_pack,
       ChromeFeatureListCreator* chrome_feature_list_creator);
 
   ~ChromeBrowserMainPartsWin() override;
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 82b943dd..113ade2 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -57,7 +57,6 @@
 #include "chrome/browser/media/webrtc/audio_debug_recordings_handler.h"
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/media/webrtc/webrtc_logging_handler_host.h"
-#include "chrome/browser/memory/chrome_memory_coordinator_delegate.h"
 #include "chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.h"
 #include "chrome/browser/nacl_host/nacl_browser_delegate_impl.h"
 #include "chrome/browser/navigation_predictor/navigation_predictor.h"
@@ -946,7 +945,7 @@
     scoped_refptr<base::SequencedTaskRunner> task_runner,
     base::Callback<void(bool)> callback,
     bool result) {
-  task_runner->PostTask(FROM_HERE, base::Bind(std::move(callback), result));
+  task_runner->PostTask(FROM_HERE, base::BindOnce(std::move(callback), result));
 }
 #endif
 
@@ -1019,10 +1018,8 @@
 }  // namespace
 
 ChromeContentBrowserClient::ChromeContentBrowserClient(
-    std::unique_ptr<ui::DataPack> data_pack,
     ChromeFeatureListCreator* chrome_feature_list_creator)
-    : service_manifest_data_pack_(std::move(data_pack)),
-      chrome_feature_list_creator_(chrome_feature_list_creator),
+    : chrome_feature_list_creator_(chrome_feature_list_creator),
       weak_factory_(this) {
 #if BUILDFLAG(ENABLE_PLUGINS)
   for (size_t i = 0; i < arraysize(kPredefinedAllowedDevChannelOrigins); ++i)
@@ -1110,34 +1107,27 @@
   ChromeBrowserMainParts* main_parts;
   // Construct the Main browser parts based on the OS type.
 #if defined(OS_WIN)
-  main_parts = new ChromeBrowserMainPartsWin(
-      parameters, std::move(service_manifest_data_pack_),
-      chrome_feature_list_creator_);
+  main_parts =
+      new ChromeBrowserMainPartsWin(parameters, chrome_feature_list_creator_);
 #elif defined(OS_MACOSX)
-  main_parts = new ChromeBrowserMainPartsMac(
-      parameters, std::move(service_manifest_data_pack_),
-      chrome_feature_list_creator_);
+  main_parts =
+      new ChromeBrowserMainPartsMac(parameters, chrome_feature_list_creator_);
 #elif defined(OS_CHROMEOS)
   main_parts = new chromeos::ChromeBrowserMainPartsChromeos(
-      parameters, std::move(service_manifest_data_pack_),
-      chrome_feature_list_creator_);
+      parameters, chrome_feature_list_creator_);
 #elif defined(OS_LINUX)
-  main_parts = new ChromeBrowserMainPartsLinux(
-      parameters, std::move(service_manifest_data_pack_),
-      chrome_feature_list_creator_);
+  main_parts =
+      new ChromeBrowserMainPartsLinux(parameters, chrome_feature_list_creator_);
 #elif defined(OS_ANDROID)
-  main_parts = new ChromeBrowserMainPartsAndroid(
-      parameters, std::move(service_manifest_data_pack_),
-      chrome_feature_list_creator_);
+  main_parts = new ChromeBrowserMainPartsAndroid(parameters,
+                                                 chrome_feature_list_creator_);
 #elif defined(OS_POSIX)
-  main_parts = new ChromeBrowserMainPartsPosix(
-      parameters, std::move(service_manifest_data_pack_),
-      chrome_feature_list_creator_);
+  main_parts =
+      new ChromeBrowserMainPartsPosix(parameters, chrome_feature_list_creator_);
 #else
   NOTREACHED();
-  main_parts = new ChromeBrowserMainParts(
-      parameters, std::move(service_manifest_data_pack_),
-      chrome_feature_list_creator_);
+  main_parts =
+      new ChromeBrowserMainParts(parameters, chrome_feature_list_creator_);
 #endif
 
   chrome::AddProfilesExtraParts(main_parts);
@@ -2308,7 +2298,7 @@
     const GURL& scope,
     const GURL& first_party_url,
     content::ResourceContext* context,
-    const base::Callback<content::WebContents*(void)>& wc_getter) {
+    base::RepeatingCallback<content::WebContents*()> wc_getter) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
@@ -2345,7 +2335,7 @@
     base::PostTaskWithTraits(
         FROM_HERE, {BrowserThread::UI},
         base::BindOnce(&TabSpecificContentSettings::ServiceWorkerAccessed,
-                       wc_getter, scope, !allow_javascript,
+                       std::move(wc_getter), scope, !allow_javascript,
                        !allow_serviceworker));
   }
   return allow_javascript && allow_serviceworker;
@@ -4326,11 +4316,6 @@
   }
 }
 
-std::unique_ptr<content::MemoryCoordinatorDelegate>
-ChromeContentBrowserClient::GetMemoryCoordinatorDelegate() {
-  return memory::ChromeMemoryCoordinatorDelegate::Create();
-}
-
 #if BUILDFLAG(ENABLE_MEDIA_REMOTING)
 void ChromeContentBrowserClient::CreateMediaRemoter(
     content::RenderFrameHost* render_frame_host,
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index f9c6c7a..65cd8fb 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -28,7 +28,6 @@
 #include "ppapi/buildflags/buildflags.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
-#include "ui/base/resource/data_pack.h"
 
 class ChromeContentBrowserClientParts;
 class PrefRegistrySimple;
@@ -78,7 +77,6 @@
 class ChromeContentBrowserClient : public content::ContentBrowserClient {
  public:
   explicit ChromeContentBrowserClient(
-      std::unique_ptr<ui::DataPack> data_pack = nullptr,
       ChromeFeatureListCreator* chrome_feature_list_creator = nullptr);
   ~ChromeContentBrowserClient() override;
 
@@ -211,7 +209,7 @@
       const GURL& scope,
       const GURL& first_party,
       content::ResourceContext* context,
-      const base::Callback<content::WebContents*(void)>& wc_getter) override;
+      base::RepeatingCallback<content::WebContents*()> wc_getter) override;
   bool AllowSharedWorker(const GURL& worker_url,
                          const GURL& main_frame_url,
                          const std::string& name,
@@ -422,8 +420,6 @@
       const base::flat_set<media::CdmProxy::Protocol>& cdm_proxy_protocols,
       base::flat_set<media::VideoCodec>* video_codecs,
       base::flat_set<media::EncryptionMode>* encryption_schemes) override;
-  std::unique_ptr<content::MemoryCoordinatorDelegate>
-  GetMemoryCoordinatorDelegate() override;
   ::rappor::RapporService* GetRapporService() override;
 #if BUILDFLAG(ENABLE_MEDIA_REMOTING)
   void CreateMediaRemoter(content::RenderFrameHost* render_frame_host,
@@ -615,8 +611,6 @@
                                               const url::Origin&>>
       worker_interfaces_parameterized_;
 
-  std::unique_ptr<ui::DataPack> service_manifest_data_pack_;
-
   ChromeFeatureListCreator* chrome_feature_list_creator_;
 
   base::WeakPtrFactory<ChromeContentBrowserClient> weak_factory_;
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 6018469..abd3426 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -53,7 +53,6 @@
     ":user_activity_event_proto",
     "//apps",
     "//ash",
-    "//ash:ash_with_content",
     "//ash/public/cpp",
     "//ash/system/message_center/arc",
     "//cc/paint",
@@ -351,8 +350,13 @@
     "apps/intent_helper/apps_navigation_types.h",
     "apps/intent_helper/page_transition_util.cc",
     "apps/intent_helper/page_transition_util.h",
+    "arc/accessibility/accessibility_node_info_data_wrapper.cc",
+    "arc/accessibility/accessibility_node_info_data_wrapper.h",
+    "arc/accessibility/accessibility_window_info_data_wrapper.cc",
+    "arc/accessibility/accessibility_window_info_data_wrapper.h",
     "arc/accessibility/arc_accessibility_helper_bridge.cc",
     "arc/accessibility/arc_accessibility_helper_bridge.h",
+    "arc/accessibility/arc_accessibility_info_data.h",
     "arc/accessibility/arc_accessibility_util.cc",
     "arc/accessibility/arc_accessibility_util.h",
     "arc/accessibility/ax_tree_source_arc.cc",
@@ -690,6 +694,7 @@
     "extensions/active_tab_permission_granter_delegate_chromeos.h",
     "extensions/default_app_order.cc",
     "extensions/default_app_order.h",
+    "extensions/default_web_app_ids.h",
     "extensions/device_local_account_external_policy_loader.cc",
     "extensions/device_local_account_external_policy_loader.h",
     "extensions/device_local_account_management_policy_provider.cc",
diff --git a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc
new file mode 100644
index 0000000..87be748
--- /dev/null
+++ b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc
@@ -0,0 +1,536 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.h"
+#include "chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h"
+#include "components/exo/wm_helper.h"
+#include "ui/accessibility/platform/ax_android_constants.h"
+
+namespace arc {
+
+using AXActionType = mojom::AccessibilityActionType;
+using AXBooleanProperty = mojom::AccessibilityBooleanProperty;
+using AXCollectionInfoData = mojom::AccessibilityCollectionInfoData;
+using AXCollectionItemInfoData = mojom::AccessibilityCollectionItemInfoData;
+using AXEventData = mojom::AccessibilityEventData;
+using AXEventType = mojom::AccessibilityEventType;
+using AXIntListProperty = mojom::AccessibilityIntListProperty;
+using AXIntProperty = mojom::AccessibilityIntProperty;
+using AXNodeInfoData = mojom::AccessibilityNodeInfoData;
+using AXRangeInfoData = mojom::AccessibilityRangeInfoData;
+using AXStringListProperty = mojom::AccessibilityStringListProperty;
+using AXStringProperty = mojom::AccessibilityStringProperty;
+
+AccessibilityNodeInfoDataWrapper::AccessibilityNodeInfoDataWrapper(
+    AXTreeSourceArc* tree_source,
+    AXNodeInfoData* node)
+    : tree_source_(tree_source), node_ptr_(node) {}
+
+bool AccessibilityNodeInfoDataWrapper::IsNode() const {
+  return true;
+}
+
+mojom::AccessibilityNodeInfoData* AccessibilityNodeInfoDataWrapper::GetNode()
+    const {
+  return node_ptr_;
+}
+
+mojom::AccessibilityWindowInfoData*
+AccessibilityNodeInfoDataWrapper::GetWindow() const {
+  return nullptr;
+}
+
+int32_t AccessibilityNodeInfoDataWrapper::GetId() const {
+  return node_ptr_->id;
+}
+
+const gfx::Rect AccessibilityNodeInfoDataWrapper::GetBounds() const {
+  return node_ptr_->bounds_in_screen;
+}
+
+bool AccessibilityNodeInfoDataWrapper::IsVisibleToUser() const {
+  return GetProperty(AXBooleanProperty::VISIBLE_TO_USER);
+}
+
+bool AccessibilityNodeInfoDataWrapper::IsFocused() const {
+  return GetProperty(AXBooleanProperty::FOCUSED);
+}
+
+bool AccessibilityNodeInfoDataWrapper::CanBeAccessibilityFocused() const {
+  // In Chrome, this means:
+  // a node with a non-generic role and:
+  // actionable nodes or top level scrollables with a name
+  ui::AXNodeData data;
+  PopulateAXRole(&data);
+  bool non_generic_role = data.role != ax::mojom::Role::kGenericContainer &&
+                          data.role != ax::mojom::Role::kGroup;
+  bool actionable = GetProperty(AXBooleanProperty::CLICKABLE) ||
+                    GetProperty(AXBooleanProperty::FOCUSABLE) ||
+                    GetProperty(AXBooleanProperty::CHECKABLE);
+  bool top_level_scrollable = HasProperty(AXStringProperty::TEXT) &&
+                              GetProperty(AXBooleanProperty::SCROLLABLE);
+  return non_generic_role && (actionable || top_level_scrollable);
+}
+
+void AccessibilityNodeInfoDataWrapper::PopulateAXRole(
+    ui::AXNodeData* out_data) const {
+  std::string class_name;
+  if (GetProperty(AXStringProperty::CLASS_NAME, &class_name)) {
+    out_data->AddStringAttribute(ax::mojom::StringAttribute::kClassName,
+                                 class_name);
+  }
+
+  if (!GetProperty(AXBooleanProperty::IMPORTANCE)) {
+    out_data->role = ax::mojom::Role::kIgnored;
+    return;
+  }
+
+  if (GetProperty(AXBooleanProperty::EDITABLE)) {
+    out_data->role = ax::mojom::Role::kTextField;
+    return;
+  }
+
+  if (GetProperty(AXBooleanProperty::HEADING)) {
+    out_data->role = ax::mojom::Role::kHeading;
+    return;
+  }
+
+  if (HasCoveringSpan(AXStringProperty::TEXT, mojom::SpanType::URL) ||
+      HasCoveringSpan(AXStringProperty::CONTENT_DESCRIPTION,
+                      mojom::SpanType::URL)) {
+    out_data->role = ax::mojom::Role::kLink;
+    return;
+  }
+
+  AXCollectionInfoData* collection_info = node_ptr_->collection_info.get();
+  if (collection_info) {
+    if (collection_info->row_count > 1 && collection_info->column_count > 1) {
+      out_data->role = ax::mojom::Role::kGrid;
+      out_data->AddIntAttribute(ax::mojom::IntAttribute::kTableRowCount,
+                                collection_info->row_count);
+      out_data->AddIntAttribute(ax::mojom::IntAttribute::kTableColumnCount,
+                                collection_info->column_count);
+      return;
+    }
+
+    if (collection_info->row_count == 1 || collection_info->column_count == 1) {
+      out_data->role = ax::mojom::Role::kList;
+      out_data->AddIntAttribute(
+          ax::mojom::IntAttribute::kSetSize,
+          std::max(collection_info->row_count, collection_info->column_count));
+      return;
+    }
+  }
+
+  AXCollectionItemInfoData* collection_item_info =
+      node_ptr_->collection_item_info.get();
+  if (collection_item_info) {
+    if (collection_item_info->is_heading) {
+      out_data->role = ax::mojom::Role::kHeading;
+      return;
+    }
+
+    // In order to properly resolve the role of this node, a collection item, we
+    // need additional information contained only in the CollectionInfo. The
+    // CollectionInfo should be an ancestor of this node.
+    AXCollectionInfoData* collection_info = nullptr;
+    for (AXNodeInfoData* container = node_ptr_; container;) {
+      if (!container)
+        break;
+      if (container->collection_info.get()) {
+        collection_info = container->collection_info.get();
+        break;
+      }
+
+      ArcAccessibilityInfoData* container_data =
+          tree_source_->GetParent(tree_source_->GetFromId(container->id));
+      if (!container_data->IsNode())
+        break;
+      container = container_data->GetNode();
+    }
+
+    if (collection_info) {
+      if (collection_info->row_count > 1 && collection_info->column_count > 1) {
+        out_data->role = ax::mojom::Role::kCell;
+        out_data->AddIntAttribute(ax::mojom::IntAttribute::kTableRowIndex,
+                                  collection_item_info->row_index);
+        out_data->AddIntAttribute(ax::mojom::IntAttribute::kTableColumnIndex,
+                                  collection_item_info->column_index);
+        return;
+      }
+
+      out_data->role = ax::mojom::Role::kListItem;
+      out_data->AddIntAttribute(ax::mojom::IntAttribute::kPosInSet,
+                                std::max(collection_item_info->row_index,
+                                         collection_item_info->column_index));
+      return;
+    }
+  }
+
+  std::string chrome_role;
+  if (GetProperty(AXStringProperty::CHROME_ROLE, &chrome_role)) {
+    ax::mojom::Role role_value = ui::ParseRole(chrome_role.c_str());
+    if (role_value != ax::mojom::Role::kNone) {
+      // The webView and rootWebArea roles differ between Android and Chrome. In
+      // particular, Android includes far fewer attributes which leads to
+      // undesirable behavior. Exclude their direct mapping.
+      out_data->role = (role_value != ax::mojom::Role::kWebView &&
+                        role_value != ax::mojom::Role::kRootWebArea)
+                           ? role_value
+                           : ax::mojom::Role::kGenericContainer;
+      return;
+    }
+  }
+
+#define MAP_ROLE(android_class_name, chrome_role) \
+  if (class_name == android_class_name) {         \
+    out_data->role = chrome_role;                 \
+    return;                                       \
+  }
+
+  // These mappings were taken from accessibility utils (Android -> Chrome) and
+  // BrowserAccessibilityAndroid. They do not completely match the above two
+  // sources.
+  MAP_ROLE(ui::kAXAbsListViewClassname, ax::mojom::Role::kList);
+  MAP_ROLE(ui::kAXButtonClassname, ax::mojom::Role::kButton);
+  MAP_ROLE(ui::kAXCheckBoxClassname, ax::mojom::Role::kCheckBox);
+  MAP_ROLE(ui::kAXCheckedTextViewClassname, ax::mojom::Role::kStaticText);
+  MAP_ROLE(ui::kAXCompoundButtonClassname, ax::mojom::Role::kCheckBox);
+  MAP_ROLE(ui::kAXDialogClassname, ax::mojom::Role::kDialog);
+  MAP_ROLE(ui::kAXEditTextClassname, ax::mojom::Role::kTextField);
+  MAP_ROLE(ui::kAXGridViewClassname, ax::mojom::Role::kTable);
+  MAP_ROLE(ui::kAXHorizontalScrollViewClassname, ax::mojom::Role::kScrollView);
+  MAP_ROLE(ui::kAXImageClassname, ax::mojom::Role::kImage);
+  MAP_ROLE(ui::kAXImageButtonClassname, ax::mojom::Role::kButton);
+  if (GetProperty(AXBooleanProperty::CLICKABLE)) {
+    MAP_ROLE(ui::kAXImageViewClassname, ax::mojom::Role::kButton);
+  } else {
+    MAP_ROLE(ui::kAXImageViewClassname, ax::mojom::Role::kImage);
+  }
+  MAP_ROLE(ui::kAXListViewClassname, ax::mojom::Role::kList);
+  MAP_ROLE(ui::kAXMenuItemClassname, ax::mojom::Role::kMenuItem);
+  MAP_ROLE(ui::kAXPagerClassname, ax::mojom::Role::kGroup);
+  MAP_ROLE(ui::kAXProgressBarClassname, ax::mojom::Role::kProgressIndicator);
+  MAP_ROLE(ui::kAXRadioButtonClassname, ax::mojom::Role::kRadioButton);
+  MAP_ROLE(ui::kAXScrollViewClassname, ax::mojom::Role::kScrollView);
+  MAP_ROLE(ui::kAXSeekBarClassname, ax::mojom::Role::kSlider);
+  MAP_ROLE(ui::kAXSpinnerClassname, ax::mojom::Role::kPopUpButton);
+  MAP_ROLE(ui::kAXSwitchClassname, ax::mojom::Role::kSwitch);
+  MAP_ROLE(ui::kAXTabWidgetClassname, ax::mojom::Role::kTabList);
+  MAP_ROLE(ui::kAXToggleButtonClassname, ax::mojom::Role::kToggleButton);
+  MAP_ROLE(ui::kAXViewClassname, ax::mojom::Role::kGenericContainer);
+  MAP_ROLE(ui::kAXViewGroupClassname, ax::mojom::Role::kGroup);
+
+#undef MAP_ROLE
+
+  std::string text;
+  GetProperty(AXStringProperty::TEXT, &text);
+  if (!text.empty())
+    out_data->role = ax::mojom::Role::kStaticText;
+  else
+    out_data->role = ax::mojom::Role::kGenericContainer;
+}
+
+void AccessibilityNodeInfoDataWrapper::PopulateAXState(
+    ui::AXNodeData* out_data) const {
+#define MAP_STATE(android_boolean_property, chrome_state) \
+  if (GetProperty(android_boolean_property))              \
+    out_data->AddState(chrome_state);
+
+  // These mappings were taken from accessibility utils (Android -> Chrome) and
+  // BrowserAccessibilityAndroid. They do not completely match the above two
+  // sources.
+  MAP_STATE(AXBooleanProperty::EDITABLE, ax::mojom::State::kEditable);
+  MAP_STATE(AXBooleanProperty::FOCUSABLE, ax::mojom::State::kFocusable);
+  MAP_STATE(AXBooleanProperty::MULTI_LINE, ax::mojom::State::kMultiline);
+  MAP_STATE(AXBooleanProperty::PASSWORD, ax::mojom::State::kProtected);
+
+#undef MAP_STATE
+
+  if (GetProperty(AXBooleanProperty::CHECKABLE)) {
+    const bool is_checked = GetProperty(AXBooleanProperty::CHECKED);
+    out_data->SetCheckedState(is_checked ? ax::mojom::CheckedState::kTrue
+                                         : ax::mojom::CheckedState::kFalse);
+  }
+
+  if (!GetProperty(AXBooleanProperty::ENABLED)) {
+    out_data->SetRestriction(ax::mojom::Restriction::kDisabled);
+  }
+
+  if (!GetProperty(AXBooleanProperty::VISIBLE_TO_USER)) {
+    out_data->AddState(ax::mojom::State::kInvisible);
+  }
+}
+
+void AccessibilityNodeInfoDataWrapper::Serialize(
+    ui::AXNodeData* out_data) const {
+  // String properties.
+  int labelled_by = -1;
+
+  // Accessible name computation picks the first non-empty string from content
+  // description, text, labelled by text, or pane title.
+  std::string name;
+  bool has_name = GetProperty(AXStringProperty::CONTENT_DESCRIPTION, &name);
+  if (name.empty())
+    has_name |= GetProperty(AXStringProperty::TEXT, &name);
+  if (name.empty() && GetProperty(AXIntProperty::LABELED_BY, &labelled_by)) {
+    ArcAccessibilityInfoData* labelled_by_node =
+        tree_source_->GetFromId(labelled_by);
+    if (labelled_by_node && labelled_by_node->IsNode()) {
+      ui::AXNodeData labelled_by_data;
+      tree_source_->SerializeNode(labelled_by_node, &labelled_by_data);
+      has_name |= labelled_by_data.GetStringAttribute(
+          ax::mojom::StringAttribute::kName, &name);
+    }
+  }
+  if (name.empty())
+    has_name |= GetProperty(AXStringProperty::PANE_TITLE, &name);
+
+  // If it exists, set tooltip value as descritiption on node.
+  std::string tooltip;
+  if (GetProperty(AXStringProperty::TOOLTIP, &tooltip)) {
+    out_data->AddStringAttribute(ax::mojom::StringAttribute::kDescription,
+                                 tooltip);
+    if (GetProperty(AXStringProperty::TEXT, &name)) {
+      out_data->SetName(name);
+    }
+  }
+
+  if (has_name) {
+    if (out_data->role == ax::mojom::Role::kTextField)
+      out_data->AddStringAttribute(ax::mojom::StringAttribute::kValue, name);
+    else
+      out_data->SetName(name);
+  }
+
+  std::string role_description;
+  if (GetProperty(AXStringProperty::ROLE_DESCRIPTION, &role_description)) {
+    out_data->AddStringAttribute(ax::mojom::StringAttribute::kRoleDescription,
+                                 role_description);
+  }
+
+  if (out_data->role == ax::mojom::Role::kRootWebArea) {
+    std::string package_name;
+    if (GetProperty(AXStringProperty::PACKAGE_NAME, &package_name)) {
+      const std::string& url =
+          base::StringPrintf("%s/%s", package_name.c_str(),
+                             tree_source_->tree_id().ToString().c_str());
+      out_data->AddStringAttribute(ax::mojom::StringAttribute::kUrl, url);
+    }
+  }
+
+  std::string place_holder;
+  if (GetProperty(AXStringProperty::HINT_TEXT, &place_holder)) {
+    out_data->AddStringAttribute(ax::mojom::StringAttribute::kPlaceholder,
+                                 place_holder);
+  }
+
+  // Int properties.
+  int traversal_before = -1, traversal_after = -1;
+  if (GetProperty(AXIntProperty::TRAVERSAL_BEFORE, &traversal_before)) {
+    out_data->AddIntAttribute(ax::mojom::IntAttribute::kPreviousFocusId,
+                              traversal_before);
+  }
+
+  if (GetProperty(AXIntProperty::TRAVERSAL_AFTER, &traversal_after)) {
+    out_data->AddIntAttribute(ax::mojom::IntAttribute::kNextFocusId,
+                              traversal_after);
+  }
+
+  // Boolean properties.
+  PopulateAXState(out_data);
+  if (GetProperty(AXBooleanProperty::SCROLLABLE)) {
+    out_data->AddBoolAttribute(ax::mojom::BoolAttribute::kScrollable, true);
+  }
+  if (GetProperty(AXBooleanProperty::CLICKABLE)) {
+    out_data->AddBoolAttribute(ax::mojom::BoolAttribute::kClickable, true);
+  }
+  if (GetProperty(AXBooleanProperty::SELECTED)) {
+    out_data->AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);
+  }
+
+  // Range info.
+  AXRangeInfoData* range_info = node_ptr_->range_info.get();
+  if (range_info) {
+    out_data->AddFloatAttribute(ax::mojom::FloatAttribute::kValueForRange,
+                                range_info->current);
+    out_data->AddFloatAttribute(ax::mojom::FloatAttribute::kMinValueForRange,
+                                range_info->min);
+    out_data->AddFloatAttribute(ax::mojom::FloatAttribute::kMaxValueForRange,
+                                range_info->max);
+  }
+
+  exo::WMHelper* wm_helper =
+      exo::WMHelper::HasInstance() ? exo::WMHelper::GetInstance() : nullptr;
+
+  // To get bounds of a node which can be passed to AXNodeData.location,
+  // - Root node must exist.
+  // - Window where this tree is attached to need to be focused.
+  if (tree_source_->GetRoot()->GetId() != -1 && wm_helper) {
+    aura::Window* active_window = tree_source_->is_notification()
+                                      ? nullptr
+                                      : wm_helper->GetActiveWindow();
+    const gfx::Rect& local_bounds = tree_source_->GetBounds(
+        tree_source_->GetFromId(GetId()), active_window);
+    out_data->location.SetRect(local_bounds.x(), local_bounds.y(),
+                               local_bounds.width(), local_bounds.height());
+  }
+
+  // Integer properties.
+  int32_t val;
+  if (GetProperty(AXIntProperty::TEXT_SELECTION_START, &val) && val >= 0)
+    out_data->AddIntAttribute(ax::mojom::IntAttribute::kTextSelStart, val);
+
+  if (GetProperty(AXIntProperty::TEXT_SELECTION_END, &val) && val >= 0)
+    out_data->AddIntAttribute(ax::mojom::IntAttribute::kTextSelEnd, val);
+
+  std::vector<int32_t> standard_action_ids;
+  if (GetProperty(AXIntListProperty::STANDARD_ACTION_IDS,
+                  &standard_action_ids)) {
+    for (size_t i = 0; i < standard_action_ids.size(); ++i) {
+      switch (static_cast<AXActionType>(standard_action_ids[i])) {
+        case AXActionType::SCROLL_BACKWARD:
+          out_data->AddAction(ax::mojom::Action::kScrollBackward);
+          break;
+        case AXActionType::SCROLL_FORWARD:
+          out_data->AddAction(ax::mojom::Action::kScrollForward);
+          break;
+        default:
+          // unmapped
+          break;
+      }
+    }
+  }
+
+  // Custom actions.
+  std::vector<int32_t> custom_action_ids;
+  if (GetProperty(AXIntListProperty::CUSTOM_ACTION_IDS, &custom_action_ids)) {
+    std::vector<std::string> custom_action_descriptions;
+
+    CHECK(GetProperty(AXStringListProperty::CUSTOM_ACTION_DESCRIPTIONS,
+                      &custom_action_descriptions));
+    CHECK(!custom_action_ids.empty());
+    CHECK_EQ(custom_action_ids.size(), custom_action_descriptions.size());
+
+    out_data->AddAction(ax::mojom::Action::kCustomAction);
+    out_data->AddIntListAttribute(ax::mojom::IntListAttribute::kCustomActionIds,
+                                  custom_action_ids);
+    out_data->AddStringListAttribute(
+        ax::mojom::StringListAttribute::kCustomActionDescriptions,
+        custom_action_descriptions);
+  }
+}
+
+const std::vector<int32_t>* AccessibilityNodeInfoDataWrapper::GetChildren()
+    const {
+  if (!node_ptr_->int_list_properties)
+    return nullptr;
+  auto it =
+      node_ptr_->int_list_properties->find(AXIntListProperty::CHILD_NODE_IDS);
+  if (it == node_ptr_->int_list_properties->end())
+    return nullptr;
+  return &(it->second);
+}
+
+bool AccessibilityNodeInfoDataWrapper::GetProperty(
+    AXBooleanProperty prop) const {
+  if (!node_ptr_->boolean_properties)
+    return false;
+
+  auto it = node_ptr_->boolean_properties->find(prop);
+  if (it == node_ptr_->boolean_properties->end())
+    return false;
+
+  return it->second;
+}
+
+bool AccessibilityNodeInfoDataWrapper::GetProperty(AXIntProperty prop,
+                                                   int32_t* out_value) const {
+  if (!node_ptr_->int_properties)
+    return false;
+
+  auto it = node_ptr_->int_properties->find(prop);
+  if (it == node_ptr_->int_properties->end())
+    return false;
+
+  *out_value = it->second;
+  return true;
+}
+
+bool AccessibilityNodeInfoDataWrapper::HasProperty(
+    AXStringProperty prop) const {
+  if (!node_ptr_->string_properties)
+    return false;
+
+  auto it = node_ptr_->string_properties->find(prop);
+  return it != node_ptr_->string_properties->end();
+}
+
+bool AccessibilityNodeInfoDataWrapper::GetProperty(
+    AXStringProperty prop,
+    std::string* out_value) const {
+  if (!HasProperty(prop))
+    return false;
+
+  auto it = node_ptr_->string_properties->find(prop);
+  *out_value = it->second;
+  return true;
+}
+
+bool AccessibilityNodeInfoDataWrapper::GetProperty(
+    AXIntListProperty prop,
+    std::vector<int32_t>* out_value) const {
+  if (!node_ptr_->int_list_properties)
+    return false;
+
+  auto it = node_ptr_->int_list_properties->find(prop);
+  if (it == node_ptr_->int_list_properties->end())
+    return false;
+
+  *out_value = it->second;
+  return true;
+}
+
+bool AccessibilityNodeInfoDataWrapper::GetProperty(
+    AXStringListProperty prop,
+    std::vector<std::string>* out_value) const {
+  if (!node_ptr_->string_list_properties)
+    return false;
+
+  auto it = node_ptr_->string_list_properties->find(prop);
+  if (it == node_ptr_->string_list_properties->end())
+    return false;
+
+  *out_value = it->second;
+  return true;
+}
+
+bool AccessibilityNodeInfoDataWrapper::HasCoveringSpan(
+    AXStringProperty prop,
+    mojom::SpanType span_type) const {
+  if (!node_ptr_->spannable_string_properties)
+    return false;
+
+  std::string text;
+  GetProperty(prop, &text);
+  if (text.empty())
+    return false;
+
+  auto span_entries_it = node_ptr_->spannable_string_properties->find(prop);
+  if (span_entries_it == node_ptr_->spannable_string_properties->end())
+    return false;
+
+  for (size_t i = 0; i < span_entries_it->second.size(); ++i) {
+    if (span_entries_it->second[i]->span_type != span_type)
+      continue;
+
+    size_t span_size =
+        span_entries_it->second[i]->end - span_entries_it->second[i]->start;
+    if (span_size == text.size())
+      return true;
+  }
+  return false;
+}
+
+}  // namespace arc
diff --git a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.h b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.h
new file mode 100644
index 0000000..387c6f8
--- /dev/null
+++ b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.h
@@ -0,0 +1,62 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_ARC_ACCESSIBILITY_ACCESSIBILITY_NODE_INFO_DATA_WRAPPER_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_ACCESSIBILITY_ACCESSIBILITY_NODE_INFO_DATA_WRAPPER_H_
+
+#include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_info_data.h"
+#include "ui/accessibility/ax_enum_util.h"
+#include "ui/accessibility/ax_node_data.h"
+
+namespace arc {
+
+class AXTreeSourceArc;
+
+// Wrapper class for an AccessibilityWindowInfoData.
+class AccessibilityNodeInfoDataWrapper : public ArcAccessibilityInfoData {
+ public:
+  explicit AccessibilityNodeInfoDataWrapper(
+      AXTreeSourceArc* tree_source,
+      mojom::AccessibilityNodeInfoData* node);
+
+  // ArcAccessibilityInfoData overrides.
+  bool IsNode() const override;
+  mojom::AccessibilityNodeInfoData* GetNode() const override;
+  mojom::AccessibilityWindowInfoData* GetWindow() const override;
+  int32_t GetId() const override;
+  const gfx::Rect GetBounds() const override;
+  bool IsVisibleToUser() const override;
+  bool IsFocused() const override;
+  bool CanBeAccessibilityFocused() const override;
+  void PopulateAXRole(ui::AXNodeData* out_data) const override;
+  void PopulateAXState(ui::AXNodeData* out_data) const override;
+  void Serialize(ui::AXNodeData* out_data) const override;
+  const std::vector<int32_t>* GetChildren() const override;
+
+  mojom::AccessibilityNodeInfoData* node() { return node_ptr_; }
+
+ private:
+  bool GetProperty(mojom::AccessibilityBooleanProperty prop) const;
+  bool GetProperty(mojom::AccessibilityIntProperty prop,
+                   int32_t* out_value) const;
+  bool HasProperty(mojom::AccessibilityStringProperty prop) const;
+  bool GetProperty(mojom::AccessibilityStringProperty prop,
+                   std::string* out_value) const;
+  bool GetProperty(mojom::AccessibilityIntListProperty prop,
+                   std::vector<int32_t>* out_value) const;
+  bool GetProperty(mojom::AccessibilityStringListProperty prop,
+                   std::vector<std::string>* out_value) const;
+
+  bool HasCoveringSpan(mojom::AccessibilityStringProperty prop,
+                       mojom::SpanType span_type) const;
+
+  AXTreeSourceArc* tree_source_ = nullptr;
+  mojom::AccessibilityNodeInfoData* node_ptr_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(AccessibilityNodeInfoDataWrapper);
+};
+
+}  // namespace arc
+
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_ACCESSIBILITY_ACCESSIBILITY_NODE_INFO_DATA_WRAPPER_H_
diff --git a/chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.cc b/chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.cc
new file mode 100644
index 0000000..c30160e
--- /dev/null
+++ b/chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.cc
@@ -0,0 +1,140 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.h"
+#include "chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h"
+
+namespace arc {
+
+AccessibilityWindowInfoDataWrapper::AccessibilityWindowInfoDataWrapper(
+    AXTreeSourceArc* tree_source,
+    mojom::AccessibilityWindowInfoData* window)
+    : tree_source_(tree_source), window_ptr_(window) {}
+
+bool AccessibilityWindowInfoDataWrapper::IsNode() const {
+  return false;
+}
+
+mojom::AccessibilityNodeInfoData* AccessibilityWindowInfoDataWrapper::GetNode()
+    const {
+  return nullptr;
+}
+
+mojom::AccessibilityWindowInfoData*
+AccessibilityWindowInfoDataWrapper::GetWindow() const {
+  return window_ptr_;
+}
+
+int32_t AccessibilityWindowInfoDataWrapper::GetId() const {
+  return window_ptr_->window_id;
+}
+
+const gfx::Rect AccessibilityWindowInfoDataWrapper::GetBounds() const {
+  return window_ptr_->bounds_in_screen;
+}
+
+bool AccessibilityWindowInfoDataWrapper::IsVisibleToUser() const {
+  // TODO(katie): Calculate this from properties.
+  return true;
+}
+
+bool AccessibilityWindowInfoDataWrapper::IsFocused() const {
+  // TODO(katie): Calculate this from properties.
+  // Is "input focus" the same as focus?
+  // https://developer.android.com/reference/android/view/accessibility/AccessibilityWindowInfo.html#isFocused()
+  return false;
+}
+
+bool AccessibilityWindowInfoDataWrapper::CanBeAccessibilityFocused() const {
+  // TODO(katie): Calculate this for windows.
+  // Can windows have a11y focus?
+  return true;
+}
+
+void AccessibilityWindowInfoDataWrapper::PopulateAXRole(
+    ui::AXNodeData* out_data) const {
+  // TODO(katie): Populate for windows using the window_type enum.
+}
+
+void AccessibilityWindowInfoDataWrapper::PopulateAXState(
+    ui::AXNodeData* out_data) const {
+  // TODO(katie): Populate for windows.
+}
+
+void AccessibilityWindowInfoDataWrapper::Serialize(
+    ui::AXNodeData* out_data) const {
+  // TODO(katie): Serialize for windows.
+  if (!tree_source_->GetRoot()) {
+    return;
+  }
+}
+
+const std::vector<int32_t>* AccessibilityWindowInfoDataWrapper::GetChildren()
+    const {
+  // TODO(katie): Combine the root_node_id with the int_list_properties
+  // of AccessibilityWindowIntListProperty::CHILD_WINDOW_IDS.
+  return nullptr;
+}
+
+bool AccessibilityWindowInfoDataWrapper::GetProperty(
+    mojom::AccessibilityWindowBooleanProperty prop) const {
+  if (!window_ptr_->boolean_properties)
+    return false;
+
+  auto it = window_ptr_->boolean_properties->find(prop);
+  if (it == window_ptr_->boolean_properties->end())
+    return false;
+
+  return it->second;
+}
+
+bool AccessibilityWindowInfoDataWrapper::GetProperty(
+    mojom::AccessibilityWindowIntProperty prop,
+    int32_t* out_value) const {
+  if (!window_ptr_->int_properties)
+    return false;
+
+  auto it = window_ptr_->int_properties->find(prop);
+  if (it == window_ptr_->int_properties->end())
+    return false;
+
+  *out_value = it->second;
+  return true;
+}
+
+bool AccessibilityWindowInfoDataWrapper::HasProperty(
+    mojom::AccessibilityWindowStringProperty prop) const {
+  if (!window_ptr_->string_properties)
+    return false;
+
+  auto it = window_ptr_->string_properties->find(prop);
+  return it != window_ptr_->string_properties->end();
+}
+
+bool AccessibilityWindowInfoDataWrapper::GetProperty(
+    mojom::AccessibilityWindowStringProperty prop,
+    std::string* out_value) const {
+  if (!HasProperty(prop))
+    return false;
+
+  auto it = window_ptr_->string_properties->find(prop);
+  *out_value = it->second;
+  return true;
+}
+
+bool AccessibilityWindowInfoDataWrapper::GetProperty(
+    mojom::AccessibilityWindowIntListProperty prop,
+    std::vector<int32_t>* out_value) const {
+  if (!window_ptr_->int_list_properties)
+    return false;
+
+  auto it = window_ptr_->int_list_properties->find(prop);
+  if (it == window_ptr_->int_list_properties->end())
+    return false;
+
+  *out_value = it->second;
+  return true;
+}
+
+}  // namespace arc
diff --git a/chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.h b/chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.h
new file mode 100644
index 0000000..7145664
--- /dev/null
+++ b/chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.h
@@ -0,0 +1,54 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_ARC_ACCESSIBILITY_ACCESSIBILITY_WINDOW_INFO_DATA_WRAPPER_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_ACCESSIBILITY_ACCESSIBILITY_WINDOW_INFO_DATA_WRAPPER_H_
+
+#include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_info_data.h"
+#include "ui/accessibility/ax_node_data.h"
+
+namespace arc {
+
+class AXTreeSourceArc;
+
+// Wrapper class for an AccessibilityWindowInfoData.
+class AccessibilityWindowInfoDataWrapper : public ArcAccessibilityInfoData {
+ public:
+  explicit AccessibilityWindowInfoDataWrapper(
+      AXTreeSourceArc* tree_source,
+      mojom::AccessibilityWindowInfoData* window);
+
+  // ArcAccessibilityInfoData overrides.
+  bool IsNode() const override;
+  mojom::AccessibilityNodeInfoData* GetNode() const override;
+  mojom::AccessibilityWindowInfoData* GetWindow() const override;
+  int32_t GetId() const override;
+  const gfx::Rect GetBounds() const override;
+  bool IsVisibleToUser() const override;
+  bool IsFocused() const override;
+  bool CanBeAccessibilityFocused() const override;
+  void PopulateAXRole(ui::AXNodeData* out_data) const override;
+  void PopulateAXState(ui::AXNodeData* out_data) const override;
+  void Serialize(ui::AXNodeData* out_data) const override;
+  const std::vector<int32_t>* GetChildren() const override;
+
+ private:
+  bool GetProperty(mojom::AccessibilityWindowBooleanProperty prop) const;
+  bool GetProperty(mojom::AccessibilityWindowIntProperty prop,
+                   int32_t* out_value) const;
+  bool HasProperty(mojom::AccessibilityWindowStringProperty prop) const;
+  bool GetProperty(mojom::AccessibilityWindowStringProperty prop,
+                   std::string* out_value) const;
+  bool GetProperty(mojom::AccessibilityWindowIntListProperty prop,
+                   std::vector<int32_t>* out_value) const;
+
+  AXTreeSourceArc* tree_source_ = nullptr;
+  mojom::AccessibilityWindowInfoData* window_ptr_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(AccessibilityWindowInfoDataWrapper);
+};
+
+}  // namespace arc
+
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_ACCESSIBILITY_ACCESSIBILITY_WINDOW_INFO_DATA_WRAPPER_H_
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_info_data.h b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_info_data.h
new file mode 100644
index 0000000..bb8bd48
--- /dev/null
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_info_data.h
@@ -0,0 +1,45 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_ARC_ACCESSIBILITY_ARC_ACCESSIBILITY_INFO_DATA_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_ACCESSIBILITY_ARC_ACCESSIBILITY_INFO_DATA_H_
+
+#include "components/arc/common/accessibility_helper.mojom.h"
+
+namespace ui {
+struct AXNodeData;
+}  // namespace ui
+
+namespace arc {
+
+// ArcAccessibilityInfoData represents a single ARC++ node or window. This class
+// can be used by AXTreeSourceArc to encapsulate ARC-side information which maps
+// to a single AXNodeData.
+class ArcAccessibilityInfoData {
+ public:
+  virtual ~ArcAccessibilityInfoData() = default;
+
+  // True if this ArcAccessibilityInfoData represents an Android node, false
+  // if it represents an Android window.
+  virtual bool IsNode() const = 0;
+
+  // These getters return nullptr if the class doesn't hold the specified type
+  // of data.
+  virtual mojom::AccessibilityNodeInfoData* GetNode() const = 0;
+  virtual mojom::AccessibilityWindowInfoData* GetWindow() const = 0;
+
+  virtual int32_t GetId() const = 0;
+  virtual const gfx::Rect GetBounds() const = 0;
+  virtual bool IsVisibleToUser() const = 0;
+  virtual bool IsFocused() const = 0;
+  virtual bool CanBeAccessibilityFocused() const = 0;
+  virtual void PopulateAXRole(ui::AXNodeData* out_data) const = 0;
+  virtual void PopulateAXState(ui::AXNodeData* out_data) const = 0;
+  virtual void Serialize(ui::AXNodeData* out_data) const = 0;
+  virtual const std::vector<int32_t>* GetChildren() const = 0;
+};
+
+}  // namespace arc
+
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_ACCESSIBILITY_ARC_ACCESSIBILITY_INFO_DATA_H_
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.cc b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.cc
index a1dcc750..bf1efc2 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.cc
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.cc
@@ -8,78 +8,46 @@
 
 namespace arc {
 
-bool GetProperty(mojom::AccessibilityNodeInfoData* node,
-                 mojom::AccessibilityBooleanProperty prop) {
-  if (!node->boolean_properties)
-    return false;
-
-  auto it = node->boolean_properties->find(prop);
-  if (it == node->boolean_properties->end())
-    return false;
-
-  return it->second;
-}
-
-bool GetProperty(mojom::AccessibilityNodeInfoData* node,
-                 mojom::AccessibilityIntProperty prop,
-                 int32_t* out_value) {
-  if (!node->int_properties)
-    return false;
-
-  auto it = node->int_properties->find(prop);
-  if (it == node->int_properties->end())
-    return false;
-
-  *out_value = it->second;
-  return true;
-}
-
-bool HasProperty(mojom::AccessibilityNodeInfoData* node,
-                 mojom::AccessibilityStringProperty prop) {
-  if (!node->string_properties)
-    return false;
-
-  auto it = node->string_properties->find(prop);
-  return it != node->string_properties->end();
-}
-
-bool GetProperty(mojom::AccessibilityNodeInfoData* node,
-                 mojom::AccessibilityStringProperty prop,
-                 std::string* out_value) {
-  if (!HasProperty(node, prop))
-    return false;
-
-  auto it = node->string_properties->find(prop);
-  *out_value = it->second;
-  return true;
-}
-
-bool GetProperty(mojom::AccessibilityNodeInfoData* node,
-                 mojom::AccessibilityIntListProperty prop,
-                 std::vector<int32_t>* out_value) {
-  if (!node->int_list_properties)
-    return false;
-
-  auto it = node->int_list_properties->find(prop);
-  if (it == node->int_list_properties->end())
-    return false;
-
-  *out_value = it->second;
-  return true;
-}
-
-bool GetProperty(mojom::AccessibilityNodeInfoData* node,
-                 mojom::AccessibilityStringListProperty prop,
-                 std::vector<std::string>* out_value) {
-  if (!node->string_list_properties)
-    return false;
-
-  auto it = node->string_list_properties->find(prop);
-  if (it == node->string_list_properties->end())
-    return false;
-
-  *out_value = it->second;
-  return true;
+ax::mojom::Event ToAXEvent(mojom::AccessibilityEventType arc_event_type) {
+  switch (arc_event_type) {
+    case mojom::AccessibilityEventType::VIEW_FOCUSED:
+    case mojom::AccessibilityEventType::VIEW_ACCESSIBILITY_FOCUSED:
+      return ax::mojom::Event::kFocus;
+    case mojom::AccessibilityEventType::VIEW_CLICKED:
+    case mojom::AccessibilityEventType::VIEW_LONG_CLICKED:
+      return ax::mojom::Event::kClicked;
+    case mojom::AccessibilityEventType::VIEW_TEXT_CHANGED:
+      return ax::mojom::Event::kTextChanged;
+    case mojom::AccessibilityEventType::VIEW_TEXT_SELECTION_CHANGED:
+      return ax::mojom::Event::kTextSelectionChanged;
+    case mojom::AccessibilityEventType::WINDOW_STATE_CHANGED:
+    case mojom::AccessibilityEventType::NOTIFICATION_STATE_CHANGED:
+    case mojom::AccessibilityEventType::WINDOW_CONTENT_CHANGED:
+    case mojom::AccessibilityEventType::WINDOWS_CHANGED:
+      return ax::mojom::Event::kLayoutComplete;
+    case mojom::AccessibilityEventType::VIEW_HOVER_ENTER:
+      return ax::mojom::Event::kHover;
+    case mojom::AccessibilityEventType::ANNOUNCEMENT:
+      return ax::mojom::Event::kAlert;
+    case mojom::AccessibilityEventType::VIEW_SCROLLED:
+      return ax::mojom::Event::kScrollPositionChanged;
+    case mojom::AccessibilityEventType::VIEW_SELECTED:
+    case mojom::AccessibilityEventType::VIEW_HOVER_EXIT:
+    case mojom::AccessibilityEventType::TOUCH_EXPLORATION_GESTURE_START:
+    case mojom::AccessibilityEventType::TOUCH_EXPLORATION_GESTURE_END:
+    case mojom::AccessibilityEventType::
+        VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY:
+    case mojom::AccessibilityEventType::GESTURE_DETECTION_START:
+    case mojom::AccessibilityEventType::GESTURE_DETECTION_END:
+    case mojom::AccessibilityEventType::TOUCH_INTERACTION_START:
+    case mojom::AccessibilityEventType::TOUCH_INTERACTION_END:
+    case mojom::AccessibilityEventType::VIEW_CONTEXT_CLICKED:
+    case mojom::AccessibilityEventType::ASSIST_READING_CONTEXT:
+      return ax::mojom::Event::kChildrenChanged;
+    default:
+      return ax::mojom::Event::kChildrenChanged;
+  }
+  return ax::mojom::Event::kChildrenChanged;
 }
 
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h
index 855afc6..4fcaacbc 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h
@@ -7,35 +7,14 @@
 
 #include <stdint.h>
 
-#include <string>
-#include <vector>
+#include "ui/accessibility/ax_enum_util.h"
 
 namespace arc {
 namespace mojom {
-class AccessibilityNodeInfoData;
-enum class AccessibilityBooleanProperty;
-enum class AccessibilityIntProperty;
-enum class AccessibilityStringProperty;
-enum class AccessibilityIntListProperty;
-enum class AccessibilityStringListProperty;
+enum class AccessibilityEventType;
 }  // namespace mojom
 
-bool GetProperty(mojom::AccessibilityNodeInfoData* node,
-                 mojom::AccessibilityBooleanProperty prop);
-bool GetProperty(mojom::AccessibilityNodeInfoData* node,
-                 mojom::AccessibilityIntProperty prop,
-                 int32_t* out_value);
-bool HasProperty(mojom::AccessibilityNodeInfoData* node,
-                 mojom::AccessibilityStringProperty prop);
-bool GetProperty(mojom::AccessibilityNodeInfoData* node,
-                 mojom::AccessibilityStringProperty prop,
-                 std::string* out_value);
-bool GetProperty(mojom::AccessibilityNodeInfoData* node,
-                 mojom::AccessibilityIntListProperty prop,
-                 std::vector<int32_t>* out_value);
-bool GetProperty(mojom::AccessibilityNodeInfoData* node,
-                 mojom::AccessibilityStringListProperty prop,
-                 std::vector<std::string>* out_value);
+ax::mojom::Event ToAXEvent(mojom::AccessibilityEventType arc_event_type);
 
 }  // namespace arc
 
diff --git a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc
index 593b66de..238b732 100644
--- a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc
+++ b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc
@@ -7,132 +7,25 @@
 #include <string>
 
 #include "base/strings/stringprintf.h"
+#include "chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.h"
+#include "chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.h"
 #include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h"
 #include "chrome/browser/extensions/api/automation_internal/automation_event_router.h"
 #include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h"
 #include "chrome/common/extensions/chrome_extension_messages.h"
-#include "components/exo/wm_helper.h"
-#include "ui/accessibility/ax_enum_util.h"
 #include "ui/accessibility/platform/ax_android_constants.h"
-#include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
-#include "ui/views/focus/focus_manager.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 
 namespace arc {
 
-using AXActionType = mojom::AccessibilityActionType;
-using AXBooleanProperty = mojom::AccessibilityBooleanProperty;
-using AXCollectionInfoData = mojom::AccessibilityCollectionInfoData;
-using AXCollectionItemInfoData = mojom::AccessibilityCollectionItemInfoData;
 using AXEventData = mojom::AccessibilityEventData;
 using AXEventType = mojom::AccessibilityEventType;
 using AXIntListProperty = mojom::AccessibilityIntListProperty;
-using AXIntProperty = mojom::AccessibilityIntProperty;
 using AXNodeInfoData = mojom::AccessibilityNodeInfoData;
-using AXRangeInfoData = mojom::AccessibilityRangeInfoData;
-using AXStringListProperty = mojom::AccessibilityStringListProperty;
-using AXStringProperty = mojom::AccessibilityStringProperty;
-
-ax::mojom::Event ToAXEvent(AXEventType arc_event_type) {
-  switch (arc_event_type) {
-    case AXEventType::VIEW_FOCUSED:
-    case AXEventType::VIEW_ACCESSIBILITY_FOCUSED:
-      return ax::mojom::Event::kFocus;
-    case AXEventType::VIEW_CLICKED:
-    case AXEventType::VIEW_LONG_CLICKED:
-      return ax::mojom::Event::kClicked;
-    case AXEventType::VIEW_TEXT_CHANGED:
-      return ax::mojom::Event::kTextChanged;
-    case AXEventType::VIEW_TEXT_SELECTION_CHANGED:
-      return ax::mojom::Event::kTextSelectionChanged;
-    case AXEventType::WINDOW_STATE_CHANGED:
-    case AXEventType::NOTIFICATION_STATE_CHANGED:
-    case AXEventType::WINDOW_CONTENT_CHANGED:
-    case AXEventType::WINDOWS_CHANGED:
-      return ax::mojom::Event::kLayoutComplete;
-    case AXEventType::VIEW_HOVER_ENTER:
-      return ax::mojom::Event::kHover;
-    case AXEventType::ANNOUNCEMENT:
-      return ax::mojom::Event::kAlert;
-    case AXEventType::VIEW_SCROLLED:
-      return ax::mojom::Event::kScrollPositionChanged;
-    case AXEventType::VIEW_SELECTED:
-    case AXEventType::VIEW_HOVER_EXIT:
-    case AXEventType::TOUCH_EXPLORATION_GESTURE_START:
-    case AXEventType::TOUCH_EXPLORATION_GESTURE_END:
-    case AXEventType::VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY:
-    case AXEventType::GESTURE_DETECTION_START:
-    case AXEventType::GESTURE_DETECTION_END:
-    case AXEventType::TOUCH_INTERACTION_START:
-    case AXEventType::TOUCH_INTERACTION_END:
-    case AXEventType::VIEW_CONTEXT_CLICKED:
-    case AXEventType::ASSIST_READING_CONTEXT:
-      return ax::mojom::Event::kChildrenChanged;
-    default:
-      return ax::mojom::Event::kChildrenChanged;
-  }
-  return ax::mojom::Event::kChildrenChanged;
-}
-
-bool HasCoveringSpan(AXNodeInfoData* data,
-                     AXStringProperty prop,
-                     mojom::SpanType span_type) {
-  if (!data->spannable_string_properties)
-    return false;
-
-  std::string text;
-  GetProperty(data, prop, &text);
-  if (text.empty())
-    return false;
-
-  auto span_entries_it = data->spannable_string_properties->find(prop);
-  if (span_entries_it == data->spannable_string_properties->end())
-    return false;
-
-  for (size_t i = 0; i < span_entries_it->second.size(); ++i) {
-    if (span_entries_it->second[i]->span_type != span_type)
-      continue;
-
-    size_t span_size =
-        span_entries_it->second[i]->end - span_entries_it->second[i]->start;
-    if (span_size == text.size())
-      return true;
-  }
-  return false;
-}
-
-void PopulateAXState(AXNodeInfoData* node, ui::AXNodeData* out_data) {
-#define MAP_STATE(android_boolean_property, chrome_state) \
-  if (GetProperty(node, android_boolean_property))        \
-    out_data->AddState(chrome_state);
-
-  // These mappings were taken from accessibility utils (Android -> Chrome) and
-  // BrowserAccessibilityAndroid. They do not completely match the above two
-  // sources.
-  MAP_STATE(AXBooleanProperty::EDITABLE, ax::mojom::State::kEditable);
-  MAP_STATE(AXBooleanProperty::FOCUSABLE, ax::mojom::State::kFocusable);
-  MAP_STATE(AXBooleanProperty::MULTI_LINE, ax::mojom::State::kMultiline);
-  MAP_STATE(AXBooleanProperty::PASSWORD, ax::mojom::State::kProtected);
-
-#undef MAP_STATE
-
-  if (GetProperty(node, AXBooleanProperty::CHECKABLE)) {
-    const bool is_checked = GetProperty(node, AXBooleanProperty::CHECKED);
-    out_data->SetCheckedState(is_checked ? ax::mojom::CheckedState::kTrue
-                                         : ax::mojom::CheckedState::kFalse);
-  }
-
-  if (!GetProperty(node, AXBooleanProperty::ENABLED)) {
-    out_data->SetRestriction(ax::mojom::Restriction::kDisabled);
-  }
-
-  if (!GetProperty(node, AXBooleanProperty::VISIBLE_TO_USER)) {
-    out_data->AddState(ax::mojom::State::kInvisible);
-  }
-}
+using AXWindowInfoData = mojom::AccessibilityWindowInfoData;
 
 AXTreeSourceArc::AXTreeSourceArc(Delegate* delegate)
     : current_tree_serializer_(new AXTreeArcSerializer(this)),
@@ -207,9 +100,10 @@
     if (parent_map_.find(id) == parent_map_.end() && id != root_id_)
       continue;
     AXNodeInfoData* node = event_data->node_data[i].get();
-    tree_map_[id] = node;
+    tree_map_[id] =
+        std::make_unique<AccessibilityNodeInfoDataWrapper>(this, node);
 
-    if (GetProperty(node, AXBooleanProperty::FOCUSED)) {
+    if (tree_map_[id]->IsFocused()) {
       focused_node_id_ = id;
     }
   }
@@ -221,8 +115,7 @@
     int32_t id = event_data->node_data[i]->id;
     if (parent_map_.find(id) == parent_map_.end() && id != root_id_)
       continue;
-    AXNodeInfoData* node = event_data->node_data[i].get();
-    cached_computed_bounds_[node] = ComputeEnclosingBounds(node);
+    cached_computed_bounds_[id] = ComputeEnclosingBounds(tree_map_[id].get());
   }
 
   ExtensionMsg_AccessibilityEventBundleParams event_bundle;
@@ -261,39 +154,38 @@
   return true;
 }
 
-AXNodeInfoData* AXTreeSourceArc::GetRoot() const {
-  AXNodeInfoData* root = GetFromId(root_id_);
+ArcAccessibilityInfoData* AXTreeSourceArc::GetRoot() const {
+  ArcAccessibilityInfoData* root = GetFromId(root_id_);
   return root;
 }
 
-AXNodeInfoData* AXTreeSourceArc::GetFromId(int32_t id) const {
+ArcAccessibilityInfoData* AXTreeSourceArc::GetFromId(int32_t id) const {
   auto it = tree_map_.find(id);
   if (it == tree_map_.end())
     return nullptr;
-  return it->second;
+  return it->second.get();
 }
 
-int32_t AXTreeSourceArc::GetId(AXNodeInfoData* node) const {
-  if (!node)
+int32_t AXTreeSourceArc::GetId(ArcAccessibilityInfoData* info_data) const {
+  if (!info_data)
     return -1;
-  return node->id;
+  return info_data->GetId();
 }
 
 void AXTreeSourceArc::GetChildren(
-    AXNodeInfoData* node,
-    std::vector<AXNodeInfoData*>* out_children) const {
-  if (!node || !node->int_list_properties)
+    ArcAccessibilityInfoData* info_data,
+    std::vector<ArcAccessibilityInfoData*>* out_children) const {
+  if (!info_data)
     return;
-
-  auto it = node->int_list_properties->find(AXIntListProperty::CHILD_NODE_IDS);
-  if (it == node->int_list_properties->end())
+  const std::vector<int32_t>* children = info_data->GetChildren();
+  if (!children)
     return;
 
   std::map<int32_t, size_t> id_to_index;
-  for (size_t i = 0; i < it->second.size(); ++i) {
-    AXNodeInfoData* child = GetFromId(it->second[i]);
+  for (size_t i = 0; i < children->size(); ++i) {
+    ArcAccessibilityInfoData* child = GetFromId((*children)[i]);
     out_children->push_back(child);
-    id_to_index[child->id] = i;
+    id_to_index[child->GetId()] = i;
   }
 
   // Sort children based on their enclosing bounding rectangles, based on their
@@ -303,8 +195,10 @@
               auto left_bounds = ComputeEnclosingBounds(left);
               auto right_bounds = ComputeEnclosingBounds(right);
 
-              if (left_bounds.IsEmpty() || right_bounds.IsEmpty())
-                return id_to_index.at(left->id) < id_to_index.at(right->id);
+              if (left_bounds.IsEmpty() || right_bounds.IsEmpty()) {
+                return id_to_index.at(left->GetId()) <
+                       id_to_index.at(right->GetId());
+              }
 
               // Top to bottom sort (non-overlapping).
               if (!left_bounds.Intersects(right_bounds))
@@ -332,215 +226,58 @@
                 return width_difference > 0;
 
               // The rects are equal.
-              return id_to_index.at(left->id) < id_to_index.at(right->id);
+              return id_to_index.at(left->GetId()) <
+                     id_to_index.at(right->GetId());
             });
 }
 
-AXNodeInfoData* AXTreeSourceArc::GetParent(AXNodeInfoData* node) const {
-  if (!node)
+ArcAccessibilityInfoData* AXTreeSourceArc::GetParent(
+    ArcAccessibilityInfoData* info_data) const {
+  if (!info_data)
     return nullptr;
-  auto it = parent_map_.find(node->id);
+  auto it = parent_map_.find(info_data->GetId());
   if (it != parent_map_.end())
     return GetFromId(it->second);
   return nullptr;
 }
 
-bool AXTreeSourceArc::IsValid(AXNodeInfoData* node) const {
-  return node;
+bool AXTreeSourceArc::IsValid(ArcAccessibilityInfoData* info_data) const {
+  return info_data;
 }
 
-bool AXTreeSourceArc::IsEqual(AXNodeInfoData* node1,
-                              AXNodeInfoData* node2) const {
-  if (!node1 || !node2)
+bool AXTreeSourceArc::IsEqual(ArcAccessibilityInfoData* info_data1,
+                              ArcAccessibilityInfoData* info_data2) const {
+  if (!info_data1 || !info_data2)
     return false;
-  return node1->id == node2->id;
+  return info_data1->GetId() == info_data2->GetId();
 }
 
-AXNodeInfoData* AXTreeSourceArc::GetNull() const {
+ArcAccessibilityInfoData* AXTreeSourceArc::GetNull() const {
   return nullptr;
 }
 
-void AXTreeSourceArc::SerializeNode(AXNodeInfoData* node,
+void AXTreeSourceArc::SerializeNode(ArcAccessibilityInfoData* info_data,
                                     ui::AXNodeData* out_data) const {
-  if (!node)
+  if (!info_data)
     return;
 
-  int32_t id = node->id;
+  int32_t id = info_data->GetId();
   out_data->id = id;
   if (id == root_id_)
     out_data->role = ax::mojom::Role::kRootWebArea;
   else
-    PopulateAXRole(node, out_data);
+    info_data->PopulateAXRole(out_data);
 
-  using AXIntListProperty = AXIntListProperty;
-  using AXIntProperty = AXIntProperty;
-  using AXStringListProperty = AXStringListProperty;
-  using AXStringProperty = AXStringProperty;
-
-  // String properties.
-  int labelled_by = -1;
-
-  // Accessible name computation picks the first non-empty string from content
-  // description, text, labelled by text, or pane title.
-  std::string name;
-  bool has_name =
-      GetProperty(node, AXStringProperty::CONTENT_DESCRIPTION, &name);
-  if (name.empty())
-    has_name |= GetProperty(node, AXStringProperty::TEXT, &name);
-  if (name.empty() &&
-      GetProperty(node, AXIntProperty::LABELED_BY, &labelled_by)) {
-    AXNodeInfoData* labelled_by_node = GetFromId(labelled_by);
-    if (labelled_by_node) {
-      ui::AXNodeData labelled_by_data;
-      SerializeNode(labelled_by_node, &labelled_by_data);
-      has_name |= labelled_by_data.GetStringAttribute(
-          ax::mojom::StringAttribute::kName, &name);
-    }
-  }
-  if (name.empty())
-    has_name |= GetProperty(node, AXStringProperty::PANE_TITLE, &name);
-
-  // If it exists, set tooltip value as descritiption on node.
-  std::string tooltip;
-  if (GetProperty(node, AXStringProperty::TOOLTIP, &tooltip)) {
-    out_data->AddStringAttribute(ax::mojom::StringAttribute::kDescription,
-                                 tooltip);
-    if (GetProperty(node, AXStringProperty::TEXT, &name)) {
-      out_data->SetName(name);
-    }
-  }
-
-  if (has_name) {
-    if (out_data->role == ax::mojom::Role::kTextField)
-      out_data->AddStringAttribute(ax::mojom::StringAttribute::kValue, name);
-    else
-      out_data->SetName(name);
-  }
-
-  std::string role_description;
-  if (GetProperty(node, AXStringProperty::ROLE_DESCRIPTION,
-                  &role_description)) {
-    out_data->AddStringAttribute(ax::mojom::StringAttribute::kRoleDescription,
-                                 role_description);
-  }
-
-  if (out_data->role == ax::mojom::Role::kRootWebArea) {
-    std::string package_name;
-    if (GetProperty(node, AXStringProperty::PACKAGE_NAME, &package_name)) {
-      const std::string& url = base::StringPrintf("%s/%s", package_name.c_str(),
-                                                  tree_id().ToString().c_str());
-      out_data->AddStringAttribute(ax::mojom::StringAttribute::kUrl, url);
-    }
-  }
-
-  std::string place_holder;
-  if (GetProperty(node, AXStringProperty::HINT_TEXT, &place_holder)) {
-    out_data->AddStringAttribute(ax::mojom::StringAttribute::kPlaceholder,
-                                 place_holder);
-  }
-
-  // Int properties.
-  int traversal_before = -1, traversal_after = -1;
-  if (GetProperty(node, AXIntProperty::TRAVERSAL_BEFORE, &traversal_before)) {
-    out_data->AddIntAttribute(ax::mojom::IntAttribute::kPreviousFocusId,
-                              traversal_before);
-  }
-
-  if (GetProperty(node, AXIntProperty::TRAVERSAL_AFTER, &traversal_after)) {
-    out_data->AddIntAttribute(ax::mojom::IntAttribute::kNextFocusId,
-                              traversal_after);
-  }
-
-  // Boolean properties.
-  PopulateAXState(node, out_data);
-  if (GetProperty(node, AXBooleanProperty::SCROLLABLE)) {
-    out_data->AddBoolAttribute(ax::mojom::BoolAttribute::kScrollable, true);
-  }
-  if (GetProperty(node, AXBooleanProperty::CLICKABLE)) {
-    out_data->AddBoolAttribute(ax::mojom::BoolAttribute::kClickable, true);
-  }
-  if (GetProperty(node, AXBooleanProperty::SELECTED)) {
-    out_data->AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);
-  }
-
-  // Range info.
-  AXRangeInfoData* range_info = node->range_info.get();
-  if (range_info) {
-    out_data->AddFloatAttribute(ax::mojom::FloatAttribute::kValueForRange,
-                                range_info->current);
-    out_data->AddFloatAttribute(ax::mojom::FloatAttribute::kMinValueForRange,
-                                range_info->min);
-    out_data->AddFloatAttribute(ax::mojom::FloatAttribute::kMaxValueForRange,
-                                range_info->max);
-  }
-
-  exo::WMHelper* wm_helper =
-      exo::WMHelper::HasInstance() ? exo::WMHelper::GetInstance() : nullptr;
-
-  // To get bounds of a node which can be passed to AXNodeData.location,
-  // - Root node must exist.
-  // - Window where this tree is attached to need to be focused.
-  if (root_id_ != -1 && wm_helper) {
-    aura::Window* active_window =
-        is_notification_ ? nullptr : wm_helper->GetActiveWindow();
-    const gfx::Rect& local_bounds = GetBounds(node, active_window);
-    out_data->location.SetRect(local_bounds.x(), local_bounds.y(),
-                               local_bounds.width(), local_bounds.height());
-  }
-
-  // Integer properties.
-  int32_t val;
-  if (GetProperty(node, AXIntProperty::TEXT_SELECTION_START, &val) && val >= 0)
-    out_data->AddIntAttribute(ax::mojom::IntAttribute::kTextSelStart, val);
-
-  if (GetProperty(node, AXIntProperty::TEXT_SELECTION_END, &val) && val >= 0)
-    out_data->AddIntAttribute(ax::mojom::IntAttribute::kTextSelEnd, val);
-
-  std::vector<int32_t> standard_action_ids;
-  if (GetProperty(node, AXIntListProperty::STANDARD_ACTION_IDS,
-                  &standard_action_ids)) {
-    for (size_t i = 0; i < standard_action_ids.size(); ++i) {
-      switch (static_cast<AXActionType>(standard_action_ids[i])) {
-        case AXActionType::SCROLL_BACKWARD:
-          out_data->AddAction(ax::mojom::Action::kScrollBackward);
-          break;
-        case AXActionType::SCROLL_FORWARD:
-          out_data->AddAction(ax::mojom::Action::kScrollForward);
-          break;
-        default:
-          // unmapped
-          break;
-      }
-    }
-  }
-
-  // Custom actions.
-  std::vector<int32_t> custom_action_ids;
-  if (GetProperty(node, AXIntListProperty::CUSTOM_ACTION_IDS,
-                  &custom_action_ids)) {
-    std::vector<std::string> custom_action_descriptions;
-
-    CHECK(GetProperty(node, AXStringListProperty::CUSTOM_ACTION_DESCRIPTIONS,
-                      &custom_action_descriptions));
-    CHECK(!custom_action_ids.empty());
-    CHECK_EQ(custom_action_ids.size(), custom_action_descriptions.size());
-
-    out_data->AddAction(ax::mojom::Action::kCustomAction);
-    out_data->AddIntListAttribute(ax::mojom::IntListAttribute::kCustomActionIds,
-                                  custom_action_ids);
-    out_data->AddStringListAttribute(
-        ax::mojom::StringListAttribute::kCustomActionDescriptions,
-        custom_action_descriptions);
-  }
+  info_data->Serialize(out_data);
 }
 
-const gfx::Rect AXTreeSourceArc::GetBounds(AXNodeInfoData* node,
+const gfx::Rect AXTreeSourceArc::GetBounds(ArcAccessibilityInfoData* info_data,
                                            aura::Window* active_window) const {
   DCHECK_NE(root_id_, -1);
 
-  gfx::Rect node_bounds = node->bounds_in_screen;
+  gfx::Rect node_bounds = info_data->GetBounds();
 
-  if (active_window && node->id == root_id_) {
+  if (active_window && info_data->GetId() == root_id_) {
     // Top level window returns its bounds in dip.
     aura::Window* toplevel_window = active_window->GetToplevelWindow();
     float scale = toplevel_window->layer()->device_scale_factor();
@@ -575,62 +312,43 @@
   }
 
   // Bounds of non-root node is relative to its tree's root.
-  gfx::Rect root_bounds = GetFromId(root_id_)->bounds_in_screen;
+  gfx::Rect root_bounds = GetFromId(root_id_)->GetBounds();
   node_bounds.Offset(-1 * root_bounds.x(), -1 * root_bounds.y());
   return node_bounds;
 }
 
-gfx::Rect AXTreeSourceArc::ComputeEnclosingBounds(AXNodeInfoData* node) const {
+gfx::Rect AXTreeSourceArc::ComputeEnclosingBounds(
+    ArcAccessibilityInfoData* info_data) const {
   gfx::Rect computed_bounds;
-
-  // Exit early if |node| is invisible.
-  if (!GetProperty(node, AXBooleanProperty::VISIBLE_TO_USER))
+  // Exit early if the node or window is invisible.
+  if (!info_data->IsVisibleToUser())
     return computed_bounds;
 
-  ComputeEnclosingBoundsInternal(node, computed_bounds);
+  ComputeEnclosingBoundsInternal(info_data, computed_bounds);
   return computed_bounds;
 }
 
 void AXTreeSourceArc::ComputeEnclosingBoundsInternal(
-    AXNodeInfoData* node,
+    ArcAccessibilityInfoData* info_data,
     gfx::Rect& computed_bounds) const {
-  auto cached_bounds = cached_computed_bounds_.find(node);
+  auto cached_bounds = cached_computed_bounds_.find(info_data->GetId());
   if (cached_bounds != cached_computed_bounds_.end()) {
     computed_bounds.Union(cached_bounds->second);
     return;
   }
 
-  if (!GetProperty(node, AXBooleanProperty::VISIBLE_TO_USER))
+  if (!info_data->IsVisibleToUser())
     return;
-
-  // Only consider nodes that can possibly be accessibility focused. In Chrome,
-  // this means:
-  // a node with a non-generic role and
-  // actionable nodes, or
-  // top level scrollables with a name
-  ui::AXNodeData data;
-  PopulateAXRole(node, &data);
-  if ((data.role != ax::mojom::Role::kGenericContainer &&
-       data.role != ax::mojom::Role::kGroup) &&
-      (GetProperty(node, AXBooleanProperty::FOCUSABLE) ||
-       GetProperty(node, AXBooleanProperty::CLICKABLE) ||
-       GetProperty(node, AXBooleanProperty::FOCUSABLE) ||
-       GetProperty(node, AXBooleanProperty::CHECKABLE) ||
-       (HasProperty(node, AXStringProperty::TEXT) &&
-        GetProperty(node, AXBooleanProperty::SCROLLABLE)))) {
-    computed_bounds.Union(node->bounds_in_screen);
+  if (info_data->CanBeAccessibilityFocused()) {
+    // Only consider nodes that can possibly be accessibility focused.
+    computed_bounds.Union(info_data->GetBounds());
     return;
   }
-
-  if (!node->int_list_properties)
+  const std::vector<int32_t>* children = info_data->GetChildren();
+  if (!children)
     return;
-
-  auto it = node->int_list_properties->find(AXIntListProperty::CHILD_NODE_IDS);
-  if (it == node->int_list_properties->end())
-    return;
-
-  for (size_t i = 0; i < it->second.size(); ++i) {
-    ComputeEnclosingBoundsInternal(GetFromId(it->second[i]), computed_bounds);
+  for (size_t i = 0; i < children->size(); ++i) {
+    ComputeEnclosingBoundsInternal(GetFromId((*children)[i]), computed_bounds);
   }
 
   return;
@@ -655,162 +373,4 @@
   router->DispatchTreeDestroyedEvent(tree_id(), nullptr);
 }
 
-void AXTreeSourceArc::PopulateAXRole(AXNodeInfoData* node,
-                                     ui::AXNodeData* out_data) const {
-  std::string class_name;
-  if (GetProperty(node, AXStringProperty::CLASS_NAME, &class_name)) {
-    out_data->AddStringAttribute(ax::mojom::StringAttribute::kClassName,
-                                 class_name);
-  }
-
-  if (!GetProperty(node, AXBooleanProperty::IMPORTANCE)) {
-    out_data->role = ax::mojom::Role::kIgnored;
-    return;
-  }
-
-  if (GetProperty(node, AXBooleanProperty::EDITABLE)) {
-    out_data->role = ax::mojom::Role::kTextField;
-    return;
-  }
-
-  if (GetProperty(node, AXBooleanProperty::HEADING)) {
-    out_data->role = ax::mojom::Role::kHeading;
-    return;
-  }
-
-  if (HasCoveringSpan(node, AXStringProperty::TEXT, mojom::SpanType::URL) ||
-      HasCoveringSpan(node, AXStringProperty::CONTENT_DESCRIPTION,
-                      mojom::SpanType::URL)) {
-    out_data->role = ax::mojom::Role::kLink;
-    return;
-  }
-
-  AXCollectionInfoData* collection_info = node->collection_info.get();
-  if (collection_info) {
-    if (collection_info->row_count > 1 && collection_info->column_count > 1) {
-      out_data->role = ax::mojom::Role::kGrid;
-      out_data->AddIntAttribute(ax::mojom::IntAttribute::kTableRowCount,
-                                collection_info->row_count);
-      out_data->AddIntAttribute(ax::mojom::IntAttribute::kTableColumnCount,
-                                collection_info->column_count);
-      return;
-    }
-
-    if (collection_info->row_count == 1 || collection_info->column_count == 1) {
-      out_data->role = ax::mojom::Role::kList;
-      out_data->AddIntAttribute(
-          ax::mojom::IntAttribute::kSetSize,
-          std::max(collection_info->row_count, collection_info->column_count));
-      return;
-    }
-  }
-
-  AXCollectionItemInfoData* collection_item_info =
-      node->collection_item_info.get();
-  if (collection_item_info) {
-    if (collection_item_info->is_heading) {
-      out_data->role = ax::mojom::Role::kHeading;
-      return;
-    }
-
-    // In order to properly resolve the role of this node, a collection item, we
-    // need additional information contained only in the CollectionInfo. The
-    // CollectionInfo should be an ancestor of this node.
-    AXCollectionInfoData* collection_info = nullptr;
-    for (AXNodeInfoData* container = node; container;) {
-      if (container->collection_info.get()) {
-        collection_info = container->collection_info.get();
-        break;
-      }
-      const auto parent_it = parent_map_.find(container->id);
-
-      // Not within a container.
-      if (parent_it == parent_map_.end())
-        break;
-
-      container = GetFromId(parent_it->second);
-    }
-
-    if (collection_info) {
-      if (collection_info->row_count > 1 && collection_info->column_count > 1) {
-        out_data->role = ax::mojom::Role::kCell;
-        out_data->AddIntAttribute(ax::mojom::IntAttribute::kTableRowIndex,
-                                  collection_item_info->row_index);
-        out_data->AddIntAttribute(ax::mojom::IntAttribute::kTableColumnIndex,
-                                  collection_item_info->column_index);
-        return;
-      }
-
-      out_data->role = ax::mojom::Role::kListItem;
-      out_data->AddIntAttribute(ax::mojom::IntAttribute::kPosInSet,
-                                std::max(collection_item_info->row_index,
-                                         collection_item_info->column_index));
-      return;
-    }
-  }
-
-  std::string chrome_role;
-  if (GetProperty(node, AXStringProperty::CHROME_ROLE, &chrome_role)) {
-    ax::mojom::Role role_value = ui::ParseRole(chrome_role.c_str());
-    if (role_value != ax::mojom::Role::kNone) {
-      // The webView and rootWebArea roles differ between Android and Chrome. In
-      // particular, Android includes far fewer attributes which leads to
-      // undesirable behavior. Exclude their direct mapping.
-      out_data->role = (role_value != ax::mojom::Role::kWebView &&
-                        role_value != ax::mojom::Role::kRootWebArea)
-                           ? role_value
-                           : ax::mojom::Role::kGenericContainer;
-      return;
-    }
-  }
-
-#define MAP_ROLE(android_class_name, chrome_role) \
-  if (class_name == android_class_name) {         \
-    out_data->role = chrome_role;                 \
-    return;                                       \
-  }
-
-  // These mappings were taken from accessibility utils (Android -> Chrome) and
-  // BrowserAccessibilityAndroid. They do not completely match the above two
-  // sources.
-  MAP_ROLE(ui::kAXAbsListViewClassname, ax::mojom::Role::kList);
-  MAP_ROLE(ui::kAXButtonClassname, ax::mojom::Role::kButton);
-  MAP_ROLE(ui::kAXCheckBoxClassname, ax::mojom::Role::kCheckBox);
-  MAP_ROLE(ui::kAXCheckedTextViewClassname, ax::mojom::Role::kStaticText);
-  MAP_ROLE(ui::kAXCompoundButtonClassname, ax::mojom::Role::kCheckBox);
-  MAP_ROLE(ui::kAXDialogClassname, ax::mojom::Role::kDialog);
-  MAP_ROLE(ui::kAXEditTextClassname, ax::mojom::Role::kTextField);
-  MAP_ROLE(ui::kAXGridViewClassname, ax::mojom::Role::kTable);
-  MAP_ROLE(ui::kAXHorizontalScrollViewClassname, ax::mojom::Role::kScrollView);
-  MAP_ROLE(ui::kAXImageClassname, ax::mojom::Role::kImage);
-  MAP_ROLE(ui::kAXImageButtonClassname, ax::mojom::Role::kButton);
-  if (GetProperty(node, AXBooleanProperty::CLICKABLE)) {
-    MAP_ROLE(ui::kAXImageViewClassname, ax::mojom::Role::kButton);
-  } else {
-    MAP_ROLE(ui::kAXImageViewClassname, ax::mojom::Role::kImage);
-  }
-  MAP_ROLE(ui::kAXListViewClassname, ax::mojom::Role::kList);
-  MAP_ROLE(ui::kAXMenuItemClassname, ax::mojom::Role::kMenuItem);
-  MAP_ROLE(ui::kAXPagerClassname, ax::mojom::Role::kGroup);
-  MAP_ROLE(ui::kAXProgressBarClassname, ax::mojom::Role::kProgressIndicator);
-  MAP_ROLE(ui::kAXRadioButtonClassname, ax::mojom::Role::kRadioButton);
-  MAP_ROLE(ui::kAXScrollViewClassname, ax::mojom::Role::kScrollView);
-  MAP_ROLE(ui::kAXSeekBarClassname, ax::mojom::Role::kSlider);
-  MAP_ROLE(ui::kAXSpinnerClassname, ax::mojom::Role::kPopUpButton);
-  MAP_ROLE(ui::kAXSwitchClassname, ax::mojom::Role::kSwitch);
-  MAP_ROLE(ui::kAXTabWidgetClassname, ax::mojom::Role::kTabList);
-  MAP_ROLE(ui::kAXToggleButtonClassname, ax::mojom::Role::kToggleButton);
-  MAP_ROLE(ui::kAXViewClassname, ax::mojom::Role::kGenericContainer);
-  MAP_ROLE(ui::kAXViewGroupClassname, ax::mojom::Role::kGroup);
-
-#undef MAP_ROLE
-
-  std::string text;
-  GetProperty(node, AXStringProperty::TEXT, &text);
-  if (!text.empty())
-    out_data->role = ax::mojom::Role::kStaticText;
-  else
-    out_data->role = ax::mojom::Role::kGenericContainer;
-}
-
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h
index 2cf843917a..c1aeaba 100644
--- a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h
+++ b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h
@@ -9,6 +9,7 @@
 #include <memory>
 #include <vector>
 
+#include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_info_data.h"
 #include "components/arc/common/accessibility_helper.mojom.h"
 #include "ui/accessibility/ax_host_delegate.h"
 #include "ui/accessibility/ax_node.h"
@@ -25,17 +26,14 @@
 namespace arc {
 class AXTreeSourceArcTest;
 
-using AXTreeArcSerializer =
-    ui::AXTreeSerializer<mojom::AccessibilityNodeInfoData*,
-                         ui::AXNodeData,
-                         ui::AXTreeData>;
+using AXTreeArcSerializer = ui::
+    AXTreeSerializer<ArcAccessibilityInfoData*, ui::AXNodeData, ui::AXTreeData>;
 
 // This class represents the accessibility tree from the focused ARC window.
-class AXTreeSourceArc
-    : public ui::AXTreeSource<mojom::AccessibilityNodeInfoData*,
-                              ui::AXNodeData,
-                              ui::AXTreeData>,
-      public ui::AXHostDelegate {
+class AXTreeSourceArc : public ui::AXTreeSource<ArcAccessibilityInfoData*,
+                                                ui::AXNodeData,
+                                                ui::AXTreeData>,
+                        public ui::AXHostDelegate {
  public:
   class Delegate {
    public:
@@ -60,25 +58,14 @@
   // Gets the window id of this tree.
   int32_t window_id() const { return window_id_; }
 
- private:
-  friend class arc::AXTreeSourceArcTest;
-  class FocusStealer;
-
-  // AXTreeSource overrides.
-  mojom::AccessibilityNodeInfoData* GetRoot() const override;
-  mojom::AccessibilityNodeInfoData* GetFromId(int32_t id) const override;
-  int32_t GetId(mojom::AccessibilityNodeInfoData* node) const override;
-  void GetChildren(mojom::AccessibilityNodeInfoData* node,
-                   std::vector<mojom::AccessibilityNodeInfoData*>* out_children)
-      const override;
-  mojom::AccessibilityNodeInfoData* GetParent(
-      mojom::AccessibilityNodeInfoData* node) const override;
-  bool IsValid(mojom::AccessibilityNodeInfoData* node) const override;
-  bool IsEqual(mojom::AccessibilityNodeInfoData* node1,
-               mojom::AccessibilityNodeInfoData* node2) const override;
-  mojom::AccessibilityNodeInfoData* GetNull() const override;
-  void SerializeNode(mojom::AccessibilityNodeInfoData* node,
+  // AXTreeSource overrides used by ArcAccessibilityInfoData subclasses.
+  // TODO(katie): should these be "friended" or "protected" instead?
+  ArcAccessibilityInfoData* GetRoot() const override;
+  ArcAccessibilityInfoData* GetFromId(int32_t id) const override;
+  void SerializeNode(ArcAccessibilityInfoData* node,
                      ui::AXNodeData* out_data) const override;
+  ArcAccessibilityInfoData* GetParent(
+      ArcAccessibilityInfoData* node) const override;
 
   // Returns bounds of a node which can be passed to AXNodeData.location. Bounds
   // are returned in the following coordinates depending on whether it's root or
@@ -87,16 +74,31 @@
   // - Non-root node is relative to the root node of this tree.
   //
   // focused_window is nullptr for notification.
-  const gfx::Rect GetBounds(mojom::AccessibilityNodeInfoData* node,
+  const gfx::Rect GetBounds(ArcAccessibilityInfoData* node,
                             aura::Window* focused_window) const;
 
+  bool is_notification() { return is_notification_; }
+
+ private:
+  friend class arc::AXTreeSourceArcTest;
+  class FocusStealer;
+
+  // AXTreeSource overrides.
+  int32_t GetId(ArcAccessibilityInfoData* node) const override;
+  void GetChildren(
+      ArcAccessibilityInfoData* node,
+      std::vector<ArcAccessibilityInfoData*>* out_children) const override;
+  bool IsValid(ArcAccessibilityInfoData* node) const override;
+  bool IsEqual(ArcAccessibilityInfoData* node1,
+               ArcAccessibilityInfoData* node2) const override;
+  ArcAccessibilityInfoData* GetNull() const override;
+
   // Computes the smallest rect that encloses all of the descendants of |node|.
-  gfx::Rect ComputeEnclosingBounds(
-      mojom::AccessibilityNodeInfoData* node) const;
+  gfx::Rect ComputeEnclosingBounds(ArcAccessibilityInfoData* node) const;
 
   // Helper to recursively compute bounds for |node|. Returns true if non-empty
   // bounds were encountered.
-  void ComputeEnclosingBoundsInternal(mojom::AccessibilityNodeInfoData* node,
+  void ComputeEnclosingBoundsInternal(ArcAccessibilityInfoData* node,
                                       gfx::Rect& computed_bounds) const;
 
   // AXHostDelegate overrides.
@@ -105,11 +107,10 @@
   // Resets tree state.
   void Reset();
 
-  void PopulateAXRole(mojom::AccessibilityNodeInfoData* node,
-                      ui::AXNodeData* out_data) const;
+  // Maps an ArcAccessibilityInfoData ID to its tree data.
+  std::map<int32_t, std::unique_ptr<ArcAccessibilityInfoData>> tree_map_;
 
-  // Maps an AccessibilityNodeInfo to its tree data.
-  std::map<int32_t, mojom::AccessibilityNodeInfoData*> tree_map_;
+  // Maps an ArcAccessibilityInfoData ID to its parent.
   std::map<int32_t, int32_t> parent_map_;
   std::unique_ptr<AXTreeArcSerializer> current_tree_serializer_;
   int32_t root_id_;
@@ -121,8 +122,10 @@
   // delegate is valid during the lifetime of this tree.
   const Delegate* const delegate_;
   std::string package_name_;
-  std::map<mojom::AccessibilityNodeInfoData*, gfx::Rect>
-      cached_computed_bounds_;
+
+  // Mapping from ArcAccessibilityInfoData ID to its cached computed bounds.
+  // This simplifies bounds calculations.
+  std::map<int32_t, gfx::Rect> cached_computed_bounds_;
 
   DISALLOW_COPY_AND_ASSIGN(AXTreeSourceArc);
 };
diff --git a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
index b5352b8a..e0ed9ce5 100644
--- a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
+++ b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h"
 
+#include "chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.h"
 #include "components/arc/common/accessibility_helper.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/accessibility/platform/ax_android_constants.h"
@@ -65,19 +66,22 @@
     tree_->NotifyAccessibilityEvent(event_data);
   }
 
-  void CallGetChildren(AXNodeInfoData* node,
-                       std::vector<AXNodeInfoData*>* out_children) const {
-    tree_->GetChildren(node, out_children);
+  void CallGetChildren(
+      mojom::AccessibilityNodeInfoData* node,
+      std::vector<ArcAccessibilityInfoData*>* out_children) const {
+    AccessibilityNodeInfoDataWrapper node_data(tree_.get(), node);
+    tree_->GetChildren(&node_data, out_children);
   }
 
-  void CallSerializeNode(AXNodeInfoData* node,
+  void CallSerializeNode(mojom::AccessibilityNodeInfoData* node,
                          std::unique_ptr<ui::AXNodeData>* out_data) const {
     ASSERT_TRUE(out_data);
+    AccessibilityNodeInfoDataWrapper node_data(tree_.get(), node);
     *out_data = std::make_unique<ui::AXNodeData>();
-    tree_->SerializeNode(node, out_data->get());
+    tree_->SerializeNode(&node_data, out_data->get());
   }
 
-  mojom::AccessibilityNodeInfoData* CallGetFromId(int32_t id) const {
+  ArcAccessibilityInfoData* CallGetFromId(int32_t id) const {
     return tree_->GetFromId(id);
   }
 
@@ -123,11 +127,11 @@
 
   // Trigger an update which refreshes the computed bounds used for reordering.
   CallNotifyAccessibilityEvent(event.get());
-  std::vector<AXNodeInfoData*> top_to_bottom;
+  std::vector<ArcAccessibilityInfoData*> top_to_bottom;
   CallGetChildren(root, &top_to_bottom);
   ASSERT_EQ(2U, top_to_bottom.size());
-  ASSERT_EQ(2, top_to_bottom[0]->id);
-  ASSERT_EQ(1, top_to_bottom[1]->id);
+  EXPECT_EQ(2, top_to_bottom[0]->GetId());
+  EXPECT_EQ(1, top_to_bottom[1]->GetId());
 
   // Non-overlapping, top to bottom.
   button1->bounds_in_screen = gfx::Rect(0, 0, 50, 50);
@@ -136,18 +140,18 @@
   top_to_bottom.clear();
   CallGetChildren(event->node_data[0].get(), &top_to_bottom);
   ASSERT_EQ(2U, top_to_bottom.size());
-  ASSERT_EQ(1, top_to_bottom[0]->id);
-  ASSERT_EQ(2, top_to_bottom[1]->id);
+  EXPECT_EQ(1, top_to_bottom[0]->GetId());
+  EXPECT_EQ(2, top_to_bottom[1]->GetId());
 
   // Overlapping; right to left.
   button1->bounds_in_screen = gfx::Rect(101, 100, 99, 100);
   button2->bounds_in_screen = gfx::Rect(100, 100, 100, 100);
   CallNotifyAccessibilityEvent(event.get());
-  std::vector<AXNodeInfoData*> left_to_right;
+  std::vector<ArcAccessibilityInfoData*> left_to_right;
   CallGetChildren(root, &left_to_right);
   ASSERT_EQ(2U, left_to_right.size());
-  ASSERT_EQ(2, left_to_right[0]->id);
-  ASSERT_EQ(1, left_to_right[1]->id);
+  EXPECT_EQ(2, left_to_right[0]->GetId());
+  EXPECT_EQ(1, left_to_right[1]->GetId());
 
   // Overlapping; left to right.
   button1->bounds_in_screen = gfx::Rect(100, 100, 100, 100);
@@ -156,8 +160,8 @@
   left_to_right.clear();
   CallGetChildren(event->node_data[0].get(), &left_to_right);
   ASSERT_EQ(2U, left_to_right.size());
-  ASSERT_EQ(1, left_to_right[0]->id);
-  ASSERT_EQ(2, left_to_right[1]->id);
+  EXPECT_EQ(1, left_to_right[0]->GetId());
+  EXPECT_EQ(2, left_to_right[1]->GetId());
 
   // Overlapping, bottom to top.
   button1->bounds_in_screen = gfx::Rect(100, 100, 100, 100);
@@ -166,8 +170,8 @@
   top_to_bottom.clear();
   CallGetChildren(event->node_data[0].get(), &top_to_bottom);
   ASSERT_EQ(2U, top_to_bottom.size());
-  ASSERT_EQ(2, top_to_bottom[0]->id);
-  ASSERT_EQ(1, top_to_bottom[1]->id);
+  EXPECT_EQ(2, top_to_bottom[0]->GetId());
+  EXPECT_EQ(1, top_to_bottom[1]->GetId());
 
   // Overlapping, top to bottom.
   button1->bounds_in_screen = gfx::Rect(100, 99, 100, 100);
@@ -176,18 +180,18 @@
   top_to_bottom.clear();
   CallGetChildren(event->node_data[0].get(), &top_to_bottom);
   ASSERT_EQ(2U, top_to_bottom.size());
-  ASSERT_EQ(1, top_to_bottom[0]->id);
-  ASSERT_EQ(2, top_to_bottom[1]->id);
+  EXPECT_EQ(1, top_to_bottom[0]->GetId());
+  EXPECT_EQ(2, top_to_bottom[1]->GetId());
 
   // Identical. smaller to larger.
   button1->bounds_in_screen = gfx::Rect(100, 100, 100, 10);
   button2->bounds_in_screen = gfx::Rect(100, 100, 100, 100);
   CallNotifyAccessibilityEvent(event.get());
-  std::vector<AXNodeInfoData*> dimension;
+  std::vector<ArcAccessibilityInfoData*> dimension;
   CallGetChildren(event->node_data[0].get(), &dimension);
   ASSERT_EQ(2U, dimension.size());
-  ASSERT_EQ(2, dimension[0]->id);
-  ASSERT_EQ(1, dimension[1]->id);
+  EXPECT_EQ(2, dimension[0]->GetId());
+  EXPECT_EQ(1, dimension[1]->GetId());
 
   button1->bounds_in_screen = gfx::Rect(100, 100, 10, 100);
   button2->bounds_in_screen = gfx::Rect(100, 100, 100, 100);
@@ -195,8 +199,8 @@
   dimension.clear();
   CallGetChildren(event->node_data[0].get(), &dimension);
   ASSERT_EQ(2U, dimension.size());
-  ASSERT_EQ(2, dimension[0]->id);
-  ASSERT_EQ(1, dimension[1]->id);
+  EXPECT_EQ(2, dimension[0]->GetId());
+  EXPECT_EQ(1, dimension[1]->GetId());
 
   // Identical. Larger to smaller.
   button1->bounds_in_screen = gfx::Rect(100, 100, 100, 100);
@@ -205,8 +209,8 @@
   dimension.clear();
   CallGetChildren(event->node_data[0].get(), &dimension);
   ASSERT_EQ(2U, dimension.size());
-  ASSERT_EQ(1, dimension[0]->id);
-  ASSERT_EQ(2, dimension[1]->id);
+  EXPECT_EQ(1, dimension[0]->GetId());
+  EXPECT_EQ(2, dimension[1]->GetId());
 
   button1->bounds_in_screen = gfx::Rect(100, 100, 100, 100);
   button2->bounds_in_screen = gfx::Rect(100, 100, 10, 100);
@@ -214,8 +218,8 @@
   dimension.clear();
   CallGetChildren(event->node_data[0].get(), &dimension);
   ASSERT_EQ(2U, dimension.size());
-  ASSERT_EQ(1, dimension[0]->id);
-  ASSERT_EQ(2, dimension[1]->id);
+  EXPECT_EQ(1, dimension[0]->GetId());
+  EXPECT_EQ(2, dimension[1]->GetId());
 }
 
 TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) {
@@ -315,15 +319,15 @@
     CallNotifyAccessibilityEvent(event.get());
 
     // Check that only the middle tree was added, and that it is correct.
-    std::vector<AXNodeInfoData*> children;
+    std::vector<ArcAccessibilityInfoData*> children;
     CallGetChildren(event->node_data.at(tree_size).get(), &children);
     ASSERT_EQ(1U, children.size());
-    EXPECT_EQ(5, children[0]->id);
+    EXPECT_EQ(5, children[0]->GetId());
     children.clear();
     CallGetChildren(event->node_data.at(tree_size + 1).get(), &children);
     ASSERT_EQ(2U, children.size());
-    EXPECT_EQ(6, children[0]->id);
-    EXPECT_EQ(7, children[1]->id);
+    EXPECT_EQ(6, children[0]->GetId());
+    EXPECT_EQ(7, children[1]->GetId());
 
     // The first and third roots are not part of the tree.
     EXPECT_EQ(nullptr, CallGetFromId(0));
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index bbbec35..7672cfd 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -503,10 +503,8 @@
 
 ChromeBrowserMainPartsChromeos::ChromeBrowserMainPartsChromeos(
     const content::MainFunctionParams& parameters,
-    std::unique_ptr<ui::DataPack> data_pack,
     ChromeFeatureListCreator* chrome_feature_list_creator)
     : ChromeBrowserMainPartsLinux(parameters,
-                                  std::move(data_pack),
                                   chrome_feature_list_creator),
       is_dbus_initialized_(chrome_feature_list_creator != nullptr) {}
 
@@ -685,6 +683,12 @@
   // AccessibilityManager and SystemKeyEventListener use InputMethodManager.
   input_method::Initialize();
 
+  // keyboard::KeyboardController initializes ChromeKeyboardUI which depends
+  // on ChromeKeyboardControllerClient.
+  chrome_keyboard_controller_client_ =
+      std::make_unique<ChromeKeyboardControllerClient>(
+          content::ServiceManagerConnection::GetForProcess()->GetConnector());
+
   // ProfileHelper has to be initialized after UserManager instance is created.
   ProfileHelper::Get()->Initialize();
 
@@ -757,9 +761,6 @@
   // Initialize the keyboard before any session state changes (i.e. before
   // loading the default profile).
   keyboard::InitializeKeyboardResources();
-  chrome_keyboard_controller_client_ =
-      std::make_unique<ChromeKeyboardControllerClient>(
-          content::ServiceManagerConnection::GetForProcess()->GetConnector());
 
   if (lock_screen_apps::StateController::IsEnabled()) {
     lock_screen_apps_state_controller_ =
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.h b/chrome/browser/chromeos/chrome_browser_main_chromeos.h
index 3d290ce..ff095cb 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.h
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.h
@@ -71,7 +71,6 @@
  public:
   ChromeBrowserMainPartsChromeos(
       const content::MainFunctionParams& parameters,
-      std::unique_ptr<ui::DataPack> data_pack,
       ChromeFeatureListCreator* chrome_feature_list_creator);
   ~ChromeBrowserMainPartsChromeos() override;
 
diff --git a/chrome/browser/chromeos/crostini/crostini_share_path.cc b/chrome/browser/chromeos/crostini/crostini_share_path.cc
index a9868ba1..0432f3c 100644
--- a/chrome/browser/chromeos/crostini/crostini_share_path.cc
+++ b/chrome/browser/chromeos/crostini/crostini_share_path.cc
@@ -75,11 +75,12 @@
              base::FilePath("/media/fuse")
                  .AppendRelativePath(drivefs_mount_point_path,
                                      &drivefs_mount_name)) {
-    // Allow subdirs of DriveFS.
+    // Allow subdirs of DriveFS except .Trash.
     request.set_drivefs_mount_name(drivefs_mount_name.value());
     base::FilePath root("root");
     base::FilePath team_drives("team_drives");
     base::FilePath computers("Computers");
+    base::FilePath trash(".Trash");  // Not to be shared!
     if (root == drivefs_path ||
         root.AppendRelativePath(drivefs_path, &relative_path)) {
       // My Drive and subdirs.
@@ -98,6 +99,11 @@
       allowed_path = true;
       request.set_storage_location(
           vm_tools::seneschal::SharePathRequest::DRIVEFS_COMPUTERS);
+    } else if (trash == drivefs_path || trash.IsParent(drivefs_path)) {
+      // Note: Do not expose .Trash which would allow linux apps to make
+      // permanent deletes from Drive.  This branch is not especially required,
+      // but is included to make it explicit that .Trash should not be shared.
+      allowed_path = false;
     }
   } else if (base::FilePath("/media/removable")
                  .AppendRelativePath(path, &relative_path)) {
diff --git a/chrome/browser/chromeos/crostini/crostini_share_path_unittest.cc b/chrome/browser/chromeos/crostini/crostini_share_path_unittest.cc
index 37bc7b3..0d6b0d9 100644
--- a/chrome/browser/chromeos/crostini/crostini_share_path_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_share_path_unittest.cc
@@ -271,6 +271,16 @@
   run_loop()->Run();
 }
 
+TEST_F(CrostiniSharePathTest, FailDriveFsTrash) {
+  SharePath(
+      profile(), "vm-running", drivefs_.Append(".Trash").Append("in-the-trash"),
+      false,
+      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+                     base::Unretained(this), false, false, nullptr, "", false,
+                     "Path is not allowed", run_loop()->QuitClosure()));
+  run_loop()->Run();
+}
+
 TEST_F(CrostiniSharePathTest, SuccessRemovable) {
   SharePath(profile(), "vm-running", base::FilePath("/media/removable/MyUSB"),
             false,
diff --git a/chrome/browser/chromeos/drive/drive_integration_service.cc b/chrome/browser/chromeos/drive/drive_integration_service.cc
index 487e518..e02d12d 100644
--- a/chrome/browser/chromeos/drive/drive_integration_service.cc
+++ b/chrome/browser/chromeos/drive/drive_integration_service.cc
@@ -346,11 +346,11 @@
                          const base::TimeTicks& time_started) {
   UMA_HISTOGRAM_ENUMERATION("DriveCommon.Lifecycle.Mount", status);
   if (status == DriveMountStatus::kSuccess) {
-    UMA_HISTOGRAM_TIMES("DriveCommon.Lifecycle.MountTime.SuccessTime",
-                        base::TimeTicks::Now() - time_started);
+    UMA_HISTOGRAM_MEDIUM_TIMES("DriveCommon.Lifecycle.MountTime.SuccessTime",
+                               base::TimeTicks::Now() - time_started);
   } else {
-    UMA_HISTOGRAM_TIMES("DriveCommon.Lifecycle.MountTime.FailTime",
-                        base::TimeTicks::Now() - time_started);
+    UMA_HISTOGRAM_MEDIUM_TIMES("DriveCommon.Lifecycle.MountTime.FailTime",
+                               base::TimeTicks::Now() - time_started);
   }
 }
 
@@ -359,8 +359,8 @@
 }
 
 void UmaEmitFirstLaunch(const base::TimeTicks& time_started) {
-  UMA_HISTOGRAM_TIMES("DriveCommon.Lifecycle.FirstLaunchTime",
-                      base::TimeTicks::Now() - time_started);
+  UMA_HISTOGRAM_MEDIUM_TIMES("DriveCommon.Lifecycle.FirstLaunchTime",
+                             base::TimeTicks::Now() - time_started);
 }
 
 }  // namespace
diff --git a/chrome/browser/chromeos/extensions/default_app_order.cc b/chrome/browser/chromeos/extensions/default_app_order.cc
index 934d955..72bec8b 100644
--- a/chrome/browser/chromeos/extensions/default_app_order.cc
+++ b/chrome/browser/chromeos/extensions/default_app_order.cc
@@ -17,6 +17,7 @@
 #include "base/task/post_task.h"
 #include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/extensions/default_web_app_ids.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/app_list/page_break_constants.h"
 #include "chrome/common/extensions/extension_constants.h"
@@ -65,10 +66,13 @@
     app_list::kInternalAppIdDiscover,
     extension_misc::kGeniusAppId,
     extension_misc::kCalculatorAppId,
+    default_web_apps::kCanvasAppId,
     extension_misc::kTextEditorAppId,
     arc::kGoogleDuo,
+    default_web_apps::kYoutubeTVAppId,
     arc::kLightRoom,
     arc::kInfinitePainter,
+    default_web_apps::kShowtimeAppId,
     extension_misc::kGooglePlusAppId,
     extension_misc::kChromeRemoteDesktopAppId,
     extensions::kWebStoreAppId,
diff --git a/chrome/browser/chromeos/extensions/default_web_app_ids.h b/chrome/browser/chromeos/extensions/default_web_app_ids.h
new file mode 100644
index 0000000..5a01582
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/default_web_app_ids.h
@@ -0,0 +1,25 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_DEFAULT_WEB_APP_IDS_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_DEFAULT_WEB_APP_IDS_H_
+
+namespace chromeos {
+namespace default_web_apps {
+
+// Generated as web_app::GenerateAppIdFromURL(GURL("https://tv.youtube.com/")).
+constexpr char kYoutubeTVAppId[] = "kiemjbkkegajmpbobdfngbmjccjhnofh";
+
+// Generated as
+// web_app::GenerateAppIdFromURL(GURL("https://www.showtime.com/")).
+constexpr char kShowtimeAppId[] = "https://canvas-internal.googleplex.com/";
+
+// Generated as
+// web_app::GenerateAppIdFromURL(GURL("https://canvas.apps.chrome/index.html)).
+constexpr char kCanvasAppId[] = "memejfanofdmelnjmboefinndljpifdm";
+
+}  // namespace default_web_apps
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_DEFAULT_WEB_APP_IDS_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
index 2e520dd0d..f031891 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
@@ -862,11 +862,17 @@
       infix = "OfflineSearchTime";
       break;
   }
-  base::UmaHistogramTimes(
-      base::StrCat(
-          {remote ? "DriveCommon.RemoteSearch." : "DriveCommon.LocalSearch.",
-           infix, success ? ".SuccessTime" : ".FailTime"}),
-      base::TimeTicks::Now() - time_started);
+  if (remote) {
+    base::UmaHistogramMediumTimes(
+        base::StrCat({"DriveCommon.RemoteSearch.", infix,
+                      success ? ".SuccessTime" : ".FailTime"}),
+        base::TimeTicks::Now() - time_started);
+  } else {
+    base::UmaHistogramMediumTimes(
+        base::StrCat({"DriveCommon.LocalSearch.", infix,
+                      success ? ".SuccessTime" : ".FailTime"}),
+        base::TimeTicks::Now() - time_started);
+  }
 }
 
 }  // namespace
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 0c2b43d..584697d 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -260,6 +260,7 @@
     ::testing::Values(ZipCase("zipFileOpenDownloads").InGuestMode(),
                       ZipCase("zipFileOpenDownloads"),
                       ZipCase("zipFileOpenDownloadsShiftJIS"),
+                      ZipCase("zipFileOpenDownloadsWithAbsolutePaths"),
                       ZipCase("zipFileOpenDrive").DisableDriveFs(),
                       ZipCase("zipFileOpenDrive").EnableDriveFs(),
                       ZipCase("zipFileOpenUsb"),
diff --git a/chrome/browser/chromeos/file_manager/path_util.cc b/chrome/browser/chromeos/file_manager/path_util.cc
index ebf2b6d..39bc644 100644
--- a/chrome/browser/chromeos/file_manager/path_util.cc
+++ b/chrome/browser/chromeos/file_manager/path_util.cc
@@ -40,12 +40,14 @@
 namespace {
 
 constexpr char kDownloadsFolderName[] = "Downloads";
+constexpr char kMyFilesFolderName[] = "MyFiles";
 constexpr char kGoogleDriveDisplayName[] = "Google Drive";
 constexpr char kMyDriveDisplayName[] = "My Drive";
 constexpr char kTeamDrivesDisplayName[] = "Team Drives";
 constexpr char kRootRelativeToDriveMount[] = "root";
 constexpr char kTeamDrivesRelativeToDriveMount[] = "team_drives";
 constexpr char kComputersRelativeToDriveMount[] = "Computers";
+constexpr char kRemovable[] = "removable";
 constexpr char kAndroidFilesMountPointName[] = "android_files";
 
 // Sync with the root name defined with the file provider in ARC++ side.
@@ -81,6 +83,22 @@
   std::move(callback).Run(*urls);
 }
 
+// On non-ChromeOS system (test+development), the primary profile uses
+// $HOME/Downloads for ease access to local files for debugging.
+bool ShouldMountPrimaryUserDownloads(Profile* profile) {
+  if (!base::SysInfo::IsRunningOnChromeOS() &&
+      user_manager::UserManager::IsInitialized()) {
+    const user_manager::User* const user =
+        chromeos::ProfileHelper::Get()->GetUserByProfile(
+            profile->GetOriginalProfile());
+    const user_manager::User* const primary_user =
+        user_manager::UserManager::Get()->GetPrimaryUser();
+    return user == primary_user;
+  }
+
+  return false;
+}
+
 }  // namespace
 
 const base::FilePath::CharType kRemovableMediaPath[] =
@@ -99,23 +117,44 @@
   if (mount_points->GetRegisteredPath(mount_point_name, &path))
     return path;
 
-  // On non-ChromeOS system (test+development), the primary profile uses
-  // $HOME/Downloads for ease for accessing local files for debugging.
-  if (!base::SysInfo::IsRunningOnChromeOS() &&
-      user_manager::UserManager::IsInitialized()) {
-    const user_manager::User* const user =
-        chromeos::ProfileHelper::Get()->GetUserByProfile(
-            profile->GetOriginalProfile());
-    const user_manager::User* const primary_user =
-        user_manager::UserManager::Get()->GetPrimaryUser();
-    if (user == primary_user)
-      return DownloadPrefs::GetDefaultDownloadDirectory();
+  // Return $HOME/Downloads as Download folder.
+  if (ShouldMountPrimaryUserDownloads(profile))
+    return DownloadPrefs::GetDefaultDownloadDirectory();
+
+  // Return <cryptohome>/MyFiles/Downloads if it feature is enabled.
+  if (base::FeatureList::IsEnabled(chromeos::features::kMyFilesVolume)) {
+    return profile->GetPath()
+        .AppendASCII(kMyFilesFolderName)
+        .AppendASCII(kDownloadsFolderName);
   }
 
   // Return <cryptohome>/Downloads.
   return profile->GetPath().AppendASCII(kDownloadsFolderName);
 }
 
+base::FilePath GetMyFilesFolderForProfile(Profile* profile) {
+  // When MyFilesVolume feature is disabled this should behave just like
+  // GetDownloadsFolderForProfile.
+  if (!base::FeatureList::IsEnabled(chromeos::features::kMyFilesVolume))
+    return GetDownloadsFolderForProfile(profile);
+
+  // Check if FilesApp has a registered path already. This happens for tests.
+  const std::string mount_point_name =
+      util::GetDownloadsMountPointName(profile);
+  storage::ExternalMountPoints* const mount_points =
+      storage::ExternalMountPoints::GetSystemInstance();
+  base::FilePath path;
+  if (mount_points->GetRegisteredPath(mount_point_name, &path))
+    return path;
+
+  // Return $HOME/Downloads as MyFiles folder.
+  if (ShouldMountPrimaryUserDownloads(profile))
+    return DownloadPrefs::GetDefaultDownloadDirectory();
+
+  // Return <cryptohome>/MyFiles.
+  return profile->GetPath().AppendASCII(kMyFilesFolderName);
+}
+
 bool MigratePathFromOldFormat(Profile* profile,
                               const base::FilePath& old_path,
                               base::FilePath* new_path) {
@@ -207,8 +246,10 @@
   //   /ChromeOS/<mapping>/path/to/file (path is shared with crostini)
   base::FilePath base_to_exclude(id);
   if (id == mount_point_name_crostini) {
+    // Crostini.
     *inside = crostini::ContainerHomeDirectoryForProfile(profile);
   } else if (id == mount_point_name_downloads) {
+    // Downloads.
     *inside =
         crostini::ContainerChromeOSBaseDirectory().Append(kDownloadsFolderName);
   } else if (id == mount_point_name_drive) {
@@ -227,6 +268,9 @@
       base_to_exclude = base_to_exclude.Append(kTeamDrivesRelativeToDriveMount);
       *inside = inside->Append(kTeamDrivesDisplayName);
     }
+  } else if (id == kRemovable) {
+    // Removable.
+    *inside = crostini::ContainerChromeOSBaseDirectory().Append(kRemovable);
   } else {
     return false;
   }
@@ -360,6 +404,15 @@
                                profile->GetPath().BaseName().value() +
                                "/Downloads",
                            kDownloadsFolderName)) {
+  } else if (ReplacePrefix(&result,
+                           std::string("/home/chronos/user/") +
+                               kMyFilesFolderName + "/" + kDownloadsFolderName,
+                           kDownloadsFolderName)) {
+  } else if (ReplacePrefix(&result,
+                           "/home/chronos/" +
+                               profile->GetPath().BaseName().value() + "/" +
+                               kMyFilesFolderName + "/" + kDownloadsFolderName,
+                           kDownloadsFolderName)) {
   } else if (drive_integration_service &&
              ReplacePrefix(&result,
                            drive_integration_service->GetMountPointPath()
diff --git a/chrome/browser/chromeos/file_manager/path_util.h b/chrome/browser/chromeos/file_manager/path_util.h
index 6a22599..0d97a44 100644
--- a/chrome/browser/chromeos/file_manager/path_util.h
+++ b/chrome/browser/chromeos/file_manager/path_util.h
@@ -27,6 +27,9 @@
 // Gets the absolute path for the 'Downloads' folder for the |profile|.
 base::FilePath GetDownloadsFolderForProfile(Profile* profile);
 
+// Gets the absolute path for the 'MyFiles' folder for the |profile|.
+base::FilePath GetMyFilesFolderForProfile(Profile* profile);
+
 // Converts |old_path| to |new_path| and returns true, if the old path points
 // to an old location of user folders (in "Downloads" or "Google Drive").
 // The |profile| argument is used for determining the location of the
diff --git a/chrome/browser/chromeos/file_manager/path_util_unittest.cc b/chrome/browser/chromeos/file_manager/path_util_unittest.cc
index a87e9ab4..bd9b9d8 100644
--- a/chrome/browser/chromeos/file_manager/path_util_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/path_util_unittest.cc
@@ -47,6 +47,66 @@
     "CHROMEOS_RELEASE_NAME=Chrome OS\n"
     "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
 
+TEST(FileManagerPathUtilTest, GetDownloadsFolderForProfile) {
+  content::TestBrowserThreadBundle thread_bundle;
+  TestingProfile profile(base::FilePath("/home/chronos/u-0123456789abcdef"));
+  std::string mount_point_name = GetDownloadsMountPointName(&profile);
+  EXPECT_EQ("Downloads", mount_point_name);
+}
+
+TEST(FileManagerPathUtilTest, GetMyFilesFolderForProfile) {
+  content::TestBrowserThreadBundle thread_bundle;
+
+  base::FilePath profile_path =
+      base::FilePath("/home/chronos/u-0123456789abcdef");
+  TestingProfile profile(profile_path);
+
+  // When running outside ChromeOS, it should return $HOME/Downloads for both
+  // MyFiles and Downloads.
+  EXPECT_EQ(DownloadPrefs::GetDefaultDownloadDirectory(),
+            GetMyFilesFolderForProfile(&profile));
+  EXPECT_EQ(DownloadPrefs::GetDefaultDownloadDirectory(),
+            GetDownloadsFolderForProfile(&profile));
+
+  // When running inside ChromeOS, it should return /home/u-{hash}/MyFiles.
+  chromeos::ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease,
+                                                              base::Time());
+  {
+    base::test::ScopedFeatureList feature_list;
+    feature_list.InitAndDisableFeature(chromeos::features::kMyFilesVolume);
+    // When MyFilesVolume feature is disabled it will return the same as
+    // Downloads.
+    EXPECT_EQ(GetDownloadsFolderForProfile(&profile),
+              GetMyFilesFolderForProfile(&profile));
+    EXPECT_EQ("/home/chronos/u-0123456789abcdef/Downloads",
+              GetDownloadsFolderForProfile(&profile).value());
+  }
+  {
+    // When MyFilesVolume feature is enabled Downloads path is inside MyFiles.
+    base::test::ScopedFeatureList feature_list;
+    feature_list.InitAndEnableFeature(chromeos::features::kMyFilesVolume);
+
+    base::FilePath myfiles_path = profile_path.AppendASCII("MyFiles");
+    base::FilePath myfiles_downloads_path =
+        myfiles_path.AppendASCII("Downloads");
+
+    EXPECT_EQ("/home/chronos/u-0123456789abcdef/MyFiles",
+              GetMyFilesFolderForProfile(&profile).value());
+    EXPECT_EQ("/home/chronos/u-0123456789abcdef/MyFiles/Downloads",
+              GetDownloadsFolderForProfile(&profile).value());
+
+    // Mount the volume to test the return from mount_points.
+    storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
+        GetDownloadsMountPointName(&profile),
+        storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
+        profile_path.Append("MyFiles"));
+
+    // Still the same: /home/u-{hash}/MyFiles.
+    EXPECT_EQ("/home/chronos/u-0123456789abcdef/MyFiles",
+              GetMyFilesFolderForProfile(&profile).value());
+  }
+}
+
 TEST(FileManagerPathUtilTest, GetPathDisplayTextForSettings) {
   content::TestBrowserThreadBundle thread_bundle;
   content::TestServiceManagerContext service_manager_context;
@@ -58,6 +118,14 @@
   EXPECT_EQ("Downloads",
             GetPathDisplayTextForSettings(
                 &profile, "/home/chronos/u-0123456789abcdef/Downloads"));
+
+  EXPECT_EQ("Downloads", GetPathDisplayTextForSettings(
+                             &profile, "/home/chronos/user/MyFiles/Downloads"));
+  EXPECT_EQ(
+      "Downloads",
+      GetPathDisplayTextForSettings(
+          &profile, "/home/chronos/u-0123456789abcdef/MyFiles/Downloads"));
+
   EXPECT_EQ("Play files \u203a foo \u203a bar",
             GetPathDisplayTextForSettings(
                 &profile, "/run/arc/sdcard/write/emulated/0/foo/bar"));
diff --git a/chrome/browser/chromeos/login/screens/discover_screen.cc b/chrome/browser/chromeos/login/screens/discover_screen.cc
index 80dbee4..b4666cf 100644
--- a/chrome/browser/chromeos/login/screens/discover_screen.cc
+++ b/chrome/browser/chromeos/login/screens/discover_screen.cc
@@ -31,14 +31,17 @@
     return;
   }
   view_->Show();
+  is_shown_ = true;
 }
 
 void DiscoverScreen::Hide() {
   view_->Hide();
+  is_shown_ = false;
 }
 
 void DiscoverScreen::OnUserAction(const std::string& action_id) {
-  if (action_id == kFinished) {
+  // Only honor finish if discover is currently being shown.
+  if (action_id == kFinished && is_shown_) {
     Finish(ScreenExitCode::DISCOVER_FINISHED);
     return;
   }
diff --git a/chrome/browser/chromeos/login/screens/discover_screen.h b/chrome/browser/chromeos/login/screens/discover_screen.h
index de9b5d01..d86a72d 100644
--- a/chrome/browser/chromeos/login/screens/discover_screen.h
+++ b/chrome/browser/chromeos/login/screens/discover_screen.h
@@ -28,6 +28,7 @@
 
  private:
   DiscoverScreenView* const view_;
+  bool is_shown_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(DiscoverScreen);
 };
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_common.cc b/chrome/browser/chromeos/login/ui/login_display_host_common.cc
index 3d7e2f7..ba0cb502 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_common.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_common.cc
@@ -65,6 +65,11 @@
 }
 
 void LoginDisplayHostCommon::Finalize(base::OnceClosure completion_callback) {
+  // If finalize is called twice the LoginDisplayHost instance will be deleted
+  // multiple times.
+  CHECK(!is_finalizing_);
+  is_finalizing_ = true;
+
   completion_callbacks_.push_back(std::move(completion_callback));
   OnFinalize();
 }
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_common.h b/chrome/browser/chromeos/login/ui/login_display_host_common.h
index 05ad25e..140d4b7 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_common.h
+++ b/chrome/browser/chromeos/login/ui/login_display_host_common.h
@@ -97,6 +97,9 @@
   // still in the process of cleaning up after login (http://crbug.com/134463).
   bool shutting_down_ = false;
 
+  // Used to make sure Finalize() is not called twice.
+  bool is_finalizing_ = false;
+
   // Make sure chrome won't exit while we are at login/oobe screen.
   std::unique_ptr<ScopedKeepAlive> keep_alive_;
 
diff --git a/chrome/browser/chromeos/login/ui/login_feedback.cc b/chrome/browser/chromeos/login/ui/login_feedback.cc
index 46ef3456..45781aa 100644
--- a/chrome/browser/chromeos/login/ui/login_feedback.cc
+++ b/chrome/browser/chromeos/login/ui/login_feedback.cc
@@ -11,7 +11,6 @@
 #include "base/callback.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
-#include "chrome/browser/chromeos/login/ui/login_display_host.h"
 #include "chrome/browser/extensions/component_loader.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/profile.h"
@@ -28,9 +27,6 @@
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
-#include "ui/aura/window.h"
-#include "ui/views/widget/widget.h"
-#include "ui/wm/core/window_util.h"
 
 namespace chromeos {
 
@@ -130,7 +126,6 @@
   bool HasFeedbackAppWindow() const;
 
   // extensions::AppWindowRegistry::Observer
-  void OnAppWindowAdded(extensions::AppWindow* app_window) override;
   void OnAppWindowRemoved(extensions::AppWindow* app_window) override;
 
  private:
@@ -157,22 +152,6 @@
               .empty();
 }
 
-void LoginFeedback::FeedbackWindowHandler::OnAppWindowAdded(
-    extensions::AppWindow* app_window) {
-  if (app_window->extension_id() != extension_misc::kFeedbackExtensionId)
-    return;
-
-  // Move the feedback window to the same container as the login screen and make
-  // it a transient child of the login screen.
-  if (LoginDisplayHost::default_host()->GetNativeWindow()) {
-    views::Widget::ReparentNativeView(
-        app_window->GetNativeWindow(),
-        LoginDisplayHost::default_host()->GetNativeWindow()->parent());
-    wm::AddTransientChild(LoginDisplayHost::default_host()->GetNativeWindow(),
-                          app_window->GetNativeWindow());
-  }
-}
-
 void LoginFeedback::FeedbackWindowHandler::OnAppWindowRemoved(
     extensions::AppWindow* app_window) {
   if (app_window->extension_id() != extension_misc::kFeedbackExtensionId)
diff --git a/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc b/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc
index b68069f7..f34e63e 100644
--- a/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc
+++ b/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc
@@ -567,8 +567,9 @@
 // images while the profile image download is still in progress. Verifies that
 // when the download completes, the profile image is ignored and does not
 // clobber the default image chosen in the meantime.
+// TODO(crbug.com/888784) disabled due to flaky timeouts.
 IN_PROC_BROWSER_TEST_F(UserImageManagerTest,
-                       ProfileImageDownloadDoesNotClobber) {
+                       DISABLED_ProfileImageDownloadDoesNotClobber) {
   const user_manager::User* user =
       user_manager::UserManager::Get()->FindUser(test_account_id1_);
   ASSERT_TRUE(user);
diff --git a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
index 79991c8..306bbc7 100644
--- a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
@@ -2304,6 +2304,11 @@
 };
 
 IN_PROC_BROWSER_TEST_P(TermsOfServiceDownloadTest, TermsOfServiceScreen) {
+  // TODO(crbug.com/898701): Running test with existant TOS path flakes, so this
+  // has been disabled.
+  if (GetParam())
+    return;
+
   // Specify Terms of Service URL.
   ASSERT_TRUE(embedded_test_server()->Start());
   device_local_account_policy_.payload().mutable_termsofserviceurl()->set_value(
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_unittest.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_unittest.cc
index 554c21f..423359f 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_unittest.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_unittest.cc
@@ -401,17 +401,14 @@
                       /*system_url_loader_factory=*/nullptr);
     if (should_create_token_forwarder_) {
       // Create the UserCloudPolicyTokenForwarder, which fetches the access
-      // token using the OAuth2PolicyFetcher and forwards it to the
+      // token using the IdentityManager and forwards it to the
       // UserCloudPolicyManagerChromeOS. This service is automatically created
       // for regular Profiles but not for testing Profiles.
-      ProfileOAuth2TokenService* token_service =
-          ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
-      ASSERT_TRUE(token_service);
       identity::IdentityManager* identity_manager =
           IdentityManagerFactory::GetForProfile(profile_);
       ASSERT_TRUE(identity_manager);
-      token_forwarder_.reset(new UserCloudPolicyTokenForwarder(
-          manager_.get(), identity_manager, token_service));
+      token_forwarder_ = std::make_unique<UserCloudPolicyTokenForwarder>(
+          manager_.get(), identity_manager);
     }
   }
 
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.cc b/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.cc
index 11042a6..7f97696 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.cc
@@ -7,20 +7,16 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
-#include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "content/public/browser/notification_source.h"
 #include "google_apis/gaia/gaia_constants.h"
+#include "services/identity/public/cpp/access_token_fetcher.h"
 
 namespace policy {
 
 UserCloudPolicyTokenForwarder::UserCloudPolicyTokenForwarder(
     UserCloudPolicyManagerChromeOS* manager,
-    identity::IdentityManager* identity_manager,
-    ProfileOAuth2TokenService* token_service)
-    : OAuth2TokenService::Consumer("policy_token_forwarder"),
-      manager_(manager),
-      identity_manager_(identity_manager),
-      token_service_(token_service) {
+    identity::IdentityManager* identity_manager)
+    : manager_(manager), identity_manager_(identity_manager) {
   // Start by waiting for the CloudPolicyService to be initialized, so that
   // we can check if it already has a DMToken or not.
   if (manager_->core()->service()->IsInitializationComplete()) {
@@ -33,7 +29,7 @@
 UserCloudPolicyTokenForwarder::~UserCloudPolicyTokenForwarder() {}
 
 void UserCloudPolicyTokenForwarder::Shutdown() {
-  request_.reset();
+  access_token_fetcher_.reset();
   identity_manager_->RemoveObserver(this);
   manager_->core()->service()->RemoveObserver(this);
 }
@@ -44,31 +40,6 @@
   RequestAccessToken();
 }
 
-void UserCloudPolicyTokenForwarder::OnGetTokenSuccess(
-    const OAuth2TokenService::Request* request,
-    const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
-  manager_->OnAccessTokenAvailable(token_response.access_token);
-  // All done here.
-  Shutdown();
-}
-
-void UserCloudPolicyTokenForwarder::OnGetTokenFailure(
-    const OAuth2TokenService::Request* request,
-    const GoogleServiceAuthError& error) {
-  // This should seldom happen: if the user is signing in for the first time
-  // then this was an online signin and network errors are unlikely; if the
-  // user had already signed in before then they should have policy cached, and
-  // RequestAccessToken() wouldn't have been invoked.
-  // Still, something just went wrong (server 500, or something). Currently
-  // we don't recover in this case, and we'll just try to register for policy
-  // again on the next signin.
-  // TODO(joaodasilva, atwilson): consider blocking signin when this happens,
-  // so that the user has to try again before getting into the session. That
-  // would guarantee that a session always has fresh policy, or at least
-  // enforces a cached policy.
-  Shutdown();
-}
-
 void UserCloudPolicyTokenForwarder::OnInitializationCompleted(
     CloudPolicyService* service) {
   Initialize();
@@ -89,8 +60,36 @@
   OAuth2TokenService::ScopeSet scopes;
   scopes.insert(GaiaConstants::kDeviceManagementServiceOAuth);
   scopes.insert(GaiaConstants::kOAuthWrapBridgeUserInfoScope);
-  request_ = token_service_->StartRequest(
-      identity_manager_->GetPrimaryAccountId(), scopes, this);
+  access_token_fetcher_ = identity_manager_->CreateAccessTokenFetcherForAccount(
+      identity_manager_->GetPrimaryAccountId(), "policy_token_forwarder",
+      scopes,
+      base::BindOnce(
+          &UserCloudPolicyTokenForwarder::OnAccessTokenFetchCompleted,
+          base::Unretained(this)),
+      identity::AccessTokenFetcher::Mode::kImmediate);
+}
+
+void UserCloudPolicyTokenForwarder::OnAccessTokenFetchCompleted(
+    GoogleServiceAuthError error,
+    identity::AccessTokenInfo token_info) {
+  DCHECK(access_token_fetcher_);
+
+  if (error.state() == GoogleServiceAuthError::NONE) {
+    manager_->OnAccessTokenAvailable(token_info.token);
+  } else {
+    // This should seldom happen: if the user is signing in for the first time
+    // then this was an online signin and network errors are unlikely; if the
+    // user had already signed in before then they should have policy cached,
+    // and RequestAccessToken() wouldn't have been invoked. Still, something
+    // just went wrong (server 500, or something). Currently we don't recover in
+    // this case, and we'll just try to register for policy again on the next
+    // signin.
+    // TODO(joaodasilva, atwilson): consider blocking signin when this happens,
+    // so that the user has to try again before getting into the session. That
+    // would guarantee that a session always has fresh policy, or at least
+    // enforces a cached policy.
+  }
+  Shutdown();
 }
 
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.h b/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.h
index 3a43b79..f663f6a 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.h
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.h
@@ -4,36 +4,38 @@
 
 #ifndef CHROME_BROWSER_CHROMEOS_POLICY_USER_CLOUD_POLICY_TOKEN_FORWARDER_H_
 #define CHROME_BROWSER_CHROMEOS_POLICY_USER_CLOUD_POLICY_TOKEN_FORWARDER_H_
+#include <memory>
 
 #include "base/macros.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/policy/core/common/cloud/cloud_policy_service.h"
-#include "google_apis/gaia/oauth2_token_service.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "services/identity/public/cpp/access_token_info.h"
 #include "services/identity/public/cpp/identity_manager.h"
 
-class ProfileOAuth2TokenService;
+namespace identity {
+class AccessTokenFetcher;
+}
 
 namespace policy {
 
 class UserCloudPolicyManagerChromeOS;
 
-// A PKS that observes a ProfileOAuth2TokenService and mints the policy access
+// A PKS that observes a IdentityManager and mints the policy access
 // token for the UserCloudPolicyManagerChromeOS, when the token service becomes
 // ready. This service decouples the UserCloudPolicyManagerChromeOS from
-// depending directly on the ProfileOAuth2TokenService, since it is initialized
+// depending directly on the IdentityManager, since it is initialized
 // much earlier.
 class UserCloudPolicyTokenForwarder
     : public KeyedService,
       public identity::IdentityManager::Observer,
-      public OAuth2TokenService::Consumer,
       public CloudPolicyService::Observer {
  public:
   // The factory of this PKS depends on the factories of these two arguments,
   // so this object will be Shutdown() first and these pointers can be used
   // until that point.
   UserCloudPolicyTokenForwarder(UserCloudPolicyManagerChromeOS* manager,
-                                identity::IdentityManager* identity_manager,
-                                ProfileOAuth2TokenService* token_service);
+                                identity::IdentityManager* identity_manager);
   ~UserCloudPolicyTokenForwarder() override;
 
   // KeyedService:
@@ -43,13 +45,6 @@
   void OnRefreshTokenUpdatedForAccount(const AccountInfo& account_info,
                                        bool is_valid) override;
 
-  // OAuth2TokenService::Consumer:
-  void OnGetTokenSuccess(
-      const OAuth2TokenService::Request* request,
-      const OAuth2AccessTokenConsumer::TokenResponse& token_response) override;
-  void OnGetTokenFailure(const OAuth2TokenService::Request* request,
-                         const GoogleServiceAuthError& error) override;
-
   // CloudPolicyService::Observer:
   void OnInitializationCompleted(CloudPolicyService* service) override;
 
@@ -57,11 +52,12 @@
   void Initialize();
 
   void RequestAccessToken();
+  void OnAccessTokenFetchCompleted(GoogleServiceAuthError error,
+                                   identity::AccessTokenInfo token_info);
 
   UserCloudPolicyManagerChromeOS* manager_;
   identity::IdentityManager* identity_manager_;
-  ProfileOAuth2TokenService* token_service_;
-  std::unique_ptr<OAuth2TokenService::Request> request_;
+  std::unique_ptr<identity::AccessTokenFetcher> access_token_fetcher_;
 
   DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyTokenForwarder);
 };
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder_factory.cc b/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder_factory.cc
index 63bfec14..9799618 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder_factory.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder_factory.cc
@@ -9,9 +9,7 @@
 #include "chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "services/identity/public/cpp/identity_manager.h"
 
 namespace policy {
@@ -27,7 +25,6 @@
         "UserCloudPolicyTokenForwarder",
         BrowserContextDependencyManager::GetInstance()) {
   DependsOn(IdentityManagerFactory::GetInstance());
-  DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
   DependsOn(UserPolicyManagerFactoryChromeOS::GetInstance());
 }
 
@@ -41,12 +38,9 @@
           profile);
   identity::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfile(profile);
-  ProfileOAuth2TokenService* token_service =
-      ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
-  if (!token_service || !manager || !identity_manager)
+  if (!manager || !identity_manager)
     return nullptr;
-  return new UserCloudPolicyTokenForwarder(manager, identity_manager,
-                                           token_service);
+  return new UserCloudPolicyTokenForwarder(manager, identity_manager);
 }
 
 bool UserCloudPolicyTokenForwarderFactory::
diff --git a/chrome/browser/chromeos/preferences.cc b/chrome/browser/chromeos/preferences.cc
index 56e45cd..e6ab8e3 100644
--- a/chrome/browser/chromeos/preferences.cc
+++ b/chrome/browser/chromeos/preferences.cc
@@ -255,6 +255,9 @@
       ash::prefs::kDictationAcceleratorDialogHasBeenAccepted, false,
       PrefRegistry::PUBLIC);
   registry->RegisterBooleanPref(
+      ash::prefs::kDisplayRotationAcceleratorDialogHasBeenAccepted, false,
+      PrefRegistry::PUBLIC);
+  registry->RegisterBooleanPref(
       ash::prefs::kAccessibilityScreenMagnifierEnabled, false,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF | PrefRegistry::PUBLIC);
   registry->RegisterBooleanPref(
@@ -274,6 +277,9 @@
       static_cast<int>(ash::kDefaultAutoclickEventType),
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF | PrefRegistry::PUBLIC);
   registry->RegisterBooleanPref(
+      ash::prefs::kAccessibilityAutoclickRevertToLeftClick, true,
+      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF | PrefRegistry::PUBLIC);
+  registry->RegisterBooleanPref(
       ash::prefs::kAccessibilityVirtualKeyboardEnabled, false,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF | PrefRegistry::PUBLIC);
   registry->RegisterBooleanPref(
diff --git a/chrome/browser/data_use_measurement/chrome_data_use_measurement.cc b/chrome/browser/data_use_measurement/chrome_data_use_measurement.cc
index 39a1fe37..f9dc665 100644
--- a/chrome/browser/data_use_measurement/chrome_data_use_measurement.cc
+++ b/chrome/browser/data_use_measurement/chrome_data_use_measurement.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/macros.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
@@ -72,12 +73,15 @@
     network::NetworkConnectionTracker* network_connection_tracker)
     : DataUseMeasurement(std::move(url_request_classifier),
                          ascriber,
-                         network_connection_tracker) {}
+                         network_connection_tracker) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
 
 void ChromeDataUseMeasurement::UpdateDataUseToMetricsService(
     int64_t total_bytes,
     bool is_cellular,
     bool is_metrics_service_usage) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Update data use of user traffic and services distinguishing cellular and
   // metrics services data use.
   UpdateMetricsUsagePrefsOnUIThread(total_bytes, is_cellular,
@@ -88,6 +92,7 @@
     int32_t network_traffic_annotation_id_hash,
     int64_t recv_bytes,
     int64_t sent_bytes) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService));
   // Negative byte numbres is not a critical problem (i.e. should have no security implications) but
@@ -114,7 +119,10 @@
     for (auto& observer : services_data_use_observer_list_)
       observer.OnServicesDataUse(recv_bytes, sent_bytes);
   }
+  UMA_HISTOGRAM_COUNTS_1M("DataUse.BytesReceived.Delegate", recv_bytes);
+  UMA_HISTOGRAM_COUNTS_1M("DataUse.BytesSent.Delegate", sent_bytes);
 #if defined(OS_ANDROID)
+  bytes_transferred_since_last_traffic_stats_query_ += recv_bytes + sent_bytes;
   MaybeRecordNetworkBytesOS();
 #endif
 }
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 1859f0a..c09d0a8 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -318,6 +318,8 @@
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
   (*s_whitelist)[ash::prefs::kAccessibilityAutoclickEventType] =
       settings_api::PrefType::PREF_TYPE_NUMBER;
+  (*s_whitelist)[ash::prefs::kAccessibilityAutoclickRevertToLeftClick] =
+      settings_api::PrefType::PREF_TYPE_BOOLEAN;
   (*s_whitelist)[ash::prefs::kAccessibilityCaretHighlightEnabled] =
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
   (*s_whitelist)[ash::prefs::kAccessibilityCursorHighlightEnabled] =
diff --git a/chrome/browser/extensions/content_script_apitest.cc b/chrome/browser/extensions/content_script_apitest.cc
index c05db45..05f2028d 100644
--- a/chrome/browser/extensions/content_script_apitest.cc
+++ b/chrome/browser/extensions/content_script_apitest.cc
@@ -26,10 +26,12 @@
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/javascript_dialog_manager.h"
+#include "content/public/browser/notification_service.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_utils.h"
 #include "extensions/browser/browsertest_util.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/notification_types.h"
@@ -360,8 +362,14 @@
 IN_PROC_BROWSER_TEST_F(ContentScriptApiTest, ContentScriptExtensionAPIs) {
   ASSERT_TRUE(StartEmbeddedTestServer());
 
+  // TODO(https://crbug.com/898682): Waiting for content scripts to load should
+  // be done as part of the extension loading process.
+  content::WindowedNotificationObserver scripts_updated_observer(
+      extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
+      content::NotificationService::AllSources());
   const extensions::Extension* extension = LoadExtension(
       test_data_dir_.AppendASCII("content_scripts/extension_api"));
+  scripts_updated_observer.Wait();
 
   ResultCatcher catcher;
   ui_test_utils::NavigateToURL(
@@ -562,7 +570,13 @@
   ext_dir1.WriteManifest(
       base::StringPrintf(kManifest, "ext1", "document_idle"));
   ext_dir1.WriteFile(FILE_PATH_LITERAL("script.js"), kBlockingScript);
+  // TODO(https://crbug.com/898682): Waiting for content scripts to load should
+  // be done as part of the extension loading process.
+  content::WindowedNotificationObserver scripts_updated_observer(
+      extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
+      content::NotificationService::AllSources());
   const Extension* ext1 = LoadExtension(ext_dir1.UnpackedPath());
+  scripts_updated_observer.Wait();
   ASSERT_TRUE(ext1);
 
   content::WebContents* web_contents =
diff --git a/chrome/browser/extensions/extension_keybinding_apitest.cc b/chrome/browser/extensions/extension_keybinding_apitest.cc
index 8bbdaa2..a2d7040 100644
--- a/chrome/browser/extensions/extension_keybinding_apitest.cc
+++ b/chrome/browser/extensions/extension_keybinding_apitest.cc
@@ -21,7 +21,6 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/extensions/browser_action_test_util.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/views_mode_controller.h"
 #include "chrome/test/base/interactive_test_utils.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/notification_service.h"
diff --git a/chrome/browser/extensions/process_management_browsertest.cc b/chrome/browser/extensions/process_management_browsertest.cc
index 696e94b8..4d4b7f5c 100644
--- a/chrome/browser/extensions/process_management_browsertest.cc
+++ b/chrome/browser/extensions/process_management_browsertest.cc
@@ -325,9 +325,13 @@
   // chrome-extension:// URIs below (not to HTTP URIs) to make sure the 1/3rd
   // of process limit also applies to normal tabs (not just to background pages
   // and scripts).
-  ui_test_utils::NavigateToURL(
+  content::RenderProcessHost* first_renderer = ui_test_utils::NavigateToURL(
       browser(), base_url.Resolve("isolated_apps/app1/main.html"));
-  ui_test_utils::NavigateToURL(
+  content::RenderProcessHostWatcher first_renderer_watcher(
+      first_renderer,
+      content::RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
+
+  content::RenderProcessHost* second_renderer = ui_test_utils::NavigateToURL(
       browser(), base_url.Resolve("api_test/management/test/basics.html"));
 
   std::set<int> process_ids;
@@ -349,6 +353,13 @@
   else
     EXPECT_EQ(5u, process_ids.size());
 
+  if (first_renderer != second_renderer) {
+    // Wait for the first renderer to be torn down before verifying the number
+    // of processes, else we race with the teardown here (specifically the
+    // FrameMsg_SwapOut -> FrameHostMsg_SwapOut_ACK round trip).
+    first_renderer_watcher.Wait();
+  }
+
   // ProcessMap will always have exactly 5 entries - one for each of the
   // extensions with a background page (api_test/browser_action/*).  There won't
   // be any additional entries, since 1) the isolated app will be associated
diff --git a/chrome/browser/extensions/wake_event_page_apitest.cc b/chrome/browser/extensions/wake_event_page_apitest.cc
index c0d8499c..fc7affd 100644
--- a/chrome/browser/extensions/wake_event_page_apitest.cc
+++ b/chrome/browser/extensions/wake_event_page_apitest.cc
@@ -13,7 +13,10 @@
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/notification_service.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+#include "extensions/browser/notification_types.h"
 #include "extensions/browser/process_manager.h"
 #include "extensions/browser/process_manager_observer.h"
 #include "extensions/common/extension.h"
@@ -111,8 +114,14 @@
                               kContentScriptJs);
     }
 
-    // Install the extension, then close its background page if desired..
+    // Install the extension, then close its background page if desired.
+    // TODO(https://crbug.com/898682): Waiting for content scripts to load
+    // should be done as part of the extension loading process.
+    content::WindowedNotificationObserver scripts_updated_observer(
+        extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
+        content::NotificationService::AllSources());
     const Extension* extension = LoadExtension(extension_dir.UnpackedPath());
+    scripts_updated_observer.Wait();
     CHECK(extension);
 
     // Regardless of |will_be_open|, we haven't closed the background page yet,
diff --git a/chrome/browser/external_protocol/external_protocol_handler.cc b/chrome/browser/external_protocol/external_protocol_handler.cc
index 3779df9..e33f45d 100644
--- a/chrome/browser/external_protocol/external_protocol_handler.cc
+++ b/chrome/browser/external_protocol/external_protocol_handler.cc
@@ -188,8 +188,8 @@
 
   PrefService* profile_prefs = profile->GetPrefs();
   if (profile_prefs) {  // May be NULL during testing.
-    DictionaryPrefUpdate update_excluded_schemas_profile(
-        profile_prefs, prefs::kExcludedSchemes);
+    const base::DictionaryValue* update_excluded_schemas_profile =
+        profile_prefs->GetDictionary(prefs::kExcludedSchemes);
     bool should_block;
     // Ignore stored block decisions. These are now not possible through the UI,
     // and previous block decisions should be ignored to allow users to recover
diff --git a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
index 8e710321..d7b5a63 100644
--- a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
+++ b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
@@ -32,6 +32,7 @@
 #if defined(OS_CHROMEOS)
 #include "ash/public/interfaces/constants.mojom.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
 #include "chrome/browser/metrics/chromeos_metrics_provider.h"
 #include "chromeos/dbus/util/version_loader.h"
 #include "chromeos/system/statistics_provider.h"
@@ -63,6 +64,7 @@
 constexpr char kArcStatusKey[] = "CHROMEOS_ARC_STATUS";
 constexpr char kMonitorInfoKey[] = "monitor_info";
 constexpr char kAccountTypeKey[] = "account_type";
+constexpr char kDemoModeConfigKey[] = "demo_mode_config";
 #else
 constexpr char kOsVersionTag[] = "OS VERSION";
 #endif
@@ -239,6 +241,9 @@
                                        ? "enabled"
                                        : "disabled");
   response->emplace(kAccountTypeKey, GetPrimaryAccountTypeString());
+  response->emplace(kDemoModeConfigKey,
+                    chromeos::DemoSession::DemoConfigToString(
+                        chromeos::DemoSession::GetDemoConfig()));
   PopulateLocalStateSettings(response.get());
 
   // Chain asynchronous fetchers: PopulateMonitorInfoAsync, PopulateEntriesAsync
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 9256c30..a5515562 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1394,8 +1394,6 @@
 const char kOverscrollHistoryNavigationName[] = "Overscroll history navigation";
 const char kOverscrollHistoryNavigationDescription[] =
     "History navigation in response to horizontal overscroll.";
-const char kOverscrollHistoryNavigationSimpleUi[] = "Simple";
-const char kOverscrollHistoryNavigationParallaxUi[] = "Parallax";
 
 const char kOverscrollStartThresholdName[] = "Overscroll start threshold";
 const char kOverscrollStartThresholdDescription[] =
@@ -2280,10 +2278,6 @@
 const char kDontPrefetchLibrariesDescription[] =
     "Don't prefetch libraries after loading.";
 
-const char kDownloadsForegroundName[] = "Enable downloads foreground";
-const char kDownloadsForegroundDescription[] =
-    "Enable downloads as a foreground service for all versions of Android.";
-
 const char kDownloadsLocationChangeName[] = "Enable downloads location change";
 const char kDownloadsLocationChangeDescription[] =
     "Enable changing default downloads storage location on Android.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 910dbe9..e857461 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -855,8 +855,6 @@
 
 extern const char kOverscrollHistoryNavigationName[];
 extern const char kOverscrollHistoryNavigationDescription[];
-extern const char kOverscrollHistoryNavigationSimpleUi[];
-extern const char kOverscrollHistoryNavigationParallaxUi[];
 
 extern const char kOverscrollStartThresholdName[];
 extern const char kOverscrollStartThresholdDescription[];
@@ -1384,9 +1382,6 @@
 extern const char kDontPrefetchLibrariesName[];
 extern const char kDontPrefetchLibrariesDescription[];
 
-extern const char kDownloadsForegroundName[];
-extern const char kDownloadsForegroundDescription[];
-
 extern const char kDownloadsLocationChangeName[];
 extern const char kDownloadsLocationChangeDescription[];
 
diff --git a/chrome/browser/global_keyboard_shortcuts_mac.mm b/chrome/browser/global_keyboard_shortcuts_mac.mm
index f00d6430..73378541 100644
--- a/chrome/browser/global_keyboard_shortcuts_mac.mm
+++ b/chrome/browser/global_keyboard_shortcuts_mac.mm
@@ -16,7 +16,6 @@
 #import "chrome/browser/app_controller_mac.h"
 #include "chrome/browser/ui/cocoa/accelerators_cocoa.h"
 #import "chrome/browser/ui/cocoa/nsmenuitem_additions.h"
-#include "chrome/browser/ui/views_mode_controller.h"
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/accelerators/platform_accelerator_cocoa.h"
 #include "ui/events/event_constants.h"
diff --git a/chrome/browser/global_keyboard_shortcuts_mac_browsertest.mm b/chrome/browser/global_keyboard_shortcuts_mac_browsertest.mm
index 0cad8e12..b917cd9 100644
--- a/chrome/browser/global_keyboard_shortcuts_mac_browsertest.mm
+++ b/chrome/browser/global_keyboard_shortcuts_mac_browsertest.mm
@@ -15,7 +15,6 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/views_mode_controller.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/interactive_test_utils.h"
 #include "chrome/test/base/ui_test_utils.h"
diff --git a/chrome/browser/infobars/infobars_browsertest.cc b/chrome/browser/infobars/infobars_browsertest.cc
index bcbf0e43..adcaafe 100644
--- a/chrome/browser/infobars/infobars_browsertest.cc
+++ b/chrome/browser/infobars/infobars_browsertest.cc
@@ -46,7 +46,6 @@
 #include "chrome/browser/ui/startup/obsolete_system_infobar_delegate.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/test/test_browser_ui.h"
-#include "chrome/browser/ui/views_mode_controller.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
diff --git a/chrome/browser/media/android/router/OWNERS b/chrome/browser/media/android/router/OWNERS
index 48f7fcf..6e368bb5 100644
--- a/chrome/browser/media/android/router/OWNERS
+++ b/chrome/browser/media/android/router/OWNERS
@@ -1,7 +1,6 @@
-imcheng@chromium.org
 mfoltz@chromium.org
 mlamouri@chromium.org
 zqzhang@chromium.org
 
 # TEAM: media-dev@chromium.org
-# COMPONENT: Blink>PresentationAPI
+# COMPONENT: Internals>Cast
diff --git a/chrome/browser/media/router/OWNERS b/chrome/browser/media/router/OWNERS
index be4e8ce..7290593 100644
--- a/chrome/browser/media/router/OWNERS
+++ b/chrome/browser/media/router/OWNERS
@@ -1,7 +1,5 @@
-imcheng@chromium.org
 mfoltz@chromium.org
 btolsch@chromium.org
 takumif@chromium.org
 
 # COMPONENT: Internals>Cast
-
diff --git a/chrome/browser/media/webrtc/native_desktop_media_list.cc b/chrome/browser/media/webrtc/native_desktop_media_list.cc
index 5eac54b..867c10f 100644
--- a/chrome/browser/media/webrtc/native_desktop_media_list.cc
+++ b/chrome/browser/media/webrtc/native_desktop_media_list.cc
@@ -291,6 +291,17 @@
 
   UpdateSourcesList(sources);
 
+  if (thumbnail_size_.IsEmpty()) {
+#if defined(USE_AURA)
+    pending_native_thumbnail_capture_ = true;
+#endif
+    base::PostTaskWithTraits(
+        FROM_HERE, {BrowserThread::UI},
+        base::BindOnce(&NativeDesktopMediaList::UpdateNativeThumbnailsFinished,
+                       weak_factory_.GetWeakPtr()));
+    return;
+  }
+
   // OnAuraThumbnailCaptured() and UpdateNativeThumbnailsFinished() are
   // guaranteed to be excuted after RefreshForAuraWindows() and
   // CaptureAuraWindowThumbnail() in the browser UI thread.
diff --git a/chrome/browser/media/webrtc/native_desktop_media_list_unittest.cc b/chrome/browser/media/webrtc/native_desktop_media_list_unittest.cc
index d592f7a..bb892a6 100644
--- a/chrome/browser/media/webrtc/native_desktop_media_list_unittest.cc
+++ b/chrome/browser/media/webrtc/native_desktop_media_list_unittest.cc
@@ -520,3 +520,36 @@
 
   run_loop.Run();
 }
+
+// This test verifies that webrtc::DesktopCapturer::CaptureFrame() is not
+// called when the thumbnail size is empty.
+TEST_F(NativeDesktopMediaListTest, EmptyThumbnail) {
+  window_capturer_ = new FakeWindowCapturer();
+  model_ = std::make_unique<NativeDesktopMediaList>(
+      DesktopMediaID::TYPE_WINDOW, base::WrapUnique(window_capturer_));
+  model_->SetThumbnailSize(gfx::Size());
+
+  // Set update period to reduce the time it takes to run tests.
+  model_->SetUpdatePeriod(base::TimeDelta::FromMilliseconds(20));
+
+  base::RunLoop run_loop;
+
+  EXPECT_CALL(observer_, OnSourceAdded(model_.get(), 0))
+      .WillOnce(
+          DoAll(CheckListSize(model_.get(), 1),
+                QuitRunLoop(base::ThreadTaskRunnerHandle::Get(), &run_loop)));
+  // Called upon webrtc::DesktopCapturer::CaptureFrame() call.
+  ON_CALL(observer_, OnSourceThumbnailChanged(_, _))
+      .WillByDefault(testing::InvokeWithoutArgs([]() { NOTREACHED(); }));
+
+  model_->StartUpdating(&observer_);
+
+  AddNativeWindow(0);
+  window_capturer_->SetWindowList(window_list_);
+
+  run_loop.Run();
+
+  EXPECT_EQ(model_->GetSource(0).id.type, DesktopMediaID::TYPE_WINDOW);
+  EXPECT_EQ(model_->GetSource(0).id.id, 0);
+  EXPECT_EQ(model_->GetSource(0).thumbnail.size(), gfx::Size());
+}
diff --git a/chrome/browser/media/webrtc/window_icon_util_mac.mm b/chrome/browser/media/webrtc/window_icon_util_mac.mm
index 690c875..0f4b1226 100644
--- a/chrome/browser/media/webrtc/window_icon_util_mac.mm
+++ b/chrome/browser/media/webrtc/window_icon_util_mac.mm
@@ -4,10 +4,11 @@
 
 #include "chrome/browser/media/webrtc/window_icon_util.h"
 
-#include <ApplicationServices/ApplicationServices.h>
-#include <Cocoa/Cocoa.h>
-#include <CoreFoundation/CoreFoundation.h>
+#import <Cocoa/Cocoa.h>
 
+#include "base/mac/foundation_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/stl_util.h"
 #include "third_party/libyuv/include/libyuv/convert_argb.h"
 
 gfx::ImageSkia GetWindowIcon(content::DesktopMediaID id) {
@@ -15,18 +16,18 @@
 
   CGWindowID ids[1];
   ids[0] = id.id;
-  CFArrayRef window_id_array =
-      CFArrayCreate(nullptr, reinterpret_cast<const void**>(&ids), 1, nullptr);
-  CFArrayRef window_array =
-      CGWindowListCreateDescriptionFromArray(window_id_array);
+  base::ScopedCFTypeRef<CFArrayRef> window_id_array(CFArrayCreate(
+      nullptr, reinterpret_cast<const void**>(&ids), base::size(ids), nullptr));
+  base::ScopedCFTypeRef<CFArrayRef> window_array(
+      CGWindowListCreateDescriptionFromArray(window_id_array));
   if (!window_array || 0 == CFArrayGetCount(window_array)) {
     return gfx::ImageSkia();
   }
 
-  CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
+  CFDictionaryRef window = base::mac::CFCastStrict<CFDictionaryRef>(
       CFArrayGetValueAtIndex(window_array, 0));
-  CFNumberRef pid_ref = reinterpret_cast<CFNumberRef>(
-      CFDictionaryGetValue(window, kCGWindowOwnerPID));
+  CFNumberRef pid_ref =
+      base::mac::GetValueFromDictionary<CFNumberRef>(window, kCGWindowOwnerPID);
 
   int pid;
   CFNumberGetValue(pid_ref, kCFNumberIntType, &pid);
@@ -34,32 +35,44 @@
   NSImage* icon_image =
       [[NSRunningApplication runningApplicationWithProcessIdentifier:pid] icon];
 
-  int width = [icon_image size].width;
-  int height = [icon_image size].height;
-
+  // Icon's NSImage defaults to the smallest which can be only 32x32.
+  NSRect proposed_rect = NSMakeRect(0, 0, 128, 128);
   CGImageRef cg_icon_image =
-      [icon_image CGImageForProposedRect:nil context:nil hints:nil];
+      [icon_image CGImageForProposedRect:&proposed_rect context:nil hints:nil];
 
-  int bits_per_pixel = CGImageGetBitsPerPixel(cg_icon_image);
-  if (bits_per_pixel != 32) {
+  // 4 components of 8 bits each.
+  if (CGImageGetBitsPerPixel(cg_icon_image) != 32 ||
+      CGImageGetBitsPerComponent(cg_icon_image) != 8) {
+    return gfx::ImageSkia();
+  }
+
+  // Premultiplied alpha and last (alpha channel is next to the blue channel)
+  if (CGImageGetAlphaInfo(cg_icon_image) != kCGImageAlphaPremultipliedLast) {
+    return gfx::ImageSkia();
+  }
+
+  // Ensure BGR like.
+  int byte_order = CGImageGetBitmapInfo(cg_icon_image) & kCGBitmapByteOrderMask;
+  if (byte_order != kCGBitmapByteOrderDefault &&
+      byte_order != kCGBitmapByteOrder32Big) {
     return gfx::ImageSkia();
   }
 
   CGDataProviderRef provider = CGImageGetDataProvider(cg_icon_image);
-  CFDataRef cf_data = CGDataProviderCopyData(provider);
+  base::ScopedCFTypeRef<CFDataRef> cf_data(CGDataProviderCopyData(provider));
 
+  int width = CGImageGetWidth(cg_icon_image);
+  int height = CGImageGetHeight(cg_icon_image);
   int src_stride = CGImageGetBytesPerRow(cg_icon_image);
   const uint8_t* src_data = CFDataGetBytePtr(cf_data);
 
   SkBitmap result;
-  result.allocN32Pixels(width, height, false);
+  result.allocN32Pixels(width, height, false /* no-premultiplied */);
 
   uint8_t* pixels_data = reinterpret_cast<uint8_t*>(result.getPixels());
 
   libyuv::ABGRToARGB(src_data, src_stride, pixels_data, result.rowBytes(),
                      width, height);
 
-  CFRelease(cf_data);
-
   return gfx::ImageSkia::CreateFrom1xBitmap(result);
-}
\ No newline at end of file
+}
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index 23202bf..a6b55d1d 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -41,6 +41,7 @@
 #include "chrome/browser/metrics/https_engagement_metrics_provider.h"
 #include "chrome/browser/metrics/metrics_reporting_state.h"
 #include "chrome/browser/metrics/network_quality_estimator_provider_impl.h"
+#include "chrome/browser/metrics/persistent_histograms.h"
 #include "chrome/browser/metrics/process_memory_metrics_emitter.h"
 #include "chrome/browser/metrics/sampling_metrics_provider.h"
 #include "chrome/browser/metrics/subprocess_metrics_provider.h"
@@ -186,8 +187,7 @@
 #endif  // defined(OS_WIN) || defined(OS_MACOSX)
 
 void RegisterFileMetricsPreferences(PrefRegistrySimple* registry) {
-  metrics::FileMetricsProvider::RegisterPrefs(
-      registry, ChromeMetricsServiceClient::kBrowserMetricsName);
+  metrics::FileMetricsProvider::RegisterPrefs(registry, kBrowserMetricsName);
 
   metrics::FileMetricsProvider::RegisterPrefs(registry,
                                               kCrashpadHistogramAllocatorName);
@@ -250,14 +250,14 @@
             ASSOCIATE_INTERNAL_PROFILE_OR_PREVIOUS_RUN,
         file_metrics_provider.get());
 
-    base::FilePath browser_metrics_upload_dir = user_data_dir.AppendASCII(
-        ChromeMetricsServiceClient::kBrowserMetricsName);
+    base::FilePath browser_metrics_upload_dir =
+        user_data_dir.AppendASCII(kBrowserMetricsName);
     if (metrics_reporting_enabled) {
       metrics::FileMetricsProvider::Params browser_metrics_params(
           browser_metrics_upload_dir,
           metrics::FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_DIR,
           metrics::FileMetricsProvider::ASSOCIATE_INTERNAL_PROFILE,
-          ChromeMetricsServiceClient::kBrowserMetricsName);
+          kBrowserMetricsName);
       browser_metrics_params.max_dir_kib = kMaxHistogramStorageKiB;
       browser_metrics_params.filter = base::BindRepeating(
           &ChromeMetricsServiceClient::FilterBrowserMetricsFiles);
@@ -397,8 +397,6 @@
 
 }  // namespace
 
-const char ChromeMetricsServiceClient::kBrowserMetricsName[] = "BrowserMetrics";
-
 // UKM suffix for field trial recording.
 const char kUKMFieldTrialSuffix[] = "UKM";
 
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.h b/chrome/browser/metrics/chrome_metrics_service_client.h
index 419b538..735d65b 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.h
+++ b/chrome/browser/metrics/chrome_metrics_service_client.h
@@ -106,12 +106,6 @@
       const base::FilePath& path);
   static void SetIsProcessRunningForTesting(IsProcessRunningFunction func);
 
-  // Persistent browser metrics need to be persisted somewhere. This constant
-  // provides a known string to be used for both the allocator's internal name
-  // and for a file on disk (relative to chrome::DIR_USER_DATA) to which they
-  // can be saved.
-  static const char kBrowserMetricsName[];
-
  private:
   FRIEND_TEST_ALL_PREFIXES(ChromeMetricsServiceClientTest, IsWebstoreExtension);
 
diff --git a/chrome/browser/metrics/metrics_service_browsertest.cc b/chrome/browser/metrics/metrics_service_browsertest.cc
index b412ffb4..044b442 100644
--- a/chrome/browser/metrics/metrics_service_browsertest.cc
+++ b/chrome/browser/metrics/metrics_service_browsertest.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/metrics/chrome_metrics_service_client.h"
 #include "chrome/browser/metrics/chrome_metrics_services_manager_client.h"
+#include "chrome/browser/metrics/persistent_histograms.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_paths.h"
@@ -293,8 +294,7 @@
 
     // Create the upload dir. Note that ASSERT macros won't fail in SetUp,
     // hence the use of CHECK.
-    upload_dir_ =
-        user_dir.AppendASCII(ChromeMetricsServiceClient::kBrowserMetricsName);
+    upload_dir_ = user_dir.AppendASCII(kBrowserMetricsName);
     CHECK(!base::PathExists(upload_dir_));
     CHECK(base::CreateDirectory(upload_dir_));
 
diff --git a/chrome/browser/metrics/persistent_histograms.cc b/chrome/browser/metrics/persistent_histograms.cc
new file mode 100644
index 0000000..e11d1bbb8
--- /dev/null
+++ b/chrome/browser/metrics/persistent_histograms.cc
@@ -0,0 +1,172 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/metrics/persistent_histograms.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/persistent_histogram_allocator.h"
+#include "base/path_service.h"
+#include "base/sys_info.h"
+#include "base/task/post_task.h"
+#include "build/build_config.h"
+#include "chrome/common/chrome_paths.h"
+#include "components/metrics/persistent_system_profile.h"
+#include "components/variations/variations_associated_data.h"
+
+namespace {
+
+// Creating a "spare" file for persistent metrics involves a lot of I/O and
+// isn't important so delay the operation for a while after startup.
+#if defined(OS_ANDROID)
+// Android needs the spare file and also launches faster.
+constexpr bool kSpareFileRequired = true;
+constexpr int kSpareFileCreateDelaySeconds = 10;
+#else
+// Desktop may have to restore a lot of tabs so give it more time before doing
+// non-essential work. The spare file is still a performance boost but not as
+// significant of one so it's not required.
+constexpr bool kSpareFileRequired = false;
+constexpr int kSpareFileCreateDelaySeconds = 90;
+#endif
+
+}  // namespace
+
+const char kBrowserMetricsName[] = "BrowserMetrics";
+
+// Check for feature enabling the use of persistent histogram storage and
+// enable the global allocator if so.
+void InstantiatePersistentHistograms() {
+  base::FilePath metrics_dir;
+  if (!base::PathService::Get(chrome::DIR_USER_DATA, &metrics_dir))
+    return;
+
+  // Create a directory for storing completed metrics files. Files in this
+  // directory must have embedded system profiles. If the directory can't be
+  // created, the file will just be deleted below.
+  base::FilePath upload_dir = metrics_dir.AppendASCII(kBrowserMetricsName);
+  base::CreateDirectory(upload_dir);
+
+  // Metrics files are typically created as a |spare_file| in the profile
+  // directory (e.g. "BrowserMetrics-spare.pma") and are then rotated into
+  // a subdirectory as a stamped file for upload when no longer in use.
+  // (e.g. "BrowserMetrics/BrowserMetrics-1234ABCD-12345.pma")
+  base::FilePath upload_file;
+  base::FilePath active_file;
+  base::FilePath spare_file;
+  base::GlobalHistogramAllocator::ConstructFilePathsForUploadDir(
+      metrics_dir, upload_dir, kBrowserMetricsName, &upload_file, &active_file,
+      &spare_file);
+
+  // This is used to report results to an UMA histogram.
+  enum InitResult {
+    kLocalMemorySuccess,
+    kLocalMemoryFailed,
+    kMappedFileSuccess,
+    kMappedFileFailed,
+    kMappedFileExists,
+    kNoSpareFile,
+    kNoUploadDir,
+    kMaxValue = kNoUploadDir
+  };
+  InitResult result;
+
+  // Create persistent/shared memory and allow histograms to be stored in
+  // it. Memory that is not actualy used won't be physically mapped by the
+  // system. BrowserMetrics usage, as reported in UMA, has the 99.99
+  // percentile around 3MiB as of 2018-10-22.
+  const size_t kAllocSize = 4 << 20;     // 4 MiB
+  const uint32_t kAllocId = 0x935DDD43;  // SHA1(BrowserMetrics)
+  std::string storage = variations::GetVariationParamValueByFeature(
+      base::kPersistentHistogramsFeature, "storage");
+
+  static const char kMappedFile[] = "MappedFile";
+  static const char kLocalMemory[] = "LocalMemory";
+
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+  // Linux kernel 4.4.0.* shows a huge number of SIGBUS crashes with persistent
+  // histograms enabled using a mapped file.  Change this to use local memory.
+  // https://bugs.chromium.org/p/chromium/issues/detail?id=753741
+  if (storage.empty() || storage == kMappedFile) {
+    int major, minor, bugfix;
+    base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix);
+    if (major == 4 && minor == 4 && bugfix == 0)
+      storage = kLocalMemory;
+  }
+#endif
+
+  // Don't use mapped-file memory by default on low-end devices, especially
+  // Android. The extra disk consumption and/or extra disk access could have
+  // a significant performance impact. https://crbug.com/896394
+  if (storage.empty() && base::SysInfo::IsLowEndDevice())
+    storage = kLocalMemory;
+
+  // Create a global histogram allocator using the desired storage type.
+  if (storage.empty() || storage == kMappedFile) {
+    if (!base::PathExists(upload_dir)) {
+      // Handle failure to create the directory.
+      result = kNoUploadDir;
+    } else if (base::PathExists(upload_file)) {
+      // "upload" filename is supposed to be unique so this shouldn't happen.
+      result = kMappedFileExists;
+    } else {
+      // Move any sparse file into the upload position.
+      base::ReplaceFile(spare_file, upload_file, nullptr);
+      // Create global allocator using the "upload" file.
+      if (kSpareFileRequired && !base::PathExists(upload_file)) {
+        result = kNoSpareFile;
+      } else if (base::GlobalHistogramAllocator::CreateWithFile(
+                     upload_file, kAllocSize, kAllocId, kBrowserMetricsName)) {
+        result = kMappedFileSuccess;
+      } else {
+        result = kMappedFileFailed;
+      }
+    }
+    // Schedule the creation of a "spare" file for use on the next run.
+    base::PostDelayedTaskWithTraits(
+        FROM_HERE,
+        {base::MayBlock(), base::TaskPriority::LOWEST,
+         base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+        base::BindOnce(base::IgnoreResult(
+                           &base::GlobalHistogramAllocator::CreateSpareFile),
+                       std::move(spare_file), kAllocSize),
+        base::TimeDelta::FromSeconds(kSpareFileCreateDelaySeconds));
+  } else if (storage == kLocalMemory) {
+    // Use local memory for storage even though it will not persist across
+    // an unclean shutdown. This sets the result but the actual creation is
+    // done below.
+    result = kLocalMemorySuccess;
+  } else {
+    // Persistent metric storage is disabled. Must return here.
+    return;
+  }
+
+  // Get the allocator that was just created and report result. Exit if the
+  // allocator could not be created.
+  UMA_HISTOGRAM_ENUMERATION("UMA.PersistentHistograms.InitResult", result);
+
+  base::GlobalHistogramAllocator* allocator =
+      base::GlobalHistogramAllocator::Get();
+  if (!allocator) {
+    // If no allocator was created above, try to create a LocalMemomory one
+    // here. This avoids repeating the call many times above. In the case where
+    // persistence is disabled, an early return is done above.
+    base::GlobalHistogramAllocator::CreateWithLocalMemory(kAllocSize, kAllocId,
+                                                          kBrowserMetricsName);
+    allocator = base::GlobalHistogramAllocator::Get();
+    if (!allocator)
+      return;
+  }
+
+  // Store a copy of the system profile in this allocator.
+  metrics::GlobalPersistentSystemProfile::GetInstance()
+      ->RegisterPersistentAllocator(allocator->memory_allocator());
+
+  // Create tracking histograms for the allocator and record storage file.
+  allocator->CreateTrackingHistograms(kBrowserMetricsName);
+}
diff --git a/chrome/browser/metrics/persistent_histograms.h b/chrome/browser/metrics/persistent_histograms.h
new file mode 100644
index 0000000..0bae8f76
--- /dev/null
+++ b/chrome/browser/metrics/persistent_histograms.h
@@ -0,0 +1,18 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_METRICS_PERSISTENT_HISTOGRAMS_H_
+#define CHROME_BROWSER_METRICS_PERSISTENT_HISTOGRAMS_H_
+
+// Persistent browser metrics need to be persisted somewhere. This constant
+// provides a known string to be used for both the allocator's internal name
+// and for a file on disk (relative to chrome::DIR_USER_DATA) to which they
+// can be saved. This is exported so the name can also be used as a "pref"
+// during configuration.
+extern const char kBrowserMetricsName[];
+
+// Do all the checking and work necessary to enable persistent histograms.
+void InstantiatePersistentHistograms();
+
+#endif  // CHROME_BROWSER_METRICS_PERSISTENT_HISTOGRAMS_H_
diff --git a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc
index 2e75356..83d4f7e 100644
--- a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc
@@ -91,6 +91,8 @@
     "PageLoad.Experimental.PaintTiming.NavigationToFirstMeaningfulPaint";
 const char kHistogramLargestImagePaint[] =
     "PageLoad.Experimental.PaintTiming.NavigationToLargestImagePaint";
+const char kHistogramLastImagePaint[] =
+    "PageLoad.Experimental.PaintTiming.NavigationToLastImagePaint";
 const char kHistogramTimeToInteractive[] =
     "PageLoad.Experimental.NavigationToInteractive";
 const char kHistogramInteractiveToInteractiveDetection[] =
@@ -533,6 +535,18 @@
   }
 }
 
+void CorePageLoadMetricsObserver::OnLastImagePaintInMainFrameDocument(
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& info) {
+  base::Optional<base::TimeDelta>& last_image_paint =
+      timing.paint_timing->last_image_paint;
+  if (last_image_paint.has_value() &&
+      WasStartedInForegroundOptionalEventInForeground(last_image_paint, info)) {
+    PAGE_LOAD_HISTOGRAM(internal::kHistogramLastImagePaint,
+                        last_image_paint.value());
+  }
+}
+
 void CorePageLoadMetricsObserver::OnPageInteractive(
     const page_load_metrics::mojom::PageLoadTiming& timing,
     const page_load_metrics::PageLoadExtraInfo& info) {
diff --git a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h
index 84f4521..1dfe1f2 100644
--- a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h
@@ -26,6 +26,7 @@
 extern const char kHistogramFirstContentfulPaint[];
 extern const char kHistogramFirstMeaningfulPaint[];
 extern const char kHistogramLargestImagePaint[];
+extern const char kHistogramLastImagePaint[];
 extern const char kHistogramTimeToInteractive[];
 extern const char kHistogramParseDuration[];
 extern const char kHistogramParseBlockedOnScriptLoad[];
@@ -214,6 +215,9 @@
   void OnLargestImagePaintInMainFrameDocument(
       const page_load_metrics::mojom::PageLoadTiming& timing,
       const page_load_metrics::PageLoadExtraInfo& info) override;
+  void OnLastImagePaintInMainFrameDocument(
+      const page_load_metrics::mojom::PageLoadTiming& timing,
+      const page_load_metrics::PageLoadExtraInfo& info) override;
 
  private:
   void RecordTimingHistograms(
diff --git a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc
index 2783914e..16bf154 100644
--- a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc
@@ -743,6 +743,89 @@
       testing::ElementsAre(base::Bucket(4780, 1)));
 }
 
+TEST_F(CorePageLoadMetricsObserverTest, LastImagePaint) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  page_load_metrics::InitPageLoadTimingForTest(&timing);
+  timing.navigation_start = base::Time::FromDoubleT(1);
+  // Pick a value that lines up with a histogram bucket.
+  timing.paint_timing->last_image_paint =
+      base::TimeDelta::FromMilliseconds(4780);
+  PopulateRequiredTimingFields(&timing);
+
+  NavigateAndCommit(GURL(kDefaultTestUrl));
+  SimulateTimingUpdate(timing);
+  // Navigate again to force histogram recording.
+  NavigateAndCommit(GURL(kDefaultTestUrl2));
+
+  EXPECT_THAT(
+      histogram_tester().GetAllSamples(internal::kHistogramLastImagePaint),
+      testing::ElementsAre(base::Bucket(4780, 1)));
+}
+
+TEST_F(CorePageLoadMetricsObserverTest,
+       LastImagePaint_DiscardBackgroundResult) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  page_load_metrics::InitPageLoadTimingForTest(&timing);
+  PopulateRequiredTimingFields(&timing);
+
+  NavigateAndCommit(GURL(kDefaultTestUrl));
+  timing.navigation_start = base::Time::FromDoubleT(1);
+  web_contents()->WasHidden();
+  // Set a large enough value to make sure it will be larger than background
+  // time, so that the result will be discarded.
+  timing.paint_timing->last_image_paint = base::TimeDelta::FromSeconds(10);
+  SimulateTimingUpdate(timing);
+  // Navigate again to force histogram recording.
+  NavigateAndCommit(GURL(kDefaultTestUrl2));
+
+  histogram_tester().ExpectTotalCount(internal::kHistogramLastImagePaint, 0);
+}
+
+TEST_F(CorePageLoadMetricsObserverTest, LastImagePaint_ReportLastCandidate) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  page_load_metrics::InitPageLoadTimingForTest(&timing);
+
+  NavigateAndCommit(GURL(kDefaultTestUrl));
+  timing.navigation_start = base::Time::FromDoubleT(1);
+  timing.paint_timing->last_image_paint =
+      base::TimeDelta::FromMilliseconds(1000);
+  PopulateRequiredTimingFields(&timing);
+  SimulateTimingUpdate(timing);
+
+  timing.paint_timing->last_image_paint =
+      base::TimeDelta::FromMilliseconds(4780);
+  PopulateRequiredTimingFields(&timing);
+  SimulateTimingUpdate(timing);
+  // Navigate again to force histogram recording.
+  NavigateAndCommit(GURL(kDefaultTestUrl2));
+
+  EXPECT_THAT(
+      histogram_tester().GetAllSamples(internal::kHistogramLastImagePaint),
+      testing::ElementsAre(base::Bucket(4780, 1)));
+}
+
+TEST_F(CorePageLoadMetricsObserverTest,
+       LastImagePaint_ReportLastNullCandidate) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  page_load_metrics::InitPageLoadTimingForTest(&timing);
+
+  NavigateAndCommit(GURL(kDefaultTestUrl));
+  timing.navigation_start = base::Time::FromDoubleT(1);
+
+  timing.paint_timing->last_image_paint =
+      base::TimeDelta::FromMilliseconds(1000);
+  PopulateRequiredTimingFields(&timing);
+  SimulateTimingUpdate(timing);
+
+  timing.paint_timing->last_image_paint = base::Optional<base::TimeDelta>();
+  PopulateRequiredTimingFields(&timing);
+  SimulateTimingUpdate(timing);
+  // Navigate again to force histogram recording.
+  NavigateAndCommit(GURL(kDefaultTestUrl2));
+
+  histogram_tester().ExpectTotalCount(internal::kHistogramLastImagePaint, 0);
+}
+
 TEST_F(CorePageLoadMetricsObserverTest, ForegroundToFirstMeaningfulPaint) {
   page_load_metrics::mojom::PageLoadTiming timing;
   page_load_metrics::InitPageLoadTimingForTest(&timing);
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
index a69e6d9..33ccc6ba 100644
--- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
@@ -171,6 +171,12 @@
     builder.SetExperimental_PaintTiming_NavigationToLargestImagePaint(
         timing.paint_timing->largest_image_paint.value().InMilliseconds());
   }
+  if (timing.paint_timing->last_image_paint.has_value() &&
+      WasStartedInForegroundOptionalEventInForeground(
+          timing.paint_timing->last_image_paint, info)) {
+    builder.SetExperimental_PaintTiming_NavigationToLastImagePaint(
+        timing.paint_timing->last_image_paint.value().InMilliseconds());
+  }
   if (timing.interactive_timing->interactive) {
     base::TimeDelta time_to_interactive =
         timing.interactive_timing->interactive.value();
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
index 02a7774b8..0beaf8a3 100644
--- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
@@ -286,6 +286,91 @@
   EXPECT_EQ(0ul, merged_entries.size());
 }
 
+TEST_F(UkmPageLoadMetricsObserverTest, LastImagePaint) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  page_load_metrics::InitPageLoadTimingForTest(&timing);
+  timing.navigation_start = base::Time::FromDoubleT(1);
+  timing.paint_timing->last_image_paint =
+      base::TimeDelta::FromMilliseconds(600);
+  PopulateRequiredTimingFields(&timing);
+
+  NavigateAndCommit(GURL(kTestUrl1));
+  SimulateTimingUpdate(timing);
+
+  // Simulate closing the tab.
+  DeleteContents();
+
+  std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
+      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+  EXPECT_EQ(1ul, merged_entries.size());
+
+  for (const auto& kv : merged_entries) {
+    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                GURL(kTestUrl1));
+    test_ukm_recorder().ExpectEntryMetric(
+        kv.second.get(),
+        PageLoad::kExperimental_PaintTiming_NavigationToLastImagePaintName,
+        600);
+    EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
+        kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
+  }
+}
+
+TEST_F(UkmPageLoadMetricsObserverTest, LastImagePaint_DiscardBackgroundResult) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  page_load_metrics::InitPageLoadTimingForTest(&timing);
+
+  PopulateRequiredTimingFields(&timing);
+
+  web_contents()->WasHidden();
+  // Set a large enough value to make sure it will be larger than background
+  // time, so that the result will be discarded.
+  timing.paint_timing->last_image_paint = base::TimeDelta::FromSeconds(10);
+  NavigateAndCommit(GURL(kTestUrl1));
+  SimulateTimingUpdate(timing);
+
+  // Simulate closing the tab.
+  DeleteContents();
+
+  std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
+      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+  EXPECT_EQ(0ul, merged_entries.size());
+}
+
+TEST_F(UkmPageLoadMetricsObserverTest, LastImagePaint_ReportLastCandidate) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  page_load_metrics::InitPageLoadTimingForTest(&timing);
+  timing.navigation_start = base::Time::FromDoubleT(1);
+
+  PopulateRequiredTimingFields(&timing);
+
+  NavigateAndCommit(GURL(kTestUrl1));
+  timing.paint_timing->last_image_paint = base::TimeDelta::FromMilliseconds(60);
+  SimulateTimingUpdate(timing);
+
+  timing.paint_timing->last_image_paint =
+      base::TimeDelta::FromMilliseconds(600);
+  SimulateTimingUpdate(timing);
+
+  // Simulate closing the tab.
+  DeleteContents();
+
+  std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
+      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+  EXPECT_EQ(1ul, merged_entries.size());
+
+  for (const auto& kv : merged_entries) {
+    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                GURL(kTestUrl1));
+    test_ukm_recorder().ExpectEntryMetric(
+        kv.second.get(),
+        PageLoad::kExperimental_PaintTiming_NavigationToLastImagePaintName,
+        600);
+    EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
+        kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
+  }
+}
+
 TEST_F(UkmPageLoadMetricsObserverTest, PageInteractive) {
   page_load_metrics::mojom::PageLoadTiming timing;
   page_load_metrics::InitPageLoadTimingForTest(&timing);
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_observer.h b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
index cb923ea..8c69b7f 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
@@ -413,12 +413,14 @@
       const mojom::PageLoadTiming& timing,
       const PageLoadExtraInfo& extra_info) {}
 
-  // As we want to report only the last one of Largest Image Paint candidates,
-  // we buffer and keep updating the candidate. The last candidate will be
-  // passed in this signature. This signature will be invoked at the end of page
-  // load's life time, around the time of the OnComplete callback.
+  // These signatures are used to report the last candidate for each of FCP++
+  // metrics. They will be invoked at the end of page load's life time, around
+  // the time of the OnComplete callback.
   virtual void OnLargestImagePaintInMainFrameDocument(
-      const mojom::PageLoadTiming& timing,
+      const mojom::PageLoadTiming& last_candidate,
+      const page_load_metrics::PageLoadExtraInfo& info) {}
+  virtual void OnLastImagePaintInMainFrameDocument(
+      const mojom::PageLoadTiming& last_candidate,
       const page_load_metrics::PageLoadExtraInfo& info) {}
 
   virtual void OnPageInteractive(const mojom::PageLoadTiming& timing,
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc
index 6f637d1..91780eb 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc
@@ -433,6 +433,13 @@
     largest_image_paint_.reset();
     should_dispatch = true;
   }
+  if (last_image_paint_) {
+    pending_merged_page_timing_->paint_timing->last_image_paint.swap(
+        last_image_paint_);
+    // Reset it so multiple shutdowns will have only one dispatch.
+    last_image_paint_.reset();
+    should_dispatch = true;
+  }
 
   if (should_dispatch) {
     DispatchTimingUpdates();
@@ -559,11 +566,13 @@
   mojom::InteractiveTimingPtr last_interactive_timing =
       std::move(pending_merged_page_timing_->interactive_timing);
 
-  // Buffer the latest candidate in largest_image_paint_. We will dispatch the
-  // last candidate at the page load end. Because we don't want to dispatch the
-  // non-last candidate here, we clear it from |new_timing|.
+  // Update the latest candidate to the corresponding buffers. We will dispatch
+  // the last candidate at the page load end. Because we don't want to dispatch
+  // the non-last candidate here, we clear it from |new_timing|.
   largest_image_paint_.swap(new_timing.paint_timing->largest_image_paint);
   new_timing.paint_timing->largest_image_paint.reset();
+  last_image_paint_.swap(new_timing.paint_timing->last_image_paint);
+  new_timing.paint_timing->last_image_paint.reset();
 
   // Update the pending_merged_page_timing_, making sure to merge the previously
   // observed |paint_timing| and |interactive_timing|, which are tracked across
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h
index 5461d856..00d3c29 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h
@@ -170,11 +170,12 @@
   // Time the navigation for this page load was initiated.
   const base::TimeTicks navigation_start_;
 
-  // As Largest Image Paint need to report the last candidate, this attribute is
+  // As FCP++ metrics need to report the last candidate, this attributes are
   // used as a buffer to store the latest one of the updating candidate. We
-  // buffer it here in a private member and only merge it back into
+  // buffer them as private members and only merge them back into
   // current_merged_page_timing_ at the end of the pageload life time.
   base::Optional<base::TimeDelta> largest_image_paint_;
+  base::Optional<base::TimeDelta> last_image_paint_;
 
   // PageLoadTiming for the currently tracked page. The fields in |paint_timing|
   // are merged across all frames in the document. All other fields are from the
diff --git a/chrome/browser/page_load_metrics/page_load_tracker.cc b/chrome/browser/page_load_metrics/page_load_tracker.cc
index 9a7f62b..c4d63acf 100644
--- a/chrome/browser/page_load_metrics/page_load_tracker.cc
+++ b/chrome/browser/page_load_metrics/page_load_tracker.cc
@@ -154,6 +154,9 @@
   if (new_timing.paint_timing->largest_image_paint &&
       !last_timing.paint_timing->largest_image_paint)
     observer->OnLargestImagePaintInMainFrameDocument(new_timing, extra_info);
+  if (new_timing.paint_timing->last_image_paint &&
+      !last_timing.paint_timing->last_image_paint)
+    observer->OnLastImagePaintInMainFrameDocument(new_timing, extra_info);
   if (new_timing.interactive_timing->interactive &&
       !last_timing.interactive_timing->interactive)
     observer->OnPageInteractive(new_timing, extra_info);
diff --git a/chrome/browser/prefs/pref_service_incognito_whitelist.cc b/chrome/browser/prefs/pref_service_incognito_whitelist.cc
index 50f7b8a..bfaaa11 100644
--- a/chrome/browser/prefs/pref_service_incognito_whitelist.cc
+++ b/chrome/browser/prefs/pref_service_incognito_whitelist.cc
@@ -46,6 +46,7 @@
     ash::prefs::kAccessibilityAutoclickEnabled,
     ash::prefs::kAccessibilityAutoclickDelayMs,
     ash::prefs::kAccessibilityAutoclickEventType,
+    ash::prefs::kAccessibilityAutoclickRevertToLeftClick,
     ash::prefs::kAccessibilityCaretHighlightEnabled,
     ash::prefs::kAccessibilityCursorHighlightEnabled,
     ash::prefs::kAccessibilityFocusHighlightEnabled,
diff --git a/chrome/browser/previews/previews_lite_page_browsertest.cc b/chrome/browser/previews/previews_lite_page_browsertest.cc
index b6f024e..626d718 100644
--- a/chrome/browser/previews/previews_lite_page_browsertest.cc
+++ b/chrome/browser/previews/previews_lite_page_browsertest.cc
@@ -71,6 +71,10 @@
 
     // Previews server will respond with HTTP 403.
     kAuthFailure = 4,
+
+    // Previews server will respond with HTTP 307 to a non-preview page and set
+    // the host-blacklist header value.
+    kHostBlacklist = 5,
   };
 
   void SetUpCommandLine(base::CommandLine* cmd) override {
@@ -353,14 +357,14 @@
     return base_https_lite_page_url().ReplaceComponents(replacements);
   }
 
-  void ClearSingleBypass() const {
+  void ClearDeciderState() const {
     PreviewsService* previews_service =
         PreviewsServiceFactory::GetForProfile(browser()->profile());
     ASSERT_TRUE(previews_service);
     PreviewsLitePageDecider* decider =
         previews_service->previews_lite_page_decider();
     ASSERT_TRUE(decider);
-    decider->ClearSingleBypassForTesting();
+    decider->ClearStateForTesting();
   }
 
   virtual GURL previews_server() const { return previews_server_->base_url(); }
@@ -473,6 +477,15 @@
         // test server will respond with HTTP 400.
         response->AddCustomHeader("Location", HttpsLitePageURL(kBypass).spec());
         break;
+      case kHostBlacklist:
+        response->set_code(net::HTTP_TEMPORARY_REDIRECT);
+        // This will not cause a redirect loop because on following this
+        // redirect, the URL will no longer be a preview URL and the embedded
+        // test server will respond with HTTP 400.
+        response->AddCustomHeader("Location",
+                                  HttpsLitePageURL(kHostBlacklist).spec());
+        response->AddCustomHeader("chrome-proxy", "host-blacklisted");
+        break;
       case kAuthFailure:
         response->set_code(net::HTTP_FORBIDDEN);
         break;
@@ -533,7 +546,7 @@
     base::HistogramTester histogram_tester;
     ui_test_utils::NavigateToURL(browser(), HttpLitePageURL(kSuccess));
     VerifyPreviewNotLoaded();
-    ClearSingleBypass();
+    ClearDeciderState();
     histogram_tester.ExpectBucketCount(
         "Previews.ServerLitePage.IneligibleReasons",
         PreviewsLitePageNavigationThrottle::IneligibleReason::kNonHttpsScheme,
@@ -556,7 +569,7 @@
     base::HistogramTester histogram_tester;
     ui_test_utils::NavigateToURL(browser(), https_media_url());
     VerifyPreviewNotLoaded();
-    ClearSingleBypass();
+    ClearDeciderState();
     histogram_tester.ExpectBucketCount(
         "Previews.ServerLitePage.BlacklistReasons",
         PreviewsLitePageNavigationThrottle::BlacklistReason::
@@ -579,7 +592,7 @@
         post_data.data(), post_data.size());
     ui_test_utils::NavigateToURL(&params);
     VerifyPreviewNotLoaded();
-    ClearSingleBypass();
+    ClearDeciderState();
     histogram_tester.ExpectBucketCount(
         "Previews.ServerLitePage.IneligibleReasons",
         PreviewsLitePageNavigationThrottle::IneligibleReason::kHttpPost, 1);
@@ -667,7 +680,7 @@
     ui_test_utils::NavigateToURL(browser(), HttpLitePageURL(kSuccess));
 
     VerifyPreviewNotLoaded();
-    ClearSingleBypass();
+    ClearDeciderState();
     histogram_tester.ExpectBucketCount(
         "Previews.ServerLitePage.IneligibleReasons",
         PreviewsLitePageNavigationThrottle::IneligibleReason::kNetworkNotSlow,
@@ -739,7 +752,7 @@
     base::HistogramTester histogram_tester;
     ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kRedirect));
     VerifyPreviewNotLoaded();
-    ClearSingleBypass();
+    ClearDeciderState();
     histogram_tester.ExpectBucketCount("Previews.ServerLitePage.Triggered",
                                        true, 1);
     histogram_tester.ExpectBucketCount(
@@ -764,7 +777,7 @@
     base::HistogramTester histogram_tester;
     ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kBypass));
     VerifyPreviewNotLoaded();
-    ClearSingleBypass();
+    ClearDeciderState();
     histogram_tester.ExpectBucketCount("Previews.ServerLitePage.Triggered",
                                        true, 1);
     histogram_tester.ExpectTotalCount(
@@ -773,6 +786,38 @@
         "Previews.ServerLitePage.ServerResponse",
         PreviewsLitePageNavigationThrottle::ServerResponse::kPreviewUnavailable,
         1);
+    histogram_tester.ExpectBucketCount(
+        "Previews.ServerLitePage.HostBlacklistedOnBypass", false, 1);
+  }
+
+  {
+    // Verify the preview is not triggered when the server responds with bypass
+    // 307 and host-blacklist.
+    base::HistogramTester histogram_tester;
+    ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kHostBlacklist));
+    VerifyPreviewNotLoaded();
+
+    histogram_tester.ExpectBucketCount("Previews.ServerLitePage.Triggered",
+                                       true, 1);
+    histogram_tester.ExpectTotalCount(
+        "Previews.ServerLitePage.HttpOnlyFallbackPenalty", 1);
+    histogram_tester.ExpectBucketCount(
+        "Previews.ServerLitePage.ServerResponse",
+        PreviewsLitePageNavigationThrottle::ServerResponse::kPreviewUnavailable,
+        1);
+    histogram_tester.ExpectBucketCount(
+        "Previews.ServerLitePage.HostBlacklistedOnBypass", true, 1);
+
+    ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
+    VerifyPreviewNotLoaded();
+    histogram_tester.ExpectBucketCount(
+        "Previews.ServerLitePage.BlacklistReasons",
+        PreviewsLitePageNavigationThrottle::BlacklistReason::kHostBlacklisted,
+        1);
+    ClearDeciderState();
+
+    ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
+    VerifyPreviewLoaded();
   }
 
   {
@@ -780,7 +825,7 @@
     base::HistogramTester histogram_tester;
     ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kAuthFailure));
     VerifyPreviewNotLoaded();
-    ClearSingleBypass();
+    ClearDeciderState();
     histogram_tester.ExpectBucketCount("Previews.ServerLitePage.Triggered",
                                        true, 1);
     histogram_tester.ExpectTotalCount(
@@ -795,7 +840,7 @@
     base::HistogramTester histogram_tester;
     ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kLoadshed));
     VerifyPreviewNotLoaded();
-    ClearSingleBypass();
+    ClearDeciderState();
     histogram_tester.ExpectBucketCount("Previews.ServerLitePage.Triggered",
                                        true, 1);
     histogram_tester.ExpectTotalCount(
@@ -832,12 +877,12 @@
   // duration [1 min, 5 mins).
   ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kLoadshed));
   VerifyPreviewNotLoaded();
-  ClearSingleBypass();
+  ClearDeciderState();
 
   clock->Advance(base::TimeDelta::FromMinutes(1));
   ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
   VerifyPreviewNotLoaded();
-  ClearSingleBypass();
+  ClearDeciderState();
 
   clock->Advance(base::TimeDelta::FromMinutes(4));
   ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
@@ -849,11 +894,11 @@
   ui_test_utils::NavigateToURL(browser(),
                                HttpsLitePageURL(kLoadshed, &headers));
   VerifyPreviewNotLoaded();
-  ClearSingleBypass();
+  ClearDeciderState();
 
   ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
   VerifyPreviewNotLoaded();
-  ClearSingleBypass();
+  ClearDeciderState();
 
   clock->Advance(base::TimeDelta::FromSeconds(31));
   ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
@@ -902,7 +947,7 @@
   // Go to a new page that doesn't Preview.
   ui_test_utils::NavigateToURL(browser(), http_url());
   VerifyPreviewNotLoaded();
-  ClearSingleBypass();
+  ClearDeciderState();
 
   // Note: |VerifyPreviewLoaded| calls |content::WaitForLoadStop()| so these are
   // safe.
@@ -914,7 +959,7 @@
   // Navigate forward.
   GetWebContents()->GetController().GoForward();
   VerifyPreviewNotLoaded();
-  ClearSingleBypass();
+  ClearDeciderState();
 
   // Navigate back again.
   GetWebContents()->GetController().GoBack();
@@ -952,7 +997,7 @@
     ui_test_utils::NavigateToURL(browser(),
                                  HttpsLitePageURL(kSuccess, nullptr, -1));
     VerifyPreviewNotLoaded();
-    ClearSingleBypass();
+    ClearDeciderState();
     histogram_tester.ExpectBucketCount(
         "Previews.ServerLitePage.ServerResponse",
         PreviewsLitePageNavigationThrottle::ServerResponse::kTimeout, 1);
@@ -963,7 +1008,7 @@
     base::HistogramTester histogram_tester;
     ui_test_utils::NavigateToURL(browser(), slow_http_url());
     VerifyPreviewNotLoaded();
-    ClearSingleBypass();
+    ClearDeciderState();
     histogram_tester.ExpectTotalCount("Previews.ServerLitePage.ServerResponse",
                                       0);
   }
@@ -1004,7 +1049,7 @@
     base::HistogramTester histogram_tester;
     ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
     VerifyPreviewNotLoaded();
-    ClearSingleBypass();
+    ClearDeciderState();
 
     histogram_tester.ExpectBucketCount("Previews.ServerLitePage.Triggered",
                                        true, 1);
@@ -1045,7 +1090,7 @@
   // Verify the preview is not triggered on HTTPS pageloads without DataSaver.
   ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
   VerifyPreviewNotLoaded();
-  ClearSingleBypass();
+  ClearDeciderState();
 }
 
 class PreviewsLitePageServerNoDataSaverHeaderBrowserTest
@@ -1082,7 +1127,7 @@
   // Verify the preview is not triggered on HTTPS pageloads without data saver.
   ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
   VerifyPreviewNotLoaded();
-  ClearSingleBypass();
+  ClearDeciderState();
 }
 
 class PreviewsLitePageNotificationDSEnabledBrowserTest
@@ -1125,7 +1170,7 @@
   ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
 
   VerifyPreviewNotLoaded();
-  ClearSingleBypass();
+  ClearDeciderState();
   EXPECT_EQ(1U, GetInfoBarService()->infobar_count());
   histogram_tester.ExpectBucketCount(
       "Previews.ServerLitePage.IneligibleReasons",
@@ -1182,6 +1227,6 @@
                        MAYBE_LitePagePreviewsInfoBarNonDataSaverUser) {
   ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
   VerifyPreviewNotLoaded();
-  ClearSingleBypass();
+  ClearDeciderState();
   EXPECT_EQ(0U, GetInfoBarService()->infobar_count());
 }
diff --git a/chrome/browser/previews/previews_lite_page_decider.cc b/chrome/browser/previews/previews_lite_page_decider.cc
index eed0629..f3c43a2 100644
--- a/chrome/browser/previews/previews_lite_page_decider.cc
+++ b/chrome/browser/previews/previews_lite_page_decider.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/previews/previews_lite_page_decider.h"
 
+#include <vector>
+
 #include "base/callback.h"
 #include "base/memory/ptr_util.h"
 #include "base/rand_util.h"
@@ -34,7 +36,45 @@
 namespace {
 const char kUserNeedsNotification[] =
     "previews.litepage.user-needs-notification";
+const char kHostBlacklist[] = "previews.litepage.host-blacklist";
+
+const size_t kMaxBlacklistEntries = 30;
+
+// Cleans up the given host blacklist by removing all stale (expiry has passed)
+// entries. If after removing all stale entries, the blacklist is still over
+// capacity, then remove the entry with the closest expiration.
+void RemoveStaleEntries(base::DictionaryValue* dict) {
+  std::vector<std::string> keys_to_delete;
+
+  base::Time min_value = base::Time::Max();
+  std::string min_key;
+  for (const auto& iter : dict->DictItems()) {
+    base::Time value = base::Time::FromDoubleT(iter.second.GetDouble());
+
+    // Delete all stale entries.
+    if (value <= base::Time::Now()) {
+      keys_to_delete.push_back(iter.first);
+      continue;
+    }
+
+    // Record the closest expiration in case we need it later on.
+    if (value < min_value) {
+      min_value = value;
+      min_key = iter.first;
+    }
+  }
+
+  // Remove all expired entries.
+  for (const std::string& key : keys_to_delete)
+    dict->RemoveKey(key);
+
+  // Remove the closest expiration if needed.
+  if (dict->DictSize() > kMaxBlacklistEntries)
+    dict->RemoveKey(min_key);
+
+  DCHECK_GE(kMaxBlacklistEntries, dict->DictSize());
 }
+}  // namespace
 
 // This WebContentsObserver watches the rest of the current navigation shows a
 // notification to the user that this preview now exists and will be used on
@@ -87,7 +127,8 @@
     : clock_(base::DefaultTickClock::GetInstance()),
       page_id_(base::RandUint64()),
       drp_settings_(nullptr),
-      pref_service_(nullptr) {
+      pref_service_(nullptr),
+      host_blacklist_(std::make_unique<base::DictionaryValue>()) {
   if (!browser_context)
     return;
 
@@ -105,6 +146,9 @@
   Profile* profile = Profile::FromBrowserContext(browser_context);
   pref_service_ = profile->GetPrefs();
   DCHECK(pref_service_);
+
+  host_blacklist_ =
+      pref_service_->GetDictionary(kHostBlacklist)->CreateDeepCopy();
 }
 
 PreviewsLitePageDecider::~PreviewsLitePageDecider() = default;
@@ -113,6 +157,8 @@
 void PreviewsLitePageDecider::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
   registry->RegisterBooleanPref(kUserNeedsNotification, true);
+  registry->RegisterDictionaryPref(kHostBlacklist,
+                                   std::make_unique<base::DictionaryValue>());
 }
 
 // static
@@ -184,8 +230,16 @@
   drp_settings_->AddDataReductionProxySettingsObserver(this);
 }
 
-void PreviewsLitePageDecider::ClearSingleBypassForTesting() {
+void PreviewsLitePageDecider::ClearBlacklist() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  host_blacklist_->Clear();
+  if (pref_service_)
+    pref_service_->Set(kHostBlacklist, *host_blacklist_);
+}
+
+void PreviewsLitePageDecider::ClearStateForTesting() {
   single_bypass_.clear();
+  host_blacklist_->Clear();
 }
 
 void PreviewsLitePageDecider::SetUserHasSeenUINotification() {
@@ -289,3 +343,26 @@
       base::BindOnce(&PreviewsLitePageDecider::SetUserHasSeenUINotification,
                      base::Unretained(this)));
 }
+
+void PreviewsLitePageDecider::BlacklistHost(const std::string& host,
+                                            base::TimeDelta duration) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // If there is an existing entry, intentionally update it.
+  host_blacklist_->SetKey(
+      host, base::Value((base::Time::Now() + duration).ToDoubleT()));
+
+  RemoveStaleEntries(host_blacklist_.get());
+  if (pref_service_)
+    pref_service_->Set(kHostBlacklist, *host_blacklist_);
+}
+
+bool PreviewsLitePageDecider::HostBlacklisted(const std::string& host) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  base::Value* value = host_blacklist_->FindKey(host);
+  if (!value)
+    return false;
+
+  DCHECK(value->is_double());
+  base::Time expiry = base::Time::FromDoubleT(value->GetDouble());
+  return expiry > base::Time::Now();
+}
diff --git a/chrome/browser/previews/previews_lite_page_decider.h b/chrome/browser/previews/previews_lite_page_decider.h
index ec471fe..8f908f0 100644
--- a/chrome/browser/previews/previews_lite_page_decider.h
+++ b/chrome/browser/previews/previews_lite_page_decider.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_DECIDER_H_
 
 #include <memory>
+#include <string>
 #include <unordered_map>
 
 #include "base/gtest_prod_util.h"
@@ -14,6 +15,7 @@
 #include "base/sequence_checker.h"
 #include "base/time/tick_clock.h"
 #include "base/time/time.h"
+#include "base/values.h"
 #include "chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
 #include "net/http/http_request_headers.h"
@@ -59,8 +61,11 @@
   void SetDRPSettingsForTesting(
       data_reduction_proxy::DataReductionProxySettings* drp_settings);
 
-  // Clears all single bypasses for testing.
-  void ClearSingleBypassForTesting();
+  // Clears the host blacklist. Used when user deletes their browsing history.
+  void ClearBlacklist();
+
+  // Clears all single bypasses and the host blacklist for testing.
+  void ClearStateForTesting();
 
   // Sets that the user has seen the UI notification.
   void SetUserHasSeenUINotification();
@@ -80,6 +85,9 @@
                          const std::string& host) override;
   bool NeedsToNotifyUser() override;
   void NotifyUser(content::WebContents* web_contents) override;
+  void BlacklistHost(const std::string& host,
+                     base::TimeDelta duration) override;
+  bool HostBlacklisted(const std::string& host) override;
 
   // data_reduction_proxy::DataReductionProxySettingsObserver:
   void OnProxyRequestHeadersChanged(
@@ -111,6 +119,11 @@
   // use this preview.
   bool need_to_show_notification_;
 
+  // A dictionary of host string to base::Time. If a hostname is a member of
+  // this dictionary, that host should be blacklisted from this preview until
+  // after the time value. This is stored persistently in prefs.
+  std::unique_ptr<base::DictionaryValue> host_blacklist_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(PreviewsLitePageDecider);
diff --git a/chrome/browser/previews/previews_lite_page_decider_unittest.cc b/chrome/browser/previews/previews_lite_page_decider_unittest.cc
index 54e6fda..99f52f7 100644
--- a/chrome/browser/previews/previews_lite_page_decider_unittest.cc
+++ b/chrome/browser/previews/previews_lite_page_decider_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "base/strings/string_number_conversions.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h"
@@ -36,6 +37,63 @@
   base::test::ScopedTaskEnvironment scoped_task_environment_;
 };
 
+TEST_F(PreviewsLitePageDeciderTest, TestHostBlacklist) {
+  const int kBlacklistDurationDays = 30;
+  const std::string kHost = "google.com";
+  const std::string kOtherHost = "chromium.org";
+  const base::TimeDelta kYesterday = base::TimeDelta::FromDays(-1);
+  const base::TimeDelta kOneDay = base::TimeDelta::FromDays(1);
+
+  std::unique_ptr<PreviewsLitePageDecider> decider =
+      std::make_unique<PreviewsLitePageDecider>(nullptr);
+  PreviewsLitePageNavigationThrottleManager* manager = decider.get();
+
+  // Simple happy case.
+  manager->BlacklistHost(kHost, kOneDay);
+  EXPECT_TRUE(manager->HostBlacklisted(kHost));
+  decider->ClearStateForTesting();
+
+  // Old entries are deleted.
+  manager->BlacklistHost(kHost, kYesterday);
+  EXPECT_FALSE(manager->HostBlacklisted(kHost));
+  decider->ClearStateForTesting();
+
+  // Oldest entry is thrown out.
+  manager->BlacklistHost(kHost, kOneDay);
+  EXPECT_TRUE(manager->HostBlacklisted(kHost));
+  for (int i = 1; i <= kBlacklistDurationDays; i++) {
+    manager->BlacklistHost(kHost + base::IntToString(i),
+                           kOneDay + base::TimeDelta::FromSeconds(i));
+  }
+  EXPECT_FALSE(manager->HostBlacklisted(kHost));
+  decider->ClearStateForTesting();
+
+  // Oldest entry is not thrown out if there was a stale entry to remove.
+  manager->BlacklistHost(kHost, kOneDay);
+  EXPECT_TRUE(manager->HostBlacklisted(kHost));
+  for (int i = 1; i <= kBlacklistDurationDays - 1; i++) {
+    manager->BlacklistHost(kHost + base::IntToString(i),
+                           kOneDay + base::TimeDelta::FromSeconds(i));
+  }
+  manager->BlacklistHost(kOtherHost, kYesterday);
+  EXPECT_TRUE(manager->HostBlacklisted(kHost));
+  decider->ClearStateForTesting();
+}
+
+TEST_F(PreviewsLitePageDeciderTest, TestClearBlacklist) {
+  const std::string kHost = "1.chromium.org";
+
+  std::unique_ptr<PreviewsLitePageDecider> decider =
+      std::make_unique<PreviewsLitePageDecider>(nullptr);
+  PreviewsLitePageNavigationThrottleManager* manager = decider.get();
+
+  manager->BlacklistHost(kHost, base::TimeDelta::FromMinutes(1));
+  EXPECT_TRUE(manager->HostBlacklisted(kHost));
+
+  decider->ClearBlacklist();
+  EXPECT_FALSE(manager->HostBlacklisted(kHost));
+}
+
 TEST_F(PreviewsLitePageDeciderTest, TestServerUnavailable) {
   struct TestCase {
     base::TimeDelta set_available_after;
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
index d7edc01d..d522e29 100644
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
@@ -49,6 +49,8 @@
 
 constexpr char kChromeProxyHeader[] = "chrome-proxy";
 
+const base::TimeDelta kBlacklistDuration = base::TimeDelta::FromDays(30);
+
 bool IsPreviewsDomain(const GURL& url) {
   GURL previews_host = previews::params::GetLitePagePreviewsDomainURL();
   return url.DomainIs(previews_host.host()) &&
@@ -235,6 +237,9 @@
     }
   }
 
+  if (manager_->HostBlacklisted(url.host()))
+    blacklist_reasons.push_back(BlacklistReason::kHostBlacklisted);
+
   // Record UMA
   for (BlacklistReason reason : blacklist_reasons) {
     UMA_HISTOGRAM_ENUMERATION("Previews.ServerLitePage.BlacklistReasons",
@@ -460,6 +465,25 @@
       UMA_HISTOGRAM_ENUMERATION("Previews.ServerLitePage.ServerResponse",
                                 ServerResponse::kRedirect);
     }
+
+    // Check if the original host should be blacklisted, as directed by the
+    // server.
+    const net::HttpResponseHeaders* response_headers =
+        navigation_handle()->GetResponseHeaders();
+
+    std::string chrome_proxy_header;
+    bool blacklist_host =
+        response_headers &&
+        response_headers->EnumerateHeader(nullptr, kChromeProxyHeader,
+                                          &chrome_proxy_header) &&
+        chrome_proxy_header.find("host-blacklisted") != std::string::npos;
+
+    if (blacklist_host)
+      manager_->BlacklistHost(GURL(original_url).host(), kBlacklistDuration);
+
+    UMA_HISTOGRAM_BOOLEAN("Previews.ServerLitePage.HostBlacklistedOnBypass",
+                          blacklist_host);
+
     return content::NavigationThrottle::PROCEED;
   }
 
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle.h b/chrome/browser/previews/previews_lite_page_navigation_throttle.h
index e5b56111..9ed5dea 100644
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle.h
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle.h
@@ -43,7 +43,8 @@
     kPathSuffixBlacklisted = 0,
     kNavigationToPreviewsDomain = 1,
     kNavigationToPrivateDomain = 2,
-    kMaxValue = kNavigationToPrivateDomain,
+    kHostBlacklisted = 3,
+    kMaxValue = kHostBlacklisted,
   };
 
   // Reasons that a navigation is not eligible for this preview. This enum must
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h b/chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h
index b524042..81b9a26 100644
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h
@@ -52,6 +52,13 @@
 
   // Prompts |this| to display the required UI notifications to the user.
   virtual void NotifyUser(content::WebContents* web_contents) = 0;
+
+  // Blacklists the given |host| for the given |duration|.
+  virtual void BlacklistHost(const std::string& host,
+                             base::TimeDelta duration) = 0;
+
+  // Returns true if the given |host| is blacklisted.
+  virtual bool HostBlacklisted(const std::string& host) = 0;
 };
 
 #endif  // CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_NAVIGATION_THROTTLE_MANAGER_H_
diff --git a/chrome/browser/previews/previews_service.cc b/chrome/browser/previews/previews_service.cc
index 9f4bd2b..c117ec0 100644
--- a/chrome/browser/previews/previews_service.cc
+++ b/chrome/browser/previews/previews_service.cc
@@ -143,3 +143,12 @@
 void PreviewsService::Shutdown() {
   previews_lite_page_decider_->Shutdown();
 }
+
+void PreviewsService::ClearBlackList(base::Time begin_time,
+                                     base::Time end_time) {
+  if (previews_ui_service_)
+    previews_ui_service_->ClearBlackList(begin_time, end_time);
+
+  if (previews_lite_page_decider_)
+    previews_lite_page_decider_->ClearBlacklist();
+}
diff --git a/chrome/browser/previews/previews_service.h b/chrome/browser/previews/previews_service.h
index 19e297d..840eee7 100644
--- a/chrome/browser/previews/previews_service.h
+++ b/chrome/browser/previews/previews_service.h
@@ -51,6 +51,10 @@
   // classes.
   void Shutdown() override;
 
+  // Clears the history of the black lists in |previews_ui_service_| and
+  // |previews_lite_page_decider_| between |begin_time| and |end_time|.
+  void ClearBlackList(base::Time begin_time, base::Time end_time);
+
   // The previews UI thread service.
   previews::PreviewsUIService* previews_ui_service() {
     return previews_ui_service_.get();
diff --git a/chrome/browser/profiling_host/background_profiling_triggers.cc b/chrome/browser/profiling_host/background_profiling_triggers.cc
index cc4a594..6c5b5f48 100644
--- a/chrome/browser/profiling_host/background_profiling_triggers.cc
+++ b/chrome/browser/profiling_host/background_profiling_triggers.cc
@@ -31,6 +31,7 @@
 const size_t kBrowserProcessMallocTriggerKb = 100 * 1024;    // 100 MB
 const size_t kGPUProcessMallocTriggerKb = 40 * 1024;         // 40 MB
 const size_t kRendererProcessMallocTriggerKb = 125 * 1024;   // 125 MB
+const size_t kUtilityProcessMallocTriggerKb = 40 * 1024;     // 40 MB
 
 // If memory usage has increased by 50MB since the last report, send another.
 const uint32_t kHighWaterMarkThresholdKb = 50 * 1024;  // 50 MB
@@ -43,6 +44,7 @@
 const size_t kBrowserProcessMallocTriggerKb = 400 * 1024;    // 400 MB
 const size_t kGPUProcessMallocTriggerKb = 400 * 1024;        // 400 MB
 const size_t kRendererProcessMallocTriggerKb = 500 * 1024;   // 500 MB
+const size_t kUtilityProcessMallocTriggerKb = 400 * 1024;    // 400 MB
 
 // If memory usage has increased by 500MB since the last report, send another.
 const uint32_t kHighWaterMarkThresholdKb = 500 * 1024;  // 500 MB
@@ -115,6 +117,9 @@
     case content::ProcessType::PROCESS_TYPE_RENDERER:
       return private_footprint_kb > kRendererProcessMallocTriggerKb;
 
+    case content::ProcessType::PROCESS_TYPE_UTILITY:
+      return private_footprint_kb > kUtilityProcessMallocTriggerKb;
+
     default:
       return false;
   }
diff --git a/chrome/browser/resources/chromeos/keyboard_overlay.css b/chrome/browser/resources/chromeos/keyboard_overlay.css
deleted file mode 100644
index 2ecea3f..0000000
--- a/chrome/browser/resources/chromeos/keyboard_overlay.css
+++ /dev/null
@@ -1,196 +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. */
-
-body {
-  background: #fff;
-  font-size: 100%;
-  margin: 0;
-  overflow: hidden;
-  padding: 0;
-  user-select: none;
-}
-
-.keyboard-overlay-keyboard {
-  background: -webkit-linear-gradient(#484848, #252525) no-repeat;
-  background-color: #252525;
-  border-radius: 6px;
-}
-
-.keyboard-overlay-instructions {
-  -webkit-box-orient: vertical;
-  background: -webkit-linear-gradient(rgb(51, 76, 126), rgb(13, 23, 43));
-  border: 2px solid rgb(87, 108, 207);
-  border-radius: 5px;
-  color: #fff;
-  display: -webkit-box;
-  position: absolute;
-  vertical-align: middle;
-  z-index: 100;
-}
-
-.keyboard-overlay-instructions-text {
-  -webkit-box-flex: 3;
-  margin: 13px auto 0;
-  max-width: 20em;
-  text-align: center;
-  vertical-align: middle;
-}
-
-.keyboard-overlay-instructions-hide-text {
-  -webkit-box-flex: 2;
-  font-weight: bold;
-  text-align: center;
-  vertical-align: middle;
-}
-
-.keyboard-overlay-learn-more-text {
-  -webkit-box-flex: 1;
-  font-size: 90%;
-  margin: 0 auto 13px;
-  text-align: center;
-  text-decoration: underline;
-  vertical-align: middle;
-}
-
-.keyboard-overlay-learn-more-text a {
-  color: #fff;
-}
-
-.keyboard-overlay-key {
-  -webkit-box-orient: vertical;
-  border: 2px solid #7f7f7f;
-  border-radius: 4px;
-  color: rgb(151, 151, 151);
-  display: -webkit-box;
-  font-size: 80%;
-  font-weight: bold;
-  position: absolute;
-}
-
-.keyboard-overlay-key-background {
-  background-color: rgba(24, 24, 24, 0.9);
-}
-
-.keyboard-overlay-key.is-shortcut {
-  border-color: #fafafa;
-  color: #9e9e9e;
-}
-
-.keyboard-overlay-shortcut-key-background {
-  background: linear-gradient(rgba(61, 61, 61, 0.8), rgba(27, 27, 27, 0.8));
-}
-
-.keyboard-overlay-key.is-shortcut.modifier-shift {
-  border-color: rgb(97, 186, 100);
-}
-
-.keyboard-overlay-key.is-shortcut.modifier-ctrl {
-  border-color: rgb(93, 128, 199);
-}
-
-.keyboard-overlay-key.is-shortcut.modifier-alt {
-  border-color: rgb(184, 84, 84);
-}
-
-.keyboard-overlay-key.is-shortcut.modifier-search {
-  border-color: rgb(204, 204, 80);
-}
-
-.keyboard-overlay-key.is-shortcut.modifier-shift.modifier-ctrl {
-  border-color: rgb(121, 172, 143);
-}
-
-.keyboard-overlay-key.is-shortcut.modifier-shift.modifier-alt {
-  border-color: rgb(191, 189, 121);
-}
-
-.keyboard-overlay-key.is-shortcut.modifier-ctrl.modifier-alt {
-  border-color: rgb(158, 84, 206);
-}
-
-.keyboard-overlay-key.is-shortcut.modifier-shift.modifier-ctrl.modifier-alt {
-  border-color: #7f7f7f;
-}
-
-.keyboard-overlay-key.pressed {
-  border-color: #fff;
-  color: #fff;
-}
-
-.keyboard-overlay-key.pressed.is-shift {
-  background: -webkit-linear-gradient(rgb(68, 161, 66), rgb(62, 95, 55));
-}
-
-.keyboard-overlay-key.pressed.is-shift.modifier-ctrl {
-  background: -webkit-linear-gradient(rgb(66, 161, 67), rgb(46, 92, 83));
-}
-
-.keyboard-overlay-key.pressed.is-shift.modifier-alt {
-  background: -webkit-linear-gradient(rgb(69, 163, 67), rgb(81, 81, 52));
-}
-
-.keyboard-overlay-key.pressed.is-shift.modifier-ctrl.modifier-alt {
-  background: -webkit-linear-gradient(rgb(82, 161, 42), rgb(79, 77, 46));
-}
-
-.keyboard-overlay-key.pressed.is-ctrl {
-  background: -webkit-linear-gradient(rgb(31, 55, 162), rgb(25, 38, 90));
-}
-
-.keyboard-overlay-key.pressed.is-ctrl.modifier-shift {
-  background: -webkit-linear-gradient(rgb(67, 159, 165), rgb(30, 55, 96));
-}
-
-.keyboard-overlay-key.pressed.is-ctrl.modifier-alt {
-  background: -webkit-linear-gradient(rgb(115, 54, 144), rgb(34, 37, 94));
-}
-
-.keyboard-overlay-key.pressed.is-ctrl.modifier-shift.modifier-alt {
-  background: -webkit-linear-gradient(rgb(115, 54, 144), rgb(33, 37, 93));
-}
-
-.keyboard-overlay-key.pressed.is-alt {
-  background: -webkit-linear-gradient(rgb(132, 44, 42), rgb(84, 30, 28));
-}
-
-.keyboard-overlay-key.pressed.is-alt.modifier-shift {
-  background: -webkit-linear-gradient(top left, rgb(116, 94, 49),
-                                                rgb(85, 36, 30));
-}
-
-.keyboard-overlay-key.pressed.is-alt.modifier-ctrl {
-  background: -webkit-linear-gradient(rgb(118, 54, 143), rgb(82, 33, 40));
-}
-
-.keyboard-overlay-key.pressed.is-alt.modifier-shift.modifier-ctrl {
-  background: -webkit-linear-gradient(top left, rgb(115, 95, 41),
-                                                rgb(80, 36, 27));
-}
-
-.keyboard-overlay-key.pressed.is-search {
-  background: -webkit-linear-gradient(rgb(242, 242, 126), rgb(186, 186, 52));
-}
-
-.keyboard-overlay-shortcut-text {
-  -webkit-box-flex: 1;
-  -webkit-box-ordinal-group: 1;
-  color: #fff;
-  padding: 1px 3px 0;
-  text-align: center;
-  text-shadow: #000 0 0 2px;
-}
-
-.keyboard-overlay-key-text {
-  -webkit-box-ordinal-group: 2;
-  padding: 0 3px 2px;
-  text-align: center;
-}
-
-.keyboard-overlay-key.is-shortcut .keyboard-overlay-key-text {
-  opacity: 0.25;
-}
-
-#instructions:focus {
-  outline: none;
-}
diff --git a/chrome/browser/resources/chromeos/keyboard_overlay.html b/chrome/browser/resources/chromeos/keyboard_overlay.html
deleted file mode 100644
index 104dcfa5..0000000
--- a/chrome/browser/resources/chromeos/keyboard_overlay.html
+++ /dev/null
@@ -1,14 +0,0 @@
-<!doctype html>
-<html i18n-values="lang:language">
-<head>
-<meta charset="utf-8" content="text/html">
-<title i18n-content="keyboardOverlayTitle"></title>
-<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
-<link rel="stylesheet" href="keyboard_overlay.css">
-<script src="chrome://resources/js/load_time_data.js"></script>
-<script src="chrome://resources/js/util.js"></script>
-<script src="keyboard_overlay.js"></script>
-<body class="keyboard-overlay-keyboard"></body>
-<script src="strings.js"></script>
-<script src="chrome://resources/js/i18n_template.js"></script>
-</html>
diff --git a/chrome/browser/resources/chromeos/keyboard_overlay.js b/chrome/browser/resources/chromeos/keyboard_overlay.js
deleted file mode 100644
index 9007f0e..0000000
--- a/chrome/browser/resources/chromeos/keyboard_overlay.js
+++ /dev/null
@@ -1,1073 +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.
-
-// <include src="keyboard_overlay_data.js">
-
-var BASE_KEYBOARD = {top: 0, left: 0, width: 1237, height: 514};
-
-var BASE_INSTRUCTIONS = {top: 194, left: 370, width: 498, height: 142};
-
-var MODIFIER_TO_CLASS = {
-  'SHIFT': 'modifier-shift',
-  'CTRL': 'modifier-ctrl',
-  'ALT': 'modifier-alt',
-  'SEARCH': 'modifier-search'
-};
-
-var IDENTIFIER_TO_CLASS = {
-  '2A': 'is-shift',
-  '36': 'is-shift',
-  '1D': 'is-ctrl',
-  'E0 1D': 'is-ctrl',
-  '38': 'is-alt',
-  'E0 38': 'is-alt',
-  'E0 5B': 'is-search'
-};
-
-var LABEL_TO_IDENTIFIER = {
-  'search': 'E0 5B',
-  'ctrl': '1D',
-  'alt': '38',
-  'caps lock': '3A',
-  'esc': '01',
-  'backspace': '0E',
-  'disabled': 'DISABLED'
-};
-
-// For KeyboardOverlayUIBrowserTest.
-var KEYCODE_TO_LABEL = {
-  8: 'backspace',
-  9: 'tab',
-  10: 'shift',
-  13: 'enter',
-  27: 'esc',
-  32: 'space',
-  33: 'pageup',
-  34: 'pagedown',
-  35: 'end',
-  36: 'home',
-  37: 'left',
-  38: 'up',
-  39: 'right',
-  40: 'down',
-  46: 'delete',
-  91: 'search',
-  92: 'search',
-  96: '0',
-  97: '1',
-  98: '2',
-  99: '3',
-  100: '4',
-  101: '5',
-  102: '6',
-  103: '7',
-  104: '8',
-  105: '9',
-  106: '*',
-  107: '+',
-  109: '-',
-  110: '.',
-  111: '/',
-  112: 'back',
-  113: 'forward',
-  114: 'reload',
-  115: 'full screen',
-  116: 'switch window',
-  117: 'bright down',
-  118: 'bright up',
-  119: 'mute',
-  120: 'vol. down',
-  121: 'vol. up',
-  152: 'power',
-  166: 'back',
-  167: 'forward',
-  168: 'reload',
-  173: 'mute',
-  174: 'vol. down',
-  175: 'vol. up',
-  183: 'full screen',
-  182: 'switch window',
-  186: ';',
-  187: '+',
-  188: ',',
-  189: '-',
-  190: '.',
-  191: '/',
-  192: '`',
-  216: 'bright down',
-  217: 'bright up',
-  218: 'bright down',
-  219: '[',
-  220: '\\',
-  221: ']',
-  222: '\'',
-  232: 'bright up',
-};
-
-/**
- * When the top row keys setting is changed so that they're treated as function
- * keys, their labels should change as well.
- */
-var TOP_ROW_KEY_LABEL_TO_FUNCTION_LABEL = {
-  'back': 'f1',
-  'forward': 'f2',
-  'reload': 'f3',
-  'full screen': 'f4',
-  'switch window': 'f5',
-  'bright down': 'f6',
-  'bright up': 'f7',
-  'mute': 'f8',
-  'vol. down': 'f9',
-  'vol. up': 'f10',
-};
-
-/**
- * The top row key labels are modified in the new keyboard layout.
- */
-var TOP_ROW_KEY_LABEL_TO_FUNCTION_LABEL_LAYOUT_2 = {
-  'back': 'f1',
-  'reload': 'f2',
-  'full screen': 'f3',
-  'switch window': 'f4',
-  'bright down': 'f5',
-  'bright up': 'f6',
-  'play / pause': 'f7',
-  'mute': 'f8',
-  'vol. down': 'f9',
-  'vol. up': 'f10',
-};
-
-/**
- * Some key labels define actions (like for example 'vol. up' or 'mute').
- * These labels should be localized. (crbug.com/471025).
- */
-var LABEL_TO_LOCALIZED_LABEL_ID = {
-  'esc': 'keyboardOverlayEscKeyLabel',
-  'back': 'keyboardOverlayBackKeyLabel',
-  'forward': 'keyboardOverlayForwardKeyLabel',
-  'reload': 'keyboardOverlayReloadKeyLabel',
-  'full screen': 'keyboardOverlayFullScreenKeyLabel',
-  'switch window': 'keyboardOverlaySwitchWinKeyLabel',
-  'bright down': 'keyboardOverlayBrightDownKeyLabel',
-  'bright up': 'keyboardOverlayBrightUpKeyLabel',
-  'mute': 'keyboardOverlayMuteKeyLabel',
-  'vol. down': 'keyboardOverlayVolDownKeyLabel',
-  'vol. up': 'keyboardOverlayVolUpKeyLabel',
-  'power': 'keyboardOverlayPowerKeyLabel',
-  'backspace': 'keyboardOverlayBackspaceKeyLabel',
-  'tab': 'keyboardOverlayTabKeyLabel',
-  'search': 'keyboardOverlaySearchKeyLabel',
-  'assistant': 'keyboardOverlayAssistantKeyLabel',
-  'play / pause': 'keyboardOverlayPlayPauseKeyLabel',
-  'system menu': 'keyboardOverlaySystemMenuKeyLabel',
-  'launcher': 'keyboardOverlayLauncherKeyLabel',
-  'enter': 'keyboardOverlayEnterKeyLabel',
-  'shift': 'keyboardOverlayShiftKeyLabel',
-  'ctrl': 'keyboardOverlayCtrlKeyLabel',
-  'alt': 'keyboardOverlayAltKeyLabel',
-  'left': 'keyboardOverlayLeftKeyLabel',
-  'right': 'keyboardOverlayRightKeyLabel',
-  'up': 'keyboardOverlayUpKeyLabel',
-  'down': 'keyboardOverlayDownKeyLabel',
-  'f1': 'keyboardOverlayF1',
-  'f2': 'keyboardOverlayF2',
-  'f3': 'keyboardOverlayF3',
-  'f4': 'keyboardOverlayF4',
-  'f5': 'keyboardOverlayF5',
-  'f6': 'keyboardOverlayF6',
-  'f7': 'keyboardOverlayF7',
-  'f8': 'keyboardOverlayF8',
-  'f9': 'keyboardOverlayF9',
-  'f10': 'keyboardOverlayF10',
-};
-
-var COMPOUND_ENTER_KEY_DATA = [815, 107, 60, 120];
-var COMPOUND_ENTER_KEY_CLIP_PATH =
-    'polygon(0% 0%, 100% 0%, 100% 100%, 28% 100%, 28% 47%, 0% 47%)';
-var COMPOUND_ENTER_KEY_OVERLAY_DIV_CLIP_PATH =
-    'polygon(12% 0%, 100% 0%, 100% 97%, 12% 97%)';
-
-var IME_ID_PREFIX = '_comp_ime_';
-var EXTENSION_ID_LEN = 32;
-
-var keyboardOverlayId = 'en_US';
-var identifierMap = {};
-
-/**
- * True after at least one keydown event has been received.
- */
-var gotKeyDown = false;
-
-/**
- * Returns the actual key label based on the treatment of the top row setting,
- * as well as whether this device has the new keyboard layout.
- * @param {string} label The unmodified key label as read from
- * keyboard_overlay_data.js.
- */
-function getTopRowKeyActualLabel(label) {
-  if (!topRowKeysAreFunctionKeys())
-    return label;
-
-  var topRowMap = hasKeyboardLayout2() ?
-      TOP_ROW_KEY_LABEL_TO_FUNCTION_LABEL_LAYOUT_2 :
-      TOP_ROW_KEY_LABEL_TO_FUNCTION_LABEL;
-
-  return topRowMap[label] || label;
-}
-
-/**
- * Returns the layout name.
- * @return {string} layout name.
- */
-function getLayoutName() {
-  return getKeyboardGlyphData().layoutName;
-}
-
-/**
- * Returns layout data.
- * @return {Array} Keyboard layout data.
- */
-function getLayout() {
-  return keyboardOverlayData['layouts'][getLayoutName()];
-}
-
-// Cache the shortcut data after it is constructed.
-var shortcutDataCache;
-
-/**
- * Returns shortcut data.
- * @return {Object} Keyboard shortcut data.
- */
-function getShortcutData() {
-  if (shortcutDataCache)
-    return shortcutDataCache;
-
-  shortcutDataCache = keyboardOverlayData['shortcut'];
-
-  if (!isDisplayUIScalingEnabled()) {
-    // Zoom screen in
-    delete shortcutDataCache['+<>CTRL<>SHIFT'];
-    // Zoom screen out
-    delete shortcutDataCache['-<>CTRL<>SHIFT'];
-    // Reset screen zoom
-    delete shortcutDataCache['0<>CTRL<>SHIFT'];
-  }
-
-  return shortcutDataCache;
-}
-
-/**
- * Returns the keyboard overlay ID.
- * @return {string} Keyboard overlay ID.
- */
-function getKeyboardOverlayId() {
-  return keyboardOverlayId;
-}
-
-/**
- * Returns keyboard glyph data.
- * @return {Object} Keyboard glyph data.
- */
-function getKeyboardGlyphData() {
-  return keyboardOverlayData['keyboardGlyph'][getKeyboardOverlayId()];
-}
-
-/**
- * Tests if the top row keys are set to be treated as function keys.
- * @return {boolean} True if the top row keys are set to be function keys.
- */
-function topRowKeysAreFunctionKeys() {
-  return loadTimeData.getBoolean('keyboardOverlayTopRowKeysAreFunctionKeys');
-}
-
-/**
- * Tests if voice interaction is enabled.
- * @return {boolean} True if voice interaction feature is enabled.
- */
-function isVoiceInteractionEnabled() {
-  return loadTimeData.getBoolean('voiceInteractionEnabled');
-}
-
-/**
- * Converts a single hex number to a character.
- * @param {string} hex Hexadecimal string.
- * @return {string} Unicode values of hexadecimal string.
- */
-function hex2char(hex) {
-  if (!hex) {
-    return '';
-  }
-  var result = '';
-  var n = parseInt(hex, 16);
-  if (n <= 0xFFFF) {
-    result += String.fromCharCode(n);
-  } else if (n <= 0x10FFFF) {
-    n -= 0x10000;
-    result +=
-        (String.fromCharCode(0xD800 | (n >> 10)) +
-         String.fromCharCode(0xDC00 | (n & 0x3FF)));
-  } else {
-    console.error('hex2Char error: Code point out of range :' + hex);
-  }
-  return result;
-}
-
-/**
- * Returns a list of modifiers normalized to ignore the distinction between
- * right or left keys.
- * @param {Array} modifiers List of modifiers with distinction between right
- *        and left keys.
- * @return {Array} List of normalized modifiers ignoring the difference between
- *         right or left keys.
- */
-function normalizeModifiers(modifiers) {
-  var result = [];
-  if (contains(modifiers, 'L_SHIFT') || contains(modifiers, 'R_SHIFT')) {
-    result.push('SHIFT');
-  }
-  if (contains(modifiers, 'L_CTRL') || contains(modifiers, 'R_CTRL')) {
-    result.push('CTRL');
-  }
-  if (contains(modifiers, 'L_ALT') || contains(modifiers, 'R_ALT')) {
-    result.push('ALT');
-  }
-  if (contains(modifiers, 'SEARCH')) {
-    result.push('SEARCH');
-  }
-  return result.sort();
-}
-
-/**
- * This table will contain the status of the modifiers.
- */
-var isPressed = {
-  'L_SHIFT': false,
-  'R_SHIFT': false,
-  'L_CTRL': false,
-  'R_CTRL': false,
-  'L_ALT': false,
-  'R_ALT': false,
-  'SEARCH': false,
-};
-
-/**
- * Returns a list of modifiers from the key event distinguishing right and left
- * keys.
- * @param {Event} e The key event.
- * @return {Array} List of modifiers based on key event.
- */
-function getModifiers(e) {
-  if (!e)
-    return [];
-
-  var keyCodeToModifier = {
-    16: 'SHIFT',
-    17: 'CTRL',
-    18: 'ALT',
-    91: 'SEARCH',
-  };
-  var modifierWithKeyCode = keyCodeToModifier[e.keyCode];
-  /** @const */ var DOM_KEY_LOCATION_LEFT = 1;
-  var side = (e.location == DOM_KEY_LOCATION_LEFT) ? 'L_' : 'R_';
-  var isKeyDown = (e.type == 'keydown');
-
-  if (modifierWithKeyCode == 'SEARCH') {
-    isPressed['SEARCH'] = isKeyDown;
-  } else {
-    isPressed[side + modifierWithKeyCode] = isKeyDown;
-  }
-
-  // make the result array
-  return result =
-             [
-               'L_SHIFT', 'R_SHIFT', 'L_CTRL', 'R_CTRL', 'L_ALT', 'R_ALT',
-               'SEARCH'
-             ]
-                 .filter(function(modifier) {
-                   return isPressed[modifier];
-                 })
-                 .sort();
-}
-
-/**
- * Returns an ID of the key.
- * @param {string} identifier Key identifier.
- * @param {number} i Key number.
- * @return {string} Key ID.
- */
-function keyId(identifier, i) {
-  return identifier + '-key-' + i;
-}
-
-/**
- * Returns an ID of the text on the key.
- * @param {string} identifier Key identifier.
- * @param {number} i Key number.
- * @return {string} Key text ID.
- */
-function keyTextId(identifier, i) {
-  return identifier + '-key-text-' + i;
-}
-
-/**
- * Returns an ID of the shortcut text.
- * @param {string} identifier Key identifier.
- * @param {number} i Key number.
- * @return {string} Key shortcut text ID.
- */
-function shortcutTextId(identifier, i) {
-  return identifier + '-shortcut-text-' + i;
-}
-
-/**
- * Returns true if |list| contains |e|.
- * @param {Array} list Container list.
- * @param {string} e Element string.
- * @return {boolean} Returns true if the list contains the element.
- */
-function contains(list, e) {
-  return list.indexOf(e) != -1;
-}
-
-/**
- * Returns a list of the class names corresponding to the identifier and
- * modifiers.
- * @param {string} identifier Key identifier.
- * @param {Array} modifiers List of key modifiers (with distinction between
- *                right and left keys).
- * @param {Array} normalizedModifiers List of key modifiers (without distinction
- *                between right or left keys).
- * @return {Array} List of class names corresponding to specified params.
- */
-function getKeyClasses(identifier, modifiers, normalizedModifiers) {
-  var classes = ['keyboard-overlay-key'];
-  for (var i = 0; i < normalizedModifiers.length; ++i) {
-    classes.push(MODIFIER_TO_CLASS[normalizedModifiers[i]]);
-  }
-
-  if ((identifier == '2A' && contains(modifiers, 'L_SHIFT')) ||
-      (identifier == '36' && contains(modifiers, 'R_SHIFT')) ||
-      (identifier == '1D' && contains(modifiers, 'L_CTRL')) ||
-      (identifier == 'E0 1D' && contains(modifiers, 'R_CTRL')) ||
-      (identifier == '38' && contains(modifiers, 'L_ALT')) ||
-      (identifier == 'E0 38' && contains(modifiers, 'R_ALT')) ||
-      (identifier == 'E0 5B' && contains(modifiers, 'SEARCH'))) {
-    classes.push('pressed');
-    classes.push(IDENTIFIER_TO_CLASS[identifier]);
-  }
-  return classes;
-}
-
-/**
- * Returns true if a character is a ASCII character.
- * @param {string} c A character to be checked.
- * @return {boolean} True if the character is an ASCII character.
- */
-function isAscii(c) {
-  var charCode = c.charCodeAt(0);
-  return 0x00 <= charCode && charCode <= 0x7F;
-}
-
-/**
- * Returns a remapped identiifer based on the preference.
- * @param {string} identifier Key identifier.
- * @return {string} Remapped identifier.
- */
-function remapIdentifier(identifier) {
-  return identifierMap[identifier] || identifier;
-}
-
-/**
- * Returns a label of the key.
- * @param {string} keyData Key glyph data.
- * @param {Array} modifiers Key Modifier list.
- * @return {string} Label of the key.
- */
-function getKeyLabel(keyData, modifiers) {
-  if (!keyData)
-    return '';
-
-  if (keyData.label)
-    return getTopRowKeyActualLabel(keyData.label);
-
-  var keyLabel = '';
-  for (var j = 1; j <= 9; j++) {
-    var pos = keyData['p' + j];
-    if (!pos) {
-      continue;
-    }
-    keyLabel = hex2char(pos);
-    if (!keyLabel) {
-      continue;
-    }
-    if (isAscii(keyLabel) &&
-        getShortcutData()[getAction(keyLabel, modifiers)]) {
-      break;
-    }
-  }
-  return keyLabel;
-}
-
-/**
- * Returns a normalized string used for a key of shortcutData.
- *
- * Examples:
- *   keyCode: 'd', modifiers: ['CTRL', 'SHIFT'] => 'd<>CTRL<>SHIFT'
- *   keyCode: 'alt', modifiers: ['ALT', 'SHIFT'] => 'ALT<>SHIFT'
- *
- * @param {string} keyCode Key code.
- * @param {Array} modifiers Key Modifier list.
- * @return {string} Normalized key shortcut data string.
- */
-function getAction(keyCode, modifiers) {
-  /** @const */ var separatorStr = '<>';
-  if (keyCode.toUpperCase() in MODIFIER_TO_CLASS) {
-    keyCode = keyCode.toUpperCase();
-    if (keyCode in modifiers) {
-      return modifiers.join(separatorStr);
-    } else {
-      var action = [keyCode].concat(modifiers);
-      action.sort();
-      return action.join(separatorStr);
-    }
-  }
-  return [keyCode].concat(modifiers).join(separatorStr);
-}
-
-/**
- * Returns a text which displayed on a key.
- * @param {string} keyData Key glyph data.
- * @return {string} Key text value.
- */
-function getKeyTextValue(keyData) {
-  if (keyData.label) {
-    // Do not show text on the space key.
-    if (keyData.label == 'space') {
-      return '';
-    }
-
-    // some key labels define actions such as 'mute' or 'vol. up'. Those actions
-    // should be localized (crbug.com/471025).
-    // If this is a top row key label, we need to convert that label to
-    // function-keys label (i.e. mute --> f8), and then use that label to get
-    // a localized one.
-    var labelToBeLocalized = getTopRowKeyActualLabel(keyData.label);
-    var localizedLabelId = LABEL_TO_LOCALIZED_LABEL_ID[labelToBeLocalized];
-    if (localizedLabelId)
-      return loadTimeData.getString(localizedLabelId);
-
-    return keyData.label;
-  }
-
-  var chars = [];
-  for (var j = 1; j <= 9; ++j) {
-    var pos = keyData['p' + j];
-    if (pos && pos.length > 0) {
-      chars.push(hex2char(pos));
-    }
-  }
-  return chars.join(' ');
-}
-
-/**
- * Updates the whole keyboard.
- * @param {Array} modifiers Key Modifier list.
- * @param {Array} normModifiers Key Modifier list ignoring the distinction
- *                between right and left keys.
- */
-function update(modifiers, normModifiers) {
-  var keyboardGlyphData = getKeyboardGlyphData();
-  var shortcutData = getShortcutData();
-  var layout = getLayout();
-  for (var i = 0; i < layout.length; ++i) {
-    var identifier = remapIdentifier(layout[i][0]);
-    var keyData = keyboardGlyphData.keys[identifier];
-    var classes = getKeyClasses(identifier, modifiers, normModifiers);
-    var keyLabel = getKeyLabel(keyData, normModifiers);
-    var shortcutId = shortcutData[getAction(keyLabel, normModifiers)];
-    if (modifiers.length == 0 && (identifier == '2A' || identifier == '36')) {
-      // Either the right or left shift keys are used to disable the caps lock
-      // if it was enabled. To fix crbug.com/453623.
-      shortcutId = 'keyboardOverlayDisableCapsLock';
-    }
-
-    classes.push('keyboard-overlay-key-background');
-
-    if (shortcutId == 'keyboardOverlayVoiceInteraction' &&
-        (!isVoiceInteractionEnabled() || hasKeyboardLayout2())) {
-      // The shortcut should be disabled either voice interaction is disabled or
-      // keyboard layout 2 is in use (in which case a dedicated key for voice
-      // interaction exists).
-      continue;
-    }
-
-    // Currently hidden behind experimental accessibility features flag.
-    if (shortcutId == 'keyboardOverlayToggleDictation')
-      continue;
-
-    if (shortcutId) {
-      classes.push('is-shortcut');
-      classes.push('keyboard-overlay-shortcut-key-background');
-    }
-
-    var key = $(keyId(identifier, i));
-    key.className = classes.join(' ');
-
-    if (!keyData) {
-      continue;
-    }
-
-    var keyText = $(keyTextId(identifier, i));
-    var keyTextValue = getKeyTextValue(keyData);
-    if (keyTextValue) {
-      keyText.style.visibility = 'visible';
-    } else {
-      keyText.style.visibility = 'hidden';
-    }
-    keyText.textContent = keyTextValue;
-
-    var shortcutText = $(shortcutTextId(identifier, i));
-    if (shortcutId) {
-      shortcutText.style.visibility = 'visible';
-      shortcutText.textContent = loadTimeData.getString(shortcutId);
-    } else {
-      shortcutText.style.visibility = 'hidden';
-    }
-
-    if (layout[i][1] == 'COMPOUND_ENTER_KEY') {
-      var overlayDivClasses =
-          getKeyClasses(identifier, modifiers, normModifiers);
-      if (shortcutId)
-        overlayDivClasses.push('is-shortcut');
-      $(keyId(identifier, i) + '-sub').className = overlayDivClasses.join(' ');
-    }
-
-    var format = keyboardGlyphData.keys[layout[i][0]].format;
-    if (format) {
-      if (format == 'left' || format == 'right') {
-        shortcutText.style.textAlign = format;
-        keyText.style.textAlign = format;
-      }
-    }
-  }
-}
-
-/**
- * A callback function for onkeydown and onkeyup events.
- * @param {Event} e Key event.
- */
-function handleKeyEvent(e) {
-  if (!getKeyboardOverlayId()) {
-    return;
-  }
-
-  var modifiers = getModifiers(e);
-
-  // To avoid flickering as the user releases the modifier keys that were held
-  // to trigger the overlay, avoid updating in response to keyup events until at
-  // least one keydown event has been received.
-  if (!gotKeyDown) {
-    if (e.type == 'keyup') {
-      return;
-    } else if (e.type == 'keydown') {
-      gotKeyDown = true;
-    }
-  }
-
-  var normModifiers = normalizeModifiers(modifiers);
-  update(modifiers, normModifiers);
-
-  var instructions = $('instructions');
-  if (modifiers.length == 0) {
-    instructions.style.visibility = 'visible';
-  } else {
-    instructions.style.visibility = 'hidden';
-  }
-
-  e.preventDefault();
-}
-
-/**
- * Initializes the layout of the keys.
- */
-function initLayout() {
-  // Add data for the caps lock key
-  var keys = getKeyboardGlyphData().keys;
-  if (!('3A' in keys)) {
-    keys['3A'] = {label: 'caps lock', format: 'left'};
-  }
-  // Add data for the special key representing a disabled key
-  keys['DISABLED'] = {label: 'disabled', format: 'left'};
-
-  var layout = getLayout();
-  var keyboard = document.body;
-  var minX = window.innerWidth;
-  var maxX = 0;
-  var minY = window.innerHeight;
-  var maxY = 0;
-  var multiplier = 1.38 * window.innerWidth / BASE_KEYBOARD.width;
-  var keyMargin = 7;
-  var offsetX = 10;
-  var offsetY = 7;
-
-  var instructions = document.createElement('div');
-  instructions.id = 'instructions';
-  instructions.className = 'keyboard-overlay-instructions';
-  instructions.setAttribute('aria-live', 'polite');
-  // Postpone showing the instructions a delay after the widget has been shown,
-  // so that chrome vox behaves correctly.
-  instructions.style.visibility = 'hidden';
-  instructions.tabIndex = '0';
-  var instructionsText = document.createElement('div');
-  instructionsText.id = 'instructions-text';
-  instructionsText.className = 'keyboard-overlay-instructions-text';
-  instructionsText.innerHTML =
-      loadTimeData.getString('keyboardOverlayInstructions');
-  instructions.appendChild(instructionsText);
-  var instructionsHideText = document.createElement('div');
-  instructionsHideText.id = 'instructions-hide-text';
-  instructionsHideText.className = 'keyboard-overlay-instructions-hide-text';
-  instructionsHideText.innerHTML =
-      loadTimeData.getString('keyboardOverlayInstructionsHide');
-  instructions.appendChild(instructionsHideText);
-  var learnMoreLinkText = document.createElement('div');
-  learnMoreLinkText.id = 'learn-more-text';
-  learnMoreLinkText.className = 'keyboard-overlay-learn-more-text';
-  learnMoreLinkText.addEventListener('click', learnMoreClicked);
-  var learnMoreLinkAnchor = document.createElement('a');
-  learnMoreLinkAnchor.href =
-      loadTimeData.getString('keyboardOverlayLearnMoreURL');
-  learnMoreLinkAnchor.textContent =
-      loadTimeData.getString('keyboardOverlayLearnMore');
-  learnMoreLinkText.appendChild(learnMoreLinkAnchor);
-  instructions.appendChild(learnMoreLinkText);
-  keyboard.appendChild(instructions);
-
-  for (var i = 0; i < layout.length; i++) {
-    var array = layout[i];
-    var identifier = remapIdentifier(array[0]);
-
-    var keyDataX = 0;
-    var keyDataY = 0;
-    var keyDataW = 0;
-    var keyDataH = 0;
-    var isCompoundEnterKey = false;
-    if (array[1] == 'COMPOUND_ENTER_KEY') {
-      keyDataX = COMPOUND_ENTER_KEY_DATA[0];
-      keyDataY = COMPOUND_ENTER_KEY_DATA[1];
-      keyDataW = COMPOUND_ENTER_KEY_DATA[2];
-      keyDataH = COMPOUND_ENTER_KEY_DATA[3];
-      isCompoundEnterKey = true;
-    } else {
-      keyDataX = array[1];
-      keyDataY = array[2];
-      keyDataW = array[3];
-      keyDataH = array[4];
-    }
-
-    var x = Math.round((keyDataX + offsetX) * multiplier);
-    var y = Math.round((keyDataY + offsetY) * multiplier);
-    var w = Math.round((keyDataW - keyMargin) * multiplier);
-    var h = Math.round((keyDataH - keyMargin) * multiplier);
-
-    var key = document.createElement('div');
-    key.id = keyId(identifier, i);
-    key.className = 'keyboard-overlay-key';
-    key.style.left = x + 'px';
-    key.style.top = y + 'px';
-    key.style.width = w + 'px';
-    key.style.height = h + 'px';
-
-    var keyText = document.createElement('div');
-    keyText.id = keyTextId(identifier, i);
-    keyText.className = 'keyboard-overlay-key-text';
-    keyText.style.visibility = 'hidden';
-    key.appendChild(keyText);
-
-    var shortcutText = document.createElement('div');
-    shortcutText.id = shortcutTextId(identifier, i);
-    shortcutText.className = 'keyboard-overlay-shortcut-text';
-    shortcutText.style.visilibity = 'hidden';
-    key.appendChild(shortcutText);
-
-    if (isCompoundEnterKey) {
-      key.style.webkitClipPath = COMPOUND_ENTER_KEY_CLIP_PATH;
-      keyText.style.webkitClipPath = COMPOUND_ENTER_KEY_CLIP_PATH;
-      shortcutText.style.webkitClipPath = COMPOUND_ENTER_KEY_CLIP_PATH;
-
-      // Add an overlay div to account for clipping and show the borders.
-      var overlayDiv = document.createElement('div');
-      overlayDiv.id = keyId(identifier, i) + '-sub';
-      overlayDiv.className = 'keyboard-overlay-key';
-      var overlayDivX = x - 3;
-      var overlayDivY = y + Math.round(h * 0.47) + 2;
-      var overlayDivW = Math.round(w * 0.28);
-      var overlayDivH = Math.round(h * (1 - 0.47)) + 1;
-
-      overlayDiv.style.left = overlayDivX + 'px';
-      overlayDiv.style.top = overlayDivY + 'px';
-      overlayDiv.style.width = overlayDivW + 'px';
-      overlayDiv.style.height = overlayDivH + 'px';
-      overlayDiv.style.webkitClipPath =
-          COMPOUND_ENTER_KEY_OVERLAY_DIV_CLIP_PATH;
-      keyboard.appendChild(overlayDiv);
-    }
-
-    keyboard.appendChild(key);
-
-    minX = Math.min(minX, x);
-    maxX = Math.max(maxX, x + w);
-    minY = Math.min(minY, y);
-    maxY = Math.max(maxY, y + h);
-  }
-
-  var width = maxX - minX + 1;
-  var height = maxY - minY + 1;
-  keyboard.style.width = (width + 2 * (minX + 1)) + 'px';
-  keyboard.style.height = (height + 2 * (minY + 1)) + 'px';
-
-  instructions.style.left = ((BASE_INSTRUCTIONS.left - BASE_KEYBOARD.left) *
-                                 width / BASE_KEYBOARD.width +
-                             minX) +
-      'px';
-  instructions.style.top = ((BASE_INSTRUCTIONS.top - BASE_KEYBOARD.top) *
-                                height / BASE_KEYBOARD.height +
-                            minY) +
-      'px';
-  instructions.style.width =
-      (width * BASE_INSTRUCTIONS.width / BASE_KEYBOARD.width) + 'px';
-  instructions.style.height =
-      (height * BASE_INSTRUCTIONS.height / BASE_KEYBOARD.height) + 'px';
-}
-
-/**
- * Returns true if the device has a diamond key.
- * @return {boolean} Returns true if the device has a diamond key.
- */
-function hasDiamondKey() {
-  return loadTimeData.getBoolean('keyboardOverlayHasChromeOSDiamondKey');
-}
-
-/**
- * @return {boolean} True if the internal keyboard of the device has the new
- * keyboard layout.
- */
-function hasKeyboardLayout2() {
-  return loadTimeData.getBoolean('keyboardOverlayUsesLayout2');
-}
-
-/**
- * Returns true if display scaling feature is enabled.
- * @return {boolean} True if display scaling feature is enabled.
- */
-function isDisplayUIScalingEnabled() {
-  return loadTimeData.getBoolean('keyboardOverlayIsDisplayUIScalingEnabled');
-}
-
-/**
- * Modifies the current layout based on the given parameters.
- *
- * @param {Object<string, Array<number>>} newLayoutData The new or updated key
- * layout positions and sizes. The keys are the key IDs, and the values are
- * bounds of the corresponding keys.
- * @param {Object<string, Object<string, string>>} newKeyData The updated key
- * data such as labels and formats. The keys are the key IDs, and the values
- * are the key data.
- * @param {Array<string>} keyIdsToRemove The key IDs to remove from the
- * current layout.
- */
-function modifyLayoutAndKeyData(newLayoutData, newKeyData, keyIdsToRemove) {
-  var layout = getLayout();
-  var indicesToRemove = [];
-  for (var i = 0; i < layout.length; i++) {
-    var keyId = layout[i][0];
-    if (keyId in newLayoutData) {
-      layout[i] = [keyId].concat(newLayoutData[keyId]);
-      delete newLayoutData[keyId];
-    }
-
-    if (keyIdsToRemove.includes(keyId))
-      indicesToRemove.push(i);
-  }
-
-  // Remove the desired keys.
-  for (var i in indicesToRemove)
-    layout.splice(indicesToRemove[i], 1);
-
-  // Add the new ones.
-  for (var keyId in newLayoutData)
-    layout.push([keyId].concat(newLayoutData[keyId]));
-
-  // Update the new key data.
-  var keyData = getKeyboardGlyphData()['keys'];
-  for (var keyId in newKeyData)
-    keyData[keyId] = newKeyData[keyId];
-}
-
-/**
- * Initializes the layout and the key labels for the keyboard that has a diamond
- * key.
- */
-function initDiamondKey() {
-  var newLayoutData = {
-    '1D': [65.0, 287.0, 60.0, 60.0],      // left Ctrl
-    '38': [185.0, 287.0, 60.0, 60.0],     // left Alt
-    'E0 5B': [125.0, 287.0, 60.0, 60.0],  // search
-    '3A': [5.0, 167.0, 105.0, 60.0],      // caps lock
-    '5B': [803.0, 6.0, 72.0, 35.0],       // lock key
-    '5D': [5.0, 287.0, 60.0, 60.0]        // diamond key
-  };
-  var newKeyData = {
-    '3A': {'label': 'caps lock', 'format': 'left'},
-    '5B': {'label': 'lock'},
-    '5D': {'label': 'diamond', 'format': 'left'}
-  };
-  var keysToRemove = [
-    '00',  // The power key.
-  ];
-  modifyLayoutAndKeyData(newLayoutData, newKeyData, keysToRemove);
-}
-
-/**
- * Initializes the layout for devices which have the new keyboard layout.
- */
-function initKeyboardLayout2() {
-  var newLayoutData = {
-    '1D': [5.0, 287.0, 80.0, 60.0],    // left Ctrl
-    'D8': [85.0, 287.0, 80.0, 60.0],   // Assistant key
-    '38': [165.0, 287.0, 80.0, 60.0],  // left Alt
-    '5D': [803.0, 6.0, 72.0, 35.0],    // System menu
-  };
-  var newKeyData = {
-    'D8': {'label': 'assistant', 'format': 'left'},
-    '5D': {'label': 'system menu'},
-    'E0 5B': {'label': 'launcher', 'format': 'left'},
-    '3B': {'label': 'back'},
-    '3C': {'label': 'reload'},
-    '3D': {'label': 'full screen'},
-    '3E': {'label': 'switch window'},
-    '3F': {'label': 'bright down'},
-    '40': {'label': 'bright up'},
-    '41': {'label': 'play / pause'},
-    '42': {'label': 'mute'},
-    '43': {'label': 'vol. down'},
-    '44': {'label': 'vol. up'},
-  };
-  var keysToRemove = [
-    '00',  // The power key.
-  ];
-  modifyLayoutAndKeyData(newLayoutData, newKeyData, keysToRemove);
-
-  // Modify the Search + top row keys shortcuts:
-  var newShortcuts = {
-    'bright down<>SEARCH': 'keyboardOverlayF5',
-    'bright up<>SEARCH': 'keyboardOverlayF6',
-    'f2<>SEARCH': 'keyboardOverlayReloadKeyLabel',
-    'f3<>SEARCH': 'keyboardOverlayFullScreenKeyLabel',
-    'f4<>SEARCH': 'keyboardOverlaySwitchWinKeyLabel',
-    'f5<>SEARCH': 'keyboardOverlayBrightDownKeyLabel',
-    'f6<>SEARCH': 'keyboardOverlayBrightUpKeyLabel',
-    'f7<>SEARCH': 'keyboardOverlayPlayPauseKeyLabel',
-    'full screen<>SEARCH': 'keyboardOverlayF3',
-    'play / pause<>SEARCH': 'keyboardOverlayF7',
-    'reload<>SEARCH': 'keyboardOverlayF2',
-    'switch window<>SEARCH': 'keyboardOverlayF4',
-  };
-  var shortcutDataCache = keyboardOverlayData['shortcut'];
-  for (var shortcutId in newShortcuts)
-    shortcutDataCache[shortcutId] = newShortcuts[shortcutId];
-}
-
-/**
- * A callback function for the onload event of the body element.
- */
-function init() {
-  document.addEventListener('keydown', handleKeyEvent);
-  document.addEventListener('keyup', handleKeyEvent);
-  chrome.send('getLabelMap');
-}
-
-/**
- * Initializes the global map for remapping identifiers of modifier keys based
- * on the preference.
- * Called after sending the 'getLabelMap' message.
- * @param {Object} remap Identifier map.
- */
-function initIdentifierMap(remap) {
-  for (var key in remap) {
-    var val = remap[key];
-    if ((key in LABEL_TO_IDENTIFIER) && (val in LABEL_TO_IDENTIFIER)) {
-      identifierMap[LABEL_TO_IDENTIFIER[key]] = LABEL_TO_IDENTIFIER[val];
-    } else {
-      console.error('Invalid label map element: ' + key + ', ' + val);
-    }
-  }
-  chrome.send('getInputMethodId');
-}
-
-/**
- * Initializes the global keyboad overlay ID and the layout of keys.
- * Called after sending the 'getInputMethodId' message.
- * @param {inputMethodId} inputMethodId Input Method Identifier.
- */
-function initKeyboardOverlayId(inputMethodId) {
-  // Libcros returns an empty string when it cannot find the keyboard overlay ID
-  // corresponding to the current input method.
-  // In such a case, fallback to the default ID (en_US).
-  var inputMethodIdToOverlayId =
-      keyboardOverlayData['inputMethodIdToOverlayId'];
-  if (inputMethodId) {
-    if (inputMethodId.startsWith(IME_ID_PREFIX)) {
-      // If the input method is a component extension IME, remove the prefix:
-      //   _comp_ime_<ext_id>
-      // The extension id is a hash value with 32 characters.
-      inputMethodId =
-          inputMethodId.slice(IME_ID_PREFIX.length + EXTENSION_ID_LEN);
-    }
-    keyboardOverlayId = inputMethodIdToOverlayId[inputMethodId];
-  }
-  if (!keyboardOverlayId) {
-    console.error('No keyboard overlay ID for ' + inputMethodId);
-    keyboardOverlayId = 'en_US';
-  }
-  while (document.body.firstChild) {
-    document.body.removeChild(document.body.firstChild);
-  }
-  // We show Japanese layout as-is because the user has chosen the layout
-  // that is quite diffrent from the physical layout that has a diamond key.
-  if (hasDiamondKey() && getLayoutName() != 'J')
-    initDiamondKey();
-  else if (hasKeyboardLayout2())
-    initKeyboardLayout2();
-
-  initLayout();
-  update([], []);
-  window.webkitRequestAnimationFrame(function() {
-    chrome.send('didPaint');
-  });
-}
-
-/**
- * Handles click events of the learn more link.
- * @param {Event} e Mouse click event.
- */
-function learnMoreClicked(e) {
-  chrome.send('openLearnMorePage');
-  chrome.send('dialogClose');
-  e.preventDefault();
-}
-
-/**
- * Called from C++ when the widget is shown.
- */
-function onWidgetShown() {
-  setTimeout(function() {
-    // Show and focus the instructions div after a delay so that chrome vox
-    // speaks it correctly.
-    $('instructions').style.visibility = 'visible';
-    $('instructions').focus();
-  }, 500);
-}
-
-document.addEventListener('DOMContentLoaded', init);
diff --git a/chrome/browser/resources/chromeos/keyboard_overlay_data.js b/chrome/browser/resources/chromeos/keyboard_overlay_data.js
deleted file mode 100644
index 4024ecd..0000000
--- a/chrome/browser/resources/chromeos/keyboard_overlay_data.js
+++ /dev/null
@@ -1,4301 +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.
-
-// This is a generated file but may contain local modifications. See
-// src/tools/gen_keyboard_overlay_data/gen_keyboard_overlay_data.py --help
-
-var keyboardOverlayData = {
-  'inputMethodIdToOverlayId': {
-    'xkb:be::fra': 'fr',
-    'xkb:be::ger': 'de',
-    'xkb:be::nld': 'nl',
-    'xkb:bg::bul': 'bg',
-    'xkb:bg:phonetic:bul': 'bg',
-    'xkb:br::por': 'pt_BR',
-    'xkb:ca::fra': 'fr_CA',
-    'xkb:ca:eng:eng': 'ca',
-    'xkb:ch::ger': 'de',
-    'xkb:ch:fr:fra': 'fr',
-    'xkb:cz::cze': 'cs',
-    'xkb:de::ger': 'de',
-    'xkb:de:neo:ger': 'de_neo',
-    'xkb:dk::dan': 'da',
-    'xkb:ee::est': 'et',
-    'xkb:es::spa': 'es',
-    'xkb:es:cat:cat': 'ca',
-    'xkb:fi::fin': 'fi',
-    'xkb:fr::fra': 'fr',
-    'xkb:gb:dvorak:eng': 'en_GB_dvorak',
-    'xkb:gb:extd:eng': 'en_GB',
-    'xkb:gr::gre': 'el',
-    'xkb:hr::scr': 'hr',
-    'xkb:hu::hun': 'hu',
-    'xkb:il::heb': 'iw',
-    'xkb:it::ita': 'it',
-    'xkb:jp::jpn': 'ja',
-    'xkb:latam::spa': 'es_419',
-    'xkb:lt::lit': 'lt',
-    'xkb:lv:apostrophe:lav': 'lv',
-    'xkb:no::nob': 'no',
-    'xkb:pl::pol': 'pl',
-    'xkb:pt::por': 'pt_PT',
-    'xkb:ro::rum': 'ro',
-    'xkb:rs::srp': 'sr',
-    'xkb:ru::rus': 'ru',
-    'xkb:ru:phonetic:rus': 'ru',
-    'xkb:se::swe': 'sv',
-    'xkb:si::slv': 'sl',
-    'xkb:sk::slo': 'sk',
-    'xkb:tr::tur': 'tr',
-    'xkb:ua::ukr': 'uk',
-    'xkb:us::eng': 'en_US',
-    'xkb:us::ind': 'en_US',
-    'xkb:us::fil': 'en_US',
-    'xkb:us::msa': 'en_US',
-    'xkb:us:altgr-intl:eng': 'en_US_altgr_intl',
-    'xkb:us:colemak:eng': 'en_US_colemak',
-    'xkb:us:dvorak:eng': 'en_US_dvorak',
-    'xkb:us:intl:eng': 'en_US_intl',
-    'xkb:us:intl:nld': 'en_US_intl',
-    'xkb:us:intl:por': 'en_US_intl',
-    'vkd_ar': 'ar',
-    'vkd_fa': 'ar',
-    'vkd_deva_phone': 'hi',
-    'vkd_th': 'th',
-    'vkd_th_pattajoti': 'th',
-    'vkd_th_tis': 'th',
-    'vkd_vi_tcvn': 'vi',
-    'vkd_vi_telex': 'vi',
-    'vkd_vi_viqr': 'vi',
-    'vkd_vi_vni': 'vi',
-    'zh-hant-t-i0-cangjie-1987': 'zh_TW',
-    'zh-hant-t-i0-cangjie-1987-x-m0-simplified': 'zh_TW',
-    'zh-hant-t-i0-und': 'zh_TW',
-    'zh-t-i0-pinyin': 'zh_CN',
-    'hangul_2set': 'ko',
-    'hangul_3set390': 'ko',
-    'hangul_3setfinal': 'ko',
-    'hangul_3setnoshift': 'ko',
-    'hangul_romaja': 'ko',
-    'hangul_ahnmatae': 'ko',
-    'mozc-jp': 'ja',
-    'zinnia-japanese': 'ja'
-  },
-  'keyboardGlyph': {
-    'ar': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p1': '21', 'p7': '31', 'p9': '661'},
-        '03': {'p1': '40', 'p7': '32', 'p9': '662'},
-        '04': {'p1': '23', 'p7': '33', 'p9': '663'},
-        '05': {'p1': '24', 'p7': '34', 'p9': '664'},
-        '06': {'p1': '66A', 'p7': '35', 'p9': '665'},
-        '07': {'p1': '5E', 'p7': '36', 'p9': '666'},
-        '08': {'p1': '26', 'p7': '37', 'p9': '667'},
-        '09': {'p1': '66D', 'p7': '38', 'p9': '668'},
-        '0A': {'p1': '28', 'p7': '39', 'p9': '669'},
-        '0B': {'p1': '29', 'p7': '30', 'p9': '660'},
-        '0C': {'p1': '5F', 'p7': '2D'},
-        '0D': {'p1': '2B', 'p7': '3D'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p3': '064E', 'p7': '71', 'p9': 'FEBF'},
-        '11': {'p3': '064B', 'p7': '77', 'p9': 'FEBB'},
-        '12': {'p3': '064F', 'p7': '65', 'p9': 'FE9B'},
-        '13': {'p3': '064C', 'p7': '72', 'p9': 'FED7'},
-        '14': {'p3': 'FEF9', 'p7': '74', 'p9': 'FED3'},
-        '15': {'p3': 'FE87', 'p7': '79', 'p9': 'FECF'},
-        '16': {'p3': '60', 'p7': '75', 'p9': 'FECB'},
-        '17': {'p3': 'F7', 'p7': '69', 'p9': 'FEEB'},
-        '18': {'p3': 'D7', 'p7': '6F', 'p9': 'FEA7'},
-        '19': {'p3': '61B', 'p7': '70', 'p9': 'FEA3'},
-        '1A': {'p3': '3C', 'p9': 'FE9F'},
-        '1B': {'p3': '3E', 'p9': 'FEA9'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p3': '650', 'p7': '61', 'p9': 'FEB7'},
-        '1F': {'p3': '064D', 'p7': '73', 'p9': 'FEB3'},
-        '20': {'p3': '005B', 'p7': '64', 'p9': 'FEF3'},
-        '21': {'p3': '005D', 'p7': '66', 'p9': 'FE91'},
-        '22': {'p3': 'FEF7', 'p7': '67', 'p9': 'FEDF'},
-        '23': {'p3': 'FE83', 'p7': '68', 'p9': 'FE8D'},
-        '24': {'p3': '640', 'p7': '6A', 'p9': 'FE97'},
-        '25': {'p3': '060C', 'p7': '6B', 'p9': 'FEE7'},
-        '26': {'p3': '002F', 'p7': '6C', 'p9': 'FEE3'},
-        '27': {'p3': '003A', 'p9': 'FEDB'},
-        '28': {'p3': '22', 'p9': 'FEC3'},
-        '29': {'p1': '007E', 'p3': 'FE7C', 'p7': '60', 'p9': 'FEAB'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p1': '007c', 'p7': '005c'},
-        '2C': {'p3': '007E', 'p7': '7A', 'p9': 'FE8B'},
-        '2D': {'p3': 'FE7E', 'p7': '78', 'p9': 'FE80'},
-        '2E': {'p3': '007B', 'p7': '63', 'p9': 'FE85'},
-        '2F': {'p3': '007D', 'p7': '76', 'p9': 'FEAD'},
-        '30': {'p3': 'FEF5', 'p7': '62', 'p9': 'FEFB'},
-        '31': {'p3': 'FE81', 'p7': '6E', 'p9': 'FEEF'},
-        '32': {'p3': '27', 'p7': '6D', 'p9': 'FE93'},
-        '33': {'p3': '002C', 'p9': 'FEED'},
-        '34': {'p3': '002E', 'p9': 'FEAF'},
-        '35': {'p3': '61F', 'p9': 'FEC7'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '37': {},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    },
-    'bg': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p1': '21', 'p7': '31'},
-        '03': {'p1': '40', 'p3': '003f', 'p7': '32'},
-        '04': {'p1': '23', 'p3': '002b', 'p7': '33'},
-        '05': {'p1': '24', 'p3': '22', 'p7': '34'},
-        '06': {'p1': '25', 'p7': '35'},
-        '07': {'p1': '5e', 'p3': '003d', 'p7': '36'},
-        '08': {'p1': '26', 'p3': '003a', 'p7': '37'},
-        '09': {'p1': '2a', 'p3': '002f', 'p7': '38'},
-        '0A': {'p1': '28', 'p3': '005f', 'p7': '39'},
-        '0B': {'p1': '29', 'p3': '2116', 'p7': '30'},
-        '0C': {'p1': '5f', 'p3': '49', 'p7': '002d'},
-        '0D': {'p1': '2b', 'p3': '56', 'p7': '3d', 'p9': '002e'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p1': '71', 'p3': '44b', 'p9': '2c'},
-        '11': {'p1': '77', 'p9': '443'},
-        '12': {'p1': '65', 'p9': '435'},
-        '13': {'p1': '72', 'p9': '438'},
-        '14': {'p1': '74', 'p9': '448'},
-        '15': {'p1': '79', 'p9': '449'},
-        '16': {'p1': '75', 'p9': '43a'},
-        '17': {'p1': '69', 'p9': '441'},
-        '18': {'p1': '6f', 'p9': '434'},
-        '19': {'p1': '70', 'p9': '437'},
-        '1A': {'p1': '7b', 'p7': '5b', 'p9': '446'},
-        '1B': {'p1': '7d', 'p3': 'a7', 'p7': '5d', 'p9': '3b'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p1': '61', 'p9': '44c'},
-        '1F': {'p1': '73', 'p9': '44f'},
-        '20': {'p1': '64', 'p9': '430'},
-        '21': {'p1': '66', 'p9': '43e'},
-        '22': {'p1': '67', 'p9': '436'},
-        '23': {'p1': '68', 'p9': '433'},
-        '24': {'p1': '6a', 'p9': '442'},
-        '25': {'p1': '6b', 'p9': '43d'},
-        '26': {'p1': '6c', 'p9': '432'},
-        '27': {'p1': '3a', 'p7': '3b', 'p9': '43c'},
-        '28': {'p1': '201E', 'p7': '27', 'p9': '447'},
-        '29': {'p1': '007e', 'p7': '60'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p1': '7c', 'p3': '29', 'p7': '5c', 'p9': '28'},
-        '2C': {'p1': '7a', 'p9': '44e'},
-        '2D': {'p1': '78', 'p9': '439'},
-        '2E': {'p1': '63', 'p9': '44a'},
-        '2F': {'p1': '76', 'p9': '44d'},
-        '30': {'p1': '62', 'p9': '444'},
-        '31': {'p1': '6e', 'p9': '445'},
-        '32': {'p1': '6d', 'p9': '43f'},
-        '33': {'p1': '3c', 'p7': '2c', 'p9': '440'},
-        '34': {'p1': '3e', 'p7': '2e', 'p9': '43b'},
-        '35': {'p1': '3f', 'p7': '2f', 'p9': '431'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '37': {},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    },
-    'ca': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31', 'p9': '007C'},
-        '03': {'p2': '22', 'p8': '32', 'p9': '40'},
-        '04': {'p2': '00B7', 'p8': '33', 'p9': '23'},
-        '05': {'p2': '24', 'p8': '34', 'p9': '007E'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '26', 'p8': '36', 'p9': '00AC'},
-        '08': {'p2': '002F', 'p8': '37'},
-        '09': {'p2': '28', 'p8': '38'},
-        '0A': {'p2': '29', 'p8': '39'},
-        '0B': {'p2': '003D', 'p8': '30'},
-        '0C': {'p2': '003F', 'p8': '27'},
-        '0D': {'p2': '00BF', 'p8': '00A1'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65', 'p9': '20AC'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '006F'},
-        '19': {'p5': '70'},
-        '1A': {'p2': '005E', 'p8': '60', 'p9': '005B'},
-        '1B': {'p2': '002A', 'p8': '002B', 'p9': '005D'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '006A'},
-        '25': {'p5': '006B'},
-        '26': {'p5': '006C'},
-        '27': {'p5': '00F1'},
-        '28': {'p2': '00A8', 'p8': '00B4', 'p9': '007B'},
-        '29': {'p2': '00AA', 'p8': '00BA', 'p9': '005C'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '00C7', 'p8': 'E7', 'p9': '007D'},
-        '2C': {'p5': '007A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '006E'},
-        '32': {'p5': '006D'},
-        '33': {'p2': '003B', 'p8': '002C'},
-        '34': {'p2': '003A', 'p8': '002E'},
-        '35': {'p2': '005F', 'p8': '002D'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p2': '003E', 'p8': '003C'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'cs': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p1': '21', 'p3': '31', 'p7': '31', 'p9': '002b'},
-        '03': {'p1': '40', 'p3': '32', 'p7': '32', 'p9': '011B'},
-        '04': {'p1': '23', 'p3': '33', 'p7': '33', 'p9': '161'},
-        '05': {'p1': '24', 'p3': '34', 'p7': '34', 'p9': '010d'},
-        '06': {'p1': '25', 'p3': '35', 'p7': '35', 'p9': '159'},
-        '07': {'p1': '5e', 'p3': '36', 'p7': '36', 'p9': '017e'},
-        '08': {'p1': '26', 'p3': '37', 'p7': '37', 'p9': 'fd'},
-        '09': {'p1': '2a', 'p3': '38', 'p7': '38', 'p9': 'e1'},
-        '0A': {'p1': '28', 'p3': '39', 'p7': '39', 'p9': 'ed'},
-        '0B': {'p1': '29', 'p3': '30', 'p7': '30', 'p9': 'e9'},
-        '0C': {'p1': '5f', 'p3': '25', 'p7': '2d', 'p9': '3d'},
-        '0D': {'p1': '2b', 'p3': '2c7', 'p7': '3d', 'p9': 'b4'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65', 'p9': '20ac'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '7A', 'p9': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '6F'},
-        '19': {'p5': '70'},
-        '1A': {'p1': '7b', 'p3': '002f', 'p7': '5b', 'p9': 'fa'},
-        '1B': {'p1': '7d', 'p3': '28', 'p7': '5d', 'p9': '29'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6A'},
-        '25': {'p5': '6B'},
-        '26': {'p5': '6C'},
-        '27': {'p1': '3a', 'p3': '22', 'p7': '3b', 'p9': '16f'},
-        '28': {'p1': '22', 'p3': '21', 'p7': '27', 'p9': 'a7'},
-        '29': {'p1': '7e', 'p3': 'b0', 'p7': '60', 'p9': '3b'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p1': '007c', 'p3': '27', 'p7': '5c', 'p9': 'a8'},
-        '2C': {'p5': '79', 'p9': '7A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '6E'},
-        '32': {'p5': '6D'},
-        '33': {'p1': '3c', 'p3': '3f', 'p7': '2c', 'p9': '2c'},
-        '34': {'p1': '3e', 'p3': '3a', 'p7': '2e', 'p9': '2e'},
-        '35': {'p1': '3f', 'p3': '5f', 'p7': '2f', 'p9': '2d'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p1': '7c', 'p3': '2a', 'p7': '5c', 'p9': '26'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'da': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p1': '21', 'p7': '31'},
-        '03': {'p1': '22', 'p7': '32', 'p9': '40'},
-        '04': {'p1': '23', 'p7': '33', 'p9': '00A3'},
-        '05': {'p1': '00A4', 'p7': '34', 'p9': '24'},
-        '06': {'p1': '25', 'p7': '35'},
-        '07': {'p1': '26', 'p7': '36'},
-        '08': {'p1': '002F', 'p7': '37', 'p9': '007B'},
-        '09': {'p1': '28', 'p7': '38', 'p9': '005B'},
-        '0A': {'p1': '29', 'p7': '39', 'p9': '005D'},
-        '0B': {'p1': '003D', 'p7': '30', 'p9': '007D'},
-        '0C': {'p1': '003F', 'p7': '002B'},
-        '0D': {'p1': '60', 'p7': '00B4', 'p9': '007C'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65', 'p9': '20AC'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '006F'},
-        '19': {'p5': '70'},
-        '1A': {'p5': 'E5'},
-        '1B': {'p1': '005E', 'p7': '00A8', 'p9': '007E'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '006A'},
-        '25': {'p5': '006B'},
-        '26': {'p5': '006C'},
-        '27': {'p5': 'E6'},
-        '28': {'p5': '00F8'},
-        '29': {'p1': '00A7', 'p7': '00BD'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p1': '002A', 'p7': '27'},
-        '2C': {'p5': '007A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '006E'},
-        '32': {'p5': '006D'},
-        '33': {'p1': '003B', 'p7': '002C'},
-        '34': {'p1': '003A', 'p7': '002E'},
-        '35': {'p1': '005F', 'p7': '002D'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p1': '003E', 'p7': '003C', 'p9': '005C'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'de': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31'},
-        '03': {'p2': '22', 'p8': '32', 'p9': '00B2'},
-        '04': {'p2': '00A7', 'p8': '33', 'p9': '00B3'},
-        '05': {'p2': '24', 'p8': '34'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '26', 'p8': '36'},
-        '08': {'p2': '002F', 'p8': '37', 'p9': '007B'},
-        '09': {'p2': '28', 'p8': '38', 'p9': '005B'},
-        '0A': {'p2': '29', 'p8': '39', 'p9': '005D'},
-        '0B': {'p2': '3D', 'p8': '30', 'p9': '7D'},
-        '0C': {'p2': '3F', 'p8': 'DF', 'p9': '5C'},
-        '0D': {'p2': '60', 'p8': 'B4'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71', 'p9': '40'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65', 'p9': '20AC'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '007A'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '006F'},
-        '19': {'p5': '70'},
-        '1A': {'p5': '00FC'},
-        '1B': {'p2': '2A', 'p8': '2B', 'p9': '7E'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'strg'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '006A'},
-        '25': {'p5': '006B'},
-        '26': {'p5': '006C'},
-        '27': {'p5': '00F6'},
-        '28': {'p5': 'E4'},
-        '29': {'p2': '00B0', 'p8': '005E'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '27', 'p8': '23'},
-        '2C': {'p5': '79'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '006E'},
-        '32': {'p5': '006D', 'p9': 'B5'},
-        '33': {'p2': '3B', 'p8': '2C'},
-        '34': {'p2': '3A', 'p8': '2E'},
-        '35': {'p2': '5F', 'p8': '2D'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p2': '3E', 'p8': '3C', 'p9': '7C'},
-        'E0 1D': {'format': 'smaller', 'label': 'strg'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'de_neo': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '00B0', 'p8': '31', 'p9': '00B9'},
-        '03': {'p2': '00A7', 'p8': '32', 'p9': '00B2'},
-        '04': {'p2': '2113', 'p8': '33', 'p9': '00B3'},
-        '05': {'p2': '00BB', 'p8': '34', 'p9': '203A'},
-        '06': {'p2': '00AB', 'p8': '35', 'p9': '2039'},
-        '07': {'p2': '24', 'p8': '36', 'p9': '00A2'},
-        '08': {'p2': '20AC', 'p8': '37', 'p9': '00A5'},
-        '09': {'p2': '201E', 'p8': '38', 'p9': '201A'},
-        '0A': {'p2': '201C', 'p8': '39', 'p9': '2018'},
-        '0B': {'p2': '201D', 'p8': '30', 'p9': '2019'},
-        '0C': {'p2': '2014', 'p8': '2D'},
-        '0D': {'p2': '00B8', 'p8': '02CB', 'p9': '02DA'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '78', 'p9': '2026'},
-        '11': {'p5': '76', 'p9': '005F'},
-        '12': {'p5': '6C', 'p9': '005B'},
-        '13': {'p5': '63', 'p9': '005D'},
-        '14': {'p5': '77', 'p9': '005E'},
-        '15': {'p5': '6B', 'p9': '21'},
-        '16': {'p5': '68', 'p9': '003C'},
-        '17': {'p5': '67', 'p9': '003E'},
-        '18': {'p5': '66', 'p9': '003D'},
-        '19': {'p5': '71', 'p9': '26'},
-        '1A': {'p5': 'DF', 'p9': '017F'},
-        '1B': {'p2': '02DC', 'p8': '02CA', 'p9': '002F'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'strg'},
-        '1E': {'p5': '75', 'p9': '005C'},
-        '1F': {'p5': '69', 'p9': '002F'},
-        '20': {'p5': '61', 'p9': '007B'},
-        '21': {'p5': '65', 'p9': '007D'},
-        '22': {'p5': '6F', 'p9': '002A'},
-        '23': {'p5': '73', 'p9': '003F'},
-        '24': {'p5': '6E', 'p9': '28'},
-        '25': {'p5': '72', 'p9': '29'},
-        '26': {'p5': '74', 'p9': '002D'},
-        '27': {'p5': '64', 'p9': '003A'},
-        '28': {'p5': '79', 'p9': '40'},
-        '29': {'p2': '02C7', 'p8': '02C6'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'label': 'mod3'},
-        '2C': {'p5': '00FC', 'p9': '23'},
-        '2D': {'p5': '00F6', 'p9': '24'},
-        '2E': {'p5': 'E4', 'p9': '007C'},
-        '2F': {'p5': '70', 'p9': '007E'},
-        '30': {'p5': '7a', 'p9': '60'},
-        '31': {'p5': '62', 'p9': '002B'},
-        '32': {'p5': '6D', 'p9': '25'},
-        '33': {'p2': '2013', 'p8': '2C', 'p9': '22'},
-        '34': {'p2': '2022', 'p8': '2E', 'p9': '27'},
-        '35': {'p5': '6A', 'p9': '003B'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'label': 'mod4'},
-        'E0 1D': {'format': 'smaller', 'label': 'strg'},
-        'E0 38': {'format': 'smaller', 'label': 'mod4'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'mod3'}
-      },
-      'layoutName': 'E'
-    },
-    'el': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'Esc'},
-        '02': {'p1': '21', 'p7': '31'},
-        '03': {'p1': '40', 'p7': '32'},
-        '04': {'p1': '23', 'p7': '33'},
-        '05': {'p1': '24', 'p7': '34'},
-        '06': {'p1': '25', 'p7': '35'},
-        '07': {'p1': '005E', 'p7': '36'},
-        '08': {'p1': '26', 'p7': '37'},
-        '09': {'p1': '002A', 'p7': '38'},
-        '0A': {'p1': '28', 'p7': '39'},
-        '0B': {'p1': '29', 'p7': '30'},
-        '0C': {'p1': '005F', 'p7': '002D'},
-        '0D': {'p1': '002B', 'p7': '003D'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p1': '51', 'p3': '3A', 'p9': '3B'},
-        '11': {'p1': '57', 'p9': '3C2'},
-        '12': {'p1': '45', 'p9': '20AC'},
-        '13': {'p1': '52', 'p9': '03A1'},
-        '14': {'p1': '54'},
-        '15': {'p1': '03A5'},
-        '16': {'p1': '55', 'p9': '398'},
-        '17': {'p1': '399'},
-        '18': {'p1': '039F'},
-        '19': {'p1': '50', 'p9': '03A0'},
-        '1A': {'p1': '007B', 'p7': '005B'},
-        '1B': {'p1': '007D', 'p7': '005D'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'Ctrl'},
-        '1E': {'p1': '391'},
-        '1F': {'p1': '53', 'p9': '03A3'},
-        '20': {'p1': '44', 'p9': '394'},
-        '21': {'p1': '46', 'p9': '3A6'},
-        '22': {'p1': '47', 'p9': '393'},
-        '23': {'p1': '397'},
-        '24': {'p1': '004A', 'p9': '039E'},
-        '25': {'p1': '039A'},
-        '26': {'p1': '004C', 'p9': '039B'},
-        '27': {'p1': '003A', 'p3': '00A8', 'p7': '003B', 'p9': '00B4'},
-        '28': {'p1': '22', 'p7': '27'},
-        '29': {'p1': '007E', 'p7': '60'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p1': '007C', 'p7': '005C'},
-        '2C': {'p1': '396'},
-        '2D': {'p1': '03A7'},
-        '2E': {'p1': '43', 'p9': '03A8'},
-        '2F': {'p1': '56', 'p9': '03A9'},
-        '30': {'p1': '392'},
-        '31': {'p1': '039D'},
-        '32': {'p1': '039C'},
-        '33': {'p1': '003C', 'p7': '002C'},
-        '34': {'p1': '003E', 'p7': '002E'},
-        '35': {'p1': '003F', 'p7': '002F'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'Alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'Ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'Alt Gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    },
-    'en_GB': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31'},
-        '03': {'p2': '22', 'p8': '32'},
-        '04': {'p2': 'A3', 'p8': '33'},
-        '05': {'p2': '24', 'p8': '34', 'p9': '20AC'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '5E', 'p8': '36'},
-        '08': {'p2': '26', 'p8': '37'},
-        '09': {'p2': '2A', 'p8': '38'},
-        '0A': {'p2': '28', 'p8': '39'},
-        '0B': {'p2': '29', 'p8': '30'},
-        '0C': {'p2': '5F', 'p8': '2D'},
-        '0D': {'p2': '2B', 'p8': '3D'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '6F'},
-        '19': {'p5': '70'},
-        '1A': {'p2': '7B', 'p8': '5B'},
-        '1B': {'p2': '7D', 'p8': '5D'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6A'},
-        '25': {'p5': '6B'},
-        '26': {'p5': '6C'},
-        '27': {'p2': '3A', 'p8': '3B'},
-        '28': {'p2': '40', 'p8': '27'},
-        '29': {'p2': 'AC', 'p8': '60', 'p9': '00A6'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '007E', 'p8': '23'},
-        '2C': {'p5': '7A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '6E'},
-        '32': {'p5': '6D'},
-        '33': {'p2': '3C', 'p8': '2C'},
-        '34': {'p2': '3E', 'p8': '2E'},
-        '35': {'p2': '3F', 'p8': '2F'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p2': '007C', 'p8': '005C'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'en_GB_dvorak': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31'},
-        '03': {'p2': '22', 'p8': '32'},
-        '04': {'p2': 'A3', 'p8': '33'},
-        '05': {'p2': '24', 'p8': '34', 'p9': '20AC'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '5E', 'p8': '36'},
-        '08': {'p2': '26', 'p8': '37'},
-        '09': {'p2': '2A', 'p8': '38'},
-        '0A': {'p2': '28', 'p8': '39'},
-        '0B': {'p2': '29', 'p8': '30'},
-        '0C': {'p2': '7B', 'p8': '5B'},
-        '0D': {'p2': '7D', 'p8': '5D'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p2': '40', 'p8': '27'},
-        '11': {'p2': '3C', 'p8': '2C'},
-        '12': {'p2': '3E', 'p8': '2E'},
-        '13': {'p5': '70'},
-        '14': {'p5': '79'},
-        '15': {'p5': '66'},
-        '16': {'p5': '67'},
-        '17': {'p5': '63'},
-        '18': {'p5': '72'},
-        '19': {'p5': '6C'},
-        '1A': {'p2': '3F', 'p8': '2F'},
-        '1B': {'p2': '2B', 'p8': '3D'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '6F'},
-        '20': {'p5': '65'},
-        '21': {'p5': '75'},
-        '22': {'p5': '69'},
-        '23': {'p5': '64'},
-        '24': {'p5': '68'},
-        '25': {'p5': '74'},
-        '26': {'p5': '6E'},
-        '27': {'p5': '73'},
-        '28': {'p2': '5F', 'p8': '2D'},
-        '29': {'p2': 'AC', 'p8': '60', 'p9': '00A6'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '007E', 'p8': '23'},
-        '2C': {'p2': '3A', 'p8': '3B'},
-        '2D': {'p5': '71'},
-        '2E': {'p5': '6A'},
-        '2F': {'p5': '6B'},
-        '30': {'p5': '78'},
-        '31': {'p5': '62'},
-        '32': {'p5': '6D'},
-        '33': {'p5': '77'},
-        '34': {'p5': '76'},
-        '35': {'p5': '7A'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p2': '007C', 'p8': '005C'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'en_US': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31'},
-        '03': {'p2': '40', 'p8': '32'},
-        '04': {'p2': '23', 'p8': '33'},
-        '05': {'p2': '24', 'p8': '34'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '5E', 'p8': '36'},
-        '08': {'p2': '26', 'p8': '37'},
-        '09': {'p2': '2A', 'p8': '38'},
-        '0A': {'p2': '28', 'p8': '39'},
-        '0B': {'p2': '29', 'p8': '30'},
-        '0C': {'p2': '5F', 'p8': '2D'},
-        '0D': {'p2': '2B', 'p8': '3D'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '6F'},
-        '19': {'p5': '70'},
-        '1A': {'p2': '7B', 'p8': '5B'},
-        '1B': {'p2': '7D', 'p8': '5D'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6A'},
-        '25': {'p5': '6B'},
-        '26': {'p5': '6C'},
-        '27': {'p2': '3A', 'p8': '3B'},
-        '28': {'p2': '22', 'p8': '27'},
-        '29': {'p2': '7E', 'p8': '60'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '7C', 'p8': '5C'},
-        '2C': {'p5': '7A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '6E'},
-        '32': {'p5': '6D'},
-        '33': {'p2': '3C', 'p8': '2C'},
-        '34': {'p2': '3E', 'p8': '2E'},
-        '35': {'p2': '3F', 'p8': '2F'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    },
-    'en_US_altgr_intl': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31'},
-        '03': {'p2': '40', 'p8': '32'},
-        '04': {'p2': '23', 'p8': '33'},
-        '05': {'p2': '24', 'p8': '34'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '5E', 'p8': '36'},
-        '08': {'p2': '26', 'p8': '37'},
-        '09': {'p2': '2A', 'p8': '38'},
-        '0A': {'p2': '28', 'p8': '39'},
-        '0B': {'p2': '29', 'p8': '30'},
-        '0C': {'p2': '5F', 'p8': '2D'},
-        '0D': {'p2': '2B', 'p8': '3D'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '6F'},
-        '19': {'p5': '70'},
-        '1A': {'p2': '7B', 'p8': '5B'},
-        '1B': {'p2': '7D', 'p8': '5D'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6A'},
-        '25': {'p5': '6B'},
-        '26': {'p5': '6C'},
-        '27': {'p2': '3A', 'p8': '3B'},
-        '28': {'p2': '22', 'p8': '27'},
-        '29': {'p2': '7E', 'p8': '60'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '7C', 'p8': '5C'},
-        '2C': {'p5': '7A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '6E'},
-        '32': {'p5': '6D'},
-        '33': {'p2': '3C', 'p8': '2C'},
-        '34': {'p2': '3E', 'p8': '2E'},
-        '35': {'p2': '3F', 'p8': '2F'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    },
-    'en_US_colemak': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31'},
-        '03': {'p2': '40', 'p8': '32'},
-        '04': {'p2': '23', 'p8': '33'},
-        '05': {'p2': '24', 'p8': '34'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '5E', 'p8': '36'},
-        '08': {'p2': '26', 'p8': '37'},
-        '09': {'p2': '2A', 'p8': '38'},
-        '0A': {'p2': '28', 'p8': '39'},
-        '0B': {'p2': '29', 'p8': '30'},
-        '0C': {'p2': '5F', 'p8': '2D'},
-        '0D': {'p2': '2B', 'p8': '3D'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '66'},
-        '13': {'p5': '70'},
-        '14': {'p5': '67'},
-        '15': {'p5': '6A'},
-        '16': {'p5': '6C'},
-        '17': {'p5': '75'},
-        '18': {'p5': '79'},
-        '19': {'p2': '3A', 'p8': '3B'},
-        '1A': {'p2': '7B', 'p8': '5B'},
-        '1B': {'p2': '7D', 'p8': '5D'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '72'},
-        '20': {'p5': '73'},
-        '21': {'p5': '74'},
-        '22': {'p5': '64'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6E'},
-        '25': {'p5': '65'},
-        '26': {'p5': '69'},
-        '27': {'p5': '6F'},
-        '28': {'p2': '22', 'p8': '27'},
-        '29': {'p2': '7E', 'p8': '60'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '7C', 'p8': '5C'},
-        '2C': {'p5': '7A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '6B'},
-        '32': {'p5': '6D'},
-        '33': {'p2': '3C', 'p8': '2C'},
-        '34': {'p2': '3E', 'p8': '2E'},
-        '35': {'p2': '3F', 'p8': '2F'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '37': {},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'backspace'}
-      },
-      'layoutName': 'U'
-    },
-    'en_US_dvorak': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31'},
-        '03': {'p2': '40', 'p8': '32'},
-        '04': {'p2': '23', 'p8': '33'},
-        '05': {'p2': '24', 'p8': '34'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '5E', 'p8': '36'},
-        '08': {'p2': '26', 'p8': '37'},
-        '09': {'p2': '2A', 'p8': '38'},
-        '0A': {'p2': '28', 'p8': '39'},
-        '0B': {'p2': '29', 'p8': '30'},
-        '0C': {'p2': '7B', 'p8': '5B'},
-        '0D': {'p2': '7D', 'p8': '5D'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p2': '22', 'p8': '27'},
-        '11': {'p2': '3C', 'p8': '2C'},
-        '12': {'p2': '3E', 'p8': '2E'},
-        '13': {'p5': '70'},
-        '14': {'p5': '79'},
-        '15': {'p5': '66'},
-        '16': {'p5': '67'},
-        '17': {'p5': '63'},
-        '18': {'p5': '72'},
-        '19': {'p5': '6C'},
-        '1A': {'p2': '3F', 'p8': '2F'},
-        '1B': {'p2': '2B', 'p8': '3D'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '6F'},
-        '20': {'p5': '65'},
-        '21': {'p5': '75'},
-        '22': {'p5': '69'},
-        '23': {'p5': '64'},
-        '24': {'p5': '68'},
-        '25': {'p5': '74'},
-        '26': {'p5': '6E'},
-        '27': {'p5': '73'},
-        '28': {'p2': '5F', 'p8': '2D'},
-        '29': {'p2': '7E', 'p8': '60'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '7C', 'p8': '5C'},
-        '2C': {'p2': '3A', 'p8': '3B'},
-        '2D': {'p5': '71'},
-        '2E': {'p5': '6A'},
-        '2F': {'p5': '6B'},
-        '30': {'p5': '78'},
-        '31': {'p5': '62'},
-        '32': {'p5': '6D'},
-        '33': {'p5': '77'},
-        '34': {'p5': '76'},
-        '35': {'p5': '7A'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '37': {},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    },
-    'en_US_intl': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31'},
-        '03': {'p2': '40', 'p8': '32'},
-        '04': {'p2': '23', 'p8': '33'},
-        '05': {'p2': '24', 'p8': '34'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '5E', 'p8': '36'},
-        '08': {'p2': '26', 'p8': '37'},
-        '09': {'p2': '2A', 'p8': '38'},
-        '0A': {'p2': '28', 'p8': '39'},
-        '0B': {'p2': '29', 'p8': '30'},
-        '0C': {'p2': '5F', 'p8': '2D'},
-        '0D': {'p2': '2B', 'p8': '3D'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '6F'},
-        '19': {'p5': '70'},
-        '1A': {'p2': '7B', 'p8': '5B'},
-        '1B': {'p2': '7D', 'p8': '5D'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6A'},
-        '25': {'p5': '6B'},
-        '26': {'p5': '6C'},
-        '27': {'p2': '3A', 'p8': '3B'},
-        '28': {'p2': '22', 'p8': '27'},
-        '29': {'p2': '7E', 'p8': '60'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '7C', 'p8': '5C'},
-        '2C': {'p5': '7A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '6E'},
-        '32': {'p5': '6D'},
-        '33': {'p2': '3C', 'p8': '2C'},
-        '34': {'p2': '3E', 'p8': '2E'},
-        '35': {'p2': '3F', 'p8': '2F'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    },
-    'es': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31', 'p9': '007C'},
-        '03': {'p2': '22', 'p8': '32', 'p9': '40'},
-        '04': {'p2': '00B7', 'p8': '33', 'p9': '23'},
-        '05': {'p2': '24', 'p8': '34', 'p9': '007E'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '26', 'p8': '36', 'p9': '00AC'},
-        '08': {'p2': '002F', 'p8': '37'},
-        '09': {'p2': '28', 'p8': '38'},
-        '0A': {'p2': '29', 'p8': '39'},
-        '0B': {'p2': '003D', 'p8': '30'},
-        '0C': {'p2': '003F', 'p8': '27'},
-        '0D': {'p2': '00BF', 'p8': '00A1'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65', 'p9': '20AC'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '006F'},
-        '19': {'p5': '70'},
-        '1A': {'p2': '005E', 'p8': '60', 'p9': '005B'},
-        '1B': {'p2': '002A', 'p8': '002B', 'p9': '005D'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '006A'},
-        '25': {'p5': '006B'},
-        '26': {'p5': '006C'},
-        '27': {'p5': '00F1'},
-        '28': {'p2': '00A8', 'p8': '00B4', 'p9': '007B'},
-        '29': {'p2': '00AA', 'p8': '00BA', 'p9': '005C'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '00C7', 'p8': 'E7', 'p9': '007D'},
-        '2C': {'p5': '007A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '006E'},
-        '32': {'p5': '006D'},
-        '33': {'p2': '003B', 'p8': '002C'},
-        '34': {'p2': '003A', 'p8': '002E'},
-        '35': {'p2': '005F', 'p8': '002D'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p2': '003E', 'p8': '003C'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'es_419': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31'},
-        '03': {'p2': '22', 'p8': '32'},
-        '04': {'p2': '23', 'p8': '33'},
-        '05': {'p2': '24', 'p8': '34'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '26', 'p8': '36'},
-        '08': {'p2': '002F', 'p8': '37'},
-        '09': {'p2': '28', 'p8': '38'},
-        '0A': {'p2': '29', 'p8': '39'},
-        '0B': {'p2': '3d', 'p8': '30'},
-        '0C': {'p2': '3f', 'p8': '27', 'p9': '005C'},
-        '0D': {'p2': 'a1', 'p8': 'bf'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71', 'p9': '40'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '6f'},
-        '19': {'p5': '70'},
-        '1A': {'p2': 'A8', 'p8': 'B4'},
-        '1B': {'p2': '2a', 'p8': '2b', 'p9': '7e'},
-        '1C': {'format': 'right', 'label': 'intro'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6a'},
-        '25': {'p5': '6b'},
-        '26': {'p5': '6c'},
-        '27': {'p5': 'f1'},
-        '28': {'p2': '5B', 'p8': '7B', 'p9': '5E'},
-        '29': {'p2': '00B0', 'p8': '007C', 'p9': '00AC'},
-        '2A': {'format': 'left', 'label': 'may\u00fas'},
-        '2B': {'p2': '5D', 'p8': '7D', 'p9': '60'},
-        '2C': {'p5': '7a'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '6e'},
-        '32': {'p5': '6d'},
-        '33': {'p2': '3b', 'p8': '2C'},
-        '34': {'p2': '3a', 'p8': '2e'},
-        '35': {'p2': '5f', 'p8': '2d'},
-        '36': {'format': 'right', 'label': 'may\u00fas'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p2': '3e', 'p8': '3c'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'et': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31'},
-        '03': {'p2': '22', 'p8': '32', 'p9': '40'},
-        '04': {'p2': '23', 'p8': '33', 'p9': 'A3'},
-        '05': {'p2': 'A4', 'p8': '34', 'p9': '24'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '26', 'p8': '36'},
-        '08': {'p2': '2F', 'p8': '37', 'p9': '7B'},
-        '09': {'p2': '28', 'p8': '38', 'p9': '5B'},
-        '0A': {'p2': '29', 'p8': '39', 'p9': '5D'},
-        '0B': {'p2': '3D', 'p8': '30', 'p9': '7D'},
-        '0C': {'p2': '3F', 'p8': '2B', 'p9': '5C'},
-        '0D': {'p2': '60', 'p8': 'B4'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65', 'p9': '20AC'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '006F'},
-        '19': {'p5': '70'},
-        '1A': {'p5': 'FC'},
-        '1B': {'p5': 'F5', 'p9': 'A7'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '006A'},
-        '25': {'p5': '006B'},
-        '26': {'p5': '006C'},
-        '27': {'p5': '00F6'},
-        '28': {'p5': 'E4', 'p9': '5E'},
-        '29': {'p2': '7E', 'p8': '2C7'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '2A', 'p8': '27', 'p9': 'BD'},
-        '2C': {'p5': '007A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '006E'},
-        '32': {'p5': '006D'},
-        '33': {'p2': '3B', 'p8': '2C'},
-        '34': {'p2': '3A', 'p8': '2E'},
-        '35': {'p2': '5F', 'p8': '2D'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p2': '3E', 'p8': '3C', 'p9': '7C'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'fi': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31'},
-        '03': {'p2': '22', 'p8': '32', 'p9': '40'},
-        '04': {'p2': '23', 'p8': '33', 'p9': '00A3'},
-        '05': {'p2': '00A4', 'p8': '34', 'p9': '24'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '26', 'p8': '36'},
-        '08': {'p2': '002F', 'p8': '37', 'p9': '007B'},
-        '09': {'p2': '28', 'p8': '38', 'p9': '005B'},
-        '0A': {'p2': '29', 'p8': '39', 'p9': '005D'},
-        '0B': {'p2': '003D', 'p8': '30', 'p9': '007D'},
-        '0C': {'p2': '003F', 'p8': '002B', 'p9': '005C'},
-        '0D': {'p2': '60', 'p8': '00B4'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65', 'p9': '20AC'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '006F'},
-        '19': {'p5': '70'},
-        '1A': {'p5': 'E5'},
-        '1B': {'p2': '005E', 'p8': '00A8', 'p9': '007E'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '006A'},
-        '25': {'p5': '006B'},
-        '26': {'p5': '006C'},
-        '27': {'p5': '00F6'},
-        '28': {'p5': 'E4'},
-        '29': {'p2': '00BD', 'p8': '00A7'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '002A', 'p8': '27'},
-        '2C': {'p5': '007A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '006E'},
-        '32': {'p5': '006D'},
-        '33': {'p2': '003B', 'p8': '002C'},
-        '34': {'p2': '003A', 'p8': '002E'},
-        '35': {'p2': '005F', 'p8': '002D'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p2': '003E', 'p8': '003C', 'p9': '007C'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'fr': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': '\u00e9chap'},
-        '02': {'p2': '31', 'p8': '26'},
-        '03': {'p2': '32', 'p8': 'E9', 'p9': '007E'},
-        '04': {'p2': '33', 'p8': '22', 'p9': '23'},
-        '05': {'p2': '34', 'p8': '27', 'p9': '007B'},
-        '06': {'p2': '35', 'p8': '28', 'p9': '005B'},
-        '07': {'p2': '36', 'p8': '2D', 'p9': '007C'},
-        '08': {'p2': '37', 'p8': 'E8', 'p9': '60'},
-        '09': {'p2': '38', 'p8': '5F', 'p9': '005C'},
-        '0A': {'p2': '39', 'p8': 'E7', 'p9': '005E'},
-        '0B': {'p2': '30', 'p8': 'E0', 'p9': '40'},
-        '0C': {'p2': '00B0', 'p8': '29', 'p9': '005D'},
-        '0D': {'p2': '002B', 'p8': '003D', 'p9': '007D'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '61'},
-        '11': {'p5': '007A'},
-        '12': {'p5': '65', 'p9': '20AC'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '006F'},
-        '19': {'p5': '70'},
-        '1A': {'p2': 'A8', 'p8': '5E'},
-        '1B': {'p2': 'A3', 'p8': '24', 'p9': 'A4'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '71'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '006A'},
-        '25': {'p5': '006B'},
-        '26': {'p5': '006C'},
-        '27': {'p5': '006D'},
-        '28': {'p2': '25', 'p8': 'F9'},
-        '29': {'p8': 'B2'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': 'B5', 'p8': '2A'},
-        '2C': {'p5': '77'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '006E'},
-        '32': {'p2': '3F', 'p8': '2C'},
-        '33': {'p2': '2E', 'p8': '3B'},
-        '34': {'p2': '2F', 'p8': '3A'},
-        '35': {'p2': 'A7', 'p8': '21'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p2': '3E', 'p8': '3C'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'fr_CA': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': '\u00e9chap'},
-        '02': {'p2': '21', 'p8': '31', 'p9': 'B1'},
-        '03': {'p2': '22', 'p8': '32', 'p9': '40'},
-        '04': {'p2': '2F', 'p8': '33', 'p9': 'A3'},
-        '05': {'p2': '24', 'p8': '34', 'p9': 'A2'},
-        '06': {'p2': '25', 'p8': '35', 'p9': 'A4'},
-        '07': {'p2': '3F', 'p8': '36', 'p9': 'AC'},
-        '08': {'p2': '26', 'p8': '37', 'p9': 'A6'},
-        '09': {'p2': '2A', 'p8': '38', 'p9': 'B2'},
-        '0A': {'p2': '28', 'p8': '39', 'p9': 'B3'},
-        '0B': {'p2': '29', 'p8': '30', 'p9': 'BC'},
-        '0C': {'p2': '5F', 'p8': '2D', 'p9': 'BD'},
-        '0D': {'p2': '2B', 'p8': '3D', 'p9': 'BE'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '006F', 'p9': '00A7'},
-        '19': {'p5': '70', 'p9': '00B6'},
-        '1A': {'p2': '5E', 'p8': '5E', 'p9': '5B'},
-        '1B': {'p2': 'A8', 'p8': 'B8', 'p9': '5D'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6A'},
-        '25': {'p5': '6B'},
-        '26': {'p5': '6C'},
-        '27': {'p2': '3A', 'p8': '3B', 'p9': '7E'},
-        '28': {'p2': '60', 'p8': '60', 'p9': '7B'},
-        '29': {'p2': '7C', 'p8': '23', 'p9': '5C'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '3E', 'p8': '3C', 'p9': '7D'},
-        '2C': {'p5': '7A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '6E'},
-        '32': {'p5': '6D', 'p9': '00B5'},
-        '33': {'p2': '27', 'p8': '2C', 'p9': 'AF'},
-        '34': {'p2': '2E', 'p8': '2E', 'p9': 'AD'},
-        '35': {'p2': 'C9', 'p8': 'E9', 'p9': 'B4'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p2': 'BB', 'p8': 'AB', 'p9': 'B0'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'hi': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p1': '21', 'p3': '090D', 'p7': '31', 'p9': '967'},
-        '03': {'p1': '40', 'p3': '945', 'p7': '32', 'p9': '968'},
-        '04': {'p1': '23', 'p3': '094D0930', 'p7': '33', 'p9': '969'},
-        '05': {'p1': '24', 'p3': '0930094D', 'p7': '34', 'p9': '96A'},
-        '06': {'p1': '25', 'p3': '091C094D091E', 'p7': '35', 'p9': '96B'},
-        '07': {'p1': '5E', 'p3': '0924094D0930', 'p7': '36', 'p9': '96C'},
-        '08': {'p1': '26', 'p3': '0915094D0937', 'p7': '37', 'p9': '96D'},
-        '09': {'p1': '2A', 'p3': '0936094D0930', 'p7': '38', 'p9': '96E'},
-        '0A': {'p1': '28', 'p3': '28', 'p7': '39', 'p9': '96F'},
-        '0B': {'p1': '29', 'p3': '29', 'p7': '30', 'p9': '966'},
-        '0C': {'p1': '5F', 'p3': '903', 'p7': '2D'},
-        '0D': {'p1': '2B', 'p3': '090B', 'p7': '3D', 'p9': '943'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p3': '914', 'p7': '71', 'p9': '094C'},
-        '11': {'p3': '910', 'p7': '77', 'p9': '948'},
-        '12': {'p3': '906', 'p7': '65', 'p9': '093E'},
-        '13': {'p3': '908', 'p7': '72', 'p9': '940'},
-        '14': {'p3': '090A', 'p7': '74', 'p9': '942'},
-        '15': {'p3': '092D', 'p7': '79', 'p9': '092C'},
-        '16': {'p3': '919', 'p7': '75', 'p9': '939'},
-        '17': {'p3': '918', 'p7': '69', 'p9': '917'},
-        '18': {'p3': '927', 'p7': '6F', 'p9': '926'},
-        '19': {'p3': '091D', 'p7': '70', 'p9': '091C'},
-        '1A': {'p1': '7B', 'p3': '922', 'p7': '5B', 'p9': '921'},
-        '1B': {'p1': '7D', 'p3': '091E', 'p7': '5D', 'p9': '093C'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p3': '913', 'p7': '61', 'p9': '094B'},
-        '1F': {'p3': '090F', 'p7': '73', 'p9': '947'},
-        '20': {'p3': '905', 'p7': '64', 'p9': '094D'},
-        '21': {'p3': '907', 'p7': '66', 'p9': '093F'},
-        '22': {'p3': '909', 'p7': '67', 'p9': '941'},
-        '23': {'p3': '092B', 'p7': '68', 'p9': '092A'},
-        '24': {'p3': '931', 'p7': '6A', 'p9': '930'},
-        '25': {'p3': '916', 'p7': '6B', 'p9': '915'},
-        '26': {'p3': '925', 'p7': '6C', 'p9': '924'},
-        '27': {'p1': '3A', 'p3': '091B', 'p7': '3B', 'p9': '091A'},
-        '28': {'p1': '22', 'p3': '920', 'p7': '27', 'p9': '091F'},
-        '29': {'p1': '7E', 'p3': '912', 'p7': '60', 'p9': '094A'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p1': '7C', 'p3': '911', 'p7': '5C', 'p9': '949'},
-        '2C': {'p3': '090E', 'p7': '7A', 'p9': '946'},
-        '2D': {'p3': '901', 'p7': '78', 'p9': '902'},
-        '2E': {'p3': '923', 'p7': '63', 'p9': '092E'},
-        '2F': {'p3': '929', 'p7': '76', 'p9': '928'},
-        '30': {'p3': '934', 'p7': '62', 'p9': '935'},
-        '31': {'p3': '933', 'p7': '6E', 'p9': '932'},
-        '32': {'p3': '936', 'p7': '6D', 'p9': '938'},
-        '33': {'p1': '3C', 'p3': '937', 'p7': '2C', 'p9': '002C'},
-        '34': {'p1': '3E', 'p3': '964', 'p7': '2E', 'p9': '002E'},
-        '35': {'p1': '3F', 'p3': '095F', 'p7': '2F', 'p9': '092F'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '37': {},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    },
-    'hr': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31', 'p9': '007E'},
-        '03': {'p2': '22', 'p8': '32', 'p9': '02C7'},
-        '04': {'p2': '23', 'p8': '33', 'p9': '005E'},
-        '05': {'p2': '24', 'p8': '34', 'p9': '02D8'},
-        '06': {'p2': '25', 'p8': '35', 'p9': '02DA'},
-        '07': {'p2': '26', 'p8': '36', 'p9': '02DB'},
-        '08': {'p2': '002F', 'p8': '37', 'p9': '60'},
-        '09': {'p2': '28', 'p8': '38', 'p9': '02D9'},
-        '0A': {'p2': '29', 'p8': '39', 'p9': '00B4'},
-        '0B': {'p2': '003D', 'p8': '30', 'p9': '02DD'},
-        '0C': {'p2': '003F', 'p8': '27', 'p9': '00A8'},
-        '0D': {'p2': '002A', 'p8': '002B', 'p9': '00B8'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71', 'p9': '005C'},
-        '11': {'p5': '77', 'p9': '007C'},
-        '12': {'p5': '65', 'p9': '20AC'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '007A'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '006F'},
-        '19': {'p5': '70'},
-        '1A': {'p5': '161', 'p9': '00F7'},
-        '1B': {'p5': '111', 'p9': '00D7'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66', 'p9': '005B'},
-        '22': {'p5': '67', 'p9': '005D'},
-        '23': {'p5': '68'},
-        '24': {'p5': '006A'},
-        '25': {'p5': '006B', 'p9': '142'},
-        '26': {'p5': '006C', 'p9': '141'},
-        '27': {'p5': '10D'},
-        '28': {'p5': '107', 'p9': '00DF'},
-        '29': {'p2': '00A8', 'p8': '00B8'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p5': '17E', 'p9': '00A4'},
-        '2C': {'p5': '79'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76', 'p9': '40'},
-        '30': {'p5': '62', 'p9': '007B'},
-        '31': {'p5': '006E', 'p9': '007D'},
-        '32': {'p5': '006D', 'p9': '00A7'},
-        '33': {'p2': '003B', 'p8': '002C'},
-        '34': {'p2': '003A', 'p8': '002E'},
-        '35': {'p2': '005F', 'p8': '002D'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p2': '003E', 'p8': '003C'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'hu': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '27', 'p8': '31', 'p9': '7e'},
-        '03': {'p2': '22', 'p8': '32', 'p9': '2c7'},
-        '04': {'p2': '2b', 'p8': '33', 'p9': '5e'},
-        '05': {'p2': '21', 'p8': '34', 'p9': '2d8'},
-        '06': {'p2': '25', 'p8': '35', 'p9': '2da'},
-        '07': {'p2': '2f', 'p8': '36', 'p9': '2db'},
-        '08': {'p2': '3d', 'p8': '37', 'p9': '60'},
-        '09': {'p2': '28', 'p8': '38', 'p9': '2d9'},
-        '0A': {'p2': '29', 'p8': '39', 'p9': 'b4'},
-        '0B': {'p5': 'f6', 'p9': '2dd'},
-        '0C': {'p5': 'fc', 'p9': 'a8'},
-        '0D': {'p5': 'f3', 'p9': 'b8'},
-        '0E': {'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71', 'p9': '5c'},
-        '11': {'p5': '77', 'p9': '7c'},
-        '12': {'p5': '65'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '7a'},
-        '16': {'p5': '75', 'p9': '20ac'},
-        '17': {'p5': '69'},
-        '18': {'p5': '6f'},
-        '19': {'p5': '70'},
-        '1A': {'p5': '151', 'p9': 'f7'},
-        '1B': {'p5': 'fa', 'p9': 'd7'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73', 'p9': '111'},
-        '20': {'p5': '64', 'p9': '110'},
-        '21': {'p5': '66', 'p9': '5b'},
-        '22': {'p5': '67', 'p9': '5d'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6a'},
-        '25': {'p5': '6b', 'p9': '142'},
-        '26': {'p5': '6c', 'p9': '141'},
-        '27': {'p5': 'e9', 'p9': '24'},
-        '28': {'p5': 'e1', 'p9': 'df'},
-        '29': {'p2': 'a7', 'p8': '30'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p5': '171', 'p9': 'a4'},
-        '2C': {'p5': '79', 'p9': '3e'},
-        '2D': {'p5': '78', 'p9': '23'},
-        '2E': {'p5': '63', 'p9': '26'},
-        '2F': {'p5': '76', 'p9': '40'},
-        '30': {'p5': '62', 'p9': '7b'},
-        '31': {'p5': '6e', 'p9': '7d'},
-        '32': {'p5': '6d'},
-        '33': {'p2': '3f', 'p8': '2c', 'p9': '3b'},
-        '34': {'p2': '3a', 'p8': '2e'},
-        '35': {'p2': '5f', 'p8': '2d', 'p9': '2a'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p5': 'ed', 'p9': '3c'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller'},
-        'E0 38': {'format': 'smaller'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'it': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31'},
-        '03': {'p2': '22', 'p8': '32'},
-        '04': {'p2': 'A3', 'p8': '33'},
-        '05': {'p2': '24', 'p8': '34'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '26', 'p8': '36'},
-        '08': {'p2': '2F', 'p8': '37'},
-        '09': {'p2': '28', 'p8': '38'},
-        '0A': {'p2': '29', 'p8': '39'},
-        '0B': {'p2': '3D', 'p8': '30'},
-        '0C': {'p2': '3F', 'p8': '27'},
-        '0D': {'p2': '5E', 'p8': 'EC'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65', 'p9': '20AC'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '006F'},
-        '19': {'p5': '70'},
-        '1A': {'p2': 'E9', 'p8': 'E8', 'p9': '5B'},
-        '1B': {'p2': '2A', 'p8': '2B', 'p9': '5D'},
-        '1C': {'format': 'right', 'label': 'invio'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6A'},
-        '25': {'p5': '6B'},
-        '26': {'p5': '6C'},
-        '27': {'p2': 'E7', 'p8': 'F2', 'p9': '40'},
-        '28': {'p2': 'B0', 'p8': 'E0', 'p9': '23'},
-        '29': {'p2': '7C', 'p8': '5C'},
-        '2A': {'format': 'left', 'label': 'maiusc'},
-        '2B': {'p2': 'A7', 'p8': 'F9', 'p9': '60'},
-        '2C': {'p5': '7A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '6E'},
-        '32': {'p5': '6D'},
-        '33': {'p2': '3B', 'p8': '2C'},
-        '34': {'p2': '3A', 'p8': '2E'},
-        '35': {'p2': '5F', 'p8': '2D'},
-        '36': {'format': 'right', 'label': 'maiusc'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p2': '3E', 'p8': '3C'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'iw': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p1': '21', 'p7': '31'},
-        '03': {'p1': '40', 'p7': '32'},
-        '04': {'p1': '23', 'p7': '33'},
-        '05': {'p1': '24', 'p3': '20aa', 'p7': '34'},
-        '06': {'p1': '25', 'p7': '35'},
-        '07': {'p1': '5E', 'p7': '36'},
-        '08': {'p1': '26', 'p7': '37'},
-        '09': {'p1': '2A', 'p7': '38'},
-        '0A': {'p1': '28', 'p7': '39'},
-        '0B': {'p1': '29', 'p7': '30'},
-        '0C': {'p1': '5F', 'p7': '2D'},
-        '0D': {'p1': '2B', 'p7': '3D'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71', 'p9': '2F'},
-        '11': {'p5': '77', 'p9': '27'},
-        '12': {'p3': '20ac', 'p5': '65', 'p9': '5e7'},
-        '13': {'p5': '72', 'p9': '5e8'},
-        '14': {'p5': '74', 'p9': '5D0'},
-        '15': {'p5': '79', 'p9': '5D8'},
-        '16': {'p5': '75', 'p9': '5D5'},
-        '17': {'p5': '69', 'p9': '5Df'},
-        '18': {'p5': '6F', 'p9': '5dd'},
-        '19': {'p5': '70', 'p9': '5e4'},
-        '1A': {'p1': '7b', 'p7': '5B'},
-        '1B': {'p1': '7D', 'p7': '5D'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61', 'p9': '5e9'},
-        '1F': {'p5': '73', 'p9': '5d3'},
-        '20': {'p5': '64', 'p9': '5d2'},
-        '21': {'p5': '66', 'p9': '5db'},
-        '22': {'p5': '67', 'p9': '5e2'},
-        '23': {'p5': '68', 'p9': '5d9'},
-        '24': {'p5': '6A', 'p9': '5d7'},
-        '25': {'p5': '6B', 'p9': '5dc'},
-        '26': {'p5': '6C', 'p9': '5da'},
-        '27': {'p1': '3a', 'p7': '3b', 'p9': '5e3'},
-        '28': {'p1': '22', 'p7': 'b4'},
-        '29': {'p1': '7E', 'p7': '60', 'p9': '3b'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p1': 'A6', 'p7': '5C'},
-        '2C': {'p5': '7A', 'p9': '5d6'},
-        '2D': {'p5': '78', 'p9': '5e1'},
-        '2E': {'p5': '63', 'p9': '5d1'},
-        '2F': {'p5': '76', 'p9': '5d4'},
-        '30': {'p5': '62', 'p9': '5e0'},
-        '31': {'p5': '6E', 'p9': '5de'},
-        '32': {'p5': '6D', 'p9': '5e6'},
-        '33': {'p1': '3c', 'p7': 'b8', 'p9': '5ea'},
-        '34': {'p1': '3e', 'p7': '2e', 'p9': '5e5'},
-        '35': {'p1': '3f', 'p7': '2f', 'p9': '2e'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    },
-    'ja': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p1': '21', 'p7': '31', 'p9': '306C'},
-        '03': {'p1': '22', 'p7': '32', 'p9': '3075'},
-        '04': {'p1': '23', 'p3': '3041', 'p7': '33', 'p9': '3042'},
-        '05': {'p1': '24', 'p3': '3045', 'p7': '34', 'p9': '3046'},
-        '06': {'p1': '25', 'p3': '3047', 'p7': '35', 'p9': '3048'},
-        '07': {'p1': '26', 'p3': '3049', 'p7': '36', 'p9': '304A'},
-        '08': {'p1': '27', 'p3': '3083', 'p7': '37', 'p9': '3084'},
-        '09': {'p1': '28', 'p3': '3085', 'p7': '38', 'p9': '3086'},
-        '0A': {'p1': '29', 'p3': '3087', 'p7': '39', 'p9': '3088'},
-        '0B': {'p3': '3092', 'p7': '30', 'p9': '308F'},
-        '0C': {'p1': '3D', 'p7': '2D', 'p9': '307B'},
-        '0D': {'p1': '7E', 'p7': '5E', 'p9': '3078'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p1': '71', 'p9': '305F'},
-        '11': {'p1': '77', 'p9': '3066'},
-        '12': {'p1': '65', 'p3': '3043', 'p9': '3044'},
-        '13': {'p1': '72', 'p9': '3059'},
-        '14': {'p1': '74', 'p9': '304B'},
-        '15': {'p1': '79', 'p9': '3093'},
-        '16': {'p1': '75', 'p9': '306A'},
-        '17': {'p1': '69', 'p9': '306B'},
-        '18': {'p1': '6F', 'p9': '3089'},
-        '19': {'p1': '70', 'p9': '305B'},
-        '1A': {'p1': '60', 'p7': '40', 'p9': '3099'},
-        '1B': {'p1': '7B', 'p3': '300C', 'p7': '5B', 'p9': '309A'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p1': '61', 'p9': '3061'},
-        '1F': {'p1': '73', 'p9': '3068'},
-        '20': {'p1': '64', 'p9': '3057'},
-        '21': {'p1': '66', 'p9': '306F'},
-        '22': {'p1': '67', 'p9': '304D'},
-        '23': {'p1': '68', 'p9': '304F'},
-        '24': {'p1': '6A', 'p9': '307E'},
-        '25': {'p1': '6B', 'p9': '306E'},
-        '26': {'p1': '6C', 'p9': '308A'},
-        '27': {'p1': '2B', 'p7': '3B', 'p9': '308C'},
-        '28': {'p1': '2A', 'p7': '3A', 'p9': '3051'},
-        '29': {'label': '\u304b\u306a / \u82f1\u6570'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p1': '7D', 'p3': '300D', 'p7': '5D', 'p9': '3080'},
-        '2C': {'p1': '7A', 'p9': '3064'},
-        '2D': {'p1': '78', 'p9': '3055'},
-        '2E': {'p1': '63', 'p9': '305D'},
-        '2F': {'p1': '76', 'p9': '3072'},
-        '30': {'p1': '62', 'p9': '3053'},
-        '31': {'p1': '6E', 'p9': '307F'},
-        '32': {'p1': '6D', 'p9': '3082'},
-        '33': {'p1': '3C', 'p3': 'FF64', 'p7': '2C', 'p9': '306D'},
-        '34': {'p1': '3E', 'p3': 'FF61', 'p7': '2E', 'p9': '308B'},
-        '35': {'p1': '3F', 'p3': 'FF65', 'p7': '2F', 'p9': '3081'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '73': {'p1': '5F', 'p7': '5C', 'p9': '308D'},
-        '79': {'format': 'smaller', 'label': '\u304b\u306a'},
-        '7B': {'format': 'smaller', 'label': '\u82f1\u6570'},
-        '7D': {'p1': '7C', 'p7': 'A5', 'p9': '30FC'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'J'
-    },
-    'ko': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31'},
-        '03': {'p2': '40', 'p8': '32'},
-        '04': {'p2': '23', 'p8': '33'},
-        '05': {'p2': '24', 'p8': '34'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '005E', 'p8': '36'},
-        '08': {'p2': '26', 'p8': '37'},
-        '09': {'p2': '002A', 'p8': '38'},
-        '0A': {'p2': '28', 'p8': '39'},
-        '0B': {'p2': '29', 'p8': '30'},
-        '0C': {'p2': '005F', 'p8': '002D'},
-        '0D': {'p2': '002B', 'p8': '003D'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p1': '71', 'p3': '3143', 'p9': '3142'},
-        '11': {'p1': '77', 'p3': '3149', 'p9': '3148'},
-        '12': {'p1': '65', 'p3': '3138', 'p9': '3137'},
-        '13': {'p1': '72', 'p3': '3132', 'p9': '3131'},
-        '14': {'p1': '74', 'p3': '3146', 'p9': '3145'},
-        '15': {'p1': '79', 'p9': '315B'},
-        '16': {'p1': '75', 'p9': '3155'},
-        '17': {'p1': '69', 'p9': '3151'},
-        '18': {'p1': '6F', 'p3': '3152', 'p9': '3150'},
-        '19': {'p1': '70', 'p3': '3156', 'p9': '3154'},
-        '1A': {'p1': '007B', 'p7': '005B'},
-        '1B': {'p1': '007D', 'p7': '005D'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p1': '61', 'p9': '3141'},
-        '1F': {'p1': '73', 'p9': '3134'},
-        '20': {'p1': '64', 'p9': '3147'},
-        '21': {'p1': '66', 'p9': '3139'},
-        '22': {'p1': '67', 'p9': '314E'},
-        '23': {'p1': '68', 'p9': '3157'},
-        '24': {'p1': '6A', 'p9': '3153'},
-        '25': {'p1': '6B', 'p9': '314F'},
-        '26': {'p1': '6C', 'p9': '3163'},
-        '27': {'p1': '003A', 'p7': '003B'},
-        '28': {'p1': '22', 'p7': '27'},
-        '29': {'p2': '007E', 'p8': '60'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p1': '007C', 'p7': '005C', 'p9': '20A9'},
-        '2C': {'p1': '7A', 'p9': '314B'},
-        '2D': {'p1': '78', 'p9': '314C'},
-        '2E': {'p1': '63', 'p9': '314A'},
-        '2F': {'p1': '76', 'p9': '314D'},
-        '30': {'p1': '62', 'p9': '3160'},
-        '31': {'p1': '6E', 'p9': '315C'},
-        '32': {'p1': '6D', 'p9': '3161'},
-        '33': {'p1': '003C', 'p7': '002C'},
-        '34': {'p1': '003E', 'p7': '002E'},
-        '35': {'p1': '003F', 'p7': '002F'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '37': {},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '73': {},
-        '79': {'format': 'smaller', 'label': '\ud55c/\uc601'},
-        '7B': {'format': 'smaller', 'label': '\ud55c\uc790'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': ' \ud55c\uc790'},
-        'E0 38': {'format': 'smaller', 'label': '\ud55c/\uc601'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    },
-    'lt': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31', 'p9': '105'},
-        '03': {'p2': '40', 'p8': '32', 'p9': '10D'},
-        '04': {'p2': '23', 'p8': '33', 'p9': '119'},
-        '05': {'p2': '24', 'p8': '34', 'p9': '117'},
-        '06': {'p2': '25', 'p8': '35', 'p9': '12F'},
-        '07': {'p2': '005E', 'p8': '36', 'p9': '161'},
-        '08': {'p2': '26', 'p8': '37', 'p9': '173'},
-        '09': {'p2': '002A', 'p8': '38', 'p9': '016B'},
-        '0A': {'p2': '28', 'p8': '39'},
-        '0B': {'p2': '29', 'p8': '30'},
-        '0C': {'p2': '005F', 'p8': '002D'},
-        '0D': {'p2': '002B', 'p8': '003D', 'p9': '017E'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '006F'},
-        '19': {'p5': '70'},
-        '1A': {'p2': '007B', 'p8': '005B'},
-        '1B': {'p2': '007D', 'p8': '005D'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '006A'},
-        '25': {'p5': '006B'},
-        '26': {'p5': '006C'},
-        '27': {'p2': '003A', 'p8': '003B'},
-        '28': {'p2': '22', 'p8': '27'},
-        '29': {'p2': '007E', 'p8': '60', 'p9': '00B4'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '005C', 'p8': '007C'},
-        '2C': {'p5': '007A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '006E'},
-        '32': {'p5': '006D'},
-        '33': {'p2': '003C', 'p8': '002C', 'p9': '201E'},
-        '34': {'p2': '003E', 'p8': '002E', 'p9': '201C'},
-        '35': {'p2': '003F', 'p8': '002F'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '37': {},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '73': {},
-        '79': {},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    },
-    'lv': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31'},
-        '03': {'p2': '40', 'p8': '32'},
-        '04': {'p2': '23', 'p8': '33'},
-        '05': {'p2': '24', 'p8': '34'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '005E', 'p8': '36'},
-        '08': {'p2': '26', 'p8': '37'},
-        '09': {'p2': '002A', 'p8': '38'},
-        '0A': {'p2': '28', 'p8': '39'},
-        '0B': {'p2': '29', 'p8': '30'},
-        '0C': {'p2': '005F', 'p8': '002D'},
-        '0D': {'p2': '002B', 'p8': '003D'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '006F'},
-        '19': {'p5': '70'},
-        '1A': {'p2': '007B', 'p8': '005B'},
-        '1B': {'p2': '007D', 'p8': '005D'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6A'},
-        '25': {'p5': '6B'},
-        '26': {'p5': '6C'},
-        '27': {'p2': '003A', 'p8': '003B'},
-        '28': {'p2': '22', 'p8': '27'},
-        '29': {'p2': '007E', 'p8': '60'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '7C', 'p8': '005C'},
-        '2C': {'p5': '7A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '6E'},
-        '32': {'p5': '6D'},
-        '33': {'p2': '003C', 'p8': '002C'},
-        '34': {'p2': '003E', 'p8': '002E'},
-        '35': {'p2': '003F', 'p8': '002F'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p2': '007C', 'p8': '005C'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    },
-    'nl': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31'},
-        '03': {'p2': '40', 'p8': '32'},
-        '04': {'p2': '23', 'p8': '33'},
-        '05': {'p2': '24', 'p8': '34'},
-        '06': {'p2': '25', 'p8': '35', 'p9': '20AC'},
-        '07': {'p2': '005E', 'p8': '36'},
-        '08': {'p2': '26', 'p8': '37'},
-        '09': {'p2': '002A', 'p8': '38'},
-        '0A': {'p2': '28', 'p8': '39'},
-        '0B': {'p2': '29', 'p8': '30'},
-        '0C': {'p2': '005F', 'p8': '002D'},
-        '0D': {'p2': '002B', 'p8': '003D'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '6F'},
-        '19': {'p5': '70'},
-        '1A': {'p2': '007B', 'p8': '005B'},
-        '1B': {'p2': '007D', 'p8': '005D'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6A'},
-        '25': {'p5': '6B'},
-        '26': {'p5': '6C'},
-        '27': {'p2': '003A', 'p8': '003B'},
-        '28': {'p2': '22', 'p8': '27'},
-        '29': {'p2': '007E', 'p8': '60'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '007C', 'p8': '005C'},
-        '2C': {'p5': '7A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '6E'},
-        '32': {'p5': '6D'},
-        '33': {'p2': '003C', 'p8': '002C'},
-        '34': {'p2': '003E', 'p8': '002E'},
-        '35': {'p2': '003F', 'p8': '002F'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    },
-    'no': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p1': '21', 'p7': '31'},
-        '03': {'p1': '22', 'p7': '32', 'p9': '40'},
-        '04': {'p1': '23', 'p7': '33', 'p9': '00A3'},
-        '05': {'p1': '00A4', 'p7': '34', 'p9': '24'},
-        '06': {'p1': '25', 'p7': '35'},
-        '07': {'p1': '26', 'p7': '36'},
-        '08': {'p1': '002F', 'p7': '37', 'p9': '007B'},
-        '09': {'p1': '28', 'p7': '38', 'p9': '005B'},
-        '0A': {'p1': '29', 'p7': '39', 'p9': '005D'},
-        '0B': {'p1': '003D', 'p7': '30', 'p9': '007D'},
-        '0C': {'p1': '003F', 'p7': '002B'},
-        '0D': {'p1': '60', 'p7': '005C', 'p9': '00B4'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65', 'p9': '20AC'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '006F'},
-        '19': {'p5': '70'},
-        '1A': {'p5': 'E5'},
-        '1B': {'p1': '5e', 'p7': '00A8', 'p9': '7e'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '006A'},
-        '25': {'p5': '006B'},
-        '26': {'p5': '006C'},
-        '27': {'p5': '00F8'},
-        '28': {'p5': 'E6'},
-        '29': {'p1': '00A7', 'p7': '007C'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '2a', 'p8': '27'},
-        '2C': {'p5': '007A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '006E'},
-        '32': {'p5': '006D', 'p9': '3bc'},
-        '33': {'p2': '003B', 'p8': '002C'},
-        '34': {'p2': '003A', 'p8': '002E'},
-        '35': {'p2': '005F', 'p8': '002D'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p2': '3e', 'p8': '003C'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'pl': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31'},
-        '03': {'p2': '40', 'p8': '32'},
-        '04': {'p2': '23', 'p8': '33'},
-        '05': {'p2': '24', 'p8': '34'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '5E', 'p8': '36'},
-        '08': {'p2': '26', 'p8': '37'},
-        '09': {'p2': '2A', 'p8': '38'},
-        '0A': {'p2': '28', 'p8': '39'},
-        '0B': {'p2': '29', 'p8': '30'},
-        '0C': {'p2': '5F', 'p8': '2D'},
-        '0D': {'p2': '2B', 'p8': '3D'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'label': 'q', 'p1': '51'},
-        '11': {'label': 'w', 'p1': '57'},
-        '12': {'label': 'e', 'p1': '45'},
-        '13': {'label': 'r', 'p1': '52'},
-        '14': {'label': 't', 'p1': '54'},
-        '15': {'label': 'y', 'p1': '59'},
-        '16': {'label': 'u', 'p1': '55'},
-        '17': {'label': 'i', 'p1': '49'},
-        '18': {'label': 'o', 'p1': '4F'},
-        '19': {'label': 'p', 'p1': '50'},
-        '1A': {'p2': '7B', 'p8': '5B'},
-        '1B': {'p2': '7D', 'p8': '5D'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'label': 'a', 'p1': '41'},
-        '1F': {'label': 's', 'p1': '53'},
-        '20': {'label': 'd', 'p1': '44'},
-        '21': {'label': 'f', 'p1': '46'},
-        '22': {'label': 'g', 'p1': '47'},
-        '23': {'label': 'h', 'p1': '48'},
-        '24': {'label': 'j', 'p1': '4A'},
-        '25': {'label': 'k', 'p1': '4B'},
-        '26': {'label': 'l', 'p1': '4C'},
-        '27': {'p2': '3A', 'p8': '3B'},
-        '28': {'p2': '22', 'p8': '27'},
-        '29': {'p2': '7E', 'p8': '60'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '7C', 'p8': '5C'},
-        '2C': {'label': 'z', 'p1': '5A'},
-        '2D': {'label': 'x', 'p1': '58'},
-        '2E': {'label': 'c', 'p1': '43'},
-        '2F': {'label': 'v', 'p1': '56'},
-        '30': {'label': 'b', 'p1': '42'},
-        '31': {'label': 'n', 'p1': '4E'},
-        '32': {'label': 'm', 'p1': '4D'},
-        '33': {'p2': '3C', 'p8': '2C'},
-        '34': {'p2': '3E', 'p8': '2E'},
-        '35': {'p2': '3F', 'p8': '2F'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    },
-    'pt_BR': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p1': '21', 'p7': '31', 'p9': 'B9'},
-        '03': {'p1': '40', 'p7': '32', 'p9': 'B2'},
-        '04': {'p1': '23', 'p7': '33', 'p9': 'B3'},
-        '05': {'p1': '24', 'p7': '34', 'p9': 'A3'},
-        '06': {'p1': '25', 'p7': '35', 'p9': 'A2'},
-        '07': {'p1': 'A8', 'p7': '36', 'p9': '00AC'},
-        '08': {'p1': '26', 'p7': '37'},
-        '09': {'p1': '2A', 'p7': '38'},
-        '0A': {'p1': '28', 'p7': '39'},
-        '0B': {'p1': '29', 'p7': '30'},
-        '0C': {'p1': '5F', 'p7': '2D'},
-        '0D': {'p1': '2B', 'p7': '3D', 'p9': 'A7'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71', 'p9': '2F'},
-        '11': {'p5': '77', 'p9': '3F'},
-        '12': {'p5': '65', 'p9': '20AC'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '6F'},
-        '19': {'p5': '70'},
-        '1A': {'p1': '60', 'p7': 'B4'},
-        '1B': {'p1': '7B', 'p7': '5B', 'p9': 'AA0332'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6A'},
-        '25': {'p5': '6B'},
-        '26': {'p5': '6C'},
-        '27': {'p5': 'e7'},
-        '28': {'p1': '5E', 'p7': '7E'},
-        '29': {'p1': '22', 'p7': '27'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p1': '7D', 'p7': '5D', 'p9': 'BA0332'},
-        '2C': {'p5': '7A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63', 'p9': '20A2'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '6E'},
-        '32': {'p5': '6D'},
-        '33': {'p1': '3C', 'p7': '2C'},
-        '34': {'p1': '3E', 'p7': '2E'},
-        '35': {'p1': '3A', 'p7': '3B'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p1': '7C', 'p7': '5C'},
-        '73': {'p1': '3F', 'p7': '2f'},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'B'
-    },
-    'pt_PT': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31'},
-        '03': {'p2': '22', 'p8': '32', 'p9': '40'},
-        '04': {'p2': '23', 'p8': '33', 'p9': 'a3'},
-        '05': {'p2': '24', 'p8': '34', 'p9': 'a7'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '26', 'p8': '36'},
-        '08': {'p2': '2f', 'p8': '37', 'p9': '7b'},
-        '09': {'p2': '28', 'p8': '38', 'p9': '5b'},
-        '0A': {'p2': '29', 'p8': '39', 'p9': '5d'},
-        '0B': {'p2': '3d', 'p8': '30', 'p9': '7d'},
-        '0C': {'p2': '3f', 'p8': '27'},
-        '0D': {'p2': 'bb', 'p8': 'ab'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65', 'p9': '20ac'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '6F'},
-        '19': {'p5': '70'},
-        '1A': {'p2': '2a', 'p8': '2b', 'p9': 'a8'},
-        '1B': {'p2': '60', 'p8': 'b4'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6A'},
-        '25': {'p5': '6B'},
-        '26': {'p5': '6C'},
-        '27': {'p5': 'e7'},
-        '28': {'p2': 'aa0332', 'p8': 'ba0332'},
-        '29': {'p2': '7c', 'p8': '5c'},
-        '2A': {'format': 'glyph_shift', 'label': 'shift'},
-        '2B': {'p2': '5e', 'p8': '7e'},
-        '2C': {'p5': '7A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '6E'},
-        '32': {'p5': '6D'},
-        '33': {'p2': '3b', 'p8': '2c'},
-        '34': {'p2': '3a', 'p8': '2e'},
-        '35': {'p2': '5f', 'p8': '2d'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p2': '3e', 'p8': '3c'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'ro': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p1': '21', 'p7': '31', 'p9': '7E'},
-        '03': {'p1': '22', 'p7': '32', 'p9': '2C7'},
-        '04': {'p1': '23', 'p7': '33', 'p9': '5E'},
-        '05': {'p1': '24', 'p7': '34', 'p9': '2D8'},
-        '06': {'p1': '25', 'p7': '35', 'p9': '2DA'},
-        '07': {'p1': '26', 'p7': '36', 'p9': '2DB'},
-        '08': {'p1': '2F', 'p7': '37', 'p9': '60'},
-        '09': {'p1': '28', 'p7': '38', 'p9': '2D9'},
-        '0A': {'p1': '29', 'p7': '39', 'p9': 'B4'},
-        '0B': {'p1': '3D', 'p7': '30', 'p9': '2DD'},
-        '0C': {'p1': '3F', 'p7': '2B', 'p9': 'A8'},
-        '0D': {'p1': '2A', 'p7': '27', 'p9': 'B8'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71', 'p9': '5c'},
-        '11': {'p5': '77', 'p9': 'A6'},
-        '12': {'p5': '65', 'p9': '20AC'},
-        '13': {'p5': '72', 'p9': 'AE'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '6F'},
-        '19': {'p5': '70'},
-        '1A': {'p5': '103', 'p9': '00F7'},
-        '1B': {'p5': '00EE', 'p9': '00D7'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61', 'p9': '201E'},
-        '1F': {'p5': '73', 'p9': '201D'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6A'},
-        '25': {'p5': '6B'},
-        '26': {'p5': '6C', 'p9': 'A3'},
-        '27': {'p5': '219', 'p9': '40'},
-        '28': {'p5': '021b', 'p9': 'DF'},
-        '29': {'p1': '5B', 'p7': '5D'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p5': 'E2'},
-        '2C': {'p5': '7a'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63', 'p9': '00A9'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62', 'p9': '7b'},
-        '31': {'p5': '6E', 'p9': '7d'},
-        '32': {'p5': '6D', 'p9': 'a7'},
-        '33': {'p1': '3B', 'p7': '2C'},
-        '34': {'p1': '3A', 'p7': '2E'},
-        '35': {'p1': '5F', 'p7': '2D'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p1': '3C', 'p7': '3E'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {'p5': 'E2'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'ru': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p1': '21', 'p7': '31'},
-        '03': {'p1': '40', 'p3': '22', 'p7': '32'},
-        '04': {'p1': '23', 'p3': '2116', 'p7': '33'},
-        '05': {'p1': '24', 'p3': '3b', 'p7': '34'},
-        '06': {'p1': '25', 'p7': '35'},
-        '07': {'p1': '5e', 'p3': '3a', 'p7': '36'},
-        '08': {'p1': '26', 'p3': '3f', 'p7': '37'},
-        '09': {'p1': '2a', 'p7': '38'},
-        '0A': {'p1': '28', 'p7': '39'},
-        '0B': {'p1': '29', 'p7': '30'},
-        '0C': {'p1': '5f', 'p7': '2d'},
-        '0D': {'p1': '2b', 'p7': '3d'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p1': '71', 'p9': '439'},
-        '11': {'p1': '77', 'p9': '446'},
-        '12': {'p1': '65', 'p9': '443'},
-        '13': {'p1': '72', 'p9': '43a'},
-        '14': {'p1': '74', 'p9': '435'},
-        '15': {'p1': '79', 'p9': '43d'},
-        '16': {'p1': '75', 'p9': '433'},
-        '17': {'p1': '69', 'p9': '448'},
-        '18': {'p1': '6F', 'p9': '449'},
-        '19': {'p1': '70', 'p9': '437'},
-        '1A': {'p1': '7b', 'p7': '5b', 'p9': '445'},
-        '1B': {'p1': '7d', 'p7': '5d', 'p9': '44a'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p1': '61', 'p9': '444'},
-        '1F': {'p1': '73', 'p9': '44b'},
-        '20': {'p1': '64', 'p9': '432'},
-        '21': {'p1': '66', 'p9': '430'},
-        '22': {'p1': '67', 'p9': '43f'},
-        '23': {'p1': '68', 'p9': '440'},
-        '24': {'p1': '6A', 'p9': '43e'},
-        '25': {'p1': '6B', 'p9': '43b'},
-        '26': {'p1': '6C', 'p9': '434'},
-        '27': {'p1': '3a', 'p7': '3b', 'p9': '436'},
-        '28': {'p1': '22', 'p7': '27', 'p9': '44d'},
-        '29': {'p1': '7e', 'p7': '60', 'p9': '451'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p1': '7c', 'p3': '2f', 'p7': '5c', 'p9': '5c'},
-        '2C': {'p1': '7A', 'p9': '44f'},
-        '2D': {'p1': '78', 'p9': '447'},
-        '2E': {'p1': '63', 'p9': '441'},
-        '2F': {'p1': '76', 'p9': '43c'},
-        '30': {'p1': '62', 'p9': '438'},
-        '31': {'p1': '6E', 'p9': '442'},
-        '32': {'p1': '6D', 'p9': '44c'},
-        '33': {'p1': '3c', 'p7': '2c', 'p9': '431'},
-        '34': {'p1': '3e', 'p7': '2e', 'p9': '44e'},
-        '35': {'p1': '3f', 'p3': '2c', 'p7': '2f', 'p9': '2e'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    },
-    'sk': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p1': '21', 'p3': '31', 'p7': '31', 'p9': '002b'},
-        '03': {'p1': '40', 'p3': '32', 'p7': '32', 'p9': '013E'},
-        '04': {'p1': '23', 'p3': '33', 'p7': '33', 'p9': '161'},
-        '05': {'p1': '24', 'p3': '34', 'p7': '34', 'p9': '010d'},
-        '06': {'p1': '25', 'p3': '35', 'p7': '35', 'p9': '165'},
-        '07': {'p1': '5e', 'p3': '36', 'p7': '36', 'p9': '017e'},
-        '08': {'p1': '26', 'p3': '37', 'p7': '37', 'p9': 'fd'},
-        '09': {'p1': '2a', 'p3': '38', 'p7': '38', 'p9': 'e1'},
-        '0A': {'p1': '28', 'p3': '39', 'p7': '39', 'p9': 'ed'},
-        '0B': {'p1': '29', 'p3': '30', 'p7': '30', 'p9': 'e9'},
-        '0C': {'p1': '5f', 'p3': '25', 'p7': '2d', 'p9': '3d'},
-        '0D': {'p1': '2b', 'p3': '2c7', 'p7': '3d', 'p9': 'b4'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65', 'p9': '20ac'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '6f'},
-        '19': {'p5': '70'},
-        '1A': {'p1': '7b', 'p3': 'b4', 'p7': '5b', 'p9': 'fa'},
-        '1B': {'p1': '7d', 'p3': '28', 'p7': '5d', 'p9': 'e4'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6a'},
-        '25': {'p5': '6b'},
-        '26': {'p5': '6c'},
-        '27': {'p1': '3a', 'p3': '22', 'p7': '3b', 'p9': 'f4'},
-        '28': {'p1': '22', 'p3': '21', 'p7': '27', 'p9': 'a7'},
-        '29': {'p1': '7e', 'p3': 'b0', 'p7': '60', 'p9': '3b'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p1': '7c', 'p3': '29', 'p7': '5c', 'p9': '148'},
-        '2C': {'p5': '7a'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '6e'},
-        '32': {'p5': '6d'},
-        '33': {'p1': '3c', 'p3': '3f', 'p7': '2c', 'p9': '2c'},
-        '34': {'p1': '3e', 'p3': '3a', 'p7': '2e', 'p9': '2e'},
-        '35': {'p1': '3f', 'p3': '5f', 'p7': '2f', 'p9': '2d'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p1': '2a', 'p3': '7c', 'p7': '26', 'p9': '5c'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'sl': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31', 'p9': '007E'},
-        '03': {'p2': '22', 'p8': '32', 'p9': '02C7'},
-        '04': {'p2': '23', 'p8': '33', 'p9': '005E'},
-        '05': {'p2': '24', 'p8': '34', 'p9': '02D8'},
-        '06': {'p2': '25', 'p8': '35', 'p9': '02DA'},
-        '07': {'p2': '26', 'p8': '36', 'p9': '02DB'},
-        '08': {'p2': '002F', 'p8': '37', 'p9': '60'},
-        '09': {'p2': '28', 'p8': '38', 'p9': '02D9'},
-        '0A': {'p2': '29', 'p8': '39', 'p9': '00B4'},
-        '0B': {'p2': '003D', 'p8': '30', 'p9': '02DD'},
-        '0C': {'p2': '003F', 'p8': '27', 'p9': '00A8'},
-        '0D': {'p2': '002A', 'p8': '002B', 'p9': '00B8'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71', 'p9': '005C'},
-        '11': {'p5': '77', 'p9': '007C'},
-        '12': {'p5': '65', 'p9': '20AC'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '7A'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '6F'},
-        '19': {'p5': '70'},
-        '1A': {'p5': '161', 'p9': '00F7'},
-        '1B': {'p5': '111', 'p9': '00D7'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66', 'p9': '005B'},
-        '22': {'p5': '67', 'p9': '005D'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6A'},
-        '25': {'p5': '6B', 'p9': '142'},
-        '26': {'p5': '6C', 'p9': '141'},
-        '27': {'p5': '10D'},
-        '28': {'p5': '107', 'p9': '00DF'},
-        '29': {'p2': '00A8', 'p8': '00B8'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p5': '17E', 'p9': '00A4'},
-        '2C': {'p5': '79'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76', 'p9': '40'},
-        '30': {'p5': '62', 'p9': '007B'},
-        '31': {'p5': '6E', 'p9': '007D'},
-        '32': {'p5': '6D', 'p9': '00A7'},
-        '33': {'p2': '003B', 'p8': '002C'},
-        '34': {'p2': '003A', 'p8': '002E'},
-        '35': {'p2': '005F', 'p8': '002D'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p2': '003E', 'p8': '003C'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'sr': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': '\u0438\u0437\u043b'},
-        '02': {'p2': '21', 'p8': '31', 'p9': '007E'},
-        '03': {'p2': '22', 'p8': '32'},
-        '04': {'p2': '23', 'p8': '33', 'p9': '005E'},
-        '05': {'p2': '24', 'p8': '34'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '26', 'p8': '36'},
-        '08': {'p2': '27', 'p8': '37', 'p9': '60'},
-        '09': {'p2': '28', 'p8': '38'},
-        '0A': {'p2': '29', 'p8': '39'},
-        '0B': {'p2': '003D', 'p8': '30'},
-        '0C': {'p2': '003F', 'p8': '27'},
-        '0D': {'p2': '002A', 'p8': '002B'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p4': '71', 'p6': '459'},
-        '11': {'p4': '77', 'p6': '045A'},
-        '12': {'p5': '65'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '7A'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '6F'},
-        '19': {'p5': '70'},
-        '1A': {'p4': '161', 'p6': '448'},
-        '1B': {'p4': '111', 'p6': '442'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': '\u043a\u043d\u0442\u0440'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66', 'p9': '005B'},
-        '22': {'p5': '67', 'p9': '005D'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6A'},
-        '25': {'p5': '6B'},
-        '26': {'p5': '6C'},
-        '27': {'p4': '10D', 'p6': '447'},
-        '28': {'p4': '107', 'p6': '045B'},
-        '29': {'p2': '007C', 'p8': '005C'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p4': '17E', 'p6': '436'},
-        '2C': {'p5': '79'},
-        '2D': {'p4': '78', 'p6': '045F'},
-        '2E': {'p4': '63', 'p6': '446'},
-        '2F': {'p5': '76', 'p9': '40'},
-        '30': {'p5': '62', 'p9': '007B'},
-        '31': {'p5': '6E', 'p9': '007D'},
-        '32': {'p5': '6D', 'p9': '00A7'},
-        '33': {'p2': '003B', 'p8': '002C'},
-        '34': {'p2': '003A', 'p8': '002E'},
-        '35': {'p2': '005F', 'p8': '002D'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': '\u0430\u043b\u0442'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p2': '003E', 'p8': '003C'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': '\u043a\u043d\u0442\u0440'},
-        'E0 38': {'format': 'smaller', 'label': '\u0430\u043b\u0442'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'sv': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31'},
-        '03': {'p2': '22', 'p8': '32', 'p9': '40'},
-        '04': {'p2': '23', 'p8': '33', 'p9': '00A3'},
-        '05': {'p2': '00A4', 'p8': '34', 'p9': '24'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '26', 'p8': '36'},
-        '08': {'p2': '002F', 'p8': '37', 'p9': '007B'},
-        '09': {'p2': '28', 'p8': '38', 'p9': '005B'},
-        '0A': {'p2': '29', 'p8': '39', 'p9': '005D'},
-        '0B': {'p2': '003D', 'p8': '30', 'p9': '007D'},
-        '0C': {'p2': '003F', 'p8': '002B', 'p9': '005C'},
-        '0D': {'p2': '60', 'p8': '00B4'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65', 'p9': '20AC'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '006F'},
-        '19': {'p5': '70'},
-        '1A': {'p5': 'E5'},
-        '1B': {'p2': '005E', 'p8': '00A8', 'p9': '007E'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '006A'},
-        '25': {'p5': '006B'},
-        '26': {'p5': '006C'},
-        '27': {'p5': '00F6'},
-        '28': {'p5': 'E4'},
-        '29': {'p2': '00BD', 'p8': '00A7'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '002A', 'p8': '27'},
-        '2C': {'p5': '007A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '006E'},
-        '32': {'p5': '006D'},
-        '33': {'p2': '003B', 'p8': '002C'},
-        '34': {'p2': '003A', 'p8': '002E'},
-        '35': {'p2': '005F', 'p8': '002D'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p2': '003E', 'p8': '003C', 'p9': '007C'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'th': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p1': '21', 'p3': '002B', 'p7': '31', 'p9': 'E45'},
-        '03': {'p1': '40', 'p3': 'E51', 'p7': '32', 'p9': '002F'},
-        '04': {'p1': '23', 'p3': 'E52', 'p7': '33', 'p9': '002D'},
-        '05': {'p1': '24', 'p3': 'E53', 'p7': '34', 'p9': 'E20'},
-        '06': {'p1': '25', 'p3': 'E54', 'p7': '35', 'p9': 'E16'},
-        '07': {'p1': '005E', 'p3': 'E39', 'p7': '36', 'p9': 'E38'},
-        '08': {'p1': '26', 'p3': '0E3F', 'p7': '37', 'p9': 'E36'},
-        '09': {'p1': '002A', 'p3': 'E55', 'p7': '38', 'p9': 'E04'},
-        '0A': {'p1': '28', 'p3': 'E56', 'p7': '39', 'p9': 'E15'},
-        '0B': {'p1': '29', 'p3': 'E57', 'p7': '30', 'p9': 'E08'},
-        '0C': {'p1': '005F', 'p3': 'E58', 'p7': '002D', 'p9': 'E02'},
-        '0D': {'p1': '002B', 'p3': 'E59', 'p7': '003D', 'p9': '0E0A'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p3': 'E50', 'p7': '71', 'p9': 'E46'},
-        '11': {'p3': '22', 'p7': '77', 'p9': 'E44'},
-        '12': {'p3': '0E0E', 'p7': '65', 'p9': 'E33'},
-        '13': {'p3': 'E11', 'p7': '72', 'p9': '0E1E'},
-        '14': {'p3': 'E18', 'p7': '74', 'p9': 'E30'},
-        '15': {'p3': '0E4D', 'p7': '79', 'p9': 'E31'},
-        '16': {'p3': '0E4A', 'p7': '75', 'p9': 'E35'},
-        '17': {'p3': 'E13', 'p7': '69', 'p9': 'E23'},
-        '18': {'p3': '0E2F', 'p7': '006F', 'p9': 'E19'},
-        '19': {'p3': '0E0D', 'p7': '70', 'p9': 'E22'},
-        '1A': {'p1': '007B', 'p3': 'E10', 'p7': '005B', 'p9': '0E1A'},
-        '1B': {'p1': '007D', 'p3': '002C', 'p7': '005D', 'p9': 'E25'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p3': 'E24', 'p7': '61', 'p9': '0E1F'},
-        '1F': {'p3': 'E06', 'p7': '73', 'p9': '0E2B'},
-        '20': {'p3': '0E0F', 'p7': '64', 'p9': 'E01'},
-        '21': {'p3': 'E42', 'p7': '66', 'p9': 'E14'},
-        '22': {'p3': '0E0C', 'p7': '67', 'p9': 'E40'},
-        '23': {'p3': 'E47', 'p7': '68', 'p9': 'E49'},
-        '24': {'p3': '0E4B', 'p7': '006A', 'p9': 'E48'},
-        '25': {'p3': 'E29', 'p7': '006B', 'p9': 'E32'},
-        '26': {'p3': 'E28', 'p7': '006C', 'p9': '0E2A'},
-        '27': {'p1': '003A', 'p3': '0E0B', 'p7': '003B', 'p9': 'E27'},
-        '28': {'p1': '22', 'p3': '002E', 'p7': '27', 'p9': 'E07'},
-        '29': {'p1': '007E', 'p3': '25', 'p7': '60', 'p9': '005F'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p1': '00A6', 'p3': 'E05', 'p7': '005C', 'p9': 'E03'},
-        '2C': {'p3': '28', 'p7': '007A', 'p9': '0E1C'},
-        '2D': {'p3': '29', 'p7': '78', 'p9': '0E1B'},
-        '2E': {'p3': 'E09', 'p7': '63', 'p9': 'E41'},
-        '2F': {'p3': '0E2E', 'p7': '76', 'p9': '0E2D'},
-        '30': {'p3': '0E3A', 'p7': '62', 'p9': 'E34'},
-        '31': {'p3': '0E4C', 'p7': '006E', 'p9': 'E37'},
-        '32': {'p3': '003F', 'p7': '006D', 'p9': 'E17'},
-        '33': {'p1': '003C', 'p3': 'E12', 'p7': '002C', 'p9': 'E21'},
-        '34': {'p1': '003E', 'p3': '0E2C', 'p7': '002E', 'p9': 'E43'},
-        '35': {'p1': '003F', 'p3': 'E26', 'p7': '002D', 'p9': '0E1D'},
-        '36': {'format': 'right', 'label': 'shirt'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    },
-    'tr': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p1': '21', 'p7': '31'},
-        '03': {'p1': '27', 'p7': '32'},
-        '04': {'p1': '5e', 'p7': '33', 'p9': '23'},
-        '05': {'p1': '2b', 'p7': '34', 'p9': '24'},
-        '06': {'p1': '25', 'p7': '35'},
-        '07': {'p1': '26', 'p7': '36'},
-        '08': {'p1': '2f', 'p7': '37', 'p9': '7b'},
-        '09': {'p1': '28', 'p7': '38', 'p9': '5b'},
-        '0A': {'p1': '29', 'p7': '39', 'p9': '5d'},
-        '0B': {'p1': '3d', 'p7': '30', 'p9': '7d'},
-        '0C': {'p1': '3f', 'p7': '2a', 'p9': '5c'},
-        '0D': {'p1': '5f', 'p7': '2d'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71', 'p9': '40'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65', 'p9': '20ac'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '6F'},
-        '19': {'p5': '70'},
-        '1A': {'p5': '11F', 'p9': 'A8'},
-        '1B': {'p5': '0FC', 'p9': '7e'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6A'},
-        '25': {'p5': '6B'},
-        '26': {'p5': '6C'},
-        '27': {'p5': '15f', 'p9': 'B4'},
-        '28': {'p5': '69'},
-        '29': {'p1': 'E9', 'p7': '22'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p1': '3b', 'p7': '2c', 'p9': '60'},
-        '2C': {'p5': '7A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '6E'},
-        '32': {'p5': '6D'},
-        '33': {'p5': '0F6'},
-        '34': {'p5': 'E7'},
-        '35': {'p1': '3a', 'p7': '2e'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '56': {'p1': '3e', 'p7': '3c', 'p9': '7c'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'E'
-    },
-    'uk': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31', 'p9': '2013'},
-        '03': {'p2': '40', 'p8': '32', 'p9': '22'},
-        '04': {'p2': '2116', 'p8': '33', 'p9': '20AC'},
-        '05': {'p2': '003B', 'p8': '34', 'p9': '24'},
-        '06': {'p2': '25', 'p8': '35', 'p9': '00BA'},
-        '07': {'p2': '003A', 'p8': '36'},
-        '08': {'p2': '003F', 'p8': '37'},
-        '09': {'p2': '002A', 'p8': '38', 'p9': '00A7'},
-        '0A': {'p2': '28', 'p8': '39'},
-        '0B': {'p2': '29', 'p8': '30'},
-        '0C': {'p2': '005F', 'p8': '002D', 'p9': '005B'},
-        '0D': {'p2': '002B', 'p8': '003D', 'p9': '005D'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71', 'p9': '439'},
-        '11': {'p5': '77', 'p9': '446'},
-        '12': {'p5': '65', 'p9': '443'},
-        '13': {'p5': '72', 'p9': '043A'},
-        '14': {'p5': '74', 'p9': '435'},
-        '15': {'p5': '79', 'p9': '043D'},
-        '16': {'p5': '75', 'p9': '433'},
-        '17': {'p5': '69', 'p9': '448'},
-        '18': {'p5': '006F', 'p9': '449'},
-        '19': {'p5': '70', 'p9': '437'},
-        '1A': {'p2': '007B', 'p8': '445'},
-        '1B': {'p2': '007D', 'p8': '044A', 'p9': '457'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61', 'p9': '444'},
-        '1F': {'p2': '73', 'p8': '044B', 'p9': '456'},
-        '20': {'p5': '64', 'p9': '432'},
-        '21': {'p5': '66', 'p9': '430'},
-        '22': {'p5': '67', 'p9': '043F'},
-        '23': {'p5': '68', 'p9': '440'},
-        '24': {'p5': '006A', 'p9': '043E'},
-        '25': {'p5': '006B', 'p9': '043B'},
-        '26': {'p5': '006C', 'p9': '434'},
-        '27': {'p2': '003A', 'p8': '003B', 'p9': '436'},
-        '28': {'p1': '22', 'p3': '454', 'p7': '27', 'p9': '044D'},
-        '29': {'p2': '491', 'p8': '27', 'p9': '20B4'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p1': '00BB', 'p3': '002F', 'p7': '00AB', 'p9': '005C'},
-        '2C': {'p5': '007A', 'p9': '044F'},
-        '2D': {'p5': '78', 'p9': '447'},
-        '2E': {'p5': '63', 'p9': '441'},
-        '2F': {'p5': '76', 'p9': '043C'},
-        '30': {'p5': '62', 'p9': '438'},
-        '31': {'p5': '006E', 'p9': '442'},
-        '32': {'p5': '006D', 'p9': '044C'},
-        '33': {'p2': '003C', 'p8': '002C', 'p9': '431'},
-        '34': {'p2': '003E', 'p8': '002E', 'p9': '044E'},
-        '35': {'p1': '003F', 'p3': '002C', 'p7': '002F', 'p9': '002E'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '37': {},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt gr'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    },
-    'vi': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p5': '103'},
-        '03': {'p5': 'E2'},
-        '04': {'p5': 'EA'},
-        '05': {'p5': 'F4'},
-        '06': {'p2': '300'},
-        '07': {'p2': '309'},
-        '08': {'p2': '303'},
-        '09': {'p2': '301'},
-        '0A': {'p2': '323'},
-        '0B': {'p5': '111'},
-        '0C': {'p2': '5F', 'p8': '2D'},
-        '0D': {'p2': '2B', 'p8': '20AB'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '6F'},
-        '19': {'p5': '70'},
-        '1A': {'p5': '1B0'},
-        '1B': {'p5': '1A1'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6A'},
-        '25': {'p5': '6B'},
-        '26': {'p5': '6C'},
-        '27': {'p2': '3A', 'p8': '3B'},
-        '28': {'p2': '22', 'p8': '27'},
-        '29': {'p2': '7E', 'p8': '60'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {},
-        '2C': {'p5': '7A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '6E'},
-        '32': {'p5': '6D'},
-        '33': {'p2': '3C', 'p8': '2C'},
-        '34': {'p2': '3E', 'p8': '2E'},
-        '35': {'p2': '3F', 'p8': '2F'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '37': {},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        '73': {},
-        '79': {'format': 'smaller'},
-        '7B': {'format': 'smaller'},
-        '7D': {'p2': 'A6', 'p8': '5C'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    },
-    'zh_CN': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p2': '21', 'p8': '31'},
-        '03': {'p2': '40', 'p8': '32'},
-        '04': {'p2': '23', 'p8': '33'},
-        '05': {'p2': '24', 'p8': '34'},
-        '06': {'p2': '25', 'p8': '35'},
-        '07': {'p2': '005E', 'p8': '36'},
-        '08': {'p2': '26', 'p8': '37'},
-        '09': {'p2': '002A', 'p8': '38'},
-        '0A': {'p2': '28', 'p8': '39'},
-        '0B': {'p2': '29', 'p8': '30'},
-        '0C': {'p2': '005F', 'p8': '002D'},
-        '0D': {'p2': '002B', 'p8': '003D'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p5': '71'},
-        '11': {'p5': '77'},
-        '12': {'p5': '65'},
-        '13': {'p5': '72'},
-        '14': {'p5': '74'},
-        '15': {'p5': '79'},
-        '16': {'p5': '75'},
-        '17': {'p5': '69'},
-        '18': {'p5': '6F'},
-        '19': {'p5': '70'},
-        '1A': {'p2': '007B', 'p8': '005B'},
-        '1B': {'p2': '007D', 'p8': '005D'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p5': '61'},
-        '1F': {'p5': '73'},
-        '20': {'p5': '64'},
-        '21': {'p5': '66'},
-        '22': {'p5': '67'},
-        '23': {'p5': '68'},
-        '24': {'p5': '6A'},
-        '25': {'p5': '6B'},
-        '26': {'p5': '6C'},
-        '27': {'p2': '003A', 'p8': '003B'},
-        '28': {'p2': '22', 'p8': '27'},
-        '29': {'p2': '007E', 'p8': '60'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p2': '007C', 'p8': '005C'},
-        '2C': {'p5': '7A'},
-        '2D': {'p5': '78'},
-        '2E': {'p5': '63'},
-        '2F': {'p5': '76'},
-        '30': {'p5': '62'},
-        '31': {'p5': '6E'},
-        '32': {'p5': '6D'},
-        '33': {'p2': '003C', 'p8': '002C'},
-        '34': {'p2': '003E', 'p8': '002E'},
-        '35': {'p2': '003F', 'p8': '002F'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    },
-    'zh_TW': {
-      'keys': {
-        '00': {'label': 'power'},
-        '01': {'format': 'smaller', 'label': 'esc'},
-        '02': {'p1': '21', 'p3': '3105', 'p7': '31'},
-        '03': {'p1': '40', 'p3': '3109', 'p7': '32'},
-        '04': {'p1': '23', 'p3': '02C7', 'p7': '33'},
-        '05': {'p1': '24', 'p3': '02CB', 'p7': '34'},
-        '06': {'p1': '25', 'p3': '3113', 'p7': '35'},
-        '07': {'p1': '005E', 'p3': '02CA', 'p7': '36'},
-        '08': {'p1': '26', 'p3': '02D9', 'p7': '37'},
-        '09': {'p1': '002A', 'p3': '311A', 'p7': '38'},
-        '0A': {'p1': '28', 'p3': '311E', 'p7': '39'},
-        '0B': {'p1': '29', 'p3': '3122', 'p7': '30'},
-        '0C': {'p1': '005F', 'p3': '3126', 'p7': '002D'},
-        '0D': {'p1': '002B', 'p7': '003D'},
-        '0E': {'format': 'right', 'label': 'backspace'},
-        '0F': {'format': 'left', 'label': 'tab'},
-        '10': {'p1': '71', 'p3': '3106', 'p7': '624B'},
-        '11': {'p1': '77', 'p3': '310A', 'p7': '7530'},
-        '12': {'p1': '65', 'p3': '310D', 'p7': '6C34'},
-        '13': {'p1': '72', 'p3': '3110', 'p7': '53E3'},
-        '14': {'p1': '74', 'p3': '3114', 'p7': '5EFF'},
-        '15': {'p1': '79', 'p3': '3117', 'p7': '535C'},
-        '16': {'p1': '75', 'p3': '3127', 'p7': '5C71'},
-        '17': {'p1': '69', 'p3': '311B', 'p7': '6208'},
-        '18': {'p1': '6F', 'p3': '311F', 'p7': '4EBA'},
-        '19': {'p1': '70', 'p3': '3123', 'p7': '5FC3'},
-        '1A': {'p1': '007B', 'p7': '005B'},
-        '1B': {'p1': '007D', 'p7': '005D'},
-        '1C': {'format': 'right', 'label': 'enter'},
-        '1D': {'format': 'left', 'label': 'ctrl'},
-        '1E': {'p1': '61', 'p3': '3107', 'p7': '65E5'},
-        '1F': {'p1': '73', 'p3': '310B', 'p7': '5C38'},
-        '20': {'p1': '64', 'p3': '310E', 'p7': '6728'},
-        '21': {'p1': '66', 'p3': '3111', 'p7': '706B'},
-        '22': {'p1': '67', 'p3': '3115', 'p7': '571F'},
-        '23': {'p1': '68', 'p3': '3118', 'p7': '7AF9'},
-        '24': {'p1': '6A', 'p3': '3128', 'p7': '5341'},
-        '25': {'p1': '6B', 'p3': '311C', 'p7': '5927'},
-        '26': {'p1': '6C', 'p3': '3120', 'p7': '4E2D'},
-        '27': {'p1': '003A', 'p3': '3124', 'p7': '003B'},
-        '28': {'p1': '22', 'p7': '27'},
-        '29': {'p1': '007E', 'p7': '60'},
-        '2A': {'format': 'left', 'label': 'shift'},
-        '2B': {'p1': '007C', 'p7': '005C'},
-        '2C': {'p1': '7A', 'p3': '3108', 'p7': '91CD'},
-        '2D': {'p1': '78', 'p3': '310C', 'p7': '96E3'},
-        '2E': {'p1': '63', 'p3': '310F', 'p7': '91D1'},
-        '2F': {'p1': '76', 'p3': '3112', 'p7': '5973'},
-        '30': {'p1': '62', 'p3': '3116', 'p7': '6708'},
-        '31': {'p1': '6E', 'p3': '3119', 'p7': '5F13'},
-        '32': {'p1': '6D', 'p3': '3129', 'p7': '4E00'},
-        '33': {'p1': '003C', 'p3': '311D', 'p7': '002C'},
-        '34': {'p1': '003E', 'p3': '3121', 'p7': '002E'},
-        '35': {'p1': '003F', 'p3': '3125', 'p7': '002F'},
-        '36': {'format': 'right', 'label': 'shift'},
-        '38': {'format': 'left', 'label': 'alt'},
-        '39': {'label': 'space'},
-        '3B': {'label': 'back'},
-        '3C': {'label': 'forward'},
-        '3D': {'label': 'reload'},
-        '3E': {'label': 'full screen'},
-        '3F': {'label': 'switch window'},
-        '40': {'label': 'bright down'},
-        '41': {'label': 'bright up'},
-        '42': {'label': 'mute'},
-        '43': {'label': 'vol. down'},
-        '44': {'label': 'vol. up'},
-        'E0 1D': {'format': 'smaller', 'label': 'ctrl'},
-        'E0 38': {'format': 'smaller', 'label': 'alt'},
-        'E0 48': {'label': 'up'},
-        'E0 4B': {'label': 'left'},
-        'E0 4D': {'label': 'right'},
-        'E0 50': {'label': 'down'},
-        'E0 5B': {'format': 'left', 'label': 'search'}
-      },
-      'layoutName': 'U'
-    }
-  },
-  'layouts': {
-    'B': [
-      ['43', 658.0, 6.0, 72.0, 35.0],      ['44', 730.0, 6.0, 73.0, 35.0],
-      ['42', 585.0, 6.0, 73.0, 35.0],      ['41', 513.0, 6.0, 72.0, 35.0],
-      ['40', 440.0, 6.0, 73.0, 35.0],      ['3F', 368.0, 6.0, 72.0, 35.0],
-      ['3D', 223.0, 6.0, 72.0, 35.0],      ['3E', 295.0, 6.0, 73.0, 35.0],
-      ['3C', 150.0, 6.0, 73.0, 35.0],      ['3B', 77.0, 6.0, 73.0, 35.0],
-      ['00', 803.0, 6.0, 72.0, 35.0],      ['01', 5.0, 6.0, 72.0, 35.0],
-      ['', 815.0, 107.0, 30.0, 60.0],      ['73', 740.0, 227.0, 60.0, 60.0],
-      ['56', 80.0, 227.0, 60.0, 60.0],     ['39', 245.0, 287.0, 330.0, 60.0],
-      ['0E', 785.0, 47.0, 90.0, 60.0],     ['1C', 830.0, 107.0, 45.0, 120.0],
-      ['36', 800.0, 227.0, 75.0, 60.0],    ['0F', 5.0, 107.0, 90.0, 60.0],
-      ['2A', 5.0, 227.0, 75.0, 60.0],      ['E0 50', 755.0, 318.0, 60.0, 29.0],
-      ['E0 48', 755.0, 287.0, 60.0, 31.0], ['E0 4D', 815.0, 287.0, 60.0, 60.0],
-      ['E0 4B', 695.0, 287.0, 60.0, 60.0], ['E0 5B', 5.0, 167.0, 105.0, 60.0],
-      ['38', 125.0, 287.0, 120.0, 60.0],   ['1D', 5.0, 287.0, 120.0, 60.0],
-      ['E0 1D', 635.0, 287.0, 60.0, 60.0], ['E0 38', 575.0, 287.0, 60.0, 60.0],
-      ['2B', 770.0, 167.0, 60.0, 60.0],    ['0D', 725.0, 47.0, 60.0, 60.0],
-      ['0C', 665.0, 47.0, 60.0, 60.0],     ['0B', 605.0, 47.0, 60.0, 60.0],
-      ['0A', 545.0, 47.0, 60.0, 60.0],     ['09', 485.0, 47.0, 60.0, 60.0],
-      ['08', 425.0, 47.0, 60.0, 60.0],     ['07', 365.0, 47.0, 60.0, 60.0],
-      ['06', 305.0, 47.0, 60.0, 60.0],     ['05', 245.0, 47.0, 60.0, 60.0],
-      ['04', 185.0, 47.0, 60.0, 60.0],     ['03', 125.0, 47.0, 60.0, 60.0],
-      ['02', 65.0, 47.0, 60.0, 60.0],      ['29', 5.0, 47.0, 60.0, 60.0],
-      ['35', 680.0, 227.0, 60.0, 60.0],    ['34', 620.0, 227.0, 60.0, 60.0],
-      ['33', 560.0, 227.0, 60.0, 60.0],    ['32', 500.0, 227.0, 60.0, 60.0],
-      ['31', 440.0, 227.0, 60.0, 60.0],    ['30', 380.0, 227.0, 60.0, 60.0],
-      ['2F', 320.0, 227.0, 60.0, 60.0],    ['2E', 260.0, 227.0, 60.0, 60.0],
-      ['2D', 200.0, 227.0, 60.0, 60.0],    ['2C', 140.0, 227.0, 60.0, 60.0],
-      ['28', 710.0, 167.0, 60.0, 60.0],    ['27', 650.0, 167.0, 60.0, 60.0],
-      ['26', 590.0, 167.0, 60.0, 60.0],    ['25', 530.0, 167.0, 60.0, 60.0],
-      ['24', 470.0, 167.0, 60.0, 60.0],    ['23', 410.0, 167.0, 60.0, 60.0],
-      ['22', 350.0, 167.0, 60.0, 60.0],    ['21', 290.0, 167.0, 60.0, 60.0],
-      ['20', 230.0, 167.0, 60.0, 60.0],    ['1F', 170.0, 167.0, 60.0, 60.0],
-      ['1E', 110.0, 167.0, 60.0, 60.0],    ['1B', 755.0, 107.0, 60.0, 60.0],
-      ['1A', 695.0, 107.0, 60.0, 60.0],    ['19', 635.0, 107.0, 60.0, 60.0],
-      ['18', 575.0, 107.0, 60.0, 60.0],    ['17', 515.0, 107.0, 60.0, 60.0],
-      ['16', 455.0, 107.0, 60.0, 60.0],    ['15', 395.0, 107.0, 60.0, 60.0],
-      ['14', 335.0, 107.0, 60.0, 60.0],    ['13', 275.0, 107.0, 60.0, 60.0],
-      ['12', 215.0, 107.0, 60.0, 60.0],    ['11', 155.0, 107.0, 60.0, 60.0],
-      ['10', 95.0, 107.0, 60.0, 60.0]
-    ],
-    'E': [
-      ['43', 658.0, 6.0, 72.0, 35.0],      ['44', 730.0, 6.0, 73.0, 35.0],
-      ['42', 585.0, 6.0, 73.0, 35.0],      ['41', 513.0, 6.0, 72.0, 35.0],
-      ['40', 440.0, 6.0, 73.0, 35.0],      ['3F', 368.0, 6.0, 72.0, 35.0],
-      ['3D', 223.0, 6.0, 72.0, 35.0],      ['3E', 295.0, 6.0, 73.0, 35.0],
-      ['3C', 150.0, 6.0, 73.0, 35.0],      ['3B', 77.0, 6.0, 73.0, 35.0],
-      ['00', 803.0, 6.0, 72.0, 35.0],      ['01', 5.0, 6.0, 72.0, 35.0],
-      ['56', 80.0, 227.0, 60.0, 60.0],     ['39', 245.0, 287.0, 330.0, 60.0],
-      ['0E', 785.0, 47.0, 90.0, 60.0],     ['1C', 'COMPOUND_ENTER_KEY'],
-      ['36', 740.0, 227.0, 135.0, 60.0],   ['0F', 5.0, 107.0, 90.0, 60.0],
-      ['2A', 5.0, 227.0, 75.0, 60.0],      ['E0 50', 755.0, 318.0, 60.0, 29.0],
-      ['E0 48', 755.0, 287.0, 60.0, 31.0], ['E0 4D', 815.0, 287.0, 60.0, 60.0],
-      ['E0 4B', 695.0, 287.0, 60.0, 60.0], ['E0 5B', 5.0, 167.0, 105.0, 60.0],
-      ['38', 125.0, 287.0, 120.0, 60.0],   ['1D', 5.0, 287.0, 120.0, 60.0],
-      ['E0 1D', 635.0, 287.0, 60.0, 60.0], ['E0 38', 575.0, 287.0, 60.0, 60.0],
-      ['2B', 770.5, 167.0, 60.0, 60.0],    ['0D', 725.0, 47.0, 60.0, 60.0],
-      ['0C', 665.0, 47.0, 60.0, 60.0],     ['0B', 605.0, 47.0, 60.0, 60.0],
-      ['0A', 545.0, 47.0, 60.0, 60.0],     ['09', 485.0, 47.0, 60.0, 60.0],
-      ['08', 425.0, 47.0, 60.0, 60.0],     ['07', 365.0, 47.0, 60.0, 60.0],
-      ['06', 305.0, 47.0, 60.0, 60.0],     ['05', 245.0, 47.0, 60.0, 60.0],
-      ['04', 185.0, 47.0, 60.0, 60.0],     ['03', 125.0, 47.0, 60.0, 60.0],
-      ['02', 65.0, 47.0, 60.0, 60.0],      ['29', 5.0, 47.0, 60.0, 60.0],
-      ['35', 680.0, 227.0, 60.0, 60.0],    ['34', 620.0, 227.0, 60.0, 60.0],
-      ['33', 560.0, 227.0, 60.0, 60.0],    ['32', 500.0, 227.0, 60.0, 60.0],
-      ['31', 440.0, 227.0, 60.0, 60.0],    ['30', 380.0, 227.0, 60.0, 60.0],
-      ['2F', 320.0, 227.0, 60.0, 60.0],    ['2E', 260.0, 227.0, 60.0, 60.0],
-      ['2D', 200.0, 227.0, 60.0, 60.0],    ['2C', 140.0, 227.0, 60.0, 60.0],
-      ['28', 710.0, 167.0, 60.0, 60.0],    ['27', 650.0, 167.0, 60.0, 60.0],
-      ['26', 590.0, 167.0, 60.0, 60.0],    ['25', 530.0, 167.0, 60.0, 60.0],
-      ['24', 470.0, 167.0, 60.0, 60.0],    ['23', 410.0, 167.0, 60.0, 60.0],
-      ['22', 350.0, 167.0, 60.0, 60.0],    ['21', 290.0, 167.0, 60.0, 60.0],
-      ['20', 230.0, 167.0, 60.0, 60.0],    ['1F', 170.0, 167.0, 60.0, 60.0],
-      ['1E', 110.0, 167.0, 60.0, 60.0],    ['1B', 755.0, 107.0, 60.0, 60.0],
-      ['1A', 695.0, 107.0, 60.0, 60.0],    ['19', 635.0, 107.0, 60.0, 60.0],
-      ['18', 575.0, 107.0, 60.0, 60.0],    ['17', 515.0, 107.0, 60.0, 60.0],
-      ['16', 455.0, 107.0, 60.0, 60.0],    ['15', 395.0, 107.0, 60.0, 60.0],
-      ['14', 335.0, 107.0, 60.0, 60.0],    ['13', 275.0, 107.0, 60.0, 60.0],
-      ['12', 215.0, 107.0, 60.0, 60.0],    ['11', 155.0, 107.0, 60.0, 60.0],
-      ['10', 95.0, 107.0, 60.0, 60.0]
-    ],
-    'J': [
-      ['43', 658.0, 6.0, 72.0, 35.0],      ['44', 730.0, 6.0, 73.0, 35.0],
-      ['42', 585.0, 6.0, 73.0, 35.0],      ['41', 513.0, 6.0, 72.0, 35.0],
-      ['40', 440.0, 6.0, 73.0, 35.0],      ['3F', 368.0, 6.0, 72.0, 35.0],
-      ['3D', 223.0, 6.0, 72.0, 35.0],      ['3E', 295.0, 6.0, 73.0, 35.0],
-      ['3C', 150.0, 6.0, 73.0, 35.0],      ['3B', 77.0, 6.0, 73.0, 35.0],
-      ['00', 803.0, 6.0, 72.0, 35.0],      ['01', 5.0, 6.0, 72.0, 35.0],
-      ['', 800.0, 107.0, 30.0, 60.0],      ['73', 725.0, 227.0, 60.0, 60.0],
-      ['39', 260.0, 287.0, 240.0, 60.0],   ['79', 500.0, 287.0, 75.0, 60.0],
-      ['7B', 185.0, 287.0, 75.0, 60.0],    ['7D', 759.0, 47.0, 56.0, 60.0],
-      ['0E', 815.0, 47.0, 60.0, 60.0],     ['1C', 815.0, 107.0, 60.0, 120.0],
-      ['36', 785.0, 227.0, 90.0, 60.0],    ['0F', 5.0, 107.0, 75.0, 60.0],
-      ['2A', 5.0, 227.0, 120.0, 60.0],     ['E0 50', 755.0, 318.0, 60.0, 29.0],
-      ['E0 48', 755.0, 287.0, 60.0, 31.0], ['E0 4D', 815.0, 287.0, 60.0, 60.0],
-      ['E0 4B', 695.0, 287.0, 60.0, 60.0], ['E0 5B', 5.0, 167.0, 90.0, 60.0],
-      ['38', 95.0, 287.0, 90.0, 60.0],     ['1D', 5.0, 287.0, 90.0, 60.0],
-      ['E0 1D', 635.0, 287.0, 60.0, 60.0], ['E0 38', 575.0, 287.0, 60.0, 60.0],
-      ['2B', 755.0, 167.0, 60.0, 60.0],    ['0D', 701.0, 47.0, 58.0, 60.0],
-      ['0C', 643.0, 47.0, 58.0, 60.0],     ['0B', 585.0, 47.0, 58.0, 60.0],
-      ['0A', 527.0, 47.0, 58.0, 60.0],     ['09', 469.0, 47.0, 58.0, 60.0],
-      ['08', 411.0, 47.0, 58.0, 60.0],     ['07', 353.0, 47.0, 58.0, 60.0],
-      ['06', 295.0, 47.0, 58.0, 60.0],     ['05', 237.0, 47.0, 58.0, 60.0],
-      ['04', 179.0, 47.0, 58.0, 60.0],     ['03', 121.0, 47.0, 58.0, 60.0],
-      ['02', 63.0, 47.0, 58.0, 60.0],      ['29', 5.0, 47.0, 58.0, 60.0],
-      ['35', 665.0, 227.0, 60.0, 60.0],    ['34', 605.0, 227.0, 60.0, 60.0],
-      ['33', 545.0, 227.0, 60.0, 60.0],    ['32', 485.0, 227.0, 60.0, 60.0],
-      ['31', 425.0, 227.0, 60.0, 60.0],    ['30', 365.0, 227.0, 60.0, 60.0],
-      ['2F', 305.0, 227.0, 60.0, 60.0],    ['2E', 245.0, 227.0, 60.0, 60.0],
-      ['2D', 185.0, 227.0, 60.0, 60.0],    ['2C', 125.0, 227.0, 60.0, 60.0],
-      ['28', 695.0, 167.0, 60.0, 60.0],    ['27', 635.0, 167.0, 60.0, 60.0],
-      ['26', 575.0, 167.0, 60.0, 60.0],    ['25', 515.0, 167.0, 60.0, 60.0],
-      ['24', 455.0, 167.0, 60.0, 60.0],    ['23', 395.0, 167.0, 60.0, 60.0],
-      ['22', 335.0, 167.0, 60.0, 60.0],    ['21', 275.0, 167.0, 60.0, 60.0],
-      ['20', 215.0, 167.0, 60.0, 60.0],    ['1F', 155.0, 167.0, 60.0, 60.0],
-      ['1E', 95.0, 167.0, 60.0, 60.0],     ['1B', 740.0, 107.0, 60.0, 60.0],
-      ['1A', 680.0, 107.0, 60.0, 60.0],    ['19', 620.0, 107.0, 60.0, 60.0],
-      ['18', 560.0, 107.0, 60.0, 60.0],    ['17', 500.0, 107.0, 60.0, 60.0],
-      ['16', 440.0, 107.0, 60.0, 60.0],    ['15', 380.0, 107.0, 60.0, 60.0],
-      ['14', 320.0, 107.0, 60.0, 60.0],    ['13', 260.0, 107.0, 60.0, 60.0],
-      ['12', 200.0, 107.0, 60.0, 60.0],    ['11', 140.0, 107.0, 60.0, 60.0],
-      ['10', 80.0, 107.0, 60.0, 60.0]
-    ],
-    'U': [
-      ['39', 245.0, 287.0, 330.0, 60.0],   ['43', 658.0, 6.0, 72.0, 35.0],
-      ['44', 730.0, 6.0, 73.0, 35.0],      ['42', 585.0, 6.0, 73.0, 35.0],
-      ['41', 513.0, 6.0, 72.0, 35.0],      ['40', 440.0, 6.0, 73.0, 35.0],
-      ['3F', 368.0, 6.0, 72.0, 35.0],      ['3D', 223.0, 6.0, 72.0, 35.0],
-      ['3E', 295.0, 6.0, 73.0, 35.0],      ['3C', 150.0, 6.0, 73.0, 35.0],
-      ['3B', 77.0, 6.0, 73.0, 35.0],       ['00', 803.0, 6.0, 72.0, 35.0],
-      ['01', 5.0, 6.0, 72.0, 35.0],        ['0E', 785.0, 47.0, 90.0, 60.0],
-      ['1C', 770.0, 167.0, 105.0, 60.0],   ['36', 740.0, 227.0, 135.0, 60.0],
-      ['0F', 5.0, 107.0, 90.0, 60.0],      ['2A', 5.0, 227.0, 135.0, 60.0],
-      ['E0 50', 755.0, 318.0, 60.0, 29.0], ['E0 48', 755.0, 287.0, 60.0, 31.0],
-      ['E0 4D', 815.0, 287.0, 60.0, 60.0], ['E0 4B', 695.0, 287.0, 60.0, 60.0],
-      ['E0 5B', 5.0, 167.0, 105.0, 60.0],  ['38', 125.0, 287.0, 120.0, 60.0],
-      ['1D', 5.0, 287.0, 120.0, 60.0],     ['E0 1D', 635.0, 287.0, 60.0, 60.0],
-      ['E0 38', 575.0, 287.0, 60.0, 60.0], ['2B', 815.0, 107.0, 60.0, 60.0],
-      ['0D', 725.0, 47.0, 60.0, 60.0],     ['0C', 665.0, 47.0, 60.0, 60.0],
-      ['0B', 605.0, 47.0, 60.0, 60.0],     ['0A', 545.0, 47.0, 60.0, 60.0],
-      ['09', 485.0, 47.0, 60.0, 60.0],     ['08', 425.0, 47.0, 60.0, 60.0],
-      ['07', 365.0, 47.0, 60.0, 60.0],     ['06', 305.0, 47.0, 60.0, 60.0],
-      ['05', 245.0, 47.0, 60.0, 60.0],     ['04', 185.0, 47.0, 60.0, 60.0],
-      ['03', 125.0, 47.0, 60.0, 60.0],     ['02', 65.0, 47.0, 60.0, 60.0],
-      ['29', 5.0, 47.0, 60.0, 60.0],       ['35', 680.0, 227.0, 60.0, 60.0],
-      ['34', 620.0, 227.0, 60.0, 60.0],    ['33', 560.0, 227.0, 60.0, 60.0],
-      ['32', 500.0, 227.0, 60.0, 60.0],    ['31', 440.0, 227.0, 60.0, 60.0],
-      ['30', 380.0, 227.0, 60.0, 60.0],    ['2F', 320.0, 227.0, 60.0, 60.0],
-      ['2E', 260.0, 227.0, 60.0, 60.0],    ['2D', 200.0, 227.0, 60.0, 60.0],
-      ['2C', 140.0, 227.0, 60.0, 60.0],    ['28', 710.0, 167.0, 60.0, 60.0],
-      ['27', 650.0, 167.0, 60.0, 60.0],    ['26', 590.0, 167.0, 60.0, 60.0],
-      ['25', 530.0, 167.0, 60.0, 60.0],    ['24', 470.0, 167.0, 60.0, 60.0],
-      ['23', 410.0, 167.0, 60.0, 60.0],    ['22', 350.0, 167.0, 60.0, 60.0],
-      ['21', 290.0, 167.0, 60.0, 60.0],    ['20', 230.0, 167.0, 60.0, 60.0],
-      ['1F', 170.0, 167.0, 60.0, 60.0],    ['1E', 110.0, 167.0, 60.0, 60.0],
-      ['1B', 755.0, 107.0, 60.0, 60.0],    ['1A', 695.0, 107.0, 60.0, 60.0],
-      ['19', 635.0, 107.0, 60.0, 60.0],    ['18', 575.0, 107.0, 60.0, 60.0],
-      ['17', 515.0, 107.0, 60.0, 60.0],    ['16', 455.0, 107.0, 60.0, 60.0],
-      ['15', 395.0, 107.0, 60.0, 60.0],    ['14', 335.0, 107.0, 60.0, 60.0],
-      ['13', 275.0, 107.0, 60.0, 60.0],    ['12', 215.0, 107.0, 60.0, 60.0],
-      ['11', 155.0, 107.0, 60.0, 60.0],    ['10', 95.0, 107.0, 60.0, 60.0]
-    ]
-  },
-  'shortcut': {
-    '+<>ALT': 'keyboardOverlayMaximizeWindow',
-    '+<>ALT<>SHIFT': 'keyboardOverlayCenterWindow',
-    '+<>CTRL': 'keyboardOverlayZoomIn',
-    '+<>CTRL<>SHIFT': 'keyboardOverlayZoomScreenIn',
-    ',<>ALT<>CTRL': 'keyboardOverlayPreviousUser',
-    '-<>ALT': 'keyboardOverlayMinimizeWindow',
-    '-<>CTRL': 'keyboardOverlayZoomOut',
-    '-<>CTRL<>SHIFT': 'keyboardOverlayZoomScreenOut',
-    '-<>SEARCH': 'keyboardOverlayF11',
-    '.<>ALT<>CTRL': 'keyboardOverlayNextUser',
-    '.<>SEARCH': 'keyboardOverlayInsert',
-    '/<>ALT<>CTRL': 'keyboardOverlayViewKeyboardOverlay',
-    '/<>ALT<>CTRL<>SHIFT': 'keyboardOverlayViewKeyboardOverlay',
-    '/<>CTRL': 'keyboardOverlayHelp',
-    '/<>CTRL<>SHIFT': 'keyboardOverlayHelp',
-    '0<>CTRL': 'keyboardOverlayResetZoom',
-    '0<>CTRL<>SHIFT': 'keyboardOverlayResetScreenZoom',
-    '0<>SEARCH': 'keyboardOverlayF10',
-    '1<>ALT': 'keyboardOverlayActivateShelfItem1',
-    '1<>CTRL': 'keyboardOverlayActivateTab1',
-    '1<>SEARCH': 'keyboardOverlayF1',
-    '2<>ALT': 'keyboardOverlayActivateShelfItem2',
-    '2<>CTRL': 'keyboardOverlayActivateTab2',
-    '2<>SEARCH': 'keyboardOverlayF2',
-    '3<>ALT': 'keyboardOverlayActivateShelfItem3',
-    '3<>CTRL': 'keyboardOverlayActivateTab3',
-    '3<>SEARCH': 'keyboardOverlayF3',
-    '4<>ALT': 'keyboardOverlayActivateShelfItem4',
-    '4<>CTRL': 'keyboardOverlayActivateTab4',
-    '4<>SEARCH': 'keyboardOverlayF4',
-    '5<>ALT': 'keyboardOverlayActivateShelfItem5',
-    '5<>CTRL': 'keyboardOverlayActivateTab5',
-    '5<>SEARCH': 'keyboardOverlayF5',
-    '6<>ALT': 'keyboardOverlayActivateShelfItem6',
-    '6<>CTRL': 'keyboardOverlayActivateTab6',
-    '6<>SEARCH': 'keyboardOverlayF6',
-    '7<>ALT': 'keyboardOverlayActivateShelfItem7',
-    '7<>CTRL': 'keyboardOverlayActivateTab7',
-    '7<>SEARCH': 'keyboardOverlayF7',
-    '8<>ALT': 'keyboardOverlayActivateShelfItem8',
-    '8<>CTRL': 'keyboardOverlayActivateTab8',
-    '8<>SEARCH': 'keyboardOverlayF8',
-    '9<>ALT': 'keyboardOverlayActivateLastShelfItem',
-    '9<>CTRL': 'keyboardOverlayActivateLastTab',
-    '9<>SEARCH': 'keyboardOverlayF9',
-    '=<>SEARCH': 'keyboardOverlayF12',
-    'ALT<>SEARCH': 'keyboardOverlayToggleCapsLock',
-    '[<>ALT': 'keyboardOverlayDockWindowLeft',
-    ']<>ALT': 'keyboardOverlayDockWindowRight',
-    'a<>CTRL': 'keyboardOverlaySelectAll',
-    'arrowkeys<>CTRL': 'keyboardOverlayWordMove',
-    'b<>ALT<>SHIFT': 'keyboardOverlayFocusBookmarks',
-    'b<>CTRL<>SHIFT': 'keyboardOverlayToggleBookmarkBar',
-    'back<>CTRL': 'keyboardOverlayFocusPreviousPane',
-    'back<>SEARCH': 'keyboardOverlayF1',
-    'backspace<>ALT': 'keyboardOverlayDelete',
-    'backspace<>CTRL': 'keyboardOverlayDeleteWord',
-    'backspace<>CTRL<>SHIFT': 'keyboardOverlayClearBrowsingDataDialog',
-    'backspace<>SEARCH': 'keyboardOverlayDelete',
-    'bright down<>ALT': 'keyboardOverlayDecreaseKeyBrightness',
-    'bright down<>ALT<>CTRL': 'keyboardOverlayMagnifierDecreaseZoom',
-    'bright down<>SEARCH': 'keyboardOverlayF6',
-    'bright up<>ALT': 'keyboardOverlayIncreaseKeyBrightness',
-    'bright up<>ALT<>CTRL': 'keyboardOverlayMagnifierIncreaseZoom',
-    'bright up<>SEARCH': 'keyboardOverlayF7',
-    'c<>CTRL': 'keyboardOverlayCopy',
-    'c<>CTRL<>SHIFT': 'keyboardOverlayDomInspector',
-    'd<>ALT': 'keyboardOverlayFocusAddressBar',
-    'd<>CTRL': 'keyboardOverlayBookmarkCurrentPage',
-    'd<>CTRL<>SEARCH': 'keyboardOverlayToggleDockedMagnifier',
-    'd<>CTRL<>SHIFT': 'keyboardOverlayBookmarkAllTabs',
-    'd<>SEARCH': 'keyboardOverlayToggleDictation',
-    'down<>ALT': 'keyboardOverlayPageDown',
-    'down<>ALT<>CTRL': 'keyboardOverlayEnd',
-    'down<>SEARCH': 'keyboardOverlayPageDown',
-    'e<>ALT': 'keyboardOverlayShowWrenchMenu',
-    'e<>CTRL': 'keyboardOverlayFocusAddressBarInSearchMode',
-    'enter<>ALT': 'keyboardOverlayOpenAddressInNewTab',
-    'enter<>CTRL': 'keyboardOverlayAddWwwAndComAndOpenAddress',
-    'enter<>SHIFT': 'keyboardOverlayFindPreviousText',
-    'esc<>SEARCH': 'keyboardOverlayTaskManager',
-    'esc<>SEARCH<>SHIFT': 'keyboardOverlayUnpin',
-    'f<>ALT': 'keyboardOverlayShowWrenchMenu',
-    'f<>CTRL': 'keyboardOverlayFindText',
-    'f1<>CTRL<>SEARCH': 'keyboardOverlayFocusPreviousPane',
-    'f1<>SEARCH': 'keyboardOverlayBackKeyLabel',
-    'f2<>CTRL<>SEARCH': 'keyboardOverlayFocusNextPane',
-    'f2<>SEARCH': 'keyboardOverlayForwardKeyLabel',
-    'f3<>CTRL<>SEARCH<>SHIFT': 'keyboardOverlayRotateScreen',
-    'f3<>SEARCH': 'keyboardOverlayReloadKeyLabel',
-    'f4<>ALT<>SEARCH': 'keyboardOverlaySwapPrimaryMonitor',
-    'f4<>CTRL<>SEARCH': 'keyboardOverlayMirrorMonitors',
-    'f4<>SEARCH': 'keyboardOverlayFullScreenKeyLabel',
-    'f5<>CTRL<>SEARCH': 'keyboardOverlayTakeScreenshot',
-    'f5<>CTRL<>SEARCH<>SHIFT': 'keyboardOverlayScreenshotRegion',
-    'f5<>ALT<>CTRL<>SEARCH': 'keyboardOverlayScreenshotWindow',
-    'f5<>SEARCH': 'keyboardOverlaySwitchWinKeyLabel',
-    'f6<>ALT<>SEARCH': 'keyboardOverlayDecreaseKeyBrightness',
-    'f6<>ALT<>CTRL<>SEARCH': 'keyboardOverlayMagnifierDecreaseZoom',
-    'f6<>SEARCH': 'keyboardOverlayBrightDownKeyLabel',
-    'f7<>ALT<>SEARCH': 'keyboardOverlayIncreaseKeyBrightness',
-    'f7<>ALT<>CTRL<>SEARCH': 'keyboardOverlayMagnifierIncreaseZoom',
-    'f7<>SEARCH': 'keyboardOverlayBrightUpKeyLabel',
-    'f8<>SEARCH': 'keyboardOverlayMuteKeyLabel',
-    'f9<>SEARCH': 'keyboardOverlayVolDownKeyLabel',
-    'f10<>SEARCH': 'keyboardOverlayVolUpKeyLabel',
-    'f10<>SHIFT': 'keyboardOverlayContextMenu',
-    'forward<>CTRL': 'keyboardOverlayFocusNextPane',
-    'forward<>SEARCH': 'keyboardOverlayF2',
-    'full screen<>ALT': 'keyboardOverlaySwapPrimaryMonitor',
-    'full screen<>CTRL': 'keyboardOverlayMirrorMonitors',
-    'full screen<>SEARCH': 'keyboardOverlayF4',
-    'full screen<>SHIFT': 'keyboardOverlayFullScreenKeyLabel',
-    'g<>CTRL': 'keyboardOverlayFindTextAgain',
-    'g<>CTRL<>SHIFT': 'keyboardOverlayFindPreviousText',
-    'h<>CTRL': 'keyboardOverlayHistory',
-    'h<>CTRL<>SEARCH': 'keyboardOverlayToggleHighContrastMode',
-    'i<>ALT<>CTRL': 'keyboardOverlayTouchHudModeChange',
-    'i<>ALT<>SHIFT': 'keyboardOverlayReportIssue',
-    'i<>CTRL<>SHIFT': 'keyboardOverlayDeveloperTools',
-    'j<>CTRL': 'keyboardOverlayDownloads',
-    'j<>CTRL<>SHIFT': 'keyboardOverlayJavascriptConsole',
-    'k<>CTRL': 'keyboardOverlayFocusAddressBarInSearchMode',
-    'k<>SEARCH<>SHIFT': 'keyboardOverlayShowImeBubble',
-    'l<>ALT<>SHIFT': 'keyboardOverlayFocusShelf',
-    'l<>CTRL': 'keyboardOverlayFocusAddressBar',
-    'l<>SEARCH': 'keyboardOverlayLockScreen',
-    'l<>SEARCH<>SHIFT': 'keyboardOverlaySuspend',
-    'left<>ALT': 'keyboardOverlayGoBack',
-    'left<>CTRL': 'keyboardOverlayPreviousWord',
-    'left<>CTRL<>SHIFT': 'keyboardOverlaySelectWordAtATime',
-    'left<>SEARCH': 'keyboardOverlayHome',
-    'm<>ALT<>SEARCH': 'keyboardOverlayMoveActiveWindowBetweenDisplays',
-    'm<>ALT<>SHIFT': 'keyboardOverlayOpenFileManager',
-    'm<>CTRL<>SEARCH': 'keyboardOverlayToggleFullscreenMagnifier',
-    'mute<>SEARCH': 'keyboardOverlayF8',
-    'n<>ALT<>SHIFT': 'keyboardOverlayShowMessageCenter',
-    'n<>CTRL': 'keyboardOverlayNewWindow',
-    'n<>CTRL<>SHIFT': 'keyboardOverlayNewIncognitoWindow',
-    'o<>CTRL': 'keyboardOverlayOpen',
-    'o<>CTRL<>SHIFT': 'keyboardOverlayBookmarkManager',
-    'p<>ALT<>CTRL': 'keyboardOverlayToggleProjectionTouchHud',
-    'p<>CTRL': 'keyboardOverlayPrint',
-    'p<>ALT<>SHIFT': 'keyboardOverlayShowStylusTools',
-    'power': 'keyboardOverlayLockScreenOrPowerOff',
-    'q<>CTRL<>SHIFT': 'keyboardOverlaySignOut',
-    'r<>CTRL': 'keyboardOverlayReloadCurrentPage',
-    'r<>CTRL<>SHIFT': 'keyboardOverlayReloadBypassingCache',
-    'reload<>ALT<>CTRL<>SHIFT': 'keyboardOverlayRotateWindow',
-    'reload<>CTRL<>SHIFT': 'keyboardOverlayRotateScreen',
-    'reload<>SEARCH': 'keyboardOverlayF3',
-    'right<>ALT': 'keyboardOverlayGoForward',
-    'right<>CTRL': 'keyboardOverlayNextWord',
-    'right<>CTRL<>SHIFT': 'keyboardOverlaySelectWordAtATime',
-    'right<>SEARCH': 'keyboardOverlayEnd',
-    's<>ALT<>SHIFT': 'keyboardOverlayShowStatusMenu',
-    's<>CTRL': 'keyboardOverlaySave',
-    'space<>CTRL': 'keyboardOverlaySelectPreviousInputMethod',
-    'space<>CTRL<>SHIFT': 'keyboardOverlayCycleThroughInputMethods',
-    'space<>SHIFT': 'keyboardOverlayScrollUpOnePage',
-    'switch window<>ALT<>CTRL': 'keyboardOverlayScreenshotWindow',
-    'switch window<>CTRL': 'keyboardOverlayTakeScreenshot',
-    'switch window<>CTRL<>SHIFT': 'keyboardOverlayScreenshotRegion',
-    'switch window<>SEARCH': 'keyboardOverlayF5',
-    't<>ALT<>CTRL': 'keyboardOverlayNewTerminal',
-    't<>ALT<>SHIFT': 'keyboardOverlayFocusToolbar',
-    't<>CTRL': 'keyboardOverlayNewTab',
-    't<>CTRL<>SHIFT': 'keyboardOverlayReopenLastClosedTab',
-    'tab<>ALT': 'keyboardOverlayNextWindow',
-    'tab<>ALT<>SHIFT': 'keyboardOverlayPreviousWindow',
-    'tab<>CTRL': 'keyboardOverlayActivateNextTab',
-    'tab<>CTRL<>SHIFT': 'keyboardOverlayActivatePreviousTab',
-    'u<>CTRL': 'keyboardOverlayViewSource',
-    'u<>CTRL<>SHIFT': 'keyboardOverlayInputUnicodeCharacters',
-    'up<>ALT': 'keyboardOverlayPageUp',
-    'up<>ALT<>CTRL': 'keyboardOverlayHome',
-    'up<>SEARCH': 'keyboardOverlayPageUp',
-    'v<>CTRL': 'keyboardOverlayPaste',
-    'v<>CTRL<>SHIFT': 'keyboardOverlayPasteAsPlainText',
-    'vol. down<>SEARCH': 'keyboardOverlayF9',
-    'vol. up<>SEARCH': 'keyboardOverlayF10',
-    'vol. up<>SEARCH<>SHIFT': 'keyboardOverlayContextMenu',
-    'w<>CTRL': 'keyboardOverlayCloseTab',
-    'w<>CTRL<>SHIFT': 'keyboardOverlayCloseWindow',
-    'x<>CTRL': 'keyboardOverlayCut',
-    'z<>ALT<>CTRL': 'keyboardOverlayToggleChromevoxSpokenFeedback',
-    'z<>CTRL': 'keyboardOverlayUndo',
-    'a<>SEARCH': 'keyboardOverlayVoiceInteraction',
-  }
-};
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html
index 54c05e36..7192cf2 100644
--- a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html
+++ b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html
@@ -1,5 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.html">
 <link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
@@ -20,6 +21,7 @@
     <multidevice-setup delegate="[[delegate_]]"
         on-setup-exited="onExitRequested_"
         on-forward-button-focus-requested="onForwardButtonFocusRequested_"
+        on-visible-page-name-changed="onVisiblePageNameChanged_"
         forward-button-text="{{forwardButtonText_}}"
         forward-button-disabled="{{forwardButtonDisabled_}}"
         cancel-button-text="{{cancelButtonText_}}"
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.js b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.js
index 1dc4634..6d7bb9c8 100644
--- a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.js
+++ b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.js
@@ -2,6 +2,26 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+cr.define('multidevice_setup_post_oobe', function() {
+  /**
+   * This enum is tied directly to a UMA enum defined in
+   * //tools/metrics/histograms/enums.xml, and should always reflect it (do not
+   * change one without changing the other).
+   * @enum {number}
+   */
+  PageNameValue = {
+    UNKNOWN: 0,
+    START: 1,
+    PASSWORD: 2,
+    SUCCESS: 3,
+    MAX_VALUE: 4,
+  };
+
+  return {
+    PageNameValue: PageNameValue,
+  };
+});
+
 /**
  * MultiDevice setup flow which is shown after OOBE has completed.
  */
@@ -64,5 +84,33 @@
   /** @private */
   onForwardButtonFocusRequested_: function() {
     this.$$('#forward-button').focus();
+  },
+
+  /**
+   * @param {!{detail: {value: multidevice_setup.PageName}}} event
+   * @private
+   */
+  onVisiblePageNameChanged_: function(event) {
+    let pageNameValue;
+    switch (event.detail.value) {
+      case multidevice_setup.PageName.START:
+        pageNameValue = multidevice_setup_post_oobe.PageNameValue.START;
+        break;
+      case multidevice_setup.PageName.PASSWORD:
+        pageNameValue = multidevice_setup_post_oobe.PageNameValue.PASSWORD;
+        break;
+      case multidevice_setup.PageName.SUCCESS:
+        pageNameValue = multidevice_setup_post_oobe.PageNameValue.SUCCESS;
+        break;
+      default:
+        console.warn('Unexpected PageName.');
+        pageNameValue = multidevice_setup_post_oobe.PageNameValue.UNKNOWN;
+        break;
+    }
+
+    chrome.send('metricsHandler:recordInHistogram', [
+      'MultiDevice.PostOOBESetupFlow.PageShown', pageNameValue,
+      multidevice_setup_post_oobe.PageNameValue.MAX_VALUE
+    ]);
   }
 });
diff --git a/chrome/browser/resources/chromeos/select_to_speak/BUILD.gn b/chrome/browser/resources/chromeos/select_to_speak/BUILD.gn
index eca51cf..6370746 100644
--- a/chrome/browser/resources/chromeos/select_to_speak/BUILD.gn
+++ b/chrome/browser/resources/chromeos/select_to_speak/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/features.gni")
+import("//chrome/common/features.gni")
 import("//testing/test.gni")
 import("//chrome/test/base/js2gtest.gni")
 import("//chrome/browser/resources/chromeos/chromevox/run_jsbundler.gni")
@@ -98,42 +99,15 @@
   is_guest_manifest = true
 }
 
-# TODO(jamescook): Fold this into browser_tests.
-test("select_to_speak_extension_tests") {
-  sources = [
-    "//chrome/browser/extensions/browsertest_util.cc",
-    "//chrome/browser/extensions/browsertest_util.h",
-    "//chrome/browser/ui/webui/web_ui_test_handler.cc",
-    "//chrome/browser/ui/webui/web_ui_test_handler.h",
-    "//chrome/test/base/extension_js_browser_test.cc",
-    "//chrome/test/base/extension_js_browser_test.h",
-    "//chrome/test/base/extension_load_waiter_one_shot.cc",
-    "//chrome/test/base/extension_load_waiter_one_shot.h",
-    "//chrome/test/base/javascript_browser_test.cc",
-    "//chrome/test/base/javascript_browser_test.h",
-    "//chrome/test/base/test_chrome_web_ui_controller_factory.cc",
-    "//chrome/test/base/test_chrome_web_ui_controller_factory.h",
-    "//chrome/test/base/web_ui_browser_test.cc",
-    "//chrome/test/base/web_ui_browser_test.h",
-  ]
+source_set("browser_tests") {
+  testonly = true
+  assert(enable_extensions)
 
   deps = [
     ":select_to_speak_extjs_tests",
-    "//base",
-    "//base/test:test_support",
-    "//chrome:browser_tests_pak",
-    "//chrome:packed_resources",
-    "//chrome:resources",
-    "//chrome/browser",
-    "//chrome/renderer",
-    "//chrome/test:browser_tests_runner",
-    "//chrome/test:test_support",
-    "//chrome/test:test_support_ui",
-    "//content/test:test_support",
-    "//testing/gmock",
-    "//testing/gtest",
   ]
 
+  # TODO(jamescook): Figure out which of these are really necessary.
   data = [
     "$root_out_dir/chrome_100_percent.pak",
     "$root_out_dir/chrome_200_percent.pak",
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume.cc b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume.cc
index b818c8f..817e0fa 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume.cc
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume.cc
@@ -351,13 +351,23 @@
       display_name = Cp437ToUtf8(path_name);
     }
 
-    ConstructMetadata(index, display_name.c_str(), path_name, size,
-                      is_directory, modification_time, is_encoded_in_utf8,
-                      &root_metadata);
+    // If the path is absolute, which is technically a violation of the ZIP
+    // spec, strip the leading '/' and treat the path as relative.
+    // TODO(amistry): Handle other cases of ill-formed paths such as "//", ".",
+    // and ".."
+    if (display_name[0] == '/') {
+      display_name = display_name.substr(1);
+    }
 
-    index_to_pathname_[index] = path_name;
-
-    ++index;
+    if (!display_name.empty()) {
+      // Some archives have a "/" entry, which turns into the empty string when
+      // the leading '/' is stripped.
+      ConstructMetadata(index, display_name.c_str(), path_name, size,
+                        is_directory, modification_time, is_encoded_in_utf8,
+                        &root_metadata);
+      index_to_pathname_[index] = path_name;
+      ++index;
+    }
 
     int return_value = volume_archive_->GoToNextFile();
     if (return_value == VolumeArchive::RESULT_FAIL) {
diff --git a/chrome/browser/resources/print_preview/new/app.js b/chrome/browser/resources/print_preview/new/app.js
index 73e4fe62..101be90 100644
--- a/chrome/browser/resources/print_preview/new/app.js
+++ b/chrome/browser/resources/print_preview/new/app.js
@@ -263,6 +263,13 @@
     if (e.code == 'KeyP') {
       if ((cr.isMac && e.metaKey && e.altKey && !e.shiftKey && !e.ctrlKey) ||
           (!cr.isMac && e.shiftKey && e.ctrlKey && !e.altKey && !e.metaKey)) {
+        // Don't use system dialog if the link isn't available.
+        const linkContainer = this.$$('print-preview-link-container');
+        if (!linkContainer || !linkContainer.systemDialogLinkAvailable()) {
+          e.preventDefault();
+          return;
+        }
+
         // Don't try to print with system dialog on Windows if the document is
         // not ready, because we send the preview document to the printer on
         // Windows.
diff --git a/chrome/browser/resources/print_preview/new/link_container.js b/chrome/browser/resources/print_preview/new/link_container.js
index db32458a7..8c388415 100644
--- a/chrome/browser/resources/print_preview/new/link_container.js
+++ b/chrome/browser/resources/print_preview/new/link_container.js
@@ -79,4 +79,9 @@
     this.fire('open-pdf-in-preview');
   },
   // </if>
+
+  /** @return {boolean} Whether the system dialog link is available. */
+  systemDialogLinkAvailable: function() {
+    return this.shouldShowSystemDialogLink_ && !this.systemDialogLinkDisabled_;
+  },
 });
diff --git a/chrome/browser/resources/print_preview/new/model.js b/chrome/browser/resources/print_preview/new/model.js
index ae43e92..d11dbcc 100644
--- a/chrome/browser/resources/print_preview/new/model.js
+++ b/chrome/browser/resources/print_preview/new/model.js
@@ -554,6 +554,11 @@
                 defaultOption.type));
       }
     } else if (
+        this.destination.id ===
+            print_preview.Destination.GooglePromotedId.DOCS ||
+        this.destination.type === print_preview.DestinationType.MOBILE) {
+      this.set('settings.color.unavailableValue', true);
+    } else if (
         caps && caps.color && caps.color.option &&
         caps.color.option.length > 0) {
       this.set(
diff --git a/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html b/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html
index 6696430..016fb4f 100644
--- a/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html
+++ b/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html
@@ -223,6 +223,13 @@
             disabled="[[!prefs.settings.a11y.autoclick.value]]">
         </settings-dropdown-menu>
       </div>
+      <div class="sub-item">
+        <settings-toggle-button class="continuation sub-item"
+          hidden$="[[!prefs.settings.a11y.autoclick.value]]"
+          pref="{{prefs.settings.a11y.autoclick_revert_to_left_click}}"
+          label="$i18n{autoclickReverToLeftClick}">
+        </settings-toggle-button>
+      </div>
     </template>
     <settings-toggle-button
         pref="{{prefs.settings.a11y.large_cursor_enabled}}"
diff --git a/chrome/browser/resources/settings/about_page/about_page.js b/chrome/browser/resources/settings/about_page/about_page.js
index 70e53fcf..256b624 100644
--- a/chrome/browser/resources/settings/about_page/about_page.js
+++ b/chrome/browser/resources/settings/about_page/about_page.js
@@ -638,4 +638,8 @@
     // </if>
     return this.showUpdateStatus_;
   },
+
+  focusSection: function() {
+    this.$$('settings-section[section="about"]').show();
+  },
 });
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.js b/chrome/browser/resources/settings/basic_page/basic_page.js
index d1cfd958..b988850 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.js
+++ b/chrome/browser/resources/settings/basic_page/basic_page.js
@@ -162,6 +162,12 @@
     return visibility !== false;
   },
 
+  focusSection: function() {
+    const section = this.getSection(settings.getCurrentRoute().section);
+    assert(section);
+    section.show();
+  },
+
   /**
    * Queues a task to search the basic sections, then another for the advanced
    * sections.
diff --git a/chrome/browser/resources/settings/device_page/device_page_browser_proxy.js b/chrome/browser/resources/settings/device_page/device_page_browser_proxy.js
index 3a1a47e..3c477e6 100644
--- a/chrome/browser/resources/settings/device_page/device_page_browser_proxy.js
+++ b/chrome/browser/resources/settings/device_page/device_page_browser_proxy.js
@@ -90,8 +90,8 @@
     /** Initializes the keyboard WebUI handler. */
     initializeKeyboard() {}
 
-    /** Shows the Ash keyboard shortcuts overlay. */
-    showKeyboardShortcutsOverlay() {}
+    /** Shows the Ash keyboard shortcut viewer. */
+    showKeyboardShortcutViewer() {}
 
     /** Requests a power status update. */
     updatePowerStatus() {}
@@ -173,8 +173,8 @@
     }
 
     /** @override */
-    showKeyboardShortcutsOverlay() {
-      chrome.send('showKeyboardShortcutsOverlay');
+    showKeyboardShortcutViewer() {
+      chrome.send('showKeyboardShortcutViewer');
     }
 
     /** @override */
diff --git a/chrome/browser/resources/settings/device_page/keyboard.html b/chrome/browser/resources/settings/device_page/keyboard.html
index 30cdd396..51a87e1 100644
--- a/chrome/browser/resources/settings/device_page/keyboard.html
+++ b/chrome/browser/resources/settings/device_page/keyboard.html
@@ -129,11 +129,11 @@
         </settings-slider>
       </div>
     </iron-collapse>
-    <div id="keyboardOverlay" class="settings-box"
-        on-click="onShowKeyboardShortcutsOverlayTap_" actionable>
-      <div class="start">$i18n{showKeyboardShortcutsOverlay}</div>
+    <div id="keyboardShortcutViewer" class="settings-box"
+        on-click="onShowKeyboardShortcutViewerTap_" actionable>
+      <div class="start">$i18n{showKeyboardShortcutViewer}</div>
       <paper-icon-button-light class="icon-external">
-        <button aria-label="$i18n{showKeyboardShortcutsOverlay}"></button>
+        <button aria-label="$i18n{showKeyboardShortcutViewer}"></button>
       </paper-icon-button-light>
     </div>
     <div class="settings-box" on-click="onShowLanguageInputTap_" actionable>
diff --git a/chrome/browser/resources/settings/device_page/keyboard.js b/chrome/browser/resources/settings/device_page/keyboard.js
index 46b8761..e128c39d 100644
--- a/chrome/browser/resources/settings/device_page/keyboard.js
+++ b/chrome/browser/resources/settings/device_page/keyboard.js
@@ -143,9 +143,9 @@
     this.showAppleCommandKey_ = keyboardParams['showAppleCommandKey'];
   },
 
-  onShowKeyboardShortcutsOverlayTap_: function() {
+  onShowKeyboardShortcutViewerTap_: function() {
     settings.DevicePageBrowserProxyImpl.getInstance()
-        .showKeyboardShortcutsOverlay();
+        .showKeyboardShortcutViewer();
   },
 
   onShowLanguageInputTap_: function() {
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_radio_button.js b/chrome/browser/resources/settings/multidevice_page/multidevice_radio_button.js
index c2163e0..e5d1217 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_radio_button.js
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_radio_button.js
@@ -9,28 +9,6 @@
     CrRadioButtonBehavior,
   ],
 
-  properties: {
-    disabled: {
-      type: Boolean,
-      reflectToAttribute: true,
-      observer: 'disabledChanged_',
-    },
-
-    name: {
-      type: String,
-      notify: true,
-    },
-  },
-
-  /**
-   * Updates attributes of the control to reflect its disabled state.
-   * @private
-   */
-  disabledChanged_: function() {
-    this.setAttribute('tabindex', this.disabled ? -1 : 0);
-    this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
-  },
-
   /**
    * Prevents on-click handles on the control from being activated when the
    * indicator is clicked.
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_smartlock_subpage.html b/chrome/browser/resources/settings/multidevice_page/multidevice_smartlock_subpage.html
index d179bbd..0db389f7 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_smartlock_subpage.html
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_smartlock_subpage.html
@@ -48,7 +48,6 @@
               label="$i18n{easyUnlockUnlockDeviceOnly}">
           </multidevice-radio-button>
           <multidevice-radio-button
-              disabled$="[[!smartLockSignInAllowed_]]"
               name="enabled"
               class="list-item"
               label="$i18n{easyUnlockUnlockDeviceAndAllowSignin}">
diff --git a/chrome/browser/resources/settings/people_page/sync_page.html b/chrome/browser/resources/settings/people_page/sync_page.html
index 9b09c39e..83f33146 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.html
+++ b/chrome/browser/resources/settings/people_page/sync_page.html
@@ -75,6 +75,11 @@
         margin-right: 8px;
       }
 
+      #disabled-by-admin-icon {
+        text-align: center;
+        width: 40px;
+      }
+
 <if expr="not chromeos">
       #toast {
         color: white;
@@ -104,6 +109,11 @@
     </template>
 </if>
 
+    <div class="settings-box first" hidden="[[!syncStatus.managed]]">
+      <iron-icon id="disabled-by-admin-icon" icon="cr20:domain"></iron-icon>
+      <div class="middle">$i18n{syncDisabledByAdministrator}</div>
+    </div>
+
     <template is="dom-if" if="[[shouldShowExistingPassphraseBelowAccount_(
       unifiedConsentEnabled, syncPrefs.passphraseRequired)]]">
       <div id="existingPassphrase" class="list-frame">
diff --git a/chrome/browser/resources/settings/privacy_page/personalization_options.html b/chrome/browser/resources/settings/privacy_page/personalization_options.html
index bf1d7b6..f985df8 100644
--- a/chrome/browser/resources/settings/privacy_page/personalization_options.html
+++ b/chrome/browser/resources/settings/privacy_page/personalization_options.html
@@ -83,11 +83,12 @@
       </settings-toggle-button>
     </template>
 <if expr="_google_chrome">
-    <settings-toggle-button
+    <settings-toggle-button id="spellCheckControl"
         pref="{{prefs.spellcheck.use_spelling_service}}"
         label="$i18n{spellingPref}"
         sub-label="$i18n{spellingDescription}"
-        disabled="[[unifiedConsentGiven]]">
+        disabled="[[unifiedConsentGiven]]"
+        hidden="[[!showSpellCheckControl_(prefs.spellcheck.dictionaries)]]">
     </settings-toggle-button>
 </if><!-- _google_chrome -->
   </template>
diff --git a/chrome/browser/resources/settings/privacy_page/personalization_options.js b/chrome/browser/resources/settings/privacy_page/personalization_options.js
index b247c94f..10b2d38 100644
--- a/chrome/browser/resources/settings/privacy_page/personalization_options.js
+++ b/chrome/browser/resources/settings/privacy_page/personalization_options.js
@@ -83,18 +83,6 @@
     // </if>
   },
 
-  /**
-   * TODO(crbug.com/855945): Use this function for the spell check error hint
-   * @param {!Event} e
-   * @private
-   */
-  onLanguageLinkBoxClick_: function(e) {
-    if (e.target.tagName === 'A') {
-      e.preventDefault();
-      settings.navigateTo(settings.routes.LANGUAGES);
-    }
-  },
-
   // <if expr="_google_chrome and not chromeos">
   /** @private */
   onMetricsReportingChange_: function() {
@@ -138,5 +126,15 @@
     settings.LifetimeBrowserProxyImpl.getInstance().restart();
   },
   // </if>
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  showSpellCheckControl_: function() {
+    return !!this.prefs.spellcheck &&
+        /** @type {!Array<string>} */
+        (this.prefs.spellcheck.dictionaries.value).length > 0;
+  },
 });
 })();
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engine_entry_css.html b/chrome/browser/resources/settings/search_engines_page/search_engine_entry_css.html
index b9b40ab0..2bc74223 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engine_entry_css.html
+++ b/chrome/browser/resources/settings/search_engines_page/search_engine_entry_css.html
@@ -3,6 +3,7 @@
     <style>
       site-favicon {
         margin-inline-end: 8px;
+        min-width: 16px;
       }
 
       .dropdown-content {
diff --git a/chrome/browser/resources/settings/settings_main/settings_main.js b/chrome/browser/resources/settings/settings_main/settings_main.js
index 156b830..dd8c3e5 100644
--- a/chrome/browser/resources/settings/settings_main/settings_main.js
+++ b/chrome/browser/resources/settings/settings_main/settings_main.js
@@ -293,4 +293,10 @@
       }, 0);
     });
   },
+
+  focusSection: function() {
+    this.$$(this.showPages_.settings ? 'settings-basic-page' :
+                                       'settings-about-page')
+        .focusSection();
+  },
 });
diff --git a/chrome/browser/resources/settings/settings_page/main_page_behavior.js b/chrome/browser/resources/settings/settings_page/main_page_behavior.js
index 5abc16e..49ef81a1 100644
--- a/chrome/browser/resources/settings/settings_page/main_page_behavior.js
+++ b/chrome/browser/resources/settings/settings_page/main_page_behavior.js
@@ -158,7 +158,7 @@
         else
           promise = this.expandSection_(currentSection);
       } else if (scrollToSection) {
-        currentSection.scrollIntoView();
+        currentSection.show();
       }
     } else if (
         this.tagName == 'SETTINGS-BASIC-PAGE' && settings.routes.ADVANCED &&
diff --git a/chrome/browser/resources/settings/settings_page/settings_section.html b/chrome/browser/resources/settings/settings_page/settings_section.html
index 7599a51..ad383e6 100644
--- a/chrome/browser/resources/settings/settings_page/settings_section.html
+++ b/chrome/browser/resources/settings/settings_page/settings_section.html
@@ -1,6 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="chrome://resources/html/util.html">
 <link rel="import" href="../animation/animation.html">
 
 <dom-module id="settings-section">
@@ -12,6 +13,7 @@
         /* Margin so the box-shadow isn't clipped during animations. */
         margin-inline-end: 3px;
         margin-inline-start: 3px;
+        outline: none;
         position: relative;
       }
 
diff --git a/chrome/browser/resources/settings/settings_page/settings_section.js b/chrome/browser/resources/settings/settings_page/settings_section.js
index a4546663..e7f9d560 100644
--- a/chrome/browser/resources/settings/settings_page/settings_section.js
+++ b/chrome/browser/resources/settings/settings_page/settings_section.js
@@ -244,6 +244,15 @@
     return animation;
   },
 
+  show: function() {
+    this.setAttribute('tabindex', '-1');
+    this.focus();
+    this.scrollIntoView();
+    listenOnce(this, ['blur', 'pointerdown'], () => {
+      this.removeAttribute('tabindex');
+    });
+  },
+
   /**
    * Helper function to animate the card's position and height.
    * @param {string} position CSS position property.
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.html b/chrome/browser/resources/settings/settings_ui/settings_ui.html
index 88d91ba7..8a0d8f8 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.html
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.html
@@ -4,6 +4,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_drawer/cr_drawer.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_toolbar/cr_toolbar.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="chrome://resources/html/util.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 <link rel="import" href="../find_shortcut_behavior.html">
 <link rel="import" href="../global_scroll_target_behavior.html">
@@ -61,8 +62,8 @@
         role="banner"
         show-menu>
     </cr-toolbar>
-    <cr-drawer id="drawer" on-close="onMenuClosed_"
-        heading="$i18n{settings}" align="$i18n{textdirection}">
+    <cr-drawer id="drawer" on-close="onMenuClose_" heading="$i18n{settings}"
+        align="$i18n{textdirection}">
       <div class="drawer-content">
         <template is="dom-if" id="drawerTemplate">
           <settings-menu page-visibility="[[pageVisibility_]]"
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.js b/chrome/browser/resources/settings/settings_ui/settings_ui.js
index 2f5ebce..cc15b83 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.js
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.js
@@ -70,9 +70,6 @@
     autofillHomeEnabled_: Boolean,
 
     /** @private */
-    enableScrollAnimator_: Boolean,
-
-    /** @private */
     lastSearchQuery_: {
       type: String,
       value: '',
@@ -159,8 +156,6 @@
     this.autofillHomeEnabled_ =
         loadTimeData.valueExists('autofillHomeEnabled') &&
         loadTimeData.getBoolean('autofillHomeEnabled');
-    this.enableScrollAnimator_ =
-        loadTimeData.getBoolean('enableScrollAnimator');
 
     this.addEventListener('show-container', () => {
       this.$.container.style.visibility = 'visible';
@@ -185,9 +180,8 @@
     document.fonts.load('bold 12px Roboto');
     settings.setGlobalScrollTarget(this.$.container);
 
-    const scrollBehavior = this.enableScrollAnimator_ ? 'smooth' : 'auto';
     const scrollToTop = top => new Promise(resolve => {
-      this.$.container.scrollTo({top, behavior: scrollBehavior});
+      this.$.container.scrollTo({top, behavior: 'smooth'});
       const onScroll = () => {
         this.debounce('scrollEnd', () => {
           this.$.container.removeEventListener('scroll', onScroll);
@@ -275,12 +269,11 @@
   },
 
   /**
-   * @param {!Event} event
+   * Called when a section is selected.
    * @private
    */
-  onIronActivate_: function(event) {
-    if (event.detail.item.id != 'advancedSubmenu')
-      this.$.drawer.close();
+  onIronActivate_: function() {
+    this.$.drawer.close();
   },
 
   /** @private */
@@ -288,14 +281,25 @@
     this.$.drawer.toggle();
   },
 
-  /** @private */
-  onMenuClosed_: function() {
-    // Add tab index so that the container can be focused.
-    this.$.container.setAttribute('tabindex', '-1');
-    this.$.container.focus();
+  /**
+   * When this is called, The drawer animation is finished, and the dialog no
+   * longer has focus. The selected section will gain focus if one was selected.
+   * Otherwise, the drawer was closed due being canceled, and the main settings
+   * container is given focus. That way the arrow keys can be used to scroll
+   * the container, and pressing tab focuses a component in settings.
+   * @private
+   */
+  onMenuClose_: function() {
+    if (this.$.drawer.wasCanceled()) {
+      // Add tab index so that the container can be focused.
+      this.$.container.setAttribute('tabindex', '-1');
+      this.$.container.focus();
 
-    listenOnce(this.$.container, ['blur', 'pointerdown'], () => {
-      this.$.container.removeAttribute('tabindex');
-    });
+      listenOnce(this.$.container, ['blur', 'pointerdown'], () => {
+        this.$.container.removeAttribute('tabindex');
+      });
+    } else {
+      this.$.main.focusSection();
+    }
   },
 });
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/BUILD.gn b/chrome/browser/resources/welcome/onboarding_welcome/BUILD.gn
index 9bad9c4e..fc918b2 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/BUILD.gn
+++ b/chrome/browser/resources/welcome/onboarding_welcome/BUILD.gn
@@ -54,7 +54,11 @@
   deps = [
     ":navigation_behavior",
     ":welcome_browser_proxy",
+    "./set_as_default/:nux_set_as_default_proxy",
+    "./shared:nux_types",
     "//ui/webui/resources/cr_elements/cr_view_manager:cr_view_manager",
+    "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js:promise_resolver",
   ]
 }
 
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email/BUILD.gn b/chrome/browser/resources/welcome/onboarding_welcome/email/BUILD.gn
index 1a66cc1..9d52f6c 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/email/BUILD.gn
+++ b/chrome/browser/resources/welcome/onboarding_welcome/email/BUILD.gn
@@ -21,6 +21,7 @@
   deps = [
     ":nux_email_proxy",
     "../:navigation_behavior",
+    "../shared:bookmark_proxy",
     "../shared:nux_types",
     "//third_party/polymer/v1_0/components-chromium/iron-a11y-announcer:iron-a11y-announcer-extracted",
     "//ui/webui/resources/js:cr",
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.html b/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.html
index fa6fc13..20ae121 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.html
@@ -9,6 +9,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="../navigation_behavior.html">
+<link rel="import" href="../shared/bookmark_proxy.html">
 <link rel="import" href="../shared/chooser_shared_css.html">
 <link rel="import" href="../shared/i18n_setup.html">
 <link rel="import" href="../shared/step_indicator.html">
@@ -46,6 +47,13 @@
             url(chrome://welcome/email/outlook_1x.png) 1x,
             url(chrome://welcome/email/outlook_2x.png) 2x);
       }
+
+      iron-icon[icon='cr:chevron-right'] {
+        height: 20px;
+        margin-left: 6px;
+        margin-right: -10px;
+        width: 20px;
+      }
     </style>
 
     <template is="dom-repeat" items="[[emailList_]]">
@@ -62,13 +70,14 @@
 
     <div class="button-bar">
       <paper-button on-click="onNoThanksClicked_">
-        $i18n{noThanks}
+        $i18n{skip}
       </paper-button>
       <step-indicator model="[[indicatorModel]]"></step-indicator>
       <div on-click="onActionButtonClicked_">
         <paper-button class="action-button" on-click="onGetStartedClicked_"
             disabled="[[!selectedEmailProvider_]]">
-          $i18n{getStarted}
+          $i18n{next}
+          <iron-icon icon="cr:chevron-right"></iron-icon>
         </paper-button>
       </div>
     </div>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.js b/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.js
index 585fe72..369cc5d 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.js
@@ -48,7 +48,10 @@
   },
 
   /** @private {nux.NuxEmailProxy} */
-  browserProxy_: null,
+  emailProxy_: null,
+
+  /** @private {nux.BookmarkProxy} */
+  bookmarkProxy_: null,
 
   /** @override */
   attached: function() {
@@ -59,10 +62,12 @@
 
   /** @override */
   ready: function() {
-    this.browserProxy_ = nux.NuxEmailProxyImpl.getInstance();
-    this.browserProxy_.recordPageInitialized();
+    this.emailProxy_ = nux.NuxEmailProxyImpl.getInstance();
+    this.bookmarkProxy_ = nux.BookmarkProxyImpl.getInstance();
 
-    this.browserProxy_.getEmailList().then(list => {
+    this.emailProxy_.recordPageInitialized();
+
+    this.emailProxy_.getEmailList().then(list => {
       this.emailList_ = list;
     });
 
@@ -72,11 +77,11 @@
         return;
 
       if (this.selectedEmailProvider_) {
-        this.browserProxy_.recordProviderSelected(
+        this.emailProxy_.recordProviderSelected(
             this.selectedEmailProvider_.id, this.emailList_.length);
       }
 
-      this.browserProxy_.recordFinalize();
+      this.emailProxy_.recordFinalize();
     });
   },
 
@@ -91,7 +96,7 @@
     else
       this.selectedEmailProvider_ = e.model.item;
 
-    this.browserProxy_.recordClickedOption();
+    this.emailProxy_.recordClickedOption();
   },
 
   /**
@@ -110,7 +115,12 @@
     e.currentTarget.classList.add('keyboard-focused');
   },
 
-  /** @private */
+  /**
+   * Returns whether |item| is selected or not.
+   * @param {!nuxEmail.EmailProviderModel} item
+   * @return boolean
+   * @private
+   */
   getSelected_: function(item) {
     return this.selectedEmailProvider_ &&
         item.name === this.selectedEmailProvider_.name;
@@ -124,7 +134,7 @@
     let emailProvider = opt_emailProvider || this.selectedEmailProvider_;
 
     if (emailProvider && emailProvider.bookmarkId)
-      this.browserProxy_.removeBookmark(emailProvider.bookmarkId);
+      this.bookmarkProxy_.removeBookmark(emailProvider.bookmarkId);
   },
 
   /**
@@ -133,7 +143,7 @@
    * @private
    */
   onSelectedEmailProviderChange_: function(newEmail, prevEmail) {
-    if (!this.browserProxy_)
+    if (!this.emailProxy_ || !this.bookmarkProxy_)
       return;
 
     if (prevEmail) {
@@ -143,18 +153,19 @@
     }
 
     if (newEmail) {
-      this.browserProxy_.toggleBookmarkBar(true);
-      this.browserProxy_.addBookmark(
+      this.emailProxy_.cacheBookmarkIcon(newEmail.id);
+      this.bookmarkProxy_.toggleBookmarkBar(true);
+      this.bookmarkProxy_.addBookmark(
           {
             title: newEmail.name,
             url: newEmail.url,
             parentId: '1',
           },
-          newEmail.id, results => {
+          results => {
             this.selectedEmailProvider_.bookmarkId = results.id;
           });
     } else {
-      this.browserProxy_.toggleBookmarkBar(this.bookmarkBarWasShown_);
+      this.bookmarkProxy_.toggleBookmarkBar(this.bookmarkBarWasShown_);
     }
 
     // Announcements are mutually exclusive, so keeping separate.
@@ -171,17 +182,17 @@
   onNoThanksClicked_: function() {
     this.finalized_ = true;
     this.revertBookmark_();
-    this.browserProxy_.toggleBookmarkBar(this.bookmarkBarWasShown_);
-    this.browserProxy_.recordNoThanks();
+    this.bookmarkProxy_.toggleBookmarkBar(this.bookmarkBarWasShown_);
+    this.emailProxy_.recordNoThanks();
     welcome.navigateToNextStep();
   },
 
   /** @private */
   onGetStartedClicked_: function() {
     this.finalized_ = true;
-    this.browserProxy_.recordProviderSelected(
+    this.emailProxy_.recordProviderSelected(
         this.selectedEmailProvider_.id, this.emailList_.length);
-    this.browserProxy_.recordGetStarted();
+    this.emailProxy_.recordGetStarted();
     // TODO(scottchen): store the selected email provider URL somewhere to
     //     redirect to at the end.
     welcome.navigateToNextStep();
@@ -190,6 +201,6 @@
   /** @private */
   onActionButtonClicked_: function() {
     if (this.$$('.action-button').disabled)
-      this.browserProxy_.recordClickedDisabledButton();
+      this.emailProxy_.recordClickedDisabledButton();
   },
 });
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email.html b/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email.html
index 3cf42aa..a67e594 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email.html
@@ -12,40 +12,29 @@
 
       .email-logo {
         content: -webkit-image-set(
-          url(chrome://welcome/logo.png) 1x,
-          url(chrome://welcome/logo2x.png) 2x);
-        height: 48px;
+          url(../images/email_provider_1x.png) 1x,
+          url(../images/email_provider_2x.png) 2x);
+        height: 38px;
         margin: auto;
         margin-bottom: 16px;
-        width: 48px;
+        width: 42px;
       }
 
       h1 {
         color: var(--google-grey-900);
         font-size: 1.5rem;
         font-weight: 500;
-        line-height: 2.5rem;
-        margin: 0;
-      }
-
-      h2 {
-        color: var(--google-grey-900);
-        font-size: 1.125rem;
-        font-weight: unset;
-        line-height: 2rem;
         margin: 0;
         margin-bottom: 48px;
       }
 
       #emailChooser {
         color: var(--google-grey-900);
-        margin-bottom: 48px;
       }
     </style>
     <div class="email-ask">
       <div class="email-logo" alt=""></div>
-      <h1>$i18n{welcomeTitle}</h1>
-      <h2>$i18n{emailPrompt}</h2>
+      <h1>$i18n{emailProviderTitle}</h1>
       <email-chooser id="emailChooser" indicator-model="[[indicatorModel]]">
       </email-chooser>
     </div>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email_proxy.js b/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email_proxy.js
index fc21f9c..7f33c22 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email_proxy.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email_proxy.js
@@ -37,29 +37,14 @@
   const INTERACTION_METRIC_COUNT =
       Object.keys(NuxEmailProvidersInteractions).length;
 
-  /**
-   * @typedef {{
-   *    parentId: string,
-   *    title: string,
-   *    url: string,
-   * }}
-   */
-  let bookmarkData;
-
   /** @interface */
   class NuxEmailProxy {
-    /** @param {string} id ID provided by callback when bookmark was added. */
-    removeBookmark(id) {}
-
     /**
-     * @param {!bookmarkData} data
+     * Email provider IDs are local to the list of email providers, so their
+     * icon must be cached by the handler that provided the IDs.
      * @param {number} emailProviderId
-     * @param {!Function} callback
      */
-    addBookmark(data, emailProviderId, callback) {}
-
-    /** @param {boolean} show */
-    toggleBookmarkBar(show) {}
+    cacheBookmarkIcon(emailProviderId) {}
 
     /**
      * Returns a promise for an array of email providers.
@@ -104,22 +89,11 @@
     }
 
     /** @override */
-    removeBookmark(id) {
-      chrome.bookmarks.remove(id);
-    }
-
-    /** @override */
-    addBookmark(data, emailProviderId, callback) {
-      chrome.bookmarks.create(data, callback);
+    cacheBookmarkIcon(emailProviderId) {
       chrome.send('cacheEmailIcon', [emailProviderId]);
     }
 
     /** @override */
-    toggleBookmarkBar(show) {
-      chrome.send('toggleBookmarkBar', [show]);
-    }
-
-    /** @override */
     getEmailList() {
       return cr.sendWithPromise('getEmailList');
     }
@@ -193,4 +167,4 @@
     NuxEmailProxy: NuxEmailProxy,
     NuxEmailProxyImpl: NuxEmailProxyImpl,
   };
-});
\ No newline at end of file
+});
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/BUILD.gn b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/BUILD.gn
index 09492bb..45020388 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/BUILD.gn
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/BUILD.gn
@@ -13,6 +13,7 @@
 
 js_library("apps_chooser") {
   deps = [
+    "../shared:bookmark_proxy",
     "//ui/webui/resources/js:cr",
   ]
 }
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.html b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.html
index 3e7fd6d..b347d8b 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.html
@@ -7,6 +7,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://welcome/google_apps/nux_google_apps_proxy.html">
 <link rel="import" href="chrome://welcome/shared/chooser_shared_css.html">
+<link rel="import" href="chrome://welcome/shared/bookmark_proxy.html">
 
 <dom-module id="apps-chooser">
   <template>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.js b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.js
index 0395fc6..b1cfd67 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.js
@@ -10,6 +10,7 @@
  *   name: string,
  *   icon: string,
  *   url: string,
+ *   bookmarkId: ?string,
  *   selected: boolean,
  * }}
  */
@@ -25,6 +26,7 @@
 
 Polymer({
   is: 'apps-chooser',
+
   properties: {
     /**
      * @type {!Array<!nuxGoogleApps.AppItem>}
@@ -36,17 +38,35 @@
       type: Boolean,
       notify: true,
       value: true,
-    }
+    },
   },
 
+  /** @private {nux.NuxGoogleAppsProxy} */
+  appsProxy_: null,
+
+  /** @private {nux.BookmarkProxy} */
+  bookmarkProxy_: null,
+
   /** @override */
   ready() {
-    nux.NuxGoogleAppsProxyImpl.getInstance().getGoogleAppsList().then(list => {
-      this.appList_ = list.map(app => {
-        app.selected = true;  // default all items selected.
-        return app;
+    this.appsProxy_ = nux.NuxGoogleAppsProxyImpl.getInstance();
+    this.bookmarkProxy_ = nux.BookmarkProxyImpl.getInstance();
+  },
+
+  /** Called when bookmarks should be created for all selected apps. */
+  populateAllBookmarks() {
+    if (!this.appList_) {
+      this.appsProxy_.getGoogleAppsList().then(list => {
+        this.appList_ = list;
+        this.appList_.forEach(app => {
+          // Default select all items.
+          app.selected = true;
+          this.updateBookmark(app);
+          // Icons only need to be added to the cache once.
+          this.appsProxy_.cacheBookmarkIcon(app.id);
+        });
       });
-    });
+    }
   },
 
   /**
@@ -54,7 +74,32 @@
    * @return {!Array<boolean>}
    */
   getSelectedAppList() {
-    return this.appList_.map(a => a.selected);
+    if (this.appList_)
+      return this.appList_.map(a => a.selected);
+    else
+      return [];
+  },
+
+  /**
+   * @param {!nuxGoogleApps.AppItem} item
+   * @private
+   */
+  updateBookmark(item) {
+    if (item.selected && !item.bookmarkId) {
+      this.bookmarkProxy_.toggleBookmarkBar(true);
+      this.bookmarkProxy_.addBookmark(
+          {
+            title: item.name,
+            url: item.url,
+            parentId: '1',
+          },
+          result => {
+            item.bookmarkId = result.id;
+          });
+    } else if (!item.selected && item.bookmarkId) {
+      this.bookmarkProxy_.removeBookmark(item.bookmarkId);
+      item.bookmarkId = null;
+    }
   },
 
   /**
@@ -63,7 +108,9 @@
    * @private
    */
   onAppClick_: function(e) {
-    e.model.set('item.selected', !e.model.item.selected);
+    let item = e.model.item;
+    e.model.set('item.selected', !item.selected);
+    this.updateBookmark(item);
     this.hasAppsSelected = this.computeHasAppsSelected_();
   },
 
@@ -88,6 +135,6 @@
    * @private
    */
   computeHasAppsSelected_: function() {
-    return this.appList_.some(a => a.selected);
+    return this.appList_ && this.appList_.some(a => a.selected);
   },
 });
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html
index 6aab1f09..5652628 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html
@@ -40,13 +40,20 @@
       apps-chooser {
         color: var(--cr-primary-text-color);
         font-size: 14px;
-        margin-bottom: 48px;
+        margin-bottom: 64px;
       }
 
       .button-bar {
         display: flex;
         justify-content: space-between;
       }
+
+      iron-icon[icon='cr:chevron-right'] {
+        height: 20px;
+        margin-left: 6px;
+        margin-right: -10px;
+        width: 20px;
+      }
     </style>
 
     <div class="apps-ask">
@@ -58,11 +65,14 @@
 
       <div class="button-bar">
         <paper-button on-click="onNoThanksClicked_">
-          $i18n{noThanks}
+          $i18n{skip}
         </paper-button>
         <step-indicator model="[[indicatorModel]]"></step-indicator>
         <paper-button class="action-button" disabled$="[[!hasAppsSelected_]]"
-            on-click="onGetStartedClicked_">$i18n{getStarted}</paper-button>
+            on-click="onGetStartedClicked_">
+          $i18n{next}
+          <iron-icon icon="cr:chevron-right"></iron-icon>
+        </paper-button>
       </div>
     </div>
   </template>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.js b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.js
index 07087ea..5825bcad 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.js
@@ -5,6 +5,8 @@
 Polymer({
   is: 'nux-google-apps',
 
+  behaviors: [welcome.NavigationBehavior],
+
   properties: {
     /** @private */
     hasAppsSelected_: Boolean,
@@ -13,16 +15,26 @@
     indicatorModel: Object,
   },
 
+  /**
+   * Elements can override onRouteChange to handle route changes.
+   * Overrides function in behavior.
+   * @param {!welcome.Routes} route
+   * @param {number} step
+   */
+  onRouteChange: function(route, step) {
+    if (`step-${step}` == this.id)
+      this.$.appChooser.populateAllBookmarks();
+  },
+
   /** @private */
   onNoThanksClicked_: function() {
-    chrome.send('rejectGoogleApps');
+    // TODO(hcarmona): Add metrics.
     welcome.navigateToNextStep();
   },
 
   /** @private */
   onGetStartedClicked_: function() {
-    let selectedApps = this.$.appChooser.getSelectedAppList();
-    nux.NuxGoogleAppsProxyImpl.getInstance().addGoogleApps(selectedApps);
+    // TODO(hcarmona): Add metrics.
     welcome.navigateToNextStep();
   },
 });
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps_proxy.js b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps_proxy.js
index 4a0f3afb..8ecc1087 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps_proxy.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps_proxy.js
@@ -6,10 +6,11 @@
   /** @interface */
   class NuxGoogleAppsProxy {
     /**
-     * Adds the selected apps to the bookmark bar.
-     * @param {!Array<boolean>} selectedApps
+     * Google app IDs are local to the list of Google apps, so their icon must
+     * be cached by the handler that provided the IDs.
+     * @param {number} appId
      */
-    addGoogleApps(selectedApps) {}
+    cacheBookmarkIcon(appId) {}
 
     /**
      * Returns a promise for an array of Google apps.
@@ -21,8 +22,8 @@
   /** @implements {nux.NuxGoogleAppsProxy} */
   class NuxGoogleAppsProxyImpl {
     /** @override */
-    addGoogleApps(selectedApps) {
-      chrome.send('addGoogleApps', selectedApps);
+    cacheBookmarkIcon(appId) {
+      chrome.send('cacheGoogleAppIcon', [appId]);
     }
 
     /** @override */
@@ -37,4 +38,4 @@
     NuxGoogleAppsProxy: NuxGoogleAppsProxy,
     NuxGoogleAppsProxyImpl: NuxGoogleAppsProxyImpl,
   };
-});
\ No newline at end of file
+});
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/aol_1x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/aol_1x.png
index 845ae4a..566cf211 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/aol_1x.png
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/aol_1x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/aol_2x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/aol_2x.png
index 016b54ee..4a5472f2 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/aol_2x.png
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/aol_2x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/chrome_store_1x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/chrome_store_1x.png
index 4307c07..a299b45 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/chrome_store_1x.png
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/chrome_store_1x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/chrome_store_2x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/chrome_store_2x.png
index 91a950f..8ccaa1c 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/chrome_store_2x.png
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/chrome_store_2x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/email_provider_1x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/email_provider_1x.png
new file mode 100644
index 0000000..4ef0c29f
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/email_provider_1x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/email_provider_2x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/email_provider_2x.png
new file mode 100644
index 0000000..62b7614
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/email_provider_2x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/gmail_1x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/gmail_1x.png
index 0e354d6..91a96a26 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/gmail_1x.png
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/gmail_1x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/gmail_2x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/gmail_2x.png
index c7296e60..782c3bb 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/gmail_2x.png
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/gmail_2x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/google_apps_1x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/google_apps_1x.png
index a0135ea..adaff6c 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/google_apps_1x.png
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/google_apps_1x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/google_apps_2x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/google_apps_2x.png
index 50c4917..eeccb7d 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/google_apps_2x.png
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/google_apps_2x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/icloud_1x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/icloud_1x.png
index 1eccfc0..f3f7538 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/icloud_1x.png
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/icloud_1x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/icloud_2x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/icloud_2x.png
index 8304aec..bb36783 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/icloud_2x.png
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/icloud_2x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/news_1x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/news_1x.png
index 0f09d60..88aa491 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/news_1x.png
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/news_1x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/news_2x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/news_2x.png
index 9e8966f..a4c31bc 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/news_2x.png
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/news_2x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/outlook_1x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/outlook_1x.png
index 4ba9b40..0c9bed6 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/outlook_1x.png
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/outlook_1x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/outlook_2x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/outlook_2x.png
index 72221a3..3c402f80 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/outlook_2x.png
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/outlook_2x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/set_as_default_1x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/set_as_default_1x.png
new file mode 100644
index 0000000..c6d2637
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/set_as_default_1x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/set_as_default_2x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/set_as_default_2x.png
new file mode 100644
index 0000000..b82f6201
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/set_as_default_2x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/set_as_default_illustration_1x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/set_as_default_illustration_1x.png
new file mode 100644
index 0000000..2624306
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/set_as_default_illustration_1x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/set_as_default_illustration_2x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/set_as_default_illustration_2x.png
new file mode 100644
index 0000000..ff642c38
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/set_as_default_illustration_2x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/yahoo_1x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/yahoo_1x.png
index fb09671..3b0dcd4 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/yahoo_1x.png
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/yahoo_1x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/yahoo_2x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/yahoo_2x.png
index 00d96c1..6e333167 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/yahoo_2x.png
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/yahoo_2x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/youtube_1x.png b/chrome/browser/resources/welcome/onboarding_welcome/images/youtube_1x.png
index 4d407117..a99bee5b 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/youtube_1x.png
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/youtube_1x.png
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/navigation_behavior.js b/chrome/browser/resources/welcome/onboarding_welcome/navigation_behavior.js
index baf9bbbc..c05b43b 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/navigation_behavior.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/navigation_behavior.js
@@ -114,8 +114,12 @@
       assert(routeObservers.delete(this));
     },
 
-    /** Elements can override onRouteChange to handle route changes. */
-    onRouteChange: function() {},
+    /**
+     * Elements can override onRouteChange to handle route changes.
+     * @param {!welcome.Routes} route
+     * @param {number} step
+     */
+    onRouteChange: function(route, step) {},
   };
 
   return {
@@ -124,4 +128,4 @@
     navigateToNextStep: navigateToNextStep,
     Routes: Routes,
   };
-});
\ No newline at end of file
+});
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd b/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd
index 308c8522..8d79153 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd
+++ b/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd
@@ -20,6 +20,10 @@
                  file="email_interstitial.js"
                  type="chrome_html"
                  preprocess="true"/>
+      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_LANDING_VIEW_HTML"
+                 file="landing_view.html"
+                 type="chrome_html"
+                 preprocess="true"/>
       <structure name="IDR_WELCOME_ONBOARDING_WELCOME_LANDING_VIEW_JS"
                  file="landing_view.js"
                  type="chrome_html"
@@ -32,6 +36,47 @@
                  file="navigation_behavior.js"
                  type="chrome_html"
                  preprocess="true"/>
+      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_ACTION_LINK_STYLE_JS"
+                 file="shared\action_link_style.js"
+                 type="chrome_html" />
+      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_ACTION_LINK_STYLE_CSS_HTML"
+                 file="shared\action_link_style_css.html"
+                 type="chrome_html" />
+      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_BOOKMARK_PROXY_HTML"
+                 file="shared\bookmark_proxy.html"
+                 type="chrome_html" />
+      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_BOOKMARK_PROXY_JS"
+                 file="shared\bookmark_proxy.js"
+                 type="chrome_html" />
+      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_CHOOSER_SHARED_CSS"
+                 file="shared\chooser_shared_css.html"
+                 type="chrome_html" />
+      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_I18N_SETUP_HTML"
+                 file="shared\i18n_setup.html"
+                 type="chrome_html" />
+      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_ONBOARDING_BACKGROUND_HTML"
+                 file="shared\onboarding_background.html"
+                 type="chrome_html" />
+      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_ONBOARDING_BACKGROUND_JS"
+                 file="shared\onboarding_background.js"
+                 type="chrome_html" />
+      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_STEP_INDICATOR_HTML"
+                 file="shared\step_indicator.html"
+                 type="chrome_html" />
+      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_STEP_INDICATOR_JS"
+                 file="shared\step_indicator.js"
+                 type="chrome_html" />
+      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_SPLASH_PAGES_SHARED_CSS"
+                 file="shared\splash_pages_shared_css.html"
+                 type="chrome_html" />
+      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SIGNIN_VIEW_HTML"
+                 file="signin_view.html"
+                 type="chrome_html"
+                 preprocess="true"/>
+      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SIGNIN_VIEW_JS"
+                 file="signin_view.js"
+                 type="chrome_html"
+                 preprocess="true"/>
       <structure name="IDR_WELCOME_ONBOARDING_WELCOME_WELCOME_APP_HTML"
                  file="welcome_app.html"
                  type="chrome_html"
@@ -58,45 +103,6 @@
                  file="welcome.js"
                  type="chrome_html"
                  preprocess="true"/>
-      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_LANDING_VIEW_HTML"
-                 file="landing_view.html"
-                 type="chrome_html"
-                 preprocess="true"/>
-      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_CHOOSER_SHARED_CSS"
-                 file="shared\chooser_shared_css.html"
-                 type="chrome_html" />
-      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_SPLASH_PAGES_SHARED_CSS"
-                 file="shared\splash_pages_shared_css.html"
-                 type="chrome_html" />
-      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_I18N_SETUP_HTML"
-                 file="shared\i18n_setup.html"
-                 type="chrome_html" />
-      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SIGNIN_VIEW_HTML"
-                 file="signin_view.html"
-                 type="chrome_html"
-                 preprocess="true"/>
-      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SIGNIN_VIEW_JS"
-                 file="signin_view.js"
-                 type="chrome_html"
-                 preprocess="true"/>
-      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_ACTION_LINK_STYLE_JS"
-                 file="shared\action_link_style.js"
-                 type="chrome_html" />
-      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_ACTION_LINK_STYLE_CSS_HTML"
-                 file="shared\action_link_style_css.html"
-                 type="chrome_html" />
-      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_ONBOARDING_BACKGROUND_HTML"
-                 file="shared\onboarding_background.html"
-                 type="chrome_html" />
-      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_ONBOARDING_BACKGROUND_JS"
-                 file="shared\onboarding_background.js"
-                 type="chrome_html" />
-      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_STEP_INDICATOR_HTML"
-                 file="shared\step_indicator.html"
-                 type="chrome_html" />
-      <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_STEP_INDICATOR_JS"
-                 file="shared\step_indicator.js"
-                 type="chrome_html" />
 
        <!-- NUX Email-->
       <structure name="IDR_NUX_EMAIL_CHOOSER_HTML"
@@ -105,20 +111,26 @@
       <structure name="IDR_NUX_EMAIL_CHOOSER_JS"
                  file="email\email_chooser.js"
                  type="chrome_html" />
-      <structure name="IDR_NUX_EMAIL_HTML"
-                 file="email\nux_email.html"
-                 type="chrome_html" />
-      <structure name="IDR_NUX_EMAIL_JS"
-                 file="email\nux_email.js"
-                 type="chrome_html" />
       <structure name="IDR_NUX_EMAIL_PROXY_HTML"
                  file="email\nux_email_proxy.html"
                  type="chrome_html" />
       <structure name="IDR_NUX_EMAIL_PROXY_JS"
                  file="email\nux_email_proxy.js"
                  type="chrome_html" />
+      <structure name="IDR_NUX_EMAIL_HTML"
+                 file="email\nux_email.html"
+                 type="chrome_html" />
+      <structure name="IDR_NUX_EMAIL_JS"
+                 file="email\nux_email.js"
+                 type="chrome_html" />
 
        <!-- NUX Google apps-->
+      <structure name="IDR_NUX_GOOGLE_APPS_CHOOSER_HTML"
+                 file="google_apps\apps_chooser.html"
+                 type="chrome_html" />
+      <structure name="IDR_NUX_GOOGLE_APPS_CHOOSER_JS"
+                 file="google_apps\apps_chooser.js"
+                 type="chrome_html" />
       <structure name="IDR_NUX_GOOGLE_APPS_HTML"
                  file="google_apps\nux_google_apps.html"
                  type="chrome_html" />
@@ -131,12 +143,6 @@
       <structure name="IDR_NUX_GOOGLE_APPS_PROXY_JS"
                  file="google_apps\nux_google_apps_proxy.js"
                  type="chrome_html" />
-      <structure name="IDR_NUX_GOOGLE_APPS_CHOOSER_HTML"
-                 file="google_apps\apps_chooser.html"
-                 type="chrome_html" />
-      <structure name="IDR_NUX_GOOGLE_APPS_CHOOSER_JS"
-                 file="google_apps\apps_chooser.js"
-                 type="chrome_html" />
       <structure name="IDR_NUX_SET_AS_DEFAULT_HTML"
                  file="set_as_default\nux_set_as_default.html"
                  type="chrome_html" />
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default.html b/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default.html
index ee33639..ed2b4f5 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default.html
@@ -14,16 +14,23 @@
     <style include="paper-button-style">
       .container {
         text-align: center;
-        width: 800px;
       }
 
       .logo {
-        /* TODO(scottchen): placeholder; replace when asset is available. */
-        background: red;
+        content: -webkit-image-set(
+          url(../images/set_as_default_1x.png) 1x,
+          url(../images/set_as_default_2x.png) 2x);
         display: inline-block;
-        height: 32px;
+        height: 38px;
         margin-bottom: 16px;
-        width: 32px;
+        width: 42px;
+      }
+
+      .illustration {
+        content: -webkit-image-set(
+          url(../images/set_as_default_illustration_1x.png) 1x,
+          url(../images/set_as_default_illustration_2x.png) 2x);
+        width: 454px;
       }
 
       h1 {
@@ -39,17 +46,10 @@
         font-size: 1.25rem;
         font-weight: unset;
         line-height: 1.875rem;
-        margin: 0;
+        margin: auto;
         margin-bottom: 48px;
         margin-top: 16px;
-      }
-
-      img {
-        /* TODO(scottchen): placeholder; replace when asset is available. */
-        background: red;
-        display: inline-block;
-        height: 186px;
-        width: 454px;
+        max-width: 400px;
       }
 
       .button-bar {
@@ -62,8 +62,7 @@
       <div class="logo"></div>
       <h1>$i18n{setDefaultHeader}</h1>
       <h2>$i18n{setDefaultSubHeader}</h2>
-      <!-- TODO(scottchen): WIP behind feature flag, add src later. -->
-      <img>
+      <div class="illustration"></div>
       <div class="button-bar">
         <paper-button on-click="onDeclineClick_">
           $i18n{setDefaultSkip}
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default.js b/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default.js
index eddc2a4..9971a83 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default.js
@@ -38,13 +38,13 @@
 
   /**
    * Automatically navigate to the next onboarding step once default changed.
-   * @param {boolean} isDefault
+   * @param {!nux.DefaultBrowserInfo} status
    * @private
    */
-  onDefaultBrowserChange_: function(isDefault) {
+  onDefaultBrowserChange_: function(status) {
     // TODO(scottchen): Add UMA collection here.
 
-    if (isDefault)
+    if (status.isDefault)
       this.finished_();
   },
 
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default_proxy.js b/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default_proxy.js
index d4b50fe..e8b79ba8 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default_proxy.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default_proxy.js
@@ -5,12 +5,18 @@
 cr.define('nux', function() {
   /** @interface */
   class NuxSetAsDefaultProxy {
+    requestDefaultBrowserState() {}
     setAsDefault() {}
   }
 
   /** @implements {nux.NuxSetAsDefaultProxy} */
   class NuxSetAsDefaultProxyImpl {
     /** @override */
+    requestDefaultBrowserState() {
+      chrome.send('requestDefaultBrowserState');
+    }
+
+    /** @override */
     setAsDefault() {
       chrome.send('setAsDefaultBrowser');
     }
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/BUILD.gn b/chrome/browser/resources/welcome/onboarding_welcome/shared/BUILD.gn
index 14fc4efd..cbc5a669 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/shared/BUILD.gn
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/BUILD.gn
@@ -6,11 +6,22 @@
 
 js_type_check("closure_compile") {
   deps = [
+    ":bookmark_proxy",
     ":nux_types",
     ":step_indicator",
   ]
 }
 
+js_library("bookmark_proxy") {
+  deps = [
+    "//ui/webui/resources/js:cr",
+  ]
+  externs_list = [
+    "$externs_path/chrome_extensions.js",
+    "$externs_path/chrome_send.js",
+  ]
+}
+
 js_library("nux_types") {
   deps = [
     "//ui/webui/resources/js:cr",
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/bookmark_proxy.html b/chrome/browser/resources/welcome/onboarding_welcome/shared/bookmark_proxy.html
new file mode 100644
index 0000000..0336d1f
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/bookmark_proxy.html
@@ -0,0 +1,2 @@
+<link rel="import" href="chrome://resources/html/cr.html">
+<script src="bookmark_proxy.js"></script>
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/bookmark_proxy.js b/chrome/browser/resources/welcome/onboarding_welcome/shared/bookmark_proxy.js
new file mode 100644
index 0000000..bc64edfb
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/bookmark_proxy.js
@@ -0,0 +1,54 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('nux', function() {
+  /**
+   * @typedef {{
+   *    parentId: string,
+   *    title: string,
+   *    url: string,
+   * }}
+   */
+  let bookmarkData;
+
+  /** @interface */
+  class BookmarkProxy {
+    /**
+     * @param {!bookmarkData} data
+     * @param {!Function} callback
+     */
+    addBookmark(data, callback) {}
+
+    /** @param {string} id ID provided by callback when bookmark was added. */
+    removeBookmark(id) {}
+
+    /** @param {boolean} show */
+    toggleBookmarkBar(show) {}
+  }
+
+  /** @implements {nux.BookmarkProxy} */
+  class BookmarkProxyImpl {
+    /** @override */
+    addBookmark(data, callback) {
+      chrome.bookmarks.create(data, callback);
+    }
+
+    /** @override */
+    removeBookmark(id) {
+      chrome.bookmarks.remove(id);
+    }
+
+    /** @override */
+    toggleBookmarkBar(show) {
+      chrome.send('toggleBookmarkBar', [show]);
+    }
+  }
+
+  cr.addSingletonGetter(BookmarkProxyImpl);
+
+  return {
+    BookmarkProxy: BookmarkProxy,
+    BookmarkProxyImpl: BookmarkProxyImpl,
+  };
+});
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/nux_types.js b/chrome/browser/resources/welcome/onboarding_welcome/shared/nux_types.js
index 858852d..e429b90 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/shared/nux_types.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/nux_types.js
@@ -20,4 +20,16 @@
  *   active: number,
  * }}
  */
-nux.stepIndicatorModel;
\ No newline at end of file
+nux.stepIndicatorModel;
+
+/**
+ * TODO(scottchen): somehow reuse from
+ * chrome/browser/resources/settings/default_browser_page/default_browser_browser_proxy.js
+ * @typedef {{
+ *   canBeDefault: boolean,
+ *   isDefault: boolean,
+ *   isDisabledByPolicy: boolean,
+ *   isUnknownError: boolean,
+ * }};
+ */
+nux.DefaultBrowserInfo;
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/step_indicator.js b/chrome/browser/resources/welcome/onboarding_welcome/shared/step_indicator.js
index c214965..4a0bee3 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/shared/step_indicator.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/step_indicator.js
@@ -25,7 +25,8 @@
    * @private
    */
   computeDots_: function() {
-    return new Array(this.model.total);
+    // If total is 1, show nothing.
+    return new Array(this.model.total > 1 ? this.model.total : 0);
   },
 
   /**
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/welcome_app.html b/chrome/browser/resources/welcome/onboarding_welcome/welcome_app.html
index 1f7bbbb..69ba9e099 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/welcome_app.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/welcome_app.html
@@ -1,11 +1,14 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/cr_view_manager/cr_view_manager.html">
+<link rel="import" href="chrome://resources/html/cr.html">
+<link rel="import" href="chrome://resources/html/promise_resolver.html">
 <link rel="import" href="google_apps/nux_google_apps.html">
 <link rel="import" href="email/nux_email.html">
 <link rel="import" href="landing_view.html">
 <link rel="import" href="navigation_behavior.html">
 <link rel="import" href="set_as_default/nux_set_as_default.html">
+<link rel="import" href="set_as_default/nux_set_as_default_proxy.html">
 <link rel="import" href="signin_view.html">
 
 <dom-module id="welcome-app">
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/welcome_app.js b/chrome/browser/resources/welcome/onboarding_welcome/welcome_app.js
index 592642f..ca2680a7 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/welcome_app.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/welcome_app.js
@@ -24,6 +24,9 @@
   /** @private {?welcome.Routes} */
   currentRoute_: null,
 
+  /** @private {!PromiseResolver} */
+  defaultCheckPromise_: new PromiseResolver(),
+
   // TODO(scottchen): instead of dummy, get data from finch/load time data.
   /** @private {NuxOnboardingModules} */
   modules_: {
@@ -32,59 +35,92 @@
     'returning-user': ['nux-set-as-default'],
   },
 
+  /** @override */
+  ready: function() {
+    /** @param {!nux.DefaultBrowserInfo} status */
+    const defaultCheckCallback = status => {
+      if (status.isDefault || !status.canBeDefault) {
+        this.defaultCheckPromise_.resolve(false);
+      } else if (!status.isDisabledByPolicy && !status.isUnknownError) {
+        this.defaultCheckPromise_.resolve(true);
+      } else {  // Unknown error.
+        this.defaultCheckPromise_.resolve(false);
+      }
+
+      cr.removeWebUIListener(defaultCheckCallback);
+    };
+
+    cr.addWebUIListener('browser-default-state-changed', defaultCheckCallback);
+
+    // TODO(scottchen): convert the request to cr.sendWithPromise
+    // (see https://crbug.com/874520#c6).
+    nux.NuxSetAsDefaultProxyImpl.getInstance().requestDefaultBrowserState();
+  },
+
   /**
    * @param {welcome.Routes} route
    * @param {number} step
    * @private
    */
   onRouteChange: function(route, step) {
+    const setStep = () => {
+      // If the specified step doesn't exist, that means there are no more
+      // steps. In that case, replace this page with NTP.
+      if (!this.$$(`#step-${step}`)) {
+        welcome.WelcomeBrowserProxyImpl.getInstance().goToNewTabPage(
+            /* replace */ true);
+      } else {  // Otherwise, go to the chosen step of that route.
+        this.$.viewManager.switchView(`step-${step}`);
+      }
+    };
+
     // If the route changed, initialize the steps of modules for that route.
-    if (this.currentRoute_ != route) {
-      this.currentRoute_ = route;
-      this.initializeModules(this.modules_[route]);
+    if (this.currentRoute_ != route && route != welcome.Routes.LANDING) {
+      this.initializeModules(this.modules_[route]).then(setStep);
+    } else {
+      setStep();
     }
 
-    // If the specified step doesn't exist, that means there are no more steps.
-    // In that case, go to NTP.
-    if (!this.$$('#step-' + step))
-      welcome.WelcomeBrowserProxyImpl.getInstance().goToNewTabPage();
-    else  // Otherwise, go to the chosen step of that route.
-      this.$.viewManager.switchView('step-' + step);
+    this.currentRoute_ = route;
   },
 
   /** @param {!Array<string>} modules Array of valid DOM element names. */
   initializeModules: function(modules) {
-    assert(this.currentRoute_);  // this.currentRoute_ should be set by now.
-    if (this.currentRoute_ == welcome.Routes.LANDING)
-      return;
     assert(modules);  // modules should be defined if on a valid route.
 
-    // Remove all views except landing.
-    this.$.viewManager
-        .querySelectorAll('[slot="view"]:not([id="step-landing"])')
-        .forEach(element => {
-          element.remove();
-        });
+    // Wait until the default-browser state is known before anything
+    // initializes.
+    return this.defaultCheckPromise_.promise.then(canSetDefault => {
+      if (!canSetDefault)
+        modules = modules.filter(module => module != 'nux-set-as-default');
 
-    let indicatorElementCount = 0;
-    for (let i = 0; i < modules.length; i++) {
-      if (MODELS_NEEDING_INDICATOR.includes(modules[i]))
-        indicatorElementCount++;
-    }
+      // Remove all views except landing.
+      this.$.viewManager
+          .querySelectorAll('[slot="view"]:not([id="step-landing"])')
+          .forEach(element => {
+            element.remove();
+          });
 
-    let indicatorActiveCount = 0;
-    modules.forEach((elementTagName, index) => {
-      const element = document.createElement(elementTagName);
-      element.id = 'step-' + (index + 1);
-      element.setAttribute('slot', 'view');
-      this.$.viewManager.appendChild(element);
-
-      if (MODELS_NEEDING_INDICATOR.includes(elementTagName)) {
-        element.indicatorModel = {
-          total: indicatorElementCount,
-          active: indicatorActiveCount++,
-        };
+      let indicatorElementCount = 0;
+      for (let i = 0; i < modules.length; i++) {
+        if (MODELS_NEEDING_INDICATOR.includes(modules[i]))
+          indicatorElementCount++;
       }
+
+      let indicatorActiveCount = 0;
+      modules.forEach((elementTagName, index) => {
+        const element = document.createElement(elementTagName);
+        element.id = 'step-' + (index + 1);
+        element.setAttribute('slot', 'view');
+        this.$.viewManager.appendChild(element);
+
+        if (MODELS_NEEDING_INDICATOR.includes(elementTagName)) {
+          element.indicatorModel = {
+            total: indicatorElementCount,
+            active: indicatorActiveCount++,
+          };
+        }
+      });
     });
   },
 });
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/welcome_browser_proxy.js b/chrome/browser/resources/welcome/onboarding_welcome/welcome_browser_proxy.js
index 825ba8a8..dc7380c1 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/welcome_browser_proxy.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/welcome_browser_proxy.js
@@ -14,7 +14,8 @@
     /** @param {?string} redirectUrl the URL to go to, after signing in. */
     handleActivateSignIn(redirectUrl) {}
 
-    goToNewTabPage() {}
+    /** @param {boolean=} replace */
+    goToNewTabPage(replace) {}
 
     /** @param {string} url */
     goToURL(url) {}
@@ -28,8 +29,11 @@
     }
 
     /** @override */
-    goToNewTabPage() {
-      window.location.assign('chrome://newtab');
+    goToNewTabPage(replace) {
+      if (replace)
+        window.location.replace('chrome://newtab');
+      else
+        window.location.assign('chrome://newtab');
     }
 
     /** @override */
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
index 9ef3967..4f5b9ac 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
@@ -20,7 +20,7 @@
 #include "chrome/browser/safe_browsing/download_protection/download_protection_util.h"
 #include "chrome/browser/safe_browsing/download_protection/ppapi_download_request.h"
 #include "chrome/common/safe_browsing/archive_analyzer_results.h"
-#include "chrome/common/safe_browsing/download_protection_util.h"
+#include "chrome/common/safe_browsing/download_type_util.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
 #include "components/safe_browsing/common/utils.h"
@@ -316,7 +316,7 @@
     *reason = REASON_NOT_BINARY_FILE;
     return false;
   }
-  *type = download_protection_util::GetDownloadType(target_path);
+  *type = download_type_util::GetDownloadType(target_path);
   return true;
 }
 
diff --git a/chrome/browser/safe_browsing/download_protection/file_analyzer.cc b/chrome/browser/safe_browsing/download_protection/file_analyzer.cc
index 38a41b9..6a3d4206 100644
--- a/chrome/browser/safe_browsing/download_protection/file_analyzer.cc
+++ b/chrome/browser/safe_browsing/download_protection/file_analyzer.cc
@@ -10,7 +10,7 @@
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "chrome/common/safe_browsing/archive_analyzer_results.h"
-#include "chrome/common/safe_browsing/download_protection_util.h"
+#include "chrome/common/safe_browsing/download_type_util.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
 #include "components/safe_browsing/features.h"
 #include "content/public/browser/browser_thread.h"
@@ -90,7 +90,7 @@
   tmp_path_ = tmp_path;
   callback_ = std::move(callback);
 
-  results_.type = download_protection_util::GetDownloadType(target_path_);
+  results_.type = download_type_util::GetDownloadType(target_path_);
 
   if (target_path_.MatchesExtension(FILE_PATH_LITERAL(".zip"))) {
     StartExtractZipFeatures();
@@ -147,7 +147,7 @@
 }
 
 void FileAnalyzer::OnFileAnalysisFinished(FileAnalyzer::Results results) {
-  results.type = download_protection_util::GetDownloadType(target_path_);
+  results.type = download_type_util::GetDownloadType(target_path_);
   results.archive_is_valid = ArchiveValid::UNSET;
   std::move(callback_).Run(results);
 }
diff --git a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
index dc6a6e0..f961c9f 100644
--- a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
@@ -23,7 +23,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/common/safe_browsing/download_protection_util.h"
+#include "chrome/common/safe_browsing/download_type_util.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
 #include "components/history/core/browser/download_constants.h"
 #include "components/history/core/browser/history_service.h"
@@ -92,7 +92,7 @@
     return true;
   }
 
-  // The default return value of download_protection_util::GetDownloadType is
+  // The default return value of download_type_util::GetDownloadType is
   // ClientDownloadRequest::WIN_EXECUTABLE.
   return download_type == ClientDownloadRequest::WIN_EXECUTABLE;
 }
@@ -106,7 +106,7 @@
   return (policies->IsCheckedBinaryFile(row.target_path) &&
           !policies->IsArchiveFile(row.target_path) &&
           IsBinaryDownloadForCurrentOS(
-              download_protection_util::GetDownloadType(row.target_path)));
+              download_type_util::GetDownloadType(row.target_path)));
 }
 
 // Returns true if a download represented by a DownloadRow is not a binary file.
@@ -209,7 +209,7 @@
   download_request->set_file_basename(
       download.target_path.BaseName().AsUTF8Unsafe());
   download_request->set_download_type(
-      download_protection_util::GetDownloadType(download.target_path));
+      download_type_util::GetDownloadType(download.target_path));
   std::string pref_locale = g_browser_process->local_state()->GetString(
       language::prefs::kApplicationLocale);
   language::ConvertToActualUILocale(&pref_locale);
diff --git a/chrome/browser/sessions/session_service.cc b/chrome/browser/sessions/session_service.cc
index 0cdcf11..4513d5a 100644
--- a/chrome/browser/sessions/session_service.cc
+++ b/chrome/browser/sessions/session_service.cc
@@ -48,6 +48,10 @@
 #include "content/public/browser/session_storage_namespace.h"
 #include "content/public/browser/web_contents.h"
 
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/crostini/crostini_util.h"
+#endif
+
 #if defined(OS_MACOSX)
 #include "chrome/browser/app_controller_mac.h"
 #endif
@@ -851,6 +855,13 @@
 bool SessionService::ShouldTrackBrowser(Browser* browser) const {
   if (browser->profile() != profile())
     return false;
+#if defined(OS_CHROMEOS)
+  // Do not track Crostini apps because they will be dead upon restoring the
+  // browser session since VMs do not automatically restart on session restore.
+  if (crostini::CrostiniAppIdFromAppName(browser->app_name())) {
+    return false;
+  }
+#endif
   // Never track app popup windows that do not have a trusted source (i.e.
   // popup windows spawned by an app). If this logic changes, be sure to also
   // change SessionRestoreImpl::CreateRestoredBrowser().
diff --git a/chrome/browser/sessions/session_service_unittest.cc b/chrome/browser/sessions/session_service_unittest.cc
index b42ae679..ac40565 100644
--- a/chrome/browser/sessions/session_service_unittest.cc
+++ b/chrome/browser/sessions/session_service_unittest.cc
@@ -588,6 +588,24 @@
   helper_.AssertTabEquals(window2_id, tab2_id, 0, 0, 1, *tab);
   helper_.AssertNavigationEquals(nav2, tab->navigations[0]);
 }
+
+// Don't track Crostini apps. Only applicable on Chrome OS.
+TEST_F(SessionServiceTest, IgnoreCrostiniApps) {
+  SessionID window2_id = SessionID::NewUnique();
+  ASSERT_NE(window2_id, window_id);
+
+  service()->SetWindowType(window2_id, Browser::TYPE_POPUP,
+                           SessionService::TYPE_NORMAL);
+  service()->SetWindowBounds(window2_id, window_bounds, ui::SHOW_STATE_NORMAL);
+  service()->SetWindowAppName(window2_id, "_crostini_fakeappid");
+
+  std::vector<std::unique_ptr<sessions::SessionWindow>> windows;
+  ReadWindows(&windows, nullptr);
+
+  for (auto& window : windows)
+    ASSERT_NE(window2_id, window->window_id);
+}
+
 #endif  // defined (OS_CHROMEOS)
 
 // Tests pruning from the front.
diff --git a/chrome/browser/speech/extension_api/tts_engine_extension_api.cc b/chrome/browser/speech/extension_api/tts_engine_extension_api.cc
index 94de6bd..4ce75dbe 100644
--- a/chrome/browser/speech/extension_api/tts_engine_extension_api.cc
+++ b/chrome/browser/speech/extension_api/tts_engine_extension_api.cc
@@ -152,7 +152,10 @@
 
   // Fall back on the extension manifest.
   auto* manifest_voices = extensions::TtsVoices::GetTtsVoices(extension);
-  return std::make_unique<std::vector<extensions::TtsVoice>>(*manifest_voices);
+  if (manifest_voices)
+    return std::make_unique<std::vector<extensions::TtsVoice>>(
+        *manifest_voices);
+  return std::make_unique<std::vector<extensions::TtsVoice>>();
 }
 
 }  // namespace
diff --git a/chrome/browser/ssl/ssl_client_certificate_selector.h b/chrome/browser/ssl/ssl_client_certificate_selector.h
index 4e1f2430..26dc9f1 100644
--- a/chrome/browser/ssl/ssl_client_certificate_selector.h
+++ b/chrome/browser/ssl/ssl_client_certificate_selector.h
@@ -33,14 +33,6 @@
     net::ClientCertIdentityList client_certs,
     std::unique_ptr<content::ClientCertificateDelegate> delegate);
 
-#if defined(OS_MACOSX)
-void ShowSSLClientCertificateSelectorCocoa(
-    content::WebContents* contents,
-    net::SSLCertRequestInfo* cert_request_info,
-    net::ClientCertIdentityList client_certs,
-    std::unique_ptr<content::ClientCertificateDelegate> delegate);
-#endif
-
 }  // namespace chrome
 
 #endif  // CHROME_BROWSER_SSL_SSL_CLIENT_CERTIFICATE_SELECTOR_H_
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 7aeed97a..7fb4e6ad 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1307,6 +1307,8 @@
       "ash/chrome_accessibility_delegate.h",
       "ash/chrome_browser_main_extra_parts_ash.cc",
       "ash/chrome_browser_main_extra_parts_ash.h",
+      "ash/chrome_keyboard_bounds_observer.cc",
+      "ash/chrome_keyboard_bounds_observer.h",
       "ash/chrome_keyboard_controller_client.cc",
       "ash/chrome_keyboard_controller_client.h",
       "ash/chrome_keyboard_ui.cc",
@@ -1497,8 +1499,6 @@
       "webui/chromeos/internet_config_dialog.h",
       "webui/chromeos/internet_detail_dialog.cc",
       "webui/chromeos/internet_detail_dialog.h",
-      "webui/chromeos/keyboard_overlay_ui.cc",
-      "webui/chromeos/keyboard_overlay_ui.h",
       "webui/chromeos/login/active_directory_password_change_screen_handler.cc",
       "webui/chromeos/login/active_directory_password_change_screen_handler.h",
       "webui/chromeos/login/app_downloading_screen_handler.cc",
@@ -1705,7 +1705,6 @@
     ]
     deps += [
       "//ash",
-      "//ash:ash_with_content",
       "//ash/components/shortcut_viewer:lib",
       "//ash/components/shortcut_viewer/public/mojom",
       "//ash/public/cpp",
@@ -1867,6 +1866,7 @@
     ]
 
     deps += [
+      "//chrome/browser/ui/webui/welcome/nux:bookmark_handler",
       "//chrome/browser/ui/webui/welcome/nux:constants",
       "//chrome/browser/ui/webui/welcome/nux:email_feature",
       "//chrome/browser/ui/webui/welcome/nux:google_apps_feature",
@@ -1966,19 +1966,6 @@
       "cocoa/confirm_quit.h",
       "cocoa/confirm_quit_panel_controller.h",
       "cocoa/confirm_quit_panel_controller.mm",
-      "cocoa/constrained_window/constrained_window_control_utils.h",
-      "cocoa/constrained_window/constrained_window_control_utils.mm",
-      "cocoa/constrained_window/constrained_window_custom_sheet.h",
-      "cocoa/constrained_window/constrained_window_custom_sheet.mm",
-      "cocoa/constrained_window/constrained_window_mac.h",
-      "cocoa/constrained_window/constrained_window_mac.mm",
-      "cocoa/constrained_window/constrained_window_sheet.h",
-      "cocoa/constrained_window/constrained_window_sheet_controller.h",
-      "cocoa/constrained_window/constrained_window_sheet_controller.mm",
-      "cocoa/constrained_window/constrained_window_sheet_info.h",
-      "cocoa/constrained_window/constrained_window_sheet_info.mm",
-      "cocoa/constrained_window/constrained_window_web_dialog_sheet.h",
-      "cocoa/constrained_window/constrained_window_web_dialog_sheet.mm",
       "cocoa/dock_icon.h",
       "cocoa/dock_icon.mm",
       "cocoa/first_run_dialog.h",
@@ -2049,10 +2036,6 @@
       "cocoa/share_menu_controller.mm",
       "cocoa/simple_message_box_cocoa.h",
       "cocoa/simple_message_box_cocoa.mm",
-      "cocoa/single_web_contents_dialog_manager_cocoa.h",
-      "cocoa/single_web_contents_dialog_manager_cocoa.mm",
-      "cocoa/ssl_client_certificate_selector_cocoa.h",
-      "cocoa/ssl_client_certificate_selector_cocoa.mm",
       "cocoa/status_icons/status_icon_mac.h",
       "cocoa/status_icons/status_icon_mac.mm",
       "cocoa/status_icons/status_tray_mac.h",
@@ -2075,8 +2058,6 @@
       "cocoa/touchbar/web_textfield_touch_bar_controller.mm",
       "cocoa/ui_localizer.h",
       "cocoa/ui_localizer.mm",
-      "cocoa/web_contents_modal_dialog_host_cocoa.h",
-      "cocoa/web_contents_modal_dialog_host_cocoa.mm",
       "cocoa/window_size_autosaver.h",
       "cocoa/window_size_autosaver.mm",
       "views/apps/chrome_app_window_client_views_mac.mm",
@@ -2739,8 +2720,6 @@
       "views/session_crashed_bubble_view.h",
       "views/simple_message_box_views.cc",
       "views/simple_message_box_views.h",
-      "views/ssl_client_certificate_selector.cc",
-      "views/ssl_client_certificate_selector.h",
       "views/status_bubble_views.cc",
       "views/status_bubble_views.h",
       "views/subtle_notification_view.cc",
@@ -2905,11 +2884,15 @@
         "views/chrome_views_delegate_mac.cc",
         "views/policy/enterprise_startup_dialog_mac_util.h",
         "views/policy/enterprise_startup_dialog_mac_util.mm",
+        "views/ssl_client_certificate_selector_mac.h",
+        "views/ssl_client_certificate_selector_mac.mm",
       ]
     } else {
       sources += [
         "views/create_application_shortcut_view.cc",
         "views/create_application_shortcut_view.h",
+        "views/ssl_client_certificate_selector.cc",
+        "views/ssl_client_certificate_selector.h",
 
         # TODO(ellyjones): Mus is not supported on Mac (there is no ui::Window
         # apart from aura::Window, which is also not supported).
diff --git a/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.cc b/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.cc
index 04c9f79..9923ff3 100644
--- a/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.cc
+++ b/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.cc
@@ -19,7 +19,9 @@
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/common/app.mojom.h"
 #include "components/crx_file/id_util.h"
-#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/canvas_image_source.h"
+#include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/paint_vector_icon.h"
 
 namespace {
@@ -28,6 +30,49 @@
 constexpr char kPlayAppPrefix[] = "play://";
 // Badge icon color, #000 at 54% opacity.
 constexpr SkColor kBadgeColor = SkColorSetARGB(0x8A, 0x00, 0x00, 0x00);
+// Size of the vector icon inside the badge.
+constexpr int kBadgeIconSize = 12;
+// Padding around the circular background of the badge.
+constexpr int kBadgePadding = 1;
+
+// The background image source for badge.
+class BadgeBackgroundImageSource : public gfx::CanvasImageSource {
+ public:
+  explicit BadgeBackgroundImageSource(int size, float padding)
+      : CanvasImageSource(gfx::Size(size, size), false), padding_(padding) {}
+  ~BadgeBackgroundImageSource() override = default;
+
+ private:
+  // gfx::CanvasImageSource overrides:
+  void Draw(gfx::Canvas* canvas) override {
+    cc::PaintFlags flags;
+    flags.setColor(SK_ColorWHITE);
+    flags.setAntiAlias(true);
+    flags.setStyle(cc::PaintFlags::kFill_Style);
+    const float origin = static_cast<float>(size().width()) / 2;
+    canvas->DrawCircle(gfx::PointF(origin, origin), origin - padding_, flags);
+  }
+
+  const float padding_;
+
+  DISALLOW_COPY_AND_ASSIGN(BadgeBackgroundImageSource);
+};
+
+gfx::ImageSkia CreateBadgeIcon(const gfx::VectorIcon& vector_icon,
+                               int badge_size,
+                               int padding,
+                               int icon_size,
+                               SkColor icon_color) {
+  gfx::ImageSkia background(
+      std::make_unique<BadgeBackgroundImageSource>(badge_size, padding),
+      gfx::Size(badge_size, badge_size));
+
+  gfx::ImageSkia foreground(
+      gfx::CreateVectorIcon(vector_icon, icon_size, icon_color));
+
+  return gfx::ImageSkiaOperations::CreateSuperimposedImage(background,
+                                                           foreground);
+}
 
 bool LaunchIntent(const std::string& intent_uri, int64_t display_id) {
   auto* arc_service_manager = arc::ArcServiceManager::Get();
@@ -67,10 +112,10 @@
   set_id(kPlayAppPrefix +
          crx_file::id_util::GenerateId(install_intent_uri().value()));
   SetDisplayType(ash::SearchResultDisplayType::kTile);
-  SetBadgeIcon(gfx::CreateVectorIcon(
+  SetBadgeIcon(CreateBadgeIcon(
       is_instant_app() ? kIcBadgeInstantIcon : kIcBadgePlayIcon,
       app_list::AppListConfig::instance().search_tile_badge_icon_dimension(),
-      kBadgeColor));
+      kBadgePadding, kBadgeIconSize, kBadgeColor));
   SetFormattedPrice(base::UTF8ToUTF16(formatted_price().value()));
   SetRating(review_score());
   SetResultType(is_instant_app() ? ash::SearchResultType::kInstantApp
diff --git a/chrome/browser/ui/app_list/search/search_util.h b/chrome/browser/ui/app_list/search/search_util.h
index 815b3eca..860567fe 100644
--- a/chrome/browser/ui/app_list/search/search_util.h
+++ b/chrome/browser/ui/app_list/search/search_util.h
@@ -20,8 +20,8 @@
   SEARCH_WEBSTORE_SEARCH_RESULT_DEPRECATED,
   // A result that opens a people search (Deprecated).
   SEARCH_PEOPLE_SEARCH_RESULT_DEPRECATED,
-  // A result that opens a suggestion.
-  SUGGESTIONS_SEARCH_RESULT,
+  // A result that opens a suggestion (Deprecated).
+  SUGGESTIONS_SEARCH_RESULT_DEPRECATED,
   // A result that is provided by the custom launcher search provider.
   LAUNCHER_SEARCH_PROVIDER_RESULT,
   // A result that is an uninstalled app from a Play Store app search.
diff --git a/chrome/browser/ui/ash/DEPS b/chrome/browser/ui/ash/DEPS
index 1c7d932..6dd98c4 100644
--- a/chrome/browser/ui/ash/DEPS
+++ b/chrome/browser/ui/ash/DEPS
@@ -28,9 +28,12 @@
   "chrome_browser_main_extra_parts_ash\.cc": [
     "+ash/shell.h",
   ],
+  # Only for !features::IsUsingWindowService().
+  "chrome_keyboard_bounds_observer\.cc": [
+    "+ash/root_window_controller.h",
+  ],
   # https://crbug.com/843332
   "chrome_keyboard_ui\.cc": [
-    "+ash/root_window_controller.h",
     "+ash/shell.h",
   ],
   # https://crbug.com/124222
diff --git a/chrome/browser/ui/ash/accelerator_commands_browsertest.cc b/chrome/browser/ui/ash/accelerator_commands_browsertest.cc
index dff3850..9b34584 100644
--- a/chrome/browser/ui/ash/accelerator_commands_browsertest.cc
+++ b/chrome/browser/ui/ash/accelerator_commands_browsertest.cc
@@ -4,9 +4,8 @@
 
 #include "ash/accelerators/accelerator_commands.h"
 
-#include "ash/accelerators/accelerator_commands.h"
-#include "ash/shell.h"
-#include "ash/wm/window_state.h"
+#include "ash/public/interfaces/constants.mojom.h"
+#include "ash/public/interfaces/shell_test_api.mojom.h"
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "build/build_config.h"
@@ -15,13 +14,20 @@
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/views/frame/browser_frame.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/common/service_manager_connection.h"
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/app_window/native_app_window.h"
+#include "services/service_manager/public/cpp/connector.h"
 #include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
+#include "ui/aura/test/mus/change_completion_waiter.h"
 #include "ui/aura/window.h"
+#include "ui/aura/window_observer.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 
@@ -43,14 +49,22 @@
   DISALLOW_COPY_AND_ASSIGN(MaximizableWidgetDelegate);
 };
 
-// Returns true if |window_state|'s window is in immersive fullscreen. Infer
-// whether the window is in immersive fullscreen based on whether the shelf is
-// hidden when the window is fullscreen. (This is not quite right because the
-// shelf is hidden if a window is in both immersive fullscreen and tab
-// fullscreen.)
-bool IsInImmersiveFullscreen(ash::wm::WindowState* window_state) {
-  return window_state->IsFullscreen() &&
-         !window_state->GetHideShelfWhenFullscreen();
+// Tells Ash to toggle fullscreen as if the user had pressed the hardware
+// fullscreen key.
+void ToggleFullscreen() {
+  ash::mojom::ShellTestApiPtr shell_test_api;
+  content::ServiceManagerConnection::GetForProcess()
+      ->GetConnector()
+      ->BindInterface(ash::mojom::kServiceName, &shell_test_api);
+  ash::mojom::ShellTestApiAsyncWaiter waiter(shell_test_api.get());
+  waiter.ToggleFullscreen();
+  aura::test::WaitForAllChangesToComplete();
+}
+
+bool IsInImmersive(aura::Window* window) {
+  aura::Window* toplevel =
+      features::IsUsingWindowService() ? window->GetRootWindow() : window;
+  return toplevel->GetProperty(aura::client::kImmersiveFullscreenKey);
 }
 
 }  // namespace
@@ -63,20 +77,22 @@
       : initial_show_state_(GetParam()) {}
   virtual ~AcceleratorCommandsFullscreenBrowserTest() {}
 
-  // Sets |window_state|'s show state to |initial_show_state_|.
-  void SetToInitialShowState(ash::wm::WindowState* window_state) {
+  // Sets |widget|'s show state to |initial_show_state_|.
+  void SetToInitialShowState(views::Widget* widget) {
     if (initial_show_state_ == ui::SHOW_STATE_MAXIMIZED)
-      window_state->Maximize();
+      widget->Maximize();
     else
-      window_state->Restore();
+      widget->Restore();
   }
 
-  // Returns true if |window_state|'s show state is |initial_show_state_|.
-  bool IsInitialShowState(const ash::wm::WindowState* window_state) const {
-    if (initial_show_state_ == ui::SHOW_STATE_MAXIMIZED)
-      return window_state->IsMaximized();
-    else
-      return window_state->IsNormalStateType();
+  // Returns true if |widget|'s show state is |initial_show_state_|.
+  bool IsInitialShowState(const views::Widget* widget) const {
+    if (initial_show_state_ == ui::SHOW_STATE_MAXIMIZED) {
+      return widget->IsMaximized();
+    } else {
+      return !widget->IsMaximized() && !widget->IsFullscreen() &&
+             !widget->IsMinimized();
+    }
   }
 
  private:
@@ -88,29 +104,33 @@
 // Test that toggling window fullscreen works properly.
 IN_PROC_BROWSER_TEST_P(AcceleratorCommandsFullscreenBrowserTest,
                        ToggleFullscreen) {
-  ASSERT_TRUE(ash::Shell::HasInstance()) << "No Instance";
-
   // 1) Browser windows.
+  aura::Window* window = browser()->window()->GetNativeWindow();
+  views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window);
   ASSERT_TRUE(browser()->is_type_tabbed());
-  ash::wm::WindowState* window_state =
-      ash::wm::GetWindowState(browser()->window()->GetNativeWindow());
-  ASSERT_TRUE(window_state->IsActive());
-  SetToInitialShowState(window_state);
-  EXPECT_TRUE(IsInitialShowState(window_state));
+  ASSERT_TRUE(widget->IsActive());
+  SetToInitialShowState(widget);
+  EXPECT_TRUE(IsInitialShowState(widget));
 
-  ash::accelerators::ToggleFullscreen();
-  EXPECT_TRUE(window_state->IsFullscreen());
-  EXPECT_TRUE(IsInImmersiveFullscreen(window_state));
+  // Wait for Ash to become aware of active widget.
+  aura::test::WaitForAllChangesToComplete();
 
-  ash::accelerators::ToggleFullscreen();
-  EXPECT_TRUE(IsInitialShowState(window_state));
+  ToggleFullscreen();
+  EXPECT_TRUE(IsInImmersive(window));
+
+  ToggleFullscreen();
+  EXPECT_FALSE(IsInImmersive(window));
+  EXPECT_TRUE(IsInitialShowState(widget));
 
   // 2) ToggleFullscreen() should have no effect on windows which cannot be
   // maximized.
-  window_state->window()->SetProperty(aura::client::kResizeBehaviorKey,
-                                      ws::mojom::kResizeBehaviorNone);
-  ash::accelerators::ToggleFullscreen();
-  EXPECT_TRUE(IsInitialShowState(window_state));
+  aura::Window* toplevel =
+      features::IsUsingWindowService() ? window->GetRootWindow() : window;
+  toplevel->SetProperty(aura::client::kResizeBehaviorKey,
+                        ws::mojom::kResizeBehaviorNone);
+  aura::test::WaitForAllChangesToComplete();
+  ToggleFullscreen();
+  EXPECT_TRUE(IsInitialShowState(widget));
 
   // 3) Hosted apps.
   Browser::CreateParams browser_create_params(
@@ -121,18 +141,21 @@
   Browser* app_host_browser = new Browser(browser_create_params);
   ASSERT_TRUE(app_host_browser->is_app());
   AddBlankTabAndShow(app_host_browser);
-  window_state =
-      ash::wm::GetWindowState(app_host_browser->window()->GetNativeWindow());
-  ASSERT_TRUE(window_state->IsActive());
-  SetToInitialShowState(window_state);
-  EXPECT_TRUE(IsInitialShowState(window_state));
+  window = app_host_browser->window()->GetNativeWindow();
+  widget = views::Widget::GetWidgetForNativeWindow(window);
+  ASSERT_TRUE(widget->IsActive());
+  SetToInitialShowState(widget);
+  EXPECT_TRUE(IsInitialShowState(widget));
 
-  ash::accelerators::ToggleFullscreen();
-  EXPECT_TRUE(window_state->IsFullscreen());
-  EXPECT_TRUE(IsInImmersiveFullscreen(window_state));
+  // Wait for Ash to become aware of active widget.
+  aura::test::WaitForAllChangesToComplete();
 
-  ash::accelerators::ToggleFullscreen();
-  EXPECT_TRUE(IsInitialShowState(window_state));
+  ToggleFullscreen();
+  EXPECT_TRUE(IsInImmersive(window));
+
+  ToggleFullscreen();
+  EXPECT_FALSE(IsInImmersive(window));
+  EXPECT_TRUE(IsInitialShowState(widget));
 
   // 4) Popup browser windows.
   browser_create_params =
@@ -141,40 +164,49 @@
   ASSERT_TRUE(popup_browser->is_type_popup());
   ASSERT_FALSE(popup_browser->is_app());
   AddBlankTabAndShow(popup_browser);
-  window_state =
-      ash::wm::GetWindowState(popup_browser->window()->GetNativeWindow());
-  ASSERT_TRUE(window_state->IsActive());
-  SetToInitialShowState(window_state);
-  EXPECT_TRUE(IsInitialShowState(window_state));
+  window = popup_browser->window()->GetNativeWindow();
+  widget = views::Widget::GetWidgetForNativeWindow(window);
+  ASSERT_TRUE(widget->IsActive());
+  SetToInitialShowState(widget);
+  EXPECT_TRUE(IsInitialShowState(widget));
 
-  ash::accelerators::ToggleFullscreen();
-  EXPECT_TRUE(window_state->IsFullscreen());
-  EXPECT_TRUE(IsInImmersiveFullscreen(window_state));
+  // Wait for Ash to become aware of active widget.
+  aura::test::WaitForAllChangesToComplete();
 
-  ash::accelerators::ToggleFullscreen();
-  EXPECT_TRUE(IsInitialShowState(window_state));
+  ToggleFullscreen();
+  EXPECT_TRUE(IsInImmersive(window));
+
+  ToggleFullscreen();
+  EXPECT_FALSE(IsInImmersive(window));
+  EXPECT_TRUE(IsInitialShowState(widget));
 
   // 5) Miscellaneous windows (e.g. task manager).
   views::Widget::InitParams params;
   params.delegate = new MaximizableWidgetDelegate();
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
-  std::unique_ptr<views::Widget> widget(new views::Widget);
+  views::Widget misc_widget;
+  widget = &misc_widget;
   widget->Init(params);
   widget->Show();
+  window = widget->GetNativeWindow();
 
-  window_state = ash::wm::GetWindowState(widget->GetNativeWindow());
-  ASSERT_TRUE(window_state->IsActive());
-  SetToInitialShowState(window_state);
-  EXPECT_TRUE(IsInitialShowState(window_state));
+  ASSERT_TRUE(widget->IsActive());
+  SetToInitialShowState(widget);
+  EXPECT_TRUE(IsInitialShowState(widget));
 
-  ash::accelerators::ToggleFullscreen();
-  EXPECT_TRUE(window_state->IsFullscreen());
-  EXPECT_TRUE(IsInImmersiveFullscreen(window_state));
+  // Wait for Ash to become aware of active widget.
+  aura::test::WaitForAllChangesToComplete();
+
+  ToggleFullscreen();
+  EXPECT_TRUE(IsInImmersive(window));
+
+  ToggleFullscreen();
+  EXPECT_FALSE(IsInImmersive(window));
+  EXPECT_TRUE(IsInitialShowState(widget));
 
   // TODO(pkotwicz|oshima): Make toggling fullscreen restore the window to its
   // show state prior to entering fullscreen.
-  ash::accelerators::ToggleFullscreen();
-  EXPECT_FALSE(window_state->IsFullscreen());
+  EXPECT_FALSE(widget->IsFullscreen());
 }
 
 INSTANTIATE_TEST_CASE_P(InitiallyRestored,
@@ -217,7 +249,6 @@
 // Test the behavior of platform apps when ToggleFullscreen() is called.
 IN_PROC_BROWSER_TEST_P(AcceleratorCommandsPlatformAppFullscreenBrowserTest,
                        ToggleFullscreen) {
-  ASSERT_TRUE(ash::Shell::HasInstance()) << "No Instance";
   const extensions::Extension* extension =
       LoadAndLaunchPlatformApp("minimal", "Launched");
 
@@ -235,13 +266,15 @@
     ASSERT_TRUE(app_window->GetBaseWindow()->IsActive());
     EXPECT_TRUE(IsInitialShowState(app_window));
 
-    ash::accelerators::ToggleFullscreen();
-    EXPECT_TRUE(native_app_window->IsFullscreen());
-    ash::wm::WindowState* window_state =
-        ash::wm::GetWindowState(native_app_window->GetNativeWindow());
-    EXPECT_TRUE(IsInImmersiveFullscreen(window_state));
+    // Wait for Ash to become aware of active widget.
+    aura::test::WaitForAllChangesToComplete();
 
-    ash::accelerators::ToggleFullscreen();
+    ToggleFullscreen();
+    EXPECT_TRUE(native_app_window->IsFullscreen());
+    EXPECT_TRUE(IsInImmersive(native_app_window->GetNativeWindow()));
+
+    ToggleFullscreen();
+    EXPECT_FALSE(native_app_window->IsFullscreen());
     EXPECT_TRUE(IsInitialShowState(app_window));
 
     CloseAppWindow(app_window);
@@ -260,13 +293,15 @@
     SetToInitialShowState(app_window);
     EXPECT_TRUE(IsInitialShowState(app_window));
 
-    ash::accelerators::ToggleFullscreen();
-    EXPECT_TRUE(native_app_window->IsFullscreen());
-    ash::wm::WindowState* window_state =
-        ash::wm::GetWindowState(native_app_window->GetNativeWindow());
-    EXPECT_FALSE(IsInImmersiveFullscreen(window_state));
+    // Wait for Ash to become aware of active widget.
+    aura::test::WaitForAllChangesToComplete();
 
-    ash::accelerators::ToggleFullscreen();
+    ToggleFullscreen();
+    EXPECT_TRUE(native_app_window->IsFullscreen());
+    EXPECT_FALSE(IsInImmersive(native_app_window->GetNativeWindow()));
+
+    ToggleFullscreen();
+    EXPECT_FALSE(native_app_window->IsFullscreen());
     EXPECT_TRUE(IsInitialShowState(app_window));
 
     CloseAppWindow(app_window);
diff --git a/chrome/browser/ui/ash/chrome_keyboard_bounds_observer.cc b/chrome/browser/ui/ash/chrome_keyboard_bounds_observer.cc
new file mode 100644
index 0000000..1e9ef9c
--- /dev/null
+++ b/chrome/browser/ui/ash/chrome_keyboard_bounds_observer.cc
@@ -0,0 +1,149 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/window.h"
+
+#include "ash/public/cpp/shell_window_ids.h"
+#include "ash/root_window_controller.h"
+#include "chrome/browser/ui/ash/chrome_keyboard_bounds_observer.h"
+#include "chrome/browser/ui/ash/chrome_keyboard_controller_client.h"
+#include "content/public/browser/render_widget_host.h"
+#include "content/public/browser/render_widget_host_iterator.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "ui/base/ui_base_features.h"
+#include "ui/gfx/geometry/insets.h"
+
+ChromeKeyboardBoundsObserver::ChromeKeyboardBoundsObserver(
+    aura::Window* keyboard_window)
+    : keyboard_window_(keyboard_window) {
+  DCHECK(keyboard_window_);
+}
+
+ChromeKeyboardBoundsObserver::~ChromeKeyboardBoundsObserver() {
+  RemoveAllObservedWindows();
+}
+
+void ChromeKeyboardBoundsObserver::UpdateOccludedBounds(
+    const gfx::Rect& occluded_bounds) {
+  occluded_bounds_ = occluded_bounds;
+
+  // Adjust the height of the viewport for visible windows on the primary
+  // display. TODO(kevers): Add EnvObserver to properly initialize insets if a
+  // window is created while the keyboard is visible.
+  std::unique_ptr<content::RenderWidgetHostIterator> widgets(
+      content::RenderWidgetHost::GetRenderWidgetHosts());
+  while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
+    content::RenderWidgetHostView* view = widget->GetView();
+    // Can be null, e.g. if the RenderWidget is being destroyed or
+    // the render process crashed.
+    if (!view)
+      continue;
+
+    if (occluded_bounds.IsEmpty()) {
+      view->SetInsets(gfx::Insets());
+      continue;
+    }
+
+    aura::Window* window = view->GetNativeView();
+    // Added while we determine if RenderWidgetHostViewChildFrame can be
+    // changed to always return a non-null value: https://crbug.com/644726.
+    // If we cannot guarantee a non-null value, then this may need to stay.
+    if (!window)
+      continue;
+
+    if (!ShouldWindowOverscroll(window))
+      continue;
+
+    gfx::Rect view_bounds = view->GetViewBounds();
+    gfx::Rect intersect = gfx::IntersectRects(view_bounds, occluded_bounds);
+    int overlap = intersect.height();
+    if (overlap > 0 && overlap < view_bounds.height())
+      view->SetInsets(gfx::Insets(0, 0, overlap, 0));
+    else
+      view->SetInsets(gfx::Insets());
+    AddObservedWindow(window);
+  }
+
+  if (occluded_bounds.IsEmpty())
+    RemoveAllObservedWindows();
+}
+
+void ChromeKeyboardBoundsObserver::AddObservedWindow(aura::Window* window) {
+  // Only observe top level windows.
+  window = window->GetToplevelWindow();
+  if (!window->HasObserver(this)) {
+    window->AddObserver(this);
+    observed_windows_.insert(window);
+  }
+}
+
+void ChromeKeyboardBoundsObserver::RemoveAllObservedWindows() {
+  for (aura::Window* window : observed_windows_)
+    window->RemoveObserver(this);
+  observed_windows_.clear();
+}
+
+void ChromeKeyboardBoundsObserver::OnWindowBoundsChanged(
+    aura::Window* window,
+    const gfx::Rect& old_bounds,
+    const gfx::Rect& new_bounds,
+    ui::PropertyChangeReason reason) {
+  UpdateInsetsForWindow(window);
+}
+
+void ChromeKeyboardBoundsObserver::OnWindowDestroyed(aura::Window* window) {
+  if (window->HasObserver(this))
+    window->RemoveObserver(this);
+  observed_windows_.erase(window);
+}
+
+void ChromeKeyboardBoundsObserver::UpdateInsetsForWindow(aura::Window* window) {
+  if (!ShouldWindowOverscroll(window))
+    return;
+
+  std::unique_ptr<content::RenderWidgetHostIterator> widgets(
+      content::RenderWidgetHost::GetRenderWidgetHosts());
+  while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
+    content::RenderWidgetHostView* view = widget->GetView();
+    if (view && window->Contains(view->GetNativeView())) {
+      gfx::Rect view_bounds = view->GetViewBounds();
+      gfx::Rect intersect = gfx::IntersectRects(view_bounds, occluded_bounds_);
+      int overlap = ShouldEnableInsets(window) ? intersect.height() : 0;
+      if (overlap > 0 && overlap < view_bounds.height())
+        view->SetInsets(gfx::Insets(0, 0, overlap, 0));
+      else
+        view->SetInsets(gfx::Insets());
+    }
+  }
+}
+
+bool ChromeKeyboardBoundsObserver::ShouldWindowOverscroll(
+    aura::Window* window) {
+  // When the WS is running, all available windows are Chrome windows and
+  // should overscroll.
+  if (::features::IsUsingWindowService())
+    return true;
+
+  aura::Window* root_window = window->GetRootWindow();
+  if (!root_window)
+    return true;
+
+  if (root_window != keyboard_window_->GetRootWindow())
+    return false;
+
+  // Shell IME window container contains virtual keyboard windows and IME
+  // windows (IME windows are created by the chrome.app.window.create API).
+  // They should never be overscrolled.
+  ash::RootWindowController* root_window_controller =
+      ash::RootWindowController::ForWindow(root_window);
+  return !root_window_controller
+              ->GetContainer(ash::kShellWindowId_ImeWindowParentContainer)
+              ->Contains(window);
+}
+
+bool ChromeKeyboardBoundsObserver::ShouldEnableInsets(aura::Window* window) {
+  return keyboard_window_->IsVisible() &&
+         keyboard_window_->GetRootWindow() == window->GetRootWindow() &&
+         ChromeKeyboardControllerClient::Get()->IsKeyboardOverscrollEnabled();
+}
diff --git a/chrome/browser/ui/ash/chrome_keyboard_bounds_observer.h b/chrome/browser/ui/ash/chrome_keyboard_bounds_observer.h
new file mode 100644
index 0000000..72c1d14
--- /dev/null
+++ b/chrome/browser/ui/ash/chrome_keyboard_bounds_observer.h
@@ -0,0 +1,47 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_ASH_CHROME_KEYBOARD_BOUNDS_OBSERVER_H_
+#define CHROME_BROWSER_UI_ASH_CHROME_KEYBOARD_BOUNDS_OBSERVER_H_
+
+#include <set>
+
+#include "base/macros.h"
+#include "ui/aura/window_observer.h"
+#include "ui/gfx/geometry/rect.h"
+
+// Class responsible for updating insets for windows overlapping the virtual
+// keyboard.
+class ChromeKeyboardBoundsObserver : public aura::WindowObserver {
+ public:
+  explicit ChromeKeyboardBoundsObserver(aura::Window* keyboard_window);
+  ~ChromeKeyboardBoundsObserver() override;
+
+  // Provides the bounds occluded by the keyboard any time they change.
+  // (i.e. by the KeyboardController through KeyboardUI::InitInsets).
+  void UpdateOccludedBounds(const gfx::Rect& occluded_bounds);
+
+ private:
+  void AddObservedWindow(aura::Window* window);
+  void RemoveAllObservedWindows();
+
+  // aura::WindowObserver
+  void OnWindowBoundsChanged(aura::Window* window,
+                             const gfx::Rect& old_bounds,
+                             const gfx::Rect& new_bounds,
+                             ui::PropertyChangeReason reason) override;
+  void OnWindowDestroyed(aura::Window* window) override;
+
+  void UpdateInsetsForWindow(aura::Window* window);
+  bool ShouldWindowOverscroll(aura::Window* window);
+  bool ShouldEnableInsets(aura::Window* window);
+
+  aura::Window* const keyboard_window_;
+  std::set<aura::Window*> observed_windows_;
+  gfx::Rect occluded_bounds_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChromeKeyboardBoundsObserver);
+};
+
+#endif  // CHROME_BROWSER_UI_ASH_CHROME_KEYBOARD_BOUNDS_OBSERVER_H_
diff --git a/chrome/browser/ui/ash/chrome_keyboard_controller_client.cc b/chrome/browser/ui/ash/chrome_keyboard_controller_client.cc
index d194fd6..2ef6b1d 100644
--- a/chrome/browser/ui/ash/chrome_keyboard_controller_client.cc
+++ b/chrome/browser/ui/ash/chrome_keyboard_controller_client.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "ash/public/interfaces/constants.mojom.h"
+#include "base/command_line.h"
 #include "base/values.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -16,8 +17,13 @@
 #include "extensions/common/api/virtual_keyboard_private.h"
 #include "extensions/common/extension_messages.h"
 #include "services/service_manager/public/cpp/connector.h"
+#include "ui/base/ime/chromeos/input_method_manager.h"
+#include "ui/base/ime/ime_bridge.h"
+#include "ui/base/ime/input_method.h"
+#include "ui/base/ime/text_input_client.h"
 #include "ui/gfx/geometry/rect.h"
-#include "ui/keyboard/keyboard_controller.h"
+#include "ui/keyboard/keyboard_resource_util.h"
+#include "ui/keyboard/keyboard_switches.h"
 
 namespace virtual_keyboard_private = extensions::api::virtual_keyboard_private;
 
@@ -45,6 +51,9 @@
   CHECK(!g_chrome_keyboard_controller_client);
   g_chrome_keyboard_controller_client = this;
 
+  if (!connector)
+    return;  // May be null in tests.
+
   connector->BindInterface(ash::mojom::kServiceName, &keyboard_controller_ptr_);
 
   // Request the configuration. This will be queued until the service is ready.
@@ -97,6 +106,39 @@
   keyboard_controller_ptr_->ReloadKeyboard();
 }
 
+bool ChromeKeyboardControllerClient::IsKeyboardOverscrollEnabled() {
+  DCHECK(cached_keyboard_config_);
+  if (cached_keyboard_config_->overscroll_behavior !=
+      keyboard::mojom::KeyboardOverscrollBehavior::kDefault) {
+    return cached_keyboard_config_->overscroll_behavior ==
+           keyboard::mojom::KeyboardOverscrollBehavior::kEnabled;
+  }
+  return !base::CommandLine::ForCurrentProcess()->HasSwitch(
+      keyboard::switches::kDisableVirtualKeyboardOverscroll);
+}
+
+GURL ChromeKeyboardControllerClient::GetVirtualKeyboardUrl() {
+  if (!virtual_keyboard_url_for_test_.is_empty())
+    return virtual_keyboard_url_for_test_;
+
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          keyboard::switches::kDisableInputView)) {
+    return GURL(keyboard::kKeyboardURL);
+  }
+
+  chromeos::input_method::InputMethodManager* ime_manager =
+      chromeos::input_method::InputMethodManager::Get();
+  if (!ime_manager || !ime_manager->GetActiveIMEState())
+    return GURL(keyboard::kKeyboardURL);
+
+  const GURL& input_view_url =
+      ime_manager->GetActiveIMEState()->GetInputViewUrl();
+  if (!input_view_url.is_valid())
+    return GURL(keyboard::kKeyboardURL);
+
+  return input_view_url;
+}
+
 void ChromeKeyboardControllerClient::FlushForTesting() {
   keyboard_controller_ptr_.FlushForTesting();
 }
diff --git a/chrome/browser/ui/ash/chrome_keyboard_controller_client.h b/chrome/browser/ui/ash/chrome_keyboard_controller_client.h
index 82706287..71e9e01e 100644
--- a/chrome/browser/ui/ash/chrome_keyboard_controller_client.h
+++ b/chrome/browser/ui/ash/chrome_keyboard_controller_client.h
@@ -11,6 +11,7 @@
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
+#include "url/gurl.h"
 
 class Profile;
 
@@ -61,11 +62,20 @@
   // Reloads the virtual keyboard if enabled.
   void ReloadKeyboard();
 
-  void FlushForTesting();
+  // Returns true if overscroll is enabled by the config or command line.
+  bool IsKeyboardOverscrollEnabled();
+
+  // Returns the URL to use for the virtual keyboard.
+  GURL GetVirtualKeyboardUrl();
 
   bool is_keyboard_enabled() { return is_keyboard_enabled_; }
 
+  void FlushForTesting();
+
   void set_profile_for_test(Profile* profile) { profile_for_test_ = profile; }
+  void set_virtual_keyboard_url_for_test(const GURL& url) {
+    virtual_keyboard_url_for_test_ = url;
+  }
 
  private:
   void OnGetInitialKeyboardConfig(keyboard::mojom::KeyboardConfigPtr config);
@@ -93,6 +103,7 @@
   base::ObserverList<Observer> observers_;
 
   Profile* profile_for_test_ = nullptr;
+  GURL virtual_keyboard_url_for_test_;
 
   base::WeakPtrFactory<ChromeKeyboardControllerClient> weak_ptr_factory_{this};
 
diff --git a/chrome/browser/ui/ash/chrome_keyboard_ui.cc b/chrome/browser/ui/ash/chrome_keyboard_ui.cc
index ad518928..e858070b 100644
--- a/chrome/browser/ui/ash/chrome_keyboard_ui.cc
+++ b/chrome/browser/ui/ash/chrome_keyboard_ui.cc
@@ -4,162 +4,53 @@
 
 #include "chrome/browser/ui/ash/chrome_keyboard_ui.h"
 
-#include <set>
 #include <string>
 #include <utility>
 
-#include "ash/public/cpp/shell_window_ids.h"
-#include "ash/root_window_controller.h"
 #include "ash/shell.h"
-#include "base/command_line.h"
-#include "base/feature_list.h"
 #include "base/macros.h"
 #include "base/no_destructor.h"
+#include "chrome/browser/ui/ash/chrome_keyboard_bounds_observer.h"
 #include "chrome/browser/ui/ash/chrome_keyboard_controller_client.h"
 #include "chrome/browser/ui/ash/chrome_keyboard_web_contents.h"
 #include "content/public/browser/browser_context.h"
-#include "content/public/browser/render_widget_host.h"
-#include "content/public/browser/render_widget_host_iterator.h"
-#include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
-#include "ui/accessibility/ax_enums.mojom.h"
-#include "ui/accessibility/platform/aura_window_properties.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
-#include "ui/base/ime/chromeos/input_method_manager.h"
 #include "ui/base/ime/ime_bridge.h"
-#include "ui/base/ime/input_method.h"
-#include "ui/base/ime/text_input_client.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor_extra/shadow.h"
-#include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/keyboard/keyboard_controller.h"
-#include "ui/keyboard/keyboard_resource_util.h"
-#include "ui/keyboard/keyboard_switches.h"
 #include "ui/wm/core/shadow_types.h"
 
 namespace {
 
 const int kShadowElevationVirtualKeyboard = 2;
 
-GURL& GetOverrideVirtualKeyboardUrl() {
-  static base::NoDestructor<GURL> url;
-  return *url;
-}
-
 }  // namespace
 
-class ChromeKeyboardUI::WindowBoundsChangeObserver
-    : public aura::WindowObserver {
- public:
-  explicit WindowBoundsChangeObserver(ChromeKeyboardUI* ui) : ui_(ui) {}
-  ~WindowBoundsChangeObserver() override {}
-
-  void AddObservedWindow(aura::Window* window) {
-    if (!window->HasObserver(this)) {
-      window->AddObserver(this);
-      observed_windows_.insert(window);
-    }
-  }
-
-  void RemoveAllObservedWindows() {
-    for (aura::Window* window : observed_windows_)
-      window->RemoveObserver(this);
-    observed_windows_.clear();
-  }
-
- private:
-  void OnWindowBoundsChanged(aura::Window* window,
-                             const gfx::Rect& old_bounds,
-                             const gfx::Rect& new_bounds,
-                             ui::PropertyChangeReason reason) override {
-    ui_->UpdateInsetsForWindow(window);
-  }
-
-  void OnWindowDestroyed(aura::Window* window) override {
-    if (window->HasObserver(this))
-      window->RemoveObserver(this);
-    observed_windows_.erase(window);
-  }
-
-  ChromeKeyboardUI* const ui_;
-  std::set<aura::Window*> observed_windows_;
-
-  DISALLOW_COPY_AND_ASSIGN(WindowBoundsChangeObserver);
-};
-
-void ChromeKeyboardUI::TestApi::SetOverrideVirtualKeyboardUrl(const GURL& url) {
-  GURL& override_url = GetOverrideVirtualKeyboardUrl();
-  override_url = url;
-}
-
 ChromeKeyboardUI::ChromeKeyboardUI(content::BrowserContext* context)
-    : browser_context_(context),
-      window_bounds_observer_(
-          std::make_unique<WindowBoundsChangeObserver>(this)) {}
+    : browser_context_(context) {}
 
 ChromeKeyboardUI::~ChromeKeyboardUI() {
   ResetInsets();
   DCHECK(!keyboard_controller());
 }
 
-void ChromeKeyboardUI::UpdateInsetsForWindow(aura::Window* window) {
-  if (!GetKeyboardWindow() || !ShouldWindowOverscroll(window))
-    return;
-
-  std::unique_ptr<content::RenderWidgetHostIterator> widgets(
-      content::RenderWidgetHost::GetRenderWidgetHosts());
-  while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
-    content::RenderWidgetHostView* view = widget->GetView();
-    if (view && window->Contains(view->GetNativeView())) {
-      gfx::Rect view_bounds = view->GetViewBounds();
-      gfx::Rect intersect = gfx::IntersectRects(
-          view_bounds, keyboard_controller()->GetWorkspaceOccludedBounds());
-      int overlap = ShouldEnableInsets(window) ? intersect.height() : 0;
-      if (overlap > 0 && overlap < view_bounds.height())
-        view->SetInsets(gfx::Insets(0, 0, overlap, 0));
-      else
-        view->SetInsets(gfx::Insets());
-    }
-  }
-}
+// keyboard::KeyboardUI:
 
 aura::Window* ChromeKeyboardUI::LoadKeyboardWindow(LoadCallback callback) {
   DCHECK(!keyboard_contents_);
 
-  GURL keyboard_url = GetVirtualKeyboardUrl();
   keyboard_contents_ = std::make_unique<ChromeKeyboardWebContents>(
-      browser_context_, keyboard_url, std::move(callback));
+      browser_context_,
+      ChromeKeyboardControllerClient::Get()->GetVirtualKeyboardUrl(),
+      std::move(callback));
 
   aura::Window* keyboard_window =
       keyboard_contents_->web_contents()->GetNativeView();
-
   keyboard_window->AddObserver(this);
-  keyboard_window->set_owned_by_parent(false);
-
-  content::RenderWidgetHostView* view =
-      keyboard_contents_->web_contents()->GetMainFrame()->GetView();
-
-  // Only use transparent background when fullscreen handwriting or the new UI
-  // is enabled. The old UI sometimes reloads itself, which will cause the
-  // keyboard to be see-through.
-  // TODO(https://crbug.com/840731): Find a permanent fix for this on the
-  // keyboard extension side.
-  if (base::FeatureList::IsEnabled(
-          features::kEnableFullscreenHandwritingVirtualKeyboard) ||
-      base::FeatureList::IsEnabled(features::kEnableVirtualKeyboardMdUi)) {
-    view->SetBackgroundColor(SK_ColorTRANSPARENT);
-    view->GetNativeView()->SetTransparent(true);
-  }
-
-  // By default, layers in WebContents are clipped at the window bounds,
-  // but this causes the shadows to be clipped too, so clipping needs to
-  // be disabled.
-  keyboard_window->layer()->SetMasksToBounds(false);
-
-  keyboard_window->SetProperty(ui::kAXRoleOverride, ax::mojom::Role::kKeyboard);
 
   return keyboard_window;
 }
@@ -184,56 +75,22 @@
 
 void ChromeKeyboardUI::ReloadKeyboardIfNeeded() {
   DCHECK(keyboard_contents_);
-  keyboard_contents_->SetKeyboardUrl(GetVirtualKeyboardUrl());
+  keyboard_contents_->SetKeyboardUrl(
+      ChromeKeyboardControllerClient::Get()->GetVirtualKeyboardUrl());
 }
 
 void ChromeKeyboardUI::InitInsets(const gfx::Rect& new_bounds) {
-  // Adjust the height of the viewport for visible windows on the primary
-  // display.
-  // TODO(kevers): Add EnvObserver to properly initialize insets if a
-  // window is created while the keyboard is visible.
-  std::unique_ptr<content::RenderWidgetHostIterator> widgets(
-      content::RenderWidgetHost::GetRenderWidgetHosts());
-  while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
-    content::RenderWidgetHostView* view = widget->GetView();
-    // Can be null, e.g. if the RenderWidget is being destroyed or
-    // the render process crashed.
-    if (!view)
-      continue;
-
-    aura::Window* window = view->GetNativeView();
-    // Added while we determine if RenderWidgetHostViewChildFrame can be
-    // changed to always return a non-null value: https://crbug.com/644726.
-    // If we cannot guarantee a non-null value, then this may need to stay.
-    if (!window)
-      continue;
-
-    if (!ShouldWindowOverscroll(window))
-      continue;
-
-    gfx::Rect view_bounds = view->GetViewBounds();
-    gfx::Rect intersect = gfx::IntersectRects(view_bounds, new_bounds);
-    int overlap = intersect.height();
-    if (overlap > 0 && overlap < view_bounds.height())
-      view->SetInsets(gfx::Insets(0, 0, overlap, 0));
-    else
-      view->SetInsets(gfx::Insets());
-    AddBoundsChangedObserver(window);
-  }
+  DCHECK(keyboard_contents_);
+  keyboard_contents_->window_bounds_observer()->UpdateOccludedBounds(
+      new_bounds);
 }
 
 void ChromeKeyboardUI::ResetInsets() {
-  const gfx::Insets insets;
-  std::unique_ptr<content::RenderWidgetHostIterator> widgets(
-      content::RenderWidgetHost::GetRenderWidgetHosts());
-  while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
-    content::RenderWidgetHostView* view = widget->GetView();
-    if (view)
-      view->SetInsets(insets);
-  }
-  window_bounds_observer_->RemoveAllObservedWindows();
+  InitInsets(gfx::Rect());
 }
 
+// aura::WindowObserver:
+
 void ChromeKeyboardUI::OnWindowBoundsChanged(aura::Window* window,
                                              const gfx::Rect& old_bounds,
                                              const gfx::Rect& new_bounds,
@@ -250,64 +107,7 @@
   SetShadowAroundKeyboard();
 }
 
-GURL ChromeKeyboardUI::GetVirtualKeyboardUrl() {
-  const GURL& override_url = GetOverrideVirtualKeyboardUrl();
-  if (!override_url.is_empty())
-    return override_url;
-
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          keyboard::switches::kDisableInputView)) {
-    return GURL(keyboard::kKeyboardURL);
-  }
-
-  chromeos::input_method::InputMethodManager* ime_manager =
-      chromeos::input_method::InputMethodManager::Get();
-  if (!ime_manager || !ime_manager->GetActiveIMEState())
-    return GURL(keyboard::kKeyboardURL);
-
-  const GURL& input_view_url =
-      ime_manager->GetActiveIMEState()->GetInputViewUrl();
-  if (!input_view_url.is_valid())
-    return GURL(keyboard::kKeyboardURL);
-
-  return input_view_url;
-}
-
-bool ChromeKeyboardUI::ShouldEnableInsets(aura::Window* window) {
-  aura::Window* contents_window = GetKeyboardWindow();
-  return (contents_window->GetRootWindow() == window->GetRootWindow() &&
-          keyboard::KeyboardController::Get()->IsKeyboardOverscrollEnabled() &&
-          contents_window->IsVisible() &&
-          keyboard_controller()->IsKeyboardVisible());
-}
-
-bool ChromeKeyboardUI::ShouldWindowOverscroll(aura::Window* window) {
-  aura::Window* root_window = window->GetRootWindow();
-  if (!root_window)
-    return true;
-
-  aura::Window* keyboard_window = GetKeyboardWindow();
-  if (!keyboard_window)
-    return false;
-
-  if (root_window != keyboard_window->GetRootWindow())
-    return false;
-
-  ash::RootWindowController* root_window_controller =
-      ash::RootWindowController::ForWindow(root_window);
-  // Shell ime window container contains virtual keyboard windows and IME
-  // windows (IME windows are created by chrome.app.window.create api). They
-  // should never be overscrolled.
-  return !root_window_controller
-              ->GetContainer(ash::kShellWindowId_ImeWindowParentContainer)
-              ->Contains(window);
-}
-
-void ChromeKeyboardUI::AddBoundsChangedObserver(aura::Window* window) {
-  aura::Window* target_window = window ? window->GetToplevelWindow() : nullptr;
-  if (target_window)
-    window_bounds_observer_->AddObservedWindow(target_window);
-}
+// private methods:
 
 void ChromeKeyboardUI::SetShadowAroundKeyboard() {
   aura::Window* contents_window = GetKeyboardWindow();
diff --git a/chrome/browser/ui/ash/chrome_keyboard_ui.h b/chrome/browser/ui/ash/chrome_keyboard_ui.h
index 00d95b3..ee939582 100644
--- a/chrome/browser/ui/ash/chrome_keyboard_ui.h
+++ b/chrome/browser/ui/ash/chrome_keyboard_ui.h
@@ -12,46 +12,28 @@
 #include "ui/keyboard/keyboard_ui.h"
 
 class ChromeKeyboardWebContents;
-class GURL;
 
 namespace aura {
 class Window;
 }
 
-namespace gfx {
-class Rect;
-}
-
 namespace content {
 class BrowserContext;
 }
 
 namespace ui {
-class InputMethod;
 class Shadow;
-}  // namespace ui
+}
 
 // Subclass of KeyboardUI. It is used by KeyboardController to get
 // access to the virtual keyboard window and setup Chrome extension functions.
 class ChromeKeyboardUI : public keyboard::KeyboardUI,
                          public aura::WindowObserver {
  public:
-  class TestApi {
-   public:
-    // Use an empty |url| to clear the override.
-    static void SetOverrideVirtualKeyboardUrl(const GURL& url);
-
-   private:
-    DISALLOW_IMPLICIT_CONSTRUCTORS(TestApi);
-  };
-
   explicit ChromeKeyboardUI(content::BrowserContext* context);
   ~ChromeKeyboardUI() override;
 
-  // Called when a window being observed changes bounds, to update its insets.
-  void UpdateInsetsForWindow(aura::Window* window);
-
-  // Overridden from KeyboardUI:
+  // keyboard::KeyboardUI:
   aura::Window* LoadKeyboardWindow(LoadCallback callback) override;
   aura::Window* GetKeyboardWindow() const override;
   ui::InputMethod* GetInputMethod() override;
@@ -59,7 +41,7 @@
   void InitInsets(const gfx::Rect& new_bounds) override;
   void ResetInsets() override;
 
-  // aura::WindowObserver overrides:
+  // aura::WindowObserver:
   void OnWindowBoundsChanged(aura::Window* window,
                              const gfx::Rect& old_bounds,
                              const gfx::Rect& new_bounds,
@@ -69,25 +51,6 @@
                              aura::Window* parent) override;
 
  private:
-  class WindowBoundsChangeObserver;
-
-  // Gets the virtual keyboard URL, either the default keyboard URL or the IME
-  // override URL.
-  GURL GetVirtualKeyboardUrl();
-
-  // Determines whether a particular window should have insets for overscroll.
-  bool ShouldEnableInsets(aura::Window* window);
-
-  // Whether this window should do an overscroll to avoid occlusion by the
-  // virtual keyboard. IME windows and virtual keyboard windows should always
-  // avoid overscroll.
-  bool ShouldWindowOverscroll(aura::Window* window);
-
-  // Adds an observer for tracking changes to a window size or
-  // position while the keyboard is displayed. Any window repositioning
-  // invalidates insets for overscrolling.
-  void AddBoundsChangedObserver(aura::Window* window);
-
   // Sets shadow around the keyboard. If shadow has not been created yet,
   // this method creates it.
   void SetShadowAroundKeyboard();
@@ -98,7 +61,6 @@
 
   std::unique_ptr<ChromeKeyboardWebContents> keyboard_contents_;
   std::unique_ptr<ui::Shadow> shadow_;
-  std::unique_ptr<WindowBoundsChangeObserver> window_bounds_observer_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeKeyboardUI);
 };
diff --git a/chrome/browser/ui/ash/chrome_keyboard_ui_unittest.cc b/chrome/browser/ui/ash/chrome_keyboard_ui_unittest.cc
index 01f2c21..16d9c01 100644
--- a/chrome/browser/ui/ash/chrome_keyboard_ui_unittest.cc
+++ b/chrome/browser/ui/ash/chrome_keyboard_ui_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "chrome/browser/ui/ash/chrome_keyboard_controller_client.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/test_chrome_web_ui_controller_factory.h"
 #include "chrome/test/base/testing_profile.h"
@@ -22,15 +23,21 @@
 
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
+    chrome_keyboard_controller_client_ =
+        std::make_unique<ChromeKeyboardControllerClient>(
+            nullptr /* connector */);
     chrome_keyboard_ui_ = std::make_unique<ChromeKeyboardUI>(profile());
   }
 
   void TearDown() override {
     chrome_keyboard_ui_.reset();
+    chrome_keyboard_controller_client_.reset();
     ChromeRenderViewHostTestHarness::TearDown();
   }
 
  protected:
+  std::unique_ptr<ChromeKeyboardControllerClient>
+      chrome_keyboard_controller_client_;
   std::unique_ptr<ChromeKeyboardUI> chrome_keyboard_ui_;
 };
 
diff --git a/chrome/browser/ui/ash/chrome_keyboard_web_contents.cc b/chrome/browser/ui/ash/chrome_keyboard_web_contents.cc
index a90bc9a..6049c3d 100644
--- a/chrome/browser/ui/ash/chrome_keyboard_web_contents.cc
+++ b/chrome/browser/ui/ash/chrome_keyboard_web_contents.cc
@@ -4,12 +4,15 @@
 
 #include "chrome/browser/ui/ash/chrome_keyboard_web_contents.h"
 
+#include "base/feature_list.h"
 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
+#include "chrome/browser/ui/ash/chrome_keyboard_bounds_observer.h"
 #include "content/public/browser/host_zoom_map.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
@@ -18,8 +21,12 @@
 #include "extensions/browser/view_type_utils.h"
 #include "extensions/common/constants.h"
 #include "third_party/blink/public/platform/web_gesture_event.h"
+#include "ui/accessibility/ax_enums.mojom.h"
+#include "ui/accessibility/platform/aura_window_properties.h"
 #include "ui/aura/window.h"
 #include "ui/base/ui_base_features.h"
+#include "ui/compositor/layer.h"
+#include "ui/keyboard/keyboard_resource_util.h"
 
 namespace {
 
@@ -136,6 +143,32 @@
       web_contents_.get());
   Observe(web_contents_.get());
   LoadContents(url);
+
+  aura::Window* keyboard_window = web_contents_->GetNativeView();
+  keyboard_window->set_owned_by_parent(false);
+
+  // Only use transparent background when fullscreen handwriting or the new UI
+  // is enabled. The old UI sometimes reloads itself, which will cause the
+  // keyboard to be see-through.
+  // TODO(https://crbug.com/840731): Find a permanent fix for this on the
+  // keyboard extension side.
+  if (base::FeatureList::IsEnabled(
+          features::kEnableFullscreenHandwritingVirtualKeyboard) ||
+      base::FeatureList::IsEnabled(features::kEnableVirtualKeyboardMdUi)) {
+    content::RenderWidgetHostView* view =
+        web_contents_->GetMainFrame()->GetView();
+    view->SetBackgroundColor(SK_ColorTRANSPARENT);
+    view->GetNativeView()->SetTransparent(true);
+  }
+
+  // By default, layers in WebContents are clipped at the window bounds,
+  // but this causes the shadows to be clipped too, so clipping needs to
+  // be disabled.
+  keyboard_window->layer()->SetMasksToBounds(false);
+  keyboard_window->SetProperty(ui::kAXRoleOverride, ax::mojom::Role::kKeyboard);
+
+  window_bounds_observer_ =
+      std::make_unique<ChromeKeyboardBoundsObserver>(keyboard_window);
 }
 
 ChromeKeyboardWebContents::~ChromeKeyboardWebContents() = default;
diff --git a/chrome/browser/ui/ash/chrome_keyboard_web_contents.h b/chrome/browser/ui/ash/chrome_keyboard_web_contents.h
index e653c34..9f65392 100644
--- a/chrome/browser/ui/ash/chrome_keyboard_web_contents.h
+++ b/chrome/browser/ui/ash/chrome_keyboard_web_contents.h
@@ -17,6 +17,8 @@
 class WebContents;
 }
 
+class ChromeKeyboardBoundsObserver;
+
 // WebContents manager for the virtual keyboard. This observes the web
 // contents, manages the content::HostZoomMap, and informs the virtual
 // keyboard controller when the contents have loaded. It also provides a
@@ -40,6 +42,10 @@
   // once host window ownership is moved to ash.
   content::WebContents* web_contents() { return web_contents_.get(); }
 
+  ChromeKeyboardBoundsObserver* window_bounds_observer() {
+    return window_bounds_observer_.get();
+  }
+
  private:
   // content::WebContentsObserver overrides
   void RenderViewCreated(content::RenderViewHost* render_view_host) override;
@@ -50,6 +56,7 @@
   void LoadContents(const GURL& url);
 
   std::unique_ptr<content::WebContents> web_contents_;
+  std::unique_ptr<ChromeKeyboardBoundsObserver> window_bounds_observer_;
   LoadCallback callback_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeKeyboardWebContents);
diff --git a/chrome/browser/ui/ash/chrome_new_window_client.cc b/chrome/browser/ui/ash/chrome_new_window_client.cc
index 8dc35ea..7971da34 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/ui/ash/chrome_new_window_client.h"
 
-#include "ash/content/keyboard_overlay/keyboard_overlay_view.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/interfaces/constants.mojom.h"
 #include "base/macros.h"
@@ -237,21 +236,6 @@
   }
 }
 
-// TODO(crbug.com/755448): Remove this when the new shortcut viewer is enabled.
-void ChromeNewWindowClient::ShowKeyboardOverlay() {
-  // Show the new keyboard shortcut viewer if the feature is enabled.
-  if (ash::features::IsKeyboardShortcutViewerEnabled()) {
-    keyboard_shortcut_viewer_util::ToggleKeyboardShortcutViewer();
-    return;
-  }
-
-  // TODO(mazda): Move the show logic to ash (http://crbug.com/124222).
-  Profile* profile = ProfileManager::GetActiveUserProfile();
-  std::string url(chrome::kChromeUIKeyboardOverlayURL);
-  ash::KeyboardOverlayView::ShowDialog(profile, new ChromeWebContentsHandler,
-                                       GURL(url));
-}
-
 void ChromeNewWindowClient::ShowKeyboardShortcutViewer() {
   keyboard_shortcut_viewer_util::ToggleKeyboardShortcutViewer();
 }
diff --git a/chrome/browser/ui/ash/chrome_new_window_client.h b/chrome/browser/ui/ash/chrome_new_window_client.h
index 9c9fe02..80afa1d 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client.h
+++ b/chrome/browser/ui/ash/chrome_new_window_client.h
@@ -35,7 +35,6 @@
   void OpenCrosh() override;
   void OpenGetHelp() override;
   void RestoreTab() override;
-  void ShowKeyboardOverlay() override;
   void ShowKeyboardShortcutViewer() override;
   void ShowTaskManager() override;
   void OpenFeedbackPage() override;
diff --git a/chrome/browser/ui/ash/keyboard_controller_browsertest.cc b/chrome/browser/ui/ash/keyboard_controller_browsertest.cc
index 546919d..7389bce4 100644
--- a/chrome/browser/ui/ash/keyboard_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/keyboard_controller_browsertest.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/chrome_keyboard_controller_client.h"
 #include "chrome/browser/ui/ash/chrome_keyboard_ui.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "content/public/browser/render_widget_host_view.h"
@@ -40,10 +41,7 @@
     InProcessBrowserTest::SetUp();
   }
 
-  void TearDown() override {
-    ChromeKeyboardUI::TestApi::SetOverrideVirtualKeyboardUrl(GURL());
-    InProcessBrowserTest::TearDown();
-  }
+  void TearDown() override { InProcessBrowserTest::TearDown(); }
 
   // Ensure that the virtual keyboard is enabled.
   void SetUpCommandLine(base::CommandLine* command_line) override {
@@ -79,7 +77,8 @@
   void MockEnableIMEInDifferentExtension(const std::string& url,
                                          const gfx::Rect& init_bounds) {
     DCHECK(!url.empty());
-    ChromeKeyboardUI::TestApi::SetOverrideVirtualKeyboardUrl(GURL(url));
+    ChromeKeyboardControllerClient::Get()->set_virtual_keyboard_url_for_test(
+        GURL(url));
     auto* keyboard_controller = keyboard::KeyboardController::Get();
     keyboard_controller->Reload();
     // Mock window.resizeTo that is expected to be called after navigate to a
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
index 7ab10e5..1c42b820 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
@@ -28,7 +28,6 @@
 #include "chrome/browser/ui/login/login_handler.h"
 #include "chrome/browser/ui/login/login_handler_test_utils.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/views_mode_controller.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index c44f2bb..e91a20e 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -62,7 +62,6 @@
 #include "chrome/browser/ui/startup/startup_browser_creator_impl.h"
 #include "chrome/browser/ui/tabs/pinned_tab_codec.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/views_mode_controller.h"
 #include "chrome/common/buildflags.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_control_utils.h b/chrome/browser/ui/cocoa/constrained_window/constrained_window_control_utils.h
deleted file mode 100644
index 6494caf..0000000
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_control_utils.h
+++ /dev/null
@@ -1,46 +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.
-
-#ifndef CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_CONTROL_UTILS_H_
-#define CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_CONTROL_UTILS_H_
-
-#import <Cocoa/Cocoa.h>
-
-#include "ui/base/resource/resource_bundle.h"
-
-namespace constrained_window {
-
-// Creates a label control.
-NSTextField* CreateLabel();
-
-// Helper function to create constrained window label string with the given
-// font and alignment.
-NSAttributedString* GetAttributedLabelString(
-    NSString* string,
-    ui::ResourceBundle::FontStyle fontStyle,
-    NSTextAlignment alignment,
-    NSLineBreakMode lineBreakMode);
-
-// Create a new NSTextField and add it to the specified parent.
-NSTextField* AddTextField(NSView* parent,
-                          const base::string16& message,
-                          const ui::ResourceBundle::FontStyle& font_style);
-
-// Add the given button to the specified parent and customize it
-// according to the parameters.
-void AddButton(NSView* parent,
-               NSButton* button,
-               int title_id,
-               id target,
-               SEL action,
-               BOOL should_auto_size);
-
-// Determine the frame required to fit the content of a string.  Uses the
-// provided height and width as preferred dimensions, where a value of
-// 0.0 indicates no preference.
-NSRect ComputeFrame(NSAttributedString* text, CGFloat width, CGFloat height);
-
-}  // namespace constrained_window
-
-#endif  // CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_CONTROL_UTILS_H_
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_control_utils.mm b/chrome/browser/ui/cocoa/constrained_window/constrained_window_control_utils.mm
deleted file mode 100644
index e5ac7357..0000000
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_control_utils.mm
+++ /dev/null
@@ -1,88 +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 "chrome/browser/ui/cocoa/constrained_window/constrained_window_control_utils.h"
-
-#include "base/mac/scoped_nsobject.h"
-#include "base/strings/sys_string_conversions.h"
-#include "skia/ext/skia_utils_mac.h"
-#import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
-#include "ui/base/l10n/l10n_util_mac.h"
-
-namespace constrained_window {
-
-NSTextField* CreateLabel() {
-  NSTextField* label =
-      [[[NSTextField alloc] initWithFrame:NSZeroRect] autorelease];
-  [label setEditable:NO];
-  [label setSelectable:NO];
-  [label setBezeled:NO];
-  [label setDrawsBackground:NO];
-  return label;
-}
-
-NSAttributedString* GetAttributedLabelString(
-    NSString* string,
-    ui::ResourceBundle::FontStyle fontStyle,
-    NSTextAlignment alignment,
-    NSLineBreakMode lineBreakMode) {
-  if (!string)
-    return nil;
-
-  const gfx::Font& font =
-      ui::ResourceBundle::GetSharedInstance().GetFont(fontStyle);
-  base::scoped_nsobject<NSMutableParagraphStyle> paragraphStyle(
-      [[NSMutableParagraphStyle alloc] init]);
-  [paragraphStyle setAlignment:alignment];
-  [paragraphStyle setLineBreakMode:lineBreakMode];
-
-  NSDictionary* attributes = @{
-      NSFontAttributeName:            font.GetNativeFont(),
-      NSParagraphStyleAttributeName:  paragraphStyle.get()
-  };
-  return [[[NSAttributedString alloc] initWithString:string
-                                          attributes:attributes] autorelease];
-}
-
-NSTextField* AddTextField(NSView* parent,
-                          const base::string16& message,
-                          const ui::ResourceBundle::FontStyle& font_style) {
-  NSTextField* textField = constrained_window::CreateLabel();
-  [textField
-      setAttributedStringValue:constrained_window::GetAttributedLabelString(
-                                   base::SysUTF16ToNSString(message),
-                                   font_style, NSNaturalTextAlignment,
-                                   NSLineBreakByWordWrapping)];
-  [parent addSubview:textField];
-  return textField;
-}
-
-void AddButton(NSView* parent,
-               NSButton* button,
-               int title_id,
-               id target,
-               SEL action,
-               BOOL should_auto_size) {
-  if (title_id)
-    [button setTitle:l10n_util::GetNSString(title_id)];
-  [button setTarget:target];
-  [button setAction:action];
-  [parent addSubview:button];
-  if (should_auto_size)
-    [GTMUILocalizerAndLayoutTweaker sizeToFitView:button];
-}
-
-NSRect ComputeFrame(NSAttributedString* text, CGFloat width, CGFloat height) {
-  NSRect frame =
-      [text boundingRectWithSize:NSMakeSize(width, height)
-                         options:NSStringDrawingUsesLineFragmentOrigin];
-
-  // boundingRectWithSize: is known to underestimate the width, so
-  // additional padding needs to be added.
-  static const CGFloat kTextViewPadding = 10;
-  frame.size.width += kTextViewPadding;
-  return frame;
-}
-
-}  // namespace constrained_window
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h b/chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h
deleted file mode 100644
index 08019cb5a..0000000
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h
+++ /dev/null
@@ -1,31 +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.
-
-#ifndef CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_CUSTOM_SHEET_H_
-#define CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_CUSTOM_SHEET_H_
-
-#import <Cocoa/Cocoa.h>
-
-#import "base/mac/scoped_nsobject.h"
-#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet.h"
-
-// Represents a custom sheet. The sheet's window is shown without using the
-// system |beginSheet:...| API.
-@interface CustomConstrainedWindowSheet : NSObject<ConstrainedWindowSheet> {
- @protected
-  base::scoped_nsobject<NSWindow> customWindow_;
-  BOOL useSimpleAnimations_;
-}
-
-- (id)initWithCustomWindow:(NSWindow*)customWindow;
-
-// Defaults to NO, unless hardware that has problems with the standard
-// animations is detected.
-// The standard animation uses private CGS APIs, which can crash the window
-// server. https://crbug.com/515627#c75
-- (void)setUseSimpleAnimations:(BOOL)simpleAnimations;
-
-@end
-
-#endif  // CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_CUSTOM_SHEET_H_
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.mm b/chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.mm
deleted file mode 100644
index 8cd4389..0000000
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.mm
+++ /dev/null
@@ -1,146 +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 "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
-
-#import <IOKit/IOKitLib.h>
-
-#include "base/mac/mach_logging.h"
-#include "base/mac/scoped_ioobject.h"
-#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h"
-#import "ui/base/cocoa/constrained_window/constrained_window_animation.h"
-
-namespace {
-
-// Length of the animation in seconds.
-const NSTimeInterval kSheetAnimationDuration = 0.18;
-
-// If the machine has a DisplayLink device attached, the WindowServer has a
-// tendency to crash https://crbug.com/755516. To avoid this, probe the
-// IOKit registry for a DisplayLink service, and turn on useSimpleAnimations_
-// if one is found. The result of this is not cached, in case the hardware
-// is attached after the first ConstrainedWindow is shown.
-bool HasDisplayLinkDevice() {
-  // The |matching| reference is consumed by IOServiceGetMatchingServices().
-  CFDictionaryRef matching = IOServiceMatching("DisplayLinkFramebuffer");
-  base::mac::ScopedIOObject<io_iterator_t> it;
-  kern_return_t kr = IOServiceGetMatchingServices(
-      kIOMasterPortDefault, matching, it.InitializeInto());
-  if (kr != KERN_SUCCESS) {
-    MACH_LOG(ERROR, kr) << "IOServiceGetMatchingServices";
-    return false;
-  }
-
-  base::mac::ScopedIOObject<io_object_t> object(IOIteratorNext(it));
-  return object != IO_OBJECT_NULL;
-}
-
-}  // namespace
-
-@implementation CustomConstrainedWindowSheet
-
-- (id)initWithCustomWindow:(NSWindow*)customWindow {
-  if ((self = [super init])) {
-    customWindow_.reset([customWindow retain]);
-    useSimpleAnimations_ = HasDisplayLinkDevice();
-  }
-  return self;
-}
-
-- (void)setUseSimpleAnimations:(BOOL)simpleAnimations {
-  useSimpleAnimations_ = simpleAnimations;
-}
-
-- (void)showSheetForWindow:(NSWindow*)window {
-  base::scoped_nsobject<NSAnimation> animation;
-  if (useSimpleAnimations_) {
-    [customWindow_ setAlphaValue:0.0];
-  } else {
-    animation.reset(
-        [[ConstrainedWindowAnimationShow alloc] initWithWindow:customWindow_]);
-  }
-  [window addChildWindow:customWindow_
-                 ordered:NSWindowAbove];
-  [self unhideSheet];
-  [self updateSheetPosition];
-  [customWindow_ makeKeyAndOrderFront:nil];
-
-  if (useSimpleAnimations_) {
-    [NSAnimationContext beginGrouping];
-    [[NSAnimationContext currentContext] setDuration:kSheetAnimationDuration];
-    [[customWindow_ animator] setAlphaValue:1.0];
-    [NSAnimationContext endGrouping];
-  } else {
-    [animation startAnimation];
-  }
-}
-
-- (void)closeSheetWithAnimation:(BOOL)withAnimation {
-  if (withAnimation) {
-    base::scoped_nsobject<NSAnimation> animation;
-    if (useSimpleAnimations_) {
-      // Use a blocking animation, rather than -[NSWindow animator].
-      NSArray* animationArray = @[ @{
-        NSViewAnimationTargetKey : customWindow_,
-        NSViewAnimationEffectKey : NSViewAnimationFadeOutEffect,
-      } ];
-      animation.reset(
-          [[NSViewAnimation alloc] initWithViewAnimations:animationArray]);
-      [animation setDuration:kSheetAnimationDuration];
-      [animation setAnimationBlockingMode:NSAnimationBlocking];
-    } else {
-      animation.reset([[ConstrainedWindowAnimationHide alloc]
-          initWithWindow:customWindow_]);
-    }
-    [animation startAnimation];
-  }
-
-  [[customWindow_ parentWindow] removeChildWindow:customWindow_];
-  [customWindow_ close];
-}
-
-- (void)hideSheet {
-  // Hide the sheet window, and any of its direct child windows, by setting the
-  // alpha to 0. This technique is used instead of -orderOut: because that may
-  // cause a Spaces change or window ordering change.
-  [customWindow_ setAlphaValue:0.0];
-  for (NSWindow* window : [customWindow_ childWindows]) {
-    [window setAlphaValue:0.0];
-  }
-}
-
-- (void)unhideSheet {
-  [customWindow_ setAlphaValue:1.0];
-  for (NSWindow* window : [customWindow_ childWindows]) {
-    [window setAlphaValue:1.0];
-  }
-}
-
-- (void)pulseSheet {
-  base::scoped_nsobject<NSAnimation> animation(
-      [[ConstrainedWindowAnimationPulse alloc] initWithWindow:customWindow_]);
-  [animation startAnimation];
-}
-
-- (void)makeSheetKeyAndOrderFront {
-  [customWindow_ makeKeyAndOrderFront:nil];
-}
-
-- (void)updateSheetPosition {
-  ConstrainedWindowSheetController* controller =
-      [ConstrainedWindowSheetController controllerForSheet:self];
-  NSPoint origin = [controller originForSheet:self
-                               withWindowSize:[customWindow_ frame].size];
-  [customWindow_ setFrameOrigin:origin];
-}
-
-- (void)resizeWithNewSize:(NSSize)size {
-  // NOOP
-}
-
-- (NSWindow*)sheetWindow {
-  return customWindow_;
-}
-
-@end
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.h b/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.h
deleted file mode 100644
index 10475c9..0000000
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.h
+++ /dev/null
@@ -1,79 +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.
-
-#ifndef CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_MAC_H_
-#define CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_MAC_H_
-
-#import <Cocoa/Cocoa.h>
-
-#include "base/mac/scoped_nsobject.h"
-#include "components/web_modal/web_contents_modal_dialog_manager.h"
-
-namespace content {
-class WebContents;
-}
-class ConstrainedWindowMac;
-class SingleWebContentsDialogManagerCocoa;
-@protocol ConstrainedWindowSheet;
-
-// A delegate for a constrained window. The delegate is notified when the
-// window closes.
-class ConstrainedWindowMacDelegate {
- public:
-  virtual void OnConstrainedWindowClosed(ConstrainedWindowMac* window) = 0;
-};
-
-// Creates a ConstrainedWindowMac, shows the dialog, and returns it.
-std::unique_ptr<ConstrainedWindowMac> CreateAndShowWebModalDialogMac(
-    ConstrainedWindowMacDelegate* delegate,
-    content::WebContents* web_contents,
-    id<ConstrainedWindowSheet> sheet);
-
-// Creates a ConstrainedWindowMac and returns it.
-std::unique_ptr<ConstrainedWindowMac> CreateWebModalDialogMac(
-    ConstrainedWindowMacDelegate* delegate,
-    content::WebContents* web_contents,
-    id<ConstrainedWindowSheet> sheet);
-
-// Constrained window implementation for Mac.
-// Normally an instance of this class is owned by the delegate. The delegate
-// should delete the instance when the window is closed.
-class ConstrainedWindowMac {
- public:
-  ConstrainedWindowMac(ConstrainedWindowMacDelegate* delegate,
-                       content::WebContents* web_contents,
-                       id<ConstrainedWindowSheet> sheet);
-  ~ConstrainedWindowMac();
-
-  // Shows the constrained window.
-  void ShowWebContentsModalDialog();
-
-  // Closes the constrained window.
-  void CloseWebContentsModalDialog();
-
-  SingleWebContentsDialogManagerCocoa* manager() const { return manager_; }
-  void set_manager(SingleWebContentsDialogManagerCocoa* manager) {
-    manager_ = manager;
-  }
-  id<ConstrainedWindowSheet> sheet() const { return sheet_.get(); }
-
-  // Called by |manager_| when the dialog is closing.
-  void OnDialogClosing();
-
-  // Whether or not the dialog was shown. If the dialog is auto-resizable, it
-  // is hidden until its WebContents initially loads.
-  bool DialogWasShown();
-
- private:
-  // Gets the dialog manager for |web_contents_|.
-  web_modal::WebContentsModalDialogManager* GetDialogManager();
-
-  ConstrainedWindowMacDelegate* delegate_;  // weak, owns us.
-  SingleWebContentsDialogManagerCocoa* manager_;  // weak, owned by WCMDM.
-  content::WebContents* web_contents_;  // weak, owned by dialog initiator.
-  base::scoped_nsprotocol<id<ConstrainedWindowSheet>> sheet_;
-  std::unique_ptr<SingleWebContentsDialogManagerCocoa> native_manager_;
-};
-
-#endif  // CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_MAC_H_
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.mm b/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.mm
deleted file mode 100644
index 4a631bb..0000000
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.mm
+++ /dev/null
@@ -1,92 +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.
-
-#include "chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/logging.h"
-#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet.h"
-#import "chrome/browser/ui/cocoa/single_web_contents_dialog_manager_cocoa.h"
-#include "components/guest_view/browser/guest_view_base.h"
-#include "content/public/browser/browser_thread.h"
-
-using web_modal::WebContentsModalDialogManager;
-
-std::unique_ptr<ConstrainedWindowMac> CreateAndShowWebModalDialogMac(
-    ConstrainedWindowMacDelegate* delegate,
-    content::WebContents* web_contents,
-    id<ConstrainedWindowSheet> sheet) {
-  ConstrainedWindowMac* window =
-      new ConstrainedWindowMac(delegate, web_contents, sheet);
-  window->ShowWebContentsModalDialog();
-  return std::unique_ptr<ConstrainedWindowMac>(window);
-}
-
-std::unique_ptr<ConstrainedWindowMac> CreateWebModalDialogMac(
-    ConstrainedWindowMacDelegate* delegate,
-    content::WebContents* web_contents,
-    id<ConstrainedWindowSheet> sheet) {
-  return std::unique_ptr<ConstrainedWindowMac>(
-      new ConstrainedWindowMac(delegate, web_contents, sheet));
-}
-
-ConstrainedWindowMac::ConstrainedWindowMac(
-    ConstrainedWindowMacDelegate* delegate,
-    content::WebContents* web_contents,
-    id<ConstrainedWindowSheet> sheet)
-    : delegate_(delegate),
-      sheet_([sheet retain]) {
-  DCHECK(sheet);
-
-  // |web_contents| may be embedded within a chain of nested GuestViews. If it
-  // is, follow the chain of embedders to the outermost WebContents and use it.
-  web_contents_ =
-      guest_view::GuestViewBase::GetTopLevelWebContents(web_contents);
-
-  native_manager_.reset(
-      new SingleWebContentsDialogManagerCocoa(this, sheet_.get(),
-                                              GetDialogManager()));
-}
-
-ConstrainedWindowMac::~ConstrainedWindowMac() {
-  CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  native_manager_.reset();
-  DCHECK(!manager_);
-}
-
-void ConstrainedWindowMac::ShowWebContentsModalDialog() {
-  std::unique_ptr<SingleWebContentsDialogManagerCocoa> dialog_manager;
-  dialog_manager = std::move(native_manager_);
-  GetDialogManager()->ShowDialogWithManager(
-      [sheet_.get() sheetWindow], std::move(dialog_manager));
-}
-
-void ConstrainedWindowMac::CloseWebContentsModalDialog() {
-  if (manager_)
-    manager_->Close();
-}
-
-void ConstrainedWindowMac::OnDialogClosing() {
-  if (delegate_)
-    delegate_->OnConstrainedWindowClosed(this);
-}
-
-bool ConstrainedWindowMac::DialogWasShown() {
-  // If the dialog was shown, |native_manager_| would have been released.
-  return !native_manager_;
-}
-
-WebContentsModalDialogManager* ConstrainedWindowMac::GetDialogManager() {
-  DCHECK(web_contents_);
-  WebContentsModalDialogManager* dialog_manager =
-      WebContentsModalDialogManager::FromWebContents(web_contents_);
-  // If WebContentsModalDialogManager::CreateForWebContents(web_contents_) was
-  // never called, then the manager will be null. E.g., for browser tabs,
-  // TabHelpers::AttachTabHelpers() calls CreateForWebContents(). It is invalid
-  // to show a dialog on some kinds of WebContents. Crash cleanly in that case.
-  CHECK(dialog_manager);
-  return dialog_manager;
-}
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac_browsertest.mm b/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac_browsertest.mm
deleted file mode 100644
index 3b65983..0000000
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac_browsertest.mm
+++ /dev/null
@@ -1,192 +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.
-
-#include "chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.h"
-
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_tabstrip.h"
-#include "chrome/browser/ui/browser_window.h"
-#import "chrome/browser/ui/cocoa/browser_window_controller.h"
-#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
-#include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
-#include "chrome/browser/ui/exclusive_access/fullscreen_controller_test.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "content/public/browser/web_contents.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "url/gurl.h"
-
-using ::testing::NiceMock;
-
-namespace {
-
-class ConstrainedWindowDelegateMock : public ConstrainedWindowMacDelegate {
- public:
-  MOCK_METHOD1(OnConstrainedWindowClosed, void(ConstrainedWindowMac*));
-};
-
-}  // namespace
-
-class ConstrainedWindowMacTest : public InProcessBrowserTest {
- public:
-  ConstrainedWindowMacTest()
-      : InProcessBrowserTest(),
-        tab0_(NULL),
-        tab1_(NULL),
-        controller_(NULL),
-        tab_view0_(NULL),
-        tab_view1_(NULL) {
-    sheet_window_.reset([[NSWindow alloc]
-        initWithContentRect:NSMakeRect(0, 0, 30, 30)
-                  styleMask:NSTitledWindowMask
-                    backing:NSBackingStoreBuffered
-                      defer:NO]);
-    [sheet_window_ setReleasedWhenClosed:NO];
-    sheet_.reset([[CustomConstrainedWindowSheet alloc]
-        initWithCustomWindow:sheet_window_]);
-    [sheet_ hideSheet];
-  }
-
-  void SetUpOnMainThread() override {
-    AddTabAtIndex(1, GURL("about:blank"), ui::PAGE_TRANSITION_LINK);
-    tab0_ = browser()->tab_strip_model()->GetWebContentsAt(0);
-    tab1_ = browser()->tab_strip_model()->GetWebContentsAt(1);
-    EXPECT_EQ(tab1_, browser()->tab_strip_model()->GetActiveWebContents());
-
-    controller_ = [BrowserWindowController browserWindowControllerForWindow:
-        browser()->window()->GetNativeWindow()];
-    EXPECT_TRUE(controller_);
-    tab_view0_ = [[controller_ tabStripController] viewAtIndex:0];
-    EXPECT_TRUE(tab_view0_);
-    tab_view1_ = [[controller_ tabStripController] viewAtIndex:1];
-    EXPECT_TRUE(tab_view1_);
-  }
-
- protected:
-  base::scoped_nsobject<CustomConstrainedWindowSheet> sheet_;
-  base::scoped_nsobject<NSWindow> sheet_window_;
-  content::WebContents* tab0_;
-  content::WebContents* tab1_;
-  BrowserWindowController* controller_;
-  NSView* tab_view0_;
-  NSView* tab_view1_;
-};
-
-// Test that a sheet added to a inactive tab is not shown until the
-// tab is activated.
-IN_PROC_BROWSER_TEST_F(ConstrainedWindowMacTest, ShowInInactiveTab) {
-  // Show dialog in non active tab.
-  NiceMock<ConstrainedWindowDelegateMock> delegate;
-  std::unique_ptr<ConstrainedWindowMac> dialog =
-      CreateAndShowWebModalDialogMac(&delegate, tab0_, sheet_);
-  EXPECT_EQ(0.0, [sheet_window_ alphaValue]);
-
-  // Switch to inactive tab.
-  browser()->tab_strip_model()->ActivateTabAt(0, true);
-  EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
-
-  dialog->CloseWebContentsModalDialog();
-}
-
-// If a tab has never been shown then the associated tab view for the web
-// content will not be created. Verify that adding a constrained window to such
-// a tab works correctly.
-IN_PROC_BROWSER_TEST_F(ConstrainedWindowMacTest, ShowInUninitializedTab) {
-  content::WebContents::CreateParams create_params(browser()->profile());
-  create_params.initially_hidden = true;
-  std::unique_ptr<content::WebContents> web_contents(
-      content::WebContents::Create(create_params));
-  chrome::AddWebContents(browser(), NULL, std::move(web_contents),
-                         WindowOpenDisposition::NEW_BACKGROUND_TAB, gfx::Rect(),
-                         false);
-  content::WebContents* tab2 =
-      browser()->tab_strip_model()->GetWebContentsAt(2);
-  ASSERT_TRUE(tab2);
-  EXPECT_FALSE([tab2->GetNativeView() superview]);
-
-  // Show dialog and verify that it's not visible yet.
-  NiceMock<ConstrainedWindowDelegateMock> delegate;
-  std::unique_ptr<ConstrainedWindowMac> dialog =
-      CreateAndShowWebModalDialogMac(&delegate, tab2, sheet_);
-  EXPECT_FALSE([sheet_window_ isVisible]);
-
-  // Activate the tab and verify that the constrained window is shown.
-  browser()->tab_strip_model()->ActivateTabAt(2, true);
-  EXPECT_TRUE([tab2->GetNativeView() superview]);
-  EXPECT_TRUE([sheet_window_ isVisible]);
-  EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
-
-  dialog->CloseWebContentsModalDialog();
-}
-
-// Test that adding a sheet disables tab dragging.
-IN_PROC_BROWSER_TEST_F(ConstrainedWindowMacTest, TabDragging) {
-  NiceMock<ConstrainedWindowDelegateMock> delegate;
-  std::unique_ptr<ConstrainedWindowMac> dialog =
-      CreateAndShowWebModalDialogMac(&delegate, tab1_, sheet_);
-
-  // Verify that the dialog disables dragging.
-  EXPECT_TRUE([controller_ isTabDraggable:tab_view0_]);
-  EXPECT_FALSE([controller_ isTabDraggable:tab_view1_]);
-
-  dialog->CloseWebContentsModalDialog();
-}
-
-// Test that closing a browser window with a sheet works.
-IN_PROC_BROWSER_TEST_F(ConstrainedWindowMacTest, BrowserWindowClose) {
-  NiceMock<ConstrainedWindowDelegateMock> delegate;
-  std::unique_ptr<ConstrainedWindowMac> dialog(
-      CreateAndShowWebModalDialogMac(&delegate, tab1_, sheet_));
-  EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
-
-  // Close the browser window.
-  base::scoped_nsobject<NSWindow> browser_window(
-      [browser()->window()->GetNativeWindow() retain]);
-  EXPECT_TRUE([browser_window isVisible]);
-  [browser()->window()->GetNativeWindow() performClose:nil];
-  EXPECT_FALSE([browser_window isVisible]);
-}
-
-// Test that closing a tab with a sheet works.
-IN_PROC_BROWSER_TEST_F(ConstrainedWindowMacTest, TabClose) {
-  NiceMock<ConstrainedWindowDelegateMock> delegate;
-  std::unique_ptr<ConstrainedWindowMac> dialog(
-      CreateAndShowWebModalDialogMac(&delegate, tab1_, sheet_));
-  EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
-
-  // Close the tab.
-  TabStripModel* tab_strip = browser()->tab_strip_model();
-  EXPECT_EQ(2, tab_strip->count());
-  EXPECT_TRUE(tab_strip->CloseWebContentsAt(1,
-                                            TabStripModel::CLOSE_USER_GESTURE));
-  EXPECT_EQ(1, tab_strip->count());
-}
-
-// Test that the sheet is still visible after entering and exiting fullscreen.
-IN_PROC_BROWSER_TEST_F(ConstrainedWindowMacTest, BrowserWindowFullscreen) {
-  NiceMock<ConstrainedWindowDelegateMock> delegate;
-  std::unique_ptr<ConstrainedWindowMac> dialog(
-      CreateAndShowWebModalDialogMac(&delegate, tab1_, sheet_));
-  EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
-
-  // Toggle fullscreen twice: one for entering and one for exiting.
-  // Check to see if the sheet is visible.
-  for (int i = 0; i < 2; i++) {
-    {
-      // NOTIFICATION_FULLSCREEN_CHANGED is sent asynchronously. Wait for the
-      // notification before testing the sheet's visibility.
-      std::unique_ptr<FullscreenNotificationObserver> waiter(
-          new FullscreenNotificationObserver());
-      browser()
-          ->exclusive_access_manager()
-          ->fullscreen_controller()
-          ->ToggleBrowserFullscreenMode();
-      waiter->Wait();
-    }
-    EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
-  }
-
-  dialog->CloseWebContentsModalDialog();
-}
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet.h b/chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet.h
deleted file mode 100644
index cf24f7d..0000000
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet.h
+++ /dev/null
@@ -1,33 +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.
-
-#ifndef CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_SHEET_H_
-#define CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_SHEET_H_
-
-#import <Cocoa/Cocoa.h>
-
-// Protocol for a sheet to be showing using |ConstrainedWindowSheetController|.
-@protocol ConstrainedWindowSheet<NSObject>
-
-- (void)showSheetForWindow:(NSWindow*)window;
-
-- (void)closeSheetWithAnimation:(BOOL)withAnimation;
-
-- (void)hideSheet;
-
-- (void)unhideSheet;
-
-- (void)pulseSheet;
-
-- (void)makeSheetKeyAndOrderFront;
-
-- (void)updateSheetPosition;
-
-- (void)resizeWithNewSize:(NSSize)size;
-
-@property(readonly, nonatomic) NSWindow* sheetWindow;
-
-@end
-
-#endif  // CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_SHEET_H_
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h b/chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h
deleted file mode 100644
index 37c0c88..0000000
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h
+++ /dev/null
@@ -1,92 +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.
-
-#ifndef CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_SHEET_CONTROLLER_H_
-#define CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_SHEET_CONTROLLER_H_
-
-#import <Cocoa/Cocoa.h>
-
-#include <memory>
-#include <vector>
-
-#include "base/mac/scoped_nsobject.h"
-
-namespace web_modal {
-class WebContentsModalDialogHost;
-}
-
-class WebContentsModalDialogHostCocoa;
-
-@protocol ConstrainedWindowSheet;
-
-// This class manages multiple tab modal sheets for a single parent window. Each
-// tab can have a single sheet and only the active tab's sheet will be visible.
-// A tab in this case is the |parentView| passed to |-showSheet:forParentView:|.
-@interface ConstrainedWindowSheetController : NSObject {
- @private
-  base::scoped_nsobject<NSMutableArray> sheets_;
-  base::scoped_nsobject<NSWindow> parentWindow_;
-  base::scoped_nsobject<NSView> activeView_;
-
-  // Flag to prevent the sheet from updating its position if it's hidden during
-  // fullscreen. Otherwise, we will get janky movements during the animation.
-  BOOL isSheetHiddenForFullscreen_;
-
-  // Class that bridges the cross-platform web_modal APIs to the Cocoa sheet
-  // controller.
-  std::unique_ptr<WebContentsModalDialogHostCocoa> dialogHost_;
-}
-
-@property(readonly, nonatomic)
-    web_modal::WebContentsModalDialogHost* dialogHost;
-@property(readonly, nonatomic) NSWindow* parentWindow;
-
-// Returns a sheet controller for |parentWindow|. If a sheet controller does not
-// exist yet then one will be created.
-+ (ConstrainedWindowSheetController*)
-    controllerForParentWindow:(NSWindow*)parentWindow;
-
-// Find a controller that's managing the given sheet. If no such controller
-// exists then nil is returned.
-+ (ConstrainedWindowSheetController*)
-    controllerForSheet:(id<ConstrainedWindowSheet>)sheet;
-
-// Find the sheet attached to the given overlay window.
-+ (id<ConstrainedWindowSheet>)sheetForOverlayWindow:(NSWindow*)overlayWindow;
-
-// Shows the given sheet over |parentView|.
-- (void)showSheet:(id<ConstrainedWindowSheet>)sheet
-    forParentView:(NSView*)parentView;
-
-// Hides |sheet| over the active view.
-- (void)hideSheet:(id<ConstrainedWindowSheet>)sheet;
-
-// Hides and unhides the sheet at the beginning and end of fullscreen
-// transition. |hideSheetForFullscreenTransition| gets called at the beginning
-// of the transition and |unhideSheetForFullscreenTransition| gets called at
-// the end.
-- (void)hideSheetForFullscreenTransition;
-- (void)unhideSheetForFullscreenTransition;
-
-// Calculates the position of the sheet for the given window size.
-- (NSPoint)originForSheet:(id<ConstrainedWindowSheet>)sheet
-           withWindowSize:(NSSize)size;
-
-// Closes the given sheet.
-- (void)closeSheet:(id<ConstrainedWindowSheet>)sheet;
-
-// Run a pulse animation for the given sheet. This does nothing if the sheet
-// is not visible.
-- (void)pulseSheet:(id<ConstrainedWindowSheet>)sheet;
-
-// Gets the number of sheets attached to the controller's window.
-- (int)sheetCount;
-
-// The size of the overlay window, which can be used to determine a preferred
-// maximum size for a dialog that should be contained within |parentView|.
-- (NSSize)overlayWindowSizeForParentView:(NSView*)parentView;
-
-@end
-
-#endif  // CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_SHEET_CONTROLLER_H_
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.mm b/chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.mm
deleted file mode 100644
index 92cdfa8..0000000
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.mm
+++ /dev/null
@@ -1,367 +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 "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h"
-
-#include <map>
-
-#include "base/logging.h"
-#include "base/mac/sdk_forward_declarations.h"
-#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet.h"
-#include "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_info.h"
-#import "chrome/browser/ui/cocoa/web_contents_modal_dialog_host_cocoa.h"
-
-namespace {
-
-// Maps parent windows to sheet controllers.
-NSMutableDictionary* g_sheetControllers;
-
-// Get a value for the given window that can be used as a key in a dictionary.
-NSValue* GetKeyForParentWindow(NSWindow* parent_window) {
-  return [NSValue valueWithNonretainedObject:parent_window];
-}
-
-// Returns the bounds to use when showing a sheet for a given parent view. This
-// returns a rect in window coordinates.
-NSRect GetSheetParentBoundsForParentView(NSView* view) {
-  // If the devtools view is open, it shrinks the size of the WebContents, so go
-  // up the hierarchy to the devtools container view to avoid that. Note that
-  // the devtools view is always in the hierarchy even if it is not open or it
-  // is detached.
-  NSView* devtools_view = [[[view superview] superview] superview];
-  if (devtools_view)
-    view = devtools_view;
-  return [view convertRect:[view bounds] toView:nil];
-}
-
-}  // namespace
-
-// An invisible overlay window placed on top of the sheet's parent view.
-// This window blocks interaction with the underlying view.
-@interface CWSheetOverlayWindow : NSWindow {
-  base::scoped_nsobject<ConstrainedWindowSheetController> controller_;
-}
-@end
-
-@interface ConstrainedWindowSheetController ()
-- (id)initWithParentWindow:(NSWindow*)parentWindow;
-- (ConstrainedWindowSheetInfo*)findSheetInfoForParentView:(NSView*)parentView;
-- (ConstrainedWindowSheetInfo*)
-    findSheetInfoForSheet:(id<ConstrainedWindowSheet>)sheet;
-- (void)onParentWindowWillClose:(NSNotification*)note;
-- (void)onParentWindowSizeDidChange:(NSNotification*)note;
-- (void)updateSheetPosition:(NSView*)parentView;
-- (NSRect)overlayWindowFrameForParentView:(NSView*)parentView;
-- (NSPoint)originForSheetSize:(NSSize)sheetSize
-              inContainerRect:(NSRect)containerRect;
-- (void)onOverlayWindowMouseDown:(CWSheetOverlayWindow*)overlayWindow;
-- (void)closeSheet:(ConstrainedWindowSheetInfo*)info
-     withAnimation:(BOOL)withAnimation;
-@end
-
-@implementation CWSheetOverlayWindow
-
-- (id)initWithContentRect:(NSRect)rect
-               controller:(ConstrainedWindowSheetController*)controller {
-  if ((self = [super initWithContentRect:rect
-                               styleMask:NSBorderlessWindowMask
-                                 backing:NSBackingStoreBuffered
-                                   defer:NO])) {
-    [self setOpaque:NO];
-    [self setBackgroundColor:[NSColor clearColor]];
-    [self setIgnoresMouseEvents:NO];
-    [self setReleasedWhenClosed:NO];
-    controller_.reset([controller retain]);
-  }
-  return self;
-}
-
-- (void)mouseDown:(NSEvent*)event {
-  [controller_ onOverlayWindowMouseDown:self];
-}
-
-@end
-
-@implementation ConstrainedWindowSheetController
-
-+ (ConstrainedWindowSheetController*)
-    controllerForParentWindow:(NSWindow*)parentWindow {
-  DCHECK(parentWindow);
-  ConstrainedWindowSheetController* controller =
-      [g_sheetControllers objectForKey:GetKeyForParentWindow(parentWindow)];
-  if (controller)
-    return controller;
-
-  base::scoped_nsobject<ConstrainedWindowSheetController> new_controller(
-      [[ConstrainedWindowSheetController alloc]
-          initWithParentWindow:parentWindow]);
-  if (!g_sheetControllers)
-    g_sheetControllers = [[NSMutableDictionary alloc] init];
-  [g_sheetControllers setObject:new_controller
-                         forKey:GetKeyForParentWindow(parentWindow)];
-  return new_controller;
-}
-
-+ (ConstrainedWindowSheetController*)
-    controllerForSheet:(id<ConstrainedWindowSheet>)sheet {
-  for (ConstrainedWindowSheetController* controller in
-       [g_sheetControllers objectEnumerator]) {
-    if ([controller findSheetInfoForSheet:sheet])
-      return controller;
-  }
-  return nil;
-}
-
-+ (id<ConstrainedWindowSheet>)sheetForOverlayWindow:(NSWindow*)overlayWindow {
-  for (ConstrainedWindowSheetController* controller in
-          [g_sheetControllers objectEnumerator]) {
-    for (ConstrainedWindowSheetInfo* info in controller->sheets_.get()) {
-      if ([overlayWindow isEqual:[info overlayWindow]])
-        return [info sheet];
-    }
-  }
-  return nil;
-}
-
-- (id)initWithParentWindow:(NSWindow*)parentWindow {
-  if ((self = [super init])) {
-    parentWindow_.reset([parentWindow retain]);
-    sheets_.reset([[NSMutableArray alloc] init]);
-
-    [[NSNotificationCenter defaultCenter]
-        addObserver:self
-           selector:@selector(onParentWindowWillClose:)
-               name:NSWindowWillCloseNotification
-             object:parentWindow_];
-  }
-  return self;
-}
-
-- (web_modal::WebContentsModalDialogHost*)dialogHost {
-  if (!dialogHost_)
-    dialogHost_.reset(new WebContentsModalDialogHostCocoa(self));
-  return dialogHost_.get();
-}
-
-- (NSWindow*)parentWindow {
-  return parentWindow_.get();
-}
-
-- (void)showSheet:(id<ConstrainedWindowSheet>)sheet
-    forParentView:(NSView*)parentView {
-  DCHECK(sheet);
-  DCHECK(parentView);
-
-  // At maximum one active view is allowed.
-  DCHECK(!activeView_.get() || [activeView_ isEqual:parentView]);
-  if (!activeView_.get())
-    activeView_.reset([parentView retain]);
-
-  // This function can be called multiple times for the same
-  // |parentView|, so sheet info could be created already.
-  ConstrainedWindowSheetInfo* existingInfo =
-      [self findSheetInfoForParentView:activeView_];
-  if (existingInfo) {
-    DCHECK([[existingInfo sheet] isEqual:sheet]);
-    [self updateSheetPosition:activeView_];
-    [existingInfo showSheet];
-    return;
-  }
-
-  // Observe the parent window's size.
-  [[NSNotificationCenter defaultCenter]
-      addObserver:self
-         selector:@selector(onParentWindowSizeDidChange:)
-             name:NSWindowDidResizeNotification
-           object:parentWindow_];
-
-  // Create an invisible overlay window.
-  NSRect rect = [self overlayWindowFrameForParentView:parentView];
-  base::scoped_nsobject<NSWindow> overlayWindow(
-      [[CWSheetOverlayWindow alloc] initWithContentRect:rect controller:self]);
-  [parentWindow_ addChildWindow:overlayWindow
-                        ordered:NSWindowAbove];
-
-  // Add an entry for the sheet.
-  base::scoped_nsobject<ConstrainedWindowSheetInfo> info(
-      [[ConstrainedWindowSheetInfo alloc] initWithSheet:sheet
-                                             parentView:parentView
-                                          overlayWindow:overlayWindow]);
-  [sheets_ addObject:info];
-
-  // Show the sheet.
-  [info showSheet];
-}
-
-- (void)hideSheet:(id<ConstrainedWindowSheet>)sheet {
-  ConstrainedWindowSheetInfo* info = [self findSheetInfoForSheet:sheet];
-  // Method can be called for already hidden sheet. http://crbug.com/589074.
-  if ([[info parentView] isEqual:activeView_]) {
-    [info hideSheet];
-    activeView_.reset();
-  }
-}
-
-- (void)hideSheetForFullscreenTransition {
-  if (ConstrainedWindowSheetInfo* sheetInfo =
-          [self findSheetInfoForParentView:activeView_]) {
-    [sheetInfo hideSheet];
-    isSheetHiddenForFullscreen_ = YES;
-  }
-}
-
-- (void)unhideSheetForFullscreenTransition {
-  isSheetHiddenForFullscreen_ = NO;
-  if (ConstrainedWindowSheetInfo* sheetInfo =
-          [self findSheetInfoForParentView:activeView_]) {
-    [self showSheet:[sheetInfo sheet] forParentView:activeView_];
-  }
-}
-
-- (NSPoint)originForSheet:(id<ConstrainedWindowSheet>)sheet
-           withWindowSize:(NSSize)size {
-  ConstrainedWindowSheetInfo* info = [self findSheetInfoForSheet:sheet];
-  DCHECK(info);
-  NSRect containerRect =
-      [self overlayWindowFrameForParentView:[info parentView]];
-  return [self originForSheetSize:size inContainerRect:containerRect];
-}
-
-- (void)closeSheet:(id<ConstrainedWindowSheet>)sheet {
-  ConstrainedWindowSheetInfo* info = [self findSheetInfoForSheet:sheet];
-  DCHECK(info);
-  [self closeSheet:info withAnimation:YES];
-}
-
-- (void)pulseSheet:(id<ConstrainedWindowSheet>)sheet {
-  ConstrainedWindowSheetInfo* info = [self findSheetInfoForSheet:sheet];
-  DCHECK(info);
-  if ([activeView_ isEqual:[info parentView]])
-    [[info sheet] pulseSheet];
-}
-
-- (int)sheetCount {
-  return [sheets_ count];
-}
-
-- (NSSize)overlayWindowSizeForParentView:(NSView*)parentView {
-  return [self overlayWindowFrameForParentView:parentView].size;
-}
-
-- (ConstrainedWindowSheetInfo*)findSheetInfoForParentView:(NSView*)parentView {
-  for (ConstrainedWindowSheetInfo* info in sheets_.get()) {
-    if ([parentView isEqual:[info parentView]])
-      return info;
-  }
-  return NULL;
-}
-
-- (ConstrainedWindowSheetInfo*)
-    findSheetInfoForSheet:(id<ConstrainedWindowSheet>)sheet {
-  for (ConstrainedWindowSheetInfo* info in sheets_.get()) {
-    if ([sheet isEqual:[info sheet]])
-      return info;
-  }
-  return NULL;
-}
-
-- (void)onParentWindowWillClose:(NSNotification*)note {
-  [[NSNotificationCenter defaultCenter]
-      removeObserver:self
-                name:NSWindowWillCloseNotification
-              object:parentWindow_];
-
-  // Close all sheets.
-  NSArray* sheets = [NSArray arrayWithArray:sheets_];
-  for (ConstrainedWindowSheetInfo* info in sheets)
-    [self closeSheet:info withAnimation:NO];
-
-  dialogHost_.reset();
-
-  // Delete this instance.
-  [g_sheetControllers removeObjectForKey:GetKeyForParentWindow(parentWindow_)];
-  if (![g_sheetControllers count]) {
-    [g_sheetControllers release];
-    g_sheetControllers = nil;
-  }
-}
-
-- (void)onParentWindowSizeDidChange:(NSNotification*)note {
-  if (isSheetHiddenForFullscreen_)
-    return;
-
-  [self updateSheetPosition:activeView_];
-}
-
-- (void)updateSheetPosition:(NSView*)parentView {
-  ConstrainedWindowSheetInfo* info =
-      [self findSheetInfoForParentView:parentView];
-  if (!info)
-    return;
-
-  NSRect rect = [self overlayWindowFrameForParentView:parentView];
-  [[info overlayWindow] setFrame:rect display:YES];
-  [[info sheet] updateSheetPosition];
-}
-
-- (NSRect)overlayWindowFrameForParentView:(NSView*)parentView {
-  NSRect viewFrame = GetSheetParentBoundsForParentView(parentView);
-
-  id<NSWindowDelegate> delegate = [[parentView window] delegate];
-  if ([delegate respondsToSelector:@selector(window:
-                                  willPositionSheet:
-                                          usingRect:)]) {
-    NSRect sheetFrame = NSZeroRect;
-    // This API needs Y to be the distance from the bottom of the overlay to
-    // the top of the sheet. X, width, and height are ignored.
-    sheetFrame.origin.y = NSMaxY(viewFrame);
-    NSRect customSheetFrame = [delegate window:[parentView window]
-                             willPositionSheet:nil
-                                     usingRect:sheetFrame];
-    viewFrame.size.height += NSMinY(customSheetFrame) - NSMinY(sheetFrame);
-  }
-
-  viewFrame = [[parentView window] convertRectToScreen:viewFrame];
-  return viewFrame;
-}
-
-- (NSPoint)originForSheetSize:(NSSize)sheetSize
-              inContainerRect:(NSRect)containerRect {
-  NSPoint origin;
-  origin.x = roundf(NSMinX(containerRect) +
-                    (NSWidth(containerRect) - sheetSize.width) / 2.0);
-  origin.y = NSMaxY(containerRect) + 5 - sheetSize.height;
-  return origin;
-}
-
-- (void)onOverlayWindowMouseDown:(CWSheetOverlayWindow*)overlayWindow {
-  for (ConstrainedWindowSheetInfo* curInfo in sheets_.get()) {
-    if ([overlayWindow isEqual:[curInfo overlayWindow]]) {
-      [self pulseSheet:[curInfo sheet]];
-      [[curInfo sheet] makeSheetKeyAndOrderFront];
-      break;
-    }
-  }
-}
-
-- (void)closeSheet:(ConstrainedWindowSheetInfo*)info
-     withAnimation:(BOOL)withAnimation {
-  if (![sheets_ containsObject:info])
-    return;
-
-  [[NSNotificationCenter defaultCenter]
-      removeObserver:self
-                name:NSWindowDidResizeNotification
-              object:parentWindow_];
-
-  if ([activeView_ isEqual:[info parentView]])
-    activeView_.reset();
-
-  [parentWindow_ removeChildWindow:[info overlayWindow]];
-  [[info sheet] closeSheetWithAnimation:withAnimation];
-  [[info overlayWindow] close];
-  [sheets_ removeObject:info];
-}
-
-@end
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller_unittest.mm b/chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller_unittest.mm
deleted file mode 100644
index 6a5feeb..0000000
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller_unittest.mm
+++ /dev/null
@@ -1,336 +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 "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h"
-
-#include <stddef.h>
-
-#include "base/mac/sdk_forward_declarations.h"
-#include "base/macros.h"
-#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
-#import "chrome/browser/ui/cocoa/test/cocoa_test_helper.h"
-#import "testing/gtest_mac.h"
-
-namespace {
-
-const int kSystemSheetReturnCode = 77;
-
-}  // namespace
-
-@interface ConstrainedWindowSystemSheetTest
-    : NSObject <ConstrainedWindowSheet> {
-  int returnCode_;
-  NSAlert* alert_;  // weak
-}
-
-@property(nonatomic, readonly) int returnCode;
-@property(nonatomic, assign) NSAlert* alert;
-
-@end
-
-@implementation ConstrainedWindowSystemSheetTest
-
-@synthesize returnCode = returnCode_;
-@synthesize alert = alert_;
-
-- (void)showSheetForWindow:(NSWindow*)window {
-  [alert_ beginSheetModalForWindow:window
-                     modalDelegate:self
-                    didEndSelector:@selector(alertDidEnd:returnCode:ctxInfo:)
-                       contextInfo:NULL];
-}
-
-- (void)closeSheetWithAnimation:(BOOL)withAnimation {
-  [NSApp endSheet:[alert_ window] returnCode:kSystemSheetReturnCode];
-}
-
-- (void)hideSheet {
-}
-
-- (void)unhideSheet {
-}
-
-- (void)pulseSheet {
-}
-
-- (void)makeSheetKeyAndOrderFront {
-}
-
-- (void)updateSheetPosition {
-}
-
-- (void)resizeWithNewSize:(NSSize)size {
-  // NOOP
-}
-
-- (NSWindow*)sheetWindow {
-  return [alert_ window];
-}
-
-- (void)alertDidEnd:(NSAlert *)alert
-         returnCode:(NSInteger)returnCode
-            ctxInfo:(void *)contextInfo {
-  returnCode_ = returnCode;
-}
-
-@end
-
-class ConstrainedWindowSheetControllerTest : public CocoaTest {
- protected:
-  void SetUp() override {
-    CocoaTest::SetUp();
-
-    // Center the window so that the sheet doesn't go offscreen.
-    [test_window() center];
-
-    // The real view setup is quite a few levels deep; recreate something
-    // similar.
-    NSRect dummy_rect = NSMakeRect(0, 0, 50, 50);
-    tab_view_parent_ = [test_window() contentView];
-    for (int i = 0; i < 3; ++i) {
-      base::scoped_nsobject<NSView> new_view(
-          [[NSView alloc] initWithFrame:dummy_rect]);
-      [tab_view_parent_ addSubview:new_view.get()];
-      tab_view_parent_ = new_view.get();
-    }
-
-    controller_.reset([[ConstrainedWindowSheetController
-            controllerForParentWindow:test_window()] retain]);
-    EXPECT_TRUE(controller_);
-
-    // Create two dummy tabs and make the first one active.
-    tab_views_.reset([[NSMutableArray alloc] init]);
-    sheet_windows_.reset([[NSMutableArray alloc] init]);
-    sheets_.reset([[NSMutableArray alloc] init]);
-    for (int i = 0; i < 2; ++i) {
-      base::scoped_nsobject<NSView> view(
-          [[NSView alloc] initWithFrame:dummy_rect]);
-      base::scoped_nsobject<NSWindow> sheet_window(
-          [[NSWindow alloc]
-            initWithContentRect:dummy_rect
-                      styleMask:NSTitledWindowMask
-                        backing:NSBackingStoreBuffered
-                          defer:NO]);
-      [sheet_window setReleasedWhenClosed:NO];
-      base::scoped_nsobject<CustomConstrainedWindowSheet> sheet(
-          [[CustomConstrainedWindowSheet alloc]
-              initWithCustomWindow:sheet_window]);
-      EXPECT_FALSE([ConstrainedWindowSheetController controllerForSheet:sheet]);
-      [tab_views_ addObject:view];
-      [sheet_windows_ addObject:sheet_window];
-      [sheets_ addObject:sheet];
-    }
-    tab0_ = [tab_views_ objectAtIndex:0];
-    tab1_ = [tab_views_ objectAtIndex:1];
-    sheet_window0_ = [sheet_windows_ objectAtIndex:0];
-    sheet_window1_ = [sheet_windows_ objectAtIndex:1];
-    sheet_window_ = sheet_window0_;
-    sheet0_ = [sheets_ objectAtIndex:0];
-    sheet1_ = [sheets_ objectAtIndex:1];
-    sheet_ = sheet0_;
-
-    active_tab_view_ = tab0_;
-    [tab_view_parent_ addSubview:active_tab_view_];
-  }
-
-  void TearDown() override {
-    sheets_.reset();
-    sheet_windows_.reset();
-    CocoaTest::TearDown();
-  }
-
-  void ActivateTabView(NSView* tab_view) {
-    for (NSView* view in tab_views_.get())
-      [view removeFromSuperview];
-    [tab_view_parent_ addSubview:tab_view];
-    CustomConstrainedWindowSheet* current_sheet =
-        [sheets_ objectAtIndex:[tab_views_ indexOfObject:active_tab_view_]];
-    [[ConstrainedWindowSheetController controllerForSheet:current_sheet]
-        hideSheet:current_sheet];
-
-    active_tab_view_ = tab_view;
-    ConstrainedWindowSheetController* controller =
-        [ConstrainedWindowSheetController
-            controllerForParentWindow:test_window()];
-    EXPECT_TRUE(controller);
-
-    CustomConstrainedWindowSheet* sheet =
-        [sheets_ objectAtIndex:[tab_views_ indexOfObject:active_tab_view_]];
-    EXPECT_TRUE(sheet);
-    [controller showSheet:sheet forParentView:active_tab_view_];
-  }
-
-  NSRect GetViewFrameInScreenCoordinates(NSView* view) {
-    NSRect rect = [view convertRect:[view bounds] toView:nil];
-    rect = [[view window] convertRectToScreen:rect];
-    return rect;
-  }
-
-  void VerifySheetXPosition(NSRect sheet_frame, NSView* parent_view) {
-    NSRect parent_frame = GetViewFrameInScreenCoordinates(parent_view);
-    CGFloat expected_x = NSMinX(parent_frame) +
-        (NSWidth(parent_frame) - NSWidth(sheet_frame)) / 2.0;
-    EXPECT_EQ(expected_x, NSMinX(sheet_frame));
-  }
-
-  CGFloat GetSheetYOffset(NSRect sheet_frame, NSView* parent_view) {
-    return NSMaxY(sheet_frame) -
-           NSMaxY(GetViewFrameInScreenCoordinates(parent_view));
-  }
-
-  base::scoped_nsobject<NSMutableArray> sheet_windows_;
-  NSWindow* sheet_window0_;
-  NSWindow* sheet_window1_;
-  NSWindow* sheet_window_;
-  base::scoped_nsobject<NSMutableArray> sheets_;
-  CustomConstrainedWindowSheet* sheet0_;
-  CustomConstrainedWindowSheet* sheet1_;
-  CustomConstrainedWindowSheet* sheet_;
-  base::scoped_nsobject<ConstrainedWindowSheetController> controller_;
-  base::scoped_nsobject<NSMutableArray> tab_views_;
-  NSView* tab_view_parent_;
-  NSView* active_tab_view_;
-  NSView* tab0_;
-  NSView* tab1_;
-};
-
-// Test showing then hiding the sheet.
-TEST_F(ConstrainedWindowSheetControllerTest, ShowHide) {
-  EXPECT_FALSE([sheet_window_ isVisible]);
-  [controller_ showSheet:sheet_ forParentView:active_tab_view_];
-  EXPECT_TRUE([ConstrainedWindowSheetController controllerForSheet:sheet_]);
-  EXPECT_TRUE([sheet_window_ isVisible]);
-
-  [controller_ closeSheet:sheet_];
-  EXPECT_FALSE([ConstrainedWindowSheetController controllerForSheet:sheet_]);
-  EXPECT_FALSE([sheet_window_ isVisible]);
-}
-
-// Test that switching tabs correctly hides the inactive tab's sheet.
-TEST_F(ConstrainedWindowSheetControllerTest, SwitchTabs) {
-  [controller_ showSheet:sheet_ forParentView:active_tab_view_];
-
-  EXPECT_TRUE([sheet_window_ isVisible]);
-  EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
-  ActivateTabView([tab_views_ objectAtIndex:1]);
-  EXPECT_TRUE([sheet_window_ isVisible]);
-  EXPECT_EQ(0.0, [sheet_window_ alphaValue]);
-  ActivateTabView([tab_views_ objectAtIndex:0]);
-  EXPECT_TRUE([sheet_window_ isVisible]);
-  EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
-}
-
-// Test that hiding a hidden tab for the second time is not affecting the
-// visible one. See http://crbug.com/589074.
-TEST_F(ConstrainedWindowSheetControllerTest, DoubleHide) {
-  ActivateTabView([tab_views_ objectAtIndex:1]);
-  ActivateTabView([tab_views_ objectAtIndex:0]);
-
-  ASSERT_TRUE([ConstrainedWindowSheetController controllerForSheet:sheet1_]);
-  [[ConstrainedWindowSheetController controllerForSheet:sheet1_]
-      hideSheet:sheet1_];
-  EXPECT_TRUE([sheet_window_ isVisible]);
-  EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
-}
-
-// Test that two parent windows with two sheet controllers don't conflict.
-TEST_F(ConstrainedWindowSheetControllerTest, TwoParentWindows) {
-  base::scoped_nsobject<NSWindow> parent_window2(
-      [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 30, 30)
-                                  styleMask:NSTitledWindowMask
-                                    backing:NSBackingStoreBuffered
-                                      defer:NO]);
-  [parent_window2 setReleasedWhenClosed:NO];
-
-  ConstrainedWindowSheetController* controller2 =
-      [ConstrainedWindowSheetController
-          controllerForParentWindow:parent_window2];
-  EXPECT_TRUE(controller2);
-  EXPECT_NSNE(controller_, controller2);
-
-  [controller2 showSheet:sheet_ forParentView:[parent_window2 contentView]];
-  EXPECT_NSEQ(controller2,
-              [ConstrainedWindowSheetController controllerForSheet:sheet_]);
-
-  [parent_window2 close];
-}
-
-// Test that resizing sheet works.
-TEST_F(ConstrainedWindowSheetControllerTest, Resize) {
-  [controller_ showSheet:sheet_ forParentView:active_tab_view_];
-
-  NSRect old_frame = [sheet_window_ frame];
-
-  NSRect sheet_frame;
-  sheet_frame.size = NSMakeSize(NSWidth(old_frame) + 100,
-                                NSHeight(old_frame) + 50);
-  sheet_frame.origin = [controller_ originForSheet:sheet_
-                                    withWindowSize:sheet_frame.size];
-
-  // Y pos should not have changed.
-  EXPECT_EQ(NSMaxY(sheet_frame), NSMaxY(old_frame));
-
-  // X pos should be centered on parent view.
-  VerifySheetXPosition(sheet_frame, active_tab_view_);
-}
-
-// Test that resizing a hidden sheet works.
-TEST_F(ConstrainedWindowSheetControllerTest, ResizeHiddenSheet) {
-  [controller_ showSheet:sheet_ forParentView:tab0_];
-  EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
-  ActivateTabView(tab1_);
-  EXPECT_EQ(0.0, [sheet_window_ alphaValue]);
-
-  NSRect old_frame = [sheet_window_ frame];
-  NSRect new_inactive_frame = NSInsetRect(old_frame, -30, -40);
-  [sheet_window_ setFrame:new_inactive_frame display:YES];
-
-  ActivateTabView(tab0_);
-  EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
-
-  NSRect new_active_frame = [sheet_window_ frame];
-  EXPECT_EQ(NSWidth(new_inactive_frame), NSWidth(new_active_frame));
-  EXPECT_EQ(NSHeight(new_inactive_frame), NSHeight(new_active_frame));
-}
-
-// Test resizing parent window keeps the sheet anchored.
-TEST_F(ConstrainedWindowSheetControllerTest, ResizeParentWindow) {
-  [controller_ showSheet:sheet_ forParentView:active_tab_view_];
-  CGFloat sheet_offset =
-      GetSheetYOffset([sheet_window_ frame], active_tab_view_);
-
-  // Test 3x3 different parent window sizes.
-  CGFloat insets[] = {-10, 0, 10};
-  NSRect old_frame = [test_window() frame];
-
-  for (size_t x = 0; x < arraysize(insets); x++) {
-    for (size_t y = 0; y < arraysize(insets); y++) {
-      NSRect resized_frame = NSInsetRect(old_frame, insets[x], insets[y]);
-      [test_window() setFrame:resized_frame display:YES];
-      NSRect sheet_frame = [sheet_window_ frame];
-
-      // Y pos should track parent view's position.
-      EXPECT_EQ(sheet_offset, GetSheetYOffset(sheet_frame, active_tab_view_));
-
-      // X pos should be centered on parent view.
-      VerifySheetXPosition(sheet_frame, active_tab_view_);
-    }
-  }
-}
-
-// Test system sheets.
-TEST_F(ConstrainedWindowSheetControllerTest, SystemSheet) {
-  base::scoped_nsobject<ConstrainedWindowSystemSheetTest> system_sheet(
-      [[ConstrainedWindowSystemSheetTest alloc] init]);
-  base::scoped_nsobject<NSAlert> alert([[NSAlert alloc] init]);
-  [system_sheet setAlert:alert];
-
-  EXPECT_FALSE([[alert window] isVisible]);
-  [controller_ showSheet:system_sheet forParentView:active_tab_view_];
-  EXPECT_TRUE([[alert window] isVisible]);
-
-  [controller_ closeSheet:system_sheet];
-  EXPECT_FALSE([[alert window] isVisible]);
-  EXPECT_EQ(kSystemSheetReturnCode, [system_sheet returnCode]);
-}
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_info.h b/chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_info.h
deleted file mode 100644
index b72e4f6..0000000
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_info.h
+++ /dev/null
@@ -1,45 +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.
-
-#ifndef CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_SHEET_INFO_H_
-#define CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_SHEET_INFO_H_
-
-#import <Cocoa/Cocoa.h>
-
-#include "base/mac/scoped_nsobject.h"
-
-@protocol ConstrainedWindowSheet;
-
-// Information about a single sheet managed by
-// ConstrainedWindowSheetController. Note, this is a private class not meant for
-// public use.
-@interface ConstrainedWindowSheetInfo : NSObject {
- @private
-  base::scoped_nsprotocol<id<ConstrainedWindowSheet>> sheet_;
-  base::scoped_nsobject<NSView> parentView_;
-  base::scoped_nsobject<NSWindow> overlayWindow_;
-  BOOL sheetDidShow_;
-}
-
-@property(nonatomic, readonly) id<ConstrainedWindowSheet> sheet;
-@property(nonatomic, readonly) NSView* parentView;
-@property(nonatomic, readonly) NSWindow* overlayWindow;
-@property(nonatomic, assign) BOOL sheetDidShow;
-
-// Initializes a info object with for the given |sheet| and associated
-// |parentView| and |overlayWindow|.
-- (id)initWithSheet:(id<ConstrainedWindowSheet>)sheet
-         parentView:(NSView*)parentView
-      overlayWindow:(NSWindow*)overlayWindow;
-
-// Hides the sheet and the associated overlay window. Hiding is done in such
-// a way as to not disturb the window cycle order.
-- (void)hideSheet;
-
-// Shows the sheet and the associated overlay window.
-- (void)showSheet;
-
-@end
-
-#endif  // CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_SHEET_INFO_H_
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_info.mm b/chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_info.mm
deleted file mode 100644
index 4c2816a6..0000000
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_info.mm
+++ /dev/null
@@ -1,86 +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.
-
-#include "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_info.h"
-
-#include "base/mac/foundation_util.h"
-#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet.h"
-#include "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h"
-
-@implementation ConstrainedWindowSheetInfo
-
-@synthesize sheetDidShow = sheetDidShow_;
-
-- (id)initWithSheet:(id<ConstrainedWindowSheet>)sheet
-         parentView:(NSView*)parentView
-      overlayWindow:(NSWindow*)overlayWindow {
-  if ((self = [super init])) {
-    sheet_.reset([sheet retain]);
-    parentView_.reset([parentView retain]);
-    overlayWindow_.reset([overlayWindow retain]);
-  }
-  return self;
-}
-
-- (id<ConstrainedWindowSheet>)sheet {
-  return sheet_;
-}
-
-- (NSView*)parentView {
-  return parentView_;
-}
-
-- (NSWindow*)overlayWindow {
-  return overlayWindow_;
-}
-
-- (void)hideSheet {
-  [sheet_ hideSheet];
-
-  // Overlay window is already invisible so just stop accepting mouse events.
-  [overlayWindow_ setIgnoresMouseEvents:YES];
-
-  // Make sure the now invisible sheet doesn't keep keyboard focus
-  [[overlayWindow_ parentWindow] makeKeyWindow];
-}
-
-- (void)showSheet {
-  [overlayWindow_ setIgnoresMouseEvents:NO];
-  if (sheetDidShow_) {
-    [sheet_ unhideSheet];
-  } else {
-    [sheet_ showSheetForWindow:overlayWindow_];
-    sheetDidShow_ = YES;
-  }
-
-  // The call to -addChildWindow:ordered: below works around a macOS bug
-  // (rdar://35418050) in 10.12 through (at least) 10.13.1: If a window (A) has
-  // a child window (B), and B has a sheet (C), then adding another child
-  // window (D) to A causes the sheet (C) to move behind its parent (B).
-  //
-  // This happens when the client certificate selector appears and then, after
-  // a timeout, the status bubble appears to indicate that the page is still
-  // loading.
-  //
-  //       ╭────────────────────────────╮
-  //       │     Browser window (A)     │
-  //       │ ┌───┬───────────────────┬──┼─┐
-  //       │ │   │ Cert selector (C) │  │ │
-  //       │ │   ╰───────────────────╯  │ │
-  //     ╭ ├─┼─────╮ Overlay window (B) │ │
-  //     ├ ╰─┼─────┴────────────────────╯ │
-  //     │   └────────────────────────────┘
-  //     ╰ Status bubble (D)
-  //
-  // Explicitly adding the sheet as a child window seems to let it participate
-  // in the window ordering process that happens when the root window gains a
-  // child so that it stays in front of its parent as expected.
-
-  if (NSWindow* sheet = [overlayWindow_ attachedSheet])
-    [overlayWindow_ addChildWindow:sheet ordered:NSWindowAbove];
-
-  [sheet_ makeSheetKeyAndOrderFront];
-}
-
-@end
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_web_dialog_sheet.h b/chrome/browser/ui/cocoa/constrained_window/constrained_window_web_dialog_sheet.h
deleted file mode 100644
index 7c37e23..0000000
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_web_dialog_sheet.h
+++ /dev/null
@@ -1,26 +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.
-
-#ifndef CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_WEB_DIALOG_SHEET_H_
-#define CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_WEB_DIALOG_SHEET_H_
-
-#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
-
-namespace ui {
-class WebDialogDelegate;
-}
-
-// Represents a custom sheet for web dialog.
-@interface WebDialogConstrainedWindowSheet : CustomConstrainedWindowSheet {
- @private
-  NSSize current_size_;
-  ui::WebDialogDelegate* web_dialog_delegate_;  // Weak.
-}
-
-- (id)initWithCustomWindow:(NSWindow*)customWindow
-         webDialogDelegate:(ui::WebDialogDelegate*)delegate;
-
-@end
-
-#endif  // CHROME_BROWSER_UI_COCOA_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_WEB_DIALOG_SHEET_H_
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_web_dialog_sheet.mm b/chrome/browser/ui/cocoa/constrained_window/constrained_window_web_dialog_sheet.mm
deleted file mode 100644
index fc92d26..0000000
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_web_dialog_sheet.mm
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_web_dialog_sheet.h"
-
-#import "ui/base/cocoa/window_size_constants.h"
-#include "ui/gfx/geometry/size.h"
-#include "ui/web_dialogs/web_dialog_delegate.h"
-
-@implementation WebDialogConstrainedWindowSheet
-
-- (id)initWithCustomWindow:(NSWindow*)customWindow
-         webDialogDelegate:(ui::WebDialogDelegate*)delegate {
-  if (self = [super initWithCustomWindow:customWindow]) {
-    current_size_ = ui::kWindowSizeDeterminedLater.size;
-    web_dialog_delegate_ = delegate;
-  }
-
-  return self;
-}
-
-- (void)updateSheetPosition {
-  if (web_dialog_delegate_) {
-    gfx::Size size;
-    web_dialog_delegate_->GetDialogSize(&size);
-
-    // If the dialog has autoresizing enabled, |size| will be empty. Use the
-    // last known dialog size.
-    NSSize content_size = size.IsEmpty() ? current_size_ :
-        NSMakeSize(size.width(), size.height());
-    [customWindow_ setContentSize:content_size];
-  }
-  [super updateSheetPosition];
-}
-
-- (void)resizeWithNewSize:(NSSize)size {
-  DCHECK(size.height > 0 && size.width > 0);
-  current_size_ = size;
-  [customWindow_ setContentSize:current_size_];
-
-  // self's updateSheetPosition() sets |customWindow_|'s contentSize to a
-  // fixed dialog size. Here, we want to resize to |size| instead. Use
-  // super rather than self to bypass the setContentSize() call for the fixed
-  // size.
-  [super updateSheetPosition];
-}
-
-@end
diff --git a/chrome/browser/ui/cocoa/nsmenuitem_additions.h b/chrome/browser/ui/cocoa/nsmenuitem_additions.h
index 5544e2a..74240f45f 100644
--- a/chrome/browser/ui/cocoa/nsmenuitem_additions.h
+++ b/chrome/browser/ui/cocoa/nsmenuitem_additions.h
@@ -20,5 +20,6 @@
 // source.
 void SetIsInputSourceDvorakQwertyForTesting(bool is_dvorak_qwerty);
 void SetIsInputSourceCzechForTesting(bool is_czech);
+void SetIsInputSourceAbcAzertyForTesting(bool is_abc_azerty);
 
 #endif  // CHROME_BROWSER_UI_COCOA_NSMENUITEM_ADDITIONS_H_
diff --git a/chrome/browser/ui/cocoa/nsmenuitem_additions.mm b/chrome/browser/ui/cocoa/nsmenuitem_additions.mm
index fda4546..7975755c 100644
--- a/chrome/browser/ui/cocoa/nsmenuitem_additions.mm
+++ b/chrome/browser/ui/cocoa/nsmenuitem_additions.mm
@@ -13,6 +13,7 @@
 namespace {
 bool g_is_input_source_dvorak_qwerty = false;
 bool g_is_input_source_czech = false;
+bool g_is_input_source_abc_azerty = false;
 }  // namespace
 
 void SetIsInputSourceDvorakQwertyForTesting(bool is_dvorak_qwerty) {
@@ -23,6 +24,10 @@
   g_is_input_source_czech = is_czech;
 }
 
+void SetIsInputSourceAbcAzertyForTesting(bool is_abc_azerty) {
+  g_is_input_source_abc_azerty = is_abc_azerty;
+}
+
 @interface KeyboardInputSourceListener : NSObject
 @end
 
@@ -55,6 +60,9 @@
   g_is_input_source_czech =
       [inputSourceID rangeOfString:@"com.apple.keylayout.Czech"].location !=
       NSNotFound;
+  g_is_input_source_abc_azerty =
+      [inputSourceID rangeOfString:@"com.apple.keylayout.ABC-AZERTY"]
+          .location != NSNotFound;
 }
 
 - (void)inputSourceDidChange:(NSNotification*)notification {
@@ -186,6 +194,19 @@
     }
   }
 
+  // On ABC-AZERTY kebyards, we want to interpet cmd + '&' as cmd + '1'. Ditto
+  // for other keyCodes that would produce a numerical key.
+  if (g_is_input_source_abc_azerty) {
+    if (eventModifiers == NSCommandKeyMask) {
+      ui::KeyboardCode windows_keycode =
+          ui::KeyboardCodeFromKeyCode(event.keyCode);
+      if (windows_keycode >= ui::VKEY_0 && windows_keycode <= ui::VKEY_9) {
+        eventString =
+            [NSString stringWithFormat:@"%d", windows_keycode - ui::VKEY_0];
+      }
+    }
+  }
+
   return [eventString isEqualToString:[self keyEquivalent]]
       && eventModifiers == [self keyEquivalentModifierMask];
 }
diff --git a/chrome/browser/ui/cocoa/nsmenuitem_additions_unittest.mm b/chrome/browser/ui/cocoa/nsmenuitem_additions_unittest.mm
index 3992cee5..32b092b 100644
--- a/chrome/browser/ui/cocoa/nsmenuitem_additions_unittest.mm
+++ b/chrome/browser/ui/cocoa/nsmenuitem_additions_unittest.mm
@@ -357,6 +357,17 @@
   key = KeyEvent(0x100108, @"4", @"", 21);
   ExpectKeyFiresItem(key, MenuItem(@"4", NSCommandKeyMask),
                      /*compareCocoa=*/false);
+
+  // On French AZERTY layout, cmd + '&' [vkeycode = 18] should instead trigger
+  // cmd + '1'. Ditto for other number keys.
+  SetIsInputSourceAbcAzertyForTesting(true);
+  key = KeyEvent(0x100108, @"&", @"&", 18);
+  ExpectKeyFiresItem(key, MenuItem(@"1", NSCommandKeyMask),
+                     /*compareCocoa=*/false);
+  key = KeyEvent(0x100108, @"é", @"é", 19);
+  ExpectKeyFiresItem(key, MenuItem(@"2", NSCommandKeyMask),
+                     /*compareCocoa=*/false);
+  SetIsInputSourceAbcAzertyForTesting(false);
 }
 
 NSString* keyCodeToCharacter(NSUInteger keyCode,
diff --git a/chrome/browser/ui/cocoa/single_web_contents_dialog_manager_cocoa.h b/chrome/browser/ui/cocoa/single_web_contents_dialog_manager_cocoa.h
deleted file mode 100644
index 49df0d0..0000000
--- a/chrome/browser/ui/cocoa/single_web_contents_dialog_manager_cocoa.h
+++ /dev/null
@@ -1,47 +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.
-
-#ifndef CHROME_BROWSER_UI_COCOA_SINGLE_WEB_CONTENTS_DIALOG_MANAGER_COCOA_H_
-#define CHROME_BROWSER_UI_COCOA_SINGLE_WEB_CONTENTS_DIALOG_MANAGER_COCOA_H_
-
-#import "base/mac/scoped_nsobject.h"
-#include "base/macros.h"
-#include "components/web_modal/single_web_contents_dialog_manager.h"
-
-class ConstrainedWindowMac;
-@protocol ConstrainedWindowSheet;
-
-// Cocoa implementation of web_modal::SingleWebContentsDialogManager for showing
-// native Cocoa sheet dialogs (of various kinds).
-class SingleWebContentsDialogManagerCocoa
-    : public web_modal::SingleWebContentsDialogManager {
- public:
-  SingleWebContentsDialogManagerCocoa(
-      ConstrainedWindowMac* client,
-      id<ConstrainedWindowSheet> sheet,
-      web_modal::SingleWebContentsDialogManagerDelegate* delegate);
-  ~SingleWebContentsDialogManagerCocoa() override;
-
-  // SingleWebContentsDialogManager overrides.
-  void Show() override;
-  void Hide() override;
-  void Close() override;
-  void Focus() override;
-  void Pulse() override;
-  void HostChanged(web_modal::WebContentsModalDialogHost* new_host) override;
-  gfx::NativeWindow dialog() override;
-
- private:
-  // Weak. Legacy DialogManager-style delegate interface that Cocoa uses.
-  ConstrainedWindowMac* client_;
-  base::scoped_nsprotocol<id<ConstrainedWindowSheet>> sheet_;
-  // Weak. Owns this.
-  web_modal::SingleWebContentsDialogManagerDelegate* delegate_;
-  // Weak. Owned by parent window.
-  web_modal::WebContentsModalDialogHost* host_;
-
-  DISALLOW_COPY_AND_ASSIGN(SingleWebContentsDialogManagerCocoa);
-};
-
-#endif  // CHROME_BROWSER_UI_COCOA_SINGLE_WEB_CONTENTS_DIALOG_MANAGER_COCOA_H_
diff --git a/chrome/browser/ui/cocoa/single_web_contents_dialog_manager_cocoa.mm b/chrome/browser/ui/cocoa/single_web_contents_dialog_manager_cocoa.mm
deleted file mode 100644
index f637716..0000000
--- a/chrome/browser/ui/cocoa/single_web_contents_dialog_manager_cocoa.mm
+++ /dev/null
@@ -1,89 +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.
-
-#import "chrome/browser/ui/cocoa/single_web_contents_dialog_manager_cocoa.h"
-
-#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
-#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.h"
-#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h"
-#include "components/web_modal/web_contents_modal_dialog_host.h"
-#include "components/web_modal/web_contents_modal_dialog_manager.h"
-#include "content/public/browser/web_contents.h"
-
-SingleWebContentsDialogManagerCocoa::SingleWebContentsDialogManagerCocoa(
-    ConstrainedWindowMac* client,
-    id<ConstrainedWindowSheet> sheet,
-    web_modal::SingleWebContentsDialogManagerDelegate* delegate)
-    : client_(client),
-      sheet_([sheet retain]),
-      delegate_(delegate),
-      host_(nullptr) {
-  DCHECK(client);
-  client->set_manager(this);
-}
-
-SingleWebContentsDialogManagerCocoa::~SingleWebContentsDialogManagerCocoa() {
-}
-
-void SingleWebContentsDialogManagerCocoa::Show() {
-  // If a dialog is initially shown on a hidden/background WebContents, the
-  // |delegate_| will defer the Show() until the WebContents is shown. If the
-  // defer happens during tab closure or tab dragging, a suspected data race or
-  // ObserverList ordering may result in |host_| being null here. If the tab is
-  // closing anyway, it doesn't matter. For tab dragging, avoid a crash, but the
-  // user may have to switch tabs again to see the dialog. See
-  // http://crbug.com/514826 for details.
-  if (!host_)
-    return;
-
-  NSView* parent_view = host_->GetHostView().GetNativeNSView();
-  // Note that simply [parent_view window] for an inactive tab is nil. However,
-  // the following should always be non-nil for all WebContents containers.
-  NSWindow* parent_window = delegate_->GetWebContents()
-                                ->GetTopLevelNativeWindow()
-                                .GetNativeNSWindow();
-
-  [[ConstrainedWindowSheetController controllerForParentWindow:parent_window]
-      showSheet:sheet_ forParentView:parent_view];
-}
-
-void SingleWebContentsDialogManagerCocoa::Hide() {
-  NSWindow* parent_window = delegate_->GetWebContents()
-                                ->GetTopLevelNativeWindow()
-                                .GetNativeNSWindow();
-  [[ConstrainedWindowSheetController controllerForParentWindow:parent_window]
-      hideSheet:sheet_];
-}
-
-void SingleWebContentsDialogManagerCocoa::Close() {
-  [[ConstrainedWindowSheetController controllerForSheet:sheet_]
-      closeSheet:sheet_];
-  client_->set_manager(nullptr);
-  bool dialog_was_open = client_->DialogWasShown();
-  client_->OnDialogClosing();      // |client_| might delete itself here.
-  if (dialog_was_open)
-    delegate_->WillClose(dialog());  // Deletes |this|.
-}
-
-void SingleWebContentsDialogManagerCocoa::Focus() {
-}
-
-void SingleWebContentsDialogManagerCocoa::Pulse() {
-  [[ConstrainedWindowSheetController controllerForSheet:sheet_]
-      pulseSheet:sheet_];
-}
-
-void SingleWebContentsDialogManagerCocoa::HostChanged(
-    web_modal::WebContentsModalDialogHost* new_host) {
-  // No need to observe the host. For Cocoa, the constrained window controller
-  // will reposition the dialog when necessary. The host can also never change.
-  // Tabs showing a dialog can not be dragged off a Cocoa browser window.
-  // However, closing a tab with a dialog open will set the host back to null.
-  DCHECK_NE(!!host_, !!new_host);
-  host_ = new_host;
-}
-
-gfx::NativeWindow SingleWebContentsDialogManagerCocoa::dialog() {
-  return [sheet_ sheetWindow];
-}
diff --git a/chrome/browser/ui/cocoa/ssl_client_certificate_selector_cocoa.h b/chrome/browser/ui/cocoa/ssl_client_certificate_selector_cocoa.h
deleted file mode 100644
index 9c77d439..0000000
--- a/chrome/browser/ui/cocoa/ssl_client_certificate_selector_cocoa.h
+++ /dev/null
@@ -1,66 +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.
-
-#ifndef CHROME_BROWSER_UI_COCOA_SSL_CLIENT_CERTIFICATE_SELECTOR_COCOA_H_
-#define CHROME_BROWSER_UI_COCOA_SSL_CLIENT_CERTIFICATE_SELECTOR_COCOA_H_
-
-#import <Cocoa/Cocoa.h>
-
-#include <memory>
-#include <vector>
-
-#include "base/mac/scoped_cftyperef.h"
-#include "base/mac/scoped_nsobject.h"
-#include "base/memory/ref_counted.h"
-#include "chrome/browser/ssl/ssl_client_certificate_selector.h"
-#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
-#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h"
-#include "net/ssl/client_cert_identity.h"
-
-namespace content {
-class BrowserContext;
-class ClientCertificateDelegate;
-}
-
-class ConstrainedWindowMac;
-@class SFChooseIdentityPanel;
-class SSLClientAuthObserverCocoaBridge;
-
-@interface SSLClientCertificateSelectorCocoa
-    : NSObject<ConstrainedWindowSheet> {
- @private
-  // The list of SecIdentityRefs offered to the user.
-  base::ScopedCFTypeRef<CFMutableArrayRef> sec_identities_;
-  // The corresponding list of ClientCertIdentities.
-  net::ClientCertIdentityList cert_identities_;
-  // A C++ object to bridge SSLClientAuthObserver notifications to us.
-  std::unique_ptr<SSLClientAuthObserverCocoaBridge> observer_;
-  base::scoped_nsobject<SFChooseIdentityPanel> panel_;
-  std::unique_ptr<ConstrainedWindowMac> constrainedWindow_;
-  base::scoped_nsobject<NSWindow> overlayWindow_;
-  BOOL closePending_;
-  // A copy of the sheet's |autoresizesSubviews| flag to restore on show.
-  BOOL oldResizesSubviews_;
-  // True if the user dismissed the dialog directly, either via the OK (continue
-  // the request with a certificate) or Cancel (continue the request with no
-  // certificate) buttons.
-  BOOL userResponded_;
-}
-
-@property (readonly, nonatomic) SFChooseIdentityPanel* panel;
-
-- (id)initWithBrowserContext:(const content::BrowserContext*)browserContext
-             certRequestInfo:(net::SSLCertRequestInfo*)certRequestInfo
-                    delegate:
-                        (std::unique_ptr<content::ClientCertificateDelegate>)
-                            delegate;
-- (void)displayForWebContents:(content::WebContents*)webContents
-                  clientCerts:(net::ClientCertIdentityList)inputClientCerts;
-- (void)closeWebContentsModalDialog;
-
-- (NSWindow*)overlayWindow;
-
-@end
-
-#endif  // CHROME_BROWSER_UI_COCOA_SSL_CLIENT_CERTIFICATE_SELECTOR_COCOA_H_
diff --git a/chrome/browser/ui/cocoa/ssl_client_certificate_selector_cocoa.mm b/chrome/browser/ui/cocoa/ssl_client_certificate_selector_cocoa.mm
deleted file mode 100644
index c7e7737..0000000
--- a/chrome/browser/ui/cocoa/ssl_client_certificate_selector_cocoa.mm
+++ /dev/null
@@ -1,329 +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 "chrome/browser/ui/cocoa/ssl_client_certificate_selector_cocoa.h"
-
-#import <SecurityInterface/SFChooseIdentityPanel.h>
-#include <stddef.h>
-
-#include <utility>
-
-#include "base/logging.h"
-#include "base/mac/foundation_util.h"
-#include "base/strings/string_util.h"
-#include "base/strings/sys_string_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "build/buildflag.h"
-#include "chrome/browser/ssl/ssl_client_auth_observer.h"
-#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.h"
-#include "chrome/grit/generated_resources.h"
-#include "components/guest_view/browser/guest_view_base.h"
-#include "components/strings/grit/components_strings.h"
-#include "components/web_modal/web_contents_modal_dialog_manager.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/client_certificate_delegate.h"
-#include "content/public/browser/web_contents.h"
-#include "net/cert/x509_certificate.h"
-#include "net/cert/x509_util_mac.h"
-#include "net/ssl/ssl_cert_request_info.h"
-#include "net/ssl/ssl_platform_key_mac.h"
-#include "ui/base/cocoa/window_size_constants.h"
-#include "ui/base/l10n/l10n_util_mac.h"
-#include "ui/base/ui_features.h"
-
-using content::BrowserThread;
-
-@interface SFChooseIdentityPanel (SystemPrivate)
-// A system-private interface that dismisses a panel whose sheet was started by
-// -beginSheetForWindow:modalDelegate:didEndSelector:contextInfo:identities:message:
-// as though the user clicked the button identified by returnCode. Verified
-// present in 10.5 through 10.12.
-- (void)_dismissWithCode:(NSInteger)code;
-@end
-
-@interface SSLClientCertificateSelectorCocoa ()
-- (void)onConstrainedWindowClosed;
-@end
-
-class SSLClientAuthObserverCocoaBridge : public SSLClientAuthObserver,
-                                         public ConstrainedWindowMacDelegate {
- public:
-  SSLClientAuthObserverCocoaBridge(
-      const content::BrowserContext* browser_context,
-      net::SSLCertRequestInfo* cert_request_info,
-      std::unique_ptr<content::ClientCertificateDelegate> delegate,
-      SSLClientCertificateSelectorCocoa* controller)
-      : SSLClientAuthObserver(browser_context,
-                              cert_request_info,
-                              std::move(delegate)),
-        controller_(controller) {}
-
-  // SSLClientAuthObserver implementation:
-  void OnCertSelectedByNotification() override {
-    [controller_ closeWebContentsModalDialog];
-  }
-
-  // ConstrainedWindowMacDelegate implementation:
-  void OnConstrainedWindowClosed(ConstrainedWindowMac* window) override {
-    // |onConstrainedWindowClosed| will delete the sheet which might be still
-    // in use higher up the call stack. Wait for the next cycle of the event
-    // loop to call this function.
-    [controller_ performSelector:@selector(onConstrainedWindowClosed)
-                      withObject:nil
-                      afterDelay:0];
-  }
-
- private:
-  SSLClientCertificateSelectorCocoa* controller_;  // weak
-};
-
-namespace chrome {
-
-void ShowSSLClientCertificateSelectorCocoa(
-    content::WebContents* contents,
-    net::SSLCertRequestInfo* cert_request_info,
-    net::ClientCertIdentityList client_certs,
-    std::unique_ptr<content::ClientCertificateDelegate> delegate) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  // Not all WebContentses can show modal dialogs.
-  //
-  // Use the top-level embedder if |contents| is a guest.
-  // GetTopLevelWebContents() will return |contents| otherwise.
-  // TODO(davidben): Move this hook to the WebContentsDelegate and only try to
-  // show a dialog in Browser's implementation. https://crbug.com/456255
-  if (web_modal::WebContentsModalDialogManager::FromWebContents(
-          guest_view::GuestViewBase::GetTopLevelWebContents(contents)) ==
-      nullptr)
-    return;
-
-  // The dialog manages its own lifetime.
-  SSLClientCertificateSelectorCocoa* selector =
-      [[SSLClientCertificateSelectorCocoa alloc]
-          initWithBrowserContext:contents->GetBrowserContext()
-                 certRequestInfo:cert_request_info
-                        delegate:std::move(delegate)];
-  [selector displayForWebContents:contents clientCerts:std::move(client_certs)];
-}
-
-}  // namespace chrome
-
-namespace {
-
-// These ClearTableViewDataSources... functions help work around a bug in macOS
-// 10.12 where SFChooseIdentityPanel leaks a window and some views, including
-// an NSTableView. Future events may make cause the table view to query its
-// dataSource, which will have been deallocated.
-//
-// Linking against the 10.12 SDK does not "fix" this issue, since
-// NSTableView.dataSource is a "weak" reference, which in non-ARC land still
-// translates to "raw pointer".
-//
-// See https://crbug.com/653093, https://crbug.com/750242 and rdar://29409207
-// for more information.
-
-void ClearTableViewDataSources(NSView* view) {
-  if (auto table_view = base::mac::ObjCCast<NSTableView>(view)) {
-    table_view.dataSource = nil;
-  } else {
-    for (NSView* subview in view.subviews) {
-      ClearTableViewDataSources(subview);
-    }
-  }
-}
-
-void ClearTableViewDataSourcesIfWindowStillExists(NSWindow* leaked_window) {
-  for (NSWindow* window in [NSApp windows]) {
-    // If the window is still in the window list...
-    if (window == leaked_window) {
-      // ...search it for table views.
-      ClearTableViewDataSources(window.contentView);
-      break;
-    }
-  }
-}
-
-void ClearTableViewDataSourcesIfNeeded(NSWindow* leaked_window) {
-  // Let the autorelease pool drain before deciding if the window was leaked.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(ClearTableViewDataSourcesIfWindowStillExists,
-                            base::Unretained(leaked_window)));
-}
-
-}  // namespace
-
-@implementation SSLClientCertificateSelectorCocoa
-
-- (id)initWithBrowserContext:(const content::BrowserContext*)browserContext
-             certRequestInfo:(net::SSLCertRequestInfo*)certRequestInfo
-                    delegate:
-                        (std::unique_ptr<content::ClientCertificateDelegate>)
-                            delegate {
-  DCHECK(browserContext);
-  DCHECK(certRequestInfo);
-  if ((self = [super init])) {
-    observer_.reset(new SSLClientAuthObserverCocoaBridge(
-        browserContext, certRequestInfo, std::move(delegate), self));
-  }
-  return self;
-}
-
-- (void)sheetDidEnd:(NSWindow*)sheet
-         returnCode:(NSInteger)returnCode
-            context:(void*)context {
-  net::ClientCertIdentity* cert = nullptr;
-  if (returnCode == NSFileHandlingPanelOKButton) {
-    CFRange range = CFRangeMake(0, CFArrayGetCount(sec_identities_));
-    CFIndex index =
-        CFArrayGetFirstIndexOfValue(sec_identities_, range, [panel_ identity]);
-    if (index != -1)
-      cert = cert_identities_[index].get();
-    else
-      NOTREACHED();
-  }
-
-  // See comment at definition; this works around a 10.12 bug.
-  ClearTableViewDataSourcesIfNeeded(sheet);
-
-  if (!closePending_) {
-    // If |closePending_| is already set, |closeSheetWithAnimation:| was called
-    // already to cancel the selection rather than continue with no
-    // certificate. Otherwise, tell the backend which identity (or none) the
-    // user selected.
-    userResponded_ = YES;
-
-    if (cert) {
-      observer_->CertificateSelected(
-          cert->certificate(),
-          CreateSSLPrivateKeyForSecIdentity(cert->certificate(),
-                                            cert->sec_identity_ref())
-              .get());
-    } else {
-      observer_->CertificateSelected(nullptr, nullptr);
-    }
-
-    constrainedWindow_->CloseWebContentsModalDialog();
-  }
-}
-
-- (void)displayForWebContents:(content::WebContents*)webContents
-                  clientCerts:(net::ClientCertIdentityList)inputClientCerts {
-  cert_identities_ = std::move(inputClientCerts);
-  // Create an array of CFIdentityRefs for the certificates:
-  size_t numCerts = cert_identities_.size();
-  sec_identities_.reset(CFArrayCreateMutable(kCFAllocatorDefault, numCerts,
-                                             &kCFTypeArrayCallBacks));
-  for (size_t i = 0; i < numCerts; ++i) {
-    DCHECK(cert_identities_[i]->sec_identity_ref());
-    CFArrayAppendValue(sec_identities_,
-                       cert_identities_[i]->sec_identity_ref());
-  }
-
-  // Get the message to display:
-  NSString* message = l10n_util::GetNSStringF(
-      IDS_CLIENT_CERT_DIALOG_TEXT,
-      base::ASCIIToUTF16(
-          observer_->cert_request_info()->host_and_port.ToString()));
-
-  // Create and set up a system choose-identity panel.
-  panel_.reset([[SFChooseIdentityPanel alloc] init]);
-  [panel_ setInformativeText:message];
-  [panel_ setDefaultButtonTitle:l10n_util::GetNSString(IDS_OK)];
-  [panel_ setAlternateButtonTitle:l10n_util::GetNSString(IDS_CANCEL)];
-  SecPolicyRef sslPolicy;
-  if (net::x509_util::CreateSSLClientPolicy(&sslPolicy) == noErr) {
-    [panel_ setPolicies:(id)sslPolicy];
-    CFRelease(sslPolicy);
-  }
-
-  constrainedWindow_ =
-      CreateAndShowWebModalDialogMac(observer_.get(), webContents, self);
-  observer_->StartObserving();
-}
-
-- (void)closeWebContentsModalDialog {
-  DCHECK(constrainedWindow_);
-  constrainedWindow_->CloseWebContentsModalDialog();
-}
-
-- (NSWindow*)overlayWindow {
-  return overlayWindow_;
-}
-
-- (SFChooseIdentityPanel*)panel {
-  return panel_;
-}
-
-- (void)showSheetForWindow:(NSWindow*)window {
-  NSString* title = l10n_util::GetNSString(IDS_CLIENT_CERT_DIALOG_TITLE);
-  overlayWindow_.reset([window retain]);
-  [panel_ beginSheetForWindow:window
-                modalDelegate:self
-               didEndSelector:@selector(sheetDidEnd:returnCode:context:)
-                  contextInfo:NULL
-                   identities:base::mac::CFToNSCast(sec_identities_)
-                      message:title];
-}
-
-- (void)closeSheetWithAnimation:(BOOL)withAnimation {
-  if (!userResponded_) {
-    // If the sheet is closed by closing the tab rather than the user explicitly
-    // hitting Cancel, |closeSheetWithAnimation:| gets called before
-    // |sheetDidEnd:|. In this case, the selection should be canceled rather
-    // than continue with no certificate. The |returnCode| parameter to
-    // |sheetDidEnd:| is the same in both cases.
-    observer_->CancelCertificateSelection();
-  }
-  closePending_ = YES;
-  overlayWindow_.reset();
-  // Closing the sheet using -[NSApp endSheet:] doesn't work so use the private
-  // method.
-  [panel_ _dismissWithCode:NSFileHandlingPanelCancelButton];
-}
-
-- (void)hideSheet {
-  NSWindow* sheetWindow = [overlayWindow_ attachedSheet];
-  [sheetWindow setAlphaValue:0.0];
-  [sheetWindow setIgnoresMouseEvents:YES];
-
-  oldResizesSubviews_ = [[sheetWindow contentView] autoresizesSubviews];
-  [[sheetWindow contentView] setAutoresizesSubviews:NO];
-}
-
-- (void)unhideSheet {
-  NSWindow* sheetWindow = [overlayWindow_ attachedSheet];
-  [[sheetWindow contentView] setAutoresizesSubviews:oldResizesSubviews_];
-  [sheetWindow setAlphaValue:1.0];
-  [sheetWindow setIgnoresMouseEvents:NO];
-}
-
-- (void)pulseSheet {
-  // NOOP
-}
-
-- (void)makeSheetKeyAndOrderFront {
-  [[overlayWindow_ attachedSheet] makeKeyAndOrderFront:nil];
-}
-
-- (void)updateSheetPosition {
-  // NOOP
-}
-
-- (void)resizeWithNewSize:(NSSize)size {
-  // NOOP
-}
-
-- (NSWindow*)sheetWindow {
-  return panel_;
-}
-
-- (void)onConstrainedWindowClosed {
-  observer_->StopObserving();
-  panel_.reset();
-  constrainedWindow_.reset();
-  [self release];
-}
-
-@end
diff --git a/chrome/browser/ui/cocoa/tab_contents/chrome_web_contents_view_delegate_mac.mm b/chrome/browser/ui/cocoa/tab_contents/chrome_web_contents_view_delegate_mac.mm
index 49b6cc4..791ba1d 100644
--- a/chrome/browser/ui/cocoa/tab_contents/chrome_web_contents_view_delegate_mac.mm
+++ b/chrome/browser/ui/cocoa/tab_contents/chrome_web_contents_view_delegate_mac.mm
@@ -13,7 +13,6 @@
 #include "chrome/browser/ui/cocoa/renderer_context_menu/render_view_context_menu_mac_cocoa.h"
 #include "chrome/browser/ui/cocoa/tab_contents/web_drag_bookmark_handler_mac.h"
 #include "chrome/browser/ui/tab_contents/chrome_web_contents_view_delegate.h"
-#include "chrome/browser/ui/views_mode_controller.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
 #import "ui/base/cocoa/focus_tracker.h"
diff --git a/chrome/browser/ui/cocoa/task_manager_mac.mm b/chrome/browser/ui/cocoa/task_manager_mac.mm
index c533fef..9bee538 100644
--- a/chrome/browser/ui/cocoa/task_manager_mac.mm
+++ b/chrome/browser/ui/cocoa/task_manager_mac.mm
@@ -22,7 +22,6 @@
 #include "chrome/browser/ui/browser_dialogs.h"
 #import "chrome/browser/ui/cocoa/window_size_autosaver.h"
 #include "chrome/browser/ui/task_manager/task_manager_columns.h"
-#include "chrome/browser/ui/views_mode_controller.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
diff --git a/chrome/browser/ui/cocoa/web_contents_modal_dialog_host_cocoa.h b/chrome/browser/ui/cocoa/web_contents_modal_dialog_host_cocoa.h
deleted file mode 100644
index 9022d48..0000000
--- a/chrome/browser/ui/cocoa/web_contents_modal_dialog_host_cocoa.h
+++ /dev/null
@@ -1,38 +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.
-
-#ifndef CHROME_BROWSER_UI_COCOA_WEB_CONTENTS_MODAL_DIALOG_HOST_COCOA_H_
-#define CHROME_BROWSER_UI_COCOA_WEB_CONTENTS_MODAL_DIALOG_HOST_COCOA_H_
-
-#include "base/macros.h"
-#include "components/web_modal/web_contents_modal_dialog_host.h"
-
-@class ConstrainedWindowSheetController;
-
-// In a Cocoa browser, the modal dialog host is a simple bridge to the
-// ConstrainedWindowSheetController to get the dialog size and parent window.
-class WebContentsModalDialogHostCocoa
-    : public web_modal::WebContentsModalDialogHost {
- public:
-  explicit WebContentsModalDialogHostCocoa(
-      ConstrainedWindowSheetController* sheet_controller);
-  ~WebContentsModalDialogHostCocoa() override;
-
-  // web_modal::ModalDialogHost:
-  gfx::NativeView GetHostView() const override;
-  gfx::Point GetDialogPosition(const gfx::Size& size) override;
-  bool ShouldActivateDialog() const override;
-  void AddObserver(web_modal::ModalDialogHostObserver* observer) override;
-  void RemoveObserver(web_modal::ModalDialogHostObserver* observer) override;
-
-  // web_modal::WebContentsModalDialogHost:
-  gfx::Size GetMaximumDialogSize() override;
-
- private:
-  ConstrainedWindowSheetController* sheet_controller_;  // Weak. Owns |this|.
-
-  DISALLOW_COPY_AND_ASSIGN(WebContentsModalDialogHostCocoa);
-};
-
-#endif  // CHROME_BROWSER_UI_COCOA_WEB_CONTENTS_MODAL_DIALOG_HOST_COCOA_H_
diff --git a/chrome/browser/ui/cocoa/web_contents_modal_dialog_host_cocoa.mm b/chrome/browser/ui/cocoa/web_contents_modal_dialog_host_cocoa.mm
deleted file mode 100644
index a95978ea..0000000
--- a/chrome/browser/ui/cocoa/web_contents_modal_dialog_host_cocoa.mm
+++ /dev/null
@@ -1,68 +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 "chrome/browser/ui/cocoa/web_contents_modal_dialog_host_cocoa.h"
-
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/tab_dialogs.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h"
-#include "ui/gfx/geometry/point.h"
-#include "ui/gfx/geometry/size.h"
-
-WebContentsModalDialogHostCocoa::WebContentsModalDialogHostCocoa(
-    ConstrainedWindowSheetController* sheet_controller)
-    : sheet_controller_(sheet_controller) {
-}
-
-WebContentsModalDialogHostCocoa::~WebContentsModalDialogHostCocoa() {
-  // Toolkit-Views calls OnHostDestroying on observers here, but the Cocoa host
-  // doesn't need to be observed.
-}
-
-gfx::NativeView WebContentsModalDialogHostCocoa::GetHostView() const {
-  // To avoid the constrained window controller having to know about the browser
-  // view layout, use the active tab in the parent window.
-  NSWindow* parent_window = [sheet_controller_ parentWindow];
-  Browser* browser = chrome::FindBrowserWithWindow(parent_window);
-  // This could be null for packaged app windows, but this dialog host is
-  // currently only used for browsers.
-  DCHECK(browser);
-  content::WebContents* web_contents =
-      browser->tab_strip_model()->GetActiveWebContents();
-  DCHECK(web_contents);
-  TabDialogs* tab_dialogs = TabDialogs::FromWebContents(web_contents);
-  DCHECK(tab_dialogs);
-
-  // Note this returns the WebContents' superview, so it doesn't really matter
-  // which WebContents inside the browser we actually chose above.
-  return tab_dialogs->GetDialogParentView();
-}
-
-gfx::Point WebContentsModalDialogHostCocoa::GetDialogPosition(
-    const gfx::Size& size) {
-  // Dialogs are always re-positioned by the constrained window sheet controller
-  // so nothing interesting to return yet.
-  return gfx::Point();
-}
-
-bool WebContentsModalDialogHostCocoa::ShouldActivateDialog() const {
-  return [[sheet_controller_ parentWindow] isMainWindow];
-}
-
-void WebContentsModalDialogHostCocoa::AddObserver(
-    web_modal::ModalDialogHostObserver* observer) {
-  NOTREACHED();
-}
-void WebContentsModalDialogHostCocoa::RemoveObserver(
-    web_modal::ModalDialogHostObserver* observer) {
-  NOTREACHED();
-}
-
-gfx::Size WebContentsModalDialogHostCocoa::GetMaximumDialogSize() {
-  // The dialog should try to fit within the overlay for the web contents.
-  // Note that, for things like print preview, this is just a suggested maximum.
-  return gfx::Size([sheet_controller_
-      overlayWindowSizeForParentView:GetHostView().GetNativeNSView()]);
-}
diff --git a/chrome/browser/ui/tests/ui_gfx_image_unittest.cc b/chrome/browser/ui/tests/ui_gfx_image_unittest.cc
deleted file mode 100644
index 99d24e4..0000000
--- a/chrome/browser/ui/tests/ui_gfx_image_unittest.cc
+++ /dev/null
@@ -1,33 +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.
-
-#include <memory>
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/gfx/image/image.h"
-#include "ui/gfx/image/image_unittest_util.h"
-
-#if defined(TOOLKIT_VIEWS)
-#include "ui/views/controls/image_view.h"
-#include "ui/views/view.h"
-#endif
-
-namespace {
-
-#if defined(TOOLKIT_VIEWS)
-TEST(UiGfxImageTest, ViewsImageView) {
-  gfx::Image image(gfx::test::CreatePlatformImage());
-
-  std::unique_ptr<views::View> container(new views::View());
-  container->SetBounds(0, 0, 200, 200);
-  container->SetVisible(true);
-
-  std::unique_ptr<views::ImageView> image_view(new views::ImageView());
-  image_view->SetImage(*image.ToImageSkia());
-  container->AddChildView(image_view.get());
-}
-#endif
-
-}  // namespace
diff --git a/chrome/browser/ui/tests/ui_gfx_image_unittest.mm b/chrome/browser/ui/tests/ui_gfx_image_unittest.mm
deleted file mode 100644
index ca25ebe..0000000
--- a/chrome/browser/ui/tests/ui_gfx_image_unittest.mm
+++ /dev/null
@@ -1,57 +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 <AppKit/AppKit.h>
-
-#import "base/mac/mac_util.h"
-#include "base/mac/scoped_nsobject.h"
-#import "chrome/browser/ui/cocoa/test/cocoa_test_helper.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/gfx/image/image.h"
-#include "ui/gfx/image/image_unittest_util.h"
-
-namespace {
-
-class UiGfxImageTest : public CocoaTest {
-};
-
-// http://crbug.com/247379
-TEST_F(UiGfxImageTest, DISABLED_CheckColor) {
-  // TODO(kbr): re-enable: http://crbug.com/222296
-  return;
-
-  gfx::Image image = gfx::Image::CreateFrom1xBitmap(
-      gfx::test::CreateBitmap(25, 25));
-  NSImage* ns_image = image.ToNSImage();
-  [ns_image lockFocus];
-  NSColor* color = NSReadPixel(NSMakePoint(10, 10));
-  [ns_image unlockFocus];
-
-  // SkBitmapToNSImage returns a bitmap in the calibrated color space (sRGB),
-  // while NSReadPixel returns a color in the device color space. Convert back
-  // to the calibrated color space before testing.
-  color = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
-
-  CGFloat components[4] = { 0 };
-  [color getComponents:components];
-
-  EXPECT_LT(components[0], 0.05);
-  EXPECT_GT(components[1], 0.95);
-  EXPECT_LT(components[2], 0.05);
-  EXPECT_GT(components[3], 0.95);
-}
-
-TEST_F(UiGfxImageTest, ImageView) {
-  base::scoped_nsobject<NSImageView> image_view(
-      [[NSImageView alloc] initWithFrame:NSMakeRect(10, 10, 25, 25)]);
-  [[test_window() contentView] addSubview:image_view];
-  [test_window() orderFront:nil];
-
-  gfx::Image image = gfx::Image::CreateFrom1xBitmap(
-      gfx::test::CreateBitmap(25, 25));
-  [image_view setImage:image.ToNSImage()];
-}
-
-}  // namespace
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
index 7a3dc11b..29e612b 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
@@ -28,6 +28,8 @@
 #include "chrome/browser/ui/ash/tablet_mode_client.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
 #include "chrome/browser/ui/views/exclusive_access_bubble_views.h"
+#include "components/session_manager/core/session_manager.h"
+#include "extensions/common/constants.h"
 #include "services/ws/public/cpp/property_type_converters.h"
 #include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/aura/client/aura_constants.h"
@@ -53,6 +55,22 @@
 
 using extensions::AppWindow;
 
+namespace {
+
+// The feedback dialog is modal during OOBE and login because it must stay above
+// the views login UI and the webui GAIA login dialog.
+bool IsLoginFeedbackModalDialog(const AppWindow* app_window) {
+  if (app_window->extension_id() != extension_misc::kFeedbackExtensionId)
+    return false;
+
+  using session_manager::SessionState;
+  SessionState state = session_manager::SessionManager::Get()->session_state();
+  return state == SessionState::OOBE || state == SessionState::LOGIN_PRIMARY ||
+         state == SessionState::LOGIN_SECONDARY;
+}
+
+}  // namespace
+
 ChromeNativeAppWindowViewsAuraAsh::ChromeNativeAppWindowViewsAuraAsh()
     : exclusive_access_manager_(
           std::make_unique<ExclusiveAccessManager>(this)) {
@@ -60,7 +78,10 @@
     TabletModeClient::Get()->AddObserver(this);
 
   if (features::IsSingleProcessMash()) {
-    MultiUserWindowManager::GetInstance()->AddObserver(this);
+    // There is no MultiUserWindowManager at the login screen, but users can
+    // open the feedback app.
+    if (MultiUserWindowManager::GetInstance())
+      MultiUserWindowManager::GetInstance()->AddObserver(this);
 
     ash_window_manager_ =
         views::MusClient::Get()
@@ -73,7 +94,7 @@
   if (TabletModeClient::Get())
     TabletModeClient::Get()->RemoveObserver(this);
 
-  if (features::IsSingleProcessMash())
+  if (features::IsSingleProcessMash() && MultiUserWindowManager::GetInstance())
     MultiUserWindowManager::GetInstance()->RemoveObserver(this);
 }
 
@@ -106,14 +127,19 @@
     views::Widget* widget) {
   ChromeNativeAppWindowViewsAura::OnBeforeWidgetInit(create_params, init_params,
                                                      widget);
-  if (create_params.is_ime_window || create_params.show_on_lock_screen) {
-    // Put ime windows and lock screen windows into their respective window
-    // containers on the primary display.
-    int container_id = create_params.is_ime_window
-                           ? ash::kShellWindowId_ImeWindowParentContainer
-                           : ash::kShellWindowId_LockActionHandlerContainer;
-    ash_util::SetupWidgetInitParamsForContainer(init_params, container_id);
-  }
+  // Some windows need to be placed in special containers, for example to make
+  // them visible at the login or lock screen.
+  base::Optional<int> container_id;
+  if (IsLoginFeedbackModalDialog(app_window()))
+    container_id = ash::kShellWindowId_LockSystemModalContainer;
+  else if (create_params.is_ime_window)
+    container_id = ash::kShellWindowId_ImeWindowParentContainer;
+  else if (create_params.show_on_lock_screen)
+    container_id = ash::kShellWindowId_LockActionHandlerContainer;
+
+  if (container_id.has_value())
+    ash_util::SetupWidgetInitParamsForContainer(init_params, *container_id);
+
   if (HasFrameColor()) {
     init_params
         ->mus_properties[ws::mojom::WindowManager::kFrameActiveColor_Property] =
@@ -147,6 +173,12 @@
   return frame;
 }
 
+ui::ModalType ChromeNativeAppWindowViewsAuraAsh::GetModalType() const {
+  if (IsLoginFeedbackModalDialog(app_window()))
+    return ui::MODAL_TYPE_SYSTEM;
+  return ChromeNativeAppWindowViewsAura::GetModalType();
+}
+
 bool ChromeNativeAppWindowViewsAuraAsh::ShouldRemoveStandardFrame() {
   if (IsFrameless())
     return true;
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.h b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.h
index 398986bb..3ee9e127 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.h
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.h
@@ -78,6 +78,7 @@
   // WidgetDelegate:
   views::NonClientFrameView* CreateNonClientFrameView(
       views::Widget* widget) override;
+  ui::ModalType GetModalType() const override;
 
   // NativeAppWindow:
   void SetFullscreen(int fullscreen_types) override;
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.cc b/chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.cc
index f206f80..00e3a5c 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
-#include "chrome/browser/ui/views_mode_controller.h"
 #include "chrome/grit/platform_locale_settings.h"
 #include "components/bookmarks/browser/base_bookmark_model_observer.h"
 #include "components/bookmarks/browser/bookmark_model.h"
diff --git a/chrome/browser/ui/views/browser_dialogs_views.cc b/chrome/browser/ui/views/browser_dialogs_views.cc
index e1037fc..57906f4 100644
--- a/chrome/browser/ui/views/browser_dialogs_views.cc
+++ b/chrome/browser/ui/views/browser_dialogs_views.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/extensions/extension_install_prompt.h"
 #include "chrome/browser/ui/login/login_handler.h"
 #include "chrome/browser/ui/views/task_manager_view.h"
-#include "chrome/browser/ui/views_mode_controller.h"
 
 // This file provides definitions of desktop browser dialog-creation methods for
 // all toolkit-views platforms.
diff --git a/chrome/browser/ui/views/find_bar_host_unittest_util_views.cc b/chrome/browser/ui/views/find_bar_host_unittest_util_views.cc
index a01d9d7a..e6da4b1 100644
--- a/chrome/browser/ui/views/find_bar_host_unittest_util_views.cc
+++ b/chrome/browser/ui/views/find_bar_host_unittest_util_views.cc
@@ -7,7 +7,6 @@
 #include "build/build_config.h"
 #include "build/buildflag.h"
 #include "chrome/browser/ui/views/dropdown_bar_host.h"
-#include "chrome/browser/ui/views_mode_controller.h"
 #include "ui/base/ui_base_features.h"
 
 namespace chrome {
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 79cccae..66ac843b 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -66,6 +66,7 @@
 class ToolbarButtonProvider;
 class ToolbarView;
 class TopContainerView;
+class TopControlsSlideControllerTest;
 class WebContentsCloseHandler;
 
 namespace extensions {
@@ -540,6 +541,7 @@
   // Do not friend BrowserViewLayout. Use the BrowserViewLayoutDelegate
   // interface to keep these two classes decoupled and testable.
   friend class BrowserViewLayoutDelegateImpl;
+  friend class TopControlsSlideControllerTest;
   FRIEND_TEST_ALL_PREFIXES(BrowserViewTest, BrowserView);
   FRIEND_TEST_ALL_PREFIXES(BrowserViewTest, AccessibleWindowTitle);
 
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
index e8d7e90..29718048 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
@@ -296,19 +296,23 @@
 void ImmersiveModeControllerAsh::OnWindowPropertyChanged(aura::Window* window,
                                                          const void* key,
                                                          intptr_t old) {
-  if (key == ash::kWindowStateTypeKey) {
-    ash::mojom::WindowStateType new_state =
-        window->GetProperty(ash::kWindowStateTypeKey);
-    ash::mojom::WindowStateType old_state =
-        static_cast<ash::mojom::WindowStateType>(old);
+  // Track locked fullscreen changes.
+  if (key == ash::kWindowPinTypeKey) {
+    browser_view_->FullscreenStateChanged();
+    return;
+  }
+
+  if (key == aura::client::kShowStateKey) {
+    ui::WindowShowState new_state =
+        window->GetProperty(aura::client::kShowStateKey);
+    auto old_state = static_cast<ui::WindowShowState>(old);
 
     // Make sure the browser stays up to date with the window's state. This is
     // necessary in classic Ash if the user exits fullscreen with the restore
     // button, and it's necessary in OopAsh if the window manager initiates a
     // fullscreen mode change (e.g. due to a WM shortcut).
-    if (controller_->IsEnabled() &&
-        (ash::IsFullscreenOrPinnedWindowStateType(old_state) ||
-         ash::IsFullscreenOrPinnedWindowStateType(new_state))) {
+    if (new_state == ui::SHOW_STATE_FULLSCREEN ||
+        old_state == ui::SHOW_STATE_FULLSCREEN) {
       // If the browser view initiated this state change,
       // BrowserView::ProcessFullscreen will no-op, so this call is harmless.
       browser_view_->FullscreenStateChanged();
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc
index 12bfdfc8..f50cb0f9 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc
@@ -21,12 +21,13 @@
 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h"
 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h"
 #include "chrome/browser/ui/views/frame/test_with_browser_view.h"
 #include "chrome/browser/ui/views/frame/top_container_view.h"
+#include "chrome/browser/ui/views/fullscreen_control/fullscreen_control_host.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "ui/aura/window.h"
+#include "ui/events/event.h"
 #include "ui/views/controls/webview/webview.h"
 
 class ImmersiveModeControllerAshTest : public TestWithBrowserView {
@@ -102,6 +103,11 @@
 
   ImmersiveModeController* controller() { return controller_; }
 
+  ash::ShelfLayoutManager* shelf() {
+    return ash::Shell::GetPrimaryRootWindowController()
+        ->GetShelfLayoutManager();
+  }
+
  private:
   // Not owned.
   ImmersiveModeController* controller_;
@@ -229,31 +235,29 @@
   AddTab(browser(), GURL("about:blank"));
 
   // The shelf should start out as visible.
-  ash::ShelfLayoutManager* shelf =
-      ash::Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
-  ASSERT_EQ(ash::SHELF_VISIBLE, shelf->visibility_state());
+  ASSERT_EQ(ash::SHELF_VISIBLE, shelf()->visibility_state());
 
   // 1) Test that entering tab fullscreen from immersive fullscreen hides
   // the shelf.
   ToggleFullscreen();
   ASSERT_TRUE(controller()->IsEnabled());
-  EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state());
+  EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf()->visibility_state());
 
   SetTabFullscreen(true);
   ASSERT_TRUE(controller()->IsEnabled());
-  EXPECT_EQ(ash::SHELF_HIDDEN, shelf->visibility_state());
+  EXPECT_EQ(ash::SHELF_HIDDEN, shelf()->visibility_state());
 
   // 2) Test that exiting tab fullscreen autohides the shelf.
   SetTabFullscreen(false);
   ASSERT_TRUE(controller()->IsEnabled());
-  EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state());
+  EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf()->visibility_state());
 
   // 3) Test that exiting tab fullscreen and immersive fullscreen correctly
   // updates the shelf visibility.
   SetTabFullscreen(true);
   ToggleFullscreen();
   ASSERT_FALSE(controller()->IsEnabled());
-  EXPECT_EQ(ash::SHELF_VISIBLE, shelf->visibility_state());
+  EXPECT_EQ(ash::SHELF_VISIBLE, shelf()->visibility_state());
 }
 
 // Ensure the circular tab-loading throbbers are not painted as layers in
@@ -278,11 +282,47 @@
   EXPECT_TRUE(tabstrip->CanPaintThrobberToLayer());
 }
 
-// Make sure that going from regular fullscreen to locked fullscreen does not
-// cause a crash. crbug.com/796171
-TEST_F(ImmersiveModeControllerAshTest, RegularFullscreenToLockedFullscreen) {
+// Regression test for crbug.com/796171.  Make sure that going from regular
+// fullscreen to locked fullscreen does not cause a crash.
+// Also test that the immersive mode is disabled afterwards (and the shelf is
+// hidden, and the fullscreen control popup doesn't show up).
+TEST_F(ImmersiveModeControllerAshTest,
+       RegularToLockedFullscreenDisablesImmersive) {
   ToggleFullscreen();
   // Set locked fullscreen state.
   browser()->window()->GetNativeWindow()->SetProperty(
       ash::kWindowPinTypeKey, ash::mojom::WindowPinType::TRUSTED_PINNED);
+
+  // We're fullscreen, immersive is disabled in locked fullscreen, and while
+  // we're at it, also make sure that the shelf is hidden.
+  EXPECT_TRUE(browser_view()->GetWidget()->IsFullscreen());
+  EXPECT_FALSE(controller()->IsEnabled());
+  EXPECT_EQ(ash::SHELF_HIDDEN, shelf()->visibility_state());
+
+  // Make sure the fullscreen control popup doesn't show up.
+  ui::MouseEvent mouse_move(ui::ET_MOUSE_MOVED, gfx::Point(1, 1), gfx::Point(),
+                            base::TimeTicks(), 0, 0);
+  browser_view()->fullscreen_control_host_for_test()->OnMouseEvent(mouse_move);
+  EXPECT_FALSE(browser_view()->fullscreen_control_host_for_test()->IsVisible());
+}
+
+// Regression test for crbug.com/883104.  Make sure that immersive fullscreen is
+// disabled in locked fullscreen mode (also the shelf is hidden, and the
+// fullscreen control popup doesn't show up).
+TEST_F(ImmersiveModeControllerAshTest, LockedFullscreenDisablesImmersive) {
+  // Set locked fullscreen state.
+  browser()->window()->GetNativeWindow()->SetProperty(
+      ash::kWindowPinTypeKey, ash::mojom::WindowPinType::TRUSTED_PINNED);
+
+  // We're fullscreen, immersive is disabled in locked fullscreen, and while
+  // we're at it, also make sure that the shelf is hidden.
+  EXPECT_TRUE(browser_view()->GetWidget()->IsFullscreen());
+  EXPECT_FALSE(controller()->IsEnabled());
+  EXPECT_EQ(ash::SHELF_HIDDEN, shelf()->visibility_state());
+
+  // Make sure the fullscreen control popup doesn't show up.
+  ui::MouseEvent mouse_move(ui::ET_MOUSE_MOVED, gfx::Point(1, 1), gfx::Point(),
+                            base::TimeTicks(), 0, 0);
+  browser_view()->fullscreen_control_host_for_test()->OnMouseEvent(mouse_move);
+  EXPECT_FALSE(browser_view()->fullscreen_control_host_for_test()->IsVisible());
 }
diff --git a/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos.cc b/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos.cc
index f4ae76e..74948320 100644
--- a/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos.cc
+++ b/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos.cc
@@ -38,6 +38,13 @@
          TabletModeClient::Get()->tablet_mode_enabled();
 }
 
+bool IsSpokenFeedbackEnabled() {
+  chromeos::AccessibilityManager* accessibility_manager =
+      chromeos::AccessibilityManager::Get();
+  return accessibility_manager &&
+         accessibility_manager->IsSpokenFeedbackEnabled();
+}
+
 // Based on the current status of |contents|, returns the browser top controls
 // shown state constraints, which specifies if the top controls are allowed to
 // be only shown, or either shown or hidden.
@@ -51,7 +58,7 @@
   if (!IsTabletModeEnabled() || contents->IsFullscreen() ||
       contents->IsFocusedElementEditable() ||
       contents->ShowingInterstitialPage() || contents->IsBeingDestroyed() ||
-      contents->IsCrashed()) {
+      contents->IsCrashed() || IsSpokenFeedbackEnabled()) {
     return content::BROWSER_CONTROLS_STATE_SHOWN;
   }
 
@@ -302,7 +309,16 @@
 
   browser_view_->browser()->tab_strip_model()->AddObserver(this);
 
-  OnEnabledStateChanged(IsTabletModeEnabled() && !browser_view->IsFullscreen());
+  chromeos::AccessibilityManager* accessibility_manager =
+      chromeos::AccessibilityManager::Get();
+  if (accessibility_manager) {
+    accessibility_status_subscription_ =
+        accessibility_manager->RegisterCallback(base::BindRepeating(
+            &TopControlsSlideControllerChromeOS::OnAccessibilityStatusChanged,
+            base::Unretained(this)));
+  }
+
+  OnEnabledStateChanged(CanEnable(base::nullopt));
 }
 
 TopControlsSlideControllerChromeOS::~TopControlsSlideControllerChromeOS() {
@@ -344,8 +360,12 @@
   // Even during those two small windows, the
   // `DoBrowserControlsShrinkRendererSize` bit should remain unchanged from its
   // current value until sliding reaches a steady state.
-  observed_tabs_[contents]->SetShownRatio(
-      ratio, is_gesture_scrolling_in_progress_ || is_sliding_in_progress_);
+  // Make sure it doesn't get updated if sliding is about to start.
+  const bool sliding_or_scrolling_in_progress =
+      is_gesture_scrolling_in_progress_ || is_sliding_in_progress_ ||
+      (IsEnabled() && ratio != 0.f && ratio != 1.f);
+  observed_tabs_[contents]->SetShownRatio(ratio,
+                                          sliding_or_scrolling_in_progress);
 
   if (!IsEnabled()) {
     // However, if sliding is disabled, we don't update |shown_ratio_|, which is
@@ -369,14 +389,13 @@
     defer_disabling_ = false;
 
     // Don't just set |is_enabled_| to false. Make sure it's a correct value.
-    OnEnabledStateChanged(IsTabletModeEnabled() &&
-                          !browser_view_->IsFullscreen());
+    OnEnabledStateChanged(CanEnable(base::nullopt));
   }
 }
 
 void TopControlsSlideControllerChromeOS::OnBrowserFullscreenStateWillChange(
     bool new_fullscreen_state) {
-  OnEnabledStateChanged(IsTabletModeEnabled() && !new_fullscreen_state);
+  OnEnabledStateChanged(CanEnable(new_fullscreen_state));
 }
 
 bool TopControlsSlideControllerChromeOS::DoBrowserControlsShrinkRendererSize(
@@ -418,7 +437,7 @@
 
 void TopControlsSlideControllerChromeOS::OnTabletModeToggled(
     bool tablet_mode_enabled) {
-  OnEnabledStateChanged(tablet_mode_enabled && !browser_view_->IsFullscreen());
+  OnEnabledStateChanged(CanEnable(base::nullopt));
 }
 
 void TopControlsSlideControllerChromeOS::TabInsertedAt(
@@ -504,15 +523,31 @@
     UpdateBrowserControlsStateShown(active_contents, true /* animate */);
 }
 
+bool TopControlsSlideControllerChromeOS::CanEnable(
+    base::Optional<bool> fullscreen_state) const {
+  return IsTabletModeEnabled() &&
+         !(fullscreen_state.value_or(browser_view_->IsFullscreen()));
+}
+
+void TopControlsSlideControllerChromeOS::OnAccessibilityStatusChanged(
+    const chromeos::AccessibilityStatusEventDetails& event_details) {
+  if (event_details.notification_type !=
+      chromeos::ACCESSIBILITY_TOGGLE_SPOKEN_FEEDBACK) {
+    return;
+  }
+
+  content::WebContents* active_contents = browser_view_->GetActiveWebContents();
+  if (active_contents)
+    UpdateBrowserControlsStateShown(active_contents, true /* animate */);
+}
+
 void TopControlsSlideControllerChromeOS::OnEnabledStateChanged(bool new_state) {
   if (new_state == is_enabled_)
     return;
 
   is_enabled_ = new_state;
 
-  TabStripModel* tab_strip_model = browser_view_->browser()->tab_strip_model();
-  content::WebContents* active_contents =
-      tab_strip_model->GetActiveWebContents();
+  content::WebContents* active_contents = browser_view_->GetActiveWebContents();
   if (!active_contents)
     return;
 
diff --git a/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos.h b/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos.h
index ff1a1262..a6ee438 100644
--- a/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos.h
+++ b/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos.h
@@ -9,6 +9,8 @@
 
 #include "base/containers/flat_map.h"
 #include "base/macros.h"
+#include "base/optional.h"
+#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/ui/ash/tablet_mode_client_observer.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "chrome/browser/ui/views/frame/top_controls_slide_controller.h"
@@ -82,6 +84,21 @@
                const content::NotificationDetails& details) override;
 
  private:
+  // Returns true if this feature can be turned on. If |fullscreen_state| is
+  // supplied, it will be used in calculating the result, otherwise the current
+  // fullscreen state will be queried from BrowserView. This is needed since
+  // BrowserView informs us with fullscreen state changes before they happen
+  // (See OnBrowserFullscreenStateWillChange()) so that we can disable the
+  // sliding behavior *before* immersive mode is entered.
+  bool CanEnable(base::Optional<bool> fullscreen_state) const;
+
+  // Called back from the AccessibilityManager so that we're updated by the
+  // status of Chromevox, which when enabled, sliding the top-controls should
+  // be disabled. This is important for users who want to touch explore and need
+  // this to be consistent.
+  void OnAccessibilityStatusChanged(
+      const chromeos::AccessibilityStatusEventDetails& event_details);
+
   void OnEnabledStateChanged(bool new_state);
 
   // Refreshes the status of the browser top controls.
@@ -148,6 +165,9 @@
 
   content::NotificationRegistrar registrar_;
 
+  std::unique_ptr<chromeos::AccessibilityStatusSubscription>
+      accessibility_status_subscription_;
+
   DISALLOW_COPY_AND_ASSIGN(TopControlsSlideControllerChromeOS);
 };
 
diff --git a/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos_browsertest.cc b/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos_browsertest.cc
index c1851e3..ddb18478 100644
--- a/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos_browsertest.cc
+++ b/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos_browsertest.cc
@@ -12,12 +12,12 @@
 #include "ash/public/interfaces/constants.mojom.h"
 #include "ash/public/interfaces/cros_display_config.mojom.h"
 #include "base/bind.h"
-#include "base/bind_helpers.h"
 #include "base/command_line.h"
 #include "base/path_service.h"
 #include "base/strings/safe_sprintf.h"
 #include "base/test/scoped_feature_list.h"
 #include "cc/base/math_util.h"
+#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/permissions/permission_request_impl.h"
 #include "chrome/browser/permissions/permission_request_manager.h"
 #include "chrome/browser/ui/ash/tablet_mode_client.h"
@@ -32,7 +32,6 @@
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "content/public/common/service_manager_connection.h"
 #include "content/public/test/browser_test_utils.h"
-#include "ipc/ipc_message_macros.h"
 #include "net/dns/mock_host_resolver.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/aura/window.h"
@@ -51,6 +50,11 @@
   kFullyHidden,
 };
 
+enum class ScrollDirection {
+  kUp,
+  kDown,
+};
+
 // Using the given |generator| and the start and end points, it generates a
 // gesture scroll sequence with appropriate velocity so that fling gesture
 // scrolls are generated.
@@ -104,13 +108,95 @@
   DISALLOW_COPY_AND_ASSIGN(TabNonEmptyPaintWaiter);
 };
 
+class TestControllerObserver {
+ public:
+  virtual void OnShownRatioChanged(float shown_ratio) = 0;
+  virtual void OnGestureScrollInProgressChanged(bool in_progress) = 0;
+
+ protected:
+  virtual ~TestControllerObserver() = default;
+};
+
+// Defines a wrapper around the real TopControlsSlideControllerChromeOS which
+// will be injected in the BrowserView. This is used to intercept the calls to
+// the real controller here in the tests witout affecting the production code.
+// An object of this class owns the instance of the real controller, and itself
+// is owned by the BrowserView (See
+// BrowserView::InjectTopControlsSlideControllerForTesting()).
+class TestController : public TopControlsSlideController {
+ public:
+  explicit TestController(
+      std::unique_ptr<TopControlsSlideController> real_controller)
+      : real_controller_(std::move(real_controller)) {
+    DCHECK(real_controller_);
+  }
+  ~TestController() override = default;
+
+  void AddObserver(TestControllerObserver* observer) {
+    observers_.AddObserver(observer);
+  }
+
+  void RemoveObserver(TestControllerObserver* observer) {
+    observers_.RemoveObserver(observer);
+  }
+
+  // TopControlsSlideController:
+  bool IsEnabled() const override { return real_controller_->IsEnabled(); }
+
+  float GetShownRatio() const override {
+    return real_controller_->GetShownRatio();
+  }
+
+  void SetShownRatio(content::WebContents* contents, float ratio) override {
+    real_controller_->SetShownRatio(contents, ratio);
+    for (auto& observer : observers_)
+      observer.OnShownRatioChanged(ratio);
+  }
+
+  void OnBrowserFullscreenStateWillChange(bool new_fullscreen_state) override {
+    real_controller_->OnBrowserFullscreenStateWillChange(new_fullscreen_state);
+  }
+
+  bool DoBrowserControlsShrinkRendererSize(
+      const content::WebContents* contents) const override {
+    return real_controller_->DoBrowserControlsShrinkRendererSize(contents);
+  }
+
+  void SetTopControlsGestureScrollInProgress(bool in_progress) override {
+    real_controller_->SetTopControlsGestureScrollInProgress(in_progress);
+    for (auto& observer : observers_)
+      observer.OnGestureScrollInProgressChanged(in_progress);
+  }
+
+  bool IsTopControlsGestureScrollInProgress() const override {
+    return real_controller_->IsTopControlsGestureScrollInProgress();
+  }
+
+ private:
+  std::unique_ptr<TopControlsSlideController> real_controller_;
+
+  base::ObserverList<TestControllerObserver>::Unchecked observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestController);
+};
+
 // Waits for a given terminal value (1.f or 0.f) of the browser top controls
 // shown ratio on a given browser window.
-class TopControlsShownRatioWaiter {
+class TopControlsShownRatioWaiter : public TestControllerObserver {
  public:
-  explicit TopControlsShownRatioWaiter(
-      const TopControlsSlideController* controller)
-      : controller_(controller) {}
+  explicit TopControlsShownRatioWaiter(TestController* controller)
+      : controller_(controller) {
+    controller_->AddObserver(this);
+  }
+
+  ~TopControlsShownRatioWaiter() override { controller_->RemoveObserver(this); }
+
+  // TestControllerObserver:
+  void OnShownRatioChanged(float shown_ratio) override { CheckRatio(); }
+
+  void OnGestureScrollInProgressChanged(bool in_progress) override {
+    CheckRatio();
+  }
 
   void WaitForRatio(float ratio) {
     DCHECK(ratio == 1.f || ratio == 0.f) << "Should only be used to wait for "
@@ -121,7 +207,10 @@
     if (CheckRatio())
       return;
 
-    run_loop_ = std::make_unique<base::RunLoop>();
+    // Use kNestableTasksAllowed to make it possible to wait inside a posted
+    // task.
+    run_loop_ = std::make_unique<base::RunLoop>(
+        base::RunLoop::Type::kNestableTasksAllowed);
     run_loop_->Run();
   }
 
@@ -139,20 +228,10 @@
       return true;
     }
 
-    ScheduleCheck();
     return false;
   }
 
-  void ScheduleCheck() {
-    base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
-        FROM_HERE,
-        base::BindOnce(
-            base::IgnoreResult(&TopControlsShownRatioWaiter::CheckRatio),
-            base::Unretained(this)),
-        base::TimeDelta::FromMilliseconds(100));
-  }
-
-  const TopControlsSlideController* controller_;
+  TestController* controller_;
 
   std::unique_ptr<base::RunLoop> run_loop_;
 
@@ -161,6 +240,8 @@
   DISALLOW_COPY_AND_ASSIGN(TopControlsShownRatioWaiter);
 };
 
+}  // namespace
+
 class TopControlsSlideControllerTest : public InProcessBrowserTest {
  public:
   TopControlsSlideControllerTest() = default;
@@ -170,8 +251,9 @@
     return BrowserView::GetBrowserViewForBrowser(browser());
   }
 
-  const TopControlsSlideController* top_controls_slide_controller() const {
-    return browser_view()->top_controls_slide_controller();
+  TestController* top_controls_slide_controller() const {
+    DCHECK(test_controller_);
+    return test_controller_;
   }
 
   // InProcessBrowserTest:
@@ -197,6 +279,13 @@
     embedded_test_server()->ServeFilesFromDirectory(
         test_data_dir.AppendASCII("chrome/test/data/top_controls_scroll"));
     ASSERT_TRUE(embedded_test_server()->Start());
+
+    InjectTestController();
+  }
+
+  void InjectTestController() {
+    browser_view()->top_controls_slide_controller_ = CreateTestController(
+        std::move(browser_view()->top_controls_slide_controller_));
   }
 
   void OpenUrlAtIndex(const GURL& url, int index) {
@@ -298,38 +387,18 @@
     }
   }
 
-  // This is used as a callback of type |ScrollStepCallback| of the function
-  // EventGenerator::GestureScrollSequenceWithCallback() that will be called at
-  // the scroll steps of ET_GESTURE_SCROLL_BEGIN, ET_GESTURE_SCROLL_UPDATE, and
-  // ET_GESTURE_SCROLL_END.
-  //
-  // It verifies the state of the browser window when the active page is being
+  // Verifies the state of the browser window when the active page is being
   // scrolled by touch gestures in such a way that will result in the top
   // controls shown ratio becoming a fractional value (i.e. sliding top-chrome
   // is in progress).
   // The |expected_shrink_renderer_size| will be checked against the
   // `DoBrowserControlsShrinkRendererSize` bit while sliding.
-  // |out_seen_fractional_shown_ratio| will be set to true if a fractional value
-  // of the shown_ratio has been seen.
-  // |event_type| and |delta| are callback parameters of |ScrollStepCallback|.
-  void CheckIntermediateScrollStep(bool expected_shrink_renderer_size,
-                                   bool* out_seen_fractional_shown_ratio,
-                                   ui::EventType event_type,
-                                   const gfx::Vector2dF& delta) {
-    // Give the event a chance to propagate to renderer before sending the
-    // next one.
-    base::RunLoop().RunUntilIdle();
-
-    if (event_type != ui::ET_GESTURE_SCROLL_UPDATE)
-      return;
-
+  void CheckIntermediateScrollStep(bool expected_shrink_renderer_size) {
     const float shown_ratio = top_controls_slide_controller()->GetShownRatio();
-    if (shown_ratio == 1.f || shown_ratio == 0.f) {
-      // Test only intermediate values.
-      return;
-    }
 
-    *out_seen_fractional_shown_ratio = true;
+    // This should only be used to verify the state of the browser while sliding
+    // is in progress.
+    ASSERT_TRUE(shown_ratio != 1.f && shown_ratio != 0.f);
 
     const int top_controls_height = browser_view()->GetTopControlsHeight();
     EXPECT_NE(top_controls_height, 0);
@@ -367,12 +436,50 @@
     CompareTranslations(expected_transform, root_view_layer->transform());
   }
 
+  // Generates a gesture fling scroll sequence to scroll the current page in the
+  // given |direction|, and waits for and verifies that top-chrome reaches the
+  // given |target_state|.
+  void ScrollAndExpectTopChromeToBe(ScrollDirection direction,
+                                    TopChromeShownState target_state) {
+    aura::Window* browser_window = browser()->window()->GetNativeWindow();
+    ui::test::EventGenerator event_generator(browser_window->GetRootWindow(),
+                                             browser_window);
+    const gfx::Point start_point = event_generator.current_location();
+    const gfx::Point end_point =
+        start_point +
+        gfx::Vector2d(0, direction == ScrollDirection::kDown ? -100 : 100);
+
+    const float target_ratio =
+        target_state == TopChromeShownState::kFullyHidden ? 0.f : 1.f;
+    TopControlsShownRatioWaiter waiter(top_controls_slide_controller());
+    GenerateGestureFlingScrollSequence(&event_generator, start_point,
+                                       end_point);
+
+    waiter.WaitForRatio(target_ratio);
+    EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(),
+                    target_ratio);
+    CheckBrowserLayout(browser_view(), target_state);
+  }
+
  private:
+  std::unique_ptr<TopControlsSlideController> CreateTestController(
+      std::unique_ptr<TopControlsSlideController> real_controller) {
+    DCHECK(real_controller);
+    auto controller =
+        std::make_unique<TestController>(std::move(real_controller));
+    test_controller_ = controller.get();
+    return std::move(controller);
+  }
+
   base::test::ScopedFeatureList scoped_feature_list_;
 
+  TestController* test_controller_ = nullptr;  // Not owned.
+
   DISALLOW_COPY_AND_ASSIGN(TopControlsSlideControllerTest);
 };
 
+namespace {
+
 IN_PROC_BROWSER_TEST_F(TopControlsSlideControllerTest, DisabledForHostedApps) {
   browser()->window()->Close();
 
@@ -458,27 +565,14 @@
   OpenUrlAtIndex(embedded_test_server()->GetURL("/top_controls_scroll.html"),
                  0);
 
-  aura::Window* browser_window = browser()->window()->GetNativeWindow();
-  ui::test::EventGenerator event_generator(browser_window->GetRootWindow(),
-                                           browser_window);
-
-  // The above EventGenerator ctor initializes current_location() to the center
-  // point of |browser_window|. Let's start a fast gesture scroll from that
-  // point towards another point above it.
-  const gfx::Point start_point = event_generator.current_location();
-  const gfx::Point end_point = start_point + gfx::Vector2d(0, -100);
-  GenerateGestureFlingScrollSequence(&event_generator, start_point, end_point);
-  TopControlsShownRatioWaiter waiter(top_controls_slide_controller());
-  waiter.WaitForRatio(0.f);
-  EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 0);
-  CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyHidden);
+  // It's possible to hide top chrome with gesture scrolling.
+  ScrollAndExpectTopChromeToBe(ScrollDirection::kDown,
+                               TopChromeShownState::kFullyHidden);
 
   // Perform another gesture scroll in the opposite direction and expect top-
   // chrome to be fully shown.
-  GenerateGestureFlingScrollSequence(&event_generator, end_point, start_point);
-  waiter.WaitForRatio(1.f);
-  EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 1.f);
-  CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyShown);
+  ScrollAndExpectTopChromeToBe(ScrollDirection::kUp,
+                               TopChromeShownState::kFullyShown);
 }
 
 IN_PROC_BROWSER_TEST_F(TopControlsSlideControllerTest,
@@ -499,34 +593,24 @@
                  0);
   ASSERT_EQ(browser()->tab_strip_model()->count(), 2);
 
-  auto* browser_window = browser()->window()->GetNativeWindow();
-  ui::test::EventGenerator event_generator(browser_window->GetRootWindow(),
-                                           browser_window);
-
   // Scroll the `top_controls_scroll.html` page such that top-chrome is now
   // fully hidden.
-  const gfx::Point start_point = event_generator.current_location();
-  const gfx::Point end_point = start_point + gfx::Vector2d(0, -100);
-  GenerateGestureFlingScrollSequence(&event_generator, start_point, end_point);
-  TopControlsShownRatioWaiter waiter(top_controls_slide_controller());
-  waiter.WaitForRatio(0.f);
-  EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 0);
-  CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyHidden);
+  ScrollAndExpectTopChromeToBe(ScrollDirection::kDown,
+                               TopChromeShownState::kFullyHidden);
 
   // Simulate (Ctrl + Tab) shortcut to select the next tab. Top-chrome should
   // show automatically.
+  TopControlsShownRatioWaiter waiter(top_controls_slide_controller());
   browser()->tab_strip_model()->SelectNextTab();
   EXPECT_EQ(browser()->tab_strip_model()->active_index(), 1);
   waiter.WaitForRatio(1.f);
   EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 1.f);
   CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyShown);
 
-  // Since this is the NTP page, gesture scrolling will not hide top-chrome. It
-  // will remain fully shown.
-  GenerateGestureFlingScrollSequence(&event_generator, start_point, end_point);
-  waiter.WaitForRatio(1.f);
-  EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 1.f);
-  CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyShown);
+  // Since this is the NTP page, gesture scrolling down will not hide
+  // top-chrome. It will remain fully shown.
+  ScrollAndExpectTopChromeToBe(ScrollDirection::kDown,
+                               TopChromeShownState::kFullyShown);
 
   // Switch back to the scrollable page, it should be possible now to hide top-
   // chrome.
@@ -534,10 +618,9 @@
   EXPECT_EQ(browser()->tab_strip_model()->active_index(), 0);
   waiter.WaitForRatio(1.f);
   EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 1.f);
-  GenerateGestureFlingScrollSequence(&event_generator, start_point, end_point);
-  waiter.WaitForRatio(0.f);
-  EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 0);
-  CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyHidden);
+
+  ScrollAndExpectTopChromeToBe(ScrollDirection::kDown,
+                               TopChromeShownState::kFullyHidden);
 
   // The `DoBrowserControlsShrinkRendererSize` bit is separately tracked for
   // each tab.
@@ -565,19 +648,12 @@
   ASSERT_EQ(browser()->tab_strip_model()->count(), 1);
 
   // Scroll to fully hide top-chrome.
-  auto* browser_window = browser()->window()->GetNativeWindow();
-  ui::test::EventGenerator event_generator(browser_window->GetRootWindow(),
-                                           browser_window);
-  const gfx::Point start_point = event_generator.current_location();
-  const gfx::Point end_point = start_point + gfx::Vector2d(0, -100);
-  GenerateGestureFlingScrollSequence(&event_generator, start_point, end_point);
-  TopControlsShownRatioWaiter waiter(top_controls_slide_controller());
-  waiter.WaitForRatio(0.f);
-  EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 0);
-  CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyHidden);
+  ScrollAndExpectTopChromeToBe(ScrollDirection::kDown,
+                               TopChromeShownState::kFullyHidden);
 
   // Simulate (Ctrl + T) by inserting a new tab. Expect top-chrome to be fully
   // shown.
+  TopControlsShownRatioWaiter waiter(top_controls_slide_controller());
   chrome::NewTab(browser());
   waiter.WaitForRatio(1.f);
   EXPECT_EQ(browser()->tab_strip_model()->active_index(), 1);
@@ -600,10 +676,8 @@
   CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyShown);
 
   // It is still possible to slide top-chrome up.
-  GenerateGestureFlingScrollSequence(&event_generator, start_point, end_point);
-  waiter.WaitForRatio(0.f);
-  EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 0);
-  CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyHidden);
+  ScrollAndExpectTopChromeToBe(ScrollDirection::kDown,
+                               TopChromeShownState::kFullyHidden);
 }
 
 IN_PROC_BROWSER_TEST_F(TopControlsSlideControllerTest,
@@ -618,19 +692,8 @@
   OpenUrlAtIndex(embedded_test_server()->GetURL("/top_controls_scroll.html"),
                  0);
 
-  auto* browser_window = browser()->window()->GetNativeWindow();
-  ui::test::EventGenerator event_generator(browser_window->GetRootWindow(),
-                                           browser_window);
-
-  // Scroll the `top_controls_scroll.html` page such that top-chrome is now
-  // fully hidden.
-  const gfx::Point start_point = event_generator.current_location();
-  const gfx::Point end_point = start_point + gfx::Vector2d(0, -100);
-  GenerateGestureFlingScrollSequence(&event_generator, start_point, end_point);
-  TopControlsShownRatioWaiter waiter(top_controls_slide_controller());
-  waiter.WaitForRatio(0.f);
-  EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 0);
-  CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyHidden);
+  ScrollAndExpectTopChromeToBe(ScrollDirection::kDown,
+                               TopChromeShownState::kFullyHidden);
 
   // Define an internal lambda that returns the javascript function body that
   // can be executed on the focus on `top_controls_scroll.html` page to focus on
@@ -657,6 +720,7 @@
 
   // Focus on the editable element in the page and expect that top-chrome will
   // be shown.
+  TopControlsShownRatioWaiter waiter(top_controls_slide_controller());
   content::WebContents* contents = browser_view()->GetActiveWebContents();
   bool bool_result = false;
   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
@@ -669,10 +733,8 @@
   // Now try scrolling in a way that would normally hide top-chrome, and expect
   // that top-chrome will be forced shown as long as the editable element is
   // focused.
-  GenerateGestureFlingScrollSequence(&event_generator, start_point, end_point);
-  waiter.WaitForRatio(1.f);
-  EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 1.f);
-  CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyShown);
+  ScrollAndExpectTopChromeToBe(ScrollDirection::kDown,
+                               TopChromeShownState::kFullyShown);
 
   // Now blur the focused editable element. Expect that top-chrome can now be
   // hidden with gesture scrolls.
@@ -680,10 +742,8 @@
   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
       contents, get_js_function_body(false /* should_focus */), &bool_result));
   EXPECT_TRUE(bool_result);
-  GenerateGestureFlingScrollSequence(&event_generator, start_point, end_point);
-  waiter.WaitForRatio(0.f);
-  EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 0);
-  CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyHidden);
+  ScrollAndExpectTopChromeToBe(ScrollDirection::kDown,
+                               TopChromeShownState::kFullyHidden);
 }
 
 // Used to wait for the browser view to change its bounds as a result of display
@@ -926,21 +986,66 @@
   page_state_update_waiter.Wait();
 
   // Scroll to fully hide top-chrome.
-  auto* browser_window = browser()->window()->GetNativeWindow();
-  ui::test::EventGenerator event_generator(browser_window->GetRootWindow(),
-                                           browser_window);
-  const gfx::Point start_point = event_generator.current_location();
-  const gfx::Point end_point = start_point + gfx::Vector2d(0, -100);
-  GenerateGestureFlingScrollSequence(&event_generator, start_point, end_point);
-  TopControlsShownRatioWaiter waiter(top_controls_slide_controller());
-  waiter.WaitForRatio(0.f);
-  EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 0);
-  CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyHidden);
+  ScrollAndExpectTopChromeToBe(ScrollDirection::kDown,
+                               TopChromeShownState::kFullyHidden);
 }
 
-// Flaky: https://crbug.com/898502
+// Waits for a fractional value of the top controls shown ratio, upon which it
+// will invoke the |on_intermediate_ratio_callback| which can be used to verify
+// the state of the browser while sliding is in progress.
+class IntermediateShownRatioWaiter : public TestControllerObserver {
+ public:
+  explicit IntermediateShownRatioWaiter(
+      TestController* controller,
+      base::OnceClosure on_intermediate_ratio_callback)
+      : controller_(controller),
+        on_intermediate_ratio_callback_(
+            std::move(on_intermediate_ratio_callback)) {
+    controller_->AddObserver(this);
+  }
+
+  ~IntermediateShownRatioWaiter() override {
+    controller_->RemoveObserver(this);
+  }
+
+  // TestControllerObserver:
+  void OnShownRatioChanged(float shown_ratio) override {
+    seen_intermediate_ratios_ = shown_ratio > 0.0 && shown_ratio < 1.f;
+    if (!seen_intermediate_ratios_)
+      return;
+
+    if (on_intermediate_ratio_callback_)
+      std::move(on_intermediate_ratio_callback_).Run();
+
+    if (run_loop_)
+      run_loop_->Quit();
+  }
+
+  void OnGestureScrollInProgressChanged(bool in_progress) override {}
+
+  void Wait() {
+    if (seen_intermediate_ratios_)
+      return;
+
+    run_loop_ = std::make_unique<base::RunLoop>(
+        base::RunLoop::Type::kNestableTasksAllowed);
+    run_loop_->Run();
+  }
+
+ private:
+  TestController* controller_;
+
+  std::unique_ptr<base::RunLoop> run_loop_;
+
+  base::OnceClosure on_intermediate_ratio_callback_;
+
+  bool seen_intermediate_ratios_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(IntermediateShownRatioWaiter);
+};
+
 IN_PROC_BROWSER_TEST_F(TopControlsSlideControllerTest,
-                       DISABLED_TestIntermediateSliding) {
+                       TestIntermediateSliding) {
   ToggleTabletMode();
   ASSERT_TRUE(GetTabletModeEnabled());
   EXPECT_TRUE(top_controls_slide_controller()->IsEnabled());
@@ -963,53 +1068,64 @@
   const gfx::Point start_point = event_generator.current_location();
   const gfx::Point end_point = start_point + gfx::Vector2d(0, -100);
 
-  // Large number of ET_GESTURE_SCROLL_UPDATE steps that we can see fractional
-  // shown ratios while scrolling is in progress.
+  // Large number of ET_GESTURE_SCROLL_UPDATE steps with small velocity so that
+  // we can see fractional shown ratios.
   const int scroll_steps = 1000;
   const base::TimeDelta scroll_step_delay =
       event_generator.CalculateScrollDurationForFlingVelocity(
-          start_point, end_point, 1000 /* velocity */, scroll_steps);
+          start_point, end_point, 2 /* velocity */, scroll_steps);
 
-  // We need to verify that a fractional value of the shown ratio has been seen,
-  // otherwise the test is useless, since we want to verify the state while
-  // sliding in in progress.
-  bool seen_fractional_shown_ratio = false;
+  {
+    // We will start scrolling while top-chrome is fully shown, in which case
+    // the `DoBrowserControlsShrinkRendererSize` bit is true. It should remain
+    // true while sliding is in progress.
+    const bool expected_shrink_renderer_size = true;
 
-  // We will start scrolling while top-chrome is fully shown, in which case the
-  // `DoBrowserControlsShrinkRendererSize` bit is true. It should remain true
-  // while sliding is in progress.
-  bool expected_shrink_renderer_size = true;
-  event_generator.GestureScrollSequenceWithCallback(
-      start_point, end_point, scroll_step_delay, scroll_steps,
-      base::BindRepeating(
-          &TopControlsSlideControllerTest::CheckIntermediateScrollStep,
-          base::Unretained(this), expected_shrink_renderer_size,
-          &seen_fractional_shown_ratio));
-  EXPECT_TRUE(seen_fractional_shown_ratio);
-  TopControlsShownRatioWaiter waiter(top_controls_slide_controller());
-  waiter.WaitForRatio(0.f);
-  EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 0);
-  CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyHidden);
+    TopControlsShownRatioWaiter waiter(top_controls_slide_controller());
+    IntermediateShownRatioWaiter fractional_ratio_waiter(
+        top_controls_slide_controller(),
+        base::BindOnce(
+            &TopControlsSlideControllerTest::CheckIntermediateScrollStep,
+            base::Unretained(this), expected_shrink_renderer_size));
+    event_generator.GestureScrollSequence(start_point, end_point,
+                                          scroll_step_delay, scroll_steps);
+    fractional_ratio_waiter.Wait();
+    waiter.WaitForRatio(0.f);
+    EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 0);
+    CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyHidden);
 
-  // Now that sliding ended, and top-chrome is fully hidden, the
-  // `DoBrowserControlsShrinkRendererSize` bit should be false ...
-  EXPECT_FALSE(
-      browser_view()->DoBrowserControlsShrinkRendererSize(active_contents));
+    // Now that sliding ended, and top-chrome is fully hidden, the
+    // `DoBrowserControlsShrinkRendererSize` bit should be false ...
+    EXPECT_FALSE(
+        browser_view()->DoBrowserControlsShrinkRendererSize(active_contents));
+  }
 
-  // ... and when scrolling in the other direction towards a fully shown
-  // top-chrome, it should remain false while sliding is in progress.
-  expected_shrink_renderer_size = false;
-  seen_fractional_shown_ratio = false;
-  event_generator.GestureScrollSequenceWithCallback(
-      end_point, start_point, scroll_step_delay, scroll_steps,
-      base::BindRepeating(
-          &TopControlsSlideControllerTest::CheckIntermediateScrollStep,
-          base::Unretained(this), expected_shrink_renderer_size,
-          &seen_fractional_shown_ratio));
-  EXPECT_TRUE(seen_fractional_shown_ratio);
-  waiter.WaitForRatio(1.f);
-  EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 1.f);
-  CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyShown);
+  content::WaitForResizeComplete(active_contents);
+
+  {
+    // ... and when scrolling in the other direction towards a fully shown
+    // top-chrome, it should remain false while sliding is in progress.
+    const bool expected_shrink_renderer_size = false;
+
+    TopControlsShownRatioWaiter waiter(top_controls_slide_controller());
+    IntermediateShownRatioWaiter fractional_ratio_waiter(
+        top_controls_slide_controller(),
+        base::BindOnce(
+            &TopControlsSlideControllerTest::CheckIntermediateScrollStep,
+            base::Unretained(this), expected_shrink_renderer_size));
+
+    event_generator.GestureScrollSequence(end_point, start_point,
+                                          scroll_step_delay, scroll_steps);
+    fractional_ratio_waiter.Wait();
+    waiter.WaitForRatio(1.f);
+    EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 1.f);
+    CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyShown);
+
+    // Now that sliding ended, and top-chrome is fully shown, the
+    // `DoBrowserControlsShrinkRendererSize` bit should be true.
+    EXPECT_TRUE(
+        browser_view()->DoBrowserControlsShrinkRendererSize(active_contents));
+  }
 }
 
 IN_PROC_BROWSER_TEST_F(TopControlsSlideControllerTest, TestPermissionBubble) {
@@ -1028,16 +1144,8 @@
       browser_view()->DoBrowserControlsShrinkRendererSize(active_contents));
 
   // Hide top chrome.
-  aura::Window* browser_window = browser()->window()->GetNativeWindow();
-  ui::test::EventGenerator event_generator(browser_window->GetRootWindow(),
-                                           browser_window);
-  const gfx::Point start_point = event_generator.current_location();
-  const gfx::Point end_point = start_point + gfx::Vector2d(0, -100);
-  GenerateGestureFlingScrollSequence(&event_generator, start_point, end_point);
-  TopControlsShownRatioWaiter waiter(top_controls_slide_controller());
-  waiter.WaitForRatio(0.f);
-  EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 0);
-  CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyHidden);
+  ScrollAndExpectTopChromeToBe(ScrollDirection::kDown,
+                               TopChromeShownState::kFullyHidden);
 
   // Fire a geolocation permission request, which should show a permission
   // request bubble resulting in top chrome unhiding.
@@ -1047,6 +1155,7 @@
       base::BindRepeating(decided), base::DoNothing() /* delete_callback */);
   auto* permission_manager =
       PermissionRequestManager::FromWebContents(active_contents);
+  TopControlsShownRatioWaiter waiter(top_controls_slide_controller());
   permission_manager->AddRequest(&permission_request);
   waiter.WaitForRatio(1.f);
   EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 1.f);
@@ -1055,11 +1164,8 @@
 
   // It shouldn't be possible to hide top-chrome as long as the bubble is
   // visible.
-  GenerateGestureFlingScrollSequence(&event_generator, start_point, end_point);
-  waiter.WaitForRatio(1.f);
-  EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 1.f);
-  CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyShown);
-  EXPECT_TRUE(permission_manager->IsBubbleVisible());
+  ScrollAndExpectTopChromeToBe(ScrollDirection::kDown,
+                               TopChromeShownState::kFullyShown);
 
   // Dismiss the bubble.
   EXPECT_TRUE(permission_manager->GetBubbleWindow());
@@ -1068,10 +1174,77 @@
   EXPECT_FALSE(permission_manager->IsBubbleVisible());
 
   // Now it is possible to hide top-chrome again.
-  GenerateGestureFlingScrollSequence(&event_generator, start_point, end_point);
-  waiter.WaitForRatio(0.f);
-  EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 0);
-  CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyHidden);
+  ScrollAndExpectTopChromeToBe(ScrollDirection::kDown,
+                               TopChromeShownState::kFullyHidden);
+}
+
+// Waits for a compositor frame to be drawn and committed on the given
+// web_contents.
+class CompositorFrameWaiter : content::WebContentsObserver {
+ public:
+  explicit CompositorFrameWaiter(content::WebContents* contents)
+      : WebContentsObserver(contents) {}
+  ~CompositorFrameWaiter() override = default;
+
+  void Wait() {
+    run_loop_ = std::make_unique<base::RunLoop>();
+    run_loop_->Run();
+    run_loop_.reset();
+  }
+
+  // content::WebContentsObserver:
+  void DidCommitAndDrawCompositorFrame() override {
+    if (run_loop_)
+      run_loop_->Quit();
+  }
+
+ private:
+  std::unique_ptr<base::RunLoop> run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(CompositorFrameWaiter);
+};
+
+IN_PROC_BROWSER_TEST_F(TopControlsSlideControllerTest, TestToggleChromeVox) {
+  ToggleTabletMode();
+  ASSERT_TRUE(GetTabletModeEnabled());
+  EXPECT_TRUE(top_controls_slide_controller()->IsEnabled());
+  EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 1.f);
+
+  OpenUrlAtIndex(embedded_test_server()->GetURL("/top_controls_scroll.html"),
+                 0);
+  content::WebContents* active_contents =
+      browser_view()->GetActiveWebContents();
+  PageStateUpdateWaiter page_state_update_waiter(active_contents);
+  page_state_update_waiter.Wait();
+  EXPECT_TRUE(
+      browser_view()->DoBrowserControlsShrinkRendererSize(active_contents));
+
+  ScrollAndExpectTopChromeToBe(ScrollDirection::kDown,
+                               TopChromeShownState::kFullyHidden);
+
+  // Enable Chromevox (spoken feedback) and expect that top-chrome will be fully
+  // shown, and sliding top-chrome is no longer enabled.
+  TopControlsShownRatioWaiter waiter(top_controls_slide_controller());
+  chromeos::AccessibilityManager::Get()->EnableSpokenFeedback(true);
+  EXPECT_TRUE(chromeos::AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
+  waiter.WaitForRatio(1.f);
+  EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 1.f);
+  EXPECT_TRUE(
+      browser_view()->DoBrowserControlsShrinkRendererSize(active_contents));
+
+  // Now disable Chromevox, and expect it's now possible to hide top-chrome with
+  // gesture scrolling.
+  CompositorFrameWaiter compositor_frame_waiter(active_contents);
+  chromeos::AccessibilityManager::Get()->EnableSpokenFeedback(false);
+  compositor_frame_waiter.Wait();
+  content::WaitForResizeComplete(active_contents);
+  EXPECT_FALSE(
+      chromeos::AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
+  EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 1.f);
+  CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyShown);
+
+  ScrollAndExpectTopChromeToBe(ScrollDirection::kDown,
+                               TopChromeShownState::kFullyHidden);
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/views/infobars/alternate_nav_infobar_view.cc b/chrome/browser/ui/views/infobars/alternate_nav_infobar_view.cc
index 883a89c3..559bbc54 100644
--- a/chrome/browser/ui/views/infobars/alternate_nav_infobar_view.cc
+++ b/chrome/browser/ui/views/infobars/alternate_nav_infobar_view.cc
@@ -11,7 +11,6 @@
 #include "base/logging.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.h"
-#include "chrome/browser/ui/views_mode_controller.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/gfx/text_elider.h"
 #include "ui/views/controls/label.h"
diff --git a/chrome/browser/ui/views/infobars/confirm_infobar.cc b/chrome/browser/ui/views/infobars/confirm_infobar.cc
index 729702c..b6aaa83 100644
--- a/chrome/browser/ui/views/infobars/confirm_infobar.cc
+++ b/chrome/browser/ui/views/infobars/confirm_infobar.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/elevation_icon_setter.h"
-#include "chrome/browser/ui/views_mode_controller.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/button/md_text_button.h"
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
index caac5642..5aafc224 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
@@ -28,7 +28,6 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/events/event_utils.h"
-#include "ui/views/controls/button/menu_button.h"
 #include "ui/views/controls/combobox/combobox.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/link.h"
@@ -84,34 +83,17 @@
       return base::ASCIIToUTF16(name);
   }
 
-  // Returns the permission label text of the |index|th permission selector row.
-  // This function returns an empty string if the permission selector row's
-  // |label_| element isn't actually a |views::Label|.
   base::string16 GetPermissionLabelTextAt(int index) {
-    views::View* view = GetPermissionSelectorAt(index)->label_;
-    if (view->GetClassName() == views::Label::kViewClassName) {
-      return static_cast<views::Label*>(view)->text();
-    }
-    return base::string16();
+    return GetPermissionSelectorAt(index)->label_->text();
   }
 
-  base::string16 GetPermissionButtonTextAt(int index) {
-    views::View* view = GetPermissionSelectorAt(index)->button();
-    if (view->GetClassName() == views::MenuButton::kViewClassName) {
-      return static_cast<views::MenuButton*>(view)->GetText();
-    } else if (view->GetClassName() == views::Combobox::kViewClassName) {
-      views::Combobox* combobox = static_cast<views::Combobox*>(view);
-      return combobox->GetTextForRow(combobox->GetSelectedRow());
-    } else {
-      NOTREACHED() << "Unknown class " << view->GetClassName();
-      return base::string16();
-    }
+  base::string16 GetPermissionComboboxTextAt(int index) {
+    auto* combobox = GetPermissionSelectorAt(index)->combobox_;
+    return combobox->GetTextForRow(combobox->GetSelectedRow());
   }
 
   void SimulateUserSelectingComboboxItemAt(int selector_index, int menu_index) {
-    views::View* view = GetPermissionSelectorAt(selector_index)->button();
-    DCHECK_EQ(views::Combobox::kViewClassName, view->GetClassName());
-    views::Combobox* combobox = static_cast<views::Combobox*>(view);
+    auto* combobox = GetPermissionSelectorAt(selector_index)->combobox_;
     combobox->SetSelectedRow(menu_index);
   }
 
@@ -264,23 +246,23 @@
 
   // Verify labels match the settings on the PermissionInfoList.
   EXPECT_EQ(base::ASCIIToUTF16("Location"), api_->GetPermissionLabelTextAt(0));
-  EXPECT_EQ(base::ASCIIToUTF16("Allow"), api_->GetPermissionButtonTextAt(0));
+  EXPECT_EQ(base::ASCIIToUTF16("Allow"), api_->GetPermissionComboboxTextAt(0));
 
   // Verify calling SetPermissionInfo() directly updates the UI.
   list.back().setting = CONTENT_SETTING_BLOCK;
   api_->SetPermissionInfo(list);
-  EXPECT_EQ(base::ASCIIToUTF16("Block"), api_->GetPermissionButtonTextAt(0));
+  EXPECT_EQ(base::ASCIIToUTF16("Block"), api_->GetPermissionComboboxTextAt(0));
 
   // Simulate a user selection via the UI. Note this will also cover logic in
   // PageInfo to update the pref.
   api_->SimulateUserSelectingComboboxItemAt(0, 1);
   EXPECT_EQ(num_expected_children, api_->permissions_view()->child_count());
-  EXPECT_EQ(base::ASCIIToUTF16("Allow"), api_->GetPermissionButtonTextAt(0));
+  EXPECT_EQ(base::ASCIIToUTF16("Allow"), api_->GetPermissionComboboxTextAt(0));
 
   // Setting to the default via the UI should keep the button around.
   api_->SimulateUserSelectingComboboxItemAt(0, 0);
   EXPECT_EQ(base::ASCIIToUTF16("Ask (default)"),
-            api_->GetPermissionButtonTextAt(0));
+            api_->GetPermissionComboboxTextAt(0));
   EXPECT_EQ(num_expected_children, api_->permissions_view()->child_count());
 
   // However, since the setting is now default, recreating the dialog with those
@@ -359,22 +341,22 @@
   num_expected_children += kViewsPerPermissionRow * list.size();
   list.back().setting = CONTENT_SETTING_BLOCK;
   api_->SetPermissionInfo(list);
-  EXPECT_EQ(base::ASCIIToUTF16("Block"), api_->GetPermissionButtonTextAt(0));
+  EXPECT_EQ(base::ASCIIToUTF16("Block"), api_->GetPermissionComboboxTextAt(0));
 
   // Simulate a user selection via the UI. Note this will also cover logic in
   // PageInfo to update the pref.
   api_->SimulateUserSelectingComboboxItemAt(0, 2);
   EXPECT_EQ(num_expected_children, api_->permissions_view()->child_count());
-  EXPECT_EQ(base::ASCIIToUTF16("Ask"), api_->GetPermissionButtonTextAt(0));
+  EXPECT_EQ(base::ASCIIToUTF16("Ask"), api_->GetPermissionComboboxTextAt(0));
 
   // Setting to the default via the UI should keep the button around.
-    api_->SimulateUserSelectingComboboxItemAt(0, 0);
-    EXPECT_EQ(base::ASCIIToUTF16("Ask (default)"),
-              api_->GetPermissionButtonTextAt(0));
+  api_->SimulateUserSelectingComboboxItemAt(0, 0);
+  EXPECT_EQ(base::ASCIIToUTF16("Ask (default)"),
+            api_->GetPermissionComboboxTextAt(0));
   EXPECT_EQ(num_expected_children, api_->permissions_view()->child_count());
 
-  // However, since the setting is now default, recreating the dialog with those
-  // settings should omit the permission from the UI.
+  // However, since the setting is now default, recreating the dialog with
+  // those settings should omit the permission from the UI.
   //
   // TODO(https://crbug.com/829576): Reconcile the comment above with the fact
   // that |num_expected_children| is not, at this point, 0 and therefore the
diff --git a/chrome/browser/ui/views/page_info/permission_selector_row.cc b/chrome/browser/ui/views/page_info/permission_selector_row.cc
index 15f4c13..2e5fc69 100644
--- a/chrome/browser/ui/views/page_info/permission_selector_row.cc
+++ b/chrome/browser/ui/views/page_info/permission_selector_row.cc
@@ -19,12 +19,10 @@
 #include "ui/base/models/combobox_model.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/image/image.h"
-#include "ui/views/controls/button/menu_button.h"
 #include "ui/views/controls/combobox/combobox.h"
 #include "ui/views/controls/combobox/combobox_listener.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
-#include "ui/views/controls/menu/menu_runner.h"
 #include "ui/views/layout/grid_layout.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
@@ -60,97 +58,6 @@
 
 namespace internal {
 
-// The |PermissionMenuButton| provides a menu for selecting a setting a
-// permissions type.
-class PermissionMenuButton : public views::MenuButton,
-                             public views::MenuButtonListener {
- public:
-  // Creates a new |PermissionMenuButton| with the passed |text|. The ownership
-  // of the |model| remains with the caller and is not transfered to the
-  // |PermissionMenuButton|. If the |show_menu_marker| flag is true, then a
-  // small icon is be displayed next to the button |text|, indicating that the
-  // button opens a drop down menu.
-  PermissionMenuButton(const base::string16& text,
-                       PermissionMenuModel* model,
-                       bool show_menu_marker);
-  ~PermissionMenuButton() override;
-
-  // Overridden from views::View.
-  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
-  void OnNativeThemeChanged(const ui::NativeTheme* theme) override;
-
- private:
-  // Overridden from views::MenuButtonListener.
-  void OnMenuButtonClicked(views::MenuButton* source,
-                           const gfx::Point& point,
-                           const ui::Event* event) override;
-
-  PermissionMenuModel* menu_model_;  // Owned by |PermissionSelectorRow|.
-  std::unique_ptr<views::MenuRunner> menu_runner_;
-
-  bool is_rtl_display_;
-
-  DISALLOW_COPY_AND_ASSIGN(PermissionMenuButton);
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// PermissionMenuButton
-///////////////////////////////////////////////////////////////////////////////
-
-PermissionMenuButton::PermissionMenuButton(const base::string16& text,
-                                           PermissionMenuModel* model,
-                                           bool show_menu_marker)
-    : MenuButton(text, this, show_menu_marker), menu_model_(model) {
-  // Since PermissionMenuButtons are added to a GridLayout, they are not always
-  // sized to their preferred size. Disclosure arrows are always right-aligned,
-  // so if the text is not right-aligned, awkward space appears between the text
-  // and the arrow.
-  SetHorizontalAlignment(gfx::ALIGN_RIGHT);
-
-  // Update the themed border before the NativeTheme is applied. Usually this
-  // happens in a call to LabelButton::OnNativeThemeChanged(). However, if
-  // PermissionMenuButton called that from its override, the NativeTheme would
-  // be available, and the button would get native GTK styling on Linux.
-  UpdateThemedBorder();
-
-  SetFocusForPlatform();
-  set_request_focus_on_press(true);
-  is_rtl_display_ =
-      base::i18n::RIGHT_TO_LEFT == base::i18n::GetStringDirection(text);
-}
-
-PermissionMenuButton::~PermissionMenuButton() {}
-
-void PermissionMenuButton::GetAccessibleNodeData(ui::AXNodeData* node_data) {
-  MenuButton::GetAccessibleNodeData(node_data);
-  node_data->SetValue(GetText());
-}
-
-void PermissionMenuButton::OnNativeThemeChanged(const ui::NativeTheme* theme) {
-  SetTextColor(
-      views::Button::STATE_NORMAL,
-      theme->GetSystemColor(ui::NativeTheme::kColorId_LabelEnabledColor));
-  SetTextColor(
-      views::Button::STATE_HOVERED,
-      theme->GetSystemColor(ui::NativeTheme::kColorId_LabelEnabledColor));
-  SetTextColor(
-      views::Button::STATE_DISABLED,
-      theme->GetSystemColor(ui::NativeTheme::kColorId_LabelDisabledColor));
-}
-
-void PermissionMenuButton::OnMenuButtonClicked(views::MenuButton* source,
-                                               const gfx::Point& point,
-                                               const ui::Event* event) {
-  menu_runner_.reset(
-      new views::MenuRunner(menu_model_, views::MenuRunner::HAS_MNEMONICS));
-
-  gfx::Point p(point);
-  p.Offset(is_rtl_display_ ? source->width() : -source->width(), 0);
-  menu_runner_->RunMenuAt(source->GetWidget()->GetTopLevelWidget(), this,
-                          gfx::Rect(p, gfx::Size()), views::MENU_ANCHOR_TOPLEFT,
-                          ui::MENU_SOURCE_NONE);
-}
-
 // This class adapts a |PermissionMenuModel| into a |ui::ComboboxModel| so that
 // |PermissionCombobox| can use it.
 class ComboboxModelAdapter : public ui::ComboboxModel {
@@ -204,8 +111,6 @@
 }
 
 // The |PermissionCombobox| provides a combobox for selecting a permission type.
-// This is only used on platforms where the permission dialog uses a combobox
-// instead of a MenuButton (currently, Mac).
 class PermissionCombobox : public views::Combobox,
                            public views::ComboboxListener {
  public:
@@ -277,7 +182,6 @@
     views::GridLayout* layout)
     : profile_(profile),
       icon_(nullptr),
-      menu_button_(nullptr),
       combobox_(nullptr) {
   const int list_item_padding = ChromeLayoutProvider::Get()->GetDistanceMetric(
                                     DISTANCE_CONTROL_LIST_VERTICAL) /
@@ -301,7 +205,7 @@
       base::Bind(&PermissionSelectorRow::PermissionChanged,
                  base::Unretained(this))));
 
-  // Create the permission menu button.
+  // Create the permission combobox.
   InitializeComboboxView(layout, permission);
 
   // Show the permission decision reason, if it was not the user.
@@ -351,9 +255,6 @@
   // causes an explosion when the Combobox attempts to stop observing the
   // ComboboxModel. This hack ensures the Combobox is deleted before its
   // ComboboxModel.
-  //
-  // Technically, the MenuButton has the same problem, but MenuButton doesn't
-  // use its model in its destructor.
   delete combobox_;
 }
 
@@ -391,18 +292,9 @@
   icon_->SetImage(
       PageInfoUI::GetPermissionIcon(permission, label_->enabled_color()));
 
-  // Update the menu button text to reflect the new setting.
-  if (menu_button_) {
-    // Re-layout will be done at the |PageInfoBubbleView| level, since
-    // that view may need to resize itself to accomodate the new sizes of its
-    // contents.
-    menu_button_->SetText(PageInfoUI::PermissionActionToUIString(
-        profile_, permission.type, permission.setting,
-        permission.default_setting, content_settings::SETTING_SOURCE_USER));
-  } else if (combobox_) {
-    bool use_default = permission.setting == CONTENT_SETTING_DEFAULT;
-    combobox_->UpdateSelectedIndex(use_default);
-  }
+  bool use_default = permission.setting == CONTENT_SETTING_DEFAULT;
+  auto* combobox = static_cast<internal::PermissionCombobox*>(combobox_);
+  combobox->UpdateSelectedIndex(use_default);
 
   for (PermissionSelectorRowObserver& observer : observer_list_) {
     observer.OnPermissionChanged(permission);
@@ -410,19 +302,10 @@
 }
 
 int PermissionSelectorRow::GetComboboxWidth() const {
-  DCHECK(combobox_);
   return combobox_->Combobox::GetPreferredSize().width();
 }
 
 void PermissionSelectorRow::SetMinComboboxWidth(int width) {
-  DCHECK(combobox_);
-  combobox_->set_min_width(width);
-}
-
-views::View* PermissionSelectorRow::button() {
-  // These casts are required because the two arms of a ?: cannot have different
-  // types T1 and T2, even if the resulting value of the ?: is about to be a T
-  // and T1 and T2 are both subtypes of T.
-  return menu_button_ ? static_cast<views::View*>(menu_button_)
-                      : static_cast<views::View*>(combobox_);
+  auto* combobox = static_cast<internal::PermissionCombobox*>(combobox_);
+  combobox->set_min_width(width);
 }
diff --git a/chrome/browser/ui/views/page_info/permission_selector_row.h b/chrome/browser/ui/views/page_info/permission_selector_row.h
index 7d5b5ec..26ff5250 100644
--- a/chrome/browser/ui/views/page_info/permission_selector_row.h
+++ b/chrome/browser/ui/views/page_info/permission_selector_row.h
@@ -15,14 +15,11 @@
 #include "chrome/browser/ui/views/page_info/permission_selector_row_observer.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
-#include "ui/views/controls/button/menu_button_listener.h"
 
 class Profile;
 
 namespace internal {
 class ComboboxModelAdapter;
-class PermissionCombobox;
-class PermissionMenuButton;
 }  // namespace internal
 
 namespace test {
@@ -34,6 +31,7 @@
 class ImageView;
 class Label;
 class View;
+class Combobox;
 }  // namespace views
 
 // A |PermissionSelectorRow| is a row in the Page Info bubble that shows a
@@ -70,10 +68,6 @@
   void InitializeComboboxView(views::GridLayout* layout,
                               const PageInfoUI::PermissionInfo& permission);
 
-  // Returns the "button" for this row, which is the control used to change the
-  // permission's value. This is either a |MenuButton| or a |Combobox|.
-  views::View* button();
-
   Profile* profile_;
 
   // Model for the permission's menu.
@@ -83,8 +77,7 @@
   // These are all owned by the views hierarchy:
   views::ImageView* icon_;
   views::Label* label_;
-  internal::PermissionMenuButton* menu_button_;
-  internal::PermissionCombobox* combobox_;
+  views::Combobox* combobox_;
 
   base::ObserverList<PermissionSelectorRowObserver, false>::Unchecked
       observer_list_;
diff --git a/chrome/browser/ui/views/payments/editor_view_controller.cc b/chrome/browser/ui/views/payments/editor_view_controller.cc
index 68591b7f..bea57a6 100644
--- a/chrome/browser/ui/views/payments/editor_view_controller.cc
+++ b/chrome/browser/ui/views/payments/editor_view_controller.cc
@@ -19,6 +19,7 @@
 #include "components/strings/grit/components_strings.h"
 #include "ui/base/ime/text_input_type.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/models/combobox_model.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/views/border.h"
diff --git a/chrome/browser/ui/views/screen_capture_notification_ui_views.cc b/chrome/browser/ui/views/screen_capture_notification_ui_views.cc
index 42c1ccfa..47be9d9 100644
--- a/chrome/browser/ui/views/screen_capture_notification_ui_views.cc
+++ b/chrome/browser/ui/views/screen_capture_notification_ui_views.cc
@@ -7,7 +7,6 @@
 #include "base/macros.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/views/chrome_views_export.h"
-#include "chrome/browser/ui/views_mode_controller.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
 #include "ui/base/hit_test.h"
diff --git a/chrome/browser/ui/views/ssl_client_certificate_selector.cc b/chrome/browser/ui/views/ssl_client_certificate_selector.cc
index 00b435fc..75aa15a 100644
--- a/chrome/browser/ui/views/ssl_client_certificate_selector.cc
+++ b/chrome/browser/ui/views/ssl_client_certificate_selector.cc
@@ -13,7 +13,6 @@
 #include "build/build_config.h"
 #include "chrome/browser/ssl/ssl_client_auth_observer.h"
 #include "chrome/browser/ui/browser_dialogs.h"
-#include "chrome/browser/ui/views_mode_controller.h"
 #include "chrome/grit/generated_resources.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/client_certificate_delegate.h"
@@ -141,12 +140,6 @@
     net::SSLCertRequestInfo* cert_request_info,
     net::ClientCertIdentityList client_certs,
     std::unique_ptr<content::ClientCertificateDelegate> delegate) {
-#if defined(OS_MACOSX)
-  return ShowSSLClientCertificateSelectorCocoa(contents, cert_request_info,
-                                               std::move(client_certs),
-                                               std::move(delegate));
-#else   // defined(OS_MACOSX)
-
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   // Not all WebContentses can show modal dialogs.
@@ -161,7 +154,6 @@
       std::move(delegate));
   selector->Init();
   selector->Show();
-#endif  // defined(OS_MACOSX)
 }
 
 }  // namespace chrome
diff --git a/chrome/browser/ui/views/ssl_client_certificate_selector_mac.h b/chrome/browser/ui/views/ssl_client_certificate_selector_mac.h
new file mode 100644
index 0000000..93dbe7b
--- /dev/null
+++ b/chrome/browser/ui/views/ssl_client_certificate_selector_mac.h
@@ -0,0 +1,43 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_SSL_CLIENT_CERTIFICATE_SELECTOR_MAC_H_
+#define CHROME_BROWSER_UI_VIEWS_SSL_CLIENT_CERTIFICATE_SELECTOR_MAC_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "net/ssl/client_cert_identity.h"
+
+// This header file exists only for testing.  Chrome should access the
+// certificate selector only through the cross-platform interface
+// chrome/browser/ssl_client_certificate_selector.h.
+
+namespace content {
+class ClientCertificateDelegate;
+class WebContents;
+}  // namespace content
+
+namespace net {
+class SSLCertRequestInfo;
+}
+
+namespace chrome {
+
+class OkAndCancelableForTesting {
+ public:
+  virtual void ClickOkButton() = 0;
+  virtual void ClickCancelButton() = 0;
+};
+
+OkAndCancelableForTesting* ShowSSLClientCertificateSelectorMacForTesting(
+    content::WebContents* contents,
+    net::SSLCertRequestInfo* cert_request_info,
+    net::ClientCertIdentityList client_certs,
+    std::unique_ptr<content::ClientCertificateDelegate> delegate,
+    base::OnceClosure dealloc_closure);
+
+}  // namespace chrome
+
+#endif  // CHROME_BROWSER_UI_VIEWS_SSL_CLIENT_CERTIFICATE_SELECTOR_MAC_H_
diff --git a/chrome/browser/ui/views/ssl_client_certificate_selector_mac.mm b/chrome/browser/ui/views/ssl_client_certificate_selector_mac.mm
new file mode 100644
index 0000000..a95fa6d2
--- /dev/null
+++ b/chrome/browser/ui/views/ssl_client_certificate_selector_mac.mm
@@ -0,0 +1,409 @@
+// 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.
+
+#include "chrome/browser/ui/views/ssl_client_certificate_selector_mac.h"
+
+#import <Cocoa/Cocoa.h>
+#import <SecurityInterface/SFChooseIdentityPanel.h>
+#include <objc/runtime.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/macros.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ssl/ssl_client_auth_observer.h"
+#include "chrome/browser/ssl/ssl_client_certificate_selector.h"
+#include "chrome/browser/ui/views/certificate_selector.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/constrained_window/constrained_window_views.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/client_certificate_delegate.h"
+#include "content/public/browser/web_contents.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util_mac.h"
+#include "net/ssl/ssl_cert_request_info.h"
+#include "net/ssl/ssl_platform_key_mac.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+#include "ui/base/ui_features.h"
+#include "ui/views/widget/widget_observer.h"
+
+@interface SFChooseIdentityPanel (SystemPrivate)
+// A system-private interface that dismisses a panel whose sheet was started by
+// -beginSheetForWindow:modalDelegate:didEndSelector:contextInfo:identities:message:
+// as though the user clicked the button identified by returnCode. Verified
+// present in 10.5 through 10.12.
+- (void)_dismissWithCode:(NSInteger)code;
+@end
+
+// This is the main class that runs the certificate selector panel. It's in
+// Objective-C mainly because the only way to get a result out of that panel is
+// a callback of a target/selector pair.
+@interface SSLClientCertificateSelectorMac : NSObject
+
+- (instancetype)
+initWithBrowserContext:(const content::BrowserContext*)browserContext
+       certRequestInfo:(net::SSLCertRequestInfo*)certRequestInfo
+              delegate:
+                  (std::unique_ptr<content::ClientCertificateDelegate>)delegate;
+- (void)createForWebContents:(content::WebContents*)webContents
+                 clientCerts:(net::ClientCertIdentityList)inputClientCerts;
+
+- (void)setOverlayWindow:(views::Widget*)overlayWindow;
+
+- (void)closeSelectorSheetWithCode:(NSModalResponse)response;
+
+@end
+
+// A bridge to the C++ world. It performs the two tasks of being a
+// SSLClientAuthObserver and bridging to the SSL authentication system, and
+// being a WidgetObserver for the overlay window so that if it is closed the
+// cert selector is shut down.
+class SSLClientAuthObserverCocoaBridge : public SSLClientAuthObserver,
+                                         public views::WidgetObserver {
+ public:
+  SSLClientAuthObserverCocoaBridge(
+      const content::BrowserContext* browser_context,
+      net::SSLCertRequestInfo* cert_request_info,
+      std::unique_ptr<content::ClientCertificateDelegate> delegate,
+      SSLClientCertificateSelectorMac* controller)
+      : SSLClientAuthObserver(browser_context,
+                              cert_request_info,
+                              std::move(delegate)),
+        controller_(controller) {}
+
+  void SetOverlayWindow(views::Widget* overlay_window) {
+    overlay_window->AddObserver(this);
+  }
+
+  // SSLClientAuthObserver implementation:
+  void OnCertSelectedByNotification() override {
+    [controller_ closeSelectorSheetWithCode:NSModalResponseStop];
+  }
+
+  // WidgetObserver:
+  void OnWidgetClosing(views::Widget* widget) override {
+    // Note that the SFChooseIdentityPanel takes a reference to its delegate in
+    // its -beginSheetForWindow:... method (bad SFChooseIdentityPanel!) so break
+    // the retain cycle by explicitly canceling the dialog.
+    [controller_ closeSelectorSheetWithCode:NSModalResponseAbort];
+  }
+
+  void OnWidgetDestroying(views::Widget* widget) override {
+    widget->RemoveObserver(this);
+  }
+
+ private:
+  SSLClientCertificateSelectorMac* controller_;  // weak, owns us
+};
+
+namespace {
+
+// These Clear[Window]TableViewDataSources... functions help work around a bug
+// in macOS where SFChooseIdentityPanel leaks a window and some views, including
+// an NSTableView. Future events may make cause the table view to query its
+// dataSource, which will have been deallocated.
+//
+// Note that this was originally thought to be 10.12+ but this reliably crashes
+// on 10.11 (says avi@).
+//
+// Linking against the 10.12 SDK does not "fix" this issue, since
+// NSTableView.dataSource is a "weak" reference, which in non-ARC land still
+// translates to "raw pointer".
+//
+// See https://crbug.com/653093, https://crbug.com/750242 and rdar://29409207
+// for more information.
+
+void ClearTableViewDataSources(NSView* view) {
+  if (auto table_view = base::mac::ObjCCast<NSTableView>(view)) {
+    table_view.dataSource = nil;
+  } else {
+    for (NSView* subview in view.subviews) {
+      ClearTableViewDataSources(subview);
+    }
+  }
+}
+
+void ClearWindowTableViewDataSources(NSWindow* window) {
+  ClearTableViewDataSources(window.contentView);
+}
+
+}  // namespace
+
+@implementation SSLClientCertificateSelectorMac {
+  // The list of SecIdentityRefs offered to the user.
+  base::scoped_nsobject<NSMutableArray> sec_identities_;
+
+  // The corresponding list of ClientCertIdentities.
+  net::ClientCertIdentityList cert_identities_;
+
+  // A C++ object to bridge SSLClientAuthObserver notifications to us.
+  std::unique_ptr<SSLClientAuthObserverCocoaBridge> observer_;
+
+  base::scoped_nsobject<SFChooseIdentityPanel> panel_;
+
+  // Invisible overlay window used to block interaction with the tab underneath.
+  views::Widget* overlayWindow_;
+}
+
+- (instancetype)
+initWithBrowserContext:(const content::BrowserContext*)browserContext
+       certRequestInfo:(net::SSLCertRequestInfo*)certRequestInfo
+              delegate:(std::unique_ptr<content::ClientCertificateDelegate>)
+                           delegate {
+  DCHECK(browserContext);
+  DCHECK(certRequestInfo);
+  if ((self = [super init])) {
+    observer_ = std::make_unique<SSLClientAuthObserverCocoaBridge>(
+        browserContext, certRequestInfo, std::move(delegate), self);
+  }
+  return self;
+}
+
+// The selector sheet ended. There are four possibilities for the return code.
+//
+// These two return codes are actually generated by the SFChooseIdentityPanel,
+// although for testing purposes the OkAndCancelableForTesting implementation
+// will also generate them to simulate the user clicking buttons.
+//
+// - NSModalResponseOK/Cancel: The user clicked the "OK" or "Cancel" button; the
+//   SSL auth system needs to be told of this choice.
+//
+// These two return codes are generated by the SSLClientAuthObserverCocoaBridge
+// to force the SFChooseIdentityPanel to be closed for various reasons.
+//
+// - NSModalResponseAbort: The user closed the owning tab; the SSL auth system
+//   needs to be told of this cancellation.
+// - NSModalResponseStop: The SSL auth system already has an answer; just tear
+//   down the dialog.
+//
+// Note that there is a disagreement between the docs and the SDK header file as
+// to the type of the return code. It has empirically been determined to be an
+// int, not an NSInteger. rdar://45344010
+- (void)sheetDidEnd:(NSWindow*)sheet
+         returnCode:(int)returnCode
+            context:(void*)context {
+  if (returnCode == NSModalResponseAbort) {
+    observer_->CancelCertificateSelection();
+  } else if (returnCode == NSModalResponseOK ||
+             returnCode == NSModalResponseCancel) {
+    net::ClientCertIdentity* cert = nullptr;
+    if (returnCode == NSModalResponseOK) {
+      NSUInteger index = [sec_identities_ indexOfObject:(id)[panel_ identity]];
+      if (index != NSNotFound)
+        cert = cert_identities_[index].get();
+    }
+
+    if (cert) {
+      observer_->CertificateSelected(
+          cert->certificate(),
+          CreateSSLPrivateKeyForSecIdentity(cert->certificate(),
+                                            cert->sec_identity_ref())
+              .get());
+    } else {
+      observer_->CertificateSelected(nullptr, nullptr);
+    }
+  } else {
+    DCHECK_EQ(NSModalResponseStop, returnCode);
+    // Do nothing else; do not call back to the SSL auth system.
+  }
+
+  // Stop observing the SSL authentication system. In theory this isn't needed
+  // as the CertificateSelected() and CancelCertificateSelection() calls both
+  // implicitly call StopObserving() and the SSL auth system calls
+  // StopObserving() before making the OnCertSelectedByNotification() callback.
+  // However, StopObserving() is idempotent so call it out of a deep paranoia
+  // born of many a dangling pointer.
+  observer_->StopObserving();
+
+  // See comment at definition; this works around a bug.
+  ClearWindowTableViewDataSources(sheet);
+
+  // Do not release SFChooseIdentityPanel here. Its -_okClicked: method, after
+  // calling out to this method, keeps accessing its ivars, and if panel_ is the
+  // last reference keeping it alive, it will crash.
+  panel_.autorelease();
+
+  overlayWindow_->Close();  // Asynchronously releases |self|.
+}
+
+- (void)createForWebContents:(content::WebContents*)webContents
+                 clientCerts:(net::ClientCertIdentityList)inputClientCerts {
+  cert_identities_ = std::move(inputClientCerts);
+
+  sec_identities_.reset([[NSMutableArray alloc] init]);
+  for (const auto& cert : cert_identities_) {
+    DCHECK(cert->sec_identity_ref());
+    [sec_identities_ addObject:(id)cert->sec_identity_ref()];
+  }
+
+  // Get the message to display:
+  NSString* message = l10n_util::GetNSStringF(
+      IDS_CLIENT_CERT_DIALOG_TEXT,
+      base::ASCIIToUTF16(
+          observer_->cert_request_info()->host_and_port.ToString()));
+
+  // Create and set up a system choose-identity panel.
+  panel_.reset([[SFChooseIdentityPanel alloc] init]);
+  [panel_ setInformativeText:message];
+  [panel_ setDefaultButtonTitle:l10n_util::GetNSString(IDS_OK)];
+  [panel_ setAlternateButtonTitle:l10n_util::GetNSString(IDS_CANCEL)];
+  base::ScopedCFTypeRef<SecPolicyRef> sslPolicy;
+  if (net::x509_util::CreateSSLClientPolicy(sslPolicy.InitializeInto()) ==
+      noErr) {
+    [panel_ setPolicies:(id)sslPolicy.get()];
+  }
+}
+
+- (void)closeSelectorSheetWithCode:(NSModalResponse)response {
+  // Closing the sheet using -[NSApp endSheet:] doesn't work, so use the private
+  // method. If the sheet is already closed then this is a message send to nil
+  // and thus a no-op.
+  [panel_ _dismissWithCode:response];
+}
+
+- (void)showSheetForWindow:(NSWindow*)window {
+  NSString* title = l10n_util::GetNSString(IDS_CLIENT_CERT_DIALOG_TITLE);
+  [panel_ beginSheetForWindow:window
+                modalDelegate:self
+               didEndSelector:@selector(sheetDidEnd:returnCode:context:)
+                  contextInfo:nil
+                   identities:sec_identities_
+                      message:title];
+  observer_->StartObserving();
+}
+
+- (void)setOverlayWindow:(views::Widget*)overlayWindow {
+  overlayWindow_ = overlayWindow;
+  observer_->SetOverlayWindow(overlayWindow_);
+}
+
+@end
+
+// A testing helper object to run a OnceClosure when deallocated. Attach it as
+// an associated object to test for deallocation of an object without
+// subclassing.
+@interface DeallocClosureCaller : NSObject
+
+- (instancetype)initWithDeallocClosure:(base::OnceClosure)deallocClosure;
+
+@end
+
+@implementation DeallocClosureCaller {
+  base::OnceClosure deallocClosure_;
+}
+
+- (instancetype)initWithDeallocClosure:(base::OnceClosure)deallocClosure {
+  if ((self = [super init])) {
+    deallocClosure_ = std::move(deallocClosure);
+  }
+  return self;
+}
+
+- (void)dealloc {
+  std::move(deallocClosure_).Run();
+  [super dealloc];
+}
+
+@end
+
+namespace {
+
+// A fully transparent, borderless web-modal dialog used to display the
+// OS-provided client certificate selector.
+class SSLClientCertificateSelectorDelegate
+    : public views::WidgetDelegateView,
+      public chrome::OkAndCancelableForTesting {
+ public:
+  SSLClientCertificateSelectorDelegate(
+      content::WebContents* contents,
+      net::SSLCertRequestInfo* cert_request_info,
+      net::ClientCertIdentityList client_certs,
+      std::unique_ptr<content::ClientCertificateDelegate> delegate)
+      : certificate_selector_([[SSLClientCertificateSelectorMac alloc]
+            initWithBrowserContext:contents->GetBrowserContext()
+                   certRequestInfo:cert_request_info
+                          delegate:std::move(delegate)]) {
+    views::Widget* overlay_window =
+        constrained_window::ShowWebModalDialogWithOverlayViews(this, contents);
+    [certificate_selector_ setOverlayWindow:overlay_window];
+    [certificate_selector_ createForWebContents:contents
+                                    clientCerts:std::move(client_certs)];
+    [certificate_selector_ showSheetForWindow:overlay_window->GetNativeWindow()
+                                                  .GetNativeNSWindow()];
+  }
+
+  // WidgetDelegate:
+  ui::ModalType GetModalType() const override { return ui::MODAL_TYPE_CHILD; }
+
+  // OkAndCancelableForTesting:
+  void ClickOkButton() override {
+    [certificate_selector_ closeSelectorSheetWithCode:NSModalResponseOK];
+  }
+
+  void ClickCancelButton() override {
+    [certificate_selector_ closeSelectorSheetWithCode:NSModalResponseCancel];
+  }
+
+  void SetDeallocClosureForTesting(base::OnceClosure dealloc_closure) {
+    DeallocClosureCaller* caller = [[DeallocClosureCaller alloc]
+        initWithDeallocClosure:std::move(dealloc_closure)];
+    // The use of the caller as the key is deliberate; nothing needs to ever
+    // look it up, so it's a convenient unique value.
+    objc_setAssociatedObject(certificate_selector_.get(), caller, caller,
+                             OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+    [caller release];
+  }
+
+ private:
+  base::scoped_nsobject<SSLClientCertificateSelectorMac> certificate_selector_;
+
+  DISALLOW_COPY_AND_ASSIGN(SSLClientCertificateSelectorDelegate);
+};
+
+}  // namespace
+
+namespace chrome {
+
+void ShowSSLClientCertificateSelector(
+    content::WebContents* contents,
+    net::SSLCertRequestInfo* cert_request_info,
+    net::ClientCertIdentityList client_certs,
+    std::unique_ptr<content::ClientCertificateDelegate> delegate) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // Not all WebContentses can show modal dialogs.
+  //
+  // TODO(davidben): Move this hook to the WebContentsDelegate and only try to
+  // show a dialog in Browser's implementation. https://crbug.com/456255
+  if (!CertificateSelector::CanShow(contents))
+    return;
+
+  new SSLClientCertificateSelectorDelegate(contents, cert_request_info,
+                                           std::move(client_certs),
+                                           std::move(delegate));
+}
+
+OkAndCancelableForTesting* ShowSSLClientCertificateSelectorMacForTesting(
+    content::WebContents* contents,
+    net::SSLCertRequestInfo* cert_request_info,
+    net::ClientCertIdentityList client_certs,
+    std::unique_ptr<content::ClientCertificateDelegate> delegate,
+    base::OnceClosure dealloc_closure) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  auto* dialog_delegate = new SSLClientCertificateSelectorDelegate(
+      contents, cert_request_info, std::move(client_certs),
+      std::move(delegate));
+  dialog_delegate->SetDeallocClosureForTesting(std::move(dealloc_closure));
+  return dialog_delegate;
+}
+
+}  // namespace chrome
diff --git a/chrome/browser/ui/cocoa/ssl_client_certificate_selector_cocoa_browsertest.mm b/chrome/browser/ui/views/ssl_client_certificate_selector_mac_browsertest.mm
similarity index 61%
rename from chrome/browser/ui/cocoa/ssl_client_certificate_selector_cocoa_browsertest.mm
rename to chrome/browser/ui/views/ssl_client_certificate_selector_mac_browsertest.mm
index 49cb2ba..065f6c9f 100644
--- a/chrome/browser/ui/cocoa/ssl_client_certificate_selector_cocoa_browsertest.mm
+++ b/chrome/browser/ui/views/ssl_client_certificate_selector_mac_browsertest.mm
@@ -2,14 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "chrome/browser/ui/cocoa/ssl_client_certificate_selector_cocoa.h"
+#include "chrome/browser/ui/views/ssl_client_certificate_selector_mac.h"
 
 #import <SecurityInterface/SFChooseIdentityPanel.h>
 
+#include <utility>
+
 #include "base/bind.h"
 #import "base/mac/mac_util.h"
 #include "base/macros.h"
-#include "base/memory/ptr_util.h"
 #include "chrome/browser/ssl/ssl_client_certificate_selector.h"
 #include "chrome/browser/ssl/ssl_client_certificate_selector_test.h"
 #include "chrome/browser/ui/browser.h"
@@ -31,14 +32,6 @@
 
 using web_modal::WebContentsModalDialogManager;
 
-@interface SFChooseIdentityPanel (SystemPrivate)
-// A system-private interface that dismisses a panel whose sheet was started by
-// -beginSheetForWindow:modalDelegate:didEndSelector:contextInfo:identities:message:
-// as though the user clicked the button identified by returnCode. Verified
-// present in 10.5 through 10.12.
-- (void)_dismissWithCode:(NSInteger)code;
-@end
-
 namespace {
 
 struct TestClientCertificateDelegateResults {
@@ -77,10 +70,10 @@
 
 }  // namespace
 
-class SSLClientCertificateSelectorCocoaTest
+class SSLClientCertificateSelectorMacTest
     : public SSLClientCertificateSelectorTestBase {
  public:
-  ~SSLClientCertificateSelectorCocoaTest() override;
+  ~SSLClientCertificateSelectorMacTest() override;
 
   // InProcessBrowserTest:
   void SetUpInProcessBrowserTestFixture() override;
@@ -97,10 +90,10 @@
   base::ScopedCFTypeRef<SecIdentityRef> sec_identity2_;
 };
 
-SSLClientCertificateSelectorCocoaTest::
-    ~SSLClientCertificateSelectorCocoaTest() = default;
+SSLClientCertificateSelectorMacTest::~SSLClientCertificateSelectorMacTest() =
+    default;
 
-void SSLClientCertificateSelectorCocoaTest::SetUpInProcessBrowserTestFixture() {
+void SSLClientCertificateSelectorMacTest::SetUpInProcessBrowserTestFixture() {
   SSLClientCertificateSelectorTestBase::SetUpInProcessBrowserTestFixture();
 
   base::FilePath certs_dir = net::GetTestCertsDirectory();
@@ -126,7 +119,7 @@
 }
 
 net::ClientCertIdentityList
-SSLClientCertificateSelectorCocoaTest::GetTestCertificateList() {
+SSLClientCertificateSelectorMacTest::GetTestCertificateList() {
   net::ClientCertIdentityList client_cert_list;
   client_cert_list.push_back(std::make_unique<net::ClientCertIdentityMac>(
       client_cert1_, base::ScopedCFTypeRef<SecIdentityRef>(sec_identity1_)));
@@ -135,7 +128,7 @@
   return client_cert_list;
 }
 
-IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorCocoaTest, Basic) {
+IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMacTest, Basic) {
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   WebContentsModalDialogManager* web_contents_modal_dialog_manager =
@@ -143,29 +136,24 @@
   EXPECT_FALSE(web_contents_modal_dialog_manager->IsDialogActive());
 
   TestClientCertificateDelegateResults results;
-  SSLClientCertificateSelectorCocoa* selector = [
-      [SSLClientCertificateSelectorCocoa alloc]
-      initWithBrowserContext:web_contents->GetBrowserContext()
-             certRequestInfo:auth_requestor_->cert_request_info_.get()
-                    delegate:base::WrapUnique(
-                                 new TestClientCertificateDelegate(&results))];
-  [selector displayForWebContents:web_contents
-                      clientCerts:GetTestCertificateList()];
-  content::RunAllPendingInMessageLoop();
-  EXPECT_TRUE([selector panel]);
+  chrome::ShowSSLClientCertificateSelector(
+      web_contents, auth_requestor_->cert_request_info_.get(),
+      GetTestCertificateList(),
+      std::make_unique<TestClientCertificateDelegate>(&results));
+  base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(web_contents_modal_dialog_manager->IsDialogActive());
 
   WebContentsModalDialogManager::TestApi test_api(
       web_contents_modal_dialog_manager);
   test_api.CloseAllDialogs();
-  content::RunAllPendingInMessageLoop();
+  base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(web_contents_modal_dialog_manager->IsDialogActive());
 
   EXPECT_TRUE(results.destroyed);
   EXPECT_FALSE(results.continue_with_certificate_called);
 }
 
-IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorCocoaTest, Cancel) {
+IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMacTest, Cancel) {
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   WebContentsModalDialogManager* web_contents_modal_dialog_manager =
@@ -173,21 +161,17 @@
   EXPECT_FALSE(web_contents_modal_dialog_manager->IsDialogActive());
 
   TestClientCertificateDelegateResults results;
-  SSLClientCertificateSelectorCocoa* selector = [
-      [SSLClientCertificateSelectorCocoa alloc]
-      initWithBrowserContext:web_contents->GetBrowserContext()
-             certRequestInfo:auth_requestor_->cert_request_info_.get()
-                    delegate:base::WrapUnique(
-                                 new TestClientCertificateDelegate(&results))];
-  [selector displayForWebContents:web_contents
-                      clientCerts:GetTestCertificateList()];
-  content::RunAllPendingInMessageLoop();
-  EXPECT_TRUE([selector panel]);
+  chrome::OkAndCancelableForTesting* ok_and_cancelable =
+      chrome::ShowSSLClientCertificateSelectorMacForTesting(
+          web_contents, auth_requestor_->cert_request_info_.get(),
+          GetTestCertificateList(),
+          std::make_unique<TestClientCertificateDelegate>(&results),
+          base::DoNothing());
+  base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(web_contents_modal_dialog_manager->IsDialogActive());
 
-  // Cancel the selector. Dunno if there is a better way to do this.
-  [[selector panel] _dismissWithCode:NSFileHandlingPanelCancelButton];
-  content::RunAllPendingInMessageLoop();
+  ok_and_cancelable->ClickCancelButton();
+  base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(web_contents_modal_dialog_manager->IsDialogActive());
 
   // ContinueWithCertificate(nullptr, nullptr) should have been called.
@@ -197,7 +181,7 @@
   EXPECT_FALSE(results.key);
 }
 
-IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorCocoaTest, Accept) {
+IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMacTest, Accept) {
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   WebContentsModalDialogManager* web_contents_modal_dialog_manager =
@@ -205,21 +189,17 @@
   EXPECT_FALSE(web_contents_modal_dialog_manager->IsDialogActive());
 
   TestClientCertificateDelegateResults results;
-  SSLClientCertificateSelectorCocoa* selector = [
-      [SSLClientCertificateSelectorCocoa alloc]
-      initWithBrowserContext:web_contents->GetBrowserContext()
-             certRequestInfo:auth_requestor_->cert_request_info_.get()
-                    delegate:base::WrapUnique(
-                                 new TestClientCertificateDelegate(&results))];
-  [selector displayForWebContents:web_contents
-                      clientCerts:GetTestCertificateList()];
-  content::RunAllPendingInMessageLoop();
-  EXPECT_TRUE([selector panel]);
+  chrome::OkAndCancelableForTesting* ok_and_cancelable =
+      chrome::ShowSSLClientCertificateSelectorMacForTesting(
+          web_contents, auth_requestor_->cert_request_info_.get(),
+          GetTestCertificateList(),
+          std::make_unique<TestClientCertificateDelegate>(&results),
+          base::DoNothing());
+  base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(web_contents_modal_dialog_manager->IsDialogActive());
 
-  // Accept the selection. Dunno if there is a better way to do this.
-  [[selector panel] _dismissWithCode:NSFileHandlingPanelOKButton];
-  content::RunAllPendingInMessageLoop();
+  ok_and_cancelable->ClickOkButton();
+  base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(web_contents_modal_dialog_manager->IsDialogActive());
 
   // The first cert in the list should have been selected.
@@ -236,34 +216,45 @@
 }
 
 // Test that switching to another tab correctly hides the sheet.
-IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorCocoaTest, HideShow) {
+IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMacTest, HideShow) {
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-  TestClientCertificateDelegateResults results;
-  SSLClientCertificateSelectorCocoa* selector = [
-      [SSLClientCertificateSelectorCocoa alloc]
-      initWithBrowserContext:web_contents->GetBrowserContext()
-             certRequestInfo:auth_requestor_->cert_request_info_.get()
-                    delegate:base::WrapUnique(
-                                 new TestClientCertificateDelegate(&results))];
-  [selector displayForWebContents:web_contents
-                      clientCerts:GetTestCertificateList()];
-  content::RunAllPendingInMessageLoop();
+  WebContentsModalDialogManager* web_contents_modal_dialog_manager =
+      WebContentsModalDialogManager::FromWebContents(web_contents);
+  EXPECT_FALSE(web_contents_modal_dialog_manager->IsDialogActive());
 
-  NSWindow* sheetWindow = [[selector overlayWindow] attachedSheet];
-  EXPECT_EQ(1.0, [sheetWindow alphaValue]);
+  // Account for any child windows that might be present before the certificate
+  // viewer is open.
+  NSWindow* window =
+      web_contents->GetTopLevelNativeWindow().GetNativeNSWindow();
+  NSUInteger num_child_windows = [[window childWindows] count];
+
+  TestClientCertificateDelegateResults results;
+  chrome::ShowSSLClientCertificateSelector(
+      web_contents, auth_requestor_->cert_request_info_.get(),
+      GetTestCertificateList(),
+      std::make_unique<TestClientCertificateDelegate>(&results));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(web_contents_modal_dialog_manager->IsDialogActive());
+
+  EXPECT_EQ(num_child_windows + 1, [[window childWindows] count]);
+  // Assume the last child is the overlay window that was added.
+  NSWindow* overlay_window = [[window childWindows] lastObject];
+
+  NSWindow* sheet_window = [overlay_window attachedSheet];
+  EXPECT_EQ(1.0, [sheet_window alphaValue]);
 
   // Switch to another tab and verify that the sheet is hidden. Interaction with
   // the tab underneath should not be blocked.
   AddBlankTabAndShow(browser());
-  EXPECT_EQ(0.0, [sheetWindow alphaValue]);
-  EXPECT_TRUE([[selector overlayWindow] ignoresMouseEvents]);
+  EXPECT_EQ(0.0, [sheet_window alphaValue]);
+  EXPECT_TRUE([overlay_window ignoresMouseEvents]);
 
   // Switch back and verify that the sheet is shown. Interaction with the tab
   // underneath should be blocked while the sheet is showing.
   chrome::SelectNumberedTab(browser(), 0);
-  EXPECT_EQ(1.0, [sheetWindow alphaValue]);
-  EXPECT_FALSE([[selector overlayWindow] ignoresMouseEvents]);
+  EXPECT_EQ(1.0, [sheet_window alphaValue]);
+  EXPECT_FALSE([overlay_window ignoresMouseEvents]);
 
   EXPECT_FALSE(results.destroyed);
   EXPECT_FALSE(results.continue_with_certificate_called);
@@ -274,48 +265,28 @@
   EXPECT_FALSE(results.continue_with_certificate_called);
 }
 
-@interface DeallocTrackingSSLClientCertificateSelectorCocoa
-    : SSLClientCertificateSelectorCocoa
-@property(nonatomic) BOOL* wasDeallocated;
-@end
-
-@implementation DeallocTrackingSSLClientCertificateSelectorCocoa
-@synthesize wasDeallocated = wasDeallocated_;
-
-- (void)dealloc {
-  *wasDeallocated_ = true;
-  [super dealloc];
-}
-
-@end
-
 // Test that we can't trigger the crash from https://crbug.com/653093
-// Disabled due to flakiness. http://crbug.com/810909
-IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorCocoaTest,
-                       DISABLED_WorkaroundCrashySierra) {
-  BOOL selector_was_deallocated = false;
+IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMacTest,
+                       WorkaroundTableViewCrash) {
+  base::RunLoop run_loop;
 
   @autoreleasepool {
     content::WebContents* web_contents =
         browser()->tab_strip_model()->GetActiveWebContents();
-    DeallocTrackingSSLClientCertificateSelectorCocoa* selector =
-        [[DeallocTrackingSSLClientCertificateSelectorCocoa alloc]
-            initWithBrowserContext:web_contents->GetBrowserContext()
-                   certRequestInfo:auth_requestor_->cert_request_info_.get()
-                          delegate:nil];
-    [selector displayForWebContents:web_contents
-                        clientCerts:GetTestCertificateList()];
-    content::RunAllPendingInMessageLoop();
+    chrome::OkAndCancelableForTesting* ok_and_cancelable =
+        chrome::ShowSSLClientCertificateSelectorMacForTesting(
+            web_contents, auth_requestor_->cert_request_info_.get(),
+            GetTestCertificateList(), nullptr, run_loop.QuitClosure());
+    base::RunLoop().RunUntilIdle();
 
-    selector.wasDeallocated = &selector_was_deallocated;
-
-    [selector.overlayWindow endSheet:selector.overlayWindow.attachedSheet];
-    content::RunAllPendingInMessageLoop();
+    ok_and_cancelable->ClickOkButton();
+    base::RunLoop().RunUntilIdle();
   }
 
-  EXPECT_TRUE(selector_was_deallocated);
+  // Wait until the deallocation lambda fires.
+  run_loop.Run();
 
-  // Without the workaround, this will crash on Sierra.
+  // Without the workaround, this will crash on just about anything 10.11+.
   [[NSNotificationCenter defaultCenter]
       postNotificationName:NSPreferredScrollerStyleDidChangeNotification
                     object:nil
diff --git a/chrome/browser/ui/views/status_bubble_views_browsertest.cc b/chrome/browser/ui/views/status_bubble_views_browsertest.cc
index 52babd1..e1743a5d 100644
--- a/chrome/browser/ui/views/status_bubble_views_browsertest.cc
+++ b/chrome/browser/ui/views/status_bubble_views_browsertest.cc
@@ -9,7 +9,6 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/views/status_bubble_views_browsertest_mac.h"
-#include "chrome/browser/ui/views_mode_controller.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "ui/views/widget/widget.h"
 
diff --git a/chrome/browser/ui/views/textfield_layout.cc b/chrome/browser/ui/views/textfield_layout.cc
index b2280201..cede024 100644
--- a/chrome/browser/ui/views/textfield_layout.cc
+++ b/chrome/browser/ui/views/textfield_layout.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/views/textfield_layout.h"
 
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "ui/base/models/combobox_model.h"
 #include "ui/views/controls/combobox/combobox.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/textfield/textfield.h"
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index a1727f46..e8ce931 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -147,7 +147,6 @@
 #include "chrome/browser/ui/webui/chromeos/first_run/first_run_ui.h"
 #include "chrome/browser/ui/webui/chromeos/internet_config_dialog.h"
 #include "chrome/browser/ui/webui/chromeos/internet_detail_dialog.h"
-#include "chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.h"
 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
 #include "chrome/browser/ui/webui/chromeos/mobile_setup_ui.h"
 #include "chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.h"
@@ -485,8 +484,6 @@
     return &NewWebUI<chromeos::DriveInternalsUI>;
   if (url.host_piece() == chrome::kChromeUIFirstRunHost)
     return &NewWebUI<chromeos::FirstRunUI>;
-  if (url.host_piece() == chrome::kChromeUIKeyboardOverlayHost)
-    return &NewWebUI<KeyboardOverlayUI>;
   if (url.host_piece() == chrome::kChromeUIMobileSetupHost)
     return &NewWebUI<chromeos::MobileSetupUI>;
   if (url.host_piece() == chrome::kChromeUIMultiDeviceSetupHost)
diff --git a/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc b/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc
deleted file mode 100644
index 3e457ff..0000000
--- a/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc
+++ /dev/null
@@ -1,465 +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.
-
-#include "chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.h"
-
-#include <stddef.h>
-
-#include <memory>
-
-#include "ash/shell.h"
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/command_line.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/values.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/common/url_constants.h"
-#include "chrome/grit/browser_resources.h"
-#include "chrome/grit/chromium_strings.h"
-#include "chrome/grit/generated_resources.h"
-#include "chromeos/chromeos_switches.h"
-#include "components/prefs/pref_service.h"
-#include "components/strings/grit/components_strings.h"
-#include "content/public/browser/page_navigator.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_contents_delegate.h"
-#include "content/public/browser/web_ui.h"
-#include "content/public/browser/web_ui_data_source.h"
-#include "content/public/browser/web_ui_message_handler.h"
-#include "ui/base/ime/chromeos/input_method_manager.h"
-#include "ui/chromeos/events/keyboard_layout_util.h"
-#include "ui/chromeos/events/modifier_key.h"
-#include "ui/chromeos/events/pref_names.h"
-#include "ui/display/manager/display_manager.h"
-
-using content::WebUIMessageHandler;
-using ui::chromeos::ModifierKey;
-using ui::WebDialogUI;
-
-namespace {
-
-const char kLearnMoreURL[] =
-#if defined(OFFICIAL_BUILD)
-    "chrome-extension://honijodknafkokifofgiaalefdiedpko/"
-    "main.html?answer=1047364";
-#else
-    "https://support.google.com/chromebook/answer/183101";
-#endif
-
-struct ModifierToLabel {
-  const ModifierKey modifier;
-  const char* label;
-} kModifierToLabels[] = {
-    {ModifierKey::kSearchKey, "search"},
-    {ModifierKey::kControlKey, "ctrl"},
-    {ModifierKey::kAltKey, "alt"},
-    {ModifierKey::kVoidKey, "disabled"},
-    {ModifierKey::kCapsLockKey, "caps lock"},
-    {ModifierKey::kEscapeKey, "esc"},
-    {ModifierKey::kBackspaceKey, "backspace"},
-};
-
-struct I18nContentToMessage {
-  const char* i18n_content;
-  int message;
-} kI18nContentToMessage[] = {
-    {"keyboardOverlayAssistantKeyLabel",
-     IDS_KEYBOARD_OVERLAY_ASSISTANT_KEY_LABEL},
-    {"keyboardOverlayPlayPauseKeyLabel",
-     IDS_KEYBOARD_OVERLAY_PLAY_PAUSE_KEY_LABEL},
-    {"keyboardOverlaySystemMenuKeyLabel",
-     IDS_KEYBOARD_OVERLAY_SYSTEM_MENU_KEY_LABEL},
-    {"keyboardOverlayLauncherKeyLabel",
-     IDS_KEYBOARD_OVERLAY_LAUNCHER_KEY_LABEL},
-    {"keyboardOverlayLearnMore", IDS_LEARN_MORE},
-    {"keyboardOverlayTitle", IDS_KEYBOARD_OVERLAY_TITLE},
-    {"keyboardOverlayEscKeyLabel", IDS_KEYBOARD_OVERLAY_ESC_KEY_LABEL},
-    {"keyboardOverlayBackKeyLabel", IDS_KEYBOARD_OVERLAY_BACK_KEY_LABEL},
-    {"keyboardOverlayForwardKeyLabel", IDS_KEYBOARD_OVERLAY_FORWARD_KEY_LABEL},
-    {"keyboardOverlayReloadKeyLabel", IDS_KEYBOARD_OVERLAY_RELOAD_KEY_LABEL},
-    {"keyboardOverlayFullScreenKeyLabel",
-     IDS_KEYBOARD_OVERLAY_FULL_SCREEN_KEY_LABEL},
-    {"keyboardOverlaySwitchWinKeyLabel",
-     IDS_KEYBOARD_OVERLAY_SWITCH_WIN_KEY_LABEL},
-    {"keyboardOverlayBrightDownKeyLabel",
-     IDS_KEYBOARD_OVERLAY_BRIGHT_DOWN_KEY_LABEL},
-    {"keyboardOverlayBrightUpKeyLabel",
-     IDS_KEYBOARD_OVERLAY_BRIGHT_UP_KEY_LABEL},
-    {"keyboardOverlayMuteKeyLabel", IDS_KEYBOARD_OVERLAY_MUTE_KEY_LABEL},
-    {"keyboardOverlayVolDownKeyLabel", IDS_KEYBOARD_OVERLAY_VOL_DOWN_KEY_LABEL},
-    {"keyboardOverlayVolUpKeyLabel", IDS_KEYBOARD_OVERLAY_VOL_UP_KEY_LABEL},
-    {"keyboardOverlayPowerKeyLabel", IDS_KEYBOARD_OVERLAY_POWER_KEY_LABEL},
-    {"keyboardOverlayBackspaceKeyLabel",
-     IDS_KEYBOARD_OVERLAY_BACKSPACE_KEY_LABEL},
-    {"keyboardOverlayTabKeyLabel", IDS_KEYBOARD_OVERLAY_TAB_KEY_LABEL},
-    {"keyboardOverlaySearchKeyLabel", IDS_KEYBOARD_OVERLAY_SEARCH_KEY_LABEL},
-    {"keyboardOverlayEnterKeyLabel", IDS_KEYBOARD_OVERLAY_ENTER_KEY_LABEL},
-    {"keyboardOverlayShiftKeyLabel", IDS_KEYBOARD_OVERLAY_SHIFT_KEY_LABEL},
-    {"keyboardOverlayCtrlKeyLabel", IDS_KEYBOARD_OVERLAY_CTRL_KEY_LABEL},
-    {"keyboardOverlayAltKeyLabel", IDS_KEYBOARD_OVERLAY_ALT_KEY_LABEL},
-    {"keyboardOverlayLeftKeyLabel", IDS_KEYBOARD_OVERLAY_LEFT_KEY_LABEL},
-    {"keyboardOverlayRightKeyLabel", IDS_KEYBOARD_OVERLAY_RIGHT_KEY_LABEL},
-    {"keyboardOverlayUpKeyLabel", IDS_KEYBOARD_OVERLAY_UP_KEY_LABEL},
-    {"keyboardOverlayDownKeyLabel", IDS_KEYBOARD_OVERLAY_DOWN_KEY_LABEL},
-    {"keyboardOverlayInstructionsHide", IDS_KEYBOARD_OVERLAY_INSTRUCTIONS_HIDE},
-    {"keyboardOverlayActivateLastShelfItem",
-     IDS_KEYBOARD_OVERLAY_ACTIVATE_LAST_SHELF_ITEM},
-    {"keyboardOverlayActivateLastTab", IDS_KEYBOARD_OVERLAY_ACTIVATE_LAST_TAB},
-    {"keyboardOverlayActivateShelfItem1",
-     IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_1},
-    {"keyboardOverlayActivateShelfItem2",
-     IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_2},
-    {"keyboardOverlayActivateShelfItem3",
-     IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_3},
-    {"keyboardOverlayActivateShelfItem4",
-     IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_4},
-    {"keyboardOverlayActivateShelfItem5",
-     IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_5},
-    {"keyboardOverlayActivateShelfItem6",
-     IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_6},
-    {"keyboardOverlayActivateShelfItem7",
-     IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_7},
-    {"keyboardOverlayActivateShelfItem8",
-     IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_8},
-    {"keyboardOverlayActivateNextTab", IDS_KEYBOARD_OVERLAY_ACTIVATE_NEXT_TAB},
-    {"keyboardOverlayActivatePreviousTab",
-     IDS_KEYBOARD_OVERLAY_ACTIVATE_PREVIOUS_TAB},
-    {"keyboardOverlayActivateTab1", IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_1},
-    {"keyboardOverlayActivateTab2", IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_2},
-    {"keyboardOverlayActivateTab3", IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_3},
-    {"keyboardOverlayActivateTab4", IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_4},
-    {"keyboardOverlayActivateTab5", IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_5},
-    {"keyboardOverlayActivateTab6", IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_6},
-    {"keyboardOverlayActivateTab7", IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_7},
-    {"keyboardOverlayActivateTab8", IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_8},
-    {"keyboardOverlayAddWwwAndComAndOpenAddress",
-     IDS_KEYBOARD_OVERLAY_ADD_WWW_AND_COM_AND_OPEN_ADDRESS},
-    {"keyboardOverlayBookmarkAllTabs", IDS_KEYBOARD_OVERLAY_BOOKMARK_ALL_TABS},
-    {"keyboardOverlayBookmarkCurrentPage",
-     IDS_KEYBOARD_OVERLAY_BOOKMARK_CURRENT_PAGE},
-    {"keyboardOverlayBookmarkManager", IDS_KEYBOARD_OVERLAY_BOOKMARK_MANAGER},
-    {"keyboardOverlayCenterWindow", IDS_KEYBOARD_OVERLAY_CENTER_WINDOW},
-    {"keyboardOverlayClearBrowsingDataDialog",
-     IDS_KEYBOARD_OVERLAY_CLEAR_BROWSING_DATA_DIALOG},
-    {"keyboardOverlayCloseTab", IDS_KEYBOARD_OVERLAY_CLOSE_TAB},
-    {"keyboardOverlayCloseWindow", IDS_KEYBOARD_OVERLAY_CLOSE_WINDOW},
-    {"keyboardOverlayContextMenu", IDS_KEYBOARD_OVERLAY_CONTEXT_MENU},
-    {"keyboardOverlayCopy", IDS_KEYBOARD_OVERLAY_COPY},
-    {"keyboardOverlayCut", IDS_KEYBOARD_OVERLAY_CUT},
-    {"keyboardOverlayCycleThroughInputMethods",
-     IDS_KEYBOARD_OVERLAY_CYCLE_THROUGH_INPUT_METHODS},
-    {"keyboardOverlayDecreaseKeyBrightness",
-     IDS_KEYBOARD_OVERLAY_DECREASE_KEY_BRIGHTNESS},
-    {"keyboardOverlayDelete", IDS_KEYBOARD_OVERLAY_DELETE},
-    {"keyboardOverlayDeleteWord", IDS_KEYBOARD_OVERLAY_DELETE_WORD},
-    {"keyboardOverlayDeveloperTools", IDS_KEYBOARD_OVERLAY_DEVELOPER_TOOLS},
-    {"keyboardOverlayDockWindowLeft", IDS_KEYBOARD_OVERLAY_DOCK_WINDOW_LEFT},
-    {"keyboardOverlayDockWindowRight", IDS_KEYBOARD_OVERLAY_DOCK_WINDOW_RIGHT},
-    {"keyboardOverlayDomInspector", IDS_KEYBOARD_OVERLAY_DOM_INSPECTOR},
-    {"keyboardOverlayDownloads", IDS_KEYBOARD_OVERLAY_DOWNLOADS},
-    {"keyboardOverlayEnd", IDS_KEYBOARD_OVERLAY_END},
-    {"keyboardOverlayF1", IDS_KEYBOARD_OVERLAY_F1},
-    {"keyboardOverlayF10", IDS_KEYBOARD_OVERLAY_F10},
-    {"keyboardOverlayF11", IDS_KEYBOARD_OVERLAY_F11},
-    {"keyboardOverlayF12", IDS_KEYBOARD_OVERLAY_F12},
-    {"keyboardOverlayF2", IDS_KEYBOARD_OVERLAY_F2},
-    {"keyboardOverlayF3", IDS_KEYBOARD_OVERLAY_F3},
-    {"keyboardOverlayF4", IDS_KEYBOARD_OVERLAY_F4},
-    {"keyboardOverlayF5", IDS_KEYBOARD_OVERLAY_F5},
-    {"keyboardOverlayF6", IDS_KEYBOARD_OVERLAY_F6},
-    {"keyboardOverlayF7", IDS_KEYBOARD_OVERLAY_F7},
-    {"keyboardOverlayF8", IDS_KEYBOARD_OVERLAY_F8},
-    {"keyboardOverlayF9", IDS_KEYBOARD_OVERLAY_F9},
-    {"keyboardOverlayFindPreviousText",
-     IDS_KEYBOARD_OVERLAY_FIND_PREVIOUS_TEXT},
-    {"keyboardOverlayFindText", IDS_KEYBOARD_OVERLAY_FIND_TEXT},
-    {"keyboardOverlayFindTextAgain", IDS_KEYBOARD_OVERLAY_FIND_TEXT_AGAIN},
-    {"keyboardOverlayFocusAddressBar", IDS_KEYBOARD_OVERLAY_FOCUS_ADDRESS_BAR},
-    {"keyboardOverlayFocusAddressBarInSearchMode",
-     IDS_KEYBOARD_OVERLAY_FOCUS_ADDRESS_BAR_IN_SEARCH_MODE},
-    {"keyboardOverlayFocusBookmarks", IDS_KEYBOARD_OVERLAY_FOCUS_BOOKMARKS},
-    {"keyboardOverlayFocusShelf", IDS_KEYBOARD_OVERLAY_FOCUS_SHELF},
-    {"keyboardOverlayFocusNextPane", IDS_KEYBOARD_OVERLAY_FOCUS_NEXT_PANE},
-    {"keyboardOverlayFocusPreviousPane",
-     IDS_KEYBOARD_OVERLAY_FOCUS_PREVIOUS_PANE},
-    {"keyboardOverlayFocusToolbar", IDS_KEYBOARD_OVERLAY_FOCUS_TOOLBAR},
-    {"keyboardOverlayGoBack", IDS_KEYBOARD_OVERLAY_GO_BACK},
-    {"keyboardOverlayGoForward", IDS_KEYBOARD_OVERLAY_GO_FORWARD},
-    {"keyboardOverlayHelp", IDS_KEYBOARD_OVERLAY_HELP},
-    {"keyboardOverlayHistory", IDS_KEYBOARD_OVERLAY_HISTORY},
-    {"keyboardOverlayHome", IDS_KEYBOARD_OVERLAY_HOME},
-    {"keyboardOverlayIncreaseKeyBrightness",
-     IDS_KEYBOARD_OVERLAY_INCREASE_KEY_BRIGHTNESS},
-    {"keyboardOverlayInputUnicodeCharacters",
-     IDS_KEYBOARD_OVERLAY_INPUT_UNICODE_CHARACTERS},
-    {"keyboardOverlayInsert", IDS_KEYBOARD_OVERLAY_INSERT},
-    {"keyboardOverlayJavascriptConsole",
-     IDS_KEYBOARD_OVERLAY_JAVASCRIPT_CONSOLE},
-    {"keyboardOverlayLockScreen", IDS_KEYBOARD_OVERLAY_LOCK_SCREEN},
-    {"keyboardOverlayLockScreenOrPowerOff",
-     IDS_KEYBOARD_OVERLAY_LOCK_SCREEN_OR_POWER_OFF},
-    {"keyboardOverlayMagnifierDecreaseZoom",
-     IDS_KEYBOARD_OVERLAY_MAGNIFIER_DECREASE_ZOOM},
-    {"keyboardOverlayMagnifierIncreaseZoom",
-     IDS_KEYBOARD_OVERLAY_MAGNIFIER_INCREASE_ZOOM},
-    {"keyboardOverlayMaximizeWindow", IDS_KEYBOARD_OVERLAY_MAXIMIZE_WINDOW},
-    {"keyboardOverlayMinimizeWindow", IDS_KEYBOARD_OVERLAY_MINIMIZE_WINDOW},
-    {"keyboardOverlayMirrorMonitors", IDS_KEYBOARD_OVERLAY_MIRROR_MONITORS},
-    // TODO(warx): keyboard overlay name for move window between displays
-    // shortcuts need to be updated when new keyboard shortcuts helper is there.
-    {"keyboardOverlayMoveActiveWindowBetweenDisplays",
-     IDS_KEYBOARD_OVERLAY_MOVE_ACTIVE_WINDOW_BETWEEN_DISPLAYS},
-    {"keyboardOverlayNewIncognitoWindow",
-     IDS_KEYBOARD_OVERLAY_NEW_INCOGNITO_WINDOW},
-    {"keyboardOverlayNewTab", IDS_KEYBOARD_OVERLAY_NEW_TAB},
-    {"keyboardOverlayNewTerminal", IDS_KEYBOARD_OVERLAY_NEW_TERMINAL},
-    {"keyboardOverlayNewWindow", IDS_KEYBOARD_OVERLAY_NEW_WINDOW},
-    {"keyboardOverlayNextUser", IDS_KEYBOARD_OVERLAY_NEXT_USER},
-    {"keyboardOverlayNextWindow", IDS_KEYBOARD_OVERLAY_NEXT_WINDOW},
-    {"keyboardOverlayNextWord", IDS_KEYBOARD_OVERLAY_NEXT_WORD},
-    {"keyboardOverlayOpen", IDS_KEYBOARD_OVERLAY_OPEN},
-    {"keyboardOverlayOpenAddressInNewTab",
-     IDS_KEYBOARD_OVERLAY_OPEN_ADDRESS_IN_NEW_TAB},
-    {"keyboardOverlayOpenFileManager", IDS_KEYBOARD_OVERLAY_OPEN_FILE_MANAGER},
-    {"keyboardOverlayPageDown", IDS_KEYBOARD_OVERLAY_PAGE_DOWN},
-    {"keyboardOverlayPageUp", IDS_KEYBOARD_OVERLAY_PAGE_UP},
-    {"keyboardOverlayPaste", IDS_KEYBOARD_OVERLAY_PASTE},
-    {"keyboardOverlayPasteAsPlainText",
-     IDS_KEYBOARD_OVERLAY_PASTE_AS_PLAIN_TEXT},
-    {"keyboardOverlayPreviousUser", IDS_KEYBOARD_OVERLAY_PREVIOUS_USER},
-    {"keyboardOverlayPreviousWindow", IDS_KEYBOARD_OVERLAY_PREVIOUS_WINDOW},
-    {"keyboardOverlayPreviousWord", IDS_KEYBOARD_OVERLAY_PREVIOUS_WORD},
-    {"keyboardOverlayPrint", IDS_KEYBOARD_OVERLAY_PRINT},
-    {"keyboardOverlayReloadCurrentPage",
-     IDS_KEYBOARD_OVERLAY_RELOAD_CURRENT_PAGE},
-    {"keyboardOverlayReloadBypassingCache",
-     IDS_KEYBOARD_OVERLAY_RELOAD_BYPASSING_CACHE},
-    {"keyboardOverlayReopenLastClosedTab",
-     IDS_KEYBOARD_OVERLAY_REOPEN_LAST_CLOSED_TAB},
-    {"keyboardOverlayReportIssue", IDS_KEYBOARD_OVERLAY_REPORT_ISSUE},
-    {"keyboardOverlayResetScreenZoom", IDS_KEYBOARD_OVERLAY_RESET_SCREEN_ZOOM},
-    {"keyboardOverlayResetZoom", IDS_KEYBOARD_OVERLAY_RESET_ZOOM},
-    {"keyboardOverlayRotateScreen", IDS_KEYBOARD_OVERLAY_ROTATE_SCREEN},
-    {"keyboardOverlayRotateWindow", IDS_KEYBOARD_OVERLAY_ROTATE_WINDOW},
-    {"keyboardOverlaySave", IDS_KEYBOARD_OVERLAY_SAVE},
-    {"keyboardOverlayScreenshotRegion", IDS_KEYBOARD_OVERLAY_SCREENSHOT_REGION},
-    {"keyboardOverlayScreenshotWindow", IDS_KEYBOARD_OVERLAY_SCREENSHOT_WINDOW},
-    {"keyboardOverlayScrollUpOnePage", IDS_KEYBOARD_OVERLAY_SCROLL_UP_ONE_PAGE},
-    {"keyboardOverlaySelectAll", IDS_KEYBOARD_OVERLAY_SELECT_ALL},
-    {"keyboardOverlaySelectPreviousInputMethod",
-     IDS_KEYBOARD_OVERLAY_SELECT_PREVIOUS_INPUT_METHOD},
-    {"keyboardOverlaySelectWordAtATime",
-     IDS_KEYBOARD_OVERLAY_SELECT_WORD_AT_A_TIME},
-    {"keyboardOverlayShowImeBubble", IDS_KEYBOARD_OVERLAY_SHOW_IME_BUBBLE},
-    {"keyboardOverlayShowMessageCenter",
-     IDS_KEYBOARD_OVERLAY_SHOW_MESSAGE_CENTER},
-    {"keyboardOverlayShowStatusMenu", IDS_KEYBOARD_OVERLAY_SHOW_STATUS_MENU},
-    {"keyboardOverlayShowStylusTools", IDS_KEYBOARD_OVERLAY_SHOW_STYLUS_TOOLS},
-    {"keyboardOverlayShowWrenchMenu", IDS_KEYBOARD_OVERLAY_SHOW_WRENCH_MENU},
-    {"keyboardOverlaySignOut", IDS_KEYBOARD_OVERLAY_SIGN_OUT},
-    {"keyboardOverlaySuspend", IDS_KEYBOARD_OVERLAY_SUSPEND},
-    {"keyboardOverlaySwapPrimaryMonitor",
-     IDS_KEYBOARD_OVERLAY_SWAP_PRIMARY_MONITOR},
-    {"keyboardOverlayTakeScreenshot", IDS_KEYBOARD_OVERLAY_TAKE_SCREENSHOT},
-    {"keyboardOverlayTaskManager", IDS_KEYBOARD_OVERLAY_TASK_MANAGER},
-    {"keyboardOverlayToggleBookmarkBar",
-     IDS_KEYBOARD_OVERLAY_TOGGLE_BOOKMARK_BAR},
-    {"keyboardOverlayToggleCapsLock", IDS_KEYBOARD_OVERLAY_TOGGLE_CAPS_LOCK},
-    {"keyboardOverlayDisableCapsLock", IDS_KEYBOARD_OVERLAY_DISABLE_CAPS_LOCK},
-    {"keyboardOverlayToggleChromevoxSpokenFeedback",
-     IDS_KEYBOARD_OVERLAY_TOGGLE_CHROMEVOX_SPOKEN_FEEDBACK},
-    {"keyboardOverlayToggleDictation", IDS_KEYBOARD_OVERLAY_TOGGLE_DICTATION},
-    {"keyboardOverlayToggleDockedMagnifier",
-     IDS_KEYBOARD_OVERLAY_TOGGLE_DOCKED_MAGNIFIER},
-    {"keyboardOverlayToggleFullscreenMagnifier",
-     IDS_KEYBOARD_OVERLAY_TOGGLE_FULLSCREEN_MAGNIFIER},
-    {"keyboardOverlayToggleHighContrastMode",
-     IDS_KEYBOARD_OVERLAY_TOGGLE_HIGH_CONTRAST_MODE},
-    {"keyboardOverlayToggleProjectionTouchHud",
-     IDS_KEYBOARD_OVERLAY_TOGGLE_PROJECTION_TOUCH_HUD},
-    {"keyboardOverlayTouchHudModeChange",
-     IDS_KEYBOARD_OVERLAY_TOUCH_HUD_MODE_CHANGE},
-    {"keyboardOverlayUndo", IDS_KEYBOARD_OVERLAY_UNDO},
-    {"keyboardOverlayUnpin", IDS_KEYBOARD_OVERLAY_UNPIN},
-    {"keyboardOverlayViewKeyboardOverlay",
-     IDS_KEYBOARD_OVERLAY_VIEW_KEYBOARD_OVERLAY},
-    {"keyboardOverlayViewSource", IDS_KEYBOARD_OVERLAY_VIEW_SOURCE},
-    {"keyboardOverlayWordMove", IDS_KEYBOARD_OVERLAY_WORD_MOVE},
-    {"keyboardOverlayZoomIn", IDS_KEYBOARD_OVERLAY_ZOOM_IN},
-    {"keyboardOverlayZoomOut", IDS_KEYBOARD_OVERLAY_ZOOM_OUT},
-    {"keyboardOverlayZoomScreenIn", IDS_KEYBOARD_OVERLAY_ZOOM_SCREEN_IN},
-    {"keyboardOverlayZoomScreenOut", IDS_KEYBOARD_OVERLAY_ZOOM_SCREEN_OUT},
-    {"keyboardOverlayVoiceInteraction",
-     IDS_KEYBOARD_OVERLAY_VOICE_INTERACTION}};
-
-bool TopRowKeysAreFunctionKeys(Profile* profile) {
-  if (!profile)
-    return false;
-
-  const PrefService* prefs = profile->GetPrefs();
-  return prefs ? prefs->GetBoolean(prefs::kLanguageSendFunctionKeys) : false;
-}
-
-std::string ModifierKeyToLabel(ModifierKey modifier) {
-  for (size_t i = 0; i < arraysize(kModifierToLabels); ++i) {
-    if (modifier == kModifierToLabels[i].modifier) {
-      return kModifierToLabels[i].label;
-    }
-  }
-  return "";
-}
-
-content::WebUIDataSource* CreateKeyboardOverlayUIHTMLSource(Profile* profile) {
-  content::WebUIDataSource* source =
-      content::WebUIDataSource::Create(chrome::kChromeUIKeyboardOverlayHost);
-
-  for (size_t i = 0; i < arraysize(kI18nContentToMessage); ++i) {
-    source->AddLocalizedString(kI18nContentToMessage[i].i18n_content,
-                               kI18nContentToMessage[i].message);
-  }
-
-  // |kI18nContentToMessage| is a static array initialized before it's possible
-  // to call ui::DeviceUsesKeyboardLayout2(), so we add the
-  // |keyboardOverlayInstructions| string at runtime here.
-  source->AddLocalizedString("keyboardOverlayInstructions",
-                             ui::DeviceUsesKeyboardLayout2()
-                                 ? IDS_KEYBOARD_OVERLAY_INSTRUCTIONS_LAYOUT2
-                                 : IDS_KEYBOARD_OVERLAY_INSTRUCTIONS);
-
-  source->AddString("keyboardOverlayLearnMoreURL",
-                    base::UTF8ToUTF16(kLearnMoreURL));
-  source->AddBoolean("keyboardOverlayHasChromeOSDiamondKey",
-                     base::CommandLine::ForCurrentProcess()->HasSwitch(
-                         chromeos::switches::kHasChromeOSDiamondKey));
-  source->AddBoolean("keyboardOverlayTopRowKeysAreFunctionKeys",
-                     TopRowKeysAreFunctionKeys(profile));
-  source->AddBoolean("voiceInteractionEnabled",
-                     chromeos::switches::IsVoiceInteractionEnabled());
-  source->AddBoolean("keyboardOverlayUsesLayout2",
-                     ui::DeviceUsesKeyboardLayout2());
-  ash::Shell* shell = ash::Shell::Get();
-  display::DisplayManager* display_manager = shell->display_manager();
-  source->AddBoolean("keyboardOverlayIsDisplayUIScalingEnabled",
-                     display_manager->IsDisplayUIScalingEnabled());
-  source->SetJsonPath("strings.js");
-  source->AddResourcePath("keyboard_overlay.js", IDR_KEYBOARD_OVERLAY_JS);
-  source->SetDefaultResource(IDR_KEYBOARD_OVERLAY_HTML);
-  return source;
-}
-
-}  // namespace
-
-// The handler for Javascript messages related to the "keyboardoverlay" view.
-class KeyboardOverlayHandler
-    : public WebUIMessageHandler,
-      public base::SupportsWeakPtr<KeyboardOverlayHandler> {
- public:
-  explicit KeyboardOverlayHandler(Profile* profile);
-  ~KeyboardOverlayHandler() override;
-
-  // WebUIMessageHandler implementation.
-  void RegisterMessages() override;
-
- private:
-  // Called when the page requires the input method ID corresponding to the
-  // current input method or keyboard layout during initialization.
-  void GetInputMethodId(const base::ListValue* args);
-
-  // Called when the page requres the information of modifier key remapping
-  // during the initialization.
-  void GetLabelMap(const base::ListValue* args);
-
-  // Called when the learn more link is clicked.
-  void OpenLearnMorePage(const base::ListValue* args);
-
-  Profile* profile_;
-
-  DISALLOW_COPY_AND_ASSIGN(KeyboardOverlayHandler);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// KeyboardOverlayHandler
-//
-////////////////////////////////////////////////////////////////////////////////
-KeyboardOverlayHandler::KeyboardOverlayHandler(Profile* profile)
-    : profile_(profile) {
-}
-
-KeyboardOverlayHandler::~KeyboardOverlayHandler() {
-}
-
-void KeyboardOverlayHandler::RegisterMessages() {
-  web_ui()->RegisterMessageCallback(
-      "getInputMethodId",
-      base::BindRepeating(&KeyboardOverlayHandler::GetInputMethodId,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "getLabelMap", base::BindRepeating(&KeyboardOverlayHandler::GetLabelMap,
-                                         base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "openLearnMorePage",
-      base::BindRepeating(&KeyboardOverlayHandler::OpenLearnMorePage,
-                          base::Unretained(this)));
-}
-
-void KeyboardOverlayHandler::GetInputMethodId(const base::ListValue* args) {
-  chromeos::input_method::InputMethodManager* manager =
-      chromeos::input_method::InputMethodManager::Get();
-  const chromeos::input_method::InputMethodDescriptor& descriptor =
-      manager->GetActiveIMEState()->GetCurrentInputMethod();
-  base::Value param(descriptor.id());
-  web_ui()->CallJavascriptFunctionUnsafe("initKeyboardOverlayId", param);
-}
-
-void KeyboardOverlayHandler::GetLabelMap(const base::ListValue* args) {
-  DCHECK(profile_);
-  PrefService* pref_service = profile_->GetPrefs();
-  using ModifierMap = std::map<ModifierKey, ModifierKey>;
-  ModifierMap modifier_map;
-  modifier_map[ModifierKey::kSearchKey] = static_cast<ModifierKey>(
-      pref_service->GetInteger(prefs::kLanguageRemapSearchKeyTo));
-  modifier_map[ModifierKey::kControlKey] = static_cast<ModifierKey>(
-      pref_service->GetInteger(prefs::kLanguageRemapControlKeyTo));
-  modifier_map[ModifierKey::kAltKey] = static_cast<ModifierKey>(
-      pref_service->GetInteger(prefs::kLanguageRemapAltKeyTo));
-  // TODO(mazda): Support prefs::kLanguageRemapCapsLockKeyTo once Caps Lock is
-  // added to the overlay UI.
-
-  base::DictionaryValue dict;
-  for (ModifierMap::const_iterator i = modifier_map.begin();
-       i != modifier_map.end(); ++i) {
-    dict.SetString(ModifierKeyToLabel(i->first), ModifierKeyToLabel(i->second));
-  }
-
-  web_ui()->CallJavascriptFunctionUnsafe("initIdentifierMap", dict);
-}
-
-void KeyboardOverlayHandler::OpenLearnMorePage(const base::ListValue* args) {
-  web_ui()->GetWebContents()->GetDelegate()->OpenURLFromTab(
-      web_ui()->GetWebContents(),
-      content::OpenURLParams(GURL(kLearnMoreURL), content::Referrer(),
-                             WindowOpenDisposition::NEW_FOREGROUND_TAB,
-                             ui::PAGE_TRANSITION_LINK, false));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// KeyboardOverlayUI
-//
-////////////////////////////////////////////////////////////////////////////////
-
-KeyboardOverlayUI::KeyboardOverlayUI(content::WebUI* web_ui)
-    : WebDialogUI(web_ui) {
-  Profile* profile = Profile::FromWebUI(web_ui);
-  web_ui->AddMessageHandler(std::make_unique<KeyboardOverlayHandler>(profile));
-
-  // Set up the chrome://keyboardoverlay/ source.
-  content::WebUIDataSource::Add(profile,
-                                CreateKeyboardOverlayUIHTMLSource(profile));
-}
diff --git a/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.h b/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.h
deleted file mode 100644
index 002dad9c..0000000
--- a/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.h
+++ /dev/null
@@ -1,19 +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.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_KEYBOARD_OVERLAY_UI_H_
-#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_KEYBOARD_OVERLAY_UI_H_
-
-#include "base/macros.h"
-#include "ui/web_dialogs/web_dialog_ui.h"
-
-class KeyboardOverlayUI : public ui::WebDialogUI {
- public:
-  explicit KeyboardOverlayUI(content::WebUI* web_ui);
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(KeyboardOverlayUI);
-};
-
-#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_KEYBOARD_OVERLAY_UI_H_
diff --git a/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui_browsertest.cc b/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui_browsertest.cc
deleted file mode 100644
index 7e96f446..0000000
--- a/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui_browsertest.cc
+++ /dev/null
@@ -1,178 +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 "ash/public/cpp/accelerators.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/url_constants.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_ui_message_handler.h"
-#include "content/public/test/browser_test_utils.h"
-#include "ui/events/keycodes/keyboard_code_conversion.h"
-
-namespace {
-
-class TestWebUIMessageHandler : public content::WebUIMessageHandler {
- public:
-  TestWebUIMessageHandler() = default;
-  ~TestWebUIMessageHandler() override = default;
-
-  // content::WebUIMessageHandler:
-  void RegisterMessages() override {
-    web_ui()->RegisterMessageCallback(
-        "didPaint",
-        base::BindRepeating(&TestWebUIMessageHandler::HandleDidPaint,
-                            base::Unretained(this)));
-  }
-
- private:
-  void HandleDidPaint(const base::ListValue*) {}
-
-  DISALLOW_COPY_AND_ASSIGN(TestWebUIMessageHandler);
-};
-
-content::WebContents* StartKeyboardOverlayUI(Browser* browser) {
-  ui_test_utils::NavigateToURL(browser,
-                               GURL(chrome::kChromeUIKeyboardOverlayURL));
-  content::WebContents* web_contents =
-      browser->tab_strip_model()->GetActiveWebContents();
-  web_contents->GetWebUI()->AddMessageHandler(
-      std::make_unique<TestWebUIMessageHandler>());
-  return web_contents;
-}
-
-bool IsDisplayUIScalingEnabled(content::WebContents* web_contents) {
-  bool is_display_ui_scaling_enabled;
-  EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
-      web_contents,
-      "domAutomationController.send(isDisplayUIScalingEnabled());",
-      &is_display_ui_scaling_enabled));
-  return is_display_ui_scaling_enabled;
-}
-
-// Skip some accelerators in the tests:
-// 1. If the accelerator has no modifier, i.e. ui::EF_NONE, or for "Caps
-// Lock", such as ui::VKEY_MENU and ui::VKEY_LWIN, the logic to show it on
-// the keyboard overlay is not by the mapping of
-// keyboardOverlayData['shortcut'], so it can not be tested by this test.
-// 2. If it has debug modifiers: ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN |
-// ui::EF_SHIFT_DOWN
-bool ShouldSkip(const ash::AcceleratorData& accelerator) {
-  return accelerator.keycode == ui::VKEY_MENU ||
-         accelerator.keycode == ui::VKEY_LWIN ||
-         accelerator.modifiers == ui::EF_NONE ||
-         accelerator.modifiers ==
-             (ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN);
-}
-
-std::string KeyboardCodeToLabel(const ash::AcceleratorData& accelerator,
-                                content::WebContents* web_contents) {
-  std::string label;
-  EXPECT_TRUE(content::ExecuteScriptAndExtractString(
-      web_contents,
-      "domAutomationController.send("
-      "  (function(number) {"
-      "       if (!!KEYCODE_TO_LABEL[number]) {"
-      "             return KEYCODE_TO_LABEL[number];"
-      "       } else {"
-      "             return 'NONE';"
-      "       }"
-      "  })(" +
-          std::to_string(static_cast<unsigned int>(accelerator.keycode)) +
-      "    )"
-      ");",
-      &label));
-  if (label == "NONE") {
-    label = base::ToLowerASCII(static_cast<char>(
-        LocatedToNonLocatedKeyboardCode(accelerator.keycode)));
-  }
-  return label;
-}
-
-std::string GenerateShortcutKey(const ash::AcceleratorData& accelerator,
-                                content::WebContents* web_contents) {
-  std::string shortcut = KeyboardCodeToLabel(accelerator, web_contents);
-  // The order of the "if" conditions should not be changed because the
-  // modifiers are expected to be alphabetical sorted in the generated
-  // shortcut.
-  if (accelerator.modifiers & ui::EF_ALT_DOWN)
-    shortcut.append("<>ALT");
-  if (accelerator.modifiers & ui::EF_CONTROL_DOWN)
-    shortcut.append("<>CTRL");
-  if (accelerator.modifiers & ui::EF_COMMAND_DOWN)
-    shortcut.append("<>SEARCH");
-  if (accelerator.modifiers & ui::EF_SHIFT_DOWN)
-    shortcut.append("<>SHIFT");
-  return shortcut;
-}
-
-bool ContainsShortcut(const std::string& shortcut,
-                      content::WebContents* web_contents) {
-  bool contains;
-  EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
-      web_contents,
-      "domAutomationController.send("
-      "  !!keyboardOverlayData['shortcut']['" + shortcut + "']"
-      ");",
-      &contains));
-  return contains;
-}
-
-}  // namespace
-
-using KeyboardOverlayUIBrowserTest = InProcessBrowserTest;
-
-// This test verifies two things:
-//
-// 1. That all accelerators in kAcceleratorData appear in the keyboard overlay
-// UI. This will fail when a new shortcut is added (or replaced) in
-// kAcceleratorData but not the overlay UI.
-//
-// 2. That the number of accelerators shared by the Ash table and the UI is the
-// expected value. This will fail when a new shortcut is added to
-// kAcceleratorData but not the overlay UI.
-IN_PROC_BROWSER_TEST_F(KeyboardOverlayUIBrowserTest,
-                       AcceleratorsShouldHaveKeyboardOverlay) {
-  content::WebContents* const web_contents = StartKeyboardOverlayUI(browser());
-  const bool is_display_ui_scaling_enabled =
-      IsDisplayUIScalingEnabled(web_contents);
-  int found_accelerators = 0;
-  for (size_t i = 0; i < ash::kAcceleratorDataLength; ++i) {
-    const ash::AcceleratorData& entry = ash::kAcceleratorData[i];
-    if (ShouldSkip(entry))
-      continue;
-
-    const std::string shortcut = GenerateShortcutKey(entry, web_contents);
-    if (!is_display_ui_scaling_enabled) {
-      if (shortcut == "-<>CTRL<>SHIFT" || shortcut == "+<>CTRL<>SHIFT" ||
-          shortcut == "0<>CTRL<>SHIFT") {
-        continue;
-      }
-    }
-
-    if (ContainsShortcut(shortcut, web_contents)) {
-      found_accelerators++;
-    } else {
-      ADD_FAILURE() << "Please add the new accelerators to keyboard "
-                       "overlay. Add one entry '" +
-                           shortcut +
-                           "' in the 'shortcut' section"
-                           " at the bottom of the file of "
-                           "'/chrome/browser/resources/chromeos/"
-                           "keyboard_overlay_data.js'. Please keep it in "
-                           "alphabetical order.";
-    }
-  }
-
-  constexpr int kExpectedFoundAccelerators = 62;
-  DCHECK_EQ(kExpectedFoundAccelerators, found_accelerators)
-      << "It seems ash::kAcceleratorData or the 'shortcut' section of "
-         "'/chrome/browser/resources/chromeos/keyboard_overlay_data.js' has "
-         "changed. Please keep the two in sync. If you've deprecated an "
-         "accelerator, remove it from keyboard_overlay_data.js. If you have "
-         "added the accelerator in both places, update "
-         "kExpectedFoundAccelerators.";
-}
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc b/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc
index 6c7b76a6..97f83f5d8 100644
--- a/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc
+++ b/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_handler.h"
 #include "chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_localized_strings_provider.h"
+#include "chrome/browser/ui/webui/metrics_handler.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/generated_resources.h"
@@ -108,6 +109,7 @@
   }
 
   web_ui->AddMessageHandler(std::make_unique<MultideviceSetupHandler>());
+  web_ui->AddMessageHandler(std::make_unique<MetricsHandler>());
   content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), source);
 
   // Add Mojo bindings to this WebUI so that Mojo calls can occur in JavaScript.
diff --git a/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler.cc b/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler.cc
index 7c5b9e8..deed948 100644
--- a/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler.cc
+++ b/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler.cc
@@ -50,10 +50,10 @@
 // HTML DOM ID used in the JavaScript code. The IDs are generated here so that
 // the DOM would have sensible name instead of autogenerated IDs.
 const char kPreviewsAllowedFlagHtmlId[] = "previews-flag";
-const char kEctFlagHtmlId[] = "ect-flag";
-const char kNoScriptFlagHtmlId[] = "noscript-flag";
-const char kResourceLoadingHintsFlagHtmlId[] = "resource-loading-hints-flag";
 const char kOfflinePageFlagHtmlId[] = "offline-page-flag";
+const char kResourceLoadingHintsFlagHtmlId[] = "resource-loading-hints-flag";
+const char kNoScriptFlagHtmlId[] = "noscript-flag";
+const char kEctFlagHtmlId[] = "ect-flag";
 const char kIgnorePreviewsBlacklistFlagHtmlId[] = "ignore-previews-blacklist";
 const char kDataSaverAltConfigHtmlId[] =
     "data-reduction-proxy-server-experiment";
@@ -61,11 +61,11 @@
 // Links to flags in chrome://flags.
 // TODO(thanhdle): Refactor into vector of structs. crbug.com/787010.
 const char kPreviewsAllowedFlagLink[] = "chrome://flags/#allow-previews";
-const char kEctFlagLink[] = "chrome://flags/#force-effective-connection-type";
-const char kNoScriptFlagLink[] = "chrome://flags/#enable-noscript-previews";
+const char kOfflinePageFlagLink[] = "chrome://flags/#enable-offline-previews";
 const char kResourceLoadingHintsFlagLink[] =
     "chrome://flags/#enable-resource-loading-hints";
-const char kOfflinePageFlagLink[] = "chrome://flags/#enable-offline-previews";
+const char kNoScriptFlagLink[] = "chrome://flags/#enable-noscript-previews";
+const char kEctFlagLink[] = "chrome://flags/#force-effective-connection-type";
 const char kIgnorePreviewsBlacklistLink[] =
     "chrome://flags/#ignore-previews-blacklist";
 const char kDataSaverAltConfigLink[] =
@@ -208,17 +208,11 @@
   previews_allowed_status->htmlId = kPreviewsAllowedHtmlId;
   statuses.push_back(std::move(previews_allowed_status));
 
-  auto client_lofi_status = mojom::PreviewsStatus::New();
-  client_lofi_status->description = kClientLoFiDescription;
-  client_lofi_status->enabled = previews::params::IsClientLoFiEnabled();
-  client_lofi_status->htmlId = kClientLoFiPreviewsHtmlId;
-  statuses.push_back(std::move(client_lofi_status));
-
-  auto noscript_status = mojom::PreviewsStatus::New();
-  noscript_status->description = kNoScriptDescription;
-  noscript_status->enabled = previews::params::IsNoScriptPreviewsEnabled();
-  noscript_status->htmlId = kNoScriptPreviewsHtmlId;
-  statuses.push_back(std::move(noscript_status));
+  auto offline_status = mojom::PreviewsStatus::New();
+  offline_status->description = kOfflineDesciption;
+  offline_status->enabled = previews::params::IsOfflinePreviewsEnabled();
+  offline_status->htmlId = kOfflinePreviewsHtmlId;
+  statuses.push_back(std::move(offline_status));
 
   auto resource_loading_hints_status = mojom::PreviewsStatus::New();
   resource_loading_hints_status->description = kResourceLoadingHintsDescription;
@@ -227,11 +221,17 @@
   resource_loading_hints_status->htmlId = kResourceLoadingHintsHtmlId;
   statuses.push_back(std::move(resource_loading_hints_status));
 
-  auto offline_status = mojom::PreviewsStatus::New();
-  offline_status->description = kOfflineDesciption;
-  offline_status->enabled = previews::params::IsOfflinePreviewsEnabled();
-  offline_status->htmlId = kOfflinePreviewsHtmlId;
-  statuses.push_back(std::move(offline_status));
+  auto noscript_status = mojom::PreviewsStatus::New();
+  noscript_status->description = kNoScriptDescription;
+  noscript_status->enabled = previews::params::IsNoScriptPreviewsEnabled();
+  noscript_status->htmlId = kNoScriptPreviewsHtmlId;
+  statuses.push_back(std::move(noscript_status));
+
+  auto client_lofi_status = mojom::PreviewsStatus::New();
+  client_lofi_status->description = kClientLoFiDescription;
+  client_lofi_status->enabled = previews::params::IsClientLoFiEnabled();
+  client_lofi_status->htmlId = kClientLoFiPreviewsHtmlId;
+  statuses.push_back(std::move(client_lofi_status));
 
   std::move(callback).Run(std::move(statuses));
 }
@@ -249,6 +249,35 @@
   previews_allowed_status->htmlId = kPreviewsAllowedFlagHtmlId;
   flags.push_back(std::move(previews_allowed_status));
 
+  auto offline_page_status = mojom::PreviewsFlag::New();
+#if defined(OS_ANDROID)
+  offline_page_status->description =
+      flag_descriptions::kEnableOfflinePreviewsName;
+  offline_page_status->value = GetFeatureFlagStatus(kOfflinePageFeatureName);
+#else
+  offline_page_status->description = "Offline Page Previews";
+  offline_page_status->value = "Only support on Android";
+#endif  // OS_ANDROID
+  offline_page_status->link = kOfflinePageFlagLink;
+  offline_page_status->htmlId = kOfflinePageFlagHtmlId;
+  flags.push_back(std::move(offline_page_status));
+
+  auto resource_loading_hints_status = mojom::PreviewsFlag::New();
+  resource_loading_hints_status->description =
+      flag_descriptions::kEnableResourceLoadingHintsName;
+  resource_loading_hints_status->link = kResourceLoadingHintsFlagLink;
+  resource_loading_hints_status->value =
+      GetFeatureFlagStatus(kResourceLoadingHintsFeatureName);
+  resource_loading_hints_status->htmlId = kResourceLoadingHintsFlagHtmlId;
+  flags.push_back(std::move(resource_loading_hints_status));
+
+  auto noscript_status = mojom::PreviewsFlag::New();
+  noscript_status->description = flag_descriptions::kEnableNoScriptPreviewsName;
+  noscript_status->link = kNoScriptFlagLink;
+  noscript_status->value = GetFeatureFlagStatus(kNoScriptFeatureName);
+  noscript_status->htmlId = kNoScriptFlagHtmlId;
+  flags.push_back(std::move(noscript_status));
+
   auto ect_status = mojom::PreviewsFlag::New();
   ect_status->description =
       flag_descriptions::kForceEffectiveConnectionTypeName;
@@ -269,35 +298,6 @@
   ignore_previews_blacklist->htmlId = kIgnorePreviewsBlacklistFlagHtmlId;
   flags.push_back(std::move(ignore_previews_blacklist));
 
-  auto noscript_status = mojom::PreviewsFlag::New();
-  noscript_status->description = flag_descriptions::kEnableNoScriptPreviewsName;
-  noscript_status->link = kNoScriptFlagLink;
-  noscript_status->value = GetFeatureFlagStatus(kNoScriptFeatureName);
-  noscript_status->htmlId = kNoScriptFlagHtmlId;
-  flags.push_back(std::move(noscript_status));
-
-  auto resource_loading_hints_status = mojom::PreviewsFlag::New();
-  resource_loading_hints_status->description =
-      flag_descriptions::kEnableResourceLoadingHintsName;
-  resource_loading_hints_status->link = kResourceLoadingHintsFlagLink;
-  resource_loading_hints_status->value =
-      GetFeatureFlagStatus(kResourceLoadingHintsFeatureName);
-  resource_loading_hints_status->htmlId = kResourceLoadingHintsFlagHtmlId;
-  flags.push_back(std::move(resource_loading_hints_status));
-
-  auto offline_page_status = mojom::PreviewsFlag::New();
-#if defined(OS_ANDROID)
-  offline_page_status->description =
-      flag_descriptions::kEnableOfflinePreviewsName;
-  offline_page_status->value = GetFeatureFlagStatus(kOfflinePageFeatureName);
-#else
-  offline_page_status->description = "Offline Page Previews";
-  offline_page_status->value = "Only support on Android";
-#endif  // OS_ANDROID
-  offline_page_status->link = kOfflinePageFlagLink;
-  offline_page_status->htmlId = kOfflinePageFlagHtmlId;
-  flags.push_back(std::move(offline_page_status));
-
   auto alt_config_status = mojom::PreviewsFlag::New();
   alt_config_status->description =
       flag_descriptions::kEnableDataReductionProxyServerExperimentDescription;
diff --git a/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler_unittest.cc b/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler_unittest.cc
index 7934497..498c0610 100644
--- a/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler_unittest.cc
@@ -49,47 +49,47 @@
 
 // The HTML DOM ID used in Javascript.
 constexpr char kPreviewsAllowedHtmlId[] = "previews-allowed-status";
-constexpr char kClientLoFiPreviewsHtmlId[] = "client-lofi-preview-status";
-constexpr char kNoScriptPreviewsHtmlId[] = "noscript-preview-status";
-constexpr char kResourceLoadingHintsHtmlId[] = "resource-loading-hints-status";
 constexpr char kOfflinePreviewsHtmlId[] = "offline-preview-status";
+constexpr char kResourceLoadingHintsHtmlId[] = "resource-loading-hints-status";
+constexpr char kNoScriptPreviewsHtmlId[] = "noscript-preview-status";
+constexpr char kClientLoFiPreviewsHtmlId[] = "client-lofi-preview-status";
 
 // Descriptions for previews.
 constexpr char kPreviewsAllowedDescription[] = "Previews Allowed";
-constexpr char kClientLoFiDescription[] = "Client LoFi Previews";
-constexpr char kNoScriptDescription[] = "NoScript Previews";
+constexpr char kOfflineDesciption[] = "Offline Previews";
 constexpr char kResourceLoadingHintsDescription[] =
     "ResourceLoadingHints Previews";
-constexpr char kOfflineDesciption[] = "Offline Previews";
+constexpr char kNoScriptDescription[] = "NoScript Previews";
+constexpr char kClientLoFiDescription[] = "Client LoFi Previews";
 
 // The HTML DOM ID used in Javascript.
+constexpr char kOfflinePageFlagHtmlId[] = "offline-page-flag";
+constexpr char kResourceLoadingHintsFlagHtmlId[] =
+    "resource-loading-hints-flag";
+constexpr char kNoScriptFlagHtmlId[] = "noscript-flag";
 constexpr char kEctFlagHtmlId[] = "ect-flag";
 constexpr char kIgnorePreviewsBlacklistFlagHtmlId[] =
     "ignore-previews-blacklist";
-constexpr char kNoScriptFlagHtmlId[] = "noscript-flag";
-constexpr char kResourceLoadingHintsFlagHtmlId[] =
-    "resource-loading-hints-flag";
-constexpr char kOfflinePageFlagHtmlId[] = "offline-page-flag";
 constexpr char kDataSaverAltConfigHtmlId[] =
     "data-reduction-proxy-server-experiment";
 
 // Links to flags in chrome://flags.
-constexpr char kNoScriptFlagLink[] = "chrome://flags/#enable-noscript-previews";
+constexpr char kOfflinePageFlagLink[] =
+    "chrome://flags/#enable-offline-previews";
 constexpr char kResourceLoadingHintsFlagLink[] =
     "chrome://flags/#enable-resource-loading-hints";
+constexpr char kNoScriptFlagLink[] = "chrome://flags/#enable-noscript-previews";
 constexpr char kEctFlagLink[] =
     "chrome://flags/#force-effective-connection-type";
 constexpr char kIgnorePreviewsBlacklistLink[] =
     "chrome://flags/#ignore-previews-blacklist";
-constexpr char kOfflinePageFlagLink[] =
-    "chrome://flags/#enable-offline-previews";
 constexpr char kDataSaverAltConfigLink[] =
     "chrome://flags/#enable-data-reduction-proxy-server-experiment";
 
 // Flag features names.
-constexpr char kNoScriptFeatureName[] = "NoScriptPreviews";
-constexpr char kResourceLoadingHintsFeatureName[] = "ResourceLoadingHints";
 constexpr char kOfflinePageFeatureName[] = "OfflinePreviews";
+constexpr char kResourceLoadingHintsFeatureName[] = "ResourceLoadingHints";
+constexpr char kNoScriptFeatureName[] = "NoScriptPreviews";
 
 constexpr char kDefaultFlagValue[] = "Default";
 constexpr char kEnabledFlagValue[] = "Enabled";
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc b/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc
index 7556aa9..15f648a 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/values.h"
+#include "chrome/browser/ui/ash/ksv/keyboard_shortcut_viewer_util.h"
 #include "chrome/browser/ui/ash/tablet_mode_client.h"
 #include "chromeos/chromeos_switches.h"
 #include "content/public/browser/web_ui.h"
@@ -68,8 +69,8 @@
       base::BindRepeating(&KeyboardHandler::HandleInitialize,
                           base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      "showKeyboardShortcutsOverlay",
-      base::BindRepeating(&KeyboardHandler::HandleShowKeyboardShortcutsOverlay,
+      "showKeyboardShortcutViewer",
+      base::BindRepeating(&KeyboardHandler::HandleShowKeyboardShortcutViewer,
                           base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
       "initializeKeyboardWatcher",
@@ -97,13 +98,9 @@
   UpdateKeyboards();
 }
 
-void KeyboardHandler::HandleShowKeyboardShortcutsOverlay(
+void KeyboardHandler::HandleShowKeyboardShortcutViewer(
     const base::ListValue* args) const {
-  ash::mojom::NewWindowControllerPtr new_window_controller;
-  content::ServiceManagerConnection::GetForProcess()
-      ->GetConnector()
-      ->BindInterface(ash::mojom::kServiceName, &new_window_controller);
-  new_window_controller->ShowKeyboardOverlay();
+  keyboard_shortcut_viewer_util::ToggleKeyboardShortcutViewer();
 }
 
 void KeyboardHandler::HandleKeyboardChange(const base::ListValue* args) {
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h b/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h
index be8f0ae6..6b071f0 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h
@@ -58,8 +58,8 @@
   // Initializes the page with the current keyboard information.
   void HandleInitialize(const base::ListValue* args);
 
-  // Shows the Ash keyboard shortcuts overlay.
-  void HandleShowKeyboardShortcutsOverlay(const base::ListValue* args) const;
+  // Shows the Ash keyboard shortcut viewer.
+  void HandleShowKeyboardShortcutViewer(const base::ListValue* args) const;
 
   // Determines what types of keyboards are attached.
   void HandleKeyboardChange(const base::ListValue* args);
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 634c8df..ed0b905 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -219,6 +219,7 @@
     {"autoclickEventTypeDoubleClick",
      IDS_SETTINGS_AUTOCLICK_EVENT_TYPE_DOUBLE_CLICK},
     {"autoclickEventTypeNoAction", IDS_SETTINGS_AUTOCLICK_EVENT_TYPE_NO_ACTION},
+    {"autoclickReverToLeftClick", IDS_SETTINGS_AUTOCLICK_REVERT_TO_LEFT_CLICK},
     {"dictationDescription", IDS_SETTINGS_ACCESSIBILITY_DICTATION_DESCRIPTION},
     {"dictationLabel", IDS_SETTINGS_ACCESSIBILITY_DICTATION_LABEL},
     {"onScreenKeyboardLabel", IDS_SETTINGS_ON_SCREEN_KEYBOARD_LABEL},
@@ -665,8 +666,8 @@
       {"keyRepeatRate", IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_RATE},
       {"keyRepeatRateSlow", IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_RATE_SLOW},
       {"keyRepeatRateFast", IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_FAST},
-      {"showKeyboardShortcutsOverlay",
-       IDS_SETTINGS_KEYBOARD_SHOW_KEYBOARD_SHORTCUTS_OVERLAY},
+      {"showKeyboardShortcutViewer",
+       IDS_SETTINGS_KEYBOARD_SHOW_SHORTCUT_VIEWER},
       {"keyboardShowLanguageAndInput",
        IDS_SETTINGS_KEYBOARD_SHOW_LANGUAGE_AND_INPUT},
   };
@@ -1747,8 +1748,7 @@
      IDS_SETTINGS_PEOPLE_SYNC_UNIFIED_CONSENT_TOGGLE_TITLE},
     {"syncOverview", IDS_SETTINGS_SYNC_OVERVIEW},
     {"syncDisabled", IDS_PROFILES_DICE_SYNC_DISABLED_TITLE},
-    {"syncDisabledByAdministrator",
-     IDS_SETTINGS_SYNC_DISABLED_BY_ADMINISTRATOR},
+    {"syncDisabledByAdministrator", IDS_SIGNED_IN_WITH_SYNC_DISABLED},
     {"syncSignin", IDS_SETTINGS_SYNC_SIGNIN},
     {"syncDisconnect", IDS_SETTINGS_PEOPLE_SIGN_OUT},
     {"syncDisconnectTitle", IDS_SETTINGS_SYNC_DISCONNECT_TITLE},
diff --git a/chrome/browser/ui/webui/settings/md_settings_ui.cc b/chrome/browser/ui/webui/settings/md_settings_ui.cc
index cf4ed21..cb9b7f53 100644
--- a/chrome/browser/ui/webui/settings/md_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_ui.cc
@@ -46,12 +46,10 @@
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/unified_consent/feature.h"
 #include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/common/content_features.h"
-#include "content/public/common/web_preferences.h"
 #include "printing/buildflags/buildflags.h"
 
 #if defined(OS_WIN)
@@ -368,11 +366,6 @@
                           base::FeatureList::IsEnabled(
                               password_manager::features::kPasswordImport));
 
-  html_source->AddBoolean("enableScrollAnimator", web_ui->GetWebContents()
-                                                      ->GetRenderViewHost()
-                                                      ->GetWebkitPreferences()
-                                                      .enable_scroll_animator);
-
   AddSettingsPageUIHandler(
       base::WrapUnique(AboutHandler::Create(html_source, profile)));
   AddSettingsPageUIHandler(
diff --git a/chrome/browser/ui/webui/settings/people_handler.cc b/chrome/browser/ui/webui/settings/people_handler.cc
index 77d1712..6635fac6 100644
--- a/chrome/browser/ui/webui/settings/people_handler.cc
+++ b/chrome/browser/ui/webui/settings/people_handler.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/profiles/profile_metrics.h"
 #include "chrome/browser/profiles/profile_window.h"
 #include "chrome/browser/signin/chrome_signin_helper.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/signin_error_controller_factory.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
@@ -220,7 +221,7 @@
 PeopleHandler::PeopleHandler(Profile* profile)
     : profile_(profile),
       configuring_sync_(false),
-      signin_observer_(this),
+      identity_manager_observer_(this),
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
       sync_service_observer_(this),
       account_tracker_observer_(this) {
@@ -310,10 +311,10 @@
       prefs::kSigninAllowed,
       base::Bind(&PeopleHandler::UpdateSyncStatus, base::Unretained(this)));
 
-  SigninManagerBase* signin_manager(
-      SigninManagerFactory::GetInstance()->GetForProfile(profile_));
-  if (signin_manager)
-    signin_observer_.Add(signin_manager);
+  identity::IdentityManager* identity_manager(
+      IdentityManagerFactory::GetInstance()->GetForProfile(profile_));
+  if (identity_manager)
+    identity_manager_observer_.Add(identity_manager);
 
   // This is intentionally not using GetSyncService(), to go around the
   // Profile::IsSyncAllowed() check.
@@ -332,7 +333,7 @@
 
 void PeopleHandler::OnJavascriptDisallowed() {
   profile_pref_registrar_.RemoveAll();
-  signin_observer_.RemoveAll();
+  identity_manager_observer_.RemoveAll();
   sync_service_observer_.RemoveAll();
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
   account_tracker_observer_.RemoveAll();
@@ -923,13 +924,13 @@
   FireWebUIListener("page-status-changed", base::Value(kDonePageStatus));
 }
 
-void PeopleHandler::GoogleSigninSucceeded(const std::string& /* account_id */,
-                                          const std::string& /* username */) {
+void PeopleHandler::OnPrimaryAccountSet(
+    const AccountInfo& primary_account_info) {
   UpdateSyncStatus();
 }
 
-void PeopleHandler::GoogleSignedOut(const std::string& /* account_id */,
-                                    const std::string& /* username */) {
+void PeopleHandler::OnPrimaryAccountCleared(
+    const AccountInfo& previous_primary_account_info) {
   UpdateSyncStatus();
 }
 
diff --git a/chrome/browser/ui/webui/settings/people_handler.h b/chrome/browser/ui/webui/settings/people_handler.h
index 6652b50..5cdcf65 100644
--- a/chrome/browser/ui/webui/settings/people_handler.h
+++ b/chrome/browser/ui/webui/settings/people_handler.h
@@ -21,15 +21,14 @@
 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/signin/core/browser/signin_buildflags.h"
-#include "components/signin/core/browser/signin_manager_base.h"
 #include "components/sync/driver/sync_service_observer.h"
+#include "services/identity/public/cpp/identity_manager.h"
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
 #include "components/signin/core/browser/account_tracker_service.h"
 #endif
 
 class LoginUIService;
-class SigninManagerBase;
 
 namespace browser_sync {
 class ProfileSyncService;
@@ -50,7 +49,7 @@
 namespace settings {
 
 class PeopleHandler : public SettingsPageUIHandler,
-                      public SigninManagerBase::Observer,
+                      public identity::IdentityManager::Observer,
                       public SyncStartupTracker::Observer,
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
                       public AccountTrackerService::Observer,
@@ -136,11 +135,10 @@
   // LoginUIService::LoginUI implementation.
   void FocusUI() override;
 
-  // SigninManagerBase::Observer implementation.
-  void GoogleSigninSucceeded(const std::string& account_id,
-                             const std::string& username) override;
-  void GoogleSignedOut(const std::string& account_id,
-                       const std::string& username) override;
+  // IdentityManager::Observer implementation.
+  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountCleared(
+      const AccountInfo& previous_primary_account_info) override;
 
   // syncer::SyncServiceObserver implementation.
   void OnStateChanged(syncer::SyncService* sync) override;
@@ -258,7 +256,8 @@
   PrefChangeRegistrar profile_pref_registrar_;
 
   // Manages observer lifetimes.
-  ScopedObserver<SigninManagerBase, PeopleHandler> signin_observer_;
+  ScopedObserver<identity::IdentityManager, PeopleHandler>
+      identity_manager_observer_;
   ScopedObserver<browser_sync::ProfileSyncService, PeopleHandler>
       sync_service_observer_;
 
diff --git a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
index 88ecd47..39487b8 100644
--- a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
+++ b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
@@ -351,7 +351,8 @@
       base::Value(sync_service_ && sync_service_->IsSyncFeatureActive() &&
                   sync_service_->GetActiveDataTypes().Has(
                       syncer::HISTORY_DELETE_DIRECTIVES)),
-      base::Value(ShouldShowCookieException(profile_)));
+      base::Value(
+          browsing_data_counter_utils::ShouldShowCookieException(profile_)));
 }
 
 void ClearBrowsingDataHandler::RefreshHistoryNotice() {
@@ -391,7 +392,8 @@
   CallJavascriptFunction(
       "cr.webUIListenerCallback", base::Value("update-counter-text"),
       base::Value(result->source()->GetPrefName()),
-      base::Value(GetChromeCounterTextFromResult(result.get(), profile_)));
+      base::Value(browsing_data_counter_utils::GetChromeCounterTextFromResult(
+          result.get(), profile_)));
 }
 
 void ClearBrowsingDataHandler::HandleTimePeriodChanged(
diff --git a/chrome/browser/ui/webui/welcome/nux/BUILD.gn b/chrome/browser/ui/webui/welcome/nux/BUILD.gn
index ad7cb57..c7ec977c 100644
--- a/chrome/browser/ui/webui/welcome/nux/BUILD.gn
+++ b/chrome/browser/ui/webui/welcome/nux/BUILD.gn
@@ -27,6 +27,22 @@
   ]
 }
 
+static_library("bookmark_handler") {
+  sources = [
+    "bookmark_handler.cc",
+    "bookmark_handler.h",
+  ]
+
+  deps = [
+    "//base",
+    "//chrome/browser:resources",
+    "//components/bookmarks/common",
+    "//components/pref_registry",
+    "//components/prefs",
+    "//content/public/browser",
+  ]
+}
+
 static_library("email_feature") {
   sources = [
     "email_handler.cc",
@@ -43,11 +59,7 @@
     ":bookmark_item",
     "//chrome/app:generated_resources",
     "//chrome/browser:resources",
-    "//components/bookmarks/browser",
-    "//components/bookmarks/common",
     "//components/favicon/core",
-    "//components/pref_registry",
-    "//components/prefs",
     "//components/resources",
     "//components/strings",
     "//components/variations",
@@ -72,11 +84,7 @@
     ":bookmark_item",
     "//chrome/app:generated_resources",
     "//chrome/browser:resources",
-    "//components/bookmarks/browser",
-    "//components/bookmarks/common",
     "//components/favicon/core",
-    "//components/pref_registry",
-    "//components/prefs",
     "//components/resources",
     "//components/strings",
     "//components/variations",
diff --git a/chrome/browser/ui/webui/welcome/nux/bookmark_handler.cc b/chrome/browser/ui/webui/welcome/nux/bookmark_handler.cc
new file mode 100644
index 0000000..53d19ff2
--- /dev/null
+++ b/chrome/browser/ui/webui/welcome/nux/bookmark_handler.cc
@@ -0,0 +1,41 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/welcome/nux/bookmark_handler.h"
+
+#include "base/bind.h"
+#include "chrome/grit/browser_resources.h"
+#include "components/bookmarks/common/bookmark_pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace nux {
+
+BookmarkHandler::BookmarkHandler(PrefService* prefs) : prefs_(prefs) {}
+
+BookmarkHandler::~BookmarkHandler() {}
+
+void BookmarkHandler::RegisterMessages() {
+  web_ui()->RegisterMessageCallback(
+      "toggleBookmarkBar",
+      base::BindRepeating(&BookmarkHandler::HandleToggleBookmarkBar,
+                          base::Unretained(this)));
+}
+
+void BookmarkHandler::HandleToggleBookmarkBar(const base::ListValue* args) {
+  bool show;
+  CHECK(args->GetBoolean(0, &show));
+  prefs_->SetBoolean(bookmarks::prefs::kShowBookmarkBar, show);
+}
+
+void BookmarkHandler::AddSources(content::WebUIDataSource* html_source,
+                                 PrefService* prefs) {
+  // Add constants to loadtime data
+  html_source->AddBoolean(
+      "bookmark_bar_shown",
+      prefs->GetBoolean(bookmarks::prefs::kShowBookmarkBar));
+  html_source->SetJsonPath("strings.js");
+}
+
+}  // namespace nux
diff --git a/chrome/browser/ui/webui/welcome/nux/bookmark_handler.h b/chrome/browser/ui/webui/welcome/nux/bookmark_handler.h
new file mode 100644
index 0000000..e803cd5
--- /dev/null
+++ b/chrome/browser/ui/webui/welcome/nux/bookmark_handler.h
@@ -0,0 +1,44 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_BOOKMARK_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_BOOKMARK_HANDLER_H_
+
+#include "base/macros.h"
+#include "base/values.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+class PrefService;
+
+namespace content {
+class WebUIDataSource;
+}  // namespace content
+
+namespace nux {
+
+class BookmarkHandler : public content::WebUIMessageHandler {
+ public:
+  explicit BookmarkHandler(PrefService* prefs);
+  ~BookmarkHandler() override;
+
+  // WebUIMessageHandler:
+  void RegisterMessages() override;
+
+  // Callbacks for JS APIs.
+  void HandleToggleBookmarkBar(const base::ListValue* args);
+
+  // Adds webui sources.
+  static void AddSources(content::WebUIDataSource* html_source,
+                         PrefService* prefs);
+
+ private:
+  // Weak reference.
+  PrefService* prefs_;
+
+  DISALLOW_COPY_AND_ASSIGN(BookmarkHandler);
+};
+
+}  // namespace nux
+
+#endif  // CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_BOOKMARK_HANDLER_H_
diff --git a/chrome/browser/ui/webui/welcome/nux/email_handler.cc b/chrome/browser/ui/webui/welcome/nux/email_handler.cc
index 2ce6b52..60e6e53 100644
--- a/chrome/browser/ui/webui/welcome/nux/email_handler.cc
+++ b/chrome/browser/ui/webui/welcome/nux/email_handler.cc
@@ -12,11 +12,9 @@
 #include "chrome/browser/ui/webui/welcome/nux/bookmark_item.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/bookmarks/common/bookmark_pref_names.h"
 #include "components/favicon/core/favicon_service.h"
 #include "components/grit/components_resources.h"
 #include "components/grit/components_scaled_resources.h"
-#include "components/prefs/pref_service.h"
 #include "components/strings/grit/components_strings.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
@@ -59,9 +57,8 @@
 static_assert(base::size(kEmail) == (size_t)EmailProviders::kCount,
               "names and histograms must match");
 
-EmailHandler::EmailHandler(PrefService* prefs,
-                           favicon::FaviconService* favicon_service)
-    : prefs_(prefs), favicon_service_(favicon_service) {}
+EmailHandler::EmailHandler(favicon::FaviconService* favicon_service)
+    : favicon_service_(favicon_service) {}
 
 EmailHandler::~EmailHandler() {}
 
@@ -71,10 +68,6 @@
                                             base::Unretained(this)));
 
   web_ui()->RegisterMessageCallback(
-      "toggleBookmarkBar",
-      base::BindRepeating(&EmailHandler::HandleToggleBookmarkBar,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
       "getEmailList", base::BindRepeating(&EmailHandler::HandleGetEmailList,
                                           base::Unretained(this)));
 }
@@ -103,12 +96,6 @@
       gfx::Size(kEmailIconSize, kEmailIconSize));
 }
 
-void EmailHandler::HandleToggleBookmarkBar(const base::ListValue* args) {
-  bool show = false;
-  CHECK(args->GetBoolean(0, &show));
-  prefs_->SetBoolean(bookmarks::prefs::kShowBookmarkBar, show);
-}
-
 void EmailHandler::HandleGetEmailList(const base::ListValue* args) {
   AllowJavascript();
   CHECK_EQ(1U, args->GetSize());
@@ -118,8 +105,7 @@
       *callback_id, bookmarkItemsToListValue(kEmail, base::size(kEmail)));
 }
 
-void EmailHandler::AddSources(content::WebUIDataSource* html_source,
-                              PrefService* prefs) {
+void EmailHandler::AddSources(content::WebUIDataSource* html_source) {
   // Add icons
   html_source->AddResourcePath("email/aol_1x.png", IDR_NUX_EMAIL_AOL_1X);
   html_source->AddResourcePath("email/aol_2x.png", IDR_NUX_EMAIL_AOL_2X);
@@ -133,12 +119,6 @@
                                IDR_NUX_EMAIL_OUTLOOK_2X);
   html_source->AddResourcePath("email/yahoo_1x.png", IDR_NUX_EMAIL_YAHOO_1X);
   html_source->AddResourcePath("email/yahoo_2x.png", IDR_NUX_EMAIL_YAHOO_2X);
-
-  // Add constants to loadtime data
-  html_source->AddBoolean(
-      "bookmark_bar_shown",
-      prefs->GetBoolean(bookmarks::prefs::kShowBookmarkBar));
-  html_source->SetJsonPath("strings.js");
 }
 
 }  // namespace nux
diff --git a/chrome/browser/ui/webui/welcome/nux/email_handler.h b/chrome/browser/ui/webui/welcome/nux/email_handler.h
index c67d726..b22afa91 100644
--- a/chrome/browser/ui/webui/welcome/nux/email_handler.h
+++ b/chrome/browser/ui/webui/welcome/nux/email_handler.h
@@ -9,8 +9,6 @@
 #include "base/values.h"
 #include "content/public/browser/web_ui_message_handler.h"
 
-class PrefService;
-
 namespace content {
 class WebUIDataSource;
 }  // namespace content
@@ -34,7 +32,7 @@
 
 class EmailHandler : public content::WebUIMessageHandler {
  public:
-  EmailHandler(PrefService* prefs, favicon::FaviconService* favicon_service);
+  explicit EmailHandler(favicon::FaviconService* favicon_service);
   ~EmailHandler() override;
 
   // WebUIMessageHandler:
@@ -42,18 +40,13 @@
 
   // Callbacks for JS APIs.
   void HandleCacheEmailIcon(const base::ListValue* args);
-  void HandleToggleBookmarkBar(const base::ListValue* args);
   void HandleGetEmailList(const base::ListValue* args);
 
   // Adds webui sources.
-  static void AddSources(content::WebUIDataSource* html_source,
-                         PrefService* prefs);
+  static void AddSources(content::WebUIDataSource* html_source);
 
  private:
   // Weak reference.
-  PrefService* prefs_;
-
-  // Weak reference.
   favicon::FaviconService* favicon_service_;
 
   DISALLOW_COPY_AND_ASSIGN(EmailHandler);
diff --git a/chrome/browser/ui/webui/welcome/nux/google_apps_handler.cc b/chrome/browser/ui/webui/welcome/nux/google_apps_handler.cc
index 18c4d374..82c56a6 100644
--- a/chrome/browser/ui/webui/welcome/nux/google_apps_handler.cc
+++ b/chrome/browser/ui/webui/welcome/nux/google_apps_handler.cc
@@ -11,11 +11,9 @@
 #include "chrome/browser/ui/webui/welcome/nux/bookmark_item.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/bookmarks/common/bookmark_pref_names.h"
 #include "components/favicon/core/favicon_service.h"
 #include "components/grit/components_resources.h"
 #include "components/grit/components_scaled_resources.h"
-#include "components/prefs/pref_service.h"
 #include "components/strings/grit/components_strings.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
@@ -56,21 +54,15 @@
 
 constexpr const int kGoogleAppIconSize = 48;  // Pixels.
 
-GoogleAppsHandler::GoogleAppsHandler(PrefService* prefs,
-                                     favicon::FaviconService* favicon_service)
-    : prefs_(prefs), favicon_service_(favicon_service) {}
+GoogleAppsHandler::GoogleAppsHandler(favicon::FaviconService* favicon_service)
+    : favicon_service_(favicon_service) {}
 
 GoogleAppsHandler::~GoogleAppsHandler() {}
 
 void GoogleAppsHandler::RegisterMessages() {
   web_ui()->RegisterMessageCallback(
-      "rejectGoogleApps",
-      base::BindRepeating(&GoogleAppsHandler::HandleRejectGoogleApps,
-                          base::Unretained(this)));
-
-  web_ui()->RegisterMessageCallback(
-      "addGoogleApps",
-      base::BindRepeating(&GoogleAppsHandler::HandleAddGoogleApps,
+      "cacheGoogleAppIcon",
+      base::BindRepeating(&GoogleAppsHandler::HandleCacheGoogleAppIcon,
                           base::Unretained(this)));
 
   web_ui()->RegisterMessageCallback(
@@ -79,41 +71,28 @@
                           base::Unretained(this)));
 }
 
-void GoogleAppsHandler::HandleRejectGoogleApps(const base::ListValue* args) {
-  UMA_HISTOGRAM_ENUMERATION(kGoogleAppsInteractionHistogram,
-                            GoogleAppsInteraction::kNoThanks,
-                            GoogleAppsInteraction::kCount);
-}
+void GoogleAppsHandler::HandleCacheGoogleAppIcon(const base::ListValue* args) {
+  int appId;
+  args->GetInteger(0, &appId);
 
-void GoogleAppsHandler::HandleAddGoogleApps(const base::ListValue* args) {
-  // Add bookmarks for all selected apps.
-  for (size_t i = 0; i < base::size(kGoogleApps); ++i) {
-    bool selected = false;
-    CHECK(args->GetBoolean(i, &selected));
-    if (selected) {
-      UMA_HISTOGRAM_ENUMERATION(
-          "FirstRun.NewUserExperience.GoogleAppsSelection",
-          (GoogleApps)kGoogleApps[i].id, GoogleApps::kCount);
-      GURL app_url = GURL(kGoogleApps[i].url);
-      // TODO(hcarmona): Add bookmark from JS.
-
-      // Preload the favicon cache with Chrome-bundled images. Otherwise, the
-      // pre-populated bookmarks don't have favicons and look bad. Favicons are
-      // updated automatically when a user visits a site.
-      favicon_service_->MergeFavicon(
-          app_url, app_url, favicon_base::IconType::kFavicon,
-          ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes(
-              kGoogleApps[i].icon),
-          gfx::Size(kGoogleAppIconSize, kGoogleAppIconSize));
+  const BookmarkItem* selectedApp = NULL;
+  for (size_t i = 0; i < base::size(kGoogleApps); i++) {
+    if (static_cast<int>(kGoogleApps[i].id) == appId) {
+      selectedApp = &kGoogleApps[i];
+      break;
     }
   }
+  CHECK(selectedApp);  // WebUI should not be able to pass non-existent ID.
 
-  // Enable bookmark bar.
-  prefs_->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true);
-
-  UMA_HISTOGRAM_ENUMERATION(kGoogleAppsInteractionHistogram,
-                            GoogleAppsInteraction::kGetStarted,
-                            GoogleAppsInteraction::kCount);
+  // Preload the favicon cache with Chrome-bundled images. Otherwise, the
+  // pre-populated bookmarks don't have favicons and look bad. Favicons are
+  // updated automatically when a user visits a site.
+  GURL app_url = GURL(selectedApp->url);
+  favicon_service_->MergeFavicon(
+      app_url, app_url, favicon_base::IconType::kFavicon,
+      ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes(
+          selectedApp->icon),
+      gfx::Size(kGoogleAppIconSize, kGoogleAppIconSize));
 }
 
 void GoogleAppsHandler::HandleGetGoogleAppsList(const base::ListValue* args) {
@@ -126,8 +105,7 @@
       bookmarkItemsToListValue(kGoogleApps, base::size(kGoogleApps)));
 }
 
-void GoogleAppsHandler::AddSources(content::WebUIDataSource* html_source,
-                                   PrefService* prefs) {
+void GoogleAppsHandler::AddSources(content::WebUIDataSource* html_source) {
   // Add icons
   html_source->AddResourcePath("google_apps/chrome_store_1x.png",
                                IDR_NUX_GOOGLE_APPS_CHROME_STORE_1X);
@@ -158,12 +136,6 @@
                                IDR_NUX_GOOGLE_APPS_YOUTUBE_1X);
   html_source->AddResourcePath("google_apps/youtube_2x.png",
                                IDR_NUX_GOOGLE_APPS_YOUTUBE_2X);
-
-  // Add constants to loadtime data
-  html_source->AddBoolean(
-      "bookmark_bar_shown",
-      prefs->GetBoolean(bookmarks::prefs::kShowBookmarkBar));
-  html_source->SetJsonPath("strings.js");
 }
 
 }  // namespace nux
diff --git a/chrome/browser/ui/webui/welcome/nux/google_apps_handler.h b/chrome/browser/ui/webui/welcome/nux/google_apps_handler.h
index e2ddf0a..9fd84ed 100644
--- a/chrome/browser/ui/webui/welcome/nux/google_apps_handler.h
+++ b/chrome/browser/ui/webui/welcome/nux/google_apps_handler.h
@@ -9,8 +9,6 @@
 #include "base/values.h"
 #include "content/public/browser/web_ui_message_handler.h"
 
-class PrefService;
-
 namespace content {
 class WebUIDataSource;
 }  // namespace content
@@ -34,27 +32,21 @@
 
 class GoogleAppsHandler : public content::WebUIMessageHandler {
  public:
-  GoogleAppsHandler(PrefService* prefs,
-                    favicon::FaviconService* favicon_service);
+  explicit GoogleAppsHandler(favicon::FaviconService* favicon_service);
   ~GoogleAppsHandler() override;
 
   // WebUIMessageHandler:
   void RegisterMessages() override;
 
   // Callbacks for JS APIs.
-  void HandleRejectGoogleApps(const base::ListValue* args);
-  void HandleAddGoogleApps(const base::ListValue* args);
+  void HandleCacheGoogleAppIcon(const base::ListValue* args);
   void HandleGetGoogleAppsList(const base::ListValue* args);
 
   // Adds webui sources.
-  static void AddSources(content::WebUIDataSource* html_source,
-                         PrefService* prefs);
+  static void AddSources(content::WebUIDataSource* html_source);
 
  private:
   // Weak reference.
-  PrefService* prefs_;
-
-  // Weak reference.
   favicon::FaviconService* favicon_service_;
 
   DISALLOW_COPY_AND_ASSIGN(GoogleAppsHandler);
diff --git a/chrome/browser/ui/webui/welcome/welcome_ui.cc b/chrome/browser/ui/webui/welcome/welcome_ui.cc
index 1eacb9e..eb110871 100644
--- a/chrome/browser/ui/webui/welcome/welcome_ui.cc
+++ b/chrome/browser/ui/webui/welcome/welcome_ui.cc
@@ -11,6 +11,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/signin/account_consistency_mode_manager.h"
+#include "chrome/browser/ui/webui/welcome/nux/bookmark_handler.h"
 #include "chrome/browser/ui/webui/welcome/nux/constants.h"
 #include "chrome/browser/ui/webui/welcome/nux/email_handler.h"
 #include "chrome/browser/ui/webui/welcome/nux/google_apps_handler.h"
@@ -50,13 +51,15 @@
 void AddOnboardingStrings(content::WebUIDataSource* html_source) {
   static constexpr LocalizedString kLocalizedStrings[] = {
       // Shared strings.
-      {"headerText", IDS_WELCOME_HEADER},
       {"acceptText", IDS_WELCOME_ACCEPT_BUTTON},
-      {"noThanks", IDS_NO_THANKS},
-      {"getStarted", IDS_ONBOARDING_WELCOME_GET_STARTED},
       {"bookmarkAdded", IDS_ONBOARDING_WELCOME_BOOKMARK_ADDED},
       {"bookmarkRemoved", IDS_ONBOARDING_WELCOME_BOOKMARK_REMOVED},
       {"bookmarkReplaced", IDS_ONBOARDING_WELCOME_BOOKMARK_REPLACED},
+      {"getStarted", IDS_ONBOARDING_WELCOME_GET_STARTED},
+      {"headerText", IDS_WELCOME_HEADER},
+      {"next", IDS_ONBOARDING_WELCOME_NEXT},
+      {"noThanks", IDS_NO_THANKS},
+      {"skip", IDS_ONBOARDING_WELCOME_SKIP},
 
       // Sign-in view strings.
       {"signInHeader", IDS_ONBOARDING_WELCOME_SIGNIN_VIEW_HEADER},
@@ -64,8 +67,7 @@
       {"signIn", IDS_ONBOARDING_WELCOME_SIGNIN_VIEW_SIGNIN},
 
       // Email provider module strings.
-      {"welcomeTitle", IDS_ONBOARDING_WELCOME_NUX_EMAIL_WELCOME_TITLE},
-      {"emailPrompt", IDS_ONBOARDING_WELCOME_NUX_EMAIL_PROMPT},
+      {"emailProviderTitle", IDS_ONBOARDING_WELCOME_NUX_EMAIL_TITLE},
 
       // Google apps module strings.
       {"googleAppsDescription",
@@ -167,17 +169,35 @@
         "images/background_svgs/yellow_semicircle.svg",
         IDR_WELCOME_ONBOARDING_WELCOME_IMAGES_BACKGROUND_SVGS_YELLOW_SEMICIRCLE_SVG);
 
+    html_source->AddResourcePath("images/email_provider_1x.png",
+                                 IDR_NUX_EMAIL_PROVIDER_LOGO_1X);
+    html_source->AddResourcePath("images/email_provider_2x.png",
+                                 IDR_NUX_EMAIL_PROVIDER_LOGO_2X);
+    html_source->AddResourcePath("images/set_as_default_1x.png",
+                                 IDR_NUX_SET_AS_DEFAULT_LOGO_1X);
+    html_source->AddResourcePath("images/set_as_default_2x.png",
+                                 IDR_NUX_SET_AS_DEFAULT_LOGO_2X);
+    html_source->AddResourcePath("images/set_as_default_illustration_1x.png",
+                                 IDR_NUX_SET_AS_DEFAULT_ILLUSTRATION_1X);
+    html_source->AddResourcePath("images/set_as_default_illustration_2x.png",
+                                 IDR_NUX_SET_AS_DEFAULT_ILLUSTRATION_2X);
+
+    // Add the shared bookmark handler for onboarding modules.
+    web_ui->AddMessageHandler(
+        std::make_unique<nux::BookmarkHandler>(profile->GetPrefs()));
+    nux::BookmarkHandler::AddSources(html_source, profile->GetPrefs());
+
     // Add email provider bookmarking onboarding module.
     web_ui->AddMessageHandler(std::make_unique<nux::EmailHandler>(
-        profile->GetPrefs(), FaviconServiceFactory::GetForProfile(
-                                 profile, ServiceAccessType::EXPLICIT_ACCESS)));
-    nux::EmailHandler::AddSources(html_source, profile->GetPrefs());
+        FaviconServiceFactory::GetForProfile(
+            profile, ServiceAccessType::EXPLICIT_ACCESS)));
+    nux::EmailHandler::AddSources(html_source);
 
     // Add google apps bookmarking onboarding module.
     web_ui->AddMessageHandler(std::make_unique<nux::GoogleAppsHandler>(
-        profile->GetPrefs(), FaviconServiceFactory::GetForProfile(
-                                 profile, ServiceAccessType::EXPLICIT_ACCESS)));
-    nux::GoogleAppsHandler::AddSources(html_source, profile->GetPrefs());
+        FaviconServiceFactory::GetForProfile(
+            profile, ServiceAccessType::EXPLICIT_ACCESS)));
+    nux::GoogleAppsHandler::AddSources(html_source);
 
     // Add set-as-default onboarding module.
     web_ui->AddMessageHandler(std::make_unique<nux::SetAsDefaultHandler>());
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.cc b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
index 2438bc11..7b83110 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
@@ -384,14 +384,15 @@
 }
 
 void AuthenticatorRequestDialogModel::UpdateAuthenticatorReferencePairingMode(
-    base::StringPiece authenticator_id) {
+    base::StringPiece authenticator_id,
+    bool is_in_pairing_mode) {
   auto it = std::find_if(
       saved_authenticators_.begin(), saved_authenticators_.end(),
       [authenticator_id](const auto& authenticator) {
         return authenticator->authenticator_id() == authenticator_id;
       });
   if (it != saved_authenticators_.end())
-    (*it)->SetIsInPairingMode(true /* is_in_pairing_mode */);
+    (*it)->SetIsInPairingMode(is_in_pairing_mode);
 }
 
 void AuthenticatorRequestDialogModel::SetSelectedAuthenticatorForTesting(
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.h b/chrome/browser/webauthn/authenticator_request_dialog_model.h
index 46e4b60..6e3a7781 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.h
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.h
@@ -264,7 +264,8 @@
   void RemoveAuthenticator(base::StringPiece authenticator_id);
 
   void UpdateAuthenticatorReferencePairingMode(
-      base::StringPiece authenticator_id);
+      base::StringPiece authenticator_id,
+      bool is_in_pairing_mode);
 
   void SetSelectedAuthenticatorForTesting(
       std::unique_ptr<AuthenticatorReference> authenticator);
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
index 71d9365..e9a5843 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
@@ -324,11 +324,13 @@
 }
 
 void ChromeAuthenticatorRequestDelegate::FidoAuthenticatorPairingModeChanged(
-    base::StringPiece authenticator_id) {
+    base::StringPiece authenticator_id,
+    bool is_in_pairing_mode) {
   if (!weak_dialog_model_)
     return;
 
-  weak_dialog_model_->UpdateAuthenticatorReferencePairingMode(authenticator_id);
+  weak_dialog_model_->UpdateAuthenticatorReferencePairingMode(
+      authenticator_id, is_in_pairing_mode);
 }
 
 void ChromeAuthenticatorRequestDelegate::BluetoothAdapterPowerChanged(
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
index 8e817375..6fd5c9d 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
@@ -95,8 +95,8 @@
   void FidoAuthenticatorRemoved(base::StringPiece authenticator_id) override;
   void FidoAuthenticatorIdChanged(base::StringPiece old_authenticator_id,
                                   std::string new_authenticator_id) override;
-  void FidoAuthenticatorPairingModeChanged(
-      base::StringPiece authenticator_id) override;
+  void FidoAuthenticatorPairingModeChanged(base::StringPiece authenticator_id,
+                                           bool is_in_pairing_mode) override;
   void BluetoothAdapterPowerChanged(bool is_powered_on) override;
 
   // AuthenticatorRequestDialogModel::Observer:
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index ce3dd976..e966627 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -232,12 +232,6 @@
 #endif
 
 #if defined(OS_ANDROID)
-// Enables downloads as a foreground service for all versions of Android.
-const base::Feature kDownloadsForeground{"DownloadsForeground",
-                                         base::FEATURE_ENABLED_BY_DEFAULT};
-#endif
-
-#if defined(OS_ANDROID)
 // Enable changing default downloads storage location on Android.
 const base::Feature kDownloadsLocationChange{"DownloadsLocationChange",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 890f0d2e..9981797b 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -147,11 +147,6 @@
 
 #if defined(OS_ANDROID)
 COMPONENT_EXPORT(CHROME_FEATURES)
-extern const base::Feature kDownloadsForeground;
-#endif
-
-#if defined(OS_ANDROID)
-COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kDownloadsLocationChange;
 #endif
 
diff --git a/chrome/common/extensions/api/BUILD.gn b/chrome/common/extensions/api/BUILD.gn
index 3d25c0a3..d86054e 100644
--- a/chrome/common/extensions/api/BUILD.gn
+++ b/chrome/common/extensions/api/BUILD.gn
@@ -56,7 +56,6 @@
     "proxy.json",
     "tts.json",
     "tts_engine.json",
-    "webstore.json",
   ]
   if (is_chromeos) {
     sources += [ "file_browser_handler.json" ]
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
index 5d7fbcb..ab6675f 100644
--- a/chrome/common/extensions/api/_api_features.json
+++ b/chrome/common/extensions/api/_api_features.json
@@ -253,7 +253,8 @@
       "chrome://chrome-signin/*",
       "chrome://media-router/*",
       "chrome://mobilesetup/*",
-      "chrome://oobe/*"
+      "chrome://oobe/*",
+      "chrome://assistant-optin/*"
     ]
   }],
   "cloudPrintPrivate": {
diff --git a/chrome/common/extensions/api/webstore.json b/chrome/common/extensions/api/webstore.json
index df5dde78..1194ab6 100644
--- a/chrome/common/extensions/api/webstore.json
+++ b/chrome/common/extensions/api/webstore.json
@@ -11,7 +11,8 @@
         "id": "InstallStage",
         "type": "string",
         "enum": ["installing", "downloading"],
-        "description": "Enum used to indicate the stage of the installation process. 'downloading' indicates that the necessary files are being downloaded, and 'installing' indicates that the files are downloaded and are being actively installed."
+        "description": "Enum used to indicate the stage of the installation process. 'downloading' indicates that the necessary files are being downloaded, and 'installing' indicates that the files are downloaded and are being actively installed.",
+        "deprecated": "Inline installation is deprecated."
       },
       {
         "id": "ErrorCode",
@@ -89,7 +90,8 @@
             "description": "A launch of the same extension is in progress.",
             "name": "launchInProgress"
           }
-        ]
+        ],
+        "deprecated": "Inline installation is deprecated."
       }
     ],  // types
     "events": [
@@ -103,7 +105,8 @@
             "$ref": "InstallStage",
             "description": "The InstallStage that just began."
           }
-        ]
+        ],
+        "deprecated": "Inline installation is deprecated."
       },  // onInstallStageChanged
       {
         "name": "onDownloadProgress",
@@ -115,7 +118,8 @@
             "type": "number",
             "description": "The progress of the download, between 0 and 1. 0 indicates no progress; 1.0 indicates complete."
           }
-        ]
+        ],
+        "deprecated": "Inline installation is deprecated."
       }  // onDownloadProgress
     ],  // events
     "functions": [
@@ -155,7 +159,8 @@
             ],
             "description": "This function is invoked when inline installation does not successfully complete. Possible reasons for this include the user canceling the dialog, the linked item not being found in the store, or the install being initiated from a non-verified site."
           }
-        ]
+        ],
+        "deprecated": "Inline installation is deprecated."
       }  // install
     ]  // functions
   }  // webstore
diff --git a/chrome/common/mac/app_shim.mojom b/chrome/common/mac/app_shim.mojom
index 3cba641..8b7a80a 100644
--- a/chrome/common/mac/app_shim.mojom
+++ b/chrome/common/mac/app_shim.mojom
@@ -34,10 +34,6 @@
       associated content.mojom.NSViewBridgeFactory&
           content_ns_views_bridge_factory);
 
-  // Signals that a previous LaunchApp message has been processed, and lets the
-  // app shim process know whether it was registered successfully.
-  LaunchAppDone(AppShimLaunchResult launch_result);
-
   // Instructs the shim to hide the app.
   Hide();
 
@@ -50,16 +46,6 @@
 
 // Interface through which the a process communicates to the browser process.
 interface AppShimHost {
-  // Signals to the main Chrome process that a shim has started. The app shim
-  // process is requesting to be associated with the given profile and app_id.
-  // Once the profile and app_id are stored, and all future messages from the
-  // app shim relate to this app.
-  LaunchApp(AppShim app_shim,
-            mojo_base.mojom.FilePath profile_dir,
-            string app_mode_id,
-            AppShimLaunchType launch_type,
-            array<mojo_base.mojom.FilePath> files);
-
   // Sent when the user has indicated a desire to focus the app, either by
   // clicking on the app's icon in the dock or by selecting it with Cmd+Tab. In
   // response, Chrome brings the app's windows to the foreground, or relaunches
@@ -74,3 +60,18 @@
   QuitApp();
 };
 
+// The initial interface provided by the browser process. Used to bootstrap to
+// the AppShim and AppShimHost interfaces.
+interface AppShimHostBootstrap {
+  // Signals to the main Chrome process that a shim has started. The app shim
+  // process is requesting to be associated with the given profile and app_id.
+  // Once the profile and app_id are stored, and all future messages from the
+  // app shim relate to this app.
+  LaunchApp(AppShimHost& host_request,
+            mojo_base.mojom.FilePath profile_dir,
+            string app_mode_id,
+            AppShimLaunchType launch_type,
+            array<mojo_base.mojom.FilePath> files) =>
+                (AppShimLaunchResult launch_result, AppShim& app_shim_request);
+};
+
diff --git a/chrome/common/page_load_metrics/page_load_metrics.mojom b/chrome/common/page_load_metrics/page_load_metrics.mojom
index c284e25..2263643 100644
--- a/chrome/common/page_load_metrics/page_load_metrics.mojom
+++ b/chrome/common/page_load_metrics/page_load_metrics.mojom
@@ -38,6 +38,9 @@
 
   // (Experimental) Time when the page's largest image is painted.
   mojo_base.mojom.TimeDelta? largest_image_paint;
+
+  // (Experimental) Time when the page's last image is painted.
+  mojo_base.mojom.TimeDelta? last_image_paint;
 };
 
 // TimeDeltas below represent durations of time during the page load.
diff --git a/chrome/common/page_load_metrics/page_load_timing.cc b/chrome/common/page_load_metrics/page_load_timing.cc
index fa7ebbd..16bc248 100644
--- a/chrome/common/page_load_metrics/page_load_timing.cc
+++ b/chrome/common/page_load_metrics/page_load_timing.cc
@@ -28,7 +28,8 @@
 bool IsEmpty(const page_load_metrics::mojom::PaintTiming& timing) {
   return !timing.first_paint && !timing.first_text_paint &&
          !timing.first_image_paint && !timing.first_contentful_paint &&
-         !timing.first_meaningful_paint && !timing.largest_image_paint;
+         !timing.first_meaningful_paint && !timing.largest_image_paint &&
+         !timing.last_image_paint;
 }
 
 bool IsEmpty(const page_load_metrics::mojom::ParseTiming& timing) {
diff --git a/chrome/common/safe_browsing/BUILD.gn b/chrome/common/safe_browsing/BUILD.gn
index 413936d..b93eeae 100644
--- a/chrome/common/safe_browsing/BUILD.gn
+++ b/chrome/common/safe_browsing/BUILD.gn
@@ -41,10 +41,10 @@
     ]
   }
 
-  source_set("download_protection_util") {
+  source_set("download_type_util") {
     sources = [
-      "download_protection_util.cc",
-      "download_protection_util.h",
+      "download_type_util.cc",
+      "download_type_util.h",
     ]
     deps = [
       ":file_type_policies",
@@ -64,7 +64,7 @@
 
     deps = [
       ":archive_analyzer_results",
-      ":download_protection_util",
+      ":download_type_util",
       ":file_type_policies",
       "//base",
       "//base:i18n",
@@ -149,7 +149,7 @@
 
     deps += [
       ":archive_analyzer_results",
-      ":download_protection_util",
+      ":download_type_util",
       ":rar_analyzer",
       "//components/safe_browsing:features",
     ]
diff --git a/chrome/common/safe_browsing/download_protection_util.cc b/chrome/common/safe_browsing/download_type_util.cc
similarity index 95%
rename from chrome/common/safe_browsing/download_protection_util.cc
rename to chrome/common/safe_browsing/download_type_util.cc
index aa170e5a..99b61a6 100644
--- a/chrome/common/safe_browsing/download_protection_util.cc
+++ b/chrome/common/safe_browsing/download_type_util.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 "chrome/common/safe_browsing/download_protection_util.h"
+#include "chrome/common/safe_browsing/download_type_util.h"
 
 #include <algorithm>
 
@@ -14,7 +14,7 @@
 #include "components/safe_browsing/features.h"
 
 namespace safe_browsing {
-namespace download_protection_util {
+namespace download_type_util {
 
 ClientDownloadRequest::DownloadType GetDownloadType(
     const base::FilePath& file) {
@@ -61,5 +61,5 @@
   return ClientDownloadRequest::WIN_EXECUTABLE;
 }
 
-}  // namespace download_protection_util
+}  // namespace download_type_util
 }  // namespace safe_browsing
diff --git a/chrome/common/safe_browsing/download_protection_util.h b/chrome/common/safe_browsing/download_type_util.h
similarity index 66%
rename from chrome/common/safe_browsing/download_protection_util.h
rename to chrome/common/safe_browsing/download_type_util.h
index d2e4579..87f17e4 100644
--- a/chrome/common/safe_browsing/download_protection_util.h
+++ b/chrome/common/safe_browsing/download_type_util.h
@@ -2,20 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_COMMON_SAFE_BROWSING_DOWNLOAD_PROTECTION_UTIL_H_
-#define CHROME_COMMON_SAFE_BROWSING_DOWNLOAD_PROTECTION_UTIL_H_
+#ifndef CHROME_COMMON_SAFE_BROWSING_DOWNLOAD_TYPE_UTIL_H_
+#define CHROME_COMMON_SAFE_BROWSING_DOWNLOAD_TYPE_UTIL_H_
 
 #include "base/files/file_path.h"
 #include "components/safe_browsing/proto/csd.pb.h"
 
 namespace safe_browsing {
-namespace download_protection_util {
+namespace download_type_util {
 
 // Returns the DownloadType of the file at |path|. This function is only valid
 // for paths that satisfy IsSupportedBinaryFile() above.
 ClientDownloadRequest::DownloadType GetDownloadType(const base::FilePath& file);
 
-}  // namespace download_protection_util
+}  // namespace download_type_util
 }  // namespace safe_browsing
 
-#endif  // CHROME_COMMON_SAFE_BROWSING_DOWNLOAD_PROTECTION_UTIL_H_
+#endif  // CHROME_COMMON_SAFE_BROWSING_DOWNLOAD_TYPE_UTIL_H_
diff --git a/chrome/common/safe_browsing/download_protection_util_unittest.cc b/chrome/common/safe_browsing/download_type_util_unittest.cc
similarity index 88%
rename from chrome/common/safe_browsing/download_protection_util_unittest.cc
rename to chrome/common/safe_browsing/download_type_util_unittest.cc
index 473e82a..1678a25 100644
--- a/chrome/common/safe_browsing/download_protection_util_unittest.cc
+++ b/chrome/common/safe_browsing/download_type_util_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/common/safe_browsing/download_protection_util.h"
+#include "chrome/common/safe_browsing/download_type_util.h"
 
 #include "base/feature_list.h"
 #include "base/files/file_path.h"
@@ -10,7 +10,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
-namespace download_protection_util {
+namespace download_type_util {
 
 TEST(DownloadProtectionUtilTest, KnownValues) {
   EXPECT_EQ(ClientDownloadRequest::WIN_EXECUTABLE,
@@ -29,6 +29,5 @@
             GetDownloadType(base::FilePath(FILE_PATH_LITERAL("foo.apk"))));
 }
 
-} // namespace download_protection_util
-} // namespace safe_browsing
-
+}  // namespace download_type_util
+}  // namespace safe_browsing
diff --git a/chrome/common/safe_browsing/rar_analyzer.cc b/chrome/common/safe_browsing/rar_analyzer.cc
index 91396e7..996c0164 100644
--- a/chrome/common/safe_browsing/rar_analyzer.cc
+++ b/chrome/common/safe_browsing/rar_analyzer.cc
@@ -11,7 +11,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "build/build_config.h"
 #include "chrome/common/safe_browsing/archive_analyzer_results.h"
-#include "chrome/common/safe_browsing/download_protection_util.h"
+#include "chrome/common/safe_browsing/download_type_util.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
 #include "third_party/unrar/src/unrar_wrapper.h"
 
@@ -83,7 +83,7 @@
       if (is_utf8_valid_basename)
         archived_binary->set_file_basename(basename_utf8);
       archived_binary->set_download_type(
-          download_protection_util::GetDownloadType(file_path));
+          download_type_util::GetDownloadType(file_path));
       archived_binary->set_length(unpacked_size);
     }
     results->archived_archive_filenames.assign(
diff --git a/chrome/common/safe_browsing/zip_analyzer.cc b/chrome/common/safe_browsing/zip_analyzer.cc
index d3d3e1c..037473084 100644
--- a/chrome/common/safe_browsing/zip_analyzer.cc
+++ b/chrome/common/safe_browsing/zip_analyzer.cc
@@ -17,7 +17,7 @@
 #include "build/build_config.h"
 #include "chrome/common/safe_browsing/archive_analyzer_results.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
-#include "chrome/common/safe_browsing/download_protection_util.h"
+#include "chrome/common/safe_browsing/download_type_util.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
 #include "components/safe_browsing/proto/csd.pb.h"
 #include "crypto/secure_hash.h"
@@ -90,7 +90,7 @@
   if (base::StreamingUtf8Validator::Validate(file_basename))
     archived_binary->set_file_basename(file_basename);
   archived_binary->set_download_type(
-      download_protection_util::GetDownloadType(file_path));
+      download_type_util::GetDownloadType(file_path));
   archived_binary->set_length(reader->current_entry_info()->original_size());
   HashingFileWriter writer(temp_file);
   if (reader->ExtractCurrentEntry(&writer,
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index f506246..af07460 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -207,8 +207,6 @@
     "chrome://internet-detail-dialog/";
 const char kChromeUIInternetConfigDialogHost[] = "internet-config-dialog";
 const char kChromeUIInternetDetailDialogHost[] = "internet-detail-dialog";
-const char kChromeUIKeyboardOverlayHost[] = "keyboardoverlay";
-const char kChromeUIKeyboardOverlayURL[] = "chrome://keyboardoverlay/";
 const char kChromeUILinuxCreditsHost[] = "linux-credits";
 const char kChromeUILinuxCreditsURL[] = "chrome://linux-credits/";
 const char kChromeUIMobileSetupHost[] = "mobilesetup";
@@ -401,7 +399,6 @@
     kChromeUICryptohomeHost,
     kChromeUIDriveInternalsHost,
     kChromeUIFirstRunHost,
-    kChromeUIKeyboardOverlayHost,
     kChromeUILinuxCreditsHost,
     kChromeUINetworkHost,
     kChromeUIOobeHost,
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index 836b544..4b08a81 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -202,8 +202,6 @@
 extern const char kChromeUIIntenetDetailDialogURL[];
 extern const char kChromeUIInternetConfigDialogHost[];
 extern const char kChromeUIInternetDetailDialogHost[];
-extern const char kChromeUIKeyboardOverlayHost[];
-extern const char kChromeUIKeyboardOverlayURL[];
 extern const char kChromeUILinuxCreditsHost[];
 extern const char kChromeUILinuxCreditsURL[];
 extern const char kChromeUIMobileSetupHost[];
diff --git a/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc b/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc
index e31504f..de6d0395 100644
--- a/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc
+++ b/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc
@@ -252,8 +252,6 @@
       IDR_WEBRTC_DESKTOP_CAPTURE_PRIVATE_CUSTOM_BINDINGS_JS);
   source_map->RegisterSource("webrtcLoggingPrivate",
                              IDR_WEBRTC_LOGGING_PRIVATE_CUSTOM_BINDINGS_JS);
-  source_map->RegisterSource("webstore", IDR_WEBSTORE_CUSTOM_BINDINGS_JS);
-
 
   // Platform app sources that are not API-specific..
   source_map->RegisterSource("chromeWebViewInternal",
diff --git a/chrome/renderer/net/available_offline_content_helper.cc b/chrome/renderer/net/available_offline_content_helper.cc
index d918e1d9..ff00d360 100644
--- a/chrome/renderer/net/available_offline_content_helper.cc
+++ b/chrome/renderer/net/available_offline_content_helper.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/renderer/net/available_offline_content_helper.h"
 
+#include "base/base64.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_value_converter.h"
 #include "base/json/json_writer.h"
@@ -22,13 +23,20 @@
 using chrome::mojom::AvailableContentType;
 
 base::Value AvailableContentToValue(const AvailableOfflineContentPtr& content) {
+  // All pieces of text content downloaded from the web will be base64 encoded
+  // to lessen security risks when this dictionary is passed as a string to
+  // |ExecuteJavaScript|.
+  std::string base64_encoded;
   base::Value value(base::Value::Type::DICTIONARY);
   value.SetKey("ID", base::Value(content->id));
   value.SetKey("name_space", base::Value(content->name_space));
-  value.SetKey("title", base::Value(content->title));
-  value.SetKey("snippet", base::Value(content->snippet));
+  base::Base64Encode(content->title, &base64_encoded);
+  value.SetKey("title_base64", base::Value(base64_encoded));
+  base::Base64Encode(content->snippet, &base64_encoded);
+  value.SetKey("snippet_base64", base::Value(base64_encoded));
   value.SetKey("date_modified", base::Value(content->date_modified));
-  value.SetKey("attribution", base::Value(content->attribution));
+  base::Base64Encode(content->attribution, &base64_encoded);
+  value.SetKey("attribution_base64", base::Value(base64_encoded));
   value.SetKey("thumbnail_data_uri",
                base::Value(content->thumbnail_data_uri.spec()));
   value.SetKey("content_type",
diff --git a/chrome/renderer/net/net_error_helper_core_unittest.cc b/chrome/renderer/net/net_error_helper_core_unittest.cc
index cebdac45..0074b33 100644
--- a/chrome/renderer/net/net_error_helper_core_unittest.cc
+++ b/chrome/renderer/net/net_error_helper_core_unittest.cc
@@ -2717,27 +2717,29 @@
   DoErrorLoad(net::ERR_INTERNET_DISCONNECTED);
   task_environment()->RunUntilIdle();
   // Note: content_type is an AvailableContentType enum value.
-  // Below, 0=kPrefetchedPage and 3=kOtherPage.
+  // Below, 0=kPrefetchedPage and 3=kOtherPage. The base64 encoded values
+  // represent the encoded versions of the values returned by
+  // |TestAvailableContent|.
   std::string want_json = R"([
       {
         "ID": "ID",
-        "attribution": "attribution",
+        "attribution_base64": "YXR0cmlidXRpb24=",
         "content_type": 0,
         "date_modified": "date_modified",
         "name_space": "name_space",
-        "snippet": "snippet",
+        "snippet_base64": "c25pcHBldA==",
         "thumbnail_data_uri": "data:image/png;base64,abc",
-        "title": "title"
+        "title_base64": "dGl0bGU="
       },
       {
         "ID": "ID2",
-        "attribution": "attribution2",
+        "attribution_base64": "YXR0cmlidXRpb24y",
         "content_type": 3,
         "date_modified": "date_modified2",
         "name_space": "name_space2",
-        "snippet": "snippet2",
+        "snippet_base64": "c25pcHBldDI=",
         "thumbnail_data_uri": "data:image/png;base64,abc",
-        "title": "title2"
+        "title_base64": "dGl0bGUy"
       }
     ])";
   base::ReplaceChars(want_json, base::kWhitespaceASCII, "", &want_json);
diff --git a/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc b/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc
index 6ca5dd2..844b352 100644
--- a/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc
+++ b/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc
@@ -320,6 +320,10 @@
     timing->paint_timing->largest_image_paint =
         ClampDelta(perf.LargestImagePaint(), start);
   }
+  if (perf.LastImagePaint() > 0.0) {
+    timing->paint_timing->last_image_paint =
+        ClampDelta(perf.LastImagePaint(), start);
+  }
   if (perf.ParseStart() > 0.0)
     timing->parse_timing->parse_start = ClampDelta(perf.ParseStart(), start);
   if (perf.ParseStop() > 0.0)
diff --git a/chrome/renderer/resources/extensions/webstore_custom_bindings.js b/chrome/renderer/resources/extensions/webstore_custom_bindings.js
deleted file mode 100644
index 6e03afa..0000000
--- a/chrome/renderer/resources/extensions/webstore_custom_bindings.js
+++ /dev/null
@@ -1,130 +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.
-
-// Custom binding for the webstore API.
-
-var webstoreNatives = requireNative('webstore');
-
-var onInstallStageChanged;
-var onDownloadProgress;
-
-function Installer() {
-  this._pendingInstall = null;
-}
-
-Installer.prototype.install = function(url, onSuccess, onFailure) {
-  console.warn('chrome.webstore.install is deprecated. See ' +
-      'https://blog.chromium.org/2018/06/improving-extension-transparency-for.html ' +
-      'for more details.');
-
-  if (this._pendingInstall)
-    throw new Error('A Chrome Web Store installation is already pending.');
-
-  // With native bindings, these calls go through argument validation, which
-  // sets optional/missing arguments to null. The native webstore bindings
-  // expect either present or undefined, so transform null to undefined.
-  if (url === null)
-    url = undefined;
-  if (onSuccess === null)
-    onSuccess = undefined;
-  if (onFailure === null)
-    onFailure = undefined;
-
-  if (url !== undefined && typeof url !== 'string') {
-    throw new Error(
-        'The Chrome Web Store item link URL parameter must be a string.');
-  }
-  if (onSuccess !== undefined && typeof onSuccess !== 'function') {
-    throw new Error('The success callback parameter must be a function.');
-  }
-  if (onFailure !== undefined && typeof onFailure !== 'function') {
-    throw new Error('The failure callback parameter must be a function.');
-  }
-
-  // Since we call Install() with a bool for if we have listeners, listeners
-  // must be set prior to the inline installation starting (this is also
-  // noted in the Event documentation in
-  // chrome/common/extensions/api/webstore.json).
-  var installId = webstoreNatives.Install(
-      onInstallStageChanged.hasListeners(),
-      onDownloadProgress.hasListeners(),
-      url,
-      onSuccess,
-      onFailure);
-  if (installId !== undefined) {
-    this._pendingInstall = {
-      installId: installId,
-      onSuccess: onSuccess,
-      onFailure: onFailure
-    };
-  }
-};
-
-Installer.prototype.onInstallResponse =
-    function(installId, success, error, resultCode) {
-  var pendingInstall = this._pendingInstall;
-  if (!pendingInstall || pendingInstall.installId != installId) {
-    // TODO(kalman): should this be an error?
-    return;
-  }
-
-  try {
-    if (success && pendingInstall.onSuccess)
-      pendingInstall.onSuccess();
-    else if (!success && pendingInstall.onFailure)
-      pendingInstall.onFailure(error, resultCode);
-  } catch (e) {
-    console.error('Exception in chrome.webstore.install response handler: ' +
-                  e.stack);
-  } finally {
-    this._pendingInstall = null;
-  }
-};
-
-Installer.prototype.onInstallStageChanged = function(installStage) {
-  onInstallStageChanged.dispatch(installStage);
-};
-
-Installer.prototype.onDownloadProgress = function(progress) {
-  onDownloadProgress.dispatch(progress);
-};
-
-var installer = new Installer();
-
-
-if (apiBridge) {
-  apiBridge.registerCustomHook(function(api) {
-    api.apiFunctions.setHandleRequest('install',
-                                      function(url, onSuccess, onFailure) {
-      installer.install(url, onSuccess, onFailure);
-    });
-
-    onInstallStageChanged = api.compiledApi.onInstallStageChanged;
-    onDownloadProgress = api.compiledApi.onDownloadProgress;
-  });
-} else {
-  var Event = require('event_bindings').Event;
-  onInstallStageChanged =
-      new Event(null, [{name: 'stage', type: 'string'}], {unmanaged: true});
-  onDownloadProgress =
-      new Event(null, [{name: 'progress', type: 'number'}], {unmanaged: true});
-
-  var chromeWebstore = {
-    install: function (url, onSuccess, onFailure) {
-      installer.install(url, onSuccess, onFailure);
-    },
-    onInstallStageChanged: onInstallStageChanged,
-    onDownloadProgress: onDownloadProgress,
-  };
-  exports.$set('binding', chromeWebstore);
-}
-
-// Called by webstore_bindings.cc.
-exports.$set('onInstallResponse',
-             $Function.bind(Installer.prototype.onInstallResponse, installer));
-exports.$set('onInstallStageChanged',
-             $Function.bind(Installer.prototype.onInstallStageChanged,
-                            installer));
-exports.$set('onDownloadProgress',
-             $Function.bind(Installer.prototype.onDownloadProgress, installer));
diff --git a/chrome/renderer/resources/renderer_resources.grd b/chrome/renderer/resources/renderer_resources.grd
index 9d8f81b..d6272f6b 100644
--- a/chrome/renderer/resources/renderer_resources.grd
+++ b/chrome/renderer/resources/renderer_resources.grd
@@ -58,7 +58,6 @@
         <include name="IDR_TTS_ENGINE_CUSTOM_BINDINGS_JS" file="extensions\tts_engine_custom_bindings.js" type="BINDATA" />
         <include name="IDR_WEBRTC_DESKTOP_CAPTURE_PRIVATE_CUSTOM_BINDINGS_JS" file="extensions\webrtc_desktop_capture_private_custom_bindings.js" type="BINDATA" />
         <include name="IDR_WEBRTC_LOGGING_PRIVATE_CUSTOM_BINDINGS_JS" file="extensions\webrtc_logging_private_custom_bindings.js" type="BINDATA" />
-        <include name="IDR_WEBSTORE_CUSTOM_BINDINGS_JS" file="extensions\webstore_custom_bindings.js" type="BINDATA" />
         <if expr="chromeos">
           <include name="IDR_CERTIFICATE_PROVIDER_CUSTOM_BINDINGS_JS" file="extensions\certificate_provider_custom_bindings.js" type="BINDATA" />
           <include name="IDR_ENTERPRISE_PLATFORM_KEYS_CUSTOM_BINDINGS_JS" file="extensions\enterprise_platform_keys_custom_bindings.js" type="BINDATA" />
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index b21db56..751dcc9 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1822,7 +1822,6 @@
         "../browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc",
         "../browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc",
         "../browser/ui/views/frame/top_controls_slide_controller_chromeos_browsertest.cc",
-        "../browser/ui/webui/chromeos/keyboard_overlay_ui_browsertest.cc",
         "../browser/ui/webui/chromeos/system_web_dialog_browsertest.cc",
         "../browser/ui/webui/settings/chromeos/device_power_handler_browsertest.cc",
       ]
@@ -1857,6 +1856,7 @@
         "//ash/public/interfaces:test_interfaces",
         "//chrome/browser/chromeos:arc_test_support",
         "//chrome/browser/resources/chromeos/chromevox:browser_tests",
+        "//chrome/browser/resources/chromeos/select_to_speak:browser_tests",
         "//chrome/services/file_util/public/cpp:browser_tests",
         "//chromeos/components/drivefs:test_support",
         "//components/arc:arc_test_support",
@@ -1956,15 +1956,14 @@
         "../browser/ui/cocoa/permission_bubble/permission_bubble_views_cocoa_browsertest.mm",
         "../browser/ui/cocoa/renderer_context_menu/render_view_context_menu_mac_cocoa_browsertest.mm",
         "../browser/ui/cocoa/share_menu_controller_browsertest.mm",
-        "../browser/ui/cocoa/ssl_client_certificate_selector_cocoa_browsertest.mm",
         "../browser/ui/cocoa/task_manager_mac_browsertest.mm",
         "../browser/ui/cocoa/touchbar/browser_window_touch_bar_controller_browsertest.mm",
         "../browser/ui/cocoa/touchbar/text_suggestions_touch_bar_controller_browsertest.mm",
         "../browser/ui/views/certificate_viewer_mac_browsertest.mm",
+        "../browser/ui/views/ssl_client_certificate_selector_mac_browsertest.mm",
 
         # TODO(crbug/845389): Re-Enable the following, which were temporarily
         # omitted from the build, but are still in use.
-        # "../browser/ui/cocoa/constrained_window/constrained_window_mac_browsertest.mm",
         # "../browser/ui/cocoa/page_info/page_info_bubble_views_mac_browsertest.mm",
       ]
       sources -= [
@@ -2721,8 +2720,6 @@
     "../browser/ui/sync/profile_signin_confirmation_helper_unittest.cc",
     "../browser/ui/sync/sync_promo_ui_unittest.cc",
     "../browser/ui/sync/tab_contents_synced_tab_delegate_unittest.cc",
-    "../browser/ui/tests/ui_gfx_image_unittest.cc",
-    "../browser/ui/tests/ui_gfx_image_unittest.mm",
     "../browser/ui/webui/fileicon_source_unittest.cc",
     "../browser/ui/webui/interventions_internals/interventions_internals_page_handler_unittest.cc",
     "../browser/ui/webui/local_state/local_state_ui_unittest.cc",
@@ -3962,7 +3959,7 @@
       "../common/safe_browsing/binary_feature_extractor_unittest.cc",
       "../common/safe_browsing/binary_feature_extractor_win_unittest.cc",
       "../common/safe_browsing/disk_image_type_sniffer_mac_unittest.cc",
-      "../common/safe_browsing/download_protection_util_unittest.cc",
+      "../common/safe_browsing/download_type_util_unittest.cc",
       "../common/safe_browsing/ipc_protobuf_message_test_messages.h",
       "../common/safe_browsing/ipc_protobuf_message_unittest.cc",
       "../common/safe_browsing/mach_o_image_reader_mac_unittest.cc",
@@ -4114,7 +4111,6 @@
       # This tests the function GetSpellCheckLanguages which is not used on
       # Mac.
       "../browser/spellchecker/spellcheck_service_unittest.cc",
-      "../browser/ui/tests/ui_gfx_image_unittest.cc",
 
       # This tests Chrome's spellchecker which Mac doesn't use.
       "../tools/convert_dict/convert_dict_unittest.cc",
@@ -4122,49 +4118,36 @@
 
     # The test fetches resources which means Mac need the app bundle to exist
     # on disk so it can pull from it.
-    deps += [
-      "//third_party/google_toolbox_for_mac",
-      "//third_party/ocmock",
+    deps += [ "//third_party/ocmock" ]
+
+    sources += [
+      "../browser/ui/cocoa/applescript/apple_event_util_unittest.mm",
+      "../browser/ui/cocoa/bookmarks/bookmark_menu_bridge_unittest.mm",
+      "../browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller_unittest.mm",
+      "../browser/ui/cocoa/color_panel_cocoa_unittest.mm",
+      "../browser/ui/cocoa/confirm_quit_panel_controller_unittest.mm",
+      "../browser/ui/cocoa/find_pasteboard_unittest.mm",
+      "../browser/ui/cocoa/first_run_dialog_controller_unittest.mm",
+      "../browser/ui/cocoa/history_menu_bridge_unittest.mm",
+      "../browser/ui/cocoa/history_menu_cocoa_controller_unittest.mm",
+      "../browser/ui/cocoa/history_overlay_controller_unittest.mm",
+      "../browser/ui/cocoa/main_menu_builder_unittest.mm",
+      "../browser/ui/cocoa/media_picker/desktop_media_picker_controller_unittest.mm",
+      "../browser/ui/cocoa/notifications/notification_builder_mac_unittest.mm",
+      "../browser/ui/cocoa/notifications/notification_response_builder_mac_unittest.mm",
+      "../browser/ui/cocoa/nsmenuitem_additions_unittest.mm",
+      "../browser/ui/cocoa/profiles/profile_menu_controller_unittest.mm",
+      "../browser/ui/cocoa/scoped_menu_bar_lock_unittest.mm",
+      "../browser/ui/cocoa/status_icons/status_icon_mac_unittest.mm",
+      "../browser/ui/cocoa/test/cocoa_profile_test.h",
+      "../browser/ui/cocoa/test/cocoa_profile_test.mm",
+      "../browser/ui/cocoa/test/run_loop_testing_unittest.mm",
+      "../browser/ui/cocoa/touchbar/browser_window_default_touch_bar_unittest.mm",
+      "../browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller_unittest.mm",
+      "../browser/ui/cocoa/touchbar/text_suggestions_touch_bar_controller_unittest.mm",
+      "../browser/ui/cocoa/window_size_autosaver_unittest.mm",
     ]
 
-    sources +=
-        [ "../browser/ui/cocoa/applescript/apple_event_util_unittest.mm" ]
-
-    # TODO(ellyjones): remove this needless if (true). These files are
-    # test sources for the cocoa code; this block should be moved back inline at
-    # the place this variable is used below.
-    if (true) {
-      cocoa_test_sources = [
-        "../browser/ui/cocoa/bookmarks/bookmark_menu_bridge_unittest.mm",
-        "../browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller_unittest.mm",
-        "../browser/ui/cocoa/color_panel_cocoa_unittest.mm",
-        "../browser/ui/cocoa/confirm_quit_panel_controller_unittest.mm",
-        "../browser/ui/cocoa/constrained_window/constrained_window_sheet_controller_unittest.mm",
-        "../browser/ui/cocoa/find_pasteboard_unittest.mm",
-        "../browser/ui/cocoa/first_run_dialog_controller_unittest.mm",
-        "../browser/ui/cocoa/history_menu_bridge_unittest.mm",
-        "../browser/ui/cocoa/history_menu_cocoa_controller_unittest.mm",
-        "../browser/ui/cocoa/history_overlay_controller_unittest.mm",
-        "../browser/ui/cocoa/main_menu_builder_unittest.mm",
-        "../browser/ui/cocoa/media_picker/desktop_media_picker_controller_unittest.mm",
-        "../browser/ui/cocoa/notifications/notification_builder_mac_unittest.mm",
-        "../browser/ui/cocoa/notifications/notification_response_builder_mac_unittest.mm",
-        "../browser/ui/cocoa/nsmenuitem_additions_unittest.mm",
-        "../browser/ui/cocoa/profiles/profile_menu_controller_unittest.mm",
-        "../browser/ui/cocoa/scoped_menu_bar_lock_unittest.mm",
-        "../browser/ui/cocoa/status_icons/status_icon_mac_unittest.mm",
-        "../browser/ui/cocoa/test/cocoa_profile_test.h",
-        "../browser/ui/cocoa/test/cocoa_profile_test.mm",
-        "../browser/ui/cocoa/test/run_loop_testing_unittest.mm",
-        "../browser/ui/cocoa/touchbar/browser_window_default_touch_bar_unittest.mm",
-        "../browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller_unittest.mm",
-        "../browser/ui/cocoa/touchbar/text_suggestions_touch_bar_controller_unittest.mm",
-        "../browser/ui/cocoa/window_size_autosaver_unittest.mm",
-      ]
-    }
-
-    sources += cocoa_test_sources
-
     # TODO(mark): We really want this for all non-static library targets,
     # but when we tried to pull it up to the common.gypi level, it broke
     # other things like the ui and startup tests. *shrug*
@@ -4855,7 +4838,6 @@
         "../browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc",
         "../browser/ui/views/passwords/password_bubble_interactive_uitest.cc",
         "../browser/ui/views/sad_tab_view_interactive_uitest.cc",
-        "../browser/ui/views/ssl_client_certificate_selector_browsertest.cc",
         "../browser/ui/views/status_icons/status_tray_state_changer_interactive_uitest_win.cc",
         "../browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc",
         "../browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h",
@@ -4893,6 +4875,10 @@
       }
       if (is_mac) {
         deps += [ "//content/test:browsertest_support" ]
+      } else {
+        sources += [
+          "../browser/ui/views/ssl_client_certificate_selector_browsertest.cc",
+        ]
       }
     }
 
diff --git a/chrome/test/base/ui_test_utils.cc b/chrome/test/base/ui_test_utils.cc
index 85883038..dc6dd4d 100644
--- a/chrome/test/base/ui_test_utils.cc
+++ b/chrome/test/base/ui_test_utils.cc
@@ -182,12 +182,14 @@
   NavigateToURL(&params);
 }
 
-void NavigateToURL(Browser* browser, const GURL& url) {
-  NavigateToURLWithDisposition(browser, url, WindowOpenDisposition::CURRENT_TAB,
-                               BROWSER_TEST_WAIT_FOR_NAVIGATION);
+content::RenderProcessHost* NavigateToURL(Browser* browser, const GURL& url) {
+  return NavigateToURLWithDisposition(browser, url,
+                                      WindowOpenDisposition::CURRENT_TAB,
+                                      BROWSER_TEST_WAIT_FOR_NAVIGATION);
 }
 
-void NavigateToURLWithDispositionBlockUntilNavigationsComplete(
+content::RenderProcessHost*
+NavigateToURLWithDispositionBlockUntilNavigationsComplete(
     Browser* browser,
     const GURL& url,
     int number_of_navigations,
@@ -217,7 +219,7 @@
     tab_added_observer.Wait();
   if (!(browser_test_flags & BROWSER_TEST_WAIT_FOR_NAVIGATION)) {
     // Some other flag caused the wait prior to this.
-    return;
+    return nullptr;
   }
   WebContents* web_contents = NULL;
   if (disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB) {
@@ -228,7 +230,7 @@
         << " Unable to wait for navigation to \"" << url.spec()
         << "\" because the new tab is not available yet";
     if (!web_contents)
-      return;
+      return nullptr;
   } else if ((disposition == WindowOpenDisposition::CURRENT_TAB) ||
              (disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB) ||
              (disposition == WindowOpenDisposition::SINGLETON_TAB)) {
@@ -237,35 +239,34 @@
   }
   if (disposition == WindowOpenDisposition::CURRENT_TAB) {
     same_tab_observer.Wait();
-    return;
+    return web_contents->GetMainFrame()->GetProcess();
   } else if (web_contents) {
     content::TestNavigationObserver observer(
         web_contents, number_of_navigations,
         content::MessageLoopRunner::QuitMode::DEFERRED);
     observer.Wait();
-    return;
+    return web_contents->GetMainFrame()->GetProcess();
   }
   EXPECT_TRUE(NULL != web_contents) << " Unable to wait for navigation to \""
                                     << url.spec() << "\""
                                     << " because we can't get the tab contents";
+  return nullptr;
 }
 
-void NavigateToURLWithDisposition(Browser* browser,
-                                  const GURL& url,
-                                  WindowOpenDisposition disposition,
-                                  int browser_test_flags) {
-  NavigateToURLWithDispositionBlockUntilNavigationsComplete(
-      browser,
-      url,
-      1,
-      disposition,
-      browser_test_flags);
+content::RenderProcessHost* NavigateToURLWithDisposition(
+    Browser* browser,
+    const GURL& url,
+    WindowOpenDisposition disposition,
+    int browser_test_flags) {
+  return NavigateToURLWithDispositionBlockUntilNavigationsComplete(
+      browser, url, 1, disposition, browser_test_flags);
 }
 
-void NavigateToURLBlockUntilNavigationsComplete(Browser* browser,
-                                                const GURL& url,
-                                                int number_of_navigations) {
-  NavigateToURLWithDispositionBlockUntilNavigationsComplete(
+content::RenderProcessHost* NavigateToURLBlockUntilNavigationsComplete(
+    Browser* browser,
+    const GURL& url,
+    int number_of_navigations) {
+  return NavigateToURLWithDispositionBlockUntilNavigationsComplete(
       browser, url, number_of_navigations, WindowOpenDisposition::CURRENT_TAB,
       BROWSER_TEST_WAIT_FOR_NAVIGATION);
 }
diff --git a/chrome/test/base/ui_test_utils.h b/chrome/test/base/ui_test_utils.h
index 8f153b5..e01b1d19 100644
--- a/chrome/test/base/ui_test_utils.h
+++ b/chrome/test/base/ui_test_utils.h
@@ -40,6 +40,7 @@
 struct NavigateParams;
 
 namespace content {
+class RenderProcessHost;
 class WebContents;
 }
 
@@ -73,36 +74,72 @@
 // Performs the provided navigation process, blocking until the navigation
 // finishes. May change the params in some cases (i.e. if the navigation
 // opens a new browser window). Uses chrome::Navigate.
+//
+// Note this does not return a RenderProcessHost for where the navigation
+// occurs, so tests using this will be unable to verify the destruction of
+// the RenderProcessHost when navigating again.
 void NavigateToURL(NavigateParams* params);
 
 // Navigates the selected tab of |browser| to |url|, blocking until the
 // navigation finishes. Simulates a POST and uses chrome::Navigate.
+//
+// Note this does not return a RenderProcessHost for where the navigation
+// occurs, so tests using this will be unable to verify the destruction of
+// the RenderProcessHost when navigating again.
 void NavigateToURLWithPost(Browser* browser, const GURL& url);
 
 // Navigates the selected tab of |browser| to |url|, blocking until the
 // navigation finishes. Uses Browser::OpenURL --> chrome::Navigate.
-void NavigateToURL(Browser* browser, const GURL& url);
+//
+// Returns a RenderProcessHost* for the renderer where the navigation
+// occured. Use this when navigating again, when the test wants to wait not
+// just for the navigation to complete but also for the previous
+// RenderProcessHost to be torn down. Navigation does NOT imply the old
+// RenderProcessHost is gone, and assuming so creates a race condition that
+// can be exagerated by artifically slowing the FrameHostMsg_SwapOut_ACK reply
+// from the renderer being navigated from.
+content::RenderProcessHost* NavigateToURL(Browser* browser, const GURL& url);
 
 // Navigates the specified tab of |browser| to |url|, blocking until the
 // navigation finishes.
 // |disposition| indicates what tab the navigation occurs in, and
 // |browser_test_flags| controls what to wait for before continuing.
-void NavigateToURLWithDisposition(Browser* browser,
-                                  const GURL& url,
-                                  WindowOpenDisposition disposition,
-                                  int browser_test_flags);
+//
+// If the |browser_test_flags| includes a request to wait for navigation, this
+// returns a RenderProcessHost* for the renderer where the navigation
+// occured. Use this when navigating again, when the test wants to wait not
+// just for the navigation to complete but also for the previous
+// RenderProcessHost to be torn down. Navigation does NOT imply the old
+// RenderProcessHost is gone, and assuming so creates a race condition that
+// can be exagerated by artifically slowing the FrameHostMsg_SwapOut_ACK reply
+// from the renderer being navigated from.
+content::RenderProcessHost* NavigateToURLWithDisposition(
+    Browser* browser,
+    const GURL& url,
+    WindowOpenDisposition disposition,
+    int browser_test_flags);
 
 // Navigates the selected tab of |browser| to |url|, blocking until the
 // number of navigations specified complete.
-void NavigateToURLBlockUntilNavigationsComplete(Browser* browser,
-                                                const GURL& url,
-                                                int number_of_navigations);
+//
+// Returns a RenderProcessHost* for the renderer where the navigation
+// occured. Use this when navigating again, when the test wants to wait not
+// just for the navigation to complete but also for the previous
+// RenderProcessHost to be torn down. Navigation does NOT imply the old
+// RenderProcessHost is gone, and assuming so creates a race condition that
+// can be exagerated by artifically slowing the FrameHostMsg_SwapOut_ACK reply
+// from the renderer being navigated from.
+content::RenderProcessHost* NavigateToURLBlockUntilNavigationsComplete(
+    Browser* browser,
+    const GURL& url,
+    int number_of_navigations);
 
 // Navigates the specified tab (via |disposition|) of |browser| to |url|,
 // blocking until the |number_of_navigations| specified complete.
 // |disposition| indicates what tab the download occurs in, and
 // |browser_test_flags| controls what to wait for before continuing.
-void NavigateToURLWithDispositionBlockUntilNavigationsComplete(
+content::RenderProcessHost*
+NavigateToURLWithDispositionBlockUntilNavigationsComplete(
     Browser* browser,
     const GURL& url,
     int number_of_navigations,
diff --git a/chrome/test/chromedriver/test/test_expectations b/chrome/test/chromedriver/test/test_expectations
index 6540321e..fe8be1a9 100644
--- a/chrome/test/chromedriver/test/test_expectations
+++ b/chrome/test/chromedriver/test/test_expectations
@@ -26,10 +26,6 @@
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2532
     'SlowLoadingPageTest.testRefreshShouldBlockUntilPageLoads',
     'PageLoadingTest.testShouldTimeoutIfAPageTakesTooLongToRefresh',
-
-    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2629
-    'FrameSwitchingTest.*',
-    'GetLogsTest.*',
 ]
 
 _OS_NEGATIVE_FILTER = {}
diff --git a/chrome/test/data/chromeos/file_manager/absolute_paths.zip b/chrome/test/data/chromeos/file_manager/absolute_paths.zip
new file mode 100644
index 0000000..0b7f8794
--- /dev/null
+++ b/chrome/test/data/chromeos/file_manager/absolute_paths.zip
Binary files differ
diff --git a/chrome/test/data/webui/multidevice_setup/integration_test.js b/chrome/test/data/webui/multidevice_setup/integration_test.js
index 8a10e922..ade06915 100644
--- a/chrome/test/data/webui/multidevice_setup/integration_test.js
+++ b/chrome/test/data/webui/multidevice_setup/integration_test.js
@@ -139,7 +139,7 @@
        * @return {!Promise} Promise that resolves when the page renders.
        */
       function setVisiblePage(visiblePageName) {
-        multiDeviceSetupElement.visiblePageName_ = visiblePageName;
+        multiDeviceSetupElement.visiblePageName = visiblePageName;
         Polymer.dom.flush();
         return test_util.waitForRender(
             multiDeviceSetupElement.$$(visiblePageName));
@@ -288,12 +288,12 @@
             return setVisiblePage(PASSWORD)
                 .then(() => {
                   const whenPageChanges = test_util.eventToPromise(
-                      'visible-page-name_-changed', multiDeviceSetupElement);
+                      'visible-page-name-changed', multiDeviceSetupElement);
                   backwardButton.click();
                   return whenPageChanges;
                 })
                 .then(() => {
-                  assertEquals(START, multiDeviceSetupElement.visiblePageName_);
+                  assertEquals(START, multiDeviceSetupElement.visiblePageName);
                   assertEquals(0, getNumSetHostDeviceCalls());
                 });
           });
@@ -311,13 +311,13 @@
                 .then(() => {
                   multiDeviceSetupElement.delegate.shouldSetHostSucceed = true;
                   const whenPageChanges = test_util.eventToPromise(
-                      'visible-page-name_-changed', multiDeviceSetupElement);
+                      'visible-page-name-changed', multiDeviceSetupElement);
                   forwardButton.click();
                   return whenPageChanges;
                 })
                 .then(() => {
                   assertEquals(
-                      SUCCESS, multiDeviceSetupElement.visiblePageName_);
+                      SUCCESS, multiDeviceSetupElement.visiblePageName);
                   assertEquals(1, getNumSetHostDeviceCalls());
                 });
           });
@@ -340,7 +340,7 @@
                 })
                 .then(() => {
                   assertEquals(
-                      PASSWORD, multiDeviceSetupElement.visiblePageName_);
+                      PASSWORD, multiDeviceSetupElement.visiblePageName);
                   assertEquals(0, getNumSetHostDeviceCalls());
                 });
           });
diff --git a/chrome/test/data/webui/print_preview/key_event_test.js b/chrome/test/data/webui/print_preview/key_event_test.js
new file mode 100644
index 0000000..d191f04
--- /dev/null
+++ b/chrome/test/data/webui/print_preview/key_event_test.js
@@ -0,0 +1,166 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('key_event_test', function() {
+  /** @enum {string} */
+  const TestNames = {
+    EnterTriggersPrint: 'enter triggers print',
+    NumpadEnterTriggersPrint: 'numpad enter triggers print',
+    EnterOnInputTriggersPrint: 'enter on input triggers print',
+    EnterOnDropdownDoesNotPrint: 'enter on dropdown does not print',
+    EnterOnButtonDoesNotPrint: 'enter on button does not print',
+    EnterOnCheckboxDoesNotPrint: 'enter on checkbox does not print',
+    EscapeClosesDialogOnMacOnly: 'escape closes dialog on mac only',
+    CmdPeriodClosesDialogOnMacOnly: 'cmd period closes dialog on mac only',
+    CtrlShiftPOpensSystemDialog: 'ctrl shift p opens system dialog',
+  };
+
+  const suiteName = 'KeyEventTest';
+  suite(suiteName, function() {
+    /** @type {?PrintPreviewAppElement} */
+    let page = null;
+
+    /** @type {?PrintPreviewNativeLayer} */
+    let nativeLayer = null;
+
+    /** @override */
+    setup(function() {
+      const initialSettings =
+          print_preview_test_utils.getDefaultInitialSettings();
+      nativeLayer = new print_preview.NativeLayerStub();
+      nativeLayer.setInitialSettings(initialSettings);
+      nativeLayer.setLocalDestinationCapabilities(
+          print_preview_test_utils.getCddTemplate(initialSettings.printerName));
+      nativeLayer.setPageCount(3);
+      print_preview.NativeLayer.setInstance(nativeLayer);
+      const pluginProxy = new print_preview.PDFPluginStub();
+      print_preview_new.PluginProxy.setInstance(pluginProxy);
+
+      PolymerTest.clearBody();
+      page = document.createElement('print-preview-app');
+      document.body.appendChild(page);
+      const previewArea = page.$.previewArea;
+      pluginProxy.setLoadCallback(previewArea.onPluginLoad_.bind(previewArea));
+
+      // Wait for initialization to complete.
+      return Promise
+          .all([
+            nativeLayer.whenCalled('getInitialSettings'),
+            nativeLayer.whenCalled('getPrinterCapabilities')
+          ])
+          .then(function() {
+            Polymer.dom.flush();
+          });
+    });
+
+    // Tests that the enter key triggers a call to print.
+    test(assert(TestNames.EnterTriggersPrint), function() {
+      const whenPrintCalled = nativeLayer.whenCalled('print');
+      MockInteractions.keyEventOn(page, 'keydown', 'Enter', [], 'Enter');
+      return whenPrintCalled;
+    });
+
+    // Tests that the numpad enter key triggers a call to print.
+    test(assert(TestNames.NumpadEnterTriggersPrint), function() {
+      const whenPrintCalled = nativeLayer.whenCalled('print');
+      MockInteractions.keyEventOn(page, 'keydown', 'NumpadEnter', [], 'Enter');
+      return whenPrintCalled;
+    });
+
+    // Tests that the enter key triggers a call to print if an input is the
+    // source of the event.
+    test(assert(TestNames.EnterOnInputTriggersPrint), function() {
+      const whenPrintCalled = nativeLayer.whenCalled('print');
+      MockInteractions.keyEventOn(
+          page.$$('print-preview-copies-settings')
+              .$$('print-preview-number-settings-section')
+              .$$('cr-input')
+              .inputElement,
+          'keydown', 'Enter', [], 'Enter');
+      return whenPrintCalled;
+    });
+
+    // Tests that the enter key does not trigger a call to print if the event
+    // comes from a dropdown.
+    test(assert(TestNames.EnterOnDropdownDoesNotPrint), function() {
+      const whenKeyEventFired = test_util.eventToPromise('keydown', page);
+      MockInteractions.keyEventOn(
+          page.$$('print-preview-layout-settings').$$('.md-select'), 'keydown',
+          'Enter', [], 'Enter');
+      return whenKeyEventFired.then(
+          () => assertEquals(0, nativeLayer.getCallCount('print')));
+    });
+
+    // Tests that the enter key does not trigger a call to print if the event
+    // comes from a button.
+    test(assert(TestNames.EnterOnButtonDoesNotPrint), function() {
+      const whenKeyEventFired = test_util.eventToPromise('keydown', page);
+      MockInteractions.keyEventOn(
+          page.$$('print-preview-destination-settings').$$('paper-button'),
+          'keydown', 'Enter', [], 'Enter');
+      return whenKeyEventFired.then(
+          () => assertEquals(0, nativeLayer.getCallCount('print')));
+    });
+
+    // Tests that the enter key does not trigger a call to print if the event
+    // comes from a checkbox.
+    test(assert(TestNames.EnterOnCheckboxDoesNotPrint), function() {
+      const moreSettingsElement = page.$$('print-preview-more-settings');
+      moreSettingsElement.$.label.click();
+      const whenKeyEventFired = test_util.eventToPromise('keydown', page);
+      MockInteractions.keyEventOn(
+          page.$$('print-preview-other-options-settings').$$('cr-checkbox'),
+          'keydown', 'Enter', [], 'Enter');
+      return whenKeyEventFired.then(
+          () => assertEquals(0, nativeLayer.getCallCount('print')));
+    });
+
+    // Tests that escape closes the dialog only on Mac.
+    test(assert(TestNames.EscapeClosesDialogOnMacOnly), function() {
+      const promise = cr.isMac ?
+          nativeLayer.whenCalled('dialogClose') :
+          test_util.eventToPromise('keydown', page).then(() => {
+            assertEquals(0, nativeLayer.getCallCount('dialogClose'));
+          });
+      MockInteractions.keyEventOn(page, 'keydown', 'Escape', [], 'Escape');
+      return promise;
+    });
+
+    // Tests that Cmd + Period closes the dialog only on Mac
+    test(assert(TestNames.CmdPeriodClosesDialogOnMacOnly), function() {
+      const promise = cr.isMac ?
+          nativeLayer.whenCalled('dialogClose') :
+          test_util.eventToPromise('keydown', page).then(() => {
+            assertEquals(0, nativeLayer.getCallCount('dialogClose'));
+          });
+      MockInteractions.keyEventOn(
+          page, 'keydown', 'Period', ['meta'], 'Period');
+      return promise;
+    });
+
+    // Tests that Ctrl+Shift+P opens the system dialog.
+    test(assert(TestNames.CtrlShiftPOpensSystemDialog), function() {
+      let promise = null;
+      if (cr.isChromeOS) {
+        // Chrome OS doesn't have a system dialog. Just make sure the key event
+        // does not trigger a crash.
+        promise = Promise.resolve();
+      } else if (cr.isWindows) {
+        promise = nativeLayer.whenCalled('print').then((printTicket) => {
+          assertTrue(JSON.parse(printTicket).showSystemDialog);
+        });
+      } else {
+        promise = nativeLayer.whenCalled('showSystemDialog');
+      }
+      const modifiers = cr.isMac ? ['meta', 'alt'] : ['ctrl', 'shift'];
+      MockInteractions.keyEventOn(page, 'keydown', 'KeyP', modifiers, 'KeyP');
+      return promise;
+    });
+  });
+
+  return {
+    suiteName: suiteName,
+    TestNames: TestNames,
+  };
+});
diff --git a/chrome/test/data/webui/print_preview/native_layer_stub.js b/chrome/test/data/webui/print_preview/native_layer_stub.js
index 23334b5..57f9774 100644
--- a/chrome/test/data/webui/print_preview/native_layer_stub.js
+++ b/chrome/test/data/webui/print_preview/native_layer_stub.js
@@ -18,6 +18,7 @@
         'print',
         'saveAppState',
         'setupPrinter',
+        'showSystemDialog',
       ]);
 
       /**
@@ -165,6 +166,11 @@
     }
 
     /** @override */
+    showSystemDialog() {
+      this.methodCalled('showSystemDialog');
+    }
+
+    /** @override */
     recordAction() {}
 
     /** @override */
diff --git a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
index dc9ee2c..1b5fd5c 100644
--- a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
+++ b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
@@ -77,6 +77,10 @@
   this.runMochaTest(settings_sections_tests.TestNames.Color);
 });
 
+TEST_F('PrintPreviewSettingsSectionsTest', 'ColorSaveToDrive', function() {
+  this.runMochaTest(settings_sections_tests.TestNames.ColorSaveToDrive);
+});
+
 TEST_F('PrintPreviewSettingsSectionsTest', 'MediaSize', function() {
   this.runMochaTest(settings_sections_tests.TestNames.MediaSize);
 });
@@ -1053,3 +1057,65 @@
 TEST_F('PrintPreviewPrintButtonTest', 'PDFPrintVisiblePreview', function() {
   this.runMochaTest(print_button_test.TestNames.PDFPrintVisiblePreview);
 });
+
+PrintPreviewKeyEventTest = class extends NewPrintPreviewTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://print/new/app.html';
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      '../settings/test_util.js',
+      '../test_browser_proxy.js',
+      'native_layer_stub.js',
+      'plugin_stub.js',
+      'print_preview_test_utils.js',
+      'key_event_test.js',
+    ]);
+  }
+
+  /** @override */
+  get suiteName() {
+    return key_event_test.suiteName;
+  }
+};
+
+TEST_F('PrintPreviewKeyEventTest', 'EnterTriggersPrint', function() {
+  this.runMochaTest(key_event_test.TestNames.EnterTriggersPrint);
+});
+
+TEST_F('PrintPreviewKeyEventTest', 'NumpadEnterTriggersPrint', function() {
+  this.runMochaTest(key_event_test.TestNames.NumpadEnterTriggersPrint);
+});
+
+TEST_F('PrintPreviewKeyEventTest', 'EnterOnInputTriggersPrint', function() {
+  this.runMochaTest(key_event_test.TestNames.EnterOnInputTriggersPrint);
+});
+
+TEST_F('PrintPreviewKeyEventTest', 'EnterOnDropdownDoesNotPrint', function() {
+  this.runMochaTest(key_event_test.TestNames.EnterOnDropdownDoesNotPrint);
+});
+
+TEST_F('PrintPreviewKeyEventTest', 'EnterOnButtonDoesNotPrint', function() {
+  this.runMochaTest(key_event_test.TestNames.EnterOnButtonDoesNotPrint);
+});
+
+TEST_F('PrintPreviewKeyEventTest', 'EnterOnCheckboxDoesNotPrint', function() {
+  this.runMochaTest(key_event_test.TestNames.EnterOnCheckboxDoesNotPrint);
+});
+
+TEST_F('PrintPreviewKeyEventTest', 'EscapeClosesDialogOnMacOnly', function() {
+  this.runMochaTest(key_event_test.TestNames.EscapeClosesDialogOnMacOnly);
+});
+
+TEST_F(
+    'PrintPreviewKeyEventTest', 'CmdPeriodClosesDialogOnMacOnly', function() {
+      this.runMochaTest(
+          key_event_test.TestNames.CmdPeriodClosesDialogOnMacOnly);
+    });
+
+TEST_F('PrintPreviewKeyEventTest', 'CtrlShiftPOpensSystemDialog', function() {
+  this.runMochaTest(key_event_test.TestNames.CtrlShiftPOpensSystemDialog);
+});
diff --git a/chrome/test/data/webui/print_preview/settings_section_test.js b/chrome/test/data/webui/print_preview/settings_section_test.js
index 6dbe25a..fcdc0f67 100644
--- a/chrome/test/data/webui/print_preview/settings_section_test.js
+++ b/chrome/test/data/webui/print_preview/settings_section_test.js
@@ -8,6 +8,7 @@
     Copies: 'copies',
     Layout: 'layout',
     Color: 'color',
+    ColorSaveToDrive: 'color save to drive',
     MediaSize: 'media size',
     MediaSizeCustomNames: 'media size custom names',
     Margins: 'margins',
@@ -261,6 +262,27 @@
       });
     });
 
+    test(assert(TestNames.ColorSaveToDrive), function() {
+      // Check that the Save to Google Drive printer does not show the color
+      // capability, but sets the value as true by default.
+      const colorElement = page.$$('print-preview-color-settings');
+      const googleDrivePrinter = new print_preview.Destination(
+          print_preview.Destination.GooglePromotedId.DOCS,
+          print_preview.DestinationType.GOOGLE,
+          print_preview.DestinationOrigin.COOKIES,
+          print_preview.Destination.GooglePromotedId.DOCS, true /* isRecent */,
+          print_preview.DestinationConnectionStatus.ONLINE, {});
+      page.set('destination_', googleDrivePrinter);
+      const capabilities =
+          print_preview_test_utils
+              .getCddTemplate(print_preview.Destination.GooglePromotedId.DOCS)
+              .capabilities;
+      delete capabilities.printer.color;
+      page.set('destination_.capabilities', capabilities);
+      assertTrue(colorElement.hidden);
+      assertEquals(true, page.getSettingValue('color'));
+    });
+
     test(assert(TestNames.MediaSize), function() {
       const mediaSizeElement = page.$$('print-preview-media-size-settings');
 
diff --git a/chrome/test/data/webui/settings/device_page_tests.js b/chrome/test/data/webui/settings/device_page_tests.js
index 6b02622..94f802f 100644
--- a/chrome/test/data/webui/settings/device_page_tests.js
+++ b/chrome/test/data/webui/settings/device_page_tests.js
@@ -18,7 +18,7 @@
    * @implements {settings.DevicePageBrowserProxy}
    */
   function TestDevicePageBrowserProxy() {
-    this.keyboardShortcutsOverlayShown_ = 0;
+    this.keyboardShortcutViewerShown_ = 0;
     this.updatePowerStatusCalled_ = 0;
     this.requestPowerManagementSettingsCalled_ = 0;
     this.requestNoteTakingApps_ = 0;
@@ -48,8 +48,8 @@
     initializeKeyboard: function() {},
 
     /** override */
-    showKeyboardShortcutsOverlay: function() {
-      this.keyboardShortcutsOverlayShown_++;
+    showKeyboardShortcutViewer: function() {
+      this.keyboardShortcutViewerShown_++;
     },
 
     /** @override */
@@ -708,12 +708,12 @@
                 false);
             expectFalse(collapse.opened);
 
-            // Test keyboard shortcut overlay button.
-            keyboardPage.$$('#keyboardOverlay').click();
+            // Test keyboard shortcut viewer button.
+            keyboardPage.$$('#keyboardShortcutViewer').click();
             expectEquals(
                 1,
                 settings.DevicePageBrowserProxyImpl.getInstance()
-                    .keyboardShortcutsOverlayShown_);
+                    .keyboardShortcutViewerShown_);
           });
     });
 
diff --git a/chrome/test/data/webui/settings/personalization_options_test.js b/chrome/test/data/webui/settings/personalization_options_test.js
index 81aa03a..e6e5761 100644
--- a/chrome/test/data/webui/settings/personalization_options_test.js
+++ b/chrome/test/data/webui/settings/personalization_options_test.js
@@ -34,5 +34,16 @@
           8,
           testElement.root.querySelectorAll('settings-toggle-button').length);
     });
+
+    test('hide spellcheck toggle when there is no dictionary', function() {
+      testElement.unifiedConsentEnabled = true;
+      testElement.prefs = {spellcheck: {dictionaries: {value: ['en-US']}}};
+      Polymer.dom.flush();
+      assertFalse(testElement.$.spellCheckControl.hidden);
+
+      testElement.prefs = {spellcheck: {dictionaries: {value: []}}};
+      Polymer.dom.flush();
+      assertTrue(testElement.$.spellCheckControl.hidden);
+    });
   });
 });
diff --git a/chrome/test/data/webui/settings/settings_ui_browsertest.js b/chrome/test/data/webui/settings/settings_ui_browsertest.js
index 488be05..28f3848 100644
--- a/chrome/test/data/webui/settings/settings_ui_browsertest.js
+++ b/chrome/test/data/webui/settings/settings_ui_browsertest.js
@@ -69,7 +69,7 @@
       return whenDone
           .then(function() {
             const whenClosed = test_util.eventToPromise('close', drawer);
-            drawer.close();
+            drawer.cancel();
             return whenClosed;
           })
           .then(() => {
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index 4c54fff..442a652 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -51,6 +51,8 @@
     "cast_quota_permission_context.h",
     "cast_resource_dispatcher_host_delegate.cc",
     "cast_resource_dispatcher_host_delegate.h",
+    "cast_web_contents_impl.cc",
+    "cast_web_contents_impl.h",
     "cast_web_contents_manager.cc",
     "cast_web_contents_manager.h",
     "cast_web_view_default.cc",
@@ -334,6 +336,7 @@
   sources = [
     "cast_content_window.cc",
     "cast_content_window.h",
+    "cast_web_contents.h",
     "cast_web_view.cc",
     "cast_web_view.h",
   ]
diff --git a/chromecast/browser/cast_extension_host.cc b/chromecast/browser/cast_extension_host.cc
index 4846b69..b19f2e4 100644
--- a/chromecast/browser/cast_extension_host.cc
+++ b/chromecast/browser/cast_extension_host.cc
@@ -60,12 +60,6 @@
   extensions::ExtensionHost::LoadInitialURL();
 }
 
-void CastExtensionHost::LoadingStateChanged(content::WebContents* source,
-                                            bool to_different_document) {
-  extensions::ExtensionHost::LoadingStateChanged(source, to_different_document);
-  delegate_->OnLoadingStateChanged(source->IsLoading());
-}
-
 void CastExtensionHost::ActivateContents(content::WebContents* contents) {
   DCHECK_EQ(contents, host_contents());
   contents->GetRenderViewHost()->GetWidget()->Focus();
diff --git a/chromecast/browser/cast_extension_host.h b/chromecast/browser/cast_extension_host.h
index 179a366..b4cb6f3 100644
--- a/chromecast/browser/cast_extension_host.h
+++ b/chromecast/browser/cast_extension_host.h
@@ -43,8 +43,6 @@
   void ActivateContents(content::WebContents* contents) override;
   void DidStartNavigation(
       content::NavigationHandle* navigation_handle) override;
-  void LoadingStateChanged(content::WebContents* source,
-                           bool to_different_document) override;
   bool DidAddMessageToConsole(content::WebContents* source,
                               int32_t level,
                               const base::string16& message,
diff --git a/chromecast/browser/cast_web_contents.h b/chromecast/browser/cast_web_contents.h
new file mode 100644
index 0000000..71d946b
--- /dev/null
+++ b/chromecast/browser/cast_web_contents.h
@@ -0,0 +1,83 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_BROWSER_CAST_WEB_CONTENTS_H_
+#define CHROMECAST_BROWSER_CAST_WEB_CONTENTS_H_
+
+#include "url/gurl.h"
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+namespace chromecast {
+
+// Simplified WebContents wrapper class for Cast platforms.
+class CastWebContents {
+ public:
+  class Delegate {
+   public:
+    // Advertises page state for the CastWebContents.
+    // Use CastWebContents::page_state() to get the new state.
+    virtual void OnPageStateChanged(CastWebContents* cast_web_contents) = 0;
+
+    // Called when the page has stopped. e.g.: A 404 occurred when loading the
+    // page or if the render process for the main frame crashes. |error_code|
+    // will return a net::Error describing the failure, or net::OK if the page
+    // closed naturally.
+    //
+    // After this method, the page state will be one of the following:
+    // CLOSED: Page was closed as expected and the WebContents exists.
+    // DESTROYED: Page was closed due to deletion of WebContents. The
+    //     CastWebContents instance is no longer usable and should be deleted.
+    // ERROR: Page is in an error state. It should be reloaded or deleted.
+    virtual void OnPageStopped(CastWebContents* cast_web_contents,
+                               int error_code) = 0;
+
+   protected:
+    virtual ~Delegate() {}
+  };
+
+  // Page state for the main frame.
+  enum class PageState {
+    IDLE,       // Main frame has not started yet.
+    LOADING,    // Main frame is loading resources.
+    LOADED,     // Main frame is loaded, but sub-frames may still be loading.
+    CLOSED,     // Page is closed and should be cleaned up.
+    DESTROYED,  // The WebContents is destroyed and can no longer be used.
+    ERROR,      // Main frame is in an error state.
+  };
+
+  CastWebContents() = default;
+  virtual ~CastWebContents() = default;
+
+  virtual content::WebContents* web_contents() const = 0;
+  virtual PageState page_state() const = 0;
+
+  // Navigates the underlying WebContents to |url|. Delegate will be notified of
+  // page progression events via OnPageStateChanged().
+  virtual void LoadUrl(const GURL& url) = 0;
+
+  // Initiate closure of the page. This invokes the appropriate unload handlers.
+  // Eventually the delegate will be notified with OnPageStopped().
+  virtual void ClosePage() = 0;
+
+  // Stop the page immediately. This will automatically invoke
+  // Delegate::OnPageStopped(error_code), allowing the delegate to delete or
+  // reload the page without waiting for page teardown, which may be handled
+  // independently.
+  virtual void Stop(int error_code) = 0;
+
+  // Set the delegate. SetDelegate(nullptr) can be used to stop notifications.
+  virtual void SetDelegate(Delegate* delegate) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CastWebContents);
+};
+
+std::ostream& operator<<(std::ostream& os, CastWebContents::PageState state);
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BROWSER_CAST_WEB_CONTENTS_H_
diff --git a/chromecast/browser/cast_web_contents_impl.cc b/chromecast/browser/cast_web_contents_impl.cc
new file mode 100644
index 0000000..05a9a89
--- /dev/null
+++ b/chromecast/browser/cast_web_contents_impl.cc
@@ -0,0 +1,324 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/browser/cast_web_contents_impl.h"
+
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "chromecast/browser/cast_browser_process.h"
+#include "chromecast/browser/devtools/remote_debugging_server.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_frame_host.h"
+#include "net/base/net_errors.h"
+#include "url/gurl.h"
+
+namespace chromecast {
+
+CastWebContentsImpl::CastWebContentsImpl(
+    CastWebContentsImpl::Delegate* delegate,
+    content::WebContents* web_contents,
+    bool enabled_for_dev)
+    : delegate_(delegate),
+      web_contents_(web_contents),
+      page_state_(PageState::IDLE),
+      enabled_for_dev_(enabled_for_dev),
+      remote_debugging_server_(
+          shell::CastBrowserProcess::GetInstance()->remote_debugging_server()),
+      closing_(false),
+      stopped_(false),
+      stop_notified_(false),
+      notifying_(false),
+      last_error_(net::OK),
+      task_runner_(base::SequencedTaskRunnerHandle::Get()),
+      weak_factory_(this) {
+  DCHECK(delegate_);
+  DCHECK(web_contents_);
+  DCHECK(web_contents_->GetController().IsInitialNavigation());
+  DCHECK(!web_contents_->IsLoading());
+  content::WebContentsObserver::Observe(web_contents_);
+  if (enabled_for_dev_) {
+    LOG(INFO) << "Enabling dev console for CastWebContentsImpl";
+    remote_debugging_server_->EnableWebContentsForDebugging(web_contents_);
+  }
+  delegate_->OnPageStateChanged(this);
+}
+
+CastWebContentsImpl::~CastWebContentsImpl() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DisableDebugging();
+}
+
+content::WebContents* CastWebContentsImpl::web_contents() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return web_contents_;
+}
+
+CastWebContents::PageState CastWebContentsImpl::page_state() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return page_state_;
+}
+
+void CastWebContentsImpl::LoadUrl(const GURL& url) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!web_contents_) {
+    LOG(ERROR) << "Cannot load URL for deleted WebContents";
+    return;
+  }
+  if (closing_) {
+    LOG(ERROR) << "Cannot load URL for WebContents while closing";
+    return;
+  }
+  closing_ = false;
+  stopped_ = false;
+  stop_notified_ = false;
+  last_error_ = net::OK;
+  start_loading_ticks_ = base::TimeTicks::Now();
+  LOG(INFO) << "Load url: " << url.possibly_invalid_spec();
+  TracePageLoadBegin(url);
+  web_contents_->GetController().LoadURL(url, content::Referrer(),
+                                         ui::PAGE_TRANSITION_TYPED, "");
+  UpdatePageState();
+  DCHECK_EQ(PageState::LOADING, page_state_);
+}
+
+void CastWebContentsImpl::ClosePage() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!web_contents_ || closing_)
+    return;
+  closing_ = true;
+  web_contents_->DispatchBeforeUnload(false /* auto_cancel */);
+  web_contents_->ClosePage();
+  // If the WebContents doesn't close within the specified timeout, then signal
+  // the page closure anyway so that the Delegate can delete the WebContents and
+  // stop the page itself.
+  task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&CastWebContentsImpl::OnClosePageTimeout,
+                     weak_factory_.GetWeakPtr()),
+      base::TimeDelta::FromMilliseconds(1000));
+}
+
+void CastWebContentsImpl::Stop(int error_code) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (stopped_) {
+    UpdatePageState();
+    return;
+  }
+  last_error_ = error_code;
+  closing_ = false;
+  stopped_ = true;
+  UpdatePageState();
+  DCHECK_NE(PageState::IDLE, page_state_);
+  DCHECK_NE(PageState::LOADING, page_state_);
+  DCHECK_NE(PageState::LOADED, page_state_);
+}
+
+void CastWebContentsImpl::SetDelegate(CastWebContents::Delegate* delegate) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  delegate_ = delegate;
+}
+
+void CastWebContentsImpl::OnClosePageTimeout() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!closing_ || stopped_) {
+    return;
+  }
+  closing_ = false;
+  Stop(net::OK);
+}
+
+void CastWebContentsImpl::RenderProcessGone(base::TerminationStatus status) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  LOG(INFO) << "Render process for main frame exited unexpectedly.";
+  Stop(net::ERR_UNEXPECTED);
+}
+
+void CastWebContentsImpl::DidFinishNavigation(
+    content::NavigationHandle* navigation_handle) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // If the navigation was not committed, it means either the page was a
+  // download or error 204/205, or the navigation never left the previous
+  // URL. Ignore these navigations.
+  if (!navigation_handle->HasCommitted()) {
+    LOG(WARNING) << "Navigation did not commit: url="
+                 << navigation_handle->GetURL();
+    return;
+  }
+
+  if (!navigation_handle->IsErrorPage())
+    return;
+
+  net::Error error_code = navigation_handle->GetNetErrorCode();
+
+  // If we abort errors in an iframe, it can create a really confusing
+  // and fragile user experience.  Rather than create a list of errors
+  // that are most likely to occur, we ignore all of them for now.
+  if (!navigation_handle->IsInMainFrame()) {
+    LOG(ERROR) << "Got error on sub-iframe: url=" << navigation_handle->GetURL()
+               << ", error=" << error_code
+               << ", description=" << net::ErrorToShortString(error_code);
+    return;
+  }
+
+  LOG(ERROR) << "Got error on navigation: url=" << navigation_handle->GetURL()
+             << ", error_code=" << error_code
+             << ", description= " << net::ErrorToShortString(error_code);
+
+  Stop(error_code);
+  DCHECK_EQ(page_state_, PageState::ERROR);
+}
+
+void CastWebContentsImpl::DidStartLoading() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  UpdatePageState();
+  DCHECK_EQ(page_state_, PageState::LOADING);
+}
+
+void CastWebContentsImpl::DidStopLoading() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  int http_status_code = 0;
+  GURL final_url;
+  content::NavigationEntry* nav_entry =
+      web_contents()->GetController().GetVisibleEntry();
+  if (nav_entry) {
+    http_status_code = nav_entry->GetHttpStatusCode();
+    final_url = nav_entry->GetVirtualURL();
+  }
+  TracePageLoadEnd(final_url);
+
+  if (http_status_code != 0 && http_status_code / 100 != 2) {
+    // We successfully loaded an error HTML page.
+    LOG(INFO) << "Failed loading page for: " << final_url
+              << "; http status code: " << http_status_code;
+    Stop(net::ERR_FAILED);
+    DCHECK_EQ(page_state_, PageState::ERROR);
+    return;
+  }
+  // Main frame finished loading.
+  base::TimeDelta load_time = base::TimeTicks::Now() - start_loading_ticks_;
+  LOG(INFO) << "Finished loading page after " << load_time.InMilliseconds()
+            << " ms, url=" << final_url;
+  PageState previous = page_state_;
+  UpdatePageState();
+  DCHECK((previous == PageState::ERROR && page_state_ == PageState::ERROR) ||
+         page_state_ == PageState::LOADED)
+      << "Page is in unexpected state: " << page_state_;
+}
+
+void CastWebContentsImpl::UpdatePageState() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  PageState last_state = page_state_;
+  if (!web_contents_) {
+    DCHECK(stopped_);
+    page_state_ = PageState::DESTROYED;
+  } else if (!stopped_) {
+    if (web_contents_->IsLoading()) {
+      page_state_ = PageState::LOADING;
+    } else {
+      page_state_ = PageState::LOADED;
+    }
+  } else if (stopped_) {
+    if (last_error_ != net::OK) {
+      page_state_ = PageState::ERROR;
+    } else {
+      page_state_ = PageState::CLOSED;
+    }
+  }
+
+  if (!delegate_)
+    return;
+  // Don't notify if the page state didn't change.
+  if (last_state == page_state_)
+    return;
+  // Don't recursively notify the delegate.
+  if (notifying_)
+    return;
+  notifying_ = true;
+  if (stopped_ && !stop_notified_) {
+    stop_notified_ = true;
+    delegate_->OnPageStopped(this, last_error_);
+  } else {
+    delegate_->OnPageStateChanged(this);
+  }
+  notifying_ = false;
+}
+
+void CastWebContentsImpl::DidFailLoad(
+    content::RenderFrameHost* render_frame_host,
+    const GURL& validated_url,
+    int error_code,
+    const base::string16& error_description) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Only report an error if we are the main frame.  See b/8433611.
+  if (render_frame_host->GetParent()) {
+    LOG(ERROR) << "Got error on sub-iframe: url=" << validated_url.spec()
+               << ", error=" << error_code;
+    return;
+  }
+  if (error_code == net::ERR_ABORTED) {
+    // ERR_ABORTED means download was aborted by the app, typically this happens
+    // when flinging URL for direct playback, the initial URLRequest gets
+    // cancelled/aborted and then the same URL is requested via the buffered
+    // data source for media::Pipeline playback.
+    LOG(INFO) << "Load canceled: url=" << validated_url.spec();
+    return;
+  }
+
+  LOG(ERROR) << "Got error on load: url=" << validated_url.spec()
+             << ", error_code=" << error_code;
+
+  TracePageLoadEnd(validated_url);
+  Stop(error_code);
+  DCHECK_EQ(PageState::ERROR, page_state_);
+}
+
+void CastWebContentsImpl::WebContentsDestroyed() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  closing_ = false;
+  DisableDebugging();
+  content::WebContentsObserver::Observe(nullptr);
+  web_contents_ = nullptr;
+  Stop(net::OK);
+  DCHECK_EQ(PageState::DESTROYED, page_state_);
+}
+
+void CastWebContentsImpl::TracePageLoadBegin(const GURL& url) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  TRACE_EVENT_ASYNC_BEGIN1("browser,navigation", "CastWebContentsImpl Launch",
+                           this, "URL", url.possibly_invalid_spec());
+}
+
+void CastWebContentsImpl::TracePageLoadEnd(const GURL& url) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  TRACE_EVENT_ASYNC_END1("browser,navigation", "CastWebContentsImpl Launch",
+                         this, "URL", url.possibly_invalid_spec());
+}
+
+void CastWebContentsImpl::DisableDebugging() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!enabled_for_dev_ || !web_contents_)
+    return;
+  LOG(INFO) << "Disabling dev console for " << web_contents_->GetVisibleURL();
+  remote_debugging_server_->DisableWebContentsForDebugging(web_contents_);
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         CastWebContentsImpl::PageState state) {
+#define CASE(state)                           \
+  case CastWebContentsImpl::PageState::state: \
+    os << #state;                             \
+    return os;
+
+  switch (state) {
+    CASE(IDLE);
+    CASE(LOADING);
+    CASE(LOADED);
+    CASE(CLOSED);
+    CASE(DESTROYED);
+    CASE(ERROR);
+  }
+#undef CASE
+}
+
+}  // namespace chromecast
diff --git a/chromecast/browser/cast_web_contents_impl.h b/chromecast/browser/cast_web_contents_impl.h
new file mode 100644
index 0000000..8d5c6a44
--- /dev/null
+++ b/chromecast/browser/cast_web_contents_impl.h
@@ -0,0 +1,84 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_BROWSER_CAST_WEB_CONTENTS_IMPL_H_
+#define CHROMECAST_BROWSER_CAST_WEB_CONTENTS_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "base/time/time.h"
+#include "chromecast/browser/cast_web_contents.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+
+namespace chromecast {
+
+namespace shell {
+class RemoteDebuggingServer;
+}  // namespace shell
+
+class CastWebContentsImpl : public CastWebContents,
+                            public content::WebContentsObserver {
+ public:
+  CastWebContentsImpl(Delegate* delegate,
+                      content::WebContents* web_contents,
+                      bool enabled_for_dev);
+  ~CastWebContentsImpl() override;
+
+  content::WebContents* web_contents() const override;
+  PageState page_state() const override;
+
+  // CastWebContents implementation:
+  void LoadUrl(const GURL& url) override;
+  void ClosePage() override;
+  void Stop(int error_code) override;
+  void SetDelegate(Delegate* delegate) override;
+
+ private:
+  // WebContentsObserver implementation:
+  void RenderProcessGone(base::TerminationStatus status) override;
+  void DidFinishNavigation(
+      content::NavigationHandle* navigation_handle) override;
+  void DidStartLoading() override;
+  void DidStopLoading() override;
+  void DidFailLoad(content::RenderFrameHost* render_frame_host,
+                   const GURL& validated_url,
+                   int error_code,
+                   const base::string16& error_description) override;
+  void WebContentsDestroyed() override;
+
+  void UpdatePageState();
+  void TracePageLoadBegin(const GURL& url);
+  void TracePageLoadEnd(const GURL& url);
+  void DisableDebugging();
+  void OnClosePageTimeout();
+
+  Delegate* delegate_;
+  content::WebContents* web_contents_;
+  PageState page_state_;
+  const bool enabled_for_dev_;
+  shell::RemoteDebuggingServer* const remote_debugging_server_;
+
+  base::TimeTicks start_loading_ticks_;
+  bool closing_;
+  bool stopped_;
+  bool stop_notified_;
+  bool notifying_;
+  int last_error_;
+
+  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
+  SEQUENCE_CHECKER(sequence_checker_);
+  base::WeakPtrFactory<CastWebContentsImpl> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CastWebContentsImpl);
+};
+
+std::ostream& operator<<(std::ostream& os,
+                         CastWebContentsImpl::PageState state);
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BROWSER_CAST_WEB_CONTENTS_IMPL_H_
diff --git a/chromecast/browser/cast_web_view.h b/chromecast/browser/cast_web_view.h
index c79161e4..fc2b0c3 100644
--- a/chromecast/browser/cast_web_view.h
+++ b/chromecast/browser/cast_web_view.h
@@ -14,6 +14,7 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "chromecast/browser/cast_content_window.h"
+#include "chromecast/browser/cast_web_contents.h"
 #include "chromecast/graphics/cast_window_manager.h"
 #include "content/public/browser/bluetooth_chooser.h"
 #include "content/public/browser/web_contents.h"
@@ -28,17 +29,9 @@
 // A simplified interface for loading and displaying WebContents in cast_shell.
 class CastWebView {
  public:
-  class Delegate : public shell::CastContentWindow::Delegate {
+  class Delegate : public CastWebContents::Delegate,
+                   public shell::CastContentWindow::Delegate {
    public:
-    // Called when the page has stopped. ie: A 404 occured when loading the page
-    // or if the render process crashes. |error_code| will return a net::Error
-    // describing the failure, or net::OK if the page closed naturally.
-    virtual void OnPageStopped(int error_code) = 0;
-
-    // Called during WebContentsDelegate::LoadingStateChanged.
-    // |loading| indicates if web_contents_ IsLoading or not.
-    virtual void OnLoadingStateChanged(bool loading) = 0;
-
     // Called when there is console log output from web_contents.
     // Returning true indicates that the delegate handled the message.
     // If false is returned the default logging mechanism will be used.
diff --git a/chromecast/browser/cast_web_view_default.cc b/chromecast/browser/cast_web_view_default.cc
index a6186d4a..93670921 100644
--- a/chromecast/browser/cast_web_view_default.cc
+++ b/chromecast/browser/cast_web_view_default.cc
@@ -13,7 +13,6 @@
 #include "chromecast/base/metrics/cast_metrics_helper.h"
 #include "chromecast/browser/cast_browser_process.h"
 #include "chromecast/browser/cast_web_contents_manager.h"
-#include "chromecast/browser/devtools/remote_debugging_server.h"
 #include "chromecast/chromecast_buildflags.h"
 #include "chromecast/public/cast_media_shlib.h"
 #include "content/public/browser/media_capture_devices.h"
@@ -60,14 +59,14 @@
     scoped_refptr<content::SiteInstance> site_instance)
     : web_contents_manager_(web_contents_manager),
       browser_context_(browser_context),
-      remote_debugging_server_(
-          shell::CastBrowserProcess::GetInstance()->remote_debugging_server()),
       site_instance_(std::move(site_instance)),
       delegate_(params.delegate),
       transparent_(params.transparent),
       allow_media_access_(params.allow_media_access),
-      enabled_for_dev_(params.enabled_for_dev),
       web_contents_(CreateWebContents(browser_context_, site_instance_)),
+      cast_web_contents_(delegate_,
+                         web_contents_.get(),
+                         params.enabled_for_dev),
       window_(shell::CastContentWindow::Create(params.window_params)),
       did_start_navigation_(false) {
   DCHECK(delegate_);
@@ -97,13 +96,6 @@
   content::MediaSession::Get(web_contents_.get())
       ->SetDuckingVolumeMultiplier(kDuckingMultiplier);
 #endif
-
-  // If this CastWebView is enabled for development, start the remote debugger.
-  if (enabled_for_dev_) {
-    LOG(INFO) << "Enabling dev console for " << web_contents_->GetVisibleURL();
-    remote_debugging_server_->EnableWebContentsForDebugging(
-        web_contents_.get());
-  }
 }
 
 CastWebViewDefault::~CastWebViewDefault() {}
@@ -124,19 +116,22 @@
 void CastWebViewDefault::ClosePage(const base::TimeDelta& shutdown_delay) {
   shutdown_delay_ = shutdown_delay;
   content::WebContentsObserver::Observe(nullptr);
-  web_contents_->DispatchBeforeUnload(false /* auto_cancel */);
-  web_contents_->ClosePage();
+  cast_web_contents_.ClosePage();
 }
 
 void CastWebViewDefault::CloseContents(content::WebContents* source) {
   DCHECK_EQ(source, web_contents_.get());
   window_.reset();  // Window destructor requires live web_contents on Android.
-  // We need to delay the deletion of web_contents_ to give (and guarantee) the
-  // renderer enough time to finish 'onunload' handler (but we don't want to
-  // wait any longer than that to delay the starting of next app).
-  web_contents_manager_->DelayWebContentsDeletion(std::move(web_contents_),
-                                                  shutdown_delay_);
-  delegate_->OnPageStopped(net::OK);
+  if (!shutdown_delay_.is_zero()) {
+    // We need to delay the deletion of web_contents_ to give (and guarantee)
+    // the renderer enough time to finish 'onunload' handler (but we don't want
+    // to wait any longer than that to delay the starting of next app).
+    web_contents_manager_->DelayWebContentsDeletion(std::move(web_contents_),
+                                                    shutdown_delay_);
+  }
+  // This will signal to the owner that |web_contents_| is no longer in use,
+  // permitting the owner to tear down.
+  cast_web_contents_.Stop(net::OK);
 }
 
 void CastWebViewDefault::InitializeWindow(CastWindowManager* window_manager,
@@ -177,11 +172,6 @@
   return source;
 }
 
-void CastWebViewDefault::LoadingStateChanged(content::WebContents* source,
-                                             bool to_different_document) {
-  delegate_->OnLoadingStateChanged(source->IsLoading());
-}
-
 void CastWebViewDefault::ActivateContents(content::WebContents* contents) {
   DCHECK_EQ(contents, web_contents_.get());
   contents->GetRenderViewHost()->GetWidget()->Focus();
@@ -282,11 +272,6 @@
              : WebContentsDelegate::RunBluetoothChooser(frame, event_handler);
 }
 
-void CastWebViewDefault::RenderProcessGone(base::TerminationStatus status) {
-  LOG(INFO) << "APP_ERROR_CHILD_PROCESS_CRASHED";
-  delegate_->OnPageStopped(net::ERR_UNEXPECTED);
-}
-
 void CastWebViewDefault::RenderViewCreated(
     content::RenderViewHost* render_view_host) {
   content::RenderWidgetHostView* view =
@@ -299,59 +284,6 @@
   }
 }
 
-void CastWebViewDefault::DidFinishNavigation(
-    content::NavigationHandle* navigation_handle) {
-  // If the navigation was not committed, it means either the page was a
-  // download or error 204/205, or the navigation never left the previous
-  // URL. Ignore these navigations.
-  if (!navigation_handle->HasCommitted()) {
-    LOG(WARNING) << "Navigation did not commit: url="
-                 << navigation_handle->GetURL();
-    return;
-  }
-
-  net::Error error_code = navigation_handle->GetNetErrorCode();
-  if (!navigation_handle->IsErrorPage())
-    return;
-
-  // If we abort errors in an iframe, it can create a really confusing
-  // and fragile user experience.  Rather than create a list of errors
-  // that are most likely to occur, we ignore all of them for now.
-  if (!navigation_handle->IsInMainFrame()) {
-    LOG(ERROR) << "Got error on sub-iframe: url=" << navigation_handle->GetURL()
-               << ", error=" << error_code
-               << ", description=" << net::ErrorToShortString(error_code);
-    return;
-  }
-
-  LOG(ERROR) << "Got error on navigation: url=" << navigation_handle->GetURL()
-             << ", error_code=" << error_code
-             << ", description= " << net::ErrorToShortString(error_code);
-  delegate_->OnPageStopped(error_code);
-}
-
-void CastWebViewDefault::DidFailLoad(
-    content::RenderFrameHost* render_frame_host,
-    const GURL& validated_url,
-    int error_code,
-    const base::string16& error_description) {
-  // Only report an error if we are the main frame.  See b/8433611.
-  if (render_frame_host->GetParent()) {
-    LOG(ERROR) << "Got error on sub-iframe: url=" << validated_url.spec()
-               << ", error=" << error_code;
-  } else if (error_code == net::ERR_ABORTED) {
-    // ERR_ABORTED means download was aborted by the app, typically this happens
-    // when flinging URL for direct playback, the initial URLRequest gets
-    // cancelled/aborted and then the same URL is requested via the buffered
-    // data source for media::Pipeline playback.
-    LOG(INFO) << "Load canceled: url=" << validated_url.spec();
-  } else {
-    LOG(ERROR) << "Got error on load: url=" << validated_url.spec()
-               << ", error_code=" << error_code;
-    delegate_->OnPageStopped(error_code);
-  }
-}
-
 void CastWebViewDefault::DidFirstVisuallyNonEmptyPaint() {
   metrics::CastMetricsHelper::GetInstance()->LogTimeToFirstPaint();
 }
diff --git a/chromecast/browser/cast_web_view_default.h b/chromecast/browser/cast_web_view_default.h
index 226de65..5cced9f 100644
--- a/chromecast/browser/cast_web_view_default.h
+++ b/chromecast/browser/cast_web_view_default.h
@@ -12,6 +12,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chromecast/browser/cast_content_window.h"
+#include "chromecast/browser/cast_web_contents_impl.h"
 #include "chromecast/browser/cast_web_view.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
@@ -25,10 +26,6 @@
 
 namespace chromecast {
 
-namespace shell {
-class RemoteDebuggingServer;
-}
-
 class CastWebContentsManager;
 class CastWindowManager;
 
@@ -58,14 +55,7 @@
 
  private:
   // WebContentsObserver implementation:
-  void RenderProcessGone(base::TerminationStatus status) override;
   void RenderViewCreated(content::RenderViewHost* render_view_host) override;
-  void DidFinishNavigation(
-      content::NavigationHandle* navigation_handle) override;
-  void DidFailLoad(content::RenderFrameHost* render_frame_host,
-                   const GURL& validated_url,
-                   int error_code,
-                   const base::string16& error_description) override;
   void DidFirstVisuallyNonEmptyPaint() override;
   void DidStartNavigation(
       content::NavigationHandle* navigation_handle) override;
@@ -81,8 +71,6 @@
       content::WebContents* source,
       const content::OpenURLParams& params) override;
   void CloseContents(content::WebContents* source) override;
-  void LoadingStateChanged(content::WebContents* source,
-                           bool to_different_document) override;
   void ActivateContents(content::WebContents* contents) override;
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
@@ -102,15 +90,14 @@
 
   CastWebContentsManager* const web_contents_manager_;
   content::BrowserContext* const browser_context_;
-  shell::RemoteDebuggingServer* remote_debugging_server_;
   const scoped_refptr<content::SiteInstance> site_instance_;
 
   Delegate* const delegate_;
   const bool transparent_;
   const bool allow_media_access_;
-  const bool enabled_for_dev_;
 
   std::unique_ptr<content::WebContents> web_contents_;
+  CastWebContentsImpl cast_web_contents_;
   std::unique_ptr<shell::CastContentWindow> window_;
   bool did_start_navigation_;
   base::TimeDelta shutdown_delay_;
diff --git a/chromecast/browser/cast_web_view_extension.cc b/chromecast/browser/cast_web_view_extension.cc
index 78f51ba..450144f 100644
--- a/chromecast/browser/cast_web_view_extension.cc
+++ b/chromecast/browser/cast_web_view_extension.cc
@@ -30,16 +30,12 @@
           initial_url,
           site_instance.get(),
           extensions::VIEW_TYPE_EXTENSION_POPUP)),
-      remote_debugging_server_(
-          shell::CastBrowserProcess::GetInstance()->remote_debugging_server()) {
+      cast_web_contents_(delegate_,
+                         extension_host_->host_contents(),
+                         params.enabled_for_dev) {
   DCHECK(delegate_);
   content::WebContentsObserver::Observe(web_contents());
   web_contents()->GetNativeView()->SetName(params.activity_id);
-  // If this CastWebView is enabled for development, start the remote debugger.
-  if (params.enabled_for_dev) {
-    LOG(INFO) << "Enabling dev console for " << web_contents()->GetVisibleURL();
-    remote_debugging_server_->EnableWebContentsForDebugging(web_contents());
-  }
 }
 
 CastWebViewExtension::~CastWebViewExtension() {
@@ -58,6 +54,7 @@
   extension_host_->CreateRenderViewSoon();
 }
 
+// Extension web view cannot be closed deliberately.
 void CastWebViewExtension::ClosePage(const base::TimeDelta& shutdown_delay) {}
 
 void CastWebViewExtension::InitializeWindow(
@@ -79,10 +76,6 @@
   window_->RevokeScreenAccess();
 }
 
-void CastWebViewExtension::WebContentsDestroyed() {
-  delegate_->OnPageStopped(net::OK);
-}
-
 void CastWebViewExtension::RenderViewCreated(
     content::RenderViewHost* render_view_host) {
   content::RenderWidgetHostView* view =
@@ -92,54 +85,4 @@
   }
 }
 
-void CastWebViewExtension::DidFinishNavigation(
-    content::NavigationHandle* navigation_handle) {
-  // If the navigation was not committed, it means either the page was a
-  // download or error 204/205, or the navigation never left the previous
-  // URL. Basically, this isn't a problem since we stayed at the existing URL.
-  if (!navigation_handle->HasCommitted())
-    return;
-
-  // The navigation committed. If we navigated to an error page then
-  // this is a bad state, and should be notified. Otherwise the navigation
-  // completed as intended.
-  if (!navigation_handle->IsErrorPage())
-    return;
-
-  net::Error error_code = navigation_handle->GetNetErrorCode();
-  // Ignore sub-frame errors.
-  if (!navigation_handle->IsInMainFrame()) {
-    LOG(ERROR) << "Got error on sub-frame: url=" << navigation_handle->GetURL()
-               << ", error=" << error_code
-               << ", description=" << net::ErrorToShortString(error_code);
-    return;
-  }
-
-  LOG(ERROR) << "Got error on navigation: url=" << navigation_handle->GetURL()
-             << ", error_code=" << error_code
-             << ", description= " << net::ErrorToShortString(error_code);
-  delegate_->OnPageStopped(error_code);
-}
-
-void CastWebViewExtension::DidFailLoad(
-    content::RenderFrameHost* render_frame_host,
-    const GURL& validated_url,
-    int error_code,
-    const base::string16& error_description) {
-  // Only report an error if we are the main frame.
-  if (render_frame_host->GetParent()) {
-    LOG(ERROR) << "Got error on sub-frame: url=" << validated_url.spec()
-               << ", error=" << error_code << ": " << error_description;
-    return;
-  }
-  LOG(ERROR) << "Got error on load: url=" << validated_url.spec()
-             << ", error_code=" << error_code << ": " << error_description;
-  ;
-  delegate_->OnPageStopped(error_code);
-}
-
-void CastWebViewExtension::RenderProcessGone(base::TerminationStatus status) {
-  delegate_->OnPageStopped(net::ERR_UNEXPECTED);
-}
-
 }  // namespace chromecast
diff --git a/chromecast/browser/cast_web_view_extension.h b/chromecast/browser/cast_web_view_extension.h
index d22ee7c..f29fe14 100644
--- a/chromecast/browser/cast_web_view_extension.h
+++ b/chromecast/browser/cast_web_view_extension.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "chromecast/browser/cast_web_contents_impl.h"
 #include "chromecast/browser/cast_web_view.h"
 #include "content/public/browser/media_capture_devices.h"
 #include "content/public/browser/navigation_handle.h"
@@ -24,14 +25,12 @@
 #include "url/gurl.h"
 
 namespace chromecast {
-namespace shell {
-class RemoteDebuggingServer;
-}
 
 class CastExtensionHost;
 
 // A simplified interface for loading and displaying WebContents in cast_shell.
-class CastWebViewExtension : public CastWebView, content::WebContentsObserver {
+class CastWebViewExtension : public CastWebView,
+                             public content::WebContentsObserver {
  public:
   // |delegate| and |browser_context| should outlive the lifetime of this
   // object.
@@ -58,20 +57,12 @@
 
  private:
   // WebContentsObserver implementation:
-  void WebContentsDestroyed() override;
   void RenderViewCreated(content::RenderViewHost* render_view_host) override;
-  void DidFinishNavigation(
-      content::NavigationHandle* navigation_handle) override;
-  void DidFailLoad(content::RenderFrameHost* render_frame_host,
-                   const GURL& validated_url,
-                   int error_code,
-                   const base::string16& error_description) override;
-  void RenderProcessGone(base::TerminationStatus status) override;
 
   Delegate* const delegate_;
   const std::unique_ptr<shell::CastContentWindow> window_;
   const std::unique_ptr<CastExtensionHost> extension_host_;
-  shell::RemoteDebuggingServer* remote_debugging_server_;
+  CastWebContentsImpl cast_web_contents_;
   scoped_refptr<content::SiteInstance> site_instance_;
 
   DISALLOW_COPY_AND_ASSIGN(CastWebViewExtension);
diff --git a/chromecast/browser/service/cast_service_simple.cc b/chromecast/browser/service/cast_service_simple.cc
index 863e953..8be27c5e 100644
--- a/chromecast/browser/service/cast_service_simple.cc
+++ b/chromecast/browser/service/cast_service_simple.cc
@@ -90,9 +90,11 @@
   cast_web_view_.reset();
 }
 
-void CastServiceSimple::OnPageStopped(int error_code) {}
+void CastServiceSimple::OnPageStopped(CastWebContents* cast_web_contents,
+                                      int error_code) {}
 
-void CastServiceSimple::OnLoadingStateChanged(bool loading) {}
+void CastServiceSimple::OnPageStateChanged(CastWebContents* cast_web_contents) {
+}
 
 void CastServiceSimple::OnWindowDestroyed() {}
 
diff --git a/chromecast/browser/service/cast_service_simple.h b/chromecast/browser/service/cast_service_simple.h
index c4b0635..9729dd4 100644
--- a/chromecast/browser/service/cast_service_simple.h
+++ b/chromecast/browser/service/cast_service_simple.h
@@ -36,8 +36,9 @@
   void StopInternal() override;
 
   // CastWebView::Delegate implementation:
-  void OnPageStopped(int error_code) override;
-  void OnLoadingStateChanged(bool loading) override;
+  void OnPageStopped(CastWebContents* cast_web_contents,
+                     int error_code) override;
+  void OnPageStateChanged(CastWebContents* cast_web_contents) override;
   bool OnAddMessageToConsoleReceived(content::WebContents* source,
                                      int32_t level,
                                      const base::string16& message,
diff --git a/chromecast/browser/test/cast_browser_test.cc b/chromecast/browser/test/cast_browser_test.cc
index 72b87cf6..8bd2989 100644
--- a/chromecast/browser/test/cast_browser_test.cc
+++ b/chromecast/browser/test/cast_browser_test.cc
@@ -83,9 +83,10 @@
   return web_contents;
 }
 
-void CastBrowserTest::OnPageStopped(int reason) {}
+void CastBrowserTest::OnPageStateChanged(CastWebContents* cast_web_contents) {}
 
-void CastBrowserTest::OnLoadingStateChanged(bool loading) {}
+void CastBrowserTest::OnPageStopped(CastWebContents* cast_web_contents,
+                                    int error_code) {}
 
 void CastBrowserTest::OnWindowDestroyed() {}
 
diff --git a/chromecast/browser/test/cast_browser_test.h b/chromecast/browser/test/cast_browser_test.h
index 85848c5b..bfb9e018 100644
--- a/chromecast/browser/test/cast_browser_test.h
+++ b/chromecast/browser/test/cast_browser_test.h
@@ -44,8 +44,9 @@
 
  private:
   // CastWebView::Delegate implementation:
-  void OnPageStopped(int error_code) override;
-  void OnLoadingStateChanged(bool loading) override;
+  void OnPageStateChanged(CastWebContents* cast_web_contents) override;
+  void OnPageStopped(CastWebContents* cast_web_contents,
+                     int error_code) override;
   void OnWindowDestroyed() override;
   void OnKeyEvent(const ui::KeyEvent& key_event) override;
   bool OnAddMessageToConsoleReceived(content::WebContents* source,
diff --git a/chromecast/media/audio/fake_external_audio_pipeline.cc b/chromecast/media/audio/fake_external_audio_pipeline.cc
index a15bad7..e19c9af 100644
--- a/chromecast/media/audio/fake_external_audio_pipeline.cc
+++ b/chromecast/media/audio/fake_external_audio_pipeline.cc
@@ -92,10 +92,32 @@
   DISALLOW_COPY_AND_ASSIGN(TestLoopBack);
 };
 
+class TestMediaMetadata {
+ public:
+  TestMediaMetadata() = default;
+
+  // Called from library.
+  void AddExternalMediaMetadataChangeObserver(
+      ExternalAudioPipelineShlib::ExternalMediaMetadataChangeObserver*
+          observer) {
+    media_metadata_change_observer_ = observer;
+  }
+  void RemoveExternalMediaMetadataChangeObserver(
+      ExternalAudioPipelineShlib::ExternalMediaMetadataChangeObserver*) {
+    media_metadata_change_observer_ = nullptr;
+  }
+
+ protected:
+  ExternalAudioPipelineShlib::ExternalMediaMetadataChangeObserver*
+      media_metadata_change_observer_ = nullptr;
+  DISALLOW_COPY_AND_ASSIGN(TestMediaMetadata);
+};
+
 // Final class includes library implementation for testing media volume/mute
 // + loopback and FakeExternalAudioPipelineSupport implementation.
 class TestMedia : public TestMediaVolumeMute,
                   public TestLoopBack,
+                  public TestMediaMetadata,
                   public testing::FakeExternalAudioPipelineSupport {
  public:
   TestMedia() = default;
@@ -125,6 +147,14 @@
     volume_change_request_observer_->OnMuteChangeRequest(muted);
   }
 
+  void UpdateExternalMediaMetadata(
+      const ExternalAudioPipelineShlib::ExternalMediaMetadata& metadata)
+      override {
+    if (media_metadata_change_observer_) {
+      media_metadata_change_observer_->OnExternalMediaMetadataChanged(metadata);
+    }
+  }
+
  private:
   bool supported_ = false;
 
@@ -222,10 +252,14 @@
 }
 
 void ExternalAudioPipelineShlib::AddExternalMediaMetadataChangeObserver(
-    ExternalMediaMetadataChangeObserver* observer) {}
+    ExternalMediaMetadataChangeObserver* observer) {
+  GetTestMedia()->AddExternalMediaMetadataChangeObserver(observer);
+}
 
 void ExternalAudioPipelineShlib::RemoveExternalMediaMetadataChangeObserver(
-    ExternalMediaMetadataChangeObserver* observer) {}
+    ExternalMediaMetadataChangeObserver* observer) {
+  GetTestMedia()->RemoveExternalMediaMetadataChangeObserver(observer);
+}
 
 std::unique_ptr<MixerOutputStream>
 ExternalAudioPipelineShlib::CreateMixerOutputStream() {
diff --git a/chromecast/media/audio/fake_external_audio_pipeline_support.h b/chromecast/media/audio/fake_external_audio_pipeline_support.h
index 98bd96c..06b758c2 100644
--- a/chromecast/media/audio/fake_external_audio_pipeline_support.h
+++ b/chromecast/media/audio/fake_external_audio_pipeline_support.h
@@ -5,6 +5,8 @@
 #ifndef CHROMECAST_MEDIA_AUDIO_FAKE_EXTERNAL_AUDIO_PIPELINE_SUPPORT_H
 #define CHROMECAST_MEDIA_AUDIO_FAKE_EXTERNAL_AUDIO_PIPELINE_SUPPORT_H
 
+#include "chromecast/public/media/external_audio_pipeline_shlib.h"
+
 namespace chromecast {
 namespace media {
 namespace testing {
@@ -21,6 +23,8 @@
   // Request for change values.
   virtual void OnVolumeChangeRequest(float level) = 0;
   virtual void OnMuteChangeRequest(bool muted) = 0;
+  virtual void UpdateExternalMediaMetadata(
+      const ExternalAudioPipelineShlib::ExternalMediaMetadata& metadata) = 0;
 
   virtual ~FakeExternalAudioPipelineSupport() {}
 };
diff --git a/chromeos/assistant/OWNERS b/chromeos/assistant/OWNERS
index 10f82ddb..c47d135 100644
--- a/chromeos/assistant/OWNERS
+++ b/chromeos/assistant/OWNERS
@@ -1,4 +1,3 @@
-muyuanli@chromium.org
 updowndota@chromium.org
 wutao@chromium.org
 xiaohuic@chromium.org
diff --git a/chromeos/chromeos_features.cc b/chromeos/chromeos_features.cc
index ec71002..be49881 100644
--- a/chromeos/chromeos_features.cc
+++ b/chromeos/chromeos_features.cc
@@ -23,6 +23,11 @@
 // If enabled, DriveFS will be used for Drive sync.
 const base::Feature kDriveFs{"DriveFS", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// If enabled, MyFiles will be a root/volume and user can create other
+// sub-folders and files in addition to the Downloads folder inside MyFiles.
+const base::Feature kMyFilesVolume{"MyFilesVolume",
+                                   base::FEATURE_DISABLED_BY_DEFAULT};
+
 // If enabled, the Chrome OS Settings UI will include a menu for the unified
 // MultiDevice settings.
 const base::Feature kEnableUnifiedMultiDeviceSettings{
diff --git a/chromeos/chromeos_features.h b/chromeos/chromeos_features.h
index 3b4d0f4..728c61a6 100644
--- a/chromeos/chromeos_features.h
+++ b/chromeos/chromeos_features.h
@@ -19,6 +19,7 @@
 CHROMEOS_EXPORT extern const base::Feature kAndroidMessagesProdEndpoint;
 CHROMEOS_EXPORT extern const base::Feature kChromeVoxArcSupport;
 CHROMEOS_EXPORT extern const base::Feature kDriveFs;
+CHROMEOS_EXPORT extern const base::Feature kMyFilesVolume;
 CHROMEOS_EXPORT extern const base::Feature kEnableUnifiedMultiDeviceSettings;
 CHROMEOS_EXPORT extern const base::Feature kEnableUnifiedMultiDeviceSetup;
 CHROMEOS_EXPORT extern const base::Feature kImeServiceConnectable;
diff --git a/chromeos/ime/input_methods.txt b/chromeos/ime/input_methods.txt
index 2181789..11f39e2 100644
--- a/chromeos/ime/input_methods.txt
+++ b/chromeos/ime/input_methods.txt
@@ -23,17 +23,6 @@
 #    screen.
 #
 # Notes:
-#   When adding a line to this list, please also add a mapping from the input
-#   method ID to the keyboard overlay ID to INPUT_METHOD_ID_TO_OVERLAY_ID in
-#
-#    * tools/gen_keyboard_overlay_data/gen_keyboard_overlay_data.py
-#
-#   and update the following files by running this script.
-#
-#    * chrome/app/generated_resources.grd
-#    * chrome/browser/resources/chromeos/keyboard_overlay_data.js
-#    * chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc
-#
 #   If you add an XKB layout which depends on AltGr or X11's Mod3Mask
 #   (e.g. Germany Neo2 XKB layout), you should also update kAltGrLayoutIds or
 #   kMod3LayoutIds in
diff --git a/components/arc/common/accessibility_helper.mojom b/components/arc/common/accessibility_helper.mojom
index 911f2af..5642e4a 100644
--- a/components/arc/common/accessibility_helper.mojom
+++ b/components/arc/common/accessibility_helper.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Next MinVersion: 12
+// Next MinVersion: 13
 
 module arc.mojom;
 
@@ -80,7 +80,7 @@
   SCROLL_UP,
   SET_PROGRESS,
   SHOW_ON_SCREEN,
-  CUSTOM_ACTION  // Not a standard action.
+  CUSTOM_ACTION,  // Not a standard action.
 };
 
 // Possible boolean properties set on an AccessibilityNodeInfo.
@@ -159,6 +159,39 @@
   CUSTOM_ACTION_DESCRIPTIONS
 };
 
+// These fields are taken from boolean properties of
+// AccessibilityWindowInfo.
+[Extensible]
+enum AccessibilityWindowBooleanProperty {
+  ACCESSIBILITY_FOCUSED,
+  FOCUSED,
+  IN_PICTURE_IN_PICTURE_MODE,
+  WINDOW_ACTIVE,
+};
+
+// These fields are taken from int attributes of
+// AccessibilityWindowInfo.
+[Extensible]
+enum AccessibilityWindowIntProperty {
+  ANCHOR_NODE_ID,
+  LAYER_ORDER,
+  PARENT_WINDOW_ID,
+};
+
+// These fields are taken from String attributes of
+// AccessibilityWindowInfo.
+[Extensible]
+enum AccessibilityWindowStringProperty {
+  TITLE,
+};
+
+// These fields are taken from List<Integer> instance members of
+// AccessibilityWindowInfo.
+[Extensible]
+enum AccessibilityWindowIntListProperty {
+  CHILD_WINDOW_IDS,
+};
+
 // This type is a subset of spans available under android.text.style.
 [Extensible]
 enum SpanType {
@@ -215,6 +248,16 @@
   float current;
 };
 
+// These fields are taken from AccessibilityWindowInfo's window types.
+[Extensible]
+enum AccessibilityWindowType {
+  TYPE_ACCESSIBILITY_OVERLAY,
+  TYPE_APPLICATION,
+  TYPE_INPUT_METHOD,
+  TYPE_SPLIT_SCREEN_DIVIDER,
+  TYPE_SYSTEM,
+};
+
 // AccessibilityNodeInfoData is a struct to contain info of
 // AccessibilityNodeInfo in Android.
 struct AccessibilityNodeInfoData {
@@ -232,6 +275,20 @@
   [MinVersion=5] AccessibilityCollectionInfoData? collection_info;
   [MinVersion=5] AccessibilityCollectionItemInfoData? collection_item_info;
   [MinVersion=5] AccessibilityRangeInfoData? range_info;
+  [MinVersion=12] int32 window_id;
+};
+
+// AccessibilityWindowInfoData is a struct to contain info about
+// AccessibilityWindowInfo in Android.
+struct AccessibilityWindowInfoData {
+  int32 window_id;
+  int32 root_node_id;
+  Rect bounds_in_screen;
+  AccessibilityWindowType window_type;
+  map<AccessibilityWindowBooleanProperty, bool>? boolean_properties;
+  map<AccessibilityWindowStringProperty, string>? string_properties;
+  map<AccessibilityWindowIntProperty, int32>? int_properties;
+  map<AccessibilityWindowIntListProperty, array<int32>>? int_list_properties;
 };
 
 // Filters the event type (and implicitly the data) sent by the ARC
@@ -273,14 +330,19 @@
   // notification_key is set only for an event on an Android notification.
   [MinVersion=6] string? notification_key;
 
-  // window_id where this event is dispatched for. While this window_id matches
-  // with AccessibilityNodeInfo.getWindowId now, do not expect that it always
-  // matches with it.
+  // window_id where this event is dispatched for. This does not match
+  // AccessibilityWindowInfo window ids, instead it is a mapping to from
+  // unique IDs in an Android task to nodes in Chrome.
   [MinVersion=6] int32 window_id;
 
   // Task associated with this event (usually the front task when this event
   // gets dispatched).
   [MinVersion=8] int32 task_id;
+
+  // The window data for the tree. This may not be populated if
+  // only one node needs to be returned, for example if the event
+  // filter type is AccessibilityFilterType.FOCUS.
+  [MinVersion=12] array<AccessibilityWindowInfoData>? window_data;
 };
 
 // AccessibilityActionData is a struct to contain info of AccessibilityAction in
diff --git a/components/arc/common/notifications.mojom b/components/arc/common/notifications.mojom
index 80416ca..8d616f1 100644
--- a/components/arc/common/notifications.mojom
+++ b/components/arc/common/notifications.mojom
@@ -78,7 +78,7 @@
 };
 
 // Flag for various feature of ARC notifications.
-[Extensible, MinVersion=17]
+[MinVersion=17]
 struct ArcNotificationFlags {
   // Bits for features.
   const uint32 SUPPORT_SNOOZE = 1;  // 1 << 0
@@ -160,7 +160,7 @@
   ArcBitmap? snapshot_image_public;
 };
 
-[Extensible, MinVersion=18]
+[MinVersion=18]
 struct ArcDoNotDisturbStatus {
   bool enabled;
 };
@@ -179,7 +179,7 @@
   bool to_be_focused_after_unlock;
 };
 
-[Extensible, MinVersion=21]
+[MinVersion=21]
 struct ArcLockScreenNotificationSetting {
   // Flag whether to show the notifications on the lock screen.
   bool show_notification;
diff --git a/components/cast_channel/OWNERS b/components/cast_channel/OWNERS
index 3590d52..e4a5810 100644
--- a/components/cast_channel/OWNERS
+++ b/components/cast_channel/OWNERS
@@ -1,4 +1,3 @@
-imcheng@chromium.org
 mfoltz@chromium.org
 
-# COMPONENT: Internals>Cast>API
+# COMPONENT: Internals>Cast>Providers
diff --git a/components/cryptauth/cryptauth_device_manager_impl.cc b/components/cryptauth/cryptauth_device_manager_impl.cc
index ec0597e..8c7bbb7 100644
--- a/components/cryptauth/cryptauth_device_manager_impl.cc
+++ b/components/cryptauth/cryptauth_device_manager_impl.cc
@@ -102,8 +102,15 @@
   return list;
 }
 
-void RecordDeviceSyncSoftwareFeaturesResult(bool success) {
+void RecordDeviceSyncSoftwareFeaturesResult(
+    bool success,
+    cryptauth::SoftwareFeature software_feature) {
   UMA_HISTOGRAM_BOOLEAN("CryptAuth.DeviceSyncSoftwareFeaturesResult", success);
+  if (!success) {
+    UMA_HISTOGRAM_ENUMERATION(
+        "CryptAuth.DeviceSyncSoftwareFeaturesResult.Failures", software_feature,
+        cryptauth::SoftwareFeature_ARRAYSIZE);
+  }
 }
 
 // Converts supported and enabled SoftwareFeature protos to a single dictionary
@@ -126,6 +133,8 @@
 
   for (const auto& enabled_software_feature : enabled_software_features) {
     std::string software_feature_key = enabled_software_feature;
+    cryptauth::SoftwareFeature software_feature =
+        SoftwareFeatureStringToEnum(software_feature_key);
 
     int software_feature_state;
     if (!dictionary->GetInteger(software_feature_key,
@@ -134,11 +143,13 @@
             SoftwareFeatureState::kSupported) {
       PA_LOG(ERROR) << "A feature is marked as enabled but not as supported: "
                     << software_feature_key;
-      RecordDeviceSyncSoftwareFeaturesResult(false /* success */);
+      RecordDeviceSyncSoftwareFeaturesResult(false /* success */,
+                                             software_feature);
 
       continue;
     } else {
-      RecordDeviceSyncSoftwareFeaturesResult(true /* success */);
+      RecordDeviceSyncSoftwareFeaturesResult(true /* success */,
+                                             software_feature);
     }
 
     dictionary->SetInteger(software_feature_key,
diff --git a/components/cryptauth/cryptauth_device_manager_impl_unittest.cc b/components/cryptauth/cryptauth_device_manager_impl_unittest.cc
index 941e9c1..9fe3a2c 100644
--- a/components/cryptauth/cryptauth_device_manager_impl_unittest.cc
+++ b/components/cryptauth/cryptauth_device_manager_impl_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/simple_test_clock.h"
 #include "components/cryptauth/fake_cryptauth_gcm_manager.h"
 #include "components/cryptauth/mock_cryptauth_client.h"
@@ -1162,4 +1163,54 @@
       SoftwareFeatureEnumToString(SoftwareFeature::MAGIC_TETHER_HOST)));
 }
 
+TEST_F(CryptAuthDeviceManagerImplTest, MetricsForEnabledAndNotSupported) {
+  ExternalDeviceInfo enabled_not_supported_device;
+  enabled_not_supported_device.set_public_key("new public key");
+  enabled_not_supported_device.add_supported_software_features(
+      SoftwareFeatureEnumToString(SoftwareFeature::BETTER_TOGETHER_HOST));
+  enabled_not_supported_device.add_enabled_software_features(
+      SoftwareFeatureEnumToString(SoftwareFeature::BETTER_TOGETHER_HOST));
+  enabled_not_supported_device.add_enabled_software_features(
+      SoftwareFeatureEnumToString(SoftwareFeature::EASY_UNLOCK_HOST));
+  enabled_not_supported_device.add_enabled_software_features(
+      "MyUnknownFeature");
+
+  GetMyDevicesResponse response;
+  response.add_devices()->CopyFrom(enabled_not_supported_device);
+
+  device_manager_->Start();
+  base::HistogramTester histogram_tester;
+  histogram_tester.ExpectTotalCount(
+      "CryptAuth.DeviceSyncSoftwareFeaturesResult", 0);
+  histogram_tester.ExpectTotalCount(
+      "CryptAuth.DeviceSyncSoftwareFeaturesResult.Failures", 0);
+
+  EXPECT_EQ(1u, device_manager_->GetSyncedDevices().size());
+  FireSchedulerForSync(INVOCATION_REASON_PERIODIC);
+  ASSERT_FALSE(success_callback_.is_null());
+  EXPECT_CALL(*this, OnSyncFinishedProxy(
+                         CryptAuthDeviceManager::SyncResult::SUCCESS,
+                         CryptAuthDeviceManager::DeviceChangeResult::CHANGED));
+  success_callback_.Run(response);
+
+  histogram_tester.ExpectTotalCount(
+      "CryptAuth.DeviceSyncSoftwareFeaturesResult", 3);
+  histogram_tester.ExpectBucketCount<bool>(
+      "CryptAuth.DeviceSyncSoftwareFeaturesResult", true, 1);
+  histogram_tester.ExpectBucketCount<bool>(
+      "CryptAuth.DeviceSyncSoftwareFeaturesResult", false, 2);
+
+  histogram_tester.ExpectTotalCount(
+      "CryptAuth.DeviceSyncSoftwareFeaturesResult.Failures", 2);
+  histogram_tester.ExpectBucketCount<cryptauth::SoftwareFeature>(
+      "CryptAuth.DeviceSyncSoftwareFeaturesResult.Failures",
+      cryptauth::SoftwareFeature::BETTER_TOGETHER_HOST, 0);
+  histogram_tester.ExpectBucketCount<cryptauth::SoftwareFeature>(
+      "CryptAuth.DeviceSyncSoftwareFeaturesResult.Failures",
+      cryptauth::SoftwareFeature::EASY_UNLOCK_HOST, 1);
+  histogram_tester.ExpectBucketCount<cryptauth::SoftwareFeature>(
+      "CryptAuth.DeviceSyncSoftwareFeaturesResult.Failures",
+      cryptauth::SoftwareFeature::UNKNOWN_FEATURE, 1);
+}
+
 }  // namespace cryptauth
diff --git a/components/data_use_measurement/core/data_use_measurement.cc b/components/data_use_measurement/core/data_use_measurement.cc
index 58ecdf0..f71f8fa 100644
--- a/components/data_use_measurement/core/data_use_measurement.cc
+++ b/components/data_use_measurement/core/data_use_measurement.cc
@@ -86,11 +86,11 @@
                               base::Unretained(this)))),
       rx_bytes_os_(0),
       tx_bytes_os_(0),
-      bytes_transferred_since_last_traffic_stats_query_(0),
       no_reads_since_background_(false),
 #endif
       network_connection_tracker_(network_connection_tracker),
       connection_type_(network::mojom::ConnectionType::CONNECTION_UNKNOWN) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(ascriber_ ||
          base::FeatureList::IsEnabled(network::features::kNetworkService));
   DCHECK(url_request_classifier_ ||
@@ -114,12 +114,14 @@
 }
 
 DataUseMeasurement::~DataUseMeasurement() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (network_connection_tracker_)
     network_connection_tracker_->RemoveNetworkConnectionObserver(this);
   DCHECK(!services_data_use_observer_list_.might_have_observers());
 }
 
 void DataUseMeasurement::OnBeforeURLRequest(net::URLRequest* request) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DataUseUserData* data_use_user_data = reinterpret_cast<DataUseUserData*>(
       request->GetUserData(DataUseUserData::kUserDataKey));
   if (!data_use_user_data) {
@@ -133,6 +135,7 @@
 
 void DataUseMeasurement::OnBeforeRedirect(const net::URLRequest& request,
                                           const GURL& new_location) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Recording data use of request on redirects.
   // TODO(rajendrant): May not be needed when http://crbug/651957 is fixed.
   UpdateDataUseToMetricsService(
@@ -148,6 +151,7 @@
 void DataUseMeasurement::OnHeadersReceived(
     net::URLRequest* request,
     const net::HttpResponseHeaders* response_headers) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DataUseUserData* data_use_user_data = reinterpret_cast<DataUseUserData*>(
       request->GetUserData(DataUseUserData::kUserDataKey));
   if (data_use_user_data) {
@@ -158,6 +162,7 @@
 
 void DataUseMeasurement::OnNetworkBytesReceived(const net::URLRequest& request,
                                                 int64_t bytes_received) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   UMA_HISTOGRAM_COUNTS_1M("DataUse.BytesReceived.Delegate", bytes_received);
   ReportDataUseUMA(request, DOWNSTREAM, bytes_received);
 #if defined(OS_ANDROID)
@@ -167,6 +172,7 @@
 
 void DataUseMeasurement::OnNetworkBytesSent(const net::URLRequest& request,
                                             int64_t bytes_sent) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   UMA_HISTOGRAM_COUNTS_1M("DataUse.BytesSent.Delegate", bytes_sent);
   ReportDataUseUMA(request, UPSTREAM, bytes_sent);
 #if defined(OS_ANDROID)
@@ -176,6 +182,7 @@
 
 void DataUseMeasurement::OnCompleted(const net::URLRequest& request,
                                      bool started) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // TODO(amohammadkhan): Verify that there is no double recording in data use
   // of redirected requests.
   UpdateDataUseToMetricsService(
@@ -310,6 +317,7 @@
 #if defined(OS_ANDROID)
 void DataUseMeasurement::OnApplicationStateChange(
     base::android::ApplicationState application_state) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   app_state_ = application_state;
   if (app_state_ != base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES) {
     last_app_background_time_ = base::TimeTicks::Now();
@@ -326,6 +334,7 @@
   // background). This reduces the overhead of repeatedly calling the API.
   static const int64_t kMinDelegateBytes = 25000;
 
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (bytes_transferred_since_last_traffic_stats_query_ < kMinDelegateBytes &&
       CurrentAppState() == DataUseUserData::FOREGROUND) {
     return;
@@ -514,6 +523,7 @@
 
 void DataUseMeasurement::OnConnectionChanged(
     network::mojom::ConnectionType type) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   connection_type_ = type;
 }
 
diff --git a/components/data_use_measurement/core/data_use_measurement.h b/components/data_use_measurement/core/data_use_measurement.h
index d4c9d51..32450ad8 100644
--- a/components/data_use_measurement/core/data_use_measurement.h
+++ b/components/data_use_measurement/core/data_use_measurement.h
@@ -13,6 +13,7 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
+#include "base/sequence_checker.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
@@ -131,11 +132,18 @@
   // Records the count of bytes received and sent by Chrome on the network as
   // reported by the operating system.
   void MaybeRecordNetworkBytesOS();
+
+  // Number of bytes received and sent by Chromium as reported by the network
+  // delegate since the operating system was last queried for traffic
+  // statistics.
+  int64_t bytes_transferred_since_last_traffic_stats_query_ = 0;
 #endif
 
   base::ObserverList<ServicesDataUseObserver>::Unchecked
       services_data_use_observer_list_;
 
+  SEQUENCE_CHECKER(sequence_checker_);
+
  private:
   friend class DataUseMeasurementTest;
 
@@ -223,11 +231,6 @@
   int64_t rx_bytes_os_;
   int64_t tx_bytes_os_;
 
-  // Number of bytes received and sent by Chromium as reported by the network
-  // delegate since the operating system was last queried for traffic
-  // statistics.
-  int64_t bytes_transferred_since_last_traffic_stats_query_;
-
   // The time at which Chromium app state changed to background. Can be null if
   // app is not in background.
   base::TimeTicks last_app_background_time_;
diff --git a/components/download/internal/common/download_stats.cc b/components/download/internal/common/download_stats.cc
index b6fd99e..91f6c99 100644
--- a/components/download/internal/common/download_stats.cc
+++ b/components/download/internal/common/download_stats.cc
@@ -58,6 +58,8 @@
 
   CONTENT_DISPOSITION_HAS_NAME_ONLY,  // Obsolete; kept for UMA compatiblity.
 
+  CONTENT_DISPOSITION_HAS_SINGLE_QUOTED_FILENAME,
+
   CONTENT_DISPOSITION_LAST_ENTRY
 };
 
@@ -779,6 +781,9 @@
   RecordContentDispositionCountFlag(
       CONTENT_DISPOSITION_HAS_RFC2047_ENCODED_STRINGS, result,
       net::HttpContentDisposition::HAS_RFC2047_ENCODED_STRINGS);
+  RecordContentDispositionCountFlag(
+      CONTENT_DISPOSITION_HAS_SINGLE_QUOTED_FILENAME, result,
+      net::HttpContentDisposition::HAS_SINGLE_QUOTED_FILENAME);
 }
 
 void RecordOpen(const base::Time& end) {
diff --git a/components/feature_engagement/public/event_constants.cc b/components/feature_engagement/public/event_constants.cc
index 59e6061..d1eec66 100644
--- a/components/feature_engagement/public/event_constants.cc
+++ b/components/feature_engagement/public/event_constants.cc
@@ -23,6 +23,7 @@
     "incognito_window_session_time_met";
 
 const char kReopenTabConditionsMet[] = "reopen_tab_conditions_met";
+const char kTabReopened[] = "tab_reopened";
 #endif  // BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
 
 #if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_IOS)
diff --git a/components/feature_engagement/public/event_constants.h b/components/feature_engagement/public/event_constants.h
index 45c30820..5baaf730 100644
--- a/components/feature_engagement/public/event_constants.h
+++ b/components/feature_engagement/public/event_constants.h
@@ -45,6 +45,8 @@
 // track user events (opening/closing tabs, focusing the omnibox, etc) on the
 // second level, it must be done manually.
 extern const char kReopenTabConditionsMet[];
+// The user reopened a previously closed tab.
+extern const char kTabReopened[];
 #endif  // BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
 
 #if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_IOS)
diff --git a/components/gcm_driver/crypto/encryption_header_parsers_unittest.cc b/components/gcm_driver/crypto/encryption_header_parsers_unittest.cc
index b7142cee..021a079 100644
--- a/components/gcm_driver/crypto/encryption_header_parsers_unittest.cc
+++ b/components/gcm_driver/crypto/encryption_header_parsers_unittest.cc
@@ -37,7 +37,6 @@
     { "keyid=foo", "foo", "", kDefaultRecordSize },
     { "keyid=foo;", "foo", "", kDefaultRecordSize },
     { "keyid=\"foo\"", "foo", "", kDefaultRecordSize },
-    { "keyid='foo'", "foo", "", kDefaultRecordSize },
     { "salt=c2l4dGVlbmNvb2xieXRlcw",
       "", "sixteencoolbytes", kDefaultRecordSize },
     { "rs=2048", "", "", 2048 },
@@ -186,7 +185,6 @@
     { "keyid=foo", "foo", "", "" },
     { "aesgcm128=c2l4dGVlbmNvb2xieXRlcw", "", "sixteencoolbytes", "" },
     { "aesgcm128=\"c2l4dGVlbmNvb2xieXRlcw\"", "", "sixteencoolbytes", "" },
-    { "aesgcm128='c2l4dGVlbmNvb2xieXRlcw'", "", "sixteencoolbytes", "" },
     { "dh=dHdlbHZlY29vbGJ5dGVz", "", "", "twelvecoolbytes" },
     { "keyid=foo;someothervalue=bar;aesgcm128=dHdlbHZlY29vbGJ5dGVz",
       "foo", "twelvecoolbytes", "" },
diff --git a/components/metrics/delegating_provider.cc b/components/metrics/delegating_provider.cc
index 2bb2013..6a06ffa 100644
--- a/components/metrics/delegating_provider.cc
+++ b/components/metrics/delegating_provider.cc
@@ -55,9 +55,7 @@
     provider->OnAppEnterBackground();
 }
 
-bool DelegatingProvider::ProvideIndependentMetrics(
-    SystemProfileProto* system_profile_proto,
-    base::HistogramSnapshotManager* snapshot_manager) {
+bool DelegatingProvider::HasIndependentMetrics() {
   // These are collected seperately for each provider.
   NOTREACHED();
   return false;
diff --git a/components/metrics/delegating_provider.h b/components/metrics/delegating_provider.h
index 55c7144..24fa7c7 100644
--- a/components/metrics/delegating_provider.h
+++ b/components/metrics/delegating_provider.h
@@ -33,9 +33,7 @@
   void OnRecordingEnabled() override;
   void OnRecordingDisabled() override;
   void OnAppEnterBackground() override;
-  bool ProvideIndependentMetrics(
-      SystemProfileProto* system_profile_proto,
-      base::HistogramSnapshotManager* snapshot_manager) override;
+  bool HasIndependentMetrics() override;
   void ProvideSystemProfileMetrics(
       SystemProfileProto* system_profile_proto) override;
   bool HasPreviousSessionData() override;
diff --git a/components/metrics/file_metrics_provider.cc b/components/metrics/file_metrics_provider.cc
index 0619185..9333235 100644
--- a/components/metrics/file_metrics_provider.cc
+++ b/components/metrics/file_metrics_provider.cc
@@ -22,6 +22,7 @@
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "base/task_runner.h"
+#include "base/task_runner_util.h"
 #include "base/time/time.h"
 #include "components/metrics/metrics_pref_names.h"
 #include "components/metrics/metrics_service.h"
@@ -33,8 +34,8 @@
 
 namespace {
 
-const base::Feature kCacheFileMetricData = {"CacheFileMetricData",
-                                            base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kBackgroundIndependentMetrics = {
+    "BackgoundIndependentMetrics", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // These structures provide values used to define how files are opened and
 // accessed. It obviates the need for multiple code-paths within several of
@@ -506,7 +507,11 @@
 
   // Cache the file data while running in a background thread so that there
   // shouldn't be any I/O when the data is accessed from the main thread.
-  if (base::FeatureList::IsEnabled(kCacheFileMetricData))
+  // Files with an internal profile, those from previous runs that include
+  // a full system profile and are fetched via ProvideIndependentMetrics(),
+  // are loaded on a background task and so there's no need to cache the
+  // data in advance.
+  if (source->association != ASSOCIATE_INTERNAL_PROFILE)
     memory_allocator->Cache();
 
   // Create an allocator for the mapped file. Ownership passes to the allocator.
@@ -624,6 +629,38 @@
   return ACCESS_RESULT_SUCCESS;
 }
 
+/* static */
+bool FileMetricsProvider::ProvideIndependentMetricsOnTaskRunner(
+    SourceInfo* source,
+    SystemProfileProto* system_profile_proto,
+    base::HistogramSnapshotManager* snapshot_manager) {
+  RecordEmbeddedProfileResult(EMBEDDED_PROFILE_ATTEMPT);
+  base::Time start_time = base::Time::Now();
+  if (PersistentSystemProfile::GetSystemProfile(
+          *source->allocator->memory_allocator(), system_profile_proto)) {
+    system_profile_proto->mutable_stability()->set_from_previous_run(true);
+    RecordHistogramSnapshotsFromSource(snapshot_manager, source);
+    UMA_HISTOGRAM_TIMES("UMA.FileMetricsProvider.EmbeddedProfile.RecordTime",
+                        base::Time::Now() - start_time);
+    RecordEmbeddedProfileResult(EMBEDDED_PROFILE_FOUND);
+    return true;
+  }
+
+  RecordEmbeddedProfileResult(EMBEDDED_PROFILE_DROPPED);
+
+  // TODO(bcwhite): Remove these once crbug/695880 is resolved.
+  int histogram_count = 0;
+  base::PersistentHistogramAllocator::Iterator histogram_iter(
+      source->allocator.get());
+  while (histogram_iter.GetNext()) {
+    ++histogram_count;
+  }
+  UMA_HISTOGRAM_COUNTS_10000(
+      "UMA.FileMetricsProvider.EmbeddedProfile.DroppedHistogramCount",
+      histogram_count);
+  return false;
+}
+
 void FileMetricsProvider::ScheduleSourcesCheck() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -718,56 +755,62 @@
   sources_for_previous_run_.clear();
 }
 
-bool FileMetricsProvider::ProvideIndependentMetrics(
+bool FileMetricsProvider::HasIndependentMetrics() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return !sources_with_profile_.empty();
+}
+
+void FileMetricsProvider::ProvideIndependentMetrics(
+    base::OnceCallback<void(bool)> done_callback,
     SystemProfileProto* system_profile_proto,
     base::HistogramSnapshotManager* snapshot_manager) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  while (!sources_with_profile_.empty()) {
-    SourceInfo* source = sources_with_profile_.begin()->get();
-    DCHECK(source->allocator);
-
-    bool success = false;
-    RecordEmbeddedProfileResult(EMBEDDED_PROFILE_ATTEMPT);
-    base::Time start_time = base::Time::Now();
-    if (PersistentSystemProfile::GetSystemProfile(
-            *source->allocator->memory_allocator(), system_profile_proto)) {
-      RecordHistogramSnapshotsFromSource(snapshot_manager, source);
-      UMA_HISTOGRAM_TIMES("UMA.FileMetricsProvider.EmbeddedProfile.RecordTime",
-                          base::Time::Now() - start_time);
-      success = true;
-      RecordEmbeddedProfileResult(EMBEDDED_PROFILE_FOUND);
-    } else {
-      RecordEmbeddedProfileResult(EMBEDDED_PROFILE_DROPPED);
-
-      // TODO(bcwhite): Remove these once crbug/695880 is resolved.
-
-      int histogram_count = 0;
-      base::PersistentHistogramAllocator::Iterator histogram_iter(
-          source->allocator.get());
-      while (histogram_iter.GetNext()) {
-        ++histogram_count;
-      }
-      UMA_HISTOGRAM_COUNTS_10000(
-          "UMA.FileMetricsProvider.EmbeddedProfile.DroppedHistogramCount",
-          histogram_count);
-    }
-
-    // Regardless of whether this source was successfully recorded, it is never
-    // read again.
-    source->read_complete = true;
-    RecordSourceAsRead(source);
-    sources_to_check_.splice(sources_to_check_.end(), sources_with_profile_,
-                             sources_with_profile_.begin());
-    ScheduleSourcesCheck();
-
-    if (success) {
-      system_profile_proto->mutable_stability()->set_from_previous_run(true);
-      return true;
-    }
+  if (sources_with_profile_.empty()) {
+    std::move(done_callback).Run(false);
+    return;
   }
 
-  return false;
+  std::unique_ptr<SourceInfo> source =
+      std::move(*sources_with_profile_.begin());
+  sources_with_profile_.pop_front();
+  SourceInfo* source_ptr = source.get();
+  DCHECK(source->allocator);
+
+  if (base::FeatureList::IsEnabled(kBackgroundIndependentMetrics)) {
+    // Do the actual work as a background task.
+    base::PostTaskAndReplyWithResult(
+        task_runner_.get(), FROM_HERE,
+        base::BindOnce(
+            &FileMetricsProvider::ProvideIndependentMetricsOnTaskRunner,
+            source_ptr, system_profile_proto, snapshot_manager),
+        base::BindOnce(&FileMetricsProvider::ProvideIndependentMetricsCleanup,
+                       weak_factory_.GetWeakPtr(), std::move(done_callback),
+                       std::move(source)));
+  } else {
+    // Do the actual work now, inline (for performance comparisons).
+    bool success = ProvideIndependentMetricsOnTaskRunner(
+        source_ptr, system_profile_proto, snapshot_manager);
+    ProvideIndependentMetricsCleanup(std::move(done_callback),
+                                     std::move(source), success);
+  }
+}
+
+void FileMetricsProvider::ProvideIndependentMetricsCleanup(
+    base::OnceCallback<void(bool)> done_callback,
+    std::unique_ptr<SourceInfo> source,
+    bool success) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // Regardless of whether this source was successfully recorded, it is
+  // never read again.
+  source->read_complete = true;
+  RecordSourceAsRead(source.get());
+  sources_to_check_.push_back(std::move(source));
+  ScheduleSourcesCheck();
+
+  // Execute the chained callback.
+  std::move(done_callback).Run(success);
 }
 
 bool FileMetricsProvider::HasPreviousSessionData() {
diff --git a/components/metrics/file_metrics_provider.h b/components/metrics/file_metrics_provider.h
index 7465d93..b3796c0 100644
--- a/components/metrics/file_metrics_provider.h
+++ b/components/metrics/file_metrics_provider.h
@@ -267,6 +267,12 @@
   static AccessResult HandleFilterSource(SourceInfo* source,
                                          const base::FilePath& path);
 
+  // The part of ProvideIndependentMetrics that runs as a background task.
+  static bool ProvideIndependentMetricsOnTaskRunner(
+      SourceInfo* source,
+      SystemProfileProto* system_profile_proto,
+      base::HistogramSnapshotManager* snapshot_manager);
+
   // Creates a task to check all monitored sources for updates.
   void ScheduleSourcesCheck();
 
@@ -282,7 +288,9 @@
 
   // metrics::MetricsProvider:
   void OnDidCreateMetricsLog() override;
-  bool ProvideIndependentMetrics(
+  bool HasIndependentMetrics() override;
+  void ProvideIndependentMetrics(
+      base::OnceCallback<void(bool)> done_callback,
       SystemProfileProto* system_profile_proto,
       base::HistogramSnapshotManager* snapshot_manager) override;
   bool HasPreviousSessionData() override;
@@ -292,6 +300,12 @@
   // base::StatisticsRecorder::HistogramProvider:
   void MergeHistogramDeltas() override;
 
+  // The part of ProvideIndependentMetrics that runs after background task.
+  void ProvideIndependentMetricsCleanup(
+      base::OnceCallback<void(bool)> done_callback,
+      std::unique_ptr<SourceInfo> source,
+      bool success);
+
   // A task-runner capable of performing I/O.
   scoped_refptr<base::TaskRunner> task_runner_;
 
diff --git a/components/metrics/file_metrics_provider_unittest.cc b/components/metrics/file_metrics_provider_unittest.cc
index ca5ca9a..c0d90575 100644
--- a/components/metrics/file_metrics_provider_unittest.cc
+++ b/components/metrics/file_metrics_provider_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/metrics/sparse_histogram.h"
 #include "base/metrics/statistics_recorder.h"
 #include "base/strings/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
@@ -109,11 +110,25 @@
     provider()->MergeHistogramDeltas();
   }
 
+  bool HasIndependentMetrics() { return provider()->HasIndependentMetrics(); }
+
   bool ProvideIndependentMetrics(
       SystemProfileProto* profile_proto,
       base::HistogramSnapshotManager* snapshot_manager) {
-    return provider()->ProvideIndependentMetrics(profile_proto,
-                                                 snapshot_manager);
+    bool success = false;
+    bool success_set = false;
+    provider()->ProvideIndependentMetrics(
+        base::BindOnce(
+            [](bool* success_ptr, bool* set_ptr, bool s) {
+              *success_ptr = s;
+              *set_ptr = true;
+            },
+            &success, &success_set),
+        profile_proto, snapshot_manager);
+
+    RunTasks();
+    CHECK(success_set);
+    return success;
   }
 
   void RecordInitialHistogramSnapshots(
@@ -139,10 +154,10 @@
     HistogramFlattenerDeltaRecorder flattener;
     base::HistogramSnapshotManager snapshot_manager(&flattener);
     SystemProfileProto profile_proto;
-    if (!provider()->ProvideIndependentMetrics(&profile_proto,
-                                               &snapshot_manager)) {
-      return 0;
-    }
+    provider()->ProvideIndependentMetrics(base::BindOnce([](bool success) {}),
+                                          &profile_proto, &snapshot_manager);
+
+    RunTasks();
     return flattener.GetRecordedDeltaHistogramNames().size();
   }
 
@@ -545,18 +560,20 @@
   // H1 should be skipped and H2 available.
   OnDidCreateMetricsLog();
   RunTasks();
-  EXPECT_EQ(2U, GetIndependentHistogramCount());
   EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("h1.pma")));
   EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h2.pma")));
   EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h3.pma")));
   EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h4.pma")));
 
+  // H2 should be read and the file deleted.
+  EXPECT_EQ(2U, GetIndependentHistogramCount());
+  RunTasks();
+  EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("h2.pma")));
+
   // Nothing else should be found but the last (valid but empty) file will
   // stick around to be processed later (should it get expanded).
-  OnDidCreateMetricsLog();
-  RunTasks();
   EXPECT_EQ(0U, GetIndependentHistogramCount());
-  EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("h2.pma")));
+  RunTasks();
   EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("h3.pma")));
   EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h4.pma")));
 }
@@ -888,6 +905,7 @@
     SystemProfileProto profile;
 
     // A read of metrics with internal profiles should return nothing.
+    EXPECT_FALSE(HasIndependentMetrics());
     EXPECT_FALSE(ProvideIndependentMetrics(&profile, &snapshot_manager));
   }
   EXPECT_TRUE(base::PathExists(metrics_file()));
@@ -929,11 +947,11 @@
 
     // A read of metrics with internal profiles should return one result.
     SystemProfileProto profile;
+    EXPECT_TRUE(HasIndependentMetrics());
     EXPECT_TRUE(ProvideIndependentMetrics(&profile, &snapshot_manager));
+    EXPECT_FALSE(HasIndependentMetrics());
     EXPECT_FALSE(ProvideIndependentMetrics(&profile, &snapshot_manager));
   }
-  EXPECT_TRUE(base::PathExists(metrics_file()));
-  OnDidCreateMetricsLog();
   RunTasks();
   EXPECT_FALSE(base::PathExists(metrics_file()));
 }
@@ -960,6 +978,7 @@
 
     // A read of metrics with internal profiles should return nothing.
     SystemProfileProto profile;
+    EXPECT_FALSE(HasIndependentMetrics());
     EXPECT_FALSE(ProvideIndependentMetrics(&profile, &snapshot_manager));
   }
   EXPECT_TRUE(base::PathExists(metrics_file()));
@@ -1002,11 +1021,11 @@
 
     // A read of metrics with internal profiles should return one result.
     SystemProfileProto profile;
+    EXPECT_TRUE(HasIndependentMetrics());
     EXPECT_TRUE(ProvideIndependentMetrics(&profile, &snapshot_manager));
+    EXPECT_FALSE(HasIndependentMetrics());
     EXPECT_FALSE(ProvideIndependentMetrics(&profile, &snapshot_manager));
   }
-  EXPECT_TRUE(base::PathExists(metrics_file()));
-  OnDidCreateMetricsLog();
   RunTasks();
   EXPECT_FALSE(base::PathExists(metrics_file()));
 }
@@ -1055,9 +1074,11 @@
   base::HistogramSnapshotManager snapshot_manager(&flattener);
   SystemProfileProto profile;
   for (int i = 0; i < file_count; ++i) {
+    EXPECT_TRUE(HasIndependentMetrics()) << i;
     EXPECT_TRUE(ProvideIndependentMetrics(&profile, &snapshot_manager)) << i;
     RunTasks();
   }
+  EXPECT_FALSE(HasIndependentMetrics());
   EXPECT_FALSE(ProvideIndependentMetrics(&profile, &snapshot_manager));
 
   OnDidCreateMetricsLog();
diff --git a/components/metrics/metrics_log.cc b/components/metrics/metrics_log.cc
index 6445b6d..37937fb 100644
--- a/components/metrics/metrics_log.cc
+++ b/components/metrics/metrics_log.cc
@@ -54,6 +54,7 @@
 class IndependentFlattener : public base::HistogramFlattener {
  public:
   explicit IndependentFlattener(MetricsLog* log) : log_(log) {}
+  ~IndependentFlattener() override {}
 
   // base::HistogramFlattener:
   void RecordDelta(const base::HistogramBase& histogram,
@@ -74,6 +75,26 @@
 
 }  // namespace
 
+MetricsLog::IndependentMetricsLoader::IndependentMetricsLoader(
+    std::unique_ptr<MetricsLog> log)
+    : log_(std::move(log)),
+      flattener_(new IndependentFlattener(log_.get())),
+      snapshot_manager_(new base::HistogramSnapshotManager(flattener_.get())) {}
+
+MetricsLog::IndependentMetricsLoader::~IndependentMetricsLoader() = default;
+
+void MetricsLog::IndependentMetricsLoader::Run(
+    base::OnceCallback<void(bool)> done_callback,
+    MetricsProvider* metrics_provider) {
+  metrics_provider->ProvideIndependentMetrics(
+      std::move(done_callback), log_->uma_proto()->mutable_system_profile(),
+      snapshot_manager_.get());
+}
+
+std::unique_ptr<MetricsLog> MetricsLog::IndependentMetricsLoader::ReleaseLog() {
+  return std::move(log_);
+}
+
 MetricsLog::MetricsLog(const std::string& client_id,
                        int session_id,
                        LogType log_type,
@@ -278,15 +299,6 @@
   return *system_profile;
 }
 
-bool MetricsLog::LoadIndependentMetrics(MetricsProvider* metrics_provider) {
-  SystemProfileProto* system_profile = uma_proto()->mutable_system_profile();
-  IndependentFlattener flattener(this);
-  base::HistogramSnapshotManager snapshot_manager(&flattener);
-
-  return metrics_provider->ProvideIndependentMetrics(system_profile,
-                                                     &snapshot_manager);
-}
-
 bool MetricsLog::LoadSavedEnvironmentFromPrefs(PrefService* local_state,
                                                std::string* app_version) {
   DCHECK(!has_environment_);
diff --git a/components/metrics/metrics_log.h b/components/metrics/metrics_log.h
index 4cedd663..078937f 100644
--- a/components/metrics/metrics_log.h
+++ b/components/metrics/metrics_log.h
@@ -22,7 +22,9 @@
 class PrefService;
 
 namespace base {
+class HistogramFlattener;
 class HistogramSamples;
+class HistogramSnapshotManager;
 }
 
 namespace metrics {
@@ -45,6 +47,33 @@
     INDEPENDENT_LOG,        // An independent log from a previous session.
   };
 
+  // Loads "independent" metrics from a metrics provider and executes a
+  // callback when complete, which could be immediate or after some
+  // execution on a background thread.
+  class IndependentMetricsLoader {
+   public:
+    explicit IndependentMetricsLoader(std::unique_ptr<MetricsLog> log);
+    ~IndependentMetricsLoader();
+
+    // Call ProvideIndependentMetrics (which may execute on a background thread)
+    // for the |metrics_provider| and execute the |done_callback| when complete
+    // with the result (true if successful). Though this can be called multiple
+    // times to include data from multiple providers, later calls will override
+    // system profile information set by earlier calls.
+    void Run(base::OnceCallback<void(bool)> done_callback,
+             MetricsProvider* metrics_provider);
+
+    // Extract the filled log. No more Run() operations can be done after this.
+    std::unique_ptr<MetricsLog> ReleaseLog();
+
+   private:
+    std::unique_ptr<MetricsLog> log_;
+    std::unique_ptr<base::HistogramFlattener> flattener_;
+    std::unique_ptr<base::HistogramSnapshotManager> snapshot_manager_;
+
+    DISALLOW_COPY_AND_ASSIGN(IndependentMetricsLoader);
+  };
+
   // Creates a new metrics log of the specified type.
   // |client_id| is the identifier for this profile on this installation
   // |session_id| is an integer that's incremented on each application launch
@@ -96,11 +125,6 @@
   const SystemProfileProto& RecordEnvironment(
       DelegatingProvider* delegating_provider);
 
-  // Loads a saved system profile and the associated metrics into the log.
-  // Returns true on success. Keep calling it with fresh logs until it returns
-  // false.
-  bool LoadIndependentMetrics(MetricsProvider* metrics_provider);
-
   // Loads the environment proto that was saved by the last RecordEnvironment()
   // call from prefs. On success, returns true and |app_version| contains the
   // recovered version. Otherwise (if there was no saved environment in prefs
diff --git a/components/metrics/metrics_provider.cc b/components/metrics/metrics_provider.cc
index d5408bb1..4c3a3907 100644
--- a/components/metrics/metrics_provider.cc
+++ b/components/metrics/metrics_provider.cc
@@ -33,10 +33,18 @@
 void MetricsProvider::OnAppEnterBackground() {
 }
 
-bool MetricsProvider::ProvideIndependentMetrics(
+bool MetricsProvider::HasIndependentMetrics() {
+  return false;
+}
+
+void MetricsProvider::ProvideIndependentMetrics(
+    base::OnceCallback<void(bool)> done_callback,
     SystemProfileProto* system_profile_proto,
     base::HistogramSnapshotManager* snapshot_manager) {
-  return false;
+  // Either the method HasIndependentMetrics() has been overridden and this
+  // method has not, or this method being called without regard to Has().
+  // Both are wrong.
+  NOTREACHED();
 }
 
 void MetricsProvider::ProvideSystemProfileMetrics(
diff --git a/components/metrics/metrics_provider.h b/components/metrics/metrics_provider.h
index 591c63ff..3a9c973 100644
--- a/components/metrics/metrics_provider.h
+++ b/components/metrics/metrics_provider.h
@@ -48,11 +48,16 @@
   // further notification after this callback.
   virtual void OnAppEnterBackground();
 
+  // Returns whether there are "independent" metrics that can be retrieved
+  // with a call to ProvideIndependentMetrics().
+  virtual bool HasIndependentMetrics();
+
   // Provides a complete and independent system profile + metrics for uploading.
-  // Any histograms added to the |snapshot_manager| will also be included. A
-  // return of false indicates there are none. Will be called repeatedly until
-  // there is nothing else.
-  virtual bool ProvideIndependentMetrics(
+  // This may or may not be executed on a background thread and the callback
+  // executed when complete. Ownership of the passed objects remains with the
+  // caller and those objects must live until the callback is executed.
+  virtual void ProvideIndependentMetrics(
+      base::OnceCallback<void(bool)> done_callback,
       SystemProfileProto* system_profile_proto,
       base::HistogramSnapshotManager* snapshot_manager);
 
diff --git a/components/metrics/metrics_service.cc b/components/metrics/metrics_service.cc
index 01ed8be1e..13b3be3 100644
--- a/components/metrics/metrics_service.cc
+++ b/components/metrics/metrics_service.cc
@@ -869,22 +869,58 @@
       &histogram_snapshot_manager_);
 }
 
+void MetricsService::PrepareProviderMetricsLogDone(
+    std::unique_ptr<MetricsLog::IndependentMetricsLoader> loader,
+    bool success) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(independent_loader_active_);
+  DCHECK(loader);
+
+  if (success) {
+    log_manager_.PauseCurrentLog();
+    log_manager_.BeginLoggingWithLog(loader->ReleaseLog());
+    log_manager_.FinishCurrentLog(log_store());
+    log_manager_.ResumePausedLog();
+  }
+
+  independent_loader_active_ = false;
+}
+
 bool MetricsService::PrepareProviderMetricsLog() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  // Create a new log. This will have some defaut values injected in it but
-  // those will be overwritten when an embedded profile is extracted.
-  std::unique_ptr<MetricsLog> log = CreateLog(MetricsLog::INDEPENDENT_LOG);
+  // If something is still pending, stop now and indicate that there is
+  // still work to do.
+  if (independent_loader_active_)
+    return true;
 
+  // Check each provider in turn for data.
   for (auto& provider : delegating_provider_.GetProviders()) {
-    if (log->LoadIndependentMetrics(provider.get())) {
-      log_manager_.PauseCurrentLog();
-      log_manager_.BeginLoggingWithLog(std::move(log));
-      log_manager_.FinishCurrentLog(log_store());
-      log_manager_.ResumePausedLog();
+    if (provider->HasIndependentMetrics()) {
+      // Create a new log. This will have some default values injected in it
+      // but those will be overwritten when an embedded profile is extracted.
+      std::unique_ptr<MetricsLog> log = CreateLog(MetricsLog::INDEPENDENT_LOG);
+
+      // Give the new log to a loader for management and then run it on the
+      // provider that has something to give. A copy of the pointer is needed
+      // because the unique_ptr may get moved before the value can be used
+      // to call Run().
+      std::unique_ptr<MetricsLog::IndependentMetricsLoader> loader =
+          std::make_unique<MetricsLog::IndependentMetricsLoader>(
+              std::move(log));
+      MetricsLog::IndependentMetricsLoader* loader_ptr = loader.get();
+      loader_ptr->Run(
+          base::BindOnce(&MetricsService::PrepareProviderMetricsLogDone,
+                         self_ptr_factory_.GetWeakPtr(), std::move(loader)),
+          provider.get());
+      independent_loader_active_ = true;
+
+      // Something was found so there may still be more work to do.
       return true;
     }
   }
+
+  // Nothing was found so indicate there is no more work to do.
   return false;
 }
 
diff --git a/components/metrics/metrics_service.h b/components/metrics/metrics_service.h
index 8a26561b..265d601c 100644
--- a/components/metrics/metrics_service.h
+++ b/components/metrics/metrics_service.h
@@ -308,6 +308,12 @@
   // i.e., histograms with the |kUmaStabilityHistogramFlag| flag set.
   void RecordCurrentStabilityHistograms();
 
+  // Handle completion of PrepareProviderMetricsLog which is run as a
+  // background task.
+  void PrepareProviderMetricsLogDone(
+      std::unique_ptr<MetricsLog::IndependentMetricsLoader> loader,
+      bool success);
+
   // Record a single independent profile and associated histogram from
   // metrics providers. If this returns true, one was found and there may
   // be more.
@@ -380,6 +386,9 @@
 
   variations::SyntheticTrialRegistry synthetic_trial_registry_;
 
+  // Indicates if loading of independent metrics is currently active.
+  bool independent_loader_active_ = false;
+
   // Redundant marker to check that we completed our shutdown, and set the
   // exited-cleanly bit in the prefs.
   static ShutdownCleanliness clean_shutdown_status_;
diff --git a/components/neterror/resources/neterror.js b/components/neterror/resources/neterror.js
index bc3affe..3d7682a3 100644
--- a/components/neterror/resources/neterror.js
+++ b/components/neterror/resources/neterror.js
@@ -203,6 +203,8 @@
 function getSuggestedContentDiv(item, index) {
   // Note: See AvailableContentToValue in available_offline_content_helper.cc
   // for the data contained in an |item|.
+  // TODO(carlosk): Present |snippet_base64| when that content becomes
+  // available.
   var visual = '';
   var extraContainerClasses = [];
   // html_inline.py will try to replace src attributes with data URIs using a
@@ -217,7 +219,7 @@
     visual = `<div><img class="${iconClass}"></div>`;
   }
 
-  if (!item.attribution)
+  if (!item.attribution_base64)
     extraContainerClasses.push('no-attribution');
 
   return `
@@ -264,9 +266,9 @@
   // plain text.
   for (var index = 0; index < suggestions.length; index++) {
     document.getElementById(`offline-content-suggestion-title-${index}`)
-        .textContent = suggestions[index].title;
+        .textContent = atob(suggestions[index].title_base64);
     document.getElementById(`offline-content-suggestion-attribution-${index}`)
-        .textContent = suggestions[index].attribution;
+        .textContent = atob(suggestions[index].attribution_base64);
   }
 
   var contentListElement = document.getElementById('offline-content-list');
diff --git a/components/os_crypt/os_crypt_features_mac.cc b/components/os_crypt/os_crypt_features_mac.cc
index 6e9351a..cdbc7b7 100644
--- a/components/os_crypt/os_crypt_features_mac.cc
+++ b/components/os_crypt/os_crypt_features_mac.cc
@@ -11,7 +11,7 @@
 // unavailable, but there is a preference set that the key was created in the
 // past.
 const base::Feature kPreventEncryptionKeyOverwrites = {
-    "PreventEncryptionKeyOverwrites", base::FEATURE_ENABLED_BY_DEFAULT};
+    "PreventEncryptionKeyOverwrites", base::FEATURE_DISABLED_BY_DEFAULT};
 
 }  // namespace features
 }  // namespace os_crypt
diff --git a/components/password_manager/core/browser/password_syncable_service_unittest.cc b/components/password_manager/core/browser/password_syncable_service_unittest.cc
index ff70da8..1f9c40a 100644
--- a/components/password_manager/core/browser/password_syncable_service_unittest.cc
+++ b/components/password_manager/core/browser/password_syncable_service_unittest.cc
@@ -496,6 +496,10 @@
                           syncer::PASSWORDS);
   EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
       .WillOnce(Return(false));
+  if (base::FeatureList::IsEnabled(features::kRecoverPasswordsForSyncUsers)) {
+    EXPECT_CALL(*password_store(), DeleteUndecryptableLogins())
+        .WillOnce(Return(DatabaseCleanupResult::kDatabaseUnavailable));
+  }
   EXPECT_CALL(*error_factory, CreateAndUploadError(_, _))
       .WillOnce(Return(error));
   // ActOnPasswordStoreChanges() below shouldn't generate any changes for Sync.
diff --git a/components/password_manager/core/common/password_manager_features.cc b/components/password_manager/core/common/password_manager_features.cc
index dc449da..560f1ce 100644
--- a/components/password_manager/core/common/password_manager_features.cc
+++ b/components/password_manager/core/common/password_manager_features.cc
@@ -22,7 +22,7 @@
 // Recovers lost passwords on Mac by deleting the ones that cannot be decrypted
 // with the present encryption key from the Keychain.
 const base::Feature kDeleteCorruptedPasswords = {
-    "DeleteCorruptedPasswords", base::FEATURE_ENABLED_BY_DEFAULT};
+    "DeleteCorruptedPasswords", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Use HTML based username detector.
 const base::Feature kHtmlBasedUsernameDetector = {
@@ -58,7 +58,7 @@
 // Deletes entries from local database on Mac which cannot be decrypted when
 // merging data with Sync.
 const base::Feature kRecoverPasswordsForSyncUsers = {
-    "RecoverPasswordsForSyncUsers", base::FEATURE_DISABLED_BY_DEFAULT};
+    "RecoverPasswordsForSyncUsers", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables the experiment for the password manager to only fill on account
 // selection, rather than autofilling on page load, with highlighting of fields.
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index e1bd567..826be5f 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -32,6 +32,7 @@
 #include "content/public/renderer/render_view.h"
 #include "mojo/public/cpp/base/shared_memory_utils.h"
 #include "net/base/escape.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "printing/buildflags/buildflags.h"
 #include "printing/metafile_skia.h"
 #include "printing/metafile_skia_wrapper.h"
@@ -547,6 +548,51 @@
 
   return result_params;
 }
+
+// Helper to compute the site (scheme and eTLD+1) for the provided frame, based
+// on the frame's origin.
+std::string GetSiteForFrame(blink::WebFrame* frame) {
+  return frame->GetSecurityOrigin().Protocol().Utf8() + "://" +
+         net::registry_controlled_domains::GetDomainAndRegistry(
+             frame->GetSecurityOrigin().Host().Utf8(),
+             net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
+}
+
+// Records metrics on how frequently printed frames contain RemoteFrames and/or
+// cross-origin frames, to help estimate how often site isolation might
+// affect printing.
+void RecordSiteIsolationPrintMetrics(blink::WebFrame* printed_frame) {
+  int remote_frame_count = 0;
+  int cross_site_frame_count = 0;
+  int cross_site_visible_frame_count = 0;
+  for (blink::WebFrame* frame = printed_frame; frame;
+       frame = frame->TraverseNext()) {
+    if (frame->IsWebRemoteFrame())
+      remote_frame_count++;
+
+    // For platforms that don't yet have site isolation, estimate how often
+    // printing would involve OOPIFs once site isolation is deployed.  Note
+    // that we want to only compare eTLD+1 and skip cross-origin but same-site
+    // cases (e.g., https://subdomain.example.com and https://example.com), as
+    // those do not typically end up in separate processes.
+    if (!frame->GetSecurityOrigin().CanAccess(
+            printed_frame->GetSecurityOrigin()) &&
+        GetSiteForFrame(frame) != GetSiteForFrame(printed_frame)) {
+      cross_site_frame_count++;
+      if (frame->IsWebLocalFrame() &&
+          frame->ToWebLocalFrame()->HasVisibleContent())
+        cross_site_visible_frame_count++;
+    }
+  }
+  UMA_HISTOGRAM_COUNTS_100("PrintPreview.SiteIsolation.RemoteFrameCount",
+                           remote_frame_count);
+  UMA_HISTOGRAM_COUNTS_100("PrintPreview.SiteIsolation.CrossSiteFrameCount",
+                           cross_site_frame_count);
+  UMA_HISTOGRAM_COUNTS_100(
+      "PrintPreview.SiteIsolation.CrossSiteVisibleFrameCount",
+      cross_site_visible_frame_count);
+}
+
 }  // namespace
 
 FrameReference::FrameReference(blink::WebLocalFrame* frame) {
@@ -1699,6 +1745,8 @@
                             printed_count);
   }
 
+  RecordSiteIsolationPrintMetrics(prep_frame_view_->frame());
+
   bool is_pdf = PrintingNodeOrPdfFrame(prep_frame_view_->frame(),
                                        prep_frame_view_->node());
   if (!PrintPagesNative(prep_frame_view_->frame(), page_count, is_pdf)) {
diff --git a/components/sync_bookmarks/bookmark_model_observer_impl.cc b/components/sync_bookmarks/bookmark_model_observer_impl.cc
index 3460d074..68560f1 100644
--- a/components/sync_bookmarks/bookmark_model_observer_impl.cc
+++ b/components/sync_bookmarks/bookmark_model_observer_impl.cc
@@ -283,43 +283,51 @@
   const std::string& suffix = syncer::GenerateSyncableBookmarkHash(
       bookmark_tracker_->model_type_state().cache_guid(), sync_id);
   DCHECK_NE(0, parent.child_count());
+  const SyncedBookmarkTracker::Entity* predecessor_entity = nullptr;
+  const SyncedBookmarkTracker::Entity* successor_entity = nullptr;
 
-  if (parent.child_count() == 1) {
-    // No siblings, the parent has no other children.
+  // Look for the first tracked predecessor.
+  for (int i = index - 1; i >= 0; i--) {
+    const bookmarks::BookmarkNode* predecessor_node = parent.GetChild(i);
+    predecessor_entity =
+        bookmark_tracker_->GetEntityForBookmarkNode(predecessor_node);
+    if (predecessor_entity) {
+      break;
+    }
+  }
+
+  // Look for the first tracked successor.
+  for (int i = index + 1; i < parent.child_count(); i++) {
+    const bookmarks::BookmarkNode* successor_node = parent.GetChild(i);
+    successor_entity =
+        bookmark_tracker_->GetEntityForBookmarkNode(successor_node);
+    if (successor_entity) {
+      break;
+    }
+  }
+
+  if (!predecessor_entity && !successor_entity) {
+    // No tracked siblings.
     return syncer::UniquePosition::InitialPosition(suffix);
   }
-  if (index == 0) {
-    const bookmarks::BookmarkNode* successor_node = parent.GetChild(1);
-    const SyncedBookmarkTracker::Entity* successor_entity =
-        bookmark_tracker_->GetEntityForBookmarkNode(successor_node);
-    DCHECK(successor_entity);
-    // Insert at the beginning.
+
+  if (!predecessor_entity && successor_entity) {
+    // No predecessor, insert before the successor.
     return syncer::UniquePosition::Before(
         syncer::UniquePosition::FromProto(
             successor_entity->metadata()->unique_position()),
         suffix);
   }
-  if (index == parent.child_count() - 1) {
-    // Insert at the end.
-    const bookmarks::BookmarkNode* predecessor_node =
-        parent.GetChild(index - 1);
-    const SyncedBookmarkTracker::Entity* predecessor_entity =
-        bookmark_tracker_->GetEntityForBookmarkNode(predecessor_node);
-    DCHECK(predecessor_entity);
+
+  if (predecessor_entity && !successor_entity) {
+    // No successor, insert after the predecessor
     return syncer::UniquePosition::After(
         syncer::UniquePosition::FromProto(
             predecessor_entity->metadata()->unique_position()),
         suffix);
   }
-  // Insert in the middle.
-  const bookmarks::BookmarkNode* successor_node = parent.GetChild(index + 1);
-  const SyncedBookmarkTracker::Entity* successor_entity =
-      bookmark_tracker_->GetEntityForBookmarkNode(successor_node);
-  DCHECK(successor_entity);
-  const bookmarks::BookmarkNode* predecessor_node = parent.GetChild(index - 1);
-  const SyncedBookmarkTracker::Entity* predecessor_entity =
-      bookmark_tracker_->GetEntityForBookmarkNode(predecessor_node);
-  DCHECK(predecessor_entity);
+
+  // Both predecessor and successor, insert in the middle.
   return syncer::UniquePosition::Between(
       syncer::UniquePosition::FromProto(
           predecessor_entity->metadata()->unique_position()),
diff --git a/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc b/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc
index 965a367..ad3b710af 100644
--- a/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc
+++ b/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc
@@ -477,6 +477,57 @@
   EXPECT_THAT(bookmark_tracker()->TrackedEntitiesCountForTest(), 1U);
 }
 
+TEST_F(BookmarkModelObserverImplTest, ShouldAddChildrenInArbitraryOrder) {
+  SyncedBookmarkTracker bookmark_tracker(
+      std::vector<NodeMetadataPair>(),
+      std::make_unique<sync_pb::ModelTypeState>());
+  BookmarkModelObserverImpl observer(base::DoNothing(), &bookmark_tracker);
+  const bookmarks::BookmarkNode* bookmark_bar_node =
+      bookmark_model()->bookmark_bar_node();
+  // Add the bookmark bar to the tracker.
+  sync_pb::EntitySpecifics specifics;
+  specifics.mutable_bookmark()->set_title(kBookmarkBarTag);
+  bookmark_tracker.Add(
+      /*sync_id=*/kBookmarkBarId,
+      /*bookmark_node=*/bookmark_model()->bookmark_bar_node(),
+      /*server_version=*/0, /*creation_time=*/base::Time::Now(),
+      syncer::UniquePosition::InitialPosition(
+          syncer::UniquePosition::RandomSuffix())
+          .ToProto(),
+      specifics);
+
+  // Build this structure:
+  // bookmark_bar
+  //  |- folder0
+  //  |- folder1
+  //  |- folder2
+  //  |- folder3
+  //  |- folder4
+
+  const bookmarks::BookmarkNode* nodes[5];
+  for (int i = 0; i < 5; i++) {
+    nodes[i] = bookmark_model()->AddFolder(
+        /*parent=*/bookmark_bar_node, /*index=*/i,
+        base::UTF8ToUTF16("folder" + std::to_string(i)));
+  }
+
+  // Now simulate calling the observer as if the nodes are added in that order.
+  // 4,0,2,3,1.
+  observer.BookmarkNodeAdded(bookmark_model(), bookmark_bar_node, 4);
+  observer.BookmarkNodeAdded(bookmark_model(), bookmark_bar_node, 0);
+  observer.BookmarkNodeAdded(bookmark_model(), bookmark_bar_node, 2);
+  observer.BookmarkNodeAdded(bookmark_model(), bookmark_bar_node, 3);
+  observer.BookmarkNodeAdded(bookmark_model(), bookmark_bar_node, 1);
+
+  ASSERT_THAT(bookmark_tracker.TrackedEntitiesCountForTest(), 6U);
+
+  // Check that position information match the children order.
+  EXPECT_TRUE(PositionOf(nodes[0]).LessThan(PositionOf(nodes[1])));
+  EXPECT_TRUE(PositionOf(nodes[1]).LessThan(PositionOf(nodes[2])));
+  EXPECT_TRUE(PositionOf(nodes[2]).LessThan(PositionOf(nodes[3])));
+  EXPECT_TRUE(PositionOf(nodes[3]).LessThan(PositionOf(nodes[4])));
+}
+
 }  // namespace
 
 }  // namespace sync_bookmarks
diff --git a/components/url_formatter/idn_spoof_checker.cc b/components/url_formatter/idn_spoof_checker.cc
index d189ba44..f16116ba 100644
--- a/components/url_formatter/idn_spoof_checker.cc
+++ b/components/url_formatter/idn_spoof_checker.cc
@@ -37,6 +37,7 @@
 
     if (is_same_skeleton) {
       *out_found = true;
+      result_ = search;
       return true;
     }
 
diff --git a/components/url_formatter/top_domains/test_domains.list b/components/url_formatter/top_domains/test_domains.list
index 466dadeb..33a13ab 100644
--- a/components/url_formatter/top_domains/test_domains.list
+++ b/components/url_formatter/top_domains/test_domains.list
@@ -30,3 +30,5 @@
 43.com
 oo.com
 qq.com
+# A domain with the same skeleton as itself:
+test.net
diff --git a/components/url_formatter/top_domains/test_domains.skeletons b/components/url_formatter/top_domains/test_domains.skeletons
index ecf5b68..b4c6cb4 100644
--- a/components/url_formatter/top_domains/test_domains.skeletons
+++ b/components/url_formatter/top_domains/test_domains.skeletons
@@ -41,3 +41,4 @@
 43.corn, 43.com
 oo.corn, oo.com
 qq.corn, qq.com
+test.net, test.net
diff --git a/components/url_formatter/url_formatter_unittest.cc b/components/url_formatter/url_formatter_unittest.cc
index 06c771353..535d780 100644
--- a/components/url_formatter/url_formatter_unittest.cc
+++ b/components/url_formatter/url_formatter_unittest.cc
@@ -1009,7 +1009,11 @@
     // The skeleton of Extended Arabic-Indic Digit Zero (۰) is a dot. Check that
     // this is handled correctly (crbug/877045).
     {"xn--dmb", L"\x06f0", true},
-};
+
+    // Test that top domains whose skeletons are the same as the domain name are
+    // handled properly. In this case, tést.net should match test.net top
+    // domain.
+    {"xn--tst-bma.net", L"t\x00e9st.net", false}};
 
 struct AdjustOffsetCase {
   size_t input_offset;
diff --git a/components/variations/net/variations_http_headers.cc b/components/variations/net/variations_http_headers.cc
index f1dc1546..1b2eb27 100644
--- a/components/variations/net/variations_http_headers.cc
+++ b/components/variations/net/variations_http_headers.cc
@@ -38,6 +38,7 @@
     ".googleusercontent.com",
     ".googlevideo.com",
     ".gstatic.com",
+    ".litepages.googlezip.net",
     ".ytimg.com",
 };
 
diff --git a/components/variations/net/variations_http_headers_unittest.cc b/components/variations/net/variations_http_headers_unittest.cc
index 661aa35..87dfc2d 100644
--- a/components/variations/net/variations_http_headers_unittest.cc
+++ b/components/variations/net/variations_http_headers_unittest.cc
@@ -145,6 +145,11 @@
       {"http://wwwgoogleweblight.com", false},
       {"https://www.googleweblight.com", false},
       {"https://a.b.googleweblight.com", false},
+
+      {"http://a.b.litepages.googlezip.net", false},
+      {"https://litepages.googlezip.net", false},
+      {"https://a.litepages.googlezip.net", true},
+      {"https://a.b.litepages.googlezip.net", true},
   };
 
   for (size_t i = 0; i < base::size(cases); ++i) {
diff --git a/components/viz/common/frame_sinks/begin_frame_source.cc b/components/viz/common/frame_sinks/begin_frame_source.cc
index ff9b0b78..27a7fa4 100644
--- a/components/viz/common/frame_sinks/begin_frame_source.cc
+++ b/components/viz/common/frame_sinks/begin_frame_source.cc
@@ -90,6 +90,25 @@
 
 BeginFrameSource::~BeginFrameSource() = default;
 
+void BeginFrameSource::SetIsGpuBusy(bool busy) {
+  if (is_gpu_busy_ == busy)
+    return;
+  is_gpu_busy_ = busy;
+  if (is_gpu_busy_) {
+    DCHECK(!request_notification_on_gpu_availability_);
+  } else if (request_notification_on_gpu_availability_) {
+    request_notification_on_gpu_availability_ = false;
+    OnGpuNoLongerBusy();
+  }
+}
+
+bool BeginFrameSource::RequestCallbackOnGpuAvailable() {
+  if (!is_gpu_busy_)
+    return false;
+  request_notification_on_gpu_availability_ = true;
+  return true;
+}
+
 void BeginFrameSource::AsValueInto(
     base::trace_event::TracedValue* state) const {
   // The lower 32 bits of source_id are the interesting piece of |source_id_|.
@@ -154,7 +173,13 @@
   return false;
 }
 
+void BackToBackBeginFrameSource::OnGpuNoLongerBusy() {
+  OnTimerTick();
+}
+
 void BackToBackBeginFrameSource::OnTimerTick() {
+  if (RequestCallbackOnGpuAvailable())
+    return;
   base::TimeTicks frame_time = time_source_->LastTickTime();
   base::TimeDelta default_interval = BeginFrameArgs::DefaultInterval();
   BeginFrameArgs args = BeginFrameArgs::Create(
@@ -229,17 +254,7 @@
   }
   BeginFrameArgs missed_args = last_begin_frame_args_;
   missed_args.type = BeginFrameArgs::MISSED;
-
-  BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs();
-  if (!last_args.IsValid() ||
-      (missed_args.frame_time >
-       last_args.frame_time + missed_args.interval / kDoubleTickDivisor)) {
-    DCHECK(missed_args.sequence_number > last_args.sequence_number ||
-           missed_args.source_id != last_args.source_id)
-        << "missed " << missed_args.AsValue()->ToString() << ", last "
-        << last_args.AsValue()->ToString();
-    FilterAndIssueBeginFrame(obs, missed_args);
-  }
+  IssueBeginFrameToObserver(obs, missed_args);
 }
 
 void DelayBasedBeginFrameSource::RemoveObserver(BeginFrameObserver* obs) {
@@ -255,17 +270,33 @@
   return true;
 }
 
+void DelayBasedBeginFrameSource::OnGpuNoLongerBusy() {
+  OnTimerTick();
+}
+
 void DelayBasedBeginFrameSource::OnTimerTick() {
+  if (RequestCallbackOnGpuAvailable())
+    return;
   last_begin_frame_args_ = CreateBeginFrameArgs(time_source_->LastTickTime());
   std::unordered_set<BeginFrameObserver*> observers(observers_);
-  for (auto* obs : observers) {
-    BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs();
-    if (!last_args.IsValid() ||
-        (last_begin_frame_args_.frame_time >
-         last_args.frame_time +
-             last_begin_frame_args_.interval / kDoubleTickDivisor)) {
-      FilterAndIssueBeginFrame(obs, last_begin_frame_args_);
+  for (auto* obs : observers)
+    IssueBeginFrameToObserver(obs, last_begin_frame_args_);
+}
+
+void DelayBasedBeginFrameSource::IssueBeginFrameToObserver(
+    BeginFrameObserver* obs,
+    const BeginFrameArgs& args) {
+  BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs();
+  if (!last_args.IsValid() ||
+      (args.frame_time >
+       last_args.frame_time + args.interval / kDoubleTickDivisor)) {
+    if (args.type == BeginFrameArgs::MISSED) {
+      DCHECK(args.sequence_number > last_args.sequence_number ||
+             args.source_id != last_args.source_id)
+          << "missed " << args.AsValue()->ToString() << ", last "
+          << last_args.AsValue()->ToString();
     }
+    FilterAndIssueBeginFrame(obs, args);
   }
 }
 
@@ -324,6 +355,11 @@
   return true;
 }
 
+void ExternalBeginFrameSource::OnGpuNoLongerBusy() {
+  OnBeginFrame(pending_begin_frame_args_);
+  pending_begin_frame_args_ = BeginFrameArgs();
+}
+
 void ExternalBeginFrameSource::OnSetBeginFrameSourcePaused(bool paused) {
   if (paused_ == paused)
     return;
@@ -342,6 +378,11 @@
         args.sequence_number <= last_begin_frame_args_.sequence_number)))
     return;
 
+  if (RequestCallbackOnGpuAvailable()) {
+    pending_begin_frame_args_ = args;
+    return;
+  }
+
   last_begin_frame_args_ = args;
   std::unordered_set<BeginFrameObserver*> observers(observers_);
   for (auto* obs : observers) {
diff --git a/components/viz/common/frame_sinks/begin_frame_source.h b/components/viz/common/frame_sinks/begin_frame_source.h
index 8b8a775..1bd8bb5 100644
--- a/components/viz/common/frame_sinks/begin_frame_source.h
+++ b/components/viz/common/frame_sinks/begin_frame_source.h
@@ -130,6 +130,10 @@
   // BeginFrames created by other sources, with different IDs.
   uint64_t source_id() const { return source_id_; }
 
+  // Sets whether the gpu is busy or not. See below the documentation for
+  // RequestCallbackOnGpuAvailable() for more details.
+  void SetIsGpuBusy(bool busy);
+
   // BeginFrameObservers use DidFinishFrame to provide back pressure to a frame
   // source about frame processing (rather than toggling SetNeedsBeginFrames
   // every frame). For example, the BackToBackFrameSource uses them to make sure
@@ -147,12 +151,27 @@
 
   virtual void AsValueInto(base::trace_event::TracedValue* state) const;
 
+ protected:
+  // Returns whether begin-frames to clients should be withheld (because the gpu
+  // is still busy, for example). If this returns true, then OnGpuNoLongerBusy()
+  // will be called once the gpu becomes available and the begin-frames can be
+  // dispatched to clients again.
+  bool RequestCallbackOnGpuAvailable();
+  virtual void OnGpuNoLongerBusy() = 0;
+
  private:
   // The higher 32 bits are used for a process restart id that changes if a
   // process allocating BeginFrameSources has been restarted. The lower 32 bits
   // are allocated from an atomic sequence.
   const uint64_t source_id_;
 
+  // The BeginFrameSource should not send the begin-frame messages to clients if
+  // gpu is busy.
+  bool is_gpu_busy_ = false;
+  // Keeps track of whether a begin-frame was paused, and whether
+  // OnGpuNoLongerBusy() should be invoked when the gpu is no longer busy.
+  bool request_notification_on_gpu_availability_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(BeginFrameSource);
 };
 
@@ -165,6 +184,7 @@
   void AddObserver(BeginFrameObserver* obs) override {}
   void RemoveObserver(BeginFrameObserver* obs) override {}
   bool IsThrottled() const override;
+  void OnGpuNoLongerBusy() override {}
 };
 
 // A frame source which ticks itself independently.
@@ -192,6 +212,7 @@
   void RemoveObserver(BeginFrameObserver* obs) override;
   void DidFinishFrame(BeginFrameObserver* obs) override;
   bool IsThrottled() const override;
+  void OnGpuNoLongerBusy() override;
 
   // SyntheticBeginFrameSource implementation.
   void OnUpdateVSyncParameters(base::TimeTicks timebase,
@@ -225,6 +246,7 @@
   void RemoveObserver(BeginFrameObserver* obs) override;
   void DidFinishFrame(BeginFrameObserver* obs) override {}
   bool IsThrottled() const override;
+  void OnGpuNoLongerBusy() override;
 
   // SyntheticBeginFrameSource implementation.
   void OnUpdateVSyncParameters(base::TimeTicks timebase,
@@ -235,6 +257,8 @@
 
  private:
   BeginFrameArgs CreateBeginFrameArgs(base::TimeTicks frame_time);
+  void IssueBeginFrameToObserver(BeginFrameObserver* obs,
+                                 const BeginFrameArgs& args);
 
   std::unique_ptr<DelayBasedTimeSource> time_source_;
   std::unordered_set<BeginFrameObserver*> observers_;
@@ -270,6 +294,7 @@
   void DidFinishFrame(BeginFrameObserver* obs) override {}
   bool IsThrottled() const override;
   void AsValueInto(base::trace_event::TracedValue* state) const override;
+  void OnGpuNoLongerBusy() override;
 
   void OnSetBeginFrameSourcePaused(bool paused);
   void OnBeginFrame(const BeginFrameArgs& args);
@@ -286,6 +311,8 @@
   bool paused_ = false;
 
  private:
+  BeginFrameArgs pending_begin_frame_args_;
+
   DISALLOW_COPY_AND_ASSIGN(ExternalBeginFrameSource);
 };
 
diff --git a/components/viz/service/display/display_scheduler.cc b/components/viz/service/display/display_scheduler.cc
index dd6451a..5c32768 100644
--- a/components/viz/service/display/display_scheduler.cc
+++ b/components/viz/service/display/display_scheduler.cc
@@ -48,6 +48,9 @@
 }
 
 DisplayScheduler::~DisplayScheduler() {
+  // It is possible for DisplayScheduler to be destroyed while there's an
+  // in-flight swap. So always mark the gpu as not busy during destruction.
+  begin_frame_source_->SetIsGpuBusy(false);
   StopObservingBeginFrames();
 }
 
@@ -504,18 +507,22 @@
 void DisplayScheduler::DidFinishFrame(bool did_draw) {
   DCHECK(begin_frame_source_);
   begin_frame_source_->DidFinishFrame(this);
-
   BeginFrameAck ack(current_begin_frame_args_, did_draw);
   client_->DidFinishFrame(ack);
 }
 
 void DisplayScheduler::DidSwapBuffers() {
   pending_swaps_++;
+  if (pending_swaps_ == max_pending_swaps_)
+    begin_frame_source_->SetIsGpuBusy(true);
+
   uint32_t swap_id = next_swap_id_++;
   TRACE_EVENT_ASYNC_BEGIN0("viz", "DisplayScheduler:pending_swaps", swap_id);
 }
 
 void DisplayScheduler::DidReceiveSwapBuffersAck() {
+  begin_frame_source_->SetIsGpuBusy(false);
+
   uint32_t swap_id = next_swap_id_ - pending_swaps_;
   pending_swaps_--;
   TRACE_EVENT_ASYNC_END0("viz", "DisplayScheduler:pending_swaps", swap_id);
diff --git a/components/viz/service/display/renderer_pixeltest.cc b/components/viz/service/display/renderer_pixeltest.cc
index f5a377a..96c7d8a 100644
--- a/components/viz/service/display/renderer_pixeltest.cc
+++ b/components/viz/service/display/renderer_pixeltest.cc
@@ -3963,6 +3963,58 @@
       cc::ExactPixelComparator(true)));
 }
 
+class SkiaRendererPixelTestWithOverdrawFeedback : public SkiaRendererPixelTest {
+ protected:
+  void SetUp() override {
+    renderer_settings_.show_overdraw_feedback = true;
+    SkiaRendererPixelTest::SetUp();
+  }
+};
+
+TEST_F(SkiaRendererPixelTestWithOverdrawFeedback, TranslucentRectangles) {
+  gfx::Rect rect(this->device_viewport_size_);
+
+  int id = 1;
+  gfx::Transform transform_to_root;
+  std::unique_ptr<RenderPass> pass =
+      CreateTestRenderPass(id, rect, transform_to_root);
+
+  gfx::Transform dark_gray_quad_to_target_transform;
+  dark_gray_quad_to_target_transform.Translate(50, 50);
+  dark_gray_quad_to_target_transform.Scale(
+      0.5f + 1.0f / (rect.width() * 2.0f),
+      0.5f + 1.0f / (rect.height() * 2.0f));
+  SharedQuadState* dark_gray_shared_state = CreateTestSharedQuadState(
+      dark_gray_quad_to_target_transform, rect, pass.get());
+
+  auto* dark_gray = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
+  dark_gray->SetNew(dark_gray_shared_state, rect, rect, 0x10444444, false);
+
+  gfx::Transform light_gray_quad_to_target_transform;
+  light_gray_quad_to_target_transform.Translate(25.5f, 25.5f);
+  light_gray_quad_to_target_transform.Scale(0.5f, 0.5f);
+  SharedQuadState* light_gray_shared_state = CreateTestSharedQuadState(
+      light_gray_quad_to_target_transform, rect, pass.get());
+
+  auto* light_gray = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
+  light_gray->SetNew(light_gray_shared_state, rect, rect, 0x10CCCCCC, false);
+
+  gfx::Transform bg_quad_to_target_transform;
+  SharedQuadState* bg_shared_state =
+      CreateTestSharedQuadState(bg_quad_to_target_transform, rect, pass.get());
+
+  auto* bg = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
+  bg->SetNew(bg_shared_state, rect, rect, SK_ColorBLACK, false);
+
+  RenderPassList pass_list;
+  pass_list.push_back(std::move(pass));
+
+  EXPECT_TRUE(this->RunPixelTest(
+      &pass_list,
+      base::FilePath(FILE_PATH_LITERAL("skia_translucent_rectangles.png")),
+      cc::ExactPixelComparator(true)));
+}
+
 using ColorSpacePair = std::tuple<gfx::ColorSpace, gfx::ColorSpace, bool>;
 
 class ColorTransformPixelTest
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index d52fb25..6d7bbe4 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -260,20 +260,6 @@
   if (sync_queries_) {
     sync_queries_->EndCurrentFrame();
   }
-
-  if (settings_->show_overdraw_feedback) {
-    sk_sp<SkImage> image = overdraw_surface_->makeImageSnapshot();
-    SkPaint paint;
-    // TODO(xing.xu) : handle this in CPU mode, the R and B should be switched
-    // in CPU mode. (http://crbug.com/896969)
-    static const SkPMColor colors[SkOverdrawColorFilter::kNumColors] = {
-        0x00000000, 0x00000000, 0x2fff0000, 0x2f00ff00, 0x3f0000ff, 0x7f0000ff,
-    };
-    sk_sp<SkColorFilter> color_filter = SkOverdrawColorFilter::Make(colors);
-    paint.setColorFilter(color_filter);
-    root_surface_->getCanvas()->drawImage(image.get(), 0, 0, &paint);
-    root_surface_->getCanvas()->flush();
-  }
   non_root_surface_ = nullptr;
   current_canvas_ = nullptr;
   current_surface_ = nullptr;
@@ -424,7 +410,6 @@
     nway_canvas_->addCanvas(overdraw_canvas_.get());
     nway_canvas_->addCanvas(root_canvas_);
     current_canvas_ = nway_canvas_.get();
-    current_surface_ = overdraw_surface_.get();
   }
 }
 
@@ -1120,6 +1105,24 @@
     }
     case DrawMode::GL:  // Fallthrough
     case DrawMode::VULKAN: {
+      // For SkiaRendererPixelTestWithOverdrawFeedback, CopyDrawnRenderPass
+      // happens before FinishDrawingFrame which results in an empty image. So
+      // force a draw here.
+      if (settings_->show_overdraw_feedback &&
+          (current_frame()->current_render_pass ==
+           current_frame()->root_render_pass)) {
+        sk_sp<SkImage> image = overdraw_surface_->makeImageSnapshot();
+        SkPaint paint;
+        // TODO(xing.xu) : handle this in CPU mode, the R and B should be
+        // switched in CPU mode. (http://crbug.com/896969)
+        static const SkPMColor colors[SkOverdrawColorFilter::kNumColors] = {
+            0x00000000, 0x00000000, 0x2fff0000,
+            0x2f00ff00, 0x3f0000ff, 0x7f0000ff,
+        };
+        sk_sp<SkColorFilter> color_filter = SkOverdrawColorFilter::Make(colors);
+        paint.setColorFilter(color_filter);
+        current_surface_->getCanvas()->drawImage(image.get(), 0, 0, &paint);
+      }
       current_canvas_->flush();
       break;
     }
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.h b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
index 923eb24..4823d4d 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support.h
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
@@ -168,6 +168,12 @@
   // string.
   static const char* GetSubmitResultAsString(SubmitResult result);
 
+  const std::vector<
+      std::pair<LocalSurfaceId, std::unique_ptr<CopyOutputRequest>>>&
+  copy_output_requests_for_testing() const {
+    return copy_output_requests_;
+  }
+
  private:
   friend class FrameSinkManagerTest;
 
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
index c5ed796..3f4d721a 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
@@ -76,15 +76,16 @@
   DCHECK(!binding_.is_bound());
   binding_.Bind(std::move(request), std::move(task_runner));
   client_ptr_ = std::move(client);
-
   client_ = client_ptr_.get();
 }
 
 void FrameSinkManagerImpl::SetLocalClient(
-    mojom::FrameSinkManagerClient* client) {
+    mojom::FrameSinkManagerClient* client,
+    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
   DCHECK(!client_ptr_);
-
+  DCHECK(!ui_task_runner_);
   client_ = client;
+  ui_task_runner_ = ui_task_runner;
 }
 
 void FrameSinkManagerImpl::ForceShutdown() {
@@ -497,12 +498,28 @@
                                             std::move(hit_test_region_list));
 }
 
-void FrameSinkManagerImpl::OnFrameTokenChanged(const FrameSinkId& frame_sink_id,
-                                               uint32_t frame_token) {
+void FrameSinkManagerImpl::OnFrameTokenChangedDirect(
+    const FrameSinkId& frame_sink_id,
+    uint32_t frame_token) {
   if (client_)
     client_->OnFrameTokenChanged(frame_sink_id, frame_token);
 }
 
+void FrameSinkManagerImpl::OnFrameTokenChanged(const FrameSinkId& frame_sink_id,
+                                               uint32_t frame_token) {
+  if (client_ptr_ || !ui_task_runner_) {
+    // This is a Mojo client or a locally-connected client *without* a task
+    // runner. In this case, call directly.
+    OnFrameTokenChangedDirect(frame_sink_id, frame_token);
+  } else {
+    // This is a locally-connected client *with* a task runner - post task.
+    ui_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&FrameSinkManagerImpl::OnFrameTokenChangedDirect,
+                       base::Unretained(this), frame_sink_id, frame_token));
+  }
+}
+
 VideoDetector* FrameSinkManagerImpl::CreateVideoDetectorForTesting(
     const base::TickClock* tick_clock,
     scoped_refptr<base::SequencedTaskRunner> task_runner) {
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.h b/components/viz/service/frame_sinks/frame_sink_manager_impl.h
index 6f43a48..7dca8e5 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.h
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.h
@@ -71,7 +71,9 @@
                         mojom::FrameSinkManagerClientPtr client);
 
   // Sets up a direction connection to |client| without using Mojo.
-  void SetLocalClient(mojom::FrameSinkManagerClient* client);
+  void SetLocalClient(
+      mojom::FrameSinkManagerClient* client,
+      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner = nullptr);
 
   // mojom::FrameSinkManager implementation:
   void RegisterFrameSinkId(const FrameSinkId& frame_sink_id,
@@ -163,6 +165,9 @@
       const base::TickClock* tick_clock,
       scoped_refptr<base::SequencedTaskRunner> task_runner);
 
+  void OnFrameTokenChangedDirect(const FrameSinkId& frame_sink_id,
+                                 uint32_t frame_token);
+
   // Called when |frame_token| is changed on a submitted CompositorFrame.
   void OnFrameTokenChanged(const FrameSinkId& frame_sink_id,
                            uint32_t frame_token);
@@ -287,10 +292,20 @@
   // platforms that don't need video detection.
   std::unique_ptr<VideoDetector> video_detector_;
 
-  // This will point to |client_ptr_| if using Mojo or a provided client if
-  // directly connected. Use this to make function calls.
+  // There are three states this can be in:
+  //  1. Mojo client: |client_| will point to |client_ptr_|, the Mojo client,
+  //     and |ui_task_runner_| will not be used. Calls to OnFrameTokenChanged()
+  //     will go through Mojo. This is the normal state.
+  //  2. Local (directly connected) client, *with* task runner: |client_| will
+  //     point to the client, |client_ptr_| will be nullptr, and calls to
+  //     OnFrameTokenChanged() will be PostTasked using |ui_task_runner_|. Used
+  //     mostly for layout tests.
+  //  3. Local (directly connected) client, *without* task runner: |client_|
+  //     will point to the client, |client_ptr_| and |ui_task_runner_| will be
+  //     nullptr, and calls to OnFrameTokenChanged() will be directly called
+  //     (without PostTask) on |client_|. Used for some unit tests.
   mojom::FrameSinkManagerClient* client_ = nullptr;
-
+  scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_ = nullptr;
   mojom::FrameSinkManagerClientPtr client_ptr_;
   mojo::Binding<mojom::FrameSinkManager> binding_;
 
diff --git a/components/viz/service/frame_sinks/primary_begin_frame_source.cc b/components/viz/service/frame_sinks/primary_begin_frame_source.cc
index 5da517c6..181f296ec 100644
--- a/components/viz/service/frame_sinks/primary_begin_frame_source.cc
+++ b/components/viz/service/frame_sinks/primary_begin_frame_source.cc
@@ -75,6 +75,11 @@
              : true;
 }
 
+void PrimaryBeginFrameSource::OnGpuNoLongerBusy() {
+  // PrimaryBeginFrameSource does not hold back the begin frames. So it doesn't
+  // need to do anything here.
+}
+
 void PrimaryBeginFrameSource::OnNeedsBeginFrames(bool needs_begin_frames) {
   if (needs_begin_frames_ == needs_begin_frames)
     return;
diff --git a/components/viz/service/frame_sinks/primary_begin_frame_source.h b/components/viz/service/frame_sinks/primary_begin_frame_source.h
index 4abbcd21..3ba4bf98 100644
--- a/components/viz/service/frame_sinks/primary_begin_frame_source.h
+++ b/components/viz/service/frame_sinks/primary_begin_frame_source.h
@@ -36,6 +36,7 @@
   void AddObserver(BeginFrameObserver* obs) override;
   void RemoveObserver(BeginFrameObserver* obs) override;
   bool IsThrottled() const override;
+  void OnGpuNoLongerBusy() override;
 
   // ExternalBeginFrameSourceClient implementation.
   void OnNeedsBeginFrames(bool needs_begin_frames) override;
diff --git a/components/viz/test/data/skia_translucent_rectangles.png b/components/viz/test/data/skia_translucent_rectangles.png
new file mode 100644
index 0000000..3b9736c
--- /dev/null
+++ b/components/viz/test/data/skia_translucent_rectangles.png
Binary files differ
diff --git a/components/viz/test/fake_external_begin_frame_source.h b/components/viz/test/fake_external_begin_frame_source.h
index 957067c5..39f893b 100644
--- a/components/viz/test/fake_external_begin_frame_source.h
+++ b/components/viz/test/fake_external_begin_frame_source.h
@@ -39,6 +39,7 @@
   void RemoveObserver(BeginFrameObserver* obs) override;
   void DidFinishFrame(BeginFrameObserver* obs) override;
   bool IsThrottled() const override;
+  void OnGpuNoLongerBusy() override {}
 
   BeginFrameArgs CreateBeginFrameArgs(
       BeginFrameArgs::CreationLocation location);
diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc
index d6320ee..9652862 100644
--- a/content/app/content_main_runner_impl.cc
+++ b/content/app/content_main_runner_impl.cc
@@ -401,12 +401,6 @@
 
 #endif  // OS_LINUX
 
-bool IsRootProcess() {
-  const base::CommandLine& command_line =
-      *base::CommandLine::ForCurrentProcess();
-  return command_line.GetSwitchValueASCII(switches::kProcessType).empty();
-}
-
 }  // namespace
 
 class ContentClientInitializer {
@@ -664,14 +658,6 @@
     return exit_code;
   completed_basic_startup_ = true;
 
-  // We will need to use data from resources.pak in later cl, so load the file
-  // now.
-  if (IsRootProcess()) {
-    ui::DataPack* data_pack = delegate_->LoadServiceManifestDataPack();
-    // TODO(ranj): Read manifest from this data pack.
-    ignore_result(data_pack);
-  }
-
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
   std::string process_type =
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index d26f4153..44d5ba9 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1188,12 +1188,6 @@
     "media/video_decoder_proxy.h",
     "media/webaudio/audio_context_manager_impl.cc",
     "media/webaudio/audio_context_manager_impl.h",
-    "memory/memory_condition_observer.cc",
-    "memory/memory_condition_observer.h",
-    "memory/memory_coordinator_default_policy.cc",
-    "memory/memory_coordinator_default_policy.h",
-    "memory/memory_coordinator_impl.cc",
-    "memory/memory_coordinator_impl.h",
     "memory/memory_monitor.cc",
     "memory/memory_monitor.h",
     "memory/memory_monitor_android.cc",
diff --git a/content/browser/appcache/appcache_request_handler.cc b/content/browser/appcache/appcache_request_handler.cc
index 42578121..ab4d742 100644
--- a/content/browser/appcache/appcache_request_handler.cc
+++ b/content/browser/appcache/appcache_request_handler.cc
@@ -570,6 +570,7 @@
 }
 
 bool AppCacheRequestHandler::MaybeCreateLoaderForResponse(
+    const GURL& request_url,
     const network::ResourceResponseHead& response,
     network::mojom::URLLoaderPtr* loader,
     network::mojom::URLLoaderClientRequest* client_request,
diff --git a/content/browser/appcache/appcache_request_handler.h b/content/browser/appcache/appcache_request_handler.h
index 40249b7..c3292f35 100644
--- a/content/browser/appcache/appcache_request_handler.h
+++ b/content/browser/appcache/appcache_request_handler.h
@@ -81,6 +81,7 @@
       FallbackCallback fallback_callback) override;
   // MaybeCreateLoaderForResponse always returns synchronously.
   bool MaybeCreateLoaderForResponse(
+      const GURL& request_url,
       const network::ResourceResponseHead& response,
       network::mojom::URLLoaderPtr* loader,
       network::mojom::URLLoaderClientRequest* client_request,
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 88327ae..a9d7b13 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -1273,7 +1273,8 @@
           switches::GetDeadlineToSynchronizeSurfaces());
 
       surface_utils::ConnectWithLocalFrameSinkManager(
-          host_frame_sink_manager_.get(), frame_sink_manager_impl_.get());
+          host_frame_sink_manager_.get(), frame_sink_manager_impl_.get(),
+          base::ThreadTaskRunnerHandle::Get());
 
       ImageTransportFactory::SetFactory(
           std::make_unique<GpuProcessTransportFactory>(
diff --git a/content/browser/compositor/surface_utils.cc b/content/browser/compositor/surface_utils.cc
index a1cf48ef..e7033f4b 100644
--- a/content/browser/compositor/surface_utils.cc
+++ b/content/browser/compositor/surface_utils.cc
@@ -52,9 +52,11 @@
 
 void ConnectWithLocalFrameSinkManager(
     viz::HostFrameSinkManager* host_frame_sink_manager,
-    viz::FrameSinkManagerImpl* frame_sink_manager_impl) {
+    viz::FrameSinkManagerImpl* frame_sink_manager_impl,
+    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
   host_frame_sink_manager->SetLocalManager(frame_sink_manager_impl);
-  frame_sink_manager_impl->SetLocalClient(host_frame_sink_manager);
+  frame_sink_manager_impl->SetLocalClient(host_frame_sink_manager,
+                                          ui_task_runner);
 }
 
 }  // namespace surface_utils
diff --git a/content/browser/compositor/surface_utils.h b/content/browser/compositor/surface_utils.h
index 10249c3..8a34418a 100644
--- a/content/browser/compositor/surface_utils.h
+++ b/content/browser/compositor/surface_utils.h
@@ -7,6 +7,8 @@
 
 #include <memory>
 
+#include "base/memory/scoped_refptr.h"
+#include "base/single_thread_task_runner.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
 #include "content/common/content_export.h"
 
@@ -28,7 +30,8 @@
 // Directly connects HostFrameSinkManager to FrameSinkManagerImpl without Mojo.
 CONTENT_EXPORT void ConnectWithLocalFrameSinkManager(
     viz::HostFrameSinkManager* host_frame_sink_manager,
-    viz::FrameSinkManagerImpl* frame_sink_manager_impl);
+    viz::FrameSinkManagerImpl* frame_sink_manager_impl,
+    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner = nullptr);
 
 }  // namespace surface_utils
 
diff --git a/content/browser/devtools/devtools_network_interceptor.cc b/content/browser/devtools/devtools_network_interceptor.cc
index 500932d3..c9a9de2 100644
--- a/content/browser/devtools/devtools_network_interceptor.cc
+++ b/content/browser/devtools/devtools_network_interceptor.cc
@@ -40,14 +40,16 @@
 
 DevToolsNetworkInterceptor::Modifications::Modifications(
     base::Optional<net::Error> error_reason,
-    base::Optional<std::string> raw_response,
+    scoped_refptr<net::HttpResponseHeaders> response_headers,
+    std::unique_ptr<std::string> response_body,
     protocol::Maybe<std::string> modified_url,
     protocol::Maybe<std::string> modified_method,
     protocol::Maybe<std::string> modified_post_data,
     std::unique_ptr<HeadersVector> modified_headers,
     std::unique_ptr<AuthChallengeResponse> auth_challenge_response)
     : error_reason(std::move(error_reason)),
-      raw_response(std::move(raw_response)),
+      response_headers(std::move(response_headers)),
+      response_body(std::move(response_body)),
       modified_url(std::move(modified_url)),
       modified_method(std::move(modified_method)),
       modified_post_data(std::move(modified_post_data)),
diff --git a/content/browser/devtools/devtools_network_interceptor.h b/content/browser/devtools/devtools_network_interceptor.h
index 331e326..0a2858c 100644
--- a/content/browser/devtools/devtools_network_interceptor.h
+++ b/content/browser/devtools/devtools_network_interceptor.h
@@ -76,7 +76,8 @@
     Modifications();
     Modifications(
         base::Optional<net::Error> error_reason,
-        base::Optional<std::string> raw_response,
+        scoped_refptr<net::HttpResponseHeaders> response_headers,
+        std::unique_ptr<std::string> response_body,
         protocol::Maybe<std::string> modified_url,
         protocol::Maybe<std::string> modified_method,
         protocol::Maybe<std::string> modified_post_data,
@@ -87,7 +88,10 @@
     // If none of the following are set then the request will be allowed to
     // continue unchanged.
     base::Optional<net::Error> error_reason;   // Finish with error.
-    base::Optional<std::string> raw_response;  // Finish with mock response.
+    // If either of the below fields is set, complete the request by
+    // responding with the provided headers and body.
+    scoped_refptr<net::HttpResponseHeaders> response_headers;
+    std::unique_ptr<std::string> response_body;
 
     // Optionally modify before sending to network.
     protocol::Maybe<std::string> modified_url;
diff --git a/content/browser/devtools/devtools_url_interceptor_request_job.cc b/content/browser/devtools/devtools_url_interceptor_request_job.cc
index cfc0913a..dd860149 100644
--- a/content/browser/devtools/devtools_url_interceptor_request_job.cc
+++ b/content/browser/devtools/devtools_url_interceptor_request_job.cc
@@ -385,14 +385,8 @@
 
 class DevToolsURLInterceptorRequestJob::MockResponseDetails {
  public:
-  MockResponseDetails(std::string response_bytes,
-                      base::TimeTicks response_time);
-
-  MockResponseDetails(
-      const scoped_refptr<net::HttpResponseHeaders>& response_headers,
-      std::string response_bytes,
-      size_t read_offset,
-      base::TimeTicks response_time);
+  MockResponseDetails(scoped_refptr<net::HttpResponseHeaders> response_headers,
+                      std::unique_ptr<std::string> response_bytes);
 
   ~MockResponseDetails();
 
@@ -412,36 +406,19 @@
 };
 
 DevToolsURLInterceptorRequestJob::MockResponseDetails::MockResponseDetails(
-    std::string response_bytes,
-    base::TimeTicks response_time)
-    : response_bytes_(std::move(response_bytes)),
+    scoped_refptr<net::HttpResponseHeaders> response_headers,
+    std::unique_ptr<std::string> response_bytes)
+    : response_headers_(std::move(response_headers)),
+      response_bytes_(response_bytes ? std::move(*response_bytes) : ""),
       read_offset_(0),
-      response_time_(response_time) {
-  int header_size = net::HttpUtil::LocateEndOfHeaders(response_bytes_.c_str(),
-                                                      response_bytes_.size());
-  if (header_size == -1) {
-    LOG(WARNING) << "Can't find headers in result";
-    response_headers_ = new net::HttpResponseHeaders("");
-  } else {
-    response_headers_ =
-        new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders(
-            response_bytes_.c_str(), header_size));
-    read_offset_ = header_size;
+      response_time_(base::TimeTicks::Now()) {
+  if (!response_headers) {
+    static const char kDummyHeaders[] = "HTTP/1.1 200 OK\0\0";
+    response_headers =
+        base::MakeRefCounted<net::HttpResponseHeaders>(kDummyHeaders);
   }
-
-  CHECK_LE(read_offset_, response_bytes_.size());
 }
 
-DevToolsURLInterceptorRequestJob::MockResponseDetails::MockResponseDetails(
-    const scoped_refptr<net::HttpResponseHeaders>& response_headers,
-    std::string response_bytes,
-    size_t read_offset,
-    base::TimeTicks response_time)
-    : response_headers_(response_headers),
-      response_bytes_(std::move(response_bytes)),
-      read_offset_(read_offset),
-      response_time_(response_time) {}
-
 DevToolsURLInterceptorRequestJob::MockResponseDetails::~MockResponseDetails() {}
 
 int DevToolsURLInterceptorRequestJob::MockResponseDetails::ReadRawData(
@@ -914,10 +891,7 @@
     // Fallthough.
     case WaitingForUserResponse::WAITING_FOR_REQUEST_ACK:
       ProcessInterceptionResponse(
-          std::make_unique<DevToolsNetworkInterceptor::Modifications>(
-              base::nullopt, base::nullopt, protocol::Maybe<std::string>(),
-              protocol::Maybe<std::string>(), protocol::Maybe<std::string>(),
-              nullptr, nullptr));
+          std::make_unique<DevToolsNetworkInterceptor::Modifications>());
       return;
     case WaitingForUserResponse::WAITING_FOR_AUTH_ACK:
       ProcessAuthResponse(DevToolsNetworkInterceptor::AuthChallengeResponse(
@@ -996,9 +970,8 @@
   raw_headers.append("Location: ");
   raw_headers.append(new_url);
   raw_headers.append(2, '\0');
-  mock_response_details_.reset(new MockResponseDetails(
-      base::MakeRefCounted<net::HttpResponseHeaders>(raw_headers), "", 0,
-      base::TimeTicks::Now()));
+  mock_response_details_ = std::make_unique<MockResponseDetails>(
+      base::MakeRefCounted<net::HttpResponseHeaders>(raw_headers), nullptr);
 
   NotifyHeadersComplete();
 }
@@ -1071,9 +1044,10 @@
     return;
   }
 
-  if (modifications->raw_response) {
-    mock_response_details_.reset(new MockResponseDetails(
-        std::move(*modifications->raw_response), base::TimeTicks::Now()));
+  if (modifications->response_headers || modifications->response_body) {
+    mock_response_details_ = std::make_unique<MockResponseDetails>(
+        std::move(modifications->response_headers),
+        std::move(modifications->response_body));
 
     // Set cookies in the network stack.
     net::CookieOptions options;
diff --git a/content/browser/devtools/devtools_url_loader_interceptor.cc b/content/browser/devtools/devtools_url_loader_interceptor.cc
index c524694..f689423 100644
--- a/content/browser/devtools/devtools_url_loader_interceptor.cc
+++ b/content/browser/devtools/devtools_url_loader_interceptor.cc
@@ -216,13 +216,12 @@
   void ProcessAuthResponse(
       const DevToolsNetworkInterceptor::AuthChallengeResponse&
           auth_challenge_response);
-  Response ProcessResponseOverride(std::string response);
+  Response ProcessResponseOverride(
+      scoped_refptr<net::HttpResponseHeaders> headers,
+      std::unique_ptr<std::string> body);
   void ProcessRedirectByClient(const GURL& redirect_url);
   void ProcessSetCookies(const net::HttpResponseHeaders& response_headers,
                          base::OnceClosure callback);
-  void SendResponseAfterCookiesSet(const std::string& response,
-                                   int header_size,
-                                   size_t body_size);
   void SendResponse(const base::StringPiece& body);
   void ApplyModificationsToRequest(
       std::unique_ptr<Modifications> modifications);
@@ -828,8 +827,9 @@
     return Response::OK();
   }
 
-  if (modifications->raw_response)
-    return ProcessResponseOverride(std::move(*modifications->raw_response));
+  if (modifications->response_headers || modifications->response_body)
+    return ProcessResponseOverride(std::move(modifications->response_headers),
+                                   std::move(modifications->response_body));
 
   if (state_ == State::kFollowRedirect) {
     if (modifications->modified_url.isJust()) {
@@ -940,21 +940,13 @@
   }
 }
 
-Response InterceptionJob::ProcessResponseOverride(std::string response) {
+Response InterceptionJob::ProcessResponseOverride(
+    scoped_refptr<net::HttpResponseHeaders> headers,
+    std::unique_ptr<std::string> maybe_body) {
   CancelRequest();
 
-  std::string raw_headers;
-  int header_size =
-      net::HttpUtil::LocateEndOfHeaders(response.c_str(), response.size());
-  if (header_size == -1) {
-    LOG(WARNING) << "Can't find headers in result";
-    header_size = 0;
-  } else {
-    raw_headers =
-        net::HttpUtil::AssembleRawHeaders(response.c_str(), header_size);
-  }
-  CHECK_LE(static_cast<size_t>(header_size), response.size());
-  size_t body_size = response.size() - header_size;
+  std::string body = maybe_body ? std::move(*maybe_body) : "";
+  size_t body_size = body.size();
 
   response_metadata_ = std::make_unique<ResponseMetadata>();
   network::ResourceResponseHead* head = &response_metadata_->head;
@@ -969,19 +961,27 @@
   head->load_timing.request_start = start_ticks_;
   head->load_timing.receive_headers_end = now_ticks;
 
-  head->headers = new net::HttpResponseHeaders(std::move(raw_headers));
+  static const char kDummyHeaders[] = "HTTP/1.1 200 OK\0\0";
+  head->headers = std::move(headers);
+  if (!head->headers) {
+    head->headers =
+        base::MakeRefCounted<net::HttpResponseHeaders>(kDummyHeaders);
+  }
   head->headers->GetMimeTypeAndCharset(&head->mime_type, &head->charset);
   if (head->mime_type.empty()) {
     size_t bytes_to_sniff =
         std::min(body_size, static_cast<size_t>(net::kMaxBytesToSniff));
-    net::SniffMimeType(response.data() + header_size, bytes_to_sniff,
-                       create_loader_params_->request.url, "",
-                       net::ForceSniffFileUrlsForHtml::kDisabled,
-                       &head->mime_type);
+    net::SniffMimeType(
+        body.data(), bytes_to_sniff, create_loader_params_->request.url, "",
+        net::ForceSniffFileUrlsForHtml::kDisabled, &head->mime_type);
     head->did_mime_sniff = true;
   }
+  // TODO(caseq): we're cheating here a bit, raw_headers() have \0's
+  // where real headers would have \r\n, but the sizes here
+  // probably don't have to be exact.
+  size_t headers_size = head->headers->raw_headers().size();
   head->content_length = body_size;
-  head->encoded_data_length = header_size;
+  head->encoded_data_length = headers_size;
   head->encoded_body_length = 0;
   head->request_start = start_ticks_;
   head->response_start = now_ticks;
@@ -989,7 +989,7 @@
   response_metadata_->transfer_size = body_size;
 
   response_metadata_->status.completion_time = base::TimeTicks::Now();
-  response_metadata_->status.encoded_data_length = response.size();
+  response_metadata_->status.encoded_data_length = headers_size + body_size;
   response_metadata_->status.encoded_body_length = body_size;
   response_metadata_->status.decoded_body_length = body_size;
 
@@ -1004,21 +1004,15 @@
     }
   }
   if (!continue_after_cookies_set) {
-    continue_after_cookies_set = base::BindOnce(
-        &InterceptionJob::SendResponseAfterCookiesSet, base::Unretained(this),
-        std::move(response), header_size, body_size);
+    continue_after_cookies_set =
+        base::BindOnce(&InterceptionJob::SendResponse, base::Unretained(this),
+                       std::move(body));
   }
   ProcessSetCookies(*head->headers, std::move(continue_after_cookies_set));
 
   return Response::OK();
 }
 
-void InterceptionJob::SendResponseAfterCookiesSet(const std::string& response,
-                                                  int header_size,
-                                                  size_t body_size) {
-  SendResponse(base::StringPiece(response.data() + header_size, body_size));
-}
-
 void InterceptionJob::ProcessSetCookies(const net::HttpResponseHeaders& headers,
                                         base::OnceClosure callback) {
   if (create_loader_params_->request.load_flags &
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index f7bbc60..22580674 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -1861,14 +1861,30 @@
     Maybe<protocol::Network::Headers> opt_headers,
     Maybe<protocol::Network::AuthChallengeResponse> auth_challenge_response,
     std::unique_ptr<ContinueInterceptedRequestCallback> callback) {
-  base::Optional<std::string> raw_response;
+  scoped_refptr<net::HttpResponseHeaders> response_headers;
+  std::unique_ptr<std::string> response_body;
+
   if (base64_raw_response.isJust()) {
     std::string decoded;
     if (!base::Base64Decode(base64_raw_response.fromJust(), &decoded)) {
       callback->sendFailure(Response::InvalidParams("Invalid rawResponse."));
       return;
     }
-    raw_response = decoded;
+
+    std::string raw_headers;
+    int header_size =
+        net::HttpUtil::LocateEndOfHeaders(decoded.c_str(), decoded.size());
+    if (header_size == -1) {
+      LOG(WARNING) << "Can't find headers in raw response";
+      header_size = 0;
+    } else {
+      raw_headers =
+          net::HttpUtil::AssembleRawHeaders(decoded.c_str(), header_size);
+    }
+    CHECK_LE(static_cast<size_t>(header_size), decoded.size());
+    response_headers =
+        base::MakeRefCounted<net::HttpResponseHeaders>(std::move(raw_headers));
+    response_body = std::make_unique<std::string>(decoded.substr(header_size));
   }
 
   base::Optional<net::Error> error;
@@ -1926,8 +1942,9 @@
 
   auto modifications =
       std::make_unique<DevToolsNetworkInterceptor::Modifications>(
-          std::move(error), std::move(raw_response), std::move(url),
-          std::move(method), std::move(post_data), std::move(override_headers),
+          std::move(error), std::move(response_headers),
+          std::move(response_body), std::move(url), std::move(method),
+          std::move(post_data), std::move(override_headers),
           std::move(override_auth));
 
   if (url_loader_interceptor_) {
diff --git a/content/browser/frame_host/render_frame_message_filter.cc b/content/browser/frame_host/render_frame_message_filter.cc
index affa952..fcde891 100644
--- a/content/browser/frame_host/render_frame_message_filter.cc
+++ b/content/browser/frame_host/render_frame_message_filter.cc
@@ -535,7 +535,6 @@
                                          const GURL& site_for_cookies,
                                          const std::string& cookie_line,
                                          SetCookieCallback callback) {
-  std::move(callback).Run();
   ChildProcessSecurityPolicyImpl* policy =
       ChildProcessSecurityPolicyImpl::GetInstance();
   if (!policy->CanAccessDataForOrigin(render_process_id_, url)) {
@@ -544,18 +543,22 @@
     SYSLOG(WARNING) << "Killing renderer: illegal cookie write. Reason: "
                     << reason;
     bad_message::ReceivedBadMessage(this, reason);
+    std::move(callback).Run();
     return;
   }
 
   net::CookieOptions options;
   std::unique_ptr<net::CanonicalCookie> cookie = net::CanonicalCookie::Create(
       url, cookie_line, base::Time::Now(), options);
-  if (!cookie)
+  if (!cookie) {
+    std::move(callback).Run();
     return;
+  }
 
   if (!GetContentClient()->browser()->AllowSetCookie(
           url, site_for_cookies, *cookie, resource_context_, render_process_id_,
           render_frame_id)) {
+    std::move(callback).Run();
     return;
   }
 
@@ -567,6 +570,7 @@
           url, resource_context_);
   if (cookie_store ||
       !base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+    std::move(callback).Run();
     if (!cookie_store)
       cookie_store = request_context_->GetURLRequestContext()->cookie_store();
 
@@ -577,10 +581,14 @@
     return;
   }
 
+  net::CookieStore::SetCookiesCallback net_callback =
+      base::BindOnce([](SetCookieCallback callback,
+                        bool success) { std::move(callback).Run(); },
+                     std::move(callback));
   (*GetCookieManager())
       ->SetCanonicalCookie(*cookie, url.SchemeIsCryptographic(),
                            !options.exclude_httponly(),
-                           net::CookieStore::SetCookiesCallback());
+                           std::move(net_callback));
 }
 
 void RenderFrameMessageFilter::GetCookies(int render_frame_id,
diff --git a/content/browser/loader/navigation_loader_interceptor.cc b/content/browser/loader/navigation_loader_interceptor.cc
index 630456cb7..c46c4b7 100644
--- a/content/browser/loader/navigation_loader_interceptor.cc
+++ b/content/browser/loader/navigation_loader_interceptor.cc
@@ -14,6 +14,7 @@
 }
 
 bool NavigationLoaderInterceptor::MaybeCreateLoaderForResponse(
+    const GURL& request_url,
     const network::ResourceResponseHead& response,
     network::mojom::URLLoaderPtr* loader,
     network::mojom::URLLoaderClientRequest* client_request,
diff --git a/content/browser/loader/navigation_loader_interceptor.h b/content/browser/loader/navigation_loader_interceptor.h
index 1ec858c..7f61938 100644
--- a/content/browser/loader/navigation_loader_interceptor.h
+++ b/content/browser/loader/navigation_loader_interceptor.h
@@ -82,6 +82,7 @@
   MaybeCreateSubresourceLoaderParams();
 
   // Returns true if the handler creates a loader for the |response| passed.
+  // |request_url| is the latest request URL including URL fragment.
   // An example of where this is used is AppCache, where the handler returns
   // fallback content for the response passed in.
   // The URLLoader interface pointer is returned in the |loader| parameter.
@@ -99,6 +100,7 @@
   // Remove this flag when we support service worker and signed exchange
   // integration. See crbug.com/894755#c1. Nullptr is not allowed.
   virtual bool MaybeCreateLoaderForResponse(
+      const GURL& request_url,
       const network::ResourceResponseHead& response,
       network::mojom::URLLoaderPtr* loader,
       network::mojom::URLLoaderClientRequest* client_request,
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index c3dea8f..9ab9c6a 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -505,7 +505,6 @@
       // header is verified and parsed, that's where the getter is used.
       interceptors_.push_back(std::make_unique<SignedExchangeRequestHandler>(
           url::Origin::Create(request_info->common_params.url),
-          request_info->common_params.url,
           GetURLLoaderOptions(request_info->is_main_frame),
           request_info->frame_tree_node_id,
           request_info->devtools_navigation_token,
@@ -728,7 +727,6 @@
       // header is verified and parsed, that's where the getter is used.
       interceptors_.push_back(std::make_unique<SignedExchangeRequestHandler>(
           url::Origin::Create(request_info->common_params.url),
-          request_info->common_params.url,
           GetURLLoaderOptions(request_info->is_main_frame),
           request_info->frame_tree_node_id,
           request_info->devtools_navigation_token,
@@ -1390,7 +1388,7 @@
       network::mojom::URLLoaderClientRequest response_client_request;
       bool skip_other_interceptors = false;
       if (interceptor->MaybeCreateLoaderForResponse(
-              response, &response_url_loader_, &response_client_request,
+              url_, response, &response_url_loader_, &response_client_request,
               url_loader_.get(), &skip_other_interceptors)) {
         if (response_loader_binding_.is_bound())
           response_loader_binding_.Close();
diff --git a/content/browser/loader/navigation_url_loader_impl_unittest.cc b/content/browser/loader/navigation_url_loader_impl_unittest.cc
index 1972331f..7ac5847 100644
--- a/content/browser/loader/navigation_url_loader_impl_unittest.cc
+++ b/content/browser/loader/navigation_url_loader_impl_unittest.cc
@@ -98,6 +98,7 @@
   }
 
   bool MaybeCreateLoaderForResponse(
+      const GURL& request_url,
       const network::ResourceResponseHead& response,
       network::mojom::URLLoaderPtr* loader,
       network::mojom::URLLoaderClientRequest* client_request,
diff --git a/content/browser/loader/resource_dispatcher_host_unittest.cc b/content/browser/loader/resource_dispatcher_host_unittest.cc
index eeaff0c..6af472b 100644
--- a/content/browser/loader/resource_dispatcher_host_unittest.cc
+++ b/content/browser/loader/resource_dispatcher_host_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/files/file_util.h"
 #include "base/location.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_number_conversions.h"
@@ -881,7 +882,7 @@
       auto type = static_cast<net::EffectiveConnectionType>(i);
       c[type] =
           network::ResourceSchedulerParamsManager::ParamsForNetworkQuality(
-              max_delayable_requests, 0.0, false);
+              max_delayable_requests, 0.0, false, base::nullopt);
     }
     host_.scheduler()->SetResourceSchedulerParamsManagerForTests(
         network::ResourceSchedulerParamsManager(c));
diff --git a/content/browser/memory/memory_condition_observer.cc b/content/browser/memory/memory_condition_observer.cc
deleted file mode 100644
index e80d4b36..0000000
--- a/content/browser/memory/memory_condition_observer.cc
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/memory/memory_condition_observer.h"
-
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/string_number_conversions.h"
-#include "content/browser/memory/memory_monitor.h"
-
-namespace content {
-
-namespace {
-
-// An expected renderer size. These values come from the median of appropriate
-// UMA stats.
-#if defined(OS_ANDROID) || defined(OS_IOS)
-const int kExpectedRendererSizeMB = 40;
-#elif defined(OS_WIN)
-const int kExpectedRendererSizeMB = 70;
-#else  // Mac, Linux, and ChromeOS
-const int kExpectedRendererSizeMB = 120;
-#endif
-
-const int kNewRenderersUntilCritical = 2;
-const int kDefaultMonitoringIntervalSeconds = 1;
-const int kMonitoringIntervalBackgroundedSeconds = 120;
-
-}  // namespace
-
-MemoryConditionObserver::MemoryConditionObserver(
-    MemoryCoordinatorImpl* coordinator,
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-    : coordinator_(coordinator), task_runner_(task_runner) {
-  DCHECK(coordinator_);
-  monitoring_interval_ =
-      base::TimeDelta::FromSeconds(kDefaultMonitoringIntervalSeconds);
-  monitoring_interval_foregrounded_ =
-      base::TimeDelta::FromSeconds(kDefaultMonitoringIntervalSeconds);
-  monitoring_interval_backgrounded_ =
-      base::TimeDelta::FromSeconds(kMonitoringIntervalBackgroundedSeconds);
-}
-
-MemoryConditionObserver::~MemoryConditionObserver() {}
-
-void MemoryConditionObserver::ScheduleUpdateCondition(base::TimeDelta delay) {
-  update_condition_closure_.Reset(base::Bind(
-      &MemoryConditionObserver::UpdateCondition, base::Unretained(this)));
-  task_runner_->PostDelayedTask(FROM_HERE, update_condition_closure_.callback(),
-                                delay);
-}
-
-void MemoryConditionObserver::OnForegrounded() {
-  SetMonitoringInterval(monitoring_interval_foregrounded_);
-}
-
-void MemoryConditionObserver::OnBackgrounded() {
-  SetMonitoringInterval(monitoring_interval_backgrounded_);
-}
-
-void MemoryConditionObserver::SetMonitoringInterval(base::TimeDelta interval) {
-  DCHECK(!interval.is_zero());
-  if (interval == monitoring_interval_)
-    return;
-  monitoring_interval_ = interval;
-  ScheduleUpdateCondition(interval);
-}
-
-MemoryCondition MemoryConditionObserver::CalculateNextCondition() {
-  int available =
-      coordinator_->memory_monitor()->GetFreeMemoryUntilCriticalMB();
-
-  // TODO(chrisha): Move this histogram recording to a better place when
-  // https://codereview.chromium.org/2479673002/ is landed.
-  UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Coordinator.FreeMemoryUntilCritical",
-                                available);
-
-  int expected_renderer_count = available / kExpectedRendererSizeMB;
-  if (available <= 0 || expected_renderer_count < kNewRenderersUntilCritical)
-    return MemoryCondition::CRITICAL;
-  return MemoryCondition::NORMAL;
-}
-
-void MemoryConditionObserver::UpdateCondition() {
-  auto next_condition = CalculateNextCondition();
-  coordinator_->UpdateConditionIfNeeded(next_condition);
-  ScheduleUpdateCondition(monitoring_interval_);
-}
-
-
-}  // namespace content
diff --git a/content/browser/memory/memory_condition_observer.h b/content/browser/memory/memory_condition_observer.h
deleted file mode 100644
index 70bf53e5c..0000000
--- a/content/browser/memory/memory_condition_observer.h
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_MEMORY_MEMORY_CONDITION_OBSERVER_H_
-#define CONTENT_BROWSER_MEMORY_MEMORY_CONDITION_OBSERVER_H_
-
-#include "base/cancelable_callback.h"
-#include "base/single_thread_task_runner.h"
-#include "base/time/time.h"
-#include "content/browser/memory/memory_coordinator_impl.h"
-#include "content/common/content_export.h"
-
-namespace content {
-
-// MemoryConditionObserver observes system memory usage and determines the
-// current MemoryCondition. It dispatches the current condition if the condition
-// has changed.
-class CONTENT_EXPORT MemoryConditionObserver {
- public:
-  // |coordinator| must outlive than this instance.
-  MemoryConditionObserver(
-      MemoryCoordinatorImpl* coordinator,
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
-  ~MemoryConditionObserver();
-
-  // Schedules a task to update memory condition. The task will be executed
-  // after |delay| has passed.
-  void ScheduleUpdateCondition(base::TimeDelta delay);
-
-  // Called when the browser is foregrounded.
-  void OnForegrounded();
-
-  // Called when the browser is backgrounded.
-  void OnBackgrounded();
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(MemoryCoordinatorImplTest, CalculateNextCondition);
-  FRIEND_TEST_ALL_PREFIXES(MemoryCoordinatorImplTest, ForceSetMemoryCondition);
-  FRIEND_TEST_ALL_PREFIXES(MemoryCoordinatorImplTest, DiscardTabUnderCritical);
-
-  // Sets the monitoring interval and reschedules a task to update memory
-  // condition.
-  void SetMonitoringInterval(base::TimeDelta interval);
-
-  // Calculates next memory condition from the amount of free memory using
-  // a heuristic.
-  MemoryCondition CalculateNextCondition();
-
-  // Periodically called to update the memory condition.
-  void UpdateCondition();
-
-  MemoryCoordinatorImpl* coordinator_;
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-  base::CancelableClosure update_condition_closure_;
-
-  // The current interval of checking the amount of free memory.
-  base::TimeDelta monitoring_interval_;
-  // The value of |monitoring_interval_| when the browser is foregrounded.
-  base::TimeDelta monitoring_interval_foregrounded_;
-  // The value of |monitoring_interval_| when the browser is backgrounded.
-  base::TimeDelta monitoring_interval_backgrounded_;
-
-  DISALLOW_COPY_AND_ASSIGN(MemoryConditionObserver);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_MEMORY_MEMORY_CONDITION_OBSERVER_H_
diff --git a/content/browser/memory/memory_coordinator_default_policy.cc b/content/browser/memory/memory_coordinator_default_policy.cc
deleted file mode 100644
index 28b0404..0000000
--- a/content/browser/memory/memory_coordinator_default_policy.cc
+++ /dev/null
@@ -1,29 +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 "content/browser/memory/memory_coordinator_default_policy.h"
-
-namespace content {
-
-MemoryCoordinatorDefaultPolicy::MemoryCoordinatorDefaultPolicy(
-    MemoryCoordinatorImpl* coordinator)
-    : coordinator_(coordinator) {
-  DCHECK(coordinator_);
-}
-
-MemoryCoordinatorDefaultPolicy::~MemoryCoordinatorDefaultPolicy() {}
-
-void MemoryCoordinatorDefaultPolicy::OnCriticalCondition() {
-  // Just trigger tab discarding for now.
-  coordinator_->DiscardTab(true);
-}
-
-void MemoryCoordinatorDefaultPolicy::OnConditionChanged(MemoryCondition prev,
-                                                        MemoryCondition next) {}
-
-void MemoryCoordinatorDefaultPolicy::OnChildVisibilityChanged(
-    int render_process_id,
-    bool is_visible) {}
-
-}  // namespace content
diff --git a/content/browser/memory/memory_coordinator_default_policy.h b/content/browser/memory/memory_coordinator_default_policy.h
deleted file mode 100644
index 29dd476..0000000
--- a/content/browser/memory/memory_coordinator_default_policy.h
+++ /dev/null
@@ -1,30 +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_MEMORY_MEMORY_COORDINATOR_DEFAULT_POLICY_H_
-#define CONTENT_BROWSER_MEMORY_MEMORY_COORDINATOR_DEFAULT_POLICY_H_
-
-#include "content/browser/memory/memory_coordinator_impl.h"
-
-namespace content {
-
-// The default policy of MemoryCoordinator.
-class MemoryCoordinatorDefaultPolicy : public MemoryCoordinatorImpl::Policy {
- public:
-  explicit MemoryCoordinatorDefaultPolicy(MemoryCoordinatorImpl* coordinator);
-  ~MemoryCoordinatorDefaultPolicy() override;
-
-  void OnCriticalCondition() override;
-  void OnConditionChanged(MemoryCondition prev, MemoryCondition next) override;
-  void OnChildVisibilityChanged(int render_process_id,
-                                bool is_visible) override;
-
- private:
-  // Not owned.
-  MemoryCoordinatorImpl* coordinator_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_MEMORY_MEMORY_COORDINATOR_DEFAULT_POLICY_H_
diff --git a/content/browser/memory/memory_coordinator_impl.cc b/content/browser/memory/memory_coordinator_impl.cc
deleted file mode 100644
index c0e70c3b..0000000
--- a/content/browser/memory/memory_coordinator_impl.cc
+++ /dev/null
@@ -1,401 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/memory/memory_coordinator_impl.h"
-
-#include "base/memory/memory_coordinator_client_registry.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/process/process_handle.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/default_tick_clock.h"
-#include "base/trace_event/trace_event.h"
-#include "content/browser/memory/memory_condition_observer.h"
-#include "content/browser/memory/memory_coordinator_default_policy.h"
-#include "content/browser/memory/memory_monitor.h"
-#include "content/public/browser/content_browser_client.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_types.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/render_widget_host.h"
-#include "content/public/common/content_features.h"
-#include "mojo/public/cpp/bindings/binding.h"
-
-namespace content {
-
-namespace {
-
-const int kDefaultMinimumTransitionPeriodSeconds = 30;
-
-mojom::MemoryState ToMojomMemoryState(MemoryState state) {
-  switch (state) {
-    case MemoryState::UNKNOWN:
-      return mojom::MemoryState::UNKNOWN;
-    case MemoryState::NORMAL:
-      return mojom::MemoryState::NORMAL;
-    case MemoryState::THROTTLED:
-      return mojom::MemoryState::THROTTLED;
-    case MemoryState::SUSPENDED:
-      return mojom::MemoryState::SUSPENDED;
-    default:
-      NOTREACHED();
-      return mojom::MemoryState::UNKNOWN;
-  }
-}
-
-const char* MemoryConditionToString(MemoryCondition condition) {
-  switch (condition) {
-    case MemoryCondition::NORMAL:
-      return "normal";
-    case MemoryCondition::CRITICAL:
-      return "critical";
-  }
-  NOTREACHED();
-  return "N/A";
-}
-
-}  // namespace
-
-// The implementation of MemoryCoordinatorHandle. See memory_coordinator.mojom
-// for the role of this class.
-class MemoryCoordinatorHandleImpl : public mojom::MemoryCoordinatorHandle {
- public:
-  MemoryCoordinatorHandleImpl(mojom::MemoryCoordinatorHandleRequest request,
-                              MemoryCoordinatorImpl* coordinator,
-                              int render_process_id);
-  ~MemoryCoordinatorHandleImpl() override;
-
-  // mojom::MemoryCoordinatorHandle:
-  void AddChild(mojom::ChildMemoryCoordinatorPtr child) override;
-
-  mojom::ChildMemoryCoordinatorPtr& child() { return child_; }
-  mojo::Binding<mojom::MemoryCoordinatorHandle>& binding() { return binding_; }
-
- private:
-  MemoryCoordinatorImpl* coordinator_;
-  int render_process_id_;
-  mojom::ChildMemoryCoordinatorPtr child_;
-  mojo::Binding<mojom::MemoryCoordinatorHandle> binding_;
-
-  DISALLOW_COPY_AND_ASSIGN(MemoryCoordinatorHandleImpl);
-};
-
-MemoryCoordinatorHandleImpl::MemoryCoordinatorHandleImpl(
-    mojom::MemoryCoordinatorHandleRequest request,
-    MemoryCoordinatorImpl* coordinator,
-    int render_process_id)
-    : coordinator_(coordinator),
-      render_process_id_(render_process_id),
-      binding_(this, std::move(request)) {
-  DCHECK(coordinator_);
-}
-
-MemoryCoordinatorHandleImpl::~MemoryCoordinatorHandleImpl() {}
-
-void MemoryCoordinatorHandleImpl::AddChild(
-    mojom::ChildMemoryCoordinatorPtr child) {
-  DCHECK(!child_.is_bound());
-  child_ = std::move(child);
-  coordinator_->OnChildAdded(render_process_id_);
-}
-
-// static
-MemoryCoordinator* MemoryCoordinator::GetInstance() {
-  return MemoryCoordinatorImpl::GetInstance();
-}
-
-// static
-MemoryCoordinatorImpl* MemoryCoordinatorImpl::GetInstance() {
-  if (!base::FeatureList::IsEnabled(features::kMemoryCoordinator))
-    return nullptr;
-  static MemoryCoordinatorImpl* instance = new MemoryCoordinatorImpl(
-      base::ThreadTaskRunnerHandle::Get(), CreateMemoryMonitor());
-  return instance;
-}
-
-MemoryCoordinatorImpl::MemoryCoordinatorImpl(
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-    std::unique_ptr<MemoryMonitor> memory_monitor)
-    : task_runner_(task_runner),
-      policy_(std::make_unique<MemoryCoordinatorDefaultPolicy>(this)),
-      delegate_(GetContentClient()->browser()->GetMemoryCoordinatorDelegate()),
-      memory_monitor_(std::move(memory_monitor)),
-      condition_observer_(
-          std::make_unique<MemoryConditionObserver>(this, task_runner)),
-      tick_clock_(base::DefaultTickClock::GetInstance()),
-      minimum_state_transition_period_(base::TimeDelta::FromSeconds(
-          kDefaultMinimumTransitionPeriodSeconds)) {
-  DCHECK(memory_monitor_.get());
-  base::MemoryCoordinatorProxy::SetMemoryCoordinator(this);
-
-  // Force the "memory_coordinator" category to show up in the trace viewer.
-  base::trace_event::TraceLog::GetCategoryGroupEnabled(
-      TRACE_DISABLED_BY_DEFAULT("memory_coordinator"));
-}
-
-MemoryCoordinatorImpl::~MemoryCoordinatorImpl() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  base::MemoryCoordinatorProxy::SetMemoryCoordinator(nullptr);
-}
-
-void MemoryCoordinatorImpl::Start() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(last_state_change_.is_null());
-
-  notification_registrar_.Add(
-      this, NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
-      NotificationService::AllBrowserContextsAndSources());
-  condition_observer_->ScheduleUpdateCondition(base::TimeDelta());
-}
-
-void MemoryCoordinatorImpl::OnForegrounded() {
-  condition_observer_->OnForegrounded();
-}
-
-void MemoryCoordinatorImpl::OnBackgrounded() {
-  condition_observer_->OnBackgrounded();
-}
-
-void MemoryCoordinatorImpl::CreateHandle(
-    int render_process_id,
-    mojom::MemoryCoordinatorHandleRequest request) {
-  std::unique_ptr<MemoryCoordinatorHandleImpl> handle(
-      new MemoryCoordinatorHandleImpl(std::move(request), this,
-                                      render_process_id));
-  handle->binding().set_connection_error_handler(
-      base::BindOnce(&MemoryCoordinatorImpl::OnConnectionError,
-                     base::Unretained(this), render_process_id));
-  CreateChildInfoMapEntry(render_process_id, std::move(handle));
-}
-
-void MemoryCoordinatorImpl::SetBrowserMemoryState(MemoryState memory_state) {
-  if (memory_state == browser_memory_state_)
-    return;
-
-  base::TimeTicks now = tick_clock_->NowTicks();
-  base::TimeDelta elapsed = now - last_state_change_;
-  if (!last_state_change_.is_null() &&
-      (elapsed < minimum_state_transition_period_)) {
-    base::TimeDelta delay = minimum_state_transition_period_ - elapsed +
-                            base::TimeDelta::FromSeconds(1);
-    delayed_browser_memory_state_setter_.Reset(
-        base::Bind(&MemoryCoordinatorImpl::SetBrowserMemoryState,
-                   base::Unretained(this), memory_state));
-    task_runner_->PostDelayedTask(
-        FROM_HERE, delayed_browser_memory_state_setter_.callback(), delay);
-    return;
-  }
-
-  if (!delayed_browser_memory_state_setter_.IsCancelled())
-    delayed_browser_memory_state_setter_.Cancel();
-
-  last_state_change_ = now;
-  browser_memory_state_ = memory_state;
-  NotifyStateToClients(memory_state);
-}
-
-bool MemoryCoordinatorImpl::SetChildMemoryState(int render_process_id,
-                                                MemoryState memory_state) {
-  // Can't set an invalid memory state.
-  if (memory_state == MemoryState::UNKNOWN)
-    return false;
-
-  // SUSPENDED state isn't supported yet.
-  if (memory_state == MemoryState::SUSPENDED)
-    return false;
-
-  // Can't send a message to a child that doesn't exist.
-  auto iter = children_.find(render_process_id);
-  if (iter == children_.end())
-    return false;
-
-  // Can't send a message to a child that isn't bound.
-  if (!iter->second.handle->child().is_bound())
-    return false;
-
-  memory_state = OverrideState(memory_state, iter->second);
-
-  // A nop doesn't need to be sent, but is considered successful.
-  if (iter->second.memory_state == memory_state)
-    return true;
-
-  // Update the internal state and send the message.
-  iter->second.memory_state = memory_state;
-  iter->second.handle->child()->OnStateChange(ToMojomMemoryState(memory_state));
-  return true;
-}
-
-bool MemoryCoordinatorImpl::TryToPurgeMemoryFromChild(int render_process_id) {
-  auto iter = children().find(render_process_id);
-  if (iter == children().end())
-    return false;
-  MemoryCoordinatorHandleImpl* handle = iter->second.handle.get();
-  if (!handle || !handle->child() || !handle->child().is_bound())
-    return false;
-  if (!iter->second.can_purge_after.is_null() &&
-      iter->second.can_purge_after > tick_clock_->NowTicks())
-    return false;
-
-  // Set |can_purge_after| to the maximum value to suppress another purge
-  // request until the child process goes foreground and then goes background
-  // again.
-  iter->second.can_purge_after = base::TimeTicks::Max();
-  handle->child()->PurgeMemory();
-  return true;
-}
-
-MemoryState MemoryCoordinatorImpl::GetCurrentMemoryState() const {
-  return browser_memory_state_;
-}
-
-void MemoryCoordinatorImpl::ForceSetMemoryCondition(MemoryCondition condition,
-                                                    base::TimeDelta duration) {
-  UpdateConditionIfNeeded(condition);
-  suppress_condition_change_until_ = tick_clock_->NowTicks() + duration;
-}
-
-void MemoryCoordinatorImpl::Observe(int type,
-                                    const NotificationSource& source,
-                                    const NotificationDetails& details) {
-  DCHECK(type == NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED);
-  RenderWidgetHost* render_widget_host = Source<RenderWidgetHost>(source).ptr();
-  RenderProcessHost* process = render_widget_host->GetProcess();
-  if (!process)
-    return;
-  bool is_visible = *Details<bool>(details).ptr();
-  policy_->OnChildVisibilityChanged(process->GetID(), is_visible);
-}
-
-MemoryState MemoryCoordinatorImpl::GetStateForProcess(
-    base::ProcessHandle handle) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (handle == base::kNullProcessHandle)
-    return MemoryState::UNKNOWN;
-  if (handle == base::GetCurrentProcessHandle())
-    return browser_memory_state_;
-
-  for (auto& iter : children()) {
-    auto* render_process_host = GetRenderProcessHost(iter.first);
-    if (render_process_host &&
-        render_process_host->GetProcess().Handle() == handle)
-      return iter.second.memory_state;
-  }
-  return MemoryState::UNKNOWN;
-}
-
-void MemoryCoordinatorImpl::UpdateConditionIfNeeded(
-    MemoryCondition next_condition) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (next_condition == MemoryCondition::CRITICAL)
-    policy_->OnCriticalCondition();
-
-  if (suppress_condition_change_until_ > tick_clock_->NowTicks() ||
-      memory_condition_ == next_condition)
-    return;
-
-  TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("memory_coordinator"),
-               "MemoryCoordinatorImpl::UpdateConditionIfNeeded", "prev",
-               MemoryConditionToString(memory_condition_), "next",
-               MemoryConditionToString(next_condition));
-  policy_->OnConditionChanged(memory_condition_, next_condition);
-  memory_condition_ = next_condition;
-}
-
-void MemoryCoordinatorImpl::DiscardTab(bool skip_unload_handlers) {
-  if (delegate_)
-    delegate_->DiscardTab(skip_unload_handlers);
-}
-
-RenderProcessHost* MemoryCoordinatorImpl::GetRenderProcessHost(
-    int render_process_id) {
-  return RenderProcessHost::FromID(render_process_id);
-}
-
-void MemoryCoordinatorImpl::SetDelegateForTesting(
-    std::unique_ptr<MemoryCoordinatorDelegate> delegate) {
-  CHECK(!delegate_);
-  delegate_ = std::move(delegate);
-}
-
-void MemoryCoordinatorImpl::SetPolicyForTesting(
-    std::unique_ptr<Policy> policy) {
-  policy_ = std::move(policy);
-}
-
-void MemoryCoordinatorImpl::AddChildForTesting(
-    int dummy_render_process_id, mojom::ChildMemoryCoordinatorPtr child) {
-  mojom::MemoryCoordinatorHandlePtr mch;
-  auto request = mojo::MakeRequest(&mch);
-  std::unique_ptr<MemoryCoordinatorHandleImpl> handle(
-      new MemoryCoordinatorHandleImpl(std::move(request), this,
-                                      dummy_render_process_id));
-  handle->AddChild(std::move(child));
-  CreateChildInfoMapEntry(dummy_render_process_id, std::move(handle));
-}
-
-void MemoryCoordinatorImpl::SetTickClockForTesting(
-    const base::TickClock* tick_clock) {
-  tick_clock_ = tick_clock;
-}
-
-void MemoryCoordinatorImpl::OnConnectionError(int render_process_id) {
-  children_.erase(render_process_id);
-}
-
-void MemoryCoordinatorImpl::OnChildAdded(int render_process_id) {
-  RenderProcessHost* render_process_host =
-      GetRenderProcessHost(render_process_id);
-  if (!render_process_host)
-    return;
-
-  // Populate an initial state of a newly created process.
-  // TODO(bashi): IsProcessBackgrounded() may return true even when tabs in the
-  // renderer process are invisible (e.g. restoring tabs all at once).
-  // Figure out a better way to set visibility.
-  policy_->OnChildVisibilityChanged(
-      render_process_id, !render_process_host->IsProcessBackgrounded());
-}
-
-MemoryState MemoryCoordinatorImpl::OverrideState(MemoryState memory_state,
-                                                 const ChildInfo& child) {
-  // We don't suspend foreground renderers. Throttle them instead.
-  if (child.is_visible && memory_state == MemoryState::SUSPENDED)
-    return MemoryState::THROTTLED;
-#if defined(OS_ANDROID)
-  // On Android, we throttle background renderers immediately.
-  // TODO(bashi): Create a specialized class of MemoryCoordinator for Android
-  // and move this ifdef to the class.
-  if (!child.is_visible && memory_state == MemoryState::NORMAL)
-    return MemoryState::THROTTLED;
-  // TODO(bashi): Suspend background renderers after a certain period of time.
-#endif  // defined(OS_ANDROID)
-  return memory_state;
-}
-
-void MemoryCoordinatorImpl::CreateChildInfoMapEntry(
-    int render_process_id,
-    std::unique_ptr<MemoryCoordinatorHandleImpl> handle) {
-  auto& child_info = children_[render_process_id];
-  // Process always start with normal memory state.
-  // We'll set renderer's memory state to the current global state when the
-  // corresponding renderer process is ready to communicate. Renderer processes
-  // call AddChild() when they are ready.
-  child_info.memory_state = MemoryState::NORMAL;
-  child_info.is_visible = true;
-  child_info.handle = std::move(handle);
-}
-
-void MemoryCoordinatorImpl::NotifyStateToClients(MemoryState state) {
-  base::MemoryCoordinatorClientRegistry::GetInstance()->Notify(state);
-}
-
-MemoryCoordinatorImpl::ChildInfo::ChildInfo() {}
-
-MemoryCoordinatorImpl::ChildInfo::ChildInfo(const ChildInfo& rhs) {
-  // This is a nop, but exists for compatibility with STL containers.
-}
-
-MemoryCoordinatorImpl::ChildInfo::~ChildInfo() {}
-
-}  // namespace content
diff --git a/content/browser/memory/memory_coordinator_impl.h b/content/browser/memory/memory_coordinator_impl.h
deleted file mode 100644
index 7291b1e..0000000
--- a/content/browser/memory/memory_coordinator_impl.h
+++ /dev/null
@@ -1,246 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_MEMORY_MEMORY_COORDINATOR_IMPL_H_
-#define CONTENT_BROWSER_MEMORY_MEMORY_COORDINATOR_IMPL_H_
-
-#include "base/callback.h"
-#include "base/cancelable_callback.h"
-#include "base/memory/memory_coordinator_client.h"
-#include "base/memory/memory_coordinator_proxy.h"
-#include "base/memory/memory_pressure_monitor.h"
-#include "base/sequence_checker.h"
-#include "base/single_thread_task_runner.h"
-#include "base/time/tick_clock.h"
-#include "base/time/time.h"
-#include "content/common/content_export.h"
-#include "content/common/memory_coordinator.mojom.h"
-#include "content/public/browser/memory_coordinator.h"
-#include "content/public/browser/memory_coordinator_delegate.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-
-namespace content {
-
-// NOTE: Memory coordinator is under development and not fully working.
-
-class MemoryConditionObserver;
-class MemoryCoordinatorHandleImpl;
-class MemoryCoordinatorImplTest;
-class MemoryMonitor;
-class RenderProcessHost;
-
-using MemoryState = base::MemoryState;
-
-// MemoryCondition is an internal state of memory coordinator which is used for
-// various things; calculating memory state for processes, requesting processes
-// to purge memory, and scheduling tab discarding.
-enum class MemoryCondition : int {
-  NORMAL = 0,
-  CRITICAL = 1,
-};
-
-// MemoryCoordinatorImpl is an implementation of MemoryCoordinator.
-class CONTENT_EXPORT MemoryCoordinatorImpl : public base::MemoryCoordinator,
-                                             public MemoryCoordinator,
-                                             public NotificationObserver {
- public:
-  static MemoryCoordinatorImpl* GetInstance();
-
-  // A policy determines what actions (e.g. purging memory from a process)
-  // MemoryCoordinator takes on various events.
-  class Policy {
-   public:
-    virtual ~Policy() {}
-
-    // Called periodically while the memory condition is CRITICAL.
-    virtual void OnCriticalCondition() {}
-    // Called when the current MemoryCondition has changed.
-    virtual void OnConditionChanged(MemoryCondition prev,
-                                    MemoryCondition next) {}
-    // Called when the visibility of a child process has changed.
-    virtual void OnChildVisibilityChanged(int render_process_id,
-                                          bool is_visible) {}
-  };
-
-  // Stores information about any known child processes.
-  struct ChildInfo {
-    // This object must be compatible with STL containers.
-    ChildInfo();
-    ChildInfo(const ChildInfo& rhs);
-    ~ChildInfo();
-
-    MemoryState memory_state;
-    bool is_visible = false;
-    base::TimeTicks can_purge_after;
-    std::unique_ptr<MemoryCoordinatorHandleImpl> handle;
-  };
-
-  MemoryCoordinatorImpl(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-                        std::unique_ptr<MemoryMonitor> monitor);
-  ~MemoryCoordinatorImpl() override;
-
-  MemoryMonitor* memory_monitor() { return memory_monitor_.get(); }
-
-  // Starts monitoring memory usage. After calling this method, memory
-  // coordinator will start dispatching state changes.
-  void Start();
-
-  // Called when the browser is foregrounded.
-  void OnForegrounded();
-
-  // Called when the browser is backgrounded.
-  void OnBackgrounded();
-
-  // Creates a handle to the provided child process.
-  void CreateHandle(int render_process_id,
-                    mojom::MemoryCoordinatorHandleRequest request);
-
-  // Set the browser's memory state and notifies it to in-process clients.
-  void SetBrowserMemoryState(MemoryState state);
-
-  // Dispatches a memory state change to the provided process. Returns true if
-  // the process is tracked by this coordinator and successfully dispatches,
-  // returns false otherwise.
-  bool SetChildMemoryState(int render_process_id, MemoryState memory_state);
-
-  // Tries to purge memory from the provided child process.
-  bool TryToPurgeMemoryFromChild(int render_process_id);
-
-  // base::MemoryCoordinator implementations:
-  MemoryState GetCurrentMemoryState() const override;
-
-  // content::MemoryCoordinator implementation:
-  MemoryState GetStateForProcess(base::ProcessHandle handle) override;
-
-  // NotificationObserver implementation:
-  void Observe(int type,
-               const NotificationSource& source,
-               const NotificationDetails& details) override;
-
-  // Returns the current memory condition.
-  MemoryCondition GetMemoryCondition() const { return memory_condition_; }
-
-  // Overrides the current memory condition to |condition|. Memory condition
-  // update tasks won't be scheduled until |duration| is passed. This means that
-  // the memory condition remains the same until |duration| is passed or
-  // another call of this method.
-  void ForceSetMemoryCondition(MemoryCondition condition,
-                               base::TimeDelta duration);
-
-  // Changes current memory condition if needed. This may trigger some actions
-  // like purging memory and memory state changes.
-  void UpdateConditionIfNeeded(MemoryCondition condition);
-
-  // Asks the delegate to discard a tab.
-  void DiscardTab(bool skip_unload_handlers);
-
-  // Gets the current TimeTicks.
-  base::TimeTicks NowTicks() const { return tick_clock_->NowTicks(); }
-
-  // A map from process ID (RenderProcessHost::GetID()) to child process info.
-  using ChildInfoMap = std::map<int, ChildInfo>;
-
-  ChildInfoMap& children() { return children_; }
-
- protected:
-  // Returns the RenderProcessHost which is correspond to the given id.
-  // Returns nullptr if there is no corresponding RenderProcessHost.
-  // This is a virtual method so that we can write tests without having
-  // actual RenderProcessHost.
-  virtual RenderProcessHost* GetRenderProcessHost(int render_process_id);
-
-  // Sets a delegate for testing.
-  void SetDelegateForTesting(
-      std::unique_ptr<MemoryCoordinatorDelegate> delegate);
-
-  // Sets a policy for testing.
-  void SetPolicyForTesting(std::unique_ptr<Policy> policy);
-
-  MemoryCoordinatorDelegate* delegate() { return delegate_.get(); }
-  Policy* policy() { return policy_.get(); }
-
-  // Adds the given ChildMemoryCoordinator as a child of this coordinator.
-  void AddChildForTesting(int dummy_render_process_id,
-                          mojom::ChildMemoryCoordinatorPtr child);
-
-  // Sets a TickClock for testing.
-  void SetTickClockForTesting(const base::TickClock* tick_clock);
-
-  // Callback invoked by mojo when the child connection goes down. Exposed
-  // for testing.
-  void OnConnectionError(int render_process_id);
-
-  base::SingleThreadTaskRunner* task_runner() { return task_runner_.get(); }
-
- private:
-#if !defined(OS_MACOSX)
-  FRIEND_TEST_ALL_PREFIXES(MemoryCoordinatorImplBrowserTest, HandleAdded);
-#endif
-  FRIEND_TEST_ALL_PREFIXES(MemoryCoordinatorImplTest, CalculateNextCondition);
-  FRIEND_TEST_ALL_PREFIXES(MemoryCoordinatorImplTest, ForceSetMemoryCondition);
-  FRIEND_TEST_ALL_PREFIXES(MemoryCoordinatorImplTest, DiscardTabUnderCritical);
-
-  friend class MemoryCoordinatorHandleImpl;
-
-  // Called when ChildMemoryCoordinator calls AddChild().
-  void OnChildAdded(int render_process_id);
-
-  // Called by SetChildMemoryState() to determine a child memory state based on
-  // the current status of the child process.
-  MemoryState OverrideState(MemoryState memroy_state, const ChildInfo& child);
-
-  // Helper function of CreateHandle and AddChildForTesting.
-  void CreateChildInfoMapEntry(
-      int render_process_id,
-      std::unique_ptr<MemoryCoordinatorHandleImpl> handle);
-
-  // Notifies a state change to in-process clients.
-  void NotifyStateToClients(MemoryState state);
-
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-  std::unique_ptr<Policy> policy_;
-  std::unique_ptr<MemoryCoordinatorDelegate> delegate_;
-  std::unique_ptr<MemoryMonitor> memory_monitor_;
-  std::unique_ptr<MemoryConditionObserver> condition_observer_;
-  const base::TickClock* tick_clock_;
-  NotificationRegistrar notification_registrar_;
-
-  // The current memory condition.
-  MemoryCondition memory_condition_ = MemoryCondition::NORMAL;
-
-  // |memory_condition_| won't be updated until this time ticks is passed.
-  base::TimeTicks suppress_condition_change_until_;
-
-  // The memory state of the browser process.
-  MemoryState browser_memory_state_ = MemoryState::NORMAL;
-
-  // The time tick of last state change of the browser process.
-  base::TimeTicks last_state_change_;
-
-  // Memory state for a process will remain unchanged until this period of time
-  // passes.
-  base::TimeDelta minimum_state_transition_period_;
-
-  // Used to delay setting browser's memory state. Cancelable to avoid executing
-  // multiple tasks in the same time frame.
-  base::CancelableClosure delayed_browser_memory_state_setter_;
-
-  // If this isn't null, purging memory from the browser process is suppressed
-  // until this ticks is passed.
-  base::TimeTicks can_purge_after_;
-
-  // Tracks child processes. An entry is added when a renderer connects to
-  // MemoryCoordinator and removed automatically when an underlying binding is
-  // disconnected.
-  ChildInfoMap children_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  DISALLOW_COPY_AND_ASSIGN(MemoryCoordinatorImpl);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_MEMORY_MEMORY_COORDINATOR_IMPL_H_
diff --git a/content/browser/memory/memory_coordinator_impl_unittest.cc b/content/browser/memory/memory_coordinator_impl_unittest.cc
deleted file mode 100644
index d99810e..0000000
--- a/content/browser/memory/memory_coordinator_impl_unittest.cc
+++ /dev/null
@@ -1,469 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/memory/memory_coordinator_impl.h"
-
-#include "base/memory/memory_coordinator_client_registry.h"
-#include "base/memory/memory_coordinator_proxy.h"
-#include "base/memory/memory_pressure_monitor.h"
-#include "base/run_loop.h"
-#include "base/test/multiprocess_test.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/test_mock_time_task_runner.h"
-#include "content/browser/memory/memory_condition_observer.h"
-#include "content/browser/memory/memory_monitor.h"
-#include "content/public/common/content_features.h"
-#include "content/public/test/mock_render_process_host.h"
-#include "content/public/test/test_browser_context.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace content {
-
-namespace {
-
-void RunUntilIdle() {
-  base::RunLoop loop;
-  loop.RunUntilIdle();
-}
-
-// A mock ChildMemoryCoordinator, for testing interaction between MC and CMC.
-class MockChildMemoryCoordinator : public mojom::ChildMemoryCoordinator {
- public:
-  MockChildMemoryCoordinator()
-      : state_(mojom::MemoryState::NORMAL),
-        on_state_change_calls_(0),
-        purge_memory_calls_(0) {}
-
-  ~MockChildMemoryCoordinator() override {}
-
-  void OnStateChange(mojom::MemoryState state) override {
-    state_ = state;
-    ++on_state_change_calls_;
-  }
-
-  void PurgeMemory() override { ++purge_memory_calls_; }
-
-  mojom::MemoryState state() const { return state_; }
-  int on_state_change_calls() const { return on_state_change_calls_; }
-  int purge_memory_calls() const { return purge_memory_calls_; }
-
- private:
-  mojom::MemoryState state_;
-  int on_state_change_calls_;
-  int purge_memory_calls_;
-};
-
-// A mock MemoryCoordinatorClient, for testing interaction between MC and
-// clients.
-class MockMemoryCoordinatorClient : public base::MemoryCoordinatorClient {
- public:
-  void OnMemoryStateChange(base::MemoryState state) override {
-    did_state_changed_ = true;
-    state_ = state;
-  }
-
-  void OnPurgeMemory() override { ++purge_memory_calls_; }
-
-  bool did_state_changed() const { return did_state_changed_; }
-  base::MemoryState state() const { return state_; }
-  int purge_memory_calls() const { return purge_memory_calls_; }
-
- private:
-  bool did_state_changed_ = false;
-  base::MemoryState state_ = base::MemoryState::NORMAL;
-  int purge_memory_calls_ = 0;
-};
-
-class MockMemoryMonitor : public MemoryMonitor {
- public:
-  MockMemoryMonitor() {}
-  ~MockMemoryMonitor() override {}
-
-  void SetFreeMemoryUntilCriticalMB(int free_memory) {
-    free_memory_ = free_memory;
-  }
-
-  // MemoryMonitor implementation
-  int GetFreeMemoryUntilCriticalMB() override { return free_memory_; }
-
- private:
-  int free_memory_ = 0;
-
-  DISALLOW_COPY_AND_ASSIGN(MockMemoryMonitor);
-};
-
-class TestMemoryCoordinatorDelegate : public MemoryCoordinatorDelegate {
- public:
-  TestMemoryCoordinatorDelegate() {}
-  ~TestMemoryCoordinatorDelegate() override {}
-
-  void DiscardTab(bool skip_unload_handlers) override { ++discard_tab_count_; }
-
-  int discard_tab_count() const { return discard_tab_count_; }
-
- private:
-  int discard_tab_count_ = 0;
-
-  DISALLOW_COPY_AND_ASSIGN(TestMemoryCoordinatorDelegate);
-};
-
-class MockMemoryCoordinatorPolicy : public MemoryCoordinatorImpl::Policy {
- public:
-  explicit MockMemoryCoordinatorPolicy(MemoryCoordinatorImpl* coordinator)
-      : coordinator_(coordinator) {
-    DCHECK(coordinator_);
-  }
-
-  ~MockMemoryCoordinatorPolicy() override = default;
-
-  void OnCriticalCondition() override {
-    coordinator_->DiscardTab(false /* skip_unload_handlers */);
-  }
-
-  void OnConditionChanged(MemoryCondition prev, MemoryCondition next) override {
-    EXPECT_NE(prev, next);
-    last_condition_ = next;
-  }
-
-  MemoryCondition last_condition() const { return last_condition_; }
-
- private:
-  MemoryCoordinatorImpl* coordinator_ = nullptr;
-  MemoryCondition last_condition_ = MemoryCondition::NORMAL;
-
-  DISALLOW_COPY_AND_ASSIGN(MockMemoryCoordinatorPolicy);
-};
-
-// A MemoryCoordinatorImpl that can be directly constructed.
-class TestMemoryCoordinatorImpl : public MemoryCoordinatorImpl {
- public:
-  // Mojo machinery for wrapping a mock ChildMemoryCoordinator.
-  struct Child {
-    Child(mojom::ChildMemoryCoordinatorPtr* cmc_ptr) : cmc_binding(&cmc) {
-      cmc_binding.Bind(mojo::MakeRequest(cmc_ptr));
-      RunUntilIdle();
-    }
-
-    MockChildMemoryCoordinator cmc;
-    mojo::Binding<mojom::ChildMemoryCoordinator> cmc_binding;
-  };
-
-  TestMemoryCoordinatorImpl(
-      scoped_refptr<base::TestMockTimeTaskRunner> task_runner)
-      : MemoryCoordinatorImpl(task_runner,
-                              std::make_unique<MockMemoryMonitor>()) {
-    SetDelegateForTesting(std::make_unique<TestMemoryCoordinatorDelegate>());
-    SetPolicyForTesting(std::make_unique<MockMemoryCoordinatorPolicy>(this));
-    SetTickClockForTesting(task_runner->GetMockTickClock());
-  }
-
-  ~TestMemoryCoordinatorImpl() override {}
-
-  using MemoryCoordinatorImpl::OnConnectionError;
-  using MemoryCoordinatorImpl::children;
-
-  MockChildMemoryCoordinator* CreateChildMemoryCoordinator(
-      int process_id) {
-    mojom::ChildMemoryCoordinatorPtr cmc_ptr;
-    children_.push_back(std::unique_ptr<Child>(new Child(&cmc_ptr)));
-    AddChildForTesting(process_id, std::move(cmc_ptr));
-    render_process_hosts_[process_id] =
-        std::make_unique<MockRenderProcessHost>(&browser_context_);
-    return &children_.back()->cmc;
-  }
-
-  RenderProcessHost* GetRenderProcessHost(int render_process_id) override {
-    return GetMockRenderProcessHost(render_process_id);
-  }
-
-  MockRenderProcessHost* GetMockRenderProcessHost(int render_process_id) {
-    auto iter = render_process_hosts_.find(render_process_id);
-    if (iter == render_process_hosts_.end())
-      return nullptr;
-    return iter->second.get();
-  }
-
-  TestMemoryCoordinatorDelegate* GetDelegate() {
-    return static_cast<TestMemoryCoordinatorDelegate*>(delegate());
-  }
-
-  MockMemoryCoordinatorPolicy* GetPolicy() {
-    return static_cast<MockMemoryCoordinatorPolicy*>(policy());
-  }
-
-  // Wrapper of MemoryCoordinator::SetMemoryState that also calls RunUntilIdle.
-  bool SetChildMemoryState(
-      int render_process_id, MemoryState memory_state) {
-    bool result = MemoryCoordinatorImpl::SetChildMemoryState(
-        render_process_id, memory_state);
-    RunUntilIdle();
-    return result;
-  }
-
-  TestBrowserContext browser_context_;
-  std::vector<std::unique_ptr<Child>> children_;
-  std::map<int, std::unique_ptr<MockRenderProcessHost>> render_process_hosts_;
-};
-
-}  // namespace
-
-class MemoryCoordinatorImplTest : public base::MultiProcessTest {
- public:
-  using MemoryState = base::MemoryState;
-
-  void SetUp() override {
-    base::MultiProcessTest::SetUp();
-    scoped_feature_list_.InitAndEnableFeature(features::kMemoryCoordinator);
-
-    task_runner_ = new base::TestMockTimeTaskRunner();
-    thread_bundle_ = std::make_unique<TestBrowserThreadBundle>();
-    coordinator_ = std::make_unique<TestMemoryCoordinatorImpl>(task_runner_);
-  }
-
-  MockMemoryMonitor* GetMockMemoryMonitor() {
-    return static_cast<MockMemoryMonitor*>(coordinator_->memory_monitor());
-  }
-
- protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
-  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
-  std::unique_ptr<content::TestBrowserThreadBundle> thread_bundle_;
-  std::unique_ptr<TestMemoryCoordinatorImpl> coordinator_;
-};
-
-TEST_F(MemoryCoordinatorImplTest, ChildRemovedOnConnectionError) {
-  coordinator_->CreateChildMemoryCoordinator(1);
-  ASSERT_EQ(1u, coordinator_->children().size());
-  coordinator_->OnConnectionError(1);
-  EXPECT_EQ(0u, coordinator_->children().size());
-}
-
-TEST_F(MemoryCoordinatorImplTest, SetMemoryStateFailsInvalidState) {
-  auto* cmc1 = coordinator_->CreateChildMemoryCoordinator(1);
-
-  EXPECT_FALSE(
-      coordinator_->SetChildMemoryState(1, MemoryState::UNKNOWN));
-  EXPECT_EQ(0, cmc1->on_state_change_calls());
-}
-
-TEST_F(MemoryCoordinatorImplTest, SetMemoryStateFailsInvalidRenderer) {
-  auto* cmc1 = coordinator_->CreateChildMemoryCoordinator(1);
-
-  EXPECT_FALSE(
-      coordinator_->SetChildMemoryState(2, MemoryState::THROTTLED));
-  EXPECT_EQ(0, cmc1->on_state_change_calls());
-}
-
-TEST_F(MemoryCoordinatorImplTest, SetMemoryStateNotDeliveredNop) {
-  auto* cmc1 = coordinator_->CreateChildMemoryCoordinator(1);
-
-  EXPECT_FALSE(
-      coordinator_->SetChildMemoryState(2, MemoryState::NORMAL));
-  EXPECT_EQ(0, cmc1->on_state_change_calls());
-}
-
-TEST_F(MemoryCoordinatorImplTest, SetMemoryStateDelivered) {
-  auto* cmc1 = coordinator_->CreateChildMemoryCoordinator(1);
-  auto* cmc2 = coordinator_->CreateChildMemoryCoordinator(2);
-
-  EXPECT_TRUE(
-      coordinator_->SetChildMemoryState(1, MemoryState::THROTTLED));
-  EXPECT_EQ(1, cmc1->on_state_change_calls());
-  EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc1->state());
-  EXPECT_EQ(0, cmc2->on_state_change_calls());
-  EXPECT_EQ(mojom::MemoryState::NORMAL, cmc2->state());
-}
-
-TEST_F(MemoryCoordinatorImplTest, PurgeMemoryChild) {
-  auto* child = coordinator_->CreateChildMemoryCoordinator(1);
-  EXPECT_EQ(0, child->purge_memory_calls());
-  child->PurgeMemory();
-  RunUntilIdle();
-  EXPECT_EQ(1, child->purge_memory_calls());
-}
-
-TEST_F(MemoryCoordinatorImplTest, SetChildMemoryState) {
-  auto* cmc = coordinator_->CreateChildMemoryCoordinator(1);
-  auto iter = coordinator_->children().find(1);
-  auto* render_process_host = coordinator_->GetMockRenderProcessHost(1);
-  ASSERT_TRUE(iter != coordinator_->children().end());
-  ASSERT_TRUE(render_process_host);
-
-  // Foreground
-  iter->second.is_visible = true;
-  render_process_host->set_is_process_backgrounded(false);
-  EXPECT_TRUE(coordinator_->SetChildMemoryState(1, MemoryState::NORMAL));
-  EXPECT_EQ(mojom::MemoryState::NORMAL, cmc->state());
-  EXPECT_TRUE(
-      coordinator_->SetChildMemoryState(1, MemoryState::THROTTLED));
-  EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc->state());
-  EXPECT_TRUE(coordinator_->SetChildMemoryState(1, MemoryState::THROTTLED));
-  EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc->state());
-
-  // Background
-  iter->second.is_visible = false;
-  render_process_host->set_is_process_backgrounded(true);
-  EXPECT_TRUE(coordinator_->SetChildMemoryState(1, MemoryState::NORMAL));
-#if defined(OS_ANDROID)
-  EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc->state());
-#else
-  EXPECT_EQ(mojom::MemoryState::NORMAL, cmc->state());
-#endif
-  EXPECT_TRUE(
-      coordinator_->SetChildMemoryState(1, MemoryState::THROTTLED));
-  EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc->state());
-}
-
-TEST_F(MemoryCoordinatorImplTest, CalculateNextCondition) {
-  auto* condition_observer = coordinator_->condition_observer_.get();
-
-  // The default condition is NORMAL.
-  EXPECT_EQ(MemoryCondition::NORMAL, coordinator_->GetMemoryCondition());
-
-  // Transitions from NORMAL
-  GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(1000);
-  EXPECT_EQ(MemoryCondition::NORMAL,
-            condition_observer->CalculateNextCondition());
-  GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(0);
-  EXPECT_EQ(MemoryCondition::CRITICAL,
-            condition_observer->CalculateNextCondition());
-
-  // Transitions from CRITICAL
-  coordinator_->memory_condition_ = MemoryCondition::CRITICAL;
-  EXPECT_EQ(MemoryCondition::CRITICAL, coordinator_->GetMemoryCondition());
-  GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(1000);
-  EXPECT_EQ(MemoryCondition::NORMAL,
-            condition_observer->CalculateNextCondition());
-}
-
-TEST_F(MemoryCoordinatorImplTest, UpdateConditionIfNeeded) {
-  auto* policy = coordinator_->GetPolicy();
-
-  // The initial condition is NORMAL.
-  EXPECT_EQ(MemoryCondition::NORMAL, coordinator_->GetMemoryCondition());
-  coordinator_->UpdateConditionIfNeeded(MemoryCondition::NORMAL);
-  RunUntilIdle();
-  EXPECT_EQ(MemoryCondition::NORMAL, policy->last_condition());
-
-  // Change to CRITICAL condition.
-  coordinator_->UpdateConditionIfNeeded(MemoryCondition::CRITICAL);
-  RunUntilIdle();
-  EXPECT_EQ(MemoryCondition::CRITICAL, policy->last_condition());
-
-  // Make sure that OnConditionChanged() won't get called when the condition is
-  // unchanged. CHECK_NE() in MockMemoryCoordinatorPolicy ensures it.
-  coordinator_->UpdateConditionIfNeeded(MemoryCondition::CRITICAL);
-  RunUntilIdle();
-  EXPECT_EQ(MemoryCondition::CRITICAL, policy->last_condition());
-}
-
-// TODO(bashi): Move ForceSetMemoryCondition?
-TEST_F(MemoryCoordinatorImplTest, ForceSetMemoryCondition) {
-  auto* condition_observer = coordinator_->condition_observer_.get();
-  GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(1000);
-
-  base::TimeDelta interval = base::TimeDelta::FromSeconds(5);
-  condition_observer->monitoring_interval_ = interval;
-
-  // Starts updating memory condition. The initial condition should be NORMAL
-  // with above configuration.
-  coordinator_->Start();
-  task_runner_->RunUntilIdle();
-  EXPECT_EQ(MemoryCondition::NORMAL, coordinator_->GetMemoryCondition());
-
-  base::TimeDelta force_set_duration = interval * 3;
-  coordinator_->ForceSetMemoryCondition(MemoryCondition::CRITICAL,
-                                        force_set_duration);
-  EXPECT_EQ(MemoryCondition::CRITICAL, coordinator_->GetMemoryCondition());
-
-  // The condition should remain CRITICAL even after some monitoring period are
-  // passed.
-  task_runner_->FastForwardBy(interval * 2);
-  task_runner_->RunUntilIdle();
-  EXPECT_EQ(MemoryCondition::CRITICAL, coordinator_->GetMemoryCondition());
-
-  // The condition should be updated after |force_set_duration| is passed.
-  task_runner_->FastForwardBy(force_set_duration);
-  task_runner_->RunUntilIdle();
-  EXPECT_EQ(MemoryCondition::NORMAL, coordinator_->GetMemoryCondition());
-
-  // Also make sure that the condition is updated based on free avaiable memory.
-  GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(0);
-  task_runner_->FastForwardBy(interval * 2);
-  task_runner_->RunUntilIdle();
-  EXPECT_EQ(MemoryCondition::CRITICAL, coordinator_->GetMemoryCondition());
-}
-
-TEST_F(MemoryCoordinatorImplTest, DiscardTab) {
-  coordinator_->DiscardTab(false /* skip_unload_handlers */);
-  EXPECT_EQ(1, coordinator_->GetDelegate()->discard_tab_count());
-}
-
-TEST_F(MemoryCoordinatorImplTest, DiscardTabUnderCritical) {
-  auto* condition_observer = coordinator_->condition_observer_.get();
-  GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(1000);
-
-  base::TimeDelta interval = base::TimeDelta::FromSeconds(5);
-  condition_observer->monitoring_interval_ = interval;
-
-  auto* delegate = coordinator_->GetDelegate();
-
-  coordinator_->Start();
-  task_runner_->RunUntilIdle();
-  EXPECT_EQ(MemoryCondition::NORMAL, coordinator_->GetMemoryCondition());
-  EXPECT_EQ(0, delegate->discard_tab_count());
-
-  // Enter CRITICAL condition. Tab discarding should start.
-  GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(0);
-  task_runner_->FastForwardBy(interval);
-  EXPECT_EQ(1, delegate->discard_tab_count());
-  task_runner_->FastForwardBy(interval);
-  EXPECT_EQ(2, delegate->discard_tab_count());
-
-  // Back to NORMAL. Tab discarding should stop.
-  GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(1000);
-  task_runner_->FastForwardBy(interval);
-  EXPECT_EQ(2, delegate->discard_tab_count());
-  task_runner_->FastForwardBy(interval);
-  EXPECT_EQ(2, delegate->discard_tab_count());
-}
-
-#if defined(OS_ANDROID)
-// TODO(jcivelli): Broken on Android. http://crbug.com/678665
-#define MAYBE_GetStateForProcess DISABLED_GetStateForProcess
-#else
-#define MAYBE_GetStateForProcess GetStateForProcess
-#endif
-TEST_F(MemoryCoordinatorImplTest, MAYBE_GetStateForProcess) {
-  EXPECT_EQ(base::MemoryState::UNKNOWN,
-            coordinator_->GetStateForProcess(base::kNullProcessHandle));
-  EXPECT_EQ(base::MemoryState::NORMAL,
-            coordinator_->GetStateForProcess(base::GetCurrentProcessHandle()));
-
-  coordinator_->CreateChildMemoryCoordinator(1);
-  coordinator_->CreateChildMemoryCoordinator(2);
-  base::Process process1 = SpawnChild("process1");
-  base::ProcessHandle process1_handle = process1.Handle();
-  base::Process process2 = SpawnChild("process2");
-  base::ProcessHandle process2_handle = process2.Handle();
-
-  coordinator_->GetMockRenderProcessHost(1)->SetProcess(std::move(process1));
-  coordinator_->GetMockRenderProcessHost(2)->SetProcess(std::move(process2));
-
-  EXPECT_EQ(base::MemoryState::NORMAL,
-            coordinator_->GetStateForProcess(process1_handle));
-  EXPECT_EQ(base::MemoryState::NORMAL,
-            coordinator_->GetStateForProcess(process2_handle));
-
-  EXPECT_TRUE(
-      coordinator_->SetChildMemoryState(1, MemoryState::THROTTLED));
-  EXPECT_EQ(base::MemoryState::THROTTLED,
-            coordinator_->GetStateForProcess(process1_handle));
-  EXPECT_EQ(base::MemoryState::NORMAL,
-            coordinator_->GetStateForProcess(process2_handle));
-}
-
-}  // namespace content
diff --git a/content/browser/presentation/OWNERS b/content/browser/presentation/OWNERS
index 1a85e3fd..bb2f523c 100644
--- a/content/browser/presentation/OWNERS
+++ b/content/browser/presentation/OWNERS
@@ -1,5 +1,6 @@
 # Presentation API OWNERS
-imcheng@chromium.org
 mfoltz@chromium.org
 mlamouri@chromium.org
 
+# TEAM: media-dev@chromium.org
+# COMPONENT: Blink>PresentationAPI
diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc
index ff1d654e..358d816 100644
--- a/content/browser/renderer_host/delegated_frame_host.cc
+++ b/content/browser/renderer_host/delegated_frame_host.cc
@@ -109,29 +109,21 @@
               },
               std::move(callback)));
 
-  if (!src_subrect.IsEmpty())
-    request->set_area(src_subrect);
-  if (!output_size.IsEmpty())
+  if (!src_subrect.IsEmpty()) {
+    request->set_area(
+        gfx::ScaleToRoundedRect(src_subrect, client_->GetDeviceScaleFactor()));
+  }
+  if (!output_size.IsEmpty()) {
+    // The CopyOutputRequest API does not allow fixing the output size. Instead
+    // we have the set area and scale in such a way that it would result in the
+    // desired output size.
+    if (!request->has_area())
+      request->set_area(gfx::Rect(surface_dip_size_));
     request->set_result_selection(gfx::Rect(output_size));
-
-  if (!request->has_area())
-    request->set_area(gfx::Rect(surface_dip_size_));
-
-  request->set_area(gfx::ScaleToRoundedRect(request->area(),
-                                            client_->GetDeviceScaleFactor()));
-
-  if (request->has_result_selection()) {
     const gfx::Rect& area = request->area();
-    const gfx::Rect& result_selection = request->result_selection();
-    if (area.IsEmpty() || result_selection.IsEmpty()) {
-      // Viz would normally return an empty result for an empty selection.
-      // However, this guard here is still necessary to protect against setting
-      // an illegal scaling ratio.
-      return;
-    }
     request->SetScaleRatio(
         gfx::Vector2d(area.width(), area.height()),
-        gfx::Vector2d(result_selection.width(), result_selection.height()));
+        gfx::Vector2d(output_size.width(), output_size.height()));
   }
   DCHECK(host_frame_sink_manager_);
   host_frame_sink_manager_->RequestCopyOfOutput(
diff --git a/content/browser/renderer_host/overscroll_configuration.cc b/content/browser/renderer_host/overscroll_configuration.cc
index 34496b1..baf88d8 100644
--- a/content/browser/renderer_host/overscroll_configuration.cc
+++ b/content/browser/renderer_host/overscroll_configuration.cc
@@ -59,13 +59,8 @@
   if (g_is_history_navigation_mode_initialized)
     return g_history_navigation_mode;
 
-  const std::string mode =
-      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-          switches::kOverscrollHistoryNavigation);
-  if (mode == "0")
+  if (!base::FeatureList::IsEnabled(features::kOverscrollHistoryNavigation))
     g_history_navigation_mode = HistoryNavigationMode::kDisabled;
-  else if (mode == "1")
-    g_history_navigation_mode = HistoryNavigationMode::kParallaxUi;
   g_is_history_navigation_mode_initialized = true;
   return g_history_navigation_mode;
 }
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 4ef0292..990a6064 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -1278,6 +1278,10 @@
   if (IsIgnoringInputEvents())
     return;
 
+  // The gesture events must have a known source.
+  DCHECK_NE(gesture_event.SourceDevice(),
+            blink::WebGestureDevice::kWebGestureDeviceUninitialized);
+
   bool scroll_update_needs_wrapping = false;
   if (gesture_event.GetType() == blink::WebInputEvent::kGestureScrollBegin) {
     // When a user starts scrolling while a fling is active, the GSB will arrive
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.cc b/content/browser/renderer_host/render_widget_host_input_event_router.cc
index ea9122d..09cec86 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router.cc
+++ b/content/browser/renderer_host/render_widget_host_input_event_router.cc
@@ -42,9 +42,13 @@
   }
 }
 
-blink::WebGestureEvent DummyGestureScrollUpdate(base::TimeTicks time_stamp) {
+blink::WebGestureEvent DummyGestureScrollUpdate(
+    base::TimeTicks time_stamp,
+    blink::WebGestureDevice source_device =
+        blink::kWebGestureDeviceUninitialized) {
   return blink::WebGestureEvent(blink::WebInputEvent::kGestureScrollUpdate,
-                                blink::WebInputEvent::kNoModifiers, time_stamp);
+                                blink::WebInputEvent::kNoModifiers, time_stamp,
+                                source_device);
 }
 
 gfx::PointF ComputePointInRootInPixels(
@@ -596,8 +600,8 @@
         // the wheel target view is destroyed and the wheel end event won't get
         // processed.
         blink::WebGestureEvent fake_scroll_update =
-            DummyGestureScrollUpdate(mouse_wheel_event.TimeStamp());
-        fake_scroll_update.SetSourceDevice(blink::kWebGestureDeviceTouchpad);
+            DummyGestureScrollUpdate(mouse_wheel_event.TimeStamp(),
+                                     bubbling_gesture_scroll_source_device_);
         SendGestureScrollEnd(bubbling_gesture_scroll_target_.target,
                              fake_scroll_update);
         bubbling_gesture_scroll_target_.target = nullptr;
@@ -788,8 +792,10 @@
 
   if (is_sequence_start) {
     if (touch_target_.target == bubbling_gesture_scroll_target_.target) {
-      SendGestureScrollEnd(bubbling_gesture_scroll_target_.target,
-                           DummyGestureScrollUpdate(touch_event.TimeStamp()));
+      SendGestureScrollEnd(
+          bubbling_gesture_scroll_target_.target,
+          DummyGestureScrollUpdate(touch_event.TimeStamp(),
+                                   bubbling_gesture_scroll_source_device_));
       CancelScrollBubbling(bubbling_gesture_scroll_target_.target);
     }
   }
@@ -1035,6 +1041,7 @@
     }
 
     bubbling_gesture_scroll_target_.target = target_view;
+    bubbling_gesture_scroll_source_device_ = event.SourceDevice();
   } else if (event.GetType() == blink::WebInputEvent::kGestureFlingCancel) {
     // TODO(828422): Remove once this issue no longer occurs.
     if (resending_view == last_fling_start_bubbled_target_) {
@@ -1080,6 +1087,8 @@
     ReportBubblingScrollToSameView(event, resending_view);
     first_bubbling_scroll_target_.target = nullptr;
     bubbling_gesture_scroll_target_.target = nullptr;
+    bubbling_gesture_scroll_source_device_ =
+        blink::kWebGestureDeviceUninitialized;
     return;
   }
 
@@ -1096,6 +1105,8 @@
       event.GetType() == blink::WebInputEvent::kGestureFlingStart) {
     first_bubbling_scroll_target_.target = nullptr;
     bubbling_gesture_scroll_target_.target = nullptr;
+    bubbling_gesture_scroll_source_device_ =
+        blink::kWebGestureDeviceUninitialized;
   }
 }
 
@@ -1166,6 +1177,8 @@
   if (target_view == first_bubbling_scroll_target_.target) {
     first_bubbling_scroll_target_.target = nullptr;
     bubbling_gesture_scroll_target_.target = nullptr;
+    bubbling_gesture_scroll_source_device_ =
+        blink::kWebGestureDeviceUninitialized;
   }
 }
 
@@ -1399,8 +1412,10 @@
     if (touchscreen_gesture_target_.target &&
         touchscreen_gesture_target_.target ==
             bubbling_gesture_scroll_target_.target) {
-      SendGestureScrollEnd(bubbling_gesture_scroll_target_.target,
-                           DummyGestureScrollUpdate(gesture_event.TimeStamp()));
+      SendGestureScrollEnd(
+          bubbling_gesture_scroll_target_.target,
+          DummyGestureScrollUpdate(gesture_event.TimeStamp(),
+                                   bubbling_gesture_scroll_source_device_));
       CancelScrollBubbling(bubbling_gesture_scroll_target_.target);
     }
   }
@@ -1518,7 +1533,8 @@
             bubbling_gesture_scroll_target_.target) {
       SendGestureScrollEnd(
           bubbling_gesture_scroll_target_.target,
-          DummyGestureScrollUpdate(touchpad_gesture_event.TimeStamp()));
+          DummyGestureScrollUpdate(touchpad_gesture_event.TimeStamp(),
+                                   bubbling_gesture_scroll_source_device_));
       CancelScrollBubbling(bubbling_gesture_scroll_target_.target);
     }
   }
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.h b/content/browser/renderer_host/render_widget_host_input_event_router.h
index 575caa9..3d20c84b 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router.h
+++ b/content/browser/renderer_host/render_widget_host_input_event_router.h
@@ -326,6 +326,11 @@
   // Tracked for the purpose of providing a root_view when dispatching emulated
   // touch/gesture events.
   RenderWidgetHostViewBase* last_emulated_event_root_view_;
+
+  // Used to send a GSE with proper source device to terminate scroll bubbling
+  // whenever needed.
+  blink::WebGestureDevice bubbling_gesture_scroll_source_device_;
+
   float last_device_scale_factor_;
 
   int active_touches_;
diff --git a/content/browser/scheduler/responsiveness/calculator.cc b/content/browser/scheduler/responsiveness/calculator.cc
index 2c82001..82b9a859 100644
--- a/content/browser/scheduler/responsiveness/calculator.cc
+++ b/content/browser/scheduler/responsiveness/calculator.cc
@@ -89,6 +89,21 @@
 void Calculator::EmitResponsiveness(int janky_slices) {
   UMA_HISTOGRAM_COUNTS_1000(
       "Browser.Responsiveness.JankyIntervalsPerThirtySeconds", janky_slices);
+
+  ++emission_count_;
+  if (emission_count_ == 1) {
+    // We log this metric every 30 seconds [unless there is no activity on the
+    // UI and IO threads]. The first time we log this will always cover startup,
+    // since we're guaranteed to have events on the UI thread during startup.
+    UMA_HISTOGRAM_COUNTS_1000(
+        "Browser.Responsiveness.JankyIntervalsPerThirtySeconds.Startup",
+        janky_slices);
+  } else {
+    // Future emissions definitely do not apply to startup.
+    UMA_HISTOGRAM_COUNTS_1000(
+        "Browser.Responsiveness.JankyIntervalsPerThirtySeconds.NonStartup",
+        janky_slices);
+  }
 }
 
 base::TimeTicks Calculator::GetLastCalculationTime() {
diff --git a/content/browser/scheduler/responsiveness/calculator.h b/content/browser/scheduler/responsiveness/calculator.h
index a5c7c40..ae24d25 100644
--- a/content/browser/scheduler/responsiveness/calculator.h
+++ b/content/browser/scheduler/responsiveness/calculator.h
@@ -121,6 +121,9 @@
   // executed, so a very long execution time should be treated similarly.
   base::TimeTicks most_recent_activity_time_;
 
+  // The number of times the responsiveness metric has been emitted.
+  int emission_count_ = 0;
+
   DISALLOW_COPY_AND_ASSIGN(Calculator);
 };
 
diff --git a/content/browser/scheduler/responsiveness/native_event_observer.cc b/content/browser/scheduler/responsiveness/native_event_observer.cc
index 0aa5e6e..ff9f906a 100644
--- a/content/browser/scheduler/responsiveness/native_event_observer.cc
+++ b/content/browser/scheduler/responsiveness/native_event_observer.cc
@@ -8,8 +8,6 @@
 // Windows headers must come first.
 #if defined(OS_WIN)
 #include <windows.h>
-
-#include <timeapi.h>
 #endif
 
 // Proceed with header includes in usual order.
@@ -29,29 +27,6 @@
 namespace content {
 namespace responsiveness {
 
-#if defined(OS_WIN) || (defined(OS_LINUX) && defined(USE_X11))
-
-namespace {
-
-// Clamps a TimeDelta to be within [0 seconds, 30 seconds].
-// Relies on the assumption that |delta| is >= 0 seconds.
-base::TimeDelta ClampDeltaFromExternalSource(const base::TimeDelta& delta) {
-  DCHECK_GE(delta, base::TimeDelta());
-
-  // Ignore pathologically long deltas. External source is probably having
-  // issues.
-  constexpr base::TimeDelta pathologically_long_duration =
-      base::TimeDelta::FromSeconds(30);
-  if (delta > pathologically_long_duration)
-    return base::TimeDelta();
-
-  return delta;
-}
-
-}  // namespace
-
-#endif  // defined(OS_WIN) || (defined(OS_LINUX) && defined(USE_X11))
-
 NativeEventObserver::NativeEventObserver(
     WillRunEventCallback will_run_event_callback,
     DidRunEventCallback did_run_event_callback)
@@ -75,7 +50,7 @@
 void NativeEventObserver::OnWindowEventDispatcherStartedProcessing(
     aura::WindowEventDispatcher* dispatcher,
     const ui::Event& event) {
-  EventInfo info{&event, event.time_stamp()};
+  EventInfo info{&event};
   events_being_processed_.push_back(info);
   will_run_event_callback_.Run(&event);
 }
@@ -83,7 +58,7 @@
 void NativeEventObserver::OnWindowEventDispatcherFinishedProcessingEvent(
     aura::WindowEventDispatcher* dispatcher) {
   EventInfo& info = events_being_processed_.back();
-  did_run_event_callback_.Run(info.unique_id, info.creation_time);
+  did_run_event_callback_.Run(info.unique_id);
   events_being_processed_.pop_back();
 }
 #endif  // defined(OS_LINUX)
@@ -99,31 +74,7 @@
   will_run_event_callback_.Run(&msg);
 }
 void NativeEventObserver::DidDispatchMSG(const MSG& msg) {
-  // On Windows, MSG.time is in units of milliseconds since system started. It
-  // uses the timer exposed by ::GetTickCount().
-  // https://blogs.msdn.microsoft.com/oldnewthing/20140122-00/?p=2013
-  //
-  // This timer has ~16ms granularity. This is okay for us since we require
-  // ~100ms granularity.
-  // https://randomascii.wordpress.com/2013/05/09/timegettime-versus-gettickcount/
-  //
-  // To convert MSG.time to TimeTicks, we subtract MSG.time from
-  // ::GetTickCount() to create a TimeDelta, and then subtract that from
-  // TimeTicks::Now().
-  //
-  // We cast both values to DWORD [uint32], perform subtraction to get a uint32,
-  // and then shove that into a TimeDelta.
-  //
-  // Note: Both measurements of time can experience rollover. If one of them has
-  // rolled over but the other has not, then the result will be very large.
-  // ClampDeltaFromExternalSource takes care of this, in addition to any other
-  // odd timing issues that may come up [e.g. MSG.time time-traveling to give a
-  // larger value than ::timeGetTime() -- this has not been observed in practice
-  // but since we don't control the code in question, we trust nothing].
-  base::TimeDelta delta = base::TimeDelta::FromMilliseconds(
-      ::GetTickCount() - static_cast<DWORD>(msg.time));
-  base::TimeDelta sanitized = ClampDeltaFromExternalSource(delta);
-  did_run_event_callback_.Run(&msg, base::TimeTicks::Now() - sanitized);
+  did_run_event_callback_.Run(&msg);
 }
 #endif  // defined(OS_WIN)
 
diff --git a/content/browser/scheduler/responsiveness/native_event_observer.h b/content/browser/scheduler/responsiveness/native_event_observer.h
index cf2ef28d..84b4f71 100644
--- a/content/browser/scheduler/responsiveness/native_event_observer.h
+++ b/content/browser/scheduler/responsiveness/native_event_observer.h
@@ -7,7 +7,6 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
-#include "base/time/time.h"
 #include "build/build_config.h"
 #include "content/common/content_export.h"
 
@@ -49,11 +48,8 @@
  public:
   using WillRunEventCallback =
       base::RepeatingCallback<void(const void* opaque_identifier)>;
-
-  // |creation_time| refers to the time at which the native event was created.
   using DidRunEventCallback =
-      base::RepeatingCallback<void(const void* opaque_identifier,
-                                   base::TimeTicks creation_time)>;
+      base::RepeatingCallback<void(const void* opaque_identifier)>;
 
   // The constructor will register the object as an observer of the native event
   // processor. The destructor will unregister the object.
@@ -71,8 +67,7 @@
   // NativeEventProcessorObserver overrides:
   // Exposed for tests.
   void WillRunNativeEvent(const void* opaque_identifier) override;
-  void DidRunNativeEvent(const void* opaque_identifier,
-                         base::TimeTicks creation_time) override;
+  void DidRunNativeEvent(const void* opaque_identifier) override;
 #elif defined(OS_LINUX)
   // aura::WindowEventDispatcherObserver overrides:
   void OnWindowEventDispatcherStartedProcessing(
@@ -93,7 +88,6 @@
 #if defined(OS_LINUX)
   struct EventInfo {
     const void* unique_id;
-    base::TimeTicks creation_time;
   };
   std::vector<EventInfo> events_being_processed_;
 #endif
diff --git a/content/browser/scheduler/responsiveness/native_event_observer_browsertest.mm b/content/browser/scheduler/responsiveness/native_event_observer_browsertest.mm
index e620742..a6b3579 100644
--- a/content/browser/scheduler/responsiveness/native_event_observer_browsertest.mm
+++ b/content/browser/scheduler/responsiveness/native_event_observer_browsertest.mm
@@ -25,21 +25,17 @@
     ASSERT_FALSE(will_run_id_);
     will_run_id_ = opaque_identifier;
   }
-  void DidRunNativeEvent(const void* opaque_identifier,
-                         base::TimeTicks creation_time) override {
+  void DidRunNativeEvent(const void* opaque_identifier) override {
     ASSERT_FALSE(did_run_id_);
     did_run_id_ = opaque_identifier;
-    creation_time_ = creation_time;
   }
 
   const void* will_run_id() { return will_run_id_; }
   const void* did_run_id() { return did_run_id_; }
-  base::TimeTicks creation_time() { return creation_time_; }
 
  private:
   const void* will_run_id_ = nullptr;
   const void* did_run_id_ = nullptr;
-  base::TimeTicks creation_time_;
 };
 
 }  // namespace
@@ -53,19 +49,12 @@
 
   EXPECT_FALSE(observer.will_run_id());
   EXPECT_FALSE(observer.did_run_id());
-  base::TimeTicks time_at_creation = base::TimeTicks::Now();
   NSEvent* event = cocoa_test_event_utils::KeyEventWithKeyCode(kVK_Return, '\r',
                                                                NSKeyDown, 0);
   [NSApp sendEvent:event];
 
   EXPECT_EQ(observer.will_run_id(), event);
   EXPECT_EQ(observer.did_run_id(), event);
-
-  // time_at_creation should be really similar to creation_time. As a sanity
-  // check, make sure they're within a second of each other.
-  EXPECT_LT(
-      fabs((observer.creation_time() - time_at_creation).InMilliseconds()),
-      1000);
 }
 
 }  // namespace responsiveness
diff --git a/content/browser/scheduler/responsiveness/native_event_observer_browsertest_win.cc b/content/browser/scheduler/responsiveness/native_event_observer_browsertest_win.cc
index 426ec6ff..5480870 100644
--- a/content/browser/scheduler/responsiveness/native_event_observer_browsertest_win.cc
+++ b/content/browser/scheduler/responsiveness/native_event_observer_browsertest_win.cc
@@ -28,17 +28,15 @@
     ASSERT_FALSE(will_run_id_);
     will_run_id_ = opaque_id;
   }
-  void DidRunEvent(const void* opaque_id, base::TimeTicks creation_time) {
+  void DidRunEvent(const void* opaque_id) {
     ASSERT_FALSE(did_run_id_);
     did_run_id_ = opaque_id;
-    creation_time_ = creation_time;
     std::move(quit_closure_).Run();
   }
 
  protected:
   const void* will_run_id_ = nullptr;
   const void* did_run_id_ = nullptr;
-  base::TimeTicks creation_time_;
   base::OnceClosure quit_closure_;
 };
 
@@ -54,7 +52,6 @@
       base::BindRepeating(
           &ResponsivenessNativeEventObserverBrowserTest::DidRunEvent,
           base::Unretained(this)));
-  base::TimeTicks time_at_creation = base::TimeTicks::Now();
 
   EXPECT_FALSE(will_run_id_);
   EXPECT_FALSE(did_run_id_);
@@ -66,10 +63,6 @@
 
   EXPECT_EQ(will_run_id_, did_run_id_);
   EXPECT_NE(will_run_id_, nullptr);
-
-  // time_at_creation should be really similar to creation_time_. As a sanity
-  // check, make sure they're within a second of each other.
-  EXPECT_LT(fabs((creation_time_ - time_at_creation).InMilliseconds()), 1000);
 }
 
 }  // namespace responsiveness
diff --git a/content/browser/scheduler/responsiveness/native_event_observer_mac.mm b/content/browser/scheduler/responsiveness/native_event_observer_mac.mm
index a074d877..7cb3238 100644
--- a/content/browser/scheduler/responsiveness/native_event_observer_mac.mm
+++ b/content/browser/scheduler/responsiveness/native_event_observer_mac.mm
@@ -27,9 +27,8 @@
 void NativeEventObserver::WillRunNativeEvent(const void* opaque_identifier) {
   will_run_event_callback_.Run(opaque_identifier);
 }
-void NativeEventObserver::DidRunNativeEvent(const void* opaque_identifier,
-                                            base::TimeTicks creation_time) {
-  did_run_event_callback_.Run(opaque_identifier, creation_time);
+void NativeEventObserver::DidRunNativeEvent(const void* opaque_identifier) {
+  did_run_event_callback_.Run(opaque_identifier);
 }
 
 }  // namespace responsiveness
diff --git a/content/browser/scheduler/responsiveness/watcher.cc b/content/browser/scheduler/responsiveness/watcher.cc
index ef5e31eb..6d5c871 100644
--- a/content/browser/scheduler/responsiveness/watcher.cc
+++ b/content/browser/scheduler/responsiveness/watcher.cc
@@ -230,8 +230,7 @@
       base::TimeTicks::Now();
 }
 
-void Watcher::DidRunEventOnUIThread(const void* opaque_identifier,
-                                    base::TimeTicks creation_time) {
+void Watcher::DidRunEventOnUIThread(const void* opaque_identifier) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   // Calls to DidRunEventOnUIThread should always be paired with
diff --git a/content/browser/scheduler/responsiveness/watcher.h b/content/browser/scheduler/responsiveness/watcher.h
index 5d3c945a..d6fafba 100644
--- a/content/browser/scheduler/responsiveness/watcher.h
+++ b/content/browser/scheduler/responsiveness/watcher.h
@@ -117,8 +117,7 @@
   // These methods are called by the NativeEventObserver of the UI thread to
   // allow Watcher to collect metadata about the events being run.
   void WillRunEventOnUIThread(const void* opaque_identifier);
-  void DidRunEventOnUIThread(const void* opaque_identifier,
-                             base::TimeTicks creation_time);
+  void DidRunEventOnUIThread(const void* opaque_identifier);
 
   // The following members are all affine to the UI thread.
   std::unique_ptr<Calculator> calculator_;
diff --git a/content/browser/scheduler/responsiveness/watcher_unittest.cc b/content/browser/scheduler/responsiveness/watcher_unittest.cc
index 82b4c7bfb..66f635b 100644
--- a/content/browser/scheduler/responsiveness/watcher_unittest.cc
+++ b/content/browser/scheduler/responsiveness/watcher_unittest.cc
@@ -173,7 +173,7 @@
 
   void* opaque_identifier = reinterpret_cast<void*>(0x1234);
   watcher_->WillRunEventOnUIThread(opaque_identifier);
-  watcher_->DidRunEventOnUIThread(opaque_identifier, base::TimeTicks());
+  watcher_->DidRunEventOnUIThread(opaque_identifier);
 
   ASSERT_EQ(1, watcher_->NumTasksOnUIThread());
 
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc
index 8f9e3d1..f57d0c661 100644
--- a/content/browser/service_worker/service_worker_context_core.cc
+++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -245,8 +245,8 @@
 
 ServiceWorkerContextCore::ProviderHostIterator::ProviderHostIterator(
     ProcessToProviderMap* map,
-    const ProviderHostPredicate& predicate)
-    : map_(map), predicate_(predicate) {
+    ProviderHostPredicate predicate)
+    : map_(map), predicate_(std::move(predicate)) {
   DCHECK(map);
   Initialize();
 }
diff --git a/content/browser/service_worker/service_worker_context_core.h b/content/browser/service_worker/service_worker_context_core.h
index 282becd..d511678d 100644
--- a/content/browser/service_worker/service_worker_context_core.h
+++ b/content/browser/service_worker/service_worker_context_core.h
@@ -93,9 +93,9 @@
    private:
     friend class ServiceWorkerContextCore;
     using ProviderHostPredicate =
-        base::Callback<bool(ServiceWorkerProviderHost*)>;
+        base::RepeatingCallback<bool(ServiceWorkerProviderHost*)>;
     ProviderHostIterator(ProcessToProviderMap* map,
-                         const ProviderHostPredicate& predicate);
+                         ProviderHostPredicate predicate);
     void Initialize();
     bool ForwardUntilMatchingProviderHost();
 
diff --git a/content/browser/service_worker/service_worker_context_watcher.cc b/content/browser/service_worker/service_worker_context_watcher.cc
index 9009603..aadc4c2 100644
--- a/content/browser/service_worker/service_worker_context_watcher.cc
+++ b/content/browser/service_worker/service_worker_context_watcher.cc
@@ -30,13 +30,13 @@
 
 ServiceWorkerContextWatcher::ServiceWorkerContextWatcher(
     scoped_refptr<ServiceWorkerContextWrapper> context,
-    const WorkerRegistrationUpdatedCallback& registration_callback,
-    const WorkerVersionUpdatedCallback& version_callback,
-    const WorkerErrorReportedCallback& error_callback)
+    WorkerRegistrationUpdatedCallback registration_callback,
+    WorkerVersionUpdatedCallback version_callback,
+    WorkerErrorReportedCallback error_callback)
     : context_(context),
-      registration_callback_(registration_callback),
-      version_callback_(version_callback),
-      error_callback_(error_callback) {
+      registration_callback_(std::move(registration_callback)),
+      version_callback_(std::move(version_callback)),
+      error_callback_(std::move(error_callback)) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
diff --git a/content/browser/service_worker/service_worker_context_watcher.h b/content/browser/service_worker/service_worker_context_watcher.h
index 756ab37..7e0da602 100644
--- a/content/browser/service_worker/service_worker_context_watcher.h
+++ b/content/browser/service_worker/service_worker_context_watcher.h
@@ -29,20 +29,20 @@
     : public ServiceWorkerContextCoreObserver,
       public base::RefCountedThreadSafe<ServiceWorkerContextWatcher> {
  public:
-  typedef base::Callback<void(
-      const std::vector<ServiceWorkerRegistrationInfo>&)>
-      WorkerRegistrationUpdatedCallback;
-  typedef base::Callback<void(const std::vector<ServiceWorkerVersionInfo>&)>
-      WorkerVersionUpdatedCallback;
-  typedef base::Callback<void(int64_t /* registration_id */,
-                              int64_t /* version_id */,
-                              const ErrorInfo&)> WorkerErrorReportedCallback;
+  using WorkerRegistrationUpdatedCallback = base::RepeatingCallback<void(
+      const std::vector<ServiceWorkerRegistrationInfo>&)>;
+  using WorkerVersionUpdatedCallback = base::RepeatingCallback<void(
+      const std::vector<ServiceWorkerVersionInfo>&)>;
+  using WorkerErrorReportedCallback =
+      base::RepeatingCallback<void(int64_t /* registration_id */,
+                                   int64_t /* version_id */,
+                                   const ErrorInfo&)>;
 
   ServiceWorkerContextWatcher(
       scoped_refptr<ServiceWorkerContextWrapper> context,
-      const WorkerRegistrationUpdatedCallback& registration_callback,
-      const WorkerVersionUpdatedCallback& version_callback,
-      const WorkerErrorReportedCallback& error_callback);
+      WorkerRegistrationUpdatedCallback registration_callback,
+      WorkerVersionUpdatedCallback version_callback,
+      WorkerErrorReportedCallback error_callback);
   void Start();
   void Stop();
 
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc b/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
index e929e0d..2b92849 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
+++ b/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
@@ -213,7 +213,7 @@
       const GURL& scope,
       const GURL& first_party,
       content::ResourceContext* context,
-      const base::Callback<WebContents*(void)>& wc_getter) override {
+      base::RepeatingCallback<WebContents*()> wc_getter) override {
     return false;
   }
 };
diff --git a/content/browser/service_worker/service_worker_internals_ui.cc b/content/browser/service_worker/service_worker_internals_ui.cc
index 9d13274..3fd03a4 100644
--- a/content/browser/service_worker/service_worker_internals_ui.cc
+++ b/content/browser/service_worker/service_worker_internals_ui.cc
@@ -46,9 +46,9 @@
 namespace {
 
 using GetRegistrationsCallback =
-    base::Callback<void(const std::vector<ServiceWorkerRegistrationInfo>&,
-                        const std::vector<ServiceWorkerVersionInfo>&,
-                        const std::vector<ServiceWorkerRegistrationInfo>&)>;
+    base::OnceCallback<void(const std::vector<ServiceWorkerRegistrationInfo>&,
+                            const std::vector<ServiceWorkerVersionInfo>&,
+                            const std::vector<ServiceWorkerRegistrationInfo>&)>;
 
 void OperationCompleteCallback(WeakPtr<ServiceWorkerInternalsUI> internals,
                                int callback_id,
@@ -201,22 +201,22 @@
 
 void DidGetStoredRegistrationsOnIOThread(
     scoped_refptr<ServiceWorkerContextWrapper> context,
-    const GetRegistrationsCallback& callback,
+    GetRegistrationsCallback callback,
     blink::ServiceWorkerStatusCode status,
     const std::vector<ServiceWorkerRegistrationInfo>& stored_registrations) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::UI},
-      base::BindOnce(callback, context->GetAllLiveRegistrationInfo(),
+      base::BindOnce(std::move(callback), context->GetAllLiveRegistrationInfo(),
                      context->GetAllLiveVersionInfo(), stored_registrations));
 }
 
 void GetRegistrationsOnIOThread(
     scoped_refptr<ServiceWorkerContextWrapper> context,
-    const GetRegistrationsCallback& callback) {
+    GetRegistrationsCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  context->GetAllRegistrations(
-      base::BindOnce(DidGetStoredRegistrationsOnIOThread, context, callback));
+  context->GetAllRegistrations(base::BindOnce(
+      DidGetStoredRegistrationsOnIOThread, context, std::move(callback)));
 }
 
 void DidGetRegistrations(
@@ -362,8 +362,9 @@
   // Safe to use base::Unretained(this) because
   // ForEachStoragePartition is synchronous.
   BrowserContext::StoragePartitionCallback remove_observer_cb =
-      base::Bind(&ServiceWorkerInternalsUI::RemoveObserverFromStoragePartition,
-                 base::Unretained(this));
+      base::BindRepeating(
+          &ServiceWorkerInternalsUI::RemoveObserverFromStoragePartition,
+          base::Unretained(this));
   BrowserContext::ForEachStoragePartition(browser_context,
                                           std::move(remove_observer_cb));
 }
@@ -393,9 +394,9 @@
       web_ui()->GetWebContents()->GetBrowserContext();
   // Safe to use base::Unretained(this) because
   // ForEachStoragePartition is synchronous.
-  BrowserContext::StoragePartitionCallback add_context_cb =
-      base::Bind(&ServiceWorkerInternalsUI::AddContextFromStoragePartition,
-                 base::Unretained(this));
+  BrowserContext::StoragePartitionCallback add_context_cb = base::BindRepeating(
+      &ServiceWorkerInternalsUI::AddContextFromStoragePartition,
+      base::Unretained(this));
   BrowserContext::ForEachStoragePartition(browser_context,
                                           std::move(add_context_cb));
 }
@@ -422,9 +423,9 @@
       FROM_HERE, {BrowserThread::IO},
       base::BindOnce(
           GetRegistrationsOnIOThread, context,
-          base::Bind(DidGetRegistrations, AsWeakPtr(), partition_id,
-                     context->is_incognito() ? base::FilePath()
-                                             : partition->GetPath())));
+          base::BindOnce(DidGetRegistrations, AsWeakPtr(), partition_id,
+                         context->is_incognito() ? base::FilePath()
+                                                 : partition->GetPath())));
 }
 
 void ServiceWorkerInternalsUI::RemoveObserverFromStoragePartition(
@@ -457,10 +458,9 @@
       web_ui()->GetWebContents()->GetBrowserContext();
   StoragePartition* result_partition(nullptr);
   BrowserContext::StoragePartitionCallback find_context_cb =
-      base::Bind(&ServiceWorkerInternalsUI::FindContext,
-                 base::Unretained(this),
-                 partition_id,
-                 &result_partition);
+      base::BindRepeating(&ServiceWorkerInternalsUI::FindContext,
+                          base::Unretained(this), partition_id,
+                          &result_partition);
   BrowserContext::ForEachStoragePartition(browser_context,
                                           std::move(find_context_cb));
   if (!result_partition)
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index ab04932..da1fc5a 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -195,7 +195,7 @@
 ServiceWorkerProviderHost::PreCreateNavigationHost(
     base::WeakPtr<ServiceWorkerContextCore> context,
     bool are_ancestors_secure,
-    const WebContentsGetter& web_contents_getter) {
+    WebContentsGetter web_contents_getter) {
   DCHECK(context);
   auto host = base::WrapUnique(new ServiceWorkerProviderHost(
       ChildProcessHost::kInvalidUniqueID,
@@ -205,7 +205,7 @@
           are_ancestors_secure, nullptr /* host_request */,
           nullptr /* client_ptr_info */),
       context));
-  host->web_contents_getter_ = web_contents_getter;
+  host->web_contents_getter_ = std::move(web_contents_getter);
 
   auto weak_ptr = host->AsWeakPtr();
   context->AddProviderHost(std::move(host));
@@ -616,8 +616,8 @@
   return GetContentClient()->browser()->AllowServiceWorker(
       scope, IsProviderForClient() ? topmost_frame_url() : document_url(),
       context_->wrapper()->resource_context(),
-      base::Bind(&WebContentsImpl::FromRenderFrameHostID, render_process_id_,
-                 frame_id()));
+      base::BindRepeating(&WebContentsImpl::FromRenderFrameHostID,
+                          render_process_id_, frame_id()));
 }
 
 void ServiceWorkerProviderHost::NotifyControllerLost() {
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index c8b23a3..572f3480 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -128,7 +128,7 @@
       public mojom::ServiceWorkerContainerHost,
       public service_manager::mojom::InterfaceProvider {
  public:
-  using WebContentsGetter = base::Callback<WebContents*(void)>;
+  using WebContentsGetter = base::RepeatingCallback<WebContents*()>;
 
   // Used to pre-create a ServiceWorkerProviderHost for a navigation. The
   // ServiceWorkerNetworkProvider will later be created in the renderer, should
@@ -144,7 +144,7 @@
   static base::WeakPtr<ServiceWorkerProviderHost> PreCreateNavigationHost(
       base::WeakPtr<ServiceWorkerContextCore> context,
       bool are_ancestors_secure,
-      const WebContentsGetter& web_contents_getter);
+      WebContentsGetter web_contents_getter);
 
   // Used for starting a service worker. Returns a provider host for the service
   // worker and partially fills |out_provider_info|.  The host stays alive as
diff --git a/content/browser/service_worker/service_worker_provider_host_unittest.cc b/content/browser/service_worker/service_worker_provider_host_unittest.cc
index 5d73cbc2..e18aad1 100644
--- a/content/browser/service_worker/service_worker_provider_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_provider_host_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/bind_helpers.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/run_loop.h"
@@ -61,7 +62,7 @@
       const GURL& scope,
       const GURL& first_party,
       content::ResourceContext* context,
-      const base::Callback<WebContents*(void)>& wc_getter) override {
+      base::RepeatingCallback<WebContents*()> wc_getter) override {
     logs_.emplace_back(scope, first_party);
     return false;
   }
@@ -301,8 +302,7 @@
       ServiceWorkerRemoteProviderEndpoint* remote_endpoint) {
     base::WeakPtr<ServiceWorkerProviderHost> host =
         ServiceWorkerProviderHost::PreCreateNavigationHost(
-            helper_->context()->AsWeakPtr(), true,
-            base::Callback<WebContents*(void)>());
+            helper_->context()->AsWeakPtr(), true, base::NullCallback());
     mojom::ServiceWorkerProviderHostInfoPtr info =
         CreateProviderHostInfoForWindow(host->provider_id(), 1 /* route_id */);
     remote_endpoint->BindWithProviderHostInfo(&info);
@@ -448,7 +448,7 @@
   base::WeakPtr<ServiceWorkerProviderHost> host =
       ServiceWorkerProviderHost::PreCreateNavigationHost(
           helper_->context()->AsWeakPtr(), true /* are_ancestors_secure */,
-          base::Callback<WebContents*(void)>());
+          base::NullCallback());
   mojom::ServiceWorkerProviderHostInfoPtr info =
       CreateProviderHostInfoForWindow(host->provider_id(), 1 /* route_id */);
   remote_endpoints_.emplace_back();
@@ -484,7 +484,7 @@
   base::WeakPtr<ServiceWorkerProviderHost> host =
       ServiceWorkerProviderHost::PreCreateNavigationHost(
           helper_->context()->AsWeakPtr(), true /* are_ancestors_secure */,
-          base::Callback<WebContents*(void)>());
+          base::NullCallback());
   mojom::ServiceWorkerProviderHostInfoPtr info =
       CreateProviderHostInfoForWindow(host->provider_id(), 1 /* route_id */);
   remote_endpoints_.emplace_back();
diff --git a/content/browser/service_worker/service_worker_registration_unittest.cc b/content/browser/service_worker/service_worker_registration_unittest.cc
index 56bd6dc..6fbc73f 100644
--- a/content/browser/service_worker/service_worker_registration_unittest.cc
+++ b/content/browser/service_worker/service_worker_registration_unittest.cc
@@ -60,7 +60,7 @@
       const GURL& scope,
       const GURL& first_party,
       content::ResourceContext* context,
-      const base::Callback<WebContents*(void)>& wc_getter) override {
+      base::RepeatingCallback<WebContents*()> wc_getter) override {
     return false;
   }
 };
diff --git a/content/browser/service_worker/service_worker_request_handler.cc b/content/browser/service_worker/service_worker_request_handler.cc
index a857aeb..7a1d4c8 100644
--- a/content/browser/service_worker/service_worker_request_handler.cc
+++ b/content/browser/service_worker/service_worker_request_handler.cc
@@ -82,7 +82,7 @@
     network::mojom::RequestContextFrameType frame_type,
     bool is_parent_frame_secure,
     scoped_refptr<network::ResourceRequestBody> body,
-    const base::Callback<WebContents*(void)>& web_contents_getter) {
+    base::RepeatingCallback<WebContents*()> web_contents_getter) {
   // Only create a handler when there is a ServiceWorkerNavigationHandlerCore
   // to take ownership of a pre-created SeviceWorkerProviderHost.
   if (!navigation_handle_core)
@@ -115,7 +115,8 @@
   // Initialize the SWProviderHost.
   base::WeakPtr<ServiceWorkerProviderHost> provider_host =
       ServiceWorkerProviderHost::PreCreateNavigationHost(
-          context->AsWeakPtr(), is_parent_frame_secure, web_contents_getter);
+          context->AsWeakPtr(), is_parent_frame_secure,
+          std::move(web_contents_getter));
 
   std::unique_ptr<ServiceWorkerRequestHandler> handler(
       provider_host->CreateRequestHandler(
@@ -146,7 +147,7 @@
     network::mojom::RequestContextFrameType frame_type,
     bool is_parent_frame_secure,
     scoped_refptr<network::ResourceRequestBody> body,
-    const base::Callback<WebContents*(void)>& web_contents_getter) {
+    base::RepeatingCallback<WebContents*()> web_contents_getter) {
   DCHECK(blink::ServiceWorkerUtils::IsServicificationEnabled());
   DCHECK(navigation_handle_core);
 
@@ -167,7 +168,8 @@
   // Initialize the SWProviderHost.
   base::WeakPtr<ServiceWorkerProviderHost> provider_host =
       ServiceWorkerProviderHost::PreCreateNavigationHost(
-          context->AsWeakPtr(), is_parent_frame_secure, web_contents_getter);
+          context->AsWeakPtr(), is_parent_frame_secure,
+          std::move(web_contents_getter));
 
   std::unique_ptr<ServiceWorkerRequestHandler> handler(
       provider_host->CreateRequestHandler(
diff --git a/content/browser/service_worker/service_worker_request_handler.h b/content/browser/service_worker/service_worker_request_handler.h
index 39b0470..09c0d51 100644
--- a/content/browser/service_worker/service_worker_request_handler.h
+++ b/content/browser/service_worker/service_worker_request_handler.h
@@ -64,7 +64,7 @@
       network::mojom::RequestContextFrameType frame_type,
       bool is_parent_frame_secure,
       scoped_refptr<network::ResourceRequestBody> body,
-      const base::Callback<WebContents*(void)>& web_contents_getter);
+      base::RepeatingCallback<WebContents*()> web_contents_getter);
 
   // S13nServiceWorker:
   // Same as InitializeForNavigation() but instead of attaching to a URLRequest,
@@ -81,7 +81,7 @@
       network::mojom::RequestContextFrameType frame_type,
       bool is_parent_frame_secure,
       scoped_refptr<network::ResourceRequestBody> body,
-      const base::Callback<WebContents*(void)>& web_contents_getter);
+      base::RepeatingCallback<WebContents*()> web_contents_getter);
 
   static std::unique_ptr<NavigationLoaderInterceptor> InitializeForSharedWorker(
       const network::ResourceRequest& resource_request,
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 40d4725..dea6420 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -10,6 +10,7 @@
 #include <map>
 #include <string>
 
+#include "base/bind_helpers.h"
 #include "base/command_line.h"
 #include "base/guid.h"
 #include "base/location.h"
@@ -2049,7 +2050,7 @@
   if ((context_->wrapper()->resource_context() &&
        !GetContentClient()->browser()->AllowServiceWorker(
            scope_, scope_, context_->wrapper()->resource_context(),
-           base::Callback<WebContents*(void)>()))) {
+           base::NullCallback()))) {
     return false;
   }
 
diff --git a/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc b/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
index db5b377..16254748 100644
--- a/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
@@ -183,8 +183,9 @@
 class MockHttpProtocolHandler
     : public net::URLRequestJobFactory::ProtocolHandler {
  public:
-  typedef base::Callback<
-      net::URLRequestJob*(net::URLRequest*, net::NetworkDelegate*)> JobCallback;
+  using JobCallback =
+      base::RepeatingCallback<net::URLRequestJob*(net::URLRequest*,
+                                                  net::NetworkDelegate*)>;
 
   explicit MockHttpProtocolHandler(ResourceContext* resource_context)
       : resource_context_(resource_context) {}
@@ -201,8 +202,8 @@
     }
     return create_job_callback_.Run(request, network_delegate);
   }
-  void SetCreateJobCallback(const JobCallback& callback) {
-    create_job_callback_ = callback;
+  void SetCreateJobCallback(JobCallback callback) {
+    create_job_callback_ = std::move(callback);
   }
 
  private:
diff --git a/content/browser/shared_worker/shared_worker_script_loader.cc b/content/browser/shared_worker/shared_worker_script_loader.cc
index f8299e8..806a092 100644
--- a/content/browser/shared_worker/shared_worker_script_loader.cc
+++ b/content/browser/shared_worker/shared_worker_script_loader.cc
@@ -251,12 +251,19 @@
     network::mojom::URLLoaderPtr* response_url_loader,
     network::mojom::URLLoaderClientRequest* response_client_request,
     ThrottlingURLLoader* url_loader) {
+  // TODO(crbug/898755): This is odd that NavigationLoaderInterceptor::
+  // MaybeCreateLoader() is called directly from SharedWorkerScriptLoader. But
+  // NavigationLoaderInterceptor::MaybeCreateLoaderForResponse() is called from
+  // SharedWorkerScriptFetcher::OnReceiveResponse(). This is due to the wired
+  // design of SharedWorkerScriptLoader and SharedWorkerScriptFetcher and the
+  // interceptors. The interceptors should be owned by
+  // SharedWorkerScriptFetcher.
   DCHECK(default_loader_used_);
   for (auto& interceptor : interceptors_) {
     bool skip_other_interceptors = false;
     if (interceptor->MaybeCreateLoaderForResponse(
-            response, response_url_loader, response_client_request, url_loader,
-            &skip_other_interceptors)) {
+            resource_request_.url, response, response_url_loader,
+            response_client_request, url_loader, &skip_other_interceptors)) {
       // Both ServiceWorkerRequestHandler and AppCacheRequestHandler don't set
       // skip_other_interceptors.
       DCHECK(!skip_other_interceptors);
diff --git a/content/browser/shared_worker/worker_browsertest.cc b/content/browser/shared_worker/worker_browsertest.cc
index b0f78e91..fad091c 100644
--- a/content/browser/shared_worker/worker_browsertest.cc
+++ b/content/browser/shared_worker/worker_browsertest.cc
@@ -96,7 +96,7 @@
     RunTest(shell(), url, expect_failure);
   }
 
-  static void QuitUIMessageLoop(base::Callback<void()> callback) {
+  static void QuitUIMessageLoop(base::OnceClosure callback) {
     base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
                              std::move(callback));
   }
diff --git a/content/browser/web_package/signed_exchange_loader.cc b/content/browser/web_package/signed_exchange_loader.cc
index f4324a6d..820fe12 100644
--- a/content/browser/web_package/signed_exchange_loader.cc
+++ b/content/browser/web_package/signed_exchange_loader.cc
@@ -310,9 +310,10 @@
       forwarding_client_->OnComplete(network::URLLoaderCompletionStatus(error));
       return;
     }
+
     // Make a fallback redirect to |request_url|.
-    DCHECK(!has_redirected_to_fallback_url_);
-    has_redirected_to_fallback_url_ = true;
+    DCHECK(!fallback_url_);
+    fallback_url_ = request_url;
     DCHECK(outer_response_timing_info_);
     forwarding_client_->OnReceiveRedirect(
         CreateRedirectInfo(request_url, outer_request_url_),
@@ -320,6 +321,7 @@
     forwarding_client_.reset();
     return;
   }
+  inner_request_url_ = request_url;
 
   // TODO(https://crbug.com/803774): Handle no-GET request_method as a error.
   DCHECK(outer_response_timing_info_);
diff --git a/content/browser/web_package/signed_exchange_loader.h b/content/browser/web_package/signed_exchange_loader.h
index 210c3bc..48b9a946 100644
--- a/content/browser/web_package/signed_exchange_loader.h
+++ b/content/browser/web_package/signed_exchange_loader.h
@@ -65,9 +65,6 @@
       scoped_refptr<SignedExchangePrefetchMetricRecorder> metric_recorder);
   ~SignedExchangeLoader() override;
 
-  bool HasRedirectedToFallbackURL() const {
-    return has_redirected_to_fallback_url_;
-  }
 
   // network::mojom::URLLoaderClient implementation
   // Only OnStartLoadingResponseBody() and OnComplete() are called.
@@ -98,6 +95,12 @@
 
   void ConnectToClient(network::mojom::URLLoaderClientPtr client);
 
+  const base::Optional<GURL>& fallback_url() const { return fallback_url_; };
+
+  const base::Optional<GURL>& inner_request_url() const {
+    return inner_request_url_;
+  }
+
   // Set nullptr to reset the mocking.
   CONTENT_EXPORT static void SetSignedExchangeHandlerFactoryForTest(
       SignedExchangeHandlerFactory* factory);
@@ -150,7 +153,6 @@
   const uint32_t url_loader_options_;
   const int load_flags_;
   const bool should_redirect_on_failure_;
-  bool has_redirected_to_fallback_url_ = false;
   const base::Optional<base::UnguessableToken> throttling_profile_id_;
   std::unique_ptr<SignedExchangeDevToolsProxy> devtools_proxy_;
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
@@ -162,6 +164,9 @@
 
   std::string content_type_;
 
+  base::Optional<GURL> fallback_url_;
+  base::Optional<GURL> inner_request_url_;
+
   base::WeakPtrFactory<SignedExchangeLoader> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SignedExchangeLoader);
diff --git a/content/browser/web_package/signed_exchange_request_handler.cc b/content/browser/web_package/signed_exchange_request_handler.cc
index 921124a..b5f898a 100644
--- a/content/browser/web_package/signed_exchange_request_handler.cc
+++ b/content/browser/web_package/signed_exchange_request_handler.cc
@@ -31,7 +31,6 @@
 
 SignedExchangeRequestHandler::SignedExchangeRequestHandler(
     url::Origin request_initiator,
-    const GURL& url,
     uint32_t url_loader_options,
     int frame_tree_node_id,
     const base::UnguessableToken& devtools_navigation_token,
@@ -42,7 +41,6 @@
     URLLoaderThrottlesGetter url_loader_throttles_getter,
     scoped_refptr<SignedExchangePrefetchMetricRecorder> metric_recorder)
     : request_initiator_(std::move(request_initiator)),
-      url_(url),
       url_loader_options_(url_loader_options),
       frame_tree_node_id_(frame_tree_node_id),
       devtools_navigation_token_(devtools_navigation_token),
@@ -59,7 +57,7 @@
 SignedExchangeRequestHandler::~SignedExchangeRequestHandler() = default;
 
 void SignedExchangeRequestHandler::MaybeCreateLoader(
-    const network::ResourceRequest& /* tentative_resource_request */,
+    const network::ResourceRequest& tentative_resource_request,
     ResourceContext* resource_context,
     LoaderCallback callback,
     FallbackCallback fallback_callback) {
@@ -67,27 +65,33 @@
     std::move(callback).Run({});
     return;
   }
-  if (signed_exchange_loader_->HasRedirectedToFallbackURL()) {
+
+  if (signed_exchange_loader_->fallback_url()) {
+    DCHECK(tentative_resource_request.url.EqualsIgnoringRef(
+        *signed_exchange_loader_->fallback_url()));
     signed_exchange_loader_ = nullptr;
     std::move(fallback_callback)
         .Run(false /* reset_subresource_loader_params */);
     return;
   }
 
+  DCHECK(tentative_resource_request.url.EqualsIgnoringRef(
+      *signed_exchange_loader_->inner_request_url()));
   std::move(callback).Run(
       base::BindOnce(&SignedExchangeRequestHandler::StartResponse,
                      weak_factory_.GetWeakPtr()));
 }
 
 bool SignedExchangeRequestHandler::MaybeCreateLoaderForResponse(
+    const GURL& request_url,
     const network::ResourceResponseHead& response,
     network::mojom::URLLoaderPtr* loader,
     network::mojom::URLLoaderClientRequest* client_request,
     ThrottlingURLLoader* url_loader,
     bool* skip_other_interceptors) {
   DCHECK(!signed_exchange_loader_);
-  if (!signed_exchange_utils::ShouldHandleAsSignedHTTPExchange(
-          request_initiator_.GetURL(), response)) {
+  if (!signed_exchange_utils::ShouldHandleAsSignedHTTPExchange(request_url,
+                                                               response)) {
     return false;
   }
 
@@ -100,11 +104,11 @@
   // the redirected request will be checked when it's restarted we suppose
   // this is fine.
   signed_exchange_loader_ = std::make_unique<SignedExchangeLoader>(
-      url_, response, std::move(client), url_loader->Unbind(),
+      request_url, response, std::move(client), url_loader->Unbind(),
       request_initiator_, url_loader_options_, load_flags_,
       true /* should_redirect_to_fallback */, throttling_profile_id_,
       std::make_unique<SignedExchangeDevToolsProxy>(
-          url_, response,
+          request_url, response,
           base::BindRepeating([](int id) { return id; }, frame_tree_node_id_),
           devtools_navigation_token_, report_raw_headers_),
       url_loader_factory_, url_loader_throttles_getter_,
diff --git a/content/browser/web_package/signed_exchange_request_handler.h b/content/browser/web_package/signed_exchange_request_handler.h
index 5b95484..a7e9a64 100644
--- a/content/browser/web_package/signed_exchange_request_handler.h
+++ b/content/browser/web_package/signed_exchange_request_handler.h
@@ -31,7 +31,6 @@
 
   SignedExchangeRequestHandler(
       url::Origin request_initiator,
-      const GURL& url,
       uint32_t url_loader_options,
       int frame_tree_node_id,
       const base::UnguessableToken& devtools_navigation_token,
@@ -50,6 +49,7 @@
       LoaderCallback callback,
       FallbackCallback fallback_callback) override;
   bool MaybeCreateLoaderForResponse(
+      const GURL& request_url,
       const network::ResourceResponseHead& response,
       network::mojom::URLLoaderPtr* loader,
       network::mojom::URLLoaderClientRequest* client_request,
@@ -67,7 +67,6 @@
   std::unique_ptr<SignedExchangeLoader> signed_exchange_loader_;
 
   url::Origin request_initiator_;
-  GURL url_;
   const uint32_t url_loader_options_;
   const int frame_tree_node_id_;
   base::Optional<const base::UnguessableToken> devtools_navigation_token_;
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 18820b24..1ff4677c 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -497,7 +497,6 @@
     "associated_interfaces.mojom",
     "child.mojom",
     "child_control.mojom",
-    "child_memory_coordinator.mojom",
     "field_trial_recorder.mojom",
     "frame.mojom",
     "frame_sink_provider.mojom",
@@ -512,7 +511,6 @@
     "media/peer_connection_tracker.mojom",
     "media/renderer_audio_input_stream_factory.mojom",
     "media/renderer_audio_output_stream_factory.mojom",
-    "memory_coordinator.mojom",
     "native_types.mojom",
     "navigation_client.mojom",
     "navigation_params.mojom",
diff --git a/content/common/child_memory_coordinator.mojom b/content/common/child_memory_coordinator.mojom
deleted file mode 100644
index fe2d4f3..0000000
--- a/content/common/child_memory_coordinator.mojom
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module content.mojom;
-
-// See base/memory/memory_coordinator_client.h for the definitions of these
-// states.
-enum MemoryState {
-  UNKNOWN = -1,
-  NORMAL = 0,
-  THROTTLED = 1,
-  SUSPENDED = 2,
-};
-
-// ChildMemoryCoordinator lives in a child process and receives memory events
-// dispatched by the central memory coordinator which lives in the browser
-// process.
-interface ChildMemoryCoordinator {
-  // Called when the central memory coodinator changes the state for child
-  // processes.
-  OnStateChange(MemoryState state);
-
-  // Requests the child process to purge memory (e.g. by dropping caches that
-  // can be easily rebuilt).
-  PurgeMemory();
-};
diff --git a/content/common/memory_coordinator.mojom b/content/common/memory_coordinator.mojom
deleted file mode 100644
index 82d40b9..0000000
--- a/content/common/memory_coordinator.mojom
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module content.mojom;
-
-import "content/common/child_memory_coordinator.mojom";
-
-// An interface to access the central memory coordinator from a child process.
-// The browser process creates a handle for each child process.
-interface MemoryCoordinatorHandle {
-  // A child process calls this method when ChildMemoryCoordinator is created.
-  AddChild(ChildMemoryCoordinator child);
-};
diff --git a/content/public/app/content_main_delegate.cc b/content/public/app/content_main_delegate.cc
index 6fce3efb..cf3c71fe 100644
--- a/content/public/app/content_main_delegate.cc
+++ b/content/public/app/content_main_delegate.cc
@@ -26,10 +26,6 @@
   return -1;
 }
 
-ui::DataPack* ContentMainDelegate::LoadServiceManifestDataPack() {
-  return nullptr;
-}
-
 #if defined(OS_MACOSX)
 
 bool ContentMainDelegate::ProcessRegistersWithSystemProcess(
diff --git a/content/public/app/content_main_delegate.h b/content/public/app/content_main_delegate.h
index f0c23432..6c47818 100644
--- a/content/public/app/content_main_delegate.h
+++ b/content/public/app/content_main_delegate.h
@@ -24,10 +24,6 @@
 class ZygoteForkDelegate;
 }  // namespace service_manager
 
-namespace ui {
-class DataPack;
-}
-
 namespace content {
 
 class ContentBrowserClient;
@@ -63,10 +59,6 @@
   // Called right before the process exits.
   virtual void ProcessExiting(const std::string& process_type) {}
 
-  // This loads the service manifest datapack, takes its ownership and returns
-  // the pointer to it.
-  virtual ui::DataPack* LoadServiceManifestDataPack();
-
 #if defined(OS_MACOSX)
   // Returns true if the process registers with the system monitor, so that we
   // can allocate an IO port for it before the sandbox is initialized. Embedders
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index 9c311114..aea05b6 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -172,8 +172,6 @@
     "media_session.h",
     "media_session_observer.cc",
     "media_session_observer.h",
-    "memory_coordinator.h",
-    "memory_coordinator_delegate.h",
     "message_port_provider.h",
     "mhtml_extra_parts.h",
     "native_event_processor_mac.h",
diff --git a/content/public/browser/authenticator_request_client_delegate.cc b/content/public/browser/authenticator_request_client_delegate.cc
index 230f8a5..94b259c 100644
--- a/content/public/browser/authenticator_request_client_delegate.cc
+++ b/content/public/browser/authenticator_request_client_delegate.cc
@@ -72,6 +72,7 @@
     std::string new_authenticator_id) {}
 
 void AuthenticatorRequestClientDelegate::FidoAuthenticatorPairingModeChanged(
-    base::StringPiece authenticator_id) {}
+    base::StringPiece authenticator_id,
+    bool is_in_pairing_mode) {}
 
 }  // namespace content
diff --git a/content/public/browser/authenticator_request_client_delegate.h b/content/public/browser/authenticator_request_client_delegate.h
index 49ddb66..6b2e4d0 100644
--- a/content/public/browser/authenticator_request_client_delegate.h
+++ b/content/public/browser/authenticator_request_client_delegate.h
@@ -118,8 +118,8 @@
   void FidoAuthenticatorRemoved(base::StringPiece device_id) override;
   void FidoAuthenticatorIdChanged(base::StringPiece old_authenticator_id,
                                   std::string new_authenticator_id) override;
-  void FidoAuthenticatorPairingModeChanged(
-      base::StringPiece authenticator_id) override;
+  void FidoAuthenticatorPairingModeChanged(base::StringPiece authenticator_id,
+                                           bool is_in_pairing_mode) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(AuthenticatorRequestClientDelegate);
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index c4f7141..8d37f89 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -15,7 +15,6 @@
 #include "content/public/browser/authenticator_request_client_delegate.h"
 #include "content/public/browser/client_certificate_delegate.h"
 #include "content/public/browser/login_delegate.h"
-#include "content/public/browser/memory_coordinator_delegate.h"
 #include "content/public/browser/navigation_ui_data.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/page_navigator.h"
@@ -278,7 +277,7 @@
     const GURL& scope,
     const GURL& first_party,
     ResourceContext* context,
-    const base::Callback<WebContents*(void)>& wc_getter) {
+    base::RepeatingCallback<WebContents*()> wc_getter) {
   return true;
 }
 
@@ -671,11 +670,6 @@
   return std::vector<service_manager::Identity>();
 }
 
-std::unique_ptr<MemoryCoordinatorDelegate>
-ContentBrowserClient::GetMemoryCoordinatorDelegate() {
-  return std::unique_ptr<MemoryCoordinatorDelegate>();
-}
-
 ::rappor::RapporService* ContentBrowserClient::GetRapporService() {
   return nullptr;
 }
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 32004a1..e55f518 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -161,7 +161,6 @@
 class DevToolsManagerDelegate;
 class LoginDelegate;
 class MediaObserver;
-class MemoryCoordinatorDelegate;
 class NavigationHandle;
 class NavigationUIData;
 class PlatformNotificationService;
@@ -508,7 +507,7 @@
       const GURL& scope,
       const GURL& first_party,
       ResourceContext* context,
-      const base::Callback<WebContents*(void)>& wc_getter);
+      base::RepeatingCallback<WebContents*()> wc_getter);
 
   // Allow the embedder to control if a Shared Worker can be connected from a
   // given tab.
@@ -1088,10 +1087,6 @@
       int sandbox_type) const;
 #endif
 
-  // Returns an instance of MemoryCoordinatorDelegate.
-  virtual std::unique_ptr<MemoryCoordinatorDelegate>
-  GetMemoryCoordinatorDelegate();
-
   // Binds a new media remoter service to |request|, if supported by the
   // embedder, for the |source| that lives in the render frame represented
   // by |render_frame_host|. This may be called multiple times if there is more
diff --git a/content/public/browser/memory_coordinator.h b/content/public/browser/memory_coordinator.h
deleted file mode 100644
index 3b7b7494..0000000
--- a/content/public/browser/memory_coordinator.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_PUBLIC_BROWSER_MEMORY_COORDINATOR_H_
-#define CONTENT_PUBLIC_BROWSER_MEMORY_COORDINATOR_H_
-
-#include "base/memory/memory_coordinator_client.h"
-#include "base/process/process_handle.h"
-#include "content/common/content_export.h"
-
-namespace content {
-
-// The interface that represents the browser side of MemoryCoordinator.
-// MemoryCoordinator determines memory state of each process (both browser
-// and renderers) and dispatches state change notifications to its clients.
-// See comments on MemoryCoordinatorClient for details.
-class CONTENT_EXPORT MemoryCoordinator {
- public:
-  virtual ~MemoryCoordinator() {}
-
-  static MemoryCoordinator* GetInstance();
-
-  // Returns the memory state of the given process handle.
-  virtual base::MemoryState GetStateForProcess(base::ProcessHandle handle) = 0;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_PUBLIC_BROWSER_MEMORY_COORDINATOR_H_
diff --git a/content/public/browser/memory_coordinator_delegate.h b/content/public/browser/memory_coordinator_delegate.h
deleted file mode 100644
index 0a1adf3..0000000
--- a/content/public/browser/memory_coordinator_delegate.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_PUBLIC_BROWSER_MEMORY_COORDINATOR_DELEGATE_H_
-#define CONTENT_PUBLIC_BROWSER_MEMORY_COORDINATOR_DELEGATE_H_
-
-#include "content/common/content_export.h"
-
-namespace content {
-
-// A delegate class which is used by the memory coordinator.
-class CONTENT_EXPORT MemoryCoordinatorDelegate {
- public:
-  virtual ~MemoryCoordinatorDelegate() {}
-
-  // Requests to discard one tab. An implementation should select a low priority
-  // tab to discard. If there is no tab that can be discarded, this doesn't take
-  // effect.
-  virtual void DiscardTab(bool skip_unload_handlers) {}
-};
-
-}  // namespace content
-
-#endif  // CONTENT_PUBLIC_BROWSER_MEMORY_COORDINATOR_DELEGATE_H_
diff --git a/content/public/browser/native_event_processor_observer_mac.h b/content/public/browser/native_event_processor_observer_mac.h
index 79bbab7..5160e22 100644
--- a/content/public/browser/native_event_processor_observer_mac.h
+++ b/content/public/browser/native_event_processor_observer_mac.h
@@ -7,7 +7,6 @@
 
 #include "base/macros.h"
 #include "base/observer_list.h"
-#include "base/time/time.h"
 #include "content/common/content_export.h"
 
 #if defined(__OBJC__)
@@ -24,9 +23,7 @@
   virtual void WillRunNativeEvent(const void* opaque_identifier) = 0;
 
   // Called right after a native event is run.
-  // |creation_time| refers to the time at which the native event was created.
-  virtual void DidRunNativeEvent(const void* opaque_identifier,
-                                 base::TimeTicks creation_time) = 0;
+  virtual void DidRunNativeEvent(const void* opaque_identifier) = 0;
 };
 
 // The constructor sends a WillRunNativeEvent callback to each observer.
diff --git a/content/public/browser/native_event_processor_observer_mac.mm b/content/public/browser/native_event_processor_observer_mac.mm
index a0e2fcc..baebbc1 100644
--- a/content/public/browser/native_event_processor_observer_mac.mm
+++ b/content/public/browser/native_event_processor_observer_mac.mm
@@ -4,34 +4,10 @@
 
 #include "content/public/browser/native_event_processor_observer_mac.h"
 
-#import <AppKit/AppKit.h>
-
 #include "base/observer_list.h"
-#include "base/time/time.h"
 
 namespace content {
 
-namespace {
-
-base::TimeTicks TimeTicksForEvent(NSEvent* event) {
-  // NSEvent.timestamp gives the creation time of the event in seconds
-  // since system startup. The baseline it should be compared agaainst is
-  // NSProcessInfo.systemUptime. To convert to base::TimeTicks, we take
-  // the difference and subtract from base::TimeTicks::Now().
-  // Observations:
-  //  1) This implementation is fast, since both systemUptime and
-  //  base::TimeTicks::Now() use the commpage [no syscalls].
-  //  2) systemUptime's implementation uses mach_absolute_time() -- see
-  //  CoreFoundation.framework. Presumably, so does NSEvent.timestamp.
-  //  mach_absolute_time() does not advance while the machine is asleep.
-  NSTimeInterval current_system_uptime =
-      [[NSProcessInfo processInfo] systemUptime];
-  return base::TimeTicks::Now() +
-         base::TimeDelta::FromSecondsD(event.timestamp - current_system_uptime);
-}
-
-}  // namespace
-
 ScopedNotifyNativeEventProcessorObserver::
     ScopedNotifyNativeEventProcessorObserver(
         base::ObserverList<NativeEventProcessorObserver>::Unchecked*
@@ -44,13 +20,8 @@
 
 ScopedNotifyNativeEventProcessorObserver::
     ~ScopedNotifyNativeEventProcessorObserver() {
-  base::TimeTicks event_creation_time;
   for (auto& obs : *observer_list_) {
-    // Compute the value in the loop to avoid doing work if there are no
-    // observers.
-    if (event_creation_time.is_null())
-      event_creation_time = TimeTicksForEvent(event_);
-    obs.DidRunNativeEvent(event_, event_creation_time);
+    obs.DidRunNativeEvent(event_);
   }
 }
 
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index a926703..7aeb6bd 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -288,6 +288,10 @@
 const base::Feature kOriginTrials{"OriginTrials",
                                   base::FEATURE_ENABLED_BY_DEFAULT};
 
+// History navigation in response to horizontal overscroll (aka gesture-nav).
+const base::Feature kOverscrollHistoryNavigation{
+    "OverscrollHistoryNavigation", base::FEATURE_ENABLED_BY_DEFAULT};
+
 // Blink PageLifecycle feature. See https://crbug.com/775194
 const base::Feature kPageLifecycle{"PageLifecycle",
                                    base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 5b7476dd..df9ae24 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -72,6 +72,7 @@
 CONTENT_EXPORT extern const base::Feature kNotificationContentImage;
 CONTENT_EXPORT extern const base::Feature kOriginPolicy;
 CONTENT_EXPORT extern const base::Feature kOriginTrials;
+CONTENT_EXPORT extern const base::Feature kOverscrollHistoryNavigation;
 CONTENT_EXPORT extern const base::Feature kPageLifecycle;
 CONTENT_EXPORT extern const base::Feature kPassiveDocumentEventListeners;
 CONTENT_EXPORT extern const base::Feature kPassiveDocumentWheelEventListeners;
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 57c7561..9c1e708 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -620,19 +620,6 @@
 const char kOverridePluginPowerSaverForTesting[] =
     "override-plugin-power-saver-for-testing";
 
-// Controls the behavior of history navigation in response to horizontal
-// overscroll.
-// Set the value to '0' to disable.
-// Set the value to '1' to enable the behavior where pages slide in and out in
-// response to the horizontal overscroll gesture and a screenshot of the target
-// page is shown.
-// Set the value to '2' to enable the simplified overscroll UI where a
-// navigation arrow slides in from the side of the screen in response to the
-// horizontal overscroll gesture.
-// Defaults to '2'.
-const char kOverscrollHistoryNavigation[] =
-    "overscroll-history-navigation";
-
 // Controls the value of the threshold to start horizontal overscroll relative
 // to the default value.
 // E.g. set the value to '133' to have the overscroll start threshold be 133%
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index c34999d..7535856 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -188,7 +188,6 @@
 CONTENT_EXPORT extern const char kDisableAppContainer[];
 CONTENT_EXPORT extern const char kNumRasterThreads[];
 CONTENT_EXPORT extern const char kOverridePluginPowerSaverForTesting[];
-CONTENT_EXPORT extern const char kOverscrollHistoryNavigation[];
 CONTENT_EXPORT extern const char kOverscrollStartThreshold[];
 CONTENT_EXPORT extern const char kPassiveListenersDefault[];
 CONTENT_EXPORT extern const char kPpapiBrokerProcess[];
diff --git a/content/public/common/url_utils.cc b/content/public/common/url_utils.cc
index ef98675..8606313 100644
--- a/content/public/common/url_utils.cc
+++ b/content/public/common/url_utils.cc
@@ -7,8 +7,10 @@
 #include <set>
 #include <string>
 
+#include "base/containers/flat_set.h"
 #include "base/logging.h"
 #include "base/no_destructor.h"
+#include "base/strings/string_piece.h"
 #include "build/build_config.h"
 #include "content/common/url_schemes.h"
 #include "content/public/common/browser_side_navigation_policy.h"
@@ -112,8 +114,8 @@
 }
 
 bool IsSafeRedirectTarget(const GURL& from_url, const GURL& to_url) {
-  static base::NoDestructor<std::set<std::string>> kUnsafeSchemes(
-      std::set<std::string>({
+  static const base::NoDestructor<base::flat_set<base::StringPiece>>
+      kUnsafeSchemes(base::flat_set<base::StringPiece>({
         url::kAboutScheme, url::kDataScheme, url::kFileScheme,
             url::kFileSystemScheme, url::kBlobScheme,
 #if defined(OS_ANDROID)
@@ -122,7 +124,7 @@
       }));
   if (HasWebUIScheme(to_url))
     return false;
-  if (kUnsafeSchemes->find(to_url.scheme()) == kUnsafeSchemes->end())
+  if (!kUnsafeSchemes->contains(to_url.scheme_piece()))
     return true;
   if (from_url.is_empty())
     return false;
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 005311f..9ed86e1 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -141,20 +141,6 @@
     "ime_event_guard.h",
     "in_process_renderer_thread.cc",
     "in_process_renderer_thread.h",
-    "indexed_db/indexed_db_callbacks_impl.cc",
-    "indexed_db/indexed_db_callbacks_impl.h",
-    "indexed_db/indexed_db_database_callbacks_impl.cc",
-    "indexed_db/indexed_db_database_callbacks_impl.h",
-    "indexed_db/indexed_db_dispatcher.cc",
-    "indexed_db/indexed_db_dispatcher.h",
-    "indexed_db/indexed_db_key_builders.cc",
-    "indexed_db/indexed_db_key_builders.h",
-    "indexed_db/webidbcursor_impl.cc",
-    "indexed_db/webidbcursor_impl.h",
-    "indexed_db/webidbdatabase_impl.cc",
-    "indexed_db/webidbdatabase_impl.h",
-    "indexed_db/webidbfactory_impl.cc",
-    "indexed_db/webidbfactory_impl.h",
     "input/frame_input_handler_impl.cc",
     "input/frame_input_handler_impl.h",
     "input/input_event_prediction.cc",
diff --git a/content/renderer/indexed_db/OWNERS b/content/renderer/indexed_db/OWNERS
deleted file mode 100644
index a740487..0000000
--- a/content/renderer/indexed_db/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-file://content/browser/indexed_db/OWNERS
-
-# TEAM: storage-dev@chromium.org
-# COMPONENT: Blink>Storage>IndexedDB
diff --git a/content/renderer/indexed_db/indexed_db_callbacks_impl.cc b/content/renderer/indexed_db/indexed_db_callbacks_impl.cc
deleted file mode 100644
index 792237fa..0000000
--- a/content/renderer/indexed_db/indexed_db_callbacks_impl.cc
+++ /dev/null
@@ -1,257 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/renderer/indexed_db/indexed_db_callbacks_impl.h"
-
-#include "content/renderer/indexed_db/indexed_db_dispatcher.h"
-#include "content/renderer/indexed_db/indexed_db_key_builders.h"
-#include "content/renderer/indexed_db/webidbcursor_impl.h"
-#include "content/renderer/indexed_db/webidbdatabase_impl.h"
-#include "third_party/blink/public/platform/file_path_conversion.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_callbacks.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_error.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_metadata.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_name_and_version.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_value.h"
-
-using blink::IndexedDBDatabaseMetadata;
-using blink::WebBlobInfo;
-using blink::WebData;
-using blink::WebIDBCallbacks;
-using blink::WebIDBDatabase;
-using blink::WebIDBMetadata;
-using blink::WebIDBNameAndVersion;
-using blink::WebIDBValue;
-using blink::WebString;
-using blink::WebVector;
-using blink::mojom::IDBDatabaseAssociatedPtrInfo;
-
-namespace content {
-
-namespace {
-
-void ConvertIndexMetadata(const blink::IndexedDBIndexMetadata& metadata,
-                          WebIDBMetadata::Index* output) {
-  output->id = metadata.id;
-  output->name = WebString::FromUTF16(metadata.name);
-  output->key_path = WebIDBKeyPathBuilder::Build(metadata.key_path);
-  output->unique = metadata.unique;
-  output->multi_entry = metadata.multi_entry;
-}
-
-void ConvertObjectStoreMetadata(
-    const blink::IndexedDBObjectStoreMetadata& metadata,
-    WebIDBMetadata::ObjectStore* output) {
-  output->id = metadata.id;
-  output->name = WebString::FromUTF16(metadata.name);
-  output->key_path = WebIDBKeyPathBuilder::Build(metadata.key_path);
-  output->auto_increment = metadata.auto_increment;
-  output->max_index_id = metadata.max_index_id;
-  output->indexes = WebVector<WebIDBMetadata::Index>(metadata.indexes.size());
-  size_t i = 0;
-  for (const auto& iter : metadata.indexes)
-    ConvertIndexMetadata(iter.second, &output->indexes[i++]);
-}
-
-void ConvertDatabaseMetadata(const IndexedDBDatabaseMetadata& metadata,
-                             WebIDBMetadata* output) {
-  output->id = metadata.id;
-  output->name = WebString::FromUTF16(metadata.name);
-  output->version = metadata.version;
-  output->max_object_store_id = metadata.max_object_store_id;
-  output->object_stores =
-      WebVector<WebIDBMetadata::ObjectStore>(metadata.object_stores.size());
-  size_t i = 0;
-  for (const auto& iter : metadata.object_stores)
-    ConvertObjectStoreMetadata(iter.second, &output->object_stores[i++]);
-}
-
-WebIDBValue ConvertReturnValue(const blink::mojom::IDBReturnValuePtr& value) {
-  if (!value)
-    return WebIDBValue(WebData(), WebVector<WebBlobInfo>());
-
-  WebIDBValue web_value = IndexedDBCallbacksImpl::ConvertValue(value->value);
-  web_value.SetInjectedPrimaryKey(WebIDBKeyBuilder::Build(value->primary_key),
-                                  WebIDBKeyPathBuilder::Build(value->key_path));
-  return web_value;
-}
-
-WebIDBNameAndVersion ConvertNameVersion(
-    const blink::mojom::IDBNameAndVersionPtr& name_and_version) {
-  return WebIDBNameAndVersion(WebString::FromUTF16(name_and_version->name),
-                              name_and_version->version);
-}
-
-}  // namespace
-
-// static
-WebIDBValue IndexedDBCallbacksImpl::ConvertValue(
-    const blink::mojom::IDBValuePtr& value) {
-  if (!value || value->bits.empty())
-    return WebIDBValue(WebData(), WebVector<WebBlobInfo>());
-
-  WebVector<WebBlobInfo> local_blob_info;
-  local_blob_info.reserve(value->blob_or_file_info.size());
-  for (size_t i = 0; i < value->blob_or_file_info.size(); ++i) {
-    const auto& info = value->blob_or_file_info[i];
-    if (info->file) {
-      local_blob_info.emplace_back(WebString::FromUTF8(info->uuid),
-                                   blink::FilePathToWebString(info->file->path),
-                                   WebString::FromUTF16(info->file->name),
-                                   WebString::FromUTF16(info->mime_type),
-                                   info->file->last_modified.ToDoubleT(),
-                                   info->size, info->blob.PassHandle());
-    } else {
-      local_blob_info.emplace_back(WebString::FromUTF8(info->uuid),
-                                   WebString::FromUTF16(info->mime_type),
-                                   info->size, info->blob.PassHandle());
-    }
-  }
-
-  return WebIDBValue(WebData(&*value->bits.begin(), value->bits.size()),
-                     std::move(local_blob_info));
-}
-
-IndexedDBCallbacksImpl::IndexedDBCallbacksImpl(
-    std::unique_ptr<WebIDBCallbacks> callbacks,
-    int64_t transaction_id,
-    const base::WeakPtr<WebIDBCursorImpl>& cursor)
-    : callbacks_(std::move(callbacks)),
-      cursor_(cursor),
-      transaction_id_(transaction_id) {}
-
-IndexedDBCallbacksImpl::~IndexedDBCallbacksImpl() = default;
-
-void IndexedDBCallbacksImpl::Error(int32_t code,
-                                   const base::string16& message) {
-  callbacks_->OnError(
-      blink::WebIDBDatabaseError(code, WebString::FromUTF16(message)));
-  callbacks_.reset();
-}
-
-void IndexedDBCallbacksImpl::SuccessNamesAndVersionsList(
-    std::vector<blink::mojom::IDBNameAndVersionPtr> names_and_versions) {
-  WebVector<WebIDBNameAndVersion> web_names_and_versions;
-  web_names_and_versions.reserve(names_and_versions.size());
-  for (const blink::mojom::IDBNameAndVersionPtr& name_version :
-       names_and_versions)
-    web_names_and_versions.emplace_back(ConvertNameVersion(name_version));
-  callbacks_->OnSuccess(web_names_and_versions);
-  callbacks_.reset();
-}
-
-void IndexedDBCallbacksImpl::SuccessStringList(
-    const std::vector<base::string16>& value) {
-  WebVector<WebString> web_value(value.size());
-  std::transform(
-      value.begin(), value.end(), web_value.begin(),
-      [](const base::string16& s) { return WebString::FromUTF16(s); });
-  callbacks_->OnSuccess(web_value);
-  callbacks_.reset();
-}
-
-void IndexedDBCallbacksImpl::Blocked(int64_t existing_version) {
-  callbacks_->OnBlocked(existing_version);
-  // Not resetting |callbacks_|.
-}
-
-void IndexedDBCallbacksImpl::UpgradeNeeded(
-    IDBDatabaseAssociatedPtrInfo database_info,
-    int64_t old_version,
-    blink::WebIDBDataLoss data_loss,
-    const std::string& data_loss_message,
-    const IndexedDBDatabaseMetadata& metadata) {
-  WebIDBDatabase* database = new WebIDBDatabaseImpl(std::move(database_info));
-  WebIDBMetadata web_metadata;
-  ConvertDatabaseMetadata(metadata, &web_metadata);
-  callbacks_->OnUpgradeNeeded(old_version, database, web_metadata, data_loss,
-                              WebString::FromUTF8(data_loss_message));
-  // Not resetting |callbacks_|.
-}
-
-void IndexedDBCallbacksImpl::SuccessDatabase(
-    IDBDatabaseAssociatedPtrInfo database_info,
-    const IndexedDBDatabaseMetadata& metadata) {
-  WebIDBDatabase* database = nullptr;
-  if (database_info.is_valid()) {
-    database = new WebIDBDatabaseImpl(std::move(database_info));
-  }
-
-  WebIDBMetadata web_metadata;
-  ConvertDatabaseMetadata(metadata, &web_metadata);
-  callbacks_->OnSuccess(database, web_metadata);
-  callbacks_.reset();
-}
-
-void IndexedDBCallbacksImpl::SuccessCursor(
-    blink::mojom::IDBCursorAssociatedPtrInfo cursor_info,
-    const IndexedDBKey& key,
-    const IndexedDBKey& primary_key,
-    blink::mojom::IDBValuePtr value) {
-  WebIDBCursorImpl* cursor =
-      new WebIDBCursorImpl(std::move(cursor_info), transaction_id_);
-  callbacks_->OnSuccess(cursor, WebIDBKeyBuilder::Build(key),
-                        WebIDBKeyBuilder::Build(primary_key),
-                        ConvertValue(value));
-  callbacks_.reset();
-}
-
-void IndexedDBCallbacksImpl::SuccessValue(
-    blink::mojom::IDBReturnValuePtr value) {
-  callbacks_->OnSuccess(ConvertReturnValue(value));
-  callbacks_.reset();
-}
-
-void IndexedDBCallbacksImpl::SuccessCursorContinue(
-    const IndexedDBKey& key,
-    const IndexedDBKey& primary_key,
-    blink::mojom::IDBValuePtr value) {
-  callbacks_->OnSuccess(WebIDBKeyBuilder::Build(key),
-                        WebIDBKeyBuilder::Build(primary_key),
-                        ConvertValue(value));
-  callbacks_.reset();
-}
-
-void IndexedDBCallbacksImpl::SuccessCursorPrefetch(
-    const std::vector<IndexedDBKey>& keys,
-    const std::vector<IndexedDBKey>& primary_keys,
-    std::vector<blink::mojom::IDBValuePtr> values) {
-  std::vector<WebIDBValue> web_values;
-  web_values.reserve(values.size());
-  for (const blink::mojom::IDBValuePtr& value : values)
-    web_values.emplace_back(ConvertValue(value));
-
-  if (cursor_) {
-    cursor_->SetPrefetchData(keys, primary_keys, std::move(web_values));
-    cursor_->CachedContinue(callbacks_.get());
-  }
-  callbacks_.reset();
-}
-
-void IndexedDBCallbacksImpl::SuccessArray(
-    std::vector<blink::mojom::IDBReturnValuePtr> values) {
-  WebVector<WebIDBValue> web_values;
-  web_values.reserve(values.size());
-  for (const blink::mojom::IDBReturnValuePtr& value : values)
-    web_values.emplace_back(ConvertReturnValue(value));
-  callbacks_->OnSuccess(std::move(web_values));
-  callbacks_.reset();
-}
-
-void IndexedDBCallbacksImpl::SuccessKey(const IndexedDBKey& key) {
-  callbacks_->OnSuccess(WebIDBKeyBuilder::Build(key));
-  callbacks_.reset();
-}
-
-void IndexedDBCallbacksImpl::SuccessInteger(int64_t value) {
-  callbacks_->OnSuccess(value);
-  callbacks_.reset();
-}
-
-void IndexedDBCallbacksImpl::Success() {
-  callbacks_->OnSuccess();
-  callbacks_.reset();
-}
-
-}  // namespace content
diff --git a/content/renderer/indexed_db/indexed_db_callbacks_impl.h b/content/renderer/indexed_db/indexed_db_callbacks_impl.h
deleted file mode 100644
index fd57217..0000000
--- a/content/renderer/indexed_db/indexed_db_callbacks_impl.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_RENDERER_INDEXED_DB_INDEXED_DB_CALLBACKS_IMPL_H_
-#define CONTENT_RENDERER_INDEXED_DB_INDEXED_DB_CALLBACKS_IMPL_H_
-
-#include "mojo/public/cpp/bindings/associated_binding.h"
-#include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
-#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
-
-using blink::IndexedDBKey;
-
-namespace blink {
-class WebIDBCallbacks;
-class WebIDBValue;
-}
-
-namespace content {
-
-class WebIDBCursorImpl;
-
-// Implements the child-process end of the pipe used to deliver callbacks. It
-// is owned by the IO thread. |callback_runner_| is used to post tasks back to
-// the thread which owns the blink::WebIDBCallbacks.
-class IndexedDBCallbacksImpl : public blink::mojom::IDBCallbacks {
- public:
-  enum : int64_t { kNoTransaction = -1 };
-
-  static blink::WebIDBValue ConvertValue(
-      const blink::mojom::IDBValuePtr& value);
-
-  IndexedDBCallbacksImpl(std::unique_ptr<blink::WebIDBCallbacks> callbacks,
-                         int64_t transaction_id,
-                         const base::WeakPtr<WebIDBCursorImpl>& cursor);
-  ~IndexedDBCallbacksImpl() override;
-
-  // blink::mojom::IDBCallbacks implementation:
-  void Error(int32_t code, const base::string16& message) override;
-  void SuccessNamesAndVersionsList(
-      std::vector<blink::mojom::IDBNameAndVersionPtr> names_and_versions)
-      override;
-  void SuccessStringList(const std::vector<base::string16>& value) override;
-  void Blocked(int64_t existing_version) override;
-  void UpgradeNeeded(blink::mojom::IDBDatabaseAssociatedPtrInfo database_info,
-                     int64_t old_version,
-                     blink::WebIDBDataLoss data_loss,
-                     const std::string& data_loss_message,
-                     const blink::IndexedDBDatabaseMetadata& metadata) override;
-  void SuccessDatabase(
-      blink::mojom::IDBDatabaseAssociatedPtrInfo database_info,
-      const blink::IndexedDBDatabaseMetadata& metadata) override;
-  void SuccessCursor(blink::mojom::IDBCursorAssociatedPtrInfo cursor,
-                     const IndexedDBKey& key,
-                     const IndexedDBKey& primary_key,
-                     blink::mojom::IDBValuePtr value) override;
-  void SuccessValue(blink::mojom::IDBReturnValuePtr value) override;
-  void SuccessCursorContinue(const IndexedDBKey& key,
-                             const IndexedDBKey& primary_key,
-                             blink::mojom::IDBValuePtr value) override;
-  void SuccessCursorPrefetch(
-      const std::vector<IndexedDBKey>& keys,
-      const std::vector<IndexedDBKey>& primary_keys,
-      std::vector<blink::mojom::IDBValuePtr> values) override;
-  void SuccessArray(
-      std::vector<blink::mojom::IDBReturnValuePtr> values) override;
-  void SuccessKey(const IndexedDBKey& key) override;
-  void SuccessInteger(int64_t value) override;
-  void Success() override;
-
- private:
-  scoped_refptr<base::SingleThreadTaskRunner> callback_runner_;
-  std::unique_ptr<blink::WebIDBCallbacks> callbacks_;
-  base::WeakPtr<WebIDBCursorImpl> cursor_;
-  int64_t transaction_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(IndexedDBCallbacksImpl);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_INDEXED_DB_INDEXED_DB_CALLBACKS_IMPL_H_
diff --git a/content/renderer/indexed_db/indexed_db_database_callbacks_impl.h b/content/renderer/indexed_db/indexed_db_database_callbacks_impl.h
deleted file mode 100644
index 304f978..0000000
--- a/content/renderer/indexed_db/indexed_db_database_callbacks_impl.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_RENDERER_INDEXED_DB_INDEXED_DB_DATABASE_CALLBACKS_IMPL_H_
-#define CONTENT_RENDERER_INDEXED_DB_INDEXED_DB_DATABASE_CALLBACKS_IMPL_H_
-
-#include "base/single_thread_task_runner.h"
-#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
-
-namespace blink {
-class WebIDBDatabaseCallbacks;
-}
-
-namespace content {
-
-class IndexedDBDatabaseCallbacksImpl
-    : public blink::mojom::IDBDatabaseCallbacks {
- public:
-  explicit IndexedDBDatabaseCallbacksImpl(
-      std::unique_ptr<blink::WebIDBDatabaseCallbacks> callbacks);
-  ~IndexedDBDatabaseCallbacksImpl() override;
-
-  // blink::mojom::IDBDatabaseCallbacks implementation
-  void ForcedClose() override;
-  void VersionChange(int64_t old_version, int64_t new_version) override;
-  void Abort(int64_t transaction_id,
-             int32_t code,
-             const base::string16& message) override;
-  void Complete(int64_t transaction_id) override;
-  void Changes(blink::mojom::IDBObserverChangesPtr changes) override;
-
- private:
-  std::unique_ptr<blink::WebIDBDatabaseCallbacks> callbacks_;
-
-  DISALLOW_COPY_AND_ASSIGN(IndexedDBDatabaseCallbacksImpl);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_INDEXED_DB_INDEXED_DB_DATABASE_CALLBACKS_IMPL_H_
diff --git a/content/renderer/indexed_db/indexed_db_dispatcher.cc b/content/renderer/indexed_db/indexed_db_dispatcher.cc
deleted file mode 100644
index 6237adb..0000000
--- a/content/renderer/indexed_db/indexed_db_dispatcher.cc
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/renderer/indexed_db/indexed_db_dispatcher.h"
-
-#include <utility>
-
-#include "base/lazy_instance.h"
-#include "base/memory/ptr_util.h"
-#include "base/threading/thread_local.h"
-#include "content/renderer/indexed_db/indexed_db_key_builders.h"
-#include "content/renderer/indexed_db/webidbcursor_impl.h"
-#include "ipc/ipc_channel.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_callbacks.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_observation.h"
-
-using blink::WebIDBKey;
-using blink::WebIDBObservation;
-using base::ThreadLocalPointer;
-
-namespace content {
-static base::LazyInstance<ThreadLocalPointer<IndexedDBDispatcher>>::Leaky
-    g_idb_dispatcher_tls = LAZY_INSTANCE_INITIALIZER;
-
-namespace {
-
-IndexedDBDispatcher* const kDeletedIndexedDBDispatcherMarker =
-    reinterpret_cast<IndexedDBDispatcher*>(0x1);
-
-}  // unnamed namespace
-
-IndexedDBDispatcher::IndexedDBDispatcher() {
-  g_idb_dispatcher_tls.Pointer()->Set(this);
-}
-
-IndexedDBDispatcher::~IndexedDBDispatcher() {
-  g_idb_dispatcher_tls.Pointer()->Set(kDeletedIndexedDBDispatcherMarker);
-}
-
-IndexedDBDispatcher* IndexedDBDispatcher::ThreadSpecificInstance() {
-  if (g_idb_dispatcher_tls.Pointer()->Get() ==
-      kDeletedIndexedDBDispatcherMarker) {
-    NOTREACHED() << "Re-instantiating TLS IndexedDBDispatcher.";
-    g_idb_dispatcher_tls.Pointer()->Set(nullptr);
-  }
-  if (g_idb_dispatcher_tls.Pointer()->Get())
-    return g_idb_dispatcher_tls.Pointer()->Get();
-
-  IndexedDBDispatcher* dispatcher = new IndexedDBDispatcher();
-  if (WorkerThread::GetCurrentId())
-    WorkerThread::AddObserver(dispatcher);
-  return dispatcher;
-}
-
-void IndexedDBDispatcher::WillStopCurrentWorkerThread() {
-  delete this;
-}
-
-void IndexedDBDispatcher::RegisterCursor(WebIDBCursorImpl* cursor) {
-  DCHECK(!base::ContainsKey(cursors_, cursor));
-  cursors_.insert(cursor);
-}
-
-void IndexedDBDispatcher::UnregisterCursor(WebIDBCursorImpl* cursor) {
-  DCHECK(base::ContainsKey(cursors_, cursor));
-  cursors_.erase(cursor);
-}
-
-void IndexedDBDispatcher::ResetCursorPrefetchCaches(
-    int64_t transaction_id,
-    WebIDBCursorImpl* exception_cursor) {
-  for (WebIDBCursorImpl* cursor : cursors_) {
-    if (cursor != exception_cursor &&
-        cursor->transaction_id() == transaction_id)
-      cursor->ResetPrefetchCache();
-  }
-}
-
-}  // namespace content
diff --git a/content/renderer/indexed_db/indexed_db_dispatcher.h b/content/renderer/indexed_db/indexed_db_dispatcher.h
deleted file mode 100644
index 1863778..0000000
--- a/content/renderer/indexed_db/indexed_db_dispatcher.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_RENDERER_INDEXED_DB_INDEXED_DB_DISPATCHER_H_
-#define CONTENT_RENDERER_INDEXED_DB_INDEXED_DB_DISPATCHER_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/strings/nullable_string16.h"
-#include "content/common/content_export.h"
-#include "content/public/renderer/worker_thread.h"
-#include "content/renderer/indexed_db/indexed_db_callbacks_impl.h"
-#include "content/renderer/indexed_db/indexed_db_database_callbacks_impl.h"
-#include "ipc/ipc_sync_message_filter.h"
-#include "third_party/blink/public/common/indexeddb/web_idb_types.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_callbacks.h"
-#include "url/origin.h"
-
-namespace content {
-class WebIDBCursorImpl;
-
-// Handle the indexed db related communication for this context thread - the
-// main thread and each worker thread have their own copies.
-class CONTENT_EXPORT IndexedDBDispatcher : public WorkerThread::Observer {
- public:
-  // Constructor made public to allow RenderThreadImpl to own a copy without
-  // failing a NOTREACHED in ThreadSpecificInstance in tests that instantiate
-  // two copies of RenderThreadImpl on the same thread.  Everyone else probably
-  // wants to use ThreadSpecificInstance().
-  IndexedDBDispatcher();
-  ~IndexedDBDispatcher() override;
-
-  static IndexedDBDispatcher* ThreadSpecificInstance();
-
-  // WorkerThread::Observer implementation.
-  void WillStopCurrentWorkerThread() override;
-
-  void RegisterCursor(WebIDBCursorImpl* cursor);
-  void UnregisterCursor(WebIDBCursorImpl* cursor);
-  // Reset cursor prefetch caches for all cursors except exception_cursor.
-  void ResetCursorPrefetchCaches(int64_t transaction_id,
-                                 WebIDBCursorImpl* exception_cursor);
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(IndexedDBDispatcherTest, CursorReset);
-  FRIEND_TEST_ALL_PREFIXES(IndexedDBDispatcherTest, CursorTransactionId);
-
-  std::unordered_set<WebIDBCursorImpl*> cursors_;
-
-  DISALLOW_COPY_AND_ASSIGN(IndexedDBDispatcher);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_INDEXED_DB_INDEXED_DB_DISPATCHER_H_
diff --git a/content/renderer/indexed_db/indexed_db_key_builders.h b/content/renderer/indexed_db/indexed_db_key_builders.h
deleted file mode 100644
index 841dbb41..0000000
--- a/content/renderer/indexed_db/indexed_db_key_builders.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_RENDERER_INDEXED_DB_INDEXED_DB_KEY_BUILDERS_H_
-#define CONTENT_RENDERER_INDEXED_DB_INDEXED_DB_KEY_BUILDERS_H_
-
-#include "base/macros.h"
-#include "content/common/content_export.h"
-#include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
-#include "third_party/blink/public/common/indexeddb/indexeddb_key_path.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_key.h"
-
-namespace blink {
-
-class IndexedDBKeyRange;
-class WebIDBKeyPath;
-class WebIDBKeyRange;
-
-}  // namespace blink
-
-namespace content {
-
-class CONTENT_EXPORT IndexedDBKeyBuilder {
- public:
-  static blink::IndexedDBKey Build(blink::WebIDBKeyView key);
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(IndexedDBKeyBuilder);
-};
-
-class CONTENT_EXPORT WebIDBKeyBuilder {
- public:
-  static blink::WebIDBKey Build(const blink::IndexedDBKey& key);
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(WebIDBKeyBuilder);
-};
-
-class CONTENT_EXPORT IndexedDBKeyRangeBuilder {
- public:
-  static blink::IndexedDBKeyRange Build(const blink::WebIDBKeyRange& key_range);
-
-  // Builds a point range (containing a single key).
-  static blink::IndexedDBKeyRange Build(blink::WebIDBKeyView key);
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(IndexedDBKeyRangeBuilder);
-};
-
-class CONTENT_EXPORT WebIDBKeyRangeBuilder {
- public:
-  static blink::WebIDBKeyRange Build(const blink::IndexedDBKeyRange& key);
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(WebIDBKeyRangeBuilder);
-};
-
-class CONTENT_EXPORT IndexedDBKeyPathBuilder {
- public:
-  static blink::IndexedDBKeyPath Build(const blink::WebIDBKeyPath& key_path);
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(IndexedDBKeyPathBuilder);
-};
-
-class CONTENT_EXPORT WebIDBKeyPathBuilder {
- public:
-  static blink::WebIDBKeyPath Build(const blink::IndexedDBKeyPath& key_path);
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(WebIDBKeyPathBuilder);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_INDEXED_DB_INDEXED_DB_KEY_BUILDERS_H_
diff --git a/content/renderer/indexed_db/webidbcursor_impl.h b/content/renderer/indexed_db/webidbcursor_impl.h
deleted file mode 100644
index 39baa2c..0000000
--- a/content/renderer/indexed_db/webidbcursor_impl.h
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_RENDERER_INDEXED_DB_WEBIDBCURSOR_IMPL_H_
-#define CONTENT_RENDERER_INDEXED_DB_WEBIDBCURSOR_IMPL_H_
-
-#include <stdint.h>
-
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/containers/circular_deque.h"
-#include "base/gtest_prod_util.h"
-#include "base/memory/ref_counted.h"
-#include "content/common/content_export.h"
-#include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
-#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_callbacks.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_cursor.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_key.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_value.h"
-
-namespace content {
-
-class IndexedDBCallbacksImpl;
-
-class CONTENT_EXPORT WebIDBCursorImpl : public blink::WebIDBCursor {
- public:
-  WebIDBCursorImpl(blink::mojom::IDBCursorAssociatedPtrInfo cursor,
-                   int64_t transaction_id);
-  ~WebIDBCursorImpl() override;
-
-  void Advance(unsigned long count, blink::WebIDBCallbacks* callback) override;
-  void CursorContinue(blink::WebIDBKeyView key,
-                      blink::WebIDBKeyView primary_key,
-                      blink::WebIDBCallbacks* callback) override;
-  void PostSuccessHandlerCallback() override;
-
-  void SetPrefetchData(const std::vector<blink::IndexedDBKey>& keys,
-                       const std::vector<blink::IndexedDBKey>& primary_keys,
-                       std::vector<blink::WebIDBValue> values);
-
-  void CachedAdvance(unsigned long count, blink::WebIDBCallbacks* callbacks);
-  void CachedContinue(blink::WebIDBCallbacks* callbacks);
-
-  // This method is virtual so it can be overridden in unit tests.
-  virtual void ResetPrefetchCache();
-
-  int64_t transaction_id() const { return transaction_id_; }
-
- private:
-  blink::mojom::IDBCallbacksAssociatedPtrInfo GetCallbacksProxy(
-      std::unique_ptr<IndexedDBCallbacksImpl> callbacks);
-
-  FRIEND_TEST_ALL_PREFIXES(IndexedDBDispatcherTest, CursorReset);
-  FRIEND_TEST_ALL_PREFIXES(IndexedDBDispatcherTest, CursorTransactionId);
-  FRIEND_TEST_ALL_PREFIXES(WebIDBCursorImplTest, AdvancePrefetchTest);
-  FRIEND_TEST_ALL_PREFIXES(WebIDBCursorImplTest, PrefetchReset);
-  FRIEND_TEST_ALL_PREFIXES(WebIDBCursorImplTest, PrefetchTest);
-
-  enum { kInvalidCursorId = -1 };
-  enum { kPrefetchContinueThreshold = 2 };
-  enum { kMinPrefetchAmount = 5 };
-  enum { kMaxPrefetchAmount = 100 };
-
-  int64_t transaction_id_;
-
-  blink::mojom::IDBCursorAssociatedPtr cursor_;
-
-  // Prefetch cache.
-  base::circular_deque<blink::IndexedDBKey> prefetch_keys_;
-  base::circular_deque<blink::IndexedDBKey> prefetch_primary_keys_;
-  base::circular_deque<blink::WebIDBValue> prefetch_values_;
-
-  // Number of continue calls that would qualify for a pre-fetch.
-  int continue_count_;
-
-  // Number of items used from the last prefetch.
-  int used_prefetches_;
-
-  // Number of onsuccess handlers we are waiting for.
-  int pending_onsuccess_callbacks_;
-
-  // Number of items to request in next prefetch.
-  int prefetch_amount_;
-
-  base::WeakPtrFactory<WebIDBCursorImpl> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(WebIDBCursorImpl);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_INDEXED_DB_WEBIDBCURSOR_IMPL_H_
diff --git a/content/renderer/indexed_db/webidbdatabase_impl.cc b/content/renderer/indexed_db/webidbdatabase_impl.cc
deleted file mode 100644
index ae54492..0000000
--- a/content/renderer/indexed_db/webidbdatabase_impl.cc
+++ /dev/null
@@ -1,360 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/renderer/indexed_db/webidbdatabase_impl.h"
-
-#include <stddef.h>
-
-#include <string>
-#include <vector>
-
-#include "base/format_macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/strings/string16.h"
-#include "base/strings/stringprintf.h"
-#include "content/renderer/indexed_db/indexed_db_callbacks_impl.h"
-#include "content/renderer/indexed_db/indexed_db_dispatcher.h"
-#include "content/renderer/indexed_db/indexed_db_key_builders.h"
-#include "mojo/public/cpp/bindings/strong_associated_binding.h"
-#include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
-#include "third_party/blink/public/platform/file_path_conversion.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_error.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_key_path.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_metadata.h"
-#include "third_party/blink/public/platform/web_blob_info.h"
-#include "third_party/blink/public/platform/web_string.h"
-#include "third_party/blink/public/platform/web_vector.h"
-
-using blink::IndexedDBKey;
-using blink::IndexedDBIndexKeys;
-using blink::WebBlobInfo;
-using blink::WebIDBCallbacks;
-using blink::WebIDBDatabase;
-using blink::WebIDBDatabaseCallbacks;
-using blink::WebIDBMetadata;
-using blink::WebIDBKey;
-using blink::WebIDBKeyPath;
-using blink::WebIDBKeyRange;
-using blink::WebIDBKeyView;
-using blink::WebString;
-using blink::WebVector;
-using blink::mojom::IDBCallbacksAssociatedPtrInfo;
-using blink::mojom::IDBDatabaseAssociatedPtrInfo;
-
-namespace content {
-
-namespace {
-
-std::vector<IndexedDBIndexKeys> ConvertWebIndexKeys(
-    const WebVector<blink::WebIDBIndexKeys>& index_keys) {
-  std::vector<IndexedDBIndexKeys> result;
-  result.reserve(index_keys.size());
-  for (size_t i = 0, len = index_keys.size(); i < len; ++i) {
-    result.emplace_back(index_keys[i].first, std::vector<IndexedDBKey>());
-    std::vector<IndexedDBKey>& result_keys = result.back().second;
-    result_keys.reserve(index_keys[i].second.size());
-    for (const WebIDBKey& index_key : index_keys[i].second)
-      result_keys.emplace_back(IndexedDBKeyBuilder::Build(index_key.View()));
-  }
-  return result;
-}
-
-}  // namespace
-
-WebIDBDatabaseImpl::WebIDBDatabaseImpl(
-    IDBDatabaseAssociatedPtrInfo database_info)
-    : database_(std::move(database_info)) {}
-
-WebIDBDatabaseImpl::~WebIDBDatabaseImpl() = default;
-
-void WebIDBDatabaseImpl::CreateObjectStore(long long transaction_id,
-                                           long long object_store_id,
-                                           const WebString& name,
-                                           const WebIDBKeyPath& key_path,
-                                           bool auto_increment) {
-  database_->CreateObjectStore(transaction_id, object_store_id, name.Utf16(),
-                               IndexedDBKeyPathBuilder::Build(key_path),
-                               auto_increment);
-}
-
-void WebIDBDatabaseImpl::DeleteObjectStore(long long transaction_id,
-                                           long long object_store_id) {
-  database_->DeleteObjectStore(transaction_id, object_store_id);
-}
-
-void WebIDBDatabaseImpl::RenameObjectStore(long long transaction_id,
-                                           long long object_store_id,
-                                           const blink::WebString& new_name) {
-  database_->RenameObjectStore(transaction_id, object_store_id,
-                               new_name.Utf16());
-}
-
-void WebIDBDatabaseImpl::CreateTransaction(
-    long long transaction_id,
-    const WebVector<long long>& object_store_ids,
-    blink::WebIDBTransactionMode mode) {
-  database_->CreateTransaction(
-      transaction_id,
-      std::vector<int64_t>(object_store_ids.begin(), object_store_ids.end()),
-      mode);
-}
-
-void WebIDBDatabaseImpl::Close() {
-  database_->Close();
-}
-
-void WebIDBDatabaseImpl::VersionChangeIgnored() {
-  database_->VersionChangeIgnored();
-}
-
-void WebIDBDatabaseImpl::AddObserver(
-    long long transaction_id,
-    int32_t observer_id,
-    bool include_transaction,
-    bool no_records,
-    bool values,
-    const std::bitset<blink::kWebIDBOperationTypeCount>& operation_types) {
-  static_assert(blink::kWebIDBOperationTypeCount < sizeof(uint16_t) * CHAR_BIT,
-                "WebIDBOperationType Count exceeds size of uint16_t");
-  database_->AddObserver(transaction_id, observer_id, include_transaction,
-                         no_records, values, operation_types.to_ulong());
-}
-
-void WebIDBDatabaseImpl::RemoveObservers(
-    const WebVector<int32_t>& observer_ids_to_remove) {
-  std::vector<int32_t> remove_observer_ids(
-      observer_ids_to_remove.Data(),
-      observer_ids_to_remove.Data() + observer_ids_to_remove.size());
-  database_->RemoveObservers(remove_observer_ids);
-}
-
-void WebIDBDatabaseImpl::Get(long long transaction_id,
-                             long long object_store_id,
-                             long long index_id,
-                             const WebIDBKeyRange& key_range,
-                             bool key_only,
-                             WebIDBCallbacks* callbacks) {
-  IndexedDBDispatcher::ThreadSpecificInstance()->ResetCursorPrefetchCaches(
-      transaction_id, nullptr);
-
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), transaction_id, nullptr);
-  database_->Get(transaction_id, object_store_id, index_id,
-                 IndexedDBKeyRangeBuilder::Build(key_range), key_only,
-                 GetCallbacksProxy(std::move(callbacks_impl)));
-}
-
-void WebIDBDatabaseImpl::GetAll(long long transaction_id,
-                                long long object_store_id,
-                                long long index_id,
-                                const WebIDBKeyRange& key_range,
-                                long long max_count,
-                                bool key_only,
-                                WebIDBCallbacks* callbacks) {
-  IndexedDBDispatcher::ThreadSpecificInstance()->ResetCursorPrefetchCaches(
-      transaction_id, nullptr);
-
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), transaction_id, nullptr);
-  database_->GetAll(transaction_id, object_store_id, index_id,
-                    IndexedDBKeyRangeBuilder::Build(key_range), key_only,
-                    max_count, GetCallbacksProxy(std::move(callbacks_impl)));
-}
-
-void WebIDBDatabaseImpl::Put(
-    long long transaction_id,
-    long long object_store_id,
-    const blink::WebData& value,
-    const WebVector<WebBlobInfo>& web_blob_info,
-    WebIDBKeyView web_primary_key,
-    blink::WebIDBPutMode put_mode,
-    WebIDBCallbacks* callbacks,
-    const WebVector<blink::WebIDBIndexKeys>& index_keys) {
-  IndexedDBKey key = IndexedDBKeyBuilder::Build(web_primary_key);
-
-  if (value.size() + key.size_estimate() > max_put_value_size_) {
-    callbacks->OnError(blink::WebIDBDatabaseError(
-        blink::kWebIDBDatabaseExceptionUnknownError,
-        WebString::FromUTF8(base::StringPrintf(
-            "The serialized value is too large"
-            " (size=%" PRIuS " bytes, max=%" PRIuS " bytes).",
-            value.size(), max_put_value_size_))));
-    return;
-  }
-
-  IndexedDBDispatcher::ThreadSpecificInstance()->ResetCursorPrefetchCaches(
-      transaction_id, nullptr);
-
-  auto mojo_value = blink::mojom::IDBValue::New();
-  DCHECK(mojo_value->bits.empty());
-  mojo_value->bits.reserve(value.size());
-  value.ForEachSegment([&mojo_value](const char* segment, size_t segment_size,
-                                     size_t segment_offset) {
-    mojo_value->bits.append(segment, segment_size);
-    return true;
-  });
-  mojo_value->blob_or_file_info.reserve(web_blob_info.size());
-  for (const WebBlobInfo& info : web_blob_info) {
-    auto blob_info = blink::mojom::IDBBlobInfo::New();
-    if (info.IsFile()) {
-      blob_info->file = blink::mojom::IDBFileInfo::New();
-      blob_info->file->path = blink::WebStringToFilePath(info.FilePath());
-      blob_info->file->name = info.FileName().Utf16();
-      blob_info->file->last_modified =
-          base::Time::FromDoubleT(info.LastModified());
-    }
-    blob_info->size = info.size();
-    blob_info->uuid = info.Uuid().Latin1();
-    DCHECK(blob_info->uuid.size());
-    blob_info->mime_type = info.GetType().Utf16();
-    blob_info->blob = blink::mojom::BlobPtrInfo(info.CloneBlobHandle(),
-                                                blink::mojom::Blob::Version_);
-    mojo_value->blob_or_file_info.push_back(std::move(blob_info));
-  }
-
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), transaction_id, nullptr);
-  database_->Put(transaction_id, object_store_id, std::move(mojo_value), key,
-                 put_mode, ConvertWebIndexKeys(index_keys),
-                 GetCallbacksProxy(std::move(callbacks_impl)));
-}
-
-void WebIDBDatabaseImpl::SetIndexKeys(
-    long long transaction_id,
-    long long object_store_id,
-    WebIDBKeyView primary_key,
-    const WebVector<blink::WebIDBIndexKeys>& index_keys) {
-  database_->SetIndexKeys(transaction_id, object_store_id,
-                          IndexedDBKeyBuilder::Build(primary_key),
-                          ConvertWebIndexKeys(index_keys));
-}
-
-void WebIDBDatabaseImpl::SetIndexesReady(
-    long long transaction_id,
-    long long object_store_id,
-    const WebVector<long long>& web_index_ids) {
-  std::vector<int64_t> index_ids(web_index_ids.Data(),
-                                 web_index_ids.Data() + web_index_ids.size());
-  database_->SetIndexesReady(transaction_id, object_store_id,
-                             std::move(index_ids));
-}
-
-void WebIDBDatabaseImpl::OpenCursor(long long transaction_id,
-                                    long long object_store_id,
-                                    long long index_id,
-                                    const WebIDBKeyRange& key_range,
-                                    blink::WebIDBCursorDirection direction,
-                                    bool key_only,
-                                    blink::WebIDBTaskType task_type,
-                                    WebIDBCallbacks* callbacks) {
-  IndexedDBDispatcher::ThreadSpecificInstance()->ResetCursorPrefetchCaches(
-      transaction_id, nullptr);
-
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), transaction_id, nullptr);
-  database_->OpenCursor(transaction_id, object_store_id, index_id,
-                        IndexedDBKeyRangeBuilder::Build(key_range), direction,
-                        key_only, task_type,
-                        GetCallbacksProxy(std::move(callbacks_impl)));
-}
-
-void WebIDBDatabaseImpl::Count(long long transaction_id,
-                               long long object_store_id,
-                               long long index_id,
-                               const WebIDBKeyRange& key_range,
-                               WebIDBCallbacks* callbacks) {
-  IndexedDBDispatcher::ThreadSpecificInstance()->ResetCursorPrefetchCaches(
-      transaction_id, nullptr);
-
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), transaction_id, nullptr);
-  database_->Count(transaction_id, object_store_id, index_id,
-                   IndexedDBKeyRangeBuilder::Build(key_range),
-                   GetCallbacksProxy(std::move(callbacks_impl)));
-}
-
-void WebIDBDatabaseImpl::Delete(long long transaction_id,
-                                long long object_store_id,
-                                WebIDBKeyView primary_key,
-                                WebIDBCallbacks* callbacks) {
-  IndexedDBDispatcher::ThreadSpecificInstance()->ResetCursorPrefetchCaches(
-      transaction_id, nullptr);
-
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), transaction_id, nullptr);
-  database_->DeleteRange(transaction_id, object_store_id,
-                         IndexedDBKeyRangeBuilder::Build(primary_key),
-                         GetCallbacksProxy(std::move(callbacks_impl)));
-}
-
-void WebIDBDatabaseImpl::DeleteRange(long long transaction_id,
-                                     long long object_store_id,
-                                     const WebIDBKeyRange& key_range,
-                                     WebIDBCallbacks* callbacks) {
-  IndexedDBDispatcher::ThreadSpecificInstance()->ResetCursorPrefetchCaches(
-      transaction_id, nullptr);
-
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), transaction_id, nullptr);
-  database_->DeleteRange(transaction_id, object_store_id,
-                         IndexedDBKeyRangeBuilder::Build(key_range),
-                         GetCallbacksProxy(std::move(callbacks_impl)));
-}
-
-void WebIDBDatabaseImpl::Clear(long long transaction_id,
-                               long long object_store_id,
-                               WebIDBCallbacks* callbacks) {
-  IndexedDBDispatcher::ThreadSpecificInstance()->ResetCursorPrefetchCaches(
-      transaction_id, nullptr);
-
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), transaction_id, nullptr);
-  database_->Clear(transaction_id, object_store_id,
-                   GetCallbacksProxy(std::move(callbacks_impl)));
-}
-
-void WebIDBDatabaseImpl::CreateIndex(long long transaction_id,
-                                     long long object_store_id,
-                                     long long index_id,
-                                     const WebString& name,
-                                     const WebIDBKeyPath& key_path,
-                                     bool unique,
-                                     bool multi_entry) {
-  database_->CreateIndex(transaction_id, object_store_id, index_id,
-                         name.Utf16(), IndexedDBKeyPathBuilder::Build(key_path),
-                         unique, multi_entry);
-}
-
-void WebIDBDatabaseImpl::DeleteIndex(long long transaction_id,
-                                     long long object_store_id,
-                                     long long index_id) {
-  database_->DeleteIndex(transaction_id, object_store_id, index_id);
-}
-
-void WebIDBDatabaseImpl::RenameIndex(long long transaction_id,
-                                     long long object_store_id,
-                                     long long index_id,
-                                     const WebString& new_name) {
-  database_->RenameIndex(transaction_id, object_store_id, index_id,
-                         new_name.Utf16());
-}
-
-void WebIDBDatabaseImpl::Abort(long long transaction_id) {
-  database_->Abort(transaction_id);
-}
-
-void WebIDBDatabaseImpl::Commit(long long transaction_id) {
-  database_->Commit(transaction_id);
-}
-
-IDBCallbacksAssociatedPtrInfo WebIDBDatabaseImpl::GetCallbacksProxy(
-    std::unique_ptr<IndexedDBCallbacksImpl> callbacks) {
-  IDBCallbacksAssociatedPtrInfo ptr_info;
-  auto request = mojo::MakeRequest(&ptr_info);
-  mojo::MakeStrongAssociatedBinding(std::move(callbacks), std::move(request));
-  return ptr_info;
-}
-
-}  // namespace content
diff --git a/content/renderer/indexed_db/webidbdatabase_impl_unittest.cc b/content/renderer/indexed_db/webidbdatabase_impl_unittest.cc
deleted file mode 100644
index 39bc0f26e..0000000
--- a/content/renderer/indexed_db/webidbdatabase_impl_unittest.cc
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "content/child/thread_safe_sender.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "content/renderer/indexed_db/mock_webidbcallbacks.h"
-#include "content/renderer/indexed_db/webidbdatabase_impl.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
-#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
-#include "third_party/blink/public/platform/web_blob_info.h"
-#include "third_party/blink/public/platform/web_data.h"
-#include "third_party/blink/public/web/web_heap.h"
-
-using blink::IndexedDBKey;
-using blink::WebBlobInfo;
-using blink::WebData;
-using blink::WebIDBCursor;
-using blink::WebIDBKey;
-using blink::WebVector;
-using testing::_;
-using testing::Invoke;
-using testing::StrictMock;
-using testing::WithArgs;
-
-namespace content {
-
-class WebIDBDatabaseImplTest : public testing::Test {
- public:
-  WebIDBDatabaseImplTest() {}
-
-  void TearDown() override { blink::WebHeap::CollectAllGarbageForTesting(); }
-
- private:
-  TestBrowserThreadBundle thread_bundle_;
-
-  DISALLOW_COPY_AND_ASSIGN(WebIDBDatabaseImplTest);
-};
-
-TEST_F(WebIDBDatabaseImplTest, ValueSizeTest) {
-  // For testing use a much smaller maximum size to prevent allocating >100 MB
-  // of memory, which crashes on memory-constrained systems.
-  const size_t kMaxValueSizeForTesting = 10 * 1024 * 1024;  // 10 MB
-
-  const std::vector<char> data(kMaxValueSizeForTesting + 1);
-  const WebData value(&data.front(), data.size());
-  const WebVector<WebBlobInfo> web_blob_info;
-
-  const int64_t transaction_id = 1;
-  const int64_t object_store_id = 2;
-
-  StrictMock<MockWebIDBCallbacks> callbacks;
-  EXPECT_CALL(callbacks, OnError(_)).Times(1);
-
-  WebIDBDatabaseImpl database_impl(nullptr);
-  database_impl.max_put_value_size_ = kMaxValueSizeForTesting;
-  const WebIDBKey idb_key = WebIDBKey::CreateNumber(0);
-  database_impl.Put(transaction_id, object_store_id, value, web_blob_info,
-                    idb_key.View(), blink::kWebIDBPutModeAddOrUpdate,
-                    &callbacks, WebVector<blink::WebIDBIndexKeys>());
-}
-
-TEST_F(WebIDBDatabaseImplTest, KeyAndValueSizeTest) {
-  // For testing use a much smaller maximum size to prevent allocating >100 MB
-  // of memory, which crashes on memory-constrained systems.
-  const size_t kMaxValueSizeForTesting = 10 * 1024 * 1024;  // 10 MB
-  const size_t kKeySize = 1024 * 1024;
-
-  const std::vector<char> data(kMaxValueSizeForTesting - kKeySize);
-  const WebData value(&data.front(), data.size());
-  const WebVector<WebBlobInfo> web_blob_info;
-  const WebIDBKey key = WebIDBKey::CreateString(blink::WebString::FromUTF16(
-      base::string16(kKeySize / sizeof(base::string16::value_type), 'x')));
-
-  const int64_t transaction_id = 1;
-  const int64_t object_store_id = 2;
-
-  StrictMock<MockWebIDBCallbacks> callbacks;
-  EXPECT_CALL(callbacks, OnError(_)).Times(1);
-
-  WebIDBDatabaseImpl database_impl(nullptr);
-  database_impl.max_put_value_size_ = kMaxValueSizeForTesting;
-  database_impl.Put(transaction_id, object_store_id, value, web_blob_info,
-                    key.View(), blink::kWebIDBPutModeAddOrUpdate, &callbacks,
-                    WebVector<blink::WebIDBIndexKeys>());
-}
-
-}  // namespace content
diff --git a/content/renderer/media/stream/webmediaplayer_ms.cc b/content/renderer/media/stream/webmediaplayer_ms.cc
index d4bb99d9..6ef81ae9 100644
--- a/content/renderer/media/stream/webmediaplayer_ms.cc
+++ b/content/renderer/media/stream/webmediaplayer_ms.cc
@@ -1211,4 +1211,17 @@
   frame_deliverer_->gpu_memory_buffer_pool_.reset(gpu_memory_buffer_pool);
 }
 
+void WebMediaPlayerMS::OnDisplayTypeChanged(
+    WebMediaPlayer::DisplayType display_type) {
+  if (!bridge_)
+    return;
+
+  compositor_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &WebMediaPlayerMSCompositor::SetForceSubmit,
+          base::Unretained(compositor_.get()),
+          display_type == WebMediaPlayer::DisplayType::kPictureInPicture));
+}
+
 }  // namespace content
diff --git a/content/renderer/media/stream/webmediaplayer_ms.h b/content/renderer/media/stream/webmediaplayer_ms.h
index 9a4dfd3..e26faac 100644
--- a/content/renderer/media/stream/webmediaplayer_ms.h
+++ b/content/renderer/media/stream/webmediaplayer_ms.h
@@ -240,6 +240,8 @@
   void TrackRemoved(const blink::WebMediaStreamTrack& track) override;
   void ActiveStateChanged(bool is_active) override;
 
+  void OnDisplayTypeChanged(WebMediaPlayer::DisplayType) override;
+
  private:
   friend class WebMediaPlayerMSTest;
 
diff --git a/content/renderer/p2p/ipc_socket_factory.cc b/content/renderer/p2p/ipc_socket_factory.cc
index 3384c6cc..16b0f59 100644
--- a/content/renderer/p2p/ipc_socket_factory.cc
+++ b/content/renderer/p2p/ipc_socket_factory.cc
@@ -226,7 +226,7 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
-  int port_;   // Port number in |addr| from Start() method.
+  rtc::SocketAddress addr_;                // Address to resolve.
   std::vector<rtc::IPAddress> addresses_;  // Resolved addresses.
 };
 
@@ -664,9 +664,9 @@
 
 void AsyncAddressResolverImpl::Start(const rtc::SocketAddress& addr) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // Copy port number from |addr|. |port_| must be copied
-  // when resolved address is returned in GetResolvedAddress.
-  port_ = addr.port();
+  // Port and hostname must be copied to the resolved address returned from
+  // GetResolvedAddress.
+  addr_ = addr;
 
   resolver_->Start(addr, base::Bind(
       &AsyncAddressResolverImpl::OnAddressResolved,
@@ -682,8 +682,8 @@
 
   for (size_t i = 0; i < addresses_.size(); ++i) {
     if (family == addresses_[i].family()) {
+      *addr = addr_;
       addr->SetResolvedIP(addresses_[i]);
-      addr->SetPort(port_);
       return true;
     }
   }
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 9cd0d75..ef96381 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -88,7 +88,6 @@
 #include "content/renderer/dom_storage/webstoragenamespace_impl.h"
 #include "content/renderer/effective_connection_type_helper.h"
 #include "content/renderer/gpu/frame_swap_message_queue.h"
-#include "content/renderer/indexed_db/indexed_db_dispatcher.h"
 #include "content/renderer/input/widget_input_handler_manager.h"
 #include "content/renderer/loader/resource_dispatcher.h"
 #include "content/renderer/low_memory_mode_controller.h"
@@ -764,7 +763,6 @@
                           base::Unretained(appcache_dispatcher())),
       GetWebMainThreadScheduler()->IPCTaskRunner());
   dom_storage_dispatcher_.reset(new DomStorageDispatcher());
-  main_thread_indexed_db_dispatcher_.reset(new IndexedDBDispatcher());
 
   vc_manager_.reset(new VideoCaptureImplManager());
 
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index e366aec..dbd473b 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -125,7 +125,6 @@
 class DomStorageDispatcher;
 class FrameSwapMessageQueue;
 class GpuVideoAcceleratorFactoriesImpl;
-class IndexedDBDispatcher;
 class LowMemoryModeController;
 class MidiMessageFilter;
 class P2PSocketDispatcher;
@@ -589,7 +588,6 @@
   // These objects live solely on the render thread.
   std::unique_ptr<AppCacheDispatcher> appcache_dispatcher_;
   std::unique_ptr<DomStorageDispatcher> dom_storage_dispatcher_;
-  std::unique_ptr<IndexedDBDispatcher> main_thread_indexed_db_dispatcher_;
   std::unique_ptr<blink::scheduler::WebThreadScheduler> main_thread_scheduler_;
   std::unique_ptr<RendererBlinkPlatformImpl> blink_platform_impl_;
   std::unique_ptr<ResourceDispatcher> resource_dispatcher_;
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 7bfbf979..9859aa5 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -2605,6 +2605,12 @@
       blink::WebRuntimeFeatures::IsBlinkGenPropertyTreesEnabled() ||
       blink::WebRuntimeFeatures::IsSlimmingPaintV2Enabled();
 
+  // Blink currently doesn't support setting fractional scroll offsets so CC
+  // must send integer values. We plan to eventually make Blink use fractional
+  // offsets internally: https://crbug.com/414283.
+  settings.commit_fractional_scroll_deltas =
+      blink::WebRuntimeFeatures::IsFractionalScrollOffsetsEnabled();
+
   // The means the renderer compositor has 2 possible modes:
   // - Threaded compositing with a scheduler.
   // - Single threaded compositing without a scheduler (for layout tests only).
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index d8728b9f..310b55f 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -46,7 +46,6 @@
 #include "content/renderer/dom_storage/session_web_storage_namespace_impl.h"
 #include "content/renderer/dom_storage/webstoragenamespace_impl.h"
 #include "content/renderer/image_capture/image_capture_frame_grabber.h"
-#include "content/renderer/indexed_db/webidbfactory_impl.h"
 #include "content/renderer/loader/child_url_loader_factory_bundle.h"
 #include "content/renderer/loader/code_cache_loader_impl.h"
 #include "content/renderer/loader/resource_dispatcher.h"
@@ -141,7 +140,6 @@
 using blink::WebBlobRegistry;
 using blink::WebCanvasCaptureHandler;
 using blink::WebDatabaseObserver;
-using blink::WebIDBFactory;
 using blink::WebImageCaptureFrameGrabber;
 using blink::WebMediaPlayer;
 using blink::WebMediaRecorderHandler;
@@ -564,17 +562,6 @@
 
 //------------------------------------------------------------------------------
 
-std::unique_ptr<blink::WebIDBFactory>
-RendererBlinkPlatformImpl::CreateIdbFactory() {
-  blink::mojom::IDBFactoryPtrInfo web_idb_factory_host_info;
-  GetInterfaceProvider()->GetInterface(
-      mojo::MakeRequest(&web_idb_factory_host_info));
-  return std::make_unique<WebIDBFactoryImpl>(
-      std::move(web_idb_factory_host_info));
-}
-
-//------------------------------------------------------------------------------
-
 WebString RendererBlinkPlatformImpl::FileSystemCreateOriginIdentifier(
     const blink::WebSecurityOrigin& origin) {
   return WebString::FromUTF8(
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index 4ec814f..d12e1fc 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -27,7 +27,6 @@
 #include "third_party/blink/public/common/screen_orientation/web_screen_orientation_type.h"
 #include "third_party/blink/public/mojom/loader/code_cache.mojom.h"
 #include "third_party/blink/public/platform/modules/cache_storage/cache_storage.mojom.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_factory.h"
 #include "third_party/blink/public/platform/modules/webdatabase/web_database.mojom.h"
 
 #if defined(OS_LINUX)
@@ -125,7 +124,6 @@
   viz::FrameSinkId GenerateFrameSinkId() override;
   bool IsLockedToSite() const override;
 
-  std::unique_ptr<blink::WebIDBFactory> CreateIdbFactory() override;
   blink::WebString FileSystemCreateOriginIdentifier(
       const blink::WebSecurityOrigin& origin) override;
 
diff --git a/content/renderer/renderer_webcookiejar_impl.cc b/content/renderer/renderer_webcookiejar_impl.cc
index d5e0ef8..07f2dccb 100644
--- a/content/renderer/renderer_webcookiejar_impl.cc
+++ b/content/renderer/renderer_webcookiejar_impl.cc
@@ -23,8 +23,7 @@
   std::string value_utf8 =
       value.Utf8(WebString::UTF8ConversionMode::kStrictReplacingErrorsWithFFFD);
   RenderThreadImpl::current()->render_frame_message_filter()->SetCookie(
-      sender_->GetRoutingID(), url, site_for_cookies, value_utf8,
-      base::DoNothing());
+      sender_->GetRoutingID(), url, site_for_cookies, value_utf8);
 }
 
 WebString RendererWebCookieJarImpl::Cookies(const WebURL& url,
diff --git a/content/renderer/service_worker/service_worker_subresource_loader.cc b/content/renderer/service_worker/service_worker_subresource_loader.cc
index ce6e8d3..56cd69d 100644
--- a/content/renderer/service_worker/service_worker_subresource_loader.cc
+++ b/content/renderer/service_worker/service_worker_subresource_loader.cc
@@ -54,8 +54,9 @@
 // whenever a response or redirect is received.
 class HeaderRewritingURLLoaderClient : public network::mojom::URLLoaderClient {
  public:
-  using RewriteHeaderCallback = base::Callback<network::ResourceResponseHead(
-      const network::ResourceResponseHead&)>;
+  using RewriteHeaderCallback =
+      base::RepeatingCallback<network::ResourceResponseHead(
+          const network::ResourceResponseHead&)>;
 
   HeaderRewritingURLLoaderClient(
       network::mojom::URLLoaderClientPtr url_loader_client,
diff --git a/content/shell/browser/layout_test/blink_test_controller.cc b/content/shell/browser/layout_test/blink_test_controller.cc
index 0585b817..c7121a9 100644
--- a/content/shell/browser/layout_test/blink_test_controller.cc
+++ b/content/shell/browser/layout_test/blink_test_controller.cc
@@ -540,29 +540,12 @@
   composite_all_frames_node_queue_ = std::queue<Node*>();
   weak_factory_.InvalidateWeakPtrs();
 
-  bool discard_main_window = false;
-  bool discard_secondary_window = false;
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kResetShellBetweenTests)) {
-    discard_main_window = true;
-    discard_secondary_window = true;
-  }
-
 #if defined(OS_ANDROID)
   // Re-using the shell's main window on Android causes issues with networking
   // requests never succeeding. See http://crbug.com/277652.
-  discard_main_window = true;
+  DiscardMainWindow();
 #endif
 
-  if (discard_main_window) {
-    DiscardMainWindow();
-    devtools_window_ = nullptr;
-  }
-  if (discard_secondary_window && secondary_window_) {
-    secondary_window_->Close();
-    secondary_window_ = nullptr;
-  }
-
   return true;
 }
 
diff --git a/content/shell/common/layout_test/layout_test_switches.cc b/content/shell/common/layout_test/layout_test_switches.cc
index 5e00a75..262a246 100644
--- a/content/shell/common/layout_test/layout_test_switches.cc
+++ b/content/shell/common/layout_test/layout_test_switches.cc
@@ -46,10 +46,6 @@
 // Encode binary layout test results (images, audio) using base64.
 const char kEncodeBinary[] = "encode-binary";
 
-// Request Content Shell between tests. This causes tests to run twice as
-// slowly, but provides more consistent results.
-const char kResetShellBetweenTests[] = "reset-shell-between-tests";
-
 // Request the render trees of pages to be dumped as text once they have
 // finished loading.
 const char kRunWebTests[] = "run-web-tests";
diff --git a/content/shell/common/layout_test/layout_test_switches.h b/content/shell/common/layout_test/layout_test_switches.h
index 5be1c26..ebbf47c 100644
--- a/content/shell/common/layout_test/layout_test_switches.h
+++ b/content/shell/common/layout_test/layout_test_switches.h
@@ -28,7 +28,6 @@
 extern const char kAlwaysUseComplexText[];
 extern const char kEnableLeakDetection[];
 extern const char kEncodeBinary[];
-extern const char kResetShellBetweenTests[];
 extern const char kRunWebTests[];
 extern const char kStableReleaseMode[];
 extern const char kTestsInBlink[];
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 6ead595..8250527 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1479,7 +1479,6 @@
     "../browser/media/session/media_session_impl_unittest.cc",
     "../browser/media/session/media_session_uma_helper_unittest.cc",
     "../browser/media/webaudio/audio_context_manager_impl_unittest.cc",
-    "../browser/memory/memory_coordinator_impl_unittest.cc",
     "../browser/memory/memory_monitor_android_unittest.cc",
     "../browser/memory/memory_monitor_win_unittest.cc",
     "../browser/memory/swap_metrics_driver_impl_unittest.cc",
@@ -1704,10 +1703,6 @@
     "../renderer/gpu/layer_tree_view_unittest.cc",
     "../renderer/gpu/queue_message_swap_promise_unittest.cc",
     "../renderer/ico_image_decoder_unittest.cc",
-    "../renderer/indexed_db/mock_webidbcallbacks.cc",
-    "../renderer/indexed_db/mock_webidbcallbacks.h",
-    "../renderer/indexed_db/webidbcursor_impl_unittest.cc",
-    "../renderer/indexed_db/webidbdatabase_impl_unittest.cc",
     "../renderer/input/input_event_prediction_unittest.cc",
     "../renderer/input/main_thread_event_queue_unittest.cc",
     "../renderer/loader/resource_dispatcher_unittest.cc",
diff --git a/content/test/data/indexeddb/cursor_prefetch.js b/content/test/data/indexeddb/cursor_prefetch.js
index 076e6a9..772f5c0 100644
--- a/content/test/data/indexeddb/cursor_prefetch.js
+++ b/content/test/data/indexeddb/cursor_prefetch.js
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 // These constants should match the ones in
-// content/child/indexed_db/webidbcursor_impl.h to make sure the test hits the
-// right code paths.
+// third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.h to
+// make sure the test hits the right code paths.
 var kPrefetchThreshold = 2;
 var kMinPrefetchAmount = 5;
 
diff --git a/content/test/gpu/gpu_tests/test_expectations.py b/content/test/gpu/gpu_tests/test_expectations.py
index 0fc3d586..cf7d7bd2 100644
--- a/content/test/gpu/gpu_tests/test_expectations.py
+++ b/content/test/gpu/gpu_tests/test_expectations.py
@@ -7,13 +7,23 @@
 
 # Valid expectation conditions are:
 #
-# Operating systems:
-#     win, xp, vista, win7, win8, win10, mac, leopard, snowleopard,
-#     lion, mountainlion, mavericks, yosemite, sierra, highsierra, mojave
-#     linux, chromeos, android
+# Operating systems (all of these are mutually exclusive, so don't
+# specify both "android" and "m" in the same expectation, for
+# example):
+#
+#     win, xp, vista, win7, win8, win10
+#
+#     mac, leopard, snowleopard, lion, mountainlion, mavericks,
+#     yosemite, sierra, highsierra, mojave
+#
+#     linux, chromeos
+#
+#     android, l, m, n, o, p, q
 #
 # Browser types:
-#     android-webview-instrumentation, android-content-shell, debug, release
+#
+#     android-webview-instrumentation, android-content-shell,
+#     android-chromium, debug, debug_x64, release, release_x64
 #
 # ASAN conditions:
 #     asan, no_asan
@@ -25,9 +35,11 @@
 WIN_CONDITIONS = ['xp', 'vista', 'win7', 'win8', 'win10']
 MAC_CONDITIONS = ['leopard', 'snowleopard', 'lion', 'mountainlion',
                   'mavericks', 'yosemite', 'sierra', 'highsierra', 'mojave']
+# These aren't expanded out into "lollipop", "marshmallow", etc.
+ANDROID_CONDITIONS = ['l', 'm', 'n', 'o', 'p', 'q']
 
 OS_CONDITIONS = ['win', 'mac', 'linux', 'chromeos', 'android'] + \
-                WIN_CONDITIONS + MAC_CONDITIONS
+                WIN_CONDITIONS + MAC_CONDITIONS + ANDROID_CONDITIONS
 
 BROWSER_TYPE_CONDITIONS = [
     'android-webview-instrumentation', 'android-content-shell',
@@ -82,16 +94,8 @@
     implementation will raise an exception if the condition is
     unsupported.
 
-    Valid expectation conditions are:
-
-    Operating systems:
-      win, xp, vista, win7, mac, leopard, snowleopard, lion,
-      mountainlion, mavericks, yosemite, sierra, linux, chromeos,
-      android
-
-    Browser types:
-      android-webview-instrumentation, android-content-shell,
-      android-chromium, debug, debug_x64, release, release_x64
+    Valid expectation conditions are listed in the comments at the top
+    of this file.
 
     Sample usage in SetExpectations in subclasses:
       self.Fail('gl-enable-vertex-attrib.html',
diff --git a/content/test/test_blink_web_unit_test_support.cc b/content/test/test_blink_web_unit_test_support.cc
index a9ceeed8b..4d75061 100644
--- a/content/test/test_blink_web_unit_test_support.cc
+++ b/content/test/test_blink_web_unit_test_support.cc
@@ -214,13 +214,6 @@
   return &blob_registry_;
 }
 
-std::unique_ptr<blink::WebIDBFactory>
-TestBlinkWebUnitTestSupport::CreateIdbFactory() {
-  NOTREACHED() <<
-      "IndexedDB cannot be tested with in-process harnesses.";
-  return nullptr;
-}
-
 std::unique_ptr<blink::WebURLLoaderFactory>
 TestBlinkWebUnitTestSupport::CreateDefaultURLLoaderFactory() {
   return std::make_unique<WebURLLoaderFactoryWithMock>(
diff --git a/content/test/test_blink_web_unit_test_support.h b/content/test/test_blink_web_unit_test_support.h
index 75010ad..44e7e95 100644
--- a/content/test/test_blink_web_unit_test_support.h
+++ b/content/test/test_blink_web_unit_test_support.h
@@ -34,7 +34,6 @@
   ~TestBlinkWebUnitTestSupport() override;
 
   blink::WebBlobRegistry* GetBlobRegistry() override;
-  std::unique_ptr<blink::WebIDBFactory> CreateIdbFactory() override;
 
   std::unique_ptr<blink::WebURLLoaderFactory> CreateDefaultURLLoaderFactory()
       override;
diff --git a/courgette/third_party/divsufsort/README.chromium b/courgette/third_party/divsufsort/README.chromium
index 0c99c05..29bee1fb 100644
--- a/courgette/third_party/divsufsort/README.chromium
+++ b/courgette/third_party/divsufsort/README.chromium
@@ -32,3 +32,4 @@
   - Added namespace divsuf.
   - Added divsufsort_with_empty().
   - Added unit tests.
+  - Patch to avoid int/uint comparison warnings.
diff --git a/courgette/third_party/divsufsort/divsufsort.cc b/courgette/third_party/divsufsort/divsufsort.cc
index 2971534..bc94ce6c 100644
--- a/courgette/third_party/divsufsort/divsufsort.cc
+++ b/courgette/third_party/divsufsort/divsufsort.cc
@@ -57,8 +57,8 @@
   saint_t c0, c1;
 
   /* Initialize bucket arrays. */
-  for(i = 0; i < BUCKET_A_SIZE; ++i) { bucket_A[i] = 0; }
-  for(i = 0; i < BUCKET_B_SIZE; ++i) { bucket_B[i] = 0; }
+  for(i = 0; i < static_cast<saidx_t>(BUCKET_A_SIZE); ++i) { bucket_A[i] = 0; }
+  for(i = 0; i < static_cast<saidx_t>(BUCKET_B_SIZE); ++i) { bucket_B[i] = 0; }
 
   /* Count the number of occurrences of the first one or two characters of each
      type A, B and B* suffix. Moreover, store the beginning position of all
@@ -84,11 +84,11 @@
 */
 
   /* Calculate the index of start/end point of each bucket. */
-  for(c0 = 0, i = 0, j = 0; c0 < ALPHABET_SIZE; ++c0) {
+  for(c0 = 0, i = 0, j = 0; c0 < static_cast<saint_t>(ALPHABET_SIZE); ++c0) {
     t = i + BUCKET_A(c0);
     BUCKET_A(c0) = i + j; /* start point */
     i = t + BUCKET_B(c0, c0);
-    for(c1 = c0 + 1; c1 < ALPHABET_SIZE; ++c1) {
+    for(c1 = c0 + 1; c1 < static_cast<saint_t>(ALPHABET_SIZE); ++c1) {
       j += BUCKET_BSTAR(c0, c1);
       BUCKET_BSTAR(c0, c1) = j; /* end point */
       i += BUCKET_B(c0, c1);
diff --git a/device/BUILD.gn b/device/BUILD.gn
index 7edaa88..fa1385f 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -106,6 +106,13 @@
     "test/run_all_unittests.cc",
   ]
 
+  if (is_fuchsia) {
+    sources += [
+      "bluetooth/test/bluetooth_test_fuchsia.cc",
+      "bluetooth/test/bluetooth_test_fuchsia.h",
+    ]
+  }
+
   deps = [
     "//base/test:test_support",
     "//base/third_party/dynamic_annotations:dynamic_annotations",
diff --git a/device/bluetooth/bluetooth_adapter_unittest.cc b/device/bluetooth/bluetooth_adapter_unittest.cc
index 274ca34..25c7256 100644
--- a/device/bluetooth/bluetooth_adapter_unittest.cc
+++ b/device/bluetooth/bluetooth_adapter_unittest.cc
@@ -39,6 +39,8 @@
 #include "device/bluetooth/test/bluetooth_test_cast.h"
 #elif defined(OS_CHROMEOS) || defined(OS_LINUX)
 #include "device/bluetooth/test/bluetooth_test_bluez.h"
+#elif defined(OS_FUCHSIA)
+#include "device/bluetooth/test/bluetooth_test_fuchsia.h"
 #endif
 
 using device::BluetoothDevice;
diff --git a/device/bluetooth/bluetooth_device_unittest.cc b/device/bluetooth/bluetooth_device_unittest.cc
index a443aef..44629ddc 100644
--- a/device/bluetooth/bluetooth_device_unittest.cc
+++ b/device/bluetooth/bluetooth_device_unittest.cc
@@ -26,6 +26,8 @@
 #include "device/bluetooth/test/bluetooth_test_cast.h"
 #elif defined(OS_CHROMEOS) || defined(OS_LINUX)
 #include "device/bluetooth/test/bluetooth_test_bluez.h"
+#elif defined(OS_FUCHSIA)
+#include "device/bluetooth/test/bluetooth_test_fuchsia.h"
 #endif
 
 namespace device {
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc b/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
index e6c286b..0f388cd 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
@@ -25,6 +25,8 @@
 #include "device/bluetooth/test/bluetooth_test_cast.h"
 #elif defined(OS_CHROMEOS) || defined(OS_LINUX)
 #include "device/bluetooth/test/bluetooth_test_bluez.h"
+#elif defined(OS_FUCHSIA)
+#include "device/bluetooth/test/bluetooth_test_fuchsia.h"
 #endif
 
 namespace device {
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc b/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc
index 3c3e5d0..95f3d22 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc
@@ -18,6 +18,8 @@
 #include "device/bluetooth/test/bluetooth_test_cast.h"
 #elif defined(OS_CHROMEOS) || defined(OS_LINUX)
 #include "device/bluetooth/test/bluetooth_test_bluez.h"
+#elif defined(OS_FUCHSIA)
+#include "device/bluetooth/test/bluetooth_test_fuchsia.h"
 #endif
 
 namespace device {
diff --git a/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc b/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc
index 9574c4dd..9969ed5 100644
--- a/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc
@@ -19,6 +19,8 @@
 #include "device/bluetooth/test/bluetooth_test_cast.h"
 #elif defined(OS_CHROMEOS) || defined(OS_LINUX)
 #include "device/bluetooth/test/bluetooth_test_bluez.h"
+#elif defined(OS_FUCHSIA)
+#include "device/bluetooth/test/bluetooth_test_fuchsia.h"
 #endif
 
 namespace device {
diff --git a/device/bluetooth/test/bluetooth_gatt_server_test.h b/device/bluetooth/test/bluetooth_gatt_server_test.h
index c2ddc972..4b741db 100644
--- a/device/bluetooth/test/bluetooth_gatt_server_test.h
+++ b/device/bluetooth/test/bluetooth_gatt_server_test.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
+#include "build/build_config.h"
 #include "device/bluetooth/bluetooth_local_gatt_service.h"
 
 #if defined(OS_ANDROID)
@@ -22,6 +23,8 @@
 #include "device/bluetooth/test/bluetooth_test_cast.h"
 #elif defined(OS_CHROMEOS) || defined(OS_LINUX)
 #include "device/bluetooth/test/bluetooth_test_bluez.h"
+#elif defined(OS_FUCHSIA)
+#include "device/bluetooth/test/bluetooth_test_fuchsia.h"
 #endif
 
 namespace device {
diff --git a/device/bluetooth/test/bluetooth_test_fuchsia.cc b/device/bluetooth/test/bluetooth_test_fuchsia.cc
new file mode 100644
index 0000000..60f75a7
--- /dev/null
+++ b/device/bluetooth/test/bluetooth_test_fuchsia.cc
@@ -0,0 +1,17 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/test/bluetooth_test_fuchsia.h"
+
+namespace device {
+
+BluetoothTestFuchsia::BluetoothTestFuchsia() = default;
+
+BluetoothTestFuchsia::~BluetoothTestFuchsia() = default;
+
+bool BluetoothTestFuchsia::PlatformSupportsLowEnergy() {
+  return true;
+}
+
+}  // namespace device
diff --git a/device/bluetooth/test/bluetooth_test_fuchsia.h b/device/bluetooth/test/bluetooth_test_fuchsia.h
new file mode 100644
index 0000000..04b9e09
--- /dev/null
+++ b/device/bluetooth/test/bluetooth_test_fuchsia.h
@@ -0,0 +1,26 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_TEST_BLUETOOTH_TEST_FUCHSIA_H_
+#define DEVICE_BLUETOOTH_TEST_BLUETOOTH_TEST_FUCHSIA_H_
+
+#include "device/bluetooth/test/bluetooth_test.h"
+
+namespace device {
+
+class BluetoothTestFuchsia : public BluetoothTestBase {
+ public:
+  BluetoothTestFuchsia();
+  ~BluetoothTestFuchsia() override;
+
+  // BluetoothTestBase overrides:
+  bool PlatformSupportsLowEnergy() override;
+};
+
+// Defines common test fixture name. Use TEST_F(BluetoothTest, YourTestName).
+using BluetoothTest = BluetoothTestFuchsia;
+
+}  // namespace device
+
+#endif  // DEVICE_BLUETOOTH_TEST_BLUETOOTH_TEST_FUCHSIA_H_
diff --git a/device/fido/ble/fido_ble_connection_unittest.cc b/device/fido/ble/fido_ble_connection_unittest.cc
index 507297fb..e04c24f 100644
--- a/device/fido/ble/fido_ble_connection_unittest.cc
+++ b/device/fido/ble/fido_ble_connection_unittest.cc
@@ -36,6 +36,8 @@
 #include "device/bluetooth/test/bluetooth_test_win.h"
 #elif defined(OS_CHROMEOS) || defined(OS_LINUX)
 #include "device/bluetooth/test/bluetooth_test_bluez.h"
+#elif defined(OS_FUCHSIA)
+#include "device/bluetooth/test/bluetooth_test_fuchsia.h"
 #endif
 
 namespace device {
diff --git a/device/fido/ble/fido_ble_discovery.cc b/device/fido/ble/fido_ble_discovery.cc
index 4a6ade3..36e8e95 100644
--- a/device/fido/ble/fido_ble_discovery.cc
+++ b/device/fido/ble/fido_ble_discovery.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/time/time.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_common.h"
 #include "device/bluetooth/bluetooth_discovery_session.h"
@@ -15,6 +16,7 @@
 #include "device/fido/ble/fido_ble_device.h"
 #include "device/fido/ble/fido_ble_uuids.h"
 #include "device/fido/fido_authenticator.h"
+#include "device/fido/fido_constants.h"
 #include "device/fido/fido_device_authenticator.h"
 
 namespace device {
@@ -38,9 +40,11 @@
   for (BluetoothDevice* device : adapter()->GetDevices()) {
     if (!CheckForExcludedDeviceAndCacheAddress(device) &&
         base::ContainsKey(device->GetUUIDs(), FidoServiceUUID())) {
-      VLOG(2) << "U2F BLE device: " << device->GetAddress();
-      AddDevice(
-          std::make_unique<FidoBleDevice>(adapter(), device->GetAddress()));
+      const auto& device_address = device->GetAddress();
+      VLOG(2) << "FIDO BLE device: " << device_address;
+      AddDevice(std::make_unique<FidoBleDevice>(adapter(), device_address));
+      CheckAndRecordDevicePairingModeOnDiscovery(
+          FidoBleDevice::GetId(device_address));
     }
   }
 
@@ -62,8 +66,11 @@
                                    BluetoothDevice* device) {
   if (!CheckForExcludedDeviceAndCacheAddress(device) &&
       base::ContainsKey(device->GetUUIDs(), FidoServiceUUID())) {
-    VLOG(2) << "Discovered U2F BLE device: " << device->GetAddress();
-    AddDevice(std::make_unique<FidoBleDevice>(adapter, device->GetAddress()));
+    const auto& device_address = device->GetAddress();
+    VLOG(2) << "Discovered FIDO BLE device: " << device_address;
+    AddDevice(std::make_unique<FidoBleDevice>(adapter, device_address));
+    CheckAndRecordDevicePairingModeOnDiscovery(
+        FidoBleDevice::GetId(device_address));
   }
 }
 
@@ -74,29 +81,29 @@
     return;
   }
 
-  const auto device_id = FidoBleDevice::GetId(device->GetAddress());
-  auto* authenticator = GetAuthenticator(device_id);
+  auto authenticator_id = FidoBleDevice::GetId(device->GetAddress());
+  auto* authenticator = GetAuthenticator(authenticator_id);
   if (!authenticator) {
-    VLOG(2) << "Discovered U2F service on existing BLE device: "
+    VLOG(2) << "Discovered FIDO service on existing BLE device: "
             << device->GetAddress();
     AddDevice(std::make_unique<FidoBleDevice>(adapter, device->GetAddress()));
+    CheckAndRecordDevicePairingModeOnDiscovery(std::move(authenticator_id));
     return;
   }
 
-  // Our model of FIDO BLE security key assumes that if BLE device is in pairing
-  // mode long enough time without pairing attempt, the device stops advertising
-  // and BluetoothAdapter::DeviceRemoved() is invoked instead of returning back
-  // to regular "non-pairing" mode. As so, we only notify observer when
-  // |fido_device| goes into pairing mode.
-  if (observer() && authenticator->device()->IsInPairingMode())
-    observer()->AuthenticatorPairingModeChanged(this, device_id);
+  if (authenticator->device()->IsInPairingMode()) {
+    RecordDevicePairingStatus(std::move(authenticator_id),
+                              PairingModeChangeType::kUnobserved);
+  }
 }
 
 void FidoBleDiscovery::DeviceRemoved(BluetoothAdapter* adapter,
                                      BluetoothDevice* device) {
   if (base::ContainsKey(device->GetUUIDs(), FidoServiceUUID())) {
-    VLOG(2) << "U2F BLE device removed: " << device->GetAddress();
-    RemoveDevice(FidoBleDevice::GetId(device->GetAddress()));
+    VLOG(2) << "FIDO BLE device removed: " << device->GetAddress();
+    auto device_id = FidoBleDevice::GetId(device->GetAddress());
+    RemoveDevice(device_id);
+    RemoveDeviceFromPairingTracker(device_id);
   }
 }
 
@@ -120,8 +127,17 @@
 
   VLOG(2) << "Discovered FIDO BLE device address change from old address : "
           << old_address << " to new address : " << device->GetAddress();
-  authenticators_.emplace(new_device_id, std::move(it->second));
-  authenticators_.erase(it);
+
+  auto change_map_keys = [&](auto* map) {
+    auto it = map->find(previous_device_id);
+    if (it != map->end()) {
+      map->emplace(new_device_id, std::move(it->second));
+      map->erase(it);
+    }
+  };
+
+  change_map_keys(&authenticators_);
+  change_map_keys(&pairing_mode_device_tracker_);
 
   if (observer()) {
     observer()->AuthenticatorIdChanged(this, previous_device_id,
@@ -151,4 +167,46 @@
   return false;
 }
 
+void FidoBleDiscovery::CheckAndRecordDevicePairingModeOnDiscovery(
+    std::string authenticator_id) {
+  auto* authenticator = GetAuthenticator(authenticator_id);
+  DCHECK(authenticator);
+  if (authenticator->device()->IsInPairingMode()) {
+    RecordDevicePairingStatus(std::move(authenticator_id),
+                              PairingModeChangeType::kObserved);
+  }
+}
+
+void FidoBleDiscovery::RecordDevicePairingStatus(std::string device_id,
+                                                 PairingModeChangeType type) {
+  auto it = pairing_mode_device_tracker_.find(device_id);
+  if (it != pairing_mode_device_tracker_.end()) {
+    it->second->Reset();
+    return;
+  }
+
+  if (observer() && type == PairingModeChangeType::kUnobserved) {
+    observer()->AuthenticatorPairingModeChanged(this, device_id,
+                                                true /* is_in_pairing_mode */);
+  }
+
+  auto pairing_mode_timer = std::make_unique<base::OneShotTimer>();
+  pairing_mode_timer->Start(
+      FROM_HERE, kBleDevicePairingModeWaitingInterval,
+      base::BindOnce(&FidoBleDiscovery::RemoveDeviceFromPairingTracker,
+                     weak_factory_.GetWeakPtr(), device_id));
+  pairing_mode_device_tracker_.emplace(std::move(device_id),
+                                       std::move(pairing_mode_timer));
+}
+
+void FidoBleDiscovery::RemoveDeviceFromPairingTracker(
+    const std::string& device_id) {
+  // Destroying the timer stops the timer scheduled task.
+  pairing_mode_device_tracker_.erase(device_id);
+  if (observer()) {
+    observer()->AuthenticatorPairingModeChanged(this, device_id,
+                                                false /* is_in_pairing_mode */);
+  }
+}
+
 }  // namespace device
diff --git a/device/fido/ble/fido_ble_discovery.h b/device/fido/ble/fido_ble_discovery.h
index 0b03b86..f786fc0 100644
--- a/device/fido/ble/fido_ble_discovery.h
+++ b/device/fido/ble/fido_ble_discovery.h
@@ -5,6 +5,8 @@
 #ifndef DEVICE_FIDO_BLE_FIDO_BLE_DISCOVERY_H_
 #define DEVICE_FIDO_BLE_FIDO_BLE_DISCOVERY_H_
 
+#include <functional>
+#include <map>
 #include <memory>
 #include <set>
 #include <string>
@@ -12,6 +14,7 @@
 #include "base/component_export.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
 #include "device/fido/ble/fido_ble_discovery_base.h"
 
 namespace device {
@@ -26,6 +29,16 @@
   ~FidoBleDiscovery() override;
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(FidoBleDiscoveryTest,
+                           DiscoveryNotifiesObserverWhenDeviceInPairingMode);
+  FRIEND_TEST_ALL_PREFIXES(FidoBleDiscoveryTest,
+                           DiscoveryNotifiesObserverWhenDeviceInNonPairingMode);
+
+  enum class PairingModeChangeType {
+    kUnobserved,
+    kObserved,
+  };
+
   static const BluetoothUUID& FidoServiceUUID();
 
   // FidoBleDiscoveryBase:
@@ -46,7 +59,20 @@
   // to |blacklisted_cable_device_addresses_|.
   bool CheckForExcludedDeviceAndCacheAddress(const BluetoothDevice* device);
 
+  void CheckAndRecordDevicePairingModeOnDiscovery(std::string authenticator_id);
+
+  // If |device_id| does not exist in |pairing_mode_device_tracker_|, add
+  // |device_id| to the map and start a timer. If the map element already
+  // exists, restart the timer.
+  void RecordDevicePairingStatus(std::string device_id,
+                                 PairingModeChangeType type);
+  void RemoveDeviceFromPairingTracker(const std::string& device_id);
+
   std::set<std::string> excluded_cable_device_addresses_;
+
+  // Maps Bluetooth FIDO authenticators that are known to be in pairing mode.
+  std::map<std::string, std::unique_ptr<base::OneShotTimer>>
+      pairing_mode_device_tracker_;
   base::WeakPtrFactory<FidoBleDiscovery> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(FidoBleDiscovery);
diff --git a/device/fido/ble/fido_ble_discovery_unittest.cc b/device/fido/ble/fido_ble_discovery_unittest.cc
index 48533cc..1b62da3 100644
--- a/device/fido/ble/fido_ble_discovery_unittest.cc
+++ b/device/fido/ble/fido_ble_discovery_unittest.cc
@@ -9,6 +9,8 @@
 
 #include "base/bind.h"
 #include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/scoped_task_environment.h"
 #include "build/build_config.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/test/bluetooth_test.h"
@@ -36,7 +38,9 @@
 namespace {
 
 using ::testing::_;
+using ::testing::Return;
 using TestMockDevice = ::testing::NiceMock<MockBluetoothDevice>;
+using NiceMockBluetoothAdapter = ::testing::NiceMock<MockBluetoothAdapter>;
 
 constexpr char kDeviceName[] = "device_name";
 constexpr char kDeviceAddress[] = "device_address";
@@ -54,48 +58,90 @@
 
 }  // namespace
 
-TEST_F(BluetoothTest, FidoBleDiscoveryNotifyObserverWhenAdapterNotPresent) {
-  FidoBleDiscovery discovery;
-  MockFidoDiscoveryObserver observer;
-  discovery.set_observer(&observer);
-  auto mock_adapter =
-      base::MakeRefCounted<::testing::NiceMock<MockBluetoothAdapter>>();
-  EXPECT_CALL(*mock_adapter, IsPresent()).WillOnce(::testing::Return(false));
-  EXPECT_CALL(*mock_adapter, SetPowered).Times(0);
-  BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
-  EXPECT_CALL(observer, DiscoveryStarted(&discovery, false));
-  discovery.Start();
+class FidoBleDiscoveryTest : public ::testing::Test {
+ public:
+  FidoBleDiscoveryTest() { discovery_.set_observer(&observer_); }
+
+  std::unique_ptr<TestMockDevice> CreateMockFidoDevice() {
+    DCHECK(adapter_);
+    auto mock_device = std::make_unique<TestMockDevice>(
+        adapter_.get(), 0 /* bluetooth_class */, kDeviceName, kDeviceAddress,
+        false /* paired */, false /* connected */);
+    EXPECT_CALL(*mock_device, GetUUIDs)
+        .WillRepeatedly(Return(
+            std::vector<BluetoothUUID>{BluetoothUUID(kFidoServiceUUID)}));
+    EXPECT_CALL(*mock_device, GetAddress)
+        .WillRepeatedly(Return(kDeviceAddress));
+
+    EXPECT_CALL(*adapter(), GetDevice(kDeviceAddress))
+        .WillRepeatedly(Return(mock_device.get()));
+
+    return mock_device;
+  }
+
+  void SetDeviceInPairingMode(TestMockDevice* device) {
+    // Update device advertisement data so that it represents BLE device in
+    // pairing mode.
+    DCHECK(adapter_);
+    device->UpdateAdvertisementData(
+        0 /* rssi */, 1 << kLeLimitedDiscoverableModeBit,
+        std::vector<BluetoothUUID>{BluetoothUUID(kFidoServiceUUID)},
+        base::nullopt /* tx_power */, BluetoothDevice::ServiceDataMap(),
+        BluetoothDevice::ManufacturerDataMap());
+    adapter_->NotifyDeviceChanged(device);
+  }
+
+  void SetMockBluetoothAdapter() {
+    adapter_ = base::MakeRefCounted<NiceMockBluetoothAdapter>();
+    BluetoothAdapterFactory::SetAdapterForTesting(adapter_);
+  }
+
+  FidoBleDiscovery* discovery() { return &discovery_; }
+  MockFidoDiscoveryObserver* observer() { return &observer_; }
+  MockBluetoothAdapter* adapter() {
+    DCHECK(adapter_);
+    return adapter_.get();
+  }
+
+ protected:
+  base::test::ScopedTaskEnvironment scoped_task_environment_{
+      base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
+
+ private:
+  FidoBleDiscovery discovery_;
+  MockFidoDiscoveryObserver observer_;
+  scoped_refptr<MockBluetoothAdapter> adapter_;
+};
+
+TEST_F(FidoBleDiscoveryTest,
+       FidoBleDiscoveryNotifyObserverWhenAdapterNotPresent) {
+  SetMockBluetoothAdapter();
+  EXPECT_CALL(*adapter(), IsPresent()).WillOnce(Return(false));
+  EXPECT_CALL(*adapter(), SetPowered).Times(0);
+  EXPECT_CALL(*observer(), DiscoveryStarted(discovery(), false));
+  discovery()->Start();
 }
 
-TEST_F(BluetoothTest, FidoBleDiscoveryResumeScanningAfterPoweredOn) {
-  FidoBleDiscovery discovery;
-  MockFidoDiscoveryObserver observer;
-  discovery.set_observer(&observer);
-  auto mock_adapter =
-      base::MakeRefCounted<::testing::NiceMock<MockBluetoothAdapter>>();
-  EXPECT_CALL(*mock_adapter, IsPresent()).WillOnce(::testing::Return(true));
-  EXPECT_CALL(*mock_adapter, IsPowered()).WillOnce(::testing::Return(false));
+TEST_F(FidoBleDiscoveryTest, FidoBleDiscoveryResumeScanningAfterPoweredOn) {
+  SetMockBluetoothAdapter();
+  EXPECT_CALL(*adapter(), IsPresent()).WillOnce(Return(true));
+  EXPECT_CALL(*adapter(), IsPowered()).WillOnce(Return(false));
 
   // After BluetoothAdapter is powered on, we expect that discovery session
   // starts again.
-  EXPECT_CALL(*mock_adapter, StartDiscoverySessionWithFilterRaw);
-  BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
-  discovery.Start();
-  mock_adapter->NotifyAdapterPoweredChanged(true);
+  EXPECT_CALL(*adapter(), StartDiscoverySessionWithFilterRaw);
+  discovery()->Start();
+  adapter()->NotifyAdapterPoweredChanged(true);
 }
 
-TEST_F(BluetoothTest, FidoBleDiscoveryNoAdapter) {
+TEST_F(FidoBleDiscoveryTest, FidoBleDiscoveryNoAdapter) {
   // We purposefully construct a temporary and provide no fake adapter,
   // simulating cases where the discovery is destroyed before obtaining a handle
   // to an adapter. This should be handled gracefully and not result in a crash.
-  FidoBleDiscovery discovery;
-
   // We don't expect any calls to the notification methods.
-  MockFidoDiscoveryObserver observer;
-  discovery.set_observer(&observer);
-  EXPECT_CALL(observer, DiscoveryStarted(&discovery, _)).Times(0);
-  EXPECT_CALL(observer, AuthenticatorAdded(&discovery, _)).Times(0);
-  EXPECT_CALL(observer, AuthenticatorRemoved(&discovery, _)).Times(0);
+  EXPECT_CALL(*observer(), DiscoveryStarted(discovery(), _)).Times(0);
+  EXPECT_CALL(*observer(), AuthenticatorAdded(discovery(), _)).Times(0);
+  EXPECT_CALL(*observer(), AuthenticatorRemoved(discovery(), _)).Times(0);
 }
 
 TEST_F(BluetoothTest, FidoBleDiscoveryFindsKnownDevice) {
@@ -250,84 +296,98 @@
   SimulateLowEnergyDevice(7);
 }
 
-TEST_F(BluetoothTest, DiscoveryDoesNotAddDuplicateDeviceOnAddressChanged) {
-  using TestMockDevice = ::testing::NiceMock<MockBluetoothDevice>;
+TEST_F(FidoBleDiscoveryTest,
+       DiscoveryDoesNotAddDuplicateDeviceOnAddressChanged) {
+  SetMockBluetoothAdapter();
+  EXPECT_CALL(*adapter(), IsPresent()).WillOnce(Return(true));
+  auto mock_device = CreateMockFidoDevice();
 
-  MockFidoDiscoveryObserver observer;
-  FidoBleDiscovery discovery;
-  discovery.set_observer(&observer);
+  EXPECT_CALL(*observer(), AuthenticatorIdChanged(discovery(), kAuthenticatorId,
+                                                  kAuthenticatorChangedId));
+  discovery()->Start();
 
-  auto mock_adapter =
-      base::MakeRefCounted<::testing::NiceMock<MockBluetoothAdapter>>();
-  EXPECT_CALL(*mock_adapter, IsPresent()).WillOnce(::testing::Return(true));
-
-  auto mock_device = std::make_unique<TestMockDevice>(
-      mock_adapter.get(), 0 /* bluetooth_class */, kDeviceName, kDeviceAddress,
-      false /* paired */, false /* connected */);
-
-  EXPECT_CALL(*mock_device.get(), GetUUIDs)
-      .WillRepeatedly(::testing::Return(
-          std::vector<BluetoothUUID>{BluetoothUUID(kFidoServiceUUID)}));
-
-  BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter.get());
-  EXPECT_CALL(observer, AuthenticatorIdChanged(&discovery, kAuthenticatorId,
-                                               kAuthenticatorChangedId));
-  discovery.Start();
-
-  EXPECT_CALL(*mock_device.get(), GetAddress)
-      .WillRepeatedly(::testing::Return(kDeviceAddress));
-  mock_adapter->NotifyDeviceChanged(mock_device.get());
+  adapter()->NotifyDeviceChanged(mock_device.get());
   ASSERT_TRUE(::testing::Mock::VerifyAndClearExpectations(mock_device.get()));
 
   EXPECT_CALL(*mock_device.get(), GetAddress)
-      .WillRepeatedly(::testing::Return(kDeviceChangedAddress));
-  for (auto& observer : mock_adapter->GetObservers()) {
-    observer.DeviceAddressChanged(mock_adapter.get(), mock_device.get(),
-                                  kDeviceAddress);
+      .WillRepeatedly(Return(kDeviceChangedAddress));
+  for (auto& observer : adapter()->GetObservers()) {
+    observer.DeviceAddressChanged(adapter(), mock_device.get(), kDeviceAddress);
   }
 
-  mock_adapter->NotifyDeviceChanged(mock_device.get());
+  adapter()->NotifyDeviceChanged(mock_device.get());
 
-  EXPECT_EQ(1u, discovery.GetAuthenticatorsForTesting().size());
-  EXPECT_TRUE(discovery.GetAuthenticatorForTesting(kAuthenticatorChangedId));
+  EXPECT_EQ(1u, discovery()->GetAuthenticatorsForTesting().size());
+  EXPECT_TRUE(discovery()->GetAuthenticatorForTesting(kAuthenticatorChangedId));
 }
 
-TEST_F(BluetoothTest, DiscoveryNotifiesObserverWhenDeviceInPairingMode) {
-  FidoBleDiscovery discovery;
-  MockFidoDiscoveryObserver observer;
-  discovery.set_observer(&observer);
+TEST_F(FidoBleDiscoveryTest, DiscoveryNotifiesObserverWhenDeviceInPairingMode) {
+  SetMockBluetoothAdapter();
+  EXPECT_CALL(*adapter(), IsPresent()).WillOnce(Return(true));
+  auto mock_device = CreateMockFidoDevice();
 
-  auto mock_adapter =
-      base::MakeRefCounted<::testing::NiceMock<MockBluetoothAdapter>>();
-  EXPECT_CALL(*mock_adapter, IsPresent()).WillOnce(::testing::Return(true));
-
-  auto mock_device = std::make_unique<TestMockDevice>(
-      mock_adapter.get(), 0 /* bluetooth_class */, kDeviceName, kDeviceAddress,
-      false /* paired */, false /* connected */);
-  EXPECT_CALL(*mock_device.get(), GetUUIDs)
-      .WillRepeatedly(::testing::Return(
-          std::vector<BluetoothUUID>{BluetoothUUID(kFidoServiceUUID)}));
-  EXPECT_CALL(*mock_adapter, GetDevice(kDeviceAddress))
-      .WillRepeatedly(::testing::Return(mock_device.get()));
-
-  BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter.get());
   const auto device_id = FidoBleDevice::GetId(kDeviceAddress);
-  discovery.Start();
+  discovery()->Start();
 
   ::testing::InSequence sequence;
-  EXPECT_CALL(observer,
-              AuthenticatorAdded(&discovery, IdMatches(kDeviceAddress)));
-  mock_adapter->NotifyDeviceChanged(mock_device.get());
+  EXPECT_CALL(*observer(),
+              AuthenticatorAdded(discovery(), IdMatches(kDeviceAddress)));
+  adapter()->NotifyDeviceChanged(mock_device.get());
 
-  EXPECT_CALL(observer, AuthenticatorPairingModeChanged(&discovery, _));
-  // Update device advertisement data so that it represents BLE device in
-  // pairing mode.
-  mock_device->UpdateAdvertisementData(
-      0 /* rssi */, 1 << kLeLimitedDiscoverableModeBit,
-      std::vector<BluetoothUUID>{BluetoothUUID(kFidoServiceUUID)},
-      base::nullopt /* tx_power */, BluetoothDevice::ServiceDataMap(),
-      BluetoothDevice::ManufacturerDataMap());
-  mock_adapter->NotifyDeviceChanged(mock_device.get());
+  EXPECT_CALL(*observer(),
+              AuthenticatorPairingModeChanged(discovery(), device_id, true));
+  SetDeviceInPairingMode(mock_device.get());
+  auto it = discovery()->pairing_mode_device_tracker_.find(device_id);
+  EXPECT_TRUE(it != discovery()->pairing_mode_device_tracker_.end());
+  EXPECT_TRUE(it->second->IsRunning());
+}
+
+TEST_F(FidoBleDiscoveryTest,
+       DiscoveryNotifiesObserverWhenDeviceInNonPairingMode) {
+  SetMockBluetoothAdapter();
+  EXPECT_CALL(*adapter(), IsPresent()).WillOnce(Return(true));
+  auto mock_device = CreateMockFidoDevice();
+
+  const auto device_id = FidoBleDevice::GetId(kDeviceAddress);
+  discovery()->Start();
+
+  ::testing::InSequence sequence;
+  EXPECT_CALL(*observer(),
+              AuthenticatorAdded(discovery(), IdMatches(kDeviceAddress)));
+  adapter()->NotifyDeviceChanged(mock_device.get());
+
+  EXPECT_CALL(*observer(),
+              AuthenticatorPairingModeChanged(discovery(), device_id, true));
+
+  SetDeviceInPairingMode(mock_device.get());
+  auto it = discovery()->pairing_mode_device_tracker_.find(device_id);
+  ASSERT_TRUE(it != discovery()->pairing_mode_device_tracker_.end());
+  EXPECT_TRUE(it->second->IsRunning());
+
+  // Simulates BLE device sending advertisement packet after some interval.
+  // Since device did not advertise that it is in pairing mode for longer than 3
+  // seconds, we expect that the discovery should
+  //   1) First set the device to be in non-pairing mode and notify the
+  //   observer.
+  //   2) When advertisement packet arrives after delay, device is re-set to
+  //   pairing mode and observer is notified.
+  //   3) When timer completes due to FastForwardUntilNoTasksRemain(),
+  //   authenticator is re-set to non-pairing mode.
+  ASSERT_TRUE(::testing::Mock::VerifyAndClearExpectations(observer()));
+  EXPECT_CALL(*observer(),
+              AuthenticatorPairingModeChanged(discovery(), device_id, false));
+  EXPECT_CALL(*observer(),
+              AuthenticatorPairingModeChanged(discovery(), device_id, true));
+  EXPECT_CALL(*observer(),
+              AuthenticatorPairingModeChanged(discovery(), device_id, false));
+
+  scoped_task_environment_.GetMainThreadTaskRunner().get()->PostDelayedTask(
+      FROM_HERE, base::BindLambdaForTesting([&, this] {
+        adapter()->NotifyDeviceChanged(mock_device.get());
+      }),
+      base::TimeDelta::FromSeconds(4));
+
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
 }
 
 }  // namespace device
diff --git a/device/fido/ble_adapter_manager_unittest.cc b/device/fido/ble_adapter_manager_unittest.cc
index c09e203..eb6ef23 100644
--- a/device/fido/ble_adapter_manager_unittest.cc
+++ b/device/fido/ble_adapter_manager_unittest.cc
@@ -47,7 +47,8 @@
   MOCK_METHOD2(FidoAuthenticatorIdChanged,
                void(base::StringPiece old_authenticator_id,
                     std::string new_authenticator_id));
-  MOCK_METHOD1(FidoAuthenticatorPairingModeChanged, void(base::StringPiece));
+  MOCK_METHOD2(FidoAuthenticatorPairingModeChanged,
+               void(base::StringPiece, bool));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockTransportAvailabilityObserver);
diff --git a/device/fido/fido_constants.cc b/device/fido/fido_constants.cc
index 316ea7c..8d477d20 100644
--- a/device/fido/fido_constants.cc
+++ b/device/fido/fido_constants.cc
@@ -79,4 +79,7 @@
 
 const char kExtensionHmacSecret[] = "hmac-secret";
 
+const base::TimeDelta kBleDevicePairingModeWaitingInterval =
+    base::TimeDelta::FromSeconds(2);
+
 }  // namespace device
diff --git a/device/fido/fido_constants.h b/device/fido/fido_constants.h
index fd6b6df2..ce33ace 100644
--- a/device/fido/fido_constants.h
+++ b/device/fido/fido_constants.h
@@ -375,6 +375,15 @@
 
 COMPONENT_EXPORT(DEVICE_FIDO) extern const char kExtensionHmacSecret[];
 
+// Maximum number of seconds the browser waits for Bluetooth authenticator to
+// send packets that advertises that the device is in pairing mode before
+// setting pairing mode to false. The interval time is set to 2 seconds, which
+// is equivalent to the maximum Bluetooth error wait interval set by the CTAP
+// spec.
+// https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html#BTCORE
+COMPONENT_EXPORT(DEVICE_FIDO)
+extern const base::TimeDelta kBleDevicePairingModeWaitingInterval;
+
 }  // namespace device
 
 #endif  // DEVICE_FIDO_FIDO_CONSTANTS_H_
diff --git a/device/fido/fido_discovery_base.h b/device/fido/fido_discovery_base.h
index 0ba4a62..6fd43084 100644
--- a/device/fido/fido_discovery_base.h
+++ b/device/fido/fido_discovery_base.h
@@ -41,11 +41,11 @@
                                         const std::string& previous_id,
                                         std::string new_id) = 0;
 
-    // Invoked when connected Bluetooth device advertises that it is in pairing
-    // mode.
-    virtual void AuthenticatorPairingModeChanged(
-        FidoDiscoveryBase* discovery,
-        const std::string& device_id) = 0;
+    // Invoked when connected Bluetooth device advertises that its pairing mode
+    // has changed.
+    virtual void AuthenticatorPairingModeChanged(FidoDiscoveryBase* discovery,
+                                                 const std::string& device_id,
+                                                 bool is_in_pairing_mode) = 0;
   };
 
   // Start authenticator discovery. The Observer must have been set before this
diff --git a/device/fido/fido_request_handler_base.cc b/device/fido/fido_request_handler_base.cc
index 6164f84..cfcf332e 100644
--- a/device/fido/fido_request_handler_base.cc
+++ b/device/fido/fido_request_handler_base.cc
@@ -238,14 +238,16 @@
 
 void FidoRequestHandlerBase::AuthenticatorPairingModeChanged(
     FidoDiscoveryBase* discovery,
-    const std::string& device_id) {
+    const std::string& device_id,
+    bool is_in_pairing_mode) {
   DCHECK_EQ(FidoTransportProtocol::kBluetoothLowEnergy, discovery->transport());
   auto it = active_authenticators_.find(device_id);
   if (it == active_authenticators_.end())
     return;
 
   if (observer_)
-    observer_->FidoAuthenticatorPairingModeChanged(device_id);
+    observer_->FidoAuthenticatorPairingModeChanged(device_id,
+                                                   is_in_pairing_mode);
 }
 
 void FidoRequestHandlerBase::AddAuthenticator(
diff --git a/device/fido/fido_request_handler_base.h b/device/fido/fido_request_handler_base.h
index 76a301c..77525e8 100644
--- a/device/fido/fido_request_handler_base.h
+++ b/device/fido/fido_request_handler_base.h
@@ -117,7 +117,8 @@
         base::StringPiece old_authenticator_id,
         std::string new_authenticator_id) = 0;
     virtual void FidoAuthenticatorPairingModeChanged(
-        base::StringPiece authenticator_id) = 0;
+        base::StringPiece authenticator_id,
+        bool is_in_pairing_mode) = 0;
   };
 
   // TODO(https://crbug.com/769631): Remove the dependency on Connector once
@@ -205,7 +206,8 @@
                               const std::string& previous_id,
                               std::string new_id) final;
   void AuthenticatorPairingModeChanged(FidoDiscoveryBase* discovery,
-                                       const std::string& device_id) final;
+                                       const std::string& device_id,
+                                       bool is_in_pairing_mode) final;
 
   void AddAuthenticator(FidoAuthenticator* authenticator);
   void NotifyObserverTransportAvailability();
diff --git a/device/fido/fido_request_handler_unittest.cc b/device/fido/fido_request_handler_unittest.cc
index aea5531..0c6f3d4 100644
--- a/device/fido/fido_request_handler_unittest.cc
+++ b/device/fido/fido_request_handler_unittest.cc
@@ -104,8 +104,8 @@
     authenticator_id_change_notification_receiver_.callback().Run(
         std::move(new_authenticator_id));
   }
-  void FidoAuthenticatorPairingModeChanged(
-      base::StringPiece authenticator_id) override {}
+  void FidoAuthenticatorPairingModeChanged(base::StringPiece authenticator_id,
+                                           bool is_in_pairing_mode) override {}
 
  private:
   TransportAvailabilityNotificationReceiver
diff --git a/device/fido/mock_fido_discovery_observer.h b/device/fido/mock_fido_discovery_observer.h
index 883b20a..ce02218 100644
--- a/device/fido/mock_fido_discovery_observer.h
+++ b/device/fido/mock_fido_discovery_observer.h
@@ -29,8 +29,8 @@
                void(FidoDiscoveryBase*, FidoAuthenticator*));
   MOCK_METHOD3(AuthenticatorIdChanged,
                void(FidoDiscoveryBase*, const std::string&, std::string));
-  MOCK_METHOD2(AuthenticatorPairingModeChanged,
-               void(FidoDiscoveryBase*, const std::string&));
+  MOCK_METHOD3(AuthenticatorPairingModeChanged,
+               void(FidoDiscoveryBase*, const std::string&, bool));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockFidoDiscoveryObserver);
diff --git a/docs/accessibility/tests.md b/docs/accessibility/tests.md
index 85439633..58a2251 100644
--- a/docs/accessibility/tests.md
+++ b/docs/accessibility/tests.md
@@ -111,8 +111,9 @@
 ### Select-To-Speak tests
 
 ```
-autoninja -C out/release unit_tests
-out/release/unit_tests --gtest_filter="*SelectToSpeak*"
+autoninja -C out/release unit_tests browser_tests
+out/release/unit_tests --gtest_filter=*SelectToSpeak*
+out/release/browser_tests --gtest_filter=*SelectToSpeak*
 ```
 
 ## Performance tests
diff --git a/extensions/browser/api/cast_channel/OWNERS b/extensions/browser/api/cast_channel/OWNERS
index 3590d52..1be33ff6 100644
--- a/extensions/browser/api/cast_channel/OWNERS
+++ b/extensions/browser/api/cast_channel/OWNERS
@@ -1,4 +1,3 @@
-imcheng@chromium.org
-mfoltz@chromium.org
+file://components/cast_channel/OWNERS
 
 # COMPONENT: Internals>Cast>API
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc
index 9a901b4f..08aba91 100644
--- a/extensions/browser/api/web_request/web_request_api.cc
+++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -520,10 +520,12 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   Proxy* proxy = GetProxyFromRequestId(request_id);
   if (!proxy) {
-    // No proxy found, so the request must already be dead.
+    // The request=>proxy map is maintained on the IO thread so it is not an
+    // error to get here and have no proxy. In this situation run the |callback|
+    // which will display a dialog for the user to enter their auth credentials.
     base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
                              base::BindOnce(std::move(callback), base::nullopt,
-                                            true /* should_cancel */));
+                                            false /* should_cancel */));
     return;
   }
 
diff --git a/gin/test/v8_test.cc b/gin/test/v8_test.cc
index e234f2c3..bb5b7f9 100644
--- a/gin/test/v8_test.cc
+++ b/gin/test/v8_test.cc
@@ -15,7 +15,9 @@
 
 namespace gin {
 
-V8Test::V8Test() = default;
+V8Test::V8Test()
+    : scoped_task_environment_(
+          base::test::ScopedTaskEnvironment::MainThreadType::IO) {}
 
 V8Test::~V8Test() = default;
 
diff --git a/gpu/command_buffer/service/context_group.h b/gpu/command_buffer/service/context_group.h
index 1820057..10cbba3 100644
--- a/gpu/command_buffer/service/context_group.h
+++ b/gpu/command_buffer/service/context_group.h
@@ -38,6 +38,7 @@
 class ServiceDiscardableManager;
 class PassthroughDiscardableManager;
 class DecoderContext;
+class MemoryTracker;
 
 namespace gles2 {
 
@@ -50,7 +51,6 @@
 class SamplerManager;
 class ShaderManager;
 class TextureManager;
-class MemoryTracker;
 struct DisallowedFeatures;
 struct PassthroughResources;
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
index 44dbaf61..b902c14 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
@@ -46,9 +46,9 @@
 #include "ui/gl/gl_version_info.h"
 
 namespace gpu {
-namespace gles2 {
-
 class MemoryTracker;
+
+namespace gles2 {
 class MockCopyTextureResourceManager;
 
 class GLES2DecoderTestBase : public ::testing::TestWithParam<bool>,
diff --git a/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.h b/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.h
index 3cc9375..d40df9d 100644
--- a/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.h
+++ b/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.h
@@ -17,8 +17,7 @@
 
 // MemoryTracker implementation that also handles recording UMA histograms for
 // periodic collection, on shutdown and on memory pressure.
-class GPU_GLES2_EXPORT GpuCommandBufferMemoryTracker
-    : public gles2::MemoryTracker {
+class GPU_GLES2_EXPORT GpuCommandBufferMemoryTracker : public MemoryTracker {
  public:
   GpuCommandBufferMemoryTracker(
       int client_id,
@@ -28,7 +27,7 @@
       scoped_refptr<base::SingleThreadTaskRunner> task_runner);
   ~GpuCommandBufferMemoryTracker() override;
 
-  // gles2::MemoryTracker implementation.
+  // MemoryTracker implementation.
   void TrackMemoryAllocatedChange(uint64_t delta) override;
   uint64_t GetSize() const override;
   uint64_t ClientTracingId() const override;
diff --git a/gpu/command_buffer/service/memory_tracking.h b/gpu/command_buffer/service/memory_tracking.h
index 740d018..05dcc9e 100644
--- a/gpu/command_buffer/service/memory_tracking.h
+++ b/gpu/command_buffer/service/memory_tracking.h
@@ -15,7 +15,6 @@
 #include "base/trace_event/trace_event.h"
 
 namespace gpu {
-namespace gles2 {
 
 // A MemoryTracker is used to propagate per-ContextGroup memory usage
 // statistics to the global GpuMemoryManager.
@@ -70,7 +69,6 @@
   DISALLOW_COPY_AND_ASSIGN(MemoryTypeTracker);
 };
 
-}  // namespace gles2
 }  // namespace gpu
 
 #endif  // GPU_COMMAND_BUFFER_SERVICE_MEMORY_TRACKING_H_
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index 63c5638..9cfd90b 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -516,7 +516,7 @@
     return feature_info_->gl_version_info();
   }
 
-  gles2::MemoryTracker* memory_tracker() { return group_->memory_tracker(); }
+  MemoryTracker* memory_tracker() { return group_->memory_tracker(); }
 
   gles2::VertexArrayManager* vertex_array_manager() {
     return vertex_array_manager_.get();
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc
index 3abb134..748d1a3 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc
@@ -7,16 +7,41 @@
 #include "base/android/android_hardware_buffer_compat.h"
 #include "base/android/scoped_hardware_buffer_handle.h"
 #include "base/logging.h"
+#include "components/viz/common/resources/resource_format_utils.h"
+#include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/ahardwarebuffer_utils.h"
+#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
 #include "gpu/command_buffer/service/mailbox_manager.h"
+#include "gpu/command_buffer/service/memory_tracking.h"
 #include "gpu/command_buffer/service/shared_image_backing.h"
-#include "gpu/config/gpu_driver_bug_workarounds.h"
+#include "gpu/command_buffer/service/shared_image_representation.h"
+#include "gpu/command_buffer/service/texture_manager.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/size.h"
-#include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_gl_api_implementation.h"
+#include "ui/gl/gl_image_ahardwarebuffer.h"
+#include "ui/gl/gl_version_info.h"
 
 namespace gpu {
 
+// Representation of a SharedImageBackingAHardwareBuffer as a GL Texture.
+class SharedImageRepresentationGLTextureAHardwareBuffer
+    : public SharedImageRepresentationGLTexture {
+ public:
+  SharedImageRepresentationGLTextureAHardwareBuffer(SharedImageManager* manager,
+                                                    SharedImageBacking* backing,
+                                                    gles2::Texture* texture)
+      : SharedImageRepresentationGLTexture(manager, backing),
+        texture_(texture) {}
+
+  gles2::Texture* GetTexture() override { return texture_; }
+
+ private:
+  gles2::Texture* texture_;
+
+  DISALLOW_COPY_AND_ASSIGN(SharedImageRepresentationGLTextureAHardwareBuffer);
+};
+
 // Implementation of SharedImageBacking that holds an AHardwareBuffer. This
 // can be used to create a GL texture or a VK Image from the AHardwareBuffer
 // backing.
@@ -28,9 +53,11 @@
       const gfx::Size& size,
       const gfx::ColorSpace& color_space,
       uint32_t usage,
-      base::android::ScopedHardwareBufferHandle handle)
+      base::android::ScopedHardwareBufferHandle handle,
+      MemoryTypeTracker* tracker)
       : SharedImageBacking(mailbox, format, size, color_space, usage),
-        hardware_buffer_handle_(std::move(handle)) {
+        hardware_buffer_handle_(std::move(handle)),
+        memory_tracker_(tracker) {
     DCHECK(hardware_buffer_handle_.is_valid());
   }
 
@@ -38,27 +65,122 @@
     // Check to make sure buffer is explicitly destroyed using Destroy() api
     // before this destructor is called.
     DCHECK(!hardware_buffer_handle_.is_valid());
+    DCHECK(!texture_);
   }
 
-  bool IsCleared() const override { return is_cleared_; }
+  bool IsCleared() const override {
+    if (texture_)
+      return texture_->IsLevelCleared(texture_->target(), 0);
+    return is_cleared_;
+  }
 
-  void SetCleared() override { is_cleared_ = true; }
+  void SetCleared() override {
+    if (texture_)
+      texture_->SetLevelCleared(texture_->target(), 0, true);
+    is_cleared_ = true;
+  }
 
   bool ProduceLegacyMailbox(MailboxManager* mailbox_manager) override {
     DCHECK(hardware_buffer_handle_.is_valid());
-
-    // Legacy mailbox is not supported.
-    return false;
+    if (!GenGLTexture())
+      return false;
+    DCHECK(texture_);
+    mailbox_manager->ProduceTexture(mailbox(), texture_);
+    return true;
   }
 
   void Destroy() override {
     DCHECK(hardware_buffer_handle_.is_valid());
+    if (texture_) {
+      texture_->RemoveLightweightRef(have_context());
+      texture_ = nullptr;
+    }
     hardware_buffer_handle_.reset();
   }
 
+ protected:
+  std::unique_ptr<SharedImageRepresentationGLTexture> ProduceGLTexture(
+      SharedImageManager* manager) override {
+    // Use same texture for all the texture representations generated from same
+    // backing.
+    if (!GenGLTexture())
+      return nullptr;
+
+    DCHECK(texture_);
+    return std::make_unique<SharedImageRepresentationGLTextureAHardwareBuffer>(
+        manager, this, texture_);
+  }
+
  private:
+  bool GenGLTexture() {
+    if (texture_)
+      return true;
+
+    DCHECK(hardware_buffer_handle_.is_valid());
+    DCHECK(usage() & SHARED_IMAGE_USAGE_GLES2);
+
+    // Target for AHB backed egl images.
+    GLenum target = GL_TEXTURE_EXTERNAL_OES;
+    GLenum get_target = GL_TEXTURE_BINDING_EXTERNAL_OES;
+
+    // Create a gles2 texture using the AhardwareBuffer.
+    gl::GLApi* api = gl::g_current_gl_context;
+    GLuint service_id = 0;
+    api->glGenTexturesFn(1, &service_id);
+    GLint old_texture_binding = 0;
+    api->glGetIntegervFn(get_target, &old_texture_binding);
+    api->glBindTextureFn(target, service_id);
+    api->glTexParameteriFn(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    api->glTexParameteriFn(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    api->glTexParameteriFn(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    api->glTexParameteriFn(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+    // Create an egl image using AHardwareBuffer.
+    auto egl_image = base::MakeRefCounted<gl::GLImageAHardwareBuffer>(size());
+    if (!egl_image->Initialize(hardware_buffer_handle_.get(), false)) {
+      LOG(ERROR) << "Failed to create EGL image ";
+      api->glBindTextureFn(target, old_texture_binding);
+      api->glDeleteTexturesFn(1, &service_id);
+      return false;
+    }
+    if (!egl_image->BindTexImage(target)) {
+      LOG(ERROR) << "Failed to bind egl image";
+      api->glBindTextureFn(target, old_texture_binding);
+      api->glDeleteTexturesFn(1, &service_id);
+      return false;
+    }
+
+    // Create a gles2 Texture.
+    texture_ = new gles2::Texture(service_id);
+    texture_->SetLightweightRef(memory_tracker_);
+    texture_->SetTarget(target, 1);
+    texture_->sampler_state_.min_filter = GL_LINEAR;
+    texture_->sampler_state_.mag_filter = GL_LINEAR;
+    texture_->sampler_state_.wrap_t = GL_CLAMP_TO_EDGE;
+    texture_->sampler_state_.wrap_s = GL_CLAMP_TO_EDGE;
+
+    // If the backing is already cleared, no need to clear it again.
+    gfx::Rect cleared_rect;
+    if (is_cleared_)
+      cleared_rect = gfx::Rect(size());
+
+    GLenum gl_format = viz::GLDataFormat(format());
+    GLenum gl_type = viz::GLDataType(format());
+    texture_->SetLevelInfo(target, 0, egl_image->GetInternalFormat(),
+                           size().width(), size().height(), 1, 0, gl_format,
+                           gl_type, cleared_rect);
+    texture_->SetLevelImage(target, 0, egl_image.get(), gles2::Texture::BOUND);
+    texture_->SetImmutable(true);
+    api->glBindTextureFn(target, old_texture_binding);
+    return true;
+  }
+
   base::android::ScopedHardwareBufferHandle hardware_buffer_handle_;
 
+  // This texture will be lazily initialised/created when ProduceGLTexture is
+  // called.
+  gles2::Texture* texture_ = nullptr;
+
   // TODO(vikassoni): In future when we add begin/end write support, we will
   // need to properly use this flag to pass the is_cleared_ information to
   // the GL texture representation while begin write and back to this class from
@@ -66,13 +188,66 @@
   // will not know if SetCleared() arrives during begin write happening on GL
   // texture representation.
   bool is_cleared_ = false;
+  MemoryTypeTracker* memory_tracker_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(SharedImageBackingAHardwareBuffer);
 };
 
 SharedImageBackingFactoryAHardwareBuffer::
     SharedImageBackingFactoryAHardwareBuffer(
-        const GpuDriverBugWorkarounds& workarounds) {
+        const GpuDriverBugWorkarounds& workarounds,
+        const GpuFeatureInfo& gpu_feature_info,
+        MemoryTracker* tracker)
+    : memory_tracker_(std::make_unique<MemoryTypeTracker>(tracker)) {
+  scoped_refptr<gles2::FeatureInfo> feature_info =
+      new gles2::FeatureInfo(workarounds, gpu_feature_info);
+  feature_info->Initialize(ContextType::CONTEXT_TYPE_OPENGLES2, false,
+                           gles2::DisallowedFeatures());
+  const gles2::Validators* validators = feature_info->validators();
+  const bool is_egl_image_external_supported =
+      feature_info->feature_flags().oes_egl_image_external;
+
+  // Build the feature info for all the resource formats.
+  for (int i = 0; i <= viz::RESOURCE_FORMAT_MAX; ++i) {
+    auto format = static_cast<viz::ResourceFormat>(i);
+    FormatInfo& info = format_info_[i];
+
+    // If AHB does not support this format, we will not be able to create this
+    // backing.
+    if (!AHardwareBufferSupportedFormat(format))
+      continue;
+    info.ahb_supported = true;
+    info.ahb_format = AHardwareBufferFormat(format);
+
+    // Check if GL_TEXTURE_EXTERNAL_OES texture target is supported. This
+    // texture target is required to use AHB backed EGLImage as a texture.
+    if (!is_egl_image_external_supported)
+      continue;
+
+    // Check if AHB backed GL texture can be created using this format and
+    // gather GL related format info.
+    // TODO(vikassoni): Add vulkan related information in future.
+    GLuint internal_format = viz::GLInternalFormat(format);
+    GLenum gl_format = viz::GLDataFormat(format);
+    GLenum gl_type = viz::GLDataType(format);
+
+    //  GLImageAHardwareBuffer currently supports internal format GL_RGBA only.
+    //  TODO(vikassoni): Pass the AHBuffer format while GLImageAHardwareBuffer
+    //  creation and based on that return the equivalent internal format as
+    //  GL_RGBA or GL_RGB.
+    if (internal_format != GL_RGBA)
+      continue;
+
+    // Validate if GL format, type and internal format is supported.
+    if (validators->texture_internal_format.IsValid(internal_format) &&
+        validators->texture_format.IsValid(gl_format) &&
+        validators->pixel_type.IsValid(gl_type)) {
+      info.gl_supported = true;
+      info.gl_format = gl_format;
+      info.gl_type = gl_type;
+      info.internal_format = internal_format;
+    }
+  }
   // TODO(vikassoni): We are using below GL api calls for now as Vulkan mode
   // doesn't exist. Once we have vulkan support, we shouldn't query GL in this
   // code until we are asked to make a GL representation (or allocate a backing
@@ -103,18 +278,32 @@
     uint32_t usage) {
   DCHECK(base::AndroidHardwareBufferCompat::IsSupportAvailable());
 
+  const FormatInfo& format_info = format_info_[format];
+
   // Check if the format is supported by AHardwareBuffer.
-  // TODO(vikassoni): We need to check if the equivalent GL texture format
-  // and VK image format support is available in the underlying hardware. Else
-  // we might not be able to use this AHB as a gl texture or VK Image.
-  // Additionally we also need to check usage flags to determine the format.
-  if (!AHardwareBufferSupportedFormat(format)) {
+  if (!format_info.ahb_supported) {
     LOG(ERROR) << "viz::ResourceFormat " << format
-               << "not supported by AHardwareBuffer";
+               << " not supported by AHardwareBuffer";
     return nullptr;
   }
 
-  // Check for size restrictions.
+  // TODO(vikassoni): Also check for !gpu_preferences.enable_vulkan and maybe
+  // some other usage flags.
+  const bool use_gles2 = usage & SHARED_IMAGE_USAGE_GLES2;
+
+  // If usage flags indicated this backing can be used as a GL texture, then do
+  // below gl related checks.
+  if (use_gles2) {
+    // Check if the GL texture can be created from AHB with this format.
+    if (!format_info.gl_supported) {
+      LOG(ERROR)
+          << "viz::ResourceFormat " << format
+          << " can not be used to create a GL texture from AHardwareBuffer.";
+      return nullptr;
+    }
+  }
+
+  // Check if AHB can be created with the current size restrictions.
   // TODO(vikassoni): Check for VK size restrictions for VK import, GL size
   // restrictions for GL import OR both if this backing is needed to be used
   // with both GL and VK.
@@ -130,7 +319,7 @@
   AHardwareBuffer_Desc hwb_desc;
   hwb_desc.width = size.width();
   hwb_desc.height = size.height();
-  hwb_desc.format = AHardwareBufferFormat(format);
+  hwb_desc.format = format_info.ahb_format;
 
   // Set usage so that gpu can both read as a texture/write as a framebuffer
   // attachment. TODO(vikassoni): Find out if we need to set some more usage
@@ -155,8 +344,13 @@
 
   auto backing = std::make_unique<SharedImageBackingAHardwareBuffer>(
       mailbox, format, size, color_space, usage,
-      base::android::ScopedHardwareBufferHandle::Adopt(buffer));
+      base::android::ScopedHardwareBufferHandle::Adopt(buffer),
+      memory_tracker_.get());
+
   return backing;
 }
 
+SharedImageBackingFactoryAHardwareBuffer::FormatInfo::FormatInfo() = default;
+SharedImageBackingFactoryAHardwareBuffer::FormatInfo::~FormatInfo() = default;
+
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.h b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.h
index 6a59cda8..e257c45b 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.h
+++ b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.h
@@ -9,6 +9,7 @@
 #include "components/viz/common/resources/resource_format.h"
 #include "gpu/command_buffer/service/shared_image_backing_factory.h"
 #include "gpu/gpu_gles2_export.h"
+#include "ui/gl/gl_bindings.h"
 
 namespace gfx {
 class Size;
@@ -18,7 +19,10 @@
 namespace gpu {
 class SharedImageBacking;
 class GpuDriverBugWorkarounds;
+struct GpuFeatureInfo;
 struct Mailbox;
+class MemoryTracker;
+class MemoryTypeTracker;
 
 // Implementation of SharedImageBackingFactory that produces AHardwareBuffer
 // backed SharedImages. This is meant to be used on Android only.
@@ -26,7 +30,9 @@
     : public SharedImageBackingFactory {
  public:
   SharedImageBackingFactoryAHardwareBuffer(
-      const GpuDriverBugWorkarounds& workarounds);
+      const GpuDriverBugWorkarounds& workarounds,
+      const GpuFeatureInfo& gpu_feature_info,
+      MemoryTracker* tracker);
   ~SharedImageBackingFactoryAHardwareBuffer() override;
 
   // SharedImageBackingFactory implementation.
@@ -38,8 +44,28 @@
       uint32_t usage) override;
 
  private:
+  struct FormatInfo {
+    FormatInfo();
+    ~FormatInfo();
+
+    // Whether this format is supported by AHardwareBuffer.
+    bool ahb_supported = false;
+    unsigned int ahb_format = 0;
+
+    // Whether this format can be used to create a GL texture from the AHB.
+    bool gl_supported = false;
+
+    // GL internal_format/format/type triplet.
+    GLuint internal_format = 0;
+    GLenum gl_format = 0;
+    GLenum gl_type = 0;
+  };
+
+  FormatInfo format_info_[viz::RESOURCE_FORMAT_MAX + 1];
+
   // Used to limit the max size of AHardwareBuffer.
   int32_t max_gl_texture_size_ = 0;
+  std::unique_ptr<MemoryTypeTracker> memory_tracker_;
 
   DISALLOW_COPY_AND_ASSIGN(SharedImageBackingFactoryAHardwareBuffer);
 };
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer_unittest.cc b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer_unittest.cc
index 185a9e46..2ee0e61 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer_unittest.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer_unittest.cc
@@ -9,6 +9,9 @@
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/mailbox_manager_impl.h"
 #include "gpu/command_buffer/service/shared_image_backing.h"
+#include "gpu/command_buffer/service/shared_image_manager.h"
+#include "gpu/command_buffer/service/shared_image_representation.h"
+#include "gpu/command_buffer/service/texture_manager.h"
 #include "gpu/config/gpu_driver_bug_workarounds.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/color_space.h"
@@ -39,7 +42,8 @@
     GpuDriverBugWorkarounds workarounds;
     workarounds.max_texture_size = INT_MAX - 1;
     backing_factory_ =
-        std::make_unique<SharedImageBackingFactoryAHardwareBuffer>(workarounds);
+        std::make_unique<SharedImageBackingFactoryAHardwareBuffer>(
+            workarounds, GpuFeatureInfo(), nullptr);
   }
 
  protected:
@@ -47,6 +51,7 @@
   scoped_refptr<gl::GLContext> context_;
   std::unique_ptr<SharedImageBackingFactoryAHardwareBuffer> backing_factory_;
   gles2::MailboxManagerImpl mailbox_manager_;
+  SharedImageManager shared_image_manager_;
 };
 
 // Basic test to check creation and deletion of AHB backed shared image.
@@ -63,13 +68,44 @@
                                                      color_space, usage);
   EXPECT_TRUE(backing);
 
-  // There is no Legacy mailbox support in AHardwareBuffer Implementation.
-  EXPECT_FALSE(backing->ProduceLegacyMailbox(&mailbox_manager_));
-  TextureBase* texture_base = mailbox_manager_.ConsumeTexture(mailbox);
-  ASSERT_FALSE(texture_base);
+  // Check clearing.
+  if (!backing->IsCleared()) {
+    backing->SetCleared();
+    EXPECT_TRUE(backing->IsCleared());
+  }
 
-  // Destroy the backing resource.
-  backing->Destroy();
+  // First, validate via a legacy mailbox.
+  GLenum expected_target = GL_TEXTURE_EXTERNAL_OES;
+  EXPECT_TRUE(backing->ProduceLegacyMailbox(&mailbox_manager_));
+  TextureBase* texture_base = mailbox_manager_.ConsumeTexture(mailbox);
+
+  // Currently there is no support for passthrough texture on android and hence
+  // in AHB backing. So the TextureBase* should be pointing to a Texture object.
+  auto* texture = gles2::Texture::CheckedCast(texture_base);
+  ASSERT_TRUE(texture);
+  EXPECT_EQ(texture->target(), expected_target);
+  EXPECT_TRUE(texture->IsImmutable());
+  int width, height, depth;
+  bool has_level =
+      texture->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height, &depth);
+  EXPECT_TRUE(has_level);
+  EXPECT_EQ(width, size.width());
+  EXPECT_EQ(height, size.height());
+
+  // Next validate via a SharedImageRepresentationGLTexture.
+  EXPECT_TRUE(shared_image_manager_.Register(std::move(backing)));
+  auto gl_representation = shared_image_manager_.ProduceGLTexture(mailbox);
+  EXPECT_TRUE(gl_representation);
+  EXPECT_TRUE(gl_representation->GetTexture()->service_id());
+  EXPECT_EQ(expected_target, gl_representation->GetTexture()->target());
+  EXPECT_EQ(size, gl_representation->size());
+  EXPECT_EQ(format, gl_representation->format());
+  EXPECT_EQ(color_space, gl_representation->color_space());
+  EXPECT_EQ(usage, gl_representation->usage());
+  gl_representation.reset();
+
+  shared_image_manager_.Unregister(mailbox);
+  EXPECT_FALSE(mailbox_manager_.ConsumeTexture(mailbox));
 }
 
 // Test to check invalid format support.
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
index d9d65d97..70d999f3 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
@@ -282,11 +282,11 @@
     const GpuDriverBugWorkarounds& workarounds,
     const GpuFeatureInfo& gpu_feature_info,
     ImageFactory* image_factory,
-    gles2::MemoryTracker* tracker)
+    MemoryTracker* tracker)
     : use_passthrough_(gpu_preferences.use_passthrough_cmd_decoder &&
                        gles2::PassthroughCommandDecoderSupported()),
       image_factory_(image_factory),
-      memory_tracker_(std::make_unique<gles2::MemoryTypeTracker>(tracker)) {
+      memory_tracker_(std::make_unique<MemoryTypeTracker>(tracker)) {
   gl::GLApi* api = gl::g_current_gl_context;
   api->glGetIntegervFn(GL_MAX_TEXTURE_SIZE, &max_texture_size_);
   if (workarounds.max_texture_size) {
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h
index 76b463ca..a51e5fa 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h
@@ -27,10 +27,7 @@
 struct GpuPreferences;
 struct Mailbox;
 class ImageFactory;
-
-namespace gles2 {
 class MemoryTracker;
-};  // namespace gles2
 
 // Implementation of SharedImageBackingFactory that produces GL-texture backed
 // SharedImages.
@@ -43,7 +40,7 @@
                                      const GpuDriverBugWorkarounds& workarounds,
                                      const GpuFeatureInfo& gpu_feature_info,
                                      ImageFactory* image_factory,
-                                     gles2::MemoryTracker* tracker);
+                                     MemoryTracker* tracker);
   ~SharedImageBackingFactoryGLTexture() override;
 
   // SharedImageBackingFactory implementation.
@@ -91,7 +88,7 @@
   // Factory used to generate GLImages for SCANOUT backings.
   ImageFactory* image_factory_ = nullptr;
 
-  std::unique_ptr<gles2::MemoryTypeTracker> memory_tracker_;
+  std::unique_ptr<MemoryTypeTracker> memory_tracker_;
   FormatInfo format_info_[viz::RESOURCE_FORMAT_MAX + 1];
   int32_t max_texture_size_ = 0;
   bool texture_usage_angle_ = false;
diff --git a/gpu/command_buffer/service/shared_image_factory.cc b/gpu/command_buffer/service/shared_image_factory.cc
index 070b1f4..844e0eb 100644
--- a/gpu/command_buffer/service/shared_image_factory.cc
+++ b/gpu/command_buffer/service/shared_image_factory.cc
@@ -33,7 +33,7 @@
     MailboxManager* mailbox_manager,
     SharedImageManager* shared_image_manager,
     ImageFactory* image_factory,
-    gles2::MemoryTracker* tracker)
+    MemoryTracker* tracker)
     : mailbox_manager_(mailbox_manager),
       shared_image_manager_(shared_image_manager),
       backing_factory_(
diff --git a/gpu/command_buffer/service/shared_image_factory.h b/gpu/command_buffer/service/shared_image_factory.h
index 9f0f44cb..873c661 100644
--- a/gpu/command_buffer/service/shared_image_factory.h
+++ b/gpu/command_buffer/service/shared_image_factory.h
@@ -24,10 +24,7 @@
 class SharedImageBackingFactory;
 struct GpuFeatureInfo;
 struct GpuPreferences;
-
-namespace gles2 {
 class MemoryTracker;
-};  // namespace gles2
 
 namespace raster {
 class WrappedSkImageFactory;
@@ -43,7 +40,7 @@
                      MailboxManager* mailbox_manager,
                      SharedImageManager* manager,
                      ImageFactory* image_factory,
-                     gles2::MemoryTracker* tracker);
+                     MemoryTracker* tracker);
   ~SharedImageFactory();
 
   bool CreateSharedImage(const Mailbox& mailbox,
diff --git a/gpu/command_buffer/service/texture_manager.h b/gpu/command_buffer/service/texture_manager.h
index 55e014a..fbcf73c 100644
--- a/gpu/command_buffer/service/texture_manager.h
+++ b/gpu/command_buffer/service/texture_manager.h
@@ -37,6 +37,7 @@
 class ServiceDiscardableManager;
 class SharedImageBackingGLTexture;
 class SharedImageBackingFactoryGLTexture;
+class SharedImageBackingAHardwareBuffer;
 class SharedImageRepresentationGLTexture;
 
 namespace gles2 {
@@ -354,6 +355,7 @@
   friend class MailboxManagerTest;
   friend class gpu::SharedImageBackingGLTexture;
   friend class gpu::SharedImageBackingFactoryGLTexture;
+  friend class gpu::SharedImageBackingAHardwareBuffer;
   friend class TextureDefinition;
   friend class TextureManager;
   friend class TextureRef;
diff --git a/gpu/command_buffer/service/transfer_buffer_manager.cc b/gpu/command_buffer/service/transfer_buffer_manager.cc
index f0538f9..c8435dc 100644
--- a/gpu/command_buffer/service/transfer_buffer_manager.cc
+++ b/gpu/command_buffer/service/transfer_buffer_manager.cc
@@ -24,8 +24,7 @@
 
 namespace gpu {
 
-TransferBufferManager::TransferBufferManager(
-    gles2::MemoryTracker* memory_tracker)
+TransferBufferManager::TransferBufferManager(MemoryTracker* memory_tracker)
     : shared_memory_bytes_allocated_(0), memory_tracker_(memory_tracker) {
   // When created from InProcessCommandBuffer, we won't have a |memory_tracker_|
   // so don't register a dump provider.
diff --git a/gpu/command_buffer/service/transfer_buffer_manager.h b/gpu/command_buffer/service/transfer_buffer_manager.h
index 78ce317..62703d5 100644
--- a/gpu/command_buffer/service/transfer_buffer_manager.h
+++ b/gpu/command_buffer/service/transfer_buffer_manager.h
@@ -20,14 +20,12 @@
 #include "gpu/command_buffer/common/command_buffer.h"
 
 namespace gpu {
-namespace gles2 {
 class MemoryTracker;
-}
 
 class GPU_EXPORT TransferBufferManager
     : public base::trace_event::MemoryDumpProvider {
  public:
-  explicit TransferBufferManager(gles2::MemoryTracker* memory_tracker);
+  explicit TransferBufferManager(MemoryTracker* memory_tracker);
   ~TransferBufferManager() override;
 
   // Overridden from base::trace_event::MemoryDumpProvider:
@@ -46,7 +44,7 @@
   typedef base::flat_map<int32_t, scoped_refptr<Buffer>> BufferMap;
   BufferMap registered_buffers_;
   size_t shared_memory_bytes_allocated_;
-  gles2::MemoryTracker* memory_tracker_;
+  MemoryTracker* memory_tracker_;
 
   DISALLOW_COPY_AND_ASSIGN(TransferBufferManager);
 };
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
index 88b8f17..9ac5c0e 100644
--- a/gpu/config/gpu_driver_bug_list.json
+++ b/gpu/config/gpu_driver_bug_list.json
@@ -1352,20 +1352,6 @@
       ]
     },
     {
-      "id": 143,
-      "description": "Timer queries crash on Intel GPUs on Linux",
-      "cr_bugs": [540543, 576991],
-      "os": {
-        "type": "linux"
-      },
-      "vendor_id": "0x8086",
-      "driver_vendor": "Mesa",
-      "disabled_extensions": [
-        "GL_ARB_timer_query",
-        "GL_EXT_timer_query"
-      ]
-    },
-    {
       "id": 144,
       "cr_bugs": [563714],
       "description": "Pack parameters work incorrectly with pack buffer bound",
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc
index 4dc9e48..b9f20b0 100644
--- a/gpu/ipc/in_process_command_buffer.cc
+++ b/gpu/ipc/in_process_command_buffer.cc
@@ -398,7 +398,7 @@
   if (params.share_command_buffer) {
     context_group_ = params.share_command_buffer->context_group_;
   } else {
-    std::unique_ptr<gles2::MemoryTracker> memory_tracker;
+    std::unique_ptr<MemoryTracker> memory_tracker;
     // Android WebView won't have a memory tracker.
     if (task_executor_->ShouldCreateMemoryTracker()) {
       const uint64_t client_tracing_id =
diff --git a/gpu/ipc/service/command_buffer_stub.cc b/gpu/ipc/service/command_buffer_stub.cc
index 89a0837..8797b25 100644
--- a/gpu/ipc/service/command_buffer_stub.cc
+++ b/gpu/ipc/service/command_buffer_stub.cc
@@ -791,7 +791,7 @@
   destruction_observers_.RemoveObserver(observer);
 }
 
-std::unique_ptr<gles2::MemoryTracker> CommandBufferStub::CreateMemoryTracker(
+std::unique_ptr<MemoryTracker> CommandBufferStub::CreateMemoryTracker(
     const GPUCreateCommandBufferConfig init_params) const {
   return std::make_unique<GpuCommandBufferMemoryTracker>(
       channel_->client_id(), channel_->client_tracing_id(),
@@ -799,7 +799,7 @@
       channel_->task_runner());
 }
 
-gles2::MemoryTracker* CommandBufferStub::GetMemoryTracker() const {
+MemoryTracker* CommandBufferStub::GetMemoryTracker() const {
   return context_group_->memory_tracker();
 }
 
diff --git a/gpu/ipc/service/command_buffer_stub.h b/gpu/ipc/service/command_buffer_stub.h
index dfd7ebb6..32a979f 100644
--- a/gpu/ipc/service/command_buffer_stub.h
+++ b/gpu/ipc/service/command_buffer_stub.h
@@ -102,7 +102,7 @@
   void OnRescheduleAfterFinished() override;
   void ScheduleGrContextCleanup() override;
 
-  gles2::MemoryTracker* GetMemoryTracker() const;
+  MemoryTracker* GetMemoryTracker() const;
 
   // Whether this command buffer can currently handle IPC messages.
   bool IsScheduled();
@@ -137,7 +137,7 @@
                                size_t url_hash,
                                GpuChannel* channel);
 
-  std::unique_ptr<gles2::MemoryTracker> CreateMemoryTracker(
+  std::unique_ptr<MemoryTracker> CreateMemoryTracker(
       const GPUCreateCommandBufferConfig init_params) const;
 
   // Must be called during Initialize(). Takes ownership to co-ordinate
diff --git a/gpu/ipc/service/gpu_channel.cc b/gpu/ipc/service/gpu_channel.cc
index 4d4d98b2..ee54e607 100644
--- a/gpu/ipc/service/gpu_channel.cc
+++ b/gpu/ipc/service/gpu_channel.cc
@@ -692,11 +692,11 @@
 
 uint64_t GpuChannel::GetMemoryUsage() const {
   // Collect the unique memory trackers in use by the |stubs_|.
-  base::flat_set<gles2::MemoryTracker*> unique_memory_trackers;
+  base::flat_set<MemoryTracker*> unique_memory_trackers;
   unique_memory_trackers.reserve(stubs_.size());
   uint64_t size = 0;
   for (const auto& kv : stubs_) {
-    gles2::MemoryTracker* tracker = kv.second->GetMemoryTracker();
+    MemoryTracker* tracker = kv.second->GetMemoryTracker();
     if (!unique_memory_trackers.insert(tracker).second) {
       // We already counted that tracker.
       continue;
diff --git a/gpu/ipc/service/shared_image_stub.h b/gpu/ipc/service/shared_image_stub.h
index b1493860..4f74d8b 100644
--- a/gpu/ipc/service/shared_image_stub.h
+++ b/gpu/ipc/service/shared_image_stub.h
@@ -23,7 +23,7 @@
 }
 
 class SharedImageStub : public IPC::Listener,
-                        public gles2::MemoryTracker,
+                        public MemoryTracker,
                         public base::trace_event::MemoryDumpProvider {
  public:
   SharedImageStub(GpuChannel* channel, int32_t route_id);
@@ -32,7 +32,7 @@
   // IPC::Listener implementation:
   bool OnMessageReceived(const IPC::Message& msg) override;
 
-  // gles2::MemoryTracker implementation:
+  // MemoryTracker implementation:
   void TrackMemoryAllocatedChange(uint64_t delta) override;
   uint64_t GetSize() const override;
   uint64_t ClientTracingId() const override;
diff --git a/gpu/vulkan/BUILD.gn b/gpu/vulkan/BUILD.gn
index 3e8862d..12f00c4a 100644
--- a/gpu/vulkan/BUILD.gn
+++ b/gpu/vulkan/BUILD.gn
@@ -48,6 +48,10 @@
       "//ui/gfx",
     ]
 
+    if (use_x11) {
+      deps += [ "//third_party/angle/third_party/vulkan-validation-layers:vulkan_validation_layers" ]
+    }
+
     data_deps = []
     if (is_fuchsia) {
       data_deps += [ "//third_party/fuchsia-sdk:vulkan_layers" ]
diff --git a/gpu/vulkan/android/vulkan_implementation_android.cc b/gpu/vulkan/android/vulkan_implementation_android.cc
index c5ef55a..c5f4c29c 100644
--- a/gpu/vulkan/android/vulkan_implementation_android.cc
+++ b/gpu/vulkan/android/vulkan_implementation_android.cc
@@ -30,7 +30,7 @@
   if (!vulkan_function_pointers->vulkan_loader_library_)
     return false;
 
-  if (!vulkan_instance_.Initialize(required_extensions)) {
+  if (!vulkan_instance_.Initialize(required_extensions, {})) {
     vulkan_instance_.Destroy();
     return false;
   }
diff --git a/gpu/vulkan/features.gni b/gpu/vulkan/features.gni
index d97fadb7..d55edde 100644
--- a/gpu/vulkan/features.gni
+++ b/gpu/vulkan/features.gni
@@ -8,5 +8,5 @@
 # For details see declare_args() in build/config/BUILDCONFIG.gn.
 declare_args() {
   # Enable experimental vulkan backend.
-  enable_vulkan = is_linux || is_fuchsia
+  enable_vulkan = is_linux || is_android || is_fuchsia
 }
diff --git a/gpu/vulkan/vulkan_instance.cc b/gpu/vulkan/vulkan_instance.cc
index b612179..749484c 100644
--- a/gpu/vulkan/vulkan_instance.cc
+++ b/gpu/vulkan/vulkan_instance.cc
@@ -113,9 +113,7 @@
   }
 
   std::unordered_set<std::string> desired_layers({
-#if !defined(USE_X11) && !defined(USE_OZONE)
-    // TODO(crbug.com/843346): Make validation work in combination with
-    // VK_KHR_xlib_surface or switch to VK_KHR_xcb_surface.
+#if defined(USE_X11) && !defined(USE_OZONE)
     "VK_LAYER_LUNARG_standard_validation",
 #endif
   });
diff --git a/ios/chrome/app/firebase_utils_unittest.mm b/ios/chrome/app/firebase_utils_unittest.mm
index 3f17037f..6b32d7d 100644
--- a/ios/chrome/app/firebase_utils_unittest.mm
+++ b/ios/chrome/app/firebase_utils_unittest.mm
@@ -32,17 +32,24 @@
 #endif
 
 #if BUILDFLAG(FIREBASE_ENABLED)
+// This always returns positively that a user is a legacy user (i.e. installed
+// Chrome before Chrome supports Firebase).
 class LegacyUserAppDistributionProvider : public TestAppDistributionProvider {
  public:
   bool IsPreFirebaseLegacyUser(int64_t install_date) override { return true; }
 };
 
-class FakeChromeBrowserProvider : public ios::TestChromeBrowserProvider {
+// LegacyUserChromeBrowserProvider is a fake ChromeBrowserProvider that
+// always returns positively that user is a legacy user. This is intended for
+// testing the case of legacy users.
+// NOTE: The default TestChromeBrowserProvider for unit tests always treats
+// a user as a new user.
+class LegacyUserChromeBrowserProvider : public ios::TestChromeBrowserProvider {
  public:
-  FakeChromeBrowserProvider()
+  LegacyUserChromeBrowserProvider()
       : app_distribution_provider_(
             std::make_unique<LegacyUserAppDistributionProvider>()) {}
-  ~FakeChromeBrowserProvider() override {}
+  ~LegacyUserChromeBrowserProvider() override {}
 
   AppDistributionProvider* GetAppDistributionProvider() const override {
     return app_distribution_provider_.get();
@@ -50,7 +57,7 @@
 
  private:
   std::unique_ptr<LegacyUserAppDistributionProvider> app_distribution_provider_;
-  DISALLOW_COPY_AND_ASSIGN(FakeChromeBrowserProvider);
+  DISALLOW_COPY_AND_ASSIGN(LegacyUserChromeBrowserProvider);
 };
 #endif  // BUILDFLAG(FIREBASE_ENABLED)
 
@@ -124,11 +131,9 @@
 TEST_F(FirebaseUtilsTest, DisabledLegacyInstallation) {
   // Sets up the ChromeProviderEnvironment with a fake provider that responds
   // positively that user is a legacy user predating Firebase integration.
-  IOSChromeScopedTestingChromeBrowserProvider provider_(
-      std::make_unique<FakeChromeBrowserProvider>());
-  // Set an app install date that is at least a year ago.
-  int64_t a_year_ago = base::TimeDelta::FromDays(365).InSeconds();
-  SetInstallDate(base::Time::Now().ToTimeT() - a_year_ago);
+  // Installation date is irrelevant in this case.
+  IOSChromeScopedTestingChromeBrowserProvider provider(
+      std::make_unique<LegacyUserChromeBrowserProvider>());
   // Expects Firebase SDK initialization to be not called.
   [[firapp_ reject] configure];
   InitializeFirebase(/*is_first_run=*/true);
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 07afe9a..4ce26f6 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1820,14 +1820,26 @@
       <message name="IDS_IOS_VOICE_SEARCH_SETTING_TTS" desc="The label for setting switch enabling text-to-speech response after voice search [Length: 29em] [iOS only]">
         Speak Answers Back
       </message>
+      <message name="IDS_IOS_SIGNIN_PROMO_BOOKMARKS_WITH_UNITY" desc="Text to inform the user that they can sign in and sync to get bookmarks shared between devices. [iOS only]">
+        To get your bookmarks on all your devices, turn on sync.
+      </message>
+      <message name="IDS_IOS_SIGNIN_PROMO_CHANGE_ACCOUNT" desc="Button that the user can press if they are not the profile that Chrome found (opposite of 'Continue as Joe Doe'). The user will be able to choose another account and sign-in with it.">
+        Change Accounts
+      </message>
+      <message name="IDS_IOS_SIGNIN_PROMO_CLOSE_ACCESSIBILITY" desc="Accessibility label to describe the close button action in the sign-in promo element.">
+        Close sign-in promo
+      </message>
       <message name="IDS_IOS_SIGNIN_PROMO_CONTINUE_AS" desc="Button that the user can press to login without asking the password and continue using Chrome with this acccount. [Length: 9em]">
         Continue as <ph name="NAME">$1<ex>John Doe</ex></ph>
       </message>
       <message name="IDS_IOS_SIGNIN_PROMO_NOT" desc="Button that the user can press if they are not the profile that Chrome found (opposite of 'Continue as Joe Doe').">
         Not <ph name="EMAIL">$1<ex>john.doe@example.com</ex>?</ph>
       </message>
-      <message name="IDS_IOS_SIGNIN_PROMO_CLOSE_ACCESSIBILITY" desc="Accessibility label to describe the close button action in the sign-in promo element.">
-        Close sign-in promo
+      <message name="IDS_IOS_SIGNIN_PROMO_RECENT_TABS_WITH_UNITY" desc="Text to inform the user that they can sign in and sync to get tabs shared between devices. [iOS only]">
+        To get your tabs from your other devices, turn on sync.
+      </message>
+      <message name="IDS_IOS_SIGNIN_PROMO_SETTINGS_WITH_UNITY" desc="Text to inform the user that they can sign in and sync to get tabs shared between devices. [iOS only]">
+        To sync and personalize across devices, turn on sync.
       </message>
       <message name="IDS_IOS_SYSTEM_MAIL_APP" desc="Name of iOS system-provided Mail app. Note that this is not consistently translated for all locales, e.g. it is still 'Mail' (in English) for French locale, but is translated to '邮件' for Chinese locale. [Length: 10em]">
         Mail
diff --git a/ios/chrome/browser/DEPS b/ios/chrome/browser/DEPS
index e1d3ada..09054ba 100644
--- a/ios/chrome/browser/DEPS
+++ b/ios/chrome/browser/DEPS
@@ -105,6 +105,7 @@
   "+rlz/buildflags",
   "+services/identity/public",
   "+services/metrics/public",
+  "+services/metrics/public/cpp",
   "+services/network/network_change_manager.h",
   "+services/network/public/mojom",
   "+services/network/public/cpp",
diff --git a/ios/chrome/browser/find_in_page/BUILD.gn b/ios/chrome/browser/find_in_page/BUILD.gn
index 88e20e3..63251473 100644
--- a/ios/chrome/browser/find_in_page/BUILD.gn
+++ b/ios/chrome/browser/find_in_page/BUILD.gn
@@ -19,8 +19,10 @@
   deps = [
     ":injected_js",
     "//base",
+    "//ios/chrome/browser/metrics:ukm_url_recorder",
     "//ios/chrome/browser/web",
     "//ios/web",
+    "//services/metrics/public/cpp:ukm_builders",
   ]
   libs = [ "CoreGraphics.framework" ]
 }
@@ -37,6 +39,7 @@
   testonly = true
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
+    "find_in_page_controller_unittest.mm",
     "find_in_page_js_unittest.mm",
     "find_tab_helper_unittest.mm",
     "js_findinpage_manager_unittest.mm",
@@ -45,7 +48,11 @@
     ":find_in_page",
     "//base",
     "//base/test:test_support",
+    "//components/ukm:test_support",
+    "//ios/chrome/browser/browser_state:test_support",
+    "//ios/chrome/browser/metrics:ukm_url_recorder",
     "//ios/chrome/browser/web:test_support",
+    "//ios/chrome/browser/web:web_internal",
     "//ios/web",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
diff --git a/ios/chrome/browser/find_in_page/find_in_page_controller.mm b/ios/chrome/browser/find_in_page/find_in_page_controller.mm
index ef009b7..ce8454c 100644
--- a/ios/chrome/browser/find_in_page/find_in_page_controller.mm
+++ b/ios/chrome/browser/find_in_page/find_in_page_controller.mm
@@ -9,16 +9,18 @@
 #import <cmath>
 #include <memory>
 
-#include "base/logging.h"
-#include "base/mac/foundation_util.h"
+#import "base/logging.h"
+#import "base/mac/foundation_util.h"
 #import "ios/chrome/browser/find_in_page/find_in_page_model.h"
 #import "ios/chrome/browser/find_in_page/js_findinpage_manager.h"
+#include "ios/chrome/browser/metrics/ukm_url_recorder.h"
 #import "ios/chrome/browser/web/dom_altering_lock.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
 #import "ios/web/public/web_state/ui/crw_web_view_proxy.h"
 #import "ios/web/public/web_state/ui/crw_web_view_scroll_view_proxy.h"
 #import "ios/web/public/web_state/web_state.h"
 #import "ios/web/public/web_state/web_state_observer_bridge.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -48,6 +50,8 @@
 // Keyboard listeners.
 - (void)keyboardDidShow:(NSNotification*)note;
 - (void)keyboardWillHide:(NSNotification*)note;
+// Records UKM metric for Find in Page search matches.
+- (void)logFindInPageSearchUKM;
 // Constantly injects the find string in page until
 // |disableFindInPageWithCompletionHandler:| is called or the find operation is
 // complete. Calls |completionHandler| if the find operation is complete.
@@ -177,6 +181,15 @@
   }
 }
 
+- (void)logFindInPageSearchUKM {
+  ukm::SourceId sourceID = ukm::GetSourceIdForWebStateDocument(_webState);
+  if (sourceID != ukm::kInvalidSourceId) {
+    ukm::builders::IOS_FindInPageSearchMatches(sourceID)
+        .SetHasMatches(_findInPageModel.matches > 0)
+        .Record(ukm::UkmRecorder::Get());
+  }
+}
+
 - (void)findStringInPage:(NSString*)query
        completionHandler:(ProceduralBlock)completionHandler {
   ProceduralBlockWithBool lockAction = ^(BOOL hasLock) {
@@ -195,9 +208,14 @@
     __weak FindInPageController* weakSelf = self;
     [_findInPageJsManager findString:query
                    completionHandler:^(BOOL finished, CGPoint point) {
-                     [weakSelf processPumpResult:finished
-                                     scrollPoint:point
-                               completionHandler:completionHandler];
+                     FindInPageController* strongSelf = weakSelf;
+                     if (!strongSelf) {
+                       return;
+                     }
+                     [strongSelf logFindInPageSearchUKM];
+                     [strongSelf processPumpResult:finished
+                                       scrollPoint:point
+                                 completionHandler:completionHandler];
                    }];
   };
   DOMAlteringLock::FromWebState(_webState)->Acquire(self, lockAction);
diff --git a/ios/chrome/browser/find_in_page/find_in_page_controller_unittest.mm b/ios/chrome/browser/find_in_page/find_in_page_controller_unittest.mm
new file mode 100644
index 0000000..b2cca01a
--- /dev/null
+++ b/ios/chrome/browser/find_in_page/find_in_page_controller_unittest.mm
@@ -0,0 +1,95 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/find_in_page/find_in_page_controller.h"
+
+#import "base/test/ios/wait_util.h"
+#include "components/ukm/test_ukm_recorder.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#import "ios/chrome/browser/find_in_page/find_in_page_model.h"
+#include "ios/chrome/browser/metrics/ukm_url_recorder.h"
+#import "ios/chrome/browser/web/chrome_web_client.h"
+#import "ios/chrome/browser/web/chrome_web_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using base::test::ios::kWaitForJSCompletionTimeout;
+using base::test::ios::WaitUntilConditionOrTimeout;
+
+namespace {
+
+const char kFindInPageUkmSearchMatchesEvent[] = "IOS.FindInPageSearchMatches";
+const char kFindInPageUkmSearchMetric[] = "HasMatches";
+
+class FindInPageControllerTest : public ChromeWebTest {
+ protected:
+  FindInPageControllerTest()
+      : ChromeWebTest(std::make_unique<ChromeWebClient>()) {}
+  ~FindInPageControllerTest() override {}
+
+  void SetUp() override {
+    ChromeWebTest::SetUp();
+    find_in_page_controller_ =
+        [[FindInPageController alloc] initWithWebState:web_state()];
+    ukm::InitializeSourceUrlRecorderForWebState(web_state());
+  };
+
+  void TearDown() override {
+    [find_in_page_controller_ detachFromWebState];
+    test_ukm_recorder_.Purge();
+    ChromeWebTest::TearDown();
+  };
+
+  FindInPageController* find_in_page_controller_ = nil;
+  ukm::TestAutoSetUkmRecorder test_ukm_recorder_;
+};
+
+// Loads html that contains "some string", searches for it, and ensures UKM has
+// logged the search as having found a match.
+TEST_F(FindInPageControllerTest, VerifyUKMLoggedTrue) {
+  test_ukm_recorder_.Purge();
+  LoadHtml(@"<html><p>some string</p></html>");
+  __block bool completion_handler_finished = false;
+  [find_in_page_controller_ findStringInPage:@"some string"
+                           completionHandler:^{
+                             completion_handler_finished = true;
+                           }];
+  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
+    return completion_handler_finished;
+  }));
+  // Single true entry should be recorded for the interaction above.
+  const auto& entries =
+      test_ukm_recorder_.GetEntriesByName(kFindInPageUkmSearchMatchesEvent);
+  ASSERT_EQ(1u, entries.size());
+  const ukm::mojom::UkmEntry* entry = entries[0];
+  EXPECT_NE(ukm::kInvalidSourceId, entry->source_id);
+  test_ukm_recorder_.ExpectEntryMetric(entry, kFindInPageUkmSearchMetric, true);
+}
+
+// Loads html that contains "some string", searches for something that does not
+// match, and ensures UKM has not logged the search as having found a match.
+TEST_F(FindInPageControllerTest, VerifyUKMLoggedFalse) {
+  test_ukm_recorder_.Purge();
+  LoadHtml(@"<html><p>some string</p></html>");
+  __block bool completion_handler_finished = false;
+  [find_in_page_controller_ findStringInPage:@"nothing"
+                           completionHandler:^{
+                             completion_handler_finished = true;
+                           }];
+  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
+    return completion_handler_finished;
+  }));
+  // Single false entry should be recorded for the interaction above.
+  const auto& entries =
+      test_ukm_recorder_.GetEntriesByName(kFindInPageUkmSearchMatchesEvent);
+  ASSERT_EQ(1u, entries.size());
+  const ukm::mojom::UkmEntry* entry = entries[0];
+  EXPECT_NE(ukm::kInvalidSourceId, entry->source_id);
+  test_ukm_recorder_.ExpectEntryMetric(entry, kFindInPageUkmSearchMetric,
+                                       false);
+}
+}
diff --git a/ios/chrome/browser/ntp/new_tab_page_tab_helper.h b/ios/chrome/browser/ntp/new_tab_page_tab_helper.h
index 01aa9dd6..02a0c59 100644
--- a/ios/chrome/browser/ntp/new_tab_page_tab_helper.h
+++ b/ios/chrome/browser/ntp/new_tab_page_tab_helper.h
@@ -37,8 +37,8 @@
 
   // web::WebStateObserver overrides:
   void WebStateDestroyed(web::WebState* web_state) override;
-  void DidStartNavigation(web::WebState* web_state,
-                          web::NavigationContext* navigation_context) override;
+  void DidFinishNavigation(web::WebState* web_state,
+                           web::NavigationContext* navigation_context) override;
 
   // Enable or disable the tab helper.
   void SetActive(bool active);
diff --git a/ios/chrome/browser/ntp/new_tab_page_tab_helper.mm b/ios/chrome/browser/ntp/new_tab_page_tab_helper.mm
index 17a3ae50..4bee7d0d 100644
--- a/ios/chrome/browser/ntp/new_tab_page_tab_helper.mm
+++ b/ios/chrome/browser/ntp/new_tab_page_tab_helper.mm
@@ -66,7 +66,7 @@
   SetActive(false);
 }
 
-void NewTabPageTabHelper::DidStartNavigation(
+void NewTabPageTabHelper::DidFinishNavigation(
     web::WebState* web_state,
     web::NavigationContext* navigation_context) {
   if (navigation_context->IsSameDocument()) {
diff --git a/ios/chrome/browser/ntp/new_tab_page_tab_helper_unittest.mm b/ios/chrome/browser/ntp/new_tab_page_tab_helper_unittest.mm
index 62017d7..2a276c0 100644
--- a/ios/chrome/browser/ntp/new_tab_page_tab_helper_unittest.mm
+++ b/ios/chrome/browser/ntp/new_tab_page_tab_helper_unittest.mm
@@ -118,19 +118,19 @@
   GURL url(kChromeUINewTabURL);
   web::FakeNavigationContext context;
   context.SetUrl(url);
-  test_web_state_.OnNavigationStarted(&context);
+  test_web_state_.OnNavigationFinished(&context);
   EXPECT_TRUE(tab_helper()->IsActive());
 
   GURL not_ntp_url(kTestURL);
   context.SetUrl(not_ntp_url);
-  test_web_state_.OnNavigationStarted(&context);
+  test_web_state_.OnNavigationFinished(&context);
   EXPECT_FALSE(tab_helper()->IsActive());
 
   context.SetUrl(url);
-  test_web_state_.OnNavigationStarted(&context);
+  test_web_state_.OnNavigationFinished(&context);
   EXPECT_TRUE(tab_helper()->IsActive());
 
   context.SetUrl(not_ntp_url);
-  test_web_state_.OnNavigationStarted(&context);
+  test_web_state_.OnNavigationFinished(&context);
   EXPECT_FALSE(tab_helper()->IsActive());
 }
diff --git a/ios/chrome/browser/search_engines/resources/search_engine.js b/ios/chrome/browser/search_engines/resources/search_engine.js
index efd9bc0..901382d 100644
--- a/ios/chrome/browser/search_engines/resources/search_engine.js
+++ b/ios/chrome/browser/search_engines/resources/search_engine.js
@@ -32,9 +32,276 @@
 __gCrWeb.searchEngine.getOpenSearchDescriptionDocumentUrl = function() {
   var links = document.getElementsByTagName('link');
   for (var i = 0; i < links.length; ++i) {
-    if (links[i].type == 'application/opensearchdescription+xml')
+    if (links[i].type == 'application/opensearchdescription+xml') {
       return links[i].href;
+    }
   }
 };
 
+/**
+ * Encodes |url| in "application/x-www-form-urlencoded" content type of <form>.
+ * The standard is defined in:
+ * https://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
+ * This solution comes from:
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
+ * @private
+ */
+function encodeFormData_(url) {
+  return encodeURIComponent(url).replace('%20', '+');
+};
+
+/**
+ * Returns if |element| can submit a form(i.e. <button> or <input
+ * type="submit">).
+ * @param {Element} element An element inside a <form>.
+ * @return {boolean} If |element| can submit the form.
+ * @private
+ */
+function isSubmitElement_(element) {
+  return (element.tagName == 'BUTTON') ||
+      (element.tagName == 'INPUT' && element.type == 'submit');
+};
+
+/**
+ * Returns if |element| is checkable(i.e. <input type="radio"> or <input
+ * type="checkbox">).
+ * @param {Element} element An element inside a <form>.
+ * @return {boolean} If |element| is checkable.
+ * @private
+ */
+function isCheckableElement_(element) {
+  return (
+      element.tagName == 'INPUT' &&
+      (element.type == 'radio' || element.type == 'checkbox'));
+};
+
+/**
+ * Records the active submit element of <form> being submitted.
+ * @type {Element}
+ * @private
+ */
+var activeSubmitElement_ = null;
+
+/**
+ * Returns the submit element which triggers the submission of |form|. If there
+ * is no submit element clicked before |form|'s submission, the first submit
+ * element of |form| will be returned.
+ * @param {Element} form The <form> on submission.
+ * @return {Element|undefined} The element which submits |form| or the first
+ * submit element in |form|. Returns undefined if not found.
+ * @private
+ */
+function getActiveSubmitElement_(form) {
+  if (activeSubmitElement_ && activeSubmitElement_.form === form) {
+    return activeSubmitElement_;
+  }
+  for (var i = 0; i < form.elements.length; ++i) {
+    if (isSubmitElement_(form.elements[i])) {
+      return form.elements[i];
+    }
+  }
+};
+
+/**
+ * A set of all the text categories of <input>'s type attribute.
+ * This set is based on:
+ * https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/html/forms/base_text_input_type.h?rcl=3e6185be26ac5e48c7921d314d2abc4a3a1572e2&l=40
+ * https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/html/forms/text_field_input_type.h?rcl=3e6185be26ac5e48c7921d314d2abc4a3a1572e2&l=41
+ * "password" is not in the map because it makes the <form> invalid.
+ * @type {Set<string>}
+ * @private
+ */
+var textInputTypes_ =
+    new Set(['email', 'search', 'tel', 'text', 'url', 'number']);
+
+/**
+ * Returns false if |element| is <input type="radio|checkbox"> or <select> and
+ * it's not in its default state, otherwise true. The default state is the state
+ * of the form element on initial load of the page, and varies depending upon
+ * the form element. The code is based on:
+ * https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/exported/web_searchable_form_data.cc?rcl=a26f1d82bbd020be06fb96518179bbb6b6146370&l=120
+ * @param {Element} element Element in <form>.
+ * @return {boolean} If the element is in its default state.
+ * @private
+ */
+function isInDefaultState_(element) {
+  if (isCheckableElement_(element)) {
+    return element.checked == element.defaultChecked;
+  } else if (element.tagName == 'SELECT') {
+    for (var i = 0; i < element.options.length; ++i) {
+      var option = element.options[i];
+      if (option.selected != option.defaultSelected) {
+        return false;
+      }
+    }
+  }
+  return true;
+};
+
+/**
+ * Looks for a suitable search text field in |form|. Returns undefined if |form|
+ * is not a valid searchable <form>. The code is based on:
+ * https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/exported/web_searchable_form_data.cc?rcl=a26f1d82bbd020be06fb96518179bbb6b6146370&l=137
+ *
+ * The criteria for a valid searchable <form>:
+ *   1. Has no <textarea>;
+ *   2. Has no <input type="password">;
+ *   3. Has no <input type="file">;
+ *   4. Has exactly one <input> with "type" from |textInputTypes_|;
+ *   5. Has no element that is not in default state;
+ * Any element that doesn't have "name" attribute or has "disabled" attribute
+ * will be ignored.
+ *
+ * @param {Element} form The form being submitted.
+ * @return {Element|undefined} The only one text <input> in |form|, or undefined
+ *   if |form| is not a valid searchable <form>.
+ * @private
+ */
+function findSuitableSearchInputElement_(form) {
+  var result = undefined;
+  for (var i = 0; i < form.elements.length; ++i) {
+    var element = form.elements[i];
+    if (element.disabled || !element.name) {
+      continue;
+    }
+    if (!isInDefaultState_(element) || element.tagName == 'TEXTAREA') {
+      return;
+    }
+    if (element.tagName == 'INPUT') {
+      if (element.type == 'file' || element.type == 'password') {
+        return;
+      }
+      if (textInputTypes_.has(element.type)) {
+        if (result) {
+          return;
+        }
+        result = element;
+      }
+    }
+  }
+  return result;
+};
+
+/**
+ * Generates a searchable URL from |form| if it's a valid searchable <form>.
+ * The code is based on:
+https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/exported/web_searchable_form_data.cc?rcl=9c80632d2b16884970961dc9a12bddd3a4ca50b5&l=218
+ *
+ * @param {Element} form The <form> element.
+ * @return {string|undefined} The searchable URL, or undefined if |form| is not
+ *   a valid searchable <form>.
+ * @private
+ * TODO(crbug.com/433824): Use <form>'s "accept-charset" attribute to encode the
+ *   searchableURL.
+ */
+function generateSearchableUrl_(form) {
+  // Only consider forms that GET data.
+  if (form.method && form.method.toLowerCase() != 'get') {
+    return;
+  }
+
+  var searchInput = findSuitableSearchInputElement_(form);
+  if (!searchInput) {
+    return;
+  }
+
+  var activeSubmitElement = getActiveSubmitElement_(form);
+
+  // The "name=value" pairs appended to the end of the action URL.
+  var queryArgs = [];
+  for (var i = 0; i < form.elements.length; ++i) {
+    var element = form.elements[i];
+    if (element.disabled || !element.name) {
+      continue;
+    }
+    if (isSubmitElement_(element)) {
+      // Only append the active submit element's name-value pair.
+      if (element === activeSubmitElement) {
+        // <input type="submit"> will have "Submit" as default "value" when
+        // submitted with empty "value" and non-empty "name". This probably
+        // comes from the default label text of <input type="submit">:
+        //   https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/submit
+        if (element.tagName == 'INPUT' && !element.value) {
+          element.value = 'Submit';
+        }
+        queryArgs.push(
+            encodeFormData_(element.name) + '=' +
+            encodeFormData_(element.value));
+      }
+      continue;
+    }
+    if (element === searchInput) {
+      queryArgs.push(encodeFormData_(element.name) + '={searchTerms}');
+    } else {
+      // Ignore unchecked checkable element.
+      if (isCheckableElement_(element) && !element.checked) {
+        continue;
+      }
+      queryArgs.push(
+          encodeFormData_(element.name) + '=' + encodeFormData_(element.value));
+    }
+  }
+  // If |form| uses "GET" method, appended query args in |form|.action should be
+  // dropped. Use URL class to get rid of these query args.
+  var url = new URL(form.action);
+  return url.origin + url.pathname + '?' + queryArgs.join('&');
+};
+
+/**
+ * Adds listener for 'click' event on |document|. When a submit element is
+ * clicked, records it in |activeSubmitElement_| for |generateSearchableUrl_|,
+ * which will be called in the 'submit' event callbacks within current call
+ * stack. Appends a callback at the end of Js task queue with timeout=0ms that
+ * sets |activeSubmitElement_| back to undefined after the submission.
+ *
+ * The call stack of form submission:
+ *   User clicks button.
+ *     "click" event emitted and bubbles up to |document|.
+ *       Records current button as |activeSubmitElement_|.
+ *       Posts callback that unsets active submit element by setTimeout(..., 0).
+ *     "click" event ends.
+ *     "submit" event emitted and bubbles up to |document|.
+ *       Generates searchable URL based on |activeSubmitElement_|.
+ *     "submit" event ends.
+ *   Call stack of user's click on button finishes.
+ *   ...
+ *   Js task queue running...
+ *   ...
+ *   Callback posted by setTimeout(..., 0) is invoked and clean up
+ *   |activeSubmitElement_|.
+ */
+document.addEventListener('click', function(event) {
+  if (event.defaultPrevented) {
+    return;
+  }
+  var element = event.target;
+  if (!(element instanceof Element) || !isSubmitElement_(element)) {
+    return;
+  }
+  activeSubmitElement_ = element;
+  setTimeout(function() {
+    if (activeSubmitElement_ === element) {
+      activeSubmitElement_ = null;
+    }
+  }, 0);
+});
+
+/**
+ * Adds listener for 'submit' event on |document|. When a <form> is submitted,
+ * try to generate a searchableUrl. If succeeded, send it back to native code.
+ * TODO(crbug.com/433824): Refactor /components/autofill/ios/form_util to reuse
+ *   FormActivityObserver, so that all the data about form submission can be
+ *   sent in a single message.
+ */
+document.addEventListener('submit', function(event) {
+  if (event.defaultPrevented || !(event.target instanceof Element)) {
+    return;
+  }
+  var url = generateSearchableUrl_(event.target);
+  if (url) {
+    __gCrWeb.message.invokeOnHost(
+        {'command': 'searchEngine.searchableUrl', 'url': url});
+  }
+}, false);
+
 }());  // End of anonymous object
diff --git a/ios/chrome/browser/search_engines/search_engine_js_unittest.mm b/ios/chrome/browser/search_engines/search_engine_js_unittest.mm
index 3d31649..366cdd2 100644
--- a/ios/chrome/browser/search_engines/search_engine_js_unittest.mm
+++ b/ios/chrome/browser/search_engines/search_engine_js_unittest.mm
@@ -3,8 +3,11 @@
 // found in the LICENSE file.
 
 #include "base/macros.h"
+#import "base/test/ios/wait_util.h"
 #import "ios/web/public/test/web_js_test.h"
 #import "ios/web/public/test/web_test_with_web_state.h"
+#import "ios/web/public/test/web_view_interaction_test_util.h"
+#import "ios/web/public/web_state/web_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
 
@@ -12,12 +15,70 @@
 #error "This file requires ARC support."
 #endif
 
+using base::test::ios::WaitUntilConditionOrTimeout;
+using base::test::ios::kWaitForJSCompletionTimeout;
+using web::test::TapWebViewElementWithId;
+using web::test::SelectWebViewElementWithId;
+
+namespace {
+const char kCommandPrefix[] = "searchEngine";
+// This is for cases where no message should be sent back from Js.
+const NSTimeInterval kWaitForJsNotReturnTimeout = 0.5;
+
+NSString* kSearchableForm =
+    @"<html>"
+    @"  <form id='f' action='index.html' method='get'>"
+    @"    <input type='search' name='q'>"
+    @"    <input type='hidden' name='hidden' value='i1'>"
+    @"    <input type='hidden' name='disabled' value='i2' disabled>"
+    @"    <input id='r1' type='radio' name='radio' value='r1' checked>"
+    @"    <input id='r2' type='radio' name='radio' value='r2'>"
+    @"    <input id='c1' type='checkbox' name='check' value='c1'>"
+    @"    <input id='c2' type='checkbox' name='check' value='c2' checked>"
+    @"    <select name='select' name='select'>"
+    @"      <option id='op1' value='op1'>op1</option>"
+    @"      <option id='op2' value='op2' selected>op2</option>"
+    @"      <option id='op3' value='op3'>op3</option>"
+    @"    </select>"
+    @"    <input id='btn1' type='submit' name='btn1' value='b1'>"
+    @"    <button id='btn2' name='btn2' value='b2'>"
+    @"  </form>"
+    @"  <input type='hidden' form='f' name='outside form' value='i3'>"
+    @"</html>";
+}
+
 // Test fixture for search_engine.js testing.
 class SearchEngineJsTest : public web::WebJsTest<web::WebTestWithWebState> {
- public:
+ protected:
   SearchEngineJsTest()
       : web::WebJsTest<web::WebTestWithWebState>(@[ @"search_engine" ]) {}
 
+  void SetUp() override {
+    WebTestWithWebState::SetUp();
+    web_state()->AddScriptCommandCallback(
+        base::BindRepeating(&SearchEngineJsTest::OnMessageFromJavaScript,
+                            base::Unretained(this)),
+        kCommandPrefix);
+  }
+
+  void TearDown() override {
+    web_state()->RemoveScriptCommandCallback(kCommandPrefix);
+    WebTestWithWebState::TearDown();
+  }
+
+  bool OnMessageFromJavaScript(const base::DictionaryValue& message,
+                               const GURL& page_url,
+                               bool has_user_gesture,
+                               bool form_in_main_frame,
+                               web::WebFrame* sender_frame) {
+    message_received_ = true;
+    message_ = message.Clone();
+    return true;
+  }
+
+  base::Value message_;
+  bool message_received_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(SearchEngineJsTest);
 };
 
@@ -53,3 +114,150 @@
       @"__gCrWeb.searchEngine.getOpenSearchDescriptionDocumentUrl();");
   EXPECT_FALSE(result);
 }
+
+// Tests that __gCrWeb.searchEngine generates and sends back a searchable
+// URL when <form> is submitted by click on the first button in <form>.
+TEST_F(SearchEngineJsTest,
+       GenerateSearchableUrlForValidFormSubmittedByFirstButton) {
+  LoadHtmlAndInject(kSearchableForm, GURL("https://abc.com"));
+  ASSERT_TRUE(TapWebViewElementWithId(web_state(), "btn1"));
+  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
+    base::RunLoop().RunUntilIdle();
+    return message_received_;
+  }));
+  const base::Value* url = message_.FindKey("url");
+  ASSERT_TRUE(url);
+  ASSERT_TRUE(url->is_string());
+  EXPECT_EQ(
+      "https://abc.com/"
+      "index.html?q={searchTerms}&hidden=i1&radio=r1&check=c2&select=op2&btn1="
+      "b1&outside+form=i3",
+      url->GetString());
+}
+
+// Tests that __gCrWeb.searchEngine generates and sends back a searchable
+// URL when <form> is submitted by click on a non-first button in <form>.
+TEST_F(SearchEngineJsTest,
+       GenerateSearchableUrlForValidFormSubmittedByNonFirstButton) {
+  LoadHtmlAndInject(kSearchableForm, GURL("https://abc.com"));
+  ASSERT_TRUE(TapWebViewElementWithId(web_state(), "btn2"));
+  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
+    base::RunLoop().RunUntilIdle();
+    return message_received_;
+  }));
+  const base::Value* url = message_.FindKey("url");
+  ASSERT_TRUE(url);
+  ASSERT_TRUE(url->is_string());
+  EXPECT_EQ(
+      "https://abc.com/"
+      "index.html?q={searchTerms}&hidden=i1&radio=r1&check=c2&select=op2&btn2="
+      "b2&outside+form=i3",
+      url->GetString());
+}
+
+// Tests that __gCrWeb.searchEngine doesn't generate and send back a searchable
+// URL for <form> with <textarea>.
+TEST_F(SearchEngineJsTest, GenerateSearchableUrlForInvalidFormWithTextArea) {
+  LoadHtmlAndInject(
+      @"<html><form><input type='search' name='q'><textarea "
+      @"name='a'></textarea><input id='btn' type='submit'></form></html>");
+  ASSERT_TRUE(TapWebViewElementWithId(web_state(), "btn"));
+  ASSERT_FALSE(WaitUntilConditionOrTimeout(kWaitForJsNotReturnTimeout, ^{
+    base::RunLoop().RunUntilIdle();
+    return message_received_;
+  }));
+}
+
+// Tests that __gCrWeb.searchEngine doesn't generate and send back a searchable
+// URL for <form> with <input type="password">.
+TEST_F(SearchEngineJsTest,
+       GenerateSearchableUrlForInvalidFormWithInputPassword) {
+  LoadHtmlAndInject(
+      @"<html><form><input type='search' name='q'><input "
+      @"type='password' name='a'><input id='btn' type='submit'></form></html>");
+  ASSERT_TRUE(TapWebViewElementWithId(web_state(), "btn"));
+  ASSERT_FALSE(WaitUntilConditionOrTimeout(kWaitForJsNotReturnTimeout, ^{
+    base::RunLoop().RunUntilIdle();
+    return message_received_;
+  }));
+}
+
+// Tests that __gCrWeb.searchEngine doesn't generate and send back a searchable
+// URL for <form> with <input type="file">.
+TEST_F(SearchEngineJsTest, GenerateSearchableUrlForInvalidFormWithInputFile) {
+  LoadHtmlAndInject(
+      @"<html><form><input type='search' name='q'><input "
+      @"type='file' name='a'><input id='btn' type='submit'</form></html>");
+  ASSERT_TRUE(TapWebViewElementWithId(web_state(), "btn"));
+  ASSERT_FALSE(WaitUntilConditionOrTimeout(kWaitForJsNotReturnTimeout, ^{
+    base::RunLoop().RunUntilIdle();
+    return message_received_;
+  }));
+}
+
+// Tests that __gCrWeb.searchEngine doesn't generate and send back a searchable
+// URL for <form> without <input type="email|search|tel|text|url|number">.
+TEST_F(SearchEngineJsTest, GenerateSearchableUrlForInvalidFormWithNoTextInput) {
+  LoadHtmlAndInject(
+      @"<html><form id='f'><input type='hidden' name='q' "
+      @"value='v'><input id='btn' type='submit'></form></html>");
+  ASSERT_TRUE(TapWebViewElementWithId(web_state(), "btn"));
+  ASSERT_FALSE(WaitUntilConditionOrTimeout(kWaitForJsNotReturnTimeout, ^{
+    base::RunLoop().RunUntilIdle();
+    return message_received_;
+  }));
+}
+
+// Tests that __gCrWeb.searchEngine doesn't generate and send back a searchable
+// URL for <form> with more than 1 <input
+// type="email|search|tel|text|url|number">.
+TEST_F(SearchEngineJsTest,
+       GenerateSearchableUrlForInvalidFormWithMoreThanOneTextInput) {
+  LoadHtmlAndInject(
+      @"<html><form id='f'><input type='search' name='q'><input "
+      @"type='text' name='q2'><input id='btn' type='submit'></form></html>");
+  ASSERT_TRUE(TapWebViewElementWithId(web_state(), "btn"));
+  ASSERT_FALSE(WaitUntilConditionOrTimeout(kWaitForJsNotReturnTimeout, ^{
+    base::RunLoop().RunUntilIdle();
+    return message_received_;
+  }));
+}
+
+// Tests that __gCrWeb.searchEngine doesn't generate and send back a searchable
+// URL for <form> with <input type='radio'> in non-default state.
+TEST_F(SearchEngineJsTest,
+       GenerateSearchableUrlForInvalidFormWithNonDefaultRadio) {
+  LoadHtmlAndInject(kSearchableForm);
+  ASSERT_TRUE(TapWebViewElementWithId(web_state(), "r2"));
+  ASSERT_TRUE(TapWebViewElementWithId(web_state(), "btn1"));
+  ASSERT_FALSE(WaitUntilConditionOrTimeout(kWaitForJsNotReturnTimeout, ^{
+    base::RunLoop().RunUntilIdle();
+    return message_received_;
+  }));
+}
+
+// Tests that __gCrWeb.searchEngine doesn't generate and send back a searchable
+// URL for <form> with <input type='checkbox'> in non-default state.
+TEST_F(SearchEngineJsTest,
+       GenerateSearchableUrlForInvalidFormWithNonDefaultCheckbox) {
+  LoadHtmlAndInject(kSearchableForm);
+  ASSERT_TRUE(TapWebViewElementWithId(web_state(), "c1"));
+  ASSERT_TRUE(TapWebViewElementWithId(web_state(), "btn1"));
+  ASSERT_FALSE(WaitUntilConditionOrTimeout(kWaitForJsNotReturnTimeout, ^{
+    base::RunLoop().RunUntilIdle();
+    return message_received_;
+  }));
+}
+
+// Tests that __gCrWeb.searchEngine.generateSearchableUrl returns undefined
+// for <form> with <select> in non-default state.
+TEST_F(SearchEngineJsTest,
+       GenerateSearchableUrlForInvalidFormWithNonDefaultSelect) {
+  LoadHtmlAndInject(kSearchableForm);
+  ASSERT_TRUE(SelectWebViewElementWithId(web_state(), "op1"));
+  ASSERT_TRUE(TapWebViewElementWithId(web_state(), "btn1"));
+  ASSERT_FALSE(WaitUntilConditionOrTimeout(kWaitForJsNotReturnTimeout, ^{
+    base::RunLoop().RunUntilIdle();
+    return message_received_;
+  }));
+}
diff --git a/ios/chrome/browser/ui/authentication/BUILD.gn b/ios/chrome/browser/ui/authentication/BUILD.gn
index 8a6231af..8120ba5f 100644
--- a/ios/chrome/browser/ui/authentication/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/BUILD.gn
@@ -102,6 +102,7 @@
   deps = [
     "//base",
     "//components/signin/core/browser",
+    "//components/unified_consent",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser",
     "//ios/chrome/browser/ui",
diff --git a/ios/chrome/browser/ui/authentication/signin_promo_item.mm b/ios/chrome/browser/ui/authentication/signin_promo_item.mm
index d49a70a9..8a35861 100644
--- a/ios/chrome/browser/ui/authentication/signin_promo_item.mm
+++ b/ios/chrome/browser/ui/authentication/signin_promo_item.mm
@@ -4,11 +4,13 @@
 
 #import "ios/chrome/browser/ui/authentication/signin_promo_item.h"
 
+#include "components/unified_consent/feature.h"
 #import "ios/chrome/browser/ui/authentication/signin_promo_view.h"
 #import "ios/chrome/browser/ui/authentication/signin_promo_view_configurator.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 #include "ios/chrome/grit/ios_chromium_strings.h"
+#include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -39,8 +41,13 @@
 
 - (void)configureCell:(SigninPromoCell*)cell {
   [super configureCell:cell];
-  cell.signinPromoView.textLabel.text =
-      l10n_util::GetNSString(IDS_IOS_SIGNIN_PROMO_SETTINGS);
+  if (unified_consent::IsUnifiedConsentFeatureEnabled()) {
+    cell.signinPromoView.textLabel.text =
+        l10n_util::GetNSString(IDS_IOS_SIGNIN_PROMO_SETTINGS_WITH_UNITY);
+  } else {
+    cell.signinPromoView.textLabel.text =
+        l10n_util::GetNSString(IDS_IOS_SIGNIN_PROMO_SETTINGS);
+  }
   [_configurator configureSigninPromoView:cell.signinPromoView];
 }
 
diff --git a/ios/chrome/browser/ui/authentication/signin_promo_view_configurator.mm b/ios/chrome/browser/ui/authentication/signin_promo_view_configurator.mm
index edbb363..06c40ad0 100644
--- a/ios/chrome/browser/ui/authentication/signin_promo_view_configurator.mm
+++ b/ios/chrome/browser/ui/authentication/signin_promo_view_configurator.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/authentication/signin_promo_view_configurator.h"
 
 #include "base/strings/sys_string_conversions.h"
+#include "components/unified_consent/feature.h"
 #import "ios/chrome/browser/ui/authentication/signin_promo_view.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
@@ -67,11 +68,17 @@
         setTitle:l10n_util::GetNSStringF(IDS_IOS_SIGNIN_PROMO_CONTINUE_AS,
                                          base::SysNSStringToUTF16(name))
         forState:UIControlStateNormal];
-    [signinPromoView.secondaryButton
-        setTitle:l10n_util::GetNSStringF(
-                     IDS_IOS_SIGNIN_PROMO_NOT,
-                     base::SysNSStringToUTF16(self.userEmail))
-        forState:UIControlStateNormal];
+    if (unified_consent::IsUnifiedConsentFeatureEnabled()) {
+      [signinPromoView.secondaryButton
+          setTitle:l10n_util::GetNSString(IDS_IOS_SIGNIN_PROMO_CHANGE_ACCOUNT)
+          forState:UIControlStateNormal];
+    } else {
+      [signinPromoView.secondaryButton
+          setTitle:l10n_util::GetNSStringF(
+                       IDS_IOS_SIGNIN_PROMO_NOT,
+                       base::SysNSStringToUTF16(self.userEmail))
+          forState:UIControlStateNormal];
+    }
     UIImage* image = self.userImage;
     if (!image) {
       image = ios::GetChromeBrowserProvider()
diff --git a/ios/chrome/browser/ui/bookmarks/cells/BUILD.gn b/ios/chrome/browser/ui/bookmarks/cells/BUILD.gn
index 8a7ace1..72777360 100644
--- a/ios/chrome/browser/ui/bookmarks/cells/BUILD.gn
+++ b/ios/chrome/browser/ui/bookmarks/cells/BUILD.gn
@@ -24,6 +24,7 @@
     "//base",
     "//base:i18n",
     "//components/bookmarks/browser:browser",
+    "//components/unified_consent",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser",
     "//ios/chrome/browser/ui",
diff --git a/ios/chrome/browser/ui/bookmarks/cells/bookmark_home_promo_item.mm b/ios/chrome/browser/ui/bookmarks/cells/bookmark_home_promo_item.mm
index 7e1b1ed7..ccb172a 100644
--- a/ios/chrome/browser/ui/bookmarks/cells/bookmark_home_promo_item.mm
+++ b/ios/chrome/browser/ui/bookmarks/cells/bookmark_home_promo_item.mm
@@ -5,11 +5,13 @@
 #import "ios/chrome/browser/ui/bookmarks/cells/bookmark_home_promo_item.h"
 
 #include "base/mac/foundation_util.h"
+#include "components/unified_consent/feature.h"
 #import "ios/chrome/browser/ui/authentication/signin_promo_view_configurator.h"
 #import "ios/chrome/browser/ui/authentication/signin_promo_view_mediator.h"
 #import "ios/chrome/browser/ui/bookmarks/cells/bookmark_table_signin_promo_cell.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #include "ios/chrome/grit/ios_chromium_strings.h"
+#include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -33,8 +35,13 @@
       base::mac::ObjCCastStrict<BookmarkTableSigninPromoCell>(cell);
 
   // Basic UI configuration
-  signinPromoCell.signinPromoView.textLabel.text =
-      l10n_util::GetNSString(IDS_IOS_SIGNIN_PROMO_BOOKMARKS);
+  if (unified_consent::IsUnifiedConsentFeatureEnabled()) {
+    signinPromoCell.signinPromoView.textLabel.text =
+        l10n_util::GetNSString(IDS_IOS_SIGNIN_PROMO_BOOKMARKS_WITH_UNITY);
+  } else {
+    signinPromoCell.signinPromoView.textLabel.text =
+        l10n_util::GetNSString(IDS_IOS_SIGNIN_PROMO_BOOKMARKS);
+  }
   signinPromoCell.signinPromoView.backgroundColor =
       styler.tableViewBackgroundColor;
   // Use the mediator to configure the rest of the Cell based on the current
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 2eb79d4..66bc184 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -4986,16 +4986,13 @@
     inForegroundWithCompletion:(ProceduralBlock)completion {
   // Create the new page image, and load with the new tab snapshot except if
   // it is the NTP.
-  CGFloat newPageOffset = 0;
   UIView* newPage = nil;
-  CGFloat offset = 0;
   GURL tabURL = tab.webState->GetVisibleURL();
   // Toolbar snapshot is only used for the UIRefresh animation.
   UIView* toolbarSnapshot;
 
   if (tabURL == kChromeUINewTabURL && !_isOffTheRecord &&
       ![self canShowTabStrip]) {
-    offset = 0;
     // Add a snapshot of the primary toolbar to the background as the
     // animation runs.
     UIViewController* toolbarViewController =
@@ -5019,20 +5016,23 @@
       newPage.frame = viewBounds;
     }
   } else {
-    UIImageView* pageScreenshot = [self pageOpenCloseAnimationView];
     [self viewForTab:tab].frame = self.contentArea.bounds;
     // Setting the frame here doesn't trigger a layout pass. Trigger it manually
     // if needed. Not triggering it can create problem if the previous frame
     // wasn't the right one, for example in https://crbug.com/852106.
     [[self viewForTab:tab] layoutIfNeeded];
-    pageScreenshot.image = SnapshotTabHelper::FromWebState(tab.webState)
-                               ->UpdateSnapshot(/*with_overlays=*/true,
-                                                /*visible_frame_only=*/true);
-    newPage = pageScreenshot;
-    offset =
-        pageScreenshot.frame.size.height - pageScreenshot.image.size.height;
+    if (base::FeatureList::IsEnabled(
+            web::features::kBrowserContainerFullscreen)) {
+      newPage = [self viewForTab:tab];
+      newPage.userInteractionEnabled = NO;
+    } else {
+      UIImageView* pageScreenshot = [self pageOpenCloseAnimationView];
+      pageScreenshot.image = SnapshotTabHelper::FromWebState(tab.webState)
+                                 ->UpdateSnapshot(/*with_overlays=*/true,
+                                                  /*visible_frame_only=*/true);
+      newPage = pageScreenshot;
+    }
   }
-  newPageOffset = newPage.frame.origin.y;
 
   // Cleanup steps needed for both UI Refresh and stack-view style animations.
   auto commonCompletion = ^{
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm
index bf98bf72..75128f879 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm
@@ -186,9 +186,6 @@
 
 - (void)webState:(web::WebState*)webState didLoadPageWithSuccess:(BOOL)success {
   DCHECK_EQ(_webState, webState);
-  if (!success)
-    return;
-
   [self setContentOffsetForWebState:webState];
 }
 
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
index 3ae6d68e..0e884b9 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
@@ -260,7 +260,7 @@
 
 - (void)focusOmnibox {
   // When the NTP and fakebox are visible, make the fakebox animates into place
-  // before focusing the omnibox.
+  // before focusing the omnibox.webState
   if (IsVisibleUrlNewTabPage([self webState]) &&
       !self.browserState->IsOffTheRecord()) {
     [self.viewController.dispatcher focusFakebox];
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm
index 9850291..fda1e2d 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm
@@ -62,6 +62,7 @@
       [[OmniboxPopupMediator alloc] initWithFetcher:std::move(imageFetcher)
                                            delegate:_popupView.get()];
   self.mediator.dispatcher = (id<BrowserCommands>)self.dispatcher;
+  self.mediator.webStateList = self.webStateList;
   self.popupViewController = [[OmniboxPopupViewController alloc] init];
   self.popupViewController.incognito = self.browserState->IsOffTheRecord();
 
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.h b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.h
index 16997a9..5e11d713 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.h
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.h
@@ -13,6 +13,7 @@
 
 @protocol BrowserCommands;
 @class OmniboxPopupPresenter;
+class WebStateList;
 
 namespace image_fetcher {
 class IOSImageDataFetcherWrapper;
@@ -57,6 +58,8 @@
 // Presenter for the popup, handling the positioning and the presentation
 // animations.
 @property(nonatomic, strong) OmniboxPopupPresenter* presenter;
+// The web state list this mediator is handling.
+@property(nonatomic, assign) WebStateList* webStateList;
 
 @end
 
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm
index e7a0859..39a0761 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm
@@ -4,15 +4,19 @@
 
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.h"
 
+#include "base/feature_list.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #import "components/image_fetcher/ios/ios_image_data_fetcher_wrapper.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_result.h"
+#include "components/omnibox/browser/omnibox_field_trial.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
+#import "ios/chrome/browser/ui/ntp/ntp_util.h"
 #import "ios/chrome/browser/ui/omnibox/autocomplete_match_formatter.h"
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -54,6 +58,14 @@
   self.hasResults = !_currentResult.empty();
 
   [self.consumer updateMatches:[self wrappedMatches] withAnimation:animation];
+
+  BOOL shortcutsEnabled = base::FeatureList::IsEnabled(
+      omnibox::kOmniboxPopupShortcutIconsInZeroState);
+  BOOL isNTP = IsVisibleUrlNewTabPage(self.webStateList->GetActiveWebState());
+
+  if (!self.hasResults && (!shortcutsEnabled || isNTP)) {
+    [self.presenter animateCollapse];
+  }
 }
 
 - (NSArray<id<AutocompleteSuggestion>>*)wrappedMatches {
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/BUILD.gn b/ios/chrome/browser/ui/omnibox/popup/shortcuts/BUILD.gn
index ab6b8dc0..c4c2852 100644
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/BUILD.gn
+++ b/ios/chrome/browser/ui/omnibox/popup/shortcuts/BUILD.gn
@@ -4,6 +4,10 @@
 
 source_set("shortcuts") {
   sources = [
+    "collection_shortcut_cell.h",
+    "collection_shortcut_cell.mm",
+    "most_visited_shortcut_cell.h",
+    "most_visited_shortcut_cell.mm",
     "shortcuts_coordinator.h",
     "shortcuts_coordinator.mm",
     "shortcuts_view_controller.h",
@@ -41,6 +45,7 @@
     "//components/reading_list/core",
     "//ios/chrome/browser/ntp_tiles",
     "//ios/chrome/browser/ui",
+    "//ios/chrome/browser/ui/commands:commands",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/favicon",
     "//ios/chrome/browser/ui/toolbar/public",
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/collection_shortcut_cell.h b/ios/chrome/browser/ui/omnibox/popup/shortcuts/collection_shortcut_cell.h
new file mode 100644
index 0000000..4b2d10d
--- /dev/null
+++ b/ios/chrome/browser/ui/omnibox/popup/shortcuts/collection_shortcut_cell.h
@@ -0,0 +1,20 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_COLLECTION_SHORTCUT_CELL_H_
+#define IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_COLLECTION_SHORTCUT_CELL_H_
+
+#import <UIKit/UIKit.h>
+
+@class NTPShortcutTileView;
+
+// A collection view subclass that contains a collection shortcut tile.
+@interface CollectionShortcutCell : UICollectionViewCell
+
+// The tile contained in the cell.
+@property(nonatomic, strong) NTPShortcutTileView* tile;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_COLLECTION_SHORTCUT_CELL_H_
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/collection_shortcut_cell.mm b/ios/chrome/browser/ui/omnibox/popup/shortcuts/collection_shortcut_cell.mm
new file mode 100644
index 0000000..f018a24
--- /dev/null
+++ b/ios/chrome/browser/ui/omnibox/popup/shortcuts/collection_shortcut_cell.mm
@@ -0,0 +1,27 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/collection_shortcut_cell.h"
+
+#import "ios/chrome/browser/ui/ntp_tile_views/ntp_shortcut_tile_view.h"
+#import "ios/chrome/common/ui_util/constraints_ui_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation CollectionShortcutCell
+
+- (instancetype)initWithFrame:(CGRect)frame {
+  self = [super initWithFrame:frame];
+  if (self) {
+    _tile = [[NTPShortcutTileView alloc] init];
+    _tile.translatesAutoresizingMaskIntoConstraints = NO;
+    [self.contentView addSubview:_tile];
+    AddSameConstraints(self.contentView, _tile);
+  }
+  return self;
+}
+
+@end
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/most_visited_shortcut_cell.h b/ios/chrome/browser/ui/omnibox/popup/shortcuts/most_visited_shortcut_cell.h
new file mode 100644
index 0000000..ecb3993
--- /dev/null
+++ b/ios/chrome/browser/ui/omnibox/popup/shortcuts/most_visited_shortcut_cell.h
@@ -0,0 +1,20 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_MOST_VISITED_SHORTCUT_CELL_H_
+#define IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_MOST_VISITED_SHORTCUT_CELL_H_
+
+#import <UIKit/UIKit.h>
+
+@class NTPMostVisitedTileView;
+
+// A collection view subclass that contains a most visited tile.
+@interface MostVisitedShortcutCell : UICollectionViewCell
+
+// The tile contained in the cell.
+@property(nonatomic, strong) NTPMostVisitedTileView* tile;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_MOST_VISITED_SHORTCUT_CELL_H_
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/most_visited_shortcut_cell.mm b/ios/chrome/browser/ui/omnibox/popup/shortcuts/most_visited_shortcut_cell.mm
new file mode 100644
index 0000000..b8e41da1
--- /dev/null
+++ b/ios/chrome/browser/ui/omnibox/popup/shortcuts/most_visited_shortcut_cell.mm
@@ -0,0 +1,32 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/most_visited_shortcut_cell.h"
+
+#import "ios/chrome/browser/ui/ntp_tile_views/ntp_most_visited_tile_view.h"
+#import "ios/chrome/common/ui_util/constraints_ui_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation MostVisitedShortcutCell
+
+- (instancetype)initWithFrame:(CGRect)frame {
+  self = [super initWithFrame:frame];
+  if (self) {
+    _tile = [[NTPMostVisitedTileView alloc] init];
+    _tile.translatesAutoresizingMaskIntoConstraints = NO;
+    [self.contentView addSubview:_tile];
+    AddSameConstraints(self.contentView, _tile);
+  }
+  return self;
+}
+
+- (void)prepareForReuse {
+  [super prepareForReuse];
+  self.tile.titleLabel.text = nil;
+}
+
+@end
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_mediator.mm b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_mediator.mm
index 458cc85e..5621d93 100644
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_mediator.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_mediator.mm
@@ -8,6 +8,8 @@
 #include "components/ntp_tiles/ntp_tile.h"
 #include "components/reading_list/core/reading_list_model.h"
 #include "ios/chrome/browser/ntp_tiles/most_visited_sites_observer_bridge.h"
+#import "ios/chrome/browser/ui/commands/application_commands.h"
+#import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/favicon/favicon_attributes_provider.h"
 #import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_consumer.h"
 #import "ios/chrome/browser/ui/toolbar/public/omnibox_focuser.h"
@@ -98,6 +100,24 @@
   [self.dispatcher cancelOmniboxEdit];
 }
 
+- (void)openBookmarks {
+  [self.dispatcher showBookmarksManager];
+  [self.dispatcher cancelOmniboxEdit];
+}
+
+- (void)openReadingList {
+  [self.dispatcher showReadingList];
+  [self.dispatcher cancelOmniboxEdit];
+}
+- (void)openRecentTabs {
+  [self.dispatcher showRecentTabs];
+  [self.dispatcher cancelOmniboxEdit];
+}
+- (void)openHistory {
+  [self.dispatcher showHistory];
+  [self.dispatcher cancelOmniboxEdit];
+}
+
 #pragma mark - MostVisitedSitesObserving
 
 - (void)onMostVisitedURLsAvailable:
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.mm b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.mm
index 518cd6e..18a5ce8 100644
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.mm
@@ -6,7 +6,10 @@
 
 #include "base/logging.h"
 #import "ios/chrome/browser/ui/ntp_tile_views/ntp_most_visited_tile_view.h"
+#import "ios/chrome/browser/ui/ntp_tile_views/ntp_shortcut_tile_view.h"
 #import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.h"
+#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/collection_shortcut_cell.h"
+#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/most_visited_shortcut_cell.h"
 #import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller_delegate.h"
 #import "ios/chrome/common/favicon/favicon_view.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
@@ -20,40 +23,12 @@
 const CGFloat kLineSpacing = 30;
 const CGFloat kItemSpacing = 10;
 const CGFloat kTopInset = 10;
+
+const NSInteger kMostVisitedSection = 0;
+const NSInteger kCollectionShortcutSection = 1;
+
 }  // namespace
 
-#pragma mark - ShortcutCell
-
-// A collection view subclass that contains a most visited tile.
-@interface ShortcutCell : UICollectionViewCell
-
-// The tile contained in the cell.
-@property(nonatomic, strong) NTPMostVisitedTileView* tile;
-
-@end
-
-@implementation ShortcutCell
-
-- (instancetype)initWithFrame:(CGRect)frame {
-  self = [super initWithFrame:frame];
-  if (self) {
-    _tile = [[NTPMostVisitedTileView alloc] init];
-    _tile.translatesAutoresizingMaskIntoConstraints = NO;
-    [self.contentView addSubview:_tile];
-    AddSameConstraints(self.contentView, _tile);
-  }
-  return self;
-}
-
-- (void)prepareForReuse {
-  [super prepareForReuse];
-  self.tile.titleLabel.text = nil;
-}
-
-@end
-
-#pragma mark - ShortcutsViewController
-
 @interface ShortcutsViewController ()<UICollectionViewDelegate,
                                       UICollectionViewDataSource>
 
@@ -99,8 +74,12 @@
   _collectionView.delegate = self;
   _collectionView.dataSource = self;
   _collectionView.backgroundColor = [UIColor clearColor];
-  [_collectionView registerClass:[ShortcutCell class]
-      forCellWithReuseIdentifier:NSStringFromClass([ShortcutCell class])];
+  [_collectionView registerClass:[MostVisitedShortcutCell class]
+      forCellWithReuseIdentifier:NSStringFromClass(
+                                     [MostVisitedShortcutCell class])];
+  [_collectionView registerClass:[CollectionShortcutCell class]
+      forCellWithReuseIdentifier:NSStringFromClass(
+                                     [CollectionShortcutCell class])];
 
   return _collectionView;
 }
@@ -144,6 +123,11 @@
 
 #pragma mark - UICollectionViewDataSource
 
+- (NSInteger)numberOfSectionsInCollectionView:
+    (UICollectionView*)collectionView {
+  return 2;
+}
+
 - (NSInteger)collectionView:(UICollectionView*)collectionView
      numberOfItemsInSection:(NSInteger)section {
   return kNumberOfItemsPerRow;
@@ -153,23 +137,70 @@
 // -dequeueReusableCellWithReuseIdentifier:forIndexPath:
 - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
                  cellForItemAtIndexPath:(NSIndexPath*)indexPath {
-  ShortcutCell* cell = [self.collectionView
-      dequeueReusableCellWithReuseIdentifier:NSStringFromClass(
-                                                 [ShortcutCell class])
-                                forIndexPath:indexPath];
-  ShortcutsMostVisitedItem* item = self.mostVisitedItems[indexPath.item];
+  if (indexPath.section == kMostVisitedSection) {
+    MostVisitedShortcutCell* cell = [self.collectionView
+        dequeueReusableCellWithReuseIdentifier:
+            NSStringFromClass([MostVisitedShortcutCell class])
+                                  forIndexPath:indexPath];
+    ShortcutsMostVisitedItem* item = self.mostVisitedItems[indexPath.item];
+    [self configureMostVisitedCell:cell withItem:item];
+    return cell;
+  }
+
+  if (indexPath.section == kCollectionShortcutSection) {
+    CollectionShortcutCell* cell = [self.collectionView
+        dequeueReusableCellWithReuseIdentifier:
+            NSStringFromClass([CollectionShortcutCell class])
+                                  forIndexPath:indexPath];
+    DCHECK(indexPath.item < 4) << "Only four collection shortcuts described in "
+                                  "NTPCollectionShortcutType are supported";
+    NTPCollectionShortcutType type = (NTPCollectionShortcutType)indexPath.item;
+    [self configureCollectionShortcutCell:cell withCollection:type];
+    return cell;
+  }
+
+  return nil;
+}
+
+- (void)configureMostVisitedCell:(MostVisitedShortcutCell*)cell
+                        withItem:(ShortcutsMostVisitedItem*)item {
   [cell.tile.faviconView configureWithAttributes:item.attributes];
   cell.tile.titleLabel.text = item.title;
-  return cell;
+}
+
+- (void)configureCollectionShortcutCell:(CollectionShortcutCell*)cell
+                         withCollection:(NTPCollectionShortcutType)type {
+  cell.tile.titleLabel.text = TitleForCollectionShortcutType(type);
+  cell.tile.iconView.image = ImageForCollectionShortcutType(type);
 }
 
 #pragma mark - UICollectionViewDelegate
 
 - (void)collectionView:(UICollectionView*)collectionView
     didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
-  ShortcutsMostVisitedItem* item = self.mostVisitedItems[indexPath.item];
-  DCHECK(item);
-  [self.commandHandler openMostVisitedItem:item];
+  if (indexPath.section == kMostVisitedSection) {
+    ShortcutsMostVisitedItem* item = self.mostVisitedItems[indexPath.item];
+    DCHECK(item);
+    [self.commandHandler openMostVisitedItem:item];
+  }
+
+  if (indexPath.section == kCollectionShortcutSection) {
+    NTPCollectionShortcutType type = (NTPCollectionShortcutType)indexPath.item;
+    switch (type) {
+      case NTPCollectionShortcutTypeBookmark:
+        [self.commandHandler openBookmarks];
+        break;
+      case NTPCollectionShortcutTypeRecentTabs:
+        [self.commandHandler openRecentTabs];
+        break;
+      case NTPCollectionShortcutTypeReadingList:
+        [self.commandHandler openReadingList];
+        break;
+      case NTPCollectionShortcutTypeHistory:
+        [self.commandHandler openHistory];
+        break;
+    }
+  }
 }
 
 @end
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller_delegate.h b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller_delegate.h
index c43ffee..cc031506 100644
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller_delegate.h
+++ b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller_delegate.h
@@ -13,6 +13,14 @@
 
 // Called when a most visited shortcut is selected by the user.
 - (void)openMostVisitedItem:(ShortcutsMostVisitedItem*)item;
+// Opens the bookmarks screen and defocuses the omnibox.
+- (void)openBookmarks;
+// Opens the reading list screen and defocuses the omnibox.
+- (void)openReadingList;
+// Opens the recent tabs screen and defocuses the omnibox.
+- (void)openRecentTabs;
+// Opens the history screen and defocuses the omnibox.
+- (void)openHistory;
 
 @end
 
diff --git a/ios/chrome/browser/ui/recent_tabs/BUILD.gn b/ios/chrome/browser/ui/recent_tabs/BUILD.gn
index ebfc2ea..1c2efbb 100644
--- a/ios/chrome/browser/ui/recent_tabs/BUILD.gn
+++ b/ios/chrome/browser/ui/recent_tabs/BUILD.gn
@@ -64,6 +64,7 @@
     "//components/sessions",
     "//components/strings",
     "//components/sync",
+    "//components/unified_consent",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/metrics:metrics_internal",
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
index 3ed5dfac..5aaf976 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
@@ -15,6 +15,7 @@
 #include "components/strings/grit/components_strings.h"
 #include "components/sync/driver/sync_service.h"
 #include "components/sync_sessions/open_tabs_ui_delegate.h"
+#include "components/unified_consent/feature.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/metrics/new_tab_page_uma.h"
 #include "ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h"
@@ -460,8 +461,13 @@
   // Configure and add a TableViewSigninPromoItem to the model.
   TableViewSigninPromoItem* signinPromoItem = [[TableViewSigninPromoItem alloc]
       initWithType:ItemTypeOtherDevicesSigninPromo];
-  signinPromoItem.text =
-      l10n_util::GetNSString(IDS_IOS_SIGNIN_PROMO_RECENT_TABS);
+  if (unified_consent::IsUnifiedConsentFeatureEnabled()) {
+    signinPromoItem.text =
+        l10n_util::GetNSString(IDS_IOS_SIGNIN_PROMO_RECENT_TABS_WITH_UNITY);
+  } else {
+    signinPromoItem.text =
+        l10n_util::GetNSString(IDS_IOS_SIGNIN_PROMO_RECENT_TABS);
+  }
   signinPromoItem.delegate = self.signinPromoViewMediator;
   signinPromoItem.configurator =
       [self.signinPromoViewMediator createConfigurator];
diff --git a/ios/chrome/browser/ui/settings/save_passwords_collection_view_controller.mm b/ios/chrome/browser/ui/settings/save_passwords_collection_view_controller.mm
index 04e9b7b..bcb7fc09 100644
--- a/ios/chrome/browser/ui/settings/save_passwords_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/save_passwords_collection_view_controller.mm
@@ -831,12 +831,14 @@
     if (!strongSelf)
       return;
     [strongSelf clearSectionWithIdentifier:SectionIdentifierSavedPasswords
-                                   ifEmpty:savedForms_.empty()];
-    [strongSelf clearSectionWithIdentifier:SectionIdentifierBlacklist
-                                   ifEmpty:blacklistedForms_.empty()];
-    [strongSelf clearSectionWithIdentifier:SectionIdentifierSearchPasswordsBox
-                                   ifEmpty:savedForms_.empty() &&
-                                           blacklistedForms_.empty()];
+                                   ifEmpty:strongSelf->savedForms_.empty()];
+    [strongSelf
+        clearSectionWithIdentifier:SectionIdentifierBlacklist
+                           ifEmpty:strongSelf->blacklistedForms_.empty()];
+    [strongSelf
+        clearSectionWithIdentifier:SectionIdentifierSearchPasswordsBox
+                           ifEmpty:strongSelf->savedForms_.empty() &&
+                                   strongSelf->blacklistedForms_.empty()];
   }
       completion:^(BOOL finished) {
         SavePasswordsCollectionViewController* strongSelf = weakSelf;
diff --git a/ios/chrome/browser/web/chrome_web_client.mm b/ios/chrome/browser/web/chrome_web_client.mm
index 9779c9f..07010c28 100644
--- a/ios/chrome/browser/web/chrome_web_client.mm
+++ b/ios/chrome/browser/web/chrome_web_client.mm
@@ -211,6 +211,15 @@
                                        bool is_off_the_record,
                                        NSString** error_html) {
   DCHECK(error);
+
+  // Return an empty page for the NTP.
+  NSString* url_spec = error.userInfo[NSURLErrorFailingURLStringErrorKey];
+  GURL url(base::SysNSStringToUTF16(url_spec));
+  if (url.GetOrigin() == kChromeUINewTabURL) {
+    *error_html = @"";
+    return;
+  }
+
   *error_html = GetErrorPage(error, is_post, is_off_the_record);
 }
 
diff --git a/ios/chrome/browser/web/chrome_web_client_unittest.mm b/ios/chrome/browser/web/chrome_web_client_unittest.mm
index f98d759..46e2bad 100644
--- a/ios/chrome/browser/web/chrome_web_client_unittest.mm
+++ b/ios/chrome/browser/web/chrome_web_client_unittest.mm
@@ -14,6 +14,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
 #include "ios/chrome/browser/passwords/credential_manager_features.h"
 #import "ios/chrome/browser/web/error_page_util.h"
 #import "ios/web/public/test/error_test_util.h"
@@ -220,3 +221,20 @@
   EXPECT_NSEQ(GetErrorPage(error, /*is_post=*/true, /*is_off_the_record=*/true),
               page);
 }
+
+// Tests PrepareErrorPage wth NTP and an empty string.
+TEST_F(ChromeWebClientTest, PrepareErrorPageNTP) {
+  ChromeWebClient web_client;
+  NSString* ntp_url = base::SysUTF8ToNSString(kChromeUINewTabURL);
+  NSDictionary* info = @{
+    NSURLErrorFailingURLStringErrorKey : ntp_url,
+  };
+  NSError* error = web::testing::CreateTestNetError([NSError
+      errorWithDomain:NSURLErrorDomain
+                 code:NSURLErrorNetworkConnectionLost
+             userInfo:info]);
+  NSString* page = nil;
+  web_client.PrepareErrorPage(error, /*is_post=*/false,
+                              /*is_off_the_record=*/false, &page);
+  EXPECT_NSEQ(@"", page);
+}
diff --git a/ios/third_party/material_components_ios/README.chromium b/ios/third_party/material_components_ios/README.chromium
index 184391a..b7be989 100644
--- a/ios/third_party/material_components_ios/README.chromium
+++ b/ios/third_party/material_components_ios/README.chromium
@@ -1,7 +1,7 @@
 Name: Material Components for iOS
 URL: https://github.com/material-components/material-components-ios
 Version: 0
-Revision: d57bd2777a261d3503c745fa5a7e080f9523a676
+Revision: ec1a04b2e9872b2b96aa294004b58c0a097b88cc
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/ios/web/features.mm b/ios/web/features.mm
index b30e3b74..0d8be623 100644
--- a/ios/web/features.mm
+++ b/ios/web/features.mm
@@ -17,16 +17,16 @@
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kWKHTTPSystemCookieStore{"WKHTTPSystemCookieStore",
-                                             base::FEATURE_ENABLED_BY_DEFAULT};
+                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kCrashOnUnexpectedURLChange{
     "CrashOnUnexpectedURLChange", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kBrowserContainerFullscreen{
-    "BrowserContainerFullscreen", base::FEATURE_DISABLED_BY_DEFAULT};
+    "BrowserContainerFullscreen", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kOutOfWebFullscreen{"OutOfWebFullscreen",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
+                                        base::FEATURE_ENABLED_BY_DEFAULT};
 
 }  // namespace features
 }  // namespace web
diff --git a/ios/web/public/test/web_view_interaction_test_util.h b/ios/web/public/test/web_view_interaction_test_util.h
index 899d0c1..f2a5ed6f 100644
--- a/ios/web/public/test/web_view_interaction_test_util.h
+++ b/ios/web/public/test/web_view_interaction_test_util.h
@@ -57,6 +57,11 @@
 // submitted using a JavaScript submit() event.
 bool SubmitWebViewFormWithId(web::WebState* web_state,
                              const std::string& form_id);
+
+// Returns whether the <option id="|element_id|"> in the passed |web_state| has
+// been selected using a JavaScript "selected=true" operation.
+bool SelectWebViewElementWithId(web::WebState* web_state,
+                                const std::string& element_id);
 }  // namespace test
 }  // namespace web
 
diff --git a/ios/web/public/test/web_view_interaction_test_util.mm b/ios/web/public/test/web_view_interaction_test_util.mm
index cd4776a6..f9ed947 100644
--- a/ios/web/public/test/web_view_interaction_test_util.mm
+++ b/ios/web/public/test/web_view_interaction_test_util.mm
@@ -29,7 +29,8 @@
 enum ElementAction {
   ELEMENT_ACTION_CLICK,
   ELEMENT_ACTION_FOCUS,
-  ELEMENT_ACTION_SUBMIT
+  ELEMENT_ACTION_SUBMIT,
+  ELEMENT_ACTION_SELECT,
 };
 
 std::unique_ptr<base::Value> ExecuteJavaScript(web::WebState* web_state,
@@ -158,6 +159,9 @@
     case ELEMENT_ACTION_SUBMIT:
       js_action = ".submit();";
       break;
+    case ELEMENT_ACTION_SELECT:
+      js_action = ".selected = true;";
+      break;
   }
   NSString* script = [NSString stringWithFormat:
                                    @"(function() {"
@@ -240,5 +244,11 @@
                                          ELEMENT_ACTION_SUBMIT, nil);
 }
 
+bool SelectWebViewElementWithId(web::WebState* web_state,
+                                const std::string& element_id) {
+  return RunActionOnWebViewElementWithId(web_state, element_id,
+                                         ELEMENT_ACTION_SELECT, nil);
+}
+
 }  // namespace test
 }  // namespace web
diff --git a/ios/web/web_state/ui/crw_web_view_content_view_unittest.mm b/ios/web/web_state/ui/crw_web_view_content_view_unittest.mm
index c5d3022..7e1d0efc 100644
--- a/ios/web/web_state/ui/crw_web_view_content_view_unittest.mm
+++ b/ios/web/web_state/ui/crw_web_view_content_view_unittest.mm
@@ -6,6 +6,7 @@
 
 #import <UIKit/UIKit.h>
 
+#import "ios/web/public/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
@@ -42,6 +43,11 @@
 
 // Tests the ContentInset method when shouldUseViewContentInset is set to NO.
 TEST_F(CRWWebViewContentViewTest, ContentInsetWithoutInsetForPadding) {
+  // This functionality has been moved out of the web// layer when
+  // kOutOfWebFullscreen is enabled.
+  if (base::FeatureList::IsEnabled(web::features::kOutOfWebFullscreen))
+    return;
+
   UIView* webView = [[UIView alloc] init];
   UIScrollView* scrollView = [[UIScrollView alloc] init];
   [webView addSubview:scrollView];
diff --git a/ios/web_view/test/web_view_autofill_inttest.mm b/ios/web_view/test/web_view_autofill_inttest.mm
index 6b7914f0..7efa958 100644
--- a/ios/web_view/test/web_view_autofill_inttest.mm
+++ b/ios/web_view/test/web_view_autofill_inttest.mm
@@ -270,7 +270,8 @@
 }
 
 // Tests that CWVAutofillController can remove a suggestion.
-TEST_F(WebViewAutofillTest, TestSuggestionFetchRemoveFetch) {
+TEST_F(WebViewAutofillTest, DISABLED_TestSuggestionFetchRemoveFetch) {
+  // TODO(crbug.com/898990): Needs to be reenabled.
   ASSERT_TRUE(test_server_->Start());
   ASSERT_TRUE(LoadTestPage());
   ASSERT_TRUE(SetFormFieldValue(kTestNameFieldID, kTestNameFieldValue));
diff --git a/media/base/limits.h b/media/base/limits.h
index 5b2cdad..2b3e811 100644
--- a/media/base/limits.h
+++ b/media/base/limits.h
@@ -74,6 +74,10 @@
 
   // Maximum buffer size supported by Web Audio.
   kMaxWebAudioBufferSize = 8192,
+
+  // Bounds for the number of threads used for software video decoding.
+  kMinVideoDecodeThreads = 2,
+  kMaxVideoDecodeThreads = 32,
 };
 
 }  // namespace limits
diff --git a/media/base/test_data_util.cc b/media/base/test_data_util.cc
index f41c68f..5670294 100644
--- a/media/base/test_data_util.cc
+++ b/media/base/test_data_util.cc
@@ -20,35 +20,35 @@
 
 // Mime types for test files. Sorted in the ASCII code order of the variable
 // names.
-const char kAacAdtsAudioOnly[] = "audio/aac";
+const char kAacAdtsAudio[] = "audio/aac";
 const char kMp2AudioSBR[] = "video/mp2t; codecs=\"avc1.4D4041,mp4a.40.5\"";
 const char kMp2tAudioVideo[] = "video/mp2t; codecs=\"mp4a.40.2, avc1.42E01E\"";
-const char kMp3AudioOnly[] = "audio/mpeg";
+const char kMp3Audio[] = "audio/mpeg";
 // MP4
-const char kMp4AacAudioOnly[] = "audio/mp4; codecs=\"mp4a.40.2\"";
-const char kMp4Av110bitVideoOnly[] = "video/mp4; codecs=\"av01.0.04M.10\"";
-const char kMp4Av1VideoOnly[] = "video/mp4; codecs=\"av01.0.04M.08\"";
-const char kMp4Avc1VideoOnly[] = "video/mp4; codecs=\"avc1.64001E\"";
+const char kMp4AacAudio[] = "audio/mp4; codecs=\"mp4a.40.2\"";
+const char kMp4Av110bitVideo[] = "video/mp4; codecs=\"av01.0.04M.10\"";
+const char kMp4Av1Video[] = "video/mp4; codecs=\"av01.0.04M.08\"";
+const char kMp4Avc1Video[] = "video/mp4; codecs=\"avc1.64001E\"";
 const char kMp4AacAudioAvc1Video[] =
     "video/mp4; codecs=\"mp4a.40.2, avc1.64001E\"";
-const char kMp4Avc3VideoOnly[] = "video/mp4; codecs=\"avc3.64001f\"";
-const char kMp4FlacAudioOnly[] = "audio/mp4; codecs=\"flac\"";
-const char kMp4OpusAudioOnly[] = "audio/mp4; codecs=\"opus\"";
-const char kMp4Vp9Profile2VideoOnly[] =
+const char kMp4Avc3Video[] = "video/mp4; codecs=\"avc3.64001f\"";
+const char kMp4FlacAudio[] = "audio/mp4; codecs=\"flac\"";
+const char kMp4OpusAudio[] = "audio/mp4; codecs=\"opus\"";
+const char kMp4Vp9Profile2Video[] =
     "video/mp4; codecs=\"vp09.02.10.10.01.02.02.02.00\"";
-const char kMp4Vp9VideoOnly[] =
+const char kMp4Vp9Video[] =
     "video/mp4; codecs=\"vp09.00.10.08.01.02.02.02.00\"";
 // WebM
-const char kWebMAv110bitVideoOnly[] = "video/webm; codecs=\"av01.0.04M.10\"";
-const char kWebMAv1VideoOnly[] = "video/webm; codecs=\"av01.0.04M.08\"";
-const char kWebMOpusAudioOnly[] = "audio/webm; codecs=\"opus\"";
+const char kWebMAv110bitVideo[] = "video/webm; codecs=\"av01.0.04M.10\"";
+const char kWebMAv1Video[] = "video/webm; codecs=\"av01.0.04M.08\"";
+const char kWebMOpusAudio[] = "audio/webm; codecs=\"opus\"";
 const char kWebMOpusAudioVp9Video[] = "video/webm; codecs=\"opus, vp9\"";
-const char kWebMVorbisAudioOnly[] = "audio/webm; codecs=\"vorbis\"";
+const char kWebMVorbisAudio[] = "audio/webm; codecs=\"vorbis\"";
 const char kWebMVorbisAudioVp8Video[] = "video/webm; codecs=\"vorbis, vp8\"";
-const char kWebMVp8VideoOnly[] = "video/webm; codecs=\"vp8\"";
-const char kWebMVp9Profile2VideoOnly[] =
+const char kWebMVp8Video[] = "video/webm; codecs=\"vp8\"";
+const char kWebMVp9Profile2Video[] =
     "video/webm; codecs=\"vp09.02.10.10.01.02.02.02.00\"";
-const char kWebMVp9VideoOnly[] = "video/webm; codecs=\"vp9\"";
+const char kWebMVp9Video[] = "video/webm; codecs=\"vp9\"";
 
 // A map from a test file name to its mime type. The file is located at
 // media/test/data.
@@ -59,89 +59,89 @@
 // Note: Some files are old and the codec string in the mime type may not be
 // accurate.
 // Warning: When adding new files, make sure the codec string is accurate. For
-// example kMp4Avc1VideoOnly is for H264 high profile. If you add a file that
-// uses main profile, a new mime type should be added.
+// example kMp4Avc1Video is for H264 high profile. If you add a file that uses
+// main profile, a new mime type should be added.
 const FileToMimeTypeMap& GetFileToMimeTypeMap() {
   static const base::NoDestructor<FileToMimeTypeMap> kFileToMimeTypeMap({
-      {"bear-1280x720-a_frag-cenc-key_rotation.mp4", kMp4AacAudioOnly},
-      {"bear-1280x720-a_frag-cenc.mp4", kMp4AacAudioOnly},
-      {"bear-1280x720-a_frag-cenc_clear-all.mp4", kMp4AacAudioOnly},
+      {"bear-1280x720-a_frag-cenc-key_rotation.mp4", kMp4AacAudio},
+      {"bear-1280x720-a_frag-cenc.mp4", kMp4AacAudio},
+      {"bear-1280x720-a_frag-cenc_clear-all.mp4", kMp4AacAudio},
       {"bear-1280x720-aac_he.ts", kMp2AudioSBR},
-      {"bear-1280x720-v_frag-avc3.mp4", kMp4Avc3VideoOnly},
-      {"bear-1280x720-v_frag-cenc-key_rotation.mp4", kMp4Avc1VideoOnly},
-      {"bear-1280x720-v_frag-cenc.mp4", kMp4Avc1VideoOnly},
-      {"bear-1280x720-v_frag-cenc_clear-all.mp4", kMp4Avc1VideoOnly},
+      {"bear-1280x720-v_frag-avc3.mp4", kMp4Avc3Video},
+      {"bear-1280x720-v_frag-cenc-key_rotation.mp4", kMp4Avc1Video},
+      {"bear-1280x720-v_frag-cenc.mp4", kMp4Avc1Video},
+      {"bear-1280x720-v_frag-cenc_clear-all.mp4", kMp4Avc1Video},
       {"bear-1280x720.ts", kMp2tAudioVideo},
       {"bear-320x240-16x9-aspect-av_enc-av.webm", kWebMVorbisAudioVp8Video},
       {"bear-320x240-16x9-aspect.webm", kWebMVorbisAudioVp8Video},
-      {"bear-320x240-audio-only.webm", kWebMVorbisAudioOnly},
+      {"bear-320x240-audio-only.webm", kWebMVorbisAudio},
       {"bear-320x240-av_enc-a.webm", kWebMVorbisAudioVp8Video},
       {"bear-320x240-av_enc-av.webm", kWebMVorbisAudioVp8Video},
       {"bear-320x240-av_enc-av_clear-1s.webm", kWebMVorbisAudioVp8Video},
       {"bear-320x240-av_enc-av_clear-all.webm", kWebMVorbisAudioVp8Video},
       {"bear-320x240-av_enc-v.webm", kWebMVorbisAudioVp8Video},
       {"bear-320x240-live.webm", kWebMVorbisAudioVp8Video},
-      {"bear-320x240-opus-a_enc-a.webm", kWebMOpusAudioOnly},
+      {"bear-320x240-opus-a_enc-a.webm", kWebMOpusAudio},
       {"bear-320x240-opus-av_enc-av.webm", kWebMOpusAudioVp9Video},
       {"bear-320x240-opus-av_enc-v.webm", kWebMOpusAudioVp9Video},
-      {"bear-320x240-v-vp9_fullsample_enc-v.webm", kWebMVp9VideoOnly},
+      {"bear-320x240-v-vp9_fullsample_enc-v.webm", kWebMVp9Video},
       {"bear-320x240-v-vp9_profile2_subsample_cenc-v.mp4",
-       kMp4Vp9Profile2VideoOnly},
+       kMp4Vp9Profile2Video},
       {"bear-320x240-v-vp9_profile2_subsample_cenc-v.webm",
-       kWebMVp9Profile2VideoOnly},
-      {"bear-320x240-v-vp9_subsample_enc-v.webm", kWebMVp9VideoOnly},
-      {"bear-320x240-v_enc-v.webm", kWebMVp8VideoOnly},
-      {"bear-320x240-v_frag-vp9-cenc.mp4", kMp4Vp9VideoOnly},
-      {"bear-320x240-v_frag-vp9.mp4", kMp4Vp9VideoOnly},
-      {"bear-320x240-video-only.webm", kWebMVp8VideoOnly},
+       kWebMVp9Profile2Video},
+      {"bear-320x240-v-vp9_subsample_enc-v.webm", kWebMVp9Video},
+      {"bear-320x240-v_enc-v.webm", kWebMVp8Video},
+      {"bear-320x240-v_frag-vp9-cenc.mp4", kMp4Vp9Video},
+      {"bear-320x240-v_frag-vp9.mp4", kMp4Vp9Video},
+      {"bear-320x240-video-only.webm", kWebMVp8Video},
       {"bear-320x240.webm", kWebMVorbisAudioVp8Video},
       {"bear-320x240_corrupted_after_init_segment.webm",
        kWebMVorbisAudioVp8Video},
-      {"bear-640x360-a_frag-cbcs.mp4", kMp4AacAudioOnly},
-      {"bear-640x360-a_frag-cenc.mp4", kMp4AacAudioOnly},
-      {"bear-640x360-a_frag.mp4", kMp4AacAudioOnly},
+      {"bear-640x360-a_frag-cbcs.mp4", kMp4AacAudio},
+      {"bear-640x360-a_frag-cenc.mp4", kMp4AacAudio},
+      {"bear-640x360-a_frag.mp4", kMp4AacAudio},
       {"bear-640x360-av_frag.mp4", kMp4AacAudioAvc1Video},
-      {"bear-640x360-v_frag-cbc1.mp4", kMp4Avc1VideoOnly},
-      {"bear-640x360-v_frag-cbcs.mp4", kMp4Avc1VideoOnly},
-      {"bear-640x360-v_frag-cenc-key_rotation.mp4", kMp4Avc1VideoOnly},
-      {"bear-640x360-v_frag-cenc-mdat.mp4", kMp4Avc1VideoOnly},
-      {"bear-640x360-v_frag-cenc-senc-no-saiz-saio.mp4", kMp4Avc1VideoOnly},
-      {"bear-640x360-v_frag-cenc-senc.mp4", kMp4Avc1VideoOnly},
-      {"bear-640x360-v_frag-cenc.mp4", kMp4Avc1VideoOnly},
-      {"bear-640x360-v_frag-cens.mp4", kMp4Avc1VideoOnly},
-      {"bear-640x360-v_frag.mp4", kMp4Avc1VideoOnly},
-      {"bear-a_enc-a.webm", kWebMVorbisAudioOnly},
-      {"bear-audio-implicit-he-aac-v1.aac", kAacAdtsAudioOnly},
-      {"bear-audio-implicit-he-aac-v2.aac", kAacAdtsAudioOnly},
-      {"bear-audio-lc-aac.aac", kAacAdtsAudioOnly},
-      {"bear-audio-main-aac.aac", kAacAdtsAudioOnly},
+      {"bear-640x360-v_frag-cbc1.mp4", kMp4Avc1Video},
+      {"bear-640x360-v_frag-cbcs.mp4", kMp4Avc1Video},
+      {"bear-640x360-v_frag-cenc-key_rotation.mp4", kMp4Avc1Video},
+      {"bear-640x360-v_frag-cenc-mdat.mp4", kMp4Avc1Video},
+      {"bear-640x360-v_frag-cenc-senc-no-saiz-saio.mp4", kMp4Avc1Video},
+      {"bear-640x360-v_frag-cenc-senc.mp4", kMp4Avc1Video},
+      {"bear-640x360-v_frag-cenc.mp4", kMp4Avc1Video},
+      {"bear-640x360-v_frag-cens.mp4", kMp4Avc1Video},
+      {"bear-640x360-v_frag.mp4", kMp4Avc1Video},
+      {"bear-a_enc-a.webm", kWebMVorbisAudio},
+      {"bear-audio-implicit-he-aac-v1.aac", kAacAdtsAudio},
+      {"bear-audio-implicit-he-aac-v2.aac", kAacAdtsAudio},
+      {"bear-audio-lc-aac.aac", kAacAdtsAudio},
+      {"bear-audio-main-aac.aac", kAacAdtsAudio},
       {"bear-audio-mp4a.69.ts", "video/mp2t; codecs=\"mp4a.69\""},
       {"bear-audio-mp4a.6B.ts", "video/mp2t; codecs=\"mp4a.6B\""},
-      {"bear-av1-320x180-10bit-cenc.mp4", kMp4Av110bitVideoOnly},
-      {"bear-av1-320x180-10bit-cenc.webm", kWebMAv110bitVideoOnly},
-      {"bear-av1-320x180-10bit.mp4", kMp4Av110bitVideoOnly},
-      {"bear-av1-320x180-10bit.webm", kWebMAv110bitVideoOnly},
-      {"bear-av1-480x360.webm", kWebMAv1VideoOnly},
-      {"bear-av1-cenc.mp4", kMp4Av1VideoOnly},
-      {"bear-av1-cenc.webm", kWebMAv1VideoOnly},
-      {"bear-av1.mp4", kMp4Av1VideoOnly},
-      {"bear-av1.webm", kWebMAv1VideoOnly},
-      {"bear-flac-cenc.mp4", kMp4FlacAudioOnly},
-      {"bear-flac_frag.mp4", kMp4FlacAudioOnly},
-      {"bear-opus.mp4", kMp4OpusAudioOnly},
-      {"bear-opus.webm", kWebMOpusAudioOnly},
-      {"bear-vp8a.webm", kWebMVp8VideoOnly},
-      {"bear-vp9-blockgroup.webm", kWebMVp9VideoOnly},
-      {"bear-vp9.webm", kWebMVp9VideoOnly},
+      {"bear-av1-320x180-10bit-cenc.mp4", kMp4Av110bitVideo},
+      {"bear-av1-320x180-10bit-cenc.webm", kWebMAv110bitVideo},
+      {"bear-av1-320x180-10bit.mp4", kMp4Av110bitVideo},
+      {"bear-av1-320x180-10bit.webm", kWebMAv110bitVideo},
+      {"bear-av1-480x360.webm", kWebMAv1Video},
+      {"bear-av1-cenc.mp4", kMp4Av1Video},
+      {"bear-av1-cenc.webm", kWebMAv1Video},
+      {"bear-av1.mp4", kMp4Av1Video},
+      {"bear-av1.webm", kWebMAv1Video},
+      {"bear-flac-cenc.mp4", kMp4FlacAudio},
+      {"bear-flac_frag.mp4", kMp4FlacAudio},
+      {"bear-opus.mp4", kMp4OpusAudio},
+      {"bear-opus.webm", kWebMOpusAudio},
+      {"bear-vp8a.webm", kWebMVp8Video},
+      {"bear-vp9-blockgroup.webm", kWebMVp9Video},
+      {"bear-vp9.webm", kWebMVp9Video},
       {"frame_size_change-av_enc-v.webm", kWebMVorbisAudioVp8Video},
-      {"icy_sfx.mp3", kMp3AudioOnly},
-      {"opus-trimming-test.mp4", kMp4OpusAudioOnly},
-      {"opus-trimming-test.webm", kWebMOpusAudioOnly},
-      {"sfx-flac_frag.mp4", kMp4FlacAudioOnly},
-      {"sfx-opus-441.webm", kWebMOpusAudioOnly},
-      {"sfx-opus_frag.mp4", kMp4OpusAudioOnly},
-      {"sfx.adts", kAacAdtsAudioOnly},
-      {"sfx.mp3", kMp3AudioOnly},
+      {"icy_sfx.mp3", kMp3Audio},
+      {"opus-trimming-test.mp4", kMp4OpusAudio},
+      {"opus-trimming-test.webm", kWebMOpusAudio},
+      {"sfx-flac_frag.mp4", kMp4FlacAudio},
+      {"sfx-opus-441.webm", kWebMOpusAudio},
+      {"sfx-opus_frag.mp4", kMp4OpusAudio},
+      {"sfx.adts", kAacAdtsAudio},
+      {"sfx.mp3", kMp3Audio},
   });
 
   return *kFileToMimeTypeMap;
diff --git a/media/base/video_decoder.cc b/media/base/video_decoder.cc
index 8eeb18e..e53cfe4 100644
--- a/media/base/video_decoder.cc
+++ b/media/base/video_decoder.cc
@@ -4,6 +4,11 @@
 
 #include "media/base/video_decoder.h"
 
+#include "base/command_line.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/sys_info.h"
+#include "media/base/limits.h"
+#include "media/base/media_switches.h"
 #include "media/base/video_frame.h"
 
 namespace media {
@@ -32,6 +37,34 @@
   return 1;
 }
 
+// static
+int VideoDecoder::GetRecommendedThreadCount(int desired_threads) {
+  // If the thread count is specified on the command line, respect it so long as
+  // it's greater than zero.
+  const auto threads =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          switches::kVideoThreads);
+  int decode_threads;
+  if (base::StringToInt(threads, &decode_threads) && decode_threads > 0)
+    return decode_threads;
+
+  // Clamp to the number of available logical processors/cores.
+  desired_threads =
+      std::min(desired_threads, base::SysInfo::NumberOfProcessors());
+
+  // Always try to use at least two threads for video decoding. There is little
+  // reason not to since current day CPUs tend to be multi-core and we measured
+  // performance benefits on older machines such as P4s with hyperthreading.
+  //
+  // All our software video decoders treat having one thread the same as having
+  // zero threads; I.e., decoding will execute on the calling thread. Therefore,
+  // at least two threads are required to allow decoding to progress outside of
+  // each Decode() call.
+  return std::min(std::max(desired_threads,
+                           static_cast<int>(limits::kMinVideoDecodeThreads)),
+                  static_cast<int>(limits::kMaxVideoDecodeThreads));
+}
+
 }  // namespace media
 
 namespace std {
diff --git a/media/base/video_decoder.h b/media/base/video_decoder.h
index e822607..f9deaf3 100644
--- a/media/base/video_decoder.h
+++ b/media/base/video_decoder.h
@@ -128,6 +128,13 @@
   // Returns maximum number of parallel decode requests.
   virtual int GetMaxDecodeRequests() const;
 
+  // Returns the recommended number of threads for software video decoding. If
+  // the --video-threads command line option is specified and is valid, that
+  // value is returned. Otherwise |desired_threads| is clamped to the number of
+  // logical processors and then further clamped to
+  // [|limits::kMinVideoDecodeThreads|, |limits::kMaxVideoDecodeThreads|].
+  static int GetRecommendedThreadCount(int desired_threads);
+
  protected:
   // Deletion is only allowed via Destroy().
   virtual ~VideoDecoder();
diff --git a/media/blink/url_index.cc b/media/blink/url_index.cc
index cbf858bb2..626b272 100644
--- a/media/blink/url_index.cc
+++ b/media/blink/url_index.cc
@@ -360,6 +360,10 @@
   }
 }
 
+bool UrlIndex::HasReachedMaxParallelPreload() const {
+  return loading_.size() >= kMaxParallelPreload;
+}
+
 UrlIndex::UrlIndex(ResourceFetchContext* fetch_context)
     : UrlIndex(fetch_context, kBlockSizeShift) {}
 
diff --git a/media/blink/url_index.h b/media/blink/url_index.h
index cfe5a8f2..27a60dd 100644
--- a/media/blink/url_index.h
+++ b/media/blink/url_index.h
@@ -320,6 +320,9 @@
   ResourceFetchContext* fetch_context() const { return fetch_context_; }
   int block_shift() const { return block_shift_; }
 
+  // Returns true kMaxParallelPreload or more urls are loading at the same time.
+  bool HasReachedMaxParallelPreload() const;
+
   // Protected rather than private for testing.
  protected:
   friend class UrlData;
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 6f54a1d..91b03e7 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -1704,16 +1704,20 @@
         DisableOverlay();
     }
 
-    if (surface_layer_mode_ !=
-        blink::WebMediaPlayer::SurfaceLayerMode::kAlways) {
+    if (surface_layer_mode_ ==
+            blink::WebMediaPlayer::SurfaceLayerMode::kAlways ||
+        (surface_layer_mode_ ==
+             blink::WebMediaPlayer::SurfaceLayerMode::kOnDemand &&
+         client_->DisplayType() ==
+             WebMediaPlayer::DisplayType::kPictureInPicture)) {
+      ActivateSurfaceLayerForVideo();
+    } else {
       DCHECK(!video_layer_);
       video_layer_ = cc::VideoLayer::Create(
           compositor_.get(),
           pipeline_metadata_.video_decoder_config.video_rotation());
       video_layer_->SetContentsOpaque(opaque_);
       client_->SetCcLayer(video_layer_.get());
-    } else {
-      ActivateSurfaceLayerForVideo();
     }
   }
 
@@ -3336,6 +3340,10 @@
   const base::TimeDelta elapsed = frame_time - load_start_time_;
   media_metrics_provider_->SetTimeToFirstFrame(elapsed);
   RecordTimingUMA("Media.TimeToFirstFrame", elapsed);
+  if (url_index_->HasReachedMaxParallelPreload()) {
+    base::UmaHistogramMediumTimes("Media.TimeToFirstFrame.SRC.ManyVideos",
+                                  elapsed);
+  }
 }
 
 void WebMediaPlayerImpl::RecordTimingUMA(const std::string& key,
diff --git a/media/filters/aom_video_decoder.cc b/media/filters/aom_video_decoder.cc
index 2f322a64..920a3a7 100644
--- a/media/filters/aom_video_decoder.cc
+++ b/media/filters/aom_video_decoder.cc
@@ -6,15 +6,11 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/command_line.h"
 #include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/sys_info.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/decoder_buffer.h"
 #include "media/base/media_log.h"
-#include "media/base/media_switches.h"
 #include "media/base/video_util.h"
 #include "third_party/libyuv/include/libyuv/convert.h"
 
@@ -29,24 +25,11 @@
 
 // Returns the number of threads.
 static int GetAomVideoDecoderThreadCount(const VideoDecoderConfig& config) {
-  // Always try to use at least two threads for video decoding. There is little
-  // reason not to since current day CPUs tend to be multi-core and we measured
-  // performance benefits on older machines such as P4s with hyperthreading.
-  constexpr int kDecodeThreads = 2;
-  int decode_threads = kDecodeThreads;
-
-  const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
-  std::string threads(cmd_line->GetSwitchValueASCII(switches::kVideoThreads));
-  if (threads.empty() || !base::StringToInt(threads, &decode_threads)) {
-    // For AOM decode when using the default thread count, increase the number
-    // of decode threads to equal the maximum number of tiles possible for
-    // higher resolution streams.
-    decode_threads = std::min(config.coded_size().width() / 256,
-                              base::SysInfo::NumberOfProcessors());
-  }
-
-  constexpr int kMaxDecodeThreads = 32;
-  return std::min(std::max(decode_threads, 0), kMaxDecodeThreads);
+  // For AOM decode when using the default thread count, increase the number
+  // of decode threads to equal the maximum number of tiles possible for
+  // higher resolution streams.
+  return VideoDecoder::GetRecommendedThreadCount(config.coded_size().width() /
+                                                 256);
 }
 
 static VideoPixelFormat AomImgFmtToVideoPixelFormat(const aom_image_t* img) {
diff --git a/media/filters/ffmpeg_video_decoder.cc b/media/filters/ffmpeg_video_decoder.cc
index cb8f1d2..aa3bf04 100644
--- a/media/filters/ffmpeg_video_decoder.cc
+++ b/media/filters/ffmpeg_video_decoder.cc
@@ -8,21 +8,16 @@
 #include <stdint.h>
 
 #include <algorithm>
-#include <string>
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
-#include "base/command_line.h"
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/sys_info.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/decoder_buffer.h"
 #include "media/base/limits.h"
 #include "media/base/media_log.h"
-#include "media/base/media_switches.h"
 #include "media/base/timestamp_constants.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_util.h"
@@ -31,70 +26,44 @@
 
 namespace media {
 
-
 // Returns the number of threads given the FFmpeg CodecID. Also inspects the
 // command line for a valid --video-threads flag.
 static int GetFFmpegVideoDecoderThreadCount(const VideoDecoderConfig& config) {
-  // Always use 2 or more threads for video decoding. Most machines today will
-  // have 2-8 execution contexts. Using more cores generally doesn't seem to
-  // increase power usage and allows us to decode video faster.
-  //
-  // Handling decoding on separate threads also frees up the pipeline thread to
-  // continue processing. Although it'd be nice to have the option of a single
-  // decoding thread, FFmpeg treats having one thread the same as having zero
-  // threads (i.e., decoding will execute on the calling thread). Yet another
-  // reason for having two threads :)
-  constexpr int kDecodeThreads = 2;
-  constexpr int kMaxDecodeThreads = 16;
+  // Most codecs are so old that more threads aren't really needed.
+  int desired_threads = limits::kMinVideoDecodeThreads;
 
-  // Refer to http://crbug.com/93932 for tsan suppressions on decoding.
-  int decode_threads = kDecodeThreads;
+  // Some ffmpeg codecs don't actually benefit from using more threads.
+  // Only add more threads for those codecs that we know will benefit.
+  switch (config.codec()) {
+    case kUnknownVideoCodec:
+    case kCodecVC1:
+    case kCodecMPEG2:
+    case kCodecHEVC:
+    case kCodecVP9:
+    case kCodecAV1:
+    case kCodecDolbyVision:
+      // We do not compile ffmpeg with support for any of these codecs.
+      break;
 
-  const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
-  std::string threads(cmd_line->GetSwitchValueASCII(switches::kVideoThreads));
-  if (threads.empty() || !base::StringToInt(threads, &decode_threads)) {
-    // Some ffmpeg codecs don't actually benefit from using more threads.
-    // Only add more threads for those codecs that we know will benefit.
-    switch (config.codec()) {
-      case kUnknownVideoCodec:
-      case kCodecVC1:
-      case kCodecMPEG2:
-      case kCodecHEVC:
-      case kCodecVP9:
-      case kCodecAV1:
-      case kCodecDolbyVision:
-        // We do not compile ffmpeg with support for any of these codecs.
-        break;
+    case kCodecTheora:
+    case kCodecMPEG4:
+      // No extra threads for these codecs.
+      break;
 
-      case kCodecTheora:
-        // No extra threads for these codecs.
-        break;
-
-      case kCodecH264:
-      case kCodecMPEG4:
-      case kCodecVP8:
-        // Normalize to three threads for 1080p content, then scale linearly
-        // with number of pixels.
-        // Examples:
-        // 4k: 12 threads
-        // 1440p: 5 threads
-        // 1080p: 3 threads
-        // anything lower than 1080p: 2 threads
-        decode_threads = config.coded_size().width() *
-                         config.coded_size().height() * 3 / 1920 / 1080;
-
-        int cores = base::SysInfo::NumberOfProcessors();
-        // Leave two execution contexts for other things to run.
-        decode_threads = std::min(decode_threads, cores - 2);
-        // Use at least two threads, or ffmpeg will decode on the calling
-        // thread.
-        decode_threads = std::max(decode_threads, kDecodeThreads);
-    }
+    case kCodecH264:
+    case kCodecVP8:
+      // Normalize to three threads for 1080p content, then scale linearly
+      // with number of pixels.
+      // Examples:
+      // 4k: 12 threads
+      // 1440p: 5 threads
+      // 1080p: 3 threads
+      // anything lower than 1080p: 2 threads
+      desired_threads = config.coded_size().width() *
+                        config.coded_size().height() * 3 / 1920 / 1080;
   }
 
-  decode_threads = std::max(decode_threads, 0);
-  decode_threads = std::min(decode_threads, kMaxDecodeThreads);
-  return decode_threads;
+  return VideoDecoder::GetRecommendedThreadCount(desired_threads);
 }
 
 static int GetVideoBufferImpl(struct AVCodecContext* s,
diff --git a/media/filters/vpx_video_decoder.cc b/media/filters/vpx_video_decoder.cc
index aa4f52d..f709832 100644
--- a/media/filters/vpx_video_decoder.cc
+++ b/media/filters/vpx_video_decoder.cc
@@ -13,17 +13,15 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
-#include "base/command_line.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/strings/string_number_conversions.h"
 #include "base/sys_byteorder.h"
-#include "base/sys_info.h"
 #include "base/trace_event/trace_event.h"
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/decoder_buffer.h"
+#include "media/base/limits.h"
 #include "media/base/media_switches.h"
 #include "media/filters/frame_buffer_pool.h"
 #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h"
@@ -37,41 +35,24 @@
 
 // Returns the number of threads.
 static int GetVpxVideoDecoderThreadCount(const VideoDecoderConfig& config) {
-  // Always try to use at least two threads for video decoding.  There is little
-  // reason not to since current day CPUs tend to be multi-core and we measured
-  // performance benefits on older machines such as P4s with hyperthreading.
-  constexpr int kDecodeThreads = 2;
-  constexpr int kMaxDecodeThreads = 32;
+  // vp8a doesn't really need more threads.
+  int desired_threads = limits::kMinVideoDecodeThreads;
 
-  // Refer to http://crbug.com/93932 for tsan suppressions on decoding.
-  int decode_threads = kDecodeThreads;
-
-  const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
-  std::string threads(cmd_line->GetSwitchValueASCII(switches::kVideoThreads));
-  if (threads.empty() || !base::StringToInt(threads, &decode_threads)) {
-    if (config.codec() == kCodecVP9) {
-      // For VP9 decode when using the default thread count, increase the number
-      // of decode threads to equal the maximum number of tiles possible for
-      // higher resolution streams.
-      const int width = config.coded_size().width();
-      if (width >= 8192)
-        decode_threads = 32;
-      else if (width >= 4096)
-        decode_threads = 16;
-      else if (width >= 2048)
-        decode_threads = 8;
-      else if (width >= 1024)
-        decode_threads = 4;
-    }
-
-    decode_threads =
-        std::min(decode_threads, base::SysInfo::NumberOfProcessors());
-    return decode_threads;
+  // For VP9 decoding increase the number of decode threads to equal the
+  // maximum number of tiles possible for higher resolution streams.
+  if (config.codec() == kCodecVP9) {
+    const int width = config.coded_size().width();
+    if (width >= 8192)
+      desired_threads = 32;
+    else if (width >= 4096)
+      desired_threads = 16;
+    else if (width >= 2048)
+      desired_threads = 8;
+    else if (width >= 1024)
+      desired_threads = 4;
   }
 
-  decode_threads = std::max(decode_threads, 0);
-  decode_threads = std::min(decode_threads, kMaxDecodeThreads);
-  return decode_threads;
+  return VideoDecoder::GetRecommendedThreadCount(desired_threads);
 }
 
 static std::unique_ptr<vpx_codec_ctx> InitializeVpxContext(
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index b777480..ce96e54 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -170,7 +170,6 @@
 #endif  // !defined(MOJO_RENDERER)
 
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
-const int k640IsoFileDurationMs = 2737;
 const int k640IsoCencFileDurationMsByDts = 2736;
 const int k640IsoCencFileDurationMsByPts = 2769;
 const int k1280IsoFileDurationMs = 2736;
@@ -180,7 +179,7 @@
 
 // Return a timeline offset for bear-320x240-live.webm.
 static base::Time kLiveTimelineOffset() {
-  // The file contians the following UTC timeline offset:
+  // The file contains the following UTC timeline offset:
   // 2012-11-10 12:34:56.789123456
   // Since base::Time only has a resolution of microseconds,
   // construct a base::Time for 2012-11-10 12:34:56.789123.
@@ -2283,11 +2282,9 @@
   Stop();
 }
 
-// Config changes from clear to encrypted are not currently supported.
-// TODO(ddorwin): Figure out why this CHECKs in AppendAtTime().
 TEST_P(MSEPipelineIntegrationTest,
-       DISABLED_ConfigChange_ClearThenEncrypted_MP4_CENC) {
-  MockMediaSource source("bear-640x360-av_frag.mp4", kAppendWholeFile);
+       MAYBE_EME(ConfigChange_ClearThenEncrypted_MP4_CENC)) {
+  MockMediaSource source("bear-640x360-v_frag.mp4", kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithEncryptedMedia(&source, &encrypted_media));
@@ -2301,19 +2298,19 @@
 
   source.EndOfStream();
 
-  base::RunLoop().Run();
-  EXPECT_EQ(CHUNK_DEMUXER_ERROR_APPEND_FAILED, pipeline_status_);
+  if (buffering_api_ == BufferingApi::kLegacyByDts)
+    EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+  else
+    EXPECT_EQ(33, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
 
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
-  // The second video was not added, so its time has not been added.
-  EXPECT_EQ(k640IsoFileDurationMs,
+  EXPECT_EQ(kAppendTimeMs + k1280IsoFileDurationMs,
             pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
 
   Play();
 
-  EXPECT_EQ(CHUNK_DEMUXER_ERROR_APPEND_FAILED, WaitUntilEndedOrError());
+  ASSERT_TRUE(WaitUntilOnEnded());
   source.Shutdown();
+  Stop();
 }
 
 // Config changes from encrypted to clear are not currently supported.
diff --git a/net/http/http_content_disposition.cc b/net/http/http_content_disposition.cc
index aba9e10..bdf8ab44 100644
--- a/net/http/http_content_disposition.cc
+++ b/net/http/http_content_disposition.cc
@@ -407,8 +407,11 @@
             "filename")) {
       DecodeFilenameValue(iter.value(), referrer_charset, &filename,
                           &parse_result_flags_);
-      if (!filename.empty())
+      if (!filename.empty()) {
         parse_result_flags_ |= HAS_FILENAME;
+        if (filename[0] == '\'')
+          parse_result_flags_ |= HAS_SINGLE_QUOTED_FILENAME;
+      }
     } else if (ext_filename.empty() &&
                base::LowerCaseEqualsASCII(
                    base::StringPiece(iter.name_begin(), iter.name_end()),
@@ -423,6 +426,9 @@
     filename_ = ext_filename;
   else
     filename_ = filename;
+
+  if (!filename.empty() && filename[0] == '\'')
+    parse_result_flags_ |= HAS_SINGLE_QUOTED_FILENAME;
 }
 
 }  // namespace net
diff --git a/net/http/http_content_disposition.h b/net/http/http_content_disposition.h
index 0f1b5ac0..7ae1bee 100644
--- a/net/http/http_content_disposition.h
+++ b/net/http/http_content_disposition.h
@@ -23,30 +23,33 @@
   // report download metrics in UMA. This enum isn't directly used in UMA but
   // mapped to another one for binary compatiblity; ie. changes are OK.
   enum ParseResultFlags {
-    INVALID                      = 0,
+    INVALID = 0,
 
     // A valid disposition-type is present.
-    HAS_DISPOSITION_TYPE         = 1 << 0,
+    HAS_DISPOSITION_TYPE = 1 << 0,
 
     // The disposition-type is not 'inline' or 'attachment'.
     HAS_UNKNOWN_DISPOSITION_TYPE = 1 << 1,
 
     // Has a valid non-empty 'filename' attribute.
-    HAS_FILENAME                 = 1 << 2,
+    HAS_FILENAME = 1 << 2,
 
     // Has a valid non-empty 'filename*' attribute.
-    HAS_EXT_FILENAME             = 1 << 3,
+    HAS_EXT_FILENAME = 1 << 3,
 
     // The following fields are properties of the 'filename' attribute:
 
     // Quoted-string contains non-ASCII characters.
-    HAS_NON_ASCII_STRINGS        = 1 << 4,
+    HAS_NON_ASCII_STRINGS = 1 << 4,
 
     // Quoted-string contains percent-encoding.
-    HAS_PERCENT_ENCODED_STRINGS  = 1 << 5,
+    HAS_PERCENT_ENCODED_STRINGS = 1 << 5,
 
     // Quoted-string contains RFC 2047 encoded words.
-    HAS_RFC2047_ENCODED_STRINGS  = 1 << 6
+    HAS_RFC2047_ENCODED_STRINGS = 1 << 6,
+
+    // Has a filename that starts with a single quote.
+    HAS_SINGLE_QUOTED_FILENAME = 1 << 7,
   };
 
   HttpContentDisposition(const std::string& header,
diff --git a/net/http/http_content_disposition_unittest.cc b/net/http/http_content_disposition_unittest.cc
index f4db044..f283ce9 100644
--- a/net/http/http_content_disposition_unittest.cc
+++ b/net/http/http_content_disposition_unittest.cc
@@ -221,16 +221,13 @@
       // http://greenbytes.de/tech/tc2231/#inlonlyquoted
       {"\"inline\"", HttpContentDisposition::INLINE, L""},
       // http://greenbytes.de/tech/tc2231/#inlwithasciifilename
-      {"inline; filename=\"foo.html\"",
-       HttpContentDisposition::INLINE,
+      {"inline; filename=\"foo.html\"", HttpContentDisposition::INLINE,
        L"foo.html"},
       // http://greenbytes.de/tech/tc2231/#inlwithfnattach
       {"inline; filename=\"Not an attachment!\"",
-       HttpContentDisposition::INLINE,
-       L"Not an attachment!"},
+       HttpContentDisposition::INLINE, L"Not an attachment!"},
       // http://greenbytes.de/tech/tc2231/#inlwithasciifilenamepdf
-      {"inline; filename=\"foo.pdf\"",
-       HttpContentDisposition::INLINE,
+      {"inline; filename=\"foo.pdf\"", HttpContentDisposition::INLINE,
        L"foo.pdf"},
       // http://greenbytes.de/tech/tc2231/#attonly
       {"attachment", HttpContentDisposition::ATTACHMENT, L""},
@@ -241,132 +238,109 @@
       // http://greenbytes.de/tech/tc2231/#attonlyucase
       {"ATTACHMENT", HttpContentDisposition::ATTACHMENT, L""},
       // http://greenbytes.de/tech/tc2231/#attwithasciifilename
-      {"attachment; filename=\"foo.html\"",
-       HttpContentDisposition::ATTACHMENT,
+      {"attachment; filename=\"foo.html\"", HttpContentDisposition::ATTACHMENT,
        L"foo.html"},
       // http://greenbytes.de/tech/tc2231/#attwithasciifnescapedchar
       {"attachment; filename=\"f\\oo.html\"",
-       HttpContentDisposition::ATTACHMENT,
-       L"foo.html"},
+       HttpContentDisposition::ATTACHMENT, L"foo.html"},
       // http://greenbytes.de/tech/tc2231/#attwithasciifnescapedquote
       {"attachment; filename=\"\\\"quoting\\\" tested.html\"",
-       HttpContentDisposition::ATTACHMENT,
-       L"\"quoting\" tested.html"},
+       HttpContentDisposition::ATTACHMENT, L"\"quoting\" tested.html"},
       // http://greenbytes.de/tech/tc2231/#attwithquotedsemicolon
       {"attachment; filename=\"Here's a semicolon;.html\"",
-       HttpContentDisposition::ATTACHMENT,
-       L"Here's a semicolon;.html"},
+       HttpContentDisposition::ATTACHMENT, L"Here's a semicolon;.html"},
       // http://greenbytes.de/tech/tc2231/#attwithfilenameandextparam
       {"attachment; foo=\"bar\"; filename=\"foo.html\"",
-       HttpContentDisposition::ATTACHMENT,
-       L"foo.html"},
+       HttpContentDisposition::ATTACHMENT, L"foo.html"},
       // http://greenbytes.de/tech/tc2231/#attwithfilenameandextparamescaped
       {"attachment; foo=\"\\\"\\\\\";filename=\"foo.html\"",
-       HttpContentDisposition::ATTACHMENT,
-       L"foo.html"},
+       HttpContentDisposition::ATTACHMENT, L"foo.html"},
       // http://greenbytes.de/tech/tc2231/#attwithasciifilenameucase
-      {"attachment; FILENAME=\"foo.html\"",
-       HttpContentDisposition::ATTACHMENT,
+      {"attachment; FILENAME=\"foo.html\"", HttpContentDisposition::ATTACHMENT,
        L"foo.html"},
       // http://greenbytes.de/tech/tc2231/#attwithasciifilenamenq
-      {"attachment; filename=foo.html",
-       HttpContentDisposition::ATTACHMENT,
+      {"attachment; filename=foo.html", HttpContentDisposition::ATTACHMENT,
        L"foo.html"},
       // http://greenbytes.de/tech/tc2231/#attwithasciifilenamenqs
       // Note: tc2231 says we should fail to parse this header.
-      {"attachment; filename=foo.html ;",
-       HttpContentDisposition::ATTACHMENT,
+      {"attachment; filename=foo.html ;", HttpContentDisposition::ATTACHMENT,
        L"foo.html"},
       // http://greenbytes.de/tech/tc2231/#attemptyparam
       // Note: tc2231 says we should fail to parse this header.
       {"attachment; ;filename=foo", HttpContentDisposition::ATTACHMENT, L"foo"},
       // http://greenbytes.de/tech/tc2231/#attwithasciifilenamenqws
       // Note: tc2231 says we should fail to parse this header.
-      {"attachment; filename=foo bar.html",
-       HttpContentDisposition::ATTACHMENT,
+      {"attachment; filename=foo bar.html", HttpContentDisposition::ATTACHMENT,
        L"foo bar.html"},
       // http://greenbytes.de/tech/tc2231/#attwithfntokensq
-      {
-       "attachment; filename='foo.bar'",
-       HttpContentDisposition::ATTACHMENT,
-       L"foo.bar"  // Should be L"'foo.bar'"
-      },
+      {"attachment; filename='foo.bar'", HttpContentDisposition::ATTACHMENT,
+       L"'foo.bar'"},
 #ifdef ICU_SHOULD_FAIL_CONVERSION_ON_INVALID_CHARACTER
       // http://greenbytes.de/tech/tc2231/#attwithisofnplain
       {
-       "attachment; filename=\"foo-\xE4html\"",
-       HttpContentDisposition::ATTACHMENT,
-       L""  // Should be L"foo-\xE4.html"
+          "attachment; filename=\"foo-\xE4html\"",
+          HttpContentDisposition::ATTACHMENT,
+          L""  // Should be L"foo-\xE4.html"
       },
 #endif
       // http://greenbytes.de/tech/tc2231/#attwithutf8fnplain
       // Note: We'll UTF-8 decode the file name, even though tc2231 says not to.
       {"attachment; filename=\"foo-\xC3\xA4.html\"",
-       HttpContentDisposition::ATTACHMENT,
-       L"foo-\xE4.html"},
+       HttpContentDisposition::ATTACHMENT, L"foo-\xE4.html"},
       // http://greenbytes.de/tech/tc2231/#attwithfnrawpctenca
       {
-       "attachment; filename=\"foo-%41.html\"",
-       HttpContentDisposition::ATTACHMENT,
-       L"foo-A.html"  // Should be L"foo-%41.html"
+          "attachment; filename=\"foo-%41.html\"",
+          HttpContentDisposition::ATTACHMENT,
+          L"foo-A.html"  // Should be L"foo-%41.html"
       },
       // http://greenbytes.de/tech/tc2231/#attwithfnusingpct
-      {"attachment; filename=\"50%.html\"",
-       HttpContentDisposition::ATTACHMENT,
+      {"attachment; filename=\"50%.html\"", HttpContentDisposition::ATTACHMENT,
        L"50%.html"},
       // http://greenbytes.de/tech/tc2231/#attwithfnrawpctencaq
       {
-       "attachment; filename=\"foo-%\\41.html\"",
-       HttpContentDisposition::ATTACHMENT,
-       L"foo-A.html"  // Should be L"foo-%41.html"
+          "attachment; filename=\"foo-%\\41.html\"",
+          HttpContentDisposition::ATTACHMENT,
+          L"foo-A.html"  // Should be L"foo-%41.html"
       },
       // http://greenbytes.de/tech/tc2231/#attwithnamepct
       // Value is skipped like other UAs.
-      {
-       "attachment; name=\"foo-%41.html\"",
-       HttpContentDisposition::ATTACHMENT,
-       L""
-      },
+      {"attachment; name=\"foo-%41.html\"", HttpContentDisposition::ATTACHMENT,
+       L""},
 #ifdef ICU_SHOULD_FAIL_CONVERSION_ON_INVALID_CHARACTER
       // http://greenbytes.de/tech/tc2231/#attwithfilenamepctandiso
       {
-       "attachment; filename=\"\xE4-%41.html\"",
-       HttpContentDisposition::ATTACHMENT,
-       L""  // Should be L"\xE4-%41.htm"
+          "attachment; filename=\"\xE4-%41.html\"",
+          HttpContentDisposition::ATTACHMENT,
+          L""  // Should be L"\xE4-%41.htm"
       },
 #endif
       // http://greenbytes.de/tech/tc2231/#attwithfnrawpctenclong
       {
-       "attachment; filename=\"foo-%c3%a4-%e2%82%ac.html\"",
-       HttpContentDisposition::ATTACHMENT,
-       L"foo-\xE4-\u20AC.html"  // Should be L"foo-%c3%a4-%e2%82%ac.html"
+          "attachment; filename=\"foo-%c3%a4-%e2%82%ac.html\"",
+          HttpContentDisposition::ATTACHMENT,
+          L"foo-\xE4-\u20AC.html"  // Should be L"foo-%c3%a4-%e2%82%ac.html"
       },
       // http://greenbytes.de/tech/tc2231/#attwithasciifilenamews1
-      {"attachment; filename =\"foo.html\"",
-       HttpContentDisposition::ATTACHMENT,
+      {"attachment; filename =\"foo.html\"", HttpContentDisposition::ATTACHMENT,
        L"foo.html"},
       // http://greenbytes.de/tech/tc2231/#attwith2filenames
       // Note: tc2231 says we should fail to parse this header.
       {"attachment; filename=\"foo.html\"; filename=\"bar.html\"",
-       HttpContentDisposition::ATTACHMENT,
-       L"foo.html"},
+       HttpContentDisposition::ATTACHMENT, L"foo.html"},
       // http://greenbytes.de/tech/tc2231/#attfnbrokentoken
       // Note: tc2231 says we should fail to parse this header.
       {"attachment; filename=foo[1](2).html",
-       HttpContentDisposition::ATTACHMENT,
-       L"foo[1](2).html"},
+       HttpContentDisposition::ATTACHMENT, L"foo[1](2).html"},
 #ifdef ICU_SHOULD_FAIL_CONVERSION_ON_INVALID_CHARACTER
       // http://greenbytes.de/tech/tc2231/#attfnbrokentokeniso
       // Note: tc2231 says we should fail to parse this header.
-      {"attachment; filename=foo-\xE4.html",
-       HttpContentDisposition::ATTACHMENT,
+      {"attachment; filename=foo-\xE4.html", HttpContentDisposition::ATTACHMENT,
        L""},
 #endif
       // http://greenbytes.de/tech/tc2231/#attfnbrokentokenutf
       // Note: tc2231 says we should fail to parse this header.
       {"attachment; filename=foo-\xC3\xA4.html",
-       HttpContentDisposition::ATTACHMENT,
-       L"foo-\xE4.html"},
+       HttpContentDisposition::ATTACHMENT, L"foo-\xE4.html"},
       // http://greenbytes.de/tech/tc2231/#attmissingdisposition
       // Note: tc2231 says we should fail to parse this header.
       {"filename=foo.html", HttpContentDisposition::INLINE, L"foo.html"},
@@ -376,79 +350,65 @@
       // http://greenbytes.de/tech/tc2231/#attmissingdisposition3
       // Note: tc2231 says we should fail to parse this header.
       {
-       "\"foo; filename=bar;baz\"; filename=qux",
-       HttpContentDisposition::INLINE,
-       L""  // Firefox gets qux
+          "\"foo; filename=bar;baz\"; filename=qux",
+          HttpContentDisposition::INLINE,
+          L""  // Firefox gets qux
       },
       // http://greenbytes.de/tech/tc2231/#attmissingdisposition4
       // Note: tc2231 says we should fail to parse this header.
-      {"filename=foo.html, filename=bar.html",
-       HttpContentDisposition::INLINE,
+      {"filename=foo.html, filename=bar.html", HttpContentDisposition::INLINE,
        L"foo.html, filename=bar.html"},
       // http://greenbytes.de/tech/tc2231/#emptydisposition
       // Note: tc2231 says we should fail to parse this header.
       {"; filename=foo.html", HttpContentDisposition::INLINE, L"foo.html"},
       // http://greenbytes.de/tech/tc2231/#attandinline
       // Note: tc2231 says we should fail to parse this header.
-      {"inline; attachment; filename=foo.html",
-       HttpContentDisposition::INLINE,
+      {"inline; attachment; filename=foo.html", HttpContentDisposition::INLINE,
        L""},
       // http://greenbytes.de/tech/tc2231/#attandinline2
       // Note: tc2231 says we should fail to parse this header.
       {"attachment; inline; filename=foo.html",
-       HttpContentDisposition::ATTACHMENT,
-       L""},
+       HttpContentDisposition::ATTACHMENT, L""},
       // http://greenbytes.de/tech/tc2231/#attbrokenquotedfn
       // Note: tc2231 says we should fail to parse this header.
       {"attachment; filename=\"foo.html\".txt",
-       HttpContentDisposition::ATTACHMENT,
-       L"foo.html\".txt"},
+       HttpContentDisposition::ATTACHMENT, L"foo.html\".txt"},
       // http://greenbytes.de/tech/tc2231/#attbrokenquotedfn2
       // Note: tc2231 says we should fail to parse this header.
-      {"attachment; filename=\"bar",
-       HttpContentDisposition::ATTACHMENT,
+      {"attachment; filename=\"bar", HttpContentDisposition::ATTACHMENT,
        L"bar"},
       // http://greenbytes.de/tech/tc2231/#attbrokenquotedfn3
       // Note: tc2231 says we should fail to parse this header.
       {"attachment; filename=foo\"bar;baz\"qux",
-       HttpContentDisposition::ATTACHMENT,
-       L"foo\"bar;baz\"qux"},
+       HttpContentDisposition::ATTACHMENT, L"foo\"bar;baz\"qux"},
       // http://greenbytes.de/tech/tc2231/#attmultinstances
       // Note: tc2231 says we should fail to parse this header.
       {"attachment; filename=foo.html, attachment; filename=bar.html",
-       HttpContentDisposition::ATTACHMENT,
-       L"foo.html, attachment"},
+       HttpContentDisposition::ATTACHMENT, L"foo.html, attachment"},
       // http://greenbytes.de/tech/tc2231/#attmissingdelim
-      {"attachment; foo=foo filename=bar",
-       HttpContentDisposition::ATTACHMENT,
+      {"attachment; foo=foo filename=bar", HttpContentDisposition::ATTACHMENT,
        L""},
       // http://greenbytes.de/tech/tc2231/#attreversed
       // Note: tc2231 says we should fail to parse this header.
-      {"filename=foo.html; attachment",
-       HttpContentDisposition::INLINE,
+      {"filename=foo.html; attachment", HttpContentDisposition::INLINE,
        L"foo.html"},
       // http://greenbytes.de/tech/tc2231/#attconfusedparam
-      {"attachment; xfilename=foo.html",
-       HttpContentDisposition::ATTACHMENT,
+      {"attachment; xfilename=foo.html", HttpContentDisposition::ATTACHMENT,
        L""},
       // http://greenbytes.de/tech/tc2231/#attabspath
-      {"attachment; filename=\"/foo.html\"",
-       HttpContentDisposition::ATTACHMENT,
+      {"attachment; filename=\"/foo.html\"", HttpContentDisposition::ATTACHMENT,
        L"/foo.html"},
       // http://greenbytes.de/tech/tc2231/#attabspathwin
       {"attachment; filename=\"\\\\foo.html\"",
-       HttpContentDisposition::ATTACHMENT,
-       L"\\foo.html"},
+       HttpContentDisposition::ATTACHMENT, L"\\foo.html"},
       // http://greenbytes.de/tech/tc2231/#dispext
       {"foobar", HttpContentDisposition::ATTACHMENT, L""},
       // http://greenbytes.de/tech/tc2231/#dispextbadfn
       {"attachment; example=\"filename=example.txt\"",
-       HttpContentDisposition::ATTACHMENT,
-       L""},
+       HttpContentDisposition::ATTACHMENT, L""},
       // http://greenbytes.de/tech/tc2231/#attnewandfn
       {"attachment; foobar=x; filename=\"foo.html\"",
-       HttpContentDisposition::ATTACHMENT,
-       L"foo.html"},
+       HttpContentDisposition::ATTACHMENT, L"foo.html"},
       // TODO(abarth): Add the filename* tests, but check
       //              HttpContentDispositionTest.Filename for overlap.
       // TODO(abarth): http://greenbytes.de/tech/tc2231/#attrfc2047token
@@ -468,61 +428,63 @@
     const char* header;
     int expected_flags;
   } kTestCases[] = {
-    // Basic feature tests
-    { "", HttpContentDisposition::INVALID },
-    { "example=x", HttpContentDisposition::INVALID },
-    { "attachment; filename=", HttpContentDisposition::HAS_DISPOSITION_TYPE },
-    { "attachment; name=", HttpContentDisposition::HAS_DISPOSITION_TYPE },
-    { "attachment; filename*=", HttpContentDisposition::HAS_DISPOSITION_TYPE },
-    { "attachment; filename==?utf-8?Q?\?=",
-      HttpContentDisposition::HAS_DISPOSITION_TYPE },
-    { "filename=x", HttpContentDisposition::HAS_FILENAME },
-    { "example; filename=x",
-      HttpContentDisposition::HAS_DISPOSITION_TYPE |
-      HttpContentDisposition::HAS_UNKNOWN_DISPOSITION_TYPE |
-      HttpContentDisposition::HAS_FILENAME},
-    { "attachment; filename=x",
-      HttpContentDisposition::HAS_DISPOSITION_TYPE |
-      HttpContentDisposition::HAS_FILENAME },
-    { "attachment; filename=x; name=y",
-      HttpContentDisposition::HAS_DISPOSITION_TYPE |
-      HttpContentDisposition::HAS_FILENAME },
-    { "attachment; name=y; filename*=utf-8''foo; name=x",
-      HttpContentDisposition::HAS_DISPOSITION_TYPE |
-      HttpContentDisposition::HAS_EXT_FILENAME },
+      // Basic feature tests
+      {"", HttpContentDisposition::INVALID},
+      {"example=x", HttpContentDisposition::INVALID},
+      {"attachment; filename=", HttpContentDisposition::HAS_DISPOSITION_TYPE},
+      {"attachment; name=", HttpContentDisposition::HAS_DISPOSITION_TYPE},
+      {"attachment; filename*=", HttpContentDisposition::HAS_DISPOSITION_TYPE},
+      {"attachment; filename==?utf-8?Q?\?=",
+       HttpContentDisposition::HAS_DISPOSITION_TYPE},
+      {"filename=x", HttpContentDisposition::HAS_FILENAME},
+      {"example; filename=x",
+       HttpContentDisposition::HAS_DISPOSITION_TYPE |
+           HttpContentDisposition::HAS_UNKNOWN_DISPOSITION_TYPE |
+           HttpContentDisposition::HAS_FILENAME},
+      {"attachment; filename=x", HttpContentDisposition::HAS_DISPOSITION_TYPE |
+                                     HttpContentDisposition::HAS_FILENAME},
+      {"attachment; filename='x'",
+       HttpContentDisposition::HAS_DISPOSITION_TYPE |
+           HttpContentDisposition::HAS_FILENAME |
+           HttpContentDisposition::HAS_SINGLE_QUOTED_FILENAME},
+      {"attachment; filename=x; name=y",
+       HttpContentDisposition::HAS_DISPOSITION_TYPE |
+           HttpContentDisposition::HAS_FILENAME},
+      {"attachment; name=y; filename*=utf-8''foo; name=x",
+       HttpContentDisposition::HAS_DISPOSITION_TYPE |
+           HttpContentDisposition::HAS_EXT_FILENAME},
 
-    // Feature tests for 'filename' attribute.
-    { "filename=foo\xcc\x88",
-      HttpContentDisposition::HAS_FILENAME |
-      HttpContentDisposition::HAS_NON_ASCII_STRINGS },
-    { "filename=foo%cc%88",
-      HttpContentDisposition::HAS_FILENAME |
-      HttpContentDisposition::HAS_PERCENT_ENCODED_STRINGS },
-    { "filename==?utf-8?Q?foo?=",
-      HttpContentDisposition::HAS_FILENAME |
-      HttpContentDisposition::HAS_RFC2047_ENCODED_STRINGS },
-    { "filename=\"=?utf-8?Q?foo?=\"",
-      HttpContentDisposition::HAS_FILENAME |
-      HttpContentDisposition::HAS_RFC2047_ENCODED_STRINGS },
-    { "filename==?utf-8?Q?foo?", HttpContentDisposition::INVALID },
+      // Feature tests for 'filename' attribute.
+      {"filename=foo\xcc\x88",
+       HttpContentDisposition::HAS_FILENAME |
+           HttpContentDisposition::HAS_NON_ASCII_STRINGS},
+      {"filename=foo%cc%88",
+       HttpContentDisposition::HAS_FILENAME |
+           HttpContentDisposition::HAS_PERCENT_ENCODED_STRINGS},
+      {"filename==?utf-8?Q?foo?=",
+       HttpContentDisposition::HAS_FILENAME |
+           HttpContentDisposition::HAS_RFC2047_ENCODED_STRINGS},
+      {"filename=\"=?utf-8?Q?foo?=\"",
+       HttpContentDisposition::HAS_FILENAME |
+           HttpContentDisposition::HAS_RFC2047_ENCODED_STRINGS},
+      {"filename==?utf-8?Q?foo?", HttpContentDisposition::INVALID},
 
-    // Test 'name' isn't a synonym for 'filename'.
-    { "name=foo\xcc\x88", HttpContentDisposition::INVALID },
+      // Test 'name' isn't a synonym for 'filename'.
+      {"name=foo\xcc\x88", HttpContentDisposition::INVALID},
 
-    // Shouldn't set |has_non_ascii_strings| based on 'name' attribute.
-    { "filename=x; name=foo\xcc\x88",
-      HttpContentDisposition::HAS_FILENAME },
-    { "filename=foo\xcc\x88 foo%cc%88 =?utf-8?Q?foo?=",
-      HttpContentDisposition::HAS_FILENAME |
-      HttpContentDisposition::HAS_NON_ASCII_STRINGS |
-      HttpContentDisposition::HAS_PERCENT_ENCODED_STRINGS |
-      HttpContentDisposition::HAS_RFC2047_ENCODED_STRINGS },
+      // Shouldn't set |has_non_ascii_strings| based on 'name' attribute.
+      {"filename=x; name=foo\xcc\x88", HttpContentDisposition::HAS_FILENAME},
+      {"filename=foo\xcc\x88 foo%cc%88 =?utf-8?Q?foo?=",
+       HttpContentDisposition::HAS_FILENAME |
+           HttpContentDisposition::HAS_NON_ASCII_STRINGS |
+           HttpContentDisposition::HAS_PERCENT_ENCODED_STRINGS |
+           HttpContentDisposition::HAS_RFC2047_ENCODED_STRINGS},
 
-    // If 'filename' attribute is invalid, should set any flags based on it.
-    { "filename=foo\xcc\x88 foo%cc%88 =?utf-8?Q?foo?",
-      HttpContentDisposition::INVALID },
-    { "filename=foo\xcc\x88 foo%cc%88 =?utf-8?Q?foo?; name=x",
-      HttpContentDisposition::INVALID },
+      // If 'filename' attribute is invalid, should set any flags based on it.
+      {"filename=foo\xcc\x88 foo%cc%88 =?utf-8?Q?foo?",
+       HttpContentDisposition::INVALID},
+      {"filename=foo\xcc\x88 foo%cc%88 =?utf-8?Q?foo?; name=x",
+       HttpContentDisposition::INVALID},
   };
 
   for (size_t i = 0; i < arraysize(kTestCases); ++i) {
diff --git a/net/http/http_util.cc b/net/http/http_util.cc
index 0097c9b..658c91b 100644
--- a/net/http/http_util.cc
+++ b/net/http/http_util.cc
@@ -542,10 +542,9 @@
 }
 
 namespace {
+
 bool IsQuote(char c) {
-  // Single quote mark isn't actually part of quoted-text production,
-  // but apparently some servers rely on this.
-  return c == '"' || c == '\'';
+  return c == '"';
 }
 
 bool UnquoteImpl(std::string::const_iterator begin,
@@ -560,16 +559,10 @@
   if (!IsQuote(*begin))
     return false;
 
-  // Anything other than double quotes in strict mode.
-  if (strict_quotes && *begin != '"')
-    return false;
-
   // No terminal quote mark.
   if (end - begin < 2 || *begin != *(end - 1))
     return false;
 
-  char quote = *begin;
-
   // Strip quotemarks
   ++begin;
   --end;
@@ -583,7 +576,7 @@
       prev_escape = true;
       continue;
     }
-    if (strict_quotes && !prev_escape && c == quote)
+    if (strict_quotes && !prev_escape && IsQuote(c))
       return false;
     prev_escape = false;
     unescaped.push_back(c);
@@ -596,6 +589,7 @@
   *out = std::move(unescaped);
   return true;
 }
+
 }  // anonymous namespace
 
 std::string HttpUtil::Unquote(std::string::const_iterator begin,
@@ -1023,7 +1017,7 @@
     std::string::const_iterator values_end,
     char delimiter)
     : values_(values_begin, values_end, std::string(1, delimiter)) {
-  values_.set_quote_chars("\'\"");
+  values_.set_quote_chars("\"");
 }
 
 HttpUtil::ValuesIterator::ValuesIterator(const ValuesIterator& other) = default;
@@ -1058,8 +1052,6 @@
       value_is_quoted_(false),
       values_optional_(optional_values == Values::NOT_REQUIRED),
       strict_quotes_(strict_quotes == Quotes::STRICT_QUOTES) {
-  if (strict_quotes_)
-    props_.set_quote_chars("\"");
 }
 
 HttpUtil::NameValuePairsIterator::NameValuePairsIterator(
@@ -1152,15 +1144,6 @@
   return true;
 }
 
-bool HttpUtil::NameValuePairsIterator::IsQuote(char c) const {
-  if (strict_quotes_)
-    return c == '"';
-
-  // The call to the file-scoped IsQuote must be qualified to avoid re-entrantly
-  // calling NameValuePairsIterator::IsQuote again.
-  return net::IsQuote(c);
-}
-
 bool HttpUtil::ParseAcceptEncoding(const std::string& accept_encoding,
                                    std::set<std::string>* allowed_encodings) {
   DCHECK(allowed_encodings);
diff --git a/net/http/http_util.h b/net/http/http_util.h
index b4dd8ef0..e8cd4df 100644
--- a/net/http/http_util.h
+++ b/net/http/http_util.h
@@ -150,8 +150,6 @@
   // unescaped actually is a valid quoted string. Returns false for an empty
   // string, a string without quotes, a string with mismatched quotes, and
   // a string with unescaped embeded quotes.
-  // In accordance with RFC 2616 this method only allows double quotes to
-  // enclose the string.
   static bool StrictUnquote(std::string::const_iterator begin,
                             std::string::const_iterator end,
                             std::string* out) WARN_UNUSED_RESULT;
@@ -344,12 +342,6 @@
     ValuesIterator(const ValuesIterator& other);
     ~ValuesIterator();
 
-    // Set the characters to regard as quotes.  By default, this includes both
-    // single and double quotes.
-    void set_quote_chars(const char* quotes) {
-      values_.set_quote_chars(quotes);
-    }
-
     // Advances the iterator to the next value, if any.  Returns true if there
     // is a next value.  Use value* methods to access the resultant value.
     bool GetNext();
@@ -439,8 +431,6 @@
                                                        value_end_); }
 
    private:
-    bool IsQuote(char c) const;
-
     HttpUtil::ValuesIterator props_;
     bool valid_;
 
diff --git a/net/http/http_util_unittest.cc b/net/http/http_util_unittest.cc
index d0b59ca..537ffe1 100644
--- a/net/http/http_util_unittest.cc
+++ b/net/http/http_util_unittest.cc
@@ -222,10 +222,6 @@
   EXPECT_STREQ("X", HttpUtil::Unquote("X").c_str());
   EXPECT_STREQ("\"", HttpUtil::Unquote("\"").c_str());
 
-  // Allow single quotes to act as quote marks.
-  // Not part of RFC 2616.
-  EXPECT_STREQ("x\"", HttpUtil::Unquote("'x\"'").c_str());
-
   // Allow quotes in the middle of the input.
   EXPECT_STREQ("foo\"bar", HttpUtil::Unquote("\"foo\"bar\"").c_str());
 
@@ -1156,25 +1152,26 @@
 }  // namespace
 
 TEST(HttpUtilTest, NameValuePairsIteratorCopyAndAssign) {
-  std::string data = "alpha='\\'a\\''; beta=\" b \"; cappa='c;'; delta=\"d\"";
+  std::string data =
+      "alpha=\"\\\"a\\\"\"; beta=\" b \"; cappa=\"c;\"; delta=\"d\"";
   HttpUtil::NameValuePairsIterator parser_a(data.begin(), data.end(), ';');
 
   EXPECT_TRUE(parser_a.valid());
   ASSERT_NO_FATAL_FAILURE(
-      CheckNextNameValuePair(&parser_a, true, true, "alpha", "'a'"));
+      CheckNextNameValuePair(&parser_a, true, true, "alpha", "\"a\""));
 
   HttpUtil::NameValuePairsIterator parser_b(parser_a);
   // a and b now point to same location
   ASSERT_NO_FATAL_FAILURE(
-      CheckCurrentNameValuePair(&parser_b, true, "alpha", "'a'"));
+      CheckCurrentNameValuePair(&parser_b, true, "alpha", "\"a\""));
   ASSERT_NO_FATAL_FAILURE(
-      CheckCurrentNameValuePair(&parser_a, true, "alpha", "'a'"));
+      CheckCurrentNameValuePair(&parser_a, true, "alpha", "\"a\""));
 
   // advance a, no effect on b
   ASSERT_NO_FATAL_FAILURE(
       CheckNextNameValuePair(&parser_a, true, true, "beta", " b "));
   ASSERT_NO_FATAL_FAILURE(
-      CheckCurrentNameValuePair(&parser_b, true, "alpha", "'a'"));
+      CheckCurrentNameValuePair(&parser_b, true, "alpha", "\"a\""));
 
   // assign b the current state of a, no effect on a
   parser_b = parser_a;
@@ -1200,10 +1197,13 @@
 }
 
 TEST(HttpUtilTest, NameValuePairsIterator) {
-  std::string data = "alpha=1; beta= 2 ;cappa =' 3; ';"
-                     "delta= \" \\\"4\\\" \"; e= \" '5'\"; e=6;"
-                     "f='\\'\\h\\e\\l\\l\\o\\ \\w\\o\\r\\l\\d\\'';"
-                     "g=''; h='hello'";
+  std::string data =
+      "alpha=1; beta= 2 ;"
+      "cappa =' 3; foo=';"
+      "cappa =\" 3; foo=\";"
+      "delta= \" \\\"4\\\" \"; e= \" '5'\"; e=6;"
+      "f=\"\\\"\\h\\e\\l\\l\\o\\ \\w\\o\\r\\l\\d\\\"\";"
+      "g=\"\"; h=\"hello\"";
   HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
   EXPECT_TRUE(parser.valid());
 
@@ -1211,8 +1211,17 @@
       CheckNextNameValuePair(&parser, true, true, "alpha", "1"));
   ASSERT_NO_FATAL_FAILURE(
       CheckNextNameValuePair(&parser, true, true, "beta", "2"));
+
+  // Single quotes shouldn't be treated as quotes.
   ASSERT_NO_FATAL_FAILURE(
-      CheckNextNameValuePair(&parser, true, true, "cappa", " 3; "));
+      CheckNextNameValuePair(&parser, true, true, "cappa", "' 3"));
+  ASSERT_NO_FATAL_FAILURE(
+      CheckNextNameValuePair(&parser, true, true, "foo", "'"));
+
+  // But double quotes should be, and can contain semi-colons and equal signs.
+  ASSERT_NO_FATAL_FAILURE(
+      CheckNextNameValuePair(&parser, true, true, "cappa", " 3; foo="));
+
   ASSERT_NO_FATAL_FAILURE(
       CheckNextNameValuePair(&parser, true, true, "delta", " \"4\" "));
   ASSERT_NO_FATAL_FAILURE(
@@ -1220,7 +1229,7 @@
   ASSERT_NO_FATAL_FAILURE(
       CheckNextNameValuePair(&parser, true, true, "e", "6"));
   ASSERT_NO_FATAL_FAILURE(
-      CheckNextNameValuePair(&parser, true, true, "f", "'hello world'"));
+      CheckNextNameValuePair(&parser, true, true, "f", "\"hello world\""));
   ASSERT_NO_FATAL_FAILURE(
       CheckNextNameValuePair(&parser, true, true, "g", std::string()));
   ASSERT_NO_FATAL_FAILURE(
@@ -1277,8 +1286,9 @@
   ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; beta"));
   ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair(std::string(), "beta"));
 
-  ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; 'beta'=2"));
-  ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair(std::string(), "'beta'=2"));
+  ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; \"beta\"=2"));
+  ASSERT_NO_FATAL_FAILURE(
+      CheckInvalidNameValuePair(std::string(), "\"beta\"=2"));
   ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", ";beta="));
   ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1",
                                                     ";beta=;cappa=2"));
@@ -1309,7 +1319,7 @@
 // See comments on the implementation of NameValuePairsIterator::GetNext
 // regarding this derogation from the spec.
 TEST(HttpUtilTest, NameValuePairsIteratorMissingEndQuote) {
-  std::string data = "name='value";
+  std::string data = "name=\"value";
   HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
   EXPECT_TRUE(parser.valid());
 
diff --git a/net/nqe/network_quality_estimator_unittest.cc b/net/nqe/network_quality_estimator_unittest.cc
index ec0d7a1..09e1f1d 100644
--- a/net/nqe/network_quality_estimator_unittest.cc
+++ b/net/nqe/network_quality_estimator_unittest.cc
@@ -1919,8 +1919,7 @@
 
 // TestTCPSocketRTT requires kernel support for tcp_info struct, and so it is
 // enabled only on certain platforms.
-#if defined(TCP_INFO) || (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || \
-    defined(OS_ANDROID)
+#if defined(TCP_INFO) || defined(OS_LINUX) || defined(OS_ANDROID)
 #define MAYBE_TestTCPSocketRTT TestTCPSocketRTT
 #else
 #define MAYBE_TestTCPSocketRTT DISABLED_TestTCPSocketRTT
diff --git a/net/socket/tcp_socket_posix.cc b/net/socket/tcp_socket_posix.cc
index 4244c614..2ab207c 100644
--- a/net/socket/tcp_socket_posix.cc
+++ b/net/socket/tcp_socket_posix.cc
@@ -8,6 +8,8 @@
 #include <netinet/tcp.h>
 #include <sys/socket.h>
 
+#include <algorithm>
+
 #include "base/atomicops.h"
 #include "base/bind.h"
 #include "base/files/file_path.h"
@@ -152,6 +154,16 @@
 #if defined(HAVE_TCP_INFO)
 // Returns a zero value if the transport RTT is unavailable.
 base::TimeDelta GetTransportRtt(SocketDescriptor fd) {
+  // It is possible for the value returned by getsockopt(TCP_INFO) to be
+  // legitimately zero due to the way the RTT is calculated where fractions are
+  // rounded down. This is specially true for virtualized environments with
+  // paravirtualized clocks.
+  //
+  // If getsockopt(TCP_INFO) succeeds and the tcpi_rtt is zero, this code
+  // assumes that the RTT got rounded down to zero and rounds it back up to this
+  // value so that callers can assume that no packets defy the laws of physics.
+  constexpr uint32_t kMinValidRttMicros = 1;
+
   tcp_info info;
   // Reset |tcpi_rtt| to verify if getsockopt() actually updates |tcpi_rtt|.
   info.tcpi_rtt = 0;
@@ -169,7 +181,8 @@
     return base::TimeDelta();
   }
 
-  return base::TimeDelta::FromMicroseconds(info.tcpi_rtt);
+  return base::TimeDelta::FromMicroseconds(
+      std::max(info.tcpi_rtt, kMinValidRttMicros));
 }
 
 // Returns true if getsockopt() call was successful. Sets
diff --git a/net/socket/tcp_socket_unittest.cc b/net/socket/tcp_socket_unittest.cc
index 9274746..c758fc8 100644
--- a/net/socket/tcp_socket_unittest.cc
+++ b/net/socket/tcp_socket_unittest.cc
@@ -684,15 +684,9 @@
   TestSPWNotifications(false, 2u, 0u, 0u);
 }
 
-#if defined(OS_CHROMEOS)
-// https://crbug.com/873851.
-#define MAYBE_SPWNoAdvance DISABLED_SPWNoAdvance
-#else
-#define MAYBE_SPWNoAdvance SPWNoAdvance
-#endif
 // One notification should be received when the socket connects. One
 // additional notification should be received for each message read.
-TEST_F(TCPSocketTest, MAYBE_SPWNoAdvance) {
+TEST_F(TCPSocketTest, SPWNoAdvance) {
   TestSPWNotifications(true, 2u, 0u, 3u);
 }
 #endif  // defined(TCP_INFO) || defined(OS_LINUX)
diff --git a/net/tools/testserver/run_testserver.cc b/net/tools/testserver/run_testserver.cc
index 378d5b0..10ea873 100644
--- a/net/tools/testserver/run_testserver.cc
+++ b/net/tools/testserver/run_testserver.cc
@@ -7,6 +7,7 @@
 #include "base/at_exit.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
+#include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
diff --git a/ppapi/utility/threading/lock.h b/ppapi/utility/threading/lock.h
index f3bfe4a..fefe3210 100644
--- a/ppapi/utility/threading/lock.h
+++ b/ppapi/utility/threading/lock.h
@@ -7,6 +7,9 @@
 
 #ifdef WIN32
 #include <windows.h>
+// MemoryBarrier is a Win32 macro that clashes with MemoryBarrier in
+// base/atomicops.h.
+#undef MemoryBarrier
 #else
 #include <pthread.h>
 #endif
diff --git a/ppapi/utility/threading/simple_thread.h b/ppapi/utility/threading/simple_thread.h
index b4d7114c..d10c859 100644
--- a/ppapi/utility/threading/simple_thread.h
+++ b/ppapi/utility/threading/simple_thread.h
@@ -8,6 +8,9 @@
 #include <stddef.h>
 #ifdef WIN32
 #include <windows.h>
+// MemoryBarrier is a Win32 macro that clashes with MemoryBarrier in
+// base/atomicops.h.
+#undef MemoryBarrier
 #else
 #include <pthread.h>
 #endif
diff --git a/services/media_session/audio_focus_manager.cc b/services/media_session/audio_focus_manager.cc
index e57c0bd..a24d0e957 100644
--- a/services/media_session/audio_focus_manager.cc
+++ b/services/media_session/audio_focus_manager.cc
@@ -83,7 +83,6 @@
 
   void MediaSessionInfoChanged(mojom::MediaSessionInfoPtr info) override {
     session_info_ = std::move(info);
-    owner_->DidChangeFocus();
   }
 
   void GetRequestId(GetRequestIdCallback callback) override {
@@ -357,9 +356,8 @@
   if (audio_focus_stack_.empty()) {
     active_media_controller_.ClearMediaSession();
   } else {
-    StackRow* row = audio_focus_stack_.back().get();
-    active_media_controller_.SetMediaSession(row->session(),
-                                             row->info()->playback_state);
+    active_media_controller_.SetMediaSession(
+        audio_focus_stack_.back()->session());
   }
 }
 
diff --git a/services/media_session/media_controller.cc b/services/media_session/media_controller.cc
index 1baf09d..1ab33d7 100644
--- a/services/media_session/media_controller.cc
+++ b/services/media_session/media_controller.cc
@@ -29,7 +29,10 @@
 void MediaController::ToggleSuspendResume() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  switch (playback_state_) {
+  if (session_info_.is_null())
+    return;
+
+  switch (session_info_->playback_state) {
     case mojom::MediaPlaybackState::kPlaying:
       Suspend();
       break;
@@ -39,17 +42,42 @@
   }
 }
 
-void MediaController::SetMediaSession(mojom::MediaSession* session,
-                                      mojom::MediaPlaybackState state) {
+void MediaController::AddObserver(mojom::MediaSessionObserverPtr observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // Flush the new observer with the latest session info. If there is no info
+  // then we will update |observer| when |MediaSessionInfoChanged| is called.
+  if (!session_info_.is_null())
+    observer->MediaSessionInfoChanged(session_info_.Clone());
+
+  observers_.AddPtr(std::move(observer));
+}
+
+void MediaController::MediaSessionInfoChanged(mojom::MediaSessionInfoPtr info) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  observers_.ForAllPtrs([&info](mojom::MediaSessionObserver* observer) {
+    observer->MediaSessionInfoChanged(info.Clone());
+  });
+
+  session_info_ = std::move(info);
+}
+
+void MediaController::SetMediaSession(mojom::MediaSession* session) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   session_ = session;
-  playback_state_ = state;
+
+  // Add |this| as an observer for |session|.
+  session_binding_.Close();
+  mojom::MediaSessionObserverPtr observer;
+  session_binding_.Bind(mojo::MakeRequest(&observer));
+  session->AddObserver(std::move(observer));
 }
 
 void MediaController::ClearMediaSession() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   session_ = nullptr;
-  playback_state_ = mojom::MediaPlaybackState::kPaused;
+  session_binding_.Close();
 }
 
 void MediaController::BindToInterface(mojom::MediaControllerRequest request) {
diff --git a/services/media_session/media_controller.h b/services/media_session/media_controller.h
index b279ca5..fa53149 100644
--- a/services/media_session/media_controller.h
+++ b/services/media_session/media_controller.h
@@ -8,7 +8,9 @@
 #include <memory>
 
 #include "base/sequence_checker.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
 #include "services/media_session/public/mojom/media_controller.mojom.h"
 #include "services/media_session/public/mojom/media_session.mojom.h"
 
@@ -16,8 +18,10 @@
 
 // MediaController provides a control surface over Mojo for controlling a
 // specific MediaSession. If |session_| is nullptr then all commands will be
-// dropped.
-class MediaController : public mojom::MediaController {
+// dropped. MediaController is also a MediaSessionObserver and will forward
+// events to added observers.
+class MediaController : public mojom::MediaController,
+                        public mojom::MediaSessionObserver {
  public:
   MediaController();
   ~MediaController() override;
@@ -26,8 +30,12 @@
   void Suspend() override;
   void Resume() override;
   void ToggleSuspendResume() override;
+  void AddObserver(mojom::MediaSessionObserverPtr) override;
 
-  void SetMediaSession(mojom::MediaSession*, mojom::MediaPlaybackState);
+  // mojom::MediaSessionObserver overrides.
+  void MediaSessionInfoChanged(mojom::MediaSessionInfoPtr) override;
+
+  void SetMediaSession(mojom::MediaSession*);
   void ClearMediaSession();
 
   void BindToInterface(mojom::MediaControllerRequest);
@@ -37,14 +45,19 @@
   // Holds mojo bindings for mojom::MediaController.
   mojo::BindingSet<mojom::MediaController> bindings_;
 
-  // The current playback state of the |session_|.
-  mojom::MediaPlaybackState playback_state_ =
-      mojom::MediaPlaybackState::kPaused;
+  // The current info for the |session_|.
+  mojom::MediaSessionInfoPtr session_info_;
 
   // Raw pointer to the local proxy. This is used for sending control events to
   // the underlying MediaSession.
   mojom::MediaSession* session_ = nullptr;
 
+  // Observers that are observing |session_|.
+  mojo::InterfacePtrSet<mojom::MediaSessionObserver> observers_;
+
+  // Binding for |this| to act as an observer to |session_|.
+  mojo::Binding<mojom::MediaSessionObserver> session_binding_{this};
+
   // Protects |session_| as it is not thread safe.
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/services/media_session/media_controller_unittest.cc b/services/media_session/media_controller_unittest.cc
index e80f159..dc3d1f1 100644
--- a/services/media_session/media_controller_unittest.cc
+++ b/services/media_session/media_controller_unittest.cc
@@ -237,4 +237,43 @@
   }
 }
 
+TEST_F(MediaControllerTest, ActiveController_Observer_StateTransition) {
+  test::MockMediaSession media_session_1;
+  test::MockMediaSession media_session_2;
+
+  {
+    test::MockMediaSessionMojoObserver observer(media_session_1);
+    RequestAudioFocus(media_session_1);
+    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+  }
+
+  {
+    test::MockMediaSessionMojoObserver observer(controller());
+    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+  }
+
+  {
+    test::MockMediaSessionMojoObserver observer(controller());
+    controller()->Suspend();
+    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kSuspended);
+  }
+
+  {
+    test::MockMediaSessionMojoObserver observer(controller());
+    RequestAudioFocus(media_session_2);
+    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+  }
+
+  {
+    test::MockMediaSessionMojoObserver observer(media_session_1);
+    media_session_1.StartDucking();
+    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kDucking);
+  }
+
+  {
+    test::MockMediaSessionMojoObserver observer(controller());
+    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+  }
+}
+
 }  // namespace media_session
diff --git a/services/media_session/mock_media_session.cc b/services/media_session/mock_media_session.cc
index 83136d3e..5ffed7c 100644
--- a/services/media_session/mock_media_session.cc
+++ b/services/media_session/mock_media_session.cc
@@ -19,6 +19,14 @@
   media_session.AddObserver(std::move(observer));
 }
 
+MockMediaSessionMojoObserver::MockMediaSessionMojoObserver(
+    mojom::MediaControllerPtr& controller)
+    : binding_(this) {
+  mojom::MediaSessionObserverPtr observer;
+  binding_.Bind(mojo::MakeRequest(&observer));
+  controller->AddObserver(std::move(observer));
+}
+
 MockMediaSessionMojoObserver::~MockMediaSessionMojoObserver() = default;
 
 void MockMediaSessionMojoObserver::MediaSessionInfoChanged(
@@ -79,6 +87,7 @@
 }
 
 void MockMediaSession::AddObserver(mojom::MediaSessionObserverPtr observer) {
+  observer->MediaSessionInfoChanged(GetMediaSessionInfoSync());
   observers_.AddPtr(std::move(observer));
 }
 
diff --git a/services/media_session/mock_media_session.h b/services/media_session/mock_media_session.h
index 80e13b4..6210526 100644
--- a/services/media_session/mock_media_session.h
+++ b/services/media_session/mock_media_session.h
@@ -11,6 +11,7 @@
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
 #include "services/media_session/public/mojom/audio_focus.mojom.h"
+#include "services/media_session/public/mojom/media_controller.mojom.h"
 #include "services/media_session/public/mojom/media_session.mojom.h"
 
 namespace base {
@@ -23,7 +24,11 @@
 // A mock MediaSessionObsever that can be used for waiting for state changes.
 class MockMediaSessionMojoObserver : public mojom::MediaSessionObserver {
  public:
+  // A MediaSessionObserver can observe a MediaSession directly or through a
+  // MediaController.
   explicit MockMediaSessionMojoObserver(mojom::MediaSession& media_session);
+  explicit MockMediaSessionMojoObserver(mojom::MediaControllerPtr& controller);
+
   ~MockMediaSessionMojoObserver() override;
 
   // mojom::MediaSessionObserver overrides.
diff --git a/services/media_session/public/cpp/test/test_media_controller.h b/services/media_session/public/cpp/test/test_media_controller.h
index a16cdb0..86f5b22 100644
--- a/services/media_session/public/cpp/test/test_media_controller.h
+++ b/services/media_session/public/cpp/test/test_media_controller.h
@@ -25,6 +25,7 @@
   void Suspend() override {}
   void Resume() override {}
   void ToggleSuspendResume() override;
+  void AddObserver(mojom::MediaSessionObserverPtr) override {}
 
   int toggle_suspend_resume_count() const {
     return toggle_suspend_resume_count_;
diff --git a/services/media_session/public/mojom/media_controller.mojom b/services/media_session/public/mojom/media_controller.mojom
index fcc4960..70ffdd4 100644
--- a/services/media_session/public/mojom/media_controller.mojom
+++ b/services/media_session/public/mojom/media_controller.mojom
@@ -4,6 +4,8 @@
 
 module media_session.mojom;
 
+import "services/media_session/public/mojom/media_session.mojom";
+
 // Controls a MediaSession. This will automatically route commands to the
 // correct session if the audio focus changes. If the media session is
 // not controllable then the commands will be no-ops.
@@ -17,4 +19,9 @@
   // This will either suspend or resume the media session based on the
   // playback state.
   ToggleSuspendResume();
+
+  // Adds an observer that will forward events from the active media session.
+  // If the active session changes then observers do not need to be readded.
+  // Adding the observer will update the observer with the latest state.
+  AddObserver(MediaSessionObserver observer);
 };
diff --git a/services/network/public/cpp/features.cc b/services/network/public/cpp/features.cc
index bd224a18..6230a08 100644
--- a/services/network/public/cpp/features.cc
+++ b/services/network/public/cpp/features.cc
@@ -42,5 +42,13 @@
 const base::Feature kDelayRequestsOnMultiplexedConnections{
     "DelayRequestsOnMultiplexedConnections", base::FEATURE_ENABLED_BY_DEFAULT};
 
+// When kUnthrottleRequestsAfterLongQueuingDelay is enabled, an upper bound
+// is placed on how long the resource scheduler can queue any given request.
+// Once a request is queued for more than the specified duration, the request
+// is dispatched to the network.
+const base::Feature kUnthrottleRequestsAfterLongQueuingDelay{
+    "UnthrottleRequestsAfterLongQueuingDelay",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace features
 }  // namespace network
diff --git a/services/network/public/cpp/features.h b/services/network/public/cpp/features.h
index be4f5a8..61594f17 100644
--- a/services/network/public/cpp/features.h
+++ b/services/network/public/cpp/features.h
@@ -25,6 +25,8 @@
 extern const base::Feature kThrottleDelayable;
 COMPONENT_EXPORT(NETWORK_CPP)
 extern const base::Feature kDelayRequestsOnMultiplexedConnections;
+COMPONENT_EXPORT(NETWORK_CPP)
+extern const base::Feature kUnthrottleRequestsAfterLongQueuingDelay;
 
 }  // namespace features
 }  // namespace network
diff --git a/services/network/public/cpp/host_resolver_mojom_traits.cc b/services/network/public/cpp/host_resolver_mojom_traits.cc
index c2cf862..481ea09 100644
--- a/services/network/public/cpp/host_resolver_mojom_traits.cc
+++ b/services/network/public/cpp/host_resolver_mojom_traits.cc
@@ -120,7 +120,7 @@
     out_hosts.push_back(DnsHost::New(host.first.first, host.second));
   }
 
-  return out_hosts;
+  return base::make_optional(std::move(out_hosts));
 }
 
 // static
@@ -165,7 +165,7 @@
         DnsOverHttpsServer::New(server.server_template, server.use_post));
   }
 
-  return out_servers;
+  return base::make_optional(std::move(out_servers));
 }
 
 // static
diff --git a/services/network/resource_scheduler.cc b/services/network/resource_scheduler.cc
index bf92db77..7aa62cd 100644
--- a/services/network/resource_scheduler.cc
+++ b/services/network/resource_scheduler.cc
@@ -20,6 +20,8 @@
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/supports_user_data.h"
+#include "base/time/default_tick_clock.h"
+#include "base/time/tick_clock.h"
 #include "base/trace_event/trace_event.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/load_flags.h"
@@ -379,7 +381,8 @@
 class ResourceScheduler::Client {
  public:
   Client(const net::NetworkQualityEstimator* const network_quality_estimator,
-         ResourceScheduler* resource_scheduler)
+         ResourceScheduler* resource_scheduler,
+         const base::TickClock* tick_clock)
       : deprecated_is_loaded_(false),
         in_flight_delayable_count_(0),
         total_layout_blocking_count_(0),
@@ -388,7 +391,10 @@
         did_scheduler_yield_(false),
         network_quality_estimator_(network_quality_estimator),
         resource_scheduler_(resource_scheduler),
+        tick_clock_(tick_clock),
         weak_ptr_factory_(this) {
+    DCHECK(tick_clock_);
+
     UpdateParamsForNetworkQuality();
     // Must not run the conflicting experiments together.
     DCHECK(!params_for_network_quality_
@@ -782,6 +788,12 @@
     if (!url_request.url().SchemeIsHTTPOrHTTPS())
       return START_REQUEST;
 
+    if (params_for_network_quality_.max_queuing_time &&
+        tick_clock_->NowTicks() - url_request.creation_time() >=
+            params_for_network_quality_.max_queuing_time) {
+      return START_REQUEST;
+    }
+
     const net::HostPortPair& host_port_pair = request->host_port_pair();
 
     bool priority_delayable =
@@ -978,11 +990,17 @@
   // configuration.
   ResourceScheduler* resource_scheduler_;
 
+  // Guaranteed to be non-null.
+  const base::TickClock* tick_clock_;
+
   base::WeakPtrFactory<ResourceScheduler::Client> weak_ptr_factory_;
 };
 
-ResourceScheduler::ResourceScheduler(bool enabled)
-    : enabled_(enabled),
+ResourceScheduler::ResourceScheduler(bool enabled,
+                                     const base::TickClock* tick_clock)
+    : tick_clock_(tick_clock ? tick_clock
+                             : base::DefaultTickClock::GetInstance()),
+      enabled_(enabled),
       priority_requests_delayable_(
           base::FeatureList::IsEnabled(kPrioritySupportedRequestsDelayable)),
       head_priority_requests_delayable_(base::FeatureList::IsEnabled(
@@ -998,6 +1016,8 @@
                                                  kYieldMsParam,
                                                  kYieldMsDefault))),
       task_runner_(base::ThreadTaskRunnerHandle::Get()) {
+  DCHECK(tick_clock_);
+
   // Don't run the two experiments together.
   if (priority_requests_delayable_ && head_priority_requests_delayable_)
     priority_requests_delayable_ = false;
@@ -1061,7 +1081,7 @@
   DCHECK(!base::ContainsKey(client_map_, client_id));
 
   client_map_[client_id] =
-      std::make_unique<Client>(network_quality_estimator, this);
+      std::make_unique<Client>(network_quality_estimator, this, tick_clock_);
 }
 
 void ResourceScheduler::OnClientDeleted(int child_id, int route_id) {
diff --git a/services/network/resource_scheduler.h b/services/network/resource_scheduler.h
index 39500f2..7e1170e 100644
--- a/services/network/resource_scheduler.h
+++ b/services/network/resource_scheduler.h
@@ -29,6 +29,7 @@
 
 namespace base {
 class SequencedTaskRunner;
+class TickClock;
 }
 
 namespace net {
@@ -81,7 +82,8 @@
     base::OnceClosure resume_callback_;
   };
 
-  explicit ResourceScheduler(bool enabled);
+  explicit ResourceScheduler(bool enabled,
+                             const base::TickClock* tick_clock = nullptr);
   ~ResourceScheduler();
 
   // Requests that this ResourceScheduler schedule, and eventually loads, the
@@ -195,6 +197,9 @@
   ClientMap client_map_;
   RequestSet unowned_requests_;
 
+  // Guaranteed to be non-null.
+  const base::TickClock* tick_clock_;
+
   // Whether or not to enable ResourceScheduling. This will almost always be
   // enabled, except for some C++ headless embedders who may implement their own
   // resource scheduling via protocol handlers.
diff --git a/services/network/resource_scheduler_params_manager.cc b/services/network/resource_scheduler_params_manager.cc
index 2ad854a..a8904a6 100644
--- a/services/network/resource_scheduler_params_manager.cc
+++ b/services/network/resource_scheduler_params_manager.cc
@@ -10,6 +10,7 @@
 #include "base/optional.h"
 #include "base/strings/string_number_conversions.h"
 #include "net/nqe/network_quality_estimator.h"
+#include "net/nqe/network_quality_estimator_params.h"
 #include "services/network/public/cpp/features.h"
 
 namespace network {
@@ -40,16 +41,22 @@
   static const char kMaxDelayableRequestsBase[] = "MaxDelayableRequests";
   static const char kEffectiveConnectionTypeBase[] = "EffectiveConnectionType";
   static const char kNonDelayableWeightBase[] = "NonDelayableWeight";
+  static constexpr base::TimeDelta kUpperBoundQueuingDuration =
+      base::TimeDelta::FromSeconds(120);
+  static constexpr base::TimeDelta kLowerBoundQueuingDuration =
+      base::TimeDelta::FromSeconds(15);
 
   ResourceSchedulerParamsManager::ParamsForNetworkQualityContainer result;
   // Set the default params for networks with ECT Slow2G and 2G. These params
   // can still be overridden using the field trial.
-  result.emplace(std::make_pair(
-      net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G,
-      ResourceSchedulerParamsManager::ParamsForNetworkQuality(8, 3.0, false)));
-  result.emplace(std::make_pair(
-      net::EFFECTIVE_CONNECTION_TYPE_2G,
-      ResourceSchedulerParamsManager::ParamsForNetworkQuality(8, 3.0, false)));
+  result.emplace(
+      std::make_pair(net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G,
+                     ResourceSchedulerParamsManager::ParamsForNetworkQuality(
+                         8, 3.0, false, base::nullopt)));
+  result.emplace(
+      std::make_pair(net::EFFECTIVE_CONNECTION_TYPE_2G,
+                     ResourceSchedulerParamsManager::ParamsForNetworkQuality(
+                         8, 3.0, false, base::nullopt)));
 
   for (int config_param_index = 1; config_param_index <= 20;
        ++config_param_index) {
@@ -86,7 +93,8 @@
       result.emplace(std::make_pair(
           effective_connection_type.value(),
           ResourceSchedulerParamsManager::ParamsForNetworkQuality(
-              max_delayable_requests, non_delayable_weight, false)));
+              max_delayable_requests, non_delayable_weight, false,
+              base::nullopt)));
     }
   }
 
@@ -118,7 +126,43 @@
         result.emplace(std::make_pair(
             effective_connection_type,
             ResourceSchedulerParamsManager::ParamsForNetworkQuality(
-                kDefaultMaxNumDelayableRequestsPerClient, 0.0, true)));
+                kDefaultMaxNumDelayableRequestsPerClient, 0.0, true,
+                base::nullopt)));
+      }
+    }
+  }
+
+  if (base::FeatureList::IsEnabled(
+          features::kUnthrottleRequestsAfterLongQueuingDelay)) {
+    int http_rtt_multiplier = base::GetFieldTrialParamByFeatureAsInt(
+        features::kUnthrottleRequestsAfterLongQueuingDelay,
+        "http_rtt_multiplier", -1);
+
+    if (http_rtt_multiplier > 0) {
+      for (int ect = net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G;
+           ect <= net::EFFECTIVE_CONNECTION_TYPE_4G; ++ect) {
+        net::EffectiveConnectionType effective_connection_type =
+            static_cast<net::EffectiveConnectionType>(ect);
+        base::TimeDelta http_rtt =
+            net::NetworkQualityEstimatorParams::GetDefaultTypicalHttpRtt(
+                effective_connection_type);
+        base::TimeDelta max_queuing_time = http_rtt * http_rtt_multiplier;
+        if (max_queuing_time < kLowerBoundQueuingDuration)
+          max_queuing_time = kLowerBoundQueuingDuration;
+        if (max_queuing_time > kUpperBoundQueuingDuration)
+          max_queuing_time = kUpperBoundQueuingDuration;
+
+        ResourceSchedulerParamsManager::ParamsForNetworkQualityContainer::
+            iterator iter = result.find(effective_connection_type);
+        if (iter != result.end()) {
+          iter->second.max_queuing_time = max_queuing_time;
+        } else {
+          result.emplace(std::make_pair(
+              effective_connection_type,
+              ResourceSchedulerParamsManager::ParamsForNetworkQuality(
+                  kDefaultMaxNumDelayableRequestsPerClient, 0.0, false,
+                  max_queuing_time)));
+        }
       }
     }
   }
@@ -133,16 +177,24 @@
     : ResourceSchedulerParamsManager::ParamsForNetworkQuality(
           kDefaultMaxNumDelayableRequestsPerClient,
           0.0,
-          false) {}
+          false,
+          base::nullopt) {}
 
 ResourceSchedulerParamsManager::ParamsForNetworkQuality::
     ParamsForNetworkQuality(size_t max_delayable_requests,
                             double non_delayable_weight,
-                            bool delay_requests_on_multiplexed_connections)
+                            bool delay_requests_on_multiplexed_connections,
+                            base::Optional<base::TimeDelta> max_queuing_time)
     : max_delayable_requests(max_delayable_requests),
       non_delayable_weight(non_delayable_weight),
       delay_requests_on_multiplexed_connections(
-          delay_requests_on_multiplexed_connections) {}
+          delay_requests_on_multiplexed_connections),
+      max_queuing_time(max_queuing_time) {}
+
+ResourceSchedulerParamsManager::ParamsForNetworkQuality::
+    ParamsForNetworkQuality(
+        const ResourceSchedulerParamsManager::ParamsForNetworkQuality& other) =
+        default;
 
 ResourceSchedulerParamsManager::ResourceSchedulerParamsManager()
     : ResourceSchedulerParamsManager(GetParamsForNetworkQualityContainer()) {}
@@ -171,7 +223,7 @@
   if (iter != params_for_network_quality_container_.end())
     return iter->second;
   return ParamsForNetworkQuality(kDefaultMaxNumDelayableRequestsPerClient, 0.0,
-                                 false);
+                                 false, base::nullopt);
 }
 
 }  // namespace network
diff --git a/services/network/resource_scheduler_params_manager.h b/services/network/resource_scheduler_params_manager.h
index 76d37be..ab866e2 100644
--- a/services/network/resource_scheduler_params_manager.h
+++ b/services/network/resource_scheduler_params_manager.h
@@ -11,6 +11,7 @@
 #include <map>
 
 #include "base/component_export.h"
+#include "base/optional.h"
 #include "base/sequence_checker.h"
 #include "net/nqe/effective_connection_type.h"
 
@@ -27,7 +28,10 @@
 
     ParamsForNetworkQuality(size_t max_delayable_requests,
                             double non_delayable_weight,
-                            bool delay_requests_on_multiplexed_connections);
+                            bool delay_requests_on_multiplexed_connections,
+                            base::Optional<base::TimeDelta> max_queuing_time);
+
+    ParamsForNetworkQuality(const ParamsForNetworkQuality& other);
 
     // The maximum number of delayable requests allowed.
     size_t max_delayable_requests;
@@ -39,6 +43,10 @@
     // True if requests to servers that support prioritization (e.g.,
     // H2/SPDY/QUIC) should be delayed similar to other HTTP 1.1 requests.
     bool delay_requests_on_multiplexed_connections;
+
+    // The maximum duration for which a request is queued after after which the
+    // request is dispatched to the network.
+    base::Optional<base::TimeDelta> max_queuing_time;
   };
 
   ResourceSchedulerParamsManager();
diff --git a/services/network/resource_scheduler_params_manager_unittest.cc b/services/network/resource_scheduler_params_manager_unittest.cc
index 742767b..64883df 100644
--- a/services/network/resource_scheduler_params_manager_unittest.cc
+++ b/services/network/resource_scheduler_params_manager_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/test/scoped_feature_list.h"
+#include "net/nqe/network_quality_estimator_params.h"
 #include "services/network/public/cpp/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -19,6 +20,9 @@
 
 namespace {
 
+static constexpr base::TimeDelta kLowerBoundQueuingDuration =
+    base::TimeDelta::FromSeconds(15);
+
 class ResourceSchedulerParamsManagerTest : public testing::Test {
  public:
   ResourceSchedulerParamsManagerTest() : field_trial_list_(nullptr) {}
@@ -100,7 +104,12 @@
             resource_scheduler_params_manager
                 .GetParamsForEffectiveConnectionType(effective_connection_type)
                 .delay_requests_on_multiplexed_connections);
+        EXPECT_FALSE(
+            resource_scheduler_params_manager
+                .GetParamsForEffectiveConnectionType(effective_connection_type)
+                .max_queuing_time.has_value());
         return;
+
       case net::EFFECTIVE_CONNECTION_TYPE_3G:
         EXPECT_EQ(10u, resource_scheduler_params_manager
                            .GetParamsForEffectiveConnectionType(
@@ -114,7 +123,12 @@
             resource_scheduler_params_manager
                 .GetParamsForEffectiveConnectionType(effective_connection_type)
                 .delay_requests_on_multiplexed_connections);
+        EXPECT_FALSE(
+            resource_scheduler_params_manager
+                .GetParamsForEffectiveConnectionType(effective_connection_type)
+                .max_queuing_time.has_value());
         return;
+
       case net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G:
       case net::EFFECTIVE_CONNECTION_TYPE_2G:
         EXPECT_EQ(8u, resource_scheduler_params_manager
@@ -129,7 +143,12 @@
             resource_scheduler_params_manager
                 .GetParamsForEffectiveConnectionType(effective_connection_type)
                 .delay_requests_on_multiplexed_connections);
+        EXPECT_FALSE(
+            resource_scheduler_params_manager
+                .GetParamsForEffectiveConnectionType(effective_connection_type)
+                .max_queuing_time.has_value());
         return;
+
       case net::EFFECTIVE_CONNECTION_TYPE_LAST:
         NOTREACHED();
         return;
@@ -195,6 +214,9 @@
       EXPECT_TRUE(resource_scheduler_params_manager
                       .GetParamsForEffectiveConnectionType(ect)
                       .delay_requests_on_multiplexed_connections);
+      EXPECT_FALSE(resource_scheduler_params_manager
+                       .GetParamsForEffectiveConnectionType(ect)
+                       .max_queuing_time.has_value());
 
     } else if (effective_connection_type == net::EFFECTIVE_CONNECTION_TYPE_3G) {
       EXPECT_EQ(10u, resource_scheduler_params_manager
@@ -206,6 +228,9 @@
       EXPECT_FALSE(resource_scheduler_params_manager
                        .GetParamsForEffectiveConnectionType(ect)
                        .delay_requests_on_multiplexed_connections);
+      EXPECT_FALSE(resource_scheduler_params_manager
+                       .GetParamsForEffectiveConnectionType(ect)
+                       .max_queuing_time.has_value());
 
     } else {
       VerifyDefaultParams(
@@ -215,6 +240,99 @@
   }
 }
 
+TEST_F(ResourceSchedulerParamsManagerTest, MaxQueuingTime) {
+  base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
+  const std::string kTrialName = "TrialFoo";
+  const std::string kGroupName = "GroupFoo";  // Value not used
+  base::test::ScopedFeatureList scoped_feature_list;
+
+  scoped_refptr<base::FieldTrial> trial =
+      base::FieldTrialList::CreateFieldTrial(kTrialName, kGroupName);
+
+  std::map<std::string, std::string> params;
+  params["http_rtt_multiplier"] = "20";
+  ASSERT_TRUE(
+      base::FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams(
+          kTrialName, kGroupName, params));
+
+  std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
+  feature_list->RegisterFieldTrialOverride(
+      features::kUnthrottleRequestsAfterLongQueuingDelay.name,
+      base::FeatureList::OVERRIDE_ENABLE_FEATURE, trial.get());
+  scoped_feature_list.InitWithFeatureList(std::move(feature_list));
+
+  ResourceSchedulerParamsManager resource_scheduler_params_manager;
+
+  for (int effective_connection_type = net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
+       effective_connection_type < net::EFFECTIVE_CONNECTION_TYPE_LAST;
+       ++effective_connection_type) {
+    net::EffectiveConnectionType ect =
+        static_cast<net::EffectiveConnectionType>(effective_connection_type);
+    base::TimeDelta typical_http_rtt =
+        net::NetworkQualityEstimatorParams::GetDefaultTypicalHttpRtt(ect);
+
+    if (effective_connection_type == net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G ||
+        effective_connection_type == net::EFFECTIVE_CONNECTION_TYPE_2G) {
+      EXPECT_EQ(8u, resource_scheduler_params_manager
+                        .GetParamsForEffectiveConnectionType(ect)
+                        .max_delayable_requests);
+      EXPECT_EQ(3.0, resource_scheduler_params_manager
+                         .GetParamsForEffectiveConnectionType(ect)
+                         .non_delayable_weight);
+      EXPECT_TRUE(resource_scheduler_params_manager
+                      .GetParamsForEffectiveConnectionType(ect)
+                      .delay_requests_on_multiplexed_connections);
+      EXPECT_EQ(typical_http_rtt * 20,
+                resource_scheduler_params_manager
+                    .GetParamsForEffectiveConnectionType(ect)
+                    .max_queuing_time);
+
+    } else if (effective_connection_type == net::EFFECTIVE_CONNECTION_TYPE_3G) {
+      EXPECT_EQ(10u, resource_scheduler_params_manager
+                         .GetParamsForEffectiveConnectionType(ect)
+                         .max_delayable_requests);
+      EXPECT_EQ(0.0, resource_scheduler_params_manager
+                         .GetParamsForEffectiveConnectionType(ect)
+                         .non_delayable_weight);
+      EXPECT_TRUE(resource_scheduler_params_manager
+                      .GetParamsForEffectiveConnectionType(ect)
+                      .delay_requests_on_multiplexed_connections);
+      EXPECT_EQ(kLowerBoundQueuingDuration,
+                resource_scheduler_params_manager
+                    .GetParamsForEffectiveConnectionType(ect)
+                    .max_queuing_time);
+
+    } else if (effective_connection_type == net::EFFECTIVE_CONNECTION_TYPE_4G) {
+      EXPECT_EQ(10u, resource_scheduler_params_manager
+                         .GetParamsForEffectiveConnectionType(ect)
+                         .max_delayable_requests);
+      EXPECT_EQ(0.0, resource_scheduler_params_manager
+                         .GetParamsForEffectiveConnectionType(ect)
+                         .non_delayable_weight);
+      EXPECT_FALSE(resource_scheduler_params_manager
+                       .GetParamsForEffectiveConnectionType(ect)
+                       .delay_requests_on_multiplexed_connections);
+      EXPECT_EQ(kLowerBoundQueuingDuration,
+                resource_scheduler_params_manager
+                    .GetParamsForEffectiveConnectionType(ect)
+                    .max_queuing_time);
+    } else {
+      EXPECT_EQ(10u, resource_scheduler_params_manager
+                         .GetParamsForEffectiveConnectionType(ect)
+                         .max_delayable_requests);
+      EXPECT_EQ(0.0, resource_scheduler_params_manager
+                         .GetParamsForEffectiveConnectionType(ect)
+                         .non_delayable_weight);
+      EXPECT_FALSE(resource_scheduler_params_manager
+                       .GetParamsForEffectiveConnectionType(ect)
+                       .delay_requests_on_multiplexed_connections);
+      EXPECT_FALSE(resource_scheduler_params_manager
+                       .GetParamsForEffectiveConnectionType(ect)
+                       .max_queuing_time.has_value());
+    }
+  }
+}
+
 // Verify that the params are parsed correctly when
 // kDelayRequestsOnMultiplexedConnections and kThrottleDelayable are enabled.
 TEST_F(ResourceSchedulerParamsManagerTest, MultipleFieldTrialsEnabled) {
@@ -275,6 +393,9 @@
       EXPECT_TRUE(resource_scheduler_params_manager
                       .GetParamsForEffectiveConnectionType(ect)
                       .delay_requests_on_multiplexed_connections);
+      EXPECT_FALSE(resource_scheduler_params_manager
+                       .GetParamsForEffectiveConnectionType(ect)
+                       .max_queuing_time.has_value());
 
     } else if (effective_connection_type == net::EFFECTIVE_CONNECTION_TYPE_3G) {
       EXPECT_EQ(12u, resource_scheduler_params_manager
@@ -286,6 +407,10 @@
       EXPECT_TRUE(resource_scheduler_params_manager
                       .GetParamsForEffectiveConnectionType(ect)
                       .delay_requests_on_multiplexed_connections);
+      EXPECT_FALSE(resource_scheduler_params_manager
+                       .GetParamsForEffectiveConnectionType(ect)
+                       .max_queuing_time.has_value());
+
     } else if (effective_connection_type == net::EFFECTIVE_CONNECTION_TYPE_4G) {
       EXPECT_EQ(14u, resource_scheduler_params_manager
                          .GetParamsForEffectiveConnectionType(ect)
@@ -296,6 +421,10 @@
       EXPECT_FALSE(resource_scheduler_params_manager
                        .GetParamsForEffectiveConnectionType(ect)
                        .delay_requests_on_multiplexed_connections);
+      EXPECT_FALSE(resource_scheduler_params_manager
+                       .GetParamsForEffectiveConnectionType(ect)
+                       .max_queuing_time.has_value());
+
     } else {
       VerifyDefaultParams(resource_scheduler_params_manager, ect);
     }
@@ -447,6 +576,10 @@
                 .GetParamsForEffectiveConnectionType(
                     net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G)
                 .non_delayable_weight);
+  EXPECT_FALSE(resource_scheduler_params_manager
+                   .GetParamsForEffectiveConnectionType(
+                       net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G)
+                   .max_queuing_time.has_value());
 
   VerifyDefaultParams(resource_scheduler_params_manager,
                       net::EFFECTIVE_CONNECTION_TYPE_2G);
@@ -460,6 +593,10 @@
                                          .GetParamsForEffectiveConnectionType(
                                              net::EFFECTIVE_CONNECTION_TYPE_3G)
                                          .non_delayable_weight);
+  EXPECT_FALSE(resource_scheduler_params_manager
+                   .GetParamsForEffectiveConnectionType(
+                       net::EFFECTIVE_CONNECTION_TYPE_3G)
+                   .max_queuing_time.has_value());
 
   VerifyDefaultParams(resource_scheduler_params_manager,
                       net::EFFECTIVE_CONNECTION_TYPE_4G);
diff --git a/services/network/resource_scheduler_unittest.cc b/services/network/resource_scheduler_unittest.cc
index 15ea7c1..52078d2 100644
--- a/services/network/resource_scheduler_unittest.cc
+++ b/services/network/resource_scheduler_unittest.cc
@@ -22,9 +22,11 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_entropy_provider.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/simple_test_tick_clock.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/timer/timer.h"
 #include "net/base/host_port_pair.h"
+#include "net/base/load_timing_info.h"
 #include "net/base/request_priority.h"
 #include "net/http/http_server_properties_impl.h"
 #include "net/nqe/network_quality_estimator_test_util.h"
@@ -171,7 +173,7 @@
     CleanupScheduler();
 
     // Destroys previous scheduler.
-    scheduler_.reset(new ResourceScheduler(enabled));
+    scheduler_.reset(new ResourceScheduler(enabled, &tick_clock_));
 
     scheduler()->SetResourceSchedulerParamsManagerForTests(
         resource_scheduler_params_manager_);
@@ -188,7 +190,7 @@
     for (int i = 0; i != net::EFFECTIVE_CONNECTION_TYPE_LAST; ++i) {
       auto type = static_cast<net::EffectiveConnectionType>(i);
       c[type] = ResourceSchedulerParamsManager::ParamsForNetworkQuality(
-          max_delayable_requests, 0.0, false);
+          max_delayable_requests, 0.0, false, base::nullopt);
     }
     return ResourceSchedulerParamsManager(std::move(c));
   }
@@ -357,9 +359,9 @@
              ResourceSchedulerParamsManager::ParamsForNetworkQuality>
         params_for_network_quality_container;
     ResourceSchedulerParamsManager::ParamsForNetworkQuality params_slow_2g(
-        8, 3.0, true);
-    ResourceSchedulerParamsManager::ParamsForNetworkQuality params_2g(8, 3.0,
-                                                                      true);
+        8, 3.0, true, base::nullopt);
+    ResourceSchedulerParamsManager::ParamsForNetworkQuality params_2g(
+        8, 3.0, true, base::nullopt);
 
     params_for_network_quality_container
         [net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G] = params_slow_2g;
@@ -376,9 +378,9 @@
              ResourceSchedulerParamsManager::ParamsForNetworkQuality>
         params_for_network_quality_container;
     ResourceSchedulerParamsManager::ParamsForNetworkQuality params_slow_2g(
-        8, 3.0, false);
-    ResourceSchedulerParamsManager::ParamsForNetworkQuality params_3g(10, 0.0,
-                                                                      false);
+        8, 3.0, false, base::nullopt);
+    ResourceSchedulerParamsManager::ParamsForNetworkQuality params_3g(
+        10, 0.0, false, base::nullopt);
 
     if (lower_delayable_count_enabled) {
       params_slow_2g.max_delayable_requests = 2;
@@ -401,6 +403,21 @@
         params_for_network_quality_container);
   }
 
+  void InitializeMaxQueuingDelayExperiment(base::TimeDelta max_queuing_time) {
+    std::map<net::EffectiveConnectionType,
+             ResourceSchedulerParamsManager::ParamsForNetworkQuality>
+        params_for_network_quality_container;
+
+    ResourceSchedulerParamsManager::ParamsForNetworkQuality params_slow_2g(
+        8, 3.0, true, base::nullopt);
+    params_slow_2g.max_queuing_time = max_queuing_time;
+    params_for_network_quality_container
+        [net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G] = params_slow_2g;
+
+    resource_scheduler_params_manager_.Reset(
+        params_for_network_quality_container);
+  }
+
   void NonDelayableThrottlesDelayableHelper(double non_delayable_weight) {
     // Should be in sync with .cc for ECT SLOW_2G,
     const int kDefaultMaxNumDelayableRequestsPerClient = 8;
@@ -438,6 +455,7 @@
   net::TestURLRequestContext context_;
   ResourceSchedulerParamsManager resource_scheduler_params_manager_;
   base::FieldTrialList field_trial_list_;
+  base::SimpleTestTickClock tick_clock_;
 };
 
 TEST_F(ResourceSchedulerTest, OneIsolatedLowRequest) {
@@ -1612,7 +1630,8 @@
 TEST_F(ResourceSchedulerTest, MultipleInstances_1) {
   SetMaxDelayableRequests(1);
   // In some circumstances there may exist multiple instances.
-  ResourceScheduler another_scheduler(false);
+  ResourceScheduler another_scheduler(false,
+                                      base::DefaultTickClock::GetInstance());
 
   std::unique_ptr<TestRequest> high(
       NewRequest("http://host/high", net::HIGHEST));
@@ -1628,7 +1647,8 @@
 
 TEST_F(ResourceSchedulerTest, MultipleInstances_2) {
   SetMaxDelayableRequests(1);
-  ResourceScheduler another_scheduler(true);
+  ResourceScheduler another_scheduler(true,
+                                      base::DefaultTickClock::GetInstance());
   another_scheduler.OnClientCreated(kChildId, kRouteId,
                                     &network_quality_estimator_);
 
@@ -1797,6 +1817,108 @@
   EXPECT_FALSE(last_singlehost->started());
 }
 
+// Verify that when |max_queuing_time| is set, requests queued for too long
+// duration are dispatched to the network.
+TEST_F(ResourceSchedulerTest, MaxQueuingDelaySet) {
+  base::TimeDelta max_queuing_time = base::TimeDelta::FromSeconds(15);
+  InitializeMaxQueuingDelayExperiment(max_queuing_time);
+  network_quality_estimator_.set_effective_connection_type(
+      net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
+
+  InitializeScheduler();
+  // The limit will matter only once the page has a body, since delayable
+  // requests are not loaded before that.
+  scheduler()->DeprecatedOnNavigate(kChildId, kRouteId);
+
+  // Throw in one high priority request to ensure that it does not matter once
+  // a body exists.
+  std::unique_ptr<TestRequest> high(
+      NewRequest("http://host/high", net::HIGHEST));
+  EXPECT_TRUE(high->started());
+
+  // Should be in sync with resource_scheduler.cc for effective connection type
+  // (ECT) 2G. For ECT of 2G, number of low priority requests allowed are:
+  // 8 - 3 * count of high priority requests in flight. That expression computes
+  // to 8 - 3 * 1  = 5.
+  const int max_low_priority_requests_allowed = 5;
+
+  std::vector<std::unique_ptr<TestRequest>> lows_singlehost;
+  // Queue up to the maximum limit. Use different host names to prevent the
+  // per host limit from kicking in.
+  for (int i = 0; i < max_low_priority_requests_allowed + 10; ++i) {
+    // Keep unique hostnames to prevent the per host limit from kicking in.
+    std::string url = "http://host" + base::IntToString(i) + "/low";
+    lows_singlehost.push_back(NewRequest(url.c_str(), net::LOWEST));
+    EXPECT_EQ(i < max_low_priority_requests_allowed,
+              lows_singlehost[i]->started());
+  }
+
+  // Advance the clock by more than |max_queuing_time|.
+  tick_clock_.SetNowTicks(base::DefaultTickClock::GetInstance()->NowTicks() +
+                          max_queuing_time + base::TimeDelta::FromSeconds(1));
+
+  // Since the requests have been queued for too long, they should now be
+  // dispatched. Trigger the calculation of queuing time by Triggering the
+  // finish of a single request.
+  lows_singlehost[0].reset();
+  base::RunLoop().RunUntilIdle();
+
+  for (int i = 1; i < max_low_priority_requests_allowed + 10; ++i) {
+    EXPECT_TRUE(lows_singlehost[i]->started());
+  }
+}
+
+// Verify that when |max_queuing_time| is not set, requests queued for too long
+// duration are not dispatched to the network.
+TEST_F(ResourceSchedulerTest, MaxQueuingDelayNotSet) {
+  base::TimeDelta max_queuing_time = base::TimeDelta::FromSeconds(15);
+  network_quality_estimator_.set_effective_connection_type(
+      net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
+
+  InitializeScheduler();
+  // The limit will matter only once the page has a body, since delayable
+  // requests are not loaded before that.
+  scheduler()->DeprecatedOnNavigate(kChildId, kRouteId);
+
+  // Throw in one high priority request to ensure that it does not matter once
+  // a body exists.
+  std::unique_ptr<TestRequest> high(
+      NewRequest("http://host/high", net::HIGHEST));
+  EXPECT_TRUE(high->started());
+
+  // Should be in sync with resource_scheduler.cc for effective connection type
+  // (ECT) 2G. For ECT of 2G, number of low priority requests allowed are:
+  // 8 - 3 * count of high priority requests in flight. That expression computes
+  // to 8 - 3 * 1  = 5.
+  const int max_low_priority_requests_allowed = 5;
+
+  std::vector<std::unique_ptr<TestRequest>> lows_singlehost;
+  // Queue up to the maximum limit. Use different host names to prevent the
+  // per host limit from kicking in.
+  for (int i = 0; i < max_low_priority_requests_allowed + 10; ++i) {
+    // Keep unique hostnames to prevent the per host limit from kicking in.
+    std::string url = "http://host" + base::IntToString(i) + "/low";
+    lows_singlehost.push_back(NewRequest(url.c_str(), net::LOWEST));
+    EXPECT_EQ(i < max_low_priority_requests_allowed,
+              lows_singlehost[i]->started());
+  }
+
+  // Advance the clock by more than |max_queuing_time|.
+  tick_clock_.SetNowTicks(base::DefaultTickClock::GetInstance()->NowTicks() +
+                          max_queuing_time + base::TimeDelta::FromSeconds(1));
+
+  // Triggering the finish of a single request should not trigger dispatch of
+  // requests that have been queued for too long.
+  lows_singlehost[0].reset();
+  base::RunLoop().RunUntilIdle();
+
+  // Starting at i=1 since the request at index 0 has been deleted.
+  for (int i = 1; i < max_low_priority_requests_allowed + 10; ++i) {
+    EXPECT_EQ(i < max_low_priority_requests_allowed + 1,
+              lows_singlehost[i]->started());
+  }
+}
+
 }  // unnamed namespace
 
 }  // namespace network
diff --git a/services/ws/client_root.cc b/services/ws/client_root.cc
index c6b63e0..c935867 100644
--- a/services/ws/client_root.cc
+++ b/services/ws/client_root.cc
@@ -11,6 +11,7 @@
 #include "services/ws/server_window.h"
 #include "services/ws/window_service.h"
 #include "services/ws/window_tree.h"
+#include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/mus/client_surface_embedder.h"
 #include "ui/aura/mus/property_converter.h"
@@ -19,6 +20,7 @@
 #include "ui/compositor/compositor.h"
 #include "ui/compositor/dip_util.h"
 #include "ui/compositor/property_change_reason.h"
+#include "ui/wm/core/coordinate_conversion.h"
 
 namespace ws {
 
@@ -96,8 +98,11 @@
 }
 
 void ClientRoot::OnLocalSurfaceIdChanged() {
-  if (!ShouldAssignLocalSurfaceId())
-    HandleBoundsOrScaleFactorChange(window_->bounds());
+  if (ShouldAssignLocalSurfaceId())
+    return;
+
+  HandleBoundsOrScaleFactorChange(is_top_level_ ? window_->GetBoundsInScreen()
+                                                : window_->bounds());
 }
 
 void ClientRoot::AttachChildFrameSinkId(ServerWindow* server_window) {
@@ -213,7 +218,21 @@
                                        const gfx::Rect& old_bounds,
                                        const gfx::Rect& new_bounds,
                                        ui::PropertyChangeReason reason) {
-  HandleBoundsOrScaleFactorChange(old_bounds);
+  if (!is_top_level_) {
+    HandleBoundsOrScaleFactorChange(old_bounds);
+    return;
+  }
+  gfx::Rect old_bounds_in_screen = old_bounds;
+  aura::Window* root = window->GetRootWindow();
+  if (root && aura::client::GetScreenPositionClient(root))
+    ::wm::ConvertRectToScreen(window->parent(), &old_bounds_in_screen);
+  if (is_moving_across_displays_) {
+    if (!scheduled_change_old_bounds_)
+      scheduled_change_old_bounds_ = old_bounds_in_screen;
+    return;
+  }
+  DCHECK(!scheduled_change_old_bounds_);
+  HandleBoundsOrScaleFactorChange(old_bounds_in_screen);
 }
 
 void ClientRoot::OnWindowAddedToRootWindow(aura::Window* window) {
@@ -221,9 +240,6 @@
   DCHECK(window->GetHost());
   window->GetHost()->AddObserver(this);
   CheckForScaleFactorChange();
-  window_tree_->window_tree_client_->OnWindowDisplayChanged(
-      window_tree_->TransportIdForWindow(window),
-      window->GetHost()->GetDisplayId());
 }
 
 void ClientRoot::OnWindowRemovingFromRootWindow(aura::Window* window,
@@ -233,6 +249,24 @@
   window->GetHost()->RemoveObserver(this);
 }
 
+void ClientRoot::OnWillMoveWindowToDisplay(aura::Window* window,
+                                           int64_t new_display_id) {
+  DCHECK(!is_moving_across_displays_);
+  is_moving_across_displays_ = true;
+}
+
+void ClientRoot::OnDidMoveWindowToDisplay(aura::Window* window) {
+  DCHECK(is_moving_across_displays_);
+  is_moving_across_displays_ = false;
+  window_tree_->window_tree_client_->OnWindowDisplayChanged(
+      window_tree_->TransportIdForWindow(window),
+      window->GetHost()->GetDisplayId());
+  if (scheduled_change_old_bounds_) {
+    HandleBoundsOrScaleFactorChange(scheduled_change_old_bounds_.value());
+    scheduled_change_old_bounds_.reset();
+  }
+}
+
 void ClientRoot::OnHostResized(aura::WindowTreeHost* host) {
   // This function is also called when the device-scale-factor changes too.
   CheckForScaleFactorChange();
diff --git a/services/ws/client_root.h b/services/ws/client_root.h
index 44e630db..86e5c6e 100644
--- a/services/ws/client_root.h
+++ b/services/ws/client_root.h
@@ -9,11 +9,13 @@
 
 #include "base/component_export.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "components/viz/common/surfaces/local_surface_id.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/host/host_frame_sink_client.h"
 #include "ui/aura/window_observer.h"
 #include "ui/aura/window_tree_host_observer.h"
+#include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace aura {
@@ -102,6 +104,9 @@
   void OnWindowAddedToRootWindow(aura::Window* window) override;
   void OnWindowRemovingFromRootWindow(aura::Window* window,
                                       aura::Window* new_root) override;
+  void OnWillMoveWindowToDisplay(aura::Window* window,
+                                 int64_t new_display_id) override;
+  void OnDidMoveWindowToDisplay(aura::Window* window) override;
 
   // aura::WindowTreeHostObserver:
   void OnHostResized(aura::WindowTreeHost* host) override;
@@ -123,6 +128,9 @@
 
   std::unique_ptr<aura::ClientSurfaceEmbedder> client_surface_embedder_;
 
+  bool is_moving_across_displays_ = false;
+  base::Optional<gfx::Rect> scheduled_change_old_bounds_;
+
   // If non-null then the fallback SurfaceInfo was supplied before the primary
   // surface. This will be pushed to the Layer once the primary surface is
   // supplied.
diff --git a/services/ws/test_change_tracker.cc b/services/ws/test_change_tracker.cc
index 5aff46b..6245f00 100644
--- a/services/ws/test_change_tracker.cc
+++ b/services/ws/test_change_tracker.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 
+#include "base/strings/pattern.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -287,7 +288,7 @@
 bool ContainsChange(const std::vector<Change>& changes,
                     const std::string& change_description) {
   for (auto& change : changes) {
-    if (change_description == ChangeToDescription(change))
+    if (base::MatchPattern(ChangeToDescription(change), change_description))
       return true;
   }
   return false;
diff --git a/services/ws/test_change_tracker.h b/services/ws/test_change_tracker.h
index 97edfb806..7c1212c8 100644
--- a/services/ws/test_change_tracker.h
+++ b/services/ws/test_change_tracker.h
@@ -146,6 +146,8 @@
                               std::vector<TestWindow>* test_windows);
 
 // Returns true if |changes| contains a Change matching |change_description|.
+// |change_description| is a pattern which should be compared with
+// base::MatchPattern (see base/strings/pattern.h for the details).
 bool ContainsChange(const std::vector<Change>& changes,
                     const std::string& change_description);
 
diff --git a/services/ws/window_tree.cc b/services/ws/window_tree.cc
index 6717015..6f1d5da8 100644
--- a/services/ws/window_tree.cc
+++ b/services/ws/window_tree.cc
@@ -30,7 +30,6 @@
 #include "services/ws/window_service_delegate.h"
 #include "services/ws/window_service_observer.h"
 #include "ui/aura/client/aura_constants.h"
-#include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/client/transient_window_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/mus/os_exchange_data_provider_mus.h"
@@ -1178,12 +1177,10 @@
   if (IsLocalSurfaceIdAssignedByClient(window))
     server_window->set_local_surface_id(local_surface_id);
 
-  aura::client::ScreenPositionClient* screen_position_client =
-      aura::client::GetScreenPositionClient(window->GetRootWindow());
-  if (IsTopLevel(window) && screen_position_client) {
+  if (IsTopLevel(window)) {
     display::Display dst_display =
         display::Screen::GetScreen()->GetDisplayMatching(bounds);
-    screen_position_client->SetBounds(window, bounds, dst_display);
+    window->SetBoundsInScreen(bounds, dst_display);
   } else {
     window->SetBounds(bounds);
   }
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index b8f6baf..cb80b3c 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -994,12 +994,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "select_to_speak_extension_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "service_manager_unittests"
       },
       {
@@ -1705,12 +1699,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "select_to_speak_extension_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "service_manager_unittests"
       },
       {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 74ed4b7..6cc42af6 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -1444,7 +1444,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
+          "--enable-features=NetworkService,NetworkServiceInProcess",
           "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
@@ -3031,7 +3031,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
+          "--enable-features=NetworkService,NetworkServiceInProcess",
           "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index 693ecbb..73033944 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -3614,7 +3614,6 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/mac_window_server_killers.browser_tests.filter",
           "--gtest_shuffle"
         ],
-        "experiment_percentage": 50,
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 4016830..1ea3fbed 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -4890,15 +4890,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "select_to_speak_extension_tests"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "service_manager_unittests"
       },
       {
@@ -6374,21 +6365,6 @@
             }
           ]
         },
-        "test": "select_to_speak_extension_tests"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
-            }
-          ]
-        },
         "test": "service_manager_unittests"
       },
       {
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index 0d58107..548e70b2 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -32,7 +32,6 @@
     "//testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter",
     "//testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter",
     "//testing/buildbot/filters/mojo.fyi.chromeos.network_browser_tests.filter",
-    "//testing/buildbot/filters/mojo.fyi.chromeos.network_interactive_ui_tests.filter",
     "//testing/buildbot/filters/mojo.fyi.network_browser_tests.filter",
     "//testing/buildbot/filters/webui_polymer2_browser_tests.filter",
     "//testing/buildbot/filters/webrtc_functional.browser_tests.filter",
@@ -96,6 +95,7 @@
 
   data = [
     "//testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter",
+    "//testing/buildbot/filters/mojo.fyi.chromeos.network_interactive_ui_tests.filter",
     "//testing/buildbot/filters/webui_polymer2_interactive_ui_tests.filter",
   ]
 }
diff --git a/testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter b/testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter
index 887b230..6efe7aba 100644
--- a/testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter
+++ b/testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter
@@ -33,8 +33,9 @@
 -AutomationApiTestWithDeviceScaleFactor.*
 -AutomationManagerAuraBrowserTest.*
 
-# ChromeVox not yet supported. https://crbug.com/594887
+# ChromeVox and Select-to-Speak not yet supported. https://crbug.com/594887
 -ChromeVox*
+-SelectToSpeak*
 
 # Touch gestures don't work in webcontents. https://crbug.com/866991.
 -TopControlsSlideControllerTest.TestScrollingMaximizedPageBeforeGoingToTabletMode
@@ -134,9 +135,6 @@
 # JS failure: hasAccessToCurrentWindow: FAIL (no message)
 -LockScreenNoteTakingTest.*
 
-# desktop_window_tree_host_mus.cc(796) Check failed: !window->GetRootWindow() || this->window() == window->GetRootWindow().
--LoginFeedbackTest.*
-
 # Missing magnification manager and also RefCounted check failed:
 # CalledOnValidSequence() from SchedulerWorkerDelegate::OnMainExit
 -LoginScreenDefaultPolicyInSessionBrowsertest.*
diff --git a/testing/buildbot/filters/chromeos.single_process_mash.ash_unittests.filter b/testing/buildbot/filters/chromeos.single_process_mash.ash_unittests.filter
index 46ddb992..a4f10f5 100644
--- a/testing/buildbot/filters/chromeos.single_process_mash.ash_unittests.filter
+++ b/testing/buildbot/filters/chromeos.single_process_mash.ash_unittests.filter
@@ -31,6 +31,8 @@
 -ShelfWidgetVirtualKeyboardTest.ClickingHidesVirtualKeyboard
 -ShelfWidgetVirtualKeyboardTest.TappingHidesVirtualKeyboard
 -ShelfWidgetVirtualKeyboardTest.DoesNotHideLockedVirtualKeyboard
+-StatusAreaWidgetVirtualKeyboardTest.ClickingVirtualKeyboardTrayHidesShownKeyboard
+-StatusAreaWidgetVirtualKeyboardTest.TappingVirtualKeyboardTrayHidesShownKeyboard
 -StatusAreaWidgetVirtualKeyboardTest.ClickingHidesVirtualKeyboard
 -StatusAreaWidgetVirtualKeyboardTest.TappingHidesVirtualKeyboard
 -StatusAreaWidgetVirtualKeyboardTest.DoesNotHideLockedVirtualKeyboard
@@ -55,6 +57,7 @@
 -VirtualKeyboardRootWindowControllerTest.ZOrderTest
 -VirtualKeyboardTest.EventsAreHandledBasedOnHitTestBounds
 -VirtualKeyboardTest.HitTestBoundsAreResetWhenContainerTypeChanges
+-VirtualKeyboardTrayTest.PerformActionTogglesVirtualKeyboard
 -WindowSelectorControllerTest.ToggleOverviewModeHidesVirtualKeyboard
 -WindowSelectorControllerTest.ToggleOverviewModeDoesNotHideLockedVirtualKeyboard
 -WorkspaceLayoutManagerKeyboardTest.IgnoreWorkAreaChangeinNonStickyMode
diff --git a/testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter b/testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter
index 2481fff..afbb30a8 100644
--- a/testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter
+++ b/testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter
@@ -27,8 +27,9 @@
 -AutomationApiTestWithDeviceScaleFactor.*
 -AutomationManagerAuraBrowserTest.*
 
-# ChromeVox not yet supported. https://crbug.com/594887
+# ChromeVox and Select-to-Speak not yet supported. https://crbug.com/594887
 -ChromeVox*
+-SelectToSpeak*
 
 # Touch gestures don't work in webcontents. https://crbug.com/866991.
 -TopControlsSlideControllerTest.DisplayRotation
@@ -54,9 +55,6 @@
 # JS failure: hasAccessToCurrentWindow: FAIL (no message)
 -LockScreenNoteTakingTest.*
 
-# desktop_window_tree_host_mus.cc(796) Check failed: !window->GetRootWindow() || this->window() == window->GetRootWindow().
--LoginFeedbackTest.*
-
 # Missing magnification manager and also RefCounted check failed:
 # CalledOnValidSequence() from SchedulerWorkerDelegate::OnMainExit
 -LoginScreenDefaultPolicyLoginScreenBrowsertest.*
@@ -185,12 +183,5 @@
 # Flaky tests. crbug.com/833144
 -GetAuthTokenFunctionPublicSessionTest.NonWhitelisted
 
-# WakeEventPageTest.NoBackgroundPage is flaky. crbug.com/895068
--WakeEventPageTest.NoBackgroundPage
-
-# ContentScriptApiTests are flaky: crbug.com/895110 and crbug.com/895070
--ContentScriptApiTest.ContentScriptExtensionAPIs
--ContentScriptApiTest.ContentScriptBlockingScriptsDontRunTwice
-
 # Test hangs in single process mash crbug.com/897012
 -SelectFileDialogExtensionBrowserTest.SelectFileVirtualKeyboard_TabletMode
diff --git a/testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter b/testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter
index c9da5a0e..d6208876 100644
--- a/testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter
+++ b/testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter
@@ -59,23 +59,5 @@
 -TabDragging/DetachToBrowserTabDragControllerTestTouch.PressSecondFingerWhileDetached/0
 -TabDragging/DetachToBrowserTabDragControllerTestTouch.SecondFingerPressTest/0
 
-# TabScrubber: crbug.com/889097
--TabScrubberTest.Bounds
--TabScrubberTest.CloseBrowser
--TabScrubberTest.DeleteBeforeHighlighted
--TabScrubberTest.DeleteHighlighted
--TabScrubberTest.FullScreenBrowser
--TabScrubberTest.MoveAfter
--TabScrubberTest.MoveBefore
--TabScrubberTest.MoveHighlighted
--TabScrubberTest.Multi
--TabScrubberTest.MultiBrowser
--TabScrubberTest.Repeated
--TabScrubberTest.RTLMoveBefore
--TabScrubberTest.RTLMulti
--TabScrubberTest.RTLSkipped
--TabScrubberTest.Single
--TabScrubberTest.Skipped
-
 # This test is flaky. https://crbug.com/897879
 -ExtensionApiTest.DisplayModeWindowIsInFullscreen
diff --git a/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter b/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter
index ec8a165..52e7ef9 100644
--- a/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter
+++ b/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter
@@ -2,7 +2,7 @@
 
 # Suites implicated in window_server deaths.
 -ConstrainedWebDialogBrowserTest.*
--SSLClientCertificateSelectorCocoaTest.*
+-SSLClientCertificateSelectorMacTest.*
 
 # Investigation, round 2.
 -All/PasswordManagerBrowserTestWithViewsFeature.*
diff --git a/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter b/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter
index e2493c5..e9029a5 100644
--- a/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter
@@ -2,11 +2,15 @@
 # feature X that is already not working), please add it beside the existing
 # failures. Otherwise please reach out to network-service-dev@.
 
-# These tests currently fail when run with --enable-features=NetworkService
+# These tests currently fail when run with --enable-features=NetworkService,NetworkServiceInProcess
+
+# Note: webview aims to support running network service in-process, so the
+# tests are also running with NS IP. Multiprocess webview currently does not
+# support running NS OOP, https://crbug.com/882650
 
 # Uncategorized timeouts or test failures.
 
-# https://crbug.com/882650 (NS OOP)
+# https://crbug.com/882650
 -*__multiprocess_mode
 
 # https://crbug.com/893561
diff --git a/testing/buildbot/filters/webui_polymer2_browser_tests.filter b/testing/buildbot/filters/webui_polymer2_browser_tests.filter
index 844dfb4..f9e3013 100644
--- a/testing/buildbot/filters/webui_polymer2_browser_tests.filter
+++ b/testing/buildbot/filters/webui_polymer2_browser_tests.filter
@@ -252,6 +252,7 @@
 PrintPreviewDestinationListTest.*
 PrintPreviewDestinationSearchTest.*
 PrintPreviewHeaderTest.*
+PrintPreviewKeyEventTest.*
 PrintPreviewLinkContainerTest.*
 PrintPreviewModelTest.*
 PrintPreviewNewDestinationSearchTest.*
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 1ee1e1f..c40e6409 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -1027,10 +1027,6 @@
     "label": "//sandbox/win:sbox_validation_tests",
     "type": "console_test_launcher",
   },
-  "select_to_speak_extension_tests": {
-    "label": "//chrome/browser/resources/chromeos/select_to_speak:select_to_speak_extension_tests",
-    "type": "windowed_test_launcher",
-  },
   "service_manager_unittests": {
     "label": "//services/service_manager/tests:service_manager_unittests",
     "type": "console_test_launcher",
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 4ab2b24..57293ea3 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -139,7 +139,6 @@
           '--test-launcher-filter-file=../../testing/buildbot/filters/mac_window_server_killers.browser_tests.filter',
           '--gtest_shuffle',
         ],
-        'experiment_percentage': 50,
         'swarming': {
           'dimension_sets': [
             {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 913948f..74685ec0 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -1730,7 +1730,6 @@
       },
       'ozone_unittests': {},
       'ozone_x11_unittests': {},
-      'select_to_speak_extension_tests': {},
       'single_process_mash_ash_unittests': {
         'test': 'ash_unittests',
         'args': [
@@ -1955,9 +1954,9 @@
           'shards': 20,
         },
         'args': [
-          # TODO(crbug.com/881572): Fix crash when running network service OOP on
-          # Android.
-          '--enable-features=NetworkService',
+          # TODO(crbug.com/882650): WebView currently does not support running network
+          # service OOP.
+          '--enable-features=NetworkService,NetworkServiceInProcess',
           '--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter',
         ],
         'test': 'webview_instrumentation_test_apk',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index a86210b..94ae74eb 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -10,6 +10,12 @@
                     "enable_features": [
                         "AImageReaderMediaPlayer"
                     ]
+                },
+                {
+                    "name": "Use_AImageReaderMediaPlayer_V3",
+                    "enable_features": [
+                        "AImageReaderMediaPlayer"
+                    ]
                 }
             ]
         }
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 52d469f..f11c39f 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -58,15 +58,15 @@
 crbug.com/591099 fast/dom/nodesFromRect/nodesFromRect-basic.html [ Failure ]
 
 # New failures are appended below by the script.
-crbug.com/591099 accessibility/aom-relation-list-properties.html [ Failure ]
-crbug.com/591099 accessibility/aria-labelledby-stay-within.html [ Failure ]
-crbug.com/591099 accessibility/aria-owns.html [ Failure ]
-crbug.com/591099 accessibility/aria-tab-roles.html [ Failure ]
-crbug.com/591099 accessibility/focusable-div.html [ Failure ]
-crbug.com/591099 accessibility/listitem-presentation-inherited.html [ Failure ]
-crbug.com/591099 accessibility/name-calc-aria-owns.html [ Failure ]
-crbug.com/591099 accessibility/presentation-owned-elements.html [ Failure ]
-crbug.com/591099 accessibility/role-attribute.html [ Failure ]
+crbug.com/591099 accessibility/aom-relation-list-properties.html [ Failure Pass ]
+crbug.com/591099 accessibility/aria-labelledby-stay-within.html [ Failure Pass ]
+crbug.com/591099 accessibility/aria-owns.html [ Failure Pass ]
+crbug.com/591099 accessibility/aria-tab-roles.html [ Failure Pass ]
+crbug.com/591099 accessibility/focusable-div.html [ Failure Pass ]
+crbug.com/591099 accessibility/listitem-presentation-inherited.html [ Failure Pass ]
+crbug.com/591099 accessibility/name-calc-aria-owns.html [ Failure Pass ]
+crbug.com/591099 accessibility/presentation-owned-elements.html [ Failure Pass ]
+crbug.com/591099 accessibility/role-attribute.html [ Failure Pass ]
 crbug.com/728378 compositing/culling/tile-occlusion-boundaries.html [ Failure ]
 crbug.com/864398 compositing/iframes/floating-self-painting-frame.html [ Failure ]
 crbug.com/591099 css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change.html [ Failure ]
@@ -261,6 +261,14 @@
 crbug.com/591099 external/wpt/requestidlecallback/callback-iframe.html [ Pass Timeout ]
 crbug.com/591099 external/wpt/service-workers/service-worker/navigation-preload/broken-chunked-encoding.https.html [ Pass ]
 crbug.com/591099 external/wpt/service-workers/service-worker/update-after-navigation-redirect.https.html [ Pass ]
+crbug.com/591099 external/wpt/wasm/webapi/instantiateStreaming-bad-imports.any.html [ Pass ]
+crbug.com/591099 external/wpt/wasm/webapi/instantiateStreaming-bad-imports.any.serviceworker.html [ Pass ]
+crbug.com/591099 external/wpt/wasm/webapi/instantiateStreaming-bad-imports.any.sharedworker.html [ Pass ]
+crbug.com/591099 external/wpt/wasm/webapi/instantiateStreaming-bad-imports.any.worker.html [ Pass ]
+crbug.com/591099 external/wpt/wasm/webapi/instantiateStreaming.any.html [ Pass ]
+crbug.com/591099 external/wpt/wasm/webapi/instantiateStreaming.any.serviceworker.html [ Pass ]
+crbug.com/591099 external/wpt/wasm/webapi/instantiateStreaming.any.sharedworker.html [ Pass ]
+crbug.com/591099 external/wpt/wasm/webapi/instantiateStreaming.any.worker.html [ Pass ]
 crbug.com/591099 external/wpt/webrtc/RTCDTMFSender-ontonechange.https.html [ Pass ]
 crbug.com/591099 external/wpt/webrtc/RTCPeerConnection-setLocalDescription-answer.html [ Pass ]
 crbug.com/591099 external/wpt/workers/Worker_cross_origin_security_err.htm [ Pass ]
@@ -321,7 +329,8 @@
 crbug.com/591099 http/tests/devtools/network/network-datareceived.js [ Failure ]
 crbug.com/591099 http/tests/devtools/network/network-datasaver-warning.js [ Failure ]
 crbug.com/591099 http/tests/devtools/persistence/persistence-merge-editor-tabs.js [ Failure ]
-crbug.com/591099 http/tests/devtools/service-workers/service-worker-v8-cache.js [ Failure Pass ]
+crbug.com/591099 http/tests/devtools/service-workers/service-worker-v8-cache.js [ Pass ]
+crbug.com/591099 http/tests/images/feature-policy-image-compression-cached-image.html [ Failure ]
 crbug.com/591099 http/tests/images/restyle-decode-error.html [ Failure ]
 crbug.com/591099 http/tests/intersection-observer/v2/cross-origin-effects.html [ Failure ]
 crbug.com/591099 http/tests/intersection-observer/v2/cross-origin-occlusion.html [ Failure ]
@@ -332,8 +341,8 @@
 crbug.com/591099 http/tests/security/setDomainRelaxationForbiddenForURLScheme.html [ Crash ]
 crbug.com/591099 idle-callback/test-runner-run-idle-tasks.html [ Crash Pass ]
 crbug.com/591099 images/color-profile-image-filter-all.html [ Failure ]
-crbug.com/591099 inspector-protocol/accessibility/accessibility-ignoredNodes.js [ Failure ]
-crbug.com/591099 inspector-protocol/accessibility/accessibility-nameSources-labelledby.js [ Failure ]
+crbug.com/591099 inspector-protocol/accessibility/accessibility-ignoredNodes.js [ Failure Pass ]
+crbug.com/591099 inspector-protocol/accessibility/accessibility-nameSources-labelledby.js [ Failure Pass ]
 crbug.com/591099 inspector-protocol/dom-snapshot/dom-snapshot-getSnapshot-pseudo-element.js [ Failure ]
 crbug.com/714962 inspector-protocol/layout-fonts/languages-emoji-rare-glyphs.js [ Failure ]
 crbug.com/591099 inspector-protocol/runtime/runtime-console-log-handle-navigate.js [ Pass ]
@@ -372,10 +381,8 @@
 crbug.com/591099 printing/absolute-position-headers-and-footers.html [ Failure ]
 crbug.com/591099 printing/iframe-svg-in-object-print.html [ Failure ]
 crbug.com/591099 scrollbars/auto-scrollbar-fit-content.html [ Failure ]
-crbug.com/591099 storage/indexeddb/mozilla/test_objectStore_openKeyCursor.html [ Pass Timeout ]
+crbug.com/591099 storage/indexeddb/mozilla/test_objectStore_openKeyCursor.html [ Pass ]
 crbug.com/591099 storage/indexeddb/objectstore-cursor.html [ Pass ]
-crbug.com/591099 svg/hixie/error/013.xml [ Failure Pass ]
-crbug.com/591099 svg/transforms/svg-css-transforms.xhtml [ Failure Pass ]
 crbug.com/591099 svg/zoom/page/zoom-img-preserveAspectRatio-support-1.html [ Failure ]
 crbug.com/591099 svg/zoom/page/zoom-svg-float-border-padding.xml [ Failure ]
 crbug.com/591099 svg/zoom/page/zoom-svg-through-object-with-absolute-size-2.xhtml [ Failure ]
@@ -388,12 +395,13 @@
 crbug.com/591099 tables/mozilla/bugs/bug50695-2.html [ Failure ]
 crbug.com/591099 tables/mozilla/bugs/bug55527.html [ Failure ]
 crbug.com/591099 transforms/3d/general/perspective-units.html [ Failure ]
-crbug.com/591099 transforms/svg-vs-css.xhtml [ Failure Pass ]
 crbug.com/870008 virtual/android/rootscroller/position-fixed-in-unscrollable-document-iframe.html [ Failure Pass ]
-crbug.com/870008 virtual/android/rootscroller/position-fixed-in-unscrollable-document.html [ Failure ]
+crbug.com/870008 virtual/android/rootscroller/position-fixed-in-unscrollable-document.html [ Failure Pass ]
 crbug.com/591099 virtual/android/url-bar/bottom-and-top-fixed-sticks-to-top.html [ Failure Pass ]
 crbug.com/591099 virtual/exotic-color-space/ [ Skip ]
 crbug.com/591099 virtual/feature-policy-vibrate/ [ Skip ]
+crbug.com/591099 virtual/fractional_scrolling/fast/scrolling/fractional-scroll-offset-iframe-fixed-position.html [ Pass ]
+crbug.com/591099 virtual/fractional_scrolling_threaded/fast/scrolling/fractional-scroll-offset-iframe-fixed-position.html [ Pass ]
 crbug.com/591099 virtual/gpu-rasterization/images/color-profile-image-filter-all.html [ Failure ]
 crbug.com/591099 virtual/gpu/fast/canvas/canvas-blending-color-over-image.html [ Pass ]
 crbug.com/591099 virtual/gpu/fast/canvas/canvas-blending-gradient-over-pattern.html [ Pass Timeout ]
diff --git a/third_party/WebKit/LayoutTests/LeakExpectations b/third_party/WebKit/LayoutTests/LeakExpectations
index 16910c46..1c46632e 100644
--- a/third_party/WebKit/LayoutTests/LeakExpectations
+++ b/third_party/WebKit/LayoutTests/LeakExpectations
@@ -46,7 +46,6 @@
 # detached, which means leaks reported by the leak detector are by design.
 # Ignore them.
 crbug.com/755625 external/wpt/beacon/beacon-error.window.html [ Leak ]
-crbug.com/762353 external/wpt/beacon/headers/header-referrer-no-referrer.html [ Leak ]
 crbug.com/651742 external/wpt/content-security-policy/connect-src/connect-src-beacon-allowed.sub.html [ Leak ]
 
 # -----------------------------------------------------------------
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 8d6acb42..715f331 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -183,7 +183,6 @@
 # Tests not yet passing with Blink FractionalScrollOffsets enabled.
 crbug.com/414283 virtual/fractional_scrolling/fast/scrolling/fractional-scroll-offset-fixed-position-non-composited.html [ Failure ]
 crbug.com/414283 virtual/fractional_scrolling/fast/scrolling/fractional-scroll-offset-iframe-fixed-position.html [ Failure ]
-crbug.com/414283 virtual/fractional_scrolling_threaded/fast/scrolling/fractional-scroll-offset-document.html [ Failure Timeout ]
 crbug.com/414283 virtual/fractional_scrolling_threaded/fast/scrolling/fractional-scroll-offset-fixed-position-non-composited.html [ Failure ]
 crbug.com/414283 virtual/fractional_scrolling_threaded/fast/scrolling/fractional-scroll-offset-iframe-fixed-position.html [ Failure ]
 
@@ -5408,7 +5407,6 @@
 crbug.com/886566 http/tests/csspaint/invalidation-content-image.html [ Pass Timeout ]
 
 # Enable AnimationWorklet tests for mainthread
-crbug.com/887659 animations/animationworklet/animation-worklet-inside-iframe.html [ Failure ]
 crbug.com/887659 animations/animationworklet/worklet-animation-currentTime.html [ Failure ]
 crbug.com/887659 animations/animationworklet/worklet-animation-local-time-after-duration.html [ Failure ]
 
@@ -5485,3 +5483,6 @@
 
 # Sheriff 2018-10-25
 crbug.com/874576 http/tests/images/feature-policy-image-compression-cached-image.html [ Skip ]
+
+# Sheriff 2018-10-26
+crbug.com/899087 [ Linux ] virtual/android/fullscreen/full-screen-iframe-allowed-video.html [ Failure Pass ]
diff --git a/third_party/WebKit/LayoutTests/accessibility/block-in-inline.html b/third_party/WebKit/LayoutTests/accessibility/block-in-inline.html
index 804ae75..c0e4d4f 100644
--- a/third_party/WebKit/LayoutTests/accessibility/block-in-inline.html
+++ b/third_party/WebKit/LayoutTests/accessibility/block-in-inline.html
@@ -26,6 +26,5 @@
     assert_false(IsAncestorOf(axInline, axBlock));
     assert_true(IsAncestorOf(axLink, axInline));
     assert_true(IsAncestorOf(axLink, axBlock));
-
 }, "Ensure that continuations are included in the accessibility tree.");
 </script>
diff --git a/third_party/WebKit/LayoutTests/accessibility/continuation4.html b/third_party/WebKit/LayoutTests/accessibility/continuation4.html
new file mode 100644
index 0000000..d29905d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/accessibility/continuation4.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+
+<div id="before">Before</div>
+<div><span><p id="after">After</p></span></div>
+
+<script>
+  test(function(t) {
+    var axBefore = accessibilityController.accessibleElementById("before");
+    assert_equals(axBefore.childAtIndex(0).name, "Before");
+    var axAfter = accessibilityController.accessibleElementById("after");
+    assert_equals(axAfter.childAtIndex(0).name, "After");
+}, "Ensure that continuations are included in the accessibility tree.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/animations/animationworklet/animation-worklet-inside-iframe-expected.txt b/third_party/WebKit/LayoutTests/animations/animationworklet/animation-worklet-inside-iframe-expected.txt
deleted file mode 100644
index 6bee364..0000000
--- a/third_party/WebKit/LayoutTests/animations/animationworklet/animation-worklet-inside-iframe-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-CONSOLE MESSAGE: line 5: iframe/iframe_animator constructor called.
-CONSOLE MESSAGE: line 12: iframe/iframe_animator animate called.
-CONSOLE MESSAGE: line 5: mainframe/duplicate_animator constructor called.
-CONSOLE MESSAGE: line 12: mainframe/duplicate_animator animate called.
-
diff --git a/third_party/WebKit/LayoutTests/animations/animationworklet/animation-worklet-inside-iframe.html b/third_party/WebKit/LayoutTests/animations/animationworklet/animation-worklet-inside-iframe.html
index a625a9b..0816bd4 100644
--- a/third_party/WebKit/LayoutTests/animations/animationworklet/animation-worklet-inside-iframe.html
+++ b/third_party/WebKit/LayoutTests/animations/animationworklet/animation-worklet-inside-iframe.html
@@ -1,4 +1,8 @@
 <!DOCTYPE html>
+<title>Test that AW inside frames with different origin causes new global scopes</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+
 <style>
 .redbox {
   width: 100px;
@@ -6,55 +10,46 @@
   background-color: #ff0000;
 }
 </style>
+
+<div id="target" class="redbox"></div>
+
 <script id="main_worklet" type="text/worklet">
-function CreateLoggingAnimator(name, localTime) {
-  return class {
-    constructor() {
-        console.log(name + " constructor called.");
-    }
-    animate(currentTime, effect) {
-      if (this.animated)
-        return;
-      this.animated = true;
-
-      console.log(name + " animate called.");
-      effect.localTime = localTime;
-    }
+registerAnimator("duplicate_animator", class {
+  animate(currentTime, effect) {
+    effect.localTime = 500;
   }
-}
-
-registerAnimator("duplicate_animator", CreateLoggingAnimator("mainframe/duplicate_animator", 1000));
+});
 </script>
 
 <script src="resources/animation-worklet-tests.js"></script>
 <script>
-if (window.testRunner) {
-  testRunner.waitUntilDone();
-  testRunner.dumpAsText();
-}
+async_test(t => {
+  // Wait for iframe to load and start its animations.
+  window.onmessage = function(msg) {
+    window.requestAnimationFrame( _ => {
+      run_test(msg.data);
+    });
+  };
 
-// Wait for iframe to load and start its animations.
-window.onmessage = function(msg) {
-  waitTwoAnimationFrames(run_test);
-};
+  // Create and load the iframe to avoid racy cases.
+  var iframe = document.createElement('iframe');
+  iframe.src = 'resources/animator-iframe.html';
+  document.body.appendChild(iframe);
 
-function run_test() {
-  runInAnimationWorklet(
-    document.getElementById('main_worklet').textContent
-  ).then(_ => {
-    // Create an animation for duplicate animator.
-    const animation = new WorkletAnimation('duplicate_animator', new KeyframeEffect(document.getElementById("target"), [{ opacity: 0 }], { duration: 1000 }));
-    animation.play();
+  function run_test(data) {
+    runInAnimationWorklet(
+      document.getElementById('main_worklet').textContent
+    ).then(_ => {
+      // Create an animation for duplicate animator.
+      const target = document.getElementById('target');
+      const animation = new WorkletAnimation('duplicate_animator', new KeyframeEffect(target, [{ opacity: 0 }], { duration: 1000 }));
+      animation.play();
 
-    if (window.testRunner) {
-      waitTwoAnimationFrames( _ => {
-        testRunner.notifyDone();
-      });
-    }
-  });
-}
+      assert_equals(data, '0.4');
+      waitTwoAnimationFrames(t.step_func_done(() => {
+        assert_equals(getComputedStyle(target).opacity, '0.5');
+      }));
+    });
+  }
+}, 'Both main frame and iframe should update the opacity of their target');
 </script>
-
-<div id="target" class="redbox"></div>
-<iframe src="resources/animator-iframe.html"></iframe>
-
diff --git a/third_party/WebKit/LayoutTests/animations/animationworklet/resources/animator-iframe.html b/third_party/WebKit/LayoutTests/animations/animationworklet/resources/animator-iframe.html
index f15991c..42cdcd8 100644
--- a/third_party/WebKit/LayoutTests/animations/animationworklet/resources/animator-iframe.html
+++ b/third_party/WebKit/LayoutTests/animations/animationworklet/resources/animator-iframe.html
@@ -9,24 +9,16 @@
 <script src="animation-worklet-tests.js"></script>
 
 <script id="iframe_worklet" type="text/worklet">
-function CreateLoggingAnimator(name, localTime) {
-  return class {
-    constructor() {
-       console.log(name + " constructor called.");
-    }
-    animate(currentTime, effect) {
-      if (this.animated)
-        return;
-      this.animated = true;
-
-      console.log(name + " animate called.");
-      effect.localTime = localTime;
-    }
+registerAnimator("iframe_animator", class {
+  animate(currentTime, effect) {
+    effect.localTime = 600;
   }
-}
-
-registerAnimator("iframe_animator", CreateLoggingAnimator("iframe/iframe_animator", 500));
-registerAnimator("duplicate_animator", CreateLoggingAnimator("iframe/duplicate_animator"), 0);
+});
+registerAnimator("duplicate_animator", class {
+  animate(currentTime, effect) {
+    effect.localTime = 800;
+  }
+});
 </script>
 
 <div id="iframe_target" class="greenbox"></div>
@@ -35,11 +27,13 @@
 runInAnimationWorklet(
   document.getElementById('iframe_worklet').textContent
 ).then(_ => {
+  const target = document.getElementById('iframe_target');
   // Only create an animation for iframe_animator.
-  const effect = new KeyframeEffect(document.getElementById("iframe_target"), [{ opacity: 0 }], { duration: 1000 });
+  const effect = new KeyframeEffect(target, [{ opacity: 0 }], { duration: 1000 });
   const animation = new WorkletAnimation('iframe_animator', effect);
   animation.play();
-
-  window.parent.postMessage('done', '*');
+  waitTwoAnimationFrames( _ => {
+    window.parent.postMessage(getComputedStyle(target).opacity, '*');
+  });
  });
-</script>
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/animations/svg/stroke-width-zoom-factor.html b/third_party/WebKit/LayoutTests/animations/svg/stroke-width-zoom-factor.html
new file mode 100644
index 0000000..cd138e5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/svg/stroke-width-zoom-factor.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Make sure stroke-width is zoom agnostic when animating</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+
+<div id="target"></div>
+
+<script>
+test(t => {
+  // Setting the zoom factor should not change the value of strokeWidth.
+  internals.setZoomFactor(5);
+
+  const anim = target.animate([
+    { strokeWidth: '10px' },
+    { strokeWidth: '15px' },
+  ], {
+    duration: 1000,
+  });
+
+  anim.currentTime = 500;
+  assert_equals(getComputedStyle(target).strokeWidth, '12.5px');
+});
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/ImageData-fidelity.html b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/ImageData-fidelity.html
new file mode 100644
index 0000000..c2d158f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/ImageData-fidelity.html
@@ -0,0 +1,126 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(function(t) {
+  var image = new Image();
+  // This image is 256 by 1 and contains an opaque grayscale ramp from 0 to 255.
+  // The image has no embedded color profile.
+  image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAABCAYAAAAx" +
+    "WXB3AAAAG0lEQVQ4T2NkYGD4z8jIyDCKR8NgNA2MvDQAAPiPA/5tZ8G+AAAAAElFTkSuQmCC";
+
+  image.onload = function() {
+    var canvas = document.createElement('canvas');
+    canvas.width = 256;
+    canvas.height = 1;
+    var ctx = canvas.getContext('2d');
+    ctx.drawImage(image, 0, 0);
+    var img = ctx.getImageData(0, 0, 256, 1);
+    t.step(function() {
+      for (var i = 0; i < 256; i++) {
+        assert_equals(img.data[4 * i], i, "red component");
+        assert_equals(img.data[4 * i + 1], i, "green component");
+        assert_equals(img.data[4 * i + 2], i, "blue component");
+        assert_equals(img.data[4 * i + 3], 255, "alpha component");
+      }
+    });
+    t.done();
+  }
+}, "Verify that drawImage->getImageData round trip preserves color values " +
+    "when image metadata has no color space and canvas uses the default " +
+    "color space.");
+
+async_test(function(t) {
+  var image = new Image();
+  // This image is 256 by 1 and contains an opaque grayscale ramp from 0 to 255.
+  // The image has an embedded sRGB color profile.
+  image.src =
+      "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAABCAYAAAAxWXB3AAAA" +
+      "G0lEQVQ4T2NkYGD4z8jIyDCKR8NgNA2MvDQAAPiPA/5tZ8G+AAAAo3pUWHRSYXcgcHJvZm" +
+      "lsZSB0eXBlIEFQUDEAAHicZU5bCsMwDPv3KXoEv/I6TrampTC20ft/LE7WETJBkK1YQrCX" +
+      "ZzmP+/I+X9vxKLAYyCGoC9En77FCV10ROWNHrM8hUW7cQZ00V/026tDZMRKbUQYDt4lJJr" +
+      "2FxeCTJc5BV4svNE4Nxl1Tn8N1LCgMIoKJ2sHvo25sHfK/odYT02luCWMP+AA5M0KbNr61" +
+      "PwAAAABJRU5ErkJggg==";
+
+  image.onload = function() {
+    var canvas = document.createElement('canvas');
+    canvas.width = 256;
+    canvas.height = 1;
+    var ctx = canvas.getContext('2d', {colorSpace: 'srgb'});
+    ctx.drawImage(image, 0, 0);
+    var img = ctx.getImageData(0, 0, 256, 1);
+    t.step(function() {
+      for (var i = 0; i < 256; i++) {
+        assert_equals(img.data[4 * i], i, "red component");
+        assert_equals(img.data[4 * i + 1], i, "green component");
+        assert_equals(img.data[4 * i + 2], i, "blue component");
+        assert_equals(img.data[4 * i + 3], 255, "alpha component");
+      }
+    });
+    t.done();
+  }
+}, "Verify that drawImage->getImageData round trip preserves color values " +
+    "when image metadata has srgb color space and canvas uses the srgb " +
+    "color space.");
+
+async_test(function(t) {
+  var image = new Image();
+  // This image is 256 by 1 and contains an opaque grayscale ramp from 0 to 255.
+  // The image has no embedded color profile.
+  image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAABCAYAAAAx" +
+    "WXB3AAAAG0lEQVQ4T2NkYGD4z8jIyDCKR8NgNA2MvDQAAPiPA/5tZ8G+AAAAAElFTkSuQmCC";
+
+  image.onload = function() {
+    var canvas = document.createElement('canvas');
+    canvas.width = 256;
+    canvas.height = 1;
+    var ctx = canvas.getContext('2d', {colorSpace: 'srgb'});
+    ctx.drawImage(image, 0, 0);
+    var img = ctx.getImageData(0, 0, 256, 1);
+    t.step(function() {
+      for (var i = 0; i < 256; i++) {
+        assert_equals(img.data[4 * i], i, "red component");
+        assert_equals(img.data[4 * i + 1], i, "green component");
+        assert_equals(img.data[4 * i + 2], i, "blue component");
+        assert_equals(img.data[4 * i + 3], 255, "alpha component");
+      }
+    });
+    t.done();
+  }
+}, "Verify that drawImage->getImageData round trip preserves color values " +
+    "when image metadata has no color space and canvas uses the srgb " +
+    "color space.");
+
+
+async_test(function(t) {
+  var image = new Image();
+  // This image is 256 by 1 and contains an opaque grayscale ramp from 0 to 255.
+  // The image has an embedded sRGB color profile.
+  image.src =
+      "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAABCAYAAAAxWXB3AAAA" +
+      "G0lEQVQ4T2NkYGD4z8jIyDCKR8NgNA2MvDQAAPiPA/5tZ8G+AAAAo3pUWHRSYXcgcHJvZm" +
+      "lsZSB0eXBlIEFQUDEAAHicZU5bCsMwDPv3KXoEv/I6TrampTC20ft/LE7WETJBkK1YQrCX" +
+      "ZzmP+/I+X9vxKLAYyCGoC9En77FCV10ROWNHrM8hUW7cQZ00V/026tDZMRKbUQYDt4lJJr" +
+      "2FxeCTJc5BV4svNE4Nxl1Tn8N1LCgMIoKJ2sHvo25sHfK/odYT02luCWMP+AA5M0KbNr61" +
+      "PwAAAABJRU5ErkJggg==";
+
+  image.onload = function() {
+    var canvas = document.createElement('canvas');
+    canvas.width = 256;
+    canvas.height = 1;
+    var ctx = canvas.getContext('2d');
+    ctx.drawImage(image, 0, 0);
+    var img = ctx.getImageData(0, 0, 256, 1);
+    t.step(function() {
+      for (var i = 0; i < 256; i++) {
+        assert_equals(img.data[4 * i], i, "red component");
+        assert_equals(img.data[4 * i + 1], i, "green component");
+        assert_equals(img.data[4 * i + 2], i, "blue component");
+        assert_equals(img.data[4 * i + 3], 255, "alpha component");
+      }
+    });
+    t.done();
+  }
+}, "Verify that drawImage->getImageData round trip preserves color values " +
+    "when image metadata has srgb color space and canvas uses the default " +
+    "color space.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-colorManaged-convertToBlob-roundtrip.html b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-colorManaged-convertToBlob-roundtrip.html
new file mode 100644
index 0000000..72c64b1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-colorManaged-convertToBlob-roundtrip.html
@@ -0,0 +1,149 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+// No matter what is the color space and pixel format of the color managed
+// canvas, toBlob() and toDataURL() always create the blob in sRGB color
+// space, and respect the legacy behavior by not including any color space
+// information in the blob/data url.
+
+var opaqueReferencePxiels, transparentReferencePixels;
+
+function testPixels(actualPixels, refPixels, testScenario)
+{
+    // Narrowing down the source of expected error:
+    // - Alpha values should always match.
+    // - Color components should be acceptably close.
+    if (testScenario.canvasColorParam.pixelFormat == 'uint8') {
+        let tolerance_color = 4;
+        for (let i = 0; i < actualPixels.length; i++) {
+            // Alpha channel
+            if (i % 4 == 3)
+                assert_equals(actualPixels[i], refPixels[i]);
+            else
+                assert_approx_equals(actualPixels[i], refPixels[i], tolerance_color);
+        }
+    } else {
+        // For half float backed canvas, we expect the error < 0.01.
+        let tolerance_color = 0.01;
+        for (let i = 0; i < actualPixels.length; i++) {
+            // Alpha channel
+            if (i % 4 == 3)
+                assert_equals(actualPixels[i], refPixels[i]);
+            else
+                assert_approx_equals(actualPixels[i], refPixels[i], tolerance_color);
+        }
+    }
+}
+
+function createCanvas(testScenario)
+{
+    var canvas = document.createElement("canvas");
+    canvas.width = 2;
+    canvas.height = 2;
+    return canvas;
+}
+
+function generateFillStyle(red, green, blue, alpha) {
+    return "rgba(" + red + "," + green + "," + blue + "," + alpha + ")";
+}
+
+function drawPatternOnCanvsa(ctx, alpha, compositeOverBlack) {
+    if (compositeOverBlack) {
+        ctx.fillStyle = generateFillStyle(0, 0, 0, 1);
+        ctx.fillRect(0, 0, 2, 2);
+    }
+    ctx.fillStyle = generateFillStyle(155, 27, 27, alpha);
+    ctx.fillRect(0, 0, 1, 1);
+    ctx.fillStyle = generateFillStyle(27, 155, 27, alpha);
+    ctx.fillRect(1, 0, 1, 1);
+    ctx.fillStyle = generateFillStyle(27, 27, 155, alpha);
+    ctx.fillRect(0, 1, 1, 1);
+    ctx.fillStyle = generateFillStyle(27, 27, 27, alpha);
+    ctx.fillRect(1, 1, 1, 1);
+}
+
+function testScenarioToString(testScenario) {
+    var str = "mimeType: " + testScenario.encodeOptions.type +
+        ", blobPixelFormat: " + testScenario.encodeOptions.pixelFormat +
+        ", source color space: " + testScenario.canvasColorParam.colorSpace +
+        ", pixel format: " + testScenario.canvasColorParam.pixelFormat +
+        ", alpha: " + testScenario.alpha;
+    return str;
+}
+
+function runConvertToBlobTest(testScenario) {
+    var srcCanvas = createCanvas(testScenario);
+    var ctx = srcCanvas.getContext('2d', testScenario.canvasColorParam);
+    var compositeOverBlack = (testScenario.encodeOptions.type == "image/jpeg");
+    drawPatternOnCanvsa(ctx, testScenario.alpha, compositeOverBlack);
+    var refPixels = ctx.getImageData(0, 0, 2, 2).dataUnion;
+
+    var t = async_test("Test canvas convertToBlob(): " +
+        testScenarioToString(testScenario));
+
+    var image = new Image();
+    image.onload = t.step_func_done(function() {
+        var dstCanvas = createCanvas(testScenario);
+        var dstCtx = dstCanvas.getContext('2d', testScenario.canvasColorParam);
+        dstCtx.drawImage(image, 0, 0);
+        var actualPixels = dstCtx.getImageData(0, 0, 2, 2).dataUnion;
+        testPixels(actualPixels, refPixels, testScenario);
+    });
+
+    srcCanvas.convertToBlob(testScenario.encodeOptions).then(
+        t.step_func(function(blob) {
+            var urlCreator = window.URL || window.webkitURL;
+            image.src = urlCreator.createObjectURL(blob);
+        }),
+        t.step_func_done(function(e) {
+            assert_false("convertToBlob failed.");
+        })
+    );
+}
+
+function runAllTests() {
+
+    var mimeTypes = ['image/png', 'image/jpeg', 'image/webp'];
+    var blobPixelFormats = ['uint8', 'uint16'];
+
+    var encodeOptionsSet = [];
+    for (var i = 0; i < mimeTypes.length; i++)
+        for (var k = 0; k < blobPixelFormats.length; k++) {
+            var encodeOptions = {};
+            encodeOptions.quality = 1;
+            encodeOptions.type = mimeTypes[i];
+            encodeOptions.pixelFormat = blobPixelFormats[k];
+            encodeOptionsSet.push(encodeOptions);
+        }
+
+    var canvasColorParams = [
+        {colorSpace: 'srgb', pixelFormat: 'uint8'},
+        {colorSpace: 'srgb', pixelFormat: 'float16'},
+    ];
+    var alphaValues = [0.5, 1];
+
+    // The *correct* way to test convertToBlob() is to directly examine the
+    // image file for the expected color space and pixels. Since this is not
+    // easy to do in javascript, we use a round-trip test here and leave the
+    // more thorough testing to unit tests.
+
+    var testScenarioSet = [];
+    for (var i = 0; i < encodeOptionsSet.length; i++)
+        for (var j = 0; j < canvasColorParams.length; j++)
+            for (var k = 0; k < alphaValues.length; k++) {
+                var testScenario = {};
+                testScenario.encodeOptions = encodeOptionsSet[i];
+                testScenario.canvasColorParam = canvasColorParams[j];
+                testScenario.alpha = alphaValues[k];
+                testScenarioSet.push(testScenario);
+            }
+
+    for (var i = 0; i < testScenarioSet.length; i++)
+        runConvertToBlobTest(testScenarioSet[i]);
+}
+
+runAllTests();
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-colorManaged-toBlob-toDataURL.html b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-colorManaged-toBlob-toDataURL.html
new file mode 100644
index 0000000..ea21d33
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-colorManaged-toBlob-toDataURL.html
@@ -0,0 +1,145 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+// No matter what is the color space and pixel format of the color managed
+// canvas, toBlob() and toDataURL() always create the blob in sRGB color
+// space, and respect the legacy behavior by not including any color space
+// information in the blob/data url.
+
+var opaqueReferencePxiels, transparentReferencePixels;
+
+function testPixels(actualPixels, alpha)
+{
+    if (alpha == 1)
+        assert_array_approx_equals(actualPixels, opaqueReferencePxiels, 1);
+    else
+        assert_array_approx_equals(actualPixels, transparentReferencePixels, 2);
+}
+
+function generateFillStyle(red, green, blue, alpha) {
+    return "rgba(" + red + "," + green + "," + blue + "," + alpha + ")";
+}
+
+function contextDrawHelper(ctx, alpha) {
+    ctx.fillStyle = generateFillStyle(155, 27, 27, alpha);
+    ctx.fillRect(0, 0, 1, 1);
+    ctx.fillStyle = generateFillStyle(27, 155, 27, alpha);
+    ctx.fillRect(1, 0, 1, 1);
+    ctx.fillStyle = generateFillStyle(27, 27, 155, alpha);
+    ctx.fillRect(0, 1, 1, 1);
+    ctx.fillStyle = generateFillStyle(27, 27, 27, alpha);
+    ctx.fillRect(1, 1, 1, 1);
+}
+
+function prepareReferenceExpectedResults() {
+    var canvas1 = document.createElement("canvas");
+    canvas1.width = 2;
+    canvas1.height = 2;
+    var ctx1 = canvas1.getContext('2d');
+    contextDrawHelper(ctx1, 1);
+    opaqueReferencePxiels = ctx1.getImageData(0, 0, 2, 2).data;
+
+    var canvas2 = document.createElement("canvas");
+    canvas2.width = 2;
+    canvas2.height = 2;
+    var ctx2 = canvas2.getContext('2d');
+    contextDrawHelper(ctx2, 0.5);
+    transparentReferencePixels = ctx2.getImageData(0, 0, 2, 2).data;
+}
+
+function createCanvas(testScenario)
+{
+    var canvas = document.createElement("canvas");
+    canvas.width = 2;
+    canvas.height = 2;
+    var ctx = canvas.getContext('2d', {colorSpace: testScenario.colorSpace,
+                                       pixelFormat: testScenario.pixelFormat});
+    contextDrawHelper(ctx, testScenario.alpha);
+    return canvas;
+}
+
+function testScenarioToString(testScenario) {
+    var str = "ImageFormat: " + testScenario.imageFormat +
+        ", source color space: " + testScenario.colorSpace +
+        ", pixel format: " + testScenario.pixelFormat +
+        ", alpha: " + testScenario.alpha;
+    if (testScenario.hasOwnProperty('colorSpaceConversion'))
+        str = str + ", intermediate color space: " +
+                testScenario.colorSpaceConversion;
+    return str;
+}
+
+function runToBlobTest(testScenario) {
+    var srcCanvas = createCanvas(testScenario);
+    var t_blob = async_test("Test if toBlob() respects legacy behavior in \
+        color managed canvas: " + testScenarioToString(testScenario));
+
+    var image = new Image();
+    image.onload = t_blob.step_func_done(function() {
+        var dstCanvas = document.createElement("canvas");
+        dstCanvas.width = 2;
+        dstCanvas.height = 2;
+        var ctx = dstCanvas.getContext('2d');
+        ctx.drawImage(image, 0, 0);
+        var actualPixels = ctx.getImageData(0, 0, 2, 2).data;
+        testPixels(actualPixels, testScenario.alpha);
+    });
+
+    srcCanvas.toBlob(function(blob) {
+        var urlCreator = window.URL || window.webkitURL;
+        image.src = urlCreator.createObjectURL(blob);
+    }, 'image/png', 1);
+}
+
+function runToDataURLTest(testScenario) {
+    var srcCanvas = createCanvas(testScenario);
+    var t_dataURL = async_test("Test if toDataURL() respects legacy behavior \
+        in color managed canvas: " + testScenarioToString(testScenario));
+
+    var image = new Image();
+    image.onload = t_dataURL.step_func_done(function() {
+        var dstCanvas = document.createElement("canvas");
+        dstCanvas.width = 2;
+        dstCanvas.height = 2;
+        var ctx = dstCanvas.getContext('2d');
+        ctx.drawImage(image, 0, 0);
+        var actualPixels = ctx.getImageData(0, 0, 2, 2).data;
+        testPixels(actualPixels, testScenario.alpha);
+    });
+    image.src = srcCanvas.toDataURL();
+}
+
+function runAllTests() {
+    prepareReferenceExpectedResults();
+
+    var imageFormats = ['image/jpeg', 'image/png', 'image/webp'];
+    var colorSpaces = [
+        {colorSpace: 'srgb', pixelFormat: 'uint8'},
+        {colorSpace: 'srgb', pixelFormat: 'float16'},
+    ];
+    var alphaValues = [0.5, 1];
+
+    var testScenarioSet = [];
+    for (var i = 0; i < imageFormats.length; i++)
+        for (var j = 0; j < colorSpaces.length; j++)
+            for (var k = 0; k < alphaValues.length; k++) {
+                var testScenario = {};
+                testScenario.imageFormat = imageFormats[i];
+                testScenario.colorSpace = colorSpaces[j].colorSpace;
+                testScenario.pixelFormat = colorSpaces[j].pixelFormat;
+                testScenario.alpha = alphaValues[k];
+                testScenarioSet.push(testScenario);
+            }
+
+    for (var i = 0; i < testScenarioSet.length; i++)
+        runToBlobTest(testScenarioSet[i]);
+
+    for (var i = 0; i < testScenarioSet.length; i++)
+        runToDataURLTest(testScenarioSet[i]);
+}
+
+runAllTests();
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-colorspace-arguments.html b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-colorspace-arguments.html
new file mode 100644
index 0000000..f44b324
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-colorspace-arguments.html
@@ -0,0 +1,48 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+
+var testScenarios = [
+    {testDescription: "Test default context creation parameters: srgb/uint8",
+        canvasColorSettings: {},
+        expectedColorSettings: {colorSpace: "srgb", pixelFormat: "uint8"}},
+    {testDescription: "Test CanvasColorSpace value srgb",
+        canvasColorSettings: {colorSpace: "srgb"},
+        expectedColorSettings: {colorSpace: "srgb", pixelFormat: "uint8"}},
+
+    {testDescription: "Test CanvasPixelFormat value uint8",
+        canvasColorSettings: {pixelFormat: "uint8"},
+        expectedColorSettings: {colorSpace: "srgb", pixelFormat: "uint8"}},
+    {testDescription: "Test CanvasPixelFormat value float16",
+        canvasColorSettings: {pixelFormat: "float16"},
+        expectedColorSettings: {colorSpace: "srgb", pixelFormat: "float16"}},
+
+    {testDescription: "Test supported color settings srgb/uint8",
+        canvasColorSettings: {colorSpace: "srgb", pixelFormat: "uint8"},
+        expectedColorSettings: {colorSpace: "srgb", pixelFormat: "uint8"}},
+    {testDescription: "Test supported color settings srgb/float16",
+        canvasColorSettings: {colorSpace: "srgb", pixelFormat: "float16"},
+        expectedColorSettings: {colorSpace: "srgb", pixelFormat: "float16"}},
+];
+
+function runTestScenario(testScenario) {
+    var t = test(function() {
+        var canvas = document. createElement('canvas');
+        var ctx = canvas.getContext('2d', testScenario.canvasColorSettings);
+        var contextAttributes = ctx.getContextAttributes();
+        assert_equals(contextAttributes.colorSpace,
+            testScenario.expectedColorSettings.colorSpace);
+        assert_equals(contextAttributes.pixelFormat,
+            testScenario.expectedColorSettings.pixelFormat);
+    }, testScenario.testDescription);
+}
+
+function runAllTests() {
+    for (var i = 0; i < testScenarios.length; i++)
+        runTestScenario(testScenarios[i]);
+}
+
+runAllTests();
+</script>
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-createImageBitmap-e_srgb.html b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-createImageBitmap-e_srgb.html
new file mode 100644
index 0000000..1e2e05c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-createImageBitmap-e_srgb.html
@@ -0,0 +1,579 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+var transparentBlack = [0, 0, 0, 0];
+
+var e_sRGB_Red =   [1, 0, 0, 1]; // sRGB(255,0,0,255)
+var e_sRGB_Green = [0, 1, 0, 1]; // sRGB(0,255,0,255)
+var e_sRGB_Blue =  [0, 0, 1, 1]; // sRGB(0,0,255,255)
+var e_sRGB_Black = [0, 0, 0, 1]; // sRGB(0,0,0,255)
+
+// sRGB(155,27,27,255)
+var e_sRGB_OpaqueRed = [0.607422, 0.105835, 0.105835, 1];
+// sRGB(27,155,27,255)
+var e_sRGB_OpaqueGreen = [0.105835, 0.607422, 0.105835, 1];
+// sRGB(27,27,155,255)
+var e_sRGB_OpaqueBlue = [0.105835, 0.105835, 0.607422, 1];
+// sRGB(27,27,27,255)
+var e_sRGB_OpaqueBlack = [0.105835, 0.105835, 0.105835, 1];
+
+// sRGB(155,27,27,128)
+var e_sRGB_TransparentRed = [0.607422, 0.105835, 0.105835, 0.501953];
+// sRGB(27, 155, 27, 128)
+var e_sRGB_TransparentGreen = [0.105835, 0.607422, 0.105835, 0.501953];
+// sRGB(27, 27, 155, 128)
+var e_sRGB_TransparentBlue = [0.105835, 0.105835, 0.607422, 0.501953];
+// sRGB(27, 27, 27, 128)
+var e_sRGB_TransparentBlack = [0.105835, 0.105835, 0.105835, 0.501953];
+
+// sRGB(226,31,31,128)
+var e_sRGB_TransparentRedImage = [0.886230, 0.121521, 0.121521, 0.501953];
+// sRGB(226,31,31,128)
+var e_sRGB_TransparentGreenImage = [0.121521, 0.886230, 0.121521, 0.501953];
+// sRGB(226,31,31,128)
+var e_sRGB_TransparentBlueImage = [0.121521, 0.121521, 0.886230, 0.501953];
+// sRGB(226,31,31,128)
+var e_sRGB_TransparentBlackImage = [0.121521, 0.121521, 0.121521, 0.501953];
+
+function testPixels(ctx, tests, sourceType)
+{
+    var actual, expected, tolerance = 0.025;
+    if (sourceType === 'video')
+        tolerance = 0.03;
+    for (var i = 0; i < tests.length; i++) {
+        actual = ctx.getImageData(tests[i][0], tests[i][1], 1, 1).dataUnion;
+        expected = tests[i][2];
+        assert_true(actual.length === expected.length);
+        for (var j = 0; j < actual.length; j++)
+           assert_approx_equals(actual[j], expected[j], tolerance, tests[i][3]);
+    }
+}
+
+function checkNoCrop(imageBitmap, colorInfo, sourceType)
+{
+    var canvas = document.createElement('canvas');
+    canvas.width = 50;
+    canvas.height = 50;
+    var ctx = canvas.getContext('2d',
+        {colorSpace: 'srgb', pixelFormat:'float16'});
+    ctx.clearRect(0, 0, canvas.width, canvas.height);
+    ctx.drawImage(imageBitmap, 0, 0);
+    var tests;
+    if (colorInfo == 'fullColor')
+        tests = [[0, 0, e_sRGB_Red, "This pixel should be e-sRGB red."],
+                 [39, 0, e_sRGB_Green, "This pixel should be e-sRGB green."],
+                 [0, 39, e_sRGB_Blue, "This pixel should be e-sRGB blue."],
+                 [39, 39, e_sRGB_Black, "This pixel should be e-sRGB black."],
+                 [41, 41, transparentBlack, "This pixel should be transparent black."]];
+    else if (colorInfo == 'opaque')
+        tests = [[0, 0, e_sRGB_OpaqueRed,
+                     "This pixel should be e-sRGB like red."],
+                 [39, 0, e_sRGB_OpaqueGreen,
+                     "This pixel should be e-sRGB like green."],
+                 [0, 39, e_sRGB_OpaqueBlue,
+                     "This pixel should be e-sRGB like blue."],
+                 [39, 39, e_sRGB_OpaqueBlack,
+                     "This pixel should be e-sRGB like black."],
+                 [41, 41, transparentBlack, "This pixel should be transparent black."]];
+    else if (colorInfo == 'transparent')
+        tests = [[0, 0, e_sRGB_TransparentRed,
+                     "This pixel should be e-sRGB transparent red."],
+                 [39, 0, e_sRGB_TransparentGreen,
+                     "This pixel should be e-sRGB transparent green."],
+                 [0, 39, e_sRGB_TransparentBlue,
+                     "This pixel should be e-sRGB transparent blue."],
+                 [39, 39, e_sRGB_TransparentBlack,
+                     "This pixel should be e-sRGB transparent black."],
+                 [41, 41, transparentBlack,
+                     "This pixel should be transparent black."]];
+    else if (colorInfo === 'transparent-image')
+        tests = [[0, 0, e_sRGB_TransparentRedImage,
+                     "This pixel should be e-sRGB transparent red."],
+                 [39, 0, e_sRGB_TransparentGreenImage,
+                     "This pixel should be e-sRGB transparent green."],
+                 [0, 39, e_sRGB_TransparentBlueImage,
+                     "This pixel should be e-sRGB transparent blue."],
+                 [39, 39, e_sRGB_TransparentBlackImage,
+                     "This pixel should be e-sRGB transparent black."],
+                 [41, 41, transparentBlack,
+                     "This pixel should be transparent black."]];
+    testPixels(ctx, tests, sourceType);
+}
+
+function checkCrop(imageBitmap, colorInfo, sourceType)
+{
+    var canvas = document.createElement('canvas');
+    canvas.width = 50;
+    canvas.height = 50;
+    var ctx = canvas.getContext('2d',
+        {colorSpace: 'srgb', pixelFormat:'float16'});
+    ctx.clearRect(0, 0, canvas.width, canvas.height);
+    ctx.drawImage(imageBitmap, 0, 0);
+    var tests;
+    if (colorInfo === 'fullColor')
+        tests = [[0, 0, e_sRGB_Red, "This pixel should be e-sRGB red."],
+                 [19, 0, e_sRGB_Green, "This pixel should be e-sRGB green."],
+                 [0, 19, e_sRGB_Blue, "This pixel should be e-sRGB blue."],
+                 [19, 19, e_sRGB_Black, "This pixel should be e-sRGB black."],
+                 [21, 21, transparentBlack, "This pixel should be transparent black."]];
+    else if (colorInfo === 'opaque')
+        tests = [[0, 0, e_sRGB_OpaqueRed,
+                     "This pixel should be e-sRGB like red."],
+                 [19, 0, e_sRGB_OpaqueGreen,
+                     "This pixel should be e-sRGB like green."],
+                 [0, 19, e_sRGB_OpaqueBlue,
+                     "This pixel should be e-sRGB like blue."],
+                 [19, 19, e_sRGB_OpaqueBlack,
+                     "This pixel should be e-sRGB like black."],
+                 [21, 21, transparentBlack, "This pixel should be transparent black."]];
+    else if (colorInfo === 'transparent')
+        tests = [[0, 0, e_sRGB_TransparentRed,
+                     "This pixel should be e-sRGB transparent red."],
+                 [19, 0, e_sRGB_TransparentGreen,
+                     "This pixel should be e-sRGB transparent green."],
+                 [0, 19, e_sRGB_TransparentBlue,
+                     "This pixel should be e-sRGB transparent blue."],
+                 [19, 19, e_sRGB_TransparentBlack,
+                     "This pixel should be e-sRGB transparent black."],
+                 [21, 21, transparentBlack,
+                     "This pixel should be transparent black."]];
+    else if (colorInfo === 'transparent-image')
+        tests = [[0, 0, e_sRGB_TransparentRedImage,
+                     "This pixel should be e-sRGB transparent red."],
+                 [19, 0, e_sRGB_TransparentGreenImage,
+                     "This pixel should be e-sRGB transparent green."],
+                 [0, 19, e_sRGB_TransparentBlueImage,
+                     "This pixel should be e-sRGB transparent blue."],
+                 [19, 19, e_sRGB_TransparentBlackImage,
+                     "This pixel should be e-sRGB transparent black."],
+                 [21, 21, transparentBlack,
+                     "This pixel should be transparent black."]];
+    testPixels(ctx, tests, sourceType);
+}
+
+
+function compareBitmaps(bitmap1, bitmap2)
+{
+    var canvas1 = document.createElement('canvas');
+    var canvas2 = document.createElement('canvas');
+    canvas1.width = 50;
+    canvas1.height = 50;
+    canvas2.width = 50;
+    canvas2.height = 50;
+    var ctx1 = canvas1.getContext('2d',
+        {colorSpace: 'srgb', pixelFormat:'float16'});
+    var ctx2 = canvas2.getContext('2d',
+        {colorSpace: 'srgb', pixelFormat:'float16'});
+    ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
+    ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
+    ctx1.drawImage(bitmap1, 0, 0);
+    ctx2.drawImage(bitmap2, 0, 0);
+    var data1 = ctx1.getImageData(0, 0, 50, 50).dataUnion;
+    var data2 = ctx2.getImageData(0, 0, 50, 50).dataUnion;
+    var dataMatched = true;
+    for (var i = 0; i < data1.length; i++) {
+        if (data1[i] != data2[i]) {
+            dataMatched = false;
+            break;
+        }
+    }
+    assert_false(dataMatched);
+}
+
+function testImageBitmap(source, colorInfo, sourceType)
+{
+    return Promise.all([
+        createImageBitmap(source, {colorSpaceConversion: "srgb",
+            resizeWidth: 40, resizeHeight: 40, resizeQuality: "high"}),
+        createImageBitmap(source, {colorSpaceConversion: "srgb",
+            resizeWidth: 40, resizeHeight: 40, resizeQuality: "medium"}),
+        createImageBitmap(source, {colorSpaceConversion: "srgb",
+            resizeWidth: 40, resizeHeight: 40, resizeQuality: "low"}),
+        createImageBitmap(source, {colorSpaceConversion: "srgb",
+            resizeWidth: 40, resizeHeight: 40, resizeQuality: "pixelated"}),
+        createImageBitmap(source, 5, 5, 10, 10, {
+            colorSpaceConversion: "srgb",
+            resizeWidth: 20, resizeHeight: 20, resizeQuality: "high"}),
+        createImageBitmap(source, 5, 5, 10, 10, {
+            colorSpaceConversion: "srgb",
+            resizeWidth: 20, resizeHeight: 20, resizeQuality: "medium"}),
+        createImageBitmap(source, 5, 5, 10, 10, {
+            colorSpaceConversion: "srgb",
+            resizeWidth: 20, resizeHeight: 20, resizeQuality: "low"}),
+        createImageBitmap(source, 5, 5, 10, 10, {
+            colorSpaceConversion: "srgb",
+            resizeWidth: 20, resizeHeight: 20, resizeQuality: "pixelated"}),
+    ]).then(([noCropHigh, noCropMedium, noCropLow, noCropPixelated, cropHigh,
+              cropMedium, cropLow, cropPixelated]) => {
+        checkNoCrop(noCropHigh, colorInfo, sourceType);
+        checkNoCrop(noCropMedium, colorInfo, sourceType);
+        checkNoCrop(noCropLow, colorInfo, sourceType);
+        checkNoCrop(noCropPixelated, colorInfo, sourceType);
+        checkCrop(cropHigh, colorInfo, sourceType);
+        checkCrop(cropMedium, colorInfo, sourceType);
+        checkCrop(cropLow, colorInfo, sourceType);
+        checkCrop(cropPixelated, colorInfo, sourceType);
+        // Brute-force comparison among all bitmaps is too expensive.
+        // In case of SVG, resize quality does not affect the images, so all
+        // of them are the same and the tests fail. Since, we ignore this test
+        // set for SVG.
+        if (sourceType != 'svg') {
+            compareBitmaps(noCropHigh, noCropMedium);
+            compareBitmaps(noCropLow, noCropPixelated);
+            compareBitmaps(cropHigh, cropMedium);
+            compareBitmaps(cropLow, cropPixelated);
+        }
+    });
+}
+
+function testImageBitmapTransparent(source)
+{
+    return testImageBitmap(source, 'transparent', 'general');
+}
+
+function testImageBitmapFromTransparentImage(source)
+{
+    return testImageBitmap(source, 'transparent-image', 'general');
+}
+
+function testImageBitmapVideoSource(source)
+{
+    return testImageBitmap(source, 'fullColor', 'video');
+}
+
+function testImageBitmapOpaque(source)
+{
+    return testImageBitmap(source, 'opaque', 'general');
+}
+
+function testImageBitmapFromSVG(source)
+{
+    return testImageBitmap(source, 'opaque', 'svg');
+}
+
+function initializeTestCanvas(canvasColorSpace, canvasPixelFormat)
+{
+    var testCanvas = document.createElement("canvas");
+    testCanvas.width = 20;
+    testCanvas.height = 20;
+    var testCtx = testCanvas.getContext('2d',
+        {colorSpace: canvasColorSpace, pixelFormat:canvasPixelFormat});
+    testCtx.fillStyle = "rgba(155, 27, 27, 1)";
+    testCtx.fillRect(0, 0, 10, 10);
+    testCtx.fillStyle = "rgba(27, 155, 27, 1)";
+    testCtx.fillRect(10, 0, 10, 10);
+    testCtx.fillStyle = "rgba(27, 27, 155, 1)";
+    testCtx.fillRect(0, 10, 10, 10);
+    testCtx.fillStyle = "rgba(27, 27, 27, 1)";
+    testCtx.fillRect(10, 10, 10, 10);
+    return testCanvas;
+}
+
+function initializeTestCanvasTransparent(canvasColorSpace, canvasPixelFormat)
+{
+    var testCanvas = document.createElement("canvas");
+    testCanvas.width = 20;
+    testCanvas.height = 20;
+    var testCtx = testCanvas.getContext('2d',
+        {colorSpace: canvasColorSpace, pixelFormat:canvasPixelFormat});
+    testCtx.fillStyle = "rgba(155, 27, 27, 0.5)";
+    testCtx.fillRect(0, 0, 10, 10);
+    testCtx.fillStyle = "rgba(27, 155, 27, 0.5)";
+    testCtx.fillRect(10, 0, 10, 10);
+    testCtx.fillStyle = "rgba(27, 27, 155, 0.5)";
+    testCtx.fillRect(0, 10, 10, 10);
+    testCtx.fillStyle = "rgba(27, 27, 27, 0.5)";
+    testCtx.fillRect(10, 10, 10, 10);
+    return testCanvas;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// HTMLImageElement - Opaque sRGB
+// File formats: Bitmap, GIF, ICO, JPEG, PNG, WEBP
+promise_test(function() {
+    return Promise.all(['bmp', 'gif', 'ico', 'jpg', 'png', 'webp'].map(
+        ext => new Promise((resolve,reject) => {
+            var image = new Image();
+            image.onload = function() {
+                resolve(image);
+            }
+            image.src = 'resources/pattern-srgb.' + ext;
+        }).then(testImageBitmapOpaque)));
+}, 'createImageBitmap in e-sRGB from an opaque sRGB HTMLImageElement (BMP, \
+GIF, ICO, JPG, PNG, WEBP) with resize.');
+
+// HTMLImageElement - Transparent sRGB
+// File formats: Bitmap, GIF, ICO, PNG, WEBP
+promise_test(function() {
+    return Promise.all(['bmp', 'ico', 'png', 'webp'].map(
+        ext => new Promise((resolve,reject) => {
+            var image = new Image();
+            image.onload = function() {
+                resolve(image);
+            }
+            image.src = 'resources/pattern-srgb-transparent.' + ext;
+        }).then(testImageBitmapFromTransparentImage)));
+}, 'createImageBitmap in e-sRGB from a transparent sRGB HTMLImageElement \
+(BMP, ICO, PNG, WEBP) with resize.');
+
+////////////////////////////////////////////////////////////////////////////////
+
+// SVG Image - sRGB
+promise_test(function() {
+    return new Promise((resolve, reject) => {
+        var image = new Image();
+        image.onload = function() {
+            resolve(image);
+        }
+        image.src = 'resources/pattern-srgb.svg'
+    }).then(testImageBitmapFromSVG);
+}, 'createImageBitmap in e-sRGB from a sRGB SVG image with resize.');
+
+////////////////////////////////////////////////////////////////////////////////
+
+// HTMLVideoElement - sRGB
+promise_test(function() {
+    return new Promise((resolve, reject) => {
+        var video = document.createElement("video");
+        video.oncanplaythrough = function() {
+            resolve(video);
+        }
+        video.src = 'resources/pattern-srgb-fullcolor.ogv'
+    }).then(testImageBitmapVideoSource);
+}, 'createImageBitmap in e-sRGB from a sRGB HTMLVideoElement with resize.');
+
+////////////////////////////////////////////////////////////////////////////////
+
+// HTMLCanvasElement - Opaque sRGB
+promise_test(function() {
+    var testCanvas = initializeTestCanvas('srgb', 'uint8');
+    return testImageBitmapOpaque(testCanvas);
+}, 'createImageBitmap in e-sRGB from an opaque sRGB HTMLCanvasElement with resize.');
+
+// HTMLCanvasElement - Opaque e-sRGB
+promise_test(function() {
+    var testCanvas = initializeTestCanvas('srgb', 'float16');
+    return testImageBitmapOpaque(testCanvas);
+}, 'createImageBitmap in e-sRGB from an opaque e-sRGB HTMLCanvasElement with resize.');
+
+////////////////////////////////////////////////////////////////////////////////
+
+// HTMLCanvasElement - Transparent sRGB
+promise_test(function() {
+    var testCanvas = initializeTestCanvasTransparent('srgb', 'uint8');
+    return testImageBitmapTransparent(testCanvas);
+}, 'createImageBitmap in e-sRGB from a transparent sRGB HTMLCanvasElement with resize.');
+
+// HTMLCanvasElement - Transparent e-sRGB
+promise_test(function() {
+    var testCanvas = initializeTestCanvasTransparent('srgb', 'float16');
+    return testImageBitmapTransparent(testCanvas);
+}, 'createImageBitmap in e-sRGB from a transparent e-sRGB HTMLCanvasElement with resize.');
+
+//////////////////////////////////////////////////////////////////////////////
+
+// Blob from file - Opaque sRGB
+promise_test(function() {
+    return new Promise((resolve, reject) => {
+        var xhr = new XMLHttpRequest();
+        xhr.open("GET", 'resources/pattern-srgb.png');
+        xhr.responseType = 'blob';
+        xhr.send();
+        xhr.onload = function() {
+            resolve(xhr.response);
+        };
+    }).then(testImageBitmapOpaque);
+}, 'createImageBitmap in e-sRGB from an opaque sRGB Blob with resize.');
+
+// Blob form file - Transparent sRGB
+promise_test(function() {
+    return new Promise((resolve, reject) => {
+        var xhr = new XMLHttpRequest();
+        xhr.open("GET", 'resources/pattern-srgb-transparent.png');
+        xhr.responseType = 'blob';
+        xhr.send();
+        xhr.onload = function() {
+            resolve(xhr.response);
+        };
+    }).then(testImageBitmapFromTransparentImage);
+}, 'createImageBitmap in e-sRGB from a transparent sRGB Blob with resize.');
+
+// Color managed blob from canvas
+function testCreateImageBitmapFromColorManagedBlob(pixelFormat, isTransparent) {
+    let canvasPixelFormat = 'uint8';
+    if (pixelFormat == 'uint16')
+        canvasPixelFormat = 'float16';
+    var testCanvas;
+    if (isTransparent)
+      testCanvas = initializeTestCanvasTransparent('srgb', canvasPixelFormat);
+    else
+      testCanvas = initializeTestCanvas('srgb', canvasPixelFormat);
+    var encodeOptions = {};
+    encodeOptions.quality = 1;
+    encodeOptions.type = 'image/png';
+    encodeOptions.pixelFormat = pixelFormat;
+
+    var t = async_test('createImageBitmap in e-sRGB from color managed Blob' +
+        ' with resize. blobPixelFormat: ' + pixelFormat +
+        ', transparency: ' + isTransparent);
+    testCanvas.convertToBlob(encodeOptions).then(
+        t.step_func_done(function(blob) {
+            if (isTransparent)
+                testImageBitmapTransparent(blob);
+            else
+                testImageBitmapOpaque(blob);
+        }));
+}
+
+function runAllCreateImageBitmapFromColorManagedBlobTests() {
+    var blobPixelFormats = ['uint8', 'uint16'];
+    var transparencies = [false, true];
+    for (var j = 0; j < blobPixelFormats.length; j++)
+        for (var k = 0; k < transparencies.length; k++) {
+            testCreateImageBitmapFromColorManagedBlob(
+                blobPixelFormats[j], transparencies[k]);
+        }
+}
+
+runAllCreateImageBitmapFromColorManagedBlobTests();
+
+////////////////////////////////////////////////////////////////////////////////
+
+// ImageData - Opaque sRGB
+promise_test(function() {
+    var canvas = initializeTestCanvas('srgb', 'uint8');
+    var ctx = canvas.getContext('2d');
+    var data = ctx.getImageData(0, 0, 20, 20);
+    return testImageBitmapOpaque(data);
+}, 'createImageBitmap in e-sRGB from an opaque sRGB ImageData with resize.');
+
+// ImageData - Opaque e-sRGB
+promise_test(function() {
+    var canvas = initializeTestCanvas('srgb', 'float16');
+    var ctx = canvas.getContext('2d',
+        {colorSpace: 'srgb', pixelFormat:'float16'});
+    var data = ctx.getImageData(0, 0, 20, 20);
+    return testImageBitmapOpaque(data);
+}, 'createImageBitmap in e-sRGB from an opaque e-sRGB ImageData with resize.');
+
+////////////////////////////////////////////////////////////////////////////////
+
+// ImageData - Transparent sRGB
+promise_test(function() {
+    var canvas = initializeTestCanvasTransparent('srgb', 'uint8');
+    var ctx = canvas.getContext('2d');
+    var data = ctx.getImageData(0, 0, 20, 20);
+    return testImageBitmapTransparent(data);
+}, 'createImageBitmap in e-sRGB from a transparent sRGB ImageData with resize.');
+
+// ImageData - Transparent e-sRGB
+promise_test(function() {
+    var canvas = initializeTestCanvasTransparent('srgb', 'float16');
+    var ctx = canvas.getContext('2d',
+        {colorSpace: 'srgb', pixelFormat:'float16'});
+    var data = ctx.getImageData(0, 0, 20, 20);
+    return testImageBitmapTransparent(data);
+}, 'createImageBitmap in e-sRGB from a transparent e-sRGB ImageData with resize.');
+
+////////////////////////////////////////////////////////////////////////////////
+
+// ImageBitmap - Opaque sRGB
+promise_test(function() {
+    var testCanvas = initializeTestCanvas('srgb', 'uint8');
+    return createImageBitmap(testCanvas).then(testImageBitmapOpaque);
+}, 'createImageBitmap in e-sRGB from an opaque sRGB ImageBitmap with resize.');
+
+// ImageBitmap - Opaque e-sRGB
+promise_test(function() {
+    var testCanvas = initializeTestCanvas('srgb', 'float16');
+    return createImageBitmap(testCanvas, {colorSpaceConversion: "srgb"}
+        ).then(testImageBitmapOpaque);
+}, 'createImageBitmap in e-sRGB from an opaque e-sRGB ImageBitmap with resize.');
+
+////////////////////////////////////////////////////////////////////////////////
+
+// ImageBitmap - Transparent sRGB
+promise_test(function() {
+    var testCanvas = initializeTestCanvasTransparent('srgb', 'uint8');
+    return createImageBitmap(testCanvas).then(testImageBitmapTransparent);
+}, 'createImageBitmap in e-sRGB from a transparent sRGB ImageBitmap with resize.');
+
+// ImageBitmap - Transparent e-sRGB
+promise_test(function() {
+    var testCanvas = initializeTestCanvasTransparent('srgb', 'float16');
+    return createImageBitmap(testCanvas, {colorSpaceConversion: "srgb"}
+        ).then(testImageBitmapTransparent);
+}, 'createImageBitmap in e-sRGB from a transparent e-sRGB ImageBitmap with resize.');
+
+////////////////////////////////////////////////////////////////////////////////
+
+function initializeOffscreenCanvas(canvasColorSpace, canvasPixelFormat)
+{
+    var canvas = document.createElement("canvas");
+    canvas.width = 20;
+    canvas.height = 20;
+    var offscreen = canvas.transferControlToOffscreen();
+    var ctx = offscreen.getContext('2d',
+        {colorSpace: canvasColorSpace, pixelFormat:canvasPixelFormat});
+    ctx.fillStyle = "rgba(155, 27, 27, 1)";
+    ctx.fillRect(0, 0, 10, 10);
+    ctx.fillStyle = "rgba(27, 155, 27, 1)";
+    ctx.fillRect(10, 0, 10, 10);
+    ctx.fillStyle = "rgba(27, 27, 155, 1)";
+    ctx.fillRect(0, 10, 10, 10);
+    ctx.fillStyle = "rgba(27, 27, 27, 1)";
+    ctx.fillRect(10, 10, 10, 10);
+    return offscreen;
+}
+
+//OffscreenCanvas - Opaque sRGB
+promise_test(function() {
+    var offscreen = initializeOffscreenCanvas('srgb', 'uint8');
+    return testImageBitmapOpaque(offscreen);
+}, 'createImageBitmap in e-sRGB from an opaque sRGB OffscreenCanvas with resize.');
+
+//OffscreenCanvas - Opaque e-sRGB
+promise_test(function() {
+    var offscreen = initializeOffscreenCanvas('srgb', 'float16');
+    return testImageBitmapOpaque(offscreen);
+}, 'createImageBitmap in e-sRGB from an opaque e-sRGB OffscreenCanvas with resize.');
+
+////////////////////////////////////////////////////////////////////////////////
+
+function initializeOffscreenCanvasTransparent(canvasColorSpace, canvasPixelFormat)
+{
+    var canvas = document.createElement("canvas");
+    canvas.width = 20;
+    canvas.height = 20;
+    var offscreen = canvas.transferControlToOffscreen();
+    var ctx = offscreen.getContext('2d',
+        {colorSpace: canvasColorSpace, pixelFormat:canvasPixelFormat});
+    ctx.fillStyle = "rgba(155, 27, 27, 0.5)";
+    ctx.fillRect(0, 0, 10, 10);
+    ctx.fillStyle = "rgba(27, 155, 27, 0.5)";
+    ctx.fillRect(10, 0, 10, 10);
+    ctx.fillStyle = "rgba(27, 27, 155, 0.5)";
+    ctx.fillRect(0, 10, 10, 10);
+    ctx.fillStyle = "rgba(27, 27, 27, 0.5)";
+    ctx.fillRect(10, 10, 10, 10);
+    return offscreen;
+}
+
+//OffscreenCanvas - Transparent sRGB
+promise_test(function() {
+    var offscreen = initializeOffscreenCanvasTransparent('srgb', 'uint8');
+    return testImageBitmapTransparent(offscreen);
+}, 'createImageBitmap in e-sRGB from a transparent sRGB OffscreenCanvas with resize.');
+
+//OffscreenCanvas - Transparent e-sRGB
+promise_test(function() {
+    var offscreen = initializeOffscreenCanvasTransparent('srgb', 'float16');
+    return testImageBitmapTransparent(offscreen);
+}, 'createImageBitmap in e-sRGB from a transparent e-sRGB OffscreenCanvas with resize.');
+
+////////////////////////////////////////////////////////////////////////////////
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-createPutGetImageData-colorManaged.html b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-createPutGetImageData-colorManaged.html
new file mode 100644
index 0000000..a6b202e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-createPutGetImageData-colorManaged.html
@@ -0,0 +1,142 @@
+<!DOCTYPE html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+var xWidth = xHeight = 10;
+
+function checkImageData(canvasColorSettings, imageData) {
+  var imageDataColorSettings = imageData.getColorSettings();
+  assert_equals(canvasColorSettings.colorSpace, imageDataColorSettings.colorSpace);
+  if (canvasColorSettings.pixelFormat == "uint8") {
+    assert_equals("uint8", imageDataColorSettings.storageFormat);
+    assert_equals(imageData.data.length, 4 * xWidth * xHeight);
+  } else {
+    assert_equals("float32", imageDataColorSettings.storageFormat);
+    assert_equals(imageData.dataUnion.length, 4 * xWidth * xHeight);
+  }
+}
+
+// Test createImageData when the canvas is color managed
+var helperImageData = new ImageData(xWidth, xHeight);
+function runTestCreateImageData(canvasColorSettings) {
+  var aCanvas = document.createElement("canvas");
+  aCanvas.width = xWidth;
+  aCanvas.height = xHeight;
+  var ctx = aCanvas.getContext('2d', canvasColorSettings);
+  var imageData = ctx.createImageData(xWidth, xHeight);
+  checkImageData(canvasColorSettings, imageData);
+
+  imageData = ctx.createImageData(helperImageData);
+  checkImageData(canvasColorSettings, imageData);
+}
+
+var testScenariosCreateImageData = [
+  ["Test color managed cretateImageData: {srgb, uint8} -> {srgb, uint8}",
+      {colorSpace: "srgb", pixelFormat: "uint8"}],
+  ["Test color managed cretateImageData: {srgb, float16} -> {srgb, float32}",
+      {colorSpace: "srgb", pixelFormat: "float16"}],
+];
+
+function runCreateImageDataTests() {
+  for (var i = 0; i < testScenariosCreateImageData.length; i++){
+    var t = test(function() {
+      runTestCreateImageData(testScenariosCreateImageData[i][1]);
+    }, testScenariosCreateImageData[i][0]);
+  }
+}
+runCreateImageDataTests();
+
+// Test getImageData when the canvas is color managed
+function runTestGetImageData(canvasColorSettings) {
+  var aCanvas = document.createElement("canvas");
+  aCanvas.width = xWidth;
+  aCanvas.height = xHeight;
+  var ctx = aCanvas.getContext('2d', canvasColorSettings);
+  var imageData = ctx.getImageData(0, 0, xWidth, xHeight);
+  checkImageData(canvasColorSettings, imageData);
+}
+
+var testScenariosGetImageData = [
+  ["Test color managed getImageData: {srgb, uint8} -> {srgb, uint8}",
+      {colorSpace: "srgb", pixelFormat: "uint8"}],
+  ["Test color managed getImageData: {srgb, float16} -> {srgb, float32}",
+      {colorSpace: "srgb", pixelFormat: "float16"}],
+];
+
+function runGetImageDataTests() {
+  for (var i = 0; i < testScenariosGetImageData.length; i++){
+    var t = test(function() {
+      runTestGetImageData(testScenariosGetImageData[i][1]);
+    }, testScenariosGetImageData[i][0]);
+  }
+}
+runGetImageDataTests();
+
+// Test putImageData when the canvas is color managed.
+
+var dataU8 = new Uint8ClampedArray(4 * xWidth * xHeight);
+var dataU16 = new Uint16Array(4 * xWidth * xHeight);
+var dataF32 = new Float32Array(4 * xWidth * xHeight);
+function prepareDataArrays() {
+  for (i = 0; i < 4 * xWidth * xHeight; i++) {
+    dataU8[i] = (i % 4 == 3) ? 255 : i % 256;
+    dataU16[i] = dataU8[i] * 257;
+    dataF32[i] = dataU8[i] / 255.0;
+  }
+}
+
+var testScenariosPutImageData = [];
+function prepareTestScenariosPutImageData() {
+  var colorSpaces = ["srgb"];
+  var imageDataStorageFormats = ["uint8", "uint16", "float32"];
+  var canvasPixelFormats = ["uint8", "float16"];
+
+  for (i = 0; i < colorSpaces.length; i++)
+    for (j = 0; j < imageDataStorageFormats.length; j++)
+      for (k = 0; k < colorSpaces.length; k++)
+        for (l = 0; l < canvasPixelFormats.length; l++) {
+          testTitle = "Test color managed putImageData: ".concat(
+            "{", colorSpaces[i], ", ", imageDataStorageFormats[j], "} -> {", colorSpaces[k],
+            ", ", canvasPixelFormats[l], "}");
+          imageDataColorSettings =
+            {colorSpace: colorSpaces[i], storageFormat: imageDataStorageFormats[j]};
+          canvasColorSettings =
+            {colorSpace: colorSpaces[k], pixelFormat: canvasPixelFormats[l]};
+          testScenariosPutImageData.push([testTitle, imageDataColorSettings, canvasColorSettings]);
+        }
+}
+
+function createAndPutImageData(data, imageDataColorSettings, canvasColorSettings) {
+  // create color managed canvas
+  var aCanvas = document.createElement("canvas");
+  aCanvas.width = xWidth;
+  aCanvas.height = xHeight;
+  var ctx = aCanvas.getContext('2d', canvasColorSettings);
+  // create color managed ImageData
+  var imageData = ctx.createImageData(data, xWidth, xHeight, imageDataColorSettings);
+  // put image data into canvas. test succeeds if this does not crash.
+  ctx.putImageData(imageData, 0, 0);
+}
+
+function runTestPutImageData(imageDataColorSettings, canvasColorSettings) {
+  createAndPutImageData(dataU8, imageDataColorSettings, canvasColorSettings);
+  createAndPutImageData(dataU16, imageDataColorSettings, canvasColorSettings);
+  createAndPutImageData(dataF32, imageDataColorSettings, canvasColorSettings);
+}
+
+prepareDataArrays();
+prepareTestScenariosPutImageData();
+
+function runPutImageDataTests() {
+  for (var i = 0; i < testScenariosPutImageData.length; i++){
+    var t = test(function() {
+      runTestPutImageData(testScenariosPutImageData[i][1]);
+    }, testScenariosPutImageData[i][0]);
+  }
+}
+runPutImageDataTests();
+
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-draw-high-bit-depth-images.html b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-draw-high-bit-depth-images.html
new file mode 100644
index 0000000..7d44381
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-draw-high-bit-depth-images.html
@@ -0,0 +1,109 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+var testImagesPath = "resources/png-16bit/";
+
+// Source of pixel comparison error in these tests:
+// - color conversion from different color profiles (sRGB, Adobe RGB, Display P3,
+//   ProPhoto and Rec 2020) to target canvas color space (sRGB, e-sRGB).
+// - comparing the result of drawing 8 bit and 16 bit PNGs with each other.
+var defaultColorConversionTolerance = 8;
+var wideGamutColorConversionTolerance = 0.05;
+
+function runTest(testScenario) {
+    var _8bitImageSrc = testImagesPath + testScenario._8bitImagePath;
+    var _16bitImageSrc = testImagesPath + testScenario._16bitImagePath;
+    var tolerance = defaultColorConversionTolerance;
+    if (testScenario.canvasColorParams.pixelFormat == 'float16')
+        tolerance = wideGamutColorConversionTolerance;
+
+    var _8bitImage = new Image();
+    var _16bitImage = new Image();
+    var t_image = async_test(testScenarioToString(testScenario));
+    _8bitImage.onload = t_image.step_func(function() {
+        _16bitImage.onload = function() {
+            var refCanvas = document.createElement("canvas");
+            refCanvas.width = refCanvas.height = 2;
+            var refCtx = refCanvas.getContext(
+                '2d', testScenario.canvasColorParams);
+            refCtx.drawImage(_8bitImage, 0, 0);
+            var refPixels = refCtx.getImageData(0, 0, 2, 2).dataUnion;
+
+            var testCanvas = document.createElement("canvas");
+            testCanvas.width = testCanvas.height = 2;
+            var testCtx = testCanvas.getContext(
+                '2d', testScenario.canvasColorParams);
+            testCtx.drawImage(_16bitImage, 0, 0);
+            var testPixels = testCtx.getImageData(0, 0, 2, 2).dataUnion;
+
+            assert_array_approx_equals(refPixels, testPixels, tolerance);
+
+            t_image.done();
+            };
+        _16bitImage.src = _16bitImageSrc;
+    });
+    _8bitImage.src = _8bitImageSrc;
+}
+
+function runAllTests() {
+    var pngColorSpaces = [
+        "_sRGB",
+        "_AdobeRGB",
+        "_DisplayP3",
+        "_ProPhoto",
+        "_Rec2020",
+    ];
+    var pngTransparencyStatus = [
+        "_opaque",
+        "_transparent",
+    ];
+    var pngInterlaceStatus = [
+        "",  // non-interlaced
+        "_interlaced",
+    ];
+
+
+    var _8bitPngPrefix = "2x2_8bit";
+    var _16bitPngPrefix = "2x2_16bit";
+
+    var canvasColorParams = [
+        {colorSpace: 'srgb', pixelFormat: 'uint8'},
+        {colorSpace: 'srgb', pixelFormat: 'float16'},
+    ];
+
+    var testScenarioSet = [];
+    var id = 1;
+    for (var i = 0; i < canvasColorParams.length; i++) {
+        for (var j = 0; j < pngColorSpaces.length; j++) {
+            for (var k = 0; k < pngTransparencyStatus.length; k++) {
+                for (var m = 0; m < pngInterlaceStatus.length; m++) {
+                    var testScenario = {};
+                    testScenario.canvasColorParams = canvasColorParams[i];
+                    testScenario._8bitImagePath = _8bitPngPrefix +
+                        pngColorSpaces[j] + pngTransparencyStatus[k] + ".png";
+                    testScenario._16bitImagePath = _16bitPngPrefix +
+                        pngInterlaceStatus[m] + pngColorSpaces[j] +
+                        pngTransparencyStatus[k] + ".png";
+                    testScenarioSet.push(testScenario);
+                }
+            }
+        }
+    }
+
+    for (var i = 0; i < testScenarioSet.length; i++)
+        runTest(testScenarioSet[i]);
+}
+
+function testScenarioToString(testScenario) {
+    var str = "Canvas color params: " +
+        testScenario.canvasColorParams.colorSpace + ", " +
+        testScenario.canvasColorParams.pixelFormat + ". Testing " +
+        testScenario._8bitImagePath + " vs " + testScenario._16bitImagePath;
+    return str;
+}
+
+runAllTests();
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-drawImage-e_srgb.html b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-drawImage-e_srgb.html
new file mode 100644
index 0000000..7620d93
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-drawImage-e_srgb.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+// sRGB(226,31,31,128)
+var e_sRGB_TransparentRed = [0.886230, 0.121521, 0.121521, 0.501953];
+// sRGB(226,31,31,128)
+var e_sRGB_TransparentGreen = [0.121521, 0.886230, 0.121521, 0.501953];
+// sRGB(226,31,31,128)
+var e_sRGB_TransparentBlue = [0.121521, 0.121521, 0.886230, 0.501953];
+// sRGB(226,31,31,128)
+var e_sRGB_TransparentBlack = [0.121521, 0.121521, 0.121521, 0.501953];
+
+function testPixels(ctx, tests)
+{
+    var actual, expected, tolerance = 0.01;
+    for (var i = 0; i < tests.length; i++) {
+      actual = ctx.getImageData(tests[i].x, tests[i].y, 1, 1).dataUnion;
+      expected = tests[i].color;
+      assert_true(actual.length === expected.length);
+      for (var j = 0; j < actual.length; j++)
+        assert_approx_equals(actual[j], expected[j], tolerance);
+    }
+}
+
+function drawSRGBImageOnExtendedSRGBCanvas(source)
+{
+  var canvas = document.createElement('canvas');
+  canvas.width = 20;
+  canvas.height = 20;
+  var ctx = canvas.getContext('2d',
+      {colorSpace: 'srgb', pixelFormat:'float16'});
+  ctx.drawImage(source, 0, 0);
+  var tests = [{x: 5, y: 5, color: e_sRGB_TransparentRed},
+               {x: 15, y: 5, color: e_sRGB_TransparentGreen},
+               {x: 5, y: 15, color: e_sRGB_TransparentBlue},
+               {x: 15, y: 15, color: e_sRGB_TransparentBlack}];
+  testPixels(ctx, tests);
+}
+
+promise_test(function() {
+  return new Promise((resolve, reject) => {
+    var image = new Image();
+    image.onload = function() {
+      resolve(image);
+    }
+    image.src = 'resources/pattern-semitransparent-srgb.png'
+  }).then(drawSRGBImageOnExtendedSRGBCanvas);
+}, 'Draw SRGB image on an e-sRGB canvas and read back the e-sRGB pixels.');
+
+</script>
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-drawImage-offscreenCanvas.html b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-drawImage-offscreenCanvas.html
new file mode 100644
index 0000000..144dca0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-drawImage-offscreenCanvas.html
@@ -0,0 +1,176 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+function initializeBlankCanvas(canvasColorSpace, canvasPixelFormat)
+{
+    var testCanvas = document.createElement("canvas");
+    testCanvas.width = 4;
+    testCanvas.height = 4;
+    var testCtx = testCanvas.getContext('2d',
+        {colorSpace: canvasColorSpace, pixelFormat:canvasPixelFormat});
+    return testCtx;
+}
+
+function initializeCanvas(canvasColorSpace, canvasPixelFormat)
+{
+    var testCanvas = document.createElement("canvas");
+    testCanvas.width = 4;
+    testCanvas.height = 4;
+    var testCtx = testCanvas.getContext('2d',
+        {colorSpace: canvasColorSpace, pixelFormat:canvasPixelFormat});
+    testCtx.fillStyle = "rgba(155, 27, 27, 1)";
+    testCtx.fillRect(0, 0, 2, 2);
+    testCtx.fillStyle = "rgba(27, 155, 27, 1)";
+    testCtx.fillRect(2, 0, 2, 2);
+    testCtx.fillStyle = "rgba(27, 27, 155, 1)";
+    testCtx.fillRect(0, 2, 2, 2);
+    testCtx.fillStyle = "rgba(27, 27, 27, 1)";
+    testCtx.fillRect(2, 2, 2, 2);
+    return testCtx;
+}
+
+function initializeCanvasTransparent(canvasColorSpace, canvasPixelFormat)
+{
+    var testCanvas = document.createElement("canvas");
+    testCanvas.width = 4;
+    testCanvas.height = 4;
+    var testCtx = testCanvas.getContext('2d',
+        {colorSpace: canvasColorSpace, pixelFormat:canvasPixelFormat});
+    testCtx.fillStyle = "rgba(155, 27, 27, 0.5)";
+    testCtx.fillRect(0, 0, 2, 2);
+    testCtx.fillStyle = "rgba(27, 155, 27, 0.5)";
+    testCtx.fillRect(2, 0, 2, 2);
+    testCtx.fillStyle = "rgba(27, 27, 155, 0.5)";
+    testCtx.fillRect(0, 2, 2, 2);
+    testCtx.fillStyle = "rgba(27, 27, 27, 0.5)";
+    testCtx.fillRect(2, 2, 2, 2);
+    return testCtx;
+}
+
+
+function initializeOffscreenCanvas(canvasColorSpace, canvasPixelFormat)
+{
+    var canvas = document.createElement("canvas");
+    canvas.width = 4;
+    canvas.height = 4;
+    var offscreen = canvas.transferControlToOffscreen();
+    var ctx = offscreen.getContext('2d',
+        {colorSpace: canvasColorSpace, pixelFormat:canvasPixelFormat});
+    ctx.fillStyle = "rgba(155, 27, 27, 1)";
+    ctx.fillRect(0, 0, 2, 2);
+    ctx.fillStyle = "rgba(27, 155, 27, 1)";
+    ctx.fillRect(2, 0, 2, 2);
+    ctx.fillStyle = "rgba(27, 27, 155, 1)";
+    ctx.fillRect(0, 2, 2, 2);
+    ctx.fillStyle = "rgba(27, 27, 27, 1)";
+    ctx.fillRect(2, 2, 2, 2);
+    return offscreen;
+}
+
+function initializeOffscreenCanvasTransparent(canvasColorSpace, canvasPixelFormat)
+{
+    var canvas = document.createElement("canvas");
+    canvas.width = 4;
+    canvas.height = 4;
+    var offscreen = canvas.transferControlToOffscreen();
+    var ctx = offscreen.getContext('2d',
+        {colorSpace: canvasColorSpace, pixelFormat:canvasPixelFormat});
+    ctx.fillStyle = "rgba(155, 27, 27, 0.5)";
+    ctx.fillRect(0, 0, 2, 2);
+    ctx.fillStyle = "rgba(27, 155, 27, 0.5)";
+    ctx.fillRect(2, 0, 2, 2);
+    ctx.fillStyle = "rgba(27, 27, 155, 0.5)";
+    ctx.fillRect(0, 2, 2, 2);
+    ctx.fillStyle = "rgba(27, 27, 27, 0.5)";
+    ctx.fillRect(2, 2, 2, 2);
+    return offscreen;
+}
+
+function testPixels(testCtx, refCtx, pixelFormat, isTrnasparent)
+{
+    var actual = testCtx.getImageData(0, 0, 4, 4).dataUnion;
+    var expected = refCtx.getImageData(0, 0, 4, 4).dataUnion;
+
+    var tolerance = 4;
+    if (pixelFormat === 'float16')
+        tolerance = 0.02;
+    if (isTrnasparent)
+        tolerance = tolerance * 2;
+    assert_array_approx_equals(actual, expected, tolerance);
+}
+
+
+function runDrawOffscreenCanvasTestOpaque(testScenario) {
+    var canvas_ctx_opaque = initializeCanvas(
+        testScenario.canvasColorSpace, testScenario.canvasPixelFormat);
+
+    var canvas_ctx_blank = initializeBlankCanvas(
+        testScenario.canvasColorSpace, testScenario.canvasPixelFormat);
+    var offscreen_canvas_opaque = initializeOffscreenCanvas(
+        testScenario.imageColorSpace, testScenario.imagePixelFormat);
+    canvas_ctx_blank.drawImage(offscreen_canvas_opaque, 0, 0);
+    testPixels(canvas_ctx_blank, canvas_ctx_opaque,
+               testScenario.canvasPixelFormat, false);
+}
+
+function runDrawOffscreenCanvasTestTransparent(testScenario) {
+    var canvas_ctx_transparent = initializeCanvasTransparent(
+        testScenario.canvasColorSpace, testScenario.canvasPixelFormat);
+
+    var canvas_ctx_blank = initializeBlankCanvas(
+        testScenario.canvasColorSpace, testScenario.canvasPixelFormat);
+    var offscreen_canvas_transparent = initializeOffscreenCanvasTransparent(
+        testScenario.imageColorSpace, testScenario.imagePixelFormat);
+    canvas_ctx_blank.drawImage(offscreen_canvas_transparent, 0, 0);
+    testPixels(canvas_ctx_blank, canvas_ctx_transparent,
+               testScenario.canvasPixelFormat, true);
+}
+
+function runAllTests() {
+    var canvasColorSpaces = ['srgb'];
+    var canvasPixelFormats = ['uint8', 'float16'];
+
+    var testScenarioSet = [];
+    for (var i = 0; i < canvasColorSpaces.length; i++) {
+        for (var j = 0; j < canvasPixelFormats.length; j++) {
+            var canvas_color_space = canvasColorSpaces[i];
+            var canvas_pixel_format = canvasPixelFormats[j];
+            for (var k = 0; k < canvasColorSpaces.length; k++) {
+                for (var m = 0; m < canvasPixelFormats.length; m++) {
+                    var image_host_color_space = canvasColorSpaces[k];
+                    var image_host_pixel_format = canvasPixelFormats[m];
+
+                    var testScenario = {};
+                    testScenario.canvasColorSpace = canvas_color_space;
+                    testScenario.canvasPixelFormat = canvas_pixel_format;
+                    testScenario.imageColorSpace = image_host_color_space;
+                    testScenario.imagePixelFormat = image_host_pixel_format;
+                    testScenarioSet.push(testScenario);
+                }
+            }
+        }
+    }
+
+    for (var i = 0; i < testScenarioSet.length; i++) {
+        test(function(t) {
+            runDrawOffscreenCanvasTestOpaque(testScenarioSet[i]);
+            runDrawOffscreenCanvasTestTransparent(testScenarioSet[i]);
+        }, testScenarioToString(testScenarioSet[i]));
+    }
+}
+
+function testScenarioToString(testScenario) {
+    var str = "Test drawing color managed OffscreenCanvas: " +
+        "Canvas color params: " +
+        testScenario.canvasColorSpace + ", " +
+        testScenario.canvasPixelFormat +
+        ";  OffscreenCanvas color params: " +
+        testScenario.imageColorSpace + ", " +
+        testScenario.imagePixelFormat;
+    return str;
+}
+
+runAllTests();
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-getImageData-e_srgb.html b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-getImageData-e_srgb.html
new file mode 100644
index 0000000..f5f0e629d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/canvas-getImageData-e_srgb.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(drawSRGBThenGetImageData_e_sRGB, 'tests drawing SRGB color on e-sRGB \
+canvas and getting e-sRGB image data.');
+test(putImageData_e_sRGBThenGetImageData_e_sRGB_InRange, 'tests putting and \
+getting in-range e-sRGB image data on e-sRGB canvas.');
+test(putImageData_e_sRGBThenGetImageData_e_sRGB_OutOfRange, 'tests putting and \
+getting out-of-range e-sRGB image data on e-sRGB canvas.');
+test(putImageDataSRGBThenGetImageData_e_sRGB, 'tests putting SRGB image data \
+on e-sRGB canvas and getting e-sRGB image data.');
+
+function assert_array_approx_equals(actual, expected, epsilon)
+{
+  assert_true(actual.length === expected.length);
+  for (var i=0; i < actual.length; i++)
+    assert_approx_equals(actual[i], expected[i], epsilon);
+}
+
+function drawSRGBThenGetImageData_e_sRGB() {
+    var canvas = document.createElement('canvas');
+    canvas.width = 10;
+    canvas.height = 10;
+    var ctx = canvas.getContext('2d',
+        {colorSpace: 'srgb', pixelFormat:'float16'});
+    ctx.fillStyle = 'rgba(51, 102, 153, 0.8)';
+    ctx.fillRect(0, 0, 10, 10);
+    var pixel = ctx.getImageData(5, 5, 1, 1).dataUnion;
+    // Check against the same color in e-sRGB. 0.01 protects the test against
+    // color conversion deviations.
+    assert_array_approx_equals(pixel, [0.2, 0.4, 0.6, 0.8], 0.01);
+}
+
+function putImageData_e_sRGBThenGetImageData_e_sRGB_InRange() {
+    var canvas = document.createElement('canvas');
+    canvas.width = 10;
+    canvas.height = 10;
+    var ctx = canvas.getContext('2d',
+        {colorSpace: 'srgb', pixelFormat:'float16'})
+    var data_e_sRGB = new Float32Array(4);
+    data_e_sRGB[0] = 0.2;
+    data_e_sRGB[1] = 0.4;
+    data_e_sRGB[2] = 0.6;
+    data_e_sRGB[3] = 0.8;
+    var imageData = ctx.createImageData(data_e_sRGB, 1, 1,
+        {colorSpace: 'srgb', storageFormat:'float32'});
+    ctx.putImageData(imageData, 5, 5);
+    var pixel = ctx.getImageData(5, 5, 1, 1).dataUnion;
+    // Check against the same color in e-sRGB. 0.001 protects the test against
+    // rounding errors.
+    assert_array_approx_equals(pixel, [0.2, 0.4, 0.6, 0.8], 0.01);
+}
+
+function putImageData_e_sRGBThenGetImageData_e_sRGB_OutOfRange() {
+    var canvas = document.createElement('canvas');
+    canvas.width = 10;
+    canvas.height = 10;
+    var ctx = canvas.getContext('2d',
+        {colorSpace: 'srgb', pixelFormat:'float16'})
+    var data_e_sRGB = new Float32Array(4);
+    data_e_sRGB[0] = 1.3;
+    data_e_sRGB[1] = -1.13;
+    data_e_sRGB[2] = 0.7;
+    data_e_sRGB[3] = 1.8;
+    var imageData = ctx.createImageData(data_e_sRGB, 1, 1,
+        {colorSpace: 'srgb', storageFormat:'float32'});
+    ctx.putImageData(imageData, 5, 5);
+    var pixel = ctx.getImageData(5, 5, 1, 1).dataUnion;
+    // Check against the same color in e-sRGB. 0.001 protects the test against
+    // rounding errors.
+    assert_array_approx_equals(pixel, [1.3, -1.13, 0.7, 1.8], 0.01);
+}
+
+function putImageDataSRGBThenGetImageData_e_sRGB() {
+    var canvas = document.createElement('canvas');
+    canvas.width = 10;
+    canvas.height = 10;
+    var ctx = canvas.getContext('2d',
+        {colorSpace: 'srgb', pixelFormat:'float16'});
+    var dataSRGB = new Uint8ClampedArray(4);
+    dataSRGB[0] = 51;
+    dataSRGB[1] = 102;
+    dataSRGB[2] = 153;
+    dataSRGB[3] = 204;
+    var imageData = ctx.createImageData(dataSRGB, 1, 1,
+        {colorSpace: 'srgb', storageFormat:'uint8'});
+    ctx.putImageData(imageData, 5, 5);
+    var pixel = ctx.getImageData(5, 5, 1, 1).dataUnion;
+    // Check against the same color in e-sRGB. 0.01 protects the test against
+    // color conversion deviations.
+    assert_array_approx_equals(pixel, [0.2, 0.4, 0.6, 0.8], 0.01);
+}
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/imageData-colorManagedBehavior.html b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/imageData-colorManagedBehavior.html
new file mode 100644
index 0000000..6bc55b7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/imageData-colorManagedBehavior.html
@@ -0,0 +1,364 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+var srgbPixels =
+    [[155,27,27,128, "srgbRed"],  [27,155,27,128, "srgbGreen"],
+     [27,27,155,128, "srgbBlue"], [27,27,27,128, "srgbBlack"]];
+
+var e_srgbPixels =
+    [[0.607422, 0.121521, 0.121521, 0.501953, "e_sRgbRed"],
+     [0.121521, 0.607422, 0.121521, 0.501953, "e_sRgbGreen"],
+     [0.121521, 0.121521, 0.607422, 0.501953, "e_sRgbBlue"],
+     [0.121521, 0.121521, 0.121521, 0.501953, "e_sRgbBlack"]];
+
+var xWidth = xHeight = 2;
+var colorCorrectionToleranceSRGB = 1;
+// The error level in SRGB->WCG_U8->SRGB is higher than normal color correction
+// operations as the pixels are clipped in WCG when U8 storage is used instead
+// of F16 storage.
+var colorCorrectionToleranceWCG_U8toSRGB_U8 = 6;
+var colorCorrectionToleranceWCG = 0.02;
+
+function testBlankPixels(actualData, width, height)
+{
+  var message = "This pixel should be transparent black";
+  var blankData = new Array(4 * width * height).fill(0);
+  assert_array_equals(actualData, blankData, message);
+}
+
+function testPixels(actualData, expectedPixels, tolerance)
+{
+  assert_equals(actualData.length, 4 * xWidth * xHeight);
+  for (var i = 0; i < xWidth * xHeight; i++) {
+    var message = "This pixel should be " + expectedPixels[i][4];
+    for (var j = 0; j < 4; j++)
+      assert_approx_equals(actualData[i*4+j], expectedPixels[i][j], tolerance,
+                           message);
+  }
+}
+
+function checkImageDataColorSettings(canvasColorSettings, imageData) {
+  var imageDataColorSettings = imageData.getColorSettings();
+  assert_equals(canvasColorSettings.colorSpace,
+                imageDataColorSettings.colorSpace);
+  if (canvasColorSettings.pixelFormat == "uint8")
+    assert_equals("uint8", imageDataColorSettings.storageFormat);
+  else
+    assert_equals("float32", imageDataColorSettings.storageFormat);
+}
+
+function checkImageDataColorValues(canvasColorSettings, imageData, isBlank = '',
+  width = xWidth, height = xHeight, isWCG_U8toSRGB_U8 = '') {
+  var data = imageData.dataUnion;
+  if (isBlank === 'isBlank') {
+    testBlankPixels(data, width, height);
+  } else {
+    var expectedPixels = srgbPixels;
+    var tolerance = colorCorrectionToleranceSRGB;
+    if (isWCG_U8toSRGB_U8 === 'isWCG_U8toSRGB_U8')
+      tolerance = colorCorrectionToleranceWCG_U8toSRGB_U8;
+    if (canvasColorSettings.pixelFormat != "uint8") {
+      expectedPixels = e_srgbPixels;
+      tolerance = colorCorrectionToleranceWCG;
+    }
+    testPixels(data, expectedPixels, tolerance);
+  }
+}
+
+function initializeColorManagedCanvas(canvasColorSettings)
+{
+  var canvas = document.createElement('canvas');
+  canvas.width = xWidth;
+  canvas.height = xHeight;
+  var ctx = canvas.getContext('2d',
+    {colorSpace: canvasColorSettings.colorSpace,
+     pixelFormat: canvasColorSettings.pixelFormat});
+  ctx.fillStyle = "rgba(155, 27, 27, 0.5)";
+  ctx.fillRect(0, 0, xWidth/2, xHeight/2);
+  ctx.fillStyle = "rgba(27, 155, 27, 0.5)";
+  ctx.fillRect(xWidth/2, 0, xWidth/2, xHeight/2);
+  ctx.fillStyle = "rgba(27, 27, 155, 0.5)";
+  ctx.fillRect(0, xHeight/2, xWidth/2, xHeight/2);
+  ctx.fillStyle = "rgba(27, 27, 27, 0.5)";
+  ctx.fillRect(xWidth/2, xHeight/2, xWidth/2, xHeight/2);
+  return canvas;
+}
+
+var canvasColorSettingsSet = [
+  {name: "SRGB", colorSettings: {colorSpace: "srgb", pixelFormat: "uint8"}},
+  {name: "e-SRGB", colorSettings: {colorSpace: "srgb", pixelFormat: "float16"}},
+];
+
+var srgbImageDataU8, e_srgbImageDataU16, e_srgbImageDataF32;
+
+function PreparePredefinedImageDataObjects() {
+  var canvas = document.createElement('canvas');
+  var ctx = canvas.getContext('2d');
+  srgbImageDataU8 = ctx.createImageData(xWidth, xHeight,
+    {colorSpace: "srgb", storageFormat: "uint8"});
+  e_srgbImageDataU16 = ctx.createImageData(xWidth, xHeight,
+    {colorSpace: "srgb", storageFormat: "uint16"});
+  e_srgbImageDataF32 = ctx.createImageData(xWidth, xHeight,
+    {colorSpace: "srgb", storageFormat: "float32"});
+
+  for (var i = 0; i < xWidth * xHeight; i++)
+    for (var j = 0; j < 4; j++) {
+      srgbImageDataU8.dataUnion[i*4+j] = srgbPixels[i][j];
+      e_srgbImageDataU16.dataUnion[i*4+j] =
+          Math.round(e_srgbPixels[i][j] * 65535);
+      e_srgbImageDataF32.dataUnion[i*4+j] = e_srgbPixels[i][j];
+    }
+}
+
+PreparePredefinedImageDataObjects();
+var imageDataColorSettingsSet = [
+  {name: "SRGB U8", colorSettings: {colorSpace: "srgb", storageFormat: "uint8"},
+      imageData: srgbImageDataU8},
+  {name: "e-SRGB U16", colorSettings: {colorSpace: "srgb",
+      storageFormat: "uint16"}, imageData: e_srgbImageDataU16},
+  {name: "e-SRGB F32", colorSettings: {colorSpace: "srgb",
+      storageFormat: "float32"}, imageData: e_srgbImageDataF32},
+];
+
+////////////////////////////////////////////////////////////////////////////////
+
+// * ImageData imagedata = ctx.createImageData(width, height);
+// No color conversion. imagedata follows the color settings of the canvas.
+
+function runTestCreateImageDataWH(canvasColorSettings) {
+  var canvas = initializeColorManagedCanvas(canvasColorSettings);
+  var ctx = canvas.getContext('2d');
+  var imageData = ctx.createImageData(xWidth, xHeight);
+  checkImageDataColorSettings(canvasColorSettings, imageData);
+}
+
+var testScenariosCreateImageDataWH = [];
+for (var i = 0; i < canvasColorSettingsSet.length; i++) {
+  var message = "Test cretateImageData(width, height) from " +
+                canvasColorSettingsSet[i].name + " canvas ";
+  testScenariosCreateImageDataWH.
+      push([message, canvasColorSettingsSet[i].colorSettings]);
+}
+
+function runTestCreateImageDataWHTests() {
+  for (var i = 0; i < testScenariosCreateImageDataWH.length; i++){
+    var t = test(function() {
+      runTestCreateImageDataWH(testScenariosCreateImageDataWH[i][1]);
+    }, testScenariosCreateImageDataWH[i][0]);
+  }
+}
+runTestCreateImageDataWHTests();
+
+////////////////////////////////////////////////////////////////////////////////
+
+// * ImageData imagedata = ctx.getImageData(sx, sy, sw, sh);
+// No color conversion. imagedata follows the color settings of the canvas.
+
+function runTestGetImageDataXYWH(canvasColorSettings) {
+  var canvas = initializeColorManagedCanvas(canvasColorSettings);
+  var ctx = canvas.getContext('2d');
+  var imageData = ctx.getImageData(0, 0, xWidth, xHeight);
+  checkImageDataColorSettings(canvasColorSettings, imageData);
+  checkImageDataColorValues(canvasColorSettings, imageData);
+}
+
+var testScenariosGetImageDataXYWH = [];
+for (var i = 0; i < canvasColorSettingsSet.length; i++) {
+  var message = "Test getImageData(sx, sy, sw, sh) from " +
+                canvasColorSettingsSet[i].name + " canvas ";
+  testScenariosGetImageDataXYWH.
+      push([message, canvasColorSettingsSet[i].colorSettings]);
+}
+
+function runTestGetImageDataXYWHTests() {
+  for (var i = 0; i < testScenariosGetImageDataXYWH.length; i++){
+    var t = test(function() {
+      runTestGetImageDataXYWH(testScenariosGetImageDataXYWH[i][1]);
+    }, testScenariosGetImageDataXYWH[i][0]);
+  }
+}
+runTestGetImageDataXYWHTests();
+
+////////////////////////////////////////////////////////////////////////////////
+
+// * void ctx.putImageData(imagedata, dx, dy, ...);
+// Color conversion, if needed, to the color settings of the canvas.
+
+function runTestPutImageDataDxDy(canvasColorSettings, imageData) {
+  var canvas = document.createElement('canvas');
+  canvas.width = xWidth * 2;
+  canvas.height = xHeight * 2;
+  var ctx = canvas.getContext('2d',
+      {colorSpace: canvasColorSettings.colorSpace,
+       pixelFormat: canvasColorSettings.pixelFormat});
+  ctx.putImageData(imageData, xWidth/2, xHeight/2);
+  var ctxImageData = ctx.getImageData(xWidth/2, xHeight/2, xWidth, xHeight);
+  checkImageDataColorSettings(canvasColorSettings, ctxImageData);
+  checkImageDataColorValues(canvasColorSettings, ctxImageData, 'noBlank',
+                            xWidth, xHeight, 'isWCG_U8toSRGB_U8');
+}
+
+var testScenariosPutImageDataDxDy = [];
+for (var i = 0; i < canvasColorSettingsSet.length; i++) {
+  for (var j = 0; j < imageDataColorSettingsSet.length; j++) {
+    var message = "Test putImageData(imagedata, dx, dy): " +
+                canvasColorSettingsSet[i].name + " canvas, " +
+                imageDataColorSettingsSet[j].name + " ImageData";
+    testScenariosPutImageDataDxDy.
+        push([message, canvasColorSettingsSet[i].colorSettings,
+              imageDataColorSettingsSet[j].imageData]);
+  }
+}
+
+function runTestPutImageDataDxDyTests() {
+  for (var i = 0; i < testScenariosPutImageDataDxDy.length; i++){
+    var t = test(function() {
+      runTestPutImageDataDxDy(
+        testScenariosPutImageDataDxDy[i][1], testScenariosPutImageDataDxDy[i][2]);
+    }, testScenariosPutImageDataDxDy[i][0]);
+  }
+}
+runTestPutImageDataDxDyTests();
+
+////////////////////////////////////////////////////////////////////////////////
+
+// * ImageData imageData = ctx.createImageData(imagedata);
+// Color conversion, if needed, to the color settings of the canvas. The
+// returned imageData should be transparent black.
+
+function runTestCreateImageDataFromImageData(canvasColorSettings, imageData) {
+  var canvas = document.createElement('canvas');
+  canvas.width = xWidth * 2;
+  canvas.height = xHeight * 2;
+  var ctx = canvas.getContext('2d',
+      {colorSpace: canvasColorSettings.colorSpace,
+       pixelFormat: canvasColorSettings.pixelFormat});
+  var ctxImageData = ctx.createImageData(imageData);
+  checkImageDataColorSettings(canvasColorSettings, ctxImageData);
+  checkImageDataColorValues(canvasColorSettings, ctxImageData, 'isBlank');
+}
+
+var testScenariosCreateImageDataFromImageData = [];
+for (var i = 0; i < canvasColorSettingsSet.length; i++) {
+  for (var j = 0; j < imageDataColorSettingsSet.length; j++) {
+    var message = "Test createImageData(imagedata): " +
+                canvasColorSettingsSet[i].name + " canvas, " +
+                imageDataColorSettingsSet[j].name + " ImageData";
+    testScenariosCreateImageDataFromImageData.
+        push([message, canvasColorSettingsSet[i].colorSettings,
+              imageDataColorSettingsSet[j].imageData]);
+  }
+}
+
+function runTestCreateImageDataFromImageDataTests() {
+  for (var i = 0; i < testScenariosCreateImageDataFromImageData.length; i++){
+    var t = test(function() {
+      runTestCreateImageDataFromImageData(
+        testScenariosCreateImageDataFromImageData[i][1],
+        testScenariosCreateImageDataFromImageData[i][2]);
+    }, testScenariosCreateImageDataFromImageData[i][0]);
+  }
+}
+runTestCreateImageDataFromImageDataTests();
+
+////////////////////////////////////////////////////////////////////////////////
+
+// *ImageData ctx.createImageData(width, height, imageDataColorSettings);
+// No color conversion to the color settings of the canvas. Blank ImageData
+// should be created based on imageDataColorSettings.
+
+function runTestCreateImageDataWHC(canvasColorSettings, imageDataColorSettings) {
+  var canvas = initializeColorManagedCanvas(canvasColorSettings);
+  var ctx = canvas.getContext('2d');
+  width = height = 3;
+  var imageData = ctx.createImageData(width, height, imageDataColorSettings);
+  var colorSettings = imageData.getColorSettings();
+  assert_equals(colorSettings.colorSpace,
+                imageDataColorSettings.colorSpace,
+                "colorSpace should match");
+  assert_equals(colorSettings.storageFormat,
+                imageDataColorSettings.storageFormat,
+                "storageFormat should match");
+  var blankData = new Array(4 * width * height).fill(0);
+  assert_array_equals(imageData.dataUnion, blankData,
+                      "ImageData should be transparent black");
+}
+
+var testScenariosCreateImageDataWHC = [];
+for (var i = 0; i < canvasColorSettingsSet.length; i++) {
+  for (var j = 0; j < imageDataColorSettingsSet.length; j++) {
+    message = "Test createImageData(width, height, imageDataColorSettings): " +
+              canvasColorSettingsSet[i].name + " canvas, " +
+              imageDataColorSettingsSet[j].name + " ImageData";
+    testScenariosCreateImageDataWHC.
+        push([message, canvasColorSettingsSet[i].colorSettings,
+              imageDataColorSettingsSet[j].colorSettings]);
+  }
+}
+
+function runTestCreateImageDataWHCTests() {
+  for (var i = 0; i < testScenariosCreateImageDataWHC.length; i++){
+    var t = test(function() {
+      runTestCreateImageDataWHC(
+        testScenariosCreateImageDataWHC[i][1],
+        testScenariosCreateImageDataWHC[i][2]);
+    }, testScenariosCreateImageDataWHC[i][0]);
+  }
+}
+runTestCreateImageDataWHCTests();
+
+////////////////////////////////////////////////////////////////////////////////
+
+// *ImageData ctx.createImageData(data, width, height, imageDataColorSettings);
+// No color conversion to the color settings of the canvas. ImageData is created
+// from param "data" with proper storage format and is tagged with the
+// CanvasColorSpace which is given in imageDataColorSettings.
+
+function runTestCreateImageDataDWHC(canvasColorSettings, imageData) {
+  var data = imageData.dataUnion;
+  width = xWidth;
+  height = xHeight;
+  var colorSettings = imageData.getColorSettings();
+
+  var canvas = initializeColorManagedCanvas(canvasColorSettings);
+  var ctx = canvas.getContext('2d');
+  var newImageData = ctx.createImageData(data, width, height, colorSettings);
+  var newColorSettings = newImageData.getColorSettings();
+  assert_equals(newColorSettings.colorSpace,
+                colorSettings.colorSpace,
+                "colorSpace should match");
+  assert_equals(newColorSettings.storageFormat,
+                colorSettings.storageFormat,
+                "storageFormat should match");
+  assert_array_equals(newImageData.dataUnion, imageData.dataUnion,
+                      "ImageData should be transparent black");
+}
+
+var testScenariosCreateImageDataDWHC = [];
+for (var i = 0; i < canvasColorSettingsSet.length; i++) {
+  for (var j = 0; j < imageDataColorSettingsSet.length; j++) {
+    message = "Test createImageData(data, width, height, imageDataColorSettings): " +
+              canvasColorSettingsSet[i].name + " canvas, " +
+              imageDataColorSettingsSet[j].name + " ImageData";
+    testScenariosCreateImageDataDWHC.
+        push([message, canvasColorSettingsSet[i].colorSettings,
+              imageDataColorSettingsSet[j].imageData]);
+  }
+}
+
+function runTestCreateImageDataDWHCTests() {
+  for (var i = 0; i < testScenariosCreateImageDataDWHC.length; i++){
+    var t = test(function() {
+      runTestCreateImageDataDWHC(
+        testScenariosCreateImageDataDWHC[i][1],
+        testScenariosCreateImageDataDWHC[i][2]);
+    }, testScenariosCreateImageDataDWHC[i][0]);
+  }
+}
+runTestCreateImageDataDWHCTests();
+
+////////////////////////////////////////////////////////////////////////////////
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/imageData-colorSpace.html b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/imageData-colorSpace.html
new file mode 100644
index 0000000..9023cbe
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/imageData-colorSpace.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+var dataU8 = new Uint8ClampedArray([255, 192, 128, 64]);
+var dataU16 = new Uint16Array([65535, 256*192, 256*128, 256*64]);
+var dataF32 = new Float32Array([1.0, 0.75, 0.5, 0.25]);
+
+function checkDataTypeAgainstStorageFormat(data, storageFormat) {
+  if (storageFormat == "uint8")
+    assert_equals(Object.prototype.toString.call(data), "[object Uint8ClampedArray]");
+  else if (storageFormat == "uint16")
+    assert_equals(Object.prototype.toString.call(data), "[object Uint16Array]");
+  else if (storageFormat == "float32")
+    assert_equals(Object.prototype.toString.call(data), "[object Float32Array]");
+}
+
+function checkColorSettings(colorSettings, colorSpace, storageFormat) {
+  assert_equals(colorSettings.colorSpace, colorSpace);
+  assert_equals(colorSettings.storageFormat, storageFormat);
+}
+
+function runTest(colorSettings, expectedColorSettings) {
+  var canvas = document.createElement("canvas");
+  var ctx = canvas.getContext("2d");
+
+  imageData = ctx.createImageData(1, 1, colorSettings);
+  checkDataTypeAgainstStorageFormat(imageData.dataUnion, colorSettings.storageFormat);
+  assert_array_equals(imageData.dataUnion, [0, 0, 0, 0]);
+  checkColorSettings(imageData.getColorSettings(), expectedColorSettings.colorSpace, expectedColorSettings.storageFormat);
+
+  imageData = ctx.createImageData(dataU8, 1, 1, colorSettings);
+  assert_equals(Object.prototype.toString.call(imageData.dataUnion), "[object Uint8ClampedArray]");
+  assert_array_equals(imageData.dataUnion, dataU8);
+  checkColorSettings(imageData.getColorSettings(), expectedColorSettings.colorSpace, "uint8");
+
+  imageData = ctx.createImageData(dataU16, 1, 1, colorSettings);
+  assert_equals(Object.prototype.toString.call(imageData.dataUnion), "[object Uint16Array]");
+  assert_array_equals(imageData.dataUnion, dataU16);
+  checkColorSettings(imageData.getColorSettings(), expectedColorSettings.colorSpace, "uint16");
+
+  imageData = ctx.createImageData(dataF32, 1, 1, colorSettings);
+  assert_equals(Object.prototype.toString.call(imageData.dataUnion), "[object Float32Array]");
+  assert_array_equals(imageData.dataUnion, dataF32);
+  checkColorSettings(imageData.getColorSettings(), expectedColorSettings.colorSpace, "float32");
+}
+
+var testScenarios = [
+    ["Test default color settings: {undefined, undefined} -> {srgb, uint8}", {}, {colorSpace: "srgb", storageFormat: "uint8"}],
+
+    ["Test default color space: {undefined, float32} -> {srgb, float32}", {storageFormat: "float32"}, {colorSpace: "srgb", storageFormat: "float32"}],
+    ["Test default storage format: {srgb, undefined} -> {srgb, uint8}", {colorSpace: "srgb"}, {colorSpace: "srgb", storageFormat: "uint8"}],
+
+    ["Test color settings: {srgb, uint8}", {colorSpace: "srgb", storageFormat: "uint8"}, {colorSpace: "srgb", storageFormat: "uint8"}],
+    ["Test color settings: {srgb, uint16}", {colorSpace: "srgb", storageFormat: "uint16"}, {colorSpace: "srgb", storageFormat: "uint16"}],
+    ["Test color settings: {srgb, float32}", {colorSpace: "srgb", storageFormat: "float32"}, {colorSpace: "srgb", storageFormat: "float32"}],
+];
+
+function runAllTests() {
+  for (var i = 0; i < testScenarios.length; i++){
+    var t = test(function() {
+      runTest(testScenarios[i][1], testScenarios[i][2]);
+    }, testScenarios[i][0]);
+  }
+}
+runAllTests();
+
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/imagedata-no-color-settings-crash.html b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/imagedata-no-color-settings-crash.html
new file mode 100644
index 0000000..d7fe956e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/imagedata-no-color-settings-crash.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function() {
+    var canvas = document.createElement('canvas');
+    var ctx = canvas.getContext('2d',
+        {})
+    var dataFloat32 = new Float32Array(4);
+    var imageData = ctx.createImageData(dataFloat32, 1, 1,
+        {});
+    ctx.putImageData(imageData, 5, 5);
+    var data = ctx.getImageData(5,5,1,1).dataUnion;
+}, "Putting a float-32 ImageData with no color settings on a context 2D should not crash.");
+
+test(function() {
+    var canvas = document.createElement('canvas');
+    var ctx = canvas.getContext('2d',
+        {})
+    var dataUint16 = new Uint16Array(4);
+    var imageData = ctx.createImageData(dataUint16, 1, 1,
+        {});
+    ctx.putImageData(imageData, 5, 5);
+    var data = ctx.getImageData(5,5,1,1).dataUnion;
+}, "Putting a uint-16 ImageData with no color settings on a context 2D should not crash.");
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-semitransparent-p3d65.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-semitransparent-p3d65.png
new file mode 100644
index 0000000..5eb3f6a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-semitransparent-p3d65.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-semitransparent-rec2020.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-semitransparent-rec2020.png
new file mode 100644
index 0000000..b64db07
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-semitransparent-rec2020.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-semitransparent-srgb.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-semitransparent-srgb.png
new file mode 100644
index 0000000..bfbba8b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-semitransparent-srgb.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb-fullcolor.ogv b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb-fullcolor.ogv
new file mode 100644
index 0000000..43a72bf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb-fullcolor.ogv
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb-transparent.bmp b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb-transparent.bmp
new file mode 100644
index 0000000..9c9561c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb-transparent.bmp
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb-transparent.ico b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb-transparent.ico
new file mode 100644
index 0000000..87e8ff4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb-transparent.ico
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb-transparent.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb-transparent.png
new file mode 100644
index 0000000..bfbba8b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb-transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb-transparent.webp b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb-transparent.webp
new file mode 100644
index 0000000..9256460
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb-transparent.webp
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.bmp b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.bmp
new file mode 100644
index 0000000..465d203
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.bmp
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.gif b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.gif
new file mode 100644
index 0000000..25bcefb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.gif
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.ico b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.ico
new file mode 100644
index 0000000..e5375826
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.ico
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.jpg b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.jpg
new file mode 100644
index 0000000..c4579e8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.png
new file mode 100644
index 0000000..1b5876b5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.svg b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.svg
new file mode 100644
index 0000000..0517130
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.svg
@@ -0,0 +1,6 @@
+<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <rect x="0" y="0" width="10px" height="10px" fill="#9b1b1b"/>
+    <rect x="10" y="0" width="10px" height="10px" fill="#1b9b1b"/>
+    <rect x="0" y="10" width="10px" height="10px" fill="#1b1b9b"/>
+    <rect x="10" y="10" width="10px" height="10px" fill="#1b1b1b"/>
+</svg>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.webp b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.webp
new file mode 100644
index 0000000..b7c0a42
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/pattern-srgb.webp
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_AdobeRGB_opaque.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_AdobeRGB_opaque.png
new file mode 100644
index 0000000..c4496db
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_AdobeRGB_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_AdobeRGB_transparent.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_AdobeRGB_transparent.png
new file mode 100644
index 0000000..3b4cfda5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_AdobeRGB_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_DisplayP3_opaque.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_DisplayP3_opaque.png
new file mode 100644
index 0000000..e7a142c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_DisplayP3_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_DisplayP3_transparent.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_DisplayP3_transparent.png
new file mode 100644
index 0000000..0b03531
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_DisplayP3_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_ProPhoto_opaque.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_ProPhoto_opaque.png
new file mode 100644
index 0000000..a1dc7dd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_ProPhoto_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_ProPhoto_transparent.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_ProPhoto_transparent.png
new file mode 100644
index 0000000..be2eb12
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_ProPhoto_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_Rec2020_opaque.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_Rec2020_opaque.png
new file mode 100644
index 0000000..e2a2d14
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_Rec2020_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_Rec2020_transparent.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_Rec2020_transparent.png
new file mode 100644
index 0000000..960d7d8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_Rec2020_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_opaque.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_opaque.png
new file mode 100644
index 0000000..80cf978
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_transparent.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_transparent.png
new file mode 100644
index 0000000..3ec565f8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_opaque.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_opaque.png
new file mode 100644
index 0000000..5f3134b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_transparent.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_transparent.png
new file mode 100644
index 0000000..500a70e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_opaque.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_opaque.png
new file mode 100644
index 0000000..b5d0e07
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_transparent.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_transparent.png
new file mode 100644
index 0000000..e4ec3e4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_Rec2020_opaque.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_Rec2020_opaque.png
new file mode 100644
index 0000000..c487d584
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_Rec2020_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_Rec2020_transparent.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_Rec2020_transparent.png
new file mode 100644
index 0000000..78fe202
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_Rec2020_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_sRGB_opaque.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_sRGB_opaque.png
new file mode 100644
index 0000000..babf232a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_sRGB_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_sRGB_transparent.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_sRGB_transparent.png
new file mode 100644
index 0000000..3016404
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_sRGB_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_sRGB_opaque.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_sRGB_opaque.png
new file mode 100644
index 0000000..8a66534
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_sRGB_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_sRGB_transparent.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_sRGB_transparent.png
new file mode 100644
index 0000000..e51cda7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_16bit_sRGB_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_AdobeRGB_opaque.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_AdobeRGB_opaque.png
new file mode 100644
index 0000000..8b787b5c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_AdobeRGB_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_AdobeRGB_transparent.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_AdobeRGB_transparent.png
new file mode 100644
index 0000000..727028e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_AdobeRGB_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_DisplayP3_opaque.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_DisplayP3_opaque.png
new file mode 100644
index 0000000..fe8bdd49
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_DisplayP3_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_DisplayP3_transparent.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_DisplayP3_transparent.png
new file mode 100644
index 0000000..b836afeb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_DisplayP3_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_ProPhoto_opaque.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_ProPhoto_opaque.png
new file mode 100644
index 0000000..5ecd868
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_ProPhoto_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_ProPhoto_transparent.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_ProPhoto_transparent.png
new file mode 100644
index 0000000..85a349dc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_ProPhoto_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_Rec2020_opaque.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_Rec2020_opaque.png
new file mode 100644
index 0000000..599cd34
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_Rec2020_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_Rec2020_transparent.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_Rec2020_transparent.png
new file mode 100644
index 0000000..ecf65c3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_Rec2020_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_sRGB_opaque.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_sRGB_opaque.png
new file mode 100644
index 0000000..9cab6d1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_sRGB_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_sRGB_transparent.png b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_sRGB_transparent.png
new file mode 100644
index 0000000..5fa01e6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/resources/png-16bit/2x2_8bit_sRGB_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/transferFromImageBitmap.html b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/transferFromImageBitmap.html
new file mode 100644
index 0000000..6855dc7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/wide-gamut-canvas/transferFromImageBitmap.html
@@ -0,0 +1,115 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+// This test examines the color managed ImageBitmapRenderingContext.
+//  - We first draw on 2D canvas with different color space and alpha
+//    properties.
+//  - Next, the canvas is used as a source image to create an ImageBitmap.
+//    Different colorSpaceConversion values are tested when creating the
+//    intermediate ImageBitmap object.
+//  - Next, ImageBitmap is transferred to an ImageBitmapRenderingContext.
+//  - Since we cannot read back pixels from ImageBitmapRenderingContext, we
+//    draw it to another 2D canvas and read back the pixels.
+//  - Finally, we compare the pixels read from the first 2D canvas with those
+//    read from the final 2D canvas. We expect them to match with some
+//    tolerance.
+
+function testPixels(actualPixels, expectedPixels, testScenario)
+{
+    var tolerance = 0;
+    if (testScenario.pixelFormat == "float16")
+        tolerance = 0.01;
+
+    assert_array_approx_equals(actualPixels, expectedPixels, tolerance);
+}
+
+function generateFillStyle(red, green, blue, alpha) {
+    return "rgba(" + red + "," + green + "," + blue + "," + alpha + ")";
+}
+
+function generateExpectedResult(testScenario, canvas)
+{
+    var ctx = canvas.getContext('2d', {colorSpace: testScenario.colorSpace,
+                                       pixelFormat: testScenario.pixelFormat});
+    ctx.fillStyle = generateFillStyle(155, 27, 27, testScenario.alpha);
+    ctx.fillRect(0, 0, 1, 1);
+    ctx.fillStyle = generateFillStyle(27, 155, 27, testScenario.alpha);
+    ctx.fillRect(1, 0, 1, 1);
+    ctx.fillStyle = generateFillStyle(27, 27, 155, testScenario.alpha);
+    ctx.fillRect(0, 1, 1, 1);
+    ctx.fillStyle = generateFillStyle(27, 27, 27, testScenario.alpha);
+    ctx.fillRect(1, 1, 1, 1);
+    return ctx.getImageData(0, 0, 2, 2).dataUnion;
+}
+
+function generateTestName(testScenario) {
+    var str = "Testing ImageBitmapRenderingContext:" +
+              " Source color space: " + testScenario.colorSpace +
+              ", pixel format: " + testScenario.pixelFormat +
+              ", alpha: " + testScenario.alpha +
+              ", intermediate color space: " +
+              testScenario.colorSpaceConversion;
+    return str;
+}
+
+function runTransferFromImageBitmapTest(testScenario) {
+    var canvas = document.createElement("canvas");
+    canvas.width = 2;
+    canvas.height = 2;
+    var expectedPixels = generateExpectedResult(testScenario, canvas);
+
+    promise_test(function() {
+        var options = {colorSpaceConversion: testScenario.colorSpaceConversion};
+        var promise = createImageBitmap(canvas, options);
+        return Promise.all([promise]).then(([imagebitmap]) => {
+            var dstCanvas = document.createElement("canvas");
+            dstCanvas.width = 2;
+            dstCanvas.height = 2;
+            var dstCtx = dstCanvas.getContext('bitmaprenderer');
+            dstCtx.transferFromImageBitmap(imagebitmap);
+
+            // ImageBitmapRenderingContext does not have ImageData, so we draw
+            // it on another canvas and read back the data.
+            var finalCanvas = document.createElement("canvas");
+            finalCanvas.width = 2;
+            finalCanvas.height = 2;
+            var ctx = finalCanvas.getContext('2d',
+                {colorSpace: testScenario.colorSpace,
+                 pixelFormat: testScenario.pixelFormat});
+            ctx.drawImage(dstCanvas, 0, 0);
+            var actualPixels = ctx.getImageData(0, 0, 2, 2).dataUnion;
+            testPixels(actualPixels, expectedPixels, testScenario);
+        });
+       }, generateTestName(testScenario));
+}
+
+
+function runAllTests() {
+    var colorSpaces = [
+        {colorSpace: 'srgb', pixelFormat: 'uint8'},
+        {colorSpace: 'srgb', pixelFormat: 'float16'},
+        ];
+    var alphaValues = [0.5, 1];
+    var colorSpaceConversions = ['none', 'default'];
+
+    var testScenarios = [];
+    for (var i = 0; i < colorSpaces.length; i++)
+        for (var j = 0; j < alphaValues.length; j++)
+            for (var k = 0; k < colorSpaceConversions.length; k++) {
+                var testScenario = {};
+                testScenario.colorSpace = colorSpaces[i].colorSpace;
+                testScenario.pixelFormat = colorSpaces[i].pixelFormat;
+                testScenario.alpha = alphaValues[j];
+                testScenario.colorSpaceConversion = colorSpaceConversions[k];
+                testScenarios.push(testScenario);
+            }
+
+    for (var i = 0; i < testScenarios.length; i++)
+        runTransferFromImageBitmapTest(testScenarios[i]);
+}
+
+runAllTests();
+
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-scroll-snap/inheritance-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/css-scroll-snap/inheritance-expected.txt
deleted file mode 100644
index 1508689..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-scroll-snap/inheritance-expected.txt
+++ /dev/null
@@ -1,41 +0,0 @@
-This is a testharness.js-based test.
-PASS Property scroll-margin-block-end has initial value 0px
-PASS Property scroll-margin-block-end does not inherit
-PASS Property scroll-margin-block-start has initial value 0px
-PASS Property scroll-margin-block-start does not inherit
-PASS Property scroll-margin-bottom has initial value 0px
-PASS Property scroll-margin-bottom does not inherit
-PASS Property scroll-margin-inline-end has initial value 0px
-PASS Property scroll-margin-inline-end does not inherit
-PASS Property scroll-margin-inline-start has initial value 0px
-PASS Property scroll-margin-inline-start does not inherit
-PASS Property scroll-margin-left has initial value 0px
-PASS Property scroll-margin-left does not inherit
-PASS Property scroll-margin-right has initial value 0px
-PASS Property scroll-margin-right does not inherit
-PASS Property scroll-margin-top has initial value 0px
-PASS Property scroll-margin-top does not inherit
-PASS Property scroll-padding-block-end has initial value auto
-PASS Property scroll-padding-block-end does not inherit
-PASS Property scroll-padding-block-start has initial value auto
-PASS Property scroll-padding-block-start does not inherit
-FAIL Property scroll-padding-bottom has initial value 0px assert_equals: expected "0px" but got "auto"
-PASS Property scroll-padding-bottom does not inherit
-PASS Property scroll-padding-inline-end has initial value auto
-PASS Property scroll-padding-inline-end does not inherit
-PASS Property scroll-padding-inline-start has initial value auto
-PASS Property scroll-padding-inline-start does not inherit
-FAIL Property scroll-padding-left has initial value 0px assert_equals: expected "0px" but got "auto"
-PASS Property scroll-padding-left does not inherit
-FAIL Property scroll-padding-right has initial value 0px assert_equals: expected "0px" but got "auto"
-PASS Property scroll-padding-right does not inherit
-FAIL Property scroll-padding-top has initial value 0px assert_equals: expected "0px" but got "auto"
-PASS Property scroll-padding-top does not inherit
-PASS Property scroll-snap-align has initial value none
-PASS Property scroll-snap-align does not inherit
-PASS Property scroll-snap-stop has initial value normal
-PASS Property scroll-snap-stop does not inherit
-PASS Property scroll-snap-type has initial value none
-PASS Property scroll-snap-type does not inherit
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-scroll-snap/inheritance.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-scroll-snap/inheritance.html
index 40600d4..b380cb40 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-scroll-snap/inheritance.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-scroll-snap/inheritance.html
@@ -25,12 +25,12 @@
 assert_not_inherited('scroll-margin-top', '0px', '10px');
 assert_not_inherited('scroll-padding-block-end', 'auto', '10px');
 assert_not_inherited('scroll-padding-block-start', 'auto', '10px');
-assert_not_inherited('scroll-padding-bottom', '0px', '10px');
+assert_not_inherited('scroll-padding-bottom', 'auto', '10px');
 assert_not_inherited('scroll-padding-inline-end', 'auto', '10px');
 assert_not_inherited('scroll-padding-inline-start', 'auto', '10px');
-assert_not_inherited('scroll-padding-left', '0px', '10px');
-assert_not_inherited('scroll-padding-right', '0px', '10px');
-assert_not_inherited('scroll-padding-top', '0px', '10px');
+assert_not_inherited('scroll-padding-left', 'auto', '10px');
+assert_not_inherited('scroll-padding-right', 'auto', '10px');
+assert_not_inherited('scroll-padding-top', 'auto', '10px');
 assert_not_inherited('scroll-snap-align', 'none', 'start end');
 assert_not_inherited('scroll-snap-stop', 'normal', 'always');
 assert_not_inherited('scroll-snap-type', 'none', 'inline proximity');
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-e_srgb.html b/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-e_srgb.html
index ed2deda3..dcfd071 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-e_srgb.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-e_srgb.html
@@ -367,7 +367,7 @@
 
 // HTMLCanvasElement - Opaque Linear RGB
 promise_test(function() {
-    var testCanvas = initializeTestCanvas('srgb', 'float16');
+    var testCanvas = initializeTestCanvas('linear-rgb', 'float16');
     return testImageBitmapOpaque(testCanvas);
 }, 'createImageBitmap in e-sRGB from an opaque Linear RGB HTMLCanvasElement with resize.');
 
@@ -399,7 +399,7 @@
 
 // HTMLCanvasElement - Transparent Linear RGB
 promise_test(function() {
-    var testCanvas = initializeTestCanvasTransparent('srgb', 'float16');
+    var testCanvas = initializeTestCanvasTransparent('linear-rgb', 'float16');
     return testImageBitmapTransparent(testCanvas);
 }, 'createImageBitmap in e-sRGB from a transparent Linear RGB HTMLCanvasElement with resize.');
 
@@ -507,9 +507,9 @@
 
 // ImageData - Opaque Linear RGB
 promise_test(function() {
-    var canvas = initializeTestCanvas('srgb', 'float16');
+    var canvas = initializeTestCanvas('linear-rgb', 'float16');
     var ctx = canvas.getContext('2d',
-        {colorSpace: 'srgb', pixelFormat:'float16'});
+        {colorSpace: 'linear-rgb', pixelFormat:'float16'});
     var data = ctx.getImageData(0, 0, 20, 20);
     return testImageBitmapOpaque(data);
 }, 'createImageBitmap in e-sRGB from an opaque Linear RGB ImageData with resize.');
@@ -553,9 +553,9 @@
 
 // ImageData - Transparent Linear RGB
 promise_test(function() {
-    var canvas = initializeTestCanvasTransparent('srgb', 'float16');
+    var canvas = initializeTestCanvasTransparent('linear-rgb', 'float16');
     var ctx = canvas.getContext('2d',
-        {colorSpace: 'srgb', pixelFormat:'float16'});
+        {colorSpace: 'linear-rgb', pixelFormat:'float16'});
     var data = ctx.getImageData(0, 0, 20, 20);
     return testImageBitmapTransparent(data);
 }, 'createImageBitmap in e-sRGB from a transparent Linear RGB ImageData with resize.');
@@ -595,8 +595,8 @@
 
 // ImageBitmap - Opaque Linear RGB
 promise_test(function() {
-    var testCanvas = initializeTestCanvas('srgb', 'float16');
-    return createImageBitmap(testCanvas, {colorSpaceConversion: "srgb"}
+    var testCanvas = initializeTestCanvas('linear-rgb', 'float16');
+    return createImageBitmap(testCanvas, {colorSpaceConversion: "linear-rgb"}
         ).then(testImageBitmapOpaque);
 }, 'createImageBitmap in e-sRGB from an opaque Linear RGB ImageBitmap with resize.');
 
@@ -631,8 +631,8 @@
 
 // ImageBitmap - Transparent Linear RGB
 promise_test(function() {
-    var testCanvas = initializeTestCanvasTransparent('srgb', 'float16');
-    return createImageBitmap(testCanvas, {colorSpaceConversion: "srgb"}
+    var testCanvas = initializeTestCanvasTransparent('linear-rgb', 'float16');
+    return createImageBitmap(testCanvas, {colorSpaceConversion: "linear-rgb"}
         ).then(testImageBitmapTransparent);
 }, 'createImageBitmap in e-sRGB from a transparent Linear RGB ImageBitmap with resize.');
 
@@ -685,7 +685,7 @@
 
 //OffscreenCanvas - Opaque Linear RGB
 promise_test(function() {
-    var offscreen = initializeOffscreenCanvas('srgb', 'float16');
+    var offscreen = initializeOffscreenCanvas('linear-rgb', 'float16');
     return testImageBitmapOpaque(offscreen);
 }, 'createImageBitmap in e-sRGB from an opaque Linear RGB OffscreenCanvas with resize.');
 
@@ -736,7 +736,7 @@
 
 //OffscreenCanvas - Transparent Linear RGB
 promise_test(function() {
-    var offscreen = initializeOffscreenCanvasTransparent('srgb', 'float16');
+    var offscreen = initializeOffscreenCanvasTransparent('linear-rgb', 'float16');
     return testImageBitmapTransparent(offscreen);
 }, 'createImageBitmap in e-sRGB from a transparent Linear RGB OffscreenCanvas with resize.');
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/layers/layer-canvas-log-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/layers/layer-canvas-log-expected.txt
index 3200056..c20361c 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/layers/layer-canvas-log-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/layers/layer-canvas-log-expected.txt
@@ -16,7 +16,6 @@
                 strokeMiter : 4
                 strokeWidth : 0
                 styleName : "Fill"
-                textAlign : "Left"
                 textEncoding : "UTF-8"
                 textScaleX : 1
                 textSize : 12
@@ -44,7 +43,6 @@
                 strokeMiter : 4
                 strokeWidth : 0
                 styleName : "Fill"
-                textAlign : "Left"
                 textEncoding : "UTF-8"
                 textScaleX : 1
                 textSize : 12
@@ -83,7 +81,6 @@
                 strokeMiter : 4
                 strokeWidth : 0
                 styleName : "Fill"
-                textAlign : "Left"
                 textEncoding : "UTF-8"
                 textScaleX : 1
                 textSize : 12
@@ -138,7 +135,6 @@
                 strokeMiter : 4
                 strokeWidth : 0
                 styleName : "Fill"
-                textAlign : "Left"
                 textEncoding : "UTF-8"
                 textScaleX : 1
                 textSize : 12
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/sxg/sxg-navigation-redirect-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/sxg/sxg-navigation-redirect-expected.txt
new file mode 100644
index 0000000..085fb0bd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/sxg/sxg-navigation-redirect-expected.txt
@@ -0,0 +1,24 @@
+Tests the signed exchange information are available when the navigation succeeded after redirect.
+
+* http://localhost:8000/resources/redirect.php?url=http%3A%2F%2F127.0.0.1%3A8000%2Floading%2Fsxg%2Fresources%2Fsxg-location.sxg
+  failed: false
+  statusCode: 302
+  resourceType: other
+* http://127.0.0.1:8000/loading/sxg/resources/sxg-location.sxg
+  failed: false
+  statusCode: 200
+  resourceType: signed-exchange
+  SignedExchangeInfo
+    Request URL: https://127.0.0.1:8443/loading/sxg/resources/inner-url.html
+    Certificate URL: https://127.0.0.1:8443/loading/sxg/resources/127.0.0.1.sxg.pem.cbor
+    Certificate Subject: 127.0.0.1
+    Certificate Issuer: web-platform-tests
+* https://127.0.0.1:8443/loading/sxg/resources/127.0.0.1.sxg.pem.cbor
+  failed: false
+  statusCode: 200
+  resourceType: other
+* https://127.0.0.1:8443/loading/sxg/resources/inner-url.html
+  failed: false
+  statusCode: 200
+  resourceType: document
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/sxg/sxg-navigation-redirect.js b/third_party/WebKit/LayoutTests/http/tests/devtools/sxg/sxg-navigation-redirect.js
new file mode 100644
index 0000000..1ea1560
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/sxg/sxg-navigation-redirect.js
@@ -0,0 +1,18 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+(async function() {
+  TestRunner.addResult('Tests the signed exchange information are available when the navigation succeeded after redirect.\n');
+  await TestRunner.loadModule('network_test_runner');
+  await TestRunner.loadModule('console_test_runner');
+  await TestRunner.showPanel('network');
+  SDK.networkLog.reset();
+  const url =
+      'http://localhost:8000/resources/redirect.php?url=' +
+      encodeURIComponent(
+        'http://127.0.0.1:8000/loading/sxg/resources/sxg-location.sxg');
+  await TestRunner.addIframe(url);
+  ConsoleTestRunner.dumpConsoleMessages();
+  NetworkTestRunner.dumpNetworkRequestsWithSignedExchangeInfo();
+  TestRunner.completeTest();
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/loading/sxg/sxg-location-origin-trial-redirect.html b/third_party/WebKit/LayoutTests/http/tests/loading/sxg/sxg-location-origin-trial-redirect.html
new file mode 100644
index 0000000..9a32bd3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/loading/sxg/sxg-location-origin-trial-redirect.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Location of SignedHTTPExchange with Origin-Trial header after redirect</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="./resources/sxg-util.js"></script>
+<body>
+<script>
+promise_test(async (t) => {
+  await waitUntilDidFinishLoadForFrame;
+  const url =
+      'http://localhost:8000/resources/redirect.php?url=' +
+      encodeURIComponent(
+        'http://127.0.0.1:8000/loading/sxg/resources/sxg-location-origin-trial.php');
+  const message = await openSXGInIframeAndWaitForMessage(t, url);
+  assert_equals(message.location, 'https://127.0.0.1:8443/loading/sxg/resources/inner-url.html');
+  assert_false(message.is_fallback);
+}, 'Location of SignedHTTPExchange with Origin-Trial header after redirect');
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/layout-fonts/emoji-cluster-fallback-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/layout-fonts/emoji-cluster-fallback-expected.txt
new file mode 100644
index 0000000..8a663758
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/layout-fonts/emoji-cluster-fallback-expected.txt
@@ -0,0 +1,8 @@
+Test passes if emoji are renderered using only Noto Color Emoji, no glyphs from Linux Libertine.
+Note: Emoji rendering output in this text only representation is not equivalent to internal page appearance.
+👮‍♀️👮‍♂️👮🏻‍♀️👮🏻‍♂️👮🏼‍♀️👮🏼‍♂️👮🏽‍♀️👮🏽‍♂️👮🏾‍♀️👮🏾‍♂️👮🏿‍♀️👮🏿‍♂️👱‍♀️👱‍♂️👱🏻‍♀️👱🏻‍♂️👱🏼‍♀️👱🏼‍♂️👱🏽‍♀️👱🏽‍♂️👱🏾‍♀️👱🏾‍♂️👱🏿‍♀️👱🏿‍♂️👳‍♀️👳‍♂️👳🏻‍♀️👳🏻‍♂️👳🏼‍♀️👳🏼‍♂️👳🏽‍♀️👳🏽‍♂️👳🏾‍♀️👳🏾‍♂️👳🏿‍♀️👳🏿‍♂️👷‍♀️👷‍♂️👷🏻‍♀️👷🏻‍♂️👷🏼‍♀️👷🏼‍♂️👷🏽‍♀️👷🏽‍♂️👷🏾‍♀️👷🏾‍♂️👷🏿‍♀️👷🏿‍♂️💂‍♀️💂‍♂️💂🏻‍♀️💂🏻‍♂️💂🏼‍♀️💂🏼‍♂️💂🏽‍♀️💂🏽‍♂️💂🏾‍♀️💂🏾‍♂️💂🏿‍♀️💂🏿‍♂️🕵🏻‍♀️🕵🏻‍♂️🕵🏼‍♀️🕵🏼‍♂️🕵🏽‍♀️🕵🏽‍♂️🕵🏾‍♀️🕵🏾‍♂️🕵🏿‍♀️🕵🏿‍♂️🕵️‍♀️🕵️‍♂️🧙‍♀️🧙‍♂️🧙🏻‍♀️🧙🏻‍♂️🧙🏼‍♀️🧙🏼‍♂️🧙🏽‍♀️🧙🏽‍♂️🧙🏾‍♀️🧙🏾‍♂️🧙🏿‍♀️🧙🏿‍♂️🧚‍♀️🧚‍♂️🧚🏻‍♀️🧚🏻‍♂️🧚🏼‍♀️🧚🏼‍♂️🧚🏽‍♀️🧚🏽‍♂️🧚🏾‍♀️🧚🏾‍♂️🧚🏿‍♀️🧚🏿‍♂️🧛‍♀️🧛‍♂️🧛🏻‍♀️🧛🏻‍♂️🧛🏼‍♀️🧛🏼‍♂️🧛🏽‍♀️🧛🏽‍♂️🧛🏾‍♀️🧛🏾‍♂️🧛🏿‍♀️🧛🏿‍♂️🧜‍♀️🧜‍♂️🧜🏻‍♀️🧜🏻‍♂️🧜🏼‍♀️🧜🏼‍♂️🧜🏽‍♀️🧜🏽‍♂️🧜🏾‍♀️🧜🏾‍♂️🧜🏿‍♀️🧜🏿‍♂️🧝‍♀️🧝‍♂️🧝🏻‍♀️🧝🏻‍♂️🧝🏼‍♀️🧝🏼‍♂️🧝🏽‍♀️🧝🏽‍♂️🧝🏾‍♀️🧝🏾‍♂️🧝🏿‍♀️🧝🏿‍♂️🧞‍♀️🧞‍♂️🧟‍♀️🧟‍♂️⛹🏻‍♀️⛹🏻‍♂️⛹🏼‍♀️⛹🏼‍♂️⛹🏽‍♀️⛹🏽‍♂️⛹🏾‍♀️⛹🏾‍♂️⛹🏿‍♀️⛹🏿‍♂️⛹️‍♀️⛹️‍♂️🏃‍♀️🏃‍♂️🏃🏻‍♀️🏃🏻‍♂️🏃🏼‍♀️🏃🏼‍♂️🏃🏽‍♀️🏃🏽‍♂️🏃🏾‍♀️🏃🏾‍♂️🏃🏿‍♀️🏃🏿‍♂️🏄‍♀️🏄‍♂️🏄🏻‍♀️🏄🏻‍♂️🏄🏼‍♀️🏄🏼‍♂️🏄🏽‍♀️🏄🏽‍♂️🏄🏾‍♀️🏄🏾‍♂️🏄🏿‍♀️🏄🏿‍♂️🏊‍♀️🏊‍♂️🏊🏻‍♀️🏊🏻‍♂️🏊🏼‍♀️🏊🏼‍♂️🏊🏽‍♀️🏊🏽‍♂️🏊🏾‍♀️🏊🏾‍♂️🏊🏿‍♀️🏊🏿‍♂️🏋🏻‍♀️🏋🏻‍♂️🏋🏼‍♀️🏋🏼‍♂️🏋🏽‍♀️🏋🏽‍♂️🏋🏾‍♀️🏋🏾‍♂️🏋🏿‍♀️🏋🏿‍♂️🏋️‍♀️🏋️‍♂️🏌🏻‍♀️🏌🏻‍♂️🏌🏼‍♀️🏌🏼‍♂️🏌🏽‍♀️🏌🏽‍♂️🏌🏾‍♀️🏌🏾‍♂️🏌🏿‍♀️🏌🏿‍♂️🏌️‍♀️🏌️‍♂️👯‍♀️👯‍♂️💆‍♀️💆‍♂️💆🏻‍♀️💆🏻‍♂️💆🏼‍♀️💆🏼‍♂️💆🏽‍♀️💆🏽‍♂️💆🏾‍♀️💆🏾‍♂️💆🏿‍♀️💆🏿‍♂️💇‍♀️💇‍♂️💇🏻‍♀️💇🏻‍♂️💇🏼‍♀️💇🏼‍♂️💇🏽‍♀️💇🏽‍♂️💇🏾‍♀️💇🏾‍♂️💇🏿‍♀️💇🏿‍♂️🚣‍♀️🚣‍♂️🚣🏻‍♀️🚣🏻‍♂️🚣🏼‍♀️🚣🏼‍♂️🚣🏽‍♀️🚣🏽‍♂️🚣🏾‍♀️🚣🏾‍♂️🚣🏿‍♀️🚣🏿‍♂️🚴‍♀️🚴‍♂️🚴🏻‍♀️🚴🏻‍♂️🚴🏼‍♀️🚴🏼‍♂️🚴🏽‍♀️🚴🏽‍♂️🚴🏾‍♀️🚴🏾‍♂️🚴🏿‍♀️🚴🏿‍♂️🚵‍♀️🚵‍♂️🚵🏻‍♀️🚵🏻‍♂️🚵🏼‍♀️🚵🏼‍♂️🚵🏽‍♀️🚵🏽‍♂️🚵🏾‍♀️🚵🏾‍♂️🚵🏿‍♀️🚵🏿‍♂️🚶‍♀️🚶‍♂️🚶🏻‍♀️🚶🏻‍♂️🚶🏼‍♀️🚶🏼‍♂️🚶🏽‍♀️🚶🏽‍♂️🚶🏾‍♀️🚶🏾‍♂️🚶🏿‍♀️🚶🏿‍♂️🤸‍♀️🤸‍♂️🤸🏻‍♀️🤸🏻‍♂️🤸🏼‍♀️🤸🏼‍♂️🤸🏽‍♀️🤸🏽‍♂️🤸🏾‍♀️🤸🏾‍♂️🤸🏿‍♀️🤸🏿‍♂️🤹‍♀️🤹‍♂️🤹🏻‍♀️🤹🏻‍♂️🤹🏼‍♀️🤹🏼‍♂️🤹🏽‍♀️🤹🏽‍♂️🤹🏾‍♀️🤹🏾‍♂️🤹🏿‍♀️🤹🏿‍♂️🤼‍♀️🤼‍♂️🤽‍♀️🤽‍♂️🤽🏻‍♀️🤽🏻‍♂️🤽🏼‍♀️🤽🏼‍♂️🤽🏽‍♀️🤽🏽‍♂️🤽🏾‍♀️🤽🏾‍♂️🤽🏿‍♀️🤽🏿‍♂️🤾‍♀️🤾‍♂️🤾🏻‍♀️🤾🏻‍♂️🤾🏼‍♀️🤾🏼‍♂️🤾🏽‍♀️🤾🏽‍♂️🤾🏾‍♀️🤾🏾‍♂️🤾🏿‍♀️🤾🏿‍♂️🧖‍♀️🧖‍♂️🧖🏻‍♀️🧖🏻‍♂️🧖🏼‍♀️🧖🏼‍♂️🧖🏽‍♀️🧖🏽‍♂️🧖🏾‍♀️🧖🏾‍♂️🧖🏿‍♀️🧖🏿‍♂️🧗‍♀️🧗‍♂️🧗🏻‍♀️🧗🏻‍♂️🧗🏼‍♀️🧗🏼‍♂️🧗🏽‍♀️🧗🏽‍♂️🧗🏾‍♀️🧗🏾‍♂️🧗🏿‍♀️🧗🏿‍♂️🧘‍♀️🧘‍♂️🧘🏻‍♀️🧘🏻‍♂️🧘🏼‍♀️🧘🏼‍♂️🧘🏽‍♀️🧘🏽‍♂️🧘🏾‍♀️🧘🏾‍♂️🧘🏿‍♀️🧘🏿‍♂️💁‍♀️💁‍♂️💁🏻‍♀️💁🏻‍♂️💁🏼‍♀️💁🏼‍♂️💁🏽‍♀️💁🏽‍♂️💁🏾‍♀️💁🏾‍♂️💁🏿‍♀️💁🏿‍♂️🙅‍♀️🙅‍♂️🙅🏻‍♀️🙅🏻‍♂️🙅🏼‍♀️🙅🏼‍♂️🙅🏽‍♀️🙅🏽‍♂️🙅🏾‍♀️🙅🏾‍♂️🙅🏿‍♀️🙅🏿‍♂️🙆‍♀️🙆‍♂️🙆🏻‍♀️🙆🏻‍♂️🙆🏼‍♀️🙆🏼‍♂️🙆🏽‍♀️🙆🏽‍♂️🙆🏾‍♀️🙆🏾‍♂️🙆🏿‍♀️🙆🏿‍♂️🙇‍♀️🙇‍♂️🙇🏻‍♀️🙇🏻‍♂️🙇🏼‍♀️🙇🏼‍♂️🙇🏽‍♀️🙇🏽‍♂️🙇🏾‍♀️🙇🏾‍♂️🙇🏿‍♀️🙇🏿‍♂️🙋‍♀️🙋‍♂️🙋🏻‍♀️🙋🏻‍♂️🙋🏼‍♀️🙋🏼‍♂️🙋🏽‍♀️🙋🏽‍♂️🙋🏾‍♀️🙋🏾‍♂️🙋🏿‍♀️🙋🏿‍♂️🙍‍♀️🙍‍♂️🙍🏻‍♀️🙍🏻‍♂️🙍🏼‍♀️🙍🏼‍♂️🙍🏽‍♀️🙍🏽‍♂️🙍🏾‍♀️🙍🏾‍♂️🙍🏿‍♀️🙍🏿‍♂️🙎‍♀️🙎‍♂️🙎🏻‍♀️🙎🏻‍♂️🙎🏼‍♀️🙎🏼‍♂️🙎🏽‍♀️🙎🏽‍♂️🙎🏾‍♀️🙎🏾‍♂️🙎🏿‍♀️🙎🏿‍♂️🤦‍♀️🤦‍♂️🤦🏻‍♀️🤦🏻‍♂️🤦🏼‍♀️🤦🏼‍♂️🤦🏽‍♀️🤦🏽‍♂️🤦🏾‍♀️🤦🏾‍♂️🤦🏿‍♀️🤦🏿‍♂️🤷‍♀️🤷‍♂️🤷🏻‍♀️🤷🏻‍♂️🤷🏼‍♀️🤷🏼‍♂️🤷🏽‍♀️🤷🏽‍♂️🤷🏾‍♀️🤷🏾‍♂️🤷🏿‍♀️🤷🏿‍♂️
+#emoji_male_female_zwj_sequences:
+"Noto Color Emoji" : 476
+
+PASS
+
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/layout-fonts/emoji-cluster-fallback.js b/third_party/WebKit/LayoutTests/inspector-protocol/layout-fonts/emoji-cluster-fallback.js
new file mode 100644
index 0000000..748632a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/layout-fonts/emoji-cluster-fallback.js
@@ -0,0 +1,37 @@
+(async function(testRunner) {
+  var page = await testRunner.createPage();
+  await page.loadHTML(`
+    <html>
+    <meta charset="UTF-8">
+    <style>
+    @font-face {
+    font-family: has_male_female_sign;
+    src: url("../../third_party/Libertine/LinLibertine_R.woff");
+    }
+    @font-face {
+    font-family: noto_color_emoji;
+    src: url("../../third_party/NotoColorEmoji/NotoColorEmoji.ttf");
+    }
+    #emoji_male_female_zwj_sequences {
+    font-family: has_male_female_sign, noto_color_emoji;
+    }
+    </style>
+    <body>
+        <div class="test">
+            <div id="emoji_male_female_zwj_sequences">👮‍♀️👮‍♂️👮🏻‍♀️👮🏻‍♂️👮🏼‍♀️👮🏼‍♂️👮🏽‍♀️👮🏽‍♂️👮🏾‍♀️👮🏾‍♂️👮🏿‍♀️👮🏿‍♂️👱‍♀️👱‍♂️👱🏻‍♀️👱🏻‍♂️👱🏼‍♀️👱🏼‍♂️👱🏽‍♀️👱🏽‍♂️👱🏾‍♀️👱🏾‍♂️👱🏿‍♀️👱🏿‍♂️👳‍♀️👳‍♂️👳🏻‍♀️👳🏻‍♂️👳🏼‍♀️👳🏼‍♂️👳🏽‍♀️👳🏽‍♂️👳🏾‍♀️👳🏾‍♂️👳🏿‍♀️👳🏿‍♂️👷‍♀️👷‍♂️👷🏻‍♀️👷🏻‍♂️👷🏼‍♀️👷🏼‍♂️👷🏽‍♀️👷🏽‍♂️👷🏾‍♀️👷🏾‍♂️👷🏿‍♀️👷🏿‍♂️💂‍♀️💂‍♂️💂🏻‍♀️💂🏻‍♂️💂🏼‍♀️💂🏼‍♂️💂🏽‍♀️💂🏽‍♂️💂🏾‍♀️💂🏾‍♂️💂🏿‍♀️💂🏿‍♂️🕵🏻‍♀️🕵🏻‍♂️🕵🏼‍♀️🕵🏼‍♂️🕵🏽‍♀️🕵🏽‍♂️🕵🏾‍♀️🕵🏾‍♂️🕵🏿‍♀️🕵🏿‍♂️🕵️‍♀️🕵️‍♂️🧙‍♀️🧙‍♂️🧙🏻‍♀️🧙🏻‍♂️🧙🏼‍♀️🧙🏼‍♂️🧙🏽‍♀️🧙🏽‍♂️🧙🏾‍♀️🧙🏾‍♂️🧙🏿‍♀️🧙🏿‍♂️🧚‍♀️🧚‍♂️🧚🏻‍♀️🧚🏻‍♂️🧚🏼‍♀️🧚🏼‍♂️🧚🏽‍♀️🧚🏽‍♂️🧚🏾‍♀️🧚🏾‍♂️🧚🏿‍♀️🧚🏿‍♂️🧛‍♀️🧛‍♂️🧛🏻‍♀️🧛🏻‍♂️🧛🏼‍♀️🧛🏼‍♂️🧛🏽‍♀️🧛🏽‍♂️🧛🏾‍♀️🧛🏾‍♂️🧛🏿‍♀️🧛🏿‍♂️🧜‍♀️🧜‍♂️🧜🏻‍♀️🧜🏻‍♂️🧜🏼‍♀️🧜🏼‍♂️🧜🏽‍♀️🧜🏽‍♂️🧜🏾‍♀️🧜🏾‍♂️🧜🏿‍♀️🧜🏿‍♂️🧝‍♀️🧝‍♂️🧝🏻‍♀️🧝🏻‍♂️🧝🏼‍♀️🧝🏼‍♂️🧝🏽‍♀️🧝🏽‍♂️🧝🏾‍♀️🧝🏾‍♂️🧝🏿‍♀️🧝🏿‍♂️🧞‍♀️🧞‍♂️🧟‍♀️🧟‍♂️⛹🏻‍♀️⛹🏻‍♂️⛹🏼‍♀️⛹🏼‍♂️⛹🏽‍♀️⛹🏽‍♂️⛹🏾‍♀️⛹🏾‍♂️⛹🏿‍♀️⛹🏿‍♂️⛹️‍♀️⛹️‍♂️🏃‍♀️🏃‍♂️🏃🏻‍♀️🏃🏻‍♂️🏃🏼‍♀️🏃🏼‍♂️🏃🏽‍♀️🏃🏽‍♂️🏃🏾‍♀️🏃🏾‍♂️🏃🏿‍♀️🏃🏿‍♂️🏄‍♀️🏄‍♂️🏄🏻‍♀️🏄🏻‍♂️🏄🏼‍♀️🏄🏼‍♂️🏄🏽‍♀️🏄🏽‍♂️🏄🏾‍♀️🏄🏾‍♂️🏄🏿‍♀️🏄🏿‍♂️🏊‍♀️🏊‍♂️🏊🏻‍♀️🏊🏻‍♂️🏊🏼‍♀️🏊🏼‍♂️🏊🏽‍♀️🏊🏽‍♂️🏊🏾‍♀️🏊🏾‍♂️🏊🏿‍♀️🏊🏿‍♂️🏋🏻‍♀️🏋🏻‍♂️🏋🏼‍♀️🏋🏼‍♂️🏋🏽‍♀️🏋🏽‍♂️🏋🏾‍♀️🏋🏾‍♂️🏋🏿‍♀️🏋🏿‍♂️🏋️‍♀️🏋️‍♂️🏌🏻‍♀️🏌🏻‍♂️🏌🏼‍♀️🏌🏼‍♂️🏌🏽‍♀️🏌🏽‍♂️🏌🏾‍♀️🏌🏾‍♂️🏌🏿‍♀️🏌🏿‍♂️🏌️‍♀️🏌️‍♂️👯‍♀️👯‍♂️💆‍♀️💆‍♂️💆🏻‍♀️💆🏻‍♂️💆🏼‍♀️💆🏼‍♂️💆🏽‍♀️💆🏽‍♂️💆🏾‍♀️💆🏾‍♂️💆🏿‍♀️💆🏿‍♂️💇‍♀️💇‍♂️💇🏻‍♀️💇🏻‍♂️💇🏼‍♀️💇🏼‍♂️💇🏽‍♀️💇🏽‍♂️💇🏾‍♀️💇🏾‍♂️💇🏿‍♀️💇🏿‍♂️🚣‍♀️🚣‍♂️🚣🏻‍♀️🚣🏻‍♂️🚣🏼‍♀️🚣🏼‍♂️🚣🏽‍♀️🚣🏽‍♂️🚣🏾‍♀️🚣🏾‍♂️🚣🏿‍♀️🚣🏿‍♂️🚴‍♀️🚴‍♂️🚴🏻‍♀️🚴🏻‍♂️🚴🏼‍♀️🚴🏼‍♂️🚴🏽‍♀️🚴🏽‍♂️🚴🏾‍♀️🚴🏾‍♂️🚴🏿‍♀️🚴🏿‍♂️🚵‍♀️🚵‍♂️🚵🏻‍♀️🚵🏻‍♂️🚵🏼‍♀️🚵🏼‍♂️🚵🏽‍♀️🚵🏽‍♂️🚵🏾‍♀️🚵🏾‍♂️🚵🏿‍♀️🚵🏿‍♂️🚶‍♀️🚶‍♂️🚶🏻‍♀️🚶🏻‍♂️🚶🏼‍♀️🚶🏼‍♂️🚶🏽‍♀️🚶🏽‍♂️🚶🏾‍♀️🚶🏾‍♂️🚶🏿‍♀️🚶🏿‍♂️🤸‍♀️🤸‍♂️🤸🏻‍♀️🤸🏻‍♂️🤸🏼‍♀️🤸🏼‍♂️🤸🏽‍♀️🤸🏽‍♂️🤸🏾‍♀️🤸🏾‍♂️🤸🏿‍♀️🤸🏿‍♂️🤹‍♀️🤹‍♂️🤹🏻‍♀️🤹🏻‍♂️🤹🏼‍♀️🤹🏼‍♂️🤹🏽‍♀️🤹🏽‍♂️🤹🏾‍♀️🤹🏾‍♂️🤹🏿‍♀️🤹🏿‍♂️🤼‍♀️🤼‍♂️🤽‍♀️🤽‍♂️🤽🏻‍♀️🤽🏻‍♂️🤽🏼‍♀️🤽🏼‍♂️🤽🏽‍♀️🤽🏽‍♂️🤽🏾‍♀️🤽🏾‍♂️🤽🏿‍♀️🤽🏿‍♂️🤾‍♀️🤾‍♂️🤾🏻‍♀️🤾🏻‍♂️🤾🏼‍♀️🤾🏼‍♂️🤾🏽‍♀️🤾🏽‍♂️🤾🏾‍♀️🤾🏾‍♂️🤾🏿‍♀️🤾🏿‍♂️🧖‍♀️🧖‍♂️🧖🏻‍♀️🧖🏻‍♂️🧖🏼‍♀️🧖🏼‍♂️🧖🏽‍♀️🧖🏽‍♂️🧖🏾‍♀️🧖🏾‍♂️🧖🏿‍♀️🧖🏿‍♂️🧗‍♀️🧗‍♂️🧗🏻‍♀️🧗🏻‍♂️🧗🏼‍♀️🧗🏼‍♂️🧗🏽‍♀️🧗🏽‍♂️🧗🏾‍♀️🧗🏾‍♂️🧗🏿‍♀️🧗🏿‍♂️🧘‍♀️🧘‍♂️🧘🏻‍♀️🧘🏻‍♂️🧘🏼‍♀️🧘🏼‍♂️🧘🏽‍♀️🧘🏽‍♂️🧘🏾‍♀️🧘🏾‍♂️🧘🏿‍♀️🧘🏿‍♂️💁‍♀️💁‍♂️💁🏻‍♀️💁🏻‍♂️💁🏼‍♀️💁🏼‍♂️💁🏽‍♀️💁🏽‍♂️💁🏾‍♀️💁🏾‍♂️💁🏿‍♀️💁🏿‍♂️🙅‍♀️🙅‍♂️🙅🏻‍♀️🙅🏻‍♂️🙅🏼‍♀️🙅🏼‍♂️🙅🏽‍♀️🙅🏽‍♂️🙅🏾‍♀️🙅🏾‍♂️🙅🏿‍♀️🙅🏿‍♂️🙆‍♀️🙆‍♂️🙆🏻‍♀️🙆🏻‍♂️🙆🏼‍♀️🙆🏼‍♂️🙆🏽‍♀️🙆🏽‍♂️🙆🏾‍♀️🙆🏾‍♂️🙆🏿‍♀️🙆🏿‍♂️🙇‍♀️🙇‍♂️🙇🏻‍♀️🙇🏻‍♂️🙇🏼‍♀️🙇🏼‍♂️🙇🏽‍♀️🙇🏽‍♂️🙇🏾‍♀️🙇🏾‍♂️🙇🏿‍♀️🙇🏿‍♂️🙋‍♀️🙋‍♂️🙋🏻‍♀️🙋🏻‍♂️🙋🏼‍♀️🙋🏼‍♂️🙋🏽‍♀️🙋🏽‍♂️🙋🏾‍♀️🙋🏾‍♂️🙋🏿‍♀️🙋🏿‍♂️🙍‍♀️🙍‍♂️🙍🏻‍♀️🙍🏻‍♂️🙍🏼‍♀️🙍🏼‍♂️🙍🏽‍♀️🙍🏽‍♂️🙍🏾‍♀️🙍🏾‍♂️🙍🏿‍♀️🙍🏿‍♂️🙎‍♀️🙎‍♂️🙎🏻‍♀️🙎🏻‍♂️🙎🏼‍♀️🙎🏼‍♂️🙎🏽‍♀️🙎🏽‍♂️🙎🏾‍♀️🙎🏾‍♂️🙎🏿‍♀️🙎🏿‍♂️🤦‍♀️🤦‍♂️🤦🏻‍♀️🤦🏻‍♂️🤦🏼‍♀️🤦🏼‍♂️🤦🏽‍♀️🤦🏽‍♂️🤦🏾‍♀️🤦🏾‍♂️🤦🏿‍♀️🤦🏿‍♂️🤷‍♀️🤷‍♂️🤷🏻‍♀️🤷🏻‍♂️🤷🏼‍♀️🤷🏼‍♂️🤷🏽‍♀️🤷🏽‍♂️🤷🏾‍♀️🤷🏾‍♂️🤷🏿‍♀️🤷🏿‍♂️</div>
+        </div>
+    </body>
+    </html>
+  `);
+  var session = await page.createSession();
+  testRunner.log('Test passes if emoji are renderered using only Noto Color Emoji, no glyphs from Linux Libertine.');
+  testRunner.log('Note: Emoji rendering output in this text only representation is not equivalent to internal page appearance.');
+
+  var helper = await testRunner.loadScript('./resources/layout-font-test.js');
+  var results = await helper(testRunner, session);
+
+  var emoji_fonts = results.find(x => x.selector === '#emoji_male_female_zwj_sequences').usedFonts;
+  var passed = (emoji_fonts.length === 1 && emoji_fonts[0].familyName.includes('Emoji'));
+  testRunner.log(passed ? 'PASS' : 'FAIL');
+  testRunner.completeTest();
+})
diff --git a/third_party/accessibility_test_framework/BUILD.gn b/third_party/accessibility_test_framework/BUILD.gn
index 89c7801..c41991d 100644
--- a/third_party/accessibility_test_framework/BUILD.gn
+++ b/third_party/accessibility_test_framework/BUILD.gn
@@ -4,7 +4,16 @@
 
 import("//build/config/android/rules.gni")
 
-java_prebuilt("accessibility_test_framework_java") {
+android_java_prebuilt("accessibility_test_framework_java") {
   testonly = true
   jar_path = "lib/accessibility-test-framework.jar"
+  deps = [
+    "//third_party/android_deps:android_support_v4_java",
+    "//third_party/android_deps:com_android_support_support_core_ui_java",
+    "//third_party/hamcrest:hamcrest_core_java",
+  ]
+
+  # TODO(smaier): remove this - crbug.com/898861
+  proguard_configs =
+      [ "//third_party/accessibility_test_framework/proguard.flags" ]
 }
diff --git a/third_party/accessibility_test_framework/README.chromium b/third_party/accessibility_test_framework/README.chromium
index 4732f64..df00e356 100644
--- a/third_party/accessibility_test_framework/README.chromium
+++ b/third_party/accessibility_test_framework/README.chromium
@@ -7,4 +7,6 @@
 Description:
 Library that contains accessibility-related checks for Android View and
 AccessibilityNodeInfo objects.
-Local Modifications: None
+Local Modifications:
+ - Added proguard.flags to suppress warnings about this library using protobuf
+   instead of protolite.
diff --git a/third_party/accessibility_test_framework/proguard.flags b/third_party/accessibility_test_framework/proguard.flags
new file mode 100644
index 0000000..c6b5e18b8
--- /dev/null
+++ b/third_party/accessibility_test_framework/proguard.flags
@@ -0,0 +1,2 @@
+-dontwarn com.google.protobuf.**
+-dontwarn com.google.android.apps.common.testing.accessibility.framework.proto.FrameworkProtos*
diff --git a/third_party/android_support_test_runner/BUILD.gn b/third_party/android_support_test_runner/BUILD.gn
index 34fdc78..71ff270 100644
--- a/third_party/android_support_test_runner/BUILD.gn
+++ b/third_party/android_support_test_runner/BUILD.gn
@@ -9,6 +9,7 @@
   jar_path = "lib/runner-release-no-dep.jar"
   deps = [
     ":exposed_instrumentation_api_publish_java",
+    "//third_party/android_tools:android_test_base_java",
     "//third_party/guava:guava_android_java",
     "//third_party/junit",
   ]
@@ -21,4 +22,7 @@
 android_aar_prebuilt("rules_java") {
   testonly = true
   aar_path = "lib/rules.aar"
+  deps = [
+    "//third_party/android_support_test_runner:runner_java",
+  ]
 }
diff --git a/third_party/blink/common/BUILD.gn b/third_party/blink/common/BUILD.gn
index 4a9d87c..bd89ad6a 100644
--- a/third_party/blink/common/BUILD.gn
+++ b/third_party/blink/common/BUILD.gn
@@ -37,7 +37,7 @@
     "indexeddb/indexeddb_key_path.cc",
     "indexeddb/indexeddb_key_range.cc",
     "indexeddb/indexeddb_metadata.cc",
-    "indexeddb/indexeddb_struct_traits.cc",
+    "indexeddb/indexeddb_mojom_traits.cc",
     "manifest/manifest.cc",
     "manifest/manifest_icon_selector.cc",
     "messaging/cloneable_message.cc",
diff --git a/third_party/blink/common/indexeddb/OWNERS b/third_party/blink/common/indexeddb/OWNERS
index 3b5f7f5..4d50d63 100644
--- a/third_party/blink/common/indexeddb/OWNERS
+++ b/third_party/blink/common/indexeddb/OWNERS
@@ -1,7 +1,7 @@
 file://content/browser/indexed_db/OWNERS
 
-per-file *_struct_traits*.*=set noparent
-per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
 per-file *.typemap=set noparent
 per-file *.typemap=file://ipc/SECURITY_OWNERS
 
diff --git a/third_party/blink/common/indexeddb/indexeddb_key.cc b/third_party/blink/common/indexeddb/indexeddb_key.cc
index 9f5e6fe2..022e1f2f 100644
--- a/third_party/blink/common/indexeddb/indexeddb_key.cc
+++ b/third_party/blink/common/indexeddb/indexeddb_key.cc
@@ -23,10 +23,10 @@
 // Very rough estimate of minimum key size overhead.
 const size_t kOverheadSize = 16;
 
-static size_t CalculateArraySize(const IndexedDBKey::KeyArray& keys) {
+size_t CalculateArraySize(const IndexedDBKey::KeyArray& keys) {
   size_t size(0);
-  for (size_t i = 0; i < keys.size(); ++i)
-    size += keys[i].size_estimate();
+  for (const auto& key : keys)
+    size += key.size_estimate();
   return size;
 }
 
diff --git a/third_party/blink/common/indexeddb/indexeddb_struct_traits.cc b/third_party/blink/common/indexeddb/indexeddb_mojom_traits.cc
similarity index 99%
rename from third_party/blink/common/indexeddb/indexeddb_struct_traits.cc
rename to third_party/blink/common/indexeddb/indexeddb_mojom_traits.cc
index d775ecd..988aca94 100644
--- a/third_party/blink/common/indexeddb/indexeddb_struct_traits.cc
+++ b/third_party/blink/common/indexeddb/indexeddb_mojom_traits.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/public/common/indexeddb/indexeddb_struct_traits.h"
+#include "third_party/blink/public/common/indexeddb/indexeddb_mojom_traits.h"
 
 #include "base/stl_util.h"
 #include "mojo/public/cpp/base/string16_mojom_traits.h"
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 991ed52..a3afaa34 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -127,13 +127,11 @@
     "platform/mac/web_sandbox_support.h",
     "platform/mac/web_scrollbar_theme.h",
     "platform/modules/background_fetch/web_background_fetch_registration.h",
+    "platform/modules/indexeddb/indexed_db_key_builder.h",
     "platform/modules/indexeddb/web_idb_callbacks.h",
-    "platform/modules/indexeddb/web_idb_cursor.h",
-    "platform/modules/indexeddb/web_idb_database.h",
     "platform/modules/indexeddb/web_idb_database_callbacks.h",
     "platform/modules/indexeddb/web_idb_database_error.h",
     "platform/modules/indexeddb/web_idb_database_exception.h",
-    "platform/modules/indexeddb/web_idb_factory.h",
     "platform/modules/indexeddb/web_idb_key.h",
     "platform/modules/indexeddb/web_idb_key_path.h",
     "platform/modules/indexeddb/web_idb_key_range.h",
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index 37bbcb45..7bae861b 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -15,7 +15,7 @@
 
   public_deps = [
     "//third_party/blink/public/common:headers",
-    "//third_party/blink/public/mojom:mojom_core",
+    "//third_party/blink/public/mojom:mojom_modules",
   ]
   deps = [
     "//third_party/blink/common",
@@ -54,7 +54,7 @@
     "indexeddb/indexeddb_key_path.h",
     "indexeddb/indexeddb_key_range.h",
     "indexeddb/indexeddb_metadata.h",
-    "indexeddb/indexeddb_struct_traits.h",
+    "indexeddb/indexeddb_mojom_traits.h",
     "indexeddb/web_idb_types.h",
     "manifest/manifest.h",
     "manifest/manifest_icon_selector.h",
@@ -85,7 +85,7 @@
 
   public_deps = [
     "//skia",
-    "//third_party/blink/public/mojom:mojom_core_headers",
+    "//third_party/blink/public/mojom:mojom_modules_headers",
   ]
 
   deps = [
diff --git a/third_party/blink/public/common/indexeddb/OWNERS b/third_party/blink/public/common/indexeddb/OWNERS
index 3b5f7f5..4d50d63 100644
--- a/third_party/blink/public/common/indexeddb/OWNERS
+++ b/third_party/blink/public/common/indexeddb/OWNERS
@@ -1,7 +1,7 @@
 file://content/browser/indexed_db/OWNERS
 
-per-file *_struct_traits*.*=set noparent
-per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
 per-file *.typemap=set noparent
 per-file *.typemap=file://ipc/SECURITY_OWNERS
 
diff --git a/third_party/blink/public/common/indexeddb/indexeddb.typemap b/third_party/blink/public/common/indexeddb/indexeddb.typemap
index cc75494..f46637a 100644
--- a/third_party/blink/public/common/indexeddb/indexeddb.typemap
+++ b/third_party/blink/public/common/indexeddb/indexeddb.typemap
@@ -12,20 +12,20 @@
 ]
 traits_headers = [
   "//mojo/public/cpp/bindings/array_traits_span.h",
-  "//third_party/blink/public/common/indexeddb/indexeddb_struct_traits.h",
+  "//third_party/blink/public/common/indexeddb/indexeddb_mojom_traits.h",
 ]
 type_mappings = [
-  "blink.mojom.IDBCursorDirection=blink::WebIDBCursorDirection",
-  "blink.mojom.IDBDataLoss=blink::WebIDBDataLoss",
-  "blink.mojom.IDBDatabaseMetadata=blink::IndexedDBDatabaseMetadata",
-  "blink.mojom.IDBIndexKeys=blink::IndexedDBIndexKeys",
-  "blink.mojom.IDBIndexMetadata=blink::IndexedDBIndexMetadata",
-  "blink.mojom.IDBKey=blink::IndexedDBKey",
-  "blink.mojom.IDBKeyPath=blink::IndexedDBKeyPath",
-  "blink.mojom.IDBKeyRange=blink::IndexedDBKeyRange",
-  "blink.mojom.IDBObjectStoreMetadata=blink::IndexedDBObjectStoreMetadata",
-  "blink.mojom.IDBOperationType=blink::WebIDBOperationType",
-  "blink.mojom.IDBPutMode=blink::WebIDBPutMode",
-  "blink.mojom.IDBTaskType=blink::WebIDBTaskType",
-  "blink.mojom.IDBTransactionMode=blink::WebIDBTransactionMode",
+  "blink.mojom.IDBCursorDirection=::blink::WebIDBCursorDirection",
+  "blink.mojom.IDBDataLoss=::blink::WebIDBDataLoss",
+  "blink.mojom.IDBDatabaseMetadata=::blink::IndexedDBDatabaseMetadata",
+  "blink.mojom.IDBIndexKeys=::blink::IndexedDBIndexKeys",
+  "blink.mojom.IDBIndexMetadata=::blink::IndexedDBIndexMetadata",
+  "blink.mojom.IDBKey=::blink::IndexedDBKey",
+  "blink.mojom.IDBKeyPath=::blink::IndexedDBKeyPath",
+  "blink.mojom.IDBKeyRange=::blink::IndexedDBKeyRange",
+  "blink.mojom.IDBObjectStoreMetadata=::blink::IndexedDBObjectStoreMetadata",
+  "blink.mojom.IDBOperationType=::blink::WebIDBOperationType",
+  "blink.mojom.IDBPutMode=::blink::WebIDBPutMode",
+  "blink.mojom.IDBTaskType=::blink::WebIDBTaskType",
+  "blink.mojom.IDBTransactionMode=::blink::WebIDBTransactionMode",
 ]
diff --git a/third_party/blink/public/common/indexeddb/indexeddb_struct_traits.h b/third_party/blink/public/common/indexeddb/indexeddb_mojom_traits.h
similarity index 94%
rename from third_party/blink/public/common/indexeddb/indexeddb_struct_traits.h
rename to third_party/blink/public/common/indexeddb/indexeddb_mojom_traits.h
index 39568f9..aaf49b2 100644
--- a/third_party/blink/public/common/indexeddb/indexeddb_struct_traits.h
+++ b/third_party/blink/public/common/indexeddb/indexeddb_mojom_traits.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_INDEXEDDB_INDEXEDDB_STRUCT_TRAITS_H_
-#define THIRD_PARTY_BLINK_PUBLIC_COMMON_INDEXEDDB_INDEXEDDB_STRUCT_TRAITS_H_
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_INDEXEDDB_INDEXEDDB_MOJOM_TRAITS_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_INDEXEDDB_INDEXEDDB_MOJOM_TRAITS_H_
 
 #include "base/containers/span.h"
 #include "third_party/blink/common/common_export.h"
@@ -36,7 +36,8 @@
   static int64_t id(const blink::IndexedDBDatabaseMetadata& metadata) {
     return metadata.id;
   }
-  static base::string16 name(const blink::IndexedDBDatabaseMetadata& metadata) {
+  static const base::string16& name(
+      const blink::IndexedDBDatabaseMetadata& metadata) {
     return metadata.name;
   }
   static int64_t version(const blink::IndexedDBDatabaseMetadata& metadata) {
@@ -74,7 +75,8 @@
   static int64_t id(const blink::IndexedDBIndexMetadata& metadata) {
     return metadata.id;
   }
-  static base::string16 name(const blink::IndexedDBIndexMetadata& metadata) {
+  static const base::string16& name(
+      const blink::IndexedDBIndexMetadata& metadata) {
     return metadata.name;
   }
   static const blink::IndexedDBKeyPath& key_path(
@@ -170,7 +172,7 @@
   static int64_t id(const blink::IndexedDBObjectStoreMetadata& metadata) {
     return metadata.id;
   }
-  static base::string16 name(
+  static const base::string16& name(
       const blink::IndexedDBObjectStoreMetadata& metadata) {
     return metadata.name;
   }
@@ -230,4 +232,4 @@
 
 }  // namespace mojo
 
-#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_INDEXEDDB_INDEXEDDB_STRUCT_TRAITS_H_
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_INDEXEDDB_INDEXEDDB_MOJOM_TRAITS_H_
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index b03d6a4..f61abec7 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -5,7 +5,7 @@
 import("//mojo/public/tools/bindings/mojom.gni")
 
 # This target includes all mojom interfaces which can be used from
-# Source/platform. In particular these mojom interfaces can't use types that
+# renderer/platform. In particular these mojom interfaces can't use types that
 # are typemapped to a type in Source/core.
 mojom("mojom_platform") {
   sources = [
@@ -31,7 +31,6 @@
     "filesystem/file_writer.mojom",
     "frame/find_in_page.mojom",
     "frame/navigation_initiator.mojom",
-    "indexeddb/indexeddb.mojom",
     "leak_detector/leak_detector.mojom",
     "loader/code_cache.mojom",
     "loader/navigation_predictor.mojom",
@@ -122,8 +121,8 @@
 }
 
 # This target can include mojom interfaces which do use types that are
-# typemapped to a type in Source/core. This also means these interfaces are not
-# available from Source/platform.
+# typemapped to a type in renderer/core. This also means these interfaces are
+# not available from renderer/platform.
 # Note that service_worker_object.mojom and service_worker.mojom depend
 # on transferable_message.mojom, and service_worker_registration.mojom depends
 # on service_worker_object.mojom, so we put these three service worker
@@ -160,3 +159,38 @@
   export_define_blink = "BLINK_CORE_IMPLEMENTATION=1"
   export_header_blink = "third_party/blink/renderer/core/core_export.h"
 }
+
+# This target can include mojom interfaces which use types that are typemapped
+# to a type in renderer/modules. This also means these interfaces are not
+# available from renderer/platform or renderer/core.
+mojom("mojom_modules") {
+  sources = [
+    "indexeddb/indexeddb.mojom",
+  ]
+
+  public_deps = [
+    ":mojom_core",
+
+    # TODO(https://crbug.com/822804): Remove when mojom bindings deps checks
+    # get fixed.
+    ":mojom_platform",
+    "//mojo/public/mojom/base",
+    "//skia/public/interfaces",
+    "//url/mojom:url_mojom_gurl",
+    "//url/mojom:url_mojom_origin",
+  ]
+
+  overridden_deps_blink = [
+    ":mojom_core",
+    ":mojom_platform",
+  ]
+  component_deps_blink = [ "//third_party/blink/renderer/core" ]
+
+  export_class_attribute = "BLINK_COMMON_EXPORT"
+  export_define = "BLINK_COMMON_IMPLEMENTATION=1"
+  export_header = "third_party/blink/common/common_export.h"
+
+  export_class_attribute_blink = "MODULES_EXPORT"
+  export_define_blink = "BLINK_MODULES_IMPLEMENTATION=1"
+  export_header_blink = "third_party/blink/renderer/modules/modules_export.h"
+}
diff --git a/third_party/blink/public/platform/modules/indexeddb/indexed_db_key_builder.h b/third_party/blink/public/platform/modules/indexeddb/indexed_db_key_builder.h
new file mode 100644
index 0000000..28a5e8f
--- /dev/null
+++ b/third_party/blink/public/platform/modules/indexeddb/indexed_db_key_builder.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INDEXEDDB_INDEXED_DB_KEY_BUILDER_H_
+#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INDEXEDDB_INDEXED_DB_KEY_BUILDER_H_
+
+#include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
+#include "third_party/blink/public/common/indexeddb/indexeddb_key_path.h"
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_key.h"
+#include "third_party/blink/public/platform/web_common.h"
+
+namespace blink {
+
+class IndexedDBKeyRange;
+class WebIDBKeyPath;
+class WebIDBKeyRange;
+
+class BLINK_EXPORT IndexedDBKeyBuilder {
+ public:
+  static IndexedDBKey Build(WebIDBKeyView key);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(IndexedDBKeyBuilder);
+};
+
+class BLINK_EXPORT WebIDBKeyBuilder {
+ public:
+  static WebIDBKey Build(const IndexedDBKey& key);
+  static WebIDBKey Build(const WebIDBKeyView& key);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WebIDBKeyBuilder);
+};
+
+class BLINK_EXPORT IndexedDBKeyRangeBuilder {
+ public:
+  static IndexedDBKeyRange Build(const WebIDBKeyRange& key_range);
+
+  // Builds a point range (containing a single key).
+  static IndexedDBKeyRange Build(WebIDBKeyView key);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(IndexedDBKeyRangeBuilder);
+};
+
+class BLINK_EXPORT WebIDBKeyRangeBuilder {
+ public:
+  static WebIDBKeyRange Build(const IndexedDBKeyRange& key);
+
+  // Builds a point range (containing a single key).
+  static WebIDBKeyRange Build(WebIDBKeyView key);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WebIDBKeyRangeBuilder);
+};
+
+class BLINK_EXPORT IndexedDBKeyPathBuilder {
+ public:
+  static IndexedDBKeyPath Build(const WebIDBKeyPath& key_path);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(IndexedDBKeyPathBuilder);
+};
+
+class BLINK_EXPORT WebIDBKeyPathBuilder {
+ public:
+  static WebIDBKeyPath Build(const IndexedDBKeyPath& key_path);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WebIDBKeyPathBuilder);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INDEXEDDB_INDEXED_DB_KEY_BUILDER_H_
diff --git a/third_party/blink/public/platform/modules/indexeddb/web_idb_database_callbacks.h b/third_party/blink/public/platform/modules/indexeddb/web_idb_database_callbacks.h
index 2343315..dd8b27d 100644
--- a/third_party/blink/public/platform/modules/indexeddb/web_idb_database_callbacks.h
+++ b/third_party/blink/public/platform/modules/indexeddb/web_idb_database_callbacks.h
@@ -40,11 +40,11 @@
 
 class WebIDBDatabaseCallbacks {
  public:
-  using ObservationIndexMap = std::unordered_map<int32_t, std::vector<int32_t>>;
+  using ObservationIndexMap = std::unordered_map<int32_t, WebVector<int32_t>>;
 
   // Maps observer to transaction, which needs an id and a scope.
   using TransactionMap =
-      std::unordered_map<int32_t, std::pair<int64_t, std::vector<int64_t>>>;
+      std::unordered_map<int32_t, std::pair<int64_t, WebVector<int64_t>>>;
 
   virtual ~WebIDBDatabaseCallbacks() = default;
 
@@ -56,8 +56,8 @@
                        const WebIDBDatabaseError&) = 0;
   virtual void OnComplete(long long transaction_id) = 0;
   virtual void OnChanges(const ObservationIndexMap&,
-                         WebVector<WebIDBObservation> observations,
-                         const TransactionMap& transactions) = 0;
+                         WebVector<WebIDBObservation>,
+                         const TransactionMap&) = 0;
   virtual void Detach() = 0;
 };
 
diff --git a/third_party/blink/public/platform/modules/indexeddb/web_idb_key.h b/third_party/blink/public/platform/modules/indexeddb/web_idb_key.h
index cdacdfe..7255f7d 100644
--- a/third_party/blink/public/platform/modules/indexeddb/web_idb_key.h
+++ b/third_party/blink/public/platform/modules/indexeddb/web_idb_key.h
@@ -62,7 +62,7 @@
 // The Blink object wrapped by WebIDBKey is immutable, so WebIDBKeyView
 // instances are implicitly const references.
 //
-// Having both WebIDBKeyView and WebIDBView is extra complexity, and we pay this
+// Having both WebIDBKeyView and WebIDBKey is extra complexity, and we pay this
 // price to avoid unnecessary memory copying. Specifically, WebIDBKeyView is
 // used to issue requests to the IndexedDB backing store.
 //
@@ -99,6 +99,19 @@
   // Only valid for NumberType.
   BLINK_EXPORT double Number() const;
 
+  BLINK_EXPORT size_t SizeEstimate() const;
+
+  // TODO(cmp): Ensure |private_| can never be null.
+  //
+  // SizeEstimate() can be called when |private_| is null.  That happens if and
+  // only if the |WebIDBKey| instance is created using WebIDBKey::CreateNull().
+  //
+  // Eventually, WebIDBKey::CreateNull() will change so that case will lead to
+  // a non-null |private_|.  At that time, this null check can change to a
+  // DCHECK that |private_| is not null and the special null case handling can
+  // be removed.
+  BLINK_EXPORT bool IsNull() const { return !private_; }
+
  private:
   const IDBKey* const private_;
 };
@@ -124,18 +137,25 @@
   BLINK_EXPORT static WebIDBKey CreateNull() noexcept { return WebIDBKey(); }
 
   // The default constructor must not be used explicitly.
-  // It is only provided for WebVector's use.
+  // It is only provided for WebVector and Mojo's use.
   BLINK_EXPORT WebIDBKey() noexcept;
 
   BLINK_EXPORT WebIDBKey(WebIDBKey&&) noexcept;
   BLINK_EXPORT WebIDBKey& operator=(WebIDBKey&&) noexcept;
 
+  // TODO(cmp): Remove copy and assignment constructors when Vector<->WebVector
+  //            conversions are no longer needed.
+  BLINK_EXPORT WebIDBKey(const WebIDBKey& rkey);
+  BLINK_EXPORT WebIDBKey& operator=(const WebIDBKey& rkey);
+
   BLINK_EXPORT ~WebIDBKey();
 
   BLINK_EXPORT WebIDBKeyView View() const {
     return WebIDBKeyView(private_.get());
   }
 
+  BLINK_EXPORT size_t SizeEstimate() const { return View().SizeEstimate(); }
+
 #if INSIDE_BLINK
   explicit WebIDBKey(std::unique_ptr<IDBKey>) noexcept;
   WebIDBKey& operator=(std::unique_ptr<IDBKey>) noexcept;
@@ -147,11 +167,6 @@
 #endif  // INSIDE_BLINK
 
  private:
-  // WebIDBKey has to be move-only, because std::unique_ptr is move-only. Making
-  // the restriction explicit results in slightly better compilation error
-  // messages in code that attempts copying.
-  WebIDBKey(const WebIDBKey&) = delete;
-  WebIDBKey& operator=(const WebIDBKey&) = delete;
 
   std::unique_ptr<IDBKey> private_;
 };
diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h
index 71666c92..2906462 100644
--- a/third_party/blink/public/platform/platform.h
+++ b/third_party/blink/public/platform/platform.h
@@ -50,7 +50,6 @@
 #include "third_party/blink/public/mojom/loader/code_cache.mojom-shared.h"
 #include "third_party/blink/public/platform/blame_context.h"
 #include "third_party/blink/public/platform/code_cache_loader.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_factory.h"
 #include "third_party/blink/public/platform/user_metrics_action.h"
 #include "third_party/blink/public/platform/web_audio_device.h"
 #include "third_party/blink/public/platform/web_common.h"
@@ -295,11 +294,6 @@
 
   virtual WebString ConvertIDNToUnicode(const WebString& host) { return host; }
 
-  // IndexedDB ----------------------------------------------------------
-
-  // Must return non-null.
-  virtual std::unique_ptr<WebIDBFactory> CreateIdbFactory() { return nullptr; }
-
   // History -------------------------------------------------------------
 
   // Returns the hash for the given canonicalized URL for use in visited
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index 100a203..7e0b1c5 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -64,6 +64,8 @@
 
   BLINK_PLATFORM_EXPORT static bool IsBlinkGenPropertyTreesEnabled();
 
+  BLINK_PLATFORM_EXPORT static bool IsFractionalScrollOffsetsEnabled();
+
   BLINK_PLATFORM_EXPORT static void EnableCompositedSelectionUpdate(bool);
   BLINK_PLATFORM_EXPORT static bool IsCompositedSelectionUpdateEnabled();
 
diff --git a/third_party/blink/renderer/bindings/core/v8/idl_types.h b/third_party/blink/renderer/bindings/core/v8/idl_types.h
index a1c7d072..b99256d 100644
--- a/third_party/blink/renderer/bindings/core/v8/idl_types.h
+++ b/third_party/blink/renderer/bindings/core/v8/idl_types.h
@@ -31,6 +31,27 @@
 struct IDLLongLong final : public IDLBaseHelper<int64_t> {};
 struct IDLUnsignedLongLong final : public IDLBaseHelper<uint64_t> {};
 
+// [Clamp] Integers
+struct IDLByteClamp final : public IDLBaseHelper<int8_t> {};
+struct IDLOctetClamp final : public IDLBaseHelper<uint8_t> {};
+struct IDLShortClamp final : public IDLBaseHelper<int16_t> {};
+struct IDLUnsignedShortClamp final : public IDLBaseHelper<uint16_t> {};
+struct IDLLongClamp final : public IDLBaseHelper<int32_t> {};
+struct IDLUnsignedLongClamp final : public IDLBaseHelper<uint32_t> {};
+struct IDLLongLongClamp final : public IDLBaseHelper<int64_t> {};
+struct IDLUnsignedLongLongClamp final : public IDLBaseHelper<uint64_t> {};
+
+// [EnforceRange] Integers
+struct IDLByteEnforceRange final : public IDLBaseHelper<int8_t> {};
+struct IDLOctetEnforceRange final : public IDLBaseHelper<uint8_t> {};
+struct IDLShortEnforceRange final : public IDLBaseHelper<int16_t> {};
+struct IDLUnsignedShortEnforceRange final : public IDLBaseHelper<uint16_t> {};
+struct IDLLongEnforceRange final : public IDLBaseHelper<int32_t> {};
+struct IDLUnsignedLongEnforceRange final : public IDLBaseHelper<uint32_t> {};
+struct IDLLongLongEnforceRange final : public IDLBaseHelper<int64_t> {};
+struct IDLUnsignedLongLongEnforceRange final : public IDLBaseHelper<uint64_t> {
+};
+
 // Strings
 // The "Base" classes are always templatized and require users to specify how JS
 // null and/or undefined are supposed to be handled.
@@ -46,6 +67,18 @@
 using IDLString = IDLStringBase<V8StringResourceMode::kDefaultMode>;
 using IDLUSVString = IDLUSVStringBase<V8StringResourceMode::kDefaultMode>;
 
+// Nullable strings
+using IDLByteStringOrNull =
+    IDLByteStringBase<V8StringResourceMode::kTreatNullAndUndefinedAsNullString>;
+using IDLStringOrNull =
+    IDLStringBase<V8StringResourceMode::kTreatNullAndUndefinedAsNullString>;
+using IDLUSVStringOrNull =
+    IDLUSVStringBase<V8StringResourceMode::kTreatNullAndUndefinedAsNullString>;
+
+// [TreatNullAs] Strings
+using IDLStringTreatNullAsEmptyString =
+    IDLStringBase<V8StringResourceMode::kTreatNullAsEmptyString>;
+
 // Double
 struct IDLDouble final : public IDLBaseHelper<double> {};
 struct IDLUnrestrictedDouble final : public IDLBaseHelper<double> {};
diff --git a/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h b/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h
index f50507024..d16edab4 100644
--- a/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h
+++ b/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h
@@ -39,14 +39,7 @@
   static int8_t NativeValue(v8::Isolate* isolate,
                             v8::Local<v8::Value> value,
                             ExceptionState& exception_state) {
-    return NativeValue(isolate, value, exception_state, kNormalConversion);
-  }
-
-  static int8_t NativeValue(v8::Isolate* isolate,
-                            v8::Local<v8::Value> value,
-                            ExceptionState& exception_state,
-                            IntegerConversionConfiguration conversion_mode) {
-    return ToInt8(isolate, value, conversion_mode, exception_state);
+    return ToInt8(isolate, value, kNormalConversion, exception_state);
   }
 };
 
@@ -56,14 +49,7 @@
   static uint8_t NativeValue(v8::Isolate* isolate,
                              v8::Local<v8::Value> value,
                              ExceptionState& exception_state) {
-    return NativeValue(isolate, value, exception_state, kNormalConversion);
-  }
-
-  static uint8_t NativeValue(v8::Isolate* isolate,
-                             v8::Local<v8::Value> value,
-                             ExceptionState& exception_state,
-                             IntegerConversionConfiguration conversion_mode) {
-    return ToUInt8(isolate, value, conversion_mode, exception_state);
+    return ToUInt8(isolate, value, kNormalConversion, exception_state);
   }
 };
 
@@ -73,14 +59,7 @@
   static int16_t NativeValue(v8::Isolate* isolate,
                              v8::Local<v8::Value> value,
                              ExceptionState& exception_state) {
-    return NativeValue(isolate, value, exception_state, kNormalConversion);
-  }
-
-  static int16_t NativeValue(v8::Isolate* isolate,
-                             v8::Local<v8::Value> value,
-                             ExceptionState& exception_state,
-                             IntegerConversionConfiguration conversion_mode) {
-    return ToInt16(isolate, value, conversion_mode, exception_state);
+    return ToInt16(isolate, value, kNormalConversion, exception_state);
   }
 };
 
@@ -90,14 +69,7 @@
   static uint16_t NativeValue(v8::Isolate* isolate,
                               v8::Local<v8::Value> value,
                               ExceptionState& exception_state) {
-    return NativeValue(isolate, value, exception_state, kNormalConversion);
-  }
-
-  static uint16_t NativeValue(v8::Isolate* isolate,
-                              v8::Local<v8::Value> value,
-                              ExceptionState& exception_state,
-                              IntegerConversionConfiguration conversion_mode) {
-    return ToUInt16(isolate, value, conversion_mode, exception_state);
+    return ToUInt16(isolate, value, kNormalConversion, exception_state);
   }
 };
 
@@ -107,14 +79,7 @@
   static int32_t NativeValue(v8::Isolate* isolate,
                              v8::Local<v8::Value> value,
                              ExceptionState& exception_state) {
-    return NativeValue(isolate, value, exception_state, kNormalConversion);
-  }
-
-  static int32_t NativeValue(v8::Isolate* isolate,
-                             v8::Local<v8::Value> value,
-                             ExceptionState& exception_state,
-                             IntegerConversionConfiguration conversion_mode) {
-    return ToInt32(isolate, value, conversion_mode, exception_state);
+    return ToInt32(isolate, value, kNormalConversion, exception_state);
   }
 };
 
@@ -124,14 +89,7 @@
   static uint32_t NativeValue(v8::Isolate* isolate,
                               v8::Local<v8::Value> value,
                               ExceptionState& exception_state) {
-    return NativeValue(isolate, value, exception_state, kNormalConversion);
-  }
-
-  static uint32_t NativeValue(v8::Isolate* isolate,
-                              v8::Local<v8::Value> value,
-                              ExceptionState& exception_state,
-                              IntegerConversionConfiguration conversion_mode) {
-    return ToUInt32(isolate, value, conversion_mode, exception_state);
+    return ToUInt32(isolate, value, kNormalConversion, exception_state);
   }
 };
 
@@ -141,14 +99,7 @@
   static int64_t NativeValue(v8::Isolate* isolate,
                              v8::Local<v8::Value> value,
                              ExceptionState& exception_state) {
-    return NativeValue(isolate, value, exception_state, kNormalConversion);
-  }
-
-  static int64_t NativeValue(v8::Isolate* isolate,
-                             v8::Local<v8::Value> value,
-                             ExceptionState& exception_state,
-                             IntegerConversionConfiguration conversion_mode) {
-    return ToInt64(isolate, value, conversion_mode, exception_state);
+    return ToInt64(isolate, value, kNormalConversion, exception_state);
   }
 };
 
@@ -158,14 +109,169 @@
   static uint64_t NativeValue(v8::Isolate* isolate,
                               v8::Local<v8::Value> value,
                               ExceptionState& exception_state) {
-    return NativeValue(isolate, value, exception_state, kNormalConversion);
+    return ToUInt64(isolate, value, kNormalConversion, exception_state);
   }
+};
 
+// [Clamp] Integers
+template <>
+struct CORE_EXPORT NativeValueTraits<IDLByteClamp>
+    : public NativeValueTraitsBase<IDLByte> {
+  static int8_t NativeValue(v8::Isolate* isolate,
+                            v8::Local<v8::Value> value,
+                            ExceptionState& exception_state) {
+    return ToInt8(isolate, value, kClamp, exception_state);
+  }
+};
+
+template <>
+struct CORE_EXPORT NativeValueTraits<IDLOctetClamp>
+    : public NativeValueTraitsBase<IDLOctet> {
+  static uint8_t NativeValue(v8::Isolate* isolate,
+                             v8::Local<v8::Value> value,
+                             ExceptionState& exception_state) {
+    return ToUInt8(isolate, value, kClamp, exception_state);
+  }
+};
+
+template <>
+struct CORE_EXPORT NativeValueTraits<IDLShortClamp>
+    : public NativeValueTraitsBase<IDLShort> {
+  static int16_t NativeValue(v8::Isolate* isolate,
+                             v8::Local<v8::Value> value,
+                             ExceptionState& exception_state) {
+    return ToInt16(isolate, value, kClamp, exception_state);
+  }
+};
+
+template <>
+struct CORE_EXPORT NativeValueTraits<IDLUnsignedShortClamp>
+    : public NativeValueTraitsBase<IDLUnsignedShort> {
+  static uint16_t NativeValue(v8::Isolate* isolate,
+                              v8::Local<v8::Value> value,
+                              ExceptionState& exception_state) {
+    return ToUInt16(isolate, value, kClamp, exception_state);
+  }
+};
+
+template <>
+struct CORE_EXPORT NativeValueTraits<IDLLongClamp>
+    : public NativeValueTraitsBase<IDLLong> {
+  static int32_t NativeValue(v8::Isolate* isolate,
+                             v8::Local<v8::Value> value,
+                             ExceptionState& exception_state) {
+    return ToInt32(isolate, value, kClamp, exception_state);
+  }
+};
+
+template <>
+struct CORE_EXPORT NativeValueTraits<IDLUnsignedLongClamp>
+    : public NativeValueTraitsBase<IDLUnsignedLong> {
+  static uint32_t NativeValue(v8::Isolate* isolate,
+                              v8::Local<v8::Value> value,
+                              ExceptionState& exception_state) {
+    return ToUInt32(isolate, value, kClamp, exception_state);
+  }
+};
+
+template <>
+struct CORE_EXPORT NativeValueTraits<IDLLongLongClamp>
+    : public NativeValueTraitsBase<IDLLongLong> {
+  static int64_t NativeValue(v8::Isolate* isolate,
+                             v8::Local<v8::Value> value,
+                             ExceptionState& exception_state) {
+    return ToInt64(isolate, value, kClamp, exception_state);
+  }
+};
+
+template <>
+struct CORE_EXPORT NativeValueTraits<IDLUnsignedLongLongClamp>
+    : public NativeValueTraitsBase<IDLUnsignedLongLong> {
   static uint64_t NativeValue(v8::Isolate* isolate,
                               v8::Local<v8::Value> value,
-                              ExceptionState& exception_state,
-                              IntegerConversionConfiguration conversion_mode) {
-    return ToUInt64(isolate, value, conversion_mode, exception_state);
+                              ExceptionState& exception_state) {
+    return ToUInt64(isolate, value, kClamp, exception_state);
+  }
+};
+
+// [EnforceRange] Integers
+template <>
+struct CORE_EXPORT NativeValueTraits<IDLByteEnforceRange>
+    : public NativeValueTraitsBase<IDLByte> {
+  static int8_t NativeValue(v8::Isolate* isolate,
+                            v8::Local<v8::Value> value,
+                            ExceptionState& exception_state) {
+    return ToInt8(isolate, value, kEnforceRange, exception_state);
+  }
+};
+
+template <>
+struct CORE_EXPORT NativeValueTraits<IDLOctetEnforceRange>
+    : public NativeValueTraitsBase<IDLOctet> {
+  static uint8_t NativeValue(v8::Isolate* isolate,
+                             v8::Local<v8::Value> value,
+                             ExceptionState& exception_state) {
+    return ToUInt8(isolate, value, kEnforceRange, exception_state);
+  }
+};
+
+template <>
+struct CORE_EXPORT NativeValueTraits<IDLShortEnforceRange>
+    : public NativeValueTraitsBase<IDLShort> {
+  static int16_t NativeValue(v8::Isolate* isolate,
+                             v8::Local<v8::Value> value,
+                             ExceptionState& exception_state) {
+    return ToInt16(isolate, value, kEnforceRange, exception_state);
+  }
+};
+
+template <>
+struct CORE_EXPORT NativeValueTraits<IDLUnsignedShortEnforceRange>
+    : public NativeValueTraitsBase<IDLUnsignedShort> {
+  static uint16_t NativeValue(v8::Isolate* isolate,
+                              v8::Local<v8::Value> value,
+                              ExceptionState& exception_state) {
+    return ToUInt16(isolate, value, kEnforceRange, exception_state);
+  }
+};
+
+template <>
+struct CORE_EXPORT NativeValueTraits<IDLLongEnforceRange>
+    : public NativeValueTraitsBase<IDLLong> {
+  static int32_t NativeValue(v8::Isolate* isolate,
+                             v8::Local<v8::Value> value,
+                             ExceptionState& exception_state) {
+    return ToInt32(isolate, value, kEnforceRange, exception_state);
+  }
+};
+
+template <>
+struct CORE_EXPORT NativeValueTraits<IDLUnsignedLongEnforceRange>
+    : public NativeValueTraitsBase<IDLUnsignedLong> {
+  static uint32_t NativeValue(v8::Isolate* isolate,
+                              v8::Local<v8::Value> value,
+                              ExceptionState& exception_state) {
+    return ToUInt32(isolate, value, kEnforceRange, exception_state);
+  }
+};
+
+template <>
+struct CORE_EXPORT NativeValueTraits<IDLLongLongEnforceRange>
+    : public NativeValueTraitsBase<IDLLongLong> {
+  static int64_t NativeValue(v8::Isolate* isolate,
+                             v8::Local<v8::Value> value,
+                             ExceptionState& exception_state) {
+    return ToInt64(isolate, value, kEnforceRange, exception_state);
+  }
+};
+
+template <>
+struct CORE_EXPORT NativeValueTraits<IDLUnsignedLongLongEnforceRange>
+    : public NativeValueTraitsBase<IDLUnsignedLongLong> {
+  static uint64_t NativeValue(v8::Isolate* isolate,
+                              v8::Local<v8::Value> value,
+                              ExceptionState& exception_state) {
+    return ToUInt64(isolate, value, kEnforceRange, exception_state);
   }
 };
 
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer.cc b/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
index 486ef25..a63e1514 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
@@ -424,7 +424,7 @@
       // cancel the task.
       //
       // TODO(leszeks): Decrease the priority of these tasks where possible.
-      BackgroundScheduler::PostOnBackgroundThreadWithTraits(
+      background_scheduler::PostOnBackgroundThreadWithTraits(
           FROM_HERE, {base::TaskPriority::USER_BLOCKING, base::MayBlock()},
           CrossThreadBind(RunBlockingScriptStreamingTask,
                           WTF::Passed(std::move(script_streaming_task)),
@@ -476,7 +476,7 @@
       // The task creation shouldn't fail, since it didn't fail before during
       // NotifyAppendData.
       CHECK(script_streaming_task);
-      BackgroundScheduler::PostOnBackgroundThreadWithTraits(
+      background_scheduler::PostOnBackgroundThreadWithTraits(
           FROM_HERE, {base::TaskPriority::USER_BLOCKING},
           CrossThreadBind(RunNonBlockingScriptStreamingTask,
                           WTF::Passed(std::move(script_streaming_task)),
diff --git a/third_party/blink/renderer/bindings/scripts/idl_definitions.py b/third_party/blink/renderer/bindings/scripts/idl_definitions.py
index 8d9b00f..b7980d1 100644
--- a/third_party/blink/renderer/bindings/scripts/idl_definitions.py
+++ b/third_party/blink/renderer/bindings/scripts/idl_definitions.py
@@ -64,6 +64,7 @@
 
 import abc
 
+from idl_types import IdlAnnotatedType
 from idl_types import IdlFrozenArrayType
 from idl_types import IdlNullableType
 from idl_types import IdlRecordType
@@ -951,7 +952,7 @@
     base_type = type_node_inner_to_type(children[0])
     if len(children) == 2:
         extended_attributes = ext_attributes_node_to_extended_attributes(children[1])
-        base_type.set_extended_attributes(extended_attributes)
+        base_type = IdlAnnotatedType(base_type, extended_attributes)
 
     if node.GetProperty('NULLABLE'):
         base_type = IdlNullableType(base_type)
diff --git a/third_party/blink/renderer/bindings/scripts/idl_types.py b/third_party/blink/renderer/bindings/scripts/idl_types.py
index cf202b3..068c33d 100644
--- a/third_party/blink/renderer/bindings/scripts/idl_types.py
+++ b/third_party/blink/renderer/bindings/scripts/idl_types.py
@@ -11,6 +11,7 @@
   IdlSequenceType
   IdlFrozenArrayType
  IdlNullableType
+ IdlAnnotatedType
 
 IdlTypes are picklable because we store them in interfaces_info.
 """
@@ -91,6 +92,11 @@
     'CallbackFunctionTreatedAsScriptValue',
 ])
 
+EXTENDED_ATTRIBUTES_APPLICABLE_TO_TYPES = frozenset([
+    'Clamp',
+    'EnforceRange',
+    'TreatNullAs',
+])
 
 ################################################################################
 # Inheritance
@@ -143,13 +149,12 @@
     dictionaries = set()
     enums = {}  # name -> values
 
-    def __init__(self, base_type, is_unrestricted=False, extended_attributes=None):
+    def __init__(self, base_type, is_unrestricted=False):
         super(IdlType, self).__init__()
         if is_unrestricted:
             self.base_type = 'unrestricted %s' % base_type
         else:
             self.base_type = base_type
-        self.extended_attributes = extended_attributes
 
     def __str__(self):
         return self.base_type
@@ -157,15 +162,10 @@
     def __getstate__(self):
         return {
             'base_type': self.base_type,
-            'extended_attributes': self.extended_attributes,
         }
 
     def __setstate__(self, state):
         self.base_type = state['base_type']
-        self.extended_attributes = state['extended_attributes']
-
-    def set_extended_attributes(self, extended_attributes):
-        self.extended_attributes = extended_attributes
 
     @property
     def is_basic_type(self):
@@ -581,3 +581,59 @@
         yield self
         for idl_type in self.inner_type.idl_types():
             yield idl_type
+
+
+################################################################################
+# IdlAnnotatedType
+################################################################################
+
+class IdlAnnotatedType(IdlTypeBase):
+    """IdlAnnoatedType represents an IDL type with extended attributes.
+    [Clamp], [EnforceRange], and [TreatNullAs] are applicable to types.
+    https://heycam.github.io/webidl/#idl-annotated-types
+    """
+
+    def __init__(self, inner_type, extended_attributes):
+        super(IdlAnnotatedType, self).__init__()
+        self.inner_type = inner_type
+        self.extended_attributes = extended_attributes
+
+        if any(key not in EXTENDED_ATTRIBUTES_APPLICABLE_TO_TYPES
+               for key in extended_attributes):
+            raise ValueError('Extended attributes not applicable to types: %s' % self)
+
+    def __str__(self):
+        annotation = ', '.join((key + ('' if val is None else '=' + val))
+                               for key, val in self.extended_attributes.iteritems())
+        return '[%s] %s' % (annotation, str(self.inner_type))
+
+    def __getattr__(self, name):
+        return getattr(self.inner_type, name)
+
+    def __getstate__(self):
+        return {
+            'inner_type': self.inner_type,
+            'extended_attributes': self.extended_attributes,
+        }
+
+    def __setstate__(self, state):
+        self.inner_type = state['inner_type']
+        self.extended_attributes = state['extended_attributes']
+
+    @property
+    def is_annotated_type(self):
+        return True
+
+    @property
+    def name(self):
+        annotation = ''.join((key + ('' if val is None else val))
+                             for key, val in sorted(self.extended_attributes.iteritems()))
+        return self.inner_type.name + annotation
+
+    def resolve_typedefs(self, typedefs):
+        self.inner_type = self.inner_type.resolve_typedefs(typedefs)
+        return self
+
+    def idl_types(self):
+        yield self
+        yield self.inner_type
diff --git a/third_party/blink/renderer/bindings/scripts/v8_types.py b/third_party/blink/renderer/bindings/scripts/v8_types.py
index 7ea42f9b..e0d63bf 100644
--- a/third_party/blink/renderer/bindings/scripts/v8_types.py
+++ b/third_party/blink/renderer/bindings/scripts/v8_types.py
@@ -141,32 +141,20 @@
 }
 
 
-def string_resource_mode(idl_type, extended_attributes=None):
+def string_resource_mode(idl_type):
     """Returns a V8StringResourceMode value corresponding to the IDL type.
 
     Args:
         idl_type:
             A string IdlType.
-        extended_attributes:
-            A mapping type with extended attribute names and values.
     """
-    extended_attributes = extended_attributes or {}
-
     if idl_type.is_nullable:
         return 'kTreatNullAndUndefinedAsNullString'
-    # TODO(lisabelle): Remove these 4 lines when we have fully supported
-    # annoteted types. (crbug.com/714866)
-    # It is because at that time 'TreatNullAs' will only appear in
-    # type_extended_attributes, not in extended_attributes.
-    if extended_attributes.get('TreatNullAs') == 'EmptyString':
-        return 'kTreatNullAsEmptyString'
-    if extended_attributes.get('TreatNullAs') == 'NullString':
-        return 'kTreatNullAsNullString'
-    type_extended_attributes = idl_type.extended_attributes or {}
-    if type_extended_attributes.get('TreatNullAs') == 'EmptyString':
-        return 'kTreatNullAsEmptyString'
-    if type_extended_attributes.get('TreatNullAs') == 'NullString':
-        return 'kTreatNullAsNullString'
+    if idl_type.is_annotated_type:
+        treat_null_as = idl_type.extended_attributes.get('TreatNullAs')
+        if treat_null_as == 'EmptyString':
+            return 'kTreatNullAsEmptyString'
+        raise ValueError('Unknown value for [TreatNullAs]: %s' % treat_null_as)
     return ''
 
 
@@ -251,7 +239,7 @@
     if idl_type.is_string_type:
         if not raw_type:
             return 'const String&' if used_as_rvalue_type else 'String'
-        return 'V8StringResource<%s>' % string_resource_mode(idl_type, extended_attributes)
+        return 'V8StringResource<%s>' % string_resource_mode(idl_type)
 
     if base_idl_type == 'ArrayBufferView' and 'FlexibleArrayBufferView' in extended_attributes:
         return 'FlexibleArrayBufferView'
@@ -630,11 +618,8 @@
     if idl_type.is_string_type:
         # Strings are handled separately because null and/or undefined are
         # processed by V8StringResource due to the [TreatNullAs] extended
-        # attribute (it also handles nullable string types).
-        name = 'IDL%s' % (idl_type.inner_type.name if idl_type.is_nullable else idl_type.name)
-        resource_mode = string_resource_mode(idl_type, extended_attributes)
-        if resource_mode:
-            name = cpp_template_type('%sBase' % name, resource_mode)
+        # attribute and nullable string types.
+        name = 'IDL%s' % idl_type.name
     elif idl_type.is_nullable:
         inner_type = native_value_traits_type_name(idl_type.inner_type, extended_attributes)
         # IDLNullable is only required for sequences and such.
@@ -669,7 +654,6 @@
     # Simple types
     idl_type = idl_type.preprocessed_type
     base_idl_type = idl_type.as_union_type.name if idl_type.is_union_type else idl_type.base_type
-    type_extended_attributes = idl_type.extended_attributes or {}
 
     if 'FlexibleArrayBufferView' in extended_attributes:
         if base_idl_type not in ARRAY_BUFFER_VIEW_AND_TYPED_ARRAY_TYPES:
@@ -680,20 +664,7 @@
         raise ValueError('Unrecognized base type for extended attribute "AllowShared": %s' % (idl_type.base_type))
 
     if idl_type.is_integer_type:
-        configuration = 'kNormalConversion'
-        # TODO(lisabelle): Remove these 4 lines when we have fully supported
-        # annoteted types.
-        # It is because at that time 'Clamp' and 'EnforceRange' will only
-        # appear in type_extended_attributes, not in extended_attributes.
-        if 'EnforceRange' in extended_attributes:
-            configuration = 'kEnforceRange'
-        elif 'Clamp' in extended_attributes:
-            configuration = 'kClamp'
-        if 'EnforceRange' in type_extended_attributes:
-            configuration = 'kEnforceRange'
-        elif 'Clamp' in type_extended_attributes:
-            configuration = 'kClamp'
-        arguments = ', '.join([v8_value, 'exceptionState', configuration])
+        arguments = ', '.join([v8_value, 'exceptionState'])
     elif base_idl_type == 'SerializedScriptValue':
         arguments = ', '.join([
             v8_value,
@@ -822,14 +793,13 @@
 ################################################################################
 
 def preprocess_idl_type(idl_type):
-    extended_attributes = idl_type.extended_attributes
     if idl_type.is_nullable:
         return IdlNullableType(idl_type.inner_type.preprocessed_type)
     if idl_type.is_enum:
         # Enumerations are internally DOMStrings
-        return IdlType('DOMString', extended_attributes)
+        return IdlType('DOMString')
     if idl_type.base_type in ['any', 'object'] or idl_type.is_custom_callback_function:
-        return IdlType('ScriptValue', extended_attributes)
+        return IdlType('ScriptValue')
     if idl_type.is_callback_function:
         return idl_type
     return idl_type
diff --git a/third_party/blink/renderer/bindings/tests/results/core/long_or_boolean.cc b/third_party/blink/renderer/bindings/tests/results/core/long_or_boolean.cc
index de48707..90ad728 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/long_or_boolean.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/long_or_boolean.cc
@@ -73,7 +73,7 @@
   }
 
   if (v8Value->IsNumber()) {
-    int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(isolate, v8Value, exceptionState, kNormalConversion);
+    int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(isolate, v8Value, exceptionState);
     if (exceptionState.HadException())
       return;
     impl.SetLong(cppValue);
@@ -81,7 +81,7 @@
   }
 
   {
-    int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(isolate, v8Value, exceptionState, kNormalConversion);
+    int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(isolate, v8Value, exceptionState);
     if (exceptionState.HadException())
       return;
     impl.SetLong(cppValue);
diff --git a/third_party/blink/renderer/bindings/tests/results/core/long_or_test_dictionary.cc b/third_party/blink/renderer/bindings/tests/results/core/long_or_test_dictionary.cc
index 42ff4ebb..8191b84f3 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/long_or_test_dictionary.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/long_or_test_dictionary.cc
@@ -87,7 +87,7 @@
   }
 
   if (v8Value->IsNumber()) {
-    int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(isolate, v8Value, exceptionState, kNormalConversion);
+    int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(isolate, v8Value, exceptionState);
     if (exceptionState.HadException())
       return;
     impl.SetLong(cppValue);
@@ -95,7 +95,7 @@
   }
 
   {
-    int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(isolate, v8Value, exceptionState, kNormalConversion);
+    int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(isolate, v8Value, exceptionState);
     if (exceptionState.HadException())
       return;
     impl.SetLong(cppValue);
diff --git a/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_or_null_sequence.cc b/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_or_null_sequence.cc
index 7a4f425..7467eba4 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_or_null_sequence.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_or_null_sequence.cc
@@ -91,7 +91,7 @@
     return;
 
   if (HasCallableIteratorSymbol(isolate, v8Value, exceptionState)) {
-    Vector<String> cppValue = NativeValueTraits<IDLSequence<IDLStringBase<kTreatNullAndUndefinedAsNullString>>>::NativeValue(isolate, v8Value, exceptionState);
+    Vector<String> cppValue = NativeValueTraits<IDLSequence<IDLStringOrNull>>::NativeValue(isolate, v8Value, exceptionState);
     if (exceptionState.HadException())
       return;
     const char* validValues[] = {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_long.cc b/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_long.cc
index b4b39d1a..1ace136 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_long.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_long.cc
@@ -81,7 +81,7 @@
   }
 
   if (v8Value->IsNumber()) {
-    int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(isolate, v8Value, exceptionState, kNormalConversion);
+    int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(isolate, v8Value, exceptionState);
     if (exceptionState.HadException())
       return;
     impl.SetLong(cppValue);
@@ -89,7 +89,7 @@
   }
 
   {
-    int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(isolate, v8Value, exceptionState, kNormalConversion);
+    int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(isolate, v8Value, exceptionState);
     if (exceptionState.HadException())
       return;
     impl.SetLong(cppValue);
diff --git a/third_party/blink/renderer/bindings/tests/results/core/unsigned_long_long_or_boolean_or_test_callback_interface.cc b/third_party/blink/renderer/bindings/tests/results/core/unsigned_long_long_or_boolean_or_test_callback_interface.cc
index 2ef955a..e3bc09e 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/unsigned_long_long_or_boolean_or_test_callback_interface.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/unsigned_long_long_or_boolean_or_test_callback_interface.cc
@@ -98,7 +98,7 @@
   }
 
   if (v8Value->IsNumber()) {
-    uint64_t cppValue = NativeValueTraits<IDLUnsignedLongLong>::NativeValue(isolate, v8Value, exceptionState, kNormalConversion);
+    uint64_t cppValue = NativeValueTraits<IDLUnsignedLongLong>::NativeValue(isolate, v8Value, exceptionState);
     if (exceptionState.HadException())
       return;
     impl.SetUnsignedLongLong(cppValue);
@@ -106,7 +106,7 @@
   }
 
   {
-    uint64_t cppValue = NativeValueTraits<IDLUnsignedLongLong>::NativeValue(isolate, v8Value, exceptionState, kNormalConversion);
+    uint64_t cppValue = NativeValueTraits<IDLUnsignedLongLong>::NativeValue(isolate, v8Value, exceptionState);
     if (exceptionState.HadException())
       return;
     impl.SetUnsignedLongLong(cppValue);
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_dictionary.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_dictionary.cc
index 41b432a..d88b4fd 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_dictionary.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_dictionary.cc
@@ -151,7 +151,7 @@
   if (applicable_to_type_long_member_value.IsEmpty() || applicable_to_type_long_member_value->IsUndefined()) {
     // Do nothing.
   } else {
-    int32_t applicable_to_type_long_member_cpp_value = NativeValueTraits<IDLLong>::NativeValue(isolate, applicable_to_type_long_member_value, exceptionState, kClamp);
+    int32_t applicable_to_type_long_member_cpp_value = NativeValueTraits<IDLLongClamp>::NativeValue(isolate, applicable_to_type_long_member_value, exceptionState);
     if (exceptionState.HadException())
       return;
     impl.setApplicableToTypeLongMember(applicable_to_type_long_member_cpp_value);
@@ -511,7 +511,7 @@
   if (long_member_value.IsEmpty() || long_member_value->IsUndefined()) {
     // Do nothing.
   } else {
-    int32_t long_member_cpp_value = NativeValueTraits<IDLLong>::NativeValue(isolate, long_member_value, exceptionState, kNormalConversion);
+    int32_t long_member_cpp_value = NativeValueTraits<IDLLong>::NativeValue(isolate, long_member_value, exceptionState);
     if (exceptionState.HadException())
       return;
     impl.setLongMember(long_member_cpp_value);
@@ -671,7 +671,7 @@
   if (string_or_null_record_member_value.IsEmpty() || string_or_null_record_member_value->IsUndefined()) {
     // Do nothing.
   } else {
-    Vector<std::pair<String, String>> string_or_null_record_member_cpp_value = NativeValueTraits<IDLRecord<IDLString, IDLStringBase<kTreatNullAndUndefinedAsNullString>>>::NativeValue(isolate, string_or_null_record_member_value, exceptionState);
+    Vector<std::pair<String, String>> string_or_null_record_member_cpp_value = NativeValueTraits<IDLRecord<IDLString, IDLStringOrNull>>::NativeValue(isolate, string_or_null_record_member_value, exceptionState);
     if (exceptionState.HadException())
       return;
     impl.setStringOrNullRecordMember(string_or_null_record_member_cpp_value);
@@ -685,7 +685,7 @@
   if (string_or_null_sequence_member_value.IsEmpty() || string_or_null_sequence_member_value->IsUndefined()) {
     // Do nothing.
   } else {
-    Vector<String> string_or_null_sequence_member_cpp_value = NativeValueTraits<IDLSequence<IDLStringBase<kTreatNullAndUndefinedAsNullString>>>::NativeValue(isolate, string_or_null_sequence_member_value, exceptionState);
+    Vector<String> string_or_null_sequence_member_cpp_value = NativeValueTraits<IDLSequence<IDLStringOrNull>>::NativeValue(isolate, string_or_null_sequence_member_value, exceptionState);
     if (exceptionState.HadException())
       return;
     impl.setStringOrNullSequenceMember(string_or_null_sequence_member_cpp_value);
@@ -835,7 +835,7 @@
   if (treat_null_as_string_sequence_member_value.IsEmpty() || treat_null_as_string_sequence_member_value->IsUndefined()) {
     // Do nothing.
   } else {
-    Vector<String> treat_null_as_string_sequence_member_cpp_value = NativeValueTraits<IDLSequence<IDLStringBase<kTreatNullAsEmptyString>>>::NativeValue(isolate, treat_null_as_string_sequence_member_value, exceptionState);
+    Vector<String> treat_null_as_string_sequence_member_cpp_value = NativeValueTraits<IDLSequence<IDLStringTreatNullAsEmptyString>>::NativeValue(isolate, treat_null_as_string_sequence_member_value, exceptionState);
     if (exceptionState.HadException())
       return;
     impl.setTreatNullAsStringSequenceMember(treat_null_as_string_sequence_member_cpp_value);
@@ -953,7 +953,7 @@
   if (usv_string_or_null_member_value.IsEmpty() || usv_string_or_null_member_value->IsUndefined()) {
     // Do nothing.
   } else {
-    V8StringResource<kTreatNullAndUndefinedAsNullString> usv_string_or_null_member_cpp_value = NativeValueTraits<IDLUSVStringBase<kTreatNullAndUndefinedAsNullString>>::NativeValue(isolate, usv_string_or_null_member_value, exceptionState);
+    V8StringResource<kTreatNullAndUndefinedAsNullString> usv_string_or_null_member_cpp_value = NativeValueTraits<IDLUSVStringOrNull>::NativeValue(isolate, usv_string_or_null_member_value, exceptionState);
     if (exceptionState.HadException())
       return;
     impl.setUsvStringOrNullMember(usv_string_or_null_member_cpp_value);
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_dictionary_derived.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_dictionary_derived.cc
index 07d66ce..d8d1d88 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_dictionary_derived.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_dictionary_derived.cc
@@ -85,7 +85,7 @@
     exceptionState.ThrowTypeError("required member requiredLongMember is undefined.");
     return;
   } else {
-    int32_t required_long_member_cpp_value = NativeValueTraits<IDLLong>::NativeValue(isolate, required_long_member_value, exceptionState, kNormalConversion);
+    int32_t required_long_member_cpp_value = NativeValueTraits<IDLLong>::NativeValue(isolate, required_long_member_value, exceptionState);
     if (exceptionState.HadException())
       return;
     impl.setRequiredLongMember(required_long_member_cpp_value);
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed.cc
index a77ad9d..a2cf6273 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed.cc
@@ -88,7 +88,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestIntegerIndexed", "length");
 
   // Prepare the value to be set.
-  int16_t cppValue = NativeValueTraits<IDLShort>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int16_t cppValue = NativeValueTraits<IDLShort>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_global.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_global.cc
index 65ebe820..fd1794e 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_global.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_global.cc
@@ -85,7 +85,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestIntegerIndexedGlobal", "length");
 
   // Prepare the value to be set.
-  uint64_t cppValue = NativeValueTraits<IDLUnsignedLongLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  uint64_t cppValue = NativeValueTraits<IDLUnsignedLongLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_primary_global.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_primary_global.cc
index 374d72f6..d995ec1 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_primary_global.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_primary_global.cc
@@ -85,7 +85,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestIntegerIndexedPrimaryGlobal", "length");
 
   // Prepare the value to be set.
-  int8_t cppValue = NativeValueTraits<IDLByte>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int8_t cppValue = NativeValueTraits<IDLByte>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
index c0736d2d..edd75c7 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
@@ -365,7 +365,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterface", "withExtendedAttributeStringAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kEnforceRange);
+  int32_t cppValue = NativeValueTraits<IDLLongEnforceRange>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -423,7 +423,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterface", "conditionalLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -562,7 +562,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterface", "usvStringOrNullAttribute");
 
   // Prepare the value to be set.
-  V8StringResource<kTreatNullAndUndefinedAsNullString> cppValue = NativeValueTraits<IDLUSVStringBase<kTreatNullAndUndefinedAsNullString>>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
+  V8StringResource<kTreatNullAndUndefinedAsNullString> cppValue = NativeValueTraits<IDLUSVStringOrNull>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -589,7 +589,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterface", "alwaysExposedAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -616,7 +616,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterface", "workerExposedAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -643,7 +643,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterface", "windowExposedAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -1149,7 +1149,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterface", "partialLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -1170,7 +1170,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterface", "partialStaticLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -1199,7 +1199,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterface", "partialCallWithExecutionContextLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -1269,7 +1269,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterface", "partialSecureContextLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -1296,7 +1296,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterface", "partial2LongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -1317,7 +1317,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterface", "partial2StaticLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -1711,7 +1711,7 @@
   TestInterfaceImplementation* impl = V8TestInterface::ToImpl(info.Holder());
 
   int32_t longArg;
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -1996,7 +1996,7 @@
   if (exceptionState.HadException())
     return;
 
-  strings = NativeValueTraits<IDLSequence<IDLStringBase<kTreatNullAndUndefinedAsNullString>>>::NativeValue(info.GetIsolate(), info[1], exceptionState);
+  strings = NativeValueTraits<IDLSequence<IDLStringOrNull>>::NativeValue(info.GetIsolate(), info[1], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2029,7 +2029,7 @@
   if (exceptionState.HadException())
     return;
 
-  strings = NativeValueTraits<IDLRecord<IDLString, IDLStringBase<kTreatNullAndUndefinedAsNullString>>>::NativeValue(info.GetIsolate(), info[1], exceptionState);
+  strings = NativeValueTraits<IDLRecord<IDLString, IDLStringOrNull>>::NativeValue(info.GetIsolate(), info[1], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2121,7 +2121,7 @@
   }
 
   int32_t longArg;
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_2.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_2.cc
index 99e97f7..a8d7dd8e 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_2.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_2.cc
@@ -88,7 +88,7 @@
   }
 
   uint32_t index;
-  index = NativeValueTraits<IDLUnsignedLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  index = NativeValueTraits<IDLUnsignedLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -111,7 +111,7 @@
 
   uint32_t index;
   TestInterfaceEmpty* value;
-  index = NativeValueTraits<IDLUnsignedLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  index = NativeValueTraits<IDLUnsignedLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -139,7 +139,7 @@
   }
 
   uint32_t index;
-  index = NativeValueTraits<IDLUnsignedLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  index = NativeValueTraits<IDLUnsignedLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_check_security.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_check_security.cc
index 72446be..acb4c4d0 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_check_security.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_check_security.cc
@@ -95,7 +95,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterfaceCheckSecurity", "longAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -122,7 +122,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterfaceCheckSecurity", "doNotCheckSecurityLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -157,7 +157,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterfaceCheckSecurity", "doNotCheckSecurityOnSetterLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -343,7 +343,7 @@
     impl->doNotCheckSecurityVoidOverloadMethod(argument1);
     return;
   }
-  argument2 = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[1], exceptionState, kNormalConversion);
+  argument2 = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[1], exceptionState);
   if (exceptionState.HadException())
     return;
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor.cc
index 1cb7f45..57afd82 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor.cc
@@ -161,7 +161,7 @@
     V8SetReturnValue(info, wrapper);
     return;
   }
-  optionalUSVStringArg = NativeValueTraits<IDLUSVStringBase<kTreatNullAndUndefinedAsNullString>>::NativeValue(info.GetIsolate(), info[7], exceptionState);
+  optionalUSVStringArg = NativeValueTraits<IDLUSVStringOrNull>::NativeValue(info.GetIsolate(), info[7], exceptionState);
   if (exceptionState.HadException())
     return;
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_2.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_2.cc
index f83dac7..72b6651 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_2.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_2.cc
@@ -138,7 +138,7 @@
     return;
   }
 
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[1], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[1], exceptionState);
   if (exceptionState.HadException())
     return;
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor.cc
index 5943f2a..ecb85c3 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor.cc
@@ -148,7 +148,7 @@
   if (exceptionState.HadException())
     return;
 
-  defaultUndefinedOptionalLongArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[2], exceptionState, kNormalConversion);
+  defaultUndefinedOptionalLongArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[2], exceptionState);
   if (exceptionState.HadException())
     return;
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_origin_trial_enabled.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_origin_trial_enabled.cc
index 70c75a8..3b3d0baf7 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_origin_trial_enabled.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_origin_trial_enabled.cc
@@ -111,7 +111,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterfaceOriginTrialEnabled", "conditionalLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
index 33964f9..3291541 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
@@ -303,7 +303,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "domTimeStampAttribute");
 
   // Prepare the value to be set.
-  uint64_t cppValue = NativeValueTraits<IDLUnsignedLongLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  uint64_t cppValue = NativeValueTraits<IDLUnsignedLongLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -357,7 +357,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "byteAttribute");
 
   // Prepare the value to be set.
-  int8_t cppValue = NativeValueTraits<IDLByte>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int8_t cppValue = NativeValueTraits<IDLByte>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -438,7 +438,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "longAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -465,7 +465,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "longLongAttribute");
 
   // Prepare the value to be set.
-  int64_t cppValue = NativeValueTraits<IDLLongLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int64_t cppValue = NativeValueTraits<IDLLongLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -492,7 +492,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "octetAttribute");
 
   // Prepare the value to be set.
-  uint8_t cppValue = NativeValueTraits<IDLOctet>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  uint8_t cppValue = NativeValueTraits<IDLOctet>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -519,7 +519,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "shortAttribute");
 
   // Prepare the value to be set.
-  int16_t cppValue = NativeValueTraits<IDLShort>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int16_t cppValue = NativeValueTraits<IDLShort>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -600,7 +600,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "unsignedLongAttribute");
 
   // Prepare the value to be set.
-  uint32_t cppValue = NativeValueTraits<IDLUnsignedLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  uint32_t cppValue = NativeValueTraits<IDLUnsignedLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -627,7 +627,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "unsignedLongLongAttribute");
 
   // Prepare the value to be set.
-  uint64_t cppValue = NativeValueTraits<IDLUnsignedLongLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  uint64_t cppValue = NativeValueTraits<IDLUnsignedLongLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -654,7 +654,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "unsignedShortAttribute");
 
   // Prepare the value to be set.
-  uint16_t cppValue = NativeValueTraits<IDLUnsignedShort>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  uint16_t cppValue = NativeValueTraits<IDLUnsignedShort>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -743,7 +743,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "cssAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -770,7 +770,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "imeAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -797,7 +797,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "svgAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -824,7 +824,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "xmlAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -1433,7 +1433,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "longOrNullAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -1592,7 +1592,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "staticLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -1860,7 +1860,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "activityLoggingAccessForAllWorldsLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -1887,7 +1887,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "activityLoggingGetterForAllWorldsLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -1914,7 +1914,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "activityLoggingSetterForAllWorldsLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2182,7 +2182,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "customGetterLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2217,7 +2217,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "deprecatedLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2244,7 +2244,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "enforceRangeLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kEnforceRange);
+  int32_t cppValue = NativeValueTraits<IDLLongEnforceRange>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2271,7 +2271,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "implementedAsLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2290,7 +2290,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "customGetterImplementedAsLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2325,7 +2325,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "measureAsLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2352,7 +2352,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "notEnumerableLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2379,7 +2379,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "originTrialEnabledLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2444,7 +2444,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "activityLoggingAccessPerWorldBindingsLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2471,7 +2471,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "activityLoggingAccessPerWorldBindingsLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2498,7 +2498,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "activityLoggingAccessForIsolatedWorldsPerWorldBindingsLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2525,7 +2525,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "activityLoggingAccessForIsolatedWorldsPerWorldBindingsLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2552,7 +2552,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "activityLoggingGetterPerWorldBindingsLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2579,7 +2579,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "activityLoggingGetterPerWorldBindingsLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2606,7 +2606,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "activityLoggingGetterForIsolatedWorldsPerWorldBindingsLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2633,7 +2633,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "activityLoggingGetterForIsolatedWorldsPerWorldBindingsLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2884,7 +2884,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "raisesExceptionLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2918,7 +2918,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "raisesExceptionGetterLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -2945,7 +2945,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "setterRaisesExceptionLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -3161,7 +3161,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "reflectLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -3190,7 +3190,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "reflectUnsignedShortAttribute");
 
   // Prepare the value to be set.
-  uint16_t cppValue = NativeValueTraits<IDLUnsignedShort>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  uint16_t cppValue = NativeValueTraits<IDLUnsignedShort>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -3219,7 +3219,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "reflectUnsignedLongAttribute");
 
   // Prepare the value to be set.
-  uint32_t cppValue = NativeValueTraits<IDLUnsignedLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  uint32_t cppValue = NativeValueTraits<IDLUnsignedLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -3653,7 +3653,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "RuntimeCallStatsCounterAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -3741,7 +3741,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "runtimeEnabledLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -3972,7 +3972,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "unforgeableLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -3999,7 +3999,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "measuredLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -4106,7 +4106,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "unscopableLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -4133,7 +4133,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "unscopableOriginTrialEnabledLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -4160,7 +4160,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "unscopableRuntimeEnabledLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -4405,7 +4405,7 @@
   }
 
   uint64_t domTimeStampArg;
-  domTimeStampArg = NativeValueTraits<IDLUnsignedLongLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  domTimeStampArg = NativeValueTraits<IDLUnsignedLongLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -4441,7 +4441,7 @@
   }
 
   int8_t byteArg;
-  byteArg = NativeValueTraits<IDLByte>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  byteArg = NativeValueTraits<IDLByte>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -4495,7 +4495,7 @@
   }
 
   int32_t longArg;
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -4513,7 +4513,7 @@
   }
 
   int64_t longLongArg;
-  longLongArg = NativeValueTraits<IDLLongLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longLongArg = NativeValueTraits<IDLLongLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -4531,7 +4531,7 @@
   }
 
   uint8_t octetArg;
-  octetArg = NativeValueTraits<IDLOctet>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  octetArg = NativeValueTraits<IDLOctet>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -4549,7 +4549,7 @@
   }
 
   int16_t shortArg;
-  shortArg = NativeValueTraits<IDLShort>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  shortArg = NativeValueTraits<IDLShort>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -4567,7 +4567,7 @@
   }
 
   uint32_t unsignedLongArg;
-  unsignedLongArg = NativeValueTraits<IDLUnsignedLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  unsignedLongArg = NativeValueTraits<IDLUnsignedLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -4585,7 +4585,7 @@
   }
 
   uint64_t unsignedLongLongArg;
-  unsignedLongLongArg = NativeValueTraits<IDLUnsignedLongLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  unsignedLongLongArg = NativeValueTraits<IDLUnsignedLongLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -4603,7 +4603,7 @@
   }
 
   uint16_t unsignedShortArg;
-  unsignedShortArg = NativeValueTraits<IDLUnsignedShort>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  unsignedShortArg = NativeValueTraits<IDLUnsignedShort>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -4646,7 +4646,7 @@
 
   int32_t longArg;
   TestInterfaceEmpty* testInterfaceEmptyArg;
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -5640,7 +5640,7 @@
   Dictionary arg2;
   V8StringResource<> arg3;
   Vector<String> variadic;
-  arg1 = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  arg1 = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -5839,7 +5839,7 @@
   if (!stringArg.Prepare())
     return;
 
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[1], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[1], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -5864,7 +5864,7 @@
       break;
     --numArgsPassed;
   }
-  byteStringArg = NativeValueTraits<IDLByteStringBase<kTreatNullAndUndefinedAsNullString>>::NativeValue(info.GetIsolate(), info[0], exceptionState);
+  byteStringArg = NativeValueTraits<IDLByteStringOrNull>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -5939,7 +5939,7 @@
     impl->voidMethodOptionalLongArg();
     return;
   }
-  optionalLongArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  optionalLongArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -5962,7 +5962,7 @@
     V8SetReturnValueString(info, impl->stringMethodOptionalLongArg(), info.GetIsolate());
     return;
   }
-  optionalLongArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  optionalLongArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -5985,7 +5985,7 @@
     V8SetReturnValue(info, impl->testInterfaceEmptyMethodOptionalLongArg());
     return;
   }
-  optionalLongArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  optionalLongArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -6008,7 +6008,7 @@
     V8SetReturnValueInt(info, impl->longMethodOptionalLongArg());
     return;
   }
-  optionalLongArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  optionalLongArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -6033,7 +6033,7 @@
       break;
     --numArgsPassed;
   }
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -6041,7 +6041,7 @@
     impl->voidMethodLongArgOptionalLongArg(longArg);
     return;
   }
-  optionalLongArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[1], exceptionState, kNormalConversion);
+  optionalLongArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[1], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -6067,7 +6067,7 @@
       break;
     --numArgsPassed;
   }
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -6075,7 +6075,7 @@
     impl->voidMethodLongArgOptionalLongArgOptionalLongArg(longArg);
     return;
   }
-  optionalLongArg1 = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[1], exceptionState, kNormalConversion);
+  optionalLongArg1 = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[1], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -6083,7 +6083,7 @@
     impl->voidMethodLongArgOptionalLongArgOptionalLongArg(longArg, optionalLongArg1);
     return;
   }
-  optionalLongArg2 = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[2], exceptionState, kNormalConversion);
+  optionalLongArg2 = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[2], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -6108,7 +6108,7 @@
       break;
     --numArgsPassed;
   }
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -6153,7 +6153,7 @@
     impl->voidMethodTestInterfaceEmptyArgOptionalLongArg(optionalTestInterfaceEmpty);
     return;
   }
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[1], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[1], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -6218,21 +6218,21 @@
   int64_t defaultLongLongArg;
   uint32_t defaultUnsignedArg;
   if (!info[0]->IsUndefined()) {
-    defaultLongArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+    defaultLongArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
     if (exceptionState.HadException())
       return;
   } else {
     defaultLongArg = 10;
   }
   if (!info[1]->IsUndefined()) {
-    defaultLongLongArg = NativeValueTraits<IDLLongLong>::NativeValue(info.GetIsolate(), info[1], exceptionState, kNormalConversion);
+    defaultLongLongArg = NativeValueTraits<IDLLongLong>::NativeValue(info.GetIsolate(), info[1], exceptionState);
     if (exceptionState.HadException())
       return;
   } else {
     defaultLongLongArg = -10;
   }
   if (!info[2]->IsUndefined()) {
-    defaultUnsignedArg = NativeValueTraits<IDLUnsignedLong>::NativeValue(info.GetIsolate(), info[2], exceptionState, kNormalConversion);
+    defaultUnsignedArg = NativeValueTraits<IDLUnsignedLong>::NativeValue(info.GetIsolate(), info[2], exceptionState);
     if (exceptionState.HadException())
       return;
   } else {
@@ -6300,7 +6300,7 @@
 
   V8StringResource<kTreatNullAndUndefinedAsNullString> defaultStringArg;
   if (!info[0]->IsUndefined()) {
-    defaultStringArg = NativeValueTraits<IDLByteStringBase<kTreatNullAndUndefinedAsNullString>>::NativeValue(info.GetIsolate(), info[0], exceptionState);
+    defaultStringArg = NativeValueTraits<IDLByteStringOrNull>::NativeValue(info.GetIsolate(), info[0], exceptionState);
     if (exceptionState.HadException())
       return;
   } else {
@@ -6480,7 +6480,7 @@
   TestObject* impl = V8TestObject::ToImpl(info.Holder());
 
   int32_t longArg;
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -6494,11 +6494,11 @@
 
   int32_t longArg1;
   int32_t longArg2;
-  longArg1 = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg1 = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
-  longArg2 = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[1], exceptionState, kNormalConversion);
+  longArg2 = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[1], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -6541,7 +6541,7 @@
   TestObject* impl = V8TestObject::ToImpl(info.Holder());
 
   int32_t longArg;
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -6569,7 +6569,7 @@
     impl->overloadedMethodB(stringArg);
     return;
   }
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[1], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[1], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -6620,7 +6620,7 @@
   TestObject* impl = V8TestObject::ToImpl(info.Holder());
 
   int32_t longArg;
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -6678,7 +6678,7 @@
   TestObject* impl = V8TestObject::ToImpl(info.Holder());
 
   int32_t longArg;
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -6748,7 +6748,7 @@
   TestObject* impl = V8TestObject::ToImpl(info.Holder());
 
   int32_t longArg;
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -6882,7 +6882,7 @@
   TestObject* impl = V8TestObject::ToImpl(info.Holder());
 
   int32_t longArg;
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -7178,7 +7178,7 @@
 
   int32_t longArg;
   Vector<ScriptValue> restArgs;
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -7436,7 +7436,7 @@
   TestObject* impl = V8TestObject::ToImpl(info.Holder());
 
   int32_t longArg;
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -7475,7 +7475,7 @@
   TestObject* impl = V8TestObject::ToImpl(info.Holder());
 
   int32_t longArg;
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -7512,7 +7512,7 @@
   ExceptionState exceptionState(info.GetIsolate(), ExceptionState::kExecutionContext, "TestObject", "overloadedStaticMethod");
 
   int32_t longArg;
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -7524,11 +7524,11 @@
 
   int32_t longArg1;
   int32_t longArg2;
-  longArg1 = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg1 = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
-  longArg2 = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[1], exceptionState, kNormalConversion);
+  longArg2 = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[1], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -7578,7 +7578,7 @@
   }
 
   uint32_t index;
-  index = NativeValueTraits<IDLUnsignedLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  index = NativeValueTraits<IDLUnsignedLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -7600,7 +7600,7 @@
 
   uint32_t index;
   V8StringResource<> value;
-  index = NativeValueTraits<IDLUnsignedLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  index = NativeValueTraits<IDLUnsignedLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -7623,7 +7623,7 @@
   }
 
   uint16_t clampUnsignedShortArg;
-  clampUnsignedShortArg = NativeValueTraits<IDLUnsignedShort>::NativeValue(info.GetIsolate(), info[0], exceptionState, kClamp);
+  clampUnsignedShortArg = NativeValueTraits<IDLUnsignedShortClamp>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -7641,7 +7641,7 @@
   }
 
   uint32_t clampUnsignedLongArg;
-  clampUnsignedLongArg = NativeValueTraits<IDLUnsignedLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kClamp);
+  clampUnsignedLongArg = NativeValueTraits<IDLUnsignedLongClamp>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -7667,7 +7667,7 @@
   TestObject* impl = V8TestObject::ToImpl(info.Holder());
 
   int32_t defaultUndefinedLongArg;
-  defaultUndefinedLongArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  defaultUndefinedLongArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -7696,7 +7696,7 @@
   }
 
   int32_t enforceRangeLongArg;
-  enforceRangeLongArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kEnforceRange);
+  enforceRangeLongArg = NativeValueTraits<IDLLongEnforceRange>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -7878,7 +7878,7 @@
   TestObject* impl = V8TestObject::ToImpl(info.Holder());
 
   int32_t arg;
-  arg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  arg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -7925,7 +7925,7 @@
   TestObject* impl = V8TestObject::ToImpl(info.Holder());
 
   int32_t arg;
-  arg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  arg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -7972,7 +7972,7 @@
   TestObject* impl = V8TestObject::ToImpl(info.Holder());
 
   int32_t arg;
-  arg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  arg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -8019,7 +8019,7 @@
   TestObject* impl = V8TestObject::ToImpl(info.Holder());
 
   int32_t arg;
-  arg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  arg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -8066,7 +8066,7 @@
   TestObject* impl = V8TestObject::ToImpl(info.Holder());
 
   int32_t arg;
-  arg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  arg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -8133,7 +8133,7 @@
   TestObject* impl = V8TestObject::ToImpl(info.Holder());
 
   int32_t arg;
-  arg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  arg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -8178,7 +8178,7 @@
   TestObject* impl = V8TestObject::ToImpl(info.Holder());
 
   int32_t arg;
-  arg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  arg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -8227,7 +8227,7 @@
   TestObject* impl = V8TestObject::ToImpl(info.Holder());
 
   int32_t arg;
-  arg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  arg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -8276,7 +8276,7 @@
   TestObject* impl = V8TestObject::ToImpl(info.Holder());
 
   int32_t arg;
-  arg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  arg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -8451,7 +8451,7 @@
     }
     return;
   }
-  optionalLongArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  optionalLongArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -8549,7 +8549,7 @@
   }
 
   int32_t longArg;
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -8595,7 +8595,7 @@
   TestObject* impl = V8TestObject::ToImpl(info.Holder());
 
   int32_t longArg;
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -8665,7 +8665,7 @@
 
   int32_t longArg;
   V8StringResource<> stringArg;
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -8684,7 +8684,7 @@
   int32_t longArg;
   V8StringResource<> stringArg;
   TestInterfaceImplementation* testInterfaceArg;
-  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kNormalConversion);
+  longArg = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -9002,7 +9002,7 @@
   }
 
   int32_t key;
-  key = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kEnforceRange);
+  key = NativeValueTraits<IDLLongEnforceRange>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -9026,7 +9026,7 @@
   }
 
   int32_t key;
-  key = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kEnforceRange);
+  key = NativeValueTraits<IDLLongEnforceRange>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -9050,7 +9050,7 @@
   }
 
   int32_t key;
-  key = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kEnforceRange);
+  key = NativeValueTraits<IDLLongEnforceRange>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -9075,7 +9075,7 @@
 
   int32_t key;
   StringOrDouble value;
-  key = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), info[0], exceptionState, kEnforceRange);
+  key = NativeValueTraits<IDLLongEnforceRange>::NativeValue(info.GetIsolate(), info[0], exceptionState);
   if (exceptionState.HadException())
     return;
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_typedefs.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_typedefs.cc
index 62d1de56..c84fb77c 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_typedefs.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_typedefs.cc
@@ -96,7 +96,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestTypedefs", "uLongLongAttribute");
 
   // Prepare the value to be set.
-  uint64_t cppValue = NativeValueTraits<IDLUnsignedLongLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  uint64_t cppValue = NativeValueTraits<IDLUnsignedLongLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -123,7 +123,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestTypedefs", "longWithClampAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kClamp);
+  int32_t cppValue = NativeValueTraits<IDLLongClamp>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_5.cc b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_5.cc
index cdf13eb..9a958bbf 100644
--- a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_5.cc
+++ b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_5.cc
@@ -253,7 +253,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterface5", "alwaysExposedAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -280,7 +280,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterface5", "workerExposedAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -307,7 +307,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterface5", "windowExposedAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_partial.cc b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_partial.cc
index 5f46e9a..763e44e 100644
--- a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_partial.cc
+++ b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_partial.cc
@@ -56,7 +56,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterface", "partial4LongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
@@ -77,7 +77,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestInterface", "partial4StaticLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_sub_object.cc b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_sub_object.cc
index b056d5e..fd113c0 100644
--- a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_sub_object.cc
+++ b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_sub_object.cc
@@ -108,7 +108,7 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestSubObject", "unforgeableLongAttribute");
 
   // Prepare the value to be set.
-  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState, kNormalConversion);
+  int32_t cppValue = NativeValueTraits<IDLLong>::NativeValue(info.GetIsolate(), v8Value, exceptionState);
   if (exceptionState.HadException())
     return;
 
diff --git a/third_party/blink/renderer/core/animation/animation.cc b/third_party/blink/renderer/core/animation/animation.cc
index 94bdffa..e96b4186 100644
--- a/third_party/blink/renderer/core/animation/animation.cc
+++ b/third_party/blink/renderer/core/animation/animation.cc
@@ -1269,7 +1269,7 @@
       CSSAnimations::IsAffectedByKeyframesFromScope(*target, tree_scope)) {
     target->SetNeedsStyleRecalc(kLocalStyleChange,
                                 StyleChangeReasonForTracing::Create(
-                                    StyleChangeReason::kStyleSheetChange));
+                                    style_change_reason::kStyleSheetChange));
   }
 }
 
diff --git a/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc
index c64acaf2..26c9322 100644
--- a/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc
@@ -154,9 +154,11 @@
     StyleResolverState& state) const {
   ComputedStyle& style = *state.Style();
   float zoom = EffectiveZoom(style);
+  CSSToLengthConversionData conversion_data = state.CssToLengthConversionData();
+  conversion_data.SetZoom(zoom);
   Length length = LengthInterpolationFunctions::CreateLength(
-      interpolable_value, non_interpolable_value,
-      state.CssToLengthConversionData(), value_range_);
+      interpolable_value, non_interpolable_value, conversion_data,
+      value_range_);
   if (LengthPropertyFunctions::SetLength(CssProperty(), style, length)) {
 #if DCHECK_IS_ON()
     // Assert that setting the length on ComputedStyle directly is identical to
diff --git a/third_party/blink/renderer/core/core_initializer.cc b/third_party/blink/renderer/core/core_initializer.cc
index e85c717..7183ea41 100644
--- a/third_party/blink/renderer/core/core_initializer.cc
+++ b/third_party/blink/renderer/core/core_initializer.cc
@@ -100,7 +100,7 @@
       FetchInitiatorTypeNames::kNamesCount + FontFamilyNames::kNamesCount +
       HTMLTokenizerNames::kNamesCount + HTTPNames::kNamesCount +
       InputModeNames::kNamesCount + InputTypeNames::kNamesCount +
-      MediaFeatureNames::kNamesCount + MediaTypeNames::kNamesCount +
+      MediaFeatureNames::kNamesCount + media_type_names::kNamesCount +
       performance_entry_names::kNamesCount;
 
   StringImpl::ReserveStaticStringsCapacityForSize(
@@ -126,7 +126,7 @@
   InputModeNames::init();
   InputTypeNames::init();
   MediaFeatureNames::init();
-  MediaTypeNames::init();
+  media_type_names::init();
   performance_entry_names::init();
 
   MediaQueryEvaluator::Init();
diff --git a/third_party/blink/renderer/core/css/inline_css_style_declaration.cc b/third_party/blink/renderer/core/css/inline_css_style_declaration.cc
index 3e7201ed..bbb0eb61 100644
--- a/third_party/blink/renderer/core/css/inline_css_style_declaration.cc
+++ b/third_party/blink/renderer/core/css/inline_css_style_declaration.cc
@@ -43,7 +43,7 @@
   parent_element_->ClearMutableInlineStyleIfEmpty();
   parent_element_->SetNeedsStyleRecalc(
       kLocalStyleChange, StyleChangeReasonForTracing::Create(
-                             StyleChangeReason::kInlineCSSStyleMutated));
+                             style_change_reason::kInlineCSSStyleMutated));
   parent_element_->InvalidateStyleAttribute();
   StyleAttributeMutationScope(this).DidInvalidateStyleAttr();
 }
diff --git a/third_party/blink/renderer/core/css/invalidation/pending_invalidations.cc b/third_party/blink/renderer/core/css/invalidation/pending_invalidations.cc
index 585ecd4..224fc759 100644
--- a/third_party/blink/renderer/core/css/invalidation/pending_invalidations.cc
+++ b/third_party/blink/renderer/core/css/invalidation/pending_invalidations.cc
@@ -32,7 +32,7 @@
       if (invalidation_set->WholeSubtreeInvalid()) {
         node.SetNeedsStyleRecalc(kSubtreeStyleChange,
                                  StyleChangeReasonForTracing::Create(
-                                     StyleChangeReason::kStyleInvalidator));
+                                     style_change_reason::kStyleInvalidator));
         requires_descendant_invalidation = false;
         break;
       }
@@ -40,7 +40,7 @@
       if (invalidation_set->InvalidatesSelf()) {
         node.SetNeedsStyleRecalc(kLocalStyleChange,
                                  StyleChangeReasonForTracing::Create(
-                                     StyleChangeReason::kStyleInvalidator));
+                                     style_change_reason::kStyleInvalidator));
       }
 
       if (!invalidation_set->IsEmpty())
@@ -94,7 +94,7 @@
     if (invalidation_set->WholeSubtreeInvalid()) {
       scheduling_parent.SetNeedsStyleRecalc(
           kSubtreeStyleChange, StyleChangeReasonForTracing::Create(
-                                   StyleChangeReason::kStyleInvalidator));
+                                   style_change_reason::kStyleInvalidator));
       return;
     }
     if (invalidation_set->InvalidatesSelf() &&
@@ -106,7 +106,7 @@
       if (descendants->WholeSubtreeInvalid()) {
         scheduling_parent.SetNeedsStyleRecalc(
             kSubtreeStyleChange, StyleChangeReasonForTracing::Create(
-                                     StyleChangeReason::kStyleInvalidator));
+                                     style_change_reason::kStyleInvalidator));
         return;
       }
       if (!pending_invalidations.Descendants().Contains(descendants))
diff --git a/third_party/blink/renderer/core/css/invalidation/style_invalidator.cc b/third_party/blink/renderer/core/css/invalidation/style_invalidator.cc
index 68e6fb9..9f9aa84 100644
--- a/third_party/blink/renderer/core/css/invalidation/style_invalidator.cc
+++ b/third_party/blink/renderer/core/css/invalidation/style_invalidator.cc
@@ -169,9 +169,9 @@
     if (const DescendantInvalidationSet* descendants =
             invalidation_set.SiblingDescendants()) {
       if (descendants->WholeSubtreeInvalid()) {
-        element.SetNeedsStyleRecalc(kSubtreeStyleChange,
-                                    StyleChangeReasonForTracing::Create(
-                                        StyleChangeReason::kStyleInvalidator));
+        element.SetNeedsStyleRecalc(
+            kSubtreeStyleChange, StyleChangeReasonForTracing::Create(
+                                     style_change_reason::kStyleInvalidator));
         return true;
       }
 
@@ -276,7 +276,7 @@
     } else if (CheckInvalidationSetsAgainstElement(element, sibling_data)) {
       element.SetNeedsStyleRecalc(kLocalStyleChange,
                                   StyleChangeReasonForTracing::Create(
-                                      StyleChangeReason::kStyleInvalidator));
+                                      style_change_reason::kStyleInvalidator));
     }
     if (UNLIKELY(element.NeedsStyleInvalidation()))
       PushInvalidationSetsForContainerNode(element, sibling_data);
@@ -295,7 +295,7 @@
     if (InsertionPointCrossing() && element.IsV0InsertionPoint()) {
       element.SetNeedsStyleRecalc(kSubtreeStyleChange,
                                   StyleChangeReasonForTracing::Create(
-                                      StyleChangeReason::kStyleInvalidator));
+                                      style_change_reason::kStyleInvalidator));
     }
   }
 
@@ -320,10 +320,11 @@
       continue;
     if (!distributed_node->IsElementNode())
       continue;
-    if (MatchesCurrentInvalidationSetsAsSlotted(ToElement(*distributed_node)))
+    if (MatchesCurrentInvalidationSetsAsSlotted(ToElement(*distributed_node))) {
       distributed_node->SetNeedsStyleRecalc(
           kLocalStyleChange, StyleChangeReasonForTracing::Create(
-                                 StyleChangeReason::kStyleInvalidator));
+                                 style_change_reason::kStyleInvalidator));
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/core/css/media_query.cc b/third_party/blink/renderer/core/css/media_query.cc
index 2a002db..2748773 100644
--- a/third_party/blink/renderer/core/css/media_query.cc
+++ b/third_party/blink/renderer/core/css/media_query.cc
@@ -56,7 +56,7 @@
     return result.ToString();
   }
 
-  if (media_type_ != MediaTypeNames::all || restrictor_ != kNone) {
+  if (media_type_ != media_type_names::kAll || restrictor_ != kNone) {
     result.Append(media_type_);
     result.Append(" and ");
   }
@@ -74,7 +74,7 @@
 }
 
 std::unique_ptr<MediaQuery> MediaQuery::CreateNotAll() {
-  return std::make_unique<MediaQuery>(MediaQuery::kNot, MediaTypeNames::all,
+  return std::make_unique<MediaQuery>(MediaQuery::kNot, media_type_names::kAll,
                                       ExpressionHeapVector());
 }
 
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator.cc b/third_party/blink/renderer/core/css/media_query_evaluator.cc
index 5c6aa02..328dbfe 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator.cc
+++ b/third_party/blink/renderer/core/css/media_query_evaluator.cc
@@ -100,7 +100,7 @@
     const String& media_type_to_match) const {
   return media_type_to_match.IsEmpty() ||
          DeprecatedEqualIgnoringCase(media_type_to_match,
-                                     MediaTypeNames::all) ||
+                                     media_type_names::kAll) ||
          DeprecatedEqualIgnoringCase(media_type_to_match, MediaType());
 }
 
@@ -322,10 +322,10 @@
   // in the query. Thus, if if the document's media type is "print", the
   // media type of the query will either be "print" or "all".
   if (DeprecatedEqualIgnoringCase(media_values.MediaType(),
-                                  MediaTypeNames::screen)) {
+                                  media_type_names::kScreen)) {
     actual_resolution = clampTo<float>(media_values.DevicePixelRatio());
   } else if (DeprecatedEqualIgnoringCase(media_values.MediaType(),
-                                         MediaTypeNames::print)) {
+                                         media_type_names::kPrint)) {
     // The resolution of images while printing should not depend on the DPI
     // of the screen. Until we support proper ways of querying this info
     // we use 300px which is considered minimum for current printers.
@@ -752,7 +752,7 @@
                                  const MediaValues& media_values) {
   // Scan only applies to 'tv' media.
   if (!DeprecatedEqualIgnoringCase(media_values.MediaType(),
-                                   MediaTypeNames::tv))
+                                   media_type_names::kTv))
     return false;
 
   if (!value.IsValid())
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator_test.cc b/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
index b8eecf9..e2de522 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
+++ b/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
@@ -209,7 +209,7 @@
   data.primary_hover_type = kHoverTypeHover;
   data.default_font_size = 16;
   data.three_d_enabled = true;
-  data.media_type = MediaTypeNames::screen;
+  data.media_type = media_type_names::kScreen;
   data.strict_mode = true;
   data.display_mode = kWebDisplayModeBrowser;
   data.display_shape = kDisplayShapeRect;
@@ -228,11 +228,11 @@
 
   // Print values.
   {
-    data.media_type = MediaTypeNames::print;
+    data.media_type = media_type_names::kPrint;
     MediaValues* media_values = MediaValuesCached::Create(data);
     MediaQueryEvaluator media_query_evaluator(*media_values);
     TestMQEvaluator(g_print_test_cases, media_query_evaluator);
-    data.media_type = MediaTypeNames::screen;
+    data.media_type = media_type_names::kScreen;
   }
 
   // Monochrome values.
@@ -261,11 +261,11 @@
 TEST(MediaQueryEvaluatorTest, Dynamic) {
   std::unique_ptr<DummyPageHolder> page_holder =
       DummyPageHolder::Create(IntSize(500, 500));
-  page_holder->GetFrameView().SetMediaType(MediaTypeNames::screen);
+  page_holder->GetFrameView().SetMediaType(media_type_names::kScreen);
 
   MediaQueryEvaluator media_query_evaluator(&page_holder->GetFrame());
   TestMQEvaluator(g_viewport_test_cases, media_query_evaluator);
-  page_holder->GetFrameView().SetMediaType(MediaTypeNames::print);
+  page_holder->GetFrameView().SetMediaType(media_type_names::kPrint);
   TestMQEvaluator(g_print_test_cases, media_query_evaluator);
 }
 
@@ -304,7 +304,7 @@
 TEST(MediaQueryEvaluatorTest, InitialViewport) {
   std::unique_ptr<DummyPageHolder> page_holder =
       DummyPageHolder::Create(IntSize(500, 500));
-  page_holder->GetFrameView().SetMediaType(MediaTypeNames::screen);
+  page_holder->GetFrameView().SetMediaType(media_type_names::kScreen);
   page_holder->GetFrameView().SetLayoutSizeFixedToFrameSize(false);
   page_holder->GetFrameView().SetInitialViewportSize(IntSize(500, 500));
   page_holder->GetFrameView().SetLayoutSize(IntSize(800, 800));
@@ -318,7 +318,7 @@
 TEST(MediaQueryEvaluatorTest, DynamicImmersive) {
   std::unique_ptr<DummyPageHolder> page_holder =
       DummyPageHolder::Create(IntSize(500, 500));
-  page_holder->GetFrameView().SetMediaType(MediaTypeNames::screen);
+  page_holder->GetFrameView().SetMediaType(media_type_names::kScreen);
 
   MediaQueryEvaluator media_query_evaluator(&page_holder->GetFrame());
   page_holder->GetDocument().GetSettings()->SetImmersiveModeEnabled(false);
diff --git a/third_party/blink/renderer/core/css/media_query_matcher_test.cc b/third_party/blink/renderer/core/css/media_query_matcher_test.cc
index 8505778f..5c9b1d8 100644
--- a/third_party/blink/renderer/core/css/media_query_matcher_test.cc
+++ b/third_party/blink/renderer/core/css/media_query_matcher_test.cc
@@ -18,7 +18,7 @@
   MediaQueryMatcher* matcher =
       MediaQueryMatcher::Create(page_holder->GetDocument());
   scoped_refptr<MediaQuerySet> query_set =
-      MediaQuerySet::Create(MediaTypeNames::all);
+      MediaQuerySet::Create(media_type_names::kAll);
   ASSERT_TRUE(matcher->Evaluate(query_set.get()));
 
   matcher->DocumentDetached();
diff --git a/third_party/blink/renderer/core/css/media_type_names.json5 b/third_party/blink/renderer/core/css/media_type_names.json5
index 8e638e10..64cee27c 100644
--- a/third_party/blink/renderer/core/css/media_type_names.json5
+++ b/third_party/blink/renderer/core/css/media_type_names.json5
@@ -1,6 +1,6 @@
 {
   metadata: {
-    namespace: "MediaType",
+    namespace: "media_type_names",
     export: "CORE_EXPORT",
   },
 
diff --git a/third_party/blink/renderer/core/css/parser/media_query_parser.cc b/third_party/blink/renderer/core/css/parser/media_query_parser.cc
index 9539cc2..29235af 100644
--- a/third_party/blink/renderer/core/css/parser/media_query_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/media_query_parser.cc
@@ -288,12 +288,12 @@
 
 MediaQueryData::MediaQueryData()
     : restrictor_(MediaQuery::kNone),
-      media_type_(MediaTypeNames::all),
+      media_type_(media_type_names::kAll),
       media_type_set_(false) {}
 
 void MediaQueryData::Clear() {
   restrictor_ = MediaQuery::kNone;
-  media_type_ = MediaTypeNames::all;
+  media_type_ = media_type_names::kAll;
   media_type_set_ = false;
   media_feature_ = String();
   expressions_.clear();
diff --git a/third_party/blink/renderer/core/css/parser/sizes_attribute_parser_test.cc b/third_party/blink/renderer/core/css/parser/sizes_attribute_parser_test.cc
index d35078d..ef509ed1 100644
--- a/third_party/blink/renderer/core/css/parser/sizes_attribute_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/sizes_attribute_parser_test.cc
@@ -85,7 +85,7 @@
   data.primary_pointer_type = kPointerTypeFine;
   data.default_font_size = 16;
   data.three_d_enabled = true;
-  data.media_type = MediaTypeNames::screen;
+  data.media_type = media_type_names::kScreen;
   data.strict_mode = true;
   data.display_mode = kWebDisplayModeBrowser;
   MediaValues* media_values = MediaValuesCached::Create(data);
@@ -166,7 +166,7 @@
   data.primary_pointer_type = kPointerTypeFine;
   data.default_font_size = 16;
   data.three_d_enabled = true;
-  data.media_type = MediaTypeNames::screen;
+  data.media_type = media_type_names::kScreen;
   data.strict_mode = true;
   data.display_mode = kWebDisplayModeBrowser;
   MediaValues* media_values = MediaValuesCached::Create(data);
diff --git a/third_party/blink/renderer/core/css/parser/sizes_calc_parser_test.cc b/third_party/blink/renderer/core/css/parser/sizes_calc_parser_test.cc
index 2409810..56b3d919 100644
--- a/third_party/blink/renderer/core/css/parser/sizes_calc_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/sizes_calc_parser_test.cc
@@ -119,7 +119,7 @@
   data.primary_pointer_type = kPointerTypeFine;
   data.default_font_size = 16;
   data.three_d_enabled = true;
-  data.media_type = MediaTypeNames::screen;
+  data.media_type = media_type_names::kScreen;
   data.strict_mode = true;
   data.display_mode = kWebDisplayModeBrowser;
   MediaValues* media_values = MediaValuesCached::Create(data);
diff --git a/third_party/blink/renderer/core/css/resolver/scoped_style_resolver.cc b/third_party/blink/renderer/core/css/resolver/scoped_style_resolver.cc
index b60eabca..32b534b 100644
--- a/third_party/blink/renderer/core/css/resolver/scoped_style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/scoped_style_resolver.cc
@@ -206,7 +206,7 @@
     InvalidationRootForTreeScope(tree_scope)
         .SetNeedsStyleRecalc(kSubtreeStyleChange,
                              StyleChangeReasonForTracing::Create(
-                                 StyleChangeReason::kStyleSheetChange));
+                                 style_change_reason::kStyleSheetChange));
     return;
   }
 
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index a8ed17a4f..f036a58 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -1929,8 +1929,8 @@
 void StyleResolver::UpdateMediaType() {
   if (LocalFrameView* view = GetDocument().View()) {
     bool was_print = print_media_type_;
-    print_media_type_ =
-        DeprecatedEqualIgnoringCase(view->MediaType(), MediaTypeNames::print);
+    print_media_type_ = DeprecatedEqualIgnoringCase(view->MediaType(),
+                                                    media_type_names::kPrint);
     if (was_print != print_media_type_)
       matched_properties_cache_.ClearViewportDependent();
   }
diff --git a/third_party/blink/renderer/core/css/style_change_reason.cc b/third_party/blink/renderer/core/css/style_change_reason.cc
index a8ff0fa..705dc78 100644
--- a/third_party/blink/renderer/core/css/style_change_reason.cc
+++ b/third_party/blink/renderer/core/css/style_change_reason.cc
@@ -10,7 +10,7 @@
 
 namespace blink {
 
-namespace StyleChangeReason {
+namespace style_change_reason {
 const char kActiveStylesheetsUpdate[] = "ActiveStylesheetsUpdate";
 const char kAnimation[] = "Animation";
 const char kAttribute[] = "Attribute";
@@ -50,7 +50,7 @@
 const char kVisuallyOrdered[] = "VisuallyOrdered";
 const char kWritingModeChange[] = "WritingModeChange";
 const char kZoom[] = "Zoom";
-}  // namespace StyleChangeReason
+}  // namespace style_change_reason
 
 namespace StyleChangeExtraData {
 DEFINE_GLOBAL(AtomicString, g_active);
diff --git a/third_party/blink/renderer/core/css/style_change_reason.h b/third_party/blink/renderer/core/css/style_change_reason.h
index 06d03249..e738e08 100644
--- a/third_party/blink/renderer/core/css/style_change_reason.h
+++ b/third_party/blink/renderer/core/css/style_change_reason.h
@@ -13,7 +13,7 @@
 
 class QualifiedName;
 
-namespace StyleChangeReason {
+namespace style_change_reason {
 extern const char kActiveStylesheetsUpdate[];
 extern const char kAnimation[];
 extern const char kAttribute[];
@@ -50,7 +50,7 @@
 extern const char kVisuallyOrdered[];
 extern const char kWritingModeChange[];
 extern const char kZoom[];
-}  // namespace StyleChangeReason
+}  // namespace style_change_reason
 typedef const char StyleChangeReasonString[];
 
 namespace StyleChangeExtraData {
@@ -89,7 +89,7 @@
 
   static StyleChangeReasonForTracing FromAttribute(
       const QualifiedName& attribute_name) {
-    return StyleChangeReasonForTracing(StyleChangeReason::kAttribute,
+    return StyleChangeReasonForTracing(style_change_reason::kAttribute,
                                        attribute_name.LocalName());
   }
 
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index 6c945d4..3a76d08 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -342,7 +342,7 @@
   // TODO(futhark@chromium.org): Should be able to use RuleSetInvalidation here.
   GetDocument().SetNeedsStyleRecalc(
       kSubtreeStyleChange, StyleChangeReasonForTracing::Create(
-                               StyleChangeReason::kDeclarativeContent));
+                               style_change_reason::kDeclarativeContent));
 }
 
 bool StyleEngine::ShouldUpdateDocumentStyleSheetCollection() const {
@@ -748,7 +748,7 @@
     resolver_->InvalidateMatchedPropertiesCache();
   GetDocument().SetNeedsStyleRecalc(
       kSubtreeStyleChange,
-      StyleChangeReasonForTracing::Create(StyleChangeReason::kFonts));
+      StyleChangeReasonForTracing::Create(style_change_reason::kFonts));
   probe::fontsUpdated(document_, nullptr, String(), nullptr);
 }
 
@@ -765,7 +765,7 @@
     resolver_->InvalidateMatchedPropertiesCache();
   GetDocument().SetNeedsStyleRecalc(
       kSubtreeStyleChange, StyleChangeReasonForTracing::Create(
-                               StyleChangeReason::kPlatformColorChange));
+                               style_change_reason::kPlatformColorChange));
 }
 
 bool StyleEngine::ShouldSkipInvalidationFor(const Element& element) const {
@@ -1081,7 +1081,7 @@
     if (invalidation_set->InvalidatesTagName(host)) {
       host.SetNeedsStyleRecalc(kLocalStyleChange,
                                StyleChangeReasonForTracing::Create(
-                                   StyleChangeReason::kStyleSheetChange));
+                                   style_change_reason::kStyleSheetChange));
       return;
     }
   }
@@ -1114,7 +1114,7 @@
     if (node->IsElementNode()) {
       node->SetNeedsStyleRecalc(kLocalStyleChange,
                                 StyleChangeReasonForTracing::Create(
-                                    StyleChangeReason::kStyleSheetChange));
+                                    style_change_reason::kStyleSheetChange));
     }
   }
 }
@@ -1230,7 +1230,7 @@
 
   GetDocument().SetNeedsStyleRecalc(
       kSubtreeStyleChange,
-      StyleChangeReasonForTracing::Create(StyleChangeReason::kSettings));
+      StyleChangeReasonForTracing::Create(style_change_reason::kSettings));
 }
 
 void StyleEngine::InitialViewportChanged() {
@@ -1264,8 +1264,9 @@
     MarkDocumentDirty();
     resolver->SetNeedsAppendAllSheets();
     GetDocument().SetNeedsStyleRecalc(
-        kSubtreeStyleChange, StyleChangeReasonForTracing::Create(
-                                 StyleChangeReason::kActiveStylesheetsUpdate));
+        kSubtreeStyleChange,
+        StyleChangeReasonForTracing::Create(
+            style_change_reason::kActiveStylesheetsUpdate));
   }
 }
 
@@ -1313,8 +1314,9 @@
   if (!tree_scope.GetDocument().body() ||
       tree_scope.GetDocument().HasNodesWithPlaceholderStyle()) {
     tree_scope.GetDocument().SetNeedsStyleRecalc(
-        kSubtreeStyleChange, StyleChangeReasonForTracing::Create(
-                                 StyleChangeReason::kCleanupPlaceholderStyles));
+        kSubtreeStyleChange,
+        StyleChangeReasonForTracing::Create(
+            style_change_reason::kCleanupPlaceholderStyles));
     return;
   }
 
@@ -1330,8 +1332,9 @@
       ((changed_rule_flags & kFontFaceRules) &&
        tree_scope.RootNode().IsDocumentNode())) {
     invalidation_root.SetNeedsStyleRecalc(
-        kSubtreeStyleChange, StyleChangeReasonForTracing::Create(
-                                 StyleChangeReason::kActiveStylesheetsUpdate));
+        kSubtreeStyleChange,
+        StyleChangeReasonForTracing::Create(
+            style_change_reason::kActiveStylesheetsUpdate));
     return;
   }
 
@@ -1502,7 +1505,7 @@
   // TODO(timloh): Invalidate only elements with this custom property set
   GetDocument().SetNeedsStyleRecalc(
       kSubtreeStyleChange, StyleChangeReasonForTracing::Create(
-                               StyleChangeReason::kPropertyRegistration));
+                               style_change_reason::kPropertyRegistration));
   if (resolver_)
     resolver_->InvalidateMatchedPropertiesCache();
 }
@@ -1510,7 +1513,7 @@
 void StyleEngine::EnvironmentVariableChanged() {
   GetDocument().SetNeedsStyleRecalc(
       kSubtreeStyleChange, StyleChangeReasonForTracing::Create(
-                               StyleChangeReason::kPropertyRegistration));
+                               style_change_reason::kPropertyRegistration));
   if (resolver_)
     resolver_->InvalidateMatchedPropertiesCache();
 }
diff --git a/third_party/blink/renderer/core/dom/container_node.cc b/third_party/blink/renderer/core/dom/container_node.cc
index 9de720d..5b6cf73 100644
--- a/third_party/blink/renderer/core/dom/container_node.cc
+++ b/third_party/blink/renderer/core/dom/container_node.cc
@@ -1044,7 +1044,7 @@
   SetNeedsStyleRecalc(
       change_type,
       StyleChangeReasonForTracing::CreateWithExtraData(
-          StyleChangeReason::kPseudoClass, StyleChangeExtraData::g_focus));
+          style_change_reason::kPseudoClass, StyleChangeExtraData::g_focus));
 
   if (IsElementNode() && ToElement(this)->ChildrenOrSiblingsAffectedByFocus())
     ToElement(this)->PseudoStateChanged(CSSSelector::kPseudoFocus);
@@ -1063,7 +1063,7 @@
           : kLocalStyleChange;
   SetNeedsStyleRecalc(change_type,
                       StyleChangeReasonForTracing::CreateWithExtraData(
-                          StyleChangeReason::kPseudoClass,
+                          style_change_reason::kPseudoClass,
                           StyleChangeExtraData::g_focus_visible));
 
   if (IsElementNode() &&
@@ -1079,7 +1079,7 @@
             : kLocalStyleChange;
     SetNeedsStyleRecalc(change_type,
                         StyleChangeReasonForTracing::CreateWithExtraData(
-                            StyleChangeReason::kPseudoClass,
+                            style_change_reason::kPseudoClass,
                             StyleChangeExtraData::g_focus_within));
   }
   if (IsElementNode() &&
@@ -1117,13 +1117,14 @@
 
   // If :focus sets display: none, we lose focus but still need to recalc our
   // style.
-  if (IsElementNode() && ToElement(this)->ChildrenOrSiblingsAffectedByFocus())
+  if (IsElementNode() && ToElement(this)->ChildrenOrSiblingsAffectedByFocus()) {
     ToElement(this)->PseudoStateChanged(CSSSelector::kPseudoFocus);
-  else
+  } else {
     SetNeedsStyleRecalc(
         kLocalStyleChange,
         StyleChangeReasonForTracing::CreateWithExtraData(
-            StyleChangeReason::kPseudoClass, StyleChangeExtraData::g_focus));
+            style_change_reason::kPseudoClass, StyleChangeExtraData::g_focus));
+  }
 
   if (RuntimeEnabledFeatures::CSSFocusVisibleEnabled()) {
     if (IsElementNode() &&
@@ -1132,7 +1133,7 @@
     } else {
       SetNeedsStyleRecalc(kLocalStyleChange,
                           StyleChangeReasonForTracing::CreateWithExtraData(
-                              StyleChangeReason::kPseudoClass,
+                              style_change_reason::kPseudoClass,
                               StyleChangeExtraData::g_focus_visible));
     }
   }
@@ -1143,7 +1144,7 @@
   } else {
     SetNeedsStyleRecalc(kLocalStyleChange,
                         StyleChangeReasonForTracing::CreateWithExtraData(
-                            StyleChangeReason::kPseudoClass,
+                            style_change_reason::kPseudoClass,
                             StyleChangeExtraData::g_focus_within));
   }
 }
@@ -1164,13 +1165,14 @@
 
   if (!GetLayoutObject()) {
     if (IsElementNode() &&
-        ToElement(this)->ChildrenOrSiblingsAffectedByActive())
+        ToElement(this)->ChildrenOrSiblingsAffectedByActive()) {
       ToElement(this)->PseudoStateChanged(CSSSelector::kPseudoActive);
-    else
-      SetNeedsStyleRecalc(
-          kLocalStyleChange,
-          StyleChangeReasonForTracing::CreateWithExtraData(
-              StyleChangeReason::kPseudoClass, StyleChangeExtraData::g_active));
+    } else {
+      SetNeedsStyleRecalc(kLocalStyleChange,
+                          StyleChangeReasonForTracing::CreateWithExtraData(
+                              style_change_reason::kPseudoClass,
+                              StyleChangeExtraData::g_active));
+    }
     return;
   }
 
@@ -1182,7 +1184,7 @@
     SetNeedsStyleRecalc(
         change_type,
         StyleChangeReasonForTracing::CreateWithExtraData(
-            StyleChangeReason::kPseudoClass, StyleChangeExtraData::g_active));
+            style_change_reason::kPseudoClass, StyleChangeExtraData::g_active));
   }
   if (IsElementNode() && ToElement(this)->ChildrenOrSiblingsAffectedByActive())
     ToElement(this)->PseudoStateChanged(CSSSelector::kPseudoActive);
@@ -1201,13 +1203,16 @@
   if (!GetLayoutObject()) {
     if (new_value)
       return;
-    if (IsElementNode() && ToElement(this)->ChildrenOrSiblingsAffectedByDrag())
+    if (IsElementNode() &&
+        ToElement(this)->ChildrenOrSiblingsAffectedByDrag()) {
       ToElement(this)->PseudoStateChanged(CSSSelector::kPseudoDrag);
-    else
+
+    } else {
       SetNeedsStyleRecalc(
           kLocalStyleChange,
           StyleChangeReasonForTracing::CreateWithExtraData(
-              StyleChangeReason::kPseudoClass, StyleChangeExtraData::g_drag));
+              style_change_reason::kPseudoClass, StyleChangeExtraData::g_drag));
+    }
     return;
   }
 
@@ -1219,7 +1224,7 @@
     SetNeedsStyleRecalc(
         change_type,
         StyleChangeReasonForTracing::CreateWithExtraData(
-            StyleChangeReason::kPseudoClass, StyleChangeExtraData::g_drag));
+            style_change_reason::kPseudoClass, StyleChangeExtraData::g_drag));
   }
   if (IsElementNode() && ToElement(this)->ChildrenOrSiblingsAffectedByDrag())
     ToElement(this)->PseudoStateChanged(CSSSelector::kPseudoDrag);
@@ -1239,7 +1244,7 @@
     SetNeedsStyleRecalc(
         change_type,
         StyleChangeReasonForTracing::CreateWithExtraData(
-            StyleChangeReason::kPseudoClass, StyleChangeExtraData::g_hover));
+            style_change_reason::kPseudoClass, StyleChangeExtraData::g_hover));
   }
   if (IsElementNode() && ToElement(this)->ChildrenOrSiblingsAffectedByHover())
     ToElement(this)->PseudoStateChanged(CSSSelector::kPseudoHover);
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 5e947da..511d80a 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -1506,7 +1506,7 @@
 
   // Document's style depends on the content language.
   SetNeedsStyleRecalc(kSubtreeStyleChange, StyleChangeReasonForTracing::Create(
-                                               StyleChangeReason::kLanguage));
+                                               style_change_reason::kLanguage));
 }
 
 void Document::setXMLVersion(const String& version,
@@ -2582,7 +2582,7 @@
       // but here we need up-to-date style immediately.
       SetNeedsStyleRecalc(kSubtreeStyleChange,
                           StyleChangeReasonForTracing::Create(
-                              StyleChangeReason::kCleanupPlaceholderStyles));
+                              style_change_reason::kCleanupPlaceholderStyles));
     }
   }
   UpdateStyleAndLayoutTree();
@@ -2783,6 +2783,8 @@
 }
 
 void Document::Shutdown() {
+  if (num_canvases_ > 0)
+    UMA_HISTOGRAM_COUNTS_100("Blink.Canvas.NumCanvasesPerPage", num_canvases_);
   TRACE_EVENT0("blink", "Document::shutdown");
   CHECK(!frame_ || frame_->Tree().ChildCount() == 0);
   if (!IsActive())
@@ -4599,7 +4601,7 @@
   if (HasNodesWithPlaceholderStyle()) {
     SetNeedsStyleRecalc(kSubtreeStyleChange,
                         StyleChangeReasonForTracing::Create(
-                            StyleChangeReason::kCleanupPlaceholderStyles));
+                            style_change_reason::kCleanupPlaceholderStyles));
   }
 
   if (DidLayoutWithPendingStylesheets() &&
@@ -5787,7 +5789,7 @@
     visually_ordered_ = should_use_visual_ordering;
     SetNeedsStyleRecalc(kSubtreeStyleChange,
                         StyleChangeReasonForTracing::Create(
-                            StyleChangeReason::kVisuallyOrdered));
+                            style_change_reason::kVisuallyOrdered));
   }
 }
 
@@ -5912,7 +5914,7 @@
                              ? kNeedsReattachStyleChange
                              : kSubtreeStyleChange;
   SetNeedsStyleRecalc(type, StyleChangeReasonForTracing::Create(
-                                StyleChangeReason::kDesignMode));
+                                style_change_reason::kDesignMode));
 }
 
 Document* Document::ParentDocument() const {
@@ -7403,7 +7405,7 @@
       order == ShadowCascadeOrder::kShadowCascadeV1) {
     SetNeedsStyleRecalc(
         kSubtreeStyleChange,
-        StyleChangeReasonForTracing::Create(StyleChangeReason::kShadow));
+        StyleChangeReasonForTracing::Create(style_change_reason::kShadow));
     UseCounter::Count(*this, WebFeature::kMixedShadowRootV0AndV1);
   }
 
@@ -7717,6 +7719,10 @@
                          : message)));
 }
 
+void Document::IncrementNumberOfCanvases() {
+  num_canvases_++;
+}
+
 void Document::SendViolationReport(
     mojom::blink::CSPViolationParamsPtr violation_params) {
   std::unique_ptr<SourceLocation> source_location = SourceLocation::Create(
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index ade9ff25..34d79b4f 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1517,6 +1517,8 @@
     parsed_feature_policies_.QuickSet(static_cast<int>(feature));
   }
 
+  void IncrementNumberOfCanvases();
+
  protected:
   Document(const DocumentInit&, DocumentClassFlags = kDefaultDocumentClass);
 
@@ -1948,6 +1950,9 @@
   // This is set through feature policy 'vertical-scroll'.
   bool is_vertical_scroll_enforced_ = false;
 
+  // The number of canvas elements on the document
+  int num_canvases_ = 0;
+
   // A list of all the navigation_initiator bindings owned by this document.
   // Used to report CSP violations that result from CSP blocking
   // navigation requests that were initiated by this document.
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 97c4071..052b5e0 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -1268,7 +1268,7 @@
   if (old_value.IsNull() != new_value.IsNull()) {
     SetNeedsStyleRecalc(kLocalStyleChange,
                         StyleChangeReasonForTracing::Create(
-                            StyleChangeReason::kInvisibleChange));
+                            style_change_reason::kInvisibleChange));
   }
   if (EqualIgnoringASCIICase(old_value, "static") &&
       !IsInsideInvisibleStaticSubtree()) {
@@ -2532,7 +2532,7 @@
   shadow_root->InsertedInto(*this);
   SetChildNeedsStyleRecalc();
   SetNeedsStyleRecalc(kSubtreeStyleChange, StyleChangeReasonForTracing::Create(
-                                               StyleChangeReason::kShadow));
+                                               style_change_reason::kShadow));
 
   probe::didPushShadowRoot(this, shadow_root);
 
@@ -2576,7 +2576,7 @@
     return;
 
   SetNeedsStyleRecalc(kLocalStyleChange, StyleChangeReasonForTracing::Create(
-                                             StyleChangeReason::kAnimation));
+                                             style_change_reason::kAnimation));
   SetAnimationStyleChange(true);
 }
 
@@ -4802,14 +4802,14 @@
 
   SetNeedsStyleRecalc(kLocalStyleChange,
                       StyleChangeReasonForTracing::Create(
-                          StyleChangeReason::kStyleSheetChange));
+                          style_change_reason::kStyleSheetChange));
   probe::didInvalidateStyleAttr(this);
 }
 
 void Element::InlineStyleChanged() {
   DCHECK(IsStyledElement());
   SetNeedsStyleRecalc(kLocalStyleChange, StyleChangeReasonForTracing::Create(
-                                             StyleChangeReason::kInline));
+                                             style_change_reason::kInline));
   DCHECK(GetElementData());
   GetElementData()->style_attribute_is_dirty_ = true;
   probe::didInvalidateStyleAttr(this);
diff --git a/third_party/blink/renderer/core/dom/first_letter_pseudo_element.cc b/third_party/blink/renderer/core/dom/first_letter_pseudo_element.cc
index 4693d51..84cd075d 100644
--- a/third_party/blink/renderer/core/dom/first_letter_pseudo_element.cc
+++ b/third_party/blink/renderer/core/dom/first_letter_pseudo_element.cc
@@ -252,8 +252,9 @@
   // first letter, we need to UpdateFirstLetter to render the new first letter
   // or remove the ::first-letter pseudo if there is no text left. Do that as
   // part of a style recalc for this ::first-letter.
-  SetNeedsStyleRecalc(kLocalStyleChange, StyleChangeReasonForTracing::Create(
-                                             StyleChangeReason::kPseudoClass));
+  SetNeedsStyleRecalc(
+      kLocalStyleChange,
+      StyleChangeReasonForTracing::Create(style_change_reason::kPseudoClass));
 }
 
 void FirstLetterPseudoElement::AttachLayoutTree(AttachContext& context) {
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index 70040973..f54a326 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -465,7 +465,6 @@
 
 void Node::setDistributeScroll(V8ScrollStateCallback* scroll_state_callback,
                                const String& native_scroll_behavior) {
-  DCHECK(IsElementNode());
   GetScrollCustomizationCallbacks().SetDistributeScroll(
       this, ScrollStateCallbackV8Impl::Create(scroll_state_callback,
                                               native_scroll_behavior));
@@ -473,28 +472,23 @@
 
 void Node::setApplyScroll(V8ScrollStateCallback* scroll_state_callback,
                           const String& native_scroll_behavior) {
-  DCHECK(IsElementNode());
   SetApplyScroll(ScrollStateCallbackV8Impl::Create(scroll_state_callback,
                                                    native_scroll_behavior));
 }
 
 void Node::SetApplyScroll(ScrollStateCallback* scroll_state_callback) {
-  DCHECK(IsElementNode());
   GetScrollCustomizationCallbacks().SetApplyScroll(this, scroll_state_callback);
 }
 
 void Node::RemoveApplyScroll() {
-  DCHECK(IsElementNode());
   GetScrollCustomizationCallbacks().RemoveApplyScroll(this);
 }
 
 ScrollStateCallback* Node::GetApplyScroll() {
-  DCHECK(IsElementNode());
   return GetScrollCustomizationCallbacks().GetApplyScroll(this);
 }
 
 void Node::NativeDistributeScroll(ScrollState& scroll_state) {
-  DCHECK(IsElementNode());
   if (scroll_state.FullyConsumed())
     return;
 
@@ -517,10 +511,11 @@
 }
 
 void Node::NativeApplyScroll(ScrollState& scroll_state) {
-  DCHECK(IsElementNode());
+  if (!GetLayoutObject())
+    return;
 
   // All elements in the scroll chain should be boxes.
-  DCHECK(!GetLayoutObject() || GetLayoutObject()->IsBox());
+  DCHECK(GetLayoutObject()->IsBox());
 
   if (scroll_state.FullyConsumed())
     return;
@@ -534,15 +529,7 @@
   // updateStyleAndLayoutIgnorePendingStylesheetsForNode.
   GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets();
 
-  LayoutBox* box_to_scroll = nullptr;
-
-  if (this == GetDocument().documentElement())
-    box_to_scroll = GetDocument().GetLayoutView();
-  else if (GetLayoutObject())
-    box_to_scroll = ToLayoutBox(GetLayoutObject());
-
-  if (!box_to_scroll)
-    return;
+  LayoutBox* box_to_scroll = ToLayoutBox(GetLayoutObject());
 
   ScrollableArea* scrollable_area =
       box_to_scroll->EnclosingBox()->GetScrollableArea();
@@ -570,7 +557,6 @@
 
 void Node::CallDistributeScroll(ScrollState& scroll_state) {
   TRACE_EVENT0("input", "Node::CallDistributeScroll");
-  DCHECK(IsElementNode());
   ScrollStateCallback* callback =
       GetScrollCustomizationCallbacks().GetDistributeScroll(this);
 
@@ -606,7 +592,6 @@
 
 void Node::CallApplyScroll(ScrollState& scroll_state) {
   TRACE_EVENT0("input", "Node::CallApplyScroll");
-  DCHECK(IsElementNode());
   // Hits ASSERTs when trying to determine whether we need to scroll on main
   // or CC. http://crbug.com/625676.
   DisableCompositingQueryAsserts disabler;
@@ -651,7 +636,6 @@
 
 void Node::WillBeginCustomizedScrollPhase(
     ScrollCustomization::ScrollDirection direction) {
-  DCHECK(IsElementNode());
   DCHECK(!GetScrollCustomizationCallbacks().InScrollPhase(this));
   LayoutBox* box = GetLayoutBox();
   if (!box)
@@ -665,7 +649,6 @@
 }
 
 void Node::DidEndCustomizedScrollPhase() {
-  DCHECK(IsElementNode());
   GetScrollCustomizationCallbacks().SetInScrollPhase(this, false);
 }
 
@@ -2919,6 +2902,11 @@
   }
 }
 
+bool Node::IsEffectiveRootScroller() const {
+  return GetLayoutObject() ? GetLayoutObject()->IsEffectiveRootScroller()
+                           : false;
+}
+
 WebPluginContainerImpl* Node::GetWebPluginContainer() const {
   if (!IsHTMLObjectElement(this) && !IsHTMLEmbedElement(this)) {
     return nullptr;
diff --git a/third_party/blink/renderer/core/dom/node.h b/third_party/blink/renderer/core/dom/node.h
index acd0c8c0..5ca4648 100644
--- a/third_party/blink/renderer/core/dom/node.h
+++ b/third_party/blink/renderer/core/dom/node.h
@@ -862,6 +862,8 @@
     return GetFlag(kInDOMNodeRemovedHandler);
   }
 
+  bool IsEffectiveRootScroller() const;
+
   // If the node is a plugin, then this returns its WebPluginContainer.
   WebPluginContainerImpl* GetWebPluginContainer() const;
 
diff --git a/third_party/blink/renderer/core/dom/tree_scope.cc b/third_party/blink/renderer/core/dom/tree_scope.cc
index 4c6894b..590dfda 100644
--- a/third_party/blink/renderer/core/dom/tree_scope.cc
+++ b/third_party/blink/renderer/core/dom/tree_scope.cc
@@ -617,10 +617,11 @@
     if (ShadowRoot* root = element->GetShadowRoot())
       root->SetNeedsStyleRecalcForViewportUnits();
     const ComputedStyle* style = element->GetComputedStyle();
-    if (style && style->HasViewportUnits())
+    if (style && style->HasViewportUnits()) {
       element->SetNeedsStyleRecalc(kLocalStyleChange,
                                    StyleChangeReasonForTracing::Create(
-                                       StyleChangeReason::kViewportUnits));
+                                       style_change_reason::kViewportUnits));
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/core/dom/v0_insertion_point.cc b/third_party/blink/renderer/core/dom/v0_insertion_point.cc
index f086e03..db1550c 100644
--- a/third_party/blink/renderer/core/dom/v0_insertion_point.cc
+++ b/third_party/blink/renderer/core/dom/v0_insertion_point.cc
@@ -159,7 +159,7 @@
     node->SetNeedsStyleRecalc(
         style_change_type,
         StyleChangeReasonForTracing::Create(
-            StyleChangeReason::kPropagateInheritChangeToDistributedNodes));
+            style_change_reason::kPropagateInheritChangeToDistributedNodes));
   }
 }
 
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index 12908cc..5416754 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -3442,10 +3442,21 @@
   scale = web_view_impl->PageScaleFactor();
 }
 
+WebRect ComputeBlockBoundHelper(WebViewImpl* web_view_impl,
+                                WebPoint point,
+                                bool ignore_clipping) {
+  DCHECK(web_view_impl->MainFrameImpl());
+  WebFrameWidgetBase* widget =
+      web_view_impl->MainFrameImpl()->FrameWidgetImpl();
+  DCHECK(widget);
+  return widget->ComputeBlockBound(point, ignore_clipping);
+}
+
 void SimulateDoubleTap(WebViewImpl* web_view_impl,
                        WebPoint& point,
                        float& scale) {
-  web_view_impl->AnimateDoubleTapZoom(point);
+  web_view_impl->AnimateDoubleTapZoom(
+      point, ComputeBlockBoundHelper(web_view_impl, point, false));
   EXPECT_TRUE(web_view_impl->FakeDoubleTapAnimationPendingForTesting());
   SimulatePageScale(web_view_impl, scale);
 }
@@ -3478,8 +3489,8 @@
       double_tap_zoom_already_legible_ratio;
 
   // Test double-tap zooming into wide div.
-  WebRect wide_block_bound = web_view_helper.GetWebView()->ComputeBlockBound(
-      double_tap_point_wide, false);
+  WebRect wide_block_bound = ComputeBlockBoundHelper(
+      web_view_helper.GetWebView(), double_tap_point_wide, false);
   web_view_helper.GetWebView()->ComputeScaleAndScrollForBlockRect(
       WebPoint(double_tap_point_wide.x, double_tap_point_wide.y),
       wide_block_bound, kTouchPointPadding,
@@ -3493,8 +3504,8 @@
   SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), scroll, scale);
 
   // Test zoom out back to minimum scale.
-  wide_block_bound = web_view_helper.GetWebView()->ComputeBlockBound(
-      double_tap_point_wide, false);
+  wide_block_bound = ComputeBlockBoundHelper(web_view_helper.GetWebView(),
+                                             double_tap_point_wide, false);
   web_view_helper.GetWebView()->ComputeScaleAndScrollForBlockRect(
       WebPoint(double_tap_point_wide.x, double_tap_point_wide.y),
       wide_block_bound, kTouchPointPadding,
@@ -3506,8 +3517,8 @@
                              scale);
 
   // Test double-tap zooming into tall div.
-  WebRect tall_block_bound = web_view_helper.GetWebView()->ComputeBlockBound(
-      double_tap_point_tall, false);
+  WebRect tall_block_bound = ComputeBlockBoundHelper(
+      web_view_helper.GetWebView(), double_tap_point_tall, false);
   web_view_helper.GetWebView()->ComputeScaleAndScrollForBlockRect(
       WebPoint(double_tap_point_tall.x, double_tap_point_tall.y),
       tall_block_bound, kTouchPointPadding,
@@ -3578,7 +3589,7 @@
   WebPoint scroll;
 
   WebRect block_bound =
-      web_view_helper.GetWebView()->ComputeBlockBound(point, true);
+      ComputeBlockBoundHelper(web_view_helper.GetWebView(), point, true);
   web_view_helper.GetWebView()->ComputeScaleAndScrollForBlockRect(
       point, block_bound, 0, 1.0f, scale, scroll);
   EXPECT_EQ(scale, 1.0f);
@@ -3641,7 +3652,10 @@
   web_view_helper.GetWebView()->ApplyViewportChanges(
       {gfx::ScrollOffset(), gfx::Vector2dF(), 1.1f, 0,
        cc::BrowserControlsState::kBoth});
-  web_view_helper.GetWebView()->AnimateDoubleTapZoom(top_point);
+
+  WebRect block_bounds =
+      ComputeBlockBoundHelper(web_view_helper.GetWebView(), top_point, false);
+  web_view_helper.GetWebView()->AnimateDoubleTapZoom(top_point, block_bounds);
   EXPECT_TRUE(
       web_view_helper.GetWebView()->FakeDoubleTapAnimationPendingForTesting());
   SimulateDoubleTap(web_view_helper.GetWebView(), bottom_point, scale);
@@ -3984,32 +3998,32 @@
   IntRect rect_right_bottom = IntRect(110, 110, 80, 80);
   IntRect block_bound;
 
-  block_bound = IntRect(
-      web_view_helper.GetWebView()->ComputeBlockBound(WebPoint(9, 9), true));
+  block_bound = IntRect(ComputeBlockBoundHelper(web_view_helper.GetWebView(),
+                                                WebPoint(9, 9), true));
   EXPECT_EQ(rect_back, block_bound);
 
-  block_bound = IntRect(
-      web_view_helper.GetWebView()->ComputeBlockBound(WebPoint(10, 10), true));
+  block_bound = IntRect(ComputeBlockBoundHelper(web_view_helper.GetWebView(),
+                                                WebPoint(10, 10), true));
   EXPECT_EQ(rect_left_top, block_bound);
 
-  block_bound = IntRect(
-      web_view_helper.GetWebView()->ComputeBlockBound(WebPoint(50, 50), true));
+  block_bound = IntRect(ComputeBlockBoundHelper(web_view_helper.GetWebView(),
+                                                WebPoint(50, 50), true));
   EXPECT_EQ(rect_left_top, block_bound);
 
-  block_bound = IntRect(
-      web_view_helper.GetWebView()->ComputeBlockBound(WebPoint(89, 89), true));
+  block_bound = IntRect(ComputeBlockBoundHelper(web_view_helper.GetWebView(),
+                                                WebPoint(89, 89), true));
   EXPECT_EQ(rect_left_top, block_bound);
 
-  block_bound = IntRect(
-      web_view_helper.GetWebView()->ComputeBlockBound(WebPoint(90, 90), true));
+  block_bound = IntRect(ComputeBlockBoundHelper(web_view_helper.GetWebView(),
+                                                WebPoint(90, 90), true));
   EXPECT_EQ(rect_back, block_bound);
 
-  block_bound = IntRect(web_view_helper.GetWebView()->ComputeBlockBound(
-      WebPoint(109, 109), true));
+  block_bound = IntRect(ComputeBlockBoundHelper(web_view_helper.GetWebView(),
+                                                WebPoint(109, 109), true));
   EXPECT_EQ(rect_back, block_bound);
 
-  block_bound = IntRect(web_view_helper.GetWebView()->ComputeBlockBound(
-      WebPoint(110, 110), true));
+  block_bound = IntRect(ComputeBlockBoundHelper(web_view_helper.GetWebView(),
+                                                WebPoint(110, 110), true));
   EXPECT_EQ(rect_right_bottom, block_bound);
 }
 
@@ -11839,7 +11853,9 @@
   // Double-tap on the target. Expect that we zoom in and the target is
   // contained in the visual viewport.
   {
-    WebView().AnimateDoubleTapZoom(WebPoint(445, 455));
+    WebPoint point(445, 455);
+    WebRect block_bounds = ComputeBlockBoundHelper(&WebView(), point, false);
+    WebView().AnimateDoubleTapZoom(point, block_bounds);
     EXPECT_TRUE(WebView().FakeDoubleTapAnimationPendingForTesting());
     ScrollOffset new_offset = ToScrollOffset(
         FloatPoint(WebView().FakePageScaleAnimationTargetPositionForTesting()));
@@ -11859,7 +11875,9 @@
   // Double-tap on the target again. We should zoom out and the target should
   // remain on screen.
   {
-    WebView().AnimateDoubleTapZoom(WebPoint(445, 455));
+    WebPoint point(445, 455);
+    WebRect block_bounds = ComputeBlockBoundHelper(&WebView(), point, false);
+    WebView().AnimateDoubleTapZoom(point, block_bounds);
     EXPECT_TRUE(WebView().FakeDoubleTapAnimationPendingForTesting());
     FloatPoint target_offset(
         WebView().FakePageScaleAnimationTargetPositionForTesting());
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 012520c..5cb0fd2 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -542,8 +542,14 @@
     case WebInputEvent::kGestureDoubleTap:
       if (web_settings_->DoubleTapToZoomEnabled() &&
           MinimumPageScaleFactor() != MaximumPageScaleFactor()) {
-        AnimateDoubleTapZoom(
-            FlooredIntPoint(scaled_event.PositionInRootFrame()));
+        if (auto* main_frame = MainFrameImpl()) {
+          IntPoint pos_in_root_frame =
+              FlooredIntPoint(scaled_event.PositionInRootFrame());
+          WebRect block_bounds =
+              main_frame->FrameWidgetImpl()->ComputeBlockBound(
+                  pos_in_root_frame, false);
+          AnimateDoubleTapZoom(pos_in_root_frame, block_bounds);
+        }
       }
       event_result = WebInputEventResult::kHandledSystem;
       WidgetClient()->DidHandleGestureEvent(event, event_cancelled);
@@ -881,43 +887,6 @@
   return WebInputEventResult::kNotHandled;
 }
 
-WebRect WebViewImpl::ComputeBlockBound(const WebPoint& point_in_root_frame,
-                                       bool ignore_clipping) {
-  if (!MainFrameImpl())
-    return WebRect();
-
-  // Use the point-based hit test to find the node.
-  HitTestLocation location(
-      MainFrameImpl()->GetFrameView()->ConvertFromRootFrame(
-          LayoutPoint(point_in_root_frame)));
-  HitTestRequest::HitTestRequestType hit_type =
-      HitTestRequest::kReadOnly | HitTestRequest::kActive |
-      (ignore_clipping ? HitTestRequest::kIgnoreClipping : 0);
-  HitTestResult result =
-      MainFrameImpl()->GetFrame()->GetEventHandler().HitTestResultAtLocation(
-          location, hit_type);
-  result.SetToShadowHostIfInRestrictedShadowRoot();
-
-  Node* node = result.InnerNodeOrImageMapImage();
-  if (!node)
-    return WebRect();
-
-  // Find the block type node based on the hit node.
-  // FIXME: This wants to walk flat tree with
-  // LayoutTreeBuilderTraversal::parent().
-  while (node &&
-         (!node->GetLayoutObject() || node->GetLayoutObject()->IsInline()))
-    node = LayoutTreeBuilderTraversal::Parent(*node);
-
-  // Return the bounding box in the root frame's coordinate space.
-  if (node) {
-    IntRect absolute_rect = node->GetLayoutObject()->AbsoluteBoundingBoxRect();
-    LocalFrame* frame = node->GetDocument().GetFrame();
-    return frame->View()->ConvertToRootFrame(absolute_rect);
-  }
-  return WebRect();
-}
-
 WebRect WebViewImpl::WidenRectWithinPageBounds(const WebRect& source,
                                                int target_margin,
                                                int minimum_margin) {
@@ -1127,12 +1096,10 @@
   UpdateAllLifecyclePhases();
 }
 
-void WebViewImpl::AnimateDoubleTapZoom(const IntPoint& point_in_root_frame) {
-  // TODO(lukasza): https://crbug.com/734209: Add OOPIF support.
-  if (!MainFrameImpl())
-    return;
+void WebViewImpl::AnimateDoubleTapZoom(const IntPoint& point_in_root_frame,
+                                       const WebRect& block_bounds) {
+  DCHECK(MainFrameImpl());
 
-  WebRect block_bounds = ComputeBlockBound(point_in_root_frame, false);
   float scale;
   WebPoint scroll;
 
@@ -1179,7 +1146,7 @@
   if (!MainFrameImpl())
     return;
 
-  WebRect block_bounds = ComputeBlockBound(
+  WebRect block_bounds = MainFrameImpl()->FrameWidgetImpl()->ComputeBlockBound(
       WebPoint(rect_in_root_frame.x + rect_in_root_frame.width / 2,
                rect_in_root_frame.y + rect_in_root_frame.height / 2),
       true);
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h
index 8b0bde13..235aea0 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -359,7 +359,7 @@
   void EnableTapHighlightAtPoint(
       const GestureEventWithHitTestResults& targeted_tap_event);
   void EnableTapHighlights(HeapVector<Member<Node>>&);
-  void AnimateDoubleTapZoom(const IntPoint&);
+  void AnimateDoubleTapZoom(const IntPoint&, const WebRect& block_bounds);
 
   void EnableFakePageScaleAnimationForTesting(bool);
   bool FakeDoubleTapAnimationPendingForTesting() const {
@@ -388,9 +388,6 @@
 
   WebSettingsImpl* SettingsImpl();
 
-  // Returns the bounding box of the block type node touched by the WebPoint.
-  WebRect ComputeBlockBound(const WebPoint&, bool ignore_clipping);
-
   WebLayerTreeView* LayerTreeView() const { return layer_tree_view_; }
   CompositorAnimationHost* AnimationHost() const {
     return animation_host_.get();
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 65c604f..5a1134e9 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -574,7 +574,7 @@
     GetDocument()->documentElement()->SetNeedsStyleRecalc(
         kSubtreeStyleChange,
         StyleChangeReasonForTracing::Create(
-            StyleChangeReason::kInheritedStyleChangeFromParentFrame));
+            style_change_reason::kInheritedStyleChangeFromParentFrame));
   }
 }
 
@@ -755,7 +755,7 @@
   document->MediaQueryAffectingValueChanged();
   document->SetNeedsStyleRecalc(
       kSubtreeStyleChange,
-      StyleChangeReasonForTracing::Create(StyleChangeReason::kZoom));
+      StyleChangeReasonForTracing::Create(style_change_reason::kZoom));
   document->UpdateStyleAndLayoutIgnorePendingStylesheets();
 }
 
@@ -763,7 +763,7 @@
   GetDocument()->MediaQueryAffectingValueChanged();
   GetDocument()->SetNeedsStyleRecalc(
       kSubtreeStyleChange,
-      StyleChangeReasonForTracing::Create(StyleChangeReason::kZoom));
+      StyleChangeReasonForTracing::Create(style_change_reason::kZoom));
   for (Frame* child = Tree().FirstChild(); child;
        child = child->Tree().NextSibling()) {
     if (child->IsLocalFrame())
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 9ab9830a..04d6fcd6 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -232,7 +232,7 @@
       first_layout_(true),
       base_background_color_(Color::kWhite),
       last_zoom_factor_(1.0f),
-      media_type_(MediaTypeNames::screen),
+      media_type_(media_type_names::kScreen),
       safe_to_propagate_scroll_to_parent_(true),
       visually_non_empty_character_count_(0),
       visually_non_empty_pixel_count_(0),
@@ -1139,7 +1139,7 @@
   if (printing) {
     if (media_type_when_not_printing_.IsNull())
       media_type_when_not_printing_ = MediaType();
-    SetMediaType(MediaTypeNames::print);
+    SetMediaType(media_type_names::kPrint);
   } else {
     if (!media_type_when_not_printing_.IsNull())
       SetMediaType(media_type_when_not_printing_);
@@ -1148,7 +1148,7 @@
 
   frame_->GetDocument()->SetNeedsStyleRecalc(
       kSubtreeStyleChange, StyleChangeReasonForTracing::Create(
-                               StyleChangeReason::kStyleSheetChange));
+                               style_change_reason::kStyleSheetChange));
 }
 
 void LocalFrameView::AddBackgroundAttachmentFixedObject(LayoutObject* object) {
@@ -3547,14 +3547,12 @@
       !frame_->GetDocument() || !frame_->GetPage())
     return false;
 
-  const TopDocumentRootScrollerController& controller =
-      frame_->GetPage()->GlobalRootScrollerController();
-
   if (!LayoutViewport())
     return false;
 
-  return root_scroller_util::ScrollableAreaForRootScroller(
-             controller.GlobalRootScroller()) == LayoutViewport();
+  const TopDocumentRootScrollerController& controller =
+      frame_->GetPage()->GlobalRootScrollerController();
+  return controller.RootScrollerArea() == LayoutViewport();
 }
 
 AXObjectCache* LocalFrameView::ExistingAXObjectCache() const {
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
index 69ab774..35d8e07 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/blink/public/web/web_widget_client.h"
 #include "third_party/blink/renderer/core/dom/element.h"
+#include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h"
 #include "third_party/blink/renderer/core/dom/user_gesture_indicator.h"
 #include "third_party/blink/renderer/core/events/web_input_event_conversion.h"
 #include "third_party/blink/renderer/core/events/wheel_event.h"
@@ -21,6 +22,9 @@
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/input/context_menu_allowed_scope.h"
 #include "third_party/blink/renderer/core/input/event_handler.h"
+#include "third_party/blink/renderer/core/layout/hit_test_location.h"
+#include "third_party/blink/renderer/core/layout/hit_test_request.h"
+#include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/page/context_menu_controller.h"
 #include "third_party/blink/renderer/core/page/drag_actions.h"
 #include "third_party/blink/renderer/core/page/drag_controller.h"
@@ -67,6 +71,39 @@
   return local_root_;
 }
 
+WebRect WebFrameWidgetBase::ComputeBlockBound(
+    const WebPoint& point_in_root_frame,
+    bool ignore_clipping) const {
+  HitTestLocation location(local_root_->GetFrameView()->ConvertFromRootFrame(
+      LayoutPoint(point_in_root_frame)));
+  HitTestRequest::HitTestRequestType hit_type =
+      HitTestRequest::kReadOnly | HitTestRequest::kActive |
+      (ignore_clipping ? HitTestRequest::kIgnoreClipping : 0);
+  HitTestResult result =
+      local_root_->GetFrame()->GetEventHandler().HitTestResultAtLocation(
+          location, hit_type);
+  result.SetToShadowHostIfInRestrictedShadowRoot();
+
+  Node* node = result.InnerNodeOrImageMapImage();
+  if (!node)
+    return WebRect();
+
+  // Find the block type node based on the hit node.
+  // FIXME: This wants to walk flat tree with
+  // LayoutTreeBuilderTraversal::parent().
+  while (node &&
+         (!node->GetLayoutObject() || node->GetLayoutObject()->IsInline()))
+    node = LayoutTreeBuilderTraversal::Parent(*node);
+
+  // Return the bounding box in the root frame's coordinate space.
+  if (node) {
+    IntRect absolute_rect = node->GetLayoutObject()->AbsoluteBoundingBoxRect();
+    LocalFrame* frame = node->GetDocument().GetFrame();
+    return frame->View()->ConvertToRootFrame(absolute_rect);
+  }
+  return WebRect();
+}
+
 void WebFrameWidgetBase::UpdateAllLifecyclePhasesAndCompositeForTesting(
     bool do_raster) {
   if (WebLayerTreeView* layer_tree_view = GetLayerTreeView()) {
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.h b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
index 8fa6222..d497f5b 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
@@ -44,6 +44,10 @@
   WebWidgetClient* Client() const { return client_; }
   WebLocalFrameImpl* LocalRootImpl() const { return local_root_; }
 
+  // Returns the bounding box of the block type node touched by the WebPoint.
+  WebRect ComputeBlockBound(const WebPoint& point_in_root_frame,
+                            bool ignore_clipping) const;
+
   void BindLocalRoot(WebLocalFrame&);
 
   // Called once the local root is bound via |BindLocalRoot()|.
diff --git a/third_party/blink/renderer/core/fullscreen/fullscreen.cc b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
index 5c6c80c..17a5d43 100644
--- a/third_party/blink/renderer/core/fullscreen/fullscreen.cc
+++ b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
@@ -967,7 +967,7 @@
   // bit surprising.
   element.SetNeedsStyleRecalc(
       kLocalStyleChange,
-      StyleChangeReasonForTracing::Create(StyleChangeReason::kFullscreen));
+      StyleChangeReasonForTracing::Create(style_change_reason::kFullscreen));
 }
 
 void Fullscreen::ElementRemoved(Element& node) {
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc b/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc
index d7520c1..ad62893 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc
@@ -340,7 +340,7 @@
                         WrapPersistent(this)));
 
     } else {
-      BackgroundScheduler::PostOnBackgroundThread(
+      background_scheduler::PostOnBackgroundThread(
           FROM_HERE,
           CrossThreadBind(&CanvasAsyncBlobCreator::EncodeImageOnEncoderThread,
                           WrapCrossThreadPersistent(this), quality));
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
index a85eb829..33d54c6 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
@@ -11,12 +11,25 @@
 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/histogram.h"
 #include "third_party/skia/include/core/SkSurface.h"
 
 namespace blink {
 
 CanvasRenderingContextHost::CanvasRenderingContextHost() = default;
 
+void CanvasRenderingContextHost::RecordCanvasSizeToUMA(unsigned width,
+                                                       unsigned height,
+                                                       bool isOffscreen) {
+  if (isOffscreen) {
+    UMA_HISTOGRAM_CUSTOM_COUNTS("Blink.OffscreenCanvas.SqrtNumberOfPixels",
+                                std::sqrt(width * height), 1, 5000, 100);
+  } else {
+    UMA_HISTOGRAM_CUSTOM_COUNTS("Blink.Canvas.SqrtNumberOfPixels",
+                                std::sqrt(width * height), 1, 5000, 100);
+  }
+}
+
 scoped_refptr<StaticBitmapImage>
 CanvasRenderingContextHost::CreateTransparentImage(const IntSize& size) const {
   if (!IsValidImageSize(size))
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h
index cc61448..8c56614 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h
@@ -34,6 +34,9 @@
  public:
   CanvasRenderingContextHost();
 
+  void static RecordCanvasSizeToUMA(unsigned width,
+                                    unsigned height,
+                                    bool isOffscreen);
   virtual void DetachContext() = 0;
 
   virtual void DidDraw(const FloatRect& rect) = 0;
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index 0558a0b..bf37efe 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -134,7 +134,11 @@
       externally_allocated_memory_(0),
       gpu_readback_invoked_in_current_frame_(false),
       gpu_readback_successive_frames_(0) {
+  CanvasRenderingContextHost::RecordCanvasSizeToUMA(size_.Width(),
+                                                    size_.Height(), false);
   UseCounter::Count(document, WebFeature::kHTMLCanvasElement);
+
+  GetDocument().IncrementNumberOfCanvases();
 }
 
 DEFINE_NODE_FACTORY(HTMLCanvasElement)
@@ -567,6 +571,9 @@
   IntSize old_size = Size();
   IntSize new_size(w, h);
 
+  if (old_size != new_size)
+    CanvasRenderingContextHost::RecordCanvasSizeToUMA(w, h, false);
+
   // If the size of an existing buffer matches, we can just clear it instead of
   // reallocating.  This optimization is only done for 2D canvases for now.
   if (had_resource_provider && old_size == new_size && Is2d()) {
diff --git a/third_party/blink/renderer/core/html/custom/custom_element_definition.cc b/third_party/blink/renderer/core/html/custom/custom_element_definition.cc
index 2764e2e..b1db1c30 100644
--- a/third_party/blink/renderer/core/html/custom/custom_element_definition.cc
+++ b/third_party/blink/renderer/core/html/custom/custom_element_definition.cc
@@ -230,9 +230,9 @@
     for (CSSStyleSheet* sheet : default_styles)
       sheet->AddToCustomElementTagNames(local_tag_name);
   }
-  element.SetNeedsStyleRecalc(kLocalStyleChange,
-                              StyleChangeReasonForTracing::Create(
-                                  StyleChangeReason::kActiveStylesheetsUpdate));
+  element.SetNeedsStyleRecalc(
+      kLocalStyleChange, StyleChangeReasonForTracing::Create(
+                             style_change_reason::kActiveStylesheetsUpdate));
 }
 
 bool CustomElementDefinition::HasAttributeChangedCallback(
diff --git a/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc b/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc
index 5289eb4f..96d9678 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc
+++ b/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc
@@ -764,7 +764,7 @@
     }
     SetNeedsStyleRecalc(
         kSubtreeStyleChange,
-        StyleChangeReasonForTracing::Create(StyleChangeReason::kControl));
+        StyleChangeReasonForTracing::Create(style_change_reason::kControl));
   }
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/date_time_field_element.cc b/third_party/blink/renderer/core/html/forms/date_time_field_element.cc
index ac6bc06..9c184ebe 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_field_element.cc
+++ b/third_party/blink/renderer/core/html/forms/date_time_field_element.cc
@@ -199,7 +199,7 @@
   SetNeedsStyleRecalc(
       kSubtreeStyleChange,
       StyleChangeReasonForTracing::CreateWithExtraData(
-          StyleChangeReason::kPseudoClass, StyleChangeExtraData::g_disabled));
+          style_change_reason::kPseudoClass, StyleChangeExtraData::g_disabled));
 }
 
 bool DateTimeFieldElement::SupportsFocus() const {
diff --git a/third_party/blink/renderer/core/html/forms/file_input_type.cc b/third_party/blink/renderer/core/html/forms/file_input_type.cc
index c6210186..a681735 100644
--- a/third_party/blink/renderer/core/html/forms/file_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/file_input_type.cc
@@ -229,7 +229,7 @@
   file_list_->clear();
   GetElement().SetNeedsStyleRecalc(
       kSubtreeStyleChange,
-      StyleChangeReasonForTracing::Create(StyleChangeReason::kControlValue));
+      StyleChangeReasonForTracing::Create(style_change_reason::kControlValue));
   GetElement().SetNeedsValidityCheck();
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/html_input_element.cc b/third_party/blink/renderer/core/html/forms/html_input_element.cc
index cc0a986..e4d0a7f 100644
--- a/third_party/blink/renderer/core/html/forms/html_input_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_input_element.cc
@@ -1080,7 +1080,7 @@
   TextControlElement::SetSuggestedValue(SanitizeValue(value));
   SetNeedsStyleRecalc(
       kSubtreeStyleChange,
-      StyleChangeReasonForTracing::Create(StyleChangeReason::kControlValue));
+      StyleChangeReasonForTracing::Create(style_change_reason::kControlValue));
   input_type_view_->UpdateView();
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/html_text_area_element.cc b/third_party/blink/renderer/core/html/forms/html_text_area_element.cc
index 0cf9a4bb..9c1006c 100644
--- a/third_party/blink/renderer/core/html/forms/html_text_area_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_text_area_element.cc
@@ -418,7 +418,7 @@
   UpdatePlaceholderVisibility();
   SetNeedsStyleRecalc(
       kSubtreeStyleChange,
-      StyleChangeReasonForTracing::Create(StyleChangeReason::kControlValue));
+      StyleChangeReasonForTracing::Create(style_change_reason::kControlValue));
   SetNeedsValidityCheck();
   if (IsFinishedParsingChildren() &&
       selection == TextControlSetValueSelection::kSetSelectionToEnd) {
@@ -482,7 +482,7 @@
   TextControlElement::SetSuggestedValue(value);
   SetNeedsStyleRecalc(
       kSubtreeStyleChange,
-      StyleChangeReasonForTracing::Create(StyleChangeReason::kControlValue));
+      StyleChangeReasonForTracing::Create(style_change_reason::kControlValue));
 }
 
 String HTMLTextAreaElement::validationMessage() const {
diff --git a/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc b/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc
index 30b5ac5f..3870bbe0 100644
--- a/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc
+++ b/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc
@@ -202,9 +202,9 @@
     GetElement().SetNeedsValidityCheck();
   } else {
     GetElement().SetNonAttributeValueByUserEdit(new_value);
-    GetElement().SetNeedsStyleRecalc(
-        kSubtreeStyleChange,
-        StyleChangeReasonForTracing::Create(StyleChangeReason::kControlValue));
+    GetElement().SetNeedsStyleRecalc(kSubtreeStyleChange,
+                                     StyleChangeReasonForTracing::Create(
+                                         style_change_reason::kControlValue));
     GetElement().DispatchInputEvent();
   }
   GetElement().NotifyFormStateChanged();
diff --git a/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc b/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc
index c07c17f..ba2974c 100644
--- a/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc
+++ b/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc
@@ -128,9 +128,9 @@
 void TextControlInnerEditorElement::SetVisibility(bool is_visible) {
   if (is_visible_ != is_visible) {
     is_visible_ = is_visible;
-    SetNeedsStyleRecalc(
-        kLocalStyleChange,
-        StyleChangeReasonForTracing::Create(StyleChangeReason::kControlValue));
+    SetNeedsStyleRecalc(kLocalStyleChange,
+                        StyleChangeReasonForTracing::Create(
+                            style_change_reason::kControlValue));
   }
 }
 
diff --git a/third_party/blink/renderer/core/html/html_body_element.cc b/third_party/blink/renderer/core/html/html_body_element.cc
index 145122c..9c8730f 100644
--- a/third_party/blink/renderer/core/html/html_body_element.cc
+++ b/third_party/blink/renderer/core/html/html_body_element.cc
@@ -115,7 +115,7 @@
 
     SetNeedsStyleRecalc(kSubtreeStyleChange,
                         StyleChangeReasonForTracing::Create(
-                            StyleChangeReason::kLinkColorChange));
+                            style_change_reason::kLinkColorChange));
   } else if (name == onafterprintAttr) {
     GetDocument().SetWindowAttributeEventListener(
         EventTypeNames::afterprint,
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc
index 0f3e0e75..7c10e84 100644
--- a/third_party/blink/renderer/core/html/html_element.cc
+++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -1015,7 +1015,7 @@
       if (ElementAffectsDirectionality(element_to_adjust)) {
         element_to_adjust->SetNeedsStyleRecalc(
             kLocalStyleChange, StyleChangeReasonForTracing::Create(
-                                   StyleChangeReason::kWritingModeChange));
+                                   style_change_reason::kWritingModeChange));
         return;
       }
     }
@@ -1025,10 +1025,11 @@
 void HTMLElement::CalculateAndAdjustDirectionality() {
   TextDirection text_direction = Directionality();
   const ComputedStyle* style = GetComputedStyle();
-  if (style && style->Direction() != text_direction)
+  if (style && style->Direction() != text_direction) {
     SetNeedsStyleRecalc(kLocalStyleChange,
                         StyleChangeReasonForTracing::Create(
-                            StyleChangeReason::kWritingModeChange));
+                            style_change_reason::kWritingModeChange));
+  }
 }
 
 void HTMLElement::AdjustDirectionalityIfNeededAfterChildrenChanged(
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
index da2aaf4..0d9e018f 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -133,7 +133,7 @@
       layer->SetNeedsCompositingInputsUpdate();
   }
   SetNeedsStyleRecalc(kLocalStyleChange, StyleChangeReasonForTracing::Create(
-                                             StyleChangeReason::kFrame));
+                                             style_change_reason::kFrame));
 
   for (ContainerNode* node = this; node; node = node->ParentOrShadowHostNode())
     node->IncrementConnectedSubframeCount();
diff --git a/third_party/blink/renderer/core/html/html_image_element.cc b/third_party/blink/renderer/core/html/html_image_element.cc
index 8a7526a..1a424daf 100644
--- a/third_party/blink/renderer/core/html/html_image_element.cc
+++ b/third_party/blink/renderer/core/html/html_image_element.cc
@@ -105,8 +105,8 @@
       sizes_set_width_(false),
       referrer_policy_(kReferrerPolicyDefault) {
   SetHasCustomStyleCallbacks();
-  if (MediaElementParserHelpers::IsMediaElement(this) &&
-      !MediaElementParserHelpers::IsUnsizedMediaEnabled(document)) {
+  if (media_element_parser_helpers::IsMediaElement(this) &&
+      !media_element_parser_helpers::IsUnsizedMediaEnabled(document)) {
     is_default_overridden_intrinsic_size_ = true;
     overridden_intrinsic_size_ =
         IntSize(LayoutReplaced::kDefaultWidth, LayoutReplaced::kDefaultHeight);
@@ -301,7 +301,7 @@
                  ExperimentalProductivityFeaturesEnabled()) {
     String message;
     bool intrinsic_size_changed =
-        MediaElementParserHelpers::ParseIntrinsicSizeAttribute(
+        media_element_parser_helpers::ParseIntrinsicSizeAttribute(
             params.new_value, this, &overridden_intrinsic_size_,
             &is_default_overridden_intrinsic_size_, &message);
     if (!message.IsEmpty()) {
diff --git a/third_party/blink/renderer/core/html/html_slot_element.cc b/third_party/blink/renderer/core/html/html_slot_element.cc
index 1dfa94a..c9142d1 100644
--- a/third_party/blink/renderer/core/html/html_slot_element.cc
+++ b/third_party/blink/renderer/core/html/html_slot_element.cc
@@ -391,7 +391,7 @@
     node->SetNeedsStyleRecalc(
         kLocalStyleChange,
         StyleChangeReasonForTracing::Create(
-            StyleChangeReason::kPropagateInheritChangeToDistributedNodes));
+            style_change_reason::kPropagateInheritChangeToDistributedNodes));
   }
 }
 
diff --git a/third_party/blink/renderer/core/html/media/html_video_element.cc b/third_party/blink/renderer/core/html/media/html_video_element.cc
index 3db572f..55ae2d44 100644
--- a/third_party/blink/renderer/core/html/media/html_video_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_video_element.cc
@@ -89,8 +89,8 @@
         new MediaCustomControlsFullscreenDetector(*this);
   }
 
-  if (MediaElementParserHelpers::IsMediaElement(this) &&
-      !MediaElementParserHelpers::IsUnsizedMediaEnabled(document)) {
+  if (media_element_parser_helpers::IsMediaElement(this) &&
+      !media_element_parser_helpers::IsUnsizedMediaEnabled(document)) {
     is_default_overridden_intrinsic_size_ = true;
     overridden_intrinsic_size_ =
         IntSize(LayoutReplaced::kDefaultWidth, LayoutReplaced::kDefaultHeight);
@@ -221,7 +221,7 @@
                  ExperimentalProductivityFeaturesEnabled()) {
     String message;
     bool intrinsic_size_changed =
-        MediaElementParserHelpers::ParseIntrinsicSizeAttribute(
+        media_element_parser_helpers::ParseIntrinsicSizeAttribute(
             params.new_value, this, &overridden_intrinsic_size_,
             &is_default_overridden_intrinsic_size_, &message);
     if (!message.IsEmpty()) {
diff --git a/third_party/blink/renderer/core/html/media/media_element_parser_helpers.cc b/third_party/blink/renderer/core/html/media/media_element_parser_helpers.cc
index d79dddb..506365a 100644
--- a/third_party/blink/renderer/core/html/media/media_element_parser_helpers.cc
+++ b/third_party/blink/renderer/core/html/media/media_element_parser_helpers.cc
@@ -17,7 +17,7 @@
 
 namespace blink {
 
-namespace MediaElementParserHelpers {
+namespace media_element_parser_helpers {
 
 bool IsMediaElement(const Element* element) {
   if ((IsHTMLImageElement(element) || IsSVGImageElement(element)) &&
@@ -82,6 +82,6 @@
   }
 }
 
-}  // namespace MediaElementParserHelpers
+}  // namespace media_element_parser_helpers
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/media/media_element_parser_helpers.h b/third_party/blink/renderer/core/html/media/media_element_parser_helpers.h
index b231442..71e454f6 100644
--- a/third_party/blink/renderer/core/html/media/media_element_parser_helpers.h
+++ b/third_party/blink/renderer/core/html/media/media_element_parser_helpers.h
@@ -13,7 +13,7 @@
 class Document;
 class LayoutObject;
 
-namespace MediaElementParserHelpers {
+namespace media_element_parser_helpers {
 
 // Parses the intrinsicSize attribute of HTMLImageElement, HTMLVideoElement, and
 // SVGImageElement. Returns true if the value is updated.
@@ -34,7 +34,7 @@
 
 void ReportUnsizedMediaViolation(const LayoutObject* layout_object);
 
-}  // namespace MediaElementParserHelpers
+}  // namespace media_element_parser_helpers
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/core/html/parser/html_preload_scanner_fuzzer.cc b/third_party/blink/renderer/core/html/parser/html_preload_scanner_fuzzer.cc
index fe32a78..0e934a41 100644
--- a/third_party/blink/renderer/core/html/parser/html_preload_scanner_fuzzer.cc
+++ b/third_party/blink/renderer/core/html/parser/html_preload_scanner_fuzzer.cc
@@ -61,7 +61,7 @@
   media_data.primary_pointer_type = kPointerTypeFine;
   media_data.default_font_size = 16;
   media_data.three_d_enabled = true;
-  media_data.media_type = MediaTypeNames::screen;
+  media_data.media_type = media_type_names::kScreen;
   media_data.strict_mode = true;
   media_data.display_mode = kWebDisplayModeBrowser;
 
diff --git a/third_party/blink/renderer/core/html/parser/html_preload_scanner_test.cc b/third_party/blink/renderer/core/html/parser/html_preload_scanner_test.cc
index 7608495b..b82abbd 100644
--- a/third_party/blink/renderer/core/html/parser/html_preload_scanner_test.cc
+++ b/third_party/blink/renderer/core/html/parser/html_preload_scanner_test.cc
@@ -213,7 +213,7 @@
     data.primary_pointer_type = kPointerTypeFine;
     data.default_font_size = 16;
     data.three_d_enabled = true;
-    data.media_type = MediaTypeNames::screen;
+    data.media_type = media_type_names::kScreen;
     data.strict_mode = true;
     data.display_mode = kWebDisplayModeBrowser;
     return data;
diff --git a/third_party/blink/renderer/core/html/track/vtt/vtt_element.cc b/third_party/blink/renderer/core/html/track/vtt/vtt_element.cc
index 4e705cf4..a8e21ff1 100644
--- a/third_party/blink/renderer/core/html/track/vtt/vtt_element.cc
+++ b/third_party/blink/renderer/core/html/track/vtt/vtt_element.cc
@@ -132,7 +132,7 @@
   SetNeedsStyleRecalc(
       kLocalStyleChange,
       StyleChangeReasonForTracing::CreateWithExtraData(
-          StyleChangeReason::kPseudoClass, StyleChangeExtraData::g_past));
+          style_change_reason::kPseudoClass, StyleChangeExtraData::g_past));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc b/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
index 78bc488d..94eb622 100644
--- a/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
+++ b/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
@@ -974,7 +974,7 @@
                                      draw_dst_rect, parsed_options.flip_y);
   std::unique_ptr<ParsedOptions> passed_parsed_options =
       std::make_unique<ParsedOptions>(parsed_options);
-  BackgroundScheduler::PostOnBackgroundThread(
+  background_scheduler::PostOnBackgroundThread(
       FROM_HERE,
       CrossThreadBind(&RasterizeImageOnBackgroundThread,
                       WrapCrossThreadPersistent(resolver),
diff --git a/third_party/blink/renderer/core/imagebitmap/image_bitmap_factories.cc b/third_party/blink/renderer/core/imagebitmap/image_bitmap_factories.cc
index 53c474e9..342dcd98 100644
--- a/third_party/blink/renderer/core/imagebitmap/image_bitmap_factories.cc
+++ b/third_party/blink/renderer/core/imagebitmap/image_bitmap_factories.cc
@@ -296,7 +296,7 @@
     DOMArrayBuffer* array_buffer) {
   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
       Platform::Current()->CurrentThread()->GetTaskRunner();
-  BackgroundScheduler::PostOnBackgroundThread(
+  background_scheduler::PostOnBackgroundThread(
       FROM_HERE,
       CrossThreadBind(
           &ImageBitmapFactories::ImageBitmapLoader::DecodeImageOnDecoderThread,
diff --git a/third_party/blink/renderer/core/input/scroll_manager.cc b/third_party/blink/renderer/core/input/scroll_manager.cc
index 50ce9d5..57eb815 100644
--- a/third_party/blink/renderer/core/input/scroll_manager.cc
+++ b/third_party/blink/renderer/core/input/scroll_manager.cc
@@ -87,14 +87,14 @@
 void ScrollManager::Trace(blink::Visitor* visitor) {
   visitor->Trace(frame_);
   visitor->Trace(scroll_gesture_handling_node_);
-  visitor->Trace(previous_gesture_scrolled_element_);
+  visitor->Trace(previous_gesture_scrolled_node_);
   visitor->Trace(scrollbar_handling_scroll_gesture_);
   visitor->Trace(resize_scrollable_area_);
 }
 
 void ScrollManager::ClearGestureScrollState() {
   scroll_gesture_handling_node_ = nullptr;
-  previous_gesture_scrolled_element_ = nullptr;
+  previous_gesture_scrolled_node_ = nullptr;
   delta_consumed_for_scroll_sequence_ = false;
   did_scroll_x_for_scroll_gesture_ = false;
   did_scroll_y_for_scroll_gesture_ = false;
@@ -128,9 +128,8 @@
   return nullptr;
 }
 
-static bool CanPropagate(const ScrollState& scroll_state,
-                         const Element& element) {
-  ScrollableArea* scrollable_area = element.GetLayoutBox()->GetScrollableArea();
+static bool CanPropagate(const ScrollState& scroll_state, const Node& node) {
+  ScrollableArea* scrollable_area = node.GetLayoutBox()->GetScrollableArea();
   if (!scrollable_area)
     return true;
 
@@ -139,10 +138,10 @@
     return true;
 
   return (scroll_state.deltaXHint() == 0 ||
-          element.GetComputedStyle()->OverscrollBehaviorX() ==
+          node.GetComputedStyle()->OverscrollBehaviorX() ==
               EOverscrollBehavior::kAuto) &&
          (scroll_state.deltaYHint() == 0 ||
-          element.GetComputedStyle()->OverscrollBehaviorY() ==
+          node.GetComputedStyle()->OverscrollBehaviorY() ==
               EOverscrollBehavior::kAuto);
 }
 
@@ -154,38 +153,27 @@
 
   DCHECK(start_node.GetLayoutObject());
   LayoutBox* cur_box = start_node.GetLayoutObject()->EnclosingBox();
-  Element* document_element = frame_->GetDocument()->documentElement();
 
   // Scrolling propagates along the containing block chain and ends at the
-  // RootScroller element. The RootScroller element will have a custom
-  // applyScroll callback that scrolls the frame or element.
+  // RootScroller node. The RootScroller node will have a custom applyScroll
+  // callback that performs scrolling as well as associated "root" actions like
+  // browser control movement and overscroll glow.
   while (cur_box) {
     Node* cur_node = cur_box->GetNode();
-    Element* cur_element = nullptr;
 
-    // FIXME: this should reject more elements, as part of crbug.com/410974.
-    if (cur_node && cur_node->IsElementNode()) {
-      cur_element = ToElement(cur_node);
-    } else if (cur_node && cur_node->IsDocumentNode()) {
-      // In normal circumastances, the documentElement will be the root
-      // scroller but the documentElement itself isn't a containing block,
-      // that'll be the document node rather than the element.
-      cur_element = document_element;
-    }
+    if (cur_node) {
+      if (CanScroll(scroll_state, *cur_node))
+        scroll_chain.push_front(DOMNodeIds::IdForNode(cur_node));
 
-    if (cur_element) {
-      if (CanScroll(scroll_state, *cur_element))
-        scroll_chain.push_front(DOMNodeIds::IdForNode(cur_element));
-      if (IsViewportScrollingElement(*cur_element) ||
-          cur_element == document_element)
+      if (cur_node->IsEffectiveRootScroller())
         break;
 
-      if (!CanPropagate(scroll_state, *cur_element)) {
+      if (!CanPropagate(scroll_state, *cur_node)) {
         // We should add the first node with non-auto overscroll-behavior to
         // the scroll chain regardlessly, as it's the only node we can latch to.
         if (scroll_chain.empty() ||
-            scroll_chain.front() != DOMNodeIds::IdForNode(cur_element)) {
-          scroll_chain.push_front(DOMNodeIds::IdForNode(cur_element));
+            scroll_chain.front() != DOMNodeIds::IdForNode(cur_node)) {
+          scroll_chain.push_front(DOMNodeIds::IdForNode(cur_node));
         }
         break;
       }
@@ -196,25 +184,18 @@
 }
 
 bool ScrollManager::CanScroll(const ScrollState& scroll_state,
-                              const Element& current_element) {
-  ScrollableArea* scrollable_area = nullptr;
-  if (IsViewportScrollingElement(current_element) ||
-      current_element == *(frame_->GetDocument()->documentElement())) {
-    if (!current_element.GetLayoutObject())
-      return false;
+                              const Node& current_node) {
+  if (!current_node.GetLayoutBox())
+    return false;
 
-    if (frame_->IsMainFrame())
-      return true;
+  // We need to always add the root scroller in the main frame even if the
+  // viewport isn't scrollable since we can always pinch-zoom and scroll as
+  // well as for overscroll effects.
+  if (current_node.IsEffectiveRootScroller() && frame_->IsMainFrame())
+    return true;
 
-    // For subframes, the viewport is added to the scroll chain only if it can
-    // actually consume some delta hints. The main frame always gets added
-    // since it produces overscroll effects.
-    scrollable_area =
-        frame_->View() ? frame_->View()->GetScrollableArea() : nullptr;
-  }
-
-  if (!scrollable_area && current_element.GetLayoutBox())
-    scrollable_area = current_element.GetLayoutBox()->GetScrollableArea();
+  ScrollableArea* scrollable_area =
+      current_node.GetLayoutBox()->GetScrollableArea();
 
   if (!scrollable_area)
     return false;
@@ -289,8 +270,6 @@
       DCHECK(main_frame.IsMainFrame());
 
       scrollable_area = main_frame.View()->GetScrollableArea();
-    } else if (node == document.documentElement()) {
-      scrollable_area = document.GetLayoutView()->GetScrollableArea();
     } else {
       scrollable_area = box->GetScrollableArea();
     }
@@ -419,7 +398,7 @@
         scroll_gesture_handling_node_->ParentOrShadowHostNode();
 
   if (!scroll_gesture_handling_node_)
-    scroll_gesture_handling_node_ = frame_->GetDocument()->documentElement();
+    scroll_gesture_handling_node_ = frame_->GetDocument();
 
   if (!scroll_gesture_handling_node_ ||
       !scroll_gesture_handling_node_->GetLayoutObject()) {
@@ -482,7 +461,7 @@
   if (!node || !node->GetLayoutObject()) {
     TRACE_EVENT_INSTANT0("input", "Lost scroll_gesture_handling_node",
                          TRACE_EVENT_SCOPE_THREAD);
-    if (previous_gesture_scrolled_element_) {
+    if (previous_gesture_scrolled_node_) {
       // When the scroll_gesture_handling_node_ gets deleted in the middle of
       // scrolling call HandleGestureScrollEvent to start scrolling a new node
       // if possible.
@@ -557,18 +536,17 @@
   scroll_state_data->delta_consumed_for_scroll_sequence =
       delta_consumed_for_scroll_sequence_;
   ScrollState* scroll_state = ScrollState::Create(std::move(scroll_state_data));
-  if (previous_gesture_scrolled_element_) {
+  if (previous_gesture_scrolled_node_) {
     // The ScrollState needs to know what the current
     // native scrolling element is, so that for an
     // inertial scroll that shouldn't propagate, only the
     // currently scrolling element responds.
     scroll_state->SetCurrentNativeScrollingNode(
-        previous_gesture_scrolled_element_);
+        previous_gesture_scrolled_node_);
   }
 
   CustomizedScroll(*scroll_state);
-  previous_gesture_scrolled_element_ =
-      ToElement(scroll_state->CurrentNativeScrollingNode());
+  previous_gesture_scrolled_node_ = scroll_state->CurrentNativeScrollingNode();
   delta_consumed_for_scroll_sequence_ =
       scroll_state->DeltaConsumedForScrollSequence();
 
@@ -578,9 +556,11 @@
   did_scroll_x_for_scroll_gesture_ |= did_scroll_x;
   did_scroll_y_for_scroll_gesture_ |= did_scroll_y;
 
-  if ((!previous_gesture_scrolled_element_ ||
-       !IsViewportScrollingElement(*previous_gesture_scrolled_element_)) &&
-      GetPage())
+  // TODO(bokan): This looks like it resets overscroll if any *effective* root
+  // scroller is scrolled. This should probably be if the *global* root
+  // scroller is scrolled.
+  if (!previous_gesture_scrolled_node_ ||
+      !previous_gesture_scrolled_node_->IsEffectiveRootScroller())
     GetPage()->GetOverscrollController().ResetAccumulated(did_scroll_x,
                                                           did_scroll_y);
 
@@ -623,16 +603,13 @@
 }
 
 LayoutBox* ScrollManager::LayoutBoxForSnapping() const {
-  if (!previous_gesture_scrolled_element_)
+  if (!previous_gesture_scrolled_node_)
     return nullptr;
-  Element* document_element = frame_->GetDocument()->documentElement();
-  return previous_gesture_scrolled_element_ == document_element
-             ? frame_->GetDocument()->GetLayoutView()
-             : previous_gesture_scrolled_element_->GetLayoutBox();
+  return previous_gesture_scrolled_node_->GetLayoutBox();
 }
 
 void ScrollManager::SnapAtGestureScrollEnd() {
-  if (!previous_gesture_scrolled_element_ ||
+  if (!previous_gesture_scrolled_node_ ||
       (!did_scroll_x_for_scroll_gesture_ && !did_scroll_y_for_scroll_gesture_))
     return;
   SnapCoordinator* snap_coordinator =
@@ -649,7 +626,7 @@
 bool ScrollManager::GetSnapFlingInfo(const gfx::Vector2dF& natural_displacement,
                                      gfx::Vector2dF* out_initial_offset,
                                      gfx::Vector2dF* out_target_offset) const {
-  if (!previous_gesture_scrolled_element_)
+  if (!previous_gesture_scrolled_node_)
     return false;
 
   SnapCoordinator* snap_coordinator =
@@ -681,8 +658,7 @@
   scroll_state_data->delta_consumed_for_scroll_sequence =
       delta_consumed_for_scroll_sequence_;
   ScrollState* scroll_state = ScrollState::Create(std::move(scroll_state_data));
-  scroll_state->SetCurrentNativeScrollingNode(
-      previous_gesture_scrolled_element_);
+  scroll_state->SetCurrentNativeScrollingNode(previous_gesture_scrolled_node_);
 
   CustomizedScroll(*scroll_state);
   FloatPoint end_position = scrollable_area->ScrollPosition();
@@ -743,16 +719,6 @@
       .HandleGestureScrollEvent(gesture_event);
 }
 
-bool ScrollManager::IsViewportScrollingElement(const Element& element) const {
-  // The root scroller is the one Element on the page designated to perform
-  // "viewport actions" like browser controls movement and overscroll glow.
-  if (!frame_->GetDocument())
-    return false;
-
-  return frame_->GetDocument()->GetRootScrollerController().ScrollsViewport(
-      element);
-}
-
 WebInputEventResult ScrollManager::HandleGestureScrollEvent(
     const WebGestureEvent& gesture_event) {
   if (!frame_->View())
@@ -788,7 +754,7 @@
     last_gesture_scroll_over_embedded_content_view_ =
         result.IsOverEmbeddedContentView();
     scroll_gesture_handling_node_ = event_target;
-    previous_gesture_scrolled_element_ = nullptr;
+    previous_gesture_scrolled_node_ = nullptr;
     delta_consumed_for_scroll_sequence_ = false;
     did_scroll_x_for_scroll_gesture_ = false;
     did_scroll_y_for_scroll_gesture_ = false;
@@ -959,16 +925,16 @@
           scroll_state.deltaXHint(), scroll_state.deltaYHint());
   for (auto id : current_scroll_chain_) {
     Node* node = DOMNodeIds::NodeForId(id);
-    if (node && node->IsElementNode())
-      ToElement(node)->WillBeginCustomizedScrollPhase(direction);
+    if (node)
+      node->WillBeginCustomizedScrollPhase(direction);
   }
 }
 
 void ScrollManager::NotifyScrollPhaseEndForCustomizedScroll() {
   for (auto id : current_scroll_chain_) {
     Node* node = DOMNodeIds::NodeForId(id);
-    if (node && node->IsElementNode())
-      ToElement(node)->DidEndCustomizedScrollPhase();
+    if (node)
+      node->DidEndCustomizedScrollPhase();
   }
 }
 
diff --git a/third_party/blink/renderer/core/input/scroll_manager.h b/third_party/blink/renderer/core/input/scroll_manager.h
index 834dfee..5dbece07 100644
--- a/third_party/blink/renderer/core/input/scroll_manager.h
+++ b/third_party/blink/renderer/core/input/scroll_manager.h
@@ -117,14 +117,12 @@
 
   Page* GetPage() const;
 
-  bool IsViewportScrollingElement(const Element&) const;
-
   bool HandleScrollGestureOnResizer(Node*, const WebGestureEvent&);
 
   void RecomputeScrollChain(const Node& start_node,
                             const ScrollState&,
                             std::deque<DOMNodeId>& scroll_chain);
-  bool CanScroll(const ScrollState&, const Element& current_element);
+  bool CanScroll(const ScrollState&, const Node& current_node);
 
   // scroller_size is set only when scrolling non root scroller.
   void ComputeScrollRelatedMetrics(
@@ -153,11 +151,11 @@
 
   bool last_gesture_scroll_over_embedded_content_view_;
 
-  // The most recent element to scroll natively during this scroll
+  // The most recent Node to scroll natively during this scroll
   // sequence. Null if no native element has scrolled this scroll
   // sequence, or if the most recent element to scroll used scroll
   // customization.
-  Member<Element> previous_gesture_scrolled_element_;
+  Member<Node> previous_gesture_scrolled_node_;
 
   // True iff some of the delta has been consumed for the current
   // scroll sequence in this frame, or any child frames. Only used
diff --git a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
index 5720a3e..8060102 100644
--- a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
@@ -1621,7 +1621,7 @@
     node_id_to_forced_pseudo_state_.erase(node_id);
   element->ownerDocument()->SetNeedsStyleRecalc(
       kSubtreeStyleChange,
-      StyleChangeReasonForTracing::Create(StyleChangeReason::kInspector));
+      StyleChangeReasonForTracing::Create(style_change_reason::kInspector));
   return Response::OK();
 }
 
@@ -2082,7 +2082,7 @@
   document->GetStyleEngine().SetRuleUsageTracker(tracker_);
   document->SetNeedsStyleRecalc(
       kSubtreeStyleChange,
-      StyleChangeReasonForTracing::Create(StyleChangeReason::kInspector));
+      StyleChangeReasonForTracing::Create(style_change_reason::kInspector));
 }
 
 void InspectorCSSAgent::DidRemoveDocument(Document* document) {}
@@ -2133,10 +2133,11 @@
   }
 
   node_id_to_forced_pseudo_state_.clear();
-  for (auto& document : documents_to_change)
+  for (auto& document : documents_to_change) {
     document->SetNeedsStyleRecalc(
         kSubtreeStyleChange,
-        StyleChangeReasonForTracing::Create(StyleChangeReason::kInspector));
+        StyleChangeReasonForTracing::Create(style_change_reason::kInspector));
+  }
 }
 
 HeapVector<Member<CSSStyleDeclaration>> InspectorCSSAgent::MatchingStyles(
@@ -2412,7 +2413,7 @@
   for (Document* document : dom_agent_->Documents()) {
     document->SetNeedsStyleRecalc(
         kSubtreeStyleChange,
-        StyleChangeReasonForTracing::Create(StyleChangeReason::kInspector));
+        StyleChangeReasonForTracing::Create(style_change_reason::kInspector));
     document->UpdateStyleAndLayoutTree();
   }
 
diff --git a/third_party/blink/renderer/core/layout/layout_image.cc b/third_party/blink/renderer/core/layout/layout_image.cc
index b06bcb8f..e59d0e00 100644
--- a/third_party/blink/renderer/core/layout/layout_image.cc
+++ b/third_party/blink/renderer/core/layout/layout_image.cc
@@ -500,7 +500,7 @@
 
     // Report violation of unsized-media policy.
     if (image_element->IsDefaultIntrinsicSize())
-      MediaElementParserHelpers::ReportUnsizedMediaViolation(this);
+      media_element_parser_helpers::ReportUnsizedMediaViolation(this);
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_text_control.cc b/third_party/blink/renderer/core/layout/layout_text_control.cc
index a067429..e307419 100644
--- a/third_party/blink/renderer/core/layout/layout_text_control.cc
+++ b/third_party/blink/renderer/core/layout/layout_text_control.cc
@@ -56,7 +56,7 @@
   if (inner_editor_layout_object) {
     inner_editor->SetNeedsStyleRecalc(
         kSubtreeStyleChange,
-        StyleChangeReasonForTracing::Create(StyleChangeReason::kControl));
+        StyleChangeReasonForTracing::Create(style_change_reason::kControl));
 
     // The inner editor element uses the LayoutTextControl's ::selection style
     // (see: GetUncachedSelectionStyle in SelectionPaintingUtils.cpp) so ensure
diff --git a/third_party/blink/renderer/core/layout/layout_video.cc b/third_party/blink/renderer/core/layout/layout_video.cc
index a535771..1b094f8 100644
--- a/third_party/blink/renderer/core/layout/layout_video.cc
+++ b/third_party/blink/renderer/core/layout/layout_video.cc
@@ -203,7 +203,7 @@
   // Report violation of unsized-media policy.
   if (auto* video_element = ToHTMLVideoElementOrNull(GetNode())) {
     if (video_element->IsDefaultIntrinsicSize())
-      MediaElementParserHelpers::ReportUnsizedMediaViolation(this);
+      media_element_parser_helpers::ReportUnsizedMediaViolation(this);
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc
index 12f9d1c..0ae9f9d 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc
@@ -158,7 +158,7 @@
 
   if (auto* svg_image_element = ToSVGImageElementOrNull(GetElement())) {
     if (svg_image_element->IsDefaultIntrinsicSize())
-      MediaElementParserHelpers::ReportUnsizedMediaViolation(this);
+      media_element_parser_helpers::ReportUnsizedMediaViolation(this);
   }
   ClearNeedsLayout();
 }
diff --git a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
index dd221e6..0a49579 100644
--- a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
+++ b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
@@ -37,14 +37,9 @@
 
 OffscreenCanvas::OffscreenCanvas(const IntSize& size) : size_(size) {}
 
-void OffscreenCanvas::RecordCanvasSizeToUMA(unsigned width, unsigned height) {
-  UMA_HISTOGRAM_CUSTOM_COUNTS("Blink.OffscreenCanvas.SqrtNumberOfPixels",
-                              std::sqrt(width * height), 1, 5000, 100);
-}
-
 OffscreenCanvas* OffscreenCanvas::Create(unsigned width, unsigned height) {
   UMA_HISTOGRAM_BOOLEAN("Blink.OffscreenCanvas.NewOffscreenCanvas", true);
-  RecordCanvasSizeToUMA(width, height);
+  CanvasRenderingContextHost::RecordCanvasSizeToUMA(width, height, true);
   return new OffscreenCanvas(
       IntSize(clampTo<int>(width), clampTo<int>(height)));
 }
@@ -116,7 +111,8 @@
     }
   }
   if (size != size_) {
-    RecordCanvasSizeToUMA(size.Width(), size.Height());
+    CanvasRenderingContextHost::RecordCanvasSizeToUMA(size.Width(),
+                                                      size.Height(), true);
   }
   size_ = size;
   if (frame_dispatcher_)
diff --git a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h
index 9399e6a7..f65a90a8 100644
--- a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h
+++ b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h
@@ -45,9 +45,6 @@
   USING_GARBAGE_COLLECTED_MIXIN(OffscreenCanvas);
   USING_PRE_FINALIZER(OffscreenCanvas, Dispose);
 
- private:
-  void static RecordCanvasSizeToUMA(unsigned width, unsigned height);
-
  public:
   static OffscreenCanvas* Create(unsigned width, unsigned height);
   ~OffscreenCanvas() override;
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc
index a6e7a0b..388e2f7 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc
@@ -111,9 +111,7 @@
 }
 
 RootScrollerController::RootScrollerController(Document& document)
-    : document_(&document),
-      effective_root_scroller_(&document),
-      document_has_document_element_(false) {}
+    : document_(&document), effective_root_scroller_(&document) {}
 
 void RootScrollerController::Trace(blink::Visitor* visitor) {
   visitor->Trace(document_);
@@ -189,50 +187,38 @@
       new_effective_root_scroller = implicit_root_scroller_;
   }
 
-  // TODO(bokan): This is a terrible hack but required because the viewport
-  // apply scroll works on Elements rather than Nodes. If we're going from
-  // !documentElement to documentElement, we can't early out even if the root
-  // scroller didn't change since the global root scroller didn't have an
-  // Element previously to put it's ViewportScrollCallback onto. We need this
-  // to kick the global root scroller to recompute itself. We can remove this
-  // if ScrollCustomization is moved to the Node rather than Element.
-  bool old_has_document_element = document_has_document_element_;
-  document_has_document_element_ = document_->documentElement();
-
-  if (old_has_document_element || !document_has_document_element_) {
-    if (effective_root_scroller_ == new_effective_root_scroller)
-      return;
-  }
+  if (effective_root_scroller_ == new_effective_root_scroller)
+    return;
 
   Node* old_effective_root_scroller = effective_root_scroller_;
   effective_root_scroller_ = new_effective_root_scroller;
 
-  if (new_effective_root_scroller != old_effective_root_scroller) {
-    if (LayoutBoxModelObject* new_obj =
-            new_effective_root_scroller->GetLayoutBoxModelObject()) {
-      if (new_obj->Layer()) {
-        new_effective_root_scroller->GetLayoutBoxModelObject()
-            ->Layer()
-            ->SetNeedsCompositingInputsUpdate();
-      }
+  DCHECK(new_effective_root_scroller);
+  if (LayoutBoxModelObject* new_obj =
+          new_effective_root_scroller->GetLayoutBoxModelObject()) {
+    if (new_obj->Layer()) {
+      new_effective_root_scroller->GetLayoutBoxModelObject()
+          ->Layer()
+          ->SetNeedsCompositingInputsUpdate();
     }
-    if (old_effective_root_scroller) {
-      if (LayoutBoxModelObject* old_obj =
-              old_effective_root_scroller->GetLayoutBoxModelObject()) {
-        if (old_obj->Layer()) {
-          old_effective_root_scroller->GetLayoutBoxModelObject()
-              ->Layer()
-              ->SetNeedsCompositingInputsUpdate();
-        }
-      }
-    }
-    if (auto* object = old_effective_root_scroller->GetLayoutObject())
-      object->SetIsEffectiveRootScroller(false);
-
-    if (auto* object = new_effective_root_scroller->GetLayoutObject())
-      object->SetIsEffectiveRootScroller(true);
   }
 
+  DCHECK(old_effective_root_scroller);
+  if (LayoutBoxModelObject* old_obj =
+          old_effective_root_scroller->GetLayoutBoxModelObject()) {
+    if (old_obj->Layer()) {
+      old_effective_root_scroller->GetLayoutBoxModelObject()
+          ->Layer()
+          ->SetNeedsCompositingInputsUpdate();
+    }
+  }
+
+  if (auto* object = old_effective_root_scroller->GetLayoutObject())
+    object->SetIsEffectiveRootScroller(false);
+
+  if (auto* object = new_effective_root_scroller->GetLayoutObject())
+    object->SetIsEffectiveRootScroller(true);
+
   ApplyRootScrollerProperties(*old_effective_root_scroller);
   ApplyRootScrollerProperties(*effective_root_scroller_);
 
@@ -419,13 +405,6 @@
       effective_root_scroller_);
 }
 
-bool RootScrollerController::ScrollsViewport(const Element& element) const {
-  if (effective_root_scroller_->IsDocumentNode())
-    return element == document_->documentElement();
-
-  return element == effective_root_scroller_.Get();
-}
-
 void RootScrollerController::ElementRemoved(const Element& element) {
   if (element != effective_root_scroller_.Get())
     return;
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h
index cdffaa1..07daee1e 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h
@@ -78,13 +78,6 @@
   // scroller.
   PaintLayer* RootScrollerPaintLayer() const;
 
-  // Used to determine which Element should scroll the viewport.  This is
-  // needed since Blink's scrolling machinery works on Elements whereas the
-  // document *Node* also scrolls so we need to designate an element one
-  // Element as the viewport scroller. Sadly, this is *not* the
-  // document.scrollingElement in general.
-  bool ScrollsViewport(const Element&) const;
-
   void ElementRemoved(const Element&);
 
   // In the "implicit root scroller" mode, we might promote an element to
@@ -162,8 +155,6 @@
   HeapHashSet<WeakMember<Element>> implicit_candidates_;
 
   WeakMember<Element> implicit_root_scroller_;
-
-  bool document_has_document_element_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
index 29c1530..8bba5fc 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
@@ -201,16 +201,9 @@
 TEST_F(RootScrollerTest, TestDefaultRootScroller) {
   Initialize("overflow-scrolling.html");
 
-  RootScrollerController& controller =
-      MainFrame()->GetDocument()->GetRootScrollerController();
-
   ASSERT_EQ(nullptr, MainFrame()->GetDocument()->rootScroller());
-
   EXPECT_EQ(MainFrame()->GetDocument(),
             EffectiveRootScroller(MainFrame()->GetDocument()));
-
-  Element* html_element = MainFrame()->GetDocument()->documentElement();
-  EXPECT_TRUE(controller.ScrollsViewport(*html_element));
 }
 
 // Make sure that replacing the documentElement doesn't change the effective
@@ -542,13 +535,13 @@
   const TopDocumentRootScrollerController& main_controller =
       MainFrame()->GetDocument()->GetPage()->GlobalRootScrollerController();
 
-  // No root scroller set, the documentElement should be the effective root
-  // and the main LocalFrameView's scroll layer should be the layer to use.
+  // No root scroller set, the document node should be the global root and the
+  // main LocalFrameView's scroll layer should be the layer to use.
   {
     EXPECT_EQ(main_controller.RootScrollerLayer(),
               MainFrameView()->LayoutViewport()->LayerForScrolling());
     EXPECT_TRUE(main_controller.IsViewportScrollCallback(
-        MainFrame()->GetDocument()->documentElement()->GetApplyScroll()));
+        MainFrame()->GetDocument()->GetApplyScroll()));
   }
 
   // Set a root scroller in the iframe. Since the main document didn't set a
@@ -559,7 +552,7 @@
     EXPECT_EQ(main_controller.RootScrollerLayer(),
               MainFrameView()->LayoutViewport()->LayerForScrolling());
     EXPECT_TRUE(main_controller.IsViewportScrollCallback(
-        MainFrame()->GetDocument()->documentElement()->GetApplyScroll()));
+        MainFrame()->GetDocument()->GetApplyScroll()));
   }
 
   // Setting the iframe as the root scroller in the main frame should now
@@ -573,14 +566,14 @@
     EXPECT_EQ(main_controller.RootScrollerLayer(),
               container_scroller->LayerForScrolling());
     EXPECT_FALSE(main_controller.IsViewportScrollCallback(
-        MainFrame()->GetDocument()->documentElement()->GetApplyScroll()));
+        MainFrame()->GetDocument()->GetApplyScroll()));
     EXPECT_TRUE(
         main_controller.IsViewportScrollCallback(container->GetApplyScroll()));
   }
 
-  // Unsetting the root scroller in the iframe should reset its effective
-  // root scroller to the iframe's documentElement and thus the iframe's
-  // documentElement becomes the global root scroller.
+  // Unsetting the root scroller in the iframe should reset its effective root
+  // scroller to the iframe's document node and thus it becomes the global root
+  // scroller.
   {
     SetAndSelectRootScroller(*iframe->contentDocument(), nullptr);
     EXPECT_EQ(main_controller.RootScrollerLayer(), iframe->contentDocument()
@@ -590,23 +583,23 @@
     EXPECT_FALSE(
         main_controller.IsViewportScrollCallback(container->GetApplyScroll()));
     EXPECT_FALSE(main_controller.IsViewportScrollCallback(
-        MainFrame()->GetDocument()->documentElement()->GetApplyScroll()));
+        MainFrame()->GetDocument()->GetApplyScroll()));
     EXPECT_TRUE(main_controller.IsViewportScrollCallback(
-        iframe->contentDocument()->documentElement()->GetApplyScroll()));
+        iframe->contentDocument()->GetApplyScroll()));
   }
 
   // Finally, unsetting the main frame's root scroller should reset it to the
-  // documentElement and corresponding layer.
+  // document node and corresponding layer.
   {
     SetAndSelectRootScroller(*MainFrame()->GetDocument(), nullptr);
     EXPECT_EQ(main_controller.RootScrollerLayer(),
               MainFrameView()->LayoutViewport()->LayerForScrolling());
     EXPECT_TRUE(main_controller.IsViewportScrollCallback(
-        MainFrame()->GetDocument()->documentElement()->GetApplyScroll()));
+        MainFrame()->GetDocument()->GetApplyScroll()));
     EXPECT_FALSE(
         main_controller.IsViewportScrollCallback(container->GetApplyScroll()));
     EXPECT_FALSE(main_controller.IsViewportScrollCallback(
-        iframe->contentDocument()->documentElement()->GetApplyScroll()));
+        iframe->contentDocument()->GetApplyScroll()));
   }
 }
 
@@ -883,8 +876,7 @@
   const TopDocumentRootScrollerController& global_controller =
       MainFrame()->GetDocument()->GetPage()->GlobalRootScrollerController();
 
-  ASSERT_EQ(MainFrame()->GetDocument()->documentElement(),
-            global_controller.GlobalRootScroller());
+  ASSERT_EQ(MainFrame()->GetDocument(), global_controller.GlobalRootScroller());
 
   MainFrameView()->UpdateAllLifecyclePhases();
   GraphicsLayer* scroll_layer = global_controller.RootScrollerLayer();
@@ -900,8 +892,7 @@
   non_main_local_root->GetFrameView()->UpdateAllLifecyclePhases();
   helper_.LocalMainFrame()->GetFrameView()->UpdateAllLifecyclePhases();
 
-  EXPECT_EQ(MainFrame()->GetDocument()->documentElement(),
-            global_controller.GlobalRootScroller());
+  EXPECT_EQ(MainFrame()->GetDocument(), global_controller.GlobalRootScroller());
   EXPECT_EQ(global_controller.RootScrollerLayer(), scroll_layer);
   EXPECT_EQ(global_controller.RootContainerLayer(), container_layer);
 }
@@ -947,8 +938,7 @@
   const TopDocumentRootScrollerController& global_controller =
       MainFrame()->GetDocument()->GetPage()->GlobalRootScrollerController();
 
-  EXPECT_EQ(MainFrame()->GetDocument()->documentElement(),
-            global_controller.GlobalRootScroller());
+  EXPECT_EQ(MainFrame()->GetDocument(), global_controller.GlobalRootScroller());
   EXPECT_EQ(MainFrameView()->LayoutViewport()->LayerForScrolling(),
             global_controller.RootScrollerLayer());
 }
@@ -2567,10 +2557,10 @@
                                     -1, cc::BrowserControlsState::kBoth});
     ASSERT_EQ(0, GetBrowserControls().ShownRatio());
 
-    Element* scroller = GetDocument()
-                            .GetPage()
-                            ->GlobalRootScrollerController()
-                            .GlobalRootScroller();
+    Node* scroller = GetDocument()
+                         .GetPage()
+                         ->GlobalRootScrollerController()
+                         .GlobalRootScroller();
     ScrollableArea* scrollable_area =
         ToLayoutBox(scroller->GetLayoutObject())->GetScrollableArea();
     scrollable_area->DidScroll(FloatPoint(0, 100000));
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_util.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_util.cc
index 322ce06d..655e9b3 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_util.cc
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_util.cc
@@ -21,48 +21,14 @@
 
 namespace root_scroller_util {
 
-ScrollableArea* ScrollableAreaForRootScroller(const Node* node) {
-  if (!node)
-    return nullptr;
-
-  if (node->IsDocumentNode() || node == node->GetDocument().documentElement()) {
-    if (!node->GetDocument().View())
-      return nullptr;
-
-    // For a FrameView, we use the layoutViewport rather than the
-    // getScrollableArea() since that could be the RootFrameViewport. The
-    // rootScroller's ScrollableArea will be swapped in as the layout viewport
-    // in RootFrameViewport so we need to ensure we get the layout viewport.
-    return node->GetDocument().View()->LayoutViewport();
-  }
-
-  DCHECK(node->IsElementNode());
-  const Element* element = ToElement(node);
-
-  if (!element->GetLayoutObject() || !element->GetLayoutObject()->IsBox())
-    return nullptr;
-
-  return ToLayoutBoxModelObject(element->GetLayoutObject())
-      ->GetScrollableArea();
-}
-
 PaintLayer* PaintLayerForRootScroller(const Node* node) {
   if (!node)
     return nullptr;
 
-  if (node->IsDocumentNode() || node == node->GetDocument().documentElement()) {
-    if (!node->GetDocument().GetLayoutView())
-      return nullptr;
-
-    return node->GetDocument().GetLayoutView()->Layer();
-  }
-
-  DCHECK(node->IsElementNode());
-  const Element* element = ToElement(node);
-  if (!element->GetLayoutObject() || !element->GetLayoutObject()->IsBox())
+  if (!node->GetLayoutObject() || !node->GetLayoutObject()->IsBox())
     return nullptr;
 
-  LayoutBox* box = ToLayoutBox(element->GetLayoutObject());
+  LayoutBox* box = ToLayoutBox(node->GetLayoutObject());
   return box->Layer();
 }
 
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_util.h b/third_party/blink/renderer/core/page/scrolling/root_scroller_util.h
index 152aba0d..241f0d8 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_util.h
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_util.h
@@ -10,15 +10,9 @@
 class LayoutBox;
 class Node;
 class PaintLayer;
-class ScrollableArea;
 
 namespace root_scroller_util {
 
-// Returns the ScrollableArea that's associated with the root scroller Node.
-// For the <html> element and document Node this will be the FrameView or root
-// PaintLayerScrollableArea.
-ScrollableArea* ScrollableAreaForRootScroller(const Node*);
-
 // Returns the PaintLayer that'll be used as the root scrolling layer. For the
 // <html> element and document Node, this returns the LayoutView's PaintLayer
 // rather than <html>'s since scrolling is handled by LayoutView.
diff --git a/third_party/blink/renderer/core/page/scrolling/scroll_state.cc b/third_party/blink/renderer/core/page/scrolling/scroll_state.cc
index f83af45..87a7f9b 100644
--- a/third_party/blink/renderer/core/page/scrolling/scroll_state.cc
+++ b/third_party/blink/renderer/core/page/scrolling/scroll_state.cc
@@ -16,9 +16,6 @@
 Node* NodeForId(DOMNodeId node_id) {
   Node* node = DOMNodeIds::NodeForId(node_id);
   DCHECK(node);
-  if (!node)
-    return nullptr;
-  DCHECK(node->IsElementNode());
   return node;
 }
 }  // namespace
diff --git a/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.cc b/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.cc
index fc1d86a..270e368 100644
--- a/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.cc
+++ b/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.cc
@@ -24,6 +24,18 @@
 
 namespace blink {
 
+namespace {
+
+ScrollableArea* GetScrollableArea(Node* node) {
+  if (!node || !node->GetLayoutObject() ||
+      !node->GetLayoutObject()->IsBoxModelObject())
+    return nullptr;
+
+  return ToLayoutBoxModelObject(node->GetLayoutObject())->GetScrollableArea();
+}
+
+}  // namespace
+
 // static
 TopDocumentRootScrollerController* TopDocumentRootScrollerController::Create(
     Page& page) {
@@ -44,20 +56,29 @@
 }
 
 void TopDocumentRootScrollerController::DidResizeViewport() {
-  if (!GlobalRootScroller())
+  if (!GlobalRootScroller() || !GlobalRootScroller()->GetDocument().IsActive())
     return;
 
+  if (!GlobalRootScroller()->GetLayoutObject())
+    return;
+
+  DCHECK(GlobalRootScroller()->GetLayoutObject()->IsBoxModelObject());
+
+  LayoutBoxModelObject* layout_object =
+      ToLayoutBoxModelObject(GlobalRootScroller()->GetLayoutObject());
+
   // Top controls can resize the viewport without invalidating compositing or
   // paint so we need to do that manually here.
-  GlobalRootScroller()->SetNeedsCompositingUpdate();
+  if (layout_object->HasLayer()) {
+    layout_object->Layer()->SetNeedsCompositingInputsUpdate();
+    layout_object->Layer()->UpdateSelfPaintingLayer();
+  }
 
-  if (GlobalRootScroller()->GetLayoutObject())
-    GlobalRootScroller()->GetLayoutObject()->SetNeedsPaintPropertyUpdate();
+  layout_object->SetNeedsPaintPropertyUpdate();
 }
 
 ScrollableArea* TopDocumentRootScrollerController::RootScrollerArea() const {
-  return root_scroller_util::ScrollableAreaForRootScroller(
-      GlobalRootScroller());
+  return GetScrollableArea(GlobalRootScroller());
 }
 
 IntSize TopDocumentRootScrollerController::RootScrollerVisibleArea() const {
@@ -78,44 +99,33 @@
          IntSize(0, browser_controls_adjustment);
 }
 
-Element* TopDocumentRootScrollerController::FindGlobalRootScrollerElement() {
+Node* TopDocumentRootScrollerController::FindGlobalRootScroller() {
   if (!TopDocument())
     return nullptr;
 
-  Node* effective_root_scroller =
+  Node* root_scroller =
       &TopDocument()->GetRootScrollerController().EffectiveRootScroller();
 
-  if (effective_root_scroller->IsDocumentNode())
-    return TopDocument()->documentElement();
-
-  DCHECK(effective_root_scroller->IsElementNode());
-  Element* element = ToElement(effective_root_scroller);
-
-  while (element && element->IsFrameOwnerElement()) {
-    HTMLFrameOwnerElement* frame_owner = ToHTMLFrameOwnerElement(element);
+  while (root_scroller && root_scroller->IsFrameOwnerElement()) {
+    HTMLFrameOwnerElement* frame_owner = ToHTMLFrameOwnerElement(root_scroller);
     DCHECK(frame_owner);
 
     Document* iframe_document = frame_owner->contentDocument();
     if (!iframe_document)
-      return element;
+      return root_scroller;
 
-    effective_root_scroller =
+    root_scroller =
         &iframe_document->GetRootScrollerController().EffectiveRootScroller();
-    if (effective_root_scroller->IsDocumentNode())
-      return iframe_document->documentElement();
-
-    element = ToElement(effective_root_scroller);
   }
 
-  return element;
+  return root_scroller;
 }
 
-void SetNeedsCompositingUpdateOnAncestors(Element* element) {
-  if (!element || !element->GetDocument().IsActive())
+void SetNeedsCompositingUpdateOnAncestors(Node* node) {
+  if (!node || !node->GetDocument().IsActive())
     return;
 
-  ScrollableArea* area =
-      root_scroller_util::ScrollableAreaForRootScroller(element);
+  ScrollableArea* area = GetScrollableArea(node);
 
   if (!area || !area->Layer())
     return;
@@ -136,12 +146,11 @@
   if (!viewport_apply_scroll_)
     return;
 
-  Element* target = FindGlobalRootScrollerElement();
+  Node* target = FindGlobalRootScroller();
   if (target == global_root_scroller_)
     return;
 
-  ScrollableArea* target_scroller =
-      root_scroller_util::ScrollableAreaForRootScroller(target);
+  ScrollableArea* target_scroller = GetScrollableArea(target);
 
   if (!target_scroller)
     return;
@@ -154,7 +163,7 @@
   // scrolling the element so it will apply scroll to the element itself.
   target->SetApplyScroll(viewport_apply_scroll_);
 
-  Element* old_root_scroller = global_root_scroller_;
+  Node* old_root_scroller = global_root_scroller_;
 
   global_root_scroller_ = target;
 
@@ -168,8 +177,7 @@
   SetNeedsCompositingUpdateOnAncestors(old_root_scroller);
   SetNeedsCompositingUpdateOnAncestors(target);
 
-  if (ScrollableArea* area = root_scroller_util::ScrollableAreaForRootScroller(
-          old_root_scroller)) {
+  if (ScrollableArea* area = GetScrollableArea(old_root_scroller)) {
     if (old_root_scroller->GetDocument().IsActive())
       area->DidChangeGlobalRootScroller();
   }
@@ -237,8 +245,7 @@
 }
 
 GraphicsLayer* TopDocumentRootScrollerController::RootScrollerLayer() const {
-  ScrollableArea* area =
-      root_scroller_util::ScrollableAreaForRootScroller(global_root_scroller_);
+  ScrollableArea* area = GetScrollableArea(global_root_scroller_);
 
   if (!area)
     return nullptr;
@@ -253,9 +260,7 @@
 }
 
 GraphicsLayer* TopDocumentRootScrollerController::RootContainerLayer() const {
-  ScrollableArea* area =
-      root_scroller_util::ScrollableAreaForRootScroller(global_root_scroller_);
-
+  ScrollableArea* area = GetScrollableArea(global_root_scroller_);
   return area ? area->LayerForContainer() : nullptr;
 }
 
@@ -263,7 +268,7 @@
   return root_scroller_util::PaintLayerForRootScroller(global_root_scroller_);
 }
 
-Element* TopDocumentRootScrollerController::GlobalRootScroller() const {
+Node* TopDocumentRootScrollerController::GlobalRootScroller() const {
   return global_root_scroller_.Get();
 }
 
diff --git a/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h b/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h
index 72c8232..2891a6d 100644
--- a/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h
+++ b/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h
@@ -12,8 +12,8 @@
 
 namespace blink {
 
-class Element;
 class LocalFrameView;
+class Node;
 class Page;
 class GraphicsLayer;
 class PaintLayer;
@@ -24,8 +24,8 @@
 
 // This class manages the the page level aspects of the root scroller.  That
 // is, given all the iframes on a page and their individual root scrollers,
-// this class will determine which ultimate Element should be used as the root
-// scroller and ensures that Element is used to scroll browser controls and
+// this class will determine which ultimate Node should be used as the root
+// scroller and ensures that Node is used to scroll browser controls and
 // provide overscroll effects. High level details are available in README.md.
 // TODO(bokan): This class is currently OOPIF unaware. crbug.com/642378.
 class CORE_EXPORT TopDocumentRootScrollerController
@@ -63,10 +63,10 @@
 
   PaintLayer* RootScrollerPaintLayer() const;
 
-  // Returns the Element that's the global root scroller.  See README.md for
+  // Returns the Node that's the global root scroller.  See README.md for
   // the difference between this and the root scroller types in
   // RootScrollerController.
-  Element* GlobalRootScroller() const;
+  Node* GlobalRootScroller() const;
 
   // Called when the root scroller in any frames on the page has changed.
   void DidChangeRootScroller();
@@ -74,7 +74,7 @@
   void DidResizeViewport();
 
   // Returns the ScrollableArea associated with the globalRootScroller(). Note,
-  // this isn't necessarily the PLSA belonging to the root scroller Element's
+  // this isn't necessarily the PLSA belonging to the root scroller Node's
   // LayoutBox.  If the root scroller is the documentElement then we use the
   // LocalFrameView (or LayoutView if root-layer-scrolls).
   ScrollableArea* RootScrollerArea() const;
@@ -86,11 +86,11 @@
  private:
   TopDocumentRootScrollerController(Page&);
 
-  // Calculates the Element that should be the globalRootScroller. On a
-  // simple page, this will simply the root frame's effectiveRootScroller but
-  // if the root scroller is set to an iframe, this will then descend into
-  // the iframe to find its effective root scroller.
-  Element* FindGlobalRootScrollerElement();
+  // Calculates the Node that should be the globalRootScroller. On a simple
+  // page, this will simply the root frame's effectiveRootScroller but if the
+  // root scroller is set to an iframe, this will then descend into the iframe
+  // to find its effective root scroller.
+  Node* FindGlobalRootScroller();
 
   // Should be called to ensure the correct element is currently set as the
   // global root scroller and that all appropriate state changes are made if
@@ -104,11 +104,11 @@
   // appropriate root scroller element.
   Member<ViewportScrollCallback> viewport_apply_scroll_;
 
-  // The page level root scroller. i.e. The actual element for which
-  // scrolling should move browser controls and produce overscroll glow. Once an
+  // The page level root scroller. i.e. The actual node for which scrolling
+  // should move browser controls and produce overscroll glow. Once an
   // m_viewportApplyScroll has been created, it will always be set on this
-  // Element.
-  WeakMember<Element> global_root_scroller_;
+  // Node.
+  WeakMember<Node> global_root_scroller_;
 
   Member<Page> page_;
 };
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index 0309dbc..2c014783 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -2313,9 +2313,7 @@
 
   const TopDocumentRootScrollerController& controller =
       GetLayoutBox()->GetDocument().GetPage()->GlobalRootScrollerController();
-
-  return root_scroller_util::ScrollableAreaForRootScroller(
-             controller.GlobalRootScroller()) == this;
+  return controller.RootScrollerArea() == this;
 }
 
 bool PaintLayerScrollableArea::ScheduleAnimation() {
diff --git a/third_party/blink/renderer/core/streams/CommonOperations.js b/third_party/blink/renderer/core/streams/CommonOperations.js
index f35f6d13f..e5f29bef 100644
--- a/third_party/blink/renderer/core/streams/CommonOperations.js
+++ b/third_party/blink/renderer/core/streams/CommonOperations.js
@@ -23,8 +23,27 @@
 
   const RangeError = global.RangeError;
   const TypeError = global.TypeError;
+  const TypeError_prototype = TypeError.prototype;
 
   const hasOwnProperty = v8.uncurryThis(global.Object.hasOwnProperty);
+  const getPrototypeOf = global.Object.getPrototypeOf.bind(global.Object);
+  const getOwnPropertyDescriptor =
+        global.Object.getOwnPropertyDescriptor.bind(global.Object);
+
+  const thenPromise = v8.uncurryThis(Promise.prototype.then);
+
+  // See unsafeCopyGlobals() below.
+  let MessagePort_addEventListener;
+  let MessagePort_postMessage;
+  let MessagePort_close;
+  let MessagePort_start;
+  let MessageEvent_data_get;
+  let DOMException;
+  let DOMException_message_get;
+  let DOMException_name_get;
+
+  const JSON_parse = global.JSON.parse.bind(global.JSON);
+  const JSON_stringify = global.JSON.stringify.bind(global.JSON);
 
   function hasOwnPropertyNoThrow(x, property) {
     // The cast of |x| to Boolean will eliminate undefined and null, which would
@@ -288,6 +307,266 @@
     }
   }
 
+  // Functions for transferable streams. See design doc
+  // https://docs.google.com/document/d/1_KuZzg5c3pncLJPFa8SuVm23AP4tft6mzPCL5at3I9M/edit
+
+  const kPull = 1;
+  const kCancel = 2;
+  const kChunk = 3;
+  const kClose = 4;
+  const kAbort = 5;
+  const kError = 6;
+
+  // This function is a stopgap until we have a way to get the original values.
+  // It is not safe because the values may have been changed by the page, and
+  // definitely cannot be shipped like this.
+  // TODO(ricea): Find a safe way to do this.
+  function unsafeCopyGlobals() {
+    MessagePort_addEventListener =
+          v8.uncurryThis(MessagePort.prototype.addEventListener);
+    MessagePort_postMessage =
+          v8.uncurryThis(MessagePort.prototype.postMessage);
+    MessagePort_close =
+          v8.uncurryThis(MessagePort.prototype.close);
+    MessagePort_start =
+          v8.uncurryThis(MessagePort.prototype.start);
+    MessageEvent_data_get =
+          v8.uncurryThis(
+              getOwnPropertyDescriptor(MessageEvent.prototype, 'data').get);
+    DOMException = self.DOMException;
+    DOMException_message_get =
+        v8.uncurryThis(
+            getOwnPropertyDescriptor(DOMException.prototype, 'message').get);
+    DOMException_name_get =
+        v8.uncurryThis(
+            getOwnPropertyDescriptor(DOMException.prototype, 'name').get);
+  }
+
+  function isATypeError(object) {
+    // There doesn't appear to be a 100% reliable way to identify a TypeError
+    // from JS.
+    return getPrototypeOf(object) === TypeError_prototype;
+  }
+
+  function isADOMException(object) {
+    try {
+      DOMException_name_get(object);
+      return true;
+    } catch (e) {
+      return false;
+    }
+  }
+
+  // We'd like to able to transfer TypeError exceptions, but we can't, so we
+  // hack around it. packReason() is guaranteed not to throw and the object
+  // produced is guaranteed to be serializable by postMessage().
+  function packReason(reason) {
+    switch (typeof reason) {
+      case 'string':
+      case 'number':
+        return {encoder: 'json', string: JSON_stringify(reason)};
+
+      case 'object':
+        try {
+          if (isATypeError(reason)) {
+            // "message" on TypeError is a normal property, meaning that if it
+            // is set, it is set on the object itself. We can take advantage of
+            // this to avoid executing user JavaScript in the case when the
+            // TypeError was generated internally.
+            let message;
+            const descriptor = getOwnPropertyDescriptor(reason, 'message');
+            if (descriptor) {
+              message = descriptor.value;
+              if (typeof message !== 'string') {
+                message = undefined;
+              }
+            }
+            return {encoder: 'typeerror', string: message};
+          }
+
+          if (isADOMException(reason)) {
+            const message = DOMException_message_get(reason);
+            const name = DOMException_name_get(reason);
+            return {
+              encoder: 'domexception',
+              string: JSON_stringify({message, name})
+            };
+          }
+
+          // JSON_stringify() is lossy, but it will serialise things that
+          // postMessage() won't.
+          return {encoder: 'json', string: JSON_stringify(reason)};
+        } catch (e) {
+          return {encoder: 'typeerror', string: 'Cannot transfer message'};
+        }
+
+      default:
+        return {encoder: 'undefined', string: undefined};
+    }
+  }
+
+  function unpackReason(packedReason) {
+    const {encoder, string} = packedReason;
+    switch (encoder) {
+      case 'json':
+        return JSON_parse(string);
+
+      case 'typeerror':
+        return new TypeError(string);
+
+      case 'domexception':
+        const {message, name} = JSON_parse(string);
+        return new DOMException(message, name);
+
+      case 'undefined':
+        return undefined;
+    }
+  }
+
+  function CreateCrossRealmTransformWritable(port) {
+    unsafeCopyGlobals();
+    let backpressurePromise = v8.createPromise();
+
+    MessagePort_addEventListener(port, 'message', evt => {
+      const {type, value} = MessageEvent_data_get(evt);
+      // assert(type === kPull || type === kCancel || type === kError);
+      switch (type) {
+        case kPull:
+          // assert(backPressurePromise !== undefined);
+          resolvePromise(backpressurePromise);
+          backpressurePromise = undefined;
+          break;
+
+        case kCancel:
+        case kError:
+          binding.WritableStreamDefaultControllerErrorIfNeeded(
+              controller, unpackReason(value));
+          if (backpressurePromise !== undefined) {
+            resolvePromise(backpressurePromise);
+            backpressurePromise = undefined;
+          }
+          break;
+      }
+    });
+
+    MessagePort_addEventListener(port, 'messageerror', () => {
+      const error = new DOMException('chunk could not be cloned',
+                                     'DataCloneError');
+      MessagePort_postMessage(port, {type: kError, value: packReason(error)});
+      MessagePort_close(port);
+      binding.WritableStreamDefaultControllerErrorIfNeeded(controller, error);
+    });
+
+    MessagePort_start(port);
+
+    function doWrite(chunk) {
+      backpressurePromise = v8.createPromise();
+      try {
+        MessagePort_postMessage(port, {type: kChunk, value: chunk});
+      } catch (e) {
+        MessagePort_postMessage(port, {type: kError, value: packReason(e)});
+        MessagePort_close(port);
+        throw e;
+      }
+    }
+
+    const stream = binding.CreateWritableStream(
+        () => undefined,
+        chunk => {
+          if (!backpressurePromise) {
+            return PromiseCall1(doWrite, null, chunk);
+          }
+          return thenPromise(backpressurePromise, () => doWrite(chunk));
+        },
+        () => {
+          MessagePort_postMessage(port, {type: kClose, value: undefined});
+          MessagePort_close(port);
+          return Promise_resolve();
+        },
+        reason => {
+          MessagePort_postMessage(port, {
+            type: kAbort,
+            value: packReason(reason)
+          });
+          MessagePort_close(port);
+          return Promise_resolve();
+        });
+
+    const controller = binding.getWritableStreamController(stream);
+    return stream;
+  }
+
+  function CreateCrossRealmTransformReadable(port) {
+    unsafeCopyGlobals();
+    let backpressurePromise = v8.createPromise();
+    let finished = false;
+
+    MessagePort_addEventListener(port, 'message', evt => {
+      const {type, value} = MessageEvent_data_get(evt);
+      // assert(type === kChunk || type === kClose || type === kAbort ||
+      //        type=kError);
+      switch (type) {
+        case kChunk:
+          if (finished) {
+            return;
+          }
+          binding.ReadableStreamDefaultControllerEnqueue(controller, value);
+          resolvePromise(backpressurePromise);
+          backpressurePromise = v8.createPromise();
+          break;
+
+        case kClose:
+          if (finished) {
+            return;
+          }
+          finished = true;
+          binding.ReadableStreamDefaultControllerClose(controller);
+          MessagePort_close(port);
+          break;
+
+        case kAbort:
+        case kError:
+          if (finished) {
+            return;
+          }
+          finished = true;
+          binding.ReadableStreamDefaultControllerError(controller,
+                                                       unpackReason(value));
+          MessagePort_close(port);
+          break;
+      }
+    });
+
+    MessagePort_addEventListener(port, 'messageerror', () => {
+      const error = new DOMException('chunk could not be cloned',
+                                     'DataCloneError');
+      MessagePort_postMessage(port, {type: kError, value: packReason(error)});
+      MessagePort_close(port);
+      binding.ReadableStreamDefaultControllerError(controller, error);
+    });
+
+    MessagePort_start(port);
+
+    const stream = binding.CreateReadableStream(
+        () => undefined,
+        () => {
+          MessagePort_postMessage(port, {type: kPull, value: undefined});
+          return backpressurePromise;
+        },
+        reason => {
+          finished = true;
+          MessagePort_postMessage(port, {
+            type: kCancel,
+            value: packReason(reason)
+          });
+          MessagePort_close(port);
+          return Promise_resolve();
+        }, /* highWaterMark = */ 0);
+
+    const controller = binding.getReadableStreamController(stream);
+    return stream;
+  }
+
   binding.streamOperations = {
     _queue,
     _queueTotalSize,
@@ -298,6 +577,8 @@
     promiseState,
     CreateAlgorithmFromUnderlyingMethod,
     CreateAlgorithmFromUnderlyingMethodPassingController,
+    CreateCrossRealmTransformWritable,
+    CreateCrossRealmTransformReadable,
     DequeueValue,
     EnqueueValueWithSize,
     PeekQueueValue,
diff --git a/third_party/blink/renderer/core/streams/ReadableStream.js b/third_party/blink/renderer/core/streams/ReadableStream.js
index 159462bf..4fcbab8 100644
--- a/third_party/blink/renderer/core/streams/ReadableStream.js
+++ b/third_party/blink/renderer/core/streams/ReadableStream.js
@@ -77,6 +77,8 @@
     CallOrNoop1,
     CreateAlgorithmFromUnderlyingMethod,
     CreateAlgorithmFromUnderlyingMethodPassingController,
+    CreateCrossRealmTransformReadable,
+    CreateCrossRealmTransformWritable,
     DequeueValue,
     EnqueueValueWithSize,
     MakeSizeAlgorithmFromSizeFunction,
@@ -129,6 +131,7 @@
   const errPipeThroughUndefinedReadable =
         'Failed to execute \'pipeThrough\' on \'ReadableStream\': parameter ' +
         '1\'s \'readable\' property is undefined.';
+  const errCannotTransferLockedStream = 'Cannot transfer a locked stream';
 
   let useCounted = false;
 
@@ -1127,6 +1130,41 @@
   }
 
   //
+  // Functions for transferable streams.
+  //
+
+  function ReadableStreamSerialize(readable) {
+    // assert(IsReadableStream(readable),
+    //        `! IsReadableStream(_readable_) is true`);
+    if (IsReadableStreamLocked(readable)) {
+      throw new TypeError(errCannotTransferLockedStream);
+    }
+
+    // TODO(ricea): Protect against changes to MessageChannel on the global
+    // object.
+    const mc = new MessageChannel();
+    const MessageChannel_port1_getter =
+          v8.uncurryThis(
+              Object.getOwnPropertyDescriptor(
+                  MessageChannel.prototype, 'port1').get);
+    const MessageChannel_port2_getter =
+          v8.uncurryThis(
+              Object.getOwnPropertyDescriptor(
+                  MessageChannel.prototype, 'port2').get);
+    const writable =
+          CreateCrossRealmTransformWritable(MessageChannel_port2_getter(mc));
+    // Failure behaviour here is not ideal.
+    const promise =
+          ReadableStreamPipeTo(readable, writable, false, false, false);
+    markPromiseAsHandled(promise);
+    return MessageChannel_port1_getter(mc);
+  }
+
+  function ReadableStreamDeserialize(port) {
+    return CreateCrossRealmTransformReadable(port);
+  }
+
+  //
   // Internal functions. Not part of the standard.
   //
 
@@ -1220,6 +1258,8 @@
     IsReadableStreamDefaultReader,
     ReadableStreamDefaultReaderRead,
     ReadableStreamTee,
+    ReadableStreamSerialize,
+    ReadableStreamDeserialize,
 
     //
     // Controller exports to Blink C++
diff --git a/third_party/blink/renderer/core/streams/WritableStream.js b/third_party/blink/renderer/core/streams/WritableStream.js
index 3ee1c57..f81eaab 100644
--- a/third_party/blink/renderer/core/streams/WritableStream.js
+++ b/third_party/blink/renderer/core/streams/WritableStream.js
@@ -1073,5 +1073,8 @@
     WritableStreamDefaultControllerErrorIfNeeded,
     isWritableStreamErroring,
     getWritableStreamController,
+
+    // Exports for CreateCrossRealmTransformWritable in CommonOperations.js
+    WritableStreamDefaultControllerClose,
   });
 });
diff --git a/third_party/blink/renderer/core/streams/readable_stream_operations.cc b/third_party/blink/renderer/core/streams/readable_stream_operations.cc
index 6f347b2..679a8bd 100644
--- a/third_party/blink/renderer/core/streams/readable_stream_operations.cc
+++ b/third_party/blink/renderer/core/streams/readable_stream_operations.cc
@@ -7,7 +7,9 @@
 #include <utility>
 
 #include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_message_port.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_script_runner.h"
+#include "third_party/blink/renderer/core/messaging/message_port.h"
 #include "third_party/blink/renderer/core/streams/underlying_source_base.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
@@ -264,4 +266,55 @@
   *new_stream2 = std::move(result2);
 }
 
+MessagePort* ReadableStreamOperations::ReadableStreamSerialize(
+    ScriptState* script_state,
+    ScriptValue stream,
+    ExceptionState& exception_state) {
+  DCHECK(IsReadableStreamForDCheck(script_state, stream));
+  v8::TryCatch block(script_state->GetIsolate());
+  v8::Local<v8::Value> args[] = {stream.V8Value()};
+  ScriptValue result(
+      script_state,
+      V8ScriptRunner::CallExtra(script_state, "ReadableStreamSerialize", args));
+  if (block.HasCaught()) {
+    exception_state.RethrowV8Exception(block.Exception());
+    return nullptr;
+  }
+  if (result.IsEmpty()) {
+    DCHECK(script_state->GetIsolate()->IsExecutionTerminating());
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kInvalidStateError,
+        "Serialize failed because execution is terminating");
+    return nullptr;
+  }
+
+  return V8MessagePort::ToImpl(
+      result.V8Value()->ToObject(script_state->GetContext()).ToLocalChecked());
+}
+
+ScriptValue ReadableStreamOperations::ReadableStreamDeserialize(
+    ScriptState* script_state,
+    MessagePort* port,
+    ExceptionState& exception_state) {
+  DCHECK(port);
+  auto* isolate = script_state->GetIsolate();
+  v8::Local<v8::Context> context = script_state->GetContext();
+  v8::Local<v8::Value> port_v8 = ToV8(port, context->Global(), isolate);
+  v8::TryCatch block(isolate);
+  v8::Local<v8::Value> args[] = {port_v8};
+  ScriptValue result(script_state,
+                     V8ScriptRunner::CallExtra(
+                         script_state, "ReadableStreamDeserialize", args));
+  if (block.HasCaught()) {
+    exception_state.RethrowV8Exception(block.Exception());
+    return ScriptValue();
+  }
+  if (result.IsEmpty()) {
+    DCHECK(script_state->GetIsolate()->IsExecutionTerminating());
+    return ScriptValue();
+  }
+  DCHECK(IsReadableStreamForDCheck(script_state, result));
+  return result;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/streams/readable_stream_operations.h b/third_party/blink/renderer/core/streams/readable_stream_operations.h
index d3665109..3674388 100644
--- a/third_party/blink/renderer/core/streams/readable_stream_operations.h
+++ b/third_party/blink/renderer/core/streams/readable_stream_operations.h
@@ -14,6 +14,7 @@
 
 class UnderlyingSourceBase;
 class ExceptionState;
+class MessagePort;
 class ScriptState;
 
 // This class has various methods for ReadableStream[Reader] implemented with
@@ -128,6 +129,20 @@
                   ScriptValue* new_stream1,
                   ScriptValue* new_stream2,
                   ExceptionState&);
+
+  // ReadableStreamSerialize. Returns a MessagePort which can be passed to
+  // ReadableStreamDeserialize to produce an equivalent ReadableStream in a
+  // different context.
+  static MessagePort* ReadableStreamSerialize(ScriptState*,
+                                              ScriptValue stream,
+                                              ExceptionState&);
+
+  // ReadableStreamDeserialize returns a new ReadableStream in the current
+  // context given a MessagePort which has possibly be transferred from another
+  // context.
+  static ScriptValue ReadableStreamDeserialize(ScriptState*,
+                                               MessagePort*,
+                                               ExceptionState&);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/streams/readable_stream_operations_test.cc b/third_party/blink/renderer/core/streams/readable_stream_operations_test.cc
index 17abd69..7459b41fd 100644
--- a/third_party/blink/renderer/core/streams/readable_stream_operations_test.cc
+++ b/third_party/blink/renderer/core/streams/readable_stream_operations_test.cc
@@ -19,6 +19,7 @@
 #include "third_party/blink/renderer/platform/bindings/v8_binding_macros.h"
 #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "v8/include/v8.h"
 
 namespace blink {
@@ -492,6 +493,43 @@
   EXPECT_EQ("hello", it2->Value());
 }
 
+TEST(ReadableStreamOperationsTest, Serialize) {
+  V8TestingScope scope;
+  TryCatchScope try_catch_scope(scope.GetIsolate());
+  ScriptValue original = EvalWithPrintingError(&scope,
+                                               "new ReadableStream({"
+                                               "  start(c) {"
+                                               "    c.enqueue('hello');"
+                                               "  }"
+                                               "})");
+  ASSERT_FALSE(original.IsEmpty());
+  MessagePort* port = ReadableStreamOperations::ReadableStreamSerialize(
+      scope.GetScriptState(), original, ASSERT_NO_EXCEPTION);
+  EXPECT_TRUE(port);
+  EXPECT_TRUE(ReadableStreamOperations::IsLocked(
+      scope.GetScriptState(), original, ASSERT_NO_EXCEPTION));
+  ScriptValue transferred = ReadableStreamOperations::ReadableStreamDeserialize(
+      scope.GetScriptState(), port, ASSERT_NO_EXCEPTION);
+  ASSERT_FALSE(transferred.IsEmpty());
+  ScriptValue reader = ReadableStreamOperations::GetReader(
+      scope.GetScriptState(), transferred, ASSERT_NO_EXCEPTION);
+  ASSERT_FALSE(reader.IsEmpty());
+  Iteration* it = new Iteration();
+  ReadableStreamOperations::DefaultReaderRead(scope.GetScriptState(), reader)
+      .Then(ReaderFunction::CreateFunction(scope.GetScriptState(), it),
+            ReadableStreamOperationsTestNotReached::CreateFunction(
+                scope.GetScriptState()));
+  // Let the message pass through the MessagePort.
+  test::RunPendingTasks();
+  // Let the Read promise resolve.
+  v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
+
+  EXPECT_TRUE(it->IsSet());
+  EXPECT_TRUE(it->IsValid());
+  EXPECT_FALSE(it->IsDone());
+  EXPECT_EQ("hello", it->Value());
+}
+
 }  // namespace
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/svg/svg_animate_element.cc b/third_party/blink/renderer/core/svg/svg_animate_element.cc
index d4251c59..fef3a6d 100644
--- a/third_party/blink/renderer/core/svg/svg_animate_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_animate_element.cc
@@ -465,8 +465,8 @@
           target_element->EnsureAnimatedSMILStyleProperties();
       if (property_set->RemoveProperty(css_property_id_)) {
         target_element->SetNeedsStyleRecalc(
-            kLocalStyleChange,
-            StyleChangeReasonForTracing::Create(StyleChangeReason::kAnimation));
+            kLocalStyleChange, StyleChangeReasonForTracing::Create(
+                                   style_change_reason::kAnimation));
       }
     }
   }
@@ -507,7 +507,7 @@
             .did_change) {
       targetElement()->SetNeedsStyleRecalc(
           kLocalStyleChange,
-          StyleChangeReasonForTracing::Create(StyleChangeReason::kAnimation));
+          StyleChangeReasonForTracing::Create(style_change_reason::kAnimation));
     }
   }
   if (IsAnimatingSVGDom()) {
diff --git a/third_party/blink/renderer/core/svg/svg_foreign_object_element.cc b/third_party/blink/renderer/core/svg/svg_foreign_object_element.cc
index 1356e69..269ffa87 100644
--- a/third_party/blink/renderer/core/svg/svg_foreign_object_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_foreign_object_element.cc
@@ -106,7 +106,7 @@
         kLocalStyleChange,
         is_width_height_attribute
             ? StyleChangeReasonForTracing::Create(
-                  StyleChangeReason::kSVGContainerSizeChange)
+                  style_change_reason::kSVGContainerSizeChange)
             : StyleChangeReasonForTracing::FromAttribute(attr_name));
 
     UpdateRelativeLengthsInformation();
diff --git a/third_party/blink/renderer/core/svg/svg_image_element.cc b/third_party/blink/renderer/core/svg/svg_image_element.cc
index 31cb703..e0c3484 100644
--- a/third_party/blink/renderer/core/svg/svg_image_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_image_element.cc
@@ -67,8 +67,8 @@
   AddToPropertyMap(height_);
   AddToPropertyMap(preserve_aspect_ratio_);
 
-  if (MediaElementParserHelpers::IsMediaElement(this) &&
-      !MediaElementParserHelpers::IsUnsizedMediaEnabled(document)) {
+  if (media_element_parser_helpers::IsMediaElement(this) &&
+      !media_element_parser_helpers::IsUnsizedMediaEnabled(document)) {
     is_default_overridden_intrinsic_size_ = true;
     overridden_intrinsic_size_ =
         IntSize(LayoutReplaced::kDefaultWidth, LayoutReplaced::kDefaultHeight);
@@ -176,7 +176,7 @@
                  ExperimentalProductivityFeaturesEnabled()) {
     String message;
     bool intrinsic_size_changed =
-        MediaElementParserHelpers::ParseIntrinsicSizeAttribute(
+        media_element_parser_helpers::ParseIntrinsicSizeAttribute(
             params.new_value, this, &overridden_intrinsic_size_,
             &is_default_overridden_intrinsic_size_, &message);
     if (!message.IsEmpty()) {
diff --git a/third_party/blink/renderer/core/svg/svg_style_element.cc b/third_party/blink/renderer/core/svg/svg_style_element.cc
index 6c25bb52..1559d25 100644
--- a/third_party/blink/renderer/core/svg/svg_style_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_style_element.cc
@@ -66,7 +66,7 @@
 
 const AtomicString& SVGStyleElement::media() const {
   const AtomicString& n = FastGetAttribute(SVGNames::mediaAttr);
-  return n.IsNull() ? MediaTypeNames::all : n;
+  return n.IsNull() ? media_type_names::kAll : n;
 }
 
 void SVGStyleElement::setMedia(const AtomicString& media) {
diff --git a/third_party/blink/renderer/core/svg/svg_svg_element.cc b/third_party/blink/renderer/core/svg/svg_svg_element.cc
index ff10645..35f22ba 100644
--- a/third_party/blink/renderer/core/svg/svg_svg_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_svg_element.cc
@@ -258,7 +258,7 @@
         InvalidateSVGPresentationAttributeStyle();
         SetNeedsStyleRecalc(kLocalStyleChange,
                             StyleChangeReasonForTracing::Create(
-                                StyleChangeReason::kSVGContainerSizeChange));
+                                style_change_reason::kSVGContainerSizeChange));
         if (layout_object)
           ToLayoutSVGRoot(layout_object)->IntrinsicSizingInfoChanged();
       }
diff --git a/third_party/blink/renderer/devtools/front_end/heap_profiler_test_runner/HeapProfilerTestRunner.js b/third_party/blink/renderer/devtools/front_end/heap_profiler_test_runner/HeapProfilerTestRunner.js
index ba23798..91193f4 100644
--- a/third_party/blink/renderer/devtools/front_end/heap_profiler_test_runner/HeapProfilerTestRunner.js
+++ b/third_party/blink/renderer/devtools/front_end/heap_profiler_test_runner/HeapProfilerTestRunner.js
@@ -716,9 +716,9 @@
 HeapProfilerTestRunner.startSamplingHeapProfiler = async function() {
   if (!UI.context.flavor(SDK.HeapProfilerModel))
     await new Promise(resolve => UI.context.addFlavorChangeListener(SDK.HeapProfilerModel, resolve));
-  Profiler.SamplingHeapProfileType.instance.startRecordingProfile();
+  Profiler.SamplingHeapProfileType.instance._startRecordingProfile();
 };
 
 HeapProfilerTestRunner.stopSamplingHeapProfiler = function() {
-  Profiler.SamplingHeapProfileType.instance.stopRecordingProfile();
+  Profiler.SamplingHeapProfileType.instance._stopRecordingProfile();
 };
diff --git a/third_party/blink/renderer/devtools/front_end/main/Main.js b/third_party/blink/renderer/devtools/front_end/main/Main.js
index 7b3c363..518fb19 100644
--- a/third_party/blink/renderer/devtools/front_end/main/Main.js
+++ b/third_party/blink/renderer/devtools/front_end/main/Main.js
@@ -117,6 +117,7 @@
     Runtime.experiments.register('oopifInlineDOM', 'OOPIF: inline DOM ', true);
     Runtime.experiments.register('pinnedExpressions', 'Pinned expressions in Console', true);
     Runtime.experiments.register('protocolMonitor', 'Protocol Monitor');
+    Runtime.experiments.register('samplingHeapProfilerTimeline', 'Sampling heap profiler timeline', true);
     Runtime.experiments.register('sourceDiff', 'Source diff');
     Runtime.experiments.register('sourcesPrettyPrint', 'Automatically pretty print in the Sources Panel');
     Runtime.experiments.register(
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/CPUProfileView.js b/third_party/blink/renderer/devtools/front_end/profiler/CPUProfileView.js
index 5b9025d..a901ccf 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/CPUProfileView.js
+++ b/third_party/blink/renderer/devtools/front_end/profiler/CPUProfileView.js
@@ -34,10 +34,11 @@
   constructor(profileHeader) {
     super();
     this._profileHeader = profileHeader;
-    this.profile = profileHeader.profileModel();
-    this.adjustedTotal = this.profile.profileHead.total;
-    this.adjustedTotal -= this.profile.idleNode ? this.profile.idleNode.total : 0;
     this.initialize(new Profiler.CPUProfileView.NodeFormatter(this));
+    const profile = profileHeader.profileModel();
+    this.adjustedTotal = profile.profileHead.total;
+    this.adjustedTotal -= profile.idleNode ? profile.idleNode.total : 0;
+    this.setProfile(profile);
   }
 
   /**
@@ -47,7 +48,7 @@
     super.wasShown();
     const lineLevelProfile = PerfUI.LineLevelProfile.instance();
     lineLevelProfile.reset();
-    lineLevelProfile.appendCPUProfile(this.profile);
+    lineLevelProfile.appendCPUProfile(this._profileHeader.profileModel());
   }
 
   /**
@@ -70,7 +71,8 @@
    * @return {!PerfUI.FlameChartDataProvider}
    */
   createFlameChartDataProvider() {
-    return new Profiler.CPUFlameChartDataProvider(this.profile, this._profileHeader._cpuProfilerModel);
+    return new Profiler.CPUFlameChartDataProvider(
+        this._profileHeader.profileModel(), this._profileHeader._cpuProfilerModel);
   }
 };
 
@@ -121,10 +123,10 @@
    */
   buttonClicked() {
     if (this._recording) {
-      this.stopRecordingProfile();
+      this._stopRecordingProfile();
       return false;
     } else {
-      this.startRecordingProfile();
+      this._startRecordingProfile();
       return true;
     }
   }
@@ -148,7 +150,7 @@
     this.addProfile(profile);
   }
 
-  startRecordingProfile() {
+  _startRecordingProfile() {
     const cpuProfilerModel = UI.context.flavor(SDK.CPUProfilerModel);
     if (this.profileBeingRecorded() || !cpuProfilerModel)
       return;
@@ -162,7 +164,7 @@
     Host.userMetrics.actionTaken(Host.UserMetrics.Action.ProfilesCPUProfileTaken);
   }
 
-  async stopRecordingProfile() {
+  async _stopRecordingProfile() {
     this._recording = false;
     if (!this.profileBeingRecorded() || !this.profileBeingRecorded()._cpuProfilerModel)
       return;
@@ -193,7 +195,7 @@
    * @override
    */
   profileBeingRecordedRemoved() {
-    this.stopRecordingProfile();
+    this._stopRecordingProfile();
   }
 };
 
@@ -272,7 +274,7 @@
    * @return {string}
    */
   formatPercent(value, node) {
-    return node.profileNode === this._profileView.profile.idleNode ? '' : Common.UIString('%.2f\xa0%%', value);
+    return node.profileNode === this._profileView.profile().idleNode ? '' : Common.UIString('%.2f\xa0%%', value);
   }
 
   /**
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/HeapProfileView.js b/third_party/blink/renderer/devtools/front_end/profiler/HeapProfileView.js
index 5660455..44cd414c 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/HeapProfileView.js
+++ b/third_party/blink/renderer/devtools/front_end/profiler/HeapProfileView.js
@@ -1,6 +1,7 @@
 // Copyright 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+
 /**
  * @implements {UI.Searchable}
  * @unrestricted
@@ -11,18 +12,109 @@
    */
   constructor(profileHeader) {
     super();
+
     this._profileHeader = profileHeader;
-    this.profile = new Profiler.SamplingHeapProfileModel(profileHeader._profile || profileHeader.protocolProfile());
-    this.adjustedTotal = this.profile.total;
+    this._profileType = profileHeader.profileType();
     const views = [
       Profiler.ProfileView.ViewTypes.Flame, Profiler.ProfileView.ViewTypes.Heavy, Profiler.ProfileView.ViewTypes.Tree
     ];
-    const isNativeProfile = [
-      Profiler.SamplingNativeHeapProfileType.TypeId, Profiler.SamplingNativeHeapSnapshotType.TypeId
-    ].includes(profileHeader.profileType().id);
+
+    const isNativeProfile = this._profileType.id === Profiler.SamplingNativeHeapProfileType.TypeId ||
+        this._profileType.id === Profiler.SamplingNativeHeapSnapshotType.TypeId;
     if (isNativeProfile)
       views.push(Profiler.ProfileView.ViewTypes.Text);
+
     this.initialize(new Profiler.HeapProfileView.NodeFormatter(this), views);
+    const profile = new Profiler.SamplingHeapProfileModel(profileHeader._profile || profileHeader.protocolProfile());
+    this.adjustedTotal = profile.total;
+    this.setProfile(profile);
+
+    this._selectedSizeText = new UI.ToolbarText();
+
+    if (Runtime.experiments.isEnabled('samplingHeapProfilerTimeline')) {
+      this._timelineOverview = new Profiler.HeapTimelineOverview();
+      this._timelineOverview.addEventListener(
+          Profiler.HeapTimelineOverview.IdsRangeChanged, this._onIdsRangeChanged.bind(this));
+      this._timelineOverview.show(this.element, this.element.firstChild);
+
+      this._profileType.addEventListener(
+          Profiler.SamplingHeapProfileType.Events.StatsUpdate, this._onStatsUpdate, this);
+      this._profileType.once(Profiler.ProfileType.Events.ProfileComplete).then(() => {
+        this._profileType.removeEventListener(
+            Profiler.SamplingHeapProfileType.Events.StatsUpdate, this._onStatsUpdate, this);
+        this._timelineOverview.updateGrid();
+      });
+    }
+  }
+
+  /**
+   * @override
+   * @return {!Array<!UI.ToolbarItem>}
+   */
+  syncToolbarItems() {
+    return [...super.syncToolbarItems(), this._selectedSizeText];
+  }
+
+  /**
+   * @param {!Common.Event} event
+   */
+  _onIdsRangeChanged(event) {
+    const minId = /** @type {number} */ (event.data.minId);
+    const maxId = /** @type {number} */ (event.data.maxId);
+    this._selectedSizeText.setText(ls`Selected size: ${Number.bytesToString(event.data.size)}`);
+    this._setSelectionRange(minId, maxId);
+  }
+
+  /**
+   * @param {number} minId
+   * @param {number} maxId
+   */
+  _setSelectionRange(minId, maxId) {
+    const profile = new Profiler.SamplingHeapProfileModel(
+        this._profileHeader._profile || this._profileHeader.protocolProfile(), minId, maxId);
+    this.adjustedTotal = profile.total;
+    this.setProfile(profile);
+  }
+
+  /**
+   * @param {!Common.Event} event
+   */
+  _onStatsUpdate(event) {
+    const profile = event.data;
+
+    if (!this._startTime) {
+      this._startTime = Date.now();
+      this._timestamps = [];
+      this._sizes = [];
+      this._max = [];
+      this._ordinals = [];
+      this._totalTime = 30000;
+      this._lastOrdinal = 0;
+    }
+
+    this._sizes.fill(0);
+    this._sizes.push(0);
+    this._timestamps.push(Date.now() - this._startTime);
+    this._ordinals.push(this._lastOrdinal + 1);
+    this._lastOrdinal = profile.samples.reduce((res, sample) => Math.max(res, sample.ordinal), this._lastOrdinal);
+    for (const sample of profile.samples) {
+      const bucket = this._ordinals.upperBound(sample.ordinal) - 1;
+      this._sizes[bucket] += sample.size;
+    }
+    this._max.push(this._sizes.peekLast());
+
+    if (this._timestamps.peekLast() > this._totalTime)
+      this._totalTime *= 2;
+
+    const samples = /** @type {!Profiler.HeapTimelineOverview.Samples} */ ({
+      sizes: this._sizes,
+      max: this._max,
+      ids: this._ordinals,
+      timestamps: this._timestamps,
+      totalTime: this._totalTime
+    });
+
+    this._timelineOverview.setSamples(samples);
   }
 
   /**
@@ -45,7 +137,8 @@
    * @return {!PerfUI.FlameChartDataProvider}
    */
   createFlameChartDataProvider() {
-    return new Profiler.HeapFlameChartDataProvider(this.profile, this._profileHeader.heapProfilerModel());
+    return new Profiler.HeapFlameChartDataProvider(
+        /** @type {!Profiler.SamplingHeapProfileModel} */ (this.profile()), this._profileHeader.heapProfilerModel());
   }
 
   /**
@@ -58,10 +151,10 @@
         `Report Version:  7\n` +
         `App Version:     ${/Chrom\S*/.exec(navigator.appVersion)[0] || 'Unknown'}\n` +
         `Node Weight:     1 KiB\n` +
-        `Total Size:      ${Math.round(this.profile.root.total / 1024)} KiB\n` +
+        `Total Size:      ${Math.round(this.profile().root.total / 1024)} KiB\n` +
         `----\n\nCall graph:\n`;
-    const sortedChildren = this.profile.root.children.sort((a, b) => b.total - a.total);
-    const modules = this.profile.modules.map(
+    const sortedChildren = this.profile().root.children.sort((a, b) => b.total - a.total);
+    const modules = this.profile().modules.map(
         m => Object.assign({address: BigInt(m.baseAddress), endAddress: BigInt(m.baseAddress) + BigInt(m.size)}, m));
     modules.sort((m1, m2) => m1.address > m2.address ? 1 : m1.address < m2.address ? -1 : 0);
     for (const child of sortedChildren)
@@ -121,6 +214,10 @@
  * @unrestricted
  */
 Profiler.SamplingHeapProfileTypeBase = class extends Profiler.ProfileType {
+  /**
+   * @param {string} typeId
+   * @param {string} description
+   */
   constructor(typeId, description) {
     super(typeId, description);
     this._recording = false;
@@ -151,7 +248,7 @@
   }
 
   get buttonTooltip() {
-    return this._recording ? Common.UIString('Stop heap profiling') : Common.UIString('Start heap profiling');
+    return this._recording ? ls`Stop heap profiling` : ls`Start heap profiling`;
   }
 
   /**
@@ -159,37 +256,36 @@
    * @return {boolean}
    */
   buttonClicked() {
-    const wasRecording = this._recording;
-    if (wasRecording)
-      this.stopRecordingProfile();
+    if (this._recording)
+      this._stopRecordingProfile();
     else
-      this.startRecordingProfile();
-    return !wasRecording;
+      this._startRecordingProfile();
+    return this._recording;
   }
 
-  startRecordingProfile() {
+  _startRecordingProfile() {
     const heapProfilerModel = UI.context.flavor(SDK.HeapProfilerModel);
     if (this.profileBeingRecorded() || !heapProfilerModel)
       return;
-    const profile = new Profiler.SamplingHeapProfileHeader(heapProfilerModel, this);
-    this.setProfileBeingRecorded(profile);
-    this.addProfile(profile);
-    profile.updateStatus(Common.UIString('Recording\u2026'));
+    const profileHeader = new Profiler.SamplingHeapProfileHeader(heapProfilerModel, this);
+    this.setProfileBeingRecorded(profileHeader);
+    this.addProfile(profileHeader);
+    profileHeader.updateStatus(ls`Recording\u2026`);
 
     const icon = UI.Icon.create('smallicon-warning');
-    icon.title = Common.UIString('Heap profiler is recording');
+    icon.title = ls`Heap profiler is recording`;
     UI.inspectorView.setPanelIcon('heap_profiler', icon);
 
     this._recording = true;
     this._startSampling();
   }
 
-  async stopRecordingProfile() {
+  async _stopRecordingProfile() {
     this._recording = false;
     if (!this.profileBeingRecorded() || !this.profileBeingRecorded().heapProfilerModel())
       return;
 
-    this.profileBeingRecorded().updateStatus(Common.UIString('Stopping\u2026'));
+    this.profileBeingRecorded().updateStatus(ls`Stopping\u2026`);
     const profile = await this._stopSampling();
     const recordedProfile = this.profileBeingRecorded();
     if (recordedProfile) {
@@ -215,7 +311,7 @@
    * @override
    */
   profileBeingRecordedRemoved() {
-    this.stopRecordingProfile();
+    this._stopRecordingProfile();
   }
 
   _startSampling() {
@@ -230,7 +326,6 @@
   }
 };
 
-
 /**
  * @unrestricted
  */
@@ -238,6 +333,8 @@
   constructor() {
     super(Profiler.SamplingHeapProfileType.TypeId, ls`Allocation sampling`);
     Profiler.SamplingHeapProfileType.instance = this;
+    this._updateTimer = null;
+    this._updateIntervalMs = 200;
   }
 
   get treeItemTitle() {
@@ -252,9 +349,19 @@
 
   /**
    * @override
+   * @return {boolean}
+   */
+  hasTemporaryView() {
+    return Runtime.experiments.isEnabled('samplingHeapProfilerTimeline');
+  }
+
+  /**
+   * @override
    */
   _startSampling() {
     this.profileBeingRecorded().heapProfilerModel().startSampling();
+    if (Runtime.experiments.isEnabled('samplingHeapProfilerTimeline'))
+      this._updateTimer = setTimeout(this._updateStats.bind(this), this._updateIntervalMs);
   }
 
   /**
@@ -262,12 +369,29 @@
    * return {!Promise<!Protocol.HeapProfiler.SamplingHeapProfile>}
    */
   _stopSampling() {
+    clearTimeout(this._updateTimer);
+    this._updateTimer = null;
+    this.dispatchEventToListeners(Profiler.SamplingHeapProfileType.Events.RecordingStopped);
     return this.profileBeingRecorded().heapProfilerModel().stopSampling();
   }
+
+  async _updateStats() {
+    const profile = await this.profileBeingRecorded().heapProfilerModel().getSamplingProfile();
+    if (!this._updateTimer)
+      return;
+    this.dispatchEventToListeners(Profiler.SamplingHeapProfileType.Events.StatsUpdate, profile);
+    this._updateTimer = setTimeout(this._updateStats.bind(this), this._updateIntervalMs);
+  }
 };
 
 Profiler.SamplingHeapProfileType.TypeId = 'SamplingHeap';
 
+/** @enum {symbol} */
+Profiler.SamplingHeapProfileType.Events = {
+  RecordingStopped: Symbol('RecordingStopped'),
+  StatsUpdate: Symbol('StatsUpdate')
+};
+
 /**
  * @unrestricted
  */
@@ -426,6 +550,8 @@
         heapProfilerModel && heapProfilerModel.debuggerModel(), type,
         title || Common.UIString('Profile %d', type.nextProfileUid()));
     this._heapProfilerModel = heapProfilerModel;
+    this._protocolProfile =
+        /** @type {!Protocol.HeapProfiler.SamplingHeapProfile} */ ({head: {callFrame: {}, children: []}});
   }
 
   /**
@@ -478,29 +604,61 @@
 Profiler.SamplingHeapProfileModel = class extends SDK.ProfileTreeModel {
   /**
    * @param {!Protocol.HeapProfiler.SamplingHeapProfile} profile
+   * @param {number=} minOrdinal
+   * @param {number=} maxOrdinal
    */
-  constructor(profile) {
+  constructor(profile, minOrdinal, maxOrdinal) {
     super();
-    this.initialize(translateProfileTree(profile.head));
     this.modules = profile.modules || [];
 
+    /** @type {?Map<number, number>} */
+    let nodeIdToSizeMap = null;
+    if (minOrdinal || maxOrdinal) {
+      nodeIdToSizeMap = new Map();
+      minOrdinal = minOrdinal || 0;
+      maxOrdinal = maxOrdinal || Infinity;
+      for (const sample of profile.samples) {
+        if (sample.ordinal < minOrdinal || sample.ordinal > maxOrdinal)
+          continue;
+        const size = nodeIdToSizeMap.get(sample.nodeId) || 0;
+        nodeIdToSizeMap.set(sample.nodeId, size + sample.size);
+      }
+    }
+
+    this.initialize(translateProfileTree(profile.head));
+
     /**
      * @param {!Protocol.HeapProfiler.SamplingHeapProfileNode} root
      * @return {!Profiler.SamplingHeapProfileNode}
      */
     function translateProfileTree(root) {
       const resultRoot = new Profiler.SamplingHeapProfileNode(root);
-      const targetNodeStack = [resultRoot];
       const sourceNodeStack = [root];
+      const targetNodeStack = [resultRoot];
       while (sourceNodeStack.length) {
         const sourceNode = sourceNodeStack.pop();
-        const parentNode = targetNodeStack.pop();
-        parentNode.children = sourceNode.children.map(child => new Profiler.SamplingHeapProfileNode(child));
-        sourceNodeStack.push.apply(sourceNodeStack, sourceNode.children);
-        targetNodeStack.push.apply(targetNodeStack, parentNode.children);
+        const targetNode = targetNodeStack.pop();
+        targetNode.children = sourceNode.children.map(child => {
+          const targetChild = new Profiler.SamplingHeapProfileNode(child);
+          if (nodeIdToSizeMap)
+            targetChild.self = nodeIdToSizeMap.get(child.id) || 0;
+          return targetChild;
+        });
+        sourceNodeStack.pushAll(sourceNode.children);
+        targetNodeStack.pushAll(targetNode.children);
       }
+      pruneEmptyBranches(resultRoot);
       return resultRoot;
     }
+
+    /**
+     * @param {!SDK.ProfileNode} node
+     * @return {boolean}
+     */
+    function pruneEmptyBranches(node) {
+      node.children = node.children.filter(pruneEmptyBranches);
+      return !!(node.children.length || node.self);
+    }
   }
 };
 
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/HeapTimelineOverview.js b/third_party/blink/renderer/devtools/front_end/profiler/HeapTimelineOverview.js
index 6b38265..2e1d3e21 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/HeapTimelineOverview.js
+++ b/third_party/blink/renderer/devtools/front_end/profiler/HeapTimelineOverview.js
@@ -204,30 +204,23 @@
     this._updateGridTimerId = 0;
     this._updateBoundaries();
     const ids = this._profileSamples.ids;
+    if (!ids.length)
+      return;
     const timestamps = this._profileSamples.timestamps;
     const sizes = this._profileSamples.sizes;
     const startTime = timestamps[0];
     const totalTime = this._profileSamples.totalTime;
     const timeLeft = startTime + totalTime * this._windowLeft;
     const timeRight = startTime + totalTime * this._windowRight;
-    let minId = 0;
-    let maxId = ids[ids.length - 1] + 1;
+    const minIndex = timestamps.lowerBound(timeLeft);
+    const maxIndex = timestamps.upperBound(timeRight);
     let size = 0;
-    for (let i = 0; i < timestamps.length; ++i) {
-      if (!timestamps[i])
-        continue;
-      if (timestamps[i] > timeRight)
-        break;
-      maxId = ids[i];
-      if (timestamps[i] < timeLeft) {
-        minId = ids[i];
-        continue;
-      }
+    for (let i = minIndex; i < maxIndex; ++i)
       size += sizes[i];
-    }
+    const minId = minIndex < ids.length ? ids[minIndex] : Infinity;
+    const maxId = maxIndex < ids.length ? ids[maxIndex] : Infinity;
 
-    this.dispatchEventToListeners(
-        Profiler.HeapTimelineOverview.IdsRangeChanged, {minId: minId, maxId: maxId, size: size});
+    this.dispatchEventToListeners(Profiler.HeapTimelineOverview.IdsRangeChanged, {minId, maxId, size});
   }
 };
 
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/ProfileView.js b/third_party/blink/renderer/devtools/front_end/profiler/ProfileView.js
index c61e1267..ab3c2e81 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/ProfileView.js
+++ b/third_party/blink/renderer/devtools/front_end/profiler/ProfileView.js
@@ -1,6 +1,7 @@
 // Copyright 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+
 /**
  * @implements {UI.Searchable}
  * @unrestricted
@@ -9,6 +10,8 @@
   constructor() {
     super(Common.UIString('Profile'));
 
+    this._profile = null;
+
     this._searchableView = new UI.SearchableView(this);
     this._searchableView.setPlaceholder(Common.UIString('Find by cost (>50ms), name or file'));
     this._searchableView.show(this.element);
@@ -62,6 +65,24 @@
   }
 
   /**
+   * @param {!SDK.ProfileTreeModel} profile
+   */
+  setProfile(profile) {
+    this._profile = profile;
+    this._bottomUpProfileDataGridTree = null;
+    this._topDownProfileDataGridTree = null;
+    this._changeView();
+    this.refresh();
+  }
+
+  /**
+   * @return {?SDK.ProfileTreeModel}
+   */
+  profile() {
+    return this._profile;
+  }
+
+  /**
    * @param {!Profiler.ProfileDataGridNode.Formatter} nodeFormatter
    * @param {!Array<string>=} viewTypes
    * @protected
@@ -122,7 +143,7 @@
 
   /**
    * @override
-   * @return {!Array.<!UI.ToolbarItem>}
+   * @return {!Array<!UI.ToolbarItem>}
    */
   syncToolbarItems() {
     return [this.viewSelectComboBox, this.focusButton, this.excludeButton, this.resetButton];
@@ -134,7 +155,7 @@
   _getBottomUpProfileDataGridTree() {
     if (!this._bottomUpProfileDataGridTree) {
       this._bottomUpProfileDataGridTree = new Profiler.BottomUpProfileDataGridTree(
-          this._nodeFormatter, this._searchableView, this.profile.root, this.adjustedTotal);
+          this._nodeFormatter, this._searchableView, this._profile.root, this.adjustedTotal);
     }
     return this._bottomUpProfileDataGridTree;
   }
@@ -145,7 +166,7 @@
   _getTopDownProfileDataGridTree() {
     if (!this._topDownProfileDataGridTree) {
       this._topDownProfileDataGridTree = new Profiler.TopDownProfileDataGridTree(
-          this._nodeFormatter, this._searchableView, this.profile.root, this.adjustedTotal);
+          this._nodeFormatter, this._searchableView, this._profile.root, this.adjustedTotal);
     }
     return this._topDownProfileDataGridTree;
   }
@@ -158,6 +179,8 @@
   }
 
   refresh() {
+    if (!this.profileDataGridTree)
+      return;
     const selectedProfileNode = this.dataGrid.selectedNode ? this.dataGrid.selectedNode.profileNode : null;
 
     this.dataGrid.rootNode().removeChildren();
@@ -288,7 +311,7 @@
   }
 
   _changeView() {
-    if (!this.profile)
+    if (!this._profile)
       return;
 
     this._searchableView.closeSearch();
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/ProfilesPanel.js b/third_party/blink/renderer/devtools/front_end/profiler/ProfilesPanel.js
index d7d5ffe..eb7ffe8 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/ProfilesPanel.js
+++ b/third_party/blink/renderer/devtools/front_end/profiler/ProfilesPanel.js
@@ -328,8 +328,7 @@
     if (i !== -1)
       this._profileToView.splice(i, 1);
 
-    const profileType = profile.profileType();
-    const typeId = profileType.id;
+    const typeId = profile.profileType().id;
     const sectionIsEmpty = this._typeIdToSidebarSection[typeId].removeProfileHeader(profile);
 
     // No other item will be selected if there aren't any other profiles, so
@@ -410,11 +409,7 @@
    * @return {number}
    */
   _indexOfViewForProfile(profile) {
-    for (let i = 0; i < this._profileToView.length; i++) {
-      if (this._profileToView[i].profile === profile)
-        return i;
-    }
-    return -1;
+    return this._profileToView.findIndex(item => item.profile === profile);
   }
 
   closeVisibleView() {
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/heapProfiler.css b/third_party/blink/renderer/devtools/front_end/profiler/heapProfiler.css
index c5f60446..7e5ce49 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/heapProfiler.css
+++ b/third_party/blink/renderer/devtools/front_end/profiler/heapProfiler.css
@@ -70,7 +70,7 @@
     flex: auto;
 }
 
-.heap-snapshot-view .heap-tracking-overview {
+.profile-view .heap-tracking-overview {
     flex: 0 0 80px;
     height: 80px;
 }
@@ -145,7 +145,7 @@
     opacity: 0.6;
 }
 
-#heap-recording-view .heap-snapshot-view {
+#heap-recording-view .profile-view {
     top: 80px;
 }
 
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/HeapProfilerModel.js b/third_party/blink/renderer/devtools/front_end/sdk/HeapProfilerModel.js
index 7de6630..60c222e8 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/HeapProfilerModel.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/HeapProfilerModel.js
@@ -45,10 +45,16 @@
    * @return {!Promise<?Protocol.HeapProfiler.SamplingHeapProfile>}
    */
   stopSampling() {
-    this._isRecording = false;
     return this._heapProfilerAgent.stopSampling();
   }
 
+  /**
+   * @return {!Promise<?Protocol.HeapProfiler.SamplingHeapProfile>}
+   */
+  getSamplingProfile() {
+    return this._heapProfilerAgent.getSamplingProfile();
+  }
+
   startNativeSampling() {
     const defaultSamplingIntervalInBytes = 65536;
     this._memoryAgent.startSampling(defaultSamplingIntervalInBytes);
diff --git a/third_party/blink/renderer/devtools/front_end/security/mainView.css b/third_party/blink/renderer/devtools/front_end/security/mainView.css
index 411048d..744b3a9 100644
--- a/third_party/blink/renderer/devtools/front_end/security/mainView.css
+++ b/third_party/blink/renderer/devtools/front_end/security/mainView.css
@@ -132,7 +132,7 @@
     display: flex;
     white-space: nowrap;
     border: none;
-    color: #8c8c8c;
+    color: rgb(90, 90, 90);
 }
 
 .security-explanation-text {
@@ -153,7 +153,7 @@
 }
 
 .security-explanation-title {
-    color: rgb(90, 90, 90);
+    color: rgb(48, 57, 66);
     margin-top: 1px;
     margin-bottom: 8px;
 }
diff --git a/third_party/blink/renderer/devtools/scripts/run_old_devtools/.gitignore b/third_party/blink/renderer/devtools/scripts/run_old_devtools/.gitignore
new file mode 100644
index 0000000..231fb44a
--- /dev/null
+++ b/third_party/blink/renderer/devtools/scripts/run_old_devtools/.gitignore
@@ -0,0 +1,2 @@
+debugging-user-data-dir
+user-data-dir
diff --git a/third_party/blink/renderer/devtools/scripts/run_old_devtools/README.md b/third_party/blink/renderer/devtools/scripts/run_old_devtools/README.md
new file mode 100644
index 0000000..0b5c1e85
--- /dev/null
+++ b/third_party/blink/renderer/devtools/scripts/run_old_devtools/README.md
@@ -0,0 +1,9 @@
+# Running Old Devtools
+
+This package launches your current version of Chromium, and an old version of Chromium.
+It then opens the DevTools of the old Chromium inside the new Chromium. This can be
+used to test devtools_compatibility.js. Remember to recompile after making changes
+to devtools_compatibility.js.
+
+## Usage
+First run `npm install` in this directory. Then run `node index.js <revision_number>`.
diff --git a/third_party/blink/renderer/devtools/scripts/run_old_devtools/index.js b/third_party/blink/renderer/devtools/scripts/run_old_devtools/index.js
new file mode 100644
index 0000000..1a91738
--- /dev/null
+++ b/third_party/blink/renderer/devtools/scripts/run_old_devtools/index.js
@@ -0,0 +1,68 @@
+const path = require('path');
+const fs = require('fs');
+const puppeteer = require('puppeteer');
+const childProcess = require('child_process');
+const CHROMIUM_DEFAULT_PATH = path.resolve(__dirname, '..', '..', '..', '..', '..', '..', 'out', 'Release', 'chrome');
+
+(async function() {
+  const targetRevision = process.argv[2];
+  if (!targetRevision || String(parseInt(targetRevision, 10)) !== targetRevision) {
+    console.log('The first argument should be a Chromium revision number, like 338865');
+    return;
+  }
+  const info = await downloadChrome(targetRevision);
+  childProcess.exec(`${info.executablePath} --remote-debugging-port=9227 --user-data-dir=user-data-dir about:blank`);
+  const browser = await puppeteer.launch({
+    executablePath: process.env.CHROMIUM_PATH || CHROMIUM_DEFAULT_PATH,
+    dumpio: true,
+    defaultViewport: null,
+    ignoreDefaultArgs: true,
+    args: puppeteer.defaultArgs({
+      headless: false,
+      args: ['--no-default-browser-check', `--custom-devtools-frontend=http://localhost:9227/devtools/`],
+      userDataDir: 'debugging-user-data-dir'
+    }).filter(x => !x.startsWith('--enable-automation'))
+  });
+  const page = (await browser.pages())[0];
+  await page.goto('http://localhost:9227');
+  const chromeVersion = await page.evaluate(async () => {
+    const data = await fetch('json/version');
+    const json = await data.json();
+    const userAgent = json['User-Agent'];
+    const start = userAgent.indexOf('Chrome/') + 'Chrome/'.length;
+    return userAgent.substring(start, userAgent.indexOf(' ', start));
+  });
+  console.log(`Launched Chromium ${chromeVersion}`);
+  await page.bringToFront();
+  await page.waitForSelector('a');
+  const realURL = await page.evaluate(() => document.querySelector('a').href);
+
+  const url = `chrome-devtools://devtools/custom/${realURL.substring('http://localhost:9227/devtools/'.length)}&remoteVersion=${chromeVersion}`;
+
+  await page.goto(url);
+})();
+
+async function downloadChrome(revision) {
+  const fetcher = puppeteer.createBrowserFetcher();
+  let progressBar = null;
+  let lastDownloadedBytes = 0;
+  return await fetcher.download(revision, (downloadedBytes, totalBytes) => {
+    if (!progressBar) {
+      const ProgressBar = require('progress');
+      progressBar = new ProgressBar(`Downloading Chromium r${revision} - ${toMegabytes(totalBytes)} [:bar] :percent :etas `, {
+        complete: '=',
+        incomplete: ' ',
+        width: 20,
+        total: totalBytes,
+      });
+    }
+    const delta = downloadedBytes - lastDownloadedBytes;
+    lastDownloadedBytes = downloadedBytes;
+    progressBar.tick(delta);
+  });
+}
+
+function toMegabytes(bytes) {
+  const mb = bytes / 1024 / 1024;
+  return `${Math.round(mb * 10) / 10} Mb`;
+}
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/scripts/run_old_devtools/package.json b/third_party/blink/renderer/devtools/scripts/run_old_devtools/package.json
new file mode 100644
index 0000000..0dc45f4d
--- /dev/null
+++ b/third_party/blink/renderer/devtools/scripts/run_old_devtools/package.json
@@ -0,0 +1,10 @@
+{
+  "main": "index.js",
+  "scripts": {
+    "start": "node index.js"
+  },
+  "dependencies": {
+    "progress": "^2.0.1",
+    "puppeteer": "^1.9.0"
+  }
+}
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index a75c00d..b9be0d1a 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -273,10 +273,14 @@
     "indexeddb/idb_test_helper.cc",
     "indexeddb/idb_transaction_test.cc",
     "indexeddb/idb_value_wrapping_test.cc",
+    "indexeddb/mock_web_idb_callbacks.cc",
+    "indexeddb/mock_web_idb_callbacks.h",
     "indexeddb/mock_web_idb_database.cc",
     "indexeddb/mock_web_idb_database.h",
     "indexeddb/mock_web_idb_factory.cc",
     "indexeddb/mock_web_idb_factory.h",
+    "indexeddb/web_idb_cursor_impl_unittest.cc",
+    "indexeddb/web_idb_database_impl_unittest.cc",
     "manifest/image_resource_type_converters_test.cc",
     "media_controls/elements/media_control_animated_arrow_container_element_test.cc",
     "media_controls/elements/media_control_display_cutout_fullscreen_button_element_test.cc",
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
index b5215b22..5f72d37 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -577,6 +577,11 @@
   if (layout_object_->IsAnonymousBlock() && !IsEditable())
     return true;
 
+  // Ignore continuations, since those are essentially duplicate copies
+  // of inline nodes with blocks inside.
+  if (layout_object_->IsElementContinuation())
+    return true;
+
   // If this element is within a parent that cannot have children, it should not
   // be exposed.
   if (IsDescendantOfLeafNode()) {
@@ -1784,6 +1789,9 @@
 //    in the layout tree, see if the parent node has a continuation, and if
 //    so follow it and make that the next sibling.
 //
+// 3. When computing the first child of a node that has a continuation but
+//    no children in the layout tree, the continuation is the first child.
+//
 // The end result is this tree, which we use as the basis for the
 // accessibility tree.
 //
@@ -1852,6 +1860,8 @@
 }
 
 // See LAYOUT TREE WALKING ALGORITHM, above, for details.
+// Return true if this layout object is the continuation of some other
+// layout object.
 static bool IsContinuation(LayoutObject* layout_object) {
   if (layout_object->IsElementContinuation())
     return true;
@@ -1864,6 +1874,19 @@
 }
 
 // See LAYOUT TREE WALKING ALGORITHM, above, for details.
+// Return the continuation of this layout object, or nullptr if it doesn't
+// have one.
+LayoutObject* GetContinuation(LayoutObject* layout_object) {
+  if (layout_object->IsLayoutInline())
+    return ToLayoutInline(layout_object)->Continuation();
+
+  if (layout_object->IsLayoutBlockFlow())
+    return ToLayoutBlockFlow(layout_object)->Continuation();
+
+  return nullptr;
+}
+
+// See LAYOUT TREE WALKING ALGORITHM, above, for details.
 AXObject* AXLayoutObject::RawFirstChild() const {
   if (!layout_object_)
     return nullptr;
@@ -1890,10 +1913,17 @@
   while (first_child && IsContinuation(first_child))
     first_child = first_child->NextSibling();
 
-  if (!first_child)
-    return nullptr;
+  // If there's a first child that's not a continuation, return that.
+  if (first_child)
+    return AXObjectCache().GetOrCreate(first_child);
 
-  return AXObjectCache().GetOrCreate(first_child);
+  // Finally check if this object has no children but it has a continuation
+  // itself - and if so, it's the first child.
+  LayoutObject* continuation = GetContinuation(layout_object_);
+  if (continuation)
+    return AXObjectCache().GetOrCreate(continuation);
+
+  return nullptr;
 }
 
 // See LAYOUT TREE WALKING ALGORITHM, above, for details.
@@ -1923,12 +1953,7 @@
   // object has a continuation, and if so, follow it.
   LayoutObject* parent = layout_object_->Parent();
   if (parent) {
-    LayoutObject* continuation = nullptr;
-    if (parent->IsLayoutInline())
-      continuation = ToLayoutInline(parent)->Continuation();
-    else if (parent->IsLayoutBlockFlow())
-      continuation = ToLayoutBlockFlow(parent)->Continuation();
-
+    LayoutObject* continuation = GetContinuation(parent);
     if (continuation)
       return AXObjectCache().GetOrCreate(continuation);
   }
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.cc b/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.cc
index 813131fc..64c91ada 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.cc
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.cc
@@ -162,7 +162,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
       Platform::Current()->CurrentThread()->GetTaskRunner();
 
-  BackgroundScheduler::PostOnBackgroundThread(
+  background_scheduler::PostOnBackgroundThread(
       FROM_HERE,
       CrossThreadBind(
           &BackgroundFetchIconLoader::DecodeAndResizeImageOnBackgroundThread,
diff --git a/third_party/blink/renderer/modules/exported/BUILD.gn b/third_party/blink/renderer/modules/exported/BUILD.gn
index f79e1d1..bfc6251 100644
--- a/third_party/blink/renderer/modules/exported/BUILD.gn
+++ b/third_party/blink/renderer/modules/exported/BUILD.gn
@@ -5,6 +5,7 @@
 
 blink_modules_sources("exported") {
   sources = [
+    "indexed_db_key_builder.cc",
     "web_apply_constraints_request.cc",
     "web_ax_context.cc",
     "web_ax_object.cc",
diff --git a/content/renderer/indexed_db/indexed_db_key_builders.cc b/third_party/blink/renderer/modules/exported/indexed_db_key_builder.cc
similarity index 62%
rename from content/renderer/indexed_db/indexed_db_key_builders.cc
rename to third_party/blink/renderer/modules/exported/indexed_db_key_builder.cc
index ad9b6b22..4825b10 100644
--- a/content/renderer/indexed_db/indexed_db_key_builders.cc
+++ b/third_party/blink/renderer/modules/exported/indexed_db_key_builder.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/renderer/indexed_db/indexed_db_key_builders.h"
+#include "third_party/blink/public/platform/modules/indexeddb/indexed_db_key_builder.h"
 
 #include <stddef.h>
 
@@ -10,7 +10,6 @@
 #include <string>
 #include <vector>
 
-#include "base/logging.h"
 #include "third_party/blink/public/common/indexeddb/indexeddb_key_range.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_key.h"
@@ -18,32 +17,18 @@
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_key_range.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_vector.h"
+#include "third_party/blink/renderer/modules/indexeddb/idb_key_range.h"
 
-using blink::IndexedDBKey;
-using blink::IndexedDBKeyPath;
-using blink::IndexedDBKeyRange;
-using blink::WebIDBKey;
-using blink::WebIDBKeyRange;
-using blink::WebIDBKeyView;
-using blink::kWebIDBKeyTypeArray;
-using blink::kWebIDBKeyTypeBinary;
-using blink::kWebIDBKeyTypeDate;
-using blink::kWebIDBKeyTypeInvalid;
-using blink::kWebIDBKeyTypeMin;
-using blink::kWebIDBKeyTypeNull;
-using blink::kWebIDBKeyTypeNumber;
-using blink::kWebIDBKeyTypeString;
-using blink::WebVector;
-using blink::WebString;
+namespace blink {
 
 namespace {
 
-IndexedDBKey::KeyArray CopyKeyArray(blink::WebIDBKeyArrayView array) {
+IndexedDBKey::KeyArray CopyKeyArray(WebIDBKeyArrayView array) {
   IndexedDBKey::KeyArray result;
   const size_t array_size = array.size();
   result.reserve(array_size);
   for (size_t i = 0; i < array_size; ++i)
-    result.emplace_back(content::IndexedDBKeyBuilder::Build(array[i]));
+    result.emplace_back(IndexedDBKeyBuilder::Build(array[i]));
   return result;
 }
 
@@ -57,15 +42,13 @@
 
 }  // anonymous namespace
 
-namespace content {
-
 // static
-IndexedDBKey IndexedDBKeyBuilder::Build(blink::WebIDBKeyView key) {
+IndexedDBKey IndexedDBKeyBuilder::Build(WebIDBKeyView key) {
   switch (key.KeyType()) {
     case kWebIDBKeyTypeArray:
       return IndexedDBKey(CopyKeyArray(key.ArrayView()));
     case kWebIDBKeyTypeBinary: {
-      const blink::WebData data = key.Binary();
+      const WebData data = key.Binary();
       std::string key_string;
       key_string.reserve(data.size());
 
@@ -87,13 +70,44 @@
     case kWebIDBKeyTypeInvalid:
       return IndexedDBKey(key.KeyType());
     case kWebIDBKeyTypeMin:
-    default:
       NOTREACHED();
       return IndexedDBKey();
   }
 }
 
 // static
+WebIDBKey WebIDBKeyBuilder::Build(const WebIDBKeyView& key) {
+  switch (key.KeyType()) {
+    case kWebIDBKeyTypeArray: {
+      const WebIDBKeyArrayView& array = key.ArrayView();
+      WebVector<WebIDBKey> web_idb_keys;
+      const size_t array_size = array.size();
+      web_idb_keys.reserve(array_size);
+      for (size_t i = 0; i < array_size; ++i)
+        web_idb_keys.emplace_back(Build(array[i]));
+      return WebIDBKey::CreateArray(std::move(web_idb_keys));
+    }
+    case kWebIDBKeyTypeBinary: {
+      const WebData data = key.Binary();
+      return WebIDBKey::CreateBinary(data);
+    }
+    case kWebIDBKeyTypeString:
+      return WebIDBKey::CreateString(key.String());
+    case kWebIDBKeyTypeDate:
+      return WebIDBKey::CreateDate(key.Date());
+    case kWebIDBKeyTypeNumber:
+      return WebIDBKey::CreateNumber(key.Number());
+    case kWebIDBKeyTypeInvalid:
+      return WebIDBKey::CreateInvalid();
+    case kWebIDBKeyTypeNull:
+      return WebIDBKey::CreateNull();
+    case kWebIDBKeyTypeMin:
+      NOTREACHED();
+      return WebIDBKey::CreateInvalid();
+  }
+}
+
+// static
 WebIDBKey WebIDBKeyBuilder::Build(const IndexedDBKey& key) {
   switch (key.type()) {
     case kWebIDBKeyTypeArray: {
@@ -104,8 +118,11 @@
         web_idb_keys.emplace_back(Build(array_element));
       return WebIDBKey::CreateArray(std::move(web_idb_keys));
     }
-    case kWebIDBKeyTypeBinary:
-      return WebIDBKey::CreateBinary(key.binary());
+    case kWebIDBKeyTypeBinary: {
+      const std::string& str = key.binary();
+      const WebData& data = WebData(str.c_str(), str.length());
+      return WebIDBKey::CreateBinary(data);
+    }
     case kWebIDBKeyTypeString:
       return WebIDBKey::CreateString(WebString::FromUTF16(key.string()));
     case kWebIDBKeyTypeDate:
@@ -117,7 +134,6 @@
     case kWebIDBKeyTypeNull:
       return WebIDBKey::CreateNull();
     case kWebIDBKeyTypeMin:
-    default:
       NOTREACHED();
       return WebIDBKey::CreateInvalid();
   }
@@ -139,6 +155,13 @@
 }
 
 // static
+WebIDBKeyRange WebIDBKeyRangeBuilder::Build(WebIDBKeyView key) {
+  return WebIDBKeyRange(WebIDBKeyBuilder::Build(key),
+                        WebIDBKeyBuilder::Build(key), false /* lower_open */,
+                        false /* upper_open */);
+}
+
+// static
 WebIDBKeyRange WebIDBKeyRangeBuilder::Build(
     const IndexedDBKeyRange& key_range) {
   return WebIDBKeyRange(WebIDBKeyBuilder::Build(key_range.lower()),
@@ -147,43 +170,31 @@
 }
 
 // static
-IndexedDBKeyPath IndexedDBKeyPathBuilder::Build(
-    const blink::WebIDBKeyPath& key_path) {
+IndexedDBKeyPath IndexedDBKeyPathBuilder::Build(const WebIDBKeyPath& key_path) {
   switch (key_path.KeyPathType()) {
-    case blink::kWebIDBKeyPathTypeString:
+    case kWebIDBKeyPathTypeString:
       return IndexedDBKeyPath(key_path.String().Utf16());
-    case blink::kWebIDBKeyPathTypeArray:
+    case kWebIDBKeyPathTypeArray:
       return IndexedDBKeyPath(CopyArray(key_path.Array()));
-    case blink::kWebIDBKeyPathTypeNull:
-      return IndexedDBKeyPath();
-    default:
-      NOTREACHED();
+    case kWebIDBKeyPathTypeNull:
       return IndexedDBKeyPath();
   }
 }
 
 // static
-blink::WebIDBKeyPath WebIDBKeyPathBuilder::Build(
-    const IndexedDBKeyPath& key_path) {
+WebIDBKeyPath WebIDBKeyPathBuilder::Build(const IndexedDBKeyPath& key_path) {
   switch (key_path.type()) {
-    case blink::kWebIDBKeyPathTypeString:
-      return blink::WebIDBKeyPath::Create(
-          WebString::FromUTF16(key_path.string()));
-    case blink::kWebIDBKeyPathTypeArray: {
+    case kWebIDBKeyPathTypeString:
+      return WebIDBKeyPath::Create(WebString::FromUTF16(key_path.string()));
+    case kWebIDBKeyPathTypeArray: {
       WebVector<WebString> key_path_vector(key_path.array().size());
-      std::transform(key_path.array().begin(), key_path.array().end(),
-                     key_path_vector.begin(),
-                     [](const typename base::string16& s) {
-                       return WebString::FromUTF16(s);
-                     });
-      return blink::WebIDBKeyPath::Create(key_path_vector);
+      for (const auto& item : key_path.array())
+        key_path_vector.emplace_back(WebString::FromUTF16(item));
+      return WebIDBKeyPath::Create(key_path_vector);
     }
-    case blink::kWebIDBKeyPathTypeNull:
-      return blink::WebIDBKeyPath::CreateNull();
-    default:
-      NOTREACHED();
-      return blink::WebIDBKeyPath::CreateNull();
+    case kWebIDBKeyPathTypeNull:
+      return WebIDBKeyPath::CreateNull();
   }
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/exported/web_idb_key.cc b/third_party/blink/renderer/modules/exported/web_idb_key.cc
index 600bfce..4b26410 100644
--- a/third_party/blink/renderer/modules/exported/web_idb_key.cc
+++ b/third_party/blink/renderer/modules/exported/web_idb_key.cc
@@ -68,6 +68,23 @@
   return private_->Number();
 }
 
+size_t WebIDBKeyView::SizeEstimate() const {
+  // TODO(cmp): Ensure |private_| can never be null.
+  //
+  // SizeEstimate() can be called when |private_| is null.  That happens if and
+  // only if the |WebIDBKey| instance is created using WebIDBKey::CreateNull().
+  //
+  // Eventually, WebIDBKey::CreateNull() will change so that case will lead to
+  // a non-null |private_|.  At that time, this null check can change to a
+  // DCHECK that |private_| is not null and the special null case handling can
+  // be removed.
+  if (this->IsNull()) {
+    return IDBKey::kIDBKeyOverheadSize;
+  }
+
+  return private_->SizeEstimate();
+}
+
 WebIDBKey WebIDBKey::CreateArray(WebVector<WebIDBKey> array) {
   IDBKey::KeyArray keys;
   keys.ReserveCapacity(SafeCast<wtf_size_t>(array.size()));
@@ -112,4 +129,11 @@
   return *this;
 }
 
+WebIDBKey::WebIDBKey(const WebIDBKey& rkey)
+    : private_(IDBKey::Clone(rkey.private_)) {}
+WebIDBKey& WebIDBKey::operator=(const WebIDBKey& rkey) {
+  private_ = IDBKey::Clone(rkey.private_);
+  return *this;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/indexeddb/BUILD.gn b/third_party/blink/renderer/modules/indexeddb/BUILD.gn
index b16dcc0..8f00ce4 100644
--- a/third_party/blink/renderer/modules/indexeddb/BUILD.gn
+++ b/third_party/blink/renderer/modules/indexeddb/BUILD.gn
@@ -58,13 +58,34 @@
     "idb_version_change_event.cc",
     "idb_version_change_event.h",
     "indexed_db.h",
+    "indexed_db_callbacks_impl.cc",
+    "indexed_db_callbacks_impl.h",
     "indexed_db_client.cc",
     "indexed_db_client.h",
+    "indexed_db_database_callbacks_impl.cc",
+    "indexed_db_database_callbacks_impl.h",
+    "indexed_db_dispatcher.cc",
+    "indexed_db_dispatcher.h",
+    "indexed_db_mojom_traits.cc",
+    "indexed_db_mojom_traits.h",
     "inspector_indexed_db_agent.cc",
     "inspector_indexed_db_agent.h",
     "web_idb_callbacks_impl.cc",
     "web_idb_callbacks_impl.h",
+    "web_idb_cursor.h",
+    "web_idb_cursor_impl.cc",
+    "web_idb_cursor_impl.h",
+    "web_idb_database.h",
     "web_idb_database_callbacks_impl.cc",
     "web_idb_database_callbacks_impl.h",
+    "web_idb_database_impl.cc",
+    "web_idb_database_impl.h",
+    "web_idb_factory.h",
+    "web_idb_factory_impl.cc",
+    "web_idb_factory_impl.h",
+  ]
+
+  public_deps = [
+    "//third_party/blink/public/mojom:mojom_modules_blink",
   ]
 }
diff --git a/third_party/blink/renderer/modules/indexeddb/OWNERS b/third_party/blink/renderer/modules/indexeddb/OWNERS
index a740487..4d50d63 100644
--- a/third_party/blink/renderer/modules/indexeddb/OWNERS
+++ b/third_party/blink/renderer/modules/indexeddb/OWNERS
@@ -1,4 +1,9 @@
 file://content/browser/indexed_db/OWNERS
 
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
+
 # TEAM: storage-dev@chromium.org
 # COMPONENT: Blink>Storage>IndexedDB
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc b/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc
index 0a815c7..d161436 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc
@@ -27,7 +27,6 @@
 
 #include <limits>
 #include <memory>
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_key_range.h"
 #include "third_party/blink/renderer/bindings/modules/v8/to_v8_for_modules.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.h"
@@ -38,6 +37,7 @@
 #include "third_party/blink/renderer/modules/indexeddb/idb_object_store.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_tracing.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_transaction.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_database.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/bindings/v8_private_property.h"
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_cursor.h b/third_party/blink/renderer/modules/indexeddb/idb_cursor.h
index 64cec7cd..821aaf2 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_cursor.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_cursor.h
@@ -29,12 +29,12 @@
 #include <memory>
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_cursor.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/bindings/modules/v8/idb_object_store_or_idb_index.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_key.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_request.h"
 #include "third_party/blink/renderer/modules/indexeddb/indexed_db.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/wtf/compiler.h"
 
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_cursor_with_value.h b/third_party/blink/renderer/modules/indexeddb/idb_cursor_with_value.h
index 91223d4..5914effa 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_cursor_with_value.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_cursor_with_value.h
@@ -28,9 +28,9 @@
 
 #include <memory>
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_cursor.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_cursor.h"
 #include "third_party/blink/renderer/modules/indexeddb/indexed_db.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_database.cc b/third_party/blink/renderer/modules/indexeddb/idb_database.cc
index db3a230..ab6dc279 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_database.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_database.cc
@@ -208,7 +208,7 @@
       IDBTransaction* transaction = nullptr;
       auto it = transactions.find(map_entry.first);
       if (it != transactions.end()) {
-        const std::pair<int64_t, std::vector<int64_t>>& obs_txn = it->second;
+        const std::pair<int64_t, WebVector<int64_t>>& obs_txn = it->second;
         HashSet<String> stores;
         for (int64_t store_id : obs_txn.second) {
           stores.insert(metadata_.object_stores.at(store_id)->name);
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_database.h b/third_party/blink/renderer/modules/indexeddb/idb_database.h
index 90c1175..c85d9183 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_database.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_database.h
@@ -29,7 +29,6 @@
 #include <memory>
 
 #include "base/memory/scoped_refptr.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_callbacks.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/bindings/core/v8/string_or_string_sequence.h"
@@ -43,6 +42,7 @@
 #include "third_party/blink/renderer/modules/indexeddb/idb_object_store_parameters.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_transaction.h"
 #include "third_party/blink/renderer/modules/indexeddb/indexed_db.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_database.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h"
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_factory.cc b/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
index 1e123b7a..85b5354 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
@@ -31,9 +31,10 @@
 #include <memory>
 #include <utility>
 
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
+#include "third_party/blink/public/platform/interface_provider.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_callbacks.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_callbacks.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_factory.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_name_and_version.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_value.h"
 #include "third_party/blink/public/platform/platform.h"
@@ -53,6 +54,8 @@
 #include "third_party/blink/renderer/modules/indexeddb/idb_key.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_tracing.h"
 #include "third_party/blink/renderer/modules/indexeddb/indexed_db_client.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_factory.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_factory_impl.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/histogram.h"
@@ -186,8 +189,13 @@
 }
 
 WebIDBFactory* IDBFactory::GetFactory() {
-  if (!web_idb_factory_)
-    web_idb_factory_ = Platform::Current()->CreateIdbFactory();
+  if (!web_idb_factory_) {
+    mojom::blink::IDBFactoryPtrInfo web_idb_factory_host_info;
+    Platform::Current()->GetInterfaceProvider()->GetInterface(
+        mojo::MakeRequest(&web_idb_factory_host_info));
+    web_idb_factory_ = std::make_unique<WebIDBFactoryImpl>(
+        std::move(web_idb_factory_host_info));
+  }
   return web_idb_factory_.get();
 }
 
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_factory.h b/third_party/blink/renderer/modules/indexeddb/idb_factory.h
index 3d7e36d..3ff9b1c 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_factory.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_factory.h
@@ -29,9 +29,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_IDB_FACTORY_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_IDB_FACTORY_H_
 
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_factory.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_open_db_request.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_factory.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_index.h b/third_party/blink/renderer/modules/indexeddb/idb_index.h
index 99a4824..e78ec6ff 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_index.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_index.h
@@ -27,13 +27,13 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_IDB_INDEX_H_
 
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_cursor.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_cursor.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_key_path.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_key_range.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_metadata.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_request.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_database.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_key.cc b/third_party/blink/renderer/modules/indexeddb/idb_key.cc
index 8ed421c..530e07b 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_key.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_key.cc
@@ -34,6 +34,40 @@
 
 namespace blink {
 
+namespace {
+
+size_t CalculateIDBKeyArraySize(const IDBKey::KeyArray& keys) {
+  size_t size(0);
+  for (const auto& key : keys)
+    size += key.get()->SizeEstimate();
+  return size;
+}
+
+}  // namespace
+
+IDBKey::IDBKey() : type_(kInvalidType), size_estimate_(kIDBKeyOverheadSize) {}
+
+IDBKey::IDBKey(Type type, double number)
+    : type_(type),
+      number_(number),
+      size_estimate_(kIDBKeyOverheadSize + sizeof(number_)) {}
+
+IDBKey::IDBKey(const class String& value)
+    : type_(kStringType),
+      string_(value),
+      size_estimate_(kIDBKeyOverheadSize + (string_.length() * sizeof(UChar))) {
+}
+
+IDBKey::IDBKey(scoped_refptr<SharedBuffer> value)
+    : type_(kBinaryType),
+      binary_(std::move(value)),
+      size_estimate_(kIDBKeyOverheadSize + binary_.get()->size()) {}
+
+IDBKey::IDBKey(KeyArray key_array)
+    : type_(kArrayType),
+      array_(std::move(key_array)),
+      size_estimate_(kIDBKeyOverheadSize + CalculateIDBKeyArraySize(array_)) {}
+
 IDBKey::~IDBKey() = default;
 
 bool IDBKey::IsValid() const {
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_key.h b/third_party/blink/renderer/modules/indexeddb/idb_key.h
index 3082acad..a57d257 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_key.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_key.h
@@ -78,8 +78,50 @@
     return base::WrapUnique(new IDBKey(std::move(array)));
   }
 
+  // TODO(cmp): This |Clone| function is necessary for WebIDBKey's ctor
+  //            functions.  It needs to be available in this header file so
+  //            web_idb_key.cc can use it.  When the IDB Blink variant typemap
+  //            moves to the renderer/modules/indexeddb/ types and off of the
+  //            WebIDB* types, this |Clone| function should be removed.
+  static std::unique_ptr<IDBKey> Clone(const std::unique_ptr<IDBKey>& rkey_in) {
+    IDBKey* rkey = rkey_in.get();
+    if (!rkey_in.get())
+      return nullptr;
+
+    switch (rkey->GetType()) {
+      case kInvalidType:
+        return IDBKey::CreateInvalid();
+      case kArrayType: {
+        IDBKey::KeyArray lkey_array;
+        const auto& rkey_array = rkey->Array();
+        for (const auto& rkey_item : rkey_array)
+          lkey_array.push_back(IDBKey::Clone(rkey_item));
+        return IDBKey::CreateArray(std::move(lkey_array));
+      }
+      case kBinaryType:
+        return IDBKey::CreateBinary(rkey->Binary());
+      case kStringType:
+        return IDBKey::CreateString(rkey->GetString());
+      case kDateType:
+        return IDBKey::CreateDate(rkey->Date());
+      case kNumberType:
+        return IDBKey::CreateNumber(rkey->Number());
+
+      case kTypeEnumMax:
+        break;  // Not used, NOTREACHED.
+    }
+    NOTREACHED();
+    return nullptr;
+  }
+
   ~IDBKey();
 
+  // Very rough estimate of minimum key size overhead.
+  //
+  // TODO(cmp): When the reference to this in web_idb_key.cc goes away, move
+  //            this variable back to idb_key.cc's anonymous namespace.
+  static const size_t kIDBKeyOverheadSize = 16;
+
   // In order of the least to the highest precedent in terms of sort order.
   // These values are written to logs. New enum values can be added, but
   // existing enums must never be renumbered or deleted and reused.
@@ -124,6 +166,7 @@
   int Compare(const IDBKey* other) const;
   bool IsLessThan(const IDBKey* other) const;
   bool IsEqual(const IDBKey* other) const;
+  size_t SizeEstimate() const { return size_estimate_; }
 
   // Returns a new key array with invalid keys and duplicates removed.
   //
@@ -140,20 +183,22 @@
  private:
   DISALLOW_COPY_AND_ASSIGN(IDBKey);
 
-  IDBKey() : type_(kInvalidType) {}
-  IDBKey(Type type, double number) : type_(type), number_(number) {}
-  explicit IDBKey(const class String& value)
-      : type_(kStringType), string_(value) {}
-  explicit IDBKey(scoped_refptr<SharedBuffer> value)
-      : type_(kBinaryType), binary_(std::move(value)) {}
-  explicit IDBKey(KeyArray key_array)
-      : type_(kArrayType), array_(std::move(key_array)) {}
+  IDBKey();
+  IDBKey(Type type, double number);
+  explicit IDBKey(const class String& value);
+  explicit IDBKey(scoped_refptr<SharedBuffer> value);
+  explicit IDBKey(KeyArray key_array);
 
   Type type_;
   KeyArray array_;
   scoped_refptr<SharedBuffer> binary_;
   const class String string_;
   const double number_ = 0;
+
+  // Initialized in IDBKey constructors based on key type and value size (see
+  // idb_key.cc).  Returned via SizeEstimate() and used in IndexedDB code to
+  // verify that a given key is small enough to pass over IPC.
+  size_t size_estimate_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_object_store.cc b/third_party/blink/renderer/modules/indexeddb/idb_object_store.cc
index 722fa6d6..51f4277d 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_object_store.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_object_store.cc
@@ -30,7 +30,6 @@
 #include "base/feature_list.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/numerics/safe_conversions.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_key.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_key_range.h"
 #include "third_party/blink/public/platform/web_blob_info.h"
@@ -49,6 +48,7 @@
 #include "third_party/blink/renderer/modules/indexeddb/idb_key_path.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_tracing.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_value_wrapping.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_database.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/histogram.h"
@@ -558,8 +558,8 @@
     key_type_histogram.Count(static_cast<int>(key->GetType()));
   }
 
-  WebVector<WebIDBIndexKeys> index_keys;
-  index_keys.reserve(Metadata().indexes.size());
+  Vector<WebIDBIndexKeys> index_keys;
+  index_keys.ReserveInitialCapacity(Metadata().indexes.size());
   for (const auto& it : Metadata().indexes) {
     if (clone.IsEmpty())
       value_wrapper.Clone(script_state, &clone);
@@ -754,8 +754,8 @@
       const IDBKey* primary_key = cursor->IdbPrimaryKey();
       ScriptValue value = cursor->value(script_state_);
 
-      WebVector<WebIDBIndexKeys> index_keys;
-      index_keys.reserve(1);
+      Vector<WebIDBIndexKeys> index_keys;
+      index_keys.ReserveInitialCapacity(1);
       index_keys.emplace_back(
           IndexMetadata().id,
           GenerateIndexKeysForValue(script_state_->GetIsolate(),
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_object_store.h b/third_party/blink/renderer/modules/indexeddb/idb_object_store.h
index 4174356a..747ece3 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_object_store.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_object_store.h
@@ -28,8 +28,6 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_cursor.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_cursor.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_index.h"
@@ -39,6 +37,8 @@
 #include "third_party/blink/renderer/modules/indexeddb/idb_metadata.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_request.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_transaction.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_database.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.h b/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.h
index 36d9ecb..31a8c98 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.h
@@ -27,8 +27,8 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_IDB_OPEN_DB_REQUEST_H_
 
 #include <memory>
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_request.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_database.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_request.h b/third_party/blink/renderer/modules/indexeddb/idb_request.h
index 5e1980d8..2e737d33 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_request.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_request.h
@@ -34,7 +34,6 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_cursor.h"
 #include "third_party/blink/public/platform/web_blob_info.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
@@ -49,6 +48,7 @@
 #include "third_party/blink/renderer/modules/indexeddb/idb_any.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_transaction.h"
 #include "third_party/blink/renderer/modules/indexeddb/indexed_db.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/blob/blob_data.h"
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_request_queue_item.cc b/third_party/blink/renderer/modules/indexeddb/idb_request_queue_item.cc
index cfbe57f..bb8753c 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_request_queue_item.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_request_queue_item.cc
@@ -9,12 +9,12 @@
 
 #include "base/callback.h"
 #include "base/memory/scoped_refptr.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_cursor.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_key.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_request.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_request_loader.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_value.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_transaction.h b/third_party/blink/renderer/modules/indexeddb/idb_transaction.h
index bfb15df..32c30c51 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_transaction.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_transaction.h
@@ -29,7 +29,6 @@
 #include <memory>
 
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h"
 #include "third_party/blink/renderer/core/dom/dom_string_list.h"
@@ -38,6 +37,7 @@
 #include "third_party/blink/renderer/modules/event_target_modules.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_metadata.h"
 #include "third_party/blink/renderer/modules/indexeddb/indexed_db.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_database.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/wtf/deque.h"
diff --git a/third_party/blink/renderer/modules/indexeddb/indexed_db.typemap b/third_party/blink/renderer/modules/indexeddb/indexed_db.typemap
new file mode 100644
index 0000000..e4f018a
--- /dev/null
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db.typemap
@@ -0,0 +1,41 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//third_party/blink/public/mojom/indexeddb/indexeddb.mojom"
+public_headers = [
+  "//third_party/blink/public/common/indexeddb/web_idb_types.h",
+  "//third_party/blink/public/platform/modules/indexeddb/web_idb_key.h",
+  "//third_party/blink/public/platform/modules/indexeddb/web_idb_key_path.h",
+  "//third_party/blink/public/platform/modules/indexeddb/web_idb_key_range.h",
+  "//third_party/blink/public/platform/modules/indexeddb/web_idb_metadata.h",
+  "//third_party/blink/public/platform/modules/indexeddb/web_idb_name_and_version.h",
+  "//third_party/blink/renderer/modules/indexeddb/idb_key_range.h",
+]
+traits_headers = [
+  "//mojo/public/cpp/base/string16_mojom_traits.h",
+  "//mojo/public/cpp/bindings/array_traits_web_vector.h",
+  "//mojo/public/cpp/bindings/array_traits_wtf_vector.h",
+  "//third_party/blink/public/common/indexeddb/indexeddb_mojom_traits.h",
+  "//third_party/blink/renderer/modules/indexeddb/indexed_db_mojom_traits.h",
+  "//third_party/blink/renderer/platform/mojo/string16_mojom_traits.h",
+]
+deps = [
+  "//mojo/public/cpp/bindings",
+  "//third_party/blink/renderer/platform/wtf",
+]
+type_mappings = [
+  "blink.mojom.IDBCursorDirection=::blink::WebIDBCursorDirection",
+  "blink.mojom.IDBDataLoss=::blink::WebIDBDataLoss",
+  "blink.mojom.IDBDatabaseMetadata=::blink::WebIDBMetadata",
+  "blink.mojom.IDBIndexKeys=::blink::WebIDBIndexKeys",
+  "blink.mojom.IDBIndexMetadata=::blink::WebIDBMetadata::Index",
+  "blink.mojom.IDBKey=::blink::WebIDBKey[move_only]",
+  "blink.mojom.IDBKeyPath=::blink::WebIDBKeyPath",
+  "blink.mojom.IDBKeyRange=::blink::WebIDBKeyRange",
+  "blink.mojom.IDBObjectStoreMetadata=::blink::WebIDBMetadata::ObjectStore",
+  "blink.mojom.IDBOperationType=::blink::WebIDBOperationType",
+  "blink.mojom.IDBPutMode=::blink::WebIDBPutMode",
+  "blink.mojom.IDBTaskType=::blink::WebIDBTaskType",
+  "blink.mojom.IDBTransactionMode=::blink::WebIDBTransactionMode",
+]
diff --git a/third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.cc b/third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.cc
new file mode 100644
index 0000000..1ccdcbfe
--- /dev/null
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.cc
@@ -0,0 +1,212 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.h"
+
+#include "third_party/blink/public/platform/file_path_conversion.h"
+#include "third_party/blink/public/platform/modules/indexeddb/indexed_db_key_builder.h"
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_callbacks.h"
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_error.h"
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_metadata.h"
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_name_and_version.h"
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_value.h"
+#include "third_party/blink/renderer/modules/indexeddb/idb_key_range.h"
+#include "third_party/blink/renderer/modules/indexeddb/indexed_db_dispatcher.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.h"
+
+using blink::IndexedDBDatabaseMetadata;
+using blink::WebBlobInfo;
+using blink::WebData;
+using blink::WebIDBCallbacks;
+using blink::WebIDBDatabase;
+using blink::WebIDBMetadata;
+using blink::WebIDBNameAndVersion;
+using blink::WebIDBValue;
+using blink::WebString;
+using blink::WebVector;
+using blink::mojom::blink::IDBDatabaseAssociatedPtrInfo;
+
+namespace blink {
+
+namespace {
+
+WebIDBValue ConvertReturnValue(const mojom::blink::IDBReturnValuePtr& value) {
+  if (!value)
+    return WebIDBValue(WebData(), WebVector<WebBlobInfo>());
+
+  WebIDBValue web_value = IndexedDBCallbacksImpl::ConvertValue(value->value);
+  web_value.SetInjectedPrimaryKey(value->primary_key, value->key_path);
+  return web_value;
+}
+
+WebIDBNameAndVersion ConvertNameVersion(
+    const mojom::blink::IDBNameAndVersionPtr& name_and_version) {
+  return WebIDBNameAndVersion(name_and_version->name,
+                              name_and_version->version);
+}
+
+}  // namespace
+
+// static
+WebIDBValue IndexedDBCallbacksImpl::ConvertValue(
+    const mojom::blink::IDBValuePtr& value) {
+  if (!value || value->bits.length() == 0)
+    return WebIDBValue(WebData(), WebVector<WebBlobInfo>());
+
+  WebVector<WebBlobInfo> local_blob_info;
+  local_blob_info.reserve(value->blob_or_file_info.size());
+  for (const auto& info : value->blob_or_file_info) {
+    if (info->file) {
+      local_blob_info.emplace_back(
+          WebString(info->uuid), FilePathToWebString(info->file->path),
+          WebString(info->file->name), WebString(info->mime_type),
+          info->file->last_modified.ToDoubleT(), info->size,
+          info->blob.PassHandle());
+    } else {
+      local_blob_info.emplace_back(WebString(info->uuid),
+                                   WebString(info->mime_type), info->size,
+                                   info->blob.PassHandle());
+    }
+  }
+
+  return WebIDBValue(WebData(value->bits.Latin1().data(), value->bits.length()),
+                     std::move(local_blob_info));
+}
+
+IndexedDBCallbacksImpl::IndexedDBCallbacksImpl(
+    std::unique_ptr<WebIDBCallbacks> callbacks,
+    int64_t transaction_id,
+    const base::WeakPtr<WebIDBCursorImpl>& cursor)
+    : callbacks_(std::move(callbacks)),
+      cursor_(cursor),
+      transaction_id_(transaction_id) {}
+
+IndexedDBCallbacksImpl::~IndexedDBCallbacksImpl() = default;
+
+void IndexedDBCallbacksImpl::Error(int32_t code, const WTF::String& message) {
+  callbacks_->OnError(WebIDBDatabaseError(code, WebString(message)));
+  callbacks_.reset();
+}
+
+void IndexedDBCallbacksImpl::SuccessNamesAndVersionsList(
+    Vector<mojom::blink::IDBNameAndVersionPtr> names_and_versions) {
+  WebVector<WebIDBNameAndVersion> web_names_and_versions;
+  web_names_and_versions.reserve(names_and_versions.size());
+  for (const mojom::blink::IDBNameAndVersionPtr& name_version :
+       names_and_versions)
+    web_names_and_versions.emplace_back(ConvertNameVersion(name_version));
+  callbacks_->OnSuccess(web_names_and_versions);
+  callbacks_.reset();
+}
+
+void IndexedDBCallbacksImpl::SuccessStringList(const Vector<String>& value) {
+  WebVector<WebString> web_value(value.size());
+  std::transform(value.begin(), value.end(), web_value.begin(),
+                 [](const WTF::String& s) { return WebString(s); });
+  callbacks_->OnSuccess(web_value);
+  callbacks_.reset();
+}
+
+void IndexedDBCallbacksImpl::Blocked(int64_t existing_version) {
+  callbacks_->OnBlocked(existing_version);
+  // Not resetting |callbacks_|.  In this instance we will have to forward at
+  // least one other call in the set OnUpgradeNeeded() / OnSuccess() /
+  // OnError().
+}
+
+void IndexedDBCallbacksImpl::UpgradeNeeded(
+    IDBDatabaseAssociatedPtrInfo database_info,
+    int64_t old_version,
+    WebIDBDataLoss data_loss,
+    const String& data_loss_message,
+    const WebIDBMetadata& web_metadata) {
+  WebIDBDatabase* database = new WebIDBDatabaseImpl(std::move(database_info));
+  callbacks_->OnUpgradeNeeded(old_version, database, web_metadata, data_loss,
+                              WebString(data_loss_message));
+  // Not resetting |callbacks_|.  In this instance we will have to forward at
+  // least one other call in the set OnSuccess() / OnError().
+}
+
+void IndexedDBCallbacksImpl::SuccessDatabase(
+    IDBDatabaseAssociatedPtrInfo database_info,
+    const WebIDBMetadata& web_metadata) {
+  WebIDBDatabase* database = nullptr;
+  if (database_info.is_valid())
+    database = new WebIDBDatabaseImpl(std::move(database_info));
+
+  callbacks_->OnSuccess(database, web_metadata);
+  callbacks_.reset();
+}
+
+void IndexedDBCallbacksImpl::SuccessCursor(
+    mojom::blink::IDBCursorAssociatedPtrInfo cursor_info,
+    WebIDBKey key,
+    WebIDBKey primary_key,
+    mojom::blink::IDBValuePtr value) {
+  WebIDBCursorImpl* cursor =
+      new WebIDBCursorImpl(std::move(cursor_info), transaction_id_);
+  callbacks_->OnSuccess(cursor, std::move(key), std::move(primary_key),
+                        ConvertValue(value));
+  callbacks_.reset();
+}
+
+void IndexedDBCallbacksImpl::SuccessValue(
+    mojom::blink::IDBReturnValuePtr value) {
+  callbacks_->OnSuccess(ConvertReturnValue(value));
+  callbacks_.reset();
+}
+
+void IndexedDBCallbacksImpl::SuccessCursorContinue(
+    WebIDBKey key,
+    WebIDBKey primary_key,
+    mojom::blink::IDBValuePtr value) {
+  callbacks_->OnSuccess(std::move(key), std::move(primary_key),
+                        ConvertValue(value));
+  callbacks_.reset();
+}
+
+void IndexedDBCallbacksImpl::SuccessCursorPrefetch(
+    Vector<WebIDBKey> keys,
+    Vector<WebIDBKey> primary_keys,
+    Vector<mojom::blink::IDBValuePtr> values) {
+  Vector<WebIDBValue> web_values;
+  web_values.ReserveInitialCapacity(values.size());
+  for (const mojom::blink::IDBValuePtr& value : values)
+    web_values.emplace_back(ConvertValue(value));
+
+  if (cursor_) {
+    cursor_->SetPrefetchData(std::move(keys), std::move(primary_keys),
+                             std::move(web_values));
+    cursor_->CachedContinue(callbacks_.get());
+  }
+  callbacks_.reset();
+}
+
+void IndexedDBCallbacksImpl::SuccessArray(
+    Vector<mojom::blink::IDBReturnValuePtr> values) {
+  WebVector<WebIDBValue> web_values;
+  web_values.reserve(values.size());
+  for (const mojom::blink::IDBReturnValuePtr& value : values)
+    web_values.emplace_back(ConvertReturnValue(value));
+  callbacks_->OnSuccess(std::move(web_values));
+  callbacks_.reset();
+}
+
+void IndexedDBCallbacksImpl::SuccessKey(WebIDBKey key) {
+  callbacks_->OnSuccess(std::move(key));
+  callbacks_.reset();
+}
+
+void IndexedDBCallbacksImpl::SuccessInteger(int64_t value) {
+  callbacks_->OnSuccess(value);
+  callbacks_.reset();
+}
+
+void IndexedDBCallbacksImpl::Success() {
+  callbacks_->OnSuccess();
+  callbacks_.reset();
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.h b/third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.h
new file mode 100644
index 0000000..dd49fd8
--- /dev/null
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.h
@@ -0,0 +1,84 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_INDEXED_DB_CALLBACKS_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_INDEXED_DB_CALLBACKS_IMPL_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
+#include "third_party/blink/public/common/indexeddb/indexeddb_metadata.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
+
+using blink::IndexedDBKey;
+
+namespace blink {
+
+class WebIDBCallbacks;
+class WebIDBCursorImpl;
+class WebIDBValue;
+
+// Implements the child-process end of the pipe used to deliver callbacks.
+// |callback_runner_| is used to post tasks back to the thread which owns the
+// blink::WebIDBCallbacks.
+class IndexedDBCallbacksImpl : public mojom::blink::IDBCallbacks {
+ public:
+  // |kNoTransaction| is used as the default transaction ID when instantiating
+  // an IndexedDBCallbacksImpl instance.  See web_idb_factory_impl.cc for those
+  // cases.
+  enum : int64_t { kNoTransaction = -1 };
+
+  static WebIDBValue ConvertValue(const mojom::blink::IDBValuePtr& value);
+
+  IndexedDBCallbacksImpl(std::unique_ptr<WebIDBCallbacks> callbacks,
+                         int64_t transaction_id,
+                         const base::WeakPtr<WebIDBCursorImpl>& cursor);
+  ~IndexedDBCallbacksImpl() override;
+
+  // mojom::blink::IDBCallbacks implementation:
+  void Error(int32_t code, const String& message) override;
+  void SuccessNamesAndVersionsList(
+      Vector<mojom::blink::IDBNameAndVersionPtr> names_and_versions) override;
+  void SuccessStringList(const Vector<String>& value) override;
+  void Blocked(int64_t existing_version) override;
+  void UpgradeNeeded(mojom::blink::IDBDatabaseAssociatedPtrInfo database_info,
+                     int64_t old_version,
+                     WebIDBDataLoss data_loss,
+                     const String& data_loss_message,
+                     const WebIDBMetadata& metadata) override;
+  void SuccessDatabase(mojom::blink::IDBDatabaseAssociatedPtrInfo database_info,
+                       const WebIDBMetadata& metadata) override;
+  void SuccessCursor(mojom::blink::IDBCursorAssociatedPtrInfo cursor,
+                     WebIDBKey key,
+                     WebIDBKey primary_key,
+                     mojom::blink::IDBValuePtr value) override;
+  void SuccessValue(mojom::blink::IDBReturnValuePtr value) override;
+  void SuccessCursorContinue(WebIDBKey key,
+                             WebIDBKey primary_key,
+                             mojom::blink::IDBValuePtr value) override;
+  void SuccessCursorPrefetch(Vector<WebIDBKey> keys,
+                             Vector<WebIDBKey> primary_keys,
+                             Vector<mojom::blink::IDBValuePtr> values) override;
+  void SuccessArray(Vector<mojom::blink::IDBReturnValuePtr> values) override;
+  void SuccessKey(WebIDBKey key) override;
+  void SuccessInteger(int64_t value) override;
+  void Success() override;
+
+ private:
+  scoped_refptr<base::SingleThreadTaskRunner> callback_runner_;
+  std::unique_ptr<WebIDBCallbacks> callbacks_;
+  base::WeakPtr<WebIDBCursorImpl> cursor_;
+  int64_t transaction_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(IndexedDBCallbacksImpl);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_INDEXED_DB_CALLBACKS_IMPL_H_
diff --git a/content/renderer/indexed_db/indexed_db_database_callbacks_impl.cc b/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.cc
similarity index 62%
rename from content/renderer/indexed_db/indexed_db_database_callbacks_impl.cc
rename to third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.cc
index a63c065..1149fab7b 100644
--- a/content/renderer/indexed_db/indexed_db_database_callbacks_impl.cc
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.cc
@@ -2,24 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/indexed_db/indexed_db_database_callbacks_impl.h"
+#include "third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.h"
 
 #include <unordered_map>
 #include <utility>
 
-#include "base/threading/thread_task_runner_handle.h"
-#include "content/renderer/indexed_db/indexed_db_callbacks_impl.h"
-#include "content/renderer/indexed_db/indexed_db_dispatcher.h"
-#include "content/renderer/indexed_db/indexed_db_key_builders.h"
+#include "third_party/blink/public/platform/modules/indexeddb/indexed_db_key_builder.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_callbacks.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_error.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_observation.h"
+#include "third_party/blink/renderer/modules/indexeddb/idb_key_range.h"
+#include "third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.h"
 
 using blink::WebVector;
 using blink::WebIDBDatabaseCallbacks;
 using blink::WebIDBObservation;
 
-namespace content {
+namespace blink {
 
 IndexedDBDatabaseCallbacksImpl::IndexedDBDatabaseCallbacksImpl(
     std::unique_ptr<WebIDBDatabaseCallbacks> callbacks)
@@ -38,10 +37,9 @@
 
 void IndexedDBDatabaseCallbacksImpl::Abort(int64_t transaction_id,
                                            int32_t code,
-                                           const base::string16& message) {
-  callbacks_->OnAbort(
-      transaction_id,
-      blink::WebIDBDatabaseError(code, blink::WebString::FromUTF16(message)));
+                                           const String& message) {
+  callbacks_->OnAbort(transaction_id,
+                      blink::WebIDBDatabaseError(code, message));
 }
 
 void IndexedDBDatabaseCallbacksImpl::Complete(int64_t transaction_id) {
@@ -49,33 +47,34 @@
 }
 
 void IndexedDBDatabaseCallbacksImpl::Changes(
-    blink::mojom::IDBObserverChangesPtr changes) {
+    mojom::blink::IDBObserverChangesPtr changes) {
   WebVector<WebIDBObservation> web_observations;
   web_observations.reserve(changes->observations.size());
   for (const auto& observation : changes->observations) {
     web_observations.emplace_back(
-        observation->object_store_id, observation->type,
-        WebIDBKeyRangeBuilder::Build(observation->key_range),
+        observation->object_store_id, observation->type, observation->key_range,
         IndexedDBCallbacksImpl::ConvertValue(observation->value));
   }
 
-  WebIDBDatabaseCallbacks::ObservationIndexMap observation_index_map(
-      changes->observation_index_map.begin(),
-      changes->observation_index_map.end());
+  std::unordered_map<int32_t, WebVector<int32_t>> observation_index_map;
+  for (const auto& observation_pair : changes->observation_index_map) {
+    observation_index_map[observation_pair.key] =
+        WebVector<int32_t>(observation_pair.value);
+  }
 
-  std::unordered_map<int32_t, std::pair<int64_t, std::vector<int64_t>>>
+  std::unordered_map<int32_t, std::pair<int64_t, WebVector<int64_t>>>
       observer_transactions;
   for (const auto& transaction_pair : changes->transaction_map) {
     // Moving an int64_t is rather silly. Sadly, std::make_pair's overloads
     // accept either two rvalue arguments, or none.
-    observer_transactions[transaction_pair.first] =
-        std::make_pair<int64_t, std::vector<int64_t>>(
-            std::move(transaction_pair.second->id),
-            std::move(transaction_pair.second->scope));
+    observer_transactions[transaction_pair.key] =
+        std::make_pair<int64_t, Vector<int64_t>>(
+            std::move(transaction_pair.value->id),
+            std::move(transaction_pair.value->scope));
   }
 
   callbacks_->OnChanges(observation_index_map, std::move(web_observations),
                         observer_transactions);
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.h b/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.h
new file mode 100644
index 0000000..702ea69
--- /dev/null
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.h
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_INDEXED_DB_DATABASE_CALLBACKS_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_INDEXED_DB_DATABASE_CALLBACKS_IMPL_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/single_thread_task_runner.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
+
+namespace blink {
+class WebIDBDatabaseCallbacks;
+
+class IndexedDBDatabaseCallbacksImpl
+    : public mojom::blink::IDBDatabaseCallbacks {
+ public:
+  explicit IndexedDBDatabaseCallbacksImpl(
+      std::unique_ptr<WebIDBDatabaseCallbacks> callbacks);
+  ~IndexedDBDatabaseCallbacksImpl() override;
+
+  // mojom::blink::IDBDatabaseCallbacks implementation
+  void ForcedClose() override;
+  void VersionChange(int64_t old_version, int64_t new_version) override;
+  void Abort(int64_t transaction_id,
+             int32_t code,
+             const WTF::String& message) override;
+  void Complete(int64_t transaction_id) override;
+  void Changes(mojom::blink::IDBObserverChangesPtr changes) override;
+
+ private:
+  std::unique_ptr<WebIDBDatabaseCallbacks> callbacks_;
+
+  DISALLOW_COPY_AND_ASSIGN(IndexedDBDatabaseCallbacksImpl);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_INDEXED_DB_DATABASE_CALLBACKS_IMPL_H_
diff --git a/third_party/blink/renderer/modules/indexeddb/indexed_db_dispatcher.cc b/third_party/blink/renderer/modules/indexeddb/indexed_db_dispatcher.cc
new file mode 100644
index 0000000..84198c4
--- /dev/null
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db_dispatcher.cc
@@ -0,0 +1,49 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/indexeddb/indexed_db_dispatcher.h"
+
+#include <utility>
+
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_callbacks.h"
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_observation.h"
+#include "third_party/blink/renderer/modules/indexeddb/idb_key_range.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.h"
+
+namespace blink {
+// static
+IndexedDBDispatcher* IndexedDBDispatcher::GetInstanceForCurrentThread() {
+  DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<IndexedDBDispatcher>,
+                                  thread_specific_instance, ());
+  return thread_specific_instance;
+}
+
+IndexedDBDispatcher::IndexedDBDispatcher() = default;
+
+// static
+void IndexedDBDispatcher::RegisterCursor(WebIDBCursorImpl* cursor) {
+  IndexedDBDispatcher* this_ptr = GetInstanceForCurrentThread();
+  DCHECK(!this_ptr->cursors_.Contains(cursor));
+  this_ptr->cursors_.insert(cursor);
+}
+
+// static
+void IndexedDBDispatcher::UnregisterCursor(WebIDBCursorImpl* cursor) {
+  IndexedDBDispatcher* this_ptr = GetInstanceForCurrentThread();
+  DCHECK(this_ptr->cursors_.Contains(cursor));
+  this_ptr->cursors_.erase(cursor);
+}
+
+// static
+void IndexedDBDispatcher::ResetCursorPrefetchCaches(
+    int64_t transaction_id,
+    WebIDBCursorImpl* except_cursor) {
+  IndexedDBDispatcher* this_ptr = GetInstanceForCurrentThread();
+  for (WebIDBCursorImpl* cursor : this_ptr->cursors_) {
+    if (cursor != except_cursor && cursor->transaction_id() == transaction_id)
+      cursor->ResetPrefetchCache();
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/indexeddb/indexed_db_dispatcher.h b/third_party/blink/renderer/modules/indexeddb/indexed_db_dispatcher.h
new file mode 100644
index 0000000..906d706
--- /dev/null
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db_dispatcher.h
@@ -0,0 +1,55 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_INDEXED_DB_DISPATCHER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_INDEXED_DB_DISPATCHER_H_
+
+#include <stdint.h>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "third_party/blink/public/common/indexeddb/web_idb_types.h"
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_callbacks.h"
+#include "third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.h"
+#include "third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
+
+namespace blink {
+class WebIDBCursorImpl;
+
+// Handle the indexed db related communication for this context thread - the
+// main thread and each worker thread have their own copies.
+class MODULES_EXPORT IndexedDBDispatcher {
+ public:
+  static void RegisterCursor(WebIDBCursorImpl* cursor);
+  static void UnregisterCursor(WebIDBCursorImpl* cursor);
+
+  // Reset cursor prefetch caches for all cursors except |except_cursor|.
+  // In most callers, |except_cursor| is passed as nullptr, causing all cursors
+  // to have their prefetch cache to be reset.  In 2 WebIDBCursorImpl callers,
+  // specifically from |Advance| and |CursorContinue|, these want to reset all
+  // cursor prefetch caches except the cursor the calls are running from.  They
+  // get that behavior by passing |this| to |ResetCursorPrefetchCaches| which
+  // skips calling |ResetPrefetchCache| on them.
+  static void ResetCursorPrefetchCaches(int64_t transaction_id,
+                                        WebIDBCursorImpl* except_cursor);
+
+ private:
+  friend class WTF::ThreadSpecific<IndexedDBDispatcher>;
+
+  static IndexedDBDispatcher* GetInstanceForCurrentThread();
+
+  IndexedDBDispatcher();
+
+  FRIEND_TEST_ALL_PREFIXES(IndexedDBDispatcherTest, CursorReset);
+  FRIEND_TEST_ALL_PREFIXES(IndexedDBDispatcherTest, CursorTransactionId);
+
+  WTF::HashSet<WebIDBCursorImpl*> cursors_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_INDEXED_DB_DISPATCHER_H_
diff --git a/third_party/blink/renderer/modules/indexeddb/indexed_db_mojom_traits.cc b/third_party/blink/renderer/modules/indexeddb/indexed_db_mojom_traits.cc
new file mode 100644
index 0000000..c0dcd1a
--- /dev/null
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db_mojom_traits.cc
@@ -0,0 +1,287 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/indexeddb/indexed_db_mojom_traits.h"
+
+#include "base/stl_util.h"
+#include "mojo/public/cpp/bindings/array_traits_web_vector.h"
+#include "mojo/public/cpp/bindings/array_traits_wtf_vector.h"
+#include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
+#include "third_party/blink/public/common/indexeddb/indexeddb_key_range.h"
+#include "third_party/blink/public/common/indexeddb/indexeddb_metadata.h"
+#include "third_party/blink/public/common/indexeddb/indexeddb_mojom_traits.h"
+#include "third_party/blink/renderer/modules/indexeddb/idb_key_range.h"
+#include "third_party/blink/renderer/platform/mojo/string16_mojom_traits.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+using blink::mojom::IDBCursorDirection;
+using blink::mojom::IDBDataLoss;
+using blink::mojom::IDBOperationType;
+
+namespace mojo {
+
+// static
+bool StructTraits<
+    blink::mojom::IDBDatabaseMetadataDataView,
+    blink::WebIDBMetadata>::Read(blink::mojom::IDBDatabaseMetadataDataView data,
+                                 blink::WebIDBMetadata* out) {
+  out->id = data.id();
+  String name;
+  if (!data.ReadName(&name))
+    return false;
+  out->name = name;
+  out->version = data.version();
+  out->max_object_store_id = data.max_object_store_id();
+  if (!data.ReadObjectStores(&out->object_stores))
+    return false;
+  return true;
+}
+
+// static
+bool StructTraits<blink::mojom::IDBIndexKeysDataView, blink::WebIDBIndexKeys>::
+    Read(blink::mojom::IDBIndexKeysDataView data, blink::WebIDBIndexKeys* out) {
+  out->first = data.index_id();
+  if (!data.ReadIndexKeys(&out->second))
+    return false;
+  return true;
+}
+
+// static
+bool StructTraits<blink::mojom::IDBIndexMetadataDataView,
+                  blink::WebIDBMetadata::Index>::
+    Read(blink::mojom::IDBIndexMetadataDataView data,
+         blink::WebIDBMetadata::Index* out) {
+  out->id = data.id();
+  String name;
+  if (!data.ReadName(&name))
+    return false;
+  out->name = name;
+  if (!data.ReadKeyPath(&out->key_path))
+    return false;
+  out->unique = data.unique();
+  out->multi_entry = data.multi_entry();
+  return true;
+}
+
+// static
+blink::mojom::IDBKeyDataDataView::Tag
+UnionTraits<blink::mojom::IDBKeyDataDataView, blink::WebIDBKey>::GetTag(
+    const blink::WebIDBKey& key) {
+  switch (key.View().KeyType()) {
+    case blink::kWebIDBKeyTypeInvalid:
+      return blink::mojom::IDBKeyDataDataView::Tag::OTHER;
+    case blink::kWebIDBKeyTypeArray:
+      return blink::mojom::IDBKeyDataDataView::Tag::KEY_ARRAY;
+    case blink::kWebIDBKeyTypeBinary:
+      return blink::mojom::IDBKeyDataDataView::Tag::BINARY;
+    case blink::kWebIDBKeyTypeString:
+      return blink::mojom::IDBKeyDataDataView::Tag::STRING;
+    case blink::kWebIDBKeyTypeDate:
+      return blink::mojom::IDBKeyDataDataView::Tag::DATE;
+    case blink::kWebIDBKeyTypeNumber:
+      return blink::mojom::IDBKeyDataDataView::Tag::NUMBER;
+    case blink::kWebIDBKeyTypeNull:
+      return blink::mojom::IDBKeyDataDataView::Tag::OTHER;
+    case blink::kWebIDBKeyTypeMin:
+      break;
+  }
+  NOTREACHED();
+  return blink::mojom::IDBKeyDataDataView::Tag::OTHER;
+}
+
+// static
+bool UnionTraits<blink::mojom::IDBKeyDataDataView, blink::WebIDBKey>::Read(
+    blink::mojom::IDBKeyDataDataView data,
+    blink::WebIDBKey* out) {
+  switch (data.tag()) {
+    case blink::mojom::IDBKeyDataDataView::Tag::KEY_ARRAY: {
+      Vector<blink::WebIDBKey> array;
+      if (!data.ReadKeyArray(&array))
+        return false;
+      blink::WebVector<blink::WebIDBKey> webvector_array;
+      for (const auto& item : array) {
+        webvector_array.emplace_back(
+            blink::WebIDBKeyBuilder::Build(item.View()));
+      }
+      *out = blink::WebIDBKey::CreateArray(std::move(webvector_array));
+      return true;
+    }
+    case blink::mojom::IDBKeyDataDataView::Tag::BINARY: {
+      Vector<uint8_t> binary_vector;
+      if (!data.ReadBinary(&binary_vector))
+        return false;
+      std::string binary_string = std::string(
+          binary_vector.data(), binary_vector.data() + binary_vector.size());
+      *out = blink::WebIDBKey::CreateBinary(
+          blink::WebData(binary_string.c_str(), binary_string.length()));
+      return true;
+    }
+    case blink::mojom::IDBKeyDataDataView::Tag::STRING: {
+      String string;
+      if (!data.ReadString(&string))
+        return false;
+      *out = blink::WebIDBKey::CreateString(blink::WebString(string));
+      return true;
+    }
+    case blink::mojom::IDBKeyDataDataView::Tag::DATE:
+      *out = blink::WebIDBKey::CreateDate(data.date());
+      return true;
+    case blink::mojom::IDBKeyDataDataView::Tag::NUMBER:
+      *out = blink::WebIDBKey::CreateNumber(data.number());
+      return true;
+    case blink::mojom::IDBKeyDataDataView::Tag::OTHER:
+      switch (data.other()) {
+        case blink::mojom::IDBDatalessKeyType::Invalid:
+          *out = blink::WebIDBKey::CreateInvalid();
+          return true;
+        case blink::mojom::IDBDatalessKeyType::Null:
+          *out = blink::WebIDBKey::CreateNull();
+          return true;
+      }
+  }
+
+  return false;
+}
+
+// static
+const blink::WebVector<blink::WebIDBKey>
+UnionTraits<blink::mojom::IDBKeyDataDataView, blink::WebIDBKey>::key_array(
+    const blink::WebIDBKey& key) {
+  const auto& array_view = key.View().ArrayView();
+  const size_t array_size = array_view.size();
+  Vector<blink::WebIDBKey> result;
+  result.ReserveInitialCapacity(array_view.size());
+  for (size_t i = 0; i < array_size; ++i)
+    result.emplace_back(blink::WebIDBKeyBuilder::Build(array_view[i]));
+  return result;
+}
+
+// static
+const Vector<uint8_t>
+UnionTraits<blink::mojom::IDBKeyDataDataView, blink::WebIDBKey>::binary(
+    const blink::WebIDBKey& key) {
+  const auto& data_view = key.View().Binary().Copy();
+  const size_t data_size = data_view.size();
+  Vector<uint8_t> result;
+  result.ReserveInitialCapacity(data_size);
+  for (const auto& item : data_view)
+    result.push_back(item);
+  return result;
+}
+
+// static
+const blink::WebIDBKey&
+StructTraits<blink::mojom::IDBKeyDataView, blink::WebIDBKey>::data(
+    const blink::WebIDBKey& key) {
+  return key;
+}
+
+// static
+bool StructTraits<blink::mojom::IDBKeyDataView, blink::WebIDBKey>::Read(
+    blink::mojom::IDBKeyDataView data,
+    blink::WebIDBKey* out) {
+  return data.ReadData(out);
+}
+
+// static
+blink::mojom::blink::IDBKeyPathDataPtr
+StructTraits<blink::mojom::IDBKeyPathDataView, blink::WebIDBKeyPath>::data(
+    const blink::WebIDBKeyPath& key_path) {
+  if (key_path.KeyPathType() == blink::kWebIDBKeyPathTypeNull)
+    return nullptr;
+
+  auto data = blink::mojom::blink::IDBKeyPathData::New();
+  switch (key_path.KeyPathType()) {
+    case blink::kWebIDBKeyPathTypeString: {
+      String key_path_string = key_path.String();
+      if (key_path_string.IsNull())
+        key_path_string = "";
+      data->set_string(key_path_string);
+      return data;
+    }
+    case blink::kWebIDBKeyPathTypeArray: {
+      const auto& array = key_path.Array();
+      const size_t array_size = array.size();
+      Vector<String> result;
+      result.ReserveInitialCapacity(array_size);
+      for (const auto& item : array)
+        result.push_back(item);
+      data->set_string_array(result);
+      return data;
+    }
+
+    case blink::kWebIDBKeyPathTypeNull:
+      break;  // Not used, NOTREACHED.
+  }
+  NOTREACHED();
+  return data;
+}
+
+// static
+bool StructTraits<blink::mojom::IDBKeyPathDataView, blink::WebIDBKeyPath>::Read(
+    blink::mojom::IDBKeyPathDataView data,
+    blink::WebIDBKeyPath* out) {
+  blink::mojom::IDBKeyPathDataDataView data_view;
+  data.GetDataDataView(&data_view);
+
+  if (data_view.is_null()) {
+    *out = blink::WebIDBKeyPath();
+    return true;
+  }
+
+  switch (data_view.tag()) {
+    case blink::mojom::IDBKeyPathDataDataView::Tag::STRING: {
+      String string;
+      if (!data_view.ReadString(&string))
+        return false;
+      *out = blink::WebIDBKeyPath(blink::WebString(string));
+      return true;
+    }
+    case blink::mojom::IDBKeyPathDataDataView::Tag::STRING_ARRAY: {
+      Vector<String> array;
+      if (!data_view.ReadStringArray(&array))
+        return false;
+      *out = blink::WebIDBKeyPath(array);
+      return true;
+    }
+  }
+
+  return false;
+}
+
+// static
+bool StructTraits<blink::mojom::IDBKeyRangeDataView, blink::WebIDBKeyRange>::
+    Read(blink::mojom::IDBKeyRangeDataView data, blink::WebIDBKeyRange* out) {
+  // TODO(cmp): Use WebIDBKey and WebIDBKeyRange directly.
+  blink::IndexedDBKey lower;
+  blink::IndexedDBKey upper;
+  if (!data.ReadLower(&lower) || !data.ReadUpper(&upper))
+    return false;
+
+  blink::IndexedDBKeyRange temp(lower, upper, data.lower_open(),
+                                data.upper_open());
+  *out = blink::WebIDBKeyRangeBuilder::Build(temp);
+  return true;
+}
+
+// static
+bool StructTraits<blink::mojom::IDBObjectStoreMetadataDataView,
+                  blink::WebIDBMetadata::ObjectStore>::
+    Read(blink::mojom::IDBObjectStoreMetadataDataView data,
+         blink::WebIDBMetadata::ObjectStore* out) {
+  out->id = data.id();
+  String name;
+  if (!data.ReadName(&name))
+    return false;
+  out->name = name;
+  if (!data.ReadKeyPath(&out->key_path))
+    return false;
+  out->auto_increment = data.auto_increment();
+  out->max_index_id = data.max_index_id();
+  if (!data.ReadIndexes(&out->indexes))
+    return false;
+  return true;
+}
+
+}  // namespace mojo
diff --git a/third_party/blink/renderer/modules/indexeddb/indexed_db_mojom_traits.h b/third_party/blink/renderer/modules/indexeddb/indexed_db_mojom_traits.h
new file mode 100644
index 0000000..3cacae4
--- /dev/null
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db_mojom_traits.h
@@ -0,0 +1,187 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_INDEXED_DB_MOJOM_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_INDEXED_DB_MOJOM_TRAITS_H_
+
+#include <stdint.h>
+
+#include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
+#include "third_party/blink/public/common/indexeddb/indexeddb_key_range.h"
+#include "third_party/blink/public/common/indexeddb/indexeddb_metadata.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
+#include "third_party/blink/public/platform/modules/indexeddb/indexed_db_key_builder.h"
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_key.h"
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_key_range.h"
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_metadata.h"
+#include "third_party/blink/public/platform/web_vector.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace mojo {
+
+template <>
+struct MODULES_EXPORT StructTraits<blink::mojom::IDBDatabaseMetadataDataView,
+                                   blink::WebIDBMetadata> {
+  static int64_t id(const blink::WebIDBMetadata& metadata) {
+    return metadata.id;
+  }
+  static WTF::String name(const blink::WebIDBMetadata& metadata) {
+    if (metadata.name.IsNull())
+      return g_empty_string;
+    return metadata.name;
+  }
+  static int64_t version(const blink::WebIDBMetadata& metadata) {
+    return metadata.version;
+  }
+  static int64_t max_object_store_id(const blink::WebIDBMetadata& metadata) {
+    return metadata.max_object_store_id;
+  }
+  static const blink::WebVector<blink::WebIDBMetadata::ObjectStore>&
+  object_stores(const blink::WebIDBMetadata& metadata) {
+    return metadata.object_stores;
+  }
+  static bool Read(blink::mojom::IDBDatabaseMetadataDataView data,
+                   blink::WebIDBMetadata* out);
+};
+
+template <>
+struct MODULES_EXPORT
+    StructTraits<blink::mojom::IDBIndexKeysDataView, blink::WebIDBIndexKeys> {
+  static int64_t index_id(const blink::WebIDBIndexKeys& index_keys) {
+    return index_keys.first;
+  }
+  static const blink::WebVector<blink::WebIDBKey>& index_keys(
+      const blink::WebIDBIndexKeys& index_keys) {
+    return index_keys.second;
+  }
+  static bool Read(blink::mojom::IDBIndexKeysDataView data,
+                   blink::WebIDBIndexKeys* out);
+};
+
+template <>
+struct MODULES_EXPORT StructTraits<blink::mojom::IDBIndexMetadataDataView,
+                                   blink::WebIDBMetadata::Index> {
+  static int64_t id(const blink::WebIDBMetadata::Index& metadata) {
+    return metadata.id;
+  }
+  static WTF::String name(const blink::WebIDBMetadata::Index& metadata) {
+    if (metadata.name.IsNull())
+      return g_empty_string;
+    return metadata.name;
+  }
+  static const blink::WebIDBKeyPath& key_path(
+      const blink::WebIDBMetadata::Index& metadata) {
+    return metadata.key_path;
+  }
+  static bool unique(const blink::WebIDBMetadata::Index& metadata) {
+    return metadata.unique;
+  }
+  static bool multi_entry(const blink::WebIDBMetadata::Index& metadata) {
+    return metadata.multi_entry;
+  }
+  static bool Read(blink::mojom::IDBIndexMetadataDataView data,
+                   blink::WebIDBMetadata::Index* out);
+};
+
+template <>
+struct MODULES_EXPORT
+    UnionTraits<blink::mojom::IDBKeyDataDataView, blink::WebIDBKey> {
+  static blink::mojom::IDBKeyDataDataView::Tag GetTag(
+      const blink::WebIDBKey& key);
+  static bool Read(blink::mojom::IDBKeyDataDataView data,
+                   blink::WebIDBKey* out);
+  static const blink::WebVector<blink::WebIDBKey> key_array(
+      const blink::WebIDBKey& key);
+  static const Vector<uint8_t> binary(const blink::WebIDBKey& key);
+  static const WTF::String string(const blink::WebIDBKey& key) {
+    String key_string = key.View().String();
+    if (key_string.IsNull())
+      key_string = g_empty_string;
+    return key_string;
+  }
+  static double date(const blink::WebIDBKey& key) { return key.View().Date(); }
+  static double number(const blink::WebIDBKey& key) {
+    return key.View().Number();
+  }
+  static blink::mojom::IDBDatalessKeyType other(const blink::WebIDBKey& key) {
+    switch (key.View().KeyType()) {
+      case blink::kWebIDBKeyTypeInvalid:
+        return blink::mojom::IDBDatalessKeyType::Invalid;
+      case blink::kWebIDBKeyTypeNull:
+        return blink::mojom::IDBDatalessKeyType::Null;
+      default:
+        NOTREACHED();
+        return blink::mojom::IDBDatalessKeyType::Invalid;
+    }
+  }
+};
+
+template <>
+struct MODULES_EXPORT
+    StructTraits<blink::mojom::IDBKeyDataView, blink::WebIDBKey> {
+  static const blink::WebIDBKey& data(const blink::WebIDBKey& key);
+  static bool Read(blink::mojom::IDBKeyDataView data, blink::WebIDBKey* out);
+};
+
+template <>
+struct MODULES_EXPORT
+    StructTraits<blink::mojom::IDBKeyPathDataView, blink::WebIDBKeyPath> {
+  static blink::mojom::blink::IDBKeyPathDataPtr data(
+      const blink::WebIDBKeyPath& key_path);
+  static bool Read(blink::mojom::IDBKeyPathDataView data,
+                   blink::WebIDBKeyPath* out);
+};
+
+template <>
+struct MODULES_EXPORT
+    StructTraits<blink::mojom::IDBKeyRangeDataView, blink::WebIDBKeyRange> {
+  static blink::WebIDBKey lower(const blink::WebIDBKeyRange& key_range) {
+    return blink::WebIDBKeyBuilder::Build(key_range.Lower());
+  }
+  static blink::WebIDBKey upper(const blink::WebIDBKeyRange& key_range) {
+    return blink::WebIDBKeyBuilder::Build(key_range.Upper());
+  }
+  static bool lower_open(const blink::WebIDBKeyRange& key_range) {
+    return key_range.LowerOpen();
+  }
+  static bool upper_open(const blink::WebIDBKeyRange& key_range) {
+    return key_range.UpperOpen();
+  }
+  static bool Read(blink::mojom::IDBKeyRangeDataView data,
+                   blink::WebIDBKeyRange* out);
+};
+
+template <>
+struct MODULES_EXPORT StructTraits<blink::mojom::IDBObjectStoreMetadataDataView,
+                                   blink::WebIDBMetadata::ObjectStore> {
+  static int64_t id(const blink::WebIDBMetadata::ObjectStore& metadata) {
+    return metadata.id;
+  }
+  static WTF::String name(const blink::WebIDBMetadata::ObjectStore& metadata) {
+    return metadata.name;
+  }
+  static const blink::WebIDBKeyPath& key_path(
+      const blink::WebIDBMetadata::ObjectStore& metadata) {
+    return metadata.key_path;
+  }
+  static bool auto_increment(
+      const blink::WebIDBMetadata::ObjectStore& metadata) {
+    return metadata.auto_increment;
+  }
+  static int64_t max_index_id(
+      const blink::WebIDBMetadata::ObjectStore& metadata) {
+    return metadata.max_index_id;
+  }
+  static const blink::WebVector<blink::WebIDBMetadata::Index>& indexes(
+      const blink::WebIDBMetadata::ObjectStore& metadata) {
+    return metadata.indexes;
+  }
+  static bool Read(blink::mojom::IDBObjectStoreMetadataDataView data,
+                   blink::WebIDBMetadata::ObjectStore* out);
+};
+
+}  // namespace mojo
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_INDEXED_DB_MOJOM_TRAITS_H_
diff --git a/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc b/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc
index 00ff718..44fbbba 100644
--- a/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc
+++ b/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc
@@ -34,7 +34,6 @@
 #include <utility>
 
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_cursor.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -59,6 +58,7 @@
 #include "third_party/blink/renderer/modules/indexeddb/idb_open_db_request.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_request.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_transaction.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
diff --git a/content/renderer/indexed_db/mock_webidbcallbacks.cc b/third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.cc
similarity index 89%
rename from content/renderer/indexed_db/mock_webidbcallbacks.cc
rename to third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.cc
index ec05f485..1f783952 100644
--- a/content/renderer/indexed_db/mock_webidbcallbacks.cc
+++ b/third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/indexed_db/mock_webidbcallbacks.h"
+#include "third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.h"
 
-namespace content {
+namespace blink {
 
 MockWebIDBCallbacks::MockWebIDBCallbacks() {}
 
@@ -36,4 +36,4 @@
   DoOnSuccess(values);
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/content/renderer/indexed_db/mock_webidbcallbacks.h b/third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.h
similarity index 90%
rename from content/renderer/indexed_db/mock_webidbcallbacks.h
rename to third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.h
index ec337180..5c9b71d 100644
--- a/content/renderer/indexed_db/mock_webidbcallbacks.h
+++ b/third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.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_RENDERER_INDEXED_DB_MOCK_WEBIDBCALLBACKS_H_
-#define CONTENT_RENDERER_INDEXED_DB_MOCK_WEBIDBCALLBACKS_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_MOCK_WEB_IDB_CALLBACKS_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_MOCK_WEB_IDB_CALLBACKS_H_
 
 #include "base/macros.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -15,7 +15,7 @@
 #include "third_party/blink/public/platform/web_blob_info.h"
 #include "third_party/blink/public/web/web_heap.h"
 
-namespace content {
+namespace blink {
 
 class MockWebIDBCallbacks : public blink::WebIDBCallbacks {
  public:
@@ -71,6 +71,6 @@
   DISALLOW_COPY_AND_ASSIGN(MockWebIDBCallbacks);
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_RENDERER_INDEXED_DB_MOCK_WEBIDBCALLBACKS_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_MOCK_WEB_IDB_CALLBACKS_H_
diff --git a/third_party/blink/renderer/modules/indexeddb/mock_web_idb_database.h b/third_party/blink/renderer/modules/indexeddb/mock_web_idb_database.h
index a7325498..c38507f 100644
--- a/third_party/blink/renderer/modules/indexeddb/mock_web_idb_database.h
+++ b/third_party/blink/renderer/modules/indexeddb/mock_web_idb_database.h
@@ -7,10 +7,10 @@
 
 #include <gmock/gmock.h>
 #include <memory>
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_key_range.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_key.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_key_range.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_database.h"
 
 namespace blink {
 
@@ -23,7 +23,7 @@
   MOCK_METHOD5(CreateObjectStore,
                void(long long transaction_id,
                     long long object_store_id,
-                    const WebString& name,
+                    const String& name,
                     const WebIDBKeyPath&,
                     bool auto_increment));
   MOCK_METHOD2(DeleteObjectStore,
@@ -31,10 +31,10 @@
   MOCK_METHOD3(RenameObjectStore,
                void(long long transaction_id,
                     long long object_store_id,
-                    const WebString& new_name));
+                    const String& new_name));
   MOCK_METHOD3(CreateTransaction,
                void(long long id,
-                    const WebVector<long long>& scope,
+                    const Vector<int64_t>& scope,
                     WebIDBTransactionMode));
   MOCK_METHOD0(Close, void());
   MOCK_METHOD0(VersionChangeIgnored, void());
@@ -44,7 +44,7 @@
                void(long long transaction_id,
                     long long object_store_id,
                     long long index_id,
-                    const WebString& name,
+                    const String& name,
                     const WebIDBKeyPath&,
                     bool unique,
                     bool multi_entry));
@@ -56,7 +56,7 @@
                void(long long transaction_id,
                     long long object_store_id,
                     long long index_id,
-                    const WebString& new_name));
+                    const String& new_name));
   MOCK_METHOD6(
       AddObserver,
       void(long long transaction_id,
@@ -67,7 +67,7 @@
            const std::bitset<kWebIDBOperationTypeCount>& operation_types));
   MOCK_CONST_METHOD1(ContainsObserverId, bool(int32_t id));
   MOCK_METHOD1(RemoveObservers,
-               void(const WebVector<int32_t>& observer_ids_to_remove));
+               void(const Vector<int32_t>& observer_ids_to_remove));
   MOCK_METHOD6(Get,
                void(long long transaction_id,
                     long long object_store_id,
@@ -88,21 +88,21 @@
                void(long long transaction_id,
                     long long object_store_id,
                     const WebData& value,
-                    const WebVector<WebBlobInfo>&,
+                    const Vector<WebBlobInfo>&,
                     WebIDBKeyView primary_key,
                     WebIDBPutMode,
                     WebIDBCallbacks*,
-                    const WebVector<WebIDBIndexKeys>&));
+                    const Vector<WebIDBIndexKeys>&));
 
   MOCK_METHOD4(SetIndexKeys,
                void(long long transaction_id,
                     long long object_store_id,
                     WebIDBKeyView primary_key,
-                    const WebVector<WebIDBIndexKeys>&));
+                    const Vector<WebIDBIndexKeys>&));
   MOCK_METHOD3(SetIndexesReady,
                void(long long transaction_id,
                     long long object_store_id,
-                    const WebVector<long long>& index_ids));
+                    const Vector<int64_t>& index_ids));
   MOCK_METHOD8(OpenCursor,
                void(long long transaction_id,
                     long long object_store_id,
diff --git a/third_party/blink/renderer/modules/indexeddb/mock_web_idb_factory.h b/third_party/blink/renderer/modules/indexeddb/mock_web_idb_factory.h
index 83c890d..47ffe2f 100644
--- a/third_party/blink/renderer/modules/indexeddb/mock_web_idb_factory.h
+++ b/third_party/blink/renderer/modules/indexeddb/mock_web_idb_factory.h
@@ -9,9 +9,9 @@
 #include <memory>
 
 #include "base/single_thread_task_runner.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_factory.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_factory.h"
 
 namespace base {
 class SingleThreadTaskRunner;
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc
index 34f3c12..6fb6626 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc
@@ -31,8 +31,6 @@
 #include <memory>
 
 #include "base/memory/ptr_util.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_cursor.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_error.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_key.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_name_and_version.h"
@@ -43,6 +41,8 @@
 #include "third_party/blink/renderer/modules/indexeddb/idb_metadata.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_request.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_value.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_database.h"
 #include "third_party/blink/renderer/platform/shared_buffer.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 
diff --git a/third_party/blink/public/platform/modules/indexeddb/web_idb_cursor.h b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor.h
similarity index 88%
rename from third_party/blink/public/platform/modules/indexeddb/web_idb_cursor.h
rename to third_party/blink/renderer/modules/indexeddb/web_idb_cursor.h
index 248df14..7e03aa02 100644
--- a/third_party/blink/public/platform/modules/indexeddb/web_idb_cursor.h
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor.h
@@ -23,18 +23,18 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INDEXEDDB_WEB_IDB_CURSOR_H_
-#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INDEXEDDB_WEB_IDB_CURSOR_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_CURSOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_CURSOR_H_
 
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_callbacks.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_key.h"
-#include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
 
 namespace blink {
 
-class WebIDBCursor {
+class MODULES_EXPORT WebIDBCursor {
  public:
   virtual ~WebIDBCursor() = default;
 
@@ -65,4 +65,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INDEXEDDB_WEB_IDB_CURSOR_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_CURSOR_H_
diff --git a/content/renderer/indexed_db/webidbcursor_impl.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.cc
similarity index 68%
rename from content/renderer/indexed_db/webidbcursor_impl.cc
rename to third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.cc
index bdf5ce9..bdf6aeb4 100644
--- a/content/renderer/indexed_db/webidbcursor_impl.cc
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.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/renderer/indexed_db/webidbcursor_impl.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.h"
 
 #include <stddef.h>
 
@@ -10,10 +10,11 @@
 #include <vector>
 
 #include "base/single_thread_task_runner.h"
-#include "content/renderer/indexed_db/indexed_db_dispatcher.h"
-#include "content/renderer/indexed_db/indexed_db_key_builders.h"
 #include "mojo/public/cpp/bindings/strong_associated_binding.h"
+#include "third_party/blink/public/platform/modules/indexeddb/indexed_db_key_builder.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_value.h"
+#include "third_party/blink/renderer/modules/indexeddb/idb_key_range.h"
+#include "third_party/blink/renderer/modules/indexeddb/indexed_db_dispatcher.h"
 
 using blink::WebBlobInfo;
 using blink::WebData;
@@ -21,13 +22,13 @@
 using blink::WebIDBKey;
 using blink::WebIDBKeyView;
 using blink::WebIDBValue;
-using blink::mojom::IDBCallbacksAssociatedPtrInfo;
-using blink::mojom::IDBCursorAssociatedPtrInfo;
+using blink::mojom::blink::IDBCallbacksAssociatedPtrInfo;
+using blink::mojom::blink::IDBCursorAssociatedPtrInfo;
 
-namespace content {
+namespace blink {
 
 WebIDBCursorImpl::WebIDBCursorImpl(
-    blink::mojom::IDBCursorAssociatedPtrInfo cursor_info,
+    mojom::blink::IDBCursorAssociatedPtrInfo cursor_info,
     int64_t transaction_id)
     : transaction_id_(transaction_id),
       cursor_(std::move(cursor_info)),
@@ -36,7 +37,7 @@
       pending_onsuccess_callbacks_(0),
       prefetch_amount_(kMinPrefetchAmount),
       weak_factory_(this) {
-  IndexedDBDispatcher::ThreadSpecificInstance()->RegisterCursor(this);
+  IndexedDBDispatcher::RegisterCursor(this);
 }
 
 WebIDBCursorImpl::~WebIDBCursorImpl() {
@@ -44,7 +45,7 @@
   // object since inside WebKit, they hold a reference to the object which owns
   // this object. But, if that ever changed, then we'd need to invalidate
   // any such pointers.
-  IndexedDBDispatcher::ThreadSpecificInstance()->UnregisterCursor(this);
+  IndexedDBDispatcher::UnregisterCursor(this);
 }
 
 void WebIDBCursorImpl::Advance(unsigned long count,
@@ -57,8 +58,7 @@
   ResetPrefetchCache();
 
   // Reset all cursor prefetch caches except for this cursor.
-  IndexedDBDispatcher::ThreadSpecificInstance()->ResetCursorPrefetchCaches(
-      transaction_id_, this);
+  IndexedDBDispatcher::ResetCursorPrefetchCaches(transaction_id_, this);
 
   auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
       std::move(callbacks), transaction_id_, weak_factory_.GetWeakPtr());
@@ -70,12 +70,12 @@
                                       WebIDBCallbacks* callbacks_ptr) {
   std::unique_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
 
-  if (key.KeyType() == blink::kWebIDBKeyTypeNull &&
-      primary_key.KeyType() == blink::kWebIDBKeyTypeNull) {
+  if (key.KeyType() == kWebIDBKeyTypeNull &&
+      primary_key.KeyType() == kWebIDBKeyTypeNull) {
     // No key(s), so this would qualify for a prefetch.
     ++continue_count_;
 
-    if (!prefetch_keys_.empty()) {
+    if (!prefetch_keys_.IsEmpty()) {
       // We have a prefetch cache, so serve the result from that.
       CachedContinue(callbacks.get());
       return;
@@ -103,13 +103,12 @@
   }
 
   // Reset all cursor prefetch caches except for this cursor.
-  IndexedDBDispatcher::ThreadSpecificInstance()->ResetCursorPrefetchCaches(
-      transaction_id_, this);
+  IndexedDBDispatcher::ResetCursorPrefetchCaches(transaction_id_, this);
 
   auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
       std::move(callbacks), transaction_id_, weak_factory_.GetWeakPtr());
-  cursor_->CursorContinue(IndexedDBKeyBuilder::Build(key),
-                          IndexedDBKeyBuilder::Build(primary_key),
+  cursor_->CursorContinue(WebIDBKeyBuilder::Build(key),
+                          WebIDBKeyBuilder::Build(primary_key),
                           GetCallbacksProxy(std::move(callbacks_impl)));
 }
 
@@ -126,14 +125,18 @@
     ResetPrefetchCache();
 }
 
-void WebIDBCursorImpl::SetPrefetchData(
-    const std::vector<IndexedDBKey>& keys,
-    const std::vector<IndexedDBKey>& primary_keys,
-    std::vector<WebIDBValue> values) {
-  prefetch_keys_.assign(keys.begin(), keys.end());
-  prefetch_primary_keys_.assign(primary_keys.begin(), primary_keys.end());
-  prefetch_values_.assign(std::make_move_iterator(values.begin()),
-                          std::make_move_iterator(values.end()));
+void WebIDBCursorImpl::SetPrefetchData(Vector<WebIDBKey> keys,
+                                       Vector<WebIDBKey> primary_keys,
+                                       Vector<WebIDBValue> values) {
+  // Keys and values are stored in reverse order so that a cache'd continue can
+  // pop a value off of the back and prevent new memory allocations.
+  prefetch_keys_.AppendRange(std::make_move_iterator(keys.rbegin()),
+                             std::make_move_iterator(keys.rend()));
+  prefetch_primary_keys_.AppendRange(
+      std::make_move_iterator(primary_keys.rbegin()),
+      std::make_move_iterator(primary_keys.rend()));
+  prefetch_values_.AppendRange(std::make_move_iterator(values.rbegin()),
+                               std::make_move_iterator(values.rend()));
 
   used_prefetches_ = 0;
   pending_onsuccess_callbacks_ = 0;
@@ -146,9 +149,9 @@
   DCHECK_EQ(prefetch_values_.size(), prefetch_keys_.size());
 
   while (count > 1) {
-    prefetch_keys_.pop_front();
-    prefetch_primary_keys_.pop_front();
-    prefetch_values_.pop_front();
+    prefetch_keys_.pop_back();
+    prefetch_primary_keys_.pop_back();
+    prefetch_values_.pop_back();
     ++used_prefetches_;
     --count;
   }
@@ -161,13 +164,15 @@
   DCHECK_EQ(prefetch_primary_keys_.size(), prefetch_keys_.size());
   DCHECK_EQ(prefetch_values_.size(), prefetch_keys_.size());
 
-  IndexedDBKey key = prefetch_keys_.front();
-  IndexedDBKey primary_key = prefetch_primary_keys_.front();
-  WebIDBValue value = std::move(prefetch_values_.front());
+  // Keys and values are stored in reverse order so that a cache'd continue can
+  // pop a value off of the back and prevent new memory allocations.
+  WebIDBKey key = std::move(prefetch_keys_.back());
+  WebIDBKey primary_key = std::move(prefetch_primary_keys_.back());
+  WebIDBValue value = std::move(prefetch_values_.back());
 
-  prefetch_keys_.pop_front();
-  prefetch_primary_keys_.pop_front();
-  prefetch_values_.pop_front();
+  prefetch_keys_.pop_back();
+  prefetch_primary_keys_.pop_back();
+  prefetch_values_.pop_back();
   ++used_prefetches_;
 
   ++pending_onsuccess_callbacks_;
@@ -180,15 +185,15 @@
     ResetPrefetchCache();
   }
 
-  callbacks->OnSuccess(WebIDBKeyBuilder::Build(key),
-                       WebIDBKeyBuilder::Build(primary_key), std::move(value));
+  callbacks->OnSuccess(std::move(key), std::move(primary_key),
+                       std::move(value));
 }
 
 void WebIDBCursorImpl::ResetPrefetchCache() {
   continue_count_ = 0;
   prefetch_amount_ = kMinPrefetchAmount;
 
-  if (prefetch_keys_.empty()) {
+  if (prefetch_keys_.IsEmpty()) {
     // No prefetch cache, so no need to reset the cursor in the back-end.
     return;
   }
@@ -212,4 +217,4 @@
   return ptr_info;
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.h b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.h
new file mode 100644
index 0000000..f9b04bee
--- /dev/null
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.h
@@ -0,0 +1,93 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_CURSOR_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_CURSOR_IMPL_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_callbacks.h"
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_key.h"
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_value.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+
+namespace blink {
+
+class IndexedDBCallbacksImpl;
+
+class MODULES_EXPORT WebIDBCursorImpl : public WebIDBCursor {
+ public:
+  WebIDBCursorImpl(mojom::blink::IDBCursorAssociatedPtrInfo cursor,
+                   int64_t transaction_id);
+  ~WebIDBCursorImpl() override;
+
+  void Advance(unsigned long count, WebIDBCallbacks* callback) override;
+  void CursorContinue(WebIDBKeyView key,
+                      WebIDBKeyView primary_key,
+                      WebIDBCallbacks* callback) override;
+  void PostSuccessHandlerCallback() override;
+
+  void SetPrefetchData(Vector<WebIDBKey> keys,
+                       Vector<WebIDBKey> primary_keys,
+                       Vector<WebIDBValue> values);
+
+  void CachedAdvance(unsigned long count, WebIDBCallbacks* callbacks);
+  void CachedContinue(WebIDBCallbacks* callbacks);
+
+  // This method is virtual so it can be overridden in unit tests.
+  virtual void ResetPrefetchCache();
+
+  int64_t transaction_id() const { return transaction_id_; }
+
+ private:
+  mojom::blink::IDBCallbacksAssociatedPtrInfo GetCallbacksProxy(
+      std::unique_ptr<IndexedDBCallbacksImpl> callbacks);
+
+  FRIEND_TEST_ALL_PREFIXES(IndexedDBDispatcherTest, CursorReset);
+  FRIEND_TEST_ALL_PREFIXES(IndexedDBDispatcherTest, CursorTransactionId);
+  FRIEND_TEST_ALL_PREFIXES(WebIDBCursorImplTest, AdvancePrefetchTest);
+  FRIEND_TEST_ALL_PREFIXES(WebIDBCursorImplTest, PrefetchReset);
+  FRIEND_TEST_ALL_PREFIXES(WebIDBCursorImplTest, PrefetchTest);
+
+  static constexpr int kPrefetchContinueThreshold = 2;
+  static constexpr int kMinPrefetchAmount = 5;
+  static constexpr int kMaxPrefetchAmount = 100;
+
+  int64_t transaction_id_;
+
+  mojom::blink::IDBCursorAssociatedPtr cursor_;
+
+  // Prefetch cache. Keys and values are stored in reverse order so that a
+  // cache'd continue can pop a value off of the back and prevent new memory
+  // allocations.
+  Vector<WebIDBKey> prefetch_keys_;
+  Vector<WebIDBKey> prefetch_primary_keys_;
+  Vector<WebIDBValue> prefetch_values_;
+
+  // Number of continue calls that would qualify for a pre-fetch.
+  int continue_count_;
+
+  // Number of items used from the last prefetch.
+  int used_prefetches_;
+
+  // Number of onsuccess handlers we are waiting for.
+  int pending_onsuccess_callbacks_;
+
+  // Number of items to request in next prefetch.
+  int prefetch_amount_;
+
+  base::WeakPtrFactory<WebIDBCursorImpl> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebIDBCursorImpl);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_CURSOR_IMPL_H_
diff --git a/content/renderer/indexed_db/webidbcursor_impl_unittest.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl_unittest.cc
similarity index 79%
rename from content/renderer/indexed_db/webidbcursor_impl_unittest.cc
rename to third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl_unittest.cc
index 212b216..36cf7c9 100644
--- a/content/renderer/indexed_db/webidbcursor_impl_unittest.cc
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl_unittest.cc
@@ -2,26 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/indexed_db/webidbcursor_impl.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.h"
 
 #include <stddef.h>
 #include <stdint.h>
 
 #include <memory>
 
-#include "base/guid.h"
 #include "base/macros.h"
-#include "base/run_loop.h"
-#include "base/test/scoped_task_environment.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/values.h"
-#include "content/child/thread_safe_sender.h"
-#include "content/renderer/indexed_db/indexed_db_key_builders.h"
-#include "content/renderer/indexed_db/mock_webidbcallbacks.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/modules/indexeddb/indexed_db_key_builder.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/platform/web_data.h"
+#include "third_party/blink/renderer/modules/indexeddb/idb_key_range.h"
+#include "third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 
 using blink::IndexedDBKey;
 using blink::WebBlobInfo;
@@ -32,16 +29,15 @@
 using blink::WebIDBValue;
 using blink::WebString;
 using blink::WebVector;
-using blink::mojom::IDBCursor;
 using testing::StrictMock;
 
-namespace content {
+namespace blink {
 
 namespace {
 
-class MockCursorImpl : public IDBCursor {
+class MockCursorImpl : public mojom::blink::IDBCursor {
  public:
-  explicit MockCursorImpl(blink::mojom::IDBCursorAssociatedRequest request)
+  explicit MockCursorImpl(mojom::blink::IDBCursorAssociatedRequest request)
       : binding_(this, std::move(request)) {
     binding_.set_connection_error_handler(base::BindOnce(
         &MockCursorImpl::CursorDestroyed, base::Unretained(this)));
@@ -49,7 +45,7 @@
 
   void Prefetch(
       int32_t count,
-      blink::mojom::IDBCallbacksAssociatedPtrInfo callbacks) override {
+      mojom::blink::IDBCallbacksAssociatedPtrInfo callbacks) override {
     ++prefetch_calls_;
     last_prefetch_count_ = count;
   }
@@ -61,14 +57,14 @@
   }
 
   void Advance(uint32_t count,
-               blink::mojom::IDBCallbacksAssociatedPtrInfo callbacks) override {
+               mojom::blink::IDBCallbacksAssociatedPtrInfo callbacks) override {
     ++advance_calls_;
   }
 
   void CursorContinue(
-      const IndexedDBKey& key,
-      const IndexedDBKey& primary_key,
-      blink::mojom::IDBCallbacksAssociatedPtrInfo callbacks) override {
+      WebIDBKey key,
+      WebIDBKey primary_key,
+      mojom::blink::IDBCallbacksAssociatedPtrInfo callbacks) override {
     ++continue_calls_;
   }
 
@@ -119,14 +115,14 @@
 class WebIDBCursorImplTest : public testing::Test {
  public:
   WebIDBCursorImplTest() : null_key_(WebIDBKey::CreateNull()) {
-    blink::mojom::IDBCursorAssociatedPtr ptr;
+    mojom::blink::IDBCursorAssociatedPtr ptr;
     mock_cursor_ = std::make_unique<MockCursorImpl>(
         mojo::MakeRequestAssociatedWithDedicatedPipe(&ptr));
     cursor_ = std::make_unique<WebIDBCursorImpl>(ptr.PassInterface(), 1);
   }
 
  protected:
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  ScopedTestingPlatformSupport<TestingPlatformSupport> platform_;
   WebIDBKey null_key_;
   std::unique_ptr<WebIDBCursorImpl> cursor_;
   std::unique_ptr<MockCursorImpl> mock_cursor_;
@@ -142,7 +138,7 @@
   for (int i = 0; i < WebIDBCursorImpl::kPrefetchContinueThreshold; ++i) {
     cursor_->CursorContinue(null_key_.View(), null_key_.View(),
                             new MockContinueCallbacks());
-    base::RunLoop().RunUntilIdle();
+    platform_->RunUntilIdle();
     EXPECT_EQ(++continue_calls, mock_cursor_->continue_calls());
     EXPECT_EQ(0, mock_cursor_->prefetch_calls());
   }
@@ -157,7 +153,7 @@
     // Initiate the prefetch
     cursor_->CursorContinue(null_key_.View(), null_key_.View(),
                             new MockContinueCallbacks());
-    base::RunLoop().RunUntilIdle();
+    platform_->RunUntilIdle();
     EXPECT_EQ(continue_calls, mock_cursor_->continue_calls());
     EXPECT_EQ(repetitions + 1, mock_cursor_->prefetch_calls());
 
@@ -167,17 +163,22 @@
     last_prefetch_count = prefetch_count;
 
     // Fill the prefetch cache as requested.
-    std::vector<IndexedDBKey> keys;
-    std::vector<IndexedDBKey> primary_keys;
-    std::vector<WebIDBValue> values;
+    Vector<WebIDBKey> keys;
+    Vector<WebIDBKey> primary_keys;
+    Vector<WebIDBValue> values;
+    size_t expected_size = 0;
     for (int i = 0; i < prefetch_count; ++i) {
-      keys.emplace_back(expected_key + i, kWebIDBKeyTypeNumber);
+      WebIDBKey key = WebIDBKey::CreateNumber(expected_key + i);
+      keys.emplace_back(std::move(key));
       primary_keys.emplace_back();
+      expected_size++;
+      EXPECT_EQ(expected_size, keys.size());
+      EXPECT_EQ(expected_size, primary_keys.size());
       WebVector<WebBlobInfo> blob_info;
       blob_info.reserve(expected_key + i);
       for (int j = 0; j < expected_key + i; ++j) {
         blob_info.emplace_back(WebBlobInfo::BlobForTesting(
-            WebString::FromLatin1(base::GenerateGUID()), "text/plain", 123));
+            WebString("blobuuid"), "text/plain", 123));
       }
       values.emplace_back(WebData(), std::move(blob_info));
     }
@@ -194,7 +195,7 @@
       WebVector<WebBlobInfo> blobs;
       cursor_->CursorContinue(null_key_.View(), null_key_.View(),
                               new MockContinueCallbacks(&key, &blobs));
-      base::RunLoop().RunUntilIdle();
+      platform_->RunUntilIdle();
       EXPECT_EQ(continue_calls, mock_cursor_->continue_calls());
       EXPECT_EQ(repetitions + 1, mock_cursor_->prefetch_calls());
 
@@ -205,7 +206,7 @@
   }
 
   cursor_.reset();
-  base::RunLoop().RunUntilIdle();
+  platform_->RunUntilIdle();
   EXPECT_TRUE(mock_cursor_->destroyed());
 }
 
@@ -216,14 +217,14 @@
     cursor_->CursorContinue(null_key_.View(), null_key_.View(),
                             new MockContinueCallbacks());
   }
-  base::RunLoop().RunUntilIdle();
+  platform_->RunUntilIdle();
   EXPECT_EQ(0, mock_cursor_->prefetch_calls());
 
   // Initiate the prefetch
   cursor_->CursorContinue(null_key_.View(), null_key_.View(),
                           new MockContinueCallbacks());
 
-  base::RunLoop().RunUntilIdle();
+  platform_->RunUntilIdle();
   EXPECT_EQ(1, mock_cursor_->prefetch_calls());
   EXPECT_EQ(static_cast<int>(WebIDBCursorImpl::kPrefetchContinueThreshold),
             mock_cursor_->continue_calls());
@@ -233,17 +234,22 @@
 
   // Fill the prefetch cache as requested.
   int expected_key = 0;
-  std::vector<IndexedDBKey> keys;
-  std::vector<IndexedDBKey> primary_keys;
-  std::vector<WebIDBValue> values;
+  Vector<WebIDBKey> keys;
+  Vector<WebIDBKey> primary_keys;
+  Vector<WebIDBValue> values;
+  size_t expected_size = 0;
   for (int i = 0; i < prefetch_count; ++i) {
-    keys.emplace_back(expected_key + i, kWebIDBKeyTypeNumber);
+    WebIDBKey key = WebIDBKey::CreateNumber(expected_key + i);
+    keys.emplace_back(std::move(key));
     primary_keys.emplace_back();
+    expected_size++;
+    EXPECT_EQ(expected_size, keys.size());
+    EXPECT_EQ(expected_size, primary_keys.size());
     WebVector<WebBlobInfo> blob_info;
     blob_info.reserve(expected_key + i);
     for (int j = 0; j < expected_key + i; ++j) {
-      blob_info.emplace_back(WebBlobInfo::BlobForTesting(
-          WebString::FromLatin1(base::GenerateGUID()), "text/plain", 123));
+      blob_info.emplace_back(WebBlobInfo::BlobForTesting(WebString("blobuuid"),
+                                                         "text/plain", 123));
     }
     values.emplace_back(WebData(), std::move(blob_info));
   }
@@ -261,23 +267,23 @@
   IndexedDBKey key;
   cursor_->CursorContinue(null_key_.View(), null_key_.View(),
                           new MockContinueCallbacks(&key));
-  base::RunLoop().RunUntilIdle();
+  platform_->RunUntilIdle();
   EXPECT_EQ(0, key.number());
 
   // IDBCursor.advance(1)
   cursor_->Advance(1, new MockContinueCallbacks(&key));
-  base::RunLoop().RunUntilIdle();
+  platform_->RunUntilIdle();
   EXPECT_EQ(1, key.number());
 
   // IDBCursor.continue()
   cursor_->CursorContinue(null_key_.View(), null_key_.View(),
                           new MockContinueCallbacks(&key));
-  base::RunLoop().RunUntilIdle();
+  platform_->RunUntilIdle();
   EXPECT_EQ(2, key.number());
 
   // IDBCursor.advance(2)
   cursor_->Advance(2, new MockContinueCallbacks(&key));
-  base::RunLoop().RunUntilIdle();
+  platform_->RunUntilIdle();
   EXPECT_EQ(4, key.number());
 
   EXPECT_EQ(0, mock_cursor_->advance_calls());
@@ -285,14 +291,14 @@
   // IDBCursor.advance(lots) - beyond the fetched amount
   cursor_->Advance(WebIDBCursorImpl::kMaxPrefetchAmount,
                    new MockContinueCallbacks(&key));
-  base::RunLoop().RunUntilIdle();
+  platform_->RunUntilIdle();
   EXPECT_EQ(1, mock_cursor_->advance_calls());
   EXPECT_EQ(1, mock_cursor_->prefetch_calls());
   EXPECT_EQ(static_cast<int>(WebIDBCursorImpl::kPrefetchContinueThreshold),
             mock_cursor_->continue_calls());
 
   cursor_.reset();
-  base::RunLoop().RunUntilIdle();
+  platform_->RunUntilIdle();
   EXPECT_TRUE(mock_cursor_->destroyed());
 }
 
@@ -303,7 +309,7 @@
   for (int i = 0; i < WebIDBCursorImpl::kPrefetchContinueThreshold; ++i) {
     cursor_->CursorContinue(null_key_.View(), null_key_.View(),
                             new MockContinueCallbacks());
-    base::RunLoop().RunUntilIdle();
+    platform_->RunUntilIdle();
     EXPECT_EQ(++continue_calls, mock_cursor_->continue_calls());
     EXPECT_EQ(0, mock_cursor_->prefetch_calls());
   }
@@ -311,7 +317,7 @@
   // Initiate the prefetch
   cursor_->CursorContinue(null_key_.View(), null_key_.View(),
                           new MockContinueCallbacks());
-  base::RunLoop().RunUntilIdle();
+  platform_->RunUntilIdle();
   EXPECT_EQ(continue_calls, mock_cursor_->continue_calls());
   EXPECT_EQ(1, mock_cursor_->prefetch_calls());
   EXPECT_EQ(0, mock_cursor_->reset_calls());
@@ -320,21 +326,21 @@
   cursor_->ResetPrefetchCache();
 
   // No reset should have been sent since nothing has been received yet.
-  base::RunLoop().RunUntilIdle();
+  platform_->RunUntilIdle();
   EXPECT_EQ(0, mock_cursor_->reset_calls());
 
   // Fill the prefetch cache as requested.
   int prefetch_count = mock_cursor_->last_prefetch_count();
-  std::vector<IndexedDBKey> keys(prefetch_count);
-  std::vector<IndexedDBKey> primary_keys(prefetch_count);
-  std::vector<WebIDBValue> values;
+  Vector<WebIDBKey> keys(prefetch_count);
+  Vector<WebIDBKey> primary_keys(prefetch_count);
+  Vector<WebIDBValue> values;
   for (int i = 0; i < prefetch_count; ++i)
     values.emplace_back(WebData(), WebVector<WebBlobInfo>());
   cursor_->SetPrefetchData(std::move(keys), std::move(primary_keys),
                            std::move(values));
 
   // No reset should have been sent since prefetch data hasn't been used.
-  base::RunLoop().RunUntilIdle();
+  platform_->RunUntilIdle();
   EXPECT_EQ(0, mock_cursor_->reset_calls());
 
   // The real dispatcher would call cursor->CachedContinue(), so do that:
@@ -342,13 +348,13 @@
   cursor_->CachedContinue(&callbacks);
 
   // Now the cursor should have reset the rest of the cache.
-  base::RunLoop().RunUntilIdle();
+  platform_->RunUntilIdle();
   EXPECT_EQ(1, mock_cursor_->reset_calls());
   EXPECT_EQ(1, mock_cursor_->last_used_count());
 
   cursor_.reset();
-  base::RunLoop().RunUntilIdle();
+  platform_->RunUntilIdle();
   EXPECT_TRUE(mock_cursor_->destroyed());
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/third_party/blink/public/platform/modules/indexeddb/web_idb_database.h b/third_party/blink/renderer/modules/indexeddb/web_idb_database.h
similarity index 84%
rename from third_party/blink/public/platform/modules/indexeddb/web_idb_database.h
rename to third_party/blink/renderer/modules/indexeddb/web_idb_database.h
index 782d60fa..f5e28eb 100644
--- a/third_party/blink/public/platform/modules/indexeddb/web_idb_database.h
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_database.h
@@ -23,17 +23,18 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INDEXEDDB_WEB_IDB_DATABASE_H_
-#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INDEXEDDB_WEB_IDB_DATABASE_H_
-
-#include "third_party/blink/public/common/indexeddb/web_idb_types.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_cursor.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_metadata.h"
-#include "third_party/blink/public/platform/web_blob_info.h"
-#include "third_party/blink/public/platform/web_common.h"
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_DATABASE_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_DATABASE_H_
 
 #include <bitset>
 
+#include "third_party/blink/public/common/indexeddb/web_idb_types.h"
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_metadata.h"
+#include "third_party/blink/public/platform/web_blob_info.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
 namespace blink {
 
 class WebData;
@@ -41,22 +42,22 @@
 class WebIDBKeyPath;
 class WebIDBKeyRange;
 
-class WebIDBDatabase {
+class MODULES_EXPORT WebIDBDatabase {
  public:
   virtual ~WebIDBDatabase() = default;
 
   virtual void CreateObjectStore(long long transaction_id,
                                  long long object_store_id,
-                                 const WebString& name,
+                                 const String& name,
                                  const WebIDBKeyPath&,
                                  bool auto_increment) = 0;
   virtual void DeleteObjectStore(long long transaction_id,
                                  long long object_store_id) = 0;
   virtual void RenameObjectStore(long long transaction_id,
                                  long long object_store_id,
-                                 const WebString& name) = 0;
+                                 const String& name) = 0;
   virtual void CreateTransaction(long long id,
-                                 const WebVector<long long>& scope,
+                                 const Vector<int64_t>& scope,
                                  WebIDBTransactionMode) = 0;
   virtual void Close() = 0;
   virtual void VersionChangeIgnored() = 0;
@@ -67,7 +68,7 @@
   virtual void CreateIndex(long long transaction_id,
                            long long object_store_id,
                            long long index_id,
-                           const WebString& name,
+                           const String& name,
                            const WebIDBKeyPath&,
                            bool unique,
                            bool multi_entry) = 0;
@@ -77,7 +78,7 @@
   virtual void RenameIndex(long long transaction_id,
                            long long object_store_id,
                            long long index_id,
-                           const WebString& new_name) = 0;
+                           const String& new_name) = 0;
 
   static const long long kMinimumIndexId = 30;
 
@@ -89,7 +90,7 @@
       bool values,
       const std::bitset<kWebIDBOperationTypeCount>& operation_types) = 0;
   virtual void RemoveObservers(
-      const WebVector<int32_t>& observer_ids_to_remove) = 0;
+      const Vector<int32_t>& observer_ids_to_remove) = 0;
   virtual void Get(long long transaction_id,
                    long long object_store_id,
                    long long index_id,
@@ -106,18 +107,18 @@
   virtual void Put(long long transaction_id,
                    long long object_store_id,
                    const WebData& value,
-                   const WebVector<WebBlobInfo>&,
+                   const Vector<WebBlobInfo>&,
                    WebIDBKeyView primary_key,
                    WebIDBPutMode,
                    WebIDBCallbacks*,
-                   const WebVector<WebIDBIndexKeys>&) = 0;
+                   const Vector<WebIDBIndexKeys>&) = 0;
   virtual void SetIndexKeys(long long transaction_id,
                             long long object_store_id,
                             WebIDBKeyView primary_key,
-                            const WebVector<WebIDBIndexKeys>&) = 0;
+                            const Vector<WebIDBIndexKeys>&) = 0;
   virtual void SetIndexesReady(long long transaction_id,
                                long long object_store_id,
-                               const WebVector<long long>& index_ids) = 0;
+                               const Vector<int64_t>& index_ids) = 0;
   virtual void OpenCursor(long long transaction_id,
                           long long object_store_id,
                           long long index_id,
@@ -149,4 +150,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INDEXEDDB_WEB_IDB_DATABASE_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_DATABASE_H_
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.cc
new file mode 100644
index 0000000..af94537
--- /dev/null
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.cc
@@ -0,0 +1,305 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.h"
+
+#include <stddef.h>
+
+#include <string>
+#include <vector>
+
+#include "base/format_macros.h"
+#include "base/memory/ptr_util.h"
+#include "mojo/public/cpp/bindings/strong_associated_binding.h"
+#include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
+#include "third_party/blink/public/platform/file_path_conversion.h"
+#include "third_party/blink/public/platform/modules/indexeddb/indexed_db_key_builder.h"
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_error.h"
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_key_path.h"
+#include "third_party/blink/public/platform/modules/indexeddb/web_idb_metadata.h"
+#include "third_party/blink/public/platform/web_blob_info.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_vector.h"
+#include "third_party/blink/renderer/modules/indexeddb/idb_key_range.h"
+#include "third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.h"
+#include "third_party/blink/renderer/modules/indexeddb/indexed_db_dispatcher.h"
+
+namespace blink {
+
+WebIDBDatabaseImpl::WebIDBDatabaseImpl(
+    mojom::blink::IDBDatabaseAssociatedPtrInfo database_info)
+    : database_(std::move(database_info)) {}
+
+WebIDBDatabaseImpl::~WebIDBDatabaseImpl() = default;
+
+void WebIDBDatabaseImpl::CreateObjectStore(long long transaction_id,
+                                           long long object_store_id,
+                                           const String& name,
+                                           const WebIDBKeyPath& key_path,
+                                           bool auto_increment) {
+  database_->CreateObjectStore(transaction_id, object_store_id, name, key_path,
+                               auto_increment);
+}
+
+void WebIDBDatabaseImpl::DeleteObjectStore(long long transaction_id,
+                                           long long object_store_id) {
+  database_->DeleteObjectStore(transaction_id, object_store_id);
+}
+
+void WebIDBDatabaseImpl::RenameObjectStore(long long transaction_id,
+                                           long long object_store_id,
+                                           const String& new_name) {
+  database_->RenameObjectStore(transaction_id, object_store_id, new_name);
+}
+
+void WebIDBDatabaseImpl::CreateTransaction(
+    long long transaction_id,
+    const Vector<int64_t>& object_store_ids,
+    WebIDBTransactionMode mode) {
+  database_->CreateTransaction(transaction_id, object_store_ids, mode);
+}
+
+void WebIDBDatabaseImpl::Close() {
+  database_->Close();
+}
+
+void WebIDBDatabaseImpl::VersionChangeIgnored() {
+  database_->VersionChangeIgnored();
+}
+
+void WebIDBDatabaseImpl::AddObserver(
+    long long transaction_id,
+    int32_t observer_id,
+    bool include_transaction,
+    bool no_records,
+    bool values,
+    const std::bitset<kWebIDBOperationTypeCount>& operation_types) {
+  static_assert(kWebIDBOperationTypeCount < sizeof(uint16_t) * CHAR_BIT,
+                "WebIDBOperationType Count exceeds size of uint16_t");
+  database_->AddObserver(transaction_id, observer_id, include_transaction,
+                         no_records, values, operation_types.to_ulong());
+}
+
+void WebIDBDatabaseImpl::RemoveObservers(const Vector<int32_t>& observer_ids) {
+  database_->RemoveObservers(observer_ids);
+}
+
+void WebIDBDatabaseImpl::Get(long long transaction_id,
+                             long long object_store_id,
+                             long long index_id,
+                             const WebIDBKeyRange& key_range,
+                             bool key_only,
+                             WebIDBCallbacks* callbacks) {
+  IndexedDBDispatcher::ResetCursorPrefetchCaches(transaction_id, nullptr);
+
+  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
+      base::WrapUnique(callbacks), transaction_id, nullptr);
+  database_->Get(transaction_id, object_store_id, index_id, key_range, key_only,
+                 GetCallbacksProxy(std::move(callbacks_impl)));
+}
+
+void WebIDBDatabaseImpl::GetAll(long long transaction_id,
+                                long long object_store_id,
+                                long long index_id,
+                                const WebIDBKeyRange& key_range,
+                                long long max_count,
+                                bool key_only,
+                                WebIDBCallbacks* callbacks) {
+  IndexedDBDispatcher::ResetCursorPrefetchCaches(transaction_id, nullptr);
+
+  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
+      base::WrapUnique(callbacks), transaction_id, nullptr);
+  database_->GetAll(transaction_id, object_store_id, index_id, key_range,
+                    key_only, max_count,
+                    GetCallbacksProxy(std::move(callbacks_impl)));
+}
+
+void WebIDBDatabaseImpl::Put(long long transaction_id,
+                             long long object_store_id,
+                             const WebData& value,
+                             const Vector<WebBlobInfo>& web_blob_info,
+                             WebIDBKeyView web_primary_key,
+                             WebIDBPutMode put_mode,
+                             WebIDBCallbacks* callbacks,
+                             const Vector<WebIDBIndexKeys>& index_keys) {
+  WebIDBKey key = WebIDBKeyBuilder::Build(web_primary_key);
+
+  if (value.size() + key.SizeEstimate() > max_put_value_size_) {
+    callbacks->OnError(WebIDBDatabaseError(
+        kWebIDBDatabaseExceptionUnknownError,
+        WebString(String::Format("The serialized value is too large"
+                                 " (size=%" PRIuS " bytes, max=%" PRIuS
+                                 " bytes).",
+                                 value.size(), max_put_value_size_))));
+    return;
+  }
+
+  IndexedDBDispatcher::ResetCursorPrefetchCaches(transaction_id, nullptr);
+
+  auto mojo_value = mojom::blink::IDBValue::New();
+  DCHECK(mojo_value->bits.IsEmpty());
+  value.ForEachSegment([&mojo_value](const char* segment, size_t segment_size,
+                                     size_t segment_offset) {
+    mojo_value->bits.append(String(segment, segment_size));
+    return true;
+  });
+  mojo_value->blob_or_file_info.ReserveInitialCapacity(web_blob_info.size());
+  for (const WebBlobInfo& info : web_blob_info) {
+    auto blob_info = mojom::blink::IDBBlobInfo::New();
+    if (info.IsFile()) {
+      blob_info->file = mojom::blink::IDBFileInfo::New();
+      blob_info->file->path = WebStringToFilePath(info.FilePath());
+      String name = info.FileName();
+      if (name.IsNull())
+        name = g_empty_string;
+      blob_info->file->name = name;
+      blob_info->file->last_modified =
+          base::Time::FromDoubleT(info.LastModified());
+    }
+    blob_info->size = info.size();
+    blob_info->uuid = info.Uuid();
+    DCHECK(!blob_info->uuid.IsEmpty());
+    String mime_type = info.GetType();
+    if (mime_type.IsNull())
+      mime_type = g_empty_string;
+    blob_info->mime_type = mime_type;
+    blob_info->blob = mojom::blink::BlobPtrInfo(info.CloneBlobHandle(),
+                                                mojom::blink::Blob::Version_);
+    mojo_value->blob_or_file_info.push_back(std::move(blob_info));
+  }
+
+  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
+      base::WrapUnique(callbacks), transaction_id, nullptr);
+  database_->Put(transaction_id, object_store_id, std::move(mojo_value),
+                 std::move(key), put_mode, std::move(index_keys),
+                 GetCallbacksProxy(std::move(callbacks_impl)));
+}
+
+void WebIDBDatabaseImpl::SetIndexKeys(
+    long long transaction_id,
+    long long object_store_id,
+    WebIDBKeyView primary_key,
+    const Vector<WebIDBIndexKeys>& index_keys) {
+  IndexedDBKey temp(IndexedDBKeyBuilder::Build(primary_key));
+  database_->SetIndexKeys(transaction_id, object_store_id,
+                          WebIDBKeyBuilder::Build(temp), std::move(index_keys));
+}
+
+void WebIDBDatabaseImpl::SetIndexesReady(long long transaction_id,
+                                         long long object_store_id,
+                                         const Vector<int64_t>& index_ids) {
+  database_->SetIndexesReady(transaction_id, object_store_id,
+                             std::move(index_ids));
+}
+
+void WebIDBDatabaseImpl::OpenCursor(long long transaction_id,
+                                    long long object_store_id,
+                                    long long index_id,
+                                    const WebIDBKeyRange& key_range,
+                                    WebIDBCursorDirection direction,
+                                    bool key_only,
+                                    WebIDBTaskType task_type,
+                                    WebIDBCallbacks* callbacks) {
+  IndexedDBDispatcher::ResetCursorPrefetchCaches(transaction_id, nullptr);
+
+  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
+      base::WrapUnique(callbacks), transaction_id, nullptr);
+  database_->OpenCursor(transaction_id, object_store_id, index_id, key_range,
+                        direction, key_only, task_type,
+                        GetCallbacksProxy(std::move(callbacks_impl)));
+}
+
+void WebIDBDatabaseImpl::Count(long long transaction_id,
+                               long long object_store_id,
+                               long long index_id,
+                               const WebIDBKeyRange& key_range,
+                               WebIDBCallbacks* callbacks) {
+  IndexedDBDispatcher::ResetCursorPrefetchCaches(transaction_id, nullptr);
+
+  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
+      base::WrapUnique(callbacks), transaction_id, nullptr);
+  database_->Count(transaction_id, object_store_id, index_id, key_range,
+                   GetCallbacksProxy(std::move(callbacks_impl)));
+}
+
+void WebIDBDatabaseImpl::Delete(long long transaction_id,
+                                long long object_store_id,
+                                WebIDBKeyView primary_key,
+                                WebIDBCallbacks* callbacks) {
+  IndexedDBDispatcher::ResetCursorPrefetchCaches(transaction_id, nullptr);
+
+  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
+      base::WrapUnique(callbacks), transaction_id, nullptr);
+  database_->DeleteRange(transaction_id, object_store_id,
+                         WebIDBKeyRangeBuilder::Build(primary_key),
+                         GetCallbacksProxy(std::move(callbacks_impl)));
+}
+
+void WebIDBDatabaseImpl::DeleteRange(long long transaction_id,
+                                     long long object_store_id,
+                                     const WebIDBKeyRange& key_range,
+                                     WebIDBCallbacks* callbacks) {
+  IndexedDBDispatcher::ResetCursorPrefetchCaches(transaction_id, nullptr);
+
+  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
+      base::WrapUnique(callbacks), transaction_id, nullptr);
+  database_->DeleteRange(transaction_id, object_store_id, key_range,
+                         GetCallbacksProxy(std::move(callbacks_impl)));
+}
+
+void WebIDBDatabaseImpl::Clear(long long transaction_id,
+                               long long object_store_id,
+                               WebIDBCallbacks* callbacks) {
+  IndexedDBDispatcher::ResetCursorPrefetchCaches(transaction_id, nullptr);
+
+  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
+      base::WrapUnique(callbacks), transaction_id, nullptr);
+  database_->Clear(transaction_id, object_store_id,
+                   GetCallbacksProxy(std::move(callbacks_impl)));
+}
+
+void WebIDBDatabaseImpl::CreateIndex(long long transaction_id,
+                                     long long object_store_id,
+                                     long long index_id,
+                                     const String& name,
+                                     const WebIDBKeyPath& key_path,
+                                     bool unique,
+                                     bool multi_entry) {
+  database_->CreateIndex(transaction_id, object_store_id, index_id, name,
+                         key_path, unique, multi_entry);
+}
+
+void WebIDBDatabaseImpl::DeleteIndex(long long transaction_id,
+                                     long long object_store_id,
+                                     long long index_id) {
+  database_->DeleteIndex(transaction_id, object_store_id, index_id);
+}
+
+void WebIDBDatabaseImpl::RenameIndex(long long transaction_id,
+                                     long long object_store_id,
+                                     long long index_id,
+                                     const String& new_name) {
+  DCHECK(!new_name.IsNull());
+  database_->RenameIndex(transaction_id, object_store_id, index_id, new_name);
+}
+
+void WebIDBDatabaseImpl::Abort(long long transaction_id) {
+  database_->Abort(transaction_id);
+}
+
+void WebIDBDatabaseImpl::Commit(long long transaction_id) {
+  database_->Commit(transaction_id);
+}
+
+mojom::blink::IDBCallbacksAssociatedPtrInfo
+WebIDBDatabaseImpl::GetCallbacksProxy(
+    std::unique_ptr<IndexedDBCallbacksImpl> callbacks) {
+  mojom::blink::IDBCallbacksAssociatedPtrInfo ptr_info;
+  auto request = mojo::MakeRequest(&ptr_info);
+  mojo::MakeStrongAssociatedBinding(std::move(callbacks), std::move(request));
+  return ptr_info;
+}
+
+}  // namespace blink
diff --git a/content/renderer/indexed_db/webidbdatabase_impl.h b/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.h
similarity index 75%
rename from content/renderer/indexed_db/webidbdatabase_impl.h
rename to third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.h
index e33154d..8ef41986 100644
--- a/content/renderer/indexed_db/webidbdatabase_impl.h
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.h
@@ -2,49 +2,43 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_INDEXED_DB_WEBIDBDATABASE_IMPL_H_
-#define CONTENT_RENDERER_INDEXED_DB_WEBIDBDATABASE_IMPL_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_DATABASE_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_DATABASE_IMPL_H_
 
 #include <stdint.h>
 
 #include <set>
 
-#include "base/memory/ref_counted.h"
 #include "base/single_thread_task_runner.h"
-#include "content/common/content_export.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
-#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_cursor.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_database.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
 
 namespace blink {
+class IndexedDBCallbacksImpl;
 class WebBlobInfo;
 class WebIDBCallbacks;
-class WebString;
-}
 
-namespace content {
-
-class IndexedDBCallbacksImpl;
-
-class CONTENT_EXPORT WebIDBDatabaseImpl : public blink::WebIDBDatabase {
+class MODULES_EXPORT WebIDBDatabaseImpl : public blink::WebIDBDatabase {
  public:
-  WebIDBDatabaseImpl(blink::mojom::IDBDatabaseAssociatedPtrInfo database);
+  WebIDBDatabaseImpl(mojom::blink::IDBDatabaseAssociatedPtrInfo database);
   ~WebIDBDatabaseImpl() override;
 
   // blink::WebIDBDatabase
   void CreateObjectStore(long long transaction_id,
                          long long objectstore_id,
-                         const blink::WebString& name,
+                         const String& name,
                          const blink::WebIDBKeyPath&,
                          bool auto_increment) override;
   void DeleteObjectStore(long long transaction_id,
                          long long object_store_id) override;
   void RenameObjectStore(long long transaction_id,
                          long long object_store_id,
-                         const blink::WebString& new_name) override;
+                         const String& new_name) override;
   void CreateTransaction(long long transaction_id,
-                         const blink::WebVector<long long>& scope,
+                         const Vector<int64_t>& scope,
                          blink::WebIDBTransactionMode mode) override;
 
   void Close() override;
@@ -57,8 +51,7 @@
                    bool values,
                    const std::bitset<blink::kWebIDBOperationTypeCount>&
                        operation_types) override;
-  void RemoveObservers(
-      const blink::WebVector<int32_t>& observer_ids_to_remove) override;
+  void RemoveObservers(const Vector<int32_t>& observer_ids) override;
 
   void Get(long long transaction_id,
            long long object_store_id,
@@ -76,18 +69,18 @@
   void Put(long long transaction_id,
            long long object_store_id,
            const blink::WebData& value,
-           const blink::WebVector<blink::WebBlobInfo>&,
+           const Vector<blink::WebBlobInfo>&,
            blink::WebIDBKeyView primary_key,
            blink::WebIDBPutMode,
            blink::WebIDBCallbacks*,
-           const blink::WebVector<blink::WebIDBIndexKeys>&) override;
+           const Vector<blink::WebIDBIndexKeys>&) override;
   void SetIndexKeys(long long transaction_id,
                     long long object_store_id,
                     blink::WebIDBKeyView primary_key,
-                    const blink::WebVector<blink::WebIDBIndexKeys>&) override;
+                    const Vector<blink::WebIDBIndexKeys>&) override;
   void SetIndexesReady(long long transaction_id,
                        long long object_store_id,
-                       const blink::WebVector<long long>& index_ids) override;
+                       const Vector<int64_t>& index_ids) override;
   void OpenCursor(long long transaction_id,
                   long long object_store_id,
                   long long index_id,
@@ -115,7 +108,7 @@
   void CreateIndex(long long transaction_id,
                    long long object_store_id,
                    long long index_id,
-                   const blink::WebString& name,
+                   const String& name,
                    const blink::WebIDBKeyPath&,
                    bool unique,
                    bool multi_entry) override;
@@ -125,12 +118,12 @@
   void RenameIndex(long long transaction_id,
                    long long object_store_id,
                    long long index_id,
-                   const blink::WebString& new_name) override;
+                   const String& new_name) override;
   void Abort(long long transaction_id) override;
   void Commit(long long transaction_id) override;
 
  private:
-  blink::mojom::IDBCallbacksAssociatedPtrInfo GetCallbacksProxy(
+  mojom::blink::IDBCallbacksAssociatedPtrInfo GetCallbacksProxy(
       std::unique_ptr<IndexedDBCallbacksImpl> callbacks);
 
   FRIEND_TEST_ALL_PREFIXES(WebIDBDatabaseImplTest, ValueSizeTest);
@@ -141,12 +134,12 @@
   // Used by unit tests to exercise behavior without allocating huge chunks
   // of memory.
   size_t max_put_value_size_ =
-      blink::mojom::kIDBMaxMessageSize - blink::mojom::kIDBMaxMessageOverhead;
+      mojom::blink::kIDBMaxMessageSize - mojom::blink::kIDBMaxMessageOverhead;
 
   std::set<int32_t> observer_ids_;
-  blink::mojom::IDBDatabaseAssociatedPtr database_;
+  mojom::blink::IDBDatabaseAssociatedPtr database_;
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_RENDERER_INDEXED_DB_WEBIDBDATABASE_IMPL_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_DATABASE_IMPL_H_
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl_unittest.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl_unittest.cc
new file mode 100644
index 0000000..a960ec61
--- /dev/null
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl_unittest.cc
@@ -0,0 +1,93 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
+#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
+#include "third_party/blink/public/platform/web_blob_info.h"
+#include "third_party/blink/public/platform/web_data.h"
+#include "third_party/blink/public/web/web_heap.h"
+#include "third_party/blink/renderer/modules/indexeddb/idb_key_range.h"
+#include "third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.h"
+
+using testing::_;
+using testing::Invoke;
+using testing::StrictMock;
+using testing::WithArgs;
+
+namespace blink {
+
+class WebIDBDatabaseImplTest : public testing::Test {};
+
+TEST_F(WebIDBDatabaseImplTest, ValueSizeTest) {
+  // For testing use a much smaller maximum size to prevent allocating >100 MB
+  // of memory, which crashes on memory-constrained systems.
+  const size_t kMaxValueSizeForTesting = 10 * 1024 * 1024;  // 10 MB
+
+  const std::vector<char> data(kMaxValueSizeForTesting + 1);
+  const WebData value(&data.front(), data.size());
+  const Vector<WebBlobInfo> blob_info;
+  const WebIDBKey key = WebIDBKey::CreateNumber(0);
+  const int64_t transaction_id = 1;
+  const int64_t object_store_id = 2;
+  StrictMock<MockWebIDBCallbacks> callbacks;
+
+  ASSERT_GT(value.size() + key.SizeEstimate(), kMaxValueSizeForTesting);
+  ThreadState::Current()->CollectAllGarbage();
+  EXPECT_CALL(callbacks, OnError(_)).Times(1);
+
+  WebIDBDatabaseImpl database_impl(nullptr);
+  database_impl.max_put_value_size_ = kMaxValueSizeForTesting;
+  database_impl.Put(transaction_id, object_store_id, value, blob_info,
+                    key.View(), blink::kWebIDBPutModeAddOrUpdate, &callbacks,
+                    Vector<blink::WebIDBIndexKeys>());
+}
+
+TEST_F(WebIDBDatabaseImplTest, KeyAndValueSizeTest) {
+  // For testing use a much smaller maximum size to prevent allocating >100 MB
+  // of memory, which crashes on memory-constrained systems.
+  const size_t kMaxValueSizeForTesting = 10 * 1024 * 1024;  // 10 MB
+  const size_t kKeySize = 1024 * 1024;
+
+  const std::vector<char> data(kMaxValueSizeForTesting - kKeySize);
+  const WebData value(&data.front(), data.size());
+  const Vector<WebBlobInfo> blob_info;
+  const int64_t transaction_id = 1;
+  const int64_t object_store_id = 2;
+  StrictMock<MockWebIDBCallbacks> callbacks;
+
+  // For this test, we want IDBKey::SizeEstimate() minus kKeySize to be the
+  // smallest value > 0.  An IDBKey with a string has a size_estimate_ equal to
+  // kOverheadSize (~16) + (string.length * sizeof(UChar)).  Create
+  // |kKeySize / sizeof(UChar)| characters in String.
+  const unsigned int number_of_chars = kKeySize / sizeof(UChar);
+  Vector<UChar> key_string_vector;
+  key_string_vector.ReserveInitialCapacity(number_of_chars);
+  key_string_vector.Fill(u'0', number_of_chars);
+  String key_string(key_string_vector);
+  DCHECK_EQ(key_string.length(), number_of_chars);
+
+  WebIDBKey key = WebIDBKey::CreateString(key_string);
+  DCHECK_EQ(value.size(), kMaxValueSizeForTesting - kKeySize);
+  DCHECK_GT(key.SizeEstimate() - kKeySize, static_cast<unsigned long>(0));
+  DCHECK_GT(value.size() + key.SizeEstimate(), kMaxValueSizeForTesting);
+
+  ThreadState::Current()->CollectAllGarbage();
+  EXPECT_CALL(callbacks, OnError(_)).Times(1);
+
+  WebIDBDatabaseImpl database_impl(nullptr);
+  database_impl.max_put_value_size_ = kMaxValueSizeForTesting;
+  database_impl.Put(transaction_id, object_store_id, value, blob_info,
+                    key.View(), blink::kWebIDBPutModeAddOrUpdate, &callbacks,
+                    Vector<blink::WebIDBIndexKeys>());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/public/platform/modules/indexeddb/web_idb_factory.h b/third_party/blink/renderer/modules/indexeddb/web_idb_factory.h
similarity index 89%
rename from third_party/blink/public/platform/modules/indexeddb/web_idb_factory.h
rename to third_party/blink/renderer/modules/indexeddb/web_idb_factory.h
index b99e5d8..71a2ce0 100644
--- a/third_party/blink/public/platform/modules/indexeddb/web_idb_factory.h
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_factory.h
@@ -26,11 +26,11 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INDEXEDDB_WEB_IDB_FACTORY_H_
-#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INDEXEDDB_WEB_IDB_FACTORY_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_FACTORY_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_FACTORY_H_
 
 #include "base/single_thread_task_runner.h"
-#include "third_party/blink/public/platform/web_common.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -43,7 +43,7 @@
 class WebSecurityOrigin;
 class WebString;
 
-class WebIDBFactory {
+class MODULES_EXPORT WebIDBFactory {
  public:
   virtual ~WebIDBFactory() = default;
 
@@ -70,4 +70,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INDEXEDDB_WEB_IDB_FACTORY_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_FACTORY_H_
diff --git a/content/renderer/indexed_db/webidbfactory_impl.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_factory_impl.cc
similarity index 71%
rename from content/renderer/indexed_db/webidbfactory_impl.cc
rename to third_party/blink/renderer/modules/indexeddb/web_idb_factory_impl.cc
index 62682a0..f158e08 100644
--- a/content/renderer/indexed_db/webidbfactory_impl.cc
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_factory_impl.cc
@@ -2,29 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/indexed_db/webidbfactory_impl.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_factory_impl.h"
 
 #include "base/memory/ptr_util.h"
-#include "content/renderer/indexed_db/indexed_db_callbacks_impl.h"
-#include "content/renderer/indexed_db/indexed_db_database_callbacks_impl.h"
-#include "content/renderer/storage_util.h"
-#include "ipc/ipc_sync_channel.h"
 #include "mojo/public/cpp/bindings/strong_associated_binding.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
 #include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.h"
+#include "third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.h"
 
-using blink::WebIDBCallbacks;
-using blink::WebIDBDatabase;
-using blink::WebIDBDatabaseCallbacks;
-using blink::WebSecurityOrigin;
-using blink::WebString;
-using blink::mojom::IDBCallbacksAssociatedPtrInfo;
-using blink::mojom::IDBDatabaseCallbacksAssociatedPtrInfo;
-using blink::mojom::IDBFactoryPtrInfo;
+namespace blink {
 
-namespace content {
-
-WebIDBFactoryImpl::WebIDBFactoryImpl(IDBFactoryPtrInfo factory_info)
+WebIDBFactoryImpl::WebIDBFactoryImpl(
+    mojom::blink::IDBFactoryPtrInfo factory_info)
     : factory_(std::move(factory_info)) {}
 
 WebIDBFactoryImpl::~WebIDBFactoryImpl() = default;
@@ -37,7 +27,7 @@
       base::WrapUnique(callbacks), IndexedDBCallbacksImpl::kNoTransaction,
       nullptr);
   factory_->GetDatabaseInfo(GetCallbacksProxy(std::move(callbacks_impl)),
-                            url::Origin(origin));
+                            origin);
 }
 
 void WebIDBFactoryImpl::GetDatabaseNames(
@@ -48,7 +38,7 @@
       base::WrapUnique(callbacks), IndexedDBCallbacksImpl::kNoTransaction,
       nullptr);
   factory_->GetDatabaseNames(GetCallbacksProxy(std::move(callbacks_impl)),
-                             url::Origin(origin));
+                             origin);
 }
 
 void WebIDBFactoryImpl::Open(
@@ -64,9 +54,10 @@
   auto database_callbacks_impl =
       std::make_unique<IndexedDBDatabaseCallbacksImpl>(
           base::WrapUnique(database_callbacks));
+  DCHECK(!name.IsNull());
   factory_->Open(GetCallbacksProxy(std::move(callbacks_impl)),
                  GetDatabaseCallbacksProxy(std::move(database_callbacks_impl)),
-                 url::Origin(origin), name.Utf16(), version, transaction_id);
+                 origin, name, version, transaction_id);
 }
 
 void WebIDBFactoryImpl::DeleteDatabase(
@@ -78,25 +69,27 @@
   auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
       base::WrapUnique(callbacks), IndexedDBCallbacksImpl::kNoTransaction,
       nullptr);
-  factory_->DeleteDatabase(GetCallbacksProxy(std::move(callbacks_impl)),
-                           url::Origin(origin), name.Utf16(), force_close);
+  DCHECK(!name.IsNull());
+  factory_->DeleteDatabase(GetCallbacksProxy(std::move(callbacks_impl)), origin,
+                           name, force_close);
 }
 
-IDBCallbacksAssociatedPtrInfo WebIDBFactoryImpl::GetCallbacksProxy(
+mojom::blink::IDBCallbacksAssociatedPtrInfo
+WebIDBFactoryImpl::GetCallbacksProxy(
     std::unique_ptr<IndexedDBCallbacksImpl> callbacks) {
-  IDBCallbacksAssociatedPtrInfo ptr_info;
+  mojom::blink::IDBCallbacksAssociatedPtrInfo ptr_info;
   auto request = mojo::MakeRequest(&ptr_info);
   mojo::MakeStrongAssociatedBinding(std::move(callbacks), std::move(request));
   return ptr_info;
 }
 
-IDBDatabaseCallbacksAssociatedPtrInfo
+mojom::blink::IDBDatabaseCallbacksAssociatedPtrInfo
 WebIDBFactoryImpl::GetDatabaseCallbacksProxy(
     std::unique_ptr<IndexedDBDatabaseCallbacksImpl> callbacks) {
-  IDBDatabaseCallbacksAssociatedPtrInfo ptr_info;
+  mojom::blink::IDBDatabaseCallbacksAssociatedPtrInfo ptr_info;
   auto request = mojo::MakeRequest(&ptr_info);
   mojo::MakeStrongAssociatedBinding(std::move(callbacks), std::move(request));
   return ptr_info;
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/content/renderer/indexed_db/webidbfactory_impl.h b/third_party/blink/renderer/modules/indexeddb/web_idb_factory_impl.h
similarity index 62%
rename from content/renderer/indexed_db/webidbfactory_impl.h
rename to third_party/blink/renderer/modules/indexeddb/web_idb_factory_impl.h
index f4a704b..dbc69d8 100644
--- a/content/renderer/indexed_db/webidbfactory_impl.h
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_factory_impl.h
@@ -2,28 +2,24 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_INDEXED_DB_WEBIDBFACTORY_IMPL_H_
-#define CONTENT_RENDERER_INDEXED_DB_WEBIDBFACTORY_IMPL_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_FACTORY_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_FACTORY_IMPL_H_
 
-#include "base/memory/ref_counted.h"
-#include "base/single_thread_task_runner.h"
-#include "content/renderer/indexed_db/indexed_db_callbacks_impl.h"
-#include "content/renderer/indexed_db/indexed_db_database_callbacks_impl.h"
-#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_callbacks.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_callbacks.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_factory.h"
+#include "third_party/blink/renderer/modules/indexeddb/idb_key_range.h"
+#include "third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.h"
+#include "third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_factory.h"
 
 namespace blink {
 class WebSecurityOrigin;
 class WebString;
-}
-
-namespace content {
 
 class WebIDBFactoryImpl : public blink::WebIDBFactory {
  public:
-  explicit WebIDBFactoryImpl(blink::mojom::IDBFactoryPtrInfo factory_info);
+  explicit WebIDBFactoryImpl(mojom::blink::IDBFactoryPtrInfo factory_info);
   ~WebIDBFactoryImpl() override;
 
   // See WebIDBFactory.h for documentation on these functions.
@@ -50,14 +46,14 @@
       scoped_refptr<base::SingleThreadTaskRunner> task_runner) override;
 
  private:
-  blink::mojom::IDBCallbacksAssociatedPtrInfo GetCallbacksProxy(
-      std::unique_ptr<IndexedDBCallbacksImpl> callbacks);
-  blink::mojom::IDBDatabaseCallbacksAssociatedPtrInfo GetDatabaseCallbacksProxy(
-      std::unique_ptr<IndexedDBDatabaseCallbacksImpl> callbacks);
+  mojom::blink::IDBCallbacksAssociatedPtrInfo GetCallbacksProxy(
+      std::unique_ptr<blink::IndexedDBCallbacksImpl> callbacks);
+  mojom::blink::IDBDatabaseCallbacksAssociatedPtrInfo GetDatabaseCallbacksProxy(
+      std::unique_ptr<blink::IndexedDBDatabaseCallbacksImpl> callbacks);
 
-  blink::mojom::IDBFactoryPtr factory_;
+  mojom::blink::IDBFactoryPtr factory_;
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_RENDERER_INDEXED_DB_WEBIDBFACTORY_IMPL_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_FACTORY_IMPL_H_
diff --git a/third_party/blink/renderer/modules/presentation/OWNERS b/third_party/blink/renderer/modules/presentation/OWNERS
index 210cf40..60162ad0 100644
--- a/third_party/blink/renderer/modules/presentation/OWNERS
+++ b/third_party/blink/renderer/modules/presentation/OWNERS
@@ -1,4 +1,3 @@
-imcheng@chromium.org
 mfoltz@chromium.org
 mlamouri@chromium.org
 
diff --git a/third_party/blink/renderer/modules/webaudio/async_audio_decoder.cc b/third_party/blink/renderer/modules/webaudio/async_audio_decoder.cc
index aae1753..bb3c040 100644
--- a/third_party/blink/renderer/modules/webaudio/async_audio_decoder.cc
+++ b/third_party/blink/renderer/modules/webaudio/async_audio_decoder.cc
@@ -55,7 +55,7 @@
       context->GetExecutionContext()->GetTaskRunner(
           blink::TaskType::kInternalMedia);
 
-  BackgroundScheduler::PostOnBackgroundThread(
+  background_scheduler::PostOnBackgroundThread(
       FROM_HERE,
       CrossThreadBind(&AsyncAudioDecoder::DecodeOnBackgroundThread,
                       WrapCrossThreadPersistent(audio_data), sample_rate,
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
index 93297eb..e84072c 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -72,6 +72,10 @@
   return RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled();
 }
 
+bool WebRuntimeFeatures::IsFractionalScrollOffsetsEnabled() {
+  return RuntimeEnabledFeatures::FractionalScrollOffsetsEnabled();
+}
+
 void WebRuntimeFeatures::EnableTestOnlyFeatures(bool enable) {
   RuntimeEnabledFeatures::SetTestFeaturesEnabled(enable);
 }
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.cc b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
index 72b2aa7..79bb7835 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
@@ -103,10 +103,8 @@
   *out_callback = viz::SingleReleaseCallback::Create(std::move(func));
 
   if (out_resource) {
-    if (SupportsAcceleratedCompositing()) {
+    if (SupportsAcceleratedCompositing())
       return PrepareAcceleratedTransferableResource(out_resource, sync_mode);
-    }
-
     return PrepareUnacceleratedTransferableResource(out_resource);
   }
   return true;
@@ -182,18 +180,13 @@
     base::WeakPtr<CanvasResourceProvider> provider,
     SkFilterQuality filter_quality,
     const CanvasColorParams& color_params) {
-  scoped_refptr<CanvasResourceBitmap> resource =
-      AdoptRef(new CanvasResourceBitmap(std::move(image), std::move(provider),
-                                        filter_quality, color_params));
-  if (resource->IsValid())
-    return resource;
-  return nullptr;
+  auto resource = AdoptRef(new CanvasResourceBitmap(
+      std::move(image), std::move(provider), filter_quality, color_params));
+  return resource->IsValid() ? resource : nullptr;
 }
 
 bool CanvasResourceBitmap::IsValid() const {
-  if (!image_)
-    return false;
-  return image_->IsValid();
+  return image_ ? image_->IsValid() : false;
 }
 
 bool CanvasResourceBitmap::IsAccelerated() const {
@@ -320,12 +313,9 @@
   if (!gl || !gr)
     return;
 
-  gfx::BufferUsage buffer_usage;
-  if (is_accelerated) {
-    buffer_usage = gfx::BufferUsage::SCANOUT;
-  } else {
-    buffer_usage = gfx::BufferUsage::SCANOUT_CPU_READ_WRITE;
-  }
+  const gfx::BufferUsage buffer_usage =
+      is_accelerated ? gfx::BufferUsage::SCANOUT
+                     : gfx::BufferUsage::SCANOUT_CPU_READ_WRITE;
 
   gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager =
       Platform::Current()->GetGpuMemoryBufferManager();
@@ -334,9 +324,9 @@
   gpu_memory_buffer_ = gpu_memory_buffer_manager->CreateGpuMemoryBuffer(
       gfx::Size(size.Width(), size.Height()), ColorParams().GetBufferFormat(),
       buffer_usage, gpu::kNullSurfaceHandle);
-  if (!gpu_memory_buffer_) {
+  if (!gpu_memory_buffer_)
     return;
-  }
+
   gpu_memory_buffer_->SetColorSpace(color_params.GetStorageGfxColorSpace());
 
   image_id_ = gl->CreateImageCHROMIUM(gpu_memory_buffer_->AsClientBuffer(),
@@ -382,8 +372,7 @@
 }
 
 IntSize CanvasResourceGpuMemoryBuffer::Size() const {
-  return IntSize(gpu_memory_buffer_->GetSize().width(),
-                 gpu_memory_buffer_->GetSize().height());
+  return IntSize(gpu_memory_buffer_->GetSize());
 }
 
 scoped_refptr<CanvasResourceGpuMemoryBuffer>
@@ -395,14 +384,10 @@
     SkFilterQuality filter_quality,
     bool is_accelerated) {
   TRACE_EVENT0("blink", "CanvasResourceGpuMemoryBuffer::Create");
-
-  scoped_refptr<CanvasResourceGpuMemoryBuffer> resource =
-      AdoptRef(new CanvasResourceGpuMemoryBuffer(
-          size, color_params, std::move(context_provider_wrapper),
-          std::move(provider), filter_quality, is_accelerated));
-  if (resource->IsValid())
-    return resource;
-  return nullptr;
+  auto resource = AdoptRef(new CanvasResourceGpuMemoryBuffer(
+      size, color_params, std::move(context_provider_wrapper),
+      std::move(provider), filter_quality, is_accelerated));
+  return resource->IsValid() ? resource : nullptr;
 }
 
 void CanvasResourceGpuMemoryBuffer::TearDown() {
@@ -638,12 +623,9 @@
     const CanvasColorParams& color_params,
     base::WeakPtr<CanvasResourceProvider> provider,
     SkFilterQuality filter_quality) {
-  scoped_refptr<CanvasResourceSharedBitmap> resource =
-      AdoptRef(new CanvasResourceSharedBitmap(
-          size, color_params, std::move(provider), filter_quality));
-  if (resource->IsValid())
-    return resource;
-  return nullptr;
+  auto resource = AdoptRef(new CanvasResourceSharedBitmap(
+      size, color_params, std::move(provider), filter_quality));
+  return resource->IsValid() ? resource : nullptr;
 }
 
 void CanvasResourceSharedBitmap::TearDown() {
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
index 983308e..26707aeb 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -185,7 +185,6 @@
   scoped_refptr<CanvasResource> ProduceFrame() final {
     TRACE_EVENT0("blink",
                  "CanvasResourceProviderTextureGpuMemoreBuffer::ProduceFrame");
-
     DCHECK(GetSkSurface());
 
     if (IsGpuContextLost())
@@ -214,7 +213,6 @@
     output_resource->CopyFromTexture(skia_texture_id,
                                      ColorParams().GLUnsizedInternalFormat(),
                                      ColorParams().GLType());
-
     return output_resource;
   }
 };
@@ -359,10 +357,8 @@
   scoped_refptr<CanvasResource> ProduceFrame() final {
     DCHECK(GetSkSurface());
     scoped_refptr<CanvasResource> output_resource = NewOrRecycledResource();
-    if (!output_resource) {
-      // Not compositable without a SharedBitmap
+    if (!output_resource)
       return nullptr;
-    }
 
     auto paint_image = MakeImageSnapshot();
     if (!paint_image)
@@ -450,8 +446,7 @@
       case kTextureGpuMemoryBufferResourceType:
         if (!SharedGpuContext::IsGpuCompositingEnabled())
           continue;
-        if (presentation_mode !=
-            CanvasResourceProvider::kAllowImageChromiumPresentationMode)
+        if (presentation_mode != kAllowImageChromiumPresentationMode)
           continue;
         if (!context_provider_wrapper)
           continue;
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
index da7e94b..60c939b 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -178,11 +178,6 @@
 
 int PropertyTreeManager::EnsureCompositorTransformNode(
     const TransformPaintPropertyNode* transform_node) {
-  DCHECK(transform_node);
-  // TODO(crbug.com/645615): Remove the failsafe here.
-  if (!transform_node)
-    return kSecondaryRootNodeId;
-
   transform_node = transform_node->Unalias();
   auto it = transform_node_map_.find(transform_node);
   if (it != transform_node_map_.end())
@@ -312,11 +307,6 @@
 
 int PropertyTreeManager::EnsureCompositorClipNode(
     const ClipPaintPropertyNode* clip_node) {
-  DCHECK(clip_node);
-  // TODO(crbug.com/645615): Remove the failsafe here.
-  if (!clip_node)
-    return kSecondaryRootNodeId;
-
   clip_node = clip_node->Unalias();
   auto it = clip_node_map_.find(clip_node);
   if (it != clip_node_map_.end())
diff --git a/third_party/blink/renderer/platform/graphics/logging_canvas.cc b/third_party/blink/renderer/platform/graphics/logging_canvas.cc
index a965a4ca..71029cd6 100644
--- a/third_party/blink/renderer/platform/graphics/logging_canvas.cc
+++ b/third_party/blink/renderer/platform/graphics/logging_canvas.cc
@@ -373,20 +373,6 @@
   };
 }
 
-String TextAlignName(SkPaint::Align align) {
-  switch (align) {
-    case SkPaint::kLeft_Align:
-      return "Left";
-    case SkPaint::kCenter_Align:
-      return "Center";
-    case SkPaint::kRight_Align:
-      return "Right";
-    default:
-      NOTREACHED();
-      return "?";
-  };
-}
-
 String StrokeCapName(SkPaint::Cap cap) {
   switch (cap) {
     case SkPaint::kButt_Cap:
@@ -474,7 +460,6 @@
   paint_item->SetString("flags", StringForSkPaintFlags(paint));
   paint_item->SetString("filterLevel",
                         FilterQualityName(paint.getFilterQuality()));
-  paint_item->SetString("textAlign", TextAlignName(paint.getTextAlign()));
   paint_item->SetString("strokeCap", StrokeCapName(paint.getStrokeCap()));
   paint_item->SetString("strokeJoin", StrokeJoinName(paint.getStrokeJoin()));
   paint_item->SetString("styleName", StyleName(paint.getStyle()));
diff --git a/third_party/blink/renderer/platform/mojo/blink_typemaps.gni b/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
index 2048446..b6b298b 100644
--- a/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
+++ b/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
@@ -10,6 +10,7 @@
   "//mojo/public/cpp/base/unguessable_token.typemap",
   "//third_party/blink/renderer/core/messaging/blink_cloneable_message.typemap",
   "//third_party/blink/renderer/core/messaging/blink_transferable_message.typemap",
+  "//third_party/blink/renderer/modules/indexeddb/indexed_db.typemap",
   "//third_party/blink/renderer/platform/blob/serialized_blob.typemap",
   "//third_party/blink/renderer/platform/mojo/big_buffer.typemap",
   "//third_party/blink/renderer/platform/mojo/big_string.typemap",
diff --git a/third_party/blink/renderer/platform/scheduler/common/background_scheduler.cc b/third_party/blink/renderer/platform/scheduler/common/background_scheduler.cc
index dbf59fa..481d2df4 100644
--- a/third_party/blink/renderer/platform/scheduler/common/background_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/background_scheduler.cc
@@ -9,14 +9,15 @@
 
 namespace blink {
 
-void BackgroundScheduler::PostOnBackgroundThread(const base::Location& location,
-                                                 CrossThreadClosure closure) {
+void background_scheduler::PostOnBackgroundThread(
+    const base::Location& location,
+    CrossThreadClosure closure) {
   PostOnBackgroundThreadWithTraits(
       location, {base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
       std::move(closure));
 }
 
-void BackgroundScheduler::PostOnBackgroundThreadWithTraits(
+void background_scheduler::PostOnBackgroundThreadWithTraits(
     const base::Location& location,
     const base::TaskTraits& traits,
     CrossThreadClosure closure) {
diff --git a/third_party/blink/renderer/platform/scheduler/common/background_scheduler_unittest.cc b/third_party/blink/renderer/platform/scheduler/common/background_scheduler_unittest.cc
index 66710ec..bcd2372cd 100644
--- a/third_party/blink/renderer/platform/scheduler/common/background_scheduler_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/background_scheduler_unittest.cc
@@ -24,7 +24,7 @@
 TEST(BackgroundSchedulerTest, RunOnBackgroundThread) {
   base::test::ScopedTaskEnvironment scoped_task_environment;
   std::unique_ptr<WaitableEvent> done_event = std::make_unique<WaitableEvent>();
-  BackgroundScheduler::PostOnBackgroundThread(
+  background_scheduler::PostOnBackgroundThread(
       FROM_HERE,
       CrossThreadBind(&PingPongTask, CrossThreadUnretained(done_event.get())));
   // Test passes by not hanging on the following wait().
diff --git a/third_party/blink/renderer/platform/scheduler/public/background_scheduler.h b/third_party/blink/renderer/platform/scheduler/public/background_scheduler.h
index 8139d02a..f11c9446 100644
--- a/third_party/blink/renderer/platform/scheduler/public/background_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/public/background_scheduler.h
@@ -13,7 +13,7 @@
 
 namespace blink {
 
-namespace BackgroundScheduler {
+namespace background_scheduler {
 
 // These are a thin wrapper around base::TaskScheduler to accomodate
 // Blink's CrossThreadClosure, which only allows background tasks
@@ -31,7 +31,7 @@
 // TODO(altimin): Expose CreateBackgroundTaskRunnerWithTraits when the
 // need arises.
 
-}  // namespace BackgroundScheduler
+}  // namespace background_scheduler
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support.cc b/third_party/blink/renderer/platform/testing/testing_platform_support.cc
index ebceac70..0a52588 100644
--- a/third_party/blink/renderer/platform/testing/testing_platform_support.cc
+++ b/third_party/blink/renderer/platform/testing/testing_platform_support.cc
@@ -110,10 +110,6 @@
   return old_platform_ ? old_platform_->GetBlobRegistry() : nullptr;
 }
 
-std::unique_ptr<WebIDBFactory> TestingPlatformSupport::CreateIdbFactory() {
-  return old_platform_ ? old_platform_->CreateIdbFactory() : nullptr;
-}
-
 WebURLLoaderMockFactory* TestingPlatformSupport::GetURLLoaderMockFactory() {
   return old_platform_ ? old_platform_->GetURLLoaderMockFactory() : nullptr;
 }
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support.h b/third_party/blink/renderer/platform/testing/testing_platform_support.h
index 87cfa93..38bf520 100644
--- a/third_party/blink/renderer/platform/testing/testing_platform_support.h
+++ b/third_party/blink/renderer/platform/testing/testing_platform_support.h
@@ -61,7 +61,6 @@
   // Platform:
   WebString DefaultLocale() override;
   WebBlobRegistry* GetBlobRegistry() override;
-  std::unique_ptr<WebIDBFactory> CreateIdbFactory() override;
   WebURLLoaderMockFactory* GetURLLoaderMockFactory() override;
   std::unique_ptr<blink::WebURLLoaderFactory> CreateDefaultURLLoaderFactory()
       override;
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index d956c2f..670c4f3f 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -220,16 +220,19 @@
             'url::.+',
 
             # Nested namespaces under the blink namespace
+            'background_scheduler::.+',
             'canvas_heuristic_parameters::.+',
             'cssvalue::.+',
             'encoding::.+',
             'event_handling_util::.+',
             'event_util::.+',
             'media_constraints_impl::.+',
+            'media_element_parser_helpers::.+',
             'network_utils::.+',
             'paint_filter_builder::.+',
             'root_scroller_util::.+',
             'scheduler::.+',
+            'style_change_reason::.+',
             'touch_action_util::.+',
             'vector_math::.+',
             'xpath::.+',
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/base.py b/third_party/blink/tools/blinkpy/web_tests/port/base.py
index ec9a9731..e42a035 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/base.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/base.py
@@ -223,7 +223,6 @@
         if not hasattr(options, 'target') or not options.target:
             self.set_option_default('target', self._options.configuration)
         self._test_configuration = None
-        self._reftest_list = {}
         self._results_directory = None
         self._virtual_test_suites = None
 
@@ -676,43 +675,9 @@
         text = self._filesystem.read_binary_file(baseline_path)
         return text.replace('\r\n', '\n')
 
-    def _get_reftest_list(self, test_name):
-        dirname = self._filesystem.join(self.layout_tests_dir(), self._filesystem.dirname(test_name))
-        if dirname not in self._reftest_list:
-            self._reftest_list[dirname] = Port._parse_reftest_list(self._filesystem, dirname)
-        return self._reftest_list[dirname]
-
-    @staticmethod
-    def _parse_reftest_list(filesystem, test_dirpath):
-        reftest_list_path = filesystem.join(test_dirpath, 'reftest.list')
-        if not filesystem.isfile(reftest_list_path):
-            return None
-        reftest_list_file = filesystem.read_text_file(reftest_list_path)
-
-        parsed_list = {}
-        for line in reftest_list_file.split('\n'):
-            line = re.sub('#.+$', '', line)
-            split_line = line.split()
-            if len(split_line) == 4:
-                # FIXME: Probably one of mozilla's extensions in the
-                # reftest.list format. Do we need to support this?
-                _log.warning("unsupported reftest.list line '%s' in %s", line, reftest_list_path)
-                continue
-            if len(split_line) < 3:
-                continue
-            expectation_type, test_file, ref_file = split_line
-            parsed_list.setdefault(filesystem.join(test_dirpath, test_file), []).append(
-                (expectation_type, filesystem.join(test_dirpath, ref_file)))
-        return parsed_list
-
     def reference_files(self, test_name):
         """Returns a list of expectation (== or !=) and filename pairs"""
 
-        # Try to extract information from reftest.list.
-        reftest_list = self._get_reftest_list(test_name)
-        if reftest_list:
-            return reftest_list.get(self._filesystem.join(self.layout_tests_dir(), test_name), [])
-
         # Try to find -expected.* or -expected-mismatch.* in the same directory.
         reftest_list = []
         for expectation in ('==', '!='):
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py b/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py
index 6eb6aec..eb506eb 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py
@@ -635,25 +635,6 @@
         self.assertFalse(port.is_slow_wpt_test('/dom/ranges/Range-attributes.html'))
         self.assertFalse(port.is_slow_wpt_test('/dom/ranges/Range-attributes-slow.html'))
 
-    def test_parse_reftest_list(self):
-        port = self.make_port(with_tests=True)
-        port.host.filesystem.files['bar/reftest.list'] = '\n'.join(['== test.html test-ref.html',
-                                                                    '',
-                                                                    '# some comment',
-                                                                    '!= test-2.html test-notref.html # more comments',
-                                                                    '== test-3.html test-ref.html',
-                                                                    '== test-3.html test-ref2.html',
-                                                                    '!= test-3.html test-notref.html',
-                                                                    'fuzzy(80,500) == test-3 test-ref.html'])
-
-        # Note that we don't support the syntax in the last line; the code should ignore it, rather than crashing.
-
-        reftest_list = Port._parse_reftest_list(port.host.filesystem, 'bar')
-        self.assertEqual(reftest_list, {
-            'bar/test.html': [('==', 'bar/test-ref.html')],
-            'bar/test-2.html': [('!=', 'bar/test-notref.html')],
-            'bar/test-3.html': [('==', 'bar/test-ref.html'), ('==', 'bar/test-ref2.html'), ('!=', 'bar/test-notref.html')]})
-
     def test_reference_files(self):
         port = self.make_port(with_tests=True)
         self.assertEqual(port.reference_files('passes/svgreftest.svg'),
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/test.py b/third_party/blink/tools/blinkpy/web_tests/port/test.py
index 03dd7be..8c79a74 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/test.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/test.py
@@ -110,14 +110,14 @@
 #
 # These numbers may need to be updated whenever we add or delete tests. This includes virtual tests.
 #
-TOTAL_TESTS = 129
+TOTAL_TESTS = 121
 TOTAL_WONTFIX = 3
 TOTAL_SKIPS = 20 + TOTAL_WONTFIX
 TOTAL_CRASHES = 76
 
 UNEXPECTED_PASSES = 1
 UNEXPECTED_NON_VIRTUAL_FAILURES = 19
-UNEXPECTED_FAILURES = 45
+UNEXPECTED_FAILURES = 40
 
 
 def unit_test_list():
@@ -229,24 +229,6 @@
     tests.add_reftest('failures/unexpected/mismatch.html', 'failures/unexpected/mismatch-expected-mismatch.html', same_image=True)
     tests.add('failures/unexpected/reftest-nopixel.html', actual_checksum=None, actual_image=None, is_reftest=True)
     tests.add('failures/unexpected/reftest-nopixel-expected.html', actual_checksum=None, actual_image=None, is_reftest=True)
-    tests.add('reftests/foo/test.html', is_reftest=True)
-    tests.add('reftests/foo/test-ref.html', is_reftest=True)
-
-    tests.add('reftests/foo/multiple-match-success.html', actual_checksum='abc', actual_image='abc', is_reftest=True)
-    tests.add('reftests/foo/multiple-match-failure.html', actual_checksum='abc', actual_image='abc', is_reftest=True)
-    tests.add('reftests/foo/multiple-mismatch-success.html', actual_checksum='abc', actual_image='abc', is_reftest=True)
-    tests.add('reftests/foo/multiple-mismatch-failure.html', actual_checksum='abc', actual_image='abc', is_reftest=True)
-    tests.add('reftests/foo/multiple-both-success.html', actual_checksum='abc', actual_image='abc', is_reftest=True)
-    tests.add('reftests/foo/multiple-both-failure.html', actual_checksum='abc', actual_image='abc', is_reftest=True)
-
-    tests.add('reftests/foo/matching-ref.html', actual_checksum='abc', actual_image='abc', is_reftest=True)
-    tests.add('reftests/foo/mismatching-ref.html', actual_checksum='def', actual_image='def', is_reftest=True)
-    tests.add('reftests/foo/second-mismatching-ref.html', actual_checksum='ghi', actual_image='ghi', is_reftest=True)
-
-    # The following files shouldn't be treated as reftests
-    tests.add_reftest('reftests/foo/unlistedtest.html', 'reftests/foo/unlistedtest-expected.html', same_image=True)
-    tests.add('reftests/foo/reference/bar/common.html', is_reftest=True)
-    tests.add('reftests/foo/reftest/bar/shared.html', is_reftest=True)
 
     tests.add('websocket/tests/passes/text.html')
 
@@ -320,26 +302,6 @@
 Bug(test) failures/expected/device_failure.html [ WontFix ]
 """)
 
-    filesystem.maybe_make_directory(LAYOUT_TEST_DIR + '/reftests/foo')
-    filesystem.write_text_file(LAYOUT_TEST_DIR + '/reftests/foo/reftest.list', """
-== test.html test-ref.html
-
-== multiple-match-success.html mismatching-ref.html
-== multiple-match-success.html matching-ref.html
-== multiple-match-failure.html mismatching-ref.html
-== multiple-match-failure.html second-mismatching-ref.html
-!= multiple-mismatch-success.html mismatching-ref.html
-!= multiple-mismatch-success.html second-mismatching-ref.html
-!= multiple-mismatch-failure.html mismatching-ref.html
-!= multiple-mismatch-failure.html matching-ref.html
-== multiple-both-success.html matching-ref.html
-== multiple-both-success.html mismatching-ref.html
-!= multiple-both-success.html second-mismatching-ref.html
-== multiple-both-failure.html matching-ref.html
-!= multiple-both-failure.html second-mismatching-ref.html
-!= multiple-both-failure.html matching-ref.html
-""")
-
     # FIXME: This test was only being ignored because of missing a leading '/'.
     # Fixing the typo causes several tests to assert, so disabling the test entirely.
     # Add in a file should be ignored by port.find_test_files().
diff --git a/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py b/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py
index 04ba3d3b..e8cd9081 100644
--- a/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py
+++ b/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py
@@ -919,14 +919,6 @@
         tests_run = get_test_results(['passes/mismatch.html'])
         self.assertEqual(tests_run[0].references, ['passes/mismatch-expected-mismatch.html'])
 
-    def test_reftest_when_not_listed_in_reftestlist(self):
-        host = MockHost()
-        logging_run(['--no-show-results', 'reftests/foo/'], tests_included=True, host=host)
-        results = parse_full_results(host.filesystem.read_text_file('/tmp/layout-test-results/full_results.json'))
-        self.assertEqual(results['tests']['reftests']['foo']['unlistedtest.html']['actual'], 'MISSING MISSING MISSING MISSING')
-        self.assertEqual(results['num_regressions'], 5)
-        self.assertEqual(results['num_flaky'], 0)
-
     def test_reftest_crash(self):
         test_results = get_test_results(['failures/unexpected/crash-reftest.html'])
         # The list of references should be empty since the test crashed and we didn't run any references.
@@ -1203,49 +1195,6 @@
         self.assertEqual(json.loads(json_failing_test_results), details.summarized_failing_results)
 
 
-class EndToEndTest(unittest.TestCase):
-
-    def test_reftest_with_two_notrefs(self):
-        # Test that we update expectations in place. If the expectation
-        # is missing, update the expected generic location.
-        host = MockHost()
-        logging_run(['--no-show-results', 'reftests/foo/'], tests_included=True, host=host)
-
-        json_string = host.filesystem.read_text_file('/tmp/layout-test-results/failing_results.json')
-        results = parse_full_results(json_string)
-        self.assertNotIn('multiple-match-success.html', results['tests']['reftests']['foo'])
-        self.assertNotIn('multiple-mismatch-success.html', results['tests']['reftests']['foo'])
-        self.assertNotIn('multiple-both-success.html', results['tests']['reftests']['foo'])
-
-        self.assertEqual(
-            results['tests']['reftests']['foo']['multiple-match-failure.html'],
-            {
-                'expected': 'PASS',
-                'actual': 'IMAGE IMAGE IMAGE IMAGE',
-                'reftest_type': ['=='],
-                'is_regression': True,
-                'is_unexpected': True,
-            })
-        self.assertEqual(
-            results['tests']['reftests']['foo']['multiple-mismatch-failure.html'],
-            {
-                'expected': 'PASS',
-                'actual': 'IMAGE IMAGE IMAGE IMAGE',
-                'reftest_type': ['!='],
-                'is_regression': True,
-                'is_unexpected': True,
-            })
-        self.assertEqual(
-            results['tests']['reftests']['foo']['multiple-both-failure.html'],
-            {
-                'expected': 'PASS',
-                'actual': 'IMAGE IMAGE IMAGE IMAGE',
-                'reftest_type': ['==', '!='],
-                'is_regression': True,
-                'is_unexpected': True,
-            })
-
-
 class RebaselineTest(unittest.TestCase, StreamTestingMixin):
     """Tests for flags which cause new baselines to be written.
 
diff --git a/third_party/espresso/BUILD.gn b/third_party/espresso/BUILD.gn
index fdbb4f0..1bd0ec65 100644
--- a/third_party/espresso/BUILD.gn
+++ b/third_party/espresso/BUILD.gn
@@ -20,6 +20,9 @@
   jar_path = "lib/espresso-contrib-release-no-dep.jar"
   deps = [
     ":espresso_core_java",
+    "//third_party/accessibility_test_framework:accessibility_test_framework_java",
+    "//third_party/android_deps:android_support_v4_java",
+    "//third_party/android_deps:com_android_support_design_java",
     "//third_party/guava:guava_android_java",
     "//third_party/hamcrest:hamcrest_core_java",
   ]
@@ -29,10 +32,13 @@
   testonly = true
   jar_path = "lib/espresso-core-release-no-dep.jar"
   deps = [
+    ":espresso_idling_java",
     "//third_party/android_deps:android_support_annotations_java",
     "//third_party/android_deps:javax_inject_javax_inject_java",
+    "//third_party/android_support_test_runner:runner_java",
     "//third_party/guava:guava_android_java",
     "//third_party/hamcrest:hamcrest_core_java",
+    "//third_party/hamcrest:hamcrest_library_java",
   ]
 }
 
@@ -45,6 +51,8 @@
   testonly = true
   jar_path = "lib/espresso-intents-release-no-dep.jar"
   deps = [
+    ":espresso_core_java",
+    "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/guava:guava_android_java",
     "//third_party/hamcrest:hamcrest_core_java",
diff --git a/third_party/google-truth/BUILD.gn b/third_party/google-truth/BUILD.gn
index 1cc0104..5ba44ac 100644
--- a/third_party/google-truth/BUILD.gn
+++ b/third_party/google-truth/BUILD.gn
@@ -7,5 +7,13 @@
 java_prebuilt("google_truth_java") {
   testonly = true
   supports_android = true
+
+  # requires_android because of dependency on guava_android.
+  requires_android = true
+
   jar_path = "lib/truth-0.40.jar"
+  deps = [
+    "//third_party/guava:guava_android_java",
+    "//third_party/junit:junit",
+  ]
 }
diff --git a/third_party/gvr-android-sdk/BUILD.gn b/third_party/gvr-android-sdk/BUILD.gn
index a35626ed..1b7b72b 100644
--- a/third_party/gvr-android-sdk/BUILD.gn
+++ b/third_party/gvr-android-sdk/BUILD.gn
@@ -6,11 +6,16 @@
 import("//build/config/c++/c++.gni")
 
 android_aar_prebuilt("controller_test_api_java") {
+  testonly = true
   aar_path = "test-libraries/controller_test_api.aar"
   proguard_configs = [ "test-libraries/proguard.txt" ]
 
   # Jar includes conflicting copies of Desugar-runtime.jar classes.
   jar_excluded_patterns = [ "*ThrowableExtension*.class" ]
+  deps = [
+    ":gvr_common_java",
+    "//third_party/guava:guava_android_java",
+  ]
 }
 
 android_aar_prebuilt("gvr_common_java") {
diff --git a/third_party/hamcrest/BUILD.gn b/third_party/hamcrest/BUILD.gn
index b0ec15aca..548ba22 100644
--- a/third_party/hamcrest/BUILD.gn
+++ b/third_party/hamcrest/BUILD.gn
@@ -8,7 +8,6 @@
   testonly = true
   deps = [
     ":hamcrest_core_java",
-    ":hamcrest_integration_java",
     ":hamcrest_library_java",
   ]
 }
@@ -20,16 +19,12 @@
   proguard_configs = [ "//third_party/hamcrest/proguard.flags" ]
 }
 
-java_prebuilt("hamcrest_integration_java") {
-  supports_android = true
-  testonly = true
-  jar_path = "lib/hamcrest-integration.jar"
-  proguard_configs = [ "//third_party/hamcrest/proguard.flags" ]
-}
-
 java_prebuilt("hamcrest_library_java") {
   supports_android = true
   testonly = true
   jar_path = "lib/hamcrest-library.jar"
   proguard_configs = [ "//third_party/hamcrest/proguard.flags" ]
+  deps = [
+    ":hamcrest_core_java",
+  ]
 }
diff --git a/third_party/hamcrest/cipd.yaml b/third_party/hamcrest/cipd.yaml
index 67c4754..70964ce 100644
--- a/third_party/hamcrest/cipd.yaml
+++ b/third_party/hamcrest/cipd.yaml
@@ -8,5 +8,4 @@
 description: hamcrest Java library
 data:
   - file: lib/hamcrest-core.jar
-  - file: lib/hamcrest-integration.jar
   - file: lib/hamcrest-library.jar
diff --git a/third_party/harfbuzz-ng/BUILD.gn b/third_party/harfbuzz-ng/BUILD.gn
index ee3d8bfc..6bf38eea 100644
--- a/third_party/harfbuzz-ng/BUILD.gn
+++ b/third_party/harfbuzz-ng/BUILD.gn
@@ -55,6 +55,7 @@
       "src/src/hb-buffer.cc",
       "src/src/hb-buffer.h",
       "src/src/hb-buffer.hh",
+      "src/src/hb-cache.hh",
       "src/src/hb-common.cc",
       "src/src/hb-common.h",
       "src/src/hb-debug.hh",
@@ -123,7 +124,6 @@
       "src/src/hb-ot-shape-complex-myanmar.cc",
       "src/src/hb-ot-shape-complex-myanmar.hh",
       "src/src/hb-ot-shape-complex-thai.cc",
-      "src/src/hb-ot-shape-complex-tibetan.cc",
       "src/src/hb-ot-shape-complex-use-machine.hh",
       "src/src/hb-ot-shape-complex-use-table.cc",
       "src/src/hb-ot-shape-complex-use.cc",
@@ -164,7 +164,7 @@
       "src/src/hb-subset-plan.hh",
       "src/src/hb-subset.h",
       "src/src/hb-subset.hh",
-      "src/src/hb-subset.hh",
+      "src/src/hb-unicode-emoji-table.hh",
       "src/src/hb-unicode.cc",
       "src/src/hb-unicode.h",
       "src/src/hb-unicode.hh",
diff --git a/third_party/harfbuzz-ng/README.chromium b/third_party/harfbuzz-ng/README.chromium
index adc04e72..3cbc9ccf 100644
--- a/third_party/harfbuzz-ng/README.chromium
+++ b/third_party/harfbuzz-ng/README.chromium
@@ -1,9 +1,9 @@
 Name: harfbuzz-ng
 Short Name: harfbuzz-ng
 URL: http://harfbuzz.org
-Version: 1.9.0
-Date: 20180910
-Revision: 54d332dd9b0263821376161cdffb60ffb3c7847f
+Version: 1.9.0.1f14107f7
+Date: 20181005
+Revision: 1f14107f71a6c3da8270ed21c3588f945fa91733
 Security Critical: yes
 License: MIT
 License File: src/COPYING
diff --git a/third_party/libsync/BUILD.gn b/third_party/libsync/BUILD.gn
index feedae6..6db1a51 100644
--- a/third_party/libsync/BUILD.gn
+++ b/third_party/libsync/BUILD.gn
@@ -27,7 +27,7 @@
 
   source_set("libsync") {
     sources = [
-      "src/include/sync.h",
+      "src/include/sync/sync.h",
       "src/sw_sync.h",
       "src/sync.c",
       "strlcpy.c",
diff --git a/third_party/libxslt/BUILD.gn b/third_party/libxslt/BUILD.gn
index de06534..5490b18 100644
--- a/third_party/libxslt/BUILD.gn
+++ b/third_party/libxslt/BUILD.gn
@@ -68,7 +68,7 @@
     "src/libxslt/xsltlocale.h",
     "src/libxslt/xsltutils.c",
     "src/libxslt/xsltutils.h",
-    "src/libxslt/xsltwin32config.h",
+    "src/libxslt/xsltwin32config.h.in",
     "win32/config.h",
   ]
 
diff --git a/third_party/robolectric/BUILD.gn b/third_party/robolectric/BUILD.gn
index e6db50c..cc7f815 100644
--- a/third_party/robolectric/BUILD.gn
+++ b/third_party/robolectric/BUILD.gn
@@ -47,6 +47,10 @@
 
 java_library("robolectric_java") {
   testonly = true
+
+  # Skip platform checks since we must depend on shadows_core_java here which
+  # depends on targets that requires_android.
+  bypass_platform_checks = true
   deps = [
     ":android-all-8.1.0-robolectric-r4402310_java",
     ":robolectric_annotations_java",
@@ -354,6 +358,10 @@
 java_library("shadows_core_java") {
   output_name = "shadows-core-3.2"
 
+  # Skip platform checks since we must depend on accessibility_test_framework_java
+  # here which requires_android.
+  bypass_platform_checks = true
+
   # TODO(mikecase): Remove this once crbug.com/638875 is fixed.
   enable_incremental_javac_override = false
 
diff --git a/third_party/ub-uiautomator/BUILD.gn b/third_party/ub-uiautomator/BUILD.gn
index cf60466..74f89a5 100644
--- a/third_party/ub-uiautomator/BUILD.gn
+++ b/third_party/ub-uiautomator/BUILD.gn
@@ -7,4 +7,9 @@
 android_java_prebuilt("ub_uiautomator_java") {
   testonly = true
   jar_path = "lib/ub-uiautomator.jar"
+  deps = [
+    "//third_party/android_tools:android_test_base_java",
+    "//third_party/android_tools:android_test_runner_java",
+    "//third_party/junit:junit",
+  ]
 }
diff --git a/third_party/webrtc_overrides/BUILD.gn b/third_party/webrtc_overrides/BUILD.gn
index 731dd68e..1a1a480 100644
--- a/third_party/webrtc_overrides/BUILD.gn
+++ b/third_party/webrtc_overrides/BUILD.gn
@@ -26,9 +26,7 @@
   # ":field_trial".
   # This configuration happens here:
   # https://cs.chromium.org/chromium/src/third_party/webrtc/webrtc.gni?l=44-51&rcl=95c56eebe0a2b31ad5752138d15b431124e17d36
-  if (is_nacl) {
-    deps += [ "//native_client_sdk/src/libraries/nacl_io" ]
-  } else if (!is_chromecast) {
+  if (!is_chromecast && !is_nacl) {
     # When Chromium doesn't build for NaCL or Chromecast, WebRTC doesn't
     # provide an implementation for field_trial and a custom one (that uses
     # base/metrics/field_trial.h is provided).
diff --git a/tools/determinism/compare_build_artifacts.py b/tools/determinism/compare_build_artifacts.py
index 4dac137..f8f2ba3 100755
--- a/tools/determinism/compare_build_artifacts.py
+++ b/tools/determinism/compare_build_artifacts.py
@@ -154,6 +154,19 @@
   return result
 
 
+def memoize(f):
+  memo = {}
+  def helper(*args):
+    if args not in memo:
+      memo[args] = f(*args)
+    return memo[args]
+  return helper
+
+
+# compare_deps() can be called with different targets that all depend on
+# "all" targets, so memoize the results of this function to make sure we
+# don't compare "all" files more than once.
+@memoize
 def compare_files(first_filepath, second_filepath):
   """Compares two binaries and return the number of differences between them.
 
diff --git a/tools/determinism/deterministic_build_whitelist.pyl b/tools/determinism/deterministic_build_whitelist.pyl
index 61cef57..30a24416 100644
--- a/tools/determinism/deterministic_build_whitelist.pyl
+++ b/tools/determinism/deterministic_build_whitelist.pyl
@@ -11,8 +11,6 @@
 # List of files that are known to be _not_ deterministic. This is a "temporary"
 # workaround to find regression on the deterministic builders.
 #
-# PNaCl general bug: https://crbug.com/429358
-#
 # TODO(sebmarchand): Remove this once all the files are deterministic.
 {
   # https://crbug.com/383340
@@ -29,11 +27,8 @@
 
   # https://crbug.com/330263
   'linux': [
-    'ppapi_nacl_tests_pnacl_newlib_x64.nexe',
     'zucchini_apply_fuzzer_seed_corpus.zip',
 
-    'browser_tests.isolated',
-
     'mr_extension',
     'mr_extension/release',
   ],
@@ -173,9 +168,6 @@
     'webkit_unit_tests',
     'wtf_unittests',
 
-    # TODO(tikuta): Remove this when crbug.com/870611 is fixed.
-    'remoting-webapp.v2.zip',
-
     # TODO(thakis): Given we list most executables above, we should probably
     # also list most isolated files here.
     'angle_unittests.isolated',
@@ -190,25 +182,15 @@
 
   # https://crbug.com/330260
   'win': [
-    # TODO(thakis): Figure out what's up with these three.
+    # TODO(thakis): Figure out what's up with these three (and their isolate).
     'mini_installer.exe',
     'mini_installer.exe.pdb',
     'previous_version_mini_installer.exe',
+    'mini_installer_tests.isolated',
 
     # These probably have mtimes in the zip headers and the scripts creating
     # them probably should use build_utils.ZipDir() instead.
     'mini_installer_tests.zip',
     'policy_templates.zip',
-
-    # TODO(tikuta): Remove this when crbug.com/870611 is fixed.
-    'remoting-me2me-host-win.zip',
-    'remoting-webapp.v2.zip',
-
-    # TODO(thakis): Figure out what's up with these two.
-    'ppapi_nacl_tests_pnacl_newlib_x32.nexe',
-    'ppapi_nacl_tests_pnacl_newlib_x64.nexe',
-
-    'browser_tests.isolated',
-    'mini_installer_tests.isolated',
   ],
 }
diff --git a/tools/gen_keyboard_overlay_data/gen_keyboard_overlay_data.py b/tools/gen_keyboard_overlay_data/gen_keyboard_overlay_data.py
deleted file mode 100755
index 073ef0b..0000000
--- a/tools/gen_keyboard_overlay_data/gen_keyboard_overlay_data.py
+++ /dev/null
@@ -1,515 +0,0 @@
-#!/usr/bin/env python
-# 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.
-
-"""Generate keyboard layout and hotkey data for the keyboard overlay.
-
-This script fetches data from the keyboard layout and hotkey data spreadsheet,
-and output the data depending on the option.
-
-  --cc: Rewrites a part of C++ code in
-      chrome/browser/chromeos/webui/keyboard_overlay_ui.cc
-
-  --grd: Rewrites a part of grd messages in
-      chrome/app/generated_resources.grd
-
-  --js: Rewrites the entire JavaScript code in
-      chrome/browser/resources/keyboard_overlay/keyboard_overlay_data.js
-
-These options can be specified at the same time.
-
-e.g.
-python gen_keyboard_overlay_data.py --cc --grd --js
-
-The output directory of the generated files can be changed with --outdir.
-
-e.g. (This will generate tmp/keyboard_overlay.js)
-python gen_keyboard_overlay_data.py --outdir=tmp --js
-"""
-
-import cStringIO
-import datetime
-import gdata.spreadsheet.service
-import getpass
-import json
-import optparse
-import os
-import re
-import sys
-
-MODIFIER_SHIFT = 1 << 0
-MODIFIER_CTRL = 1 << 1
-MODIFIER_ALT = 1 << 2
-
-KEYBOARD_GLYPH_SPREADSHEET_KEY = '0Ao3KldW9piwEdExLbGR6TmZ2RU9aUjFCMmVxWkVqVmc'
-HOTKEY_SPREADSHEET_KEY = '0AqzoqbAMLyEPdE1RQXdodk1qVkFyTWtQbUxROVM1cXc'
-CC_OUTDIR = 'chrome/browser/ui/webui/chromeos'
-CC_FILENAME = 'keyboard_overlay_ui.cc'
-GRD_OUTDIR = 'chrome/app'
-GRD_FILENAME = 'chromeos_strings.grdp'
-JS_OUTDIR = 'chrome/browser/resources/chromeos'
-JS_FILENAME = 'keyboard_overlay_data.js'
-CC_START = r'IDS_KEYBOARD_OVERLAY_INSTRUCTIONS_HIDE },'
-CC_END = r'};'
-GRD_START = r'  <!-- BEGIN GENERATED KEYBOARD OVERLAY STRINGS -->'
-GRD_END = r'  <!-- END GENERATED KEYBOARD OVERLAY STRINGS -->'
-
-LABEL_MAP = {
-  'glyph_arrow_down': 'down',
-  'glyph_arrow_left': 'left',
-  'glyph_arrow_right': 'right',
-  'glyph_arrow_up': 'up',
-  'glyph_back': 'back',
-  'glyph_backspace': 'backspace',
-  'glyph_brightness_down': 'bright down',
-  'glyph_brightness_up': 'bright up',
-  'glyph_enter': 'enter',
-  'glyph_forward': 'forward',
-  'glyph_fullscreen': 'full screen',
-  # Kana/Eisu key on Japanese keyboard
-  'glyph_ime': u'\u304b\u306a\u0020\u002f\u0020\u82f1\u6570',
-  'glyph_lock': 'lock',
-  'glyph_overview': 'switch window',
-  'glyph_power': 'power',
-  'glyph_right': 'right',
-  'glyph_reload': 'reload',
-  'glyph_search': 'search',
-  'glyph_shift': 'shift',
-  'glyph_tab': 'tab',
-  'glyph_tools': 'tools',
-  'glyph_volume_down': 'vol. down',
-  'glyph_volume_mute': 'mute',
-  'glyph_volume_up': 'vol. up',
-};
-
-INPUT_METHOD_ID_TO_OVERLAY_ID = {
-  'xkb:be::fra': 'fr',
-  'xkb:be::ger': 'de',
-  'xkb:be::nld': 'nl',
-  'xkb:bg::bul': 'bg',
-  'xkb:bg:phonetic:bul': 'bg',
-  'xkb:br::por': 'pt_BR',
-  'xkb:ca::fra': 'fr_CA',
-  'xkb:ca:eng:eng': 'ca',
-  'xkb:ch::ger': 'de',
-  'xkb:ch:fr:fra': 'fr',
-  'xkb:cz::cze': 'cs',
-  'xkb:de::ger': 'de',
-  'xkb:de:neo:ger': 'de_neo',
-  'xkb:dk::dan': 'da',
-  'xkb:ee::est': 'et',
-  'xkb:es::spa': 'es',
-  'xkb:es:cat:cat': 'ca',
-  'xkb:fi::fin': 'fi',
-  'xkb:fr::fra': 'fr',
-  'xkb:gb:dvorak:eng': 'en_GB_dvorak',
-  'xkb:gb:extd:eng': 'en_GB',
-  'xkb:gr::gre': 'el',
-  'xkb:hr::scr': 'hr',
-  'xkb:hu::hun': 'hu',
-  'xkb:il::heb': 'iw',
-  'xkb:it::ita': 'it',
-  'xkb:jp::jpn': 'ja',
-  'xkb:latam::spa': 'es_419',
-  'xkb:lt::lit': 'lt',
-  'xkb:lv:apostrophe:lav': 'lv',
-  'xkb:no::nob': 'no',
-  'xkb:pl::pol': 'pl',
-  'xkb:pt::por': 'pt_PT',
-  'xkb:ro::rum': 'ro',
-  'xkb:rs::srp': 'sr',
-  'xkb:ru::rus': 'ru',
-  'xkb:ru:phonetic:rus': 'ru',
-  'xkb:se::swe': 'sv',
-  'xkb:si::slv': 'sl',
-  'xkb:sk::slo': 'sk',
-  'xkb:tr::tur': 'tr',
-  'xkb:ua::ukr': 'uk',
-  'xkb:us::eng': 'en_US',
-  'xkb:us::fil': 'en_US',
-  'xkb:us::ind': 'en_US',
-  'xkb:us::msa': 'en_US',
-  'xkb:us:altgr-intl:eng': 'en_US_altgr_intl',
-  'xkb:us:colemak:eng': 'en_US_colemak',
-  'xkb:us:dvorak:eng': 'en_US_dvorak',
-  'xkb:us:intl:eng': 'en_US_intl',
-  'xkb:us:intl:nld': 'en_US_intl',
-  'xkb:us:intl:por': 'en_US_intl',
-  'xkb:us:workman:eng': 'en_US_workman',
-  'xkb:us:workman-intl:eng': 'en_US_workman_intl',
-}
-
-# The file was first generated in 2012 and we have a policy of not updating
-# copyright dates.
-COPYRIGHT_HEADER=\
-"""// 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.
-
-// This is a generated file but may contain local modifications. See
-// src/tools/gen_keyboard_overlay_data/gen_keyboard_overlay_data.py --help
-"""
-
-# A snippet for grd file
-GRD_SNIPPET_TEMPLATE="""  <message name="%s" desc="%s">
-    %s
-  </message>
-"""
-
-# A snippet for C++ file
-CC_SNIPPET_TEMPLATE="""  { "%s", %s },
-"""
-
-
-def SplitBehavior(behavior):
-  """Splits the behavior to compose a message or i18n-content value.
-
-  Examples:
-    'Activate last tab' => ['Activate', 'last', 'tab']
-    'Close tab' => ['Close', 'tab']
-  """
-  return [x for x in re.split('[ ()"-.,]', behavior) if len(x) > 0]
-
-
-def ToMessageName(behavior):
-  """Composes a message name for grd file.
-
-  Examples:
-    'Activate last tab' => IDS_KEYBOARD_OVERLAY_ACTIVATE_LAST_TAB
-    'Close tab' => IDS_KEYBOARD_OVERLAY_CLOSE_TAB
-  """
-  segments = [segment.upper() for segment in SplitBehavior(behavior)]
-  return 'IDS_KEYBOARD_OVERLAY_' + ('_'.join(segments))
-
-
-def ToMessageDesc(description):
-  """Composes a message description for grd file."""
-  message_desc = 'The text in the keyboard overlay to explain the shortcut'
-  if description:
-    message_desc = '%s (%s).' % (message_desc, description)
-  else:
-    message_desc += '.'
-  return message_desc
-
-
-def Toi18nContent(behavior):
-  """Composes a i18n-content value for HTML/JavaScript files.
-
-  Examples:
-    'Activate last tab' => keyboardOverlayActivateLastTab
-    'Close tab' => keyboardOverlayCloseTab
-  """
-  segments = [segment.lower() for segment in SplitBehavior(behavior)]
-  result = 'keyboardOverlay'
-  for segment in segments:
-    result += segment[0].upper() + segment[1:]
-  return result
-
-
-def ToKeys(hotkey):
-  """Converts the action value to shortcut keys used from JavaScript.
-
-  Examples:
-    'Ctrl - 9' => '9<>CTRL'
-    'Ctrl - Shift - Tab' => 'tab<>CTRL<>SHIFT'
-  """
-  values = hotkey.split(' - ')
-  modifiers = sorted(value.upper() for value in values
-                     if value in ['Shift', 'Ctrl', 'Alt', 'Search'])
-  keycode = [value.lower() for value in values
-             if value not in ['Shift', 'Ctrl', 'Alt', 'Search']]
-  # The keys which are highlighted even without modifier keys.
-  base_keys = ['backspace', 'power']
-  if not modifiers and (keycode and keycode[0] not in base_keys):
-    return None
-  return '<>'.join(keycode + modifiers)
-
-
-def ParseOptions():
-  """Parses the input arguemnts and returns options."""
-  # default_username = os.getusername() + '@google.com';
-  default_username = '%s@google.com' % os.environ.get('USER')
-  parser = optparse.OptionParser()
-  parser.add_option('--key', dest='key',
-                    help='The key of the spreadsheet (required).')
-  parser.add_option('--username', dest='username',
-                    default=default_username,
-                    help='Your user name (default: %s).' % default_username)
-  parser.add_option('--password', dest='password',
-                    help='Your password.')
-  parser.add_option('--account_type', default='GOOGLE', dest='account_type',
-                    help='Account type used for gdata login (default: GOOGLE)')
-  parser.add_option('--js', dest='js', default=False, action='store_true',
-                    help='Output js file.')
-  parser.add_option('--grd', dest='grd', default=False, action='store_true',
-                    help='Output resource file.')
-  parser.add_option('--cc', dest='cc', default=False, action='store_true',
-                    help='Output cc file.')
-  parser.add_option('--outdir', dest='outdir', default=None,
-                    help='Specify the directory files are generated.')
-  (options, unused_args) = parser.parse_args()
-
-  if not options.username.endswith('google.com'):
-    print 'google.com account is necessary to use this script.'
-    sys.exit(-1)
-
-  if (not (options.js or options.grd or options.cc)):
-    print 'Either --js, --grd, or --cc needs to be specified.'
-    sys.exit(-1)
-
-  # Get the password from the terminal, if needed.
-  if not options.password:
-    options.password = getpass.getpass(
-        'Application specific password for %s: ' % options.username)
-  return options
-
-
-def InitClient(options):
-  """Initializes the spreadsheet client."""
-  client = gdata.spreadsheet.service.SpreadsheetsService()
-  client.email = options.username
-  client.password = options.password
-  client.source = 'Spread Sheet'
-  client.account_type = options.account_type
-  print 'Logging in as %s (%s)' % (client.email, client.account_type)
-  client.ProgrammaticLogin()
-  return client
-
-
-def PrintDiffs(message, lhs, rhs):
-  """Prints the differences between |lhs| and |rhs|."""
-  dif = set(lhs).difference(rhs)
-  if dif:
-    print message, ', '.join(dif)
-
-
-def FetchSpreadsheetFeeds(client, key, sheets, cols):
-  """Fetch feeds from the spreadsheet.
-
-  Args:
-    client: A spreadsheet client to be used for fetching data.
-    key: A key string of the spreadsheet to be fetched.
-    sheets: A list of the sheet names to read data from.
-    cols: A list of columns to read data from.
-  """
-  worksheets_feed = client.GetWorksheetsFeed(key)
-  print 'Fetching data from the worksheet: %s' % worksheets_feed.title.text
-  worksheets_data = {}
-  titles = []
-  for entry in worksheets_feed.entry:
-    worksheet_id = entry.id.text.split('/')[-1]
-    list_feed = client.GetListFeed(key, worksheet_id)
-    list_data = []
-    # Hack to deal with sheet names like 'sv (Copy of fl)'
-    title = list_feed.title.text.split('(')[0].strip()
-    titles.append(title)
-    if title not in sheets:
-      continue
-    print 'Reading data from the sheet: %s' % list_feed.title.text
-    for i, entry in enumerate(list_feed.entry):
-      line_data = {}
-      for k in entry.custom:
-        if (k not in cols) or (not entry.custom[k].text):
-          continue
-        line_data[k] = entry.custom[k].text
-      list_data.append(line_data)
-    worksheets_data[title] = list_data
-  PrintDiffs('Exist only on the spreadsheet: ', titles, sheets)
-  PrintDiffs('Specified but do not exist on the spreadsheet: ', sheets, titles)
-  return worksheets_data
-
-
-def FetchKeyboardGlyphData(client):
-  """Fetches the keyboard glyph data from the spreadsheet."""
-  glyph_cols = ['scancode', 'p0', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7',
-                'p8', 'p9', 'label', 'format', 'notes']
-  keyboard_glyph_data = FetchSpreadsheetFeeds(
-      client, KEYBOARD_GLYPH_SPREADSHEET_KEY,
-      INPUT_METHOD_ID_TO_OVERLAY_ID.values(), glyph_cols)
-  ret = {}
-  for lang in keyboard_glyph_data:
-    ret[lang] = {}
-    keys = {}
-    for line in keyboard_glyph_data[lang]:
-      scancode = line.get('scancode')
-      if (not scancode) and line.get('notes'):
-        ret[lang]['layoutName'] = line['notes']
-        continue
-      del line['scancode']
-      if 'notes' in line:
-        del line['notes']
-      if 'label' in line:
-        line['label'] = LABEL_MAP.get(line['label'], line['label'])
-      keys[scancode] = line
-    # Add a label to space key
-    if '39' not in keys:
-      keys['39'] = {'label': 'space'}
-    ret[lang]['keys'] = keys
-  return ret
-
-
-def FetchLayoutsData(client):
-  """Fetches the keyboard glyph data from the spreadsheet."""
-  layout_names = ['U_layout', 'J_layout', 'E_layout', 'B_layout']
-  cols = ['scancode', 'x', 'y', 'w', 'h']
-  layouts = FetchSpreadsheetFeeds(client, KEYBOARD_GLYPH_SPREADSHEET_KEY,
-                                  layout_names, cols)
-  ret = {}
-  for layout_name, layout in layouts.items():
-    ret[layout_name[0]] = []
-    for row in layout:
-      line = []
-      for col in cols:
-        value = row.get(col)
-        if not value:
-          line.append('')
-        else:
-          if col != 'scancode':
-            value = float(value)
-          line.append(value)
-      ret[layout_name[0]].append(line)
-  return ret
-
-
-def FetchHotkeyData(client):
-  """Fetches the hotkey data from the spreadsheet."""
-  hotkey_sheet = ['Cross Platform Behaviors']
-  hotkey_cols = ['behavior', 'context', 'kind', 'actionctrlctrlcmdonmac',
-                 'chromeos', 'descriptionfortranslation']
-  hotkey_data = FetchSpreadsheetFeeds(client, HOTKEY_SPREADSHEET_KEY,
-                                      hotkey_sheet, hotkey_cols)
-  action_to_id = {}
-  id_to_behavior = {}
-  # (behavior, action)
-  result = []
-  for line in hotkey_data['Cross Platform Behaviors']:
-    if (not line.get('chromeos')) or (line.get('kind') != 'Key'):
-      continue
-    action = ToKeys(line['actionctrlctrlcmdonmac'])
-    if not action:
-      continue
-    behavior = line['behavior'].strip()
-    description = line.get('descriptionfortranslation')
-    result.append((behavior, action, description))
-  return result
-
-
-def UniqueBehaviors(hotkey_data):
-  """Retrieves a sorted list of unique behaviors from |hotkey_data|."""
-  return sorted(set((behavior, description) for (behavior, _, description)
-                    in hotkey_data),
-                cmp=lambda x, y: cmp(ToMessageName(x[0]), ToMessageName(y[0])))
-
-
-def GetPath(path_from_src):
-  """Returns the absolute path of the specified path."""
-  path = os.path.join(os.path.dirname(__file__), '../..', path_from_src)
-  if not os.path.isfile(path):
-    print 'WARNING: %s does not exist. Maybe moved or renamed?' % path
-  return path
-
-
-def OutputFile(outpath, snippet):
-  """Output the snippet into the specified path."""
-  out = file(outpath, 'w')
-  out.write(COPYRIGHT_HEADER + '\n')
-  out.write(snippet)
-  print 'Output ' + os.path.normpath(outpath)
-
-
-def RewriteFile(start, end, original_dir, original_filename, snippet,
-                outdir=None):
-  """Replaces a part of the specified file with snippet and outputs it."""
-  original_path = GetPath(os.path.join(original_dir, original_filename))
-  original = file(original_path, 'r')
-  original_content = original.read()
-  original.close()
-  if outdir:
-    outpath = os.path.join(outdir, original_filename)
-  else:
-    outpath = original_path
-  out = file(outpath, 'w')
-  rx = re.compile(r'%s\n.*?%s\n' % (re.escape(start), re.escape(end)),
-                  re.DOTALL)
-  new_content = re.sub(rx, '%s\n%s%s\n' % (start, snippet, end),
-                       original_content)
-  out.write(new_content)
-  out.close()
-  print 'Output ' + os.path.normpath(outpath)
-
-
-def OutputJson(keyboard_glyph_data, hotkey_data, layouts, var_name, outdir):
-  """Outputs the keyboard overlay data as a JSON file."""
-  action_to_id = {}
-  for (behavior, action, _) in hotkey_data:
-    i18nContent = Toi18nContent(behavior)
-    action_to_id[action] = i18nContent
-  data = {'keyboardGlyph': keyboard_glyph_data,
-          'shortcut': action_to_id,
-          'layouts': layouts,
-          'inputMethodIdToOverlayId': INPUT_METHOD_ID_TO_OVERLAY_ID}
-
-  if not outdir:
-    outdir = JS_OUTDIR
-  outpath = GetPath(os.path.join(outdir, JS_FILENAME))
-  json_data =  json.dumps(data, sort_keys=True, indent=2)
-  # Remove redundant spaces after ','
-  json_data = json_data.replace(', \n', ',\n')
-  # Replace double quotes with single quotes to avoid lint warnings.
-  json_data = json_data.replace('\"', '\'')
-  snippet = 'var %s = %s;\n' % (var_name, json_data)
-  OutputFile(outpath, snippet)
-
-
-def OutputGrd(hotkey_data, outdir):
-  """Outputs a part of messages in the grd file."""
-  snippet = cStringIO.StringIO()
-  for (behavior, description) in UniqueBehaviors(hotkey_data):
-    # Do not generate message for 'Show wrench menu'. It is handled manually
-    # based on branding.
-    if behavior == 'Show wrench menu':
-      continue
-    snippet.write(GRD_SNIPPET_TEMPLATE %
-                  (ToMessageName(behavior), ToMessageDesc(description),
-                   behavior))
-
-  RewriteFile(GRD_START, GRD_END, GRD_OUTDIR, GRD_FILENAME, snippet.getvalue(),
-              outdir)
-
-
-def OutputCC(hotkey_data, outdir):
-  """Outputs a part of code in the C++ file."""
-  snippet = cStringIO.StringIO()
-  for (behavior, _) in UniqueBehaviors(hotkey_data):
-    message_name = ToMessageName(behavior)
-    output = CC_SNIPPET_TEMPLATE % (Toi18nContent(behavior), message_name)
-    # Break the line if the line is longer than 80 characters
-    if len(output) > 80:
-      output = output.replace(' ' + message_name, '\n    %s' % message_name)
-    snippet.write(output)
-
-  RewriteFile(CC_START, CC_END, CC_OUTDIR, CC_FILENAME, snippet.getvalue(),
-              outdir)
-
-
-def main():
-  options = ParseOptions()
-  client = InitClient(options)
-  hotkey_data = FetchHotkeyData(client)
-
-  if options.js:
-    keyboard_glyph_data = FetchKeyboardGlyphData(client)
-
-  if options.js:
-    layouts = FetchLayoutsData(client)
-    OutputJson(keyboard_glyph_data, hotkey_data, layouts, 'keyboardOverlayData',
-               options.outdir)
-  if options.grd:
-    OutputGrd(hotkey_data, options.outdir)
-  if options.cc:
-    OutputCC(hotkey_data, options.outdir)
-
-
-if __name__ == '__main__':
-  main()
diff --git a/tools/grit/grit/format/html_inline.py b/tools/grit/grit/format/html_inline.py
index d777019..b196b25 100755
--- a/tools/grit/grit/format/html_inline.py
+++ b/tools/grit/grit/format/html_inline.py
@@ -443,9 +443,9 @@
     # Replace contents of url() for css attributes: content, background,
     # or *-image.
     return re.sub('(content|background|[\w-]*-image):[^;]*' +
-                  '(url\((?P<quote1>"|\'|)[^"\'()]*(?P=quote1)\)|' +
-                      'image-set\(' +
-                          '([ ]*url\((?P<quote2>"|\'|)[^"\'()]*(?P=quote2)\)' +
+                  '(url\((?!\[\[|{{)(?P<quote1>"|\'|)[^"\'()]*(?P=quote1)\)|' +
+                      'image-set\(([ ]*url\((?!\[\[|{{)' +
+                          '(?P<quote2>"|\'|)[^"\'()]*(?P=quote2)\)' +
                               '[ ]*[0-9.]*x[ ]*(,[ ]*)?)+\))',
                   lambda m: InlineCSSUrls(m, filepath),
                   text)
diff --git a/tools/grit/grit/format/html_inline_unittest.py b/tools/grit/grit/format/html_inline_unittest.py
index 5040fa3..25d5fef 100755
--- a/tools/grit/grit/format/html_inline_unittest.py
+++ b/tools/grit/grit/format/html_inline_unittest.py
@@ -58,6 +58,8 @@
       'test.css': '''
       .image {
         background: url('test.png');
+        background-image: url([[ignoreMe]]);
+        background-image: image-set(url({{alsoMe}}), 1x);
       }
       ''',
 
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index eb8105aa..735cf45 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -262,6 +262,7 @@
       'Chromium Linux Goma RBE Staging (dbg) (clobber)': 'debug_bot',
       'Chromium Linux Goma RBE Staging (dbg)': 'debug_bot',
       'Chromium Mac Goma RBE Staging (clobber)': 'release_bot',
+      'Chromium Android ARM 32-bit Goma RBE Staging': 'android_release_bot_minimal_symbols',
     },
 
     'chromium.gpu': {
diff --git a/tools/metrics/actions/README.md b/tools/metrics/actions/README.md
index 7161493..6361c038 100644
--- a/tools/metrics/actions/README.md
+++ b/tools/metrics/actions/README.md
@@ -14,7 +14,8 @@
 ## Coding (Emitting to User Actions)
 
 Generally you'll want to call `base::RecordAction()`, which is defined in
-[https://cs.chromium.org/chromium/src/base/metrics/user_metrics.h](user_metrics.h).
+[user_metrics.h](https://cs.chromium.org/chromium/src/base/metrics/user_metrics.h).
+
 
 ### Emit at a High-Level, not Deep in the Implementation
 
@@ -97,7 +98,7 @@
 excessively (see [advice above](#Do-Not-Emit-Excessively)).
 
 In addition to testing interactively, you can have unit tests check the number
-of times a user action was emitted.  See [user_action_tester.h](https://cs.chromium.org/chromium/src/base/test/user_action_tester.h)
+of times a user action was emitted.  See [user_action_tester.h](https://cs.chromium.org/chromium/src/base/test/metrics/user_action_tester.h)
 for details.
 
 ## Interpreting the Resulting Data
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 2cfe793..a7cfb34c 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -920,6 +920,10 @@
 </action>
 
 <action name="Accel_Show_Keyboard_Overlay">
+  <obsolete>
+    Deprecated 10/2018. The keyboard overlay was removed and replaced by
+    keyboard shortcut viewer.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <description>Please enter the description of this user action.</description>
 </action>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index ab7d76f7..b6223c6 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -9295,6 +9295,18 @@
   <int value="5" label="Failed to query dictionary attack counter"/>
 </enum>
 
+<enum name="CryptAuthSoftwareFeature">
+  <int value="0" label="UNKNOWN_FEATURE"/>
+  <int value="1" label="BETTER_TOGETHER_HOST"/>
+  <int value="2" label="BETTER_TOGETHER_CLIENT"/>
+  <int value="3" label="EASY_UNLOCK_HOST"/>
+  <int value="4" label="EASY_UNLOCK_CLIENT"/>
+  <int value="5" label="MAGIC_TETHER_HOST"/>
+  <int value="6" label="MAGIC_TETHER_CLIENT"/>
+  <int value="7" label="SMS_CONNECT_HOST"/>
+  <int value="8" label="SMS_CONNECT_CLIENT"/>
+</enum>
+
 <enum name="CryptohomeChecksumStatus">
   <int value="0" label="Checksum OK"/>
   <int value="1" label="Checksum does not exist"/>
@@ -11678,6 +11690,7 @@
   <int value="8" label="Has percent encoded strings"/>
   <int value="9" label="Has RFC 2047 encoded strings"/>
   <int value="10" label="Has 'name' attribute only (Obsolete 04/2015)"/>
+  <int value="11" label="Filename is a single quoted string"/>
 </enum>
 
 <enum name="DownloadContentType">
@@ -15210,6 +15223,19 @@
   <int value="6" label="EXPECT_CT_HEADER_PROCESSED"/>
 </enum>
 
+<!-- This must be kept current with explore_sites.h's
+     ExploreSitesCatalogError. -->
+
+<enum name="ExploreSitesCatalogError">
+  <int value="0" label="Parse Failed"/>
+  <int value="1" label="Category with no title"/>
+  <int value="2" label="Categoy with unknown type"/>
+  <int value="3" label="Category with no sites"/>
+  <int value="4" label="Site with bad URL"/>
+  <int value="5" label="Site with missing title"/>
+  <int value="6" label="Site with missing icon"/>
+</enum>
+
 <!-- This must be kept current with
      //chrome/browser/android/explore_sites/catalog.proto's CategoryType. -->
 
@@ -29649,6 +29675,7 @@
   <int value="-696693295" label="Canvas2DImageChromium:disabled"/>
   <int value="-684900739" label="disable-merge-key-char-events"/>
   <int value="-684223908" label="enable-android-wallpapers-app"/>
+  <int value="-683306022" label="OverscrollHistoryNavigation:enabled"/>
   <int value="-680787130" label="ExperimentalVRFeatures:disabled"/>
   <int value="-680589442" label="MacRTL:disabled"/>
   <int value="-674804217" label="SoleIntegration:enabled"/>
@@ -31099,6 +31126,7 @@
   <int value="2129251171" label="memlog-sampling"/>
   <int value="2129929643" label="enable-use-zoom-for-dsf"/>
   <int value="2134480727" label="MediaSessionAccelerators:disabled"/>
+  <int value="2135408204" label="OverscrollHistoryNavigation:disabled"/>
   <int value="2137347307" label="enable-drive-apps-in-app-list"/>
   <int value="2137599770" label="enable-win32k-renderer-lockdown"/>
   <int value="2138146331" label="OmniboxVoiceSearchAlwaysVisible:enabled"/>
@@ -33693,6 +33721,13 @@
   <int value="2" label="SUCCESS">All operations succeeded.</int>
 </enum>
 
+<enum name="MultiDevice_PostOOBESetupFlow_Page">
+  <int value="0" label="Unknown page"/>
+  <int value="1" label="Start page"/>
+  <int value="2" label="Password page"/>
+  <int value="3" label="Success page"/>
+</enum>
+
 <enum name="MultiDeviceSetupNotification">
   <int value="0" label="New user, potential host exists"/>
   <int value="1" label="Existing user, host switched"/>
@@ -41071,6 +41106,7 @@
   <int value="0" label="Path suffix"/>
   <int value="1" label="Navigation to Previews Domain"/>
   <int value="2" label="Navigation to Private Domain"/>
+  <int value="3" label="The requested host was blacklisted by the server"/>
 </enum>
 
 <enum name="PreviewsServerLitePageIneligibleReason">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 468c6a7..0beae34b 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -8745,7 +8745,7 @@
 </histogram>
 
 <histogram name="Blink.Canvas.IsComposited" enum="BooleanSuccess"
-    expires_after="2018-12-31">
+    expires_after="2020-10-24">
   <owner>aaronhk@chromium.org</owner>
   <owner>fserb@chromium.org</owner>
   <summary>
@@ -8754,6 +8754,15 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.Canvas.NumCanvasesPerPage" units="canvases"
+    expires_after="2020-8-30">
+  <owner>aaronhk@chromium.org</owner>
+  <owner>fserb@chromium.org</owner>
+  <summary>
+    When a Document is shutdown, reports the number of canvases on that page.
+  </summary>
+</histogram>
+
 <histogram name="Blink.Canvas.OffscreenCommitTimer" units="microseconds">
   <owner>junov@chromium.org</owner>
   <owner>xidachen@chromium.org</owner>
@@ -8798,6 +8807,16 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.Canvas.SqrtNumberOfPixels" units="sqrt(pixels)"
+    expires_after="2020-8-30">
+  <owner>aaronhk@chromium.org</owner>
+  <owner>fserb@chromium.org</owner>
+  <summary>
+    Stores the square root of the number of pixels in a new or resized offscreen
+    canvas. Emitted from CanvasRenderingContextHost::RecordCanvasSizeToUMA.
+  </summary>
+</histogram>
+
 <histogram name="Blink.Canvas.ToBlob.CompleteEncodingDelay"
     units="microseconds">
   <owner>junov@chromium.org</owner>
@@ -9553,12 +9572,13 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.OffscreenCanvas.SqrtNumberOfPixels" units="sqrt(pixels)">
+<histogram name="Blink.OffscreenCanvas.SqrtNumberOfPixels" units="sqrt(pixels)"
+    expires_after="2020-8-30">
   <owner>aaronhk@chromium.org</owner>
   <owner>fserb@chromium.org</owner>
   <summary>
     Stores the square root of the number of pixels in a new or resized offscreen
-    canvas.
+    canvas. Emitted from CanvasRenderingContextHost::RecordCanvasSizeToUMA.
   </summary>
 </histogram>
 
@@ -9774,7 +9794,7 @@
 </histogram>
 
 <histogram name="Blink.Script.SchedulingType" enum="ScriptSchedulingType"
-    expires_after="2018-12-31">
+    expires_after="2020-10-24">
   <owner>kouhei@chromium.org</owner>
   <owner>hiroshige@chromium.org</owner>
   <summary>
@@ -11347,7 +11367,9 @@
 </histogram>
 
 <histogram name="Browser.Responsiveness.JankyIntervalsPerThirtySeconds"
-    units="janks" expires_after="M75">
+    units="janks">
+<!-- Name completed by histogram_suffixes name="JankyIntervalsPerThirtySeconds" -->
+
   <owner>erikchen@chromium.org</owner>
   <owner>tdresser@chromium.org</owner>
   <summary>
@@ -16782,7 +16804,19 @@
   <summary>
     Indicates that there was no issue retrieving supported and enabled software
     features, or that there were enabled features which were not in the
-    supported feature set.
+    supported feature set. See the histogram
+    &quot;CryptAuth.DeviceSyncSoftwareFeaturesResult.Failures&quot; for a
+    breakdown of failures by feature type.
+  </summary>
+</histogram>
+
+<histogram name="CryptAuth.DeviceSyncSoftwareFeaturesResult.Failures"
+    enum="CryptAuthSoftwareFeature">
+  <owner>hansberry@chromium.org</owner>
+  <summary>
+    Breaks down by feature type
+    &quot;CryptAuth.DeviceSyncSoftwareFeaturesResult&quot;'s count of enabled
+    features which were not in the supported feature set.
   </summary>
 </histogram>
 
@@ -18949,12 +18983,18 @@
 
 <histogram name="DataUse.BytesReceived" units="bytes">
   <owner>tbansal@chromium.org</owner>
-  <summary>Count of total bytes received by the Chrome on the network.</summary>
+  <summary>
+    Count of total bytes received by the Chrome on the network. With network
+    servicification this is recorded when requests complete.
+  </summary>
 </histogram>
 
 <histogram name="DataUse.BytesSent" units="bytes">
   <owner>tbansal@chromium.org</owner>
-  <summary>Count of total bytes sent by the Chrome on the network.</summary>
+  <summary>
+    Count of total bytes sent by the Chrome on the network. With network
+    servicification this is recorded when requests complete.
+  </summary>
 </histogram>
 
 <histogram name="DataUse.ContentType.Services" enum="DataUseContentType">
@@ -28665,6 +28705,14 @@
   </summary>
 </histogram>
 
+<histogram name="ExploreSites.CatalogError" enum="ExploreSitesCatalogError">
+  <owner>dewittj@chromium.org</owner>
+  <owner>petewil@chromium.org</owner>
+  <summary>
+    If we find a bad catalog from the network, report which problems it had.
+  </summary>
+</histogram>
+
 <histogram name="ExploreSites.CategoryClick" enum="ExploreSitesCategories">
   <owner>dewittj@chromium.org</owner>
   <owner>petewil@chromium.org</owner>
@@ -45777,6 +45825,16 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="Media.TimeToFirstFrame.SRC.ManyVideos" units="ms">
+  <owner>hubbe@chromium.org</owner>
+  <owner>media-dev@chromium.org</owner>
+  <summary>
+    Time in milliseconds from when WebMediaPlayerImpl starts loading until the
+    first video frame has been shown IFF six or more videos are loading in
+    parallel.
+  </summary>
+</histogram>
+
 <histogram base="true" name="Media.TimeToMetadata" units="ms">
   <owner>dalecurtis@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
@@ -50504,6 +50562,21 @@
   </summary>
 </histogram>
 
+<histogram name="MultiDevice.PostOOBESetupFlow.PageShown"
+    enum="MultiDevice_PostOOBESetupFlow_Page">
+  <owner>hansberry@chromium.org</owner>
+  <summary>
+    Provides a count of how many times each page of the MultiDevice setup flow
+    was reached. This allows analysis of user dropoff between subsequent pages.
+
+    The formula &quot;Success page&quot; / &quot;Start page&quot; provides the
+    overall &quot;success rate&quot; of the MultiDevice setup flow, at a quick
+    glance.
+
+    The sum of each bucket's count is not meaningful.
+  </summary>
+</histogram>
+
 <histogram name="MultiDeviceSetup_NotificationClicked"
     enum="MultiDeviceSetupNotification">
   <owner>jordynass@chromium.org</owner>
@@ -72312,6 +72385,18 @@
   <summary>
     Measures the time from navigation timing's navigation start to the time the
     largest image is first painted after fully loaded, for main frame documents.
+    See http://bit.ly/fcp_plus_plus for details.
+  </summary>
+</histogram>
+
+<histogram name="PageLoad.Experimental.PaintTiming.NavigationToLastImagePaint"
+    units="ms" expires_after="2019-04-23">
+  <owner>maxlg@chromium.org</owner>
+  <owner>speed-metrics-dev@chromium.org</owner>
+  <summary>
+    Measures the time from navigation timing's navigation start to the time the
+    last image is first painted after fully loaded, for main frame documents.
+    See http://bit.ly/fcp_plus_plus for details.
   </summary>
 </histogram>
 
@@ -82835,6 +82920,15 @@
   </summary>
 </histogram>
 
+<histogram name="Previews.ServerLitePage.HostBlacklistedOnBypass"
+    enum="Boolean">
+  <owner>robertogden@chromium.org</owner>
+  <summary>
+    Whether or not the server directed Chrome to blacklist the requested host on
+    a bypass response.
+  </summary>
+</histogram>
+
 <histogram name="Previews.ServerLitePage.HttpOnlyFallbackPenalty" units="ms">
   <owner>robertogden@chromium.org</owner>
   <summary>
@@ -83296,6 +83390,30 @@
   <summary>Time taken to render to PDF for print preview.</summary>
 </histogram>
 
+<histogram name="PrintPreview.SiteIsolation.CrossSiteFrameCount" units="frames">
+  <owner>alexmos@chromium.org</owner>
+  <summary>
+    The number of cross-site frames contained in a document being printed, as
+    defined by comparing scheme and eTLD+1.
+  </summary>
+</histogram>
+
+<histogram name="PrintPreview.SiteIsolation.CrossSiteVisibleFrameCount"
+    units="frames">
+  <owner>alexmos@chromium.org</owner>
+  <summary>
+    The number of cross-site visible frames contained in a document being
+    printed.
+  </summary>
+</histogram>
+
+<histogram name="PrintPreview.SiteIsolation.RemoteFrameCount" units="frames">
+  <owner>alexmos@chromium.org</owner>
+  <summary>
+    The number of out-of-process frames contained in a document being printed.
+  </summary>
+</histogram>
+
 <histogram name="PrintPreview.UserAction" enum="PrintPreviewUserActionType">
   <owner>vitalybuka@chromium.org</owner>
   <summary>
@@ -94307,7 +94425,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchBarNoOverlap.PeekDuration" units="ms">
+<histogram name="Search.ContextualSearchBarNoOverlap.PeekDuration" units="ms"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94327,7 +94446,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchBarOverlap.PeekDuration" units="ms">
+<histogram name="Search.ContextualSearchBarOverlap.PeekDuration" units="ms"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94338,7 +94458,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchBarOverlapSeen"
-    enum="ContextualSearchBarOverlapSeen">
+    enum="ContextualSearchBarOverlapSeen" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94348,7 +94468,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchBasePageProtocol"
-    enum="ContextualSearchBasePageProtocol">
+    enum="ContextualSearchBasePageProtocol" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94393,7 +94513,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchDurationBetweenTriggerAndScrollNotSeen"
-    units="ms">
+    units="ms" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94405,7 +94525,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchDurationBetweenTriggerAndScrollSeen"
-    units="ms">
+    units="ms" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94440,7 +94560,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchDurationSeen" units="ms">
+<histogram name="Search.ContextualSearchDurationSeen" units="ms"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94449,7 +94570,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchDurationUnseen" units="ms">
+<histogram name="Search.ContextualSearchDurationUnseen" units="ms"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94459,7 +94581,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchDurationUnseenChained" units="ms">
+<histogram name="Search.ContextualSearchDurationUnseenChained" units="ms"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94470,7 +94593,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchEnterClosed"
-    enum="ContextualSearchEnterClosedStateChange">
+    enum="ContextualSearchEnterClosedStateChange" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94480,7 +94603,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchEnterExpanded"
-    enum="ContextualSearchEnterExpandedStateChange">
+    enum="ContextualSearchEnterExpandedStateChange" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94490,7 +94613,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchEnterMaximized"
-    enum="ContextualSearchEnterMaximizedStateChange">
+    enum="ContextualSearchEnterMaximizedStateChange" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94500,7 +94623,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchEnterPeeked"
-    enum="ContextualSearchEnterPeekedStateChange">
+    enum="ContextualSearchEnterPeekedStateChange" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94521,7 +94644,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchExitClosed"
-    enum="ContextualSearchExitClosedStateChange">
+    enum="ContextualSearchExitClosedStateChange" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94531,7 +94654,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchExitExpanded"
-    enum="ContextualSearchExitExpandedStateChange">
+    enum="ContextualSearchExitExpandedStateChange" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94541,7 +94664,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchExitMaximized"
-    enum="ContextualSearchExitMaximizedStateChange">
+    enum="ContextualSearchExitMaximizedStateChange" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94571,7 +94694,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchFirstRunFlowOutcome"
-    enum="ContextualSearchPreferenceState">
+    enum="ContextualSearchPreferenceState" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94581,7 +94704,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchFirstRunPanelSeen"
-    enum="ContextualSearchFirstRunPanelSeen">
+    enum="ContextualSearchFirstRunPanelSeen" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94617,7 +94740,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchLiteralSearchDuration" units="ms">
+<histogram name="Search.ContextualSearchLiteralSearchDuration" units="ms"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94628,7 +94752,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchLowPrioritySearchRequestStatus"
-    enum="ContextualSearchSearchRequestStatus">
+    enum="ContextualSearchSearchRequestStatus" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94658,7 +94782,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchPanelOpenDuration" units="ms">
+<histogram name="Search.ContextualSearchPanelOpenDuration" units="ms"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94762,7 +94887,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchPrevious28DayCtr" units="%">
+<histogram name="Search.ContextualSearchPrevious28DayCtr" units="%"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94772,7 +94898,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchPrevious28DayImpressions" units="views">
+<histogram name="Search.ContextualSearchPrevious28DayImpressions" units="views"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94782,7 +94909,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchPreviousWeekCtr" units="%">
+<histogram name="Search.ContextualSearchPreviousWeekCtr" units="%"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94792,7 +94920,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchPreviousWeekImpressions" units="views">
+<histogram name="Search.ContextualSearchPreviousWeekImpressions" units="views"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94811,7 +94940,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchPromoOutcomeByGesture"
-    enum="ContextualSearchOutcomeByGesture">
+    enum="ContextualSearchOutcomeByGesture" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94900,7 +95029,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchQuickActions.Shown" enum="BooleanShown">
+<histogram name="Search.ContextualSearchQuickActions.Shown" enum="BooleanShown"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94944,7 +95074,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchRecentScrollSuppression"
-    enum="ContextualSearchTapSuppression">
+    enum="ContextualSearchTapSuppression" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94953,7 +95083,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchResolutionDuration" units="ms">
+<histogram name="Search.ContextualSearchResolutionDuration" units="ms"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94962,7 +95093,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchResolvedSearchDuration" units="ms">
+<histogram name="Search.ContextualSearchResolvedSearchDuration" units="ms"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -94973,7 +95105,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchResolvedTermWords"
-    enum="ContextualSearchResolvedTermWords">
+    enum="ContextualSearchResolvedTermWords" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -95019,7 +95151,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchScreenTopSuppressed"
-    enum="ContextualSearchTapSuppression">
+    enum="ContextualSearchTapSuppression" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -95050,7 +95182,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchSelectionLengthNotSeen" units="chars">
+<histogram name="Search.ContextualSearchSelectionLengthNotSeen" units="chars"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -95060,7 +95193,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchSelectionLengthSeen" units="chars">
+<histogram name="Search.ContextualSearchSelectionLengthSeen" units="chars"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -95071,7 +95205,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchSelectionLengthSuppression"
-    enum="ContextualSearchTapSuppression">
+    enum="ContextualSearchTapSuppression" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -95081,7 +95215,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchSelectionValid"
-    enum="ContextualSearchSelectionValid">
+    enum="ContextualSearchSelectionValid" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -95127,7 +95261,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchTapDurationNotSeen" units="ms">
+<histogram name="Search.ContextualSearchTapDurationNotSeen" units="ms"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -95137,7 +95272,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchTapDurationSeen" units="ms">
+<histogram name="Search.ContextualSearchTapDurationSeen" units="ms"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -95159,7 +95295,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchTapLongDurationSeen"
-    enum="ContextualSearchResultsSeen">
+    enum="ContextualSearchResultsSeen" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -95169,7 +95305,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchTapLongWordSeen"
-    enum="ContextualSearchResultsSeen">
+    enum="ContextualSearchResultsSeen" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -95179,7 +95315,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchTapOnWordMiddleSeen"
-    enum="ContextualSearchResultsSeen">
+    enum="ContextualSearchResultsSeen" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -95189,7 +95325,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchTapShortDurationSeen"
-    enum="ContextualSearchResultsSeen">
+    enum="ContextualSearchResultsSeen" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -95199,7 +95335,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchTapShortWordSeen"
-    enum="ContextualSearchResultsSeen">
+    enum="ContextualSearchResultsSeen" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -95208,7 +95344,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchTapsSinceOpenDecided" units="taps">
+<histogram name="Search.ContextualSearchTapsSinceOpenDecided" units="taps"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -95217,7 +95354,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchTapsSinceOpenUndecided" units="taps">
+<histogram name="Search.ContextualSearchTapsSinceOpenUndecided" units="taps"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -95241,7 +95379,7 @@
 
 <histogram
     name="Search.ContextualSearchTapSuppressionSeen.AnyHeuristicSatisfied"
-    enum="ContextualSearchSuppressionResultsSeen">
+    enum="ContextualSearchSuppressionResultsSeen" expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -95251,7 +95389,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchTimeToSearch" units="ms">
+<histogram name="Search.ContextualSearchTimeToSearch" units="ms"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -95260,7 +95399,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchTopLocationNotSeen" units="dps">
+<histogram name="Search.ContextualSearchTopLocationNotSeen" units="dps"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -95269,7 +95409,8 @@
   </summary>
 </histogram>
 
-<histogram name="Search.ContextualSearchTopLocationSeen" units="dps">
+<histogram name="Search.ContextualSearchTopLocationSeen" units="dps"
+    expires_after="M72">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -126227,6 +126368,7 @@
   <suffix name="DriveComputers" label="Shared path in Drive Computers volume."/>
   <suffix name="MyDrive" label="Shared path in My Drive volume."/>
   <suffix name="Other" label="Shared path in any other volume."/>
+  <suffix name="Removable" label="Shared path in removable USB volume."/>
   <suffix name="TeamDrive" label="Shared path in Team Drive volume."/>
   <affected-histogram name="FileBrowser.CrostiniSharedPaths.Depth"/>
 </histogram_suffixes>
@@ -127108,6 +127250,15 @@
   <affected-histogram name="DNS.PrefetchResolution"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="JankyIntervalsPerThirtySeconds" separator=".">
+  <suffix name="NonStartup"
+      label="Does not include jank in the first 30 seconds [startup]."/>
+  <suffix name="Startup"
+      label="Only counts jank in the first 30 seconds [startup]."/>
+  <affected-histogram
+      name="Browser.Responsiveness.JankyIntervalsPerThirtySeconds"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="JSDialogs.DialogType" separator=".">
   <suffix name="Alert" label="window.alert() dialog"/>
   <suffix name="BeforeUnload" label="dialog caused by window.onbeforeunload"/>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index d5d8521..b23b11a 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -3064,7 +3064,16 @@
     <summary>
       Measures the time in milliseconds from navigation timing's navigation
       start to the time when the page first paints the largest image within
-      viewport after the image finishes loading.
+      viewport after the image finishes loading. See http://bit.ly/fcp_plus_plus
+      for more details.
+    </summary>
+  </metric>
+  <metric name="Experimental.PaintTiming.NavigationToLastImagePaint">
+    <summary>
+      Measures the time in milliseconds from navigation timing's navigation
+      start to the time when the page first paints the last image within
+      viewport after the image finishes loading. See http://bit.ly/fcp_plus_plus
+      for more details.
     </summary>
   </metric>
   <metric name="InteractiveTiming.FirstInputDelay">
@@ -5283,6 +5292,19 @@
   </metric>
 </event>
 
+<event name="IOS.FindInPageSearchMatches">
+  <owner>thegreenfrog@chromium.org</owner>
+  <owner>michaeldo@chromium.org</owner>
+  <summary>
+    Logged when the FindInPage returns a user search request result.
+  </summary>
+  <metric name="HasMatches">
+    <summary>
+      True if there were matches.
+    </summary>
+  </metric>
+</event>
+
 <event name="IOS.URLMismatchInLegacyAndSlimNavigationManager">
   <owner>eugenebut@chromium.org</owner>
   <owner>danyao@chromium.org</owner>
diff --git a/ui/android/delegated_frame_host_android.cc b/ui/android/delegated_frame_host_android.cc
index bae7a18..97ebf78c 100644
--- a/ui/android/delegated_frame_host_android.cc
+++ b/ui/android/delegated_frame_host_android.cc
@@ -162,24 +162,17 @@
 
   if (!src_subrect.IsEmpty())
     request->set_area(src_subrect);
-  if (!output_size.IsEmpty())
+  if (!output_size.IsEmpty()) {
+    // The CopyOutputRequest API does not allow fixing the output size. Instead
+    // we have the set area and scale in such a way that it would result in the
+    // desired output size.
+    if (!request->has_area())
+      request->set_area(gfx::Rect(surface_size_in_pixels_));
     request->set_result_selection(gfx::Rect(output_size));
-
-  if (!request->has_area())
-    request->set_area(gfx::Rect(surface_size_in_pixels_));
-
-  if (request->has_result_selection()) {
     const gfx::Rect& area = request->area();
-    const gfx::Rect& result_selection = request->result_selection();
-    if (area.IsEmpty() || result_selection.IsEmpty()) {
-      // Viz would normally return an empty result for an empty selection.
-      // However, this guard here is still necessary to protect against setting
-      // an illegal scaling ratio.
-      return;
-    }
     request->SetScaleRatio(
         gfx::Vector2d(area.width(), area.height()),
-        gfx::Vector2d(result_selection.width(), result_selection.height()));
+        gfx::Vector2d(output_size.width(), output_size.height()));
   }
 
   host_frame_sink_manager_->RequestCopyOfOutput(
@@ -187,8 +180,7 @@
 }
 
 bool DelegatedFrameHostAndroid::CanCopyFromCompositingSurface() const {
-  return local_surface_id_.is_valid() && view_->GetWindowAndroid() &&
-         view_->GetWindowAndroid()->GetCompositor();
+  return local_surface_id_.is_valid();
 }
 
 void DelegatedFrameHostAndroid::EvictDelegatedFrame() {
diff --git a/ui/android/delegated_frame_host_android_unittest.cc b/ui/android/delegated_frame_host_android_unittest.cc
index a40627b4..a5fdfb6 100644
--- a/ui/android/delegated_frame_host_android_unittest.cc
+++ b/ui/android/delegated_frame_host_android_unittest.cc
@@ -321,5 +321,31 @@
   EXPECT_TRUE(frame_host_->HasSavedFrame());
 }
 
+// Verify that when a source rect or output size is not provided to
+// CopyFromCompositingSurface, the corresponding values in CopyOutputRequest
+// are also not initialized.
+TEST_F(DelegatedFrameHostAndroidSurfaceSynchronizationTest,
+       FullSurfaceCapture) {
+  // First embed a surface to make sure we have something to copy from.
+  viz::LocalSurfaceId id = allocator_.GenerateId();
+  gfx::Size size(100, 100);
+  frame_host_->EmbedSurface(id, size, cc::DeadlinePolicy::UseDefaultDeadline());
+
+  // Request readback without source rect or output size specified.
+  frame_host_->CopyFromCompositingSurface(gfx::Rect(), gfx::Size(),
+                                          base::DoNothing());
+
+  // Make sure the resulting CopyOutputRequest does not have its area or result
+  // selection set.
+  const std::vector<
+      std::pair<viz::LocalSurfaceId, std::unique_ptr<viz::CopyOutputRequest>>>&
+      requests = frame_sink_manager_impl_.GetFrameSinkForId(frame_sink_id_)
+                     ->copy_output_requests_for_testing();
+  ASSERT_EQ(1u, requests.size());
+  viz::CopyOutputRequest* request = requests[0].second.get();
+  EXPECT_FALSE(request->has_area());
+  EXPECT_FALSE(request->has_result_selection());
+}
+
 }  // namespace
 }  // namespace ui
diff --git a/ui/android/java/res/drawable-v21/button_compat.xml b/ui/android/java/res/drawable-v21/button_compat.xml
deleted file mode 100644
index 226ef429..0000000
--- a/ui/android/java/res/drawable-v21/button_compat.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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. -->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="?android:attr/colorControlHighlight">
-    <item android:drawable="@drawable/button_compat_shape" />
-</ripple>
diff --git a/ui/android/java/res/values-v17/styles.xml b/ui/android/java/res/values-v17/styles.xml
index bf36b446..8629751 100644
--- a/ui/android/java/res/values-v17/styles.xml
+++ b/ui/android/java/res/values-v17/styles.xml
@@ -49,14 +49,6 @@
         <item name="buttonRaised">false</item>
     </style>
 
-    <!-- TODO(huayinz): Remove these once button unification is done. -->
-    <style name="ButtonCompat" parent="ButtonCompatBase" tools:ignore="UnusedResources">
-        <item name="android:background">@drawable/button_compat_shape</item>
-    </style>
-    <style name="ButtonCompatBorderless" parent="ButtonCompat" tools:ignore="UnusedResources">
-        <item name="android:background">?android:attr/selectableItemBackground</item>
-    </style>
-
     <!-- Used by Chrome and Content -->
     <!-- TODO(huayinz): Update prefixes for text appearance styles in ui/android. -->
     <style name="TextAppearance" parent="android:TextAppearance" tools:ignore="UnusedResources" />
diff --git a/ui/android/java/res/values-v21/styles.xml b/ui/android/java/res/values-v21/styles.xml
index b8f1de04..3f99004 100644
--- a/ui/android/java/res/values-v21/styles.xml
+++ b/ui/android/java/res/values-v21/styles.xml
@@ -3,14 +3,6 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 <resources xmlns:tools="http://schemas.android.com/tools">
-    <!-- Buttons -->
-    <style name="ButtonCompat" parent="ButtonCompatBase">
-        <item name="android:background">@drawable/button_compat</item>
-    </style>
-    <style name="ButtonCompatBorderless" parent="ButtonCompat">
-        <item name="android:background">@drawable/button_borderless_compat</item>
-    </style>
-
     <!-- Used by Chrome and Content -->
     <style name="RobotoMediumStyle" tools:ignore="UnusedResources">
         <item name="android:fontFamily">sans-serif-medium</item>
diff --git a/ui/android/resources/resource_manager_impl.cc b/ui/android/resources/resource_manager_impl.cc
index 24ac7b3..c7d48bd 100644
--- a/ui/android/resources/resource_manager_impl.cc
+++ b/ui/android/resources/resource_manager_impl.cc
@@ -159,9 +159,14 @@
   // Build a color filter to use on the base resource. This filter multiplies
   // the RGB components by the components of the new color but retains the
   // alpha of the original image.
+  SkScalar color_matrix[20] = {
+      0, 0, 0, 0, SkColorGetR(tint_color),
+      0, 0, 0, 0, SkColorGetG(tint_color),
+      0, 0, 0, 0, SkColorGetB(tint_color),
+      0, 0, 0, 1, 0};
   SkPaint color_filter;
   color_filter.setColorFilter(
-      SkColorFilter::MakeModeFilter(tint_color, SkBlendMode::kModulate));
+      SkColorFilter::MakeMatrixFilterRowMajor255(color_matrix));
 
   // Draw the resource and make it immutable.
   base_image->ui_resource()
diff --git a/ui/android/window_android.cc b/ui/android/window_android.cc
index b4089b7..84f7e17 100644
--- a/ui/android/window_android.cc
+++ b/ui/android/window_android.cc
@@ -42,6 +42,7 @@
   void RemoveObserver(viz::BeginFrameObserver* obs) override;
   void DidFinishFrame(viz::BeginFrameObserver* obs) override {}
   bool IsThrottled() const override { return true; }
+  void OnGpuNoLongerBusy() override;
 
   void OnVSync(base::TimeTicks frame_time, base::TimeDelta vsync_period);
   void OnPauseChanged(bool paused);
@@ -96,6 +97,11 @@
     window_->SetNeedsBeginFrames(false);
 }
 
+void WindowAndroid::WindowBeginFrameSource::OnGpuNoLongerBusy() {
+  for (auto& obs : observers_)
+    obs.OnBeginFrame(last_begin_frame_args_);
+}
+
 void WindowAndroid::WindowBeginFrameSource::OnVSync(
     base::TimeTicks frame_time,
     base::TimeDelta vsync_period) {
@@ -106,9 +112,9 @@
       deadline, vsync_period, viz::BeginFrameArgs::NORMAL);
   DCHECK(last_begin_frame_args_.IsValid());
   next_sequence_number_++;
-
-  for (auto& obs : observers_)
-    obs.OnBeginFrame(last_begin_frame_args_);
+  if (RequestCallbackOnGpuAvailable())
+    return;
+  OnGpuNoLongerBusy();
 }
 
 void WindowAndroid::WindowBeginFrameSource::OnPauseChanged(bool paused) {
diff --git a/ui/aura/window.cc b/ui/aura/window.cc
index e2f4bd7bdb..8f20151 100644
--- a/ui/aura/window.cc
+++ b/ui/aura/window.cc
@@ -334,14 +334,27 @@
 
 void Window::SetBoundsInScreen(const gfx::Rect& new_bounds_in_screen,
                                const display::Display& dst_display) {
-  Window* root = GetRootWindow();
-  if (root) {
-    aura::client::ScreenPositionClient* screen_position_client =
-        aura::client::GetScreenPositionClient(root);
-    screen_position_client->SetBounds(this, new_bounds_in_screen, dst_display);
-    return;
+  WindowTreeHost* host = GetHost();
+  bool is_moving = false;
+  if (host && host->GetDisplayId() != dst_display.id()) {
+    is_moving = true;
+    for (auto& observer : observers_)
+      observer.OnWillMoveWindowToDisplay(this, dst_display.id());
   }
-  SetBounds(new_bounds_in_screen);
+
+  aura::client::ScreenPositionClient* screen_position_client = nullptr;
+  Window* root = GetRootWindow();
+  if (root)
+    screen_position_client = aura::client::GetScreenPositionClient(root);
+  if (screen_position_client)
+    screen_position_client->SetBounds(this, new_bounds_in_screen, dst_display);
+  else
+    SetBounds(new_bounds_in_screen);
+
+  if (is_moving) {
+    for (auto& observer : observers_)
+      observer.OnDidMoveWindowToDisplay(this);
+  }
 }
 
 gfx::Rect Window::GetTargetBounds() const {
diff --git a/ui/aura/window_observer.h b/ui/aura/window_observer.h
index 65e10f9..3f81110 100644
--- a/ui/aura/window_observer.h
+++ b/ui/aura/window_observer.h
@@ -148,6 +148,17 @@
   virtual void OnWindowRemovingFromRootWindow(Window* window,
                                               Window* new_root) {}
 
+  // Called from SetBoundsInScreen() when a window is moving to a new display as
+  // the result of changing bounds. |new_display_id| is the specified new
+  // display id. This is called before the bounds are actually changed.
+  virtual void OnWillMoveWindowToDisplay(Window* window,
+                                         int64_t new_display_id) {}
+
+  // Called from SetBoundsInScreen() when the task of the window moving to a new
+  // display finished. Sometimes the window may stay in the old display, but
+  // this will be called anyways.
+  virtual void OnDidMoveWindowToDisplay(Window* window) {}
+
   // Called when the window title has changed.
   virtual void OnWindowTitleChanged(Window* window) {}
 
diff --git a/ui/base/material_design/material_design_controller.cc b/ui/base/material_design/material_design_controller.cc
index 1bdff8eb..583a54c 100644
--- a/ui/base/material_design/material_design_controller.cc
+++ b/ui/base/material_design/material_design_controller.cc
@@ -9,11 +9,11 @@
 #include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/logging.h"
+#include "base/message_loop/message_loop.h"
 #include "base/no_destructor.h"
 #include "base/observer_list.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/trace_event/trace_event.h"
-#include "build/build_config.h"
 #include "build/buildflag.h"
 #include "ui/base/material_design/material_design_controller_observer.h"
 #include "ui/base/ui_base_features.h"
@@ -25,11 +25,31 @@
 
 #if defined(OS_WIN)
 #include "base/win/win_util.h"
+#include "base/win/windows_version.h"
 #include "ui/base/win/hidden_window.h"
+#include "ui/gfx/win/singleton_hwnd.h"
+#include "ui/gfx/win/singleton_hwnd_observer.h"
 #endif
 
 namespace ui {
 
+namespace {
+
+#if defined(OS_WIN)
+void TabletModeWatcherWinProc(HWND hwnd,
+                              UINT message,
+                              WPARAM wparam,
+                              LPARAM lparam) {
+  if (message == WM_SETTINGCHANGE) {
+    MaterialDesignController::OnTabletModeToggled(
+        base::win::IsWindows10TabletMode(
+            gfx::SingletonHwnd::GetInstance()->hwnd()));
+  }
+}
+#endif  // defined(OS_WIN)
+
+}  // namespace
+
 bool MaterialDesignController::is_mode_initialized_ = false;
 
 MaterialDesignController::Mode MaterialDesignController::mode_ =
@@ -52,20 +72,14 @@
              switches::kTopChromeMDMaterialRefreshTouchOptimized) {
     SetMode(MATERIAL_TOUCH_REFRESH);
   } else if (switch_value == switches::kTopChromeMDMaterialRefreshDynamic) {
-    is_refresh_dynamic_ui_ = true;
-
-    // TabletModeClient's default state is in non-tablet mode.
-    SetMode(MATERIAL_REFRESH);
+    MaybeSetDynamicRefreshMode();
   } else {
     if (!switch_value.empty()) {
       LOG(ERROR) << "Invalid value='" << switch_value
                  << "' for command line switch '" << switches::kTopChromeMD
                  << "'.";
     }
-#if defined(OS_CHROMEOS)
-    is_refresh_dynamic_ui_ = true;
-#endif
-    SetMode(MATERIAL_REFRESH);
+    MaybeSetDynamicRefreshMode();
   }
 
   // Ideally, there would be a more general, "initialize random stuff here"
@@ -132,4 +146,30 @@
   }
 }
 
+// static
+void MaterialDesignController::MaybeSetDynamicRefreshMode() {
+#if defined(OS_CHROMEOS)
+  // TabletModeClient's default state is in non-tablet mode.
+  SetMode(MATERIAL_REFRESH);
+  is_refresh_dynamic_ui_ = true;
+#elif defined(OS_WIN)
+  if (base::win::GetVersion() < base::win::VERSION_WIN10 ||
+      !base::MessageLoopForUI::IsCurrent()) {
+    SetMode(MATERIAL_REFRESH);
+    return;
+  }
+
+  MaterialDesignController::GetInstance()->singleton_hwnd_observer_ =
+      std::make_unique<gfx::SingletonHwndObserver>(
+          base::BindRepeating(TabletModeWatcherWinProc));
+  SetMode(base::win::IsWindows10TabletMode(
+              gfx::SingletonHwnd::GetInstance()->hwnd())
+              ? MATERIAL_TOUCH_REFRESH
+              : MATERIAL_REFRESH);
+  is_refresh_dynamic_ui_ = true;
+#else
+  SetMode(MATERIAL_REFRESH);
+#endif
+}
+
 }  // namespace ui
diff --git a/ui/base/material_design/material_design_controller.h b/ui/base/material_design/material_design_controller.h
index b795718..55da0ec 100644
--- a/ui/base/material_design/material_design_controller.h
+++ b/ui/base/material_design/material_design_controller.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "base/observer_list.h"
+#include "build/build_config.h"
 #include "ui/base/ui_base_export.h"
 
 namespace base {
@@ -14,6 +15,10 @@
 class NoDestructor;
 }
 
+namespace gfx {
+class SingletonHwndObserver;
+}
+
 namespace ui {
 
 class MaterialDesignControllerObserver;
@@ -76,6 +81,11 @@
   // used by tests to directly set the mode.
   static void SetMode(Mode mode);
 
+  // Sets |is_refresh_dynamic_ui_| to true and initializes the |mode_|. If the
+  // platform does not support tablet mode switching, |mode_| will be set to
+  // MATERIAL_REFRESH and |is_refresh_dynamic_ui_| will be left untouched.
+  static void MaybeSetDynamicRefreshMode();
+
   // Tracks whether |mode_| has been initialized. This is necessary to avoid
   // checking the |mode_| early in initialization before a call to Initialize().
   // Tests can use it to reset the state back to a clean state during tear down.
@@ -88,6 +98,10 @@
   // MATERIAL_TOUCH_REFRESH depending on the tablet state.
   static bool is_refresh_dynamic_ui_;
 
+#if defined(OS_WIN)
+  std::unique_ptr<gfx::SingletonHwndObserver> singleton_hwnd_observer_;
+#endif
+
   base::ObserverList<MaterialDesignControllerObserver> observers_;
 
   DISALLOW_COPY_AND_ASSIGN(MaterialDesignController);
diff --git a/ui/base/resource/resource_bundle.cc b/ui/base/resource/resource_bundle.cc
index 315a281..3bcbb234 100644
--- a/ui/base/resource/resource_bundle.cc
+++ b/ui/base/resource/resource_bundle.cc
@@ -247,18 +247,6 @@
 }
 #endif  // !defined(OS_ANDROID)
 
-void ResourceBundle::AddDataPack(std::unique_ptr<DataPack> data_pack) {
-#if DCHECK_IS_ON()
-  data_pack->CheckForDuplicateResources(data_packs_);
-#endif
-
-  if (GetScaleForScaleFactor(data_pack->GetScaleFactor()) >
-      GetScaleForScaleFactor(max_scale_factor_))
-    max_scale_factor_ = data_pack->GetScaleFactor();
-
-  data_packs_.push_back(std::move(data_pack));
-}
-
 void ResourceBundle::AddDataPackFromPath(const base::FilePath& path,
                                          ScaleFactor scale_factor) {
   AddDataPackFromPathInternal(path, scale_factor, false);
@@ -778,6 +766,18 @@
   }
 }
 
+void ResourceBundle::AddDataPack(std::unique_ptr<DataPack> data_pack) {
+#if DCHECK_IS_ON()
+  data_pack->CheckForDuplicateResources(data_packs_);
+#endif
+
+  if (GetScaleForScaleFactor(data_pack->GetScaleFactor()) >
+      GetScaleForScaleFactor(max_scale_factor_))
+    max_scale_factor_ = data_pack->GetScaleFactor();
+
+  data_packs_.push_back(std::move(data_pack));
+}
+
 void ResourceBundle::InitDefaultFontList() {
 #if defined(OS_CHROMEOS)
   // InitDefaultFontList() is called earlier than overriding the locale strings.
diff --git a/ui/base/resource/resource_bundle.h b/ui/base/resource/resource_bundle.h
index c05b5b9..667b5b12 100644
--- a/ui/base/resource/resource_bundle.h
+++ b/ui/base/resource/resource_bundle.h
@@ -158,10 +158,6 @@
   // Check if the .pak for the given locale exists.
   bool LocaleDataPakExists(const std::string& locale);
 
-  // Inserts |data_pack| to |data_pack_| and updates |max_scale_factor_|
-  // accordingly.
-  void AddDataPack(std::unique_ptr<DataPack> data_pack);
-
   // Registers additional data pack files with this ResourceBundle.  When
   // looking for a DataResource, we will search these files after searching the
   // main module. |path| should be the complete path to the pack file if known
@@ -352,6 +348,10 @@
                                    ScaleFactor scale_factor,
                                    bool optional);
 
+  // Inserts |data_pack| to |data_pack_| and updates |max_scale_factor_|
+  // accordingly.
+  void AddDataPack(std::unique_ptr<DataPack> data_pack);
+
   // Try to load the locale specific strings from an external data module.
   // Returns the locale that is loaded.
   std::string LoadLocaleResources(const std::string& pref_locale);
diff --git a/ui/base/resource/resource_bundle_android.cc b/ui/base/resource/resource_bundle_android.cc
index 6ce54d1..16907b59 100644
--- a/ui/base/resource/resource_bundle_android.cc
+++ b/ui/base/resource/resource_bundle_android.cc
@@ -11,6 +11,7 @@
 #include "base/path_service.h"
 #include "jni/ResourceBundle_jni.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/data_pack.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/ui_base_paths.h"
 
@@ -175,21 +176,6 @@
   }
 }
 
-std::unique_ptr<DataPack> GetDataPackFromPackFile(
-    const char* path_within_apk,
-    const base::FilePath& disk_file_path) {
-  if (LoadFromApkOrFile(path_within_apk, &disk_file_path, &g_resources_pack_fd,
-                        &g_resources_pack_region)) {
-    std::unique_ptr<DataPack> data_pack =
-        std::make_unique<DataPack>(SCALE_FACTOR_NONE);
-    if (data_pack->LoadFromFileRegion(base::File(g_resources_pack_fd),
-                                      g_resources_pack_region)) {
-      return data_pack;
-    }
-  }
-  return nullptr;
-}
-
 int GetMainAndroidPackFd(base::MemoryMappedFile::Region* out_region) {
   DCHECK_GE(g_resources_pack_fd, 0);
   *out_region = g_resources_pack_region;
diff --git a/ui/base/resource/resource_bundle_android.h b/ui/base/resource/resource_bundle_android.h
index 2913731..57059b0 100644
--- a/ui/base/resource/resource_bundle_android.h
+++ b/ui/base/resource/resource_bundle_android.h
@@ -9,7 +9,6 @@
 #include <string>
 
 #include "base/files/memory_mapped_file.h"
-#include "ui/base/resource/data_pack.h"
 #include "ui/base/ui_base_export.h"
 
 namespace ui {
@@ -20,10 +19,6 @@
     const char* path_within_apk,
     const base::FilePath& disk_file_path);
 
-UI_BASE_EXPORT std::unique_ptr<DataPack> GetDataPackFromPackFile(
-    const char* path_within_apk,
-    const base::FilePath& disk_file_path);
-
 // Returns the file descriptor and region for resources.pak.
 UI_BASE_EXPORT int GetMainAndroidPackFd(
     base::MemoryMappedFile::Region* out_region);
diff --git a/ui/compositor/test/test_compositor_host_ozone.cc b/ui/compositor/test/test_compositor_host_ozone.cc
index 23a63bb..88e4c53 100644
--- a/ui/compositor/test/test_compositor_host_ozone.cc
+++ b/ui/compositor/test/test_compositor_host_ozone.cc
@@ -88,7 +88,11 @@
                   false /* enable_surface_synchronization */,
                   false /* enable_pixel_canvas */) {}
 
-TestCompositorHostOzone::~TestCompositorHostOzone() {}
+TestCompositorHostOzone::~TestCompositorHostOzone() {
+  // |window_| should be destroyed earlier than |window_delegate_| as it refers
+  // to its delegate on destroying.
+  window_.reset();
+}
 
 void TestCompositorHostOzone::Show() {
   ui::PlatformWindowInitProperties properties;
diff --git a/ui/events/gesture_detection/touch_disposition_gesture_filter.cc b/ui/events/gesture_detection/touch_disposition_gesture_filter.cc
index 682df9c..6f6af6f 100644
--- a/ui/events/gesture_detection/touch_disposition_gesture_filter.cc
+++ b/ui/events/gesture_detection/touch_disposition_gesture_filter.cc
@@ -181,9 +181,9 @@
                     Tail().back().unique_touch_event_id()));
   }
   if (!Head().empty()) {
-    DCHECK_NE(packet.unique_touch_event_id(),
-              Head().front().unique_touch_event_id());
-
+    DCHECK((packet.gesture_source() == GestureEventDataPacket::TOUCH_TIMEOUT) ||
+           packet.unique_touch_event_id() !=
+               Head().front().unique_touch_event_id());
   }
 
   Tail().push(packet);
diff --git a/ui/file_manager/audio_player/elements/audio_player.js b/ui/file_manager/audio_player/elements/audio_player.js
index d25f5955..38697f3 100644
--- a/ui/file_manager/audio_player/elements/audio_player.js
+++ b/ui/file_manager/audio_player/elements/audio_player.js
@@ -7,6 +7,8 @@
 
   listeners: {
     'toggle-pause-event': 'onTogglePauseEvent_',
+    'next-track-event': 'onNextTrackEvent_',
+    'previous-track-event': 'onPreviousTrackEvent_',
     'small-forward-skip-event': 'onSmallForwardSkipEvent_',
     'small-backword-skip-event': 'onSmallBackwordSkipEvent_',
     'big-forward-skip-event': 'onBigForwardSkipEvent_',
@@ -80,8 +82,6 @@
    * element is ready.
    */
   ready: function() {
-    this.addEventListener('keydown', this.onKeyDown_.bind(this));
-
     this.$.audioController.addEventListener('dragging-changed',
         this.onDraggingChanged_.bind(this));
 
@@ -412,27 +412,6 @@
   },
 
   /**
-   * Invoked when the 'keydown' event is fired.
-   * @param {Event} event The event object.
-   */
-  onKeyDown_: function(event) {
-    switch (event.key) {
-      case 'MediaTrackNext':
-        this.onControllerNextClicked();
-        break;
-      case 'MediaPlayPause':
-        this.playing = !this.playing;
-        break;
-      case 'MediaTrackPrevious':
-        this.onControllerPreviousClicked();
-        break;
-      case 'MediaStop':
-        // TODO: Define "Stop" behavior.
-        break;
-    }
-  },
-
-  /**
    * Computes volume value for audio element. (should be in [0.0, 1.0])
    * @param {number} volume Volume which is set in the UI. ([0, 100])
    * @return {number}
@@ -480,4 +459,14 @@
   onBigBackwordSkipEvent_: function(event) {
     this.$.audioController.bigSkip(false);
   },
+
+  /** @private */
+  onNextTrackEvent_: function(event) {
+    this.onControllerNextClicked();
+  },
+
+  /** @private */
+  onPreviousTrackEvent_: function(event) {
+    this.onControllerPreviousClicked();
+  },
 });
diff --git a/ui/file_manager/audio_player/js/audio_player.js b/ui/file_manager/audio_player/js/audio_player.js
index e55d087..81b0804c 100644
--- a/ui/file_manager/audio_player/js/audio_player.js
+++ b/ui/file_manager/audio_player/js/audio_player.js
@@ -385,6 +385,7 @@
 
     case ' ': // Space
     case 'k':
+    case 'MediaPlayPause':
       this.player_.dispatchEvent(new Event('toggle-pause-event'));
       break;
     case 'ArrowUp':
@@ -403,6 +404,17 @@
     case 'j':
       this.player_.dispatchEvent(new Event('big-backword-skip-event'));
       break;
+    case ']':
+    case 'MediaTrackNext':
+      this.player_.dispatchEvent(new Event('next-track-event'));
+      break;
+    case '[':
+    case 'MediaTrackPrevious':
+      this.player_.dispatchEvent(new Event('previous-track-event'));
+      break;
+    case 'MediaStop':
+      // TODO: Define "Stop" behavior.
+      break;
   }
 };
 
diff --git a/ui/file_manager/file_manager/foreground/js/crostini.js b/ui/file_manager/file_manager/foreground/js/crostini.js
index 1d625d1f..b0f412f 100644
--- a/ui/file_manager/file_manager/foreground/js/crostini.js
+++ b/ui/file_manager/file_manager/foreground/js/crostini.js
@@ -17,9 +17,11 @@
  */
 Crostini.VALID_ROOT_TYPES_FOR_SHARE = new Map([
   [VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT, 'DriveComputers'],
+  [VolumeManagerCommon.RootType.COMPUTER, 'DriveComputers'],
   [VolumeManagerCommon.RootType.DOWNLOADS, 'Downloads'],
   [VolumeManagerCommon.RootType.DRIVE, 'MyDrive'],
   [VolumeManagerCommon.RootType.TEAM_DRIVE, 'TeamDrive'],
+  [VolumeManagerCommon.RootType.REMOVABLE, 'Removable'],
 ]);
 
 /** @private {string} */
diff --git a/ui/file_manager/file_manager/foreground/js/crostini_unittest.js b/ui/file_manager/file_manager/foreground/js/crostini_unittest.js
index 1373a9f6..2bc29768 100644
--- a/ui/file_manager/file_manager/foreground/js/crostini_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/crostini_unittest.js
@@ -12,9 +12,10 @@
   }
 };
 
-const volumeManagerTest = {
+let volumeManagerRootType = 'testroot';
+const volumeManager = {
   getLocationInfo: (entry) => {
-    return {rootType: 'testroot'};
+    return {rootType: volumeManagerRootType};
   },
 };
 
@@ -26,26 +27,21 @@
   const foo2 = new MockDirectoryEntry(mockFileSystem, '/foo2');
   const foobar2 = new MockDirectoryEntry(mockFileSystem, '/foo2/bar2');
 
-  assertFalse(Crostini.isPathShared(foo1, volumeManagerTest));
+  assertFalse(Crostini.isPathShared(foo1, volumeManager));
 
-  Crostini.registerSharedPath(foo1, volumeManagerTest);
-  assertFalse(Crostini.isPathShared(root, volumeManagerTest));
-  assertTrue(Crostini.isPathShared(foo1, volumeManagerTest));
-  assertTrue(Crostini.isPathShared(foobar1, volumeManagerTest));
+  Crostini.registerSharedPath(foo1, volumeManager);
+  assertFalse(Crostini.isPathShared(root, volumeManager));
+  assertTrue(Crostini.isPathShared(foo1, volumeManager));
+  assertTrue(Crostini.isPathShared(foobar1, volumeManager));
 
-  Crostini.registerSharedPath(foobar2, volumeManagerTest);
-  assertFalse(Crostini.isPathShared(foo2, volumeManagerTest));
-  assertTrue(Crostini.isPathShared(foobar2, volumeManagerTest));
+  Crostini.registerSharedPath(foobar2, volumeManager);
+  assertFalse(Crostini.isPathShared(foo2, volumeManager));
+  assertTrue(Crostini.isPathShared(foobar2, volumeManager));
 
-  Crostini.unregisterSharedPath(foobar2, volumeManagerTest);
-  assertFalse(Crostini.isPathShared(foobar2, volumeManagerTest));
+  Crostini.unregisterSharedPath(foobar2, volumeManager);
+  assertFalse(Crostini.isPathShared(foobar2, volumeManager));
 }
 
-const volumeManagerDownloads = {
-  getLocationInfo: (entry) => {
-    return {rootType: 'downloads'};
-  },
-};
 
 function testCanSharePath() {
   Crostini.IS_CROSTINI_FILES_ENABLED = true;
@@ -57,25 +53,28 @@
   const fooFile = new MockEntry(mockFileSystem, '/foo/file');
   const fooFolder = new MockDirectoryEntry(mockFileSystem, '/foo/folder');
 
-  assertFalse(Crostini.canSharePath(root, true, volumeManagerTest));
-  assertFalse(Crostini.canSharePath(root, false, volumeManagerTest));
-  assertFalse(Crostini.canSharePath(rootFile, true, volumeManagerTest));
-  assertFalse(Crostini.canSharePath(rootFile, false, volumeManagerTest));
-  assertFalse(Crostini.canSharePath(rootFolder, true, volumeManagerTest));
-  assertFalse(Crostini.canSharePath(rootFolder, false, volumeManagerTest));
-  assertFalse(Crostini.canSharePath(fooFile, true, volumeManagerTest));
-  assertFalse(Crostini.canSharePath(fooFile, false, volumeManagerTest));
-  assertFalse(Crostini.canSharePath(fooFolder, true, volumeManagerTest));
-  assertFalse(Crostini.canSharePath(fooFolder, false, volumeManagerTest));
+  assertFalse(Crostini.canSharePath(root, true, volumeManager));
+  assertFalse(Crostini.canSharePath(root, false, volumeManager));
+  assertFalse(Crostini.canSharePath(rootFile, true, volumeManager));
+  assertFalse(Crostini.canSharePath(rootFile, false, volumeManager));
+  assertFalse(Crostini.canSharePath(rootFolder, true, volumeManager));
+  assertFalse(Crostini.canSharePath(rootFolder, false, volumeManager));
+  assertFalse(Crostini.canSharePath(fooFile, true, volumeManager));
+  assertFalse(Crostini.canSharePath(fooFile, false, volumeManager));
+  assertFalse(Crostini.canSharePath(fooFolder, true, volumeManager));
+  assertFalse(Crostini.canSharePath(fooFolder, false, volumeManager));
 
-  assertFalse(Crostini.canSharePath(root, true, volumeManagerDownloads));
-  assertFalse(Crostini.canSharePath(root, false, volumeManagerDownloads));
-  assertFalse(Crostini.canSharePath(rootFile, true, volumeManagerDownloads));
-  assertTrue(Crostini.canSharePath(rootFile, false, volumeManagerDownloads));
-  assertTrue(Crostini.canSharePath(rootFolder, true, volumeManagerDownloads));
-  assertTrue(Crostini.canSharePath(rootFolder, false, volumeManagerDownloads));
-  assertFalse(Crostini.canSharePath(fooFile, true, volumeManagerDownloads));
-  assertTrue(Crostini.canSharePath(fooFile, false, volumeManagerDownloads));
-  assertTrue(Crostini.canSharePath(fooFolder, true, volumeManagerDownloads));
-  assertTrue(Crostini.canSharePath(fooFolder, false, volumeManagerDownloads));
+  for (type in Crostini.VALID_ROOT_TYPES_FOR_SHARE) {
+    volumeManagerRootType = type;
+    assertFalse(Crostini.canSharePath(root, true, volumeManager));
+    assertFalse(Crostini.canSharePath(root, false, volumeManager));
+    assertFalse(Crostini.canSharePath(rootFile, true, volumeManager));
+    assertTrue(Crostini.canSharePath(rootFile, false, volumeManager));
+    assertTrue(Crostini.canSharePath(rootFolder, true, volumeManager));
+    assertTrue(Crostini.canSharePath(rootFolder, false, volumeManager));
+    assertFalse(Crostini.canSharePath(fooFile, true, volumeManager));
+    assertTrue(Crostini.canSharePath(fooFile, false, volumeManager));
+    assertTrue(Crostini.canSharePath(fooFolder, true, volumeManager));
+    assertTrue(Crostini.canSharePath(fooFolder, false, volumeManager));
+  }
 }
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
index c78e9c8f..723bf4e0 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
@@ -11,7 +11,7 @@
  * Instead, we separate their implementations to this separate object and call
  * it with setting 'this' from DirectoryTree/Item.
  */
-var DirectoryItemTreeBaseMethods = {};
+const DirectoryItemTreeBaseMethods = {};
 
 /**
  * Finds an item by entry and returns it.
@@ -20,8 +20,8 @@
  * @this {(DirectoryItem|DirectoryTree)}
  */
 DirectoryItemTreeBaseMethods.getItemByEntry = function(entry) {
-  for (var i = 0; i < this.items.length; i++) {
-    var item = this.items[i];
+  for (let i = 0; i < this.items.length; i++) {
+    const item = this.items[i];
     if (!item.entry)
       continue;
     if (util.isSameEntry(item.entry, entry)) {
@@ -60,8 +60,8 @@
  * @this {(DirectoryItem|VolumeItem|DirectoryTree)}
  */
 DirectoryItemTreeBaseMethods.searchAndSelectByEntry = function(entry) {
-  for (var i = 0; i < this.items.length; i++) {
-    var item = this.items[i];
+  for (let i = 0; i < this.items.length; i++) {
+    const item = this.items[i];
     if (!item.entry)
       continue;
 
@@ -98,8 +98,8 @@
  */
 DirectoryItemTreeBaseMethods.recordUMASelectedEntry = function(
     e, rootType, isRootEntry) {
-  var expandIconSelected = e.target.classList.contains('expand-icon');
-  var metricName = 'Location.OnEntrySelected.TopLevel';
+  const expandIconSelected = e.target.classList.contains('expand-icon');
+  let metricName = 'Location.OnEntrySelected.TopLevel';
   if (!expandIconSelected && isRootEntry) {
     metricName = 'Location.OnEntrySelected.TopLevel';
   } else if (!expandIconSelected && !isRootEntry) {
@@ -110,13 +110,13 @@
     metricName = 'Location.OnEntryExpandedOrCollapsed.NonTopLevel';
   }
 
-  metrics.recordEnum(metricName, rootType, VolumeManagerCommon.RootTypesForUMA);
+  metrics.recordEnum(
+      metricName, rootType, VolumeManagerCommon.RootTypesForUMA);
 };
 
 Object.freeze(DirectoryItemTreeBaseMethods);
 
-var TREE_ITEM_INNER_HTML =
-    '<div class="tree-row">' +
+const TREE_ITEM_INNER_HTML = '<div class="tree-row">' +
     ' <paper-ripple fit class="recenteringTouch"></paper-ripple>' +
     ' <span class="expand-icon"></span>' +
     ' <span class="icon"></span>' +
@@ -137,10 +137,10 @@
  * @constructor
  */
 function DirectoryItem(label, tree) {
-  var item = /** @type {DirectoryItem} */ (new cr.ui.TreeItem());
+  const item = /** @type {DirectoryItem} */ (new cr.ui.TreeItem());
   // Get the original label id defined by TreeItem, before overwriting
   // prototype.
-  var labelId = item.labelElement.id;
+  const labelId = item.labelElement.id;
   item.__proto__ = DirectoryItem.prototype;
   if (window.IN_TEST) {
     item.setAttribute('dir-type', 'DirectoryItem');
@@ -282,15 +282,16 @@
  * @this {DirectoryItem}
  */
 DirectoryItem.prototype.updateSubElementsFromList = function(recursive) {
-  var index = 0;
-  var tree = this.parentTree_;
-  var item;
+  let index = 0;
+  const tree = this.parentTree_;
+  let item;
   while (this.entries_[index]) {
-    var currentEntry = this.entries_[index];
-    var currentElement = this.items[index];
-    var label = util.getEntryLabel(
-        tree.volumeManager_.getLocationInfo(currentEntry),
-        currentEntry) || '';
+    const currentEntry = this.entries_[index];
+    const currentElement = this.items[index];
+    const label =
+        util.getEntryLabel(
+            tree.volumeManager_.getLocationInfo(currentEntry), currentEntry) ||
+        '';
 
     if (index >= this.items.length) {
       // If currentEntry carries its navigationModel we generate an item
@@ -334,7 +335,7 @@
     }
   }
 
-  var removedChild;
+  let removedChild;
   while (removedChild = this.items[index]) {
     this.remove(removedChild);
   }
@@ -374,8 +375,7 @@
  * @param {boolean=} opt_unused Unused.
  * @override
  */
-DirectoryItem.prototype.scrollIntoViewIfNeeded = function(opt_unused) {
-};
+DirectoryItem.prototype.scrollIntoViewIfNeeded = function(opt_unused) {};
 
 /**
  * Removes the child node, but without selecting the parent item, to avoid
@@ -396,7 +396,7 @@
  * to the ambiguous may-have-children state.
  */
 DirectoryItem.prototype.clearHasChildren = function() {
-  var rowItem = this.firstElementChild;
+  const rowItem = this.firstElementChild;
   this.removeAttribute('has-children');
   rowItem.removeAttribute('has-children');
 };
@@ -414,38 +414,21 @@
   }
   this.updateSubDirectories(
       true /* recursive */,
-      function() {
+      () => {
         if (!this.insideDrive)
           return;
         this.parentTree_.metadataModel_.get(
             this.entries_,
             constants.LIST_CONTAINER_METADATA_PREFETCH_PROPERTY_NAMES);
-      }.bind(this),
-      function() {
+      },
+      () => {
         this.expanded = false;
-      }.bind(this));
+      });
 
   e.stopPropagation();
 };
 
 /**
- * Returns all (recursive) visible children of parentItem.
- * @param {DirectoryItem} parentItem The parent item to start searching from.
- * @return {!Array<Entry>}
- */
-let getVisibleChildEntries = function(parentItem) {
-  if (parentItem.expanded == false) {
-    return [parentItem.entry];
-  }
-  let visibleChildEntries = [];
-  for (let childItem of parentItem.items) {
-    visibleChildEntries.concat(
-        visibleChildEntries, getVisibleChildEntries(childItem));
-  }
-  return visibleChildEntries;
-};
-
-/**
  * Invoked when the item is being collapsed.
  * @param {!Event} e Event.
  * @private
@@ -462,8 +445,8 @@
     // to update recursively when items expand this proactively
     // collapses all children to avoid having to traverse large
     // parts of the tree when reopened.
-    for (var i = 0; i < this.items.length; i++) {
-      var item = this.items[i];
+    for (let i = 0; i < this.items.length; i++) {
+      const item = this.items[i];
 
       if (item.expanded) {
         item.expanded = false;
@@ -493,7 +476,7 @@
 
   // If this is DriveVolumeItem, the UMA has already been recorded.
   if (!(this instanceof DriveVolumeItem)) {
-    var location = this.tree.volumeManager.getLocationInfo(this.entry);
+    const location = this.tree.volumeManager.getLocationInfo(this.entry);
     DirectoryItemTreeBaseMethods.recordUMASelectedEntry.call(
         this, e, location.rootType, location.isRootEntry);
   }
@@ -513,34 +496,34 @@
     return;
   }
 
-  var sortEntries = function(fileFilter, entries) {
+  const sortEntries = (fileFilter, entries) => {
     entries.sort(util.compareName);
     return entries.filter(fileFilter.filter.bind(fileFilter));
   };
 
-  var onSuccess = function(entries) {
+  const onSuccess = (entries) => {
     this.entries_ = entries;
     this.updateSubElementsFromList(recursive);
     opt_successCallback && opt_successCallback();
-  }.bind(this);
+  };
 
-  var reader = this.entry.createReader();
-  var entries = [];
-  var readEntry = function() {
-    reader.readEntries(function(results) {
+  const reader = this.entry.createReader();
+  const entries = [];
+  const readEntry = () => {
+    reader.readEntries((results) => {
       if (!results.length) {
         onSuccess(sortEntries(this.fileFilter_, entries));
         return;
       }
 
-      for (var i = 0; i < results.length; i++) {
-        var entry = results[i];
+      for (let i = 0; i < results.length; i++) {
+        const entry = results[i];
         if (entry.isDirectory)
           entries.push(entry);
       }
       readEntry();
-    }.bind(this));
-  }.bind(this);
+    });
+  };
   readEntry();
 };
 
@@ -558,8 +541,8 @@
   }
 
   // Traverse the entire subtree to find the changed element.
-  for (var i = 0; i < this.items.length; i++) {
-    var item = this.items[i];
+  for (let i = 0; i < this.items.length; i++) {
+    const item = this.items[i];
     if (!item.entry)
       continue;
     if (util.isDescendantEntry(item.entry, changedDirectoryEntry) ||
@@ -573,8 +556,7 @@
 /**
  * Update the icon based on whether the folder is shared on Drive.
  */
-DirectoryItem.prototype.updateSharedStatusIcon = function() {
-};
+DirectoryItem.prototype.updateSharedStatusIcon = function() {};
 
 /**
  * Select the item corresponding to the given {@code entry}.
@@ -627,7 +609,7 @@
  * @constructor
  */
 function SubDirectoryItem(label, dirEntry, parentDirItem, tree) {
-  var item = new DirectoryItem(label, tree);
+  const item = new DirectoryItem(label, tree);
   item.__proto__ = SubDirectoryItem.prototype;
 
   if (window.IN_TEST)
@@ -641,9 +623,9 @@
   }
 
   // Sets up icons of the item.
-  var icon = item.querySelector('.icon');
+  const icon = item.querySelector('.icon');
   icon.classList.add('item-icon');
-  var location = tree.volumeManager.getLocationInfo(item.entry);
+  const location = tree.volumeManager.getLocationInfo(item.entry);
   if (location && location.rootType && location.isRootEntry) {
     icon.setAttribute('volume-type-icon', location.rootType);
     if (window.IN_TEST && location.volumeInfo) {
@@ -687,7 +669,7 @@
  * @override
  */
 SubDirectoryItem.prototype.updateSharedStatusIcon = function() {
-  var icon = this.querySelector('.icon');
+  const icon = this.querySelector('.icon');
   const metadata =
       this.parentTree_.metadataModel.getCache([this.dirEntry_], ['shared']);
   icon.classList.toggle('shared', !!(metadata[0] && metadata[0].shared));
@@ -704,7 +686,7 @@
  * @constructor
  */
 function EntryListItem(rootType, modelItem, tree) {
-  var item = new DirectoryItem(modelItem.label, tree);
+  const item = new DirectoryItem(modelItem.label, tree);
   // Get the original label id defined by TreeItem, before overwriting
   // prototype.
   item.__proto__ = EntryListItem.prototype;
@@ -717,7 +699,7 @@
   item.dirEntry_ = modelItem.entry;
   item.parentTree_ = tree;
 
-  var icon = queryRequiredElement('.icon', item);
+  const icon = queryRequiredElement('.icon', item);
   icon.classList.add('item-icon');
   icon.setAttribute('root-type-icon', rootType);
   return item;
@@ -797,7 +779,7 @@
  * @constructor
  */
 function VolumeItem(modelItem, tree) {
-  var item = /** @type {VolumeItem} */ (
+  const item = /** @type {VolumeItem} */ (
       new DirectoryItem(modelItem.volumeInfo.label, tree));
   item.__proto__ = VolumeItem.prototype;
 
@@ -833,7 +815,7 @@
     item.setContextMenu_(tree.contextMenuForRootItems);
 
   // Populate children of this volume using resolved display root.
-  item.volumeInfo_.resolveDisplayRoot(function(displayRoot) {
+  item.volumeInfo_.resolveDisplayRoot((displayRoot) => {
     item.updateSubDirectories(false /* recursive */);
   });
 
@@ -894,8 +876,8 @@
  * @override
  */
 VolumeItem.prototype.activate = function() {
-  var directoryModel = this.parentTree_.directoryModel;
-  var onEntryResolved = function(entry) {
+  const directoryModel = this.parentTree_.directoryModel;
+  const onEntryResolved = (entry) => {
     // Changes directory to the model item's root directory if needed.
     if (!util.isSameEntry(directoryModel.getCurrentDirEntry(), entry)) {
       metrics.recordUserAction('FolderShortcut.Navigate');
@@ -904,14 +886,14 @@
     // In case of failure in resolveDisplayRoot() in the volume's constructor,
     // update the volume's children here.
     this.updateSubDirectories(false);
-  }.bind(this);
+  };
 
   this.volumeInfo_.resolveDisplayRoot(
       onEntryResolved,
-      function() {
+      () => {
         // Error, the display root is not available. It may happen on Drive.
         this.parentTree_.dataModel.onItemNotFoundError(this.modelItem);
-      }.bind(this));
+      });
 };
 
 /**
@@ -922,7 +904,7 @@
  */
 VolumeItem.prototype.setupIcon_ = function(icon, volumeInfo) {
   icon.classList.add('item-icon');
-  var backgroundImage =
+  const backgroundImage =
       util.iconSetToCSSBackgroundImageValue(volumeInfo.iconSet);
   if (backgroundImage !== 'none') {
     // The icon div is not yet added to DOM, therefore it is impossible to
@@ -947,29 +929,29 @@
  * @private
  */
 VolumeItem.prototype.setupEjectButton_ = function(rowElement) {
-  var ejectButton = cr.doc.createElement('button');
+  const ejectButton = cr.doc.createElement('button');
   // Block other mouse handlers.
-  ejectButton.addEventListener('mouseup', function(event) {
+  ejectButton.addEventListener('mouseup', (event) => {
     event.stopPropagation();
   });
-  ejectButton.addEventListener('mousedown', function(event) {
+  ejectButton.addEventListener('mousedown', (event) => {
     event.stopPropagation();
   });
   ejectButton.className = 'root-eject';
   ejectButton.setAttribute('aria-label', str('UNMOUNT_DEVICE_BUTTON_LABEL'));
   ejectButton.setAttribute('tabindex', '0');
-  ejectButton.addEventListener('click', function(event) {
+  ejectButton.addEventListener('click', (event) => {
     event.stopPropagation();
-    var unmountCommand = cr.doc.querySelector('command#unmount');
+    const unmountCommand = cr.doc.querySelector('command#unmount');
     // Let's make sure 'canExecute' state of the command is properly set for
     // the root before executing it.
     unmountCommand.canExecuteChange(this);
     unmountCommand.execute(this);
-  }.bind(this));
+  });
   rowElement.appendChild(ejectButton);
 
   // Add paper-ripple effect on the eject button.
-  var ripple = cr.doc.createElement('paper-ripple');
+  const ripple = cr.doc.createElement('paper-ripple');
   ripple.setAttribute('fit', '');
   ripple.className = 'circle recenteringTouch';
   ejectButton.appendChild(ripple);
@@ -981,7 +963,7 @@
  * @private
  */
 VolumeItem.prototype.setupRenamePlaceholder_ = function(rowElement) {
-  var placeholder = cr.doc.createElement('span');
+  const placeholder = cr.doc.createElement('span');
   placeholder.className = 'rename-placeholder';
   rowElement.appendChild(placeholder);
 };
@@ -1000,7 +982,7 @@
  * @constructor
  */
 function DriveVolumeItem(modelItem, tree) {
-  var item = new VolumeItem(modelItem, tree);
+  const item = new VolumeItem(modelItem, tree);
   item.__proto__ = DriveVolumeItem.prototype;
   item.classList.add('drive-volume');
   if (window.IN_TEST)
@@ -1038,9 +1020,9 @@
   if (!e.target.classList.contains('expand-icon')) {
     // If the Drive volume is clicked, select one of the children instead of
     // this item itself.
-    this.volumeInfo_.resolveDisplayRoot(function(displayRoot) {
+    this.volumeInfo_.resolveDisplayRoot((displayRoot) => {
       this.searchAndSelectByEntry(displayRoot);
-    }.bind(this));
+    });
   }
 
   DirectoryItemTreeBaseMethods.recordUMASelectedEntry.call(
@@ -1061,7 +1043,7 @@
  * @private
  */
 DriveVolumeItem.prototype.createTeamDrivesGrandRoot_ = function() {
-  return new Promise(resolve => {
+  return new Promise((resolve) => {
     const teamDriveGrandRoot = this.volumeInfo_.teamDriveDisplayRoot;
     if (!teamDriveGrandRoot) {
       // Team Drive is disabled.
@@ -1070,7 +1052,7 @@
     }
 
     let index;
-    for (var i = 0; i < this.items.length; i++) {
+    for (let i = 0; i < this.items.length; i++) {
       const entry = this.items[i] && this.items[i].entry;
       if (entry && util.isSameEntry(entry, teamDriveGrandRoot)) {
         index = i;
@@ -1079,7 +1061,7 @@
     }
 
     const reader = teamDriveGrandRoot.createReader();
-    reader.readEntries(results => {
+    reader.readEntries((results) => {
       metrics.recordSmallCount('TeamDrivesCount', results.length);
       // Only create grand root if there is at least 1 child/result.
       if (results.length) {
@@ -1127,7 +1109,7 @@
  * @private
  */
 DriveVolumeItem.prototype.createComputersGrandRoot_ = function() {
-  return new Promise(resolve => {
+  return new Promise((resolve) => {
     const computerGrandRoot = this.volumeInfo_.computersDisplayRoot;
     if (!computerGrandRoot) {
       // Computer is disabled.
@@ -1192,25 +1174,25 @@
   if (!this.entry || this.hasChildren)
     return;
 
-  var entries = [this.entry];
+  let entries = [this.entry];
 
-  var teamDrivesDisplayRoot = this.volumeInfo_.teamDriveDisplayRoot;
+  const teamDrivesDisplayRoot = this.volumeInfo_.teamDriveDisplayRoot;
   if (!!teamDrivesDisplayRoot) {
     entries.push(teamDrivesDisplayRoot);
   }
 
-  var computersDisplayRoot = this.volumeInfo_.computersDisplayRoot;
+  const computersDisplayRoot = this.volumeInfo_.computersDisplayRoot;
   if (!!computersDisplayRoot) {
     entries.push(computersDisplayRoot);
   }
 
   // Drive volume has children including fake entries (offline, recent, ...)
-  var fakeEntries = [];
+  const fakeEntries = [];
   if (this.parentTree_.fakeEntriesVisible_) {
-    for (var key in this.volumeInfo_.fakeEntries)
+    for (const key in this.volumeInfo_.fakeEntries)
       fakeEntries.push(this.volumeInfo_.fakeEntries[key]);
     // This list is sorted by URL on purpose.
-    fakeEntries.sort(function(a, b) {
+    fakeEntries.sort((a, b) => {
       if (a.toURL() === b.toURL())
         return 0;
       return b.toURL() > a.toURL() ? 1 : -1;
@@ -1218,7 +1200,7 @@
     entries = entries.concat(fakeEntries);
   }
 
-  for (var i = 0; i < entries.length; i++) {
+  for (let i = 0; i < entries.length; i++) {
     // Only create the team drives root if there is at least 1 team drive.
     const entry = entries[i];
     if (entry === teamDrivesDisplayRoot) {
@@ -1251,7 +1233,7 @@
   // If Team Drive grand root has been removed and we receive an update for an
   // team drive, we need to create the Team Drive grand root.
   if (isTeamDriveChild) {
-    this.createTeamDrivesGrandRoot_().then(teamDriveGrandRootItem => {
+    this.createTeamDrivesGrandRoot_().then((teamDriveGrandRootItem) => {
       if (teamDriveGrandRootItem)
         teamDriveGrandRootItem.updateItemByEntry(changedDirectoryEntry);
     });
@@ -1262,7 +1244,7 @@
   // If Computers grand root has been removed and we receive an update for an
   // computer, we need to create the Computers grand root.
   if (isComputersChild) {
-    this.createComputersGrandRoot_().then(computersGrandRootItem => {
+    this.createComputersGrandRoot_().then((computersGrandRootItem) => {
       if (computersGrandRootItem)
         computersGrandRootItem.updateItemByEntry(changedDirectoryEntry);
     });
@@ -1318,10 +1300,10 @@
  * @constructor
  */
 function ShortcutItem(modelItem, tree) {
-  var item = /** @type {ShortcutItem} */ (new cr.ui.TreeItem());
+  const item = /** @type {ShortcutItem} */ (new cr.ui.TreeItem());
   // Get the original label id defined by TreeItem, before overwriting
   // prototype.
-  var labelId = item.labelElement.id;
+  const labelId = item.labelElement.id;
   item.__proto__ = ShortcutItem.prototype;
 
   if (window.IN_TEST)
@@ -1333,7 +1315,7 @@
   item.innerHTML = TREE_ITEM_INNER_HTML;
   item.labelElement.id = labelId;
 
-  var icon = item.querySelector('.icon');
+  const icon = item.querySelector('.icon');
   icon.classList.add('item-icon');
   icon.setAttribute('volume-type-icon', 'shortcut');
 
@@ -1392,7 +1374,7 @@
   // Resets file selection when a volume is clicked.
   this.parentTree_.directoryModel.clearSelection();
 
-  var location = this.tree.volumeManager.getLocationInfo(this.entry);
+  const location = this.tree.volumeManager.getLocationInfo(this.entry);
   DirectoryItemTreeBaseMethods.recordUMASelectedEntry.call(
       this, e, location.rootType, location.isRootEntry);
 };
@@ -1419,26 +1401,26 @@
  * Change current entry to the entry corresponding to this shortcut.
  */
 ShortcutItem.prototype.activate = function() {
-  var directoryModel = this.parentTree_.directoryModel;
-  var onEntryResolved = function(entry) {
+  const directoryModel = this.parentTree_.directoryModel;
+  const onEntryResolved = (entry) => {
     // Changes directory to the model item's root directory if needed.
     if (!util.isSameEntry(directoryModel.getCurrentDirEntry(), entry)) {
       metrics.recordUserAction('FolderShortcut.Navigate');
       directoryModel.changeDirectoryEntry(entry);
     }
-  }.bind(this);
+  };
 
   // For shortcuts we already have an Entry, but it has to be resolved again
   // in case, it points to a non-existing directory.
   window.webkitResolveLocalFileSystemURL(
       this.entry.toURL(),
       onEntryResolved,
-      function() {
+      () => {
         // Error, the entry can't be re-resolved. It may happen for shortcuts
         // which targets got removed after resolving the Entry during
         // initialization.
         this.parentTree_.dataModel.onItemNotFoundError(this.modelItem);
-      }.bind(this));
+      });
 };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1453,10 +1435,10 @@
  * @constructor
  */
 function FakeItem(rootType, modelItem, tree) {
-  var item = new cr.ui.TreeItem();
+  const item = new cr.ui.TreeItem();
   // Get the original label id defined by TreeItem, before overwriting
   // prototype.
-  var labelId = item.labelElement.id;
+  const labelId = item.labelElement.id;
   item.__proto__ = FakeItem.prototype;
   if (window.IN_TEST) {
     item.setAttribute('dir-type', 'FakeItem');
@@ -1472,7 +1454,7 @@
   item.label = modelItem.label;
   item.directoryModel_ = tree.directoryModel;
 
-  var icon = queryRequiredElement('.icon', item);
+  const icon = queryRequiredElement('.icon', item);
   icon.classList.add('item-icon');
   icon.setAttribute('root-type-icon', rootType);
 
@@ -1686,13 +1668,13 @@
 DirectoryTree.prototype.updateAndSelectNewDirectory = function(
     parentDirectory, newDirectory) {
   // Expand parent directory.
-  var parentItem = DirectoryItemTreeBaseMethods.getItemByEntry.call(
-      this, parentDirectory);
+  const parentItem =
+      DirectoryItemTreeBaseMethods.getItemByEntry.call(this, parentDirectory);
   parentItem.expanded = true;
 
   // If new directory is already added to the tree, just select it.
-  for (var i = 0; i < parentItem.items.length; i++) {
-    var item = parentItem.items[i];
+  for (let i = 0; i < parentItem.items.length; i++) {
+    const item = parentItem.items[i];
     if (util.isSameEntry(item.entry, newDirectory)) {
       this.selectedItem = item;
       return;
@@ -1700,10 +1682,10 @@
   }
 
   // Create new item, and add it.
-  var newDirectoryItem = new SubDirectoryItem(
-      newDirectory.name, newDirectory, parentItem, this);
+  const newDirectoryItem =
+      new SubDirectoryItem(newDirectory.name, newDirectory, parentItem, this);
 
-  var addAt = 0;
+  let addAt = 0;
   while (addAt < parentItem.items.length &&
       parentItem.items[addAt].entry.name < newDirectory.name) {
     addAt++;
@@ -1794,10 +1776,10 @@
  */
 DirectoryTree.prototype.searchAndSelectByEntry = function(entry) {
   // If the |entry| is same as one of volumes or shortcuts, select it.
-  for (var i = 0; i < this.items.length; i++) {
+  for (let i = 0; i < this.items.length; i++) {
     // Skips the Drive root volume. For Drive entries, one of children of Drive
     // root or shortcuts should be selected.
-    var item = this.items[i];
+    const item = this.items[i];
     if (item instanceof DriveVolumeItem)
       continue;
 
@@ -1807,8 +1789,8 @@
     }
   }
   // Otherwise, search whole tree.
-  var found = DirectoryItemTreeBaseMethods.searchAndSelectByEntry.call(
-      this, entry);
+  const found =
+      DirectoryItemTreeBaseMethods.searchAndSelectByEntry.call(this, entry);
   return found;
 };
 
@@ -1871,7 +1853,7 @@
  * @private
  */
 DirectoryTree.prototype.onEntriesChanged_ = function(event) {
-  var directories = event.entries.filter((entry) => entry.isDirectory);
+  const directories = event.entries.filter((entry) => entry.isDirectory);
 
   if (directories.length === 0)
     return;
@@ -1882,10 +1864,10 @@
       Promise.all(
           directories.map((directory) =>
             new Promise(directory.getParent.bind(directory))))
-          .then(function(parentDirectories) {
+          .then((parentDirectories) => {
         parentDirectories.forEach((parentDirectory) =>
             this.updateTreeByEntry_(parentDirectory));
-      }.bind(this));
+      });
       break;
     case util.EntryChangedKind.DELETED:
       directories.forEach((directory) => this.updateTreeByEntry_(directory));
@@ -1908,16 +1890,16 @@
     return;
 
   this.updateSubDirectories(false /* recursive */);
-  var currentSequence = ++this.sequence_;
-  var volumeInfo = this.volumeManager_.getVolumeInfo(entry);
+  const currentSequence = ++this.sequence_;
+  const volumeInfo = this.volumeManager_.getVolumeInfo(entry);
   if (!volumeInfo)
     return;
-  volumeInfo.resolveDisplayRoot(function() {
+  volumeInfo.resolveDisplayRoot(() => {
     if (this.sequence_ !== currentSequence)
       return;
     if (!this.searchAndSelectByEntry(entry))
       this.selectedItem = null;
-  }.bind(this));
+  });
 };
 
 /**
@@ -1989,41 +1971,41 @@
  */
 DirectoryTree.prototype.updateTreeByEntry_ = function(entry) {
   entry.getDirectory(entry.fullPath, {create: false},
-      function() {
+      () => {
         // If entry exists.
         // e.g. /a/b is deleted while watching /a.
-        for (var i = 0; i < this.items.length; i++) {
+        for (let i = 0; i < this.items.length; i++) {
           if (this.items[i] instanceof VolumeItem ||
               this.items[i] instanceof EntryListItem)
             this.items[i].updateItemByEntry(entry);
         }
-      }.bind(this),
-      function() {
+      },
+      () => {
         // If entry does not exist, try to get parent and update the subtree by
         // it.
         // e.g. /a/b is deleted while watching /a/b. Try to update /a in this
         //     case.
-        entry.getParent(function(parentEntry) {
+        entry.getParent((parentEntry) => {
           this.updateTreeByEntry_(parentEntry);
-        }.bind(this), function(error) {
+        }, (error) => {
           // If it fails to get parent, update the subtree by volume.
           // e.g. /a/b is deleted while watching /a/b/c. getParent of /a/b/c
           //     fails in this case. We falls back to volume update.
           //
           // TODO(yawano): Try to get parent path also in this case by
           //     manipulating path string.
-          var volumeInfo = this.volumeManager.getVolumeInfo(entry);
+          const volumeInfo = this.volumeManager.getVolumeInfo(entry);
           if (!volumeInfo)
             return;
 
-          for (var i = 0; i < this.items.length; i++) {
+          for (let i = 0; i < this.items.length; i++) {
             if (this.items[i] instanceof VolumeItem &&
                 this.items[i].volumeInfo === volumeInfo) {
               this.items[i].updateSubDirectories(true /* recursive */);
             }
           }
-        }.bind(this));
-      }.bind(this));
+        });
+      });
 };
 
 /**
@@ -2040,16 +2022,16 @@
  * @private
  */
 DirectoryTree.prototype.onListContentChanged_ = function() {
-  this.updateSubDirectories(false, function() {
+  this.updateSubDirectories(false, () => {
     // If no item is selected now, try to select the item corresponding to
     // current directory because the current directory might have been populated
     // in this tree in previous updateSubDirectories().
     if (!this.selectedItem) {
-      var currentDir = this.directoryModel_.getCurrentDirEntry();
+      const currentDir = this.directoryModel_.getCurrentDirEntry();
       if (currentDir)
         this.selectByEntry(currentDir);
     }
-  }.bind(this));
+  });
 };
 
 /**
diff --git a/ui/file_manager/image_loader/request.js b/ui/file_manager/image_loader/request.js
index 5d4f22f1..0a6d69b 100644
--- a/ui/file_manager/image_loader/request.js
+++ b/ui/file_manager/image_loader/request.js
@@ -330,6 +330,7 @@
           video.currentTime = ImageRequest.VIDEO_THUMBNAIL_POSITION;
           video.preload = 'auto';
           video.src = url;
+          video.load();
         }),
         new Promise((resolve) => {
           setTimeout(resolve, ImageRequest.MAX_MILLISECONDS_TO_LOAD_VIDEO);
diff --git a/ui/file_manager/integration_tests/audio_player/click_control_buttons.js b/ui/file_manager/integration_tests/audio_player/click_control_buttons.js
index 74fa0ca..300657a 100644
--- a/ui/file_manager/integration_tests/audio_player/click_control_buttons.js
+++ b/ui/file_manager/integration_tests/audio_player/click_control_buttons.js
@@ -33,41 +33,75 @@
 testcase.togglePlayState = function() {
   var openAudio = launch('local', 'downloads', [ENTRIES.beautiful]);
   var appId;
-  return openAudio.then(function(args) {
-    appId = args[0];
-  }).then(function() {
-    // Audio player should start playing automatically,
-    return remoteCallAudioPlayer.waitForElement(
-        appId, 'audio-player[playing]');
-  }).then(function() {
-    // .. and the play button label should be 'Pause'.
-    return remoteCallAudioPlayer.waitForElement(
-        appId, [controlPanelQuery('#play[aria-label="Pause"]')]);
-  }).then(function() {
-    // Clicking on the play button should
-    return remoteCallAudioPlayer.callRemoteTestUtil(
-        'fakeMouseClick', appId, [controlPanelQuery('#play')]);
-  }).then(function() {
-    // ... change the audio playback state to pause,
-    return remoteCallAudioPlayer.waitForElement(
-        appId, 'audio-player:not([playing])');
-  }).then(function() {
-    // ... and the play button label should be 'Play'.
-    return remoteCallAudioPlayer.waitForElement(
-        appId, [controlPanelQuery('#play[aria-label="Play"]')]);
-  }).then(function() {
-    // Clicking on the play button again should
-    return remoteCallAudioPlayer.callRemoteTestUtil(
-        'fakeMouseClick', appId, [controlPanelQuery('#play')]);
-  }).then(function() {
-    // ... change the audio playback state to playing,
-    return remoteCallAudioPlayer.waitForElement(
-        appId, 'audio-player[playing]');
-  }).then(function() {
-    // ... and the play button label should be 'Pause'.
-    return remoteCallAudioPlayer.waitForElement(
-        appId, [controlPanelQuery('#play[aria-label="Pause"]')]);
-  });
+  return openAudio
+      .then(function(args) {
+        appId = args[0];
+      })
+      .then(function() {
+        // Audio player should start playing automatically,
+        return remoteCallAudioPlayer.waitForElement(
+            appId, 'audio-player[playing]');
+      })
+      .then(function() {
+        // .. and the play button label should be 'Pause'.
+        return remoteCallAudioPlayer.waitForElement(
+            appId, [controlPanelQuery('#play[aria-label="Pause"]')]);
+      })
+      .then(function() {
+
+        // First test a media key, before any element may have acquired focus.
+        return remoteCallAudioPlayer.fakeKeyDown(
+            appId, null, 'MediaPlayPause', false, false, false);
+      })
+      .then(function(result) {
+        chrome.test.assertTrue(!!result, 'fakeKeyDown failed.');
+        return remoteCallAudioPlayer.waitForElement(
+            appId, 'audio-player:not([playing])');
+      })
+      .then(function(element) {
+        chrome.test.assertTrue(!!element);
+        return remoteCallAudioPlayer.fakeKeyDown(
+            appId, null, 'MediaPlayPause', false, false, false);
+      })
+      .then(function(result) {
+        chrome.test.assertTrue(!!result, 'fakeKeyDown failed.');
+        return remoteCallAudioPlayer.waitForElement(
+            appId, 'audio-player[playing]');
+      })
+      .then(function(element) {
+        chrome.test.assertTrue(!!element);
+
+        // Clicking on the play button should Play.
+        return remoteCallAudioPlayer.callRemoteTestUtil(
+            'fakeMouseClick', appId, [controlPanelQuery('#play')]);
+      })
+      .then(function(result) {
+        chrome.test.assertTrue(!!result, 'fakeMouseClick failed.');
+        // ... change the audio playback state to pause,
+        return remoteCallAudioPlayer.waitForElement(
+            appId, 'audio-player:not([playing])');
+      })
+      .then(function() {
+        // ... and the play button label should be 'Play'.
+        return remoteCallAudioPlayer.waitForElement(
+            appId, [controlPanelQuery('#play[aria-label="Play"]')]);
+      })
+      .then(function() {
+        // Clicking on the play button again should
+        return remoteCallAudioPlayer.callRemoteTestUtil(
+            'fakeMouseClick', appId, [controlPanelQuery('#play')]);
+      })
+      .then(function(result) {
+        chrome.test.assertTrue(!!result, 'fakeMouseClick failed.');
+        // ... change the audio playback state to playing,
+        return remoteCallAudioPlayer.waitForElement(
+            appId, 'audio-player[playing]');
+      })
+      .then(function() {
+        // ... and the play button label should be 'Pause'.
+        return remoteCallAudioPlayer.waitForElement(
+            appId, [controlPanelQuery('#play[aria-label="Pause"]')]);
+      });
 };
 
 /**
diff --git a/ui/file_manager/integration_tests/file_manager/zip_files.js b/ui/file_manager/integration_tests/file_manager/zip_files.js
index da6817187..e9cf6df 100644
--- a/ui/file_manager/integration_tests/file_manager/zip_files.js
+++ b/ui/file_manager/integration_tests/file_manager/zip_files.js
@@ -44,6 +44,26 @@
 }
 
 /**
+ * Returns the expected file list row entries after opening (unzipping) the
+ * ENTRIES.zipArchiveWithAbsolutePaths file list entry.
+ */
+function getUnzippedFileListRowEntriesAbsolutePathsRoot() {
+  return [
+    ['foo', '--', 'Folder', 'Oct 11, 2018, 9:44 AM'],
+    ['hello.txt', '13 bytes', 'Plain text', 'Oct 11, 2018, 9:44 AM']
+  ];
+}
+
+/**
+ * Returns the expected file list row entries after opening (unzipping) the
+ * ENTRIES.zipArchiveWithAbsolutePaths file list entry and moving into the
+ * subdirectory.
+ */
+function getUnzippedFileListRowEntriesAbsolutePathsSubdir() {
+  return [['bye.txt', '9 bytes', 'Plain text', 'Oct 11, 2018, 9:44 AM']];
+}
+
+/**
  * Tests zip file open (aka unzip) from Downloads.
  */
 testcase.zipFileOpenDownloads = function() {
@@ -77,6 +97,57 @@
 };
 
 /**
+ * Tests zip file, with absolute paths, open (aka unzip) from Downloads.
+ */
+testcase.zipFileOpenDownloadsWithAbsolutePaths = function() {
+  let appId;
+
+  StepsRunner.run([
+    // Open Files app on Downloads containing a zip file.
+    function() {
+      setupAndWaitUntilReady(
+          null, RootPath.DOWNLOADS, this.next,
+          [ENTRIES.zipArchiveWithAbsolutePaths], []);
+    },
+    // Select the zip file.
+    function(result) {
+      appId = result.windowId;
+      remoteCall.callRemoteTestUtil('selectFile', appId, ['absolute_paths.zip'])
+          .then(this.next);
+    },
+    // Press the Enter key.
+    function(result) {
+      chrome.test.assertTrue(!!result, 'selectFile failed');
+      const key = ['#file-list', 'Enter', false, false, false];
+      remoteCall.callRemoteTestUtil('fakeKeyDown', appId, key, this.next);
+    },
+    // Check: the zip file content should be shown (unzip).
+    function(result) {
+      chrome.test.assertTrue(!!result, 'fakeKeyDown failed');
+      const files = getUnzippedFileListRowEntriesAbsolutePathsRoot();
+      remoteCall.waitForFiles(appId, files).then(this.next);
+    },
+    // Select the directory in the ZIP file.
+    function(result) {
+      remoteCall.callRemoteTestUtil('selectFile', appId, ['foo'])
+          .then(this.next);
+    },
+    // Press the Enter key.
+    function(result) {
+      chrome.test.assertTrue(!!result, 'selectFile failed');
+      const key = ['#file-list', 'Enter', false, false, false];
+      remoteCall.callRemoteTestUtil('fakeKeyDown', appId, key, this.next);
+    },
+    // Check: the zip file content should be shown (unzip).
+    function(result) {
+      chrome.test.assertTrue(!!result, 'fakeKeyDown failed');
+      const files = getUnzippedFileListRowEntriesAbsolutePathsSubdir();
+      remoteCall.waitForFiles(appId, files).then(this.next);
+    },
+  ]);
+};
+
+/**
  * Tests zip file open (aka unzip) from Google Drive.
  */
 testcase.zipFileOpenDrive = function() {
diff --git a/ui/file_manager/integration_tests/test_util.js b/ui/file_manager/integration_tests/test_util.js
index 15a7a7f..36f9393 100644
--- a/ui/file_manager/integration_tests/test_util.js
+++ b/ui/file_manager/integration_tests/test_util.js
@@ -643,6 +643,17 @@
     typeText: 'Zip archive'
   }),
 
+  zipArchiveWithAbsolutePaths: new TestEntryInfo({
+    type: EntryType.FILE,
+    sourceFileName: 'absolute_paths.zip',
+    targetPath: 'absolute_paths.zip',
+    mimeType: 'application/x-zip',
+    lastModifiedTime: 'Jan 1, 2014, 1:00 AM',
+    nameText: 'absolute_paths.zip',
+    sizeText: '400 bytes',
+    typeText: 'Zip archive'
+  }),
+
   debPackage: new TestEntryInfo({
     type: EntryType.FILE,
     sourceFileName: 'package.deb',
diff --git a/ui/message_center/views/message_view.cc b/ui/message_center/views/message_view.cc
index 0708aaa..0652bc73 100644
--- a/ui/message_center/views/message_view.cc
+++ b/ui/message_center/views/message_view.cc
@@ -313,7 +313,7 @@
   return is_nested_ ? layer() : GetWidget()->GetLayer();
 }
 
-void MessageView::OnSlideChanged() {
+void MessageView::OnSlideChanged(bool in_progress) {
   for (auto* observer : slide_observers_) {
     observer->OnSlideChanged(notification_id_);
   }
diff --git a/ui/message_center/views/message_view.h b/ui/message_center/views/message_view.h
index 1332544..c49da09 100644
--- a/ui/message_center/views/message_view.h
+++ b/ui/message_center/views/message_view.h
@@ -126,7 +126,7 @@
 
   // message_center::SlideOutController::Delegate:
   ui::Layer* GetSlideOutLayer() override;
-  void OnSlideChanged() override;
+  void OnSlideChanged(bool in_progress) override;
   void OnSlideOut() override;
 
   // views::FocusChangeListener:
diff --git a/ui/message_center/views/slide_out_controller.cc b/ui/message_center/views/slide_out_controller.cc
index e8c2c17..18134bd 100644
--- a/ui/message_center/views/slide_out_controller.cc
+++ b/ui/message_center/views/slide_out_controller.cc
@@ -49,7 +49,6 @@
     }
     CaptureControlOpenState();
     RestoreVisualState();
-    delegate_->OnSlideChanged();
     return;
   }
 
@@ -70,6 +69,7 @@
       default:
         NOTREACHED();
     }
+    delegate_->OnSlideChanged(true);
   } else if (event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
     // The scroll-update events include the incremental scroll amount.
     gesture_amount_ += event->details().scroll_x();
@@ -102,6 +102,7 @@
     gfx::Transform transform;
     transform.Translate(scroll_amount, 0.0);
     layer->SetTransform(transform);
+    delegate_->OnSlideChanged(true);
   } else if (event->type() == ui::ET_GESTURE_SCROLL_END) {
     float scrolled_ratio = fabsf(gesture_amount_) / width;
     if (mode_ == SlideMode::FULL &&
@@ -114,7 +115,6 @@
     RestoreVisualState();
   }
 
-  delegate_->OnSlideChanged();
   event->SetHandled();
 }
 
@@ -125,6 +125,7 @@
   ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
   settings.SetTransitionDuration(
       base::TimeDelta::FromMilliseconds(kSwipeRestoreDurationMS));
+  settings.AddObserver(this);
   gfx::Transform transform;
   switch (control_open_state_) {
     case SwipeControlOpenState::CLOSED:
@@ -137,8 +138,20 @@
       transform.Translate(swipe_control_width_, 0);
       break;
   }
+
+  if (layer->transform() == transform && layer->opacity() == 1.f) {
+    // Here, nothing are changed and no animation starts. In this case, just
+    // calls OnSlideChanged(in_progress = false) to notify end of horizontal
+    // slide (including animations) to observers.
+    delegate_->OnSlideChanged(false);
+    return;
+  }
+
+  // In this case, animation starts. OnImplicitAnimationsCompleted will be
+  // called just after the animation finishes.
   layer->SetTransform(transform);
   layer->SetOpacity(1.f);
+  delegate_->OnSlideChanged(true);
 }
 
 void SlideOutController::SlideOutAndClose(int direction) {
@@ -153,14 +166,22 @@
   gfx::Transform transform;
   int width = layer->bounds().width();
   transform.Translate(direction < 0 ? -width : width, 0.0);
+
+  // An animation starts. OnImplicitAnimationsCompleted will be called just
+  // after the animation finishes.
   layer->SetTransform(transform);
   layer->SetOpacity(0.f);
-  delegate_->OnSlideChanged();
+  delegate_->OnSlideChanged(true);
 }
 
 void SlideOutController::OnImplicitAnimationsCompleted() {
-  delegate_->OnSlideChanged();
-  delegate_->OnSlideOut();
+  delegate_->OnSlideChanged(false);
+
+  // Call Delegate::OnSlideOut() if this animation came from
+  // SlideOutAndClose().
+  ui::Layer* layer = delegate_->GetSlideOutLayer();
+  if (layer->opacity() == 0)
+    delegate_->OnSlideOut();
 }
 
 void SlideOutController::SetSwipeControlWidth(int swipe_control_width) {
diff --git a/ui/message_center/views/slide_out_controller.h b/ui/message_center/views/slide_out_controller.h
index e429a4f..1cd86926 100644
--- a/ui/message_center/views/slide_out_controller.h
+++ b/ui/message_center/views/slide_out_controller.h
@@ -31,7 +31,9 @@
     virtual ui::Layer* GetSlideOutLayer() = 0;
 
     // Called when a slide starts, ends, or is updated.
-    virtual void OnSlideChanged() = 0;
+    // The argument is true if the slide starts or in progress, false if it
+    // ends.
+    virtual void OnSlideChanged(bool in_progress) = 0;
 
     // Called when user intends to close the View by sliding it out.
     virtual void OnSlideOut() = 0;
diff --git a/ui/ozone/platform/wayland/wayland_window.cc b/ui/ozone/platform/wayland/wayland_window.cc
index 0d02721..3c3a590 100644
--- a/ui/ozone/platform/wayland/wayland_window.cc
+++ b/ui/ozone/platform/wayland/wayland_window.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
 #include "ui/base/hit_test.h"
 #include "ui/events/event.h"
@@ -79,9 +80,17 @@
   // Set a class property key, which allows |this| to be used for interactive
   // events, e.g. move or resize.
   SetWmMoveResizeHandler(this, AsWmMoveResizeHandler());
+
+  // Set a class property key, which allows |this| to be used for drag action.
+  SetWmDragHandler(this, this);
 }
 
 WaylandWindow::~WaylandWindow() {
+  if (drag_closed_callback_) {
+    std::move(drag_closed_callback_)
+        .Run(DragDropTypes::DragOperation::DRAG_NONE);
+  }
+
   PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
   connection_->RemoveWindow(surface_.id());
 
@@ -216,6 +225,15 @@
   connection_->ScheduleFlush();
 }
 
+void WaylandWindow::StartDrag(const ui::OSExchangeData& data,
+                              int operation,
+                              gfx::NativeCursor cursor,
+                              base::OnceCallback<void(int)> callback) {
+  DCHECK(!drag_closed_callback_);
+  drag_closed_callback_ = std::move(callback);
+  connection_->StartDrag(data, operation);
+}
+
 void WaylandWindow::Show() {
   if (!is_tooltip_)  // Tooltip windows should not get keyboard focus
     set_keyboard_focus(true);
@@ -570,7 +588,7 @@
 }
 
 void WaylandWindow::OnDragSessionClose(uint32_t dnd_action) {
-  NOTIMPLEMENTED_LOG_ONCE();
+  std::move(drag_closed_callback_).Run(dnd_action);
 }
 
 bool WaylandWindow::IsMinimized() const {
diff --git a/ui/ozone/platform/wayland/wayland_window.h b/ui/ozone/platform/wayland/wayland_window.h
index ad9eb0c..7e54fb49 100644
--- a/ui/ozone/platform/wayland/wayland_window.h
+++ b/ui/ozone/platform/wayland/wayland_window.h
@@ -5,6 +5,7 @@
 #ifndef UI_OZONE_PLATFORM_WAYLAND_WAYLAND_WINDOW_H_
 #define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_WINDOW_H_
 
+#include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "ui/events/platform/platform_event_dispatcher.h"
 #include "ui/gfx/geometry/rect.h"
@@ -12,6 +13,7 @@
 #include "ui/ozone/platform/wayland/wayland_object.h"
 #include "ui/platform_window/platform_window.h"
 #include "ui/platform_window/platform_window_delegate.h"
+#include "ui/platform_window/platform_window_handler/wm_drag_handler.h"
 #include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h"
 
 namespace gfx {
@@ -35,7 +37,8 @@
 
 class WaylandWindow : public PlatformWindow,
                       public PlatformEventDispatcher,
-                      public WmMoveResizeHandler {
+                      public WmMoveResizeHandler,
+                      public WmDragHandler {
  public:
   WaylandWindow(PlatformWindowDelegate* delegate,
                 WaylandConnection* connection);
@@ -81,6 +84,12 @@
       int hittest,
       const gfx::Point& pointer_location) override;
 
+  // WmDragHandler
+  void StartDrag(const ui::OSExchangeData& data,
+                 int operation,
+                 gfx::NativeCursor cursor,
+                 base::OnceCallback<void(int)> callback) override;
+
   // PlatformWindow
   void Show() override;
   void Hide() override;
@@ -162,6 +171,8 @@
   // The current cursor bitmap (immutable).
   scoped_refptr<BitmapCursorOzone> bitmap_;
 
+  base::OnceCallback<void(int)> drag_closed_callback_;
+
   gfx::Rect bounds_;
   gfx::Rect pending_bounds_;
   // The bounds of our window before we were maximized or fullscreen.
diff --git a/ui/platform_window/platform_window_handler/BUILD.gn b/ui/platform_window/platform_window_handler/BUILD.gn
index be75a75..65ea155 100644
--- a/ui/platform_window/platform_window_handler/BUILD.gn
+++ b/ui/platform_window/platform_window_handler/BUILD.gn
@@ -8,6 +8,8 @@
   output_name = "platform_window_handler_libs"
 
   sources = [
+    "wm_drag_handler.cc",
+    "wm_drag_handler.h",
     "wm_move_resize_handler.cc",
     "wm_move_resize_handler.h",
     "wm_platform_export.h",
diff --git a/ui/platform_window/platform_window_handler/wm_drag_handler.cc b/ui/platform_window/platform_window_handler/wm_drag_handler.cc
new file mode 100644
index 0000000..74a6d2da
--- /dev/null
+++ b/ui/platform_window/platform_window_handler/wm_drag_handler.cc
@@ -0,0 +1,25 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/platform_window/platform_window_handler/wm_drag_handler.h"
+
+#include "ui/base/class_property.h"
+#include "ui/platform_window/platform_window.h"
+
+DEFINE_UI_CLASS_PROPERTY_TYPE(ui::WmDragHandler*)
+
+namespace ui {
+
+DEFINE_UI_CLASS_PROPERTY_KEY(WmDragHandler*, kWmDragHandlerKey, nullptr);
+
+void SetWmDragHandler(PlatformWindow* platform_window,
+                      WmDragHandler* drag_handler) {
+  platform_window->SetProperty(kWmDragHandlerKey, drag_handler);
+}
+
+WmDragHandler* GetWmDragHandler(const PlatformWindow& platform_window) {
+  return platform_window.GetProperty(kWmDragHandlerKey);
+}
+
+}  // namespace ui
diff --git a/ui/platform_window/platform_window_handler/wm_drag_handler.h b/ui/platform_window/platform_window_handler/wm_drag_handler.h
new file mode 100644
index 0000000..1fc3af3
--- /dev/null
+++ b/ui/platform_window/platform_window_handler/wm_drag_handler.h
@@ -0,0 +1,39 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_DRAG_HANDLER_H_
+#define UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_DRAG_HANDLER_H_
+
+#include "base/bind.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/platform_window/platform_window_handler/wm_platform_export.h"
+
+namespace ui {
+class OSExchangeData;
+class PlatformWindow;
+
+class WM_PLATFORM_EXPORT WmDragHandler {
+ public:
+  // Starts dragging with |data| which it wants to deliver to the destination.
+  // |operation| is the suggested operation which is bitmask of DRAG_NONE,
+  // DRAG_MOVE, DRAG_COPY and DRAG_LINK in DragDropTypes::DragOperation to the
+  // destination and the destination sets the final operation when the drop
+  // action is performed.
+  virtual void StartDrag(const OSExchangeData& data,
+                         int operation,
+                         gfx::NativeCursor cursor,
+                         base::OnceCallback<void(int)> callback) = 0;
+
+ protected:
+  virtual ~WmDragHandler() {}
+};
+
+WM_PLATFORM_EXPORT void SetWmDragHandler(PlatformWindow* platform_window,
+                                         WmDragHandler* drag_handler);
+WM_PLATFORM_EXPORT WmDragHandler* GetWmDragHandler(
+    const PlatformWindow& platform_window);
+
+}  // namespace ui
+
+#endif  // UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_DRAG_HANDLER_H_
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 03eba1c9..d791e376 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -721,8 +721,12 @@
         ]
         deps += [ "//ui/events:dom_keyboard_layout" ]
       } else if (use_ozone) {
-        public += [ "widget/desktop_aura/desktop_screen_ozone.h" ]
-        sources += [ "widget/desktop_aura/desktop_screen_ozone.cc" ]
+        sources += [
+          "widget/desktop_aura/desktop_drag_drop_client_ozone.cc",
+          "widget/desktop_aura/desktop_drag_drop_client_ozone.h",
+          "widget/desktop_aura/desktop_screen_ozone.cc",
+          "widget/desktop_aura/desktop_screen_ozone.h",
+        ]
       }
       if (is_linux) {
         sources += [
@@ -1180,8 +1184,10 @@
   }
 
   if (is_linux && !is_chromeos && !use_x11) {
-    sources +=
-        [ "widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc" ]
+    sources += [
+      "widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc",
+      "widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc",
+    ]
   }
 
   if (!is_chromeos) {
@@ -1192,6 +1198,7 @@
     ":test_support",
     ":views_unittests_sources",
     "//mojo/core/embedder",
+    "//ui/platform_window/platform_window_handler",
   ]
 }
 
@@ -1245,7 +1252,7 @@
       "//ui/wm/public",
     ]
 
-    if (!is_chromeos && ((is_linux && !use_x11) || is_fuchsia)) {
+    if (!is_chromeos && (is_linux && !use_x11)) {
       sources += [ "widget/desktop_aura/desktop_window_tree_host_platform_interactive_uitest.cc" ]
     }
 
diff --git a/ui/views/controls/button/label_button.cc b/ui/views/controls/button/label_button.cc
index 0ed719ea..f9b069b 100644
--- a/ui/views/controls/button/label_button.cc
+++ b/ui/views/controls/button/label_button.cc
@@ -32,9 +32,12 @@
 #include "ui/views/window/dialog_delegate.h"
 
 namespace views {
+namespace {
+// The length of the hover fade animation.
+constexpr int kHoverAnimationDurationMs = 170;
+}  // namespace
 
 // static
-const int LabelButton::kHoverAnimationDurationMs = 170;
 const char LabelButton::kViewClassName[] = "LabelButton";
 
 LabelButton::LabelButton(ButtonListener* listener,
diff --git a/ui/views/controls/button/label_button.h b/ui/views/controls/button/label_button.h
index b2323da..1ff051a 100644
--- a/ui/views/controls/button/label_button.h
+++ b/ui/views/controls/button/label_button.h
@@ -28,9 +28,6 @@
 // LabelButton is a button with text and an icon, it's not focusable by default.
 class VIEWS_EXPORT LabelButton : public Button, public NativeThemeDelegate {
  public:
-  // The length of the hover fade animation.
-  static const int kHoverAnimationDurationMs;
-
   static const char kViewClassName[];
 
   // Creates a LabelButton with ButtonPressed() events sent to |listener| and
diff --git a/ui/views/controls/combobox/combobox.cc b/ui/views/controls/combobox/combobox.cc
index f900b99..41a5ac2b 100644
--- a/ui/views/controls/combobox/combobox.cc
+++ b/ui/views/controls/combobox/combobox.cc
@@ -4,93 +4,41 @@
 
 #include "ui/views/controls/combobox/combobox.h"
 
-#include <stddef.h>
-
-#include <utility>
-
 #include "base/logging.h"
-#include "base/macros.h"
 #include "build/build_config.h"
 #include "ui/accessibility/ax_action_data.h"
 #include "ui/accessibility/ax_node_data.h"
-#include "ui/base/default_style.h"
 #include "ui/base/ime/input_method.h"
+#include "ui/base/models/combobox_model.h"
 #include "ui/base/models/combobox_model_observer.h"
-#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/models/menu_model.h"
 #include "ui/events/event.h"
-#include "ui/gfx/animation/throb_animation.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/scoped_canvas.h"
 #include "ui/gfx/text_utils.h"
-#include "ui/native_theme/common_theme.h"
 #include "ui/native_theme/native_theme.h"
-#include "ui/native_theme/native_theme_aura.h"
-#include "ui/resources/grit/ui_resources.h"
 #include "ui/views/animation/flood_fill_ink_drop_ripple.h"
-#include "ui/views/animation/ink_drop_highlight.h"
 #include "ui/views/animation/ink_drop_impl.h"
 #include "ui/views/background.h"
-#include "ui/views/controls/button/button.h"
-#include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/combobox/combobox_listener.h"
 #include "ui/views/controls/focus_ring.h"
 #include "ui/views/controls/focusable_border.h"
 #include "ui/views/controls/menu/menu_config.h"
 #include "ui/views/controls/menu/menu_runner.h"
 #include "ui/views/controls/prefix_selector.h"
-#include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/layout/layout_provider.h"
 #include "ui/views/mouse_constants.h"
-#include "ui/views/painter.h"
-#include "ui/views/resources/grit/views_resources.h"
 #include "ui/views/style/platform_style.h"
+#include "ui/views/style/typography.h"
 #include "ui/views/widget/widget.h"
 
 namespace views {
 
 namespace {
 
-// STYLE_ACTION arrow container padding widths.
-const int kActionLeftPadding = 12;
-const int kActionRightPadding = 11;
-
-// Menu border widths
-const int kMenuBorderWidthLeft = 1;
-const int kMenuBorderWidthTop = 1;
-const int kMenuBorderWidthRight = 1;
-
-// Limit how small a combobox can be.
-const int kMinComboboxWidth = 25;
-
-// Define the id of the first item in the menu (since it needs to be > 0)
-const int kFirstMenuItemId = 1000;
-
 // Used to indicate that no item is currently selected by the user.
-const int kNoSelection = -1;
-
-const int kBodyButtonImages[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON);
-const int kHoveredBodyButtonImages[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON_H);
-const int kPressedBodyButtonImages[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON_P);
-const int kFocusedBodyButtonImages[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON_F);
-const int kFocusedHoveredBodyButtonImages[] =
-    IMAGE_GRID(IDR_COMBOBOX_BUTTON_F_H);
-const int kFocusedPressedBodyButtonImages[] =
-    IMAGE_GRID(IDR_COMBOBOX_BUTTON_F_P);
-
-#define MENU_IMAGE_GRID(x) { \
-    x ## _MENU_TOP, x ## _MENU_CENTER, x ## _MENU_BOTTOM, }
-
-const int kMenuButtonImages[] = MENU_IMAGE_GRID(IDR_COMBOBOX_BUTTON);
-const int kHoveredMenuButtonImages[] = MENU_IMAGE_GRID(IDR_COMBOBOX_BUTTON_H);
-const int kPressedMenuButtonImages[] = MENU_IMAGE_GRID(IDR_COMBOBOX_BUTTON_P);
-const int kFocusedMenuButtonImages[] = MENU_IMAGE_GRID(IDR_COMBOBOX_BUTTON_F);
-const int kFocusedHoveredMenuButtonImages[] =
-    MENU_IMAGE_GRID(IDR_COMBOBOX_BUTTON_F_H);
-const int kFocusedPressedMenuButtonImages[] =
-    MENU_IMAGE_GRID(IDR_COMBOBOX_BUTTON_F_P);
-
-#undef MENU_IMAGE_GRID
+constexpr int kNoSelection = -1;
 
 SkColor GetTextColorForEnableState(const Combobox& combobox, bool enabled) {
   return style::GetColor(
@@ -98,30 +46,10 @@
       enabled ? style::STYLE_PRIMARY : style::STYLE_DISABLED);
 }
 
-gfx::Rect PositionArrowWithinContainer(const gfx::Rect& container_bounds,
-                                       const gfx::Size& arrow_size,
-                                       Combobox::Style style) {
-  gfx::Rect bounds(container_bounds);
-  if (style == Combobox::STYLE_ACTION) {
-    // This positions the arrow horizontally. The later call to
-    // ClampToCenteredSize will position it vertically without touching the
-    // horizontal position.
-    bounds.Inset(kActionLeftPadding, 0, kActionRightPadding, 0);
-    DCHECK_EQ(bounds.width(), arrow_size.width());
-  }
-
-  bounds.ClampToCenteredSize(arrow_size);
-  return bounds;
-}
-
 // The transparent button which holds a button state but is not rendered.
 class TransparentButton : public Button {
  public:
-  TransparentButton(ButtonListener* listener, bool animate_state_change)
-      : Button(listener) {
-    set_animate_on_state_change(animate_state_change);
-    if (animate_state_change)
-      SetAnimationDuration(LabelButton::kHoverAnimationDurationMs);
+  explicit TransparentButton(ButtonListener* listener) : Button(listener) {
     SetFocusBehavior(FocusBehavior::NEVER);
     set_notify_action(PlatformStyle::kMenuNotifyActivationAction);
 
@@ -180,103 +108,6 @@
 }
 #endif
 
-// Returns the image resource ids of an array for the body button.
-//
-// TODO(hajimehoshi): This function should return the images for the 'disabled'
-// status. (crbug/270052)
-const int* GetBodyButtonImageIds(bool focused,
-                                 Button::ButtonState state,
-                                 size_t* num) {
-  DCHECK(num);
-  *num = 9;
-  switch (state) {
-    case Button::STATE_DISABLED:
-      return focused ? kFocusedBodyButtonImages : kBodyButtonImages;
-    case Button::STATE_NORMAL:
-      return focused ? kFocusedBodyButtonImages : kBodyButtonImages;
-    case Button::STATE_HOVERED:
-      return focused ?
-          kFocusedHoveredBodyButtonImages : kHoveredBodyButtonImages;
-    case Button::STATE_PRESSED:
-      return focused ?
-          kFocusedPressedBodyButtonImages : kPressedBodyButtonImages;
-    default:
-      NOTREACHED();
-  }
-  return NULL;
-}
-
-// Returns the image resource ids of an array for the menu button.
-const int* GetMenuButtonImageIds(bool focused,
-                                 Button::ButtonState state,
-                                 size_t* num) {
-  DCHECK(num);
-  *num = 3;
-  switch (state) {
-    case Button::STATE_DISABLED:
-      return focused ? kFocusedMenuButtonImages : kMenuButtonImages;
-    case Button::STATE_NORMAL:
-      return focused ? kFocusedMenuButtonImages : kMenuButtonImages;
-    case Button::STATE_HOVERED:
-      return focused ?
-          kFocusedHoveredMenuButtonImages : kHoveredMenuButtonImages;
-    case Button::STATE_PRESSED:
-      return focused ?
-          kFocusedPressedMenuButtonImages : kPressedMenuButtonImages;
-    default:
-      NOTREACHED();
-  }
-  return NULL;
-}
-
-// Returns the images for the menu buttons.
-std::vector<const gfx::ImageSkia*> GetMenuButtonImages(
-    bool focused,
-    Button::ButtonState state) {
-  const int* ids;
-  size_t num_ids;
-  ids = GetMenuButtonImageIds(focused, state, &num_ids);
-  std::vector<const gfx::ImageSkia*> images;
-  images.reserve(num_ids);
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  for (size_t i = 0; i < num_ids; i++)
-    images.push_back(rb.GetImageSkiaNamed(ids[i]));
-  return images;
-}
-
-// Paints three images in a column at the given location. The center image is
-// stretched so as to fit the given height.
-void PaintImagesVertically(gfx::Canvas* canvas,
-                           const gfx::ImageSkia& top_image,
-                           const gfx::ImageSkia& center_image,
-                           const gfx::ImageSkia& bottom_image,
-                           int x, int y, int width, int height) {
-  canvas->DrawImageInt(top_image,
-                       0, 0, top_image.width(), top_image.height(),
-                       x, y, width, top_image.height(), false);
-  y += top_image.height();
-  int center_height = height - top_image.height() - bottom_image.height();
-  canvas->DrawImageInt(center_image,
-                       0, 0, center_image.width(), center_image.height(),
-                       x, y, width, center_height, false);
-  y += center_height;
-  canvas->DrawImageInt(bottom_image,
-                       0, 0, bottom_image.width(), bottom_image.height(),
-                       x, y, width, bottom_image.height(), false);
-}
-
-// Paints the arrow button.
-void PaintArrowButton(
-    gfx::Canvas* canvas,
-    const std::vector<const gfx::ImageSkia*>& arrow_button_images,
-    int x, int height) {
-  PaintImagesVertically(canvas,
-                        *arrow_button_images[0],
-                        *arrow_button_images[1],
-                        *arrow_button_images[2],
-                        x, 0, arrow_button_images[0]->width(), height);
-}
-
 }  // namespace
 
 // static
@@ -295,8 +126,7 @@
 
  private:
   bool UseCheckmarks() const {
-    return owner_->style_ != STYLE_ACTION &&
-           MenuConfig::instance().check_selected_combobox_item;
+    return MenuConfig::instance().check_selected_combobox_item;
   }
 
   // Overridden from MenuModel:
@@ -305,12 +135,8 @@
   int GetItemCount() const override { return model_->GetItemCount(); }
 
   ItemType GetTypeAt(int index) const override {
-    if (model_->IsItemSeparatorAt(index)) {
-      // In action menus, disallow <item>, <separator>, ... since that would put
-      // a separator at the top of the menu.
-      DCHECK(index != 1 || owner_->style_ != STYLE_ACTION);
+    if (model_->IsItemSeparatorAt(index))
       return TYPE_SEPARATOR;
-    }
     return UseCheckmarks() ? TYPE_CHECK : TYPE_COMMAND;
   }
 
@@ -319,6 +145,8 @@
   }
 
   int GetCommandIdAt(int index) const override {
+    // Define the id of the first item in the menu (since it needs to be > 0)
+    constexpr int kFirstMenuItemId = 1000;
     return index + kFirstMenuItemId;
   }
 
@@ -357,13 +185,6 @@
     return model_->IsItemEnabledAt(index);
   }
 
-  bool IsVisibleAt(int index) const override {
-    // When STYLE_ACTION is used, the first item is not added to the menu. It is
-    // assumed that the first item is always selected and rendered on the top of
-    // the action button.
-    return index > 0 || owner_->style_ != STYLE_ACTION;
-  }
-
   void HighlightChangedTo(int index) override {}
 
   void ActivatedAt(int index) override {
@@ -396,22 +217,19 @@
 ////////////////////////////////////////////////////////////////////////////////
 // Combobox, public:
 
-Combobox::Combobox(std::unique_ptr<ui::ComboboxModel> model, Style style)
-    : Combobox(model.get(), style) {
+Combobox::Combobox(std::unique_ptr<ui::ComboboxModel> model)
+    : Combobox(model.get()) {
   owned_model_ = std::move(model);
 }
 
-Combobox::Combobox(ui::ComboboxModel* model, Style style)
+Combobox::Combobox(ui::ComboboxModel* model)
     : model_(model),
-      style_(style),
       listener_(nullptr),
-      selected_index_(style == STYLE_ACTION ? 0 : model_->GetDefaultIndex()),
+      selected_index_(model_->GetDefaultIndex()),
       invalid_(false),
       menu_model_(new ComboboxMenuModel(this, model)),
-      text_button_(new TransparentButton(this, style_ == STYLE_ACTION)),
-      arrow_button_(new TransparentButton(this, style_ == STYLE_ACTION)),
-      size_to_largest_label_(style_ == STYLE_NORMAL),
-      weak_ptr_factory_(this) {
+      arrow_button_(new TransparentButton(this)),
+      size_to_largest_label_(true) {
   ModelChanged();
 #if defined(OS_MACOSX)
   SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
@@ -421,29 +239,7 @@
 
   UpdateBorder();
 
-  // Initialize the button images.
-  Button::ButtonState button_states[] = {
-    Button::STATE_DISABLED,
-    Button::STATE_NORMAL,
-    Button::STATE_HOVERED,
-    Button::STATE_PRESSED,
-  };
-  for (int i = 0; i < 2; i++) {
-    for (size_t state_index = 0; state_index < arraysize(button_states);
-         state_index++) {
-      Button::ButtonState state = button_states[state_index];
-      size_t num;
-      bool focused = !!i;
-      const int* ids = GetBodyButtonImageIds(focused, state, &num);
-      body_button_painters_[focused][state] =
-          Painter::CreateImageGridPainter(ids);
-      menu_button_images_[focused][state] = GetMenuButtonImages(focused, state);
-    }
-  }
-
-  text_button_->SetVisible(true);
   arrow_button_->SetVisible(true);
-  AddChildView(text_button_);
   AddChildView(arrow_button_);
 
   // A layer is applied to make sure that canvas bounds are snapped to pixel
@@ -463,8 +259,7 @@
 
 // static
 const gfx::FontList& Combobox::GetFontList() {
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  return rb.GetFontListWithDelta(ui::kLabelFontSizeDelta);
+  return style::GetFont(style::CONTEXT_BUTTON, style::STYLE_PRIMARY);
 }
 
 void Combobox::ModelChanged() {
@@ -482,9 +277,6 @@
 }
 
 void Combobox::SetSelectedIndex(int index) {
-  if (style_ == STYLE_ACTION)
-    return;
-
   selected_index_ = index;
   if (size_to_largest_label_) {
     SchedulePaint();
@@ -495,9 +287,6 @@
 }
 
 bool Combobox::SelectValue(const base::string16& value) {
-  if (style_ == STYLE_ACTION)
-    return false;
-
   for (int i = 0; i < model()->GetItemCount(); ++i) {
     if (value == model()->GetItemAt(i)) {
       SetSelectedIndex(i);
@@ -532,25 +321,7 @@
 
 void Combobox::Layout() {
   View::Layout();
-
-  int text_button_width = 0;
-  int arrow_button_width = 0;
-
-  switch (style_) {
-    case STYLE_NORMAL: {
-      arrow_button_width = width();
-      break;
-    }
-    case STYLE_ACTION: {
-      arrow_button_width = GetArrowContainerWidth();
-      text_button_width = width() - arrow_button_width;
-      break;
-    }
-  }
-
-  int arrow_button_x = std::max(0, text_button_width);
-  text_button_->SetBounds(0, 0, std::max(0, text_button_width), height());
-  arrow_button_->SetBounds(arrow_button_x, 0, arrow_button_width, height());
+  arrow_button_->SetBounds(0, 0, width(), height());
 }
 
 void Combobox::OnNativeThemeChanged(const ui::NativeTheme* theme) {
@@ -585,6 +356,9 @@
 // Combobox, View overrides:
 
 gfx::Size Combobox::CalculatePreferredSize() const {
+  // Limit how small a combobox can be.
+  constexpr int kMinComboboxWidth = 25;
+
   // The preferred size will drive the local bounds which in turn is used to set
   // the minimum width for the dropdown list.
   gfx::Insets insets = GetInsets();
@@ -664,21 +438,9 @@
       new_index = GetAdjacentIndex(model(), -1, selected_index_);
       break;
 
-    case ui::VKEY_SPACE:
-      if (style_ == STYLE_ACTION) {
-        // When pressing space, the click event will be raised after the key is
-        // released.
-        text_button_->SetState(Button::STATE_PRESSED);
-      } else {
-        show_menu = true;
-      }
-      break;
-
     case ui::VKEY_RETURN:
-      if (style_ == STYLE_ACTION)
-        OnPerformAction();
-      else
-        show_menu = true;
+    case ui::VKEY_SPACE:
+      show_menu = true;
       break;
 #endif  // OS_MACOSX
     default:
@@ -687,8 +449,7 @@
 
   if (show_menu) {
     ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD);
-  } else if (new_index != selected_index_ && new_index != kNoSelection &&
-             style_ != STYLE_ACTION) {
+  } else if (new_index != selected_index_ && new_index != kNoSelection) {
     DCHECK(!model()->IsItemSeparatorAt(new_index));
     selected_index_ = new_index;
     OnPerformAction();
@@ -697,31 +458,10 @@
   return true;
 }
 
-bool Combobox::OnKeyReleased(const ui::KeyEvent& e) {
-  if (style_ != STYLE_ACTION)
-    return false;  // crbug.com/127520
-
-  if (e.key_code() == ui::VKEY_SPACE && style_ == STYLE_ACTION &&
-      text_button_->state() == Button::STATE_PRESSED)
-    OnPerformAction();
-
-  return false;
-}
-
 void Combobox::OnPaint(gfx::Canvas* canvas) {
-  switch (style_) {
-    case STYLE_NORMAL: {
-      OnPaintBackground(canvas);
-      PaintText(canvas);
-      OnPaintBorder(canvas);
-      break;
-    }
-    case STYLE_ACTION: {
-      PaintButtons(canvas);
-      PaintText(canvas);
-      break;
-    }
-  }
+  OnPaintBackground(canvas);
+  PaintText(canvas);
+  OnPaintBorder(canvas);
 }
 
 void Combobox::OnFocus() {
@@ -779,29 +519,22 @@
   if (!enabled())
     return;
 
-  if (sender == text_button_) {
-    OnPerformAction();
-  } else {
-    DCHECK_EQ(arrow_button_, sender);
-    // TODO(hajimehoshi): Fix the problem that the arrow button blinks when
-    // cliking this while the dropdown menu is opened.
-    const base::TimeDelta delta = base::Time::Now() - closed_time_;
-    if (delta.InMilliseconds() <= kMinimumMsBetweenButtonClicks)
-      return;
+  // TODO(hajimehoshi): Fix the problem that the arrow button blinks when
+  // cliking this while the dropdown menu is opened.
+  const base::TimeDelta delta = base::Time::Now() - closed_time_;
+  if (delta.InMilliseconds() <= kMinimumMsBetweenButtonClicks)
+    return;
 
-    ui::MenuSourceType source_type = ui::MENU_SOURCE_MOUSE;
-    if (event.IsKeyEvent())
-      source_type = ui::MENU_SOURCE_KEYBOARD;
-    else if (event.IsGestureEvent() || event.IsTouchEvent())
-      source_type = ui::MENU_SOURCE_TOUCH;
-    ShowDropDownMenu(source_type);
-  }
+  ui::MenuSourceType source_type = ui::MENU_SOURCE_MOUSE;
+  if (event.IsKeyEvent())
+    source_type = ui::MENU_SOURCE_KEYBOARD;
+  else if (event.IsGestureEvent() || event.IsTouchEvent())
+    source_type = ui::MENU_SOURCE_TOUCH;
+  ShowDropDownMenu(source_type);
 }
 
 void Combobox::UpdateBorder() {
   std::unique_ptr<FocusableBorder> border(new FocusableBorder());
-  if (style_ == STYLE_ACTION)
-    border->SetInsets(5, 10, 5, 10);
   if (invalid_)
     border->SetColorId(ui::NativeTheme::kColorId_AlertSeverityHigh);
   SetBorder(std::move(border));
@@ -842,8 +575,7 @@
 
   gfx::Rect arrow_bounds(disclosure_arrow_offset, 0, GetArrowContainerWidth(),
                          height());
-  arrow_bounds =
-      PositionArrowWithinContainer(arrow_bounds, ArrowSize(), style_);
+  arrow_bounds.ClampToCenteredSize(ArrowSize());
   AdjustBoundsForRTLUI(&arrow_bounds);
 
   {
@@ -874,82 +606,28 @@
   }
 }
 
-void Combobox::PaintButtons(gfx::Canvas* canvas) {
-  DCHECK(style_ == STYLE_ACTION);
-
-  gfx::ScopedRTLFlipCanvas scoped_canvas(canvas, width());
-
-  bool focused = HasFocus();
-  const std::vector<const gfx::ImageSkia*>& arrow_button_images =
-      menu_button_images_[focused][
-          arrow_button_->state() == Button::STATE_HOVERED ?
-          Button::STATE_NORMAL : arrow_button_->state()];
-
-  int text_button_hover_alpha =
-      text_button_->state() == Button::STATE_PRESSED ? 0 :
-      static_cast<int>(static_cast<TransparentButton*>(text_button_)->
-                       GetAnimationValue() * 255);
-  if (text_button_hover_alpha < 255) {
-    canvas->SaveLayerAlpha(255 - text_button_hover_alpha);
-    Painter* text_button_painter =
-        body_button_painters_[focused][
-            text_button_->state() == Button::STATE_HOVERED ?
-            Button::STATE_NORMAL : text_button_->state()].get();
-    Painter::PaintPainterAt(canvas, text_button_painter,
-                            gfx::Rect(0, 0, text_button_->width(), height()));
-    canvas->Restore();
-  }
-  if (0 < text_button_hover_alpha) {
-    canvas->SaveLayerAlpha(text_button_hover_alpha);
-    Painter* text_button_hovered_painter =
-        body_button_painters_[focused][Button::STATE_HOVERED].get();
-    Painter::PaintPainterAt(canvas, text_button_hovered_painter,
-                            gfx::Rect(0, 0, text_button_->width(), height()));
-    canvas->Restore();
-  }
-
-  int arrow_button_hover_alpha =
-      arrow_button_->state() == Button::STATE_PRESSED ? 0 :
-      static_cast<int>(static_cast<TransparentButton*>(arrow_button_)->
-                       GetAnimationValue() * 255);
-  if (arrow_button_hover_alpha < 255) {
-    canvas->SaveLayerAlpha(255 - arrow_button_hover_alpha);
-    PaintArrowButton(canvas, arrow_button_images, arrow_button_->x(), height());
-    canvas->Restore();
-  }
-  if (0 < arrow_button_hover_alpha) {
-    canvas->SaveLayerAlpha(arrow_button_hover_alpha);
-    const std::vector<const gfx::ImageSkia*>& arrow_button_hovered_images =
-        menu_button_images_[focused][Button::STATE_HOVERED];
-    PaintArrowButton(canvas, arrow_button_hovered_images,
-                     arrow_button_->x(), height());
-    canvas->Restore();
-  }
-}
-
 void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) {
+  // Menu border widths.
+  constexpr int kMenuBorderWidthLeft = 1;
+  constexpr int kMenuBorderWidthTop = 1;
+  constexpr int kMenuBorderWidthRight = 1;
+
   gfx::Rect lb = GetLocalBounds();
   gfx::Point menu_position(lb.origin());
 
-  if (style_ == STYLE_NORMAL) {
-    // Inset the menu's requested position so the border of the menu lines up
-    // with the border of the combobox.
-    menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft);
-    menu_position.set_y(menu_position.y() + kMenuBorderWidthTop);
-  }
+  // Inset the menu's requested position so the border of the menu lines up
+  // with the border of the combobox.
+  menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft);
+  menu_position.set_y(menu_position.y() + kMenuBorderWidthTop);
+
   lb.set_width(lb.width() - (kMenuBorderWidthLeft + kMenuBorderWidthRight));
 
   View::ConvertPointToScreen(this, &menu_position);
 
   gfx::Rect bounds(menu_position, lb.size());
 
-  Button::ButtonState original_state = Button::STATE_NORMAL;
-  if (arrow_button_) {
-    original_state = arrow_button_->state();
-    arrow_button_->SetState(Button::STATE_PRESSED);
-  }
-  MenuAnchorPosition anchor_position =
-      style_ == STYLE_ACTION ? MENU_ANCHOR_TOPRIGHT : MENU_ANCHOR_TOPLEFT;
+  Button::ButtonState original_state = arrow_button_->state();
+  arrow_button_->SetState(Button::STATE_PRESSED);
 
   // Allow |menu_runner_| to be set by the testing API, but if this method is
   // ever invoked recursively, ensure the old menu is closed.
@@ -959,14 +637,13 @@
                        base::Bind(&Combobox::OnMenuClosed,
                                   base::Unretained(this), original_state)));
   }
-  menu_runner_->RunMenuAt(GetWidget(), nullptr, bounds, anchor_position,
+  menu_runner_->RunMenuAt(GetWidget(), nullptr, bounds, MENU_ANCHOR_TOPLEFT,
                           source_type);
 }
 
 void Combobox::OnMenuClosed(Button::ButtonState original_button_state) {
   menu_runner_.reset();
-  if (arrow_button_)
-    arrow_button_->SetState(original_button_state);
+  arrow_button_->SetState(original_button_state);
   closed_time_ = base::Time::Now();
 }
 
@@ -974,13 +651,10 @@
   NotifyAccessibilityEvent(ax::mojom::Event::kValueChanged, true);
   SchedulePaint();
 
-  // This combobox may be deleted by the listener.
-  base::WeakPtr<Combobox> weak_ptr = weak_ptr_factory_.GetWeakPtr();
   if (listener_)
     listener_->OnPerformAction(this);
 
-  if (weak_ptr && style_ == STYLE_ACTION)
-    selected_index_ = 0;
+  // Note |this| may be deleted by |listener_|.
 }
 
 gfx::Size Combobox::ArrowSize() const {
@@ -1011,10 +685,7 @@
 
 int Combobox::GetArrowContainerWidth() const {
   constexpr int kPaddingWidth = 8;
-  int padding = style_ == STYLE_NORMAL
-                    ? kPaddingWidth * 2
-                    : kActionLeftPadding + kActionRightPadding;
-  return ArrowSize().width() + padding;
+  return ArrowSize().width() + kPaddingWidth * 2;
 }
 
 }  // namespace views
diff --git a/ui/views/controls/combobox/combobox.h b/ui/views/controls/combobox/combobox.h
index d6132543..9f86b461 100644
--- a/ui/views/controls/combobox/combobox.h
+++ b/ui/views/controls/combobox/combobox.h
@@ -6,12 +6,9 @@
 #define UI_VIEWS_CONTROLS_COMBOBOX_COMBOBOX_H_
 
 #include "base/macros.h"
-#include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
-#include "ui/base/models/combobox_model.h"
 #include "ui/views/controls/button/button.h"
-#include "ui/views/controls/focus_ring.h"
 #include "ui/views/controls/prefix_delegate.h"
 
 namespace gfx {
@@ -19,6 +16,7 @@
 }
 
 namespace ui {
+class ComboboxModel;
 class MenuModel;
 }
 
@@ -28,40 +26,23 @@
 }
 
 class ComboboxListener;
-class Button;
+class FocusRing;
 class MenuRunner;
-class Painter;
 class PrefixSelector;
 
 // A non-editable combobox (aka a drop-down list or selector).
-// Combobox has two distinct parts, the drop down arrow and the text. Combobox
-// offers two distinct behaviors:
-// * STYLE_NORMAL: typical combobox, clicking on the text and/or button shows
-//   the drop down, arrow keys change selection or show the menu depending on
-//   the platform, selected index can be changed by the user to something other
-//   than the first item.
-// * STYLE_ACTION: clicking on the text notifies the listener. The menu can be
-//   shown only by clicking on the arrow, except on Mac where it can be shown
-//   through the keyboard. The selected index is always reverted to 0 after the
-//   listener is notified.
+// Combobox has two distinct parts, the drop down arrow and the text.
 class VIEWS_EXPORT Combobox : public View,
                               public PrefixDelegate,
                               public ButtonListener {
  public:
-  // The style of the combobox.
-  enum Style {
-    STYLE_NORMAL,
-    STYLE_ACTION,
-  };
-
   // The combobox's class name.
   static const char kViewClassName[];
 
   // |model| is owned by the combobox when using this constructor.
-  explicit Combobox(std::unique_ptr<ui::ComboboxModel> model,
-                    Style style = STYLE_NORMAL);
+  explicit Combobox(std::unique_ptr<ui::ComboboxModel> model);
   // |model| is not owned by the combobox when using this constructor.
-  explicit Combobox(ui::ComboboxModel* model, Style style = STYLE_NORMAL);
+  explicit Combobox(ui::ComboboxModel* model);
   ~Combobox() override;
 
   static const gfx::FontList& GetFontList();
@@ -99,7 +80,6 @@
   const char* GetClassName() const override;
   bool SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) override;
   bool OnKeyPressed(const ui::KeyEvent& e) override;
-  bool OnKeyReleased(const ui::KeyEvent& e) override;
   void OnPaint(gfx::Canvas* canvas) override;
   void OnFocus() override;
   void OnBlur() override;
@@ -136,9 +116,6 @@
   // Draws the selected value of the drop down list
   void PaintText(gfx::Canvas* canvas);
 
-  // Draws the button images.
-  void PaintButtons(gfx::Canvas* canvas);
-
   // Show the drop down list
   void ShowDropDownMenu(ui::MenuSourceType source_type);
 
@@ -148,14 +125,10 @@
   // Called when the selection is changed by the user.
   void OnPerformAction();
 
-  int GetDisclosureArrowLeftPadding() const;
-  int GetDisclosureArrowRightPadding() const;
-
   // Returns the size of the disclosure arrow.
   gfx::Size ArrowSize() const;
 
-  // Finds the size of the largest menu label or, for STYLE_ACTION, the size of
-  // the selected label.
+  // Finds the size of the largest menu label.
   gfx::Size GetContentSize() const;
 
   // Handles the clicking event.
@@ -176,9 +149,6 @@
   // Reference to our model, which may be owned or not.
   ui::ComboboxModel* model_;
 
-  // The visual style of this combobox.
-  const Style style_;
-
   // Our listener. Not owned. Notified when the selected index change.
   ComboboxListener* listener_;
 
@@ -208,29 +178,15 @@
   // The maximum dimensions of the content in the dropdown.
   gfx::Size content_size_;
 
-  // The painters or images that are used when |style_| is STYLE_BUTTONS. The
-  // first index means the state of unfocused or focused.
-  // The images are owned by ResourceBundle.
-  std::unique_ptr<Painter> body_button_painters_[2][Button::STATE_COUNT];
-  std::vector<const gfx::ImageSkia*>
-      menu_button_images_[2][Button::STATE_COUNT];
-
-  // The transparent buttons to handle events and render buttons. These are
-  // placed on top of this combobox as child views, accept event and manage the
-  // button states. These are not rendered but when |style_| is
-  // STYLE_NOTIFY_ON_CLICK, a Combobox renders the button images according to
-  // these button states.
-  // The base View takes the ownerships of these as child views.
-  Button* text_button_;
+  // A transparent button that handles events and holds button state. Placed on
+  // top of the combobox as a child view. Doesn't paint itself, but serves as a
+  // host for inkdrops.
   Button* arrow_button_;
 
   // Set while the dropdown is showing. Ensures the menu is closed if |this| is
   // destroyed.
   std::unique_ptr<MenuRunner> menu_runner_;
 
-  // The image to be drawn for this combobox's arrow.
-  gfx::ImageSkia arrow_image_;
-
   // When true, the size of contents is defined by the selected label.
   // Otherwise, it's defined by the widest label in the menu. If this is set to
   // true, the parent view must relayout in ChildPreferredSizeChanged().
@@ -239,9 +195,6 @@
   // The focus ring for this Combobox.
   std::unique_ptr<FocusRing> focus_ring_;
 
-  // Used for making calbacks.
-  base::WeakPtrFactory<Combobox> weak_ptr_factory_;
-
   DISALLOW_COPY_AND_ASSIGN(Combobox);
 };
 
diff --git a/ui/views/controls/combobox/combobox_listener.h b/ui/views/controls/combobox/combobox_listener.h
index 2522e03..71442cd 100644
--- a/ui/views/controls/combobox/combobox_listener.h
+++ b/ui/views/controls/combobox/combobox_listener.h
@@ -16,12 +16,11 @@
 class VIEWS_EXPORT ComboboxListener {
  public:
   // Invoked when the user does the appropriate gesture that some action should
-  // be performed. For both STYLE_NORMAL and STYLE_ACTION this is invoked if the
-  // user clicks on the menu button and then clicks an item. For STYLE_NORMAL
-  // this is also invoked when the menu is not showing and the does a gesture to
-  // change the selection (for example, presses the home or end keys). This is
-  // not invoked when the menu is shown and the user changes the selection
-  // without closing the menu.
+  // be performed. This is invoked if the user clicks on the menu button and
+  // then clicks an item, and also when the menu is not showing and the does a
+  // gesture to change the selection (for example, presses the home or end
+  // keys). This is not invoked when the menu is shown and the user changes the
+  // selection without closing the menu.
   virtual void OnPerformAction(Combobox* combobox) = 0;
 
  protected:
diff --git a/ui/views/controls/combobox/combobox_unittest.cc b/ui/views/controls/combobox/combobox_unittest.cc
index 5c32deb..0d7424f 100644
--- a/ui/views/controls/combobox/combobox_unittest.cc
+++ b/ui/views/controls/combobox/combobox_unittest.cc
@@ -38,8 +38,8 @@
 // OnKeyReleased() methods.
 class TestCombobox : public Combobox {
  public:
-  TestCombobox(ui::ComboboxModel* model, Combobox::Style style)
-      : Combobox(model, style), key_handled_(false), key_received_(false) {}
+  explicit TestCombobox(ui::ComboboxModel* model)
+      : Combobox(model), key_handled_(false), key_received_(false) {}
 
   bool OnKeyPressed(const ui::KeyEvent& e) override {
     key_received_ = true;
@@ -196,14 +196,14 @@
     ViewsTestBase::TearDown();
   }
 
-  void InitCombobox(const std::set<int>* separators, Combobox::Style style) {
+  void InitCombobox(const std::set<int>* separators) {
     model_.reset(new TestComboboxModel());
 
     if (separators)
       model_->SetSeparators(*separators);
 
     ASSERT_FALSE(combobox_);
-    combobox_ = new TestCombobox(model_.get(), style);
+    combobox_ = new TestCombobox(model_.get());
     test_api_.reset(new ComboboxTestApi(combobox_));
     test_api_->InstallTestMenuRunner(&menu_show_count_);
     combobox_->set_id(1);
@@ -282,7 +282,7 @@
 // Tests whether the various Mac specific keyboard shortcuts invoke the dropdown
 // menu or not.
 TEST_F(ComboboxTest, KeyTestMac) {
-  InitCombobox(nullptr, Combobox::STYLE_NORMAL);
+  InitCombobox(nullptr);
   PressKey(ui::VKEY_END);
   EXPECT_EQ(0, combobox_->selected_index());
   EXPECT_EQ(1, menu_show_count_);
@@ -331,7 +331,7 @@
   model_.reset(new TestComboboxModel());
 
   ASSERT_FALSE(combobox_);
-  combobox_ = new TestCombobox(model_.get(), Combobox::STYLE_NORMAL);
+  combobox_ = new TestCombobox(model_.get());
   combobox_->SetEnabled(false);
 
   widget_ = new Widget;
@@ -352,7 +352,7 @@
 // Tests the behavior of various keyboard shortcuts on the currently selected
 // index.
 TEST_F(ComboboxTest, KeyTest) {
-  InitCombobox(nullptr, Combobox::STYLE_NORMAL);
+  InitCombobox(nullptr);
   PressKey(ui::VKEY_END);
   EXPECT_EQ(model_->GetItemCount(), combobox_->selected_index() + 1);
   PressKey(ui::VKEY_HOME);
@@ -377,7 +377,7 @@
 TEST_F(ComboboxTest, SkipSeparatorSimple) {
   std::set<int> separators;
   separators.insert(2);
-  InitCombobox(&separators, Combobox::STYLE_NORMAL);
+  InitCombobox(&separators);
   EXPECT_EQ(0, combobox_->selected_index());
   PressKey(ui::VKEY_DOWN);
   EXPECT_EQ(1, combobox_->selected_index());
@@ -398,7 +398,7 @@
 TEST_F(ComboboxTest, SkipSeparatorBeginning) {
   std::set<int> separators;
   separators.insert(0);
-  InitCombobox(&separators, Combobox::STYLE_NORMAL);
+  InitCombobox(&separators);
   EXPECT_EQ(1, combobox_->selected_index());
   PressKey(ui::VKEY_DOWN);
   EXPECT_EQ(2, combobox_->selected_index());
@@ -419,7 +419,7 @@
 TEST_F(ComboboxTest, SkipSeparatorEnd) {
   std::set<int> separators;
   separators.insert(TestComboboxModel::kItemCount - 1);
-  InitCombobox(&separators, Combobox::STYLE_NORMAL);
+  InitCombobox(&separators);
   combobox_->SetSelectedIndex(8);
   PressKey(ui::VKEY_DOWN);
   EXPECT_EQ(8, combobox_->selected_index());
@@ -437,7 +437,7 @@
   separators.insert(0);
   separators.insert(1);
   separators.insert(2);
-  InitCombobox(&separators, Combobox::STYLE_NORMAL);
+  InitCombobox(&separators);
   EXPECT_EQ(3, combobox_->selected_index());
   PressKey(ui::VKEY_DOWN);
   EXPECT_EQ(4, combobox_->selected_index());
@@ -461,7 +461,7 @@
   separators.insert(4);
   separators.insert(5);
   separators.insert(6);
-  InitCombobox(&separators, Combobox::STYLE_NORMAL);
+  InitCombobox(&separators);
   combobox_->SetSelectedIndex(3);
   PressKey(ui::VKEY_DOWN);
   EXPECT_EQ(7, combobox_->selected_index());
@@ -477,7 +477,7 @@
   separators.insert(7);
   separators.insert(8);
   separators.insert(9);
-  InitCombobox(&separators, Combobox::STYLE_NORMAL);
+  InitCombobox(&separators);
   combobox_->SetSelectedIndex(6);
   PressKey(ui::VKEY_DOWN);
   EXPECT_EQ(6, combobox_->selected_index());
@@ -499,7 +499,7 @@
   separators.insert(0);
   separators.insert(1);
   separators.insert(9);
-  InitCombobox(&separators, Combobox::STYLE_NORMAL);
+  InitCombobox(&separators);
   for (int i = 0; i < combobox_->GetRowCount(); ++i) {
     if (separators.count(i) != 0) {
       EXPECT_TRUE(combobox_->GetTextForRow(i).empty()) << i;
@@ -512,7 +512,7 @@
 
 // Verifies selecting the first matching value (and returning whether found).
 TEST_F(ComboboxTest, SelectValue) {
-  InitCombobox(nullptr, Combobox::STYLE_NORMAL);
+  InitCombobox(nullptr);
   ASSERT_EQ(model_->GetDefaultIndex(), combobox_->selected_index());
   EXPECT_TRUE(combobox_->SelectValue(ASCIIToUTF16("PEANUT BUTTER")));
   EXPECT_EQ(0, combobox_->selected_index());
@@ -522,50 +522,19 @@
   EXPECT_EQ(1, combobox_->selected_index());
 }
 
-TEST_F(ComboboxTest, SelectValueActionStyle) {
-  // With the action style, the selected index is always 0.
-  InitCombobox(nullptr, Combobox::STYLE_ACTION);
-  EXPECT_FALSE(combobox_->SelectValue(ASCIIToUTF16("PEANUT BUTTER")));
-  EXPECT_EQ(0, combobox_->selected_index());
-  EXPECT_FALSE(combobox_->SelectValue(ASCIIToUTF16("JELLY")));
-  EXPECT_EQ(0, combobox_->selected_index());
-  EXPECT_FALSE(combobox_->SelectValue(ASCIIToUTF16("BANANAS")));
-  EXPECT_EQ(0, combobox_->selected_index());
-}
-
-TEST_F(ComboboxTest, SelectIndexActionStyle) {
-  InitCombobox(nullptr, Combobox::STYLE_ACTION);
-
-  // With the action style, the selected index is always 0.
-  combobox_->SetSelectedIndex(1);
-  EXPECT_EQ(0, combobox_->selected_index());
-  combobox_->SetSelectedIndex(2);
-  EXPECT_EQ(0, combobox_->selected_index());
-  combobox_->SetSelectedIndex(3);
-  EXPECT_EQ(0, combobox_->selected_index());
-}
-
 TEST_F(ComboboxTest, ListenerHandlesDelete) {
   TestComboboxModel model;
 
   // |combobox| will be deleted on change.
-  TestCombobox* combobox = new TestCombobox(&model, Combobox::STYLE_NORMAL);
+  TestCombobox* combobox = new TestCombobox(&model);
   std::unique_ptr<EvilListener> evil_listener(new EvilListener());
   combobox->set_listener(evil_listener.get());
   ASSERT_NO_FATAL_FAILURE(ComboboxTestApi(combobox).PerformActionAt(2));
   EXPECT_TRUE(evil_listener->deleted());
-
-  // With STYLE_ACTION
-  // |combobox| will be deleted on change.
-  combobox = new TestCombobox(&model, Combobox::STYLE_ACTION);
-  evil_listener.reset(new EvilListener());
-  combobox->set_listener(evil_listener.get());
-  ASSERT_NO_FATAL_FAILURE(ComboboxTestApi(combobox).PerformActionAt(2));
-  EXPECT_TRUE(evil_listener->deleted());
 }
 
 TEST_F(ComboboxTest, Click) {
-  InitCombobox(nullptr, Combobox::STYLE_NORMAL);
+  InitCombobox(nullptr);
 
   TestComboboxListener listener;
   combobox_->set_listener(&listener);
@@ -580,7 +549,7 @@
 }
 
 TEST_F(ComboboxTest, ClickButDisabled) {
-  InitCombobox(nullptr, Combobox::STYLE_NORMAL);
+  InitCombobox(nullptr);
 
   TestComboboxListener listener;
   combobox_->set_listener(&listener);
@@ -596,44 +565,25 @@
 }
 
 TEST_F(ComboboxTest, NotifyOnClickWithReturnKey) {
-  InitCombobox(nullptr, Combobox::STYLE_NORMAL);
+  InitCombobox(nullptr);
 
   TestComboboxListener listener;
   combobox_->set_listener(&listener);
 
-  // With STYLE_NORMAL, the click event is ignored. Instead the menu is shown.
+  // The click event is ignored. Instead the menu is shown.
   PressKey(ui::VKEY_RETURN);
   EXPECT_EQ(PlatformStyle::kReturnClicksFocusedControl ? 1 : 0,
             menu_show_count_);
   EXPECT_FALSE(listener.on_perform_action_called());
 }
 
-TEST_F(ComboboxTest, NotifyOnClickWithReturnKeyActionStyle) {
-  InitCombobox(nullptr, Combobox::STYLE_ACTION);
-
-  TestComboboxListener listener;
-  combobox_->set_listener(&listener);
-
-  // With STYLE_ACTION, the click event is notified and the menu is not shown.
-  PressKey(ui::VKEY_RETURN);
-  EXPECT_EQ(0, menu_show_count_);
-
-  if (PlatformStyle::kReturnClicksFocusedControl) {
-    EXPECT_TRUE(listener.on_perform_action_called());
-    EXPECT_EQ(0, listener.perform_action_index());
-  } else {
-    EXPECT_FALSE(listener.on_perform_action_called());
-    EXPECT_EQ(-1, listener.perform_action_index());
-  }
-}
-
 TEST_F(ComboboxTest, NotifyOnClickWithSpaceKey) {
-  InitCombobox(nullptr, Combobox::STYLE_NORMAL);
+  InitCombobox(nullptr);
 
   TestComboboxListener listener;
   combobox_->set_listener(&listener);
 
-  // With STYLE_NORMAL, the click event is ignored. Instead the menu is shwon.
+  // The click event is ignored. Instead the menu is shwon.
   PressKey(ui::VKEY_SPACE);
   EXPECT_EQ(1, menu_show_count_);
   EXPECT_FALSE(listener.on_perform_action_called());
@@ -645,7 +595,7 @@
 
 // Test that accessibility action events show the combobox dropdown.
 TEST_F(ComboboxTest, ShowViaAccessibleAction) {
-  InitCombobox(nullptr, Combobox::STYLE_NORMAL);
+  InitCombobox(nullptr);
 
   ui::AXActionData data;
   data.action = ax::mojom::Action::kDoDefault;
@@ -673,37 +623,8 @@
   EXPECT_EQ(1, menu_show_count_);  // No change.
 }
 
-TEST_F(ComboboxTest, NotifyOnClickWithSpaceKeyActionStyle) {
-  InitCombobox(nullptr, Combobox::STYLE_ACTION);
-
-  TestComboboxListener listener;
-  combobox_->set_listener(&listener);
-
-  // With STYLE_ACTION, the click event is notified after releasing and the menu
-  // is not shown. On Mac, the menu should be shown.
-  PressKey(ui::VKEY_SPACE);
-#if defined(OS_MACOSX)
-  EXPECT_EQ(1, menu_show_count_);
-#else
-  EXPECT_EQ(0, menu_show_count_);
-#endif
-  EXPECT_FALSE(listener.on_perform_action_called());
-  EXPECT_EQ(-1, listener.perform_action_index());
-
-  ReleaseKey(ui::VKEY_SPACE);
-#if defined(OS_MACOSX)
-  EXPECT_EQ(1, menu_show_count_);
-  EXPECT_FALSE(listener.on_perform_action_called());
-  EXPECT_EQ(-1, listener.perform_action_index());
-#else
-  EXPECT_EQ(0, menu_show_count_);
-  EXPECT_TRUE(listener.on_perform_action_called());
-  EXPECT_EQ(0, listener.perform_action_index());
-#endif
-}
-
 TEST_F(ComboboxTest, NotifyOnClickWithMouse) {
-  InitCombobox(nullptr, Combobox::STYLE_ACTION);
+  InitCombobox(nullptr);
 
   TestComboboxListener listener;
   combobox_->set_listener(&listener);
@@ -735,12 +656,13 @@
   PerformMousePress(left_point);
   PerformMouseRelease(left_point);
 
-  EXPECT_EQ(1, menu_show_count_);  // Unchanged.
-  EXPECT_EQ(0, listener.perform_action_index());
+  // Both the text and the arrow may toggle the menu.
+  EXPECT_EQ(2, menu_show_count_);
+  EXPECT_EQ(-1, listener.perform_action_index());  // Nothing selected.
 }
 
 TEST_F(ComboboxTest, ConsumingPressKeyEvents) {
-  InitCombobox(nullptr, Combobox::STYLE_NORMAL);
+  InitCombobox(nullptr);
 
   EXPECT_TRUE(combobox_->OnKeyPressed(
       ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SPACE, ui::EF_NONE)));
@@ -756,33 +678,11 @@
   }
 }
 
-TEST_F(ComboboxTest, ConsumingKeyPressEventsActionStyle) {
-  // When the combobox's style is STYLE_ACTION, pressing events of a space key
-  // or an enter key will be consumed and the menu is not shown. However, on
-  // Mac, space will show the menu.
-  InitCombobox(nullptr, Combobox::STYLE_ACTION);
-
-  EXPECT_EQ(PlatformStyle::kReturnClicksFocusedControl,
-            combobox_->OnKeyPressed(ui::KeyEvent(
-                ui::ET_KEY_PRESSED, ui::VKEY_RETURN, ui::EF_NONE)));
-  EXPECT_EQ(0, menu_show_count_);
-
-  EXPECT_TRUE(combobox_->OnKeyPressed(
-      ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SPACE, ui::EF_NONE)));
-#if defined(OS_MACOSX)
-  EXPECT_EQ(1, menu_show_count_);
-#else
-  EXPECT_EQ(0, menu_show_count_);
-#endif
-}
-
 TEST_F(ComboboxTest, ContentWidth) {
   std::vector<std::string> values;
   VectorComboboxModel model(&values);
-  TestCombobox combobox(&model, Combobox::STYLE_NORMAL);
-  TestCombobox action_combobox(&model, Combobox::STYLE_ACTION);
+  TestCombobox combobox(&model);
   ComboboxTestApi test_api(&combobox);
-  ComboboxTestApi action_test_api(&action_combobox);
 
   std::string long_item = "this is the long item";
   std::string short_item = "s";
@@ -790,13 +690,11 @@
   values.resize(1);
   values[0] = long_item;
   combobox.ModelChanged();
-  action_combobox.ModelChanged();
 
   const int long_item_width = test_api.content_size().width();
 
   values[0] = short_item;
   combobox.ModelChanged();
-  action_combobox.ModelChanged();
 
   const int short_item_width = test_api.content_size().width();
 
@@ -804,20 +702,16 @@
   values[0] = short_item;
   values[1] = long_item;
   combobox.ModelChanged();
-  action_combobox.ModelChanged();
 
-  // When the style is STYLE_NORMAL, the width will fit with the longest item.
+  // The width will fit with the longest item.
   EXPECT_EQ(long_item_width, test_api.content_size().width());
-
-  // When the style is STYLE_ACTION, the width will fit with the selected item's
-  // width.
-  EXPECT_EQ(short_item_width, action_test_api.content_size().width());
+  EXPECT_LT(short_item_width, test_api.content_size().width());
 }
 
 // Test that model updates preserve the selected index, so long as it is in
 // range.
 TEST_F(ComboboxTest, ModelChanged) {
-  InitCombobox(nullptr, Combobox::STYLE_NORMAL);
+  InitCombobox(nullptr);
 
   EXPECT_EQ(0, combobox_->GetSelectedRow());
   EXPECT_EQ(10, combobox_->GetRowCount());
@@ -859,7 +753,7 @@
 }
 
 TEST_F(ComboboxTest, TypingPrefixNotifiesListener) {
-  InitCombobox(nullptr, Combobox::STYLE_NORMAL);
+  InitCombobox(nullptr);
 
   TestComboboxListener listener;
   combobox_->set_listener(&listener);
@@ -902,7 +796,7 @@
   const int kSeparatorIndex = 3;
   std::set<int> separators;
   separators.insert(kSeparatorIndex);
-  InitCombobox(&separators, Combobox::STYLE_NORMAL);
+  InitCombobox(&separators);
 
   ui::MenuModel* menu_model = test_api_->menu_model();
 
@@ -931,26 +825,4 @@
   EXPECT_TRUE(menu_model->IsVisibleAt(0));
 }
 
-// Check that with STYLE_ACTION, the first item (only) is not shown.
-TEST_F(ComboboxTest, MenuModelActionStyleMac) {
-  const int kSeparatorIndex = 3;
-  std::set<int> separators;
-  separators.insert(kSeparatorIndex);
-  InitCombobox(&separators, Combobox::STYLE_ACTION);
-
-  ui::MenuModel* menu_model = test_api_->menu_model();
-
-  EXPECT_EQ(TestComboboxModel::kItemCount, menu_model->GetItemCount());
-  EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR,
-            menu_model->GetTypeAt(kSeparatorIndex));
-
-  EXPECT_EQ(ui::MenuModel::TYPE_COMMAND, menu_model->GetTypeAt(0));
-  EXPECT_EQ(ui::MenuModel::TYPE_COMMAND, menu_model->GetTypeAt(1));
-
-  EXPECT_EQ(ASCIIToUTF16("PEANUT BUTTER"), menu_model->GetLabelAt(0));
-  EXPECT_EQ(ASCIIToUTF16("JELLY"), menu_model->GetLabelAt(1));
-  EXPECT_FALSE(menu_model->IsVisibleAt(0));
-  EXPECT_TRUE(menu_model->IsVisibleAt(1));
-}
-
 }  // namespace views
diff --git a/ui/views/examples/combobox_example.cc b/ui/views/examples/combobox_example.cc
index 9a79fcfb..b5ec924 100644
--- a/ui/views/examples/combobox_example.cc
+++ b/ui/views/examples/combobox_example.cc
@@ -50,32 +50,17 @@
   disabled_combobox_->SetSelectedIndex(4);
   disabled_combobox_->SetEnabled(false);
 
-  action_combobox_ = new Combobox(std::make_unique<ComboboxModelExample>(),
-                                  Combobox::STYLE_ACTION);
-  action_combobox_->set_listener(this);
-  // Note: STYLE_ACTION comboboxes always have the first item selected by
-  // default.
-
   container->SetLayoutManager(
       std::make_unique<BoxLayout>(BoxLayout::kVertical, gfx::Insets(10, 0), 5));
   container->AddChildView(combobox_);
   container->AddChildView(disabled_combobox_);
-  container->AddChildView(action_combobox_);
 }
 
 void ComboboxExample::OnPerformAction(Combobox* combobox) {
-  if (combobox == combobox_) {
-    PrintStatus("Selected: %s",
-                base::UTF16ToUTF8(
-                    combobox->model()->GetItemAt(combobox->selected_index()))
-                    .c_str());
-  } else if (combobox == action_combobox_) {
-    PrintStatus("Action: %s", base::UTF16ToUTF8(combobox->model()->GetItemAt(
+  DCHECK_EQ(combobox, combobox_);
+  PrintStatus("Selected: %s", base::UTF16ToUTF8(combobox->model()->GetItemAt(
                                                     combobox->selected_index()))
                                   .c_str());
-  } else {
-    NOTREACHED() << "Surprising combobox.";
-  }
 }
 
 }  // namespace examples
diff --git a/ui/views/examples/combobox_example.h b/ui/views/examples/combobox_example.h
index 43d7e7e..868ab2b6 100644
--- a/ui/views/examples/combobox_example.h
+++ b/ui/views/examples/combobox_example.h
@@ -28,7 +28,6 @@
 
   Combobox* combobox_ = nullptr;
   Combobox* disabled_combobox_ = nullptr;
-  Combobox* action_combobox_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(ComboboxExample);
 };
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_bottom.png b/ui/views/resources/default_100_percent/common/combobox_button_bottom.png
deleted file mode 100644
index aeb7dcb..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_bottom.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_bottom_focused.png b/ui/views/resources/default_100_percent/common/combobox_button_bottom_focused.png
deleted file mode 100644
index 8b5944e..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_bottom_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_bottom_focused_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_bottom_focused_hover.png
deleted file mode 100644
index ccb7cd3..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_bottom_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_bottom_focused_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_bottom_focused_pressed.png
deleted file mode 100644
index 3c74ef7e..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_bottom_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_bottom_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_bottom_hover.png
deleted file mode 100644
index 0279262f..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_bottom_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_bottom_left.png b/ui/views/resources/default_100_percent/common/combobox_button_bottom_left.png
deleted file mode 100644
index 66836356..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_bottom_left.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_bottom_left_focused.png b/ui/views/resources/default_100_percent/common/combobox_button_bottom_left_focused.png
deleted file mode 100644
index c0f012f..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_bottom_left_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_bottom_left_focused_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_bottom_left_focused_hover.png
deleted file mode 100644
index 6f86b3b7..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_bottom_left_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_bottom_left_focused_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_bottom_left_focused_pressed.png
deleted file mode 100644
index 05d7d75..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_bottom_left_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_bottom_left_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_bottom_left_hover.png
deleted file mode 100644
index 3d7f360..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_bottom_left_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_bottom_left_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_bottom_left_pressed.png
deleted file mode 100644
index ed9595d8..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_bottom_left_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_bottom_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_bottom_pressed.png
deleted file mode 100644
index def4873..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_bottom_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_bottom_right.png b/ui/views/resources/default_100_percent/common/combobox_button_bottom_right.png
deleted file mode 100644
index 63dc232..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_bottom_right.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_bottom_right_focused.png b/ui/views/resources/default_100_percent/common/combobox_button_bottom_right_focused.png
deleted file mode 100644
index b2b523e..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_bottom_right_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_bottom_right_focused_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_bottom_right_focused_hover.png
deleted file mode 100644
index e4ee6a6..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_bottom_right_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_bottom_right_focused_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_bottom_right_focused_pressed.png
deleted file mode 100644
index 3c74ef7e..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_bottom_right_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_bottom_right_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_bottom_right_hover.png
deleted file mode 100644
index e9f0648..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_bottom_right_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_bottom_right_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_bottom_right_pressed.png
deleted file mode 100644
index def4873..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_bottom_right_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_center.png b/ui/views/resources/default_100_percent/common/combobox_button_center.png
deleted file mode 100644
index 14e9762..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_center.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_center_focused.png b/ui/views/resources/default_100_percent/common/combobox_button_center_focused.png
deleted file mode 100644
index 14e9762..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_center_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_center_focused_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_center_focused_hover.png
deleted file mode 100644
index 5b9fb5fc..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_center_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_center_focused_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_center_focused_pressed.png
deleted file mode 100644
index 8d52222..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_center_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_center_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_center_hover.png
deleted file mode 100644
index 5b9fb5fc..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_center_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_center_left.png b/ui/views/resources/default_100_percent/common/combobox_button_center_left.png
deleted file mode 100644
index 25e93d3..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_center_left.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_center_left_focused.png b/ui/views/resources/default_100_percent/common/combobox_button_center_left_focused.png
deleted file mode 100644
index 3ab0e25..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_center_left_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_center_left_focused_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_center_left_focused_hover.png
deleted file mode 100644
index dfdfe2c..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_center_left_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_center_left_focused_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_center_left_focused_pressed.png
deleted file mode 100644
index 621636c..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_center_left_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_center_left_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_center_left_hover.png
deleted file mode 100644
index e74682ef..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_center_left_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_center_left_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_center_left_pressed.png
deleted file mode 100644
index 32411a2e..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_center_left_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_center_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_center_pressed.png
deleted file mode 100644
index 8d52222..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_center_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_center_right.png b/ui/views/resources/default_100_percent/common/combobox_button_center_right.png
deleted file mode 100644
index bd141c4..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_center_right.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_center_right_focused.png b/ui/views/resources/default_100_percent/common/combobox_button_center_right_focused.png
deleted file mode 100644
index bd141c4..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_center_right_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_center_right_focused_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_center_right_focused_hover.png
deleted file mode 100644
index c3141746b..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_center_right_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_center_right_focused_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_center_right_focused_pressed.png
deleted file mode 100644
index 8d52222..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_center_right_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_center_right_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_center_right_hover.png
deleted file mode 100644
index c3141746b..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_center_right_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_center_right_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_center_right_pressed.png
deleted file mode 100644
index 8d52222..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_center_right_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_menu_bottom.png b/ui/views/resources/default_100_percent/common/combobox_button_menu_bottom.png
deleted file mode 100644
index 34b0b02..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_menu_bottom.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_menu_bottom_focused.png b/ui/views/resources/default_100_percent/common/combobox_button_menu_bottom_focused.png
deleted file mode 100644
index 5eb45a04..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_menu_bottom_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_menu_bottom_focused_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_menu_bottom_focused_hover.png
deleted file mode 100644
index 69941569..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_menu_bottom_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_menu_bottom_focused_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_menu_bottom_focused_pressed.png
deleted file mode 100644
index 4320fec..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_menu_bottom_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_menu_bottom_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_menu_bottom_hover.png
deleted file mode 100644
index 821402c..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_menu_bottom_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_menu_bottom_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_menu_bottom_pressed.png
deleted file mode 100644
index e85e49a..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_menu_bottom_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_menu_center.png b/ui/views/resources/default_100_percent/common/combobox_button_menu_center.png
deleted file mode 100644
index b22f3b9..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_menu_center.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_menu_center_focused.png b/ui/views/resources/default_100_percent/common/combobox_button_menu_center_focused.png
deleted file mode 100644
index e94f4a6..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_menu_center_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_menu_center_focused_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_menu_center_focused_hover.png
deleted file mode 100644
index d2fa901..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_menu_center_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_menu_center_focused_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_menu_center_focused_pressed.png
deleted file mode 100644
index e092be5b..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_menu_center_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_menu_center_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_menu_center_hover.png
deleted file mode 100644
index 25a09ed..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_menu_center_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_menu_center_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_menu_center_pressed.png
deleted file mode 100644
index a73b684e..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_menu_center_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_menu_top.png b/ui/views/resources/default_100_percent/common/combobox_button_menu_top.png
deleted file mode 100644
index 40eb193..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_menu_top.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_menu_top_focused.png b/ui/views/resources/default_100_percent/common/combobox_button_menu_top_focused.png
deleted file mode 100644
index 08053ed..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_menu_top_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_menu_top_focused_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_menu_top_focused_hover.png
deleted file mode 100644
index 75c6d20b..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_menu_top_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_menu_top_focused_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_menu_top_focused_pressed.png
deleted file mode 100644
index 51ada794..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_menu_top_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_menu_top_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_menu_top_hover.png
deleted file mode 100644
index b85ed7a..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_menu_top_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_menu_top_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_menu_top_pressed.png
deleted file mode 100644
index 14ba21d..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_menu_top_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_top.png b/ui/views/resources/default_100_percent/common/combobox_button_top.png
deleted file mode 100644
index aeede822..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_top.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_top_focused.png b/ui/views/resources/default_100_percent/common/combobox_button_top_focused.png
deleted file mode 100644
index ea1bd67..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_top_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_top_focused_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_top_focused_hover.png
deleted file mode 100644
index 5227e00..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_top_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_top_focused_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_top_focused_pressed.png
deleted file mode 100644
index b44fa53..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_top_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_top_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_top_hover.png
deleted file mode 100644
index 05535ae..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_top_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_top_left.png b/ui/views/resources/default_100_percent/common/combobox_button_top_left.png
deleted file mode 100644
index 7d27ba9..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_top_left.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_top_left_focused.png b/ui/views/resources/default_100_percent/common/combobox_button_top_left_focused.png
deleted file mode 100644
index 776e6dd8..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_top_left_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_top_left_focused_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_top_left_focused_hover.png
deleted file mode 100644
index 7d2932d..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_top_left_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_top_left_focused_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_top_left_focused_pressed.png
deleted file mode 100644
index b1daeb61..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_top_left_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_top_left_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_top_left_hover.png
deleted file mode 100644
index 74df4626..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_top_left_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_top_left_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_top_left_pressed.png
deleted file mode 100644
index bc9f437..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_top_left_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_top_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_top_pressed.png
deleted file mode 100644
index a0867f6..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_top_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_top_right.png b/ui/views/resources/default_100_percent/common/combobox_button_top_right.png
deleted file mode 100644
index f13445f9..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_top_right.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_top_right_focused.png b/ui/views/resources/default_100_percent/common/combobox_button_top_right_focused.png
deleted file mode 100644
index 43f7b24..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_top_right_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_top_right_focused_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_top_right_focused_hover.png
deleted file mode 100644
index 49adefc..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_top_right_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_top_right_focused_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_top_right_focused_pressed.png
deleted file mode 100644
index b44fa53..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_top_right_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_top_right_hover.png b/ui/views/resources/default_100_percent/common/combobox_button_top_right_hover.png
deleted file mode 100644
index 361068af7..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_top_right_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_100_percent/common/combobox_button_top_right_pressed.png b/ui/views/resources/default_100_percent/common/combobox_button_top_right_pressed.png
deleted file mode 100644
index a0867f6..0000000
--- a/ui/views/resources/default_100_percent/common/combobox_button_top_right_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_bottom.png b/ui/views/resources/default_200_percent/common/combobox_button_bottom.png
deleted file mode 100644
index 7092a15..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_bottom.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_bottom_focused.png b/ui/views/resources/default_200_percent/common/combobox_button_bottom_focused.png
deleted file mode 100644
index 1c219df..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_bottom_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_bottom_focused_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_bottom_focused_hover.png
deleted file mode 100644
index 695e8543..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_bottom_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_bottom_focused_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_bottom_focused_pressed.png
deleted file mode 100644
index a920886..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_bottom_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_bottom_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_bottom_hover.png
deleted file mode 100644
index 8d7a249..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_bottom_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_bottom_left.png b/ui/views/resources/default_200_percent/common/combobox_button_bottom_left.png
deleted file mode 100644
index f092b65..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_bottom_left.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_bottom_left_focused.png b/ui/views/resources/default_200_percent/common/combobox_button_bottom_left_focused.png
deleted file mode 100644
index 8f3d372..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_bottom_left_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_bottom_left_focused_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_bottom_left_focused_hover.png
deleted file mode 100644
index f2bd26f..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_bottom_left_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_bottom_left_focused_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_bottom_left_focused_pressed.png
deleted file mode 100644
index 860a274..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_bottom_left_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_bottom_left_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_bottom_left_hover.png
deleted file mode 100644
index 764cb58..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_bottom_left_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_bottom_left_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_bottom_left_pressed.png
deleted file mode 100644
index 4645e11..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_bottom_left_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_bottom_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_bottom_pressed.png
deleted file mode 100644
index bb419b32..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_bottom_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_bottom_right.png b/ui/views/resources/default_200_percent/common/combobox_button_bottom_right.png
deleted file mode 100644
index 350dd633..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_bottom_right.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_bottom_right_focused.png b/ui/views/resources/default_200_percent/common/combobox_button_bottom_right_focused.png
deleted file mode 100644
index c9c0784..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_bottom_right_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_bottom_right_focused_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_bottom_right_focused_hover.png
deleted file mode 100644
index a3559de..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_bottom_right_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_bottom_right_focused_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_bottom_right_focused_pressed.png
deleted file mode 100644
index a920886..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_bottom_right_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_bottom_right_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_bottom_right_hover.png
deleted file mode 100644
index 4d6a63f..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_bottom_right_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_bottom_right_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_bottom_right_pressed.png
deleted file mode 100644
index bb419b32..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_bottom_right_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_center.png b/ui/views/resources/default_200_percent/common/combobox_button_center.png
deleted file mode 100644
index 303031f..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_center.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_center_focused.png b/ui/views/resources/default_200_percent/common/combobox_button_center_focused.png
deleted file mode 100644
index 303031f..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_center_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_center_focused_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_center_focused_hover.png
deleted file mode 100644
index 4e3d7755..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_center_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_center_focused_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_center_focused_pressed.png
deleted file mode 100644
index 17d9ec3..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_center_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_center_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_center_hover.png
deleted file mode 100644
index 4e3d7755..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_center_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_center_left.png b/ui/views/resources/default_200_percent/common/combobox_button_center_left.png
deleted file mode 100644
index 055ce6c1..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_center_left.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_center_left_focused.png b/ui/views/resources/default_200_percent/common/combobox_button_center_left_focused.png
deleted file mode 100644
index 974aaab..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_center_left_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_center_left_focused_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_center_left_focused_hover.png
deleted file mode 100644
index 96d6ef95..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_center_left_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_center_left_focused_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_center_left_focused_pressed.png
deleted file mode 100644
index 1b69bdeb..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_center_left_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_center_left_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_center_left_hover.png
deleted file mode 100644
index 3870e56..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_center_left_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_center_left_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_center_left_pressed.png
deleted file mode 100644
index 7bb30da..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_center_left_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_center_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_center_pressed.png
deleted file mode 100644
index 17d9ec3..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_center_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_center_right.png b/ui/views/resources/default_200_percent/common/combobox_button_center_right.png
deleted file mode 100644
index ebba1d4..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_center_right.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_center_right_focused.png b/ui/views/resources/default_200_percent/common/combobox_button_center_right_focused.png
deleted file mode 100644
index ebba1d4..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_center_right_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_center_right_focused_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_center_right_focused_hover.png
deleted file mode 100644
index f48cfa38..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_center_right_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_center_right_focused_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_center_right_focused_pressed.png
deleted file mode 100644
index 17d9ec3..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_center_right_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_center_right_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_center_right_hover.png
deleted file mode 100644
index f48cfa38..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_center_right_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_center_right_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_center_right_pressed.png
deleted file mode 100644
index 17d9ec3..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_center_right_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_menu_bottom.png b/ui/views/resources/default_200_percent/common/combobox_button_menu_bottom.png
deleted file mode 100644
index b279ff8e0..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_menu_bottom.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_menu_bottom_focused.png b/ui/views/resources/default_200_percent/common/combobox_button_menu_bottom_focused.png
deleted file mode 100644
index 9ef1e9e..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_menu_bottom_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_menu_bottom_focused_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_menu_bottom_focused_hover.png
deleted file mode 100644
index 599813921..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_menu_bottom_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_menu_bottom_focused_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_menu_bottom_focused_pressed.png
deleted file mode 100644
index afaec86a..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_menu_bottom_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_menu_bottom_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_menu_bottom_hover.png
deleted file mode 100644
index 755c582a..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_menu_bottom_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_menu_bottom_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_menu_bottom_pressed.png
deleted file mode 100644
index 885d008..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_menu_bottom_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_menu_center.png b/ui/views/resources/default_200_percent/common/combobox_button_menu_center.png
deleted file mode 100644
index b09a70f..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_menu_center.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_menu_center_focused.png b/ui/views/resources/default_200_percent/common/combobox_button_menu_center_focused.png
deleted file mode 100644
index d5d02d2..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_menu_center_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_menu_center_focused_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_menu_center_focused_hover.png
deleted file mode 100644
index ec3fc63d..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_menu_center_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_menu_center_focused_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_menu_center_focused_pressed.png
deleted file mode 100644
index f05b3a1..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_menu_center_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_menu_center_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_menu_center_hover.png
deleted file mode 100644
index 06512b6..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_menu_center_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_menu_center_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_menu_center_pressed.png
deleted file mode 100644
index 408af30..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_menu_center_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_menu_top.png b/ui/views/resources/default_200_percent/common/combobox_button_menu_top.png
deleted file mode 100644
index 1703b12..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_menu_top.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_menu_top_focused.png b/ui/views/resources/default_200_percent/common/combobox_button_menu_top_focused.png
deleted file mode 100644
index 0fa55b8..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_menu_top_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_menu_top_focused_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_menu_top_focused_hover.png
deleted file mode 100644
index 88a965ce..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_menu_top_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_menu_top_focused_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_menu_top_focused_pressed.png
deleted file mode 100644
index abed0e1c..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_menu_top_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_menu_top_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_menu_top_hover.png
deleted file mode 100644
index 5928ec7..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_menu_top_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_menu_top_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_menu_top_pressed.png
deleted file mode 100644
index 1e3c086d..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_menu_top_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_top.png b/ui/views/resources/default_200_percent/common/combobox_button_top.png
deleted file mode 100644
index 8fdb8ea..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_top.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_top_focused.png b/ui/views/resources/default_200_percent/common/combobox_button_top_focused.png
deleted file mode 100644
index 3c5f5bf..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_top_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_top_focused_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_top_focused_hover.png
deleted file mode 100644
index ac4d094..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_top_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_top_focused_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_top_focused_pressed.png
deleted file mode 100644
index 3c82cca4..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_top_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_top_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_top_hover.png
deleted file mode 100644
index 928418f..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_top_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_top_left.png b/ui/views/resources/default_200_percent/common/combobox_button_top_left.png
deleted file mode 100644
index 9888825..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_top_left.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_top_left_focused.png b/ui/views/resources/default_200_percent/common/combobox_button_top_left_focused.png
deleted file mode 100644
index 653418c4..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_top_left_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_top_left_focused_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_top_left_focused_hover.png
deleted file mode 100644
index 0b09897..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_top_left_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_top_left_focused_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_top_left_focused_pressed.png
deleted file mode 100644
index fac760c..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_top_left_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_top_left_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_top_left_hover.png
deleted file mode 100644
index 694d0dc..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_top_left_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_top_left_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_top_left_pressed.png
deleted file mode 100644
index eda07da..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_top_left_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_top_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_top_pressed.png
deleted file mode 100644
index 924ce682..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_top_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_top_right.png b/ui/views/resources/default_200_percent/common/combobox_button_top_right.png
deleted file mode 100644
index ce5bfe5..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_top_right.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_top_right_focused.png b/ui/views/resources/default_200_percent/common/combobox_button_top_right_focused.png
deleted file mode 100644
index e71ec77..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_top_right_focused.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_top_right_focused_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_top_right_focused_hover.png
deleted file mode 100644
index 7def249..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_top_right_focused_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_top_right_focused_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_top_right_focused_pressed.png
deleted file mode 100644
index 3c82cca4..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_top_right_focused_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_top_right_hover.png b/ui/views/resources/default_200_percent/common/combobox_button_top_right_hover.png
deleted file mode 100644
index 7ab4e33..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_top_right_hover.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/default_200_percent/common/combobox_button_top_right_pressed.png b/ui/views/resources/default_200_percent/common/combobox_button_top_right_pressed.png
deleted file mode 100644
index 924ce682..0000000
--- a/ui/views/resources/default_200_percent/common/combobox_button_top_right_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ui/views/resources/views_resources.grd b/ui/views/resources/views_resources.grd
index 9b6b9964..b1bdbd0 100644
--- a/ui/views/resources/views_resources.grd
+++ b/ui/views/resources/views_resources.grd
@@ -53,78 +53,6 @@
         <structure type="chrome_scaled_image" name="IDR_CLOSE_H" file="close_hover.png" />
         <structure type="chrome_scaled_image" name="IDR_CLOSE_P" file="close_pressed.png" />
       </if>
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_BOTTOM" file="common/combobox_button_bottom.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_H_BOTTOM" file="common/combobox_button_bottom_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_P_BOTTOM" file="common/combobox_button_bottom_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_BOTTOM_LEFT" file="common/combobox_button_bottom_left.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_H_BOTTOM_LEFT" file="common/combobox_button_bottom_left_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_P_BOTTOM_LEFT" file="common/combobox_button_bottom_left_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_BOTTOM_RIGHT" file="common/combobox_button_bottom_right.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_H_BOTTOM_RIGHT" file="common/combobox_button_bottom_right_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_P_BOTTOM_RIGHT" file="common/combobox_button_bottom_right_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_CENTER" file="common/combobox_button_center.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_H_CENTER" file="common/combobox_button_center_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_P_CENTER" file="common/combobox_button_center_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_LEFT" file="common/combobox_button_center_left.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_H_LEFT" file="common/combobox_button_center_left_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_P_LEFT" file="common/combobox_button_center_left_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_RIGHT" file="common/combobox_button_center_right.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_H_RIGHT" file="common/combobox_button_center_right_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_P_RIGHT" file="common/combobox_button_center_right_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_MENU_BOTTOM" file="common/combobox_button_menu_bottom.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_H_MENU_BOTTOM" file="common/combobox_button_menu_bottom_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_P_MENU_BOTTOM" file="common/combobox_button_menu_bottom_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_MENU_CENTER" file="common/combobox_button_menu_center.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_H_MENU_CENTER" file="common/combobox_button_menu_center_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_P_MENU_CENTER" file="common/combobox_button_menu_center_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_MENU_TOP" file="common/combobox_button_menu_top.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_H_MENU_TOP" file="common/combobox_button_menu_top_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_P_MENU_TOP" file="common/combobox_button_menu_top_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_TOP" file="common/combobox_button_top.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_H_TOP" file="common/combobox_button_top_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_P_TOP" file="common/combobox_button_top_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_TOP_LEFT" file="common/combobox_button_top_left.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_H_TOP_LEFT" file="common/combobox_button_top_left_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_P_TOP_LEFT" file="common/combobox_button_top_left_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_TOP_RIGHT" file="common/combobox_button_top_right.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_H_TOP_RIGHT" file="common/combobox_button_top_right_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_P_TOP_RIGHT" file="common/combobox_button_top_right_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_BOTTOM" file="common/combobox_button_bottom_focused.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_H_BOTTOM" file="common/combobox_button_bottom_focused_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_P_BOTTOM" file="common/combobox_button_bottom_focused_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_BOTTOM_LEFT" file="common/combobox_button_bottom_left_focused.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_H_BOTTOM_LEFT" file="common/combobox_button_bottom_left_focused_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_P_BOTTOM_LEFT" file="common/combobox_button_bottom_left_focused_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_BOTTOM_RIGHT" file="common/combobox_button_bottom_right_focused.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_H_BOTTOM_RIGHT" file="common/combobox_button_bottom_right_focused_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_P_BOTTOM_RIGHT" file="common/combobox_button_bottom_right_focused_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_CENTER" file="common/combobox_button_center_focused.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_H_CENTER" file="common/combobox_button_center_focused_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_P_CENTER" file="common/combobox_button_center_focused_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_LEFT" file="common/combobox_button_center_left_focused.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_H_LEFT" file="common/combobox_button_center_left_focused_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_P_LEFT" file="common/combobox_button_center_left_focused_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_RIGHT" file="common/combobox_button_center_right_focused.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_H_RIGHT" file="common/combobox_button_center_right_focused_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_P_RIGHT" file="common/combobox_button_center_right_focused_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_MENU_BOTTOM" file="common/combobox_button_menu_bottom_focused.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_H_MENU_BOTTOM" file="common/combobox_button_menu_bottom_focused_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_P_MENU_BOTTOM" file="common/combobox_button_menu_bottom_focused_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_MENU_CENTER" file="common/combobox_button_menu_center_focused.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_H_MENU_CENTER" file="common/combobox_button_menu_center_focused_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_P_MENU_CENTER" file="common/combobox_button_menu_center_focused_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_MENU_TOP" file="common/combobox_button_menu_top_focused.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_H_MENU_TOP" file="common/combobox_button_menu_top_focused_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_P_MENU_TOP" file="common/combobox_button_menu_top_focused_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_TOP" file="common/combobox_button_top_focused.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_H_TOP" file="common/combobox_button_top_focused_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_P_TOP" file="common/combobox_button_top_focused_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_TOP_LEFT" file="common/combobox_button_top_left_focused.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_H_TOP_LEFT" file="common/combobox_button_top_left_focused_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_P_TOP_LEFT" file="common/combobox_button_top_left_focused_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_TOP_RIGHT" file="common/combobox_button_top_right_focused.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_H_TOP_RIGHT" file="common/combobox_button_top_right_focused_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_COMBOBOX_BUTTON_F_P_TOP_RIGHT" file="common/combobox_button_top_right_focused_pressed.png" />
       <structure type="chrome_scaled_image" name="IDR_CONTENT_BOTTOM_CENTER" file="content_bottom_center.png" />
       <structure type="chrome_scaled_image" name="IDR_CONTENT_BOTTOM_LEFT_CORNER" file="content_bottom_left_corner.png" />
       <structure type="chrome_scaled_image" name="IDR_CONTENT_BOTTOM_RIGHT_CORNER" file="content_bottom_right_corner.png" />
diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc
index 65927754..f0f7060 100644
--- a/ui/views/view_unittest.cc
+++ b/ui/views/view_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/macros.h"
 #include "base/rand_util.h"
 #include "base/run_loop.h"
+#include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -3665,8 +3666,8 @@
   View::Views views;
   v1.GetViewsInGroup(kGroup, &views);
   EXPECT_EQ(2U, views.size());
-  EXPECT_NE(views.cend(), std::find(views.cbegin(), views.cend(), &v3));
-  EXPECT_NE(views.cend(), std::find(views.cbegin(), views.cend(), &v4));
+  EXPECT_TRUE(base::ContainsValue(views, &v3));
+  EXPECT_TRUE(base::ContainsValue(views, &v4));
 }
 
 TEST_F(ViewTest, AddExistingChild) {
diff --git a/ui/views/widget/ax_native_widget_mac_unittest.mm b/ui/views/widget/ax_native_widget_mac_unittest.mm
index 86dfc83..45e70d3 100644
--- a/ui/views/widget/ax_native_widget_mac_unittest.mm
+++ b/ui/views/widget/ax_native_widget_mac_unittest.mm
@@ -16,6 +16,7 @@
 #include "ui/accessibility/ax_node_data.h"
 #import "ui/accessibility/platform/ax_platform_node_mac.h"
 #include "ui/base/ime/text_input_type.h"
+#include "ui/base/models/combobox_model.h"
 #import "ui/gfx/mac/coordinate_conversion.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/combobox/combobox.h"
diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc
new file mode 100644
index 0000000..a5bf57f
--- /dev/null
+++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc
@@ -0,0 +1,110 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h"
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "ui/aura/client/capture_client.h"
+#include "ui/aura/client/cursor_client.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/base/dragdrop/os_exchange_data_provider_aura.h"
+#include "ui/platform_window/platform_window_delegate.h"
+#include "ui/platform_window/platform_window_handler/wm_drag_handler.h"
+#include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
+
+namespace views {
+
+DesktopDragDropClientOzone::DesktopDragDropClientOzone(
+    aura::Window* root_window,
+    views::DesktopNativeCursorManager* cursor_manager,
+    ui::WmDragHandler* drag_handler)
+    : root_window_(root_window),
+      cursor_manager_(cursor_manager),
+      drag_handler_(drag_handler) {}
+
+DesktopDragDropClientOzone::~DesktopDragDropClientOzone() {
+  if (in_move_loop_)
+    DragCancel();
+}
+
+int DesktopDragDropClientOzone::StartDragAndDrop(
+    const ui::OSExchangeData& data,
+    aura::Window* root_window,
+    aura::Window* source_window,
+    const gfx::Point& root_location,
+    int operation,
+    ui::DragDropTypes::DragEventSource source) {
+  if (!drag_handler_)
+    return ui::DragDropTypes::DragOperation::DRAG_NONE;
+
+  DCHECK(!in_move_loop_);
+  base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
+  quit_closure_ = run_loop.QuitClosure();
+
+  // Chrome expects starting drag and drop to release capture.
+  aura::Window* capture_window =
+      aura::client::GetCaptureClient(root_window)->GetGlobalCaptureWindow();
+  if (capture_window)
+    capture_window->ReleaseCapture();
+
+  aura::client::CursorClient* cursor_client =
+      aura::client::GetCursorClient(root_window);
+
+  initial_cursor_ = source_window->GetHost()->last_cursor();
+  drag_operation_ = operation;
+  cursor_client->SetCursor(
+      cursor_manager_->GetInitializedCursor(ui::CursorType::kGrabbing));
+
+  drag_handler_->StartDrag(
+      data, operation, cursor_client->GetCursor(),
+      base::BindOnce(&DesktopDragDropClientOzone::OnDragSessionClosed,
+                     base::Unretained(this)));
+  in_move_loop_ = true;
+  run_loop.Run();
+  DragDropSessionCompleted();
+  return drag_operation_;
+}
+
+void DesktopDragDropClientOzone::DragCancel() {
+  QuitRunLoop();
+}
+
+bool DesktopDragDropClientOzone::IsDragDropInProgress() {
+  return in_move_loop_;
+}
+
+void DesktopDragDropClientOzone::AddObserver(
+    aura::client::DragDropClientObserver* observer) {
+  NOTIMPLEMENTED_LOG_ONCE();
+}
+
+void DesktopDragDropClientOzone::RemoveObserver(
+    aura::client::DragDropClientObserver* observer) {
+  NOTIMPLEMENTED_LOG_ONCE();
+}
+
+void DesktopDragDropClientOzone::OnDragSessionClosed(int dnd_action) {
+  drag_operation_ = dnd_action;
+  QuitRunLoop();
+}
+
+void DesktopDragDropClientOzone::DragDropSessionCompleted() {
+  aura::client::CursorClient* cursor_client =
+      aura::client::GetCursorClient(root_window_);
+  if (!cursor_client)
+    return;
+
+  cursor_client->SetCursor(initial_cursor_);
+}
+
+void DesktopDragDropClientOzone::QuitRunLoop() {
+  in_move_loop_ = false;
+  if (quit_closure_.is_null())
+    return;
+  std::move(quit_closure_).Run();
+}
+
+}  // namespace views
diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h
new file mode 100644
index 0000000..e3632a7
--- /dev/null
+++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h
@@ -0,0 +1,66 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_OZONE_H_
+#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_OZONE_H_
+
+#include "base/callback.h"
+#include "ui/aura/client/drag_drop_client.h"
+#include "ui/base/cursor/cursor.h"
+#include "ui/platform_window/platform_window_handler/wm_drag_handler.h"
+#include "ui/views/views_export.h"
+
+namespace views {
+class DesktopNativeCursorManager;
+
+class VIEWS_EXPORT DesktopDragDropClientOzone
+    : public aura::client::DragDropClient {
+ public:
+  DesktopDragDropClientOzone(aura::Window* root_window,
+                             views::DesktopNativeCursorManager* cursor_manager,
+                             ui::WmDragHandler* drag_handler);
+  ~DesktopDragDropClientOzone() override;
+
+  void OnDragSessionClosed(int operation);
+
+  // Overridden from aura::client::DragDropClient:
+  int StartDragAndDrop(const ui::OSExchangeData& data,
+                       aura::Window* root_window,
+                       aura::Window* source_window,
+                       const gfx::Point& root_location,
+                       int operation,
+                       ui::DragDropTypes::DragEventSource source) override;
+  void DragCancel() override;
+  bool IsDragDropInProgress() override;
+  void AddObserver(aura::client::DragDropClientObserver* observer) override;
+  void RemoveObserver(aura::client::DragDropClientObserver* observer) override;
+
+ private:
+  void DragDropSessionCompleted();
+  void QuitRunLoop();
+
+  aura::Window* const root_window_;
+
+  DesktopNativeCursorManager* cursor_manager_;
+
+  ui::WmDragHandler* const drag_handler_;
+
+  // Cursor in use prior to the move loop starting. Restored when the move loop
+  // quits.
+  gfx::NativeCursor initial_cursor_;
+
+  base::OnceClosure quit_closure_;
+
+  // The operation bitfield.
+  int drag_operation_ = 0;
+
+  //  The flag that controls whether it has a nested run loop.
+  bool in_move_loop_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientOzone);
+};
+
+}  // namespace views
+
+#endif  // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_OZONE_H_
diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc
new file mode 100644
index 0000000..a9b8649
--- /dev/null
+++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc
@@ -0,0 +1,121 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h"
+
+#include "base/memory/weak_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/base/dragdrop/os_exchange_data.h"
+#include "ui/platform_window/platform_window_handler/wm_drag_handler.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/views_delegate.h"
+#include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
+#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
+#include "ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h"
+
+namespace views {
+
+namespace {
+
+class FakeWmDragHandler;
+
+// A fake handler, which initiates dragging.
+class FakeWmDragHandler : public ui::WmDragHandler {
+ public:
+  FakeWmDragHandler() : weak_ptr_factory_(this) {}
+  ~FakeWmDragHandler() override = default;
+
+  // ui::WmDragHandler
+  void StartDrag(const OSExchangeData& data,
+                 const int operation,
+                 gfx::NativeCursor cursor,
+                 base::OnceCallback<void(int)> callback) override {
+    callback_ = std::move(callback);
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(
+                       [](base::OnceCallback<void(int)> callback) {
+                         std::move(callback).Run(ui::DragDropTypes::DRAG_COPY);
+                       },
+                       std::move(callback_)));
+  }
+
+ private:
+  base::OnceCallback<void(int)> callback_;
+  base::WeakPtrFactory<FakeWmDragHandler> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeWmDragHandler);
+};
+
+}  // namespace
+
+class DesktopDragDropClientOzoneTest : public ViewsTestBase {
+ public:
+  DesktopDragDropClientOzoneTest() = default;
+  ~DesktopDragDropClientOzoneTest() override = default;
+
+  int StartDragAndDrop() {
+    ui::OSExchangeData data;
+    data.SetString(base::ASCIIToUTF16("Test"));
+    SkBitmap drag_bitmap;
+    drag_bitmap.allocN32Pixels(10, 10);
+    drag_bitmap.eraseARGB(0xFF, 0, 0, 0);
+    gfx::ImageSkia drag_image(gfx::ImageSkia::CreateFrom1xBitmap(drag_bitmap));
+    data.provider().SetDragImage(drag_image, gfx::Vector2d());
+
+    return client_->StartDragAndDrop(
+        data, widget_->GetNativeWindow()->GetRootWindow(),
+        widget_->GetNativeWindow(), gfx::Point(),
+        ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE,
+        ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
+  }
+
+  // ViewsTestBase:
+  void SetUp() override {
+    ViewsTestBase::SetUp();
+    test_views_delegate()->set_use_desktop_native_widgets(true);
+
+    // Create widget to initiate the drags.
+    widget_ = std::make_unique<Widget>();
+    Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
+    params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+    params.native_widget = new DesktopNativeWidgetAura(widget_.get());
+    params.bounds = gfx::Rect(100, 100);
+    widget_->Init(params);
+    widget_->Show();
+
+    aura::Window* window = widget_->GetNativeWindow();
+    cursor_manager_ = std::make_unique<DesktopNativeCursorManager>();
+    drag_handler_ = std::make_unique<FakeWmDragHandler>();
+    client_ = std::make_unique<DesktopDragDropClientOzone>(
+        window, cursor_manager_.get(), drag_handler_.get());
+  }
+
+  void TearDown() override {
+    client_.reset();
+    cursor_manager_.reset();
+    drag_handler_.reset();
+    widget_.reset();
+    ViewsTestBase::TearDown();
+  }
+
+ private:
+  std::unique_ptr<DesktopDragDropClientOzone> client_;
+  std::unique_ptr<DesktopNativeCursorManager> cursor_manager_;
+  std::unique_ptr<FakeWmDragHandler> drag_handler_;
+
+  // The widget used to initiate drags.
+  std::unique_ptr<Widget> widget_;
+
+  DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientOzoneTest);
+};
+
+TEST_F(DesktopDragDropClientOzoneTest, StartDrag) {
+  int result = StartDragAndDrop();
+  EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
+}
+
+}  // namespace views
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
index 44033ed3..8aaf011 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
@@ -15,6 +15,7 @@
 #include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h"
 #include "ui/platform_window/platform_window_init_properties.h"
 #include "ui/views/corewm/tooltip_aura.h"
+#include "ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h"
 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
 #include "ui/views/widget/desktop_aura/window_event_filter.h"
 #include "ui/views/widget/widget_aura_utils.h"
@@ -96,6 +97,7 @@
     const Widget::InitParams& params) {
   native_widget_delegate_->OnNativeWidgetCreated(true);
 
+#if defined(OS_LINUX)
   // Setup a non_client_window_event_filter, which handles resize/move, double
   // click and other events.
   DCHECK(!non_client_window_event_filter_);
@@ -108,6 +110,7 @@
 
   non_client_window_event_filter_ = std::move(window_event_filter);
   window()->AddPreTargetHandler(non_client_window_event_filter_.get());
+#endif
 }
 
 void DesktopWindowTreeHostPlatform::OnWidgetInitDone() {}
@@ -122,9 +125,9 @@
 std::unique_ptr<aura::client::DragDropClient>
 DesktopWindowTreeHostPlatform::CreateDragDropClient(
     DesktopNativeCursorManager* cursor_manager) {
-  // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED_LOG_ONCE();
-  return nullptr;
+  ui::WmDragHandler* drag_handler = ui::GetWmDragHandler(*(platform_window()));
+  return std::make_unique<DesktopDragDropClientOzone>(window(), cursor_manager,
+                                                      drag_handler);
 }
 
 void DesktopWindowTreeHostPlatform::Close() {
@@ -539,11 +542,13 @@
 }
 
 void DesktopWindowTreeHostPlatform::RemoveNonClientEventFilter() {
+#if defined(OS_LINUX)
   if (!non_client_window_event_filter_)
     return;
 
   window()->RemovePreTargetHandler(non_client_window_event_filter_.get());
   non_client_window_event_filter_.reset();
+#endif
 }
 
 Widget* DesktopWindowTreeHostPlatform::GetWidget() {
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
index e0b98abd..fa10671 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
@@ -6,6 +6,7 @@
 #define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_HOST_PLATFORM_H_
 
 #include "base/memory/weak_ptr.h"
+#include "build/build_config.h"
 #include "ui/aura/window_tree_host_platform.h"
 #include "ui/views/views_export.h"
 #include "ui/views/widget/desktop_aura/desktop_window_tree_host.h"
@@ -120,8 +121,10 @@
 
   bool is_active_ = false;
 
+#if defined(OS_LINUX)
   // A handler for events intended for non client area.
   std::unique_ptr<WindowEventFilter> non_client_window_event_filter_;
+#endif
 
   base::WeakPtrFactory<DesktopWindowTreeHostPlatform> weak_factory_{this};
 
diff --git a/ui/webui/mojo_web_ui_controller.cc b/ui/webui/mojo_web_ui_controller.cc
index 769e194..4931648 100644
--- a/ui/webui/mojo_web_ui_controller.cc
+++ b/ui/webui/mojo_web_ui_controller.cc
@@ -24,11 +24,8 @@
     content::RenderFrameHost* render_frame_host,
     const std::string& interface_name,
     mojo::ScopedMessagePipeHandle* interface_pipe) {
-  if (!registry_.CanBindInterface(interface_name)) {
-    LOG(WARNING) << "Cannot bind request to " << interface_name << "; ignoring "
-                 << "request.";
+  if (!registry_.CanBindInterface(interface_name))
     return;
-  }
 
   // Right now, this is expected to be called only for main frames.
   if (render_frame_host->GetParent()) {
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html
index a75218e..660f1ac 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html
@@ -33,7 +33,7 @@
       }
     </style>
     <iron-pages attr-for-selected="is"
-        selected="[[visiblePageName_]]"
+        selected="[[visiblePageName]]"
         selected-item="{{visiblePage_}}">
       <template is="dom-if" if="[[shouldPasswordPageBeIncluded_(delegate)]]"
           restamp>
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js
index 3dc0dd4..2ec322f4 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js
@@ -41,7 +41,7 @@
       forwardButtonDisabled: {
         type: Boolean,
         computed: 'shouldForwardButtonBeDisabled_(' +
-            'passwordPageForwardButtonDisabled_, visiblePageName_)',
+            'passwordPageForwardButtonDisabled_, visiblePageName)',
         notify: true
       },
 
@@ -68,12 +68,12 @@
       /**
        * Element name of the currently visible page.
        *
-       * @private {!multidevice_setup.PageName}
+       * @type {!multidevice_setup.PageName}
        */
-      visiblePageName_: {
+      visiblePageName: {
         type: String,
         value: PageName.START,
-        notify: true,  // For testing purposes only.
+        notify: true,
       },
 
       /**
@@ -167,10 +167,10 @@
     /** @private */
     onBackwardNavigationRequested_: function() {
       // The back button is only visible on the password page.
-      assert(this.visiblePageName_ == PageName.PASSWORD);
+      assert(this.visiblePageName == PageName.PASSWORD);
 
       this.$$('password-page').clearPasswordTextInput();
-      this.visiblePageName_ = PageName.START;
+      this.visiblePageName = PageName.START;
     },
 
     /** @private */
@@ -187,9 +187,9 @@
 
     /** @private */
     navigateForward_: function() {
-      switch (this.visiblePageName_) {
+      switch (this.visiblePageName) {
         case PageName.FAILURE:
-          this.visiblePageName_ = PageName.START;
+          this.visiblePageName = PageName.START;
           return;
         case PageName.PASSWORD:
           this.$$('password-page').clearPasswordTextInput();
@@ -200,7 +200,7 @@
           return;
         case PageName.START:
           if (this.delegate.isPasswordRequiredToSetHost())
-            this.visiblePageName_ = PageName.PASSWORD;
+            this.visiblePageName = PageName.PASSWORD;
           else
             this.setHostDevice_();
           return;
@@ -225,7 +225,7 @@
               return;
             }
 
-            this.visiblePageName_ = PageName.SUCCESS;
+            this.visiblePageName = PageName.SUCCESS;
             this.fire('forward-button-focus-requested');
           })
           .catch((error) => {
@@ -254,7 +254,7 @@
      * @private
      */
     shouldForwardButtonBeDisabled_: function() {
-      return (this.visiblePageName_ == PageName.PASSWORD) &&
+      return (this.visiblePageName == PageName.PASSWORD) &&
           this.passwordPageForwardButtonDisabled_;
     },
 
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.html b/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.html
index 993b7c9..d40bb91b 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.html
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.html
@@ -45,26 +45,30 @@
 <dom-module id="setup-pin-keyboard">
   <template>
     <style include="settings-shared">
+      #errorIcon {
+        --iron-icon-height: 20px;
+        --iron-icon-width: 20px;
+        display: none;
+      }
+
       .error {
         color: var(--google-red-600);
       }
 
-      .error > iron-icon {
+      .error #errorIcon {
         --iron-icon-fill-color: var(--google-red-600);
+        display: inline-block;
       }
 
       .warning {
         color: var(--cr-secondary-text-color);
       }
 
-      .warning > iron-icon {
-        --iron-icon-fill-color: var(--google-grey-refresh-700);
-      }
-
       #problemDiv {
         align-items: center;
         display: flex;
         flex-direction: row;
+        font-size: 10px;
         height: 32px;
         min-height: 0;
       }
@@ -74,10 +78,6 @@
       #problemDiv[invisible] {
         visibility: hidden;
       }
-
-      #problemMessage {
-        font-size: 10px;
-      }
     </style>
     <pin-keyboard id="pinKeyboard" on-pin-change="onPinChange_"
         on-submit="onPinSubmit_" value="{{pinKeyboardValue_}}"
@@ -86,13 +86,11 @@
       <!-- Warning/error; only shown if title is hidden. -->
       <div id="problemDiv" class$="[[problemClass_]]"
           invisible$="[[!problemMessageId_]]" problem>
-        <div>
-          <iron-icon icon="cr:error-outline"></iron-icon>
-          <span id="problemMessage">
-            [[formatProblemMessage_(locale, problemMessageId_,
-                  problemMessageParameters_)]]
-          </span>
-        </div>
+        <iron-icon id="errorIcon" icon="cr:error-outline"></iron-icon>
+        <span>
+          [[formatProblemMessage_(locale, problemMessageId_,
+                problemMessageParameters_)]]
+        </span>
       </div>
     </pin-keyboard>
   </template>
diff --git a/webrunner/BUILD.gn b/webrunner/BUILD.gn
index e544f7d..34bfb1f 100644
--- a/webrunner/BUILD.gn
+++ b/webrunner/BUILD.gn
@@ -39,7 +39,7 @@
     "app/web_content_runner.h",
   ]
   deps = [
-    ":fidl",
+    ":web_fidl",
     "//base",
     "//url",
   ]
@@ -53,8 +53,8 @@
 
 executable("service_exe") {
   deps = [
-    ":fidl",
     ":service_lib",
+    ":web_fidl",
     "//base",
     "//content/public/app:both",
     "//services/service_manager/embedder:embedder_switches",
@@ -96,7 +96,7 @@
     "$root_out_dir/webrunner.pak",
   ]
   public_deps = [
-    ":fidl",
+    ":web_fidl",
   ]
   configs += [ ":webrunner_implementation" ]
 
@@ -186,8 +186,8 @@
     "browser/test/data",
   ]
   deps = [
-    ":fidl",
     ":service_lib",
+    ":web_fidl",
     "//base/test:test_support",
     "//content/test:test_support",
     "//net:test_support",
@@ -202,8 +202,8 @@
     "service/context_provider_impl_unittest.cc",
   ]
   deps = [
-    ":fidl",
     ":service_lib",
+    ":web_fidl",
     "//base/test:run_all_unittests",
     "//base/test:test_support",
     "//testing/gmock",
@@ -211,16 +211,16 @@
   ]
 }
 
-fidl_library("fidl") {
+fidl_library("web_fidl") {
   library_name = "web"
   namespace = "chromium"
 
   sources = [
-    "fidl/context.fidl",
-    "fidl/context_provider.fidl",
-    "fidl/frame.fidl",
-    "fidl/navigation_controller.fidl",
-    "fidl/navigation_event_observer.fidl",
+    "fidl/web/context.fidl",
+    "fidl/web/context_provider.fidl",
+    "fidl/web/frame.fidl",
+    "fidl/web/navigation_controller.fidl",
+    "fidl/web/navigation_event_observer.fidl",
   ]
 
   public_deps = [
diff --git a/webrunner/fidl/context.fidl b/webrunner/fidl/web/context.fidl
similarity index 100%
rename from webrunner/fidl/context.fidl
rename to webrunner/fidl/web/context.fidl
diff --git a/webrunner/fidl/context_provider.fidl b/webrunner/fidl/web/context_provider.fidl
similarity index 100%
rename from webrunner/fidl/context_provider.fidl
rename to webrunner/fidl/web/context_provider.fidl
diff --git a/webrunner/fidl/frame.fidl b/webrunner/fidl/web/frame.fidl
similarity index 100%
rename from webrunner/fidl/frame.fidl
rename to webrunner/fidl/web/frame.fidl
diff --git a/webrunner/fidl/navigation_controller.fidl b/webrunner/fidl/web/navigation_controller.fidl
similarity index 100%
rename from webrunner/fidl/navigation_controller.fidl
rename to webrunner/fidl/web/navigation_controller.fidl
diff --git a/webrunner/fidl/navigation_event_observer.fidl b/webrunner/fidl/web/navigation_event_observer.fidl
similarity index 100%
rename from webrunner/fidl/navigation_event_observer.fidl
rename to webrunner/fidl/web/navigation_event_observer.fidl