diff --git a/.gn b/.gn
index 8fb99ce..6f27729 100644
--- a/.gn
+++ b/.gn
@@ -323,7 +323,6 @@
   "//third_party/accessibility_test_framework/*",
   "//third_party/adobe/*",
   "//third_party/afl/*",
-  "//third_party/analytics/*",
   "//third_party/android_build_tools/*",
   "//third_party/android_crazy_linker/*",
   "//third_party/android_data_chart/*",
diff --git a/AUTHORS b/AUTHORS
index 80d5c38..567fe15 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -277,6 +277,7 @@
 Ganesh Borle <ganesh.borle@samsung.com>
 Gao Chun <chun.gao@intel.com>
 Gao Chun <gaochun.dev@gmail.com>
+Gaurav Dhol <gaurav.dhol@einfochips.com>
 Gautham Banasandra <gautham.bangalore@gmail.com>
 George Adams <geoada@amazon.com>
 George Joseph <kottackal.george@gmail.com>
@@ -783,7 +784,7 @@
 Sergey Putilin <p.sergey@samsung.com>
 Sergey Shekyan <shekyan@gmail.com>
 Sergio Carlos Morales Angeles <carloschilazo@gmail.com>
-Sergiy Byelozyorov <rryk.ua@gmail.com>
+Sergiy Belozorov <rryk.ua@gmail.com>
 Seshadri Mahalingam <seshadri.mahalingam@gmail.com>
 Seungkyu Lee <zx6658@gmail.com>
 Sevan Janiyan <venture37@geeklan.co.uk>
@@ -980,8 +981,7 @@
 Rajesh Mahindra <rmahindra@uber.com>
 Yuan-Pin Yu <yjames@uber.com>
 Vinoth Chandar <vinoth@uber.com>
-Zheng Xu <zxu@kobo.com>
-Gaurav Dhol <gaurav.dhol@einfochips.com>

+Zheng Xu <zxu@kobo.com>

 
 ACCESS CO., LTD. <*@access-company.com>
 Akamai Inc. <*@akamai.com>
diff --git a/BUILD.gn b/BUILD.gn
index 46bcfd07..dbe4b22 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -15,18 +15,18 @@
 import("//components/nacl/features.gni")
 import("//device/vr/buildflags/buildflags.gni")
 import("//extensions/buildflags/buildflags.gni")
-import("//media/media_options.gni")
+import("//gpu/vulkan/features.gni")
 import("//media/gpu/args.gni")
+import("//media/media_options.gni")
 import("//remoting/remoting_enable.gni")
+import("//testing/test.gni")
 import("//third_party/openh264/openh264_args.gni")
 import("//tools/ipc_fuzzer/ipc_fuzzer.gni")
 import("//ui/base/ui_features.gni")
-import("//ui/webui/webui_features.gni")
 import("//ui/ozone/ozone.gni")
+import("//ui/webui/webui_features.gni")
 import("//v8/gni/v8.gni")
 import("//v8/snapshot_toolchain.gni")
-import("//gpu/vulkan/features.gni")
-import("//testing/test.gni")
 
 if (is_android) {
   import("//build/config/android/config.gni")
@@ -304,6 +304,9 @@
       "//content/public/android:content_junit_tests",
       "//content/shell/android:content_shell_apk",
       "//device:device_junit_tests",
+
+      # TODO(https://crbug.com/879065): remove once tests have been migrated to
+      # the video_decode_accelerator_tests target.
       "//media/gpu:video_decode_accelerator_unittest",
       "//net/android:net_junit_tests",
       "//services:service_junit_tests",
@@ -397,7 +400,6 @@
       "//ash:ash_unittests",
       "//ash/app_list:app_list_demo",
       "//ash/app_list:app_list_unittests",
-      "//chrome/browser/resources/chromeos/zip_archiver/cpp:ziparchiver_unittests",
       "//chromeos:chromeos_unittests",
       "//chromeos/components:chromeos_components_unittests",
       "//chromeos/components/proximity_auth:proximity_auth_unittests",
@@ -553,7 +555,6 @@
         "//media/cast:tap_proxy",
         "//skia:filter_fuzz_stub",
         "//skia:image_operations_bench",
-        "//third_party/sqlite:sqlite_shell",
         "//ui/snapshot:snapshot_unittests",
       ]
 
@@ -669,6 +670,10 @@
     deps += [ "//third_party/perfetto:all" ]
   }
 
+  if (is_mac || is_linux) {
+    deps += [ "//third_party/sqlite:sqlite_shell" ]
+  }
+
   if (is_linux && !is_chromeos && !is_chromecast) {
     # TODO(GYP): Figure out if any of these should be in gn_all
     # and figure out how cross-platform they are
@@ -717,15 +722,21 @@
     }
   }
 
-  if ((is_linux && !is_chromeos && !is_chromecast) ||
+  if ((is_linux && !is_chromecast) ||
       (is_win && (use_drfuzz || use_libfuzzer)) || (use_libfuzzer && is_mac)) {
     deps += [
       "//testing/libfuzzer/fuzzers",
-      "//testing/libfuzzer/tests:libfuzzer_tests",
       "//third_party/icu/fuzzers",
       "//third_party/qcms:fuzzers",
       "//third_party/zlib/contrib/tests/fuzzers",
     ]
+
+    # TODO(crbug.com/906751): Get the libFuzzer tests working on Windows.
+    # Disable them for now becaue they cause the Windows clang ToT builder to
+    # fail.
+    if (!is_win) {
+      deps += [ "//testing/libfuzzer/tests:libfuzzer_tests" ]
+    }
   }
 
   if (enable_nacl) {
@@ -781,6 +792,16 @@
   }
 }
 
+if ((is_linux || is_win) && enable_remoting && !use_ozone) {
+  # This group is used for network annotation check test.
+  group("shipped_binaries") {
+    deps = [
+      "//chrome:chrome",
+      "//remoting/host:host",
+    ]
+  }
+}
+
 if (is_fuchsia) {
   # TODO(https://crbug.com/731217): This can't practically be in //v8 without
   # duplicating all the Fuchsia running infrastructure there.
@@ -870,6 +891,10 @@
 
     if (use_v4l2_codec || use_vaapi) {
       deps += [
+        "//media/gpu:video_decode_accelerator_tests",
+
+        # TODO(https://crbug.com/879065): remove once tests have been migrated
+        # to the above target.
         "//media/gpu:video_decode_accelerator_unittest",
         "//media/gpu:video_encode_accelerator_unittest",
       ]
@@ -1009,9 +1034,9 @@
       "//testing/scripts/common.py",
       "//testing/scripts/run_isolated_script_test.py",
       "//testing/xvfb.py",
-      "//third_party/WebKit/LayoutTests/",
       "//third_party/blink/perf_tests/",
       "//third_party/blink/tools/",
+      "//third_party/blink/web_tests/",
       "//third_party/pywebsocket/src/mod_pywebsocket/",
     ]
 
@@ -1070,17 +1095,17 @@
       "//testing/scripts/run_isolated_script_test.py",
       "//testing/test_env.py",
       "//testing/xvfb.py",
-      "//third_party/WebKit/LayoutTests/ASANExpectations",
-      "//third_party/WebKit/LayoutTests/LeakExpectations",
-      "//third_party/WebKit/LayoutTests/MSANExpectations",
-      "//third_party/WebKit/LayoutTests/NeverFixTests",
-      "//third_party/WebKit/LayoutTests/StaleTestExpectations",
-      "//third_party/WebKit/LayoutTests/SlowTests",
-      "//third_party/WebKit/LayoutTests/TestExpectations",
-      "//third_party/WebKit/LayoutTests/VirtualTestSuites",
       "//third_party/blink/renderer/bindings/scripts/",
       "//third_party/blink/renderer/build/scripts/",
       "//third_party/blink/tools/",
+      "//third_party/blink/web_tests/ASANExpectations",
+      "//third_party/blink/web_tests/LeakExpectations",
+      "//third_party/blink/web_tests/MSANExpectations",
+      "//third_party/blink/web_tests/NeverFixTests",
+      "//third_party/blink/web_tests/SlowTests",
+      "//third_party/blink/web_tests/StaleTestExpectations",
+      "//third_party/blink/web_tests/TestExpectations",
+      "//third_party/blink/web_tests/VirtualTestSuites",
       "//third_party/catapult/common/py_utils/",
       "//third_party/catapult/devil/",
       "//third_party/catapult/dependency_manager/",
diff --git a/DEPS b/DEPS
index 4a61048..7ebf90b 100644
--- a/DEPS
+++ b/DEPS
@@ -31,7 +31,6 @@
   'build_with_chromium',
   'checkout_android',
   'checkout_android_native_support',
-  'checkout_libaom',
   'checkout_nacl',
   'checkout_oculus_sdk',
 ]
@@ -80,9 +79,6 @@
   # the gn arg 'use_clang_coverage').
   'checkout_clang_coverage_tools': False,
 
-  # libaom provides support for AV1.
-  'checkout_libaom': True,
-
   # By default do not check out the Oculus SDK. Only available for Googlers.
   'checkout_oculus_sdk' : 'checkout_src_internal and checkout_win',
 
@@ -116,23 +112,23 @@
   # 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': '73b4a1f572546d46213c07eb65073940a0983221',
+  'skia_revision': 'e6305f38b584ac51368a98de6a76d3f3205c21bf',
   # 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': '9a0639762e7ada6acab150e3097d4ded62caf9de',
+  'v8_revision': '6acd03c9b8a8232aee95f25fbf6ae822aaedae75',
   # 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.
-  'swarming_revision': '7f463e66e1c4bb830ef3de9d046aa227d28e1b00',
+  'swarming_revision': '157bec8a25cc4ebd6a16052510d08b05b6102aad',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '778bf09deea44363f5b9aa5a8795e264936099c3',
+  'angle_revision': '5f01324fb627a502823d9e359d243c3430920258',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
-  'buildtools_revision': '13a00f110ef910a25763346d6538b60f12845656',
+  'buildtools_revision': '04161ec8d7c781e4498c699254c69ba0dd959fde',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -140,7 +136,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': 'cf927b1f0823b51a519fcec6f1919b092a58918e',
+  'pdfium_revision': 'c3daaa87ec17c6e91de95fcaa1b5d255a0d7f978',
   # 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.
@@ -148,7 +144,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
-  'boringssl_revision': 'f241a59dcca617c5b9d9880a8a9fd92996a654be',
+  'boringssl_revision': '0f5ecd3a854546d943104e1f7421e489b7f4d5aa',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -164,19 +160,19 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling NaCl
   # and whatever else without interference from each other.
-  'nacl_revision': '99673869a3cd8731d924bd32fa486feebfdc6c4f',
+  'nacl_revision': 'f701a90597fc85979319447c0cd44c3b52201c78',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': 'fb0d66d04c4dd8d7f9604af1a6001b2737cb5098',
+  'freetype_revision': '3dd4e76b19f3cd4f706c3455d6ae01765d07eee5',
   # 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': '1f14107f71a6c3da8270ed21c3588f945fa91733',
+  'harfbuzz_revision': '574d888c8a409295a952361a39c8e83a52a0fc3d',
   # 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': 'b81a9c76c982129c6ec624367bcb0c0dca4a49d3',
+  'catapult_revision': 'eee0f6ad0df532b4ba3eee2bf04a5a155817586c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -192,7 +188,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'feed_revision': '107851225e86b1ab6a222ce8ca7ac3729652273a',
+  'feed_revision': '23d32902a8bd6f4d55dea6a46fff43325c61b532',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling android_sdk_build-tools_version
   # and whatever else without interference from each other.
@@ -224,11 +220,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': 'cd22b3155708d7922524e7f52551e0794b3c1d62',
+  'spv_tools_revision': '3ee605d7ccb960345a454bad57e54238c66bfb05',
   # 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': '7cb43009d543e90698dd300eb26dfd6d9a9bb100',
+  'spv_headers_revision': 'bbf63435c37aafac5d1cdfc287401d87cd3ca364',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -240,7 +236,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '69b44ee6a1269afb5c7622189de236af0c4ec2a7',
+  'dawn_revision': '813bfbd061128113dd7b4c7c80321b536597c362',
 }
 
 # Only these hosts are allowed for dependencies in this DEPS file.
@@ -311,7 +307,7 @@
   },
 
   'src/ios/third_party/earl_grey2/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + 'f9ae5c477b3b1f8e38559867ca21dfb034c21d92',
+      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '3f9a3f28029bb7547b799a74458e45484b4a55f9',
       'condition': 'checkout_ios',
   },
 
@@ -347,7 +343,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '9f4ad51008e503ca15bdcd33077418a45f4b2824',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '6edafbda8131badfe6c57d4bb326071939a9713a',
       'condition': 'checkout_ios',
   },
 
@@ -408,15 +404,15 @@
       'packages': [
         {
           'package': 'infra/tools/luci/isolate/${{platform}}',
-          'version': 'git_revision:b4183943efee5d2a2d444c8551877ae96b78d8b2',
+          'version': 'git_revision:f140d0c3c6acecf6ca6cd8d66ddad9421158ab40',
         },
         {
           'package': 'infra/tools/luci/isolated/${{platform}}',
-          'version': 'git_revision:b4183943efee5d2a2d444c8551877ae96b78d8b2',
+          'version': 'git_revision:f140d0c3c6acecf6ca6cd8d66ddad9421158ab40',
         },
         {
           'package': 'infra/tools/luci/swarming/${{platform}}',
-          'version': 'git_revision:b4183943efee5d2a2d444c8551877ae96b78d8b2',
+          'version': 'git_revision:f140d0c3c6acecf6ca6cd8d66ddad9421158ab40',
         },
       ],
       'dep_type': 'cipd',
@@ -516,7 +512,7 @@
       'packages': [
           {
        'package': 'chromium/third_party/android_tools_bundletool',
-       'version': 'version:0.6.2-cr0',
+       'version': 'version:0.7.1-cr0',
    },
       ],
       'condition': 'checkout_android',
@@ -572,7 +568,7 @@
     Var('dawn_git') + '/dawn.git' + '@' +  Var('dawn_revision'),
 
   'src/third_party/glfw/src':
-    Var('chromium_git') + '/external/github.com/glfw/glfw.git@' +  '096efdf798896cff80a0b2db08d7398b703406fe',
+    Var('chromium_git') + '/external/github.com/glfw/glfw.git@' +  '2de2589f910b1a85905f425be4d32f33cec092df',
 
   'src/third_party/apache-portable-runtime/src': {
       'url': Var('chromium_git') + '/external/apache-portable-runtime.git' + '@' + 'c3f11fcd86b42922834cae91103cf068246c6bb6',
@@ -655,7 +651,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'a91e38e61cfb76fe278df270fe866890fdc31ac3',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '81cbcd45e1a28db58d86bb556e1c40a289f35ef9',
       'condition': 'checkout_linux',
   },
 
@@ -675,12 +671,12 @@
   },
 
   'src/third_party/custom_tabs_client/src': {
-      'url': Var('chromium_git') + '/custom-tabs-client.git' + '@' + 'c813ed8fcc4ece1838dba8dddf6d8ca39d6c6785',
+      'url': Var('chromium_git') + '/custom-tabs-client.git' + '@' + 'b61484e1cf0e849fe2f1c2271effa7748efc7db6',
       'condition': 'checkout_android',
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'c6ffd7af7d84ec354a92a0dc11613ec23cf82c60',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '9af233a5084871843af872224e4012e3f3f3af2f',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -821,7 +817,7 @@
     Var('chromium_git') + '/chromium/deps/hunspell_dictionaries.git' + '@' + 'a9bac57ce6c9d390a52ebaad3259f5fdb871210e',
 
   'src/third_party/icu':
-    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '45f655f2feb7069a7b9b47d1b1a596807bfd4220',
+    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '407b39301e71006b68bd38e770f35d32398a7b14',
 
   'src/third_party/icu4j': {
       'packages': [
@@ -867,10 +863,8 @@
   'src/third_party/libaddressinput/src':
     Var('chromium_git') + '/external/libaddressinput.git' + '@' + 'd7ed8e2f3f35ce9a3aafdfdc48745ceab66e7229',
 
-  'src/third_party/libaom/source/libaom': {
-    'url': Var('aomedia_git') + '/aom.git' + '@' +  '716f2896c6babc140a6a774c21653c3f4d04d9de',
-    'condition': 'checkout_libaom',
-  },
+  'src/third_party/libaom/source/libaom':
+    Var('aomedia_git') + '/aom.git' + '@' +  '67223a304e098da309776bac253456f75f613fc2',
 
   # Userspace interface to kernel DRM services.
   'src/third_party/libdrm/src': {
@@ -908,7 +902,7 @@
   },
 
   'src/third_party/libvpx/source/libvpx':
-    Var('chromium_git') + '/webm/libvpx.git' + '@' +  '4a8c248744500f9caf00588ca312efce5659e45e',
+    Var('chromium_git') + '/webm/libvpx.git' + '@' +  '615922dfb53103aa3d1620790b97d2ba3b4c76ad',
 
   'src/third_party/libwebm/source':
     Var('chromium_git') + '/webm/libwebm.git' + '@' + 'e4931ebc0a816458c18a6734e91a4d1b5acd5c56',
@@ -963,7 +957,7 @@
 
   'src/third_party/nasm': {
       'url': Var('chromium_git') + '/chromium/deps/nasm.git' + '@' +
-      'a0a6951e259bd347c133969740348bb5ebb468c4'
+      '4ee6a69ce33be1e96fd3c44a6e3ae3d8177453da'
   },
 
   'src/third_party/netty-tcnative/src': {
@@ -1014,7 +1008,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'acedb7bc166fb805a037507253e935f3ea0fd0f9',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '7b49412a6e09da8fe1d37dca67db94c12a509ffc',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1032,6 +1026,17 @@
       'dep_type': 'cipd',
   },
 
+  'src/third_party/protoc_javalite': {
+      'packages': [
+          {
+              'package': 'chromium/third_party/protoc_javalite',
+              'version': 'version:3.0.0-cr1',
+          },
+      ],
+      'condition': 'checkout_android',
+      'dep_type': 'cipd',
+  },
+
   # Dependency of chromite.git and skia.
   'src/third_party/pyelftools': {
       'url': Var('chromium_git') + '/chromiumos/third_party/pyelftools.git' + '@' + '19b3e610c86fcadb837d252c794cb5e8008826ae',
@@ -1076,7 +1081,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/r8',
-              'version': 'version:1.4.4-cr0',
+              'version': 'uM1IGlYVeBYwmhwRCSMVqRvmu4YFlL7M2yLwZ1DWUvAC',
           },
       ],
       'condition': 'checkout_android',
@@ -1092,7 +1097,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/robolectric',
-              'version': 'version:3.5.1',
+              'version': 'Q-aurrjcIq02dl2ws-M-IhnIydurvTbUMR_xD_zBJ0YC',
           },
       ],
       'condition': 'checkout_android',
@@ -1166,7 +1171,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '5b6cbd789b9b91b4e46dde883c9f2ecb31eddade',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '179a3923b9e402f427728d52b3024a3de1696a66',
+    Var('webrtc_git') + '/src.git' + '@' + '0d007d7c4f11414bb4f0712cb53f7eea4a1b0795',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1197,7 +1202,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@a73c9823f6c5659ceaf3b7539da381369fa209e2',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@f9b825600fd5a15c59671bb4fe92037c544ab7a7',
     'condition': 'checkout_src_internal',
   },
 
@@ -1205,7 +1210,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/android_deps/libs/com_google_android_play_core_verification',
-              'version': 'version:1.3.6-cr1',
+              'version': 'version:1.3.7-cr0',
           },
       ],
       'condition': 'checkout_android',
@@ -1830,6 +1835,17 @@
       'dep_type': 'cipd',
   },
 
+  'src/third_party/android_deps/libs/com_google_protobuf_protobuf_lite': {
+      'packages': [
+          {
+              'package': 'chromium/third_party/android_deps/libs/com_google_protobuf_protobuf_lite',
+              'version': 'version:3.0.1-cr0',
+          },
+      ],
+      'condition': 'checkout_android',
+      'dep_type': 'cipd',
+  },
+
   'src/third_party/android_deps/libs/com_squareup_javapoet': {
       'packages': [
           {
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 2484e48..c3db098 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -616,8 +616,8 @@
     r'test[\\/]data[\\/]',
     r'^components[\\/]policy[\\/]resources[\\/]policy_templates\.json$',
     r'^third_party[\\/]protobuf[\\/]',
-    r'^third_party[\\/]WebKit[\\/]LayoutTests[\\/]external[\\/]wpt[\\/]',
     r'^third_party[\\/]blink[\\/]renderer[\\/]devtools[\\/]protocol\.json$',
+    r'^third_party[\\/]blink[\\/]web_tests[\\/]external[\\/]wpt[\\/]',
 ]
 
 
@@ -629,6 +629,7 @@
     'OS_BSD',
     'OS_CAT',       # For testing.
     'OS_CHROMEOS',
+    'OS_CYGWIN',    # third_party code.
     'OS_FREEBSD',
     'OS_FUCHSIA',
     'OS_IOS',
@@ -666,12 +667,13 @@
     'build/android/gyp/create_test_runner_script.pydeps',
     'build/android/gyp/create_tool_wrapper.pydeps',
     'build/android/gyp/desugar.pydeps',
+    'build/android/gyp/dexsplitter.pydeps',
     'build/android/gyp/dex.pydeps',
     'build/android/gyp/dist_aar.pydeps',
     'build/android/gyp/emma_instr.pydeps',
     'build/android/gyp/filter_zip.pydeps',
     'build/android/gyp/gcc_preprocess.pydeps',
-    'build/android/gyp/generate_proguarded_module_jar.pydeps',
+    'build/android/gyp/generate_linker_version_script.pydeps',
     'build/android/gyp/ijar.pydeps',
     'build/android/gyp/java_cpp_enum.pydeps',
     'build/android/gyp/javac.pydeps',
@@ -690,8 +692,8 @@
     'build/android/test_runner.pydeps',
     'build/android/test_wrapper/logdog_wrapper.pydeps',
     'build/protoc_java.pydeps',
-    'build/secondary/third_party/android_platform/'
-        'development/scripts/stack.pydeps',
+    ('build/secondary/third_party/android_platform/'
+     'development/scripts/stack.pydeps'),
     'net/tools/testserver/testserver.pydeps',
 ]
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 6ab7089..8f2a55d 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -130,6 +130,10 @@
     'arc_fileapi': {
       'filepath': 'chrome/browser/chromeos/arc/fileapi'
     },
+    'arc_ime': {
+      'filepath': 'chrome/browser/chromeos/arc/input_method_manager/'\
+                  '|components/arc/ime/'
+    },
     'arc_kiosk': {
       'filepath': 'chrome/browser/chromeos/app_mode/arc/'\
                   '|components/arc/kiosk/'\
@@ -255,11 +259,11 @@
     'blink_audio': {
       'filepath': 'third_party/blink/renderer/platform/audio' \
                   '|third_party/blink/renderer/modules/webaudio' \
-                  '|third_party/WebKit/LayoutTests/external/wpt/webaudio',
+                  '|third_party/blink/web_tests/external/wpt/webaudio',
     },
     'blink_battery_status': {
       'filepath': 'third_party/blink/renderer/modules/battery/' \
-                  '|third_party/WebKit/LayoutTests/battery-status/' \
+                  '|third_party/blink/web_tests/battery-status/' \
                   '|third_party/blink/renderer/core/frame/platform_event_(controller|dispatcher)'
     },
     'blink_bindings': {
@@ -306,24 +310,24 @@
     },
     'blink_css_flexbox': {
       'filepath': 'third_party/blink/renderer/core/rendering/.*flex' \
-                  '|third_party/WebKit/LayoutTests/fast/deprecated-flexbox/' \
-                  '|third_party/WebKit/LayoutTests/css3/flexbox/' \
-                  '|third_party/WebKit/LayoutTests/ietestcenter/css3/flexbox/'
+                  '|third_party/blink/web_tests/fast/deprecated-flexbox/' \
+                  '|third_party/blink/web_tests/css3/flexbox/' \
+                  '|third_party/blink/web_tests/ietestcenter/css3/flexbox/'
     },
     'blink_css_fragmentation_tests': {
-      'filepath': 'third_party/WebKit/LayoutTests/fast/multicol/' \
-                  '|third_party/WebKit/LayoutTests/fast/pagination/' \
-                  '|third_party/WebKit/LayoutTests/fragmentation/' \
-                  '|third_party/WebKit/LayoutTests/printing/'
+      'filepath': 'third_party/blink/web_tests/fast/multicol/' \
+                  '|third_party/blink/web_tests/fast/pagination/' \
+                  '|third_party/blink/web_tests/fragmentation/' \
+                  '|third_party/blink/web_tests/printing/'
     },
     'blink_css_grid_layout': {
       'filepath': 'third_party/blink/renderer/core/layout/.*grid' \
                   '|third_party/blink/renderer/core/paint/.*grid' \
                   '|third_party/blink/renderer/core/css/.*grid' \
-                  '|third_party/WebKit/LayoutTests/fast/css-grid-layout/' \
-                  '|third_party/WebKit/LayoutTests/ietestcenter/css3/grid/' \
-                  '|third_party/WebKit/LayoutTests/platform/.*/fast/css-grid-layout/' \
-                  '|third_party/WebKit/LayoutTests/platform/.*/ietestcenter/css3/grid/'
+                  '|third_party/blink/web_tests/fast/css-grid-layout/' \
+                  '|third_party/blink/web_tests/ietestcenter/css3/grid/' \
+                  '|third_party/blink/web_tests/platform/.*/fast/css-grid-layout/' \
+                  '|third_party/blink/web_tests/platform/.*/ietestcenter/css3/grid/'
     },
     'blink_custom_elements': {
       'filepath': 'third_party/blink/renderer/core/html/custom/' \
@@ -333,7 +337,7 @@
       'filepath': 'content/browser/device_sensors/' \
                   '|content/test/data/device_sensors/' \
                   '|third_party/blink/renderer/modules/device_orientation/' \
-                  '|third_party/WebKit/LayoutTests/device_orientation/' \
+                  '|third_party/blink/web_tests/device_orientation/' \
                   '|third_party/blink/renderer/core/frame/platform_event_(controller|dispatcher)' \
                   '|third_party/blink/renderer/core/frame/device_single_window_event_controller'
     },
@@ -359,14 +363,14 @@
       'filepath': 'third_party/blink/renderer/modules/filesystem/' \
                   '|third_party/blink/renderer/core/fileapi/' \
                   '|third_party/blink/renderer/platform/.*file' \
-                  '|third_party/WebKit/LayoutTests/fast/file' \
+                  '|third_party/blink/web_tests/fast/file' \
                   '|third_party/blink/public/.*_file'
     },
     'blink_frames': {
       'filepath': 'third_party/blink/renderer/core/frame/'
     },
     'blink_geolocation': {
-      'filepath': 'third_party/WebKit/LayoutTests/geolocation-api/' \
+      'filepath': 'third_party/blink/web_tests/geolocation-api/' \
                   '|third_party/blink/renderer/modules/geolocation/' \
                   '|third_party/blink/public/web/.*geolocation'
     },
@@ -387,7 +391,7 @@
                   '|third_party/blink/public/platform/modules/indexeddb' \
                   '|third_party/blink/public/mojom/indexeddb' \
                   '|third_party/blink/renderer/modules/indexeddb/' \
-                  '|third_party/WebKit/LayoutTests/storage/indexeddb'
+                  '|third_party/blink/web_tests/storage/indexeddb'
     },
     'blink_input': {
       'filepath': 'third_party/blink/renderer/core/input/'
@@ -416,7 +420,7 @@
                   '|third_party/blink/renderer/modules/(encryptedmedia|mediasource)' \
                   '|third_party/blink/renderer/platform/drm/' \
                   '|third_party/blink/renderer/platform/graphics/media/' \
-                  '|third_party/WebKit/LayoutTests/media/' \
+                  '|third_party/blink/web_tests/media/' \
                   '|third_party/blink/public/.*_media'
     },
     'blink_media_queries': {
@@ -430,14 +434,14 @@
                   '|third_party/blink/renderer/platform/mediastream/' \
                   '|third_party/blink/renderer/platform/exported/web_media_stream' \
                   '|third_party/blink/renderer/platform/exported/web_rtc' \
-                  '|third_party/WebKit/LayoutTests/fast/mediastream/'
+                  '|third_party/blink/web_tests/fast/mediastream/'
     },
     'blink_modules': {
       'filepath': 'third_party/blink/renderer/modules'
     },
     'blink_navigator_content_utils': {
       'filepath': 'third_party/blink/renderer/modules/navigatorcontentutils' \
-                  '|third_party/WebKit/LayoutTests/fast/dom/navigatorcontentutils'
+                  '|third_party/blink/web_tests/fast/dom/navigatorcontentutils'
     },
     'blink_out_of_process_frames': {
       'filepath': 'third_party/blink/renderer/core/frame/frame_owner' \
@@ -450,11 +454,8 @@
     },
     'blink_paintworklet': {
       'filepath': 'third_party/blink/renderer/modules/csspaint/' \
-                  '|third_party/WebKit/LayoutTests/http/tests/csspaint/' \
-                  '|third_party/WebKit/LayoutTests/externla/wpt/css/css-paint-api/'
-    },
-    'blink_performance_timing': {
-      'filepath': 'third_party/blink/renderer/core/timing/'
+                  '|third_party/blink/web_tests/http/tests/csspaint/' \
+                  '|third_party/blink/web_tests/externla/wpt/css/css-paint-api/'
     },
     'blink_permissions': {
       'filepath': 'third_party/blink/renderer/modules/permissions/' \
@@ -513,12 +514,12 @@
     },
     'blink_service_worker' : {
       'filepath': 'third_party/blink/renderer/modules/service_worker' \
-                  '|third_party/WebKit/LayoutTests/http/tests/serviceworker' \
-                  '|third_party/WebKit/LayoutTests/external/wpt/service-workers'
+                  '|third_party/blink/web_tests/http/tests/serviceworker' \
+                  '|third_party/blink/web_tests/external/wpt/service-workers'
     },
     'blink_service_worker_tests' : {
-      'filepath': 'third_party/WebKit/LayoutTests/http/tests/serviceworker' \
-                  '|third_party/WebKit/LayoutTests/external/wpt/service-workers'
+      'filepath': 'third_party/blink/web_tests/http/tests/serviceworker' \
+                  '|third_party/blink/web_tests/external/wpt/service-workers'
     },
     'blink_shadow_dom': {
       'filepath': 'third_party/blink/renderer/core/dom/.*shadow' \
@@ -530,7 +531,7 @@
       'filepath': 'third_party/blink/renderer/core/editing/spellcheck'
     },
     'blink_spv2_layout_tests': {
-      'filepath': 'third_party/WebKit/LayoutTests/virtual/spv2'
+      'filepath': 'third_party/blink/web_tests/virtual/spv2'
     },
     'blink_streams': {
       'filepath': 'third_party/blink/renderer/core/streams/'
@@ -547,10 +548,10 @@
                   '|third_party/blink/renderer/core/html/meida/html_media_element' \
                   '|third_party/blink/renderer/core/html/shadow/media_control' \
                   '|third_party/blink/renderer/core/css/mediaControl' \
-                  '|third_party/WebKit/LayoutTests/media/track/'
+                  '|third_party/blink/web_tests/media/track/'
     },
     'blink_vibration': {
-      'filepath': 'third_party/WebKit/LayoutTests/vibration/' \
+      'filepath': 'third_party/blink/web_tests/vibration/' \
                   '|third_party/blink/renderer/modules/vibration/'
     },
     'blink_viewport_interaction': {
@@ -561,7 +562,7 @@
                   '|third_party/blink/renderer/core/html/html_meta_element'
     },
     'blink_w3ctests': {
-      'filepath': 'third_party/WebKit/LayoutTests/external/' \
+      'filepath': 'third_party/blink/web_tests/external/' \
                   '|third_party/blink/tools/blinkpy/w3c/'
     },
     'blink_webp': {
@@ -574,9 +575,9 @@
     },
     'blink_workers': {
       'filepath': 'third_party/blink/renderer/core/workers' \
-                  '|third_party/WebKit/LayoutTests/http/tests/workers' \
-                  '|third_party/WebKit/LayoutTests/fast/workers' \
-                  '|third_party/WebKit/LayoutTests/external/wpt/workers'
+                  '|third_party/blink/web_tests/http/tests/workers' \
+                  '|third_party/blink/web_tests/fast/workers' \
+                  '|third_party/blink/web_tests/external/wpt/workers'
     },
     'blink_wtf': {
       'filepath': 'third_party/blink/renderer/platform/wtf',
@@ -1031,7 +1032,10 @@
                   'chrome/browser/search/|'\
                   'chrome/browser/ui/search/|'\
                   'chrome/browser/resources/local_ntp/|'\
-                  'chrome/common/search_types.*',
+                  'chrome/common/search/|'\
+                  'chrome/test/data/local_ntp/|'\
+                  'components/search_provider_logos/|'\
+                  'components/test/data/search_provider_logos/',
     },
     'ios': {
       'filepath': 'ios/',
@@ -1097,7 +1101,7 @@
       'filepath': 'content/renderer/media_capture_from_element/' \
                   '|third_party/blink/renderer/modules/mediacapture/' \
                   '|third_party/blink/renderer/platform/exported/web_canvas_capture' \
-                  '|third_party/WebKit/LayoutTests/fast/mediacapturefromelement/' \
+                  '|third_party/blink/web_tests/fast/mediacapturefromelement/' \
                   '|third_party/blink/public/platform/web_canvas_capture'
     },
     'media_controls': {
@@ -1120,7 +1124,7 @@
       'filepath': 'content/renderer/media_recorder/' \
                   '|third_party/blink/renderer/modules/mediarecorder/' \
                   '|third_party/blink/renderer/platform/exported/web_media_recorder' \
-                  '|third_party/WebKit/LayoutTests/fast/mediarecorder/' \
+                  '|third_party/blink/web_tests/fast/mediarecorder/' \
                   '|third_party/blink/public/platform/web_media_recorder'
     },
     'media_remoting': {
@@ -1257,8 +1261,8 @@
         'content/public/common/platform_notification|'\
         'content/renderer/notification_|'\
         'chrome/notification_helper/|'\
-        'third_party/WebKit/LayoutTests/external/wpt/notifications|'\
-        'third_party/WebKit/LayoutTests/http/tests/notifications|'\
+        'third_party/blink/web_tests/external/wpt/notifications|'\
+        'third_party/blink/web_tests/http/tests/notifications|'\
         'third_party/blink/renderer/modules/notifications|'\
         'third_party/blink/public/platform/modules/notifications|'\
         'ui/message_center/'
@@ -1363,9 +1367,9 @@
                   '|ios/chrome/browser/ui/payments'\
                   '|ios/web/payments'\
                   '|ios/web/public/payments'\
-                  '|third_party/WebKit/LayoutTests/external/wpt/payment-request'\
-                  '|third_party/WebKit/LayoutTests/http/tests/payments'\
-                  '|third_party/WebKit/LayoutTests/payments'\
+                  '|third_party/blink/web_tests/external/wpt/payment-request'\
+                  '|third_party/blink/web_tests/http/tests/payments'\
+                  '|third_party/blink/web_tests/payments'\
                   '|third_party/blink/public/platform/modules/payments'\
                   '|third_party/blink/renderer/modules/payments'
     },
@@ -1414,7 +1418,7 @@
     'presentation': {
       'filepath': 'content/(browser|common|renderer)/presentation/' \
                   '|third_party/blink/public/platform/modules/presentation/' \
-                  '|third_party/WebKit/LayoutTests/presentation/' \
+                  '|third_party/blink/web_tests/presentation/' \
                   '|third_party/blink/renderer/modules/presentation/'
     },
     'print_preview': {
@@ -1493,10 +1497,6 @@
                   '|third_party/hunspell/'\
                   '|third_party/hunspell_dictionaries/',
     },
-    'stack_sampling_profiler': {
-      'filepath': 'components/metrics/*call_stack_profile*'\
-                  '|base/profiler/',
-    },
     'startup': {
       'filepath': 'chrome/browser/ui/startup/',
     },
@@ -1506,6 +1506,9 @@
     'styleguide': {
       'filepath': '^styleguide/',
     },
+    'styleguide_python': {
+      'filepath': '^styleguide/python',
+    },
     'subresource_filter': {
       'filepath': 'subresource_filter|SubresourceFilter|url_pattern_index',
     },
@@ -1702,8 +1705,8 @@
                   '|chrome/android/javatests/src/org/chromium/chrome/browser/WebShare.*'\
                   '|chrome/browser/ui/views/webshare/'\
                   '|chrome/browser/webshare/'\
-                  '|third_party/WebKit/LayoutTests/webshare/'\
-                  '|third_party/WebKit/LayoutTests/external/wpt/web-share/'\
+                  '|third_party/blink/web_tests/webshare/'\
+                  '|third_party/blink/web_tests/external/wpt/web-share/'\
                   '|third_party/blink/public/platform/modules/webshare/'\
                   '|third_party/blink/renderer/modules/webshare/',
     },
@@ -1813,6 +1816,7 @@
     'arc_auth': ['khmel+watch@chromium.org'],
     'arc_common': ['hashimoto+watch@chromium.org'],
     'arc_fileapi': ['nya+watch@chromium.org'],
+    'arc_ime': ['yhanada+watch@chromium.org'],
     'arc_kiosk': ['poromov+watch@chromium.org'],
     'arc_net': ['abhishekbh@chromium.org',
                 'cernekee@chromium.org',
@@ -1964,7 +1968,6 @@
     'blink_paint' : ['blink-reviews-paint@chromium.org',
                      'dongseong.hwang@intel.com'],
     'blink_paintworklet' : ['xidachen@chromium.org'],
-    'blink_performance_timing': ['panicker+watch@chromium.org'],
     'blink_permissions': ['mlamouri+watch-blink@chromium.org',
                           'permissions-reviews@chromium.org'],
     'blink_platform': ['kinuko+watch@chromium.org'],
@@ -2210,15 +2213,10 @@
                         'raphael.kubo.da.costa@intel.com'],
     'installer_win': ['grt+watch@chromium.org',
                       'wfh+watch@chromium.org'],
-    'instant': ['dcblack@chromium.org',
-                'donnd+watch@chromium.org',
-                'jered+watch@chromium.org',
-                'jfweitz+watch@chromium.org',
-                'kmadhusu+watch@chromium.org',
-                'melevin+watch@chromium.org',
-                'samarth+watch@chromium.org',
-                'skanuj+watch@chromium.org',
-                'chrome-custom-fit+reviews@google.com'],
+    'instant': ['gayane+watch@chromium.org',
+                'kristipark+watch@chromium.org',
+                'kmilka+watch@chromium.org',
+                'ramyan+watch@chromium.org'],
     'ios': ['ios-reviews@chromium.org'],
     'ios_chrome': ['ios-reviews+chrome@chromium.org',
                    'noyau+watch@chromium.org',
@@ -2407,13 +2405,15 @@
     'spellcheck': ['rlp+watch@chromium.org',
                    'rouslan+spell@chromium.org',
                    'timvolodine@chromium.org'],
-    'stack_sampling_profiler': ['chengx+watch@chromium.org'],
     'startup': ['grt+watch@chromium.org',
                 'pastarmovj+watch@chromium.org'],
     'streams': ['zork+watch@chromium.org'],
     'styleguide': ['danakj+watch@chromium.org',
                    'jbroman+cpp@chromium.org',
                    'vmpstr+watch@chromium.org'],
+    'styleguide_python': ['agrieve+watch@chromium.org',
+                          'estevenson+watch@chromium.org',
+                          'wnwen+watch@chromium.org'],
     'subresource_filter': ['subresource-filter-reviews@chromium.org'],
     'supervised_users': [],
     'sync': ['sync-reviews@chromium.org'],
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 0c21b9a..5302d57 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -623,6 +623,8 @@
     "browser/net/input_stream_reader.h",
     "browser/net/token_binding_manager.cc",
     "browser/net/token_binding_manager.h",
+    "browser/net_helpers.cc",
+    "browser/net_helpers.h",
     "browser/parent_compositor_draw_constraints.cc",
     "browser/parent_compositor_draw_constraints.h",
     "browser/parent_output_surface.cc",
diff --git a/android_webview/browser/aw_autofill_client.cc b/android_webview/browser/aw_autofill_client.cc
index 9ffa0ad..dd214e2 100644
--- a/android_webview/browser/aw_autofill_client.cc
+++ b/android_webview/browser/aw_autofill_client.cc
@@ -282,7 +282,6 @@
 }
 
 void AwAutofillClient::ConfirmAccountNameFixFlow(
-    std::unique_ptr<base::DictionaryValue> legal_message,
     base::OnceCallback<void(const base::string16&)> callback) {
   NOTIMPLEMENTED();
 }
@@ -328,7 +327,7 @@
 
 void AwAutofillClient::ConfirmCreditCardFillAssist(
     const autofill::CreditCard& card,
-    const base::Closure& callback) {
+    base::OnceClosure callback) {
   NOTIMPLEMENTED();
 }
 
diff --git a/android_webview/browser/aw_autofill_client.h b/android_webview/browser/aw_autofill_client.h
index c48665c..eca5533 100644
--- a/android_webview/browser/aw_autofill_client.h
+++ b/android_webview/browser/aw_autofill_client.h
@@ -80,7 +80,6 @@
   void ShowLocalCardMigrationDialog(
       base::OnceClosure show_migration_dialog_closure) override;
   void ConfirmAccountNameFixFlow(
-      std::unique_ptr<base::DictionaryValue> legal_message,
       base::OnceCallback<void(const base::string16&)> callback) override;
   void ConfirmMigrateLocalCardToCloud(
       std::unique_ptr<base::DictionaryValue> legal_message,
@@ -104,7 +103,7 @@
       bool show_prompt,
       UserAcceptedUploadCallback callback) override;
   void ConfirmCreditCardFillAssist(const autofill::CreditCard& card,
-                                   const base::Closure& callback) override;
+                                   base::OnceClosure callback) override;
   void LoadRiskData(
       base::OnceCallback<void(const std::string&)> callback) override;
   bool HasCreditCardScanFeature() override;
diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc
index d7164ad..3b247a9 100644
--- a/android_webview/browser/aw_browser_context.cc
+++ b/android_webview/browser/aw_browser_context.cc
@@ -220,8 +220,7 @@
 
 content::ResourceContext* AwBrowserContext::GetResourceContext() {
   if (!resource_context_) {
-    resource_context_.reset(
-        new AwResourceContext(url_request_context_getter_.get()));
+    resource_context_.reset(new AwResourceContext);
   }
   return resource_context_.get();
 }
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index 81acbd4..84d2ce7 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -848,9 +848,11 @@
 bool AwContentBrowserClient::WillCreateURLLoaderFactory(
     content::BrowserContext* browser_context,
     content::RenderFrameHost* frame,
+    int render_process_id,
     bool is_navigation,
     const url::Origin& request_initiator,
     network::mojom::URLLoaderFactoryRequest* factory_request,
+    network::mojom::TrustedURLLoaderHeaderClientPtrInfo* header_client,
     bool* bypass_redirect_checks) {
   DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService));
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -858,7 +860,7 @@
   auto proxied_request = std::move(*factory_request);
   network::mojom::URLLoaderFactoryPtrInfo target_factory_info;
   *factory_request = mojo::MakeRequest(&target_factory_info);
-  int process_id = is_navigation ? 0 : frame->GetProcess()->GetID();
+  int process_id = is_navigation ? 0 : render_process_id;
 
   // Android WebView has one non off-the-record browser context.
   base::PostTaskWithTraits(
diff --git a/android_webview/browser/aw_content_browser_client.h b/android_webview/browser/aw_content_browser_client.h
index afafb94..d4e9371 100644
--- a/android_webview/browser/aw_content_browser_client.h
+++ b/android_webview/browser/aw_content_browser_client.h
@@ -209,9 +209,11 @@
   bool WillCreateURLLoaderFactory(
       content::BrowserContext* browser_context,
       content::RenderFrameHost* frame,
+      int render_process_id,
       bool is_navigation,
       const url::Origin& request_initiator,
       network::mojom::URLLoaderFactoryRequest* factory_request,
+      network::mojom::TrustedURLLoaderHeaderClientPtrInfo* header_client,
       bool* bypass_redirect_checks) override;
 
   AwFeatureListCreator* aw_feature_list_creator() {
diff --git a/android_webview/browser/aw_contents.cc b/android_webview/browser/aw_contents.cc
index 0cf07c5c..1b30da5 100644
--- a/android_webview/browser/aw_contents.cc
+++ b/android_webview/browser/aw_contents.cc
@@ -235,7 +235,6 @@
 
 AwContents::AwContents(std::unique_ptr<WebContents> web_contents)
     : content::WebContentsObserver(web_contents.get()),
-      functor_(nullptr),
       browser_view_renderer_(
           this,
           base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI})),
@@ -395,16 +394,8 @@
 }
 
 void AwContents::SetAwGLFunctor(AwGLFunctor* functor) {
-  if (functor == functor_) {
-    return;
-  }
-  functor_ = functor;
-  if (functor_) {
-    browser_view_renderer_.SetCurrentCompositorFrameConsumer(
-        functor_->GetCompositorFrameConsumer());
-  } else {
-    browser_view_renderer_.SetCurrentCompositorFrameConsumer(nullptr);
-  }
+  browser_view_renderer_.SetCurrentCompositorFrameConsumer(
+      functor ? functor->GetCompositorFrameConsumer() : nullptr);
 }
 
 void AwContents::SetAwGLFunctor(JNIEnv* env,
@@ -1479,6 +1470,34 @@
       ConvertJavaStringToUTF16(env, script), js_callback);
 }
 
+void AwContents::RendererUnresponsive(
+    content::RenderProcessHost* render_process_host) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+  if (obj.is_null())
+    return;
+
+  AwRenderProcess* aw_render_process =
+      AwRenderProcess::GetInstanceForRenderProcessHost(render_process_host);
+  Java_AwContents_onRendererUnresponsive(env, obj,
+                                         aw_render_process->GetJavaObject());
+}
+
+void AwContents::RendererResponsive(
+    content::RenderProcessHost* render_process_host) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+  if (obj.is_null())
+    return;
+
+  AwRenderProcess* aw_render_process =
+      AwRenderProcess::GetInstanceForRenderProcessHost(render_process_host);
+  Java_AwContents_onRendererResponsive(env, obj,
+                                       aw_render_process->GetJavaObject());
+}
+
 void AwContents::OnRenderProcessGone(int child_process_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   JNIEnv* env = AttachCurrentThread();
diff --git a/android_webview/browser/aw_contents.h b/android_webview/browser/aw_contents.h
index 5abbceb..c623fc5 100644
--- a/android_webview/browser/aw_contents.h
+++ b/android_webview/browser/aw_contents.h
@@ -339,6 +339,9 @@
   jlong GetAutofillProvider(JNIEnv* env,
                             const base::android::JavaParamRef<jobject>& obj);
 
+  void RendererUnresponsive(content::RenderProcessHost* render_process_host);
+  void RendererResponsive(content::RenderProcessHost* render_process_host);
+
   // content::WebContentsObserver overrides
   void RenderViewHostChanged(content::RenderViewHost* old_host,
                              content::RenderViewHost* new_host) override;
@@ -374,7 +377,6 @@
   void SetAwGLFunctor(AwGLFunctor* functor);
 
   JavaObjectWeakGlobalRef java_ref_;
-  AwGLFunctor* functor_;
   BrowserViewRenderer browser_view_renderer_;  // Must outlive |web_contents_|.
   std::unique_ptr<content::WebContents> web_contents_;
   std::unique_ptr<AwWebContentsDelegate> web_contents_delegate_;
diff --git a/android_webview/browser/aw_contents_io_thread_client.cc b/android_webview/browser/aw_contents_io_thread_client.cc
index 936a948..d6bfc3a 100644
--- a/android_webview/browser/aw_contents_io_thread_client.cc
+++ b/android_webview/browser/aw_contents_io_thread_client.cc
@@ -29,6 +29,7 @@
 #include "jni/AwContentsIoThreadClient_jni.h"
 #include "net/base/data_url.h"
 #include "net/url_request/url_request.h"
+#include "services/network/public/cpp/resource_request.h"
 
 using base::android::AttachCurrentThread;
 using base::android::ConvertUTF8ToJavaString;
@@ -341,7 +342,7 @@
 namespace {
 
 std::unique_ptr<AwWebResourceResponse> RunShouldInterceptRequest(
-    const AwWebResourceRequest& request,
+    AwWebResourceRequest request,
     JavaObjectWeakGlobalRef ref) {
   base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK);
 
@@ -372,7 +373,7 @@
 }  // namespace
 
 void AwContentsIoThreadClient::ShouldInterceptRequestAsync(
-    const net::URLRequest* request,
+    AwWebResourceRequest request,
     ShouldInterceptRequestResultCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   base::OnceCallback<std::unique_ptr<AwWebResourceResponse>()> get_response =
@@ -385,7 +386,7 @@
   }
   if (!bg_thread_client_object_.is_null()) {
     get_response = base::BindOnce(
-        &RunShouldInterceptRequest, AwWebResourceRequest(*request),
+        &RunShouldInterceptRequest, std::move(request),
         JavaObjectWeakGlobalRef(env, bg_thread_client_object_.obj()));
   }
   base::PostTaskAndReplyWithResult(sequenced_task_runner_.get(), FROM_HERE,
diff --git a/android_webview/browser/aw_contents_io_thread_client.h b/android_webview/browser/aw_contents_io_thread_client.h
index b994655..546092e 100644
--- a/android_webview/browser/aw_contents_io_thread_client.h
+++ b/android_webview/browser/aw_contents_io_thread_client.h
@@ -27,6 +27,7 @@
 namespace android_webview {
 
 class AwWebResourceResponse;
+struct AwWebResourceRequest;
 
 // This class provides a means of calling Java methods on an instance that has
 // a 1:1 relationship with a WebContents instance directly from the IO thread.
@@ -111,7 +112,7 @@
   using ShouldInterceptRequestResultCallback =
       base::OnceCallback<void(std::unique_ptr<AwWebResourceResponse>)>;
   void ShouldInterceptRequestAsync(
-      const net::URLRequest* request,
+      AwWebResourceRequest request,
       ShouldInterceptRequestResultCallback callback);
 
   // Retrieve the AllowContentAccess setting value of this AwContents.
diff --git a/android_webview/browser/aw_metrics_service_client.cc b/android_webview/browser/aw_metrics_service_client.cc
index d4062684..8f02742 100644
--- a/android_webview/browser/aw_metrics_service_client.cc
+++ b/android_webview/browser/aw_metrics_service_client.cc
@@ -279,7 +279,9 @@
   JNIEnv* env = base::android::AttachCurrentThread();
   base::android::ScopedJavaLocalRef<jstring> j_app_name =
       Java_AwMetricsServiceClient_getAppPackageName(env);
-  return ConvertJavaStringToUTF8(env, j_app_name);
+  if (j_app_name)
+    return ConvertJavaStringToUTF8(env, j_app_name);
+  return std::string();
 }
 
 AwMetricsServiceClient::AwMetricsServiceClient()
diff --git a/android_webview/browser/aw_proxying_url_loader_factory.cc b/android_webview/browser/aw_proxying_url_loader_factory.cc
index 6829b4e..bb1506b4 100644
--- a/android_webview/browser/aw_proxying_url_loader_factory.cc
+++ b/android_webview/browser/aw_proxying_url_loader_factory.cc
@@ -7,6 +7,9 @@
 #include <utility>
 
 #include "android_webview/browser/aw_contents_client_bridge.h"
+#include "android_webview/browser/aw_contents_io_thread_client.h"
+#include "android_webview/browser/net/aw_web_resource_response.h"
+#include "android_webview/browser/net_helpers.h"
 #include "android_webview/browser/renderer_host/auto_login_parser.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/post_task.h"
@@ -16,6 +19,7 @@
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/url_utils.h"
+#include "net/base/load_flags.h"
 #include "net/http/http_util.h"
 
 namespace android_webview {
@@ -57,17 +61,23 @@
   void OnComplete(const network::URLLoaderCompletionStatus& status) override;
 
   // network::mojom::URLLoader
-  void FollowRedirect(const base::Optional<std::vector<std::string>>&
-                          to_be_removed_request_headers,
-                      const base::Optional<net::HttpRequestHeaders>&
-                          modified_request_headers) override;
+  void FollowRedirect(
+      const base::Optional<std::vector<std::string>>&
+          to_be_removed_request_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_request_headers,
+      const base::Optional<GURL>& new_url) override;
   void ProceedWithResponse() override;
   void SetPriority(net::RequestPriority priority,
                    int32_t intra_priority_value) override;
   void PauseReadingBodyFromNet() override;
   void ResumeReadingBodyFromNet() override;
 
+  void ContinueAfterIntercept();
+  void InterceptResponseReceived(
+      std::unique_ptr<AwWebResourceResponse> response);
+
  private:
+  std::unique_ptr<AwContentsIoThreadClient> GetIoThreadClient();
   void OnRequestError(const network::URLLoaderCompletionStatus& status);
 
   // TODO(timvolodine): consider factoring this out of this class.
@@ -126,6 +136,31 @@
   // TODO(timvolodine): add async check shouldOverrideUrlLoading and
   // shouldInterceptRequest.
 
+  std::unique_ptr<AwContentsIoThreadClient> io_thread_client =
+      GetIoThreadClient();
+  DCHECK(io_thread_client);
+  request_.load_flags = GetCacheModeForClient(io_thread_client.get());
+
+  // TODO: verify the case when WebContents::RenderFrameDeleted is called
+  // before network request is intercepted (i.e. if that's possible and
+  // whether it can result in any issues).
+  io_thread_client->ShouldInterceptRequestAsync(
+      AwWebResourceRequest(request_),
+      base::BindOnce(&InterceptedRequest::InterceptResponseReceived,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void InterceptedRequest::InterceptResponseReceived(
+    std::unique_ptr<AwWebResourceResponse> response) {
+  if (response) {
+    // TODO(timvolodine): handle the case where response contains data,
+    // i.e. is actually overridden, crbug.com/893566.
+  } else {
+    ContinueAfterIntercept();
+  }
+}
+
+void InterceptedRequest::ContinueAfterIntercept() {
   if (!target_loader_ && target_factory_) {
     network::mojom::URLLoaderClientPtr proxied_client;
     proxied_client_binding_.Bind(mojo::MakeRequest(&proxied_client));
@@ -285,10 +320,11 @@
 void InterceptedRequest::FollowRedirect(
     const base::Optional<std::vector<std::string>>&
         to_be_removed_request_headers,
-    const base::Optional<net::HttpRequestHeaders>& modified_request_headers) {
+    const base::Optional<net::HttpRequestHeaders>& modified_request_headers,
+    const base::Optional<GURL>& new_url) {
   if (target_loader_) {
     target_loader_->FollowRedirect(to_be_removed_request_headers,
-                                   modified_request_headers);
+                                   modified_request_headers, new_url);
   }
 
   Restart();
@@ -315,6 +351,16 @@
     target_loader_->ResumeReadingBodyFromNet();
 }
 
+std::unique_ptr<AwContentsIoThreadClient>
+InterceptedRequest::GetIoThreadClient() {
+  // |process_id_| == 0 indicates this is a navigation, and so we should use the
+  // frame_tree_node_id API (with request_.render_frame_id).
+  return process_id_
+             ? AwContentsIoThreadClient::FromID(process_id_,
+                                                request_.render_frame_id)
+             : AwContentsIoThreadClient::FromID(request_.render_frame_id);
+}
+
 void InterceptedRequest::OnRequestError(
     const network::URLLoaderCompletionStatus& status) {
   target_client_->OnComplete(status);
diff --git a/android_webview/browser/aw_render_thread_context_provider.cc b/android_webview/browser/aw_render_thread_context_provider.cc
index 95e06d5..f971a6c 100644
--- a/android_webview/browser/aw_render_thread_context_provider.cc
+++ b/android_webview/browser/aw_render_thread_context_provider.cc
@@ -28,13 +28,13 @@
 scoped_refptr<AwRenderThreadContextProvider>
 AwRenderThreadContextProvider::Create(
     scoped_refptr<gl::GLSurface> surface,
-    scoped_refptr<gpu::CommandBufferTaskExecutor> task_executor) {
-  return new AwRenderThreadContextProvider(surface, std::move(task_executor));
+    gpu::CommandBufferTaskExecutor* task_executor) {
+  return new AwRenderThreadContextProvider(surface, task_executor);
 }
 
 AwRenderThreadContextProvider::AwRenderThreadContextProvider(
     scoped_refptr<gl::GLSurface> surface,
-    scoped_refptr<gpu::CommandBufferTaskExecutor> task_executor) {
+    gpu::CommandBufferTaskExecutor* task_executor) {
   DCHECK(main_thread_checker_.CalledOnValidThread());
 
   // This is an onscreen context, wrapping the GLSurface given to us from
@@ -66,9 +66,9 @@
   limits.min_transfer_buffer_size = 64 * 1024;
 
   context_ = std::make_unique<gpu::GLInProcessContext>();
-  context_->Initialize(std::move(task_executor), surface,
-                       surface->IsOffscreen(), gpu::kNullSurfaceHandle,
-                       attributes, limits, nullptr, nullptr, nullptr);
+  context_->Initialize(task_executor, surface, surface->IsOffscreen(),
+                       gpu::kNullSurfaceHandle, attributes, limits, nullptr,
+                       nullptr, nullptr);
 
   context_->GetImplementation()->SetLostContextCallback(base::BindOnce(
       &AwRenderThreadContextProvider::OnLostContext, base::Unretained(this)));
diff --git a/android_webview/browser/aw_render_thread_context_provider.h b/android_webview/browser/aw_render_thread_context_provider.h
index 3767baf..4be7a0f 100644
--- a/android_webview/browser/aw_render_thread_context_provider.h
+++ b/android_webview/browser/aw_render_thread_context_provider.h
@@ -36,7 +36,7 @@
  public:
   static scoped_refptr<AwRenderThreadContextProvider> Create(
       scoped_refptr<gl::GLSurface> surface,
-      scoped_refptr<gpu::CommandBufferTaskExecutor> task_executor);
+      gpu::CommandBufferTaskExecutor* task_executor);
 
   // Gives the GL internal format that should be used for calling CopyTexImage2D
   // on the default framebuffer.
@@ -60,9 +60,8 @@
  protected:
   friend class base::RefCountedThreadSafe<AwRenderThreadContextProvider>;
 
-  AwRenderThreadContextProvider(
-      scoped_refptr<gl::GLSurface> surface,
-      scoped_refptr<gpu::CommandBufferTaskExecutor> task_executor);
+  AwRenderThreadContextProvider(scoped_refptr<gl::GLSurface> surface,
+                                gpu::CommandBufferTaskExecutor* task_executor);
   ~AwRenderThreadContextProvider() override;
 
  private:
diff --git a/android_webview/browser/aw_resource_context.cc b/android_webview/browser/aw_resource_context.cc
index 56c29cb..8de62b9 100644
--- a/android_webview/browser/aw_resource_context.cc
+++ b/android_webview/browser/aw_resource_context.cc
@@ -12,9 +12,7 @@
 
 namespace android_webview {
 
-AwResourceContext::AwResourceContext(net::URLRequestContextGetter* getter)
-    : getter_(getter) {
-  DCHECK(getter_);
+AwResourceContext::AwResourceContext() {
 }
 
 AwResourceContext::~AwResourceContext() {
@@ -40,9 +38,4 @@
   return iter != extra_headers_.end() ? iter->second : std::string();
 }
 
-net::URLRequestContext* AwResourceContext::GetRequestContext() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  return getter_->GetURLRequestContext();
-}
-
 }  // namespace android_webview
diff --git a/android_webview/browser/aw_resource_context.h b/android_webview/browser/aw_resource_context.h
index fa60478..2095d8e 100644
--- a/android_webview/browser/aw_resource_context.h
+++ b/android_webview/browser/aw_resource_context.h
@@ -14,26 +14,17 @@
 
 class GURL;
 
-namespace net {
-class URLRequestContextGetter;
-}
-
 namespace android_webview {
 
 class AwResourceContext : public content::ResourceContext {
  public:
-  explicit AwResourceContext(net::URLRequestContextGetter* getter);
+  AwResourceContext();
   ~AwResourceContext() override;
 
   void SetExtraHeaders(const GURL& url, const std::string& headers);
   std::string GetExtraHeaders(const GURL& url);
 
-  // content::ResourceContext implementation.
-  net::URLRequestContext* GetRequestContext() override;
-
  private:
-  net::URLRequestContextGetter* getter_;
-
   base::Lock extra_headers_lock_;
   std::map<std::string, std::string> extra_headers_;
 
diff --git a/android_webview/browser/aw_safe_browsing_blocking_page.cc b/android_webview/browser/aw_safe_browsing_blocking_page.cc
index 7d2a75a..10b8ed1 100644
--- a/android_webview/browser/aw_safe_browsing_blocking_page.cc
+++ b/android_webview/browser/aw_safe_browsing_blocking_page.cc
@@ -101,7 +101,6 @@
             IsMainPageLoadBlocked(unsafe_resources),
             safe_browsing::IsExtendedReportingOptInAllowed(*pref_service),
             false,  // is_off_the_record
-            false,  // is_unified_consent_enabled
             safe_browsing::IsExtendedReportingEnabled(*pref_service),
             safe_browsing::IsExtendedReportingPolicyManaged(*pref_service),
             pref_service->GetBoolean(
diff --git a/android_webview/browser/aw_web_contents_delegate.cc b/android_webview/browser/aw_web_contents_delegate.cc
index 428a722..f2366f9 100644
--- a/android_webview/browser/aw_web_contents_delegate.cc
+++ b/android_webview/browser/aw_web_contents_delegate.cc
@@ -59,6 +59,36 @@
 
 AwWebContentsDelegate::~AwWebContentsDelegate() {}
 
+void AwWebContentsDelegate::RendererUnresponsive(
+    content::WebContents* source,
+    content::RenderWidgetHost* render_widget_host,
+    base::RepeatingClosure hang_monitor_restarter) {
+  AwContents* aw_contents = AwContents::FromWebContents(source);
+  if (!aw_contents)
+    return;
+
+  content::RenderProcessHost* render_process_host =
+      render_widget_host->GetProcess();
+  if (render_process_host->IsInitializedAndNotDead()) {
+    aw_contents->RendererUnresponsive(render_widget_host->GetProcess());
+    hang_monitor_restarter.Run();
+  }
+}
+
+void AwWebContentsDelegate::RendererResponsive(
+    content::WebContents* source,
+    content::RenderWidgetHost* render_widget_host) {
+  AwContents* aw_contents = AwContents::FromWebContents(source);
+  if (!aw_contents)
+    return;
+
+  content::RenderProcessHost* render_process_host =
+      render_widget_host->GetProcess();
+  if (render_process_host->IsInitializedAndNotDead()) {
+    aw_contents->RendererResponsive(render_widget_host->GetProcess());
+  }
+}
+
 content::JavaScriptDialogManager*
 AwWebContentsDelegate::GetJavaScriptDialogManager(WebContents* source) {
   return g_javascript_dialog_manager.Pointer();
diff --git a/android_webview/browser/aw_web_contents_delegate.h b/android_webview/browser/aw_web_contents_delegate.h
index 1abe7b4..25462538 100644
--- a/android_webview/browser/aw_web_contents_delegate.h
+++ b/android_webview/browser/aw_web_contents_delegate.h
@@ -17,6 +17,16 @@
  public:
   AwWebContentsDelegate(JNIEnv* env, jobject obj);
   ~AwWebContentsDelegate() override;
+
+  void RendererUnresponsive(
+      content::WebContents* source,
+      content::RenderWidgetHost* render_widget_host,
+      base::RepeatingClosure hang_monitor_restarter) override;
+
+  void RendererResponsive(
+      content::WebContents* source,
+      content::RenderWidgetHost* render_widget_host) override;
+
   content::JavaScriptDialogManager* GetJavaScriptDialogManager(
       content::WebContents* source) override;
   void FindReply(content::WebContents* web_contents,
diff --git a/android_webview/browser/deferred_gpu_command_service.cc b/android_webview/browser/deferred_gpu_command_service.cc
index ca7382d..046ae3e 100644
--- a/android_webview/browser/deferred_gpu_command_service.cc
+++ b/android_webview/browser/deferred_gpu_command_service.cc
@@ -8,7 +8,6 @@
 #include "android_webview/browser/render_thread_manager.h"
 #include "base/command_line.h"
 #include "base/lazy_instance.h"
-#include "base/no_destructor.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/synchronization/lock.h"
 #include "base/trace_event/trace_event.h"
@@ -146,9 +145,8 @@
 
 // static
 DeferredGpuCommandService* DeferredGpuCommandService::GetInstance() {
-  static base::NoDestructor<scoped_refptr<DeferredGpuCommandService>> service(
-      CreateDeferredGpuCommandService());
-  return service->get();
+  static DeferredGpuCommandService* service = CreateDeferredGpuCommandService();
+  return service;
 }
 
 DeferredGpuCommandService::DeferredGpuCommandService(
diff --git a/android_webview/browser/deferred_gpu_command_service.h b/android_webview/browser/deferred_gpu_command_service.h
index 27d9cae..a504c76 100644
--- a/android_webview/browser/deferred_gpu_command_service.h
+++ b/android_webview/browser/deferred_gpu_command_service.h
@@ -46,6 +46,8 @@
  public:
   static DeferredGpuCommandService* GetInstance();
 
+  ~DeferredGpuCommandService() override;
+
   // gpu::CommandBufferTaskExecutor implementation.
   bool ForceVirtualizedGLContexts() const override;
   bool ShouldCreateMemoryTracker() const override;
@@ -67,9 +69,6 @@
   // idle tasks during the idle run.
   void PerformAllIdleWork();
 
- protected:
-  ~DeferredGpuCommandService() override;
-
  private:
   friend class ScopedAllowGL;
   friend class TaskForwardingSequence;
diff --git a/android_webview/browser/hardware_renderer.cc b/android_webview/browser/hardware_renderer.cc
index cccd77c..410c03d 100644
--- a/android_webview/browser/hardware_renderer.cc
+++ b/android_webview/browser/hardware_renderer.cc
@@ -165,7 +165,9 @@
 void HardwareRenderer::AllocateSurface() {
   DCHECK(!child_id_.is_valid());
   parent_local_surface_id_allocator_->GenerateId();
-  child_id_ = parent_local_surface_id_allocator_->GetCurrentLocalSurfaceId();
+  child_id_ =
+      parent_local_surface_id_allocator_->GetCurrentLocalSurfaceIdAllocation()
+          .local_surface_id();
   surfaces_->AddChildId(viz::SurfaceId(frame_sink_id_, child_id_));
 }
 
diff --git a/android_webview/browser/net/aw_request_interceptor.cc b/android_webview/browser/net/aw_request_interceptor.cc
index d94a51d..07629be 100644
--- a/android_webview/browser/net/aw_request_interceptor.cc
+++ b/android_webview/browser/net/aw_request_interceptor.cc
@@ -10,6 +10,7 @@
 #include "android_webview/browser/aw_contents_io_thread_client.h"
 #include "android_webview/browser/input_stream.h"
 #include "android_webview/browser/net/android_stream_reader_url_request_job.h"
+#include "android_webview/browser/net/aw_web_resource_request.h"
 #include "android_webview/browser/net/aw_web_resource_response.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string_number_conversions.h"
@@ -90,7 +91,7 @@
     callback_ = std::move(callback);
     io_thread_client_->ShouldInterceptRequestAsync(
         // The request is only used while preparing the call, not retained.
-        request,
+        AwWebResourceRequest(*request),
         base::BindOnce(
             &ShouldInterceptRequestAdaptor::WebResourceResponseObtained,
             // The lifetime of the DelegateObtainer is managed by
diff --git a/android_webview/browser/net_helpers.cc b/android_webview/browser/net_helpers.cc
new file mode 100644
index 0000000..3ebb56f
--- /dev/null
+++ b/android_webview/browser/net_helpers.cc
@@ -0,0 +1,35 @@
+// 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 "android_webview/browser/net_helpers.h"
+
+#include "android_webview/browser/aw_contents_io_thread_client.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "net/base/load_flags.h"
+
+namespace android_webview {
+
+int GetCacheModeForClient(AwContentsIoThreadClient* client) {
+  AwContentsIoThreadClient::CacheMode cache_mode = client->GetCacheMode();
+  switch (cache_mode) {
+    case AwContentsIoThreadClient::LOAD_CACHE_ELSE_NETWORK:
+      // If the resource is in the cache (even if expired), load from cache.
+      // Otherwise, fall back to network.
+      return net::LOAD_SKIP_CACHE_VALIDATION;
+    case AwContentsIoThreadClient::LOAD_NO_CACHE:
+      // Always load from the network, don't use the cache.
+      return net::LOAD_BYPASS_CACHE;
+    case AwContentsIoThreadClient::LOAD_CACHE_ONLY:
+      // If the resource is in the cache (even if expired), load from cache. Do
+      // not fall back to the network.
+      return net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION;
+    default:
+      // If the resource is in the cache (and is valid), load from cache.
+      // Otherwise, fall back to network. This is the usual (default) case.
+      return 0;
+  }
+}
+
+}  // namespace android_webview
diff --git a/android_webview/browser/net_helpers.h b/android_webview/browser/net_helpers.h
new file mode 100644
index 0000000..6a82511
--- /dev/null
+++ b/android_webview/browser/net_helpers.h
@@ -0,0 +1,19 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ANDROID_WEBVIEW_BROWSER_NET_HELPERS_H_
+#define ANDROID_WEBVIEW_BROWSER_NET_HELPERS_H_
+
+#include <memory>
+
+namespace android_webview {
+
+class AwContentsIoThreadClient;
+
+// Gets the net-layer load_flags which reflect |client|'s cache mode.
+int GetCacheModeForClient(AwContentsIoThreadClient* client);
+
+}  // namespace android_webview
+
+#endif  // ANDROID_WEBVIEW_BROWSER_NET_HELPERS_H_
diff --git a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
index a35c743..6ad95c7 100644
--- a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
+++ b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
@@ -14,6 +14,7 @@
 #include "android_webview/browser/aw_resource_context.h"
 #include "android_webview/browser/aw_safe_browsing_resource_throttle.h"
 #include "android_webview/browser/net/aw_web_resource_request.h"
+#include "android_webview/browser/net_helpers.h"
 #include "android_webview/browser/renderer_host/auto_login_parser.h"
 #include "android_webview/common/url_constants.h"
 #include "base/task/post_task.h"
@@ -249,20 +250,9 @@
     SetCacheControlFlag(
         request_, net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION);
   } else {
-    AwContentsIoThreadClient::CacheMode cache_mode = io_client->GetCacheMode();
-    switch (cache_mode) {
-      case AwContentsIoThreadClient::LOAD_CACHE_ELSE_NETWORK:
-        SetCacheControlFlag(request_, net::LOAD_SKIP_CACHE_VALIDATION);
-        break;
-      case AwContentsIoThreadClient::LOAD_NO_CACHE:
-        SetCacheControlFlag(request_, net::LOAD_BYPASS_CACHE);
-        break;
-      case AwContentsIoThreadClient::LOAD_CACHE_ONLY:
-        SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE |
-                                          net::LOAD_SKIP_CACHE_VALIDATION);
-        break;
-      default:
-        break;
+    int cache_mode = GetCacheModeForClient(io_client.get());
+    if (cache_mode) {
+      SetCacheControlFlag(request_, cache_mode);
     }
   }
   return false;
diff --git a/android_webview/browser/surfaces_instance.cc b/android_webview/browser/surfaces_instance.cc
index 9e6a653..0c08c21 100644
--- a/android_webview/browser/surfaces_instance.cc
+++ b/android_webview/browser/surfaces_instance.cc
@@ -154,15 +154,18 @@
   frame.metadata.device_scale_factor = device_scale_factor;
   frame.metadata.referenced_surfaces = GetChildIdsRanges();
 
-  if (!root_id_.is_valid() || viewport != surface_size_ ||
+  if (!root_id_allocation_.IsValid() || viewport != surface_size_ ||
       device_scale_factor != device_scale_factor_) {
     parent_local_surface_id_allocator_->GenerateId();
-    root_id_ = parent_local_surface_id_allocator_->GetCurrentLocalSurfaceId();
+    root_id_allocation_ = parent_local_surface_id_allocator_
+                              ->GetCurrentLocalSurfaceIdAllocation();
     surface_size_ = viewport;
     device_scale_factor_ = device_scale_factor;
-    display_->SetLocalSurfaceId(root_id_, device_scale_factor);
+    display_->SetLocalSurfaceId(root_id_allocation_.local_surface_id(),
+                                device_scale_factor);
   }
-  support_->SubmitCompositorFrame(root_id_, std::move(frame));
+  support_->SubmitCompositorFrame(root_id_allocation_.local_surface_id(),
+                                  std::move(frame));
 
   display_->Resize(viewport);
   display_->DrawAndSwap();
@@ -172,7 +175,7 @@
 void SurfacesInstance::AddChildId(const viz::SurfaceId& child_id) {
   DCHECK(!base::ContainsValue(child_ids_, child_id));
   child_ids_.push_back(child_id);
-  if (root_id_.is_valid())
+  if (root_id_allocation_.IsValid())
     SetSolidColorRootFrame();
 }
 
@@ -180,7 +183,7 @@
   auto itr = std::find(child_ids_.begin(), child_ids_.end(), child_id);
   DCHECK(itr != child_ids_.end());
   child_ids_.erase(itr);
-  if (root_id_.is_valid())
+  if (root_id_allocation_.IsValid())
     SetSolidColorRootFrame();
 }
 
@@ -205,7 +208,8 @@
       viz::BeginFrameAck::CreateManualAckWithDamage();
   frame.metadata.referenced_surfaces = GetChildIdsRanges();
   frame.metadata.device_scale_factor = device_scale_factor_;
-  support_->SubmitCompositorFrame(root_id_, std::move(frame));
+  support_->SubmitCompositorFrame(root_id_allocation_.local_surface_id(),
+                                  std::move(frame));
 }
 
 void SurfacesInstance::DidReceiveCompositorFrameAck(
diff --git a/android_webview/browser/surfaces_instance.h b/android_webview/browser/surfaces_instance.h
index 570defe..93aa6f6 100644
--- a/android_webview/browser/surfaces_instance.h
+++ b/android_webview/browser/surfaces_instance.h
@@ -11,6 +11,7 @@
 #include "base/memory/ref_counted.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
 #include "components/viz/common/surfaces/frame_sink_id_allocator.h"
+#include "components/viz/common/surfaces/local_surface_id_allocation.h"
 #include "components/viz/common/surfaces/surface_id.h"
 #include "components/viz/service/display/display_client.h"
 #include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h"
@@ -94,7 +95,7 @@
       parent_local_surface_id_allocator_;
   std::unique_ptr<viz::CompositorFrameSinkSupport> support_;
 
-  viz::LocalSurfaceId root_id_;
+  viz::LocalSurfaceIdAllocation root_id_allocation_;
   float device_scale_factor_ = 1.0f;
   std::vector<viz::SurfaceId> child_ids_;
 
diff --git a/android_webview/common/android_webview_message_generator.cc b/android_webview/common/android_webview_message_generator.cc
index d8b0080..8c5451e 100644
--- a/android_webview/common/android_webview_message_generator.cc
+++ b/android_webview/common/android_webview_message_generator.cc
@@ -10,10 +10,6 @@
 #include "ipc/struct_constructor_macros.h"
 #include "android_webview/common/android_webview_message_generator.h"
 
-// Generate destructors.
-#include "ipc/struct_destructor_macros.h"
-#include "android_webview/common/android_webview_message_generator.h"
-
 // Generate param traits write methods.
 #include "ipc/param_traits_write_macros.h"
 namespace IPC {
diff --git a/android_webview/common/aw_content_client.cc b/android_webview/common/aw_content_client.cc
index 1514c94..babb0fe 100644
--- a/android_webview/common/aw_content_client.cc
+++ b/android_webview/common/aw_content_client.cc
@@ -45,6 +45,7 @@
   schemes->local_schemes.push_back(url::kContentScheme);
   schemes->secure_schemes.push_back(
       android_webview::kAndroidWebViewVideoPosterScheme);
+  schemes->allow_non_standard_schemes_in_origins = true;
 }
 
 std::string AwContentClient::GetProduct() const {
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
index d77fc1c..e34c01d 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
@@ -265,7 +265,7 @@
                     || mAppTargetSdkVersion < Build.VERSION_CODES.LOLLIPOP);
 
             mAwContents = new AwContents(mFactory.getBrowserContextOnUiThread(), mWebView, mContext,
-                    new InternalAccessAdapter(), new WebViewNativeDrawGLFunctorFactory(),
+                    new InternalAccessAdapter(), new WebViewNativeDrawFunctorFactory(),
                     mContentsClientAdapter, mWebSettings.getAwSettings(),
                     new AwContents.DependencyFactory() {
                         @Override
@@ -2275,11 +2275,10 @@
         checkThread();
         return new AwPrintDocumentAdapter(mAwContents.getPdfExporter(), documentName);
     }
-    // AwContents.NativeDrawGLFunctorFactory implementation ----------------------------------
-    private class WebViewNativeDrawGLFunctorFactory
-            implements AwContents.NativeDrawGLFunctorFactory {
+    // AwContents.NativeDrawFunctorFactory implementation ----------------------------------
+    private class WebViewNativeDrawFunctorFactory implements AwContents.NativeDrawFunctorFactory {
         @Override
-        public AwContents.NativeDrawGLFunctor createFunctor(long context) {
+        public AwContents.NativeDrawGLFunctor createGLFunctor(long context) {
             return new DrawGLFunctor(context, mFactory.getWebViewDelegate());
         }
     }
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java
index f022593..aa04e28 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java
@@ -43,6 +43,7 @@
 import org.chromium.android_webview.AwContentsClientBridge;
 import org.chromium.android_webview.AwGeolocationPermissions;
 import org.chromium.android_webview.AwHttpAuthHandler;
+import org.chromium.android_webview.AwRenderProcess;
 import org.chromium.android_webview.AwRenderProcessGoneDetail;
 import org.chromium.android_webview.AwSafeBrowsingResponse;
 import org.chromium.android_webview.AwWebResourceResponse;
@@ -1206,6 +1207,14 @@
         }
     }
 
+    // TODO(tobiasjs) connect to support app-settable callbacks.
+    @Override
+    public void onRendererUnresponsive(final AwRenderProcess renderProcess) {}
+
+    // TODO(tobiasjs) connect to support app-settable callbacks.
+    @Override
+    public void onRendererResponsive(final AwRenderProcess renderProcess) {}
+
     @Override
     public boolean onRenderProcessGone(final AwRenderProcessGoneDetail detail) {
         // WebViewClient.onRenderProcessGone was added in O.
diff --git a/android_webview/java/src/org/chromium/android_webview/AutofillActionModeCallback.java b/android_webview/java/src/org/chromium/android_webview/AutofillActionModeCallback.java
index e033c35..7e62640 100644
--- a/android_webview/java/src/org/chromium/android_webview/AutofillActionModeCallback.java
+++ b/android_webview/java/src/org/chromium/android_webview/AutofillActionModeCallback.java
@@ -42,7 +42,7 @@
             MenuItem item = menu.add(
                     Menu.NONE, mAutofillMenuItem, Menu.CATEGORY_SECONDARY, mAutofillMenuItemTitle);
             item.setShowAsActionFlags(
-                    MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+                    MenuItem.SHOW_AS_ACTION_NEVER | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
         }
         return true;
     }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 85b9d55..dcf13c8 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -255,11 +255,11 @@
      * calling back into Chromium code to render the the contents of a Chromium frame into
      * an Android view.
      */
-    public interface NativeDrawGLFunctorFactory {
+    public interface NativeDrawFunctorFactory {
         /**
-         * Create a functor associated with native context |context|.
+         * Create a GL functor associated with native context |context|.
          */
-        NativeDrawGLFunctor createFunctor(long context);
+        NativeDrawGLFunctor createGLFunctor(long context);
     }
 
     /**
@@ -360,7 +360,7 @@
     private final AwContentsIoThreadClient mIoThreadClient;
     private final InterceptNavigationDelegateImpl mInterceptNavigationDelegate;
     private InternalAccessDelegate mInternalAccessAdapter;
-    private final NativeDrawGLFunctorFactory mNativeDrawGLFunctorFactory;
+    private final NativeDrawFunctorFactory mNativeDrawFunctorFactory;
     private final AwLayoutSizer mLayoutSizer;
     private final AwZoomControls mZoomControls;
     private final AwScrollOffsetManager mScrollOffsetManager;
@@ -816,7 +816,7 @@
      * @param containerView the view-hierarchy item this object will be bound to.
      * @param context the context to use, usually containerView.getContext().
      * @param internalAccessAdapter to access private methods on containerView.
-     * @param nativeGLDelegate to access the GL functor provided by the WebView.
+     * @param nativeDrawFunctorFactory to access the functor provided by the WebView.
      * @param contentsClient will receive API callbacks from this WebView Contents.
      * @param awSettings AwSettings instance used to configure the AwContents.
      *
@@ -824,10 +824,10 @@
      */
     public AwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context,
             InternalAccessDelegate internalAccessAdapter,
-            NativeDrawGLFunctorFactory nativeDrawGLFunctorFactory, AwContentsClient contentsClient,
+            NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient,
             AwSettings awSettings) {
         this(browserContext, containerView, context, internalAccessAdapter,
-                nativeDrawGLFunctorFactory, contentsClient, awSettings, new DependencyFactory());
+                nativeDrawFunctorFactory, contentsClient, awSettings, new DependencyFactory());
     }
 
     /**
@@ -839,7 +839,7 @@
      */
     public AwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context,
             InternalAccessDelegate internalAccessAdapter,
-            NativeDrawGLFunctorFactory nativeDrawGLFunctorFactory, AwContentsClient contentsClient,
+            NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient,
             AwSettings settings, DependencyFactory dependencyFactory) {
         try (ScopedSysTraceEvent e1 = ScopedSysTraceEvent.scoped("AwContents.constructor")) {
             mRendererPriority = RendererPriority.HIGH;
@@ -859,7 +859,7 @@
             mAutofillProvider = dependencyFactory.createAutofillProvider(context, mContainerView);
             mAppTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
             mInternalAccessAdapter = internalAccessAdapter;
-            mNativeDrawGLFunctorFactory = nativeDrawGLFunctorFactory;
+            mNativeDrawFunctorFactory = nativeDrawFunctorFactory;
             mContentsClient = contentsClient;
             mContentsClient.getCallbackHelper().setCancelCallbackPoller(
                     () -> AwContents.this.isDestroyedOrNoOperation(NO_WARN));
@@ -1313,6 +1313,18 @@
         return mJavascriptInjector;
     }
 
+    @CalledByNative
+    private void onRendererResponsive(AwRenderProcess renderProcess) {
+        if (isDestroyed(NO_WARN)) return;
+        mContentsClient.onRendererResponsive(renderProcess);
+    }
+
+    @CalledByNative
+    private void onRendererUnresponsive(AwRenderProcess renderProcess) {
+        if (isDestroyed(NO_WARN)) return;
+        mContentsClient.onRendererUnresponsive(renderProcess);
+    }
+
     @VisibleForTesting
     @CalledByNative
     protected void onRenderProcessGone(int childProcessID) {
@@ -3381,7 +3393,7 @@
             }
 
             if (canvas.isHardwareAccelerated() && mDrawFunctor == null) {
-                setFunctor(new AwGLFunctor(mNativeDrawGLFunctorFactory, mContainerView));
+                setFunctor(new AwGLFunctor(mNativeDrawFunctorFactory, mContainerView));
             }
 
             mScrollOffsetManager.syncScrollOffsetFromOnDraw();
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java b/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java
index 022d5fb..cd30bee 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java
@@ -436,5 +436,8 @@
         mCallbackHelper.postOnReceivedTitle(mTitle);
     }
 
+    public abstract void onRendererUnresponsive(AwRenderProcess renderProcess);
+    public abstract void onRendererResponsive(AwRenderProcess renderProcess);
+
     public abstract boolean onRenderProcessGone(AwRenderProcessGoneDetail detail);
 }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwGLFunctor.java b/android_webview/java/src/org/chromium/android_webview/AwGLFunctor.java
index c067cc4..2893030 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwGLFunctor.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwGLFunctor.java
@@ -27,10 +27,10 @@
     // Counts outstanding requestDrawGL calls as well as window attach count.
     private int mRefCount;
 
-    public AwGLFunctor(AwContents.NativeDrawGLFunctorFactory nativeDrawGLFunctorFactory,
-            ViewGroup containerView) {
+    public AwGLFunctor(
+            AwContents.NativeDrawFunctorFactory nativeDrawFunctorFactory, ViewGroup containerView) {
         mNativeAwGLFunctor = nativeCreate(this);
-        mNativeDrawGLFunctor = nativeDrawGLFunctorFactory.createFunctor(
+        mNativeDrawGLFunctor = nativeDrawFunctorFactory.createGLFunctor(
                 nativeGetAwDrawGLViewContext(mNativeAwGLFunctor));
         mContainerView = containerView;
         if (mNativeDrawGLFunctor.supportsDrawGLFunctorReleasedCallback()) {
diff --git a/android_webview/java/src/org/chromium/android_webview/AwMetricsServiceClient.java b/android_webview/java/src/org/chromium/android_webview/AwMetricsServiceClient.java
index 4ee4cbf..3051d09 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwMetricsServiceClient.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwMetricsServiceClient.java
@@ -44,6 +44,8 @@
     private static final int GUID_SIZE = 32 + 4;
     private static final String GUID_FILE_NAME = "metrics_guid";
 
+    private static final String PLAY_STORE_PACKAGE_NAME = "com.android.vending";
+
     private static boolean isAppOptedOut(Context appContext) {
         try {
             ApplicationInfo info = appContext.getPackageManager().getApplicationInfo(
@@ -87,7 +89,17 @@
 
     @CalledByNative
     private static String getAppPackageName() {
-        return ContextUtils.getApplicationContext().getPackageName();
+        Context appCtx = ContextUtils.getApplicationContext();
+        return shouldRecordPackageName(appCtx) ? appCtx.getPackageName() : null;
+    }
+
+    private static boolean shouldRecordPackageName(Context appCtx) {
+        // Only record if it is system apps or installed from PlayStore.
+        String packageName = appCtx.getPackageName();
+        String installerPackageName =
+                appCtx.getPackageManager().getInstallerPackageName(packageName);
+        return (appCtx.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) != 0
+                || (PLAY_STORE_PACKAGE_NAME.equals(installerPackageName));
     }
 
     public static native void nativeSetHaveMetricsConsent(boolean enabled);
diff --git a/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java b/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java
index 853423c..0422649f 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java
@@ -105,9 +105,10 @@
 
         AwContentsClient client = mAwContentsClient.get();
         if (client != null) {
-            // OnPageStarted is not called for fragment navigations.
+            // OnPageStarted is not called for in-page navigations, which include fragment
+            // navigations and navigation from history.push/replaceState.
             // Error page is handled by AwContentsClientBridge.onReceivedError.
-            if (!isFragmentNavigation && !isErrorPage
+            if (!isSameDocument && !isErrorPage
                     && AwFeatureList.pageStartedOnCommitEnabled(isRendererInitiated)) {
                 client.getCallbackHelper().postOnPageStarted(url);
             }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
index 314ebe7..b2701be 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
@@ -21,7 +21,7 @@
 import org.chromium.android_webview.AwContents;
 import org.chromium.android_webview.AwContents.DependencyFactory;
 import org.chromium.android_webview.AwContents.InternalAccessDelegate;
-import org.chromium.android_webview.AwContents.NativeDrawGLFunctorFactory;
+import org.chromium.android_webview.AwContents.NativeDrawFunctorFactory;
 import org.chromium.android_webview.AwContentsClient;
 import org.chromium.android_webview.AwSettings;
 import org.chromium.android_webview.test.util.GraphicsTestUtils;
@@ -360,7 +360,7 @@
         AwContents awContents = testDependencyFactory.createAwContents(mBrowserContext,
                 testContainerView, testContainerView.getContext(),
                 testContainerView.getInternalAccessDelegate(),
-                testContainerView.getNativeDrawGLFunctorFactory(), awContentsClient, awSettings,
+                testContainerView.getNativeDrawFunctorFactory(), awContentsClient, awSettings,
                 testDependencyFactory);
         testContainerView.initialize(awContents);
         return testContainerView;
@@ -608,11 +608,10 @@
 
         public AwContents createAwContents(AwBrowserContext browserContext, ViewGroup containerView,
                 Context context, InternalAccessDelegate internalAccessAdapter,
-                NativeDrawGLFunctorFactory nativeDrawGLFunctorFactory,
-                AwContentsClient contentsClient, AwSettings settings,
-                DependencyFactory dependencyFactory) {
+                NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient,
+                AwSettings settings, DependencyFactory dependencyFactory) {
             return new AwContents(browserContext, containerView, context, internalAccessAdapter,
-                    nativeDrawGLFunctorFactory, contentsClient, settings, dependencyFactory);
+                    nativeDrawFunctorFactory, contentsClient, settings, dependencyFactory);
         }
     }
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnRendererUnresponsiveTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnRendererUnresponsiveTest.java
new file mode 100644
index 0000000..66f62a0
--- /dev/null
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnRendererUnresponsiveTest.java
@@ -0,0 +1,169 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.android_webview.test;
+
+import static org.chromium.android_webview.test.OnlyRunIn.ProcessMode.MULTI_PROCESS;
+
+import android.support.test.filters.LargeTest;
+import android.view.KeyEvent;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.android_webview.AwContents;
+import org.chromium.android_webview.AwRenderProcess;
+import org.chromium.android_webview.AwRenderProcessGoneDetail;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.Feature;
+import org.chromium.content_public.common.ContentUrlConstants;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for AwContentsClient.onRenderProcessGone callback.
+ */
+@RunWith(AwJUnit4ClassRunner.class)
+public class AwContentsClientOnRendererUnresponsiveTest {
+    @Rule
+    public AwActivityTestRule mActivityTestRule = new AwActivityTestRule();
+
+    private static final String TAG = "AwRendererUnresponsive";
+
+    private static class RendererTransientlyUnresponsiveTestAwContentsClient
+            extends TestAwContentsClient {
+        private CallbackHelper mUnresponsiveCallbackHelper;
+        private CallbackHelper mResponsiveCallbackHelper;
+
+        public RendererTransientlyUnresponsiveTestAwContentsClient() {
+            mUnresponsiveCallbackHelper = new CallbackHelper();
+            mResponsiveCallbackHelper = new CallbackHelper();
+        }
+
+        void transientlyBlockBlinkThread(final AwContents awContents) {
+            ThreadUtils.runOnUiThread(() -> {
+                // clang-format off
+                awContents.evaluateJavaScript(
+                        "let t0 = performance.now();\n" +
+                        "while(performance.now() < t0 + 6000 /*ms*/) {}",
+                        null);
+                // clang-format on
+            });
+        }
+
+        void awaitRecovery() throws Exception {
+            // unresponsive signal should occur after 5 seconds, so wait for 6.
+            mUnresponsiveCallbackHelper.waitForCallback(0, 1, 6, TimeUnit.SECONDS);
+            Assert.assertEquals(1, mUnresponsiveCallbackHelper.getCallCount());
+            // blink main thread is transiently blocked for 6 seconds; wait
+            // an additional 5 seconds.
+            mResponsiveCallbackHelper.waitForCallback(0, 1, 5, TimeUnit.SECONDS);
+            Assert.assertEquals(1, mResponsiveCallbackHelper.getCallCount());
+        }
+
+        @Override
+        public void onRendererResponsive(AwRenderProcess process) {
+            mResponsiveCallbackHelper.notifyCalled();
+        }
+
+        @Override
+        public void onRendererUnresponsive(AwRenderProcess process) {
+            // onRendererResponsive should not have been called yet.
+            Assert.assertEquals(0, mResponsiveCallbackHelper.getCallCount());
+            mUnresponsiveCallbackHelper.notifyCalled();
+        }
+    }
+
+    private static class RendererUnresponsiveTestAwContentsClient extends TestAwContentsClient {
+        // The renderer unresponsive callback should be called repeatedly. We will wait for two
+        // callbacks.
+        static final int UNRESPONSIVE_CALLBACK_COUNT = 2;
+
+        private CallbackHelper mUnresponsiveCallbackHelper;
+        private CallbackHelper mTerminatedCallbackHelper;
+
+        public RendererUnresponsiveTestAwContentsClient() {
+            mUnresponsiveCallbackHelper = new CallbackHelper();
+            mTerminatedCallbackHelper = new CallbackHelper();
+        }
+
+        void permanentlyBlockBlinkThread(final AwContents awContents) {
+            ThreadUtils.runOnUiThread(() -> { awContents.evaluateJavaScript("while(1);", null); });
+        }
+
+        void awaitRendererTermination() throws Exception {
+            // The input ack timeout is 5 seconds, and the unresponsive callback should retrigger at
+            // that interval, so wait for the expected multiple of 5 seconds, plus one second
+            // leeway.
+            mUnresponsiveCallbackHelper.waitForCallback(
+                    0, 2, 5 * UNRESPONSIVE_CALLBACK_COUNT + 1, TimeUnit.SECONDS);
+            Assert.assertEquals(2, mUnresponsiveCallbackHelper.getCallCount());
+
+            mTerminatedCallbackHelper.waitForCallback(0, 1, 5, TimeUnit.SECONDS);
+            Assert.assertEquals(1, mTerminatedCallbackHelper.getCallCount());
+        }
+
+        @Override
+        public boolean onRenderProcessGone(AwRenderProcessGoneDetail detail) {
+            mTerminatedCallbackHelper.notifyCalled();
+            return true;
+        }
+
+        @Override
+        public void onRendererUnresponsive(AwRenderProcess process) {
+            mUnresponsiveCallbackHelper.notifyCalled();
+            if (mUnresponsiveCallbackHelper.getCallCount() == UNRESPONSIVE_CALLBACK_COUNT) {
+                process.terminate();
+            }
+        }
+    }
+
+    private void sendInputEvent(final AwContents awContents) {
+        ThreadUtils.runOnUiThread(() -> {
+            awContents.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
+        });
+    }
+    @Test
+    @Feature({"AndroidWebView"})
+    @LargeTest
+    @OnlyRunIn(MULTI_PROCESS)
+    public void testOnRendererUnresponsive() throws Throwable {
+        RendererUnresponsiveTestAwContentsClient contentsClient =
+                new RendererUnresponsiveTestAwContentsClient();
+        AwTestContainerView testView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(contentsClient);
+        final AwContents awContents = testView.getAwContents();
+
+        AwActivityTestRule.enableJavaScriptOnUiThread(awContents);
+        mActivityTestRule.loadUrlSync(awContents, contentsClient.getOnPageFinishedHelper(),
+                ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
+
+        contentsClient.permanentlyBlockBlinkThread(awContents);
+        // Sending a key event while the renderer is unresponsive will cause onRendererUnresponsive
+        // to be called.
+        sendInputEvent(awContents);
+        contentsClient.awaitRendererTermination();
+    }
+
+    @Test
+    @Feature({"AndroidWebView"})
+    @LargeTest
+    public void testTransientUnresponsiveness() throws Throwable {
+        RendererTransientlyUnresponsiveTestAwContentsClient contentsClient =
+                new RendererTransientlyUnresponsiveTestAwContentsClient();
+        AwTestContainerView testView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(contentsClient);
+        final AwContents awContents = testView.getAwContents();
+
+        AwActivityTestRule.enableJavaScriptOnUiThread(awContents);
+        mActivityTestRule.loadUrlSync(awContents, contentsClient.getOnPageFinishedHelper(),
+                ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
+        contentsClient.transientlyBlockBlinkThread(awContents);
+        sendInputEvent(awContents);
+        contentsClient.awaitRecovery();
+    }
+}
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java
index e863005..6902655 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java
@@ -302,8 +302,11 @@
                 CommonResources.makeHtmlPageWithSimpleLinkTo(anchorLinkUrl + "#anchor"));
 
         if (useLoadData) {
+            final String html =
+                    CommonResources.makeHtmlPageWithSimpleLinkTo("#anchor").replace("#", "%23");
+            // Loading the html via a data URI requires us to encode '#' symbols as '%23'.
             mActivityTestRule.loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
-                    CommonResources.makeHtmlPageWithSimpleLinkTo("%23anchor"), "text/html", false);
+                    html, "text/html", false);
         } else {
             mActivityTestRule.loadUrlSync(
                     mAwContents, mContentsClient.getOnPageFinishedHelper(), anchorLinkUrl);
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsRenderTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsRenderTest.java
index 936720a..fff2de8 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsRenderTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsRenderTest.java
@@ -67,9 +67,11 @@
         setBackgroundColorOnUiThread(Color.YELLOW);
         GraphicsTestUtils.pollForBackgroundColor(mAwContents, Color.YELLOW);
 
+        final String html = "<html><head><style>body {background-color:#227788}</style></head>"
+                + "<body></body></html>";
+        // Loading the html via a data URI requires us to encode '#' symbols as '%23'.
         mActivityTestRule.loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
-                "data:text/html,<html><head><style>body {background-color:%23227788}</style></head>"
-                        + "<body></body></html>");
+                "data:text/html," + html.replace("#", "%23"));
         final int teal = 0xFF227788;
         GraphicsTestUtils.pollForBackgroundColor(mAwContents, teal);
 
@@ -99,9 +101,11 @@
     @SmallTest
     @Feature({"AndroidWebView"})
     public void testForceDrawWhenInvisible() throws Throwable {
+        final String html = "<html><head><style>body {background-color:#227788}</style></head>"
+                + "<body>Hello world!</body></html>";
+        // Loading the html via a data URI requires us to encode '#' symbols as '%23'.
         mActivityTestRule.loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
-                "data:text/html,<html><head><style>body {background-color:%23227788}</style></head>"
-                        + "<body>Hello world!</body></html>");
+                "data:text/html," + html.replace("#", "%23"));
 
         Bitmap visibleBitmap = null;
         Bitmap invisibleBitmap = null;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
index 63239a0..a8bdf1a 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
@@ -3019,10 +3019,12 @@
         final String page = "<!doctype html>"
                 + "<script>"
                 + "window.onload = function() {"
-                + "  document.title = CSS.supports('color', '%23AABBCCDD');"
+                + "  document.title = CSS.supports('color', '#AABBCCDD');"
                 + "};"
                 + "</script>";
-        mActivityTestRule.loadDataSync(awContents, onPageFinishedHelper, page, "text/html", false);
+        // Loading the html via a data URI requires us to encode '#' symbols as '%23'.
+        mActivityTestRule.loadDataSync(
+                awContents, onPageFinishedHelper, page.replace("#", "%23"), "text/html", false);
         String actualTitle = mActivityTestRule.getTitleOnUiThread(awContents);
         Assert.assertEquals(expectedTitle, actualTitle);
     }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java
index f80de4ad..4d1380f 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java
@@ -18,6 +18,7 @@
 import org.chromium.android_webview.AwContents;
 import org.chromium.android_webview.AwCookieManager;
 import org.chromium.android_webview.AwSettings;
+import org.chromium.android_webview.AwWebResourceResponse;
 import org.chromium.android_webview.test.util.CommonResources;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.Feature;
@@ -28,8 +29,11 @@
 import org.chromium.content_public.common.ContentUrlConstants;
 import org.chromium.net.test.util.TestWebServer;
 
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.nio.charset.Charset;
 import java.util.List;
 
 /**
@@ -566,9 +570,87 @@
         AwSettings contentSettings = mActivityTestRule.getAwSettingsOnUiThread(mAwContents);
         contentSettings.setJavaScriptEnabled(true);
         loadDataWithBaseUrlSync("", "text/html", false, baseUri, null);
-        // TODO(dcheng): https://crbug.com/896059 this should be fixed as "x-thread://".
-        Assert.assertEquals("\"null\"",
+        Assert.assertEquals("\"x-thread://\"",
                 mActivityTestRule.executeJavaScriptAndWaitForResult(
                         mAwContents, mContentsClient, "window.origin;"));
     }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testXhrForHttpSchemeUrl() throws Throwable {
+        Assert.assertTrue(verifyXhrForUrls("https://google.com/1", "https://google.com/2"));
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    // https://crbug.com/900528
+    public void testXhrForCustomSchemeUrl() throws Throwable {
+        Assert.assertTrue(verifyXhrForUrls("myscheme://mydomain/1", "myscheme://mydomain/2"));
+    }
+
+    /**
+     * Verify that XHR can be correctly run with the set base URI, regardless of its scheme.
+     *
+     * @param baseUri Base URI to start from.
+     * @param textUri The text URI to fetch the text from.
+     * @throws Throwable
+     */
+    private boolean verifyXhrForUrls(String baseUri, String textUri) throws Throwable {
+        final String successMsg = "SUCCESS";
+        final String errorMsg = "ERROR";
+        final String data = "<html><head><script type='text/javascript'>"
+                + "var xhr = new XMLHttpRequest();"
+                + "xhr.open('GET', '" + textUri + "', true);"
+                + "xhr.onload = function(e) {"
+                + "  if (xhr.readyState === 4 && xhr.status === 200) {"
+                + "    document.title = xhr.responseText;"
+                + "  } else {"
+                + "    console.log('Error status: ' + xhr.statusText);"
+                + "    document.title = '" + errorMsg + "';"
+                + "  }"
+                + "};"
+                + "xhr.onerror = function(e) {"
+                + "  console.log('Error status: ' + xhr.statusText);"
+                + "  document.title = '" + errorMsg + "';"
+                + "};"
+                + "xhr.send(null);"
+                + "</script></head></html>";
+
+        // Intercept TEXT URI request, and respond with 'SUCCESS'.
+        TestAwContentsClient client = new TestAwContentsClient() {
+            @Override
+            public AwWebResourceResponse shouldInterceptRequest(AwWebResourceRequest request) {
+                String url = request.url;
+                if (textUri.equals(url)) {
+                    return new AwWebResourceResponse(
+                            "text/plaintext", "utf-8", createInputStreamForString(successMsg));
+                } else {
+                    return super.shouldInterceptRequest(request);
+                }
+            }
+        };
+
+        // We need extra setup with the new client.
+        final AwTestContainerView testContainerView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(client);
+        AwContents awContents = testContainerView.getAwContents();
+        mActivityTestRule.getAwSettingsOnUiThread(awContents).setJavaScriptEnabled(true);
+
+        // Starting from BASE URI, load data that loads JS URI.
+        mActivityTestRule.loadDataWithBaseUrlSync(awContents, client.getOnPageFinishedHelper(),
+                data, "text/html", false, baseUri, null);
+
+        // Polling here as XHR may take extra steps to change the title.
+        AwActivityTestRule.pollInstrumentationThread(() -> {
+            String title = mActivityTestRule.getTitleOnUiThread(awContents);
+            return successMsg.equals(title) || errorMsg.equals(title);
+        });
+        return successMsg.equals(mActivityTestRule.getTitleOnUiThread(awContents));
+    }
+
+    private InputStream createInputStreamForString(String str) {
+        return new ByteArrayInputStream(str.getBytes(Charset.defaultCharset()));
+    }
 }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
index eba1882..7b871f7 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
@@ -27,7 +27,7 @@
 import org.chromium.android_webview.AwContents;
 import org.chromium.android_webview.AwContents.DependencyFactory;
 import org.chromium.android_webview.AwContents.InternalAccessDelegate;
-import org.chromium.android_webview.AwContents.NativeDrawGLFunctorFactory;
+import org.chromium.android_webview.AwContents.NativeDrawFunctorFactory;
 import org.chromium.android_webview.AwContentsClient;
 import org.chromium.android_webview.AwContentsStatics;
 import org.chromium.android_webview.AwSafeBrowsingConfigHelper;
@@ -233,11 +233,10 @@
 
         public MockAwContents(AwBrowserContext browserContext, ViewGroup containerView,
                 Context context, InternalAccessDelegate internalAccessAdapter,
-                NativeDrawGLFunctorFactory nativeDrawGLFunctorFactory,
-                AwContentsClient contentsClient, AwSettings settings,
-                DependencyFactory dependencyFactory) {
+                NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient,
+                AwSettings settings, DependencyFactory dependencyFactory) {
             super(browserContext, containerView, context, internalAccessAdapter,
-                    nativeDrawGLFunctorFactory, contentsClient, settings, dependencyFactory);
+                    nativeDrawFunctorFactory, contentsClient, settings, dependencyFactory);
             mCanShowInterstitial = true;
             mCanShowBigInterstitial = true;
         }
@@ -300,11 +299,10 @@
         @Override
         public AwContents createAwContents(AwBrowserContext browserContext, ViewGroup containerView,
                 Context context, InternalAccessDelegate internalAccessAdapter,
-                NativeDrawGLFunctorFactory nativeDrawGLFunctorFactory,
-                AwContentsClient contentsClient, AwSettings settings,
-                DependencyFactory dependencyFactory) {
+                NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient,
+                AwSettings settings, DependencyFactory dependencyFactory) {
             return new MockAwContents(browserContext, containerView, context, internalAccessAdapter,
-                    nativeDrawGLFunctorFactory, contentsClient, settings, dependencyFactory);
+                    nativeDrawFunctorFactory, contentsClient, settings, dependencyFactory);
         }
     }
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContents.java b/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContents.java
index af2d2c3..bfaf711 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContents.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContents.java
@@ -45,10 +45,10 @@
 
     public TestAwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context,
             InternalAccessDelegate internalAccessAdapter,
-            NativeDrawGLFunctorFactory nativeDrawGLFunctorFactory, AwContentsClient contentsClient,
+            NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient,
             AwSettings settings, DependencyFactory dependencyFactory) {
         super(browserContext, containerView, context, internalAccessAdapter,
-                nativeDrawGLFunctorFactory, contentsClient, settings, dependencyFactory);
+                nativeDrawFunctorFactory, contentsClient, settings, dependencyFactory);
 
         mRenderProcessGoneHelper = new RenderProcessGoneHelper();
         mRenderProcessGoneObservers = new ArrayList<RenderProcessGoneObserver>();
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/services/VisualStateCallbackTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/services/VisualStateCallbackTest.java
index b78b3f2..d534e50 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/services/VisualStateCallbackTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/services/VisualStateCallbackTest.java
@@ -20,7 +20,7 @@
 import org.chromium.android_webview.AwContents;
 import org.chromium.android_webview.AwContents.DependencyFactory;
 import org.chromium.android_webview.AwContents.InternalAccessDelegate;
-import org.chromium.android_webview.AwContents.NativeDrawGLFunctorFactory;
+import org.chromium.android_webview.AwContents.NativeDrawFunctorFactory;
 import org.chromium.android_webview.AwContentsClient;
 import org.chromium.android_webview.AwRenderProcessGoneDetail;
 import org.chromium.android_webview.AwSettings;
@@ -78,11 +78,10 @@
         public VisualStateCallbackTestAwContents(AwBrowserContext browserContext,
                 ViewGroup containerView, Context context,
                 InternalAccessDelegate internalAccessAdapter,
-                NativeDrawGLFunctorFactory nativeDrawGLFunctorFactory,
-                AwContentsClient contentsClient, AwSettings settings,
-                DependencyFactory dependencyFactory) {
+                NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient,
+                AwSettings settings, DependencyFactory dependencyFactory) {
             super(browserContext, containerView, context, internalAccessAdapter,
-                    nativeDrawGLFunctorFactory, contentsClient, settings, dependencyFactory);
+                    nativeDrawFunctorFactory, contentsClient, settings, dependencyFactory);
             mVisualStateCallbackHelper = new VisualStateCallbackHelper();
         }
 
@@ -113,11 +112,10 @@
         @Override
         public AwContents createAwContents(AwBrowserContext browserContext, ViewGroup containerView,
                 Context context, InternalAccessDelegate internalAccessAdapter,
-                NativeDrawGLFunctorFactory nativeDrawGLFunctorFactory,
-                AwContentsClient contentsClient, AwSettings settings,
-                DependencyFactory dependencyFactory) {
+                NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient,
+                AwSettings settings, DependencyFactory dependencyFactory) {
             return new VisualStateCallbackTestAwContents(browserContext, containerView, context,
-                    internalAccessAdapter, nativeDrawGLFunctorFactory, contentsClient, settings,
+                    internalAccessAdapter, nativeDrawFunctorFactory, contentsClient, settings,
                     dependencyFactory);
         }
     }
diff --git a/android_webview/lib/aw_main_delegate.cc b/android_webview/lib/aw_main_delegate.cc
index e722cc1..65c016e 100644
--- a/android_webview/lib/aw_main_delegate.cc
+++ b/android_webview/lib/aw_main_delegate.cc
@@ -101,8 +101,9 @@
   // WebView does not yet support screen orientation locking.
   cl->AppendSwitch(switches::kDisableScreenOrientationLock);
 
-  // WebView does not currently support Web Speech API (crbug.com/487255)
-  cl->AppendSwitch(switches::kDisableSpeechAPI);
+  // WebView does not currently support Web Speech Synthesis API,
+  // but it does support Web Speech Recognition API (crbug.com/487255).
+  cl->AppendSwitch(switches::kDisableSpeechSynthesisAPI);
 
   // WebView does not currently support the Permissions API (crbug.com/490120)
   cl->AppendSwitch(switches::kDisablePermissionsAPI);
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/IsomorphicObjectBoundaryInterface.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/IsomorphicObjectBoundaryInterface.java
index a0e3846..7cccfc4 100644
--- a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/IsomorphicObjectBoundaryInterface.java
+++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/IsomorphicObjectBoundaryInterface.java
@@ -35,5 +35,5 @@
      * @return The peer object associated with this object, which either exists already, or has
      *         been freshly created and recorded.
      */
-    Object getOrCreatePeer(Callable<Object> creationCallable) throws Exception;
+    Object getOrCreatePeer(Callable<Object> creationCallable);
 }
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/BoundaryInterfaceReflectionUtil.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/BoundaryInterfaceReflectionUtil.java
index 566535b..6badd3a 100644
--- a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/BoundaryInterfaceReflectionUtil.java
+++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/BoundaryInterfaceReflectionUtil.java
@@ -10,6 +10,8 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.Collection;
 
 /**
  * A set of utility methods used for calling across the support library boundary.
@@ -129,14 +131,21 @@
         }
     }
 
+    private static boolean isDebuggable() {
+        return !Build.TYPE.equals("user");
+    }
+
     /**
      * Check whether a set of features {@param features} contains a certain feature {@param
      * soughtFeature}.
      */
+    public static boolean containsFeature(Collection<String> features, String soughtFeature) {
+        assert !soughtFeature.endsWith(Features.DEV_SUFFIX);
+        return features.contains(soughtFeature)
+                || (isDebuggable() && features.contains(soughtFeature + Features.DEV_SUFFIX));
+    }
+
     public static boolean containsFeature(String[] features, String soughtFeature) {
-        for (String feature : features) {
-            if (feature.equals(soughtFeature)) return true;
-        }
-        return false;
+        return containsFeature(Arrays.asList(features), soughtFeature);
     }
 }
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java
index 59ce1df..b382c0b 100644
--- a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java
+++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java
@@ -10,6 +10,9 @@
  * Chromium can share its definition.
  */
 public class Features {
+    // Features suffixed with DEV will only be visible on debug devices.
+    public static final String DEV_SUFFIX = ":dev";
+
     // This class just contains constants representing features.
     private Features() {}
 
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/IsomorphicAdapter.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/IsomorphicAdapter.java
index 0fc210f0..e563e46 100644
--- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/IsomorphicAdapter.java
+++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/IsomorphicAdapter.java
@@ -16,14 +16,18 @@
     abstract AwSupportLibIsomorphic getPeeredObject();
 
     @Override
-    public Object getOrCreatePeer(Callable<Object> creationCallable) throws Exception {
+    public Object getOrCreatePeer(Callable<Object> creationCallable) {
         AwSupportLibIsomorphic peeredObject = getPeeredObject();
         if (peeredObject == null) {
             return null;
         }
         Object peer = peeredObject.getSupportLibObject();
         if (peer == null) {
-            peeredObject.setSupportLibObject(peer = creationCallable.call());
+            try {
+                peeredObject.setSupportLibObject(peer = creationCallable.call());
+            } catch (Exception e) {
+                throw new RuntimeException("Could not create peered object", e);
+            }
         }
         return peer;
     }
diff --git a/android_webview/test/BUILD.gn b/android_webview/test/BUILD.gn
index 89ab61a..49e22ae 100644
--- a/android_webview/test/BUILD.gn
+++ b/android_webview/test/BUILD.gn
@@ -192,6 +192,7 @@
     "../javatests/src/org/chromium/android_webview/test/AwContentsClientFullScreenTest.java",
     "../javatests/src/org/chromium/android_webview/test/AwContentsClientGetDefaultVideoPosterTest.java",
     "../javatests/src/org/chromium/android_webview/test/AwContentsClientOnFormResubmissionTest.java",
+    "../javatests/src/org/chromium/android_webview/test/AwContentsClientOnRendererUnresponsiveTest.java",
     "../javatests/src/org/chromium/android_webview/test/AwContentsClientOnRenderProcessGoneTest.java",
     "../javatests/src/org/chromium/android_webview/test/AwContentsClientOnScaleChangedTest.java",
     "../javatests/src/org/chromium/android_webview/test/AwContentsClientOnUnhandledKeyEventTest.java",
diff --git a/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java b/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java
index 653679e..0d97cef 100644
--- a/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java
+++ b/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java
@@ -219,7 +219,7 @@
 
         testContainerView.initialize(new AwContents(mBrowserContext, testContainerView,
                 testContainerView.getContext(), testContainerView.getInternalAccessDelegate(),
-                testContainerView.getNativeDrawGLFunctorFactory(), awContentsClient, awSettings));
+                testContainerView.getNativeDrawFunctorFactory(), awContentsClient, awSettings));
         testContainerView.getAwContents().getSettings().setJavaScriptEnabled(true);
         if (mDevToolsServer == null) {
             mDevToolsServer = new AwDevToolsServer();
diff --git a/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java b/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java
index 8d79205..39284c4 100644
--- a/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java
+++ b/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java
@@ -277,8 +277,8 @@
         return mAwContents;
     }
 
-    public AwContents.NativeDrawGLFunctorFactory getNativeDrawGLFunctorFactory() {
-        return new NativeDrawGLFunctorFactory();
+    public AwContents.NativeDrawFunctorFactory getNativeDrawFunctorFactory() {
+        return new NativeDrawFunctorFactory();
     }
 
     public AwContents.InternalAccessDelegate getInternalAccessDelegate() {
@@ -436,9 +436,9 @@
         return mAwContents.performAccessibilityAction(action, arguments);
     }
 
-    private class NativeDrawGLFunctorFactory implements AwContents.NativeDrawGLFunctorFactory {
+    private class NativeDrawFunctorFactory implements AwContents.NativeDrawFunctorFactory {
         @Override
-        public NativeDrawGLFunctor createFunctor(long context) {
+        public NativeDrawGLFunctor createGLFunctor(long context) {
             return new NativeDrawGLFunctor(context);
         }
     }
diff --git a/android_webview/test/shell/src/org/chromium/android_webview/test/NullContentsClient.java b/android_webview/test/shell/src/org/chromium/android_webview/test/NullContentsClient.java
index 2f112b2..abfa001 100644
--- a/android_webview/test/shell/src/org/chromium/android_webview/test/NullContentsClient.java
+++ b/android_webview/test/shell/src/org/chromium/android_webview/test/NullContentsClient.java
@@ -18,6 +18,7 @@
 import org.chromium.android_webview.AwContentsClientBridge;
 import org.chromium.android_webview.AwGeolocationPermissions;
 import org.chromium.android_webview.AwHttpAuthHandler;
+import org.chromium.android_webview.AwRenderProcess;
 import org.chromium.android_webview.AwRenderProcessGoneDetail;
 import org.chromium.android_webview.AwSafeBrowsingResponse;
 import org.chromium.android_webview.AwWebResourceResponse;
@@ -259,6 +260,12 @@
     }
 
     @Override
+    public void onRendererUnresponsive(AwRenderProcess process) {}
+
+    @Override
+    public void onRendererResponsive(AwRenderProcess process) {}
+
+    @Override
     public boolean onRenderProcessGone(AwRenderProcessGoneDetail detail) {
         return false;
     }
diff --git a/android_webview/tools/system_webview_shell/BUILD.gn b/android_webview/tools/system_webview_shell/BUILD.gn
index c4013a4..658f42e 100644
--- a/android_webview/tools/system_webview_shell/BUILD.gn
+++ b/android_webview/tools/system_webview_shell/BUILD.gn
@@ -78,13 +78,13 @@
   ]
   data = [
     "test/data/",
-    "//third_party/WebKit/LayoutTests/platform/linux/virtual/stable/webexposed/",
-    "//third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/",
-    "//third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/",
-    "//third_party/WebKit/LayoutTests/resources/global-interface-listing.js",
-    "//third_party/WebKit/LayoutTests/virtual/stable/webexposed/",
-    "//third_party/WebKit/LayoutTests/webexposed/global-interface-listing.html",
-    "//third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt",
+    "//third_party/blink/web_tests/platform/linux/virtual/stable/webexposed/",
+    "//third_party/blink/web_tests/platform/mac/virtual/stable/webexposed/",
+    "//third_party/blink/web_tests/platform/win/virtual/stable/webexposed/",
+    "//third_party/blink/web_tests/resources/global-interface-listing.js",
+    "//third_party/blink/web_tests/virtual/stable/webexposed/",
+    "//third_party/blink/web_tests/webexposed/global-interface-listing.html",
+    "//third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt",
   ]
   additional_apks = [ system_webview_apk_target ]
 }
diff --git a/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java b/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java
index f9ad2c0..2f7b7b3 100644
--- a/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java
+++ b/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java
@@ -51,7 +51,7 @@
     private static final String EXTERNAL_PREFIX = UrlUtils.getIsolatedTestRoot() + "/";
     private static final String BASE_WEBVIEW_TEST_PATH =
             "android_webview/tools/system_webview_shell/test/data/";
-    private static final String BASE_BLINK_TEST_PATH = "third_party/WebKit/LayoutTests/";
+    private static final String BASE_BLINK_TEST_PATH = "third_party/blink/web_tests/";
     private static final String PATH_WEBVIEW_PREFIX = EXTERNAL_PREFIX + BASE_WEBVIEW_TEST_PATH;
     private static final String PATH_BLINK_PREFIX = EXTERNAL_PREFIX + BASE_BLINK_TEST_PATH;
     private static final String GLOBAL_LISTING_FILE =
diff --git a/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt b/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
index 1e4419a..853e086a 100644
--- a/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
+++ b/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
@@ -1,12 +1,8 @@
-# speech api not enabled in webview, crbug.com/487255
+# speech synthesis api not enabled in webview
+# but speech recognition is enabled, crbug.com/487255
 interface SpeechSynthesisErrorEvent : SpeechSynthesisEvent
 interface SpeechSynthesisEvent : Event
 interface SpeechSynthesisUtterance : EventTarget
-interface webkitSpeechGrammar
-interface webkitSpeechGrammarList
-interface webkitSpeechRecognition : EventTarget
-interface webkitSpeechRecognitionError : Event
-interface webkitSpeechRecognitionEvent : Event
 
 # permissions api not enabled in webview, crbug.com/490120
 interface PermissionStatus : EventTarget
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index ec018be..cc3ebc9 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -168,8 +168,6 @@
     "assistant/assistant_interaction_controller.h",
     "assistant/assistant_notification_controller.cc",
     "assistant/assistant_notification_controller.h",
-    "assistant/assistant_response_processor.cc",
-    "assistant/assistant_response_processor.h",
     "assistant/assistant_screen_context_controller.cc",
     "assistant/assistant_screen_context_controller.h",
     "assistant/assistant_setup_controller.cc",
@@ -184,6 +182,8 @@
     "assistant/model/assistant_interaction_model_observer.h",
     "assistant/model/assistant_query.cc",
     "assistant/model/assistant_query.h",
+    "assistant/model/assistant_query_history.cc",
+    "assistant/model/assistant_query_history.h",
     "assistant/model/assistant_response.cc",
     "assistant/model/assistant_response.h",
     "assistant/model/assistant_screen_context_model.cc",
@@ -524,6 +524,7 @@
     "multi_profile_uma.cc",
     "multi_profile_uma.h",
     "multi_user/multi_user_window_manager.cc",
+    "multi_user/multi_user_window_manager_window_delegate.h",
     "multi_user/user_switch_animator.cc",
     "multi_user/user_switch_animator.h",
     "network_connect_delegate_mus.cc",
@@ -663,7 +664,6 @@
     "system/bluetooth/bluetooth_feature_pod_controller.h",
     "system/bluetooth/bluetooth_notification_controller.cc",
     "system/bluetooth/bluetooth_notification_controller.h",
-    "system/bluetooth/bluetooth_observer.h",
     "system/bluetooth/bluetooth_power_controller.cc",
     "system/bluetooth/bluetooth_power_controller.h",
     "system/bluetooth/tray_bluetooth_helper.cc",
@@ -787,6 +787,8 @@
     "system/network/network_row_title_view.h",
     "system/network/network_state_list_detailed_view.cc",
     "system/network/network_state_list_detailed_view.h",
+    "system/network/network_tray_icon_strategy.cc",
+    "system/network/network_tray_icon_strategy.h",
     "system/network/network_tray_view.cc",
     "system/network/network_tray_view.h",
     "system/network/sms_observer.cc",
@@ -1264,6 +1266,8 @@
     "ws/ash_gpu_interface_provider.h",
     "ws/ash_window_manager.cc",
     "ws/ash_window_manager.h",
+    "ws/multi_user_window_manager_bridge.cc",
+    "ws/multi_user_window_manager_bridge.h",
     "ws/window_lookup.cc",
     "ws/window_service_delegate_impl.cc",
     "ws/window_service_delegate_impl.h",
@@ -1282,6 +1286,7 @@
     "//ash/wayland",
     "//components/discardable_memory/public/interfaces",
     "//mojo/public/cpp/system",
+    "//services/device/public/cpp/bluetooth",
     "//services/device/public/mojom",
     "//services/media_session/public/mojom",
     "//services/service_manager/public/cpp",
@@ -1578,6 +1583,8 @@
     "//ui/display",
     "//ui/display:test_support",
     "//ui/gl:test_support",
+    "//ui/keyboard",
+    "//ui/keyboard:mojom",
     "//ui/message_center",
     "//ui/message_center:test_support",
     "//ui/message_center/public/cpp",
@@ -1624,6 +1631,7 @@
     "ash_service_unittest.cc",
     "assistant/assistant_controller_unittest.cc",
     "assistant/assistant_screen_context_controller_unittest.cc",
+    "assistant/model/assistant_query_history_unittest.cc",
     "assistant/ui/assistant_container_view_unittest.cc",
     "assistant/util/deep_link_util_unittest.cc",
     "autoclick/autoclick_drag_event_rewriter_unittest.cc",
@@ -1907,6 +1915,7 @@
     "//ash/components/tap_visualizer:unit_tests",
     "//ash/keyboard/arc",
     "//ash/public/cpp",
+    "//ash/public/cpp:test_support",
     "//ash/public/cpp:unit_tests",
     "//ash/public/cpp/vector_icons",
     "//ash/resources/vector_icons",
@@ -2100,6 +2109,8 @@
     "app_list/test/app_list_test_helper.h",
     "app_list/test/test_app_list_client.cc",
     "app_list/test/test_app_list_client.h",
+    "app_menu/notification_menu_view_test_api.cc",
+    "app_menu/notification_menu_view_test_api.h",
     "display/display_configuration_controller_test_api.cc",
     "display/display_configuration_controller_test_api.h",
     "display/mirror_window_test_api.cc",
@@ -2129,12 +2140,6 @@
     "metrics/user_metrics_recorder_test_api.h",
     "mojo_test_interface_factory.cc",
     "mojo_test_interface_factory.h",
-
-    # TODO(jamescook): Consider adding a //ash/public/cpp:test_support target.
-    "app_menu/notification_menu_view_test_api.cc",
-    "app_menu/notification_menu_view_test_api.h",
-    "public/cpp/immersive/immersive_fullscreen_controller_test_api.cc",
-    "public/cpp/immersive/immersive_fullscreen_controller_test_api.h",
     "rotator/screen_rotation_animator_test_api.cc",
     "rotator/screen_rotation_animator_test_api.h",
     "session/test_session_controller_client.cc",
@@ -2223,6 +2228,7 @@
     "//ash/app_menu",
     "//ash/components/fast_ink",
     "//ash/public/cpp",
+    "//ash/public/cpp:test_support",
     "//ash/public/interfaces:test_interfaces",
     "//base",
     "//base:i18n",
diff --git a/ash/accessibility/touch_accessibility_enabler_unittest.cc b/ash/accessibility/touch_accessibility_enabler_unittest.cc
index 6a423e1..f6ea202 100644
--- a/ash/accessibility/touch_accessibility_enabler_unittest.cc
+++ b/ash/accessibility/touch_accessibility_enabler_unittest.cc
@@ -104,12 +104,12 @@
                                      enabler_->GetWeakPtr()));
 
   EXPECT_TRUE(enabler_->IsInNoFingersDownForTesting());
-  generator_->set_current_location(gfx::Point(11, 12));
+  generator_->set_current_screen_location(gfx::Point(11, 12));
   generator_->PressTouchId(1);
 
   simulated_clock_.Advance(base::TimeDelta::FromMilliseconds(500));
 
-  generator_->set_current_location(gfx::Point(22, 34));
+  generator_->set_current_screen_location(gfx::Point(22, 34));
   generator_->PressTouchId(2);
 
   EXPECT_TRUE(enabler_->IsInTwoFingersDownForTesting());
@@ -124,7 +124,7 @@
 TEST_F(TouchAccessibilityEnablerTest, EntersOneFingerDownMode) {
   EXPECT_TRUE(enabler_->IsInNoFingersDownForTesting());
   EXPECT_FALSE(enabler_->IsInOneFingerDownForTesting());
-  generator_->set_current_location(gfx::Point(11, 12));
+  generator_->set_current_screen_location(gfx::Point(11, 12));
   generator_->PressTouch();
 
   EXPECT_FALSE(enabler_->IsInNoFingersDownForTesting());
@@ -133,10 +133,10 @@
 
 TEST_F(TouchAccessibilityEnablerTest, EntersTwoFingersDownMode) {
   EXPECT_TRUE(enabler_->IsInNoFingersDownForTesting());
-  generator_->set_current_location(gfx::Point(11, 12));
+  generator_->set_current_screen_location(gfx::Point(11, 12));
   generator_->PressTouchId(1);
 
-  generator_->set_current_location(gfx::Point(22, 34));
+  generator_->set_current_screen_location(gfx::Point(22, 34));
   generator_->PressTouchId(2);
 
   EXPECT_TRUE(enabler_->IsInTwoFingersDownForTesting());
@@ -144,10 +144,10 @@
 
 TEST_F(TouchAccessibilityEnablerTest, PlaysProgressSound) {
   EXPECT_TRUE(enabler_->IsInNoFingersDownForTesting());
-  generator_->set_current_location(gfx::Point(11, 12));
+  generator_->set_current_screen_location(gfx::Point(11, 12));
   generator_->PressTouchId(1);
 
-  generator_->set_current_location(gfx::Point(22, 34));
+  generator_->set_current_screen_location(gfx::Point(22, 34));
   generator_->PressTouchId(2);
 
   EXPECT_TRUE(enabler_->IsInTwoFingersDownForTesting());
@@ -163,10 +163,10 @@
 
 TEST_F(TouchAccessibilityEnablerTest, TogglesSpokenFeedback) {
   EXPECT_TRUE(enabler_->IsInNoFingersDownForTesting());
-  generator_->set_current_location(gfx::Point(11, 12));
+  generator_->set_current_screen_location(gfx::Point(11, 12));
   generator_->PressTouchId(1);
 
-  generator_->set_current_location(gfx::Point(22, 34));
+  generator_->set_current_screen_location(gfx::Point(22, 34));
   generator_->PressTouchId(2);
 
   EXPECT_TRUE(enabler_->IsInTwoFingersDownForTesting());
@@ -186,17 +186,17 @@
 
 TEST_F(TouchAccessibilityEnablerTest, ThreeFingersCancelsDetection) {
   EXPECT_TRUE(enabler_->IsInNoFingersDownForTesting());
-  generator_->set_current_location(gfx::Point(11, 12));
+  generator_->set_current_screen_location(gfx::Point(11, 12));
   generator_->PressTouchId(1);
 
-  generator_->set_current_location(gfx::Point(22, 34));
+  generator_->set_current_screen_location(gfx::Point(22, 34));
   generator_->PressTouchId(2);
 
   EXPECT_TRUE(enabler_->IsInTwoFingersDownForTesting());
   EXPECT_TRUE(delegate_.started());
   EXPECT_FALSE(delegate_.stopped());
 
-  generator_->set_current_location(gfx::Point(33, 56));
+  generator_->set_current_screen_location(gfx::Point(33, 56));
   generator_->PressTouchId(3);
 
   EXPECT_TRUE(enabler_->IsInWaitForNoFingersForTesting());
@@ -206,7 +206,7 @@
 
 TEST_F(TouchAccessibilityEnablerTest, MovingFingerPastSlopCancelsDetection) {
   EXPECT_TRUE(enabler_->IsInNoFingersDownForTesting());
-  generator_->set_current_location(gfx::Point(11, 12));
+  generator_->set_current_screen_location(gfx::Point(11, 12));
   generator_->PressTouch();
 
   int slop = gesture_detector_config_.double_tap_slop;
diff --git a/ash/accessibility/touch_exploration_controller_unittest.cc b/ash/accessibility/touch_exploration_controller_unittest.cc
index fc013a51..c6c97ff 100644
--- a/ash/accessibility/touch_exploration_controller_unittest.cc
+++ b/ash/accessibility/touch_exploration_controller_unittest.cc
@@ -434,7 +434,7 @@
 
   SwitchTouchExplorationMode(true);
   EXPECT_FALSE(IsInTouchToMouseMode());
-  generator_->set_current_location(gfx::Point(11, 12));
+  generator_->set_current_screen_location(gfx::Point(11, 12));
   generator_->PressTouch();
   generator_->MoveTouch(gfx::Point(11 + half_slop, 12));
   EXPECT_FALSE(IsInTouchToMouseMode());
@@ -448,7 +448,7 @@
 TEST_F(TouchExplorationTest, OneFingerTap) {
   SwitchTouchExplorationMode(true);
   gfx::Point location(11, 12);
-  generator_->set_current_location(location);
+  generator_->set_current_screen_location(location);
   generator_->PressTouch();
   generator_->ReleaseTouch();
   AdvanceSimulatedTimePastTapDelay();
@@ -468,7 +468,7 @@
 
   gfx::Point location_start(11, 12);
   gfx::Point location_end(13, 14);
-  generator_->set_current_location(location_start);
+  generator_->set_current_screen_location(location_start);
   generator_->PressTouch();
   AdvanceSimulatedTimePastTapDelay();
   generator_->MoveTouch(location_end);
@@ -601,14 +601,14 @@
 
   // Send a tap at location1.
   gfx::Point location0(11, 12);
-  generator_->set_current_location(location0);
+  generator_->set_current_screen_location(location0);
   generator_->PressTouch();
   generator_->ReleaseTouch();
 
   // Send a tap at location2, after the double-tap timeout, but before the
   // timer fires.
   gfx::Point location1(33, 34);
-  generator_->set_current_location(location1);
+  generator_->set_current_screen_location(location1);
   simulated_clock_.Advance(base::TimeDelta::FromMilliseconds(301));
   generator_->PressTouch();
   generator_->ReleaseTouch();
@@ -633,7 +633,7 @@
 
   // Tap at one location, and get a mouse move event.
   gfx::Point tap_location(51, 52);
-  generator_->set_current_location(tap_location);
+  generator_->set_current_screen_location(tap_location);
   generator_->PressTouchId(1);
   generator_->ReleaseTouchId(1);
   AdvanceSimulatedTimePastTapDelay();
@@ -651,7 +651,7 @@
   // a single touch press and release at the location of the tap,
   // not at the location of the double-tap.
   gfx::Point double_tap_location(33, 34);
-  generator_->set_current_location(double_tap_location);
+  generator_->set_current_screen_location(double_tap_location);
   generator_->PressTouch();
   generator_->ReleaseTouch();
   generator_->PressTouch();
@@ -675,7 +675,7 @@
 
   // Tap at one location, and get a mouse move event.
   gfx::Point tap_location(51, 52);
-  generator_->set_current_location(tap_location);
+  generator_->set_current_screen_location(tap_location);
   generator_->PressTouchId(1);
   generator_->ReleaseTouchId(1);
   AdvanceSimulatedTimePastTapDelay();
@@ -692,7 +692,7 @@
 
   // The press of the second tap happens in time, but the release does not.
   gfx::Point double_tap_location(33, 34);
-  generator_->set_current_location(double_tap_location);
+  generator_->set_current_screen_location(double_tap_location);
   generator_->PressTouch();
   generator_->ReleaseTouch();
   simulated_clock_.Advance(gesture_detector_config_.double_tap_timeout -
@@ -714,7 +714,7 @@
 
   // Tap at one location, and get a mouse move event.
   gfx::Point tap_location(51, 52);
-  generator_->set_current_location(tap_location);
+  generator_->set_current_screen_location(tap_location);
   generator_->PressTouchId(1);
   generator_->ReleaseTouchId(1);
   AdvanceSimulatedTimePastTapDelay();
@@ -733,7 +733,7 @@
   // Now double-tap at a different location. This should result in
   // a click gesture.
   gfx::Point double_tap_location(33, 34);
-  generator_->set_current_location(double_tap_location);
+  generator_->set_current_screen_location(double_tap_location);
   generator_->PressTouch();
   generator_->ReleaseTouch();
   generator_->PressTouch();
@@ -753,7 +753,7 @@
 
   // Tap at one location, and get a mouse move event.
   gfx::Point tap_location(11, 12);
-  generator_->set_current_location(tap_location);
+  generator_->set_current_screen_location(tap_location);
   generator_->PressTouch();
   generator_->ReleaseTouch();
   AdvanceSimulatedTimePastTapDelay();
@@ -771,11 +771,11 @@
   // This should result in a single touch press at the location of the tap,
   // not at the location of the double-tap.
   gfx::Point first_tap_location(13, 14);
-  generator_->set_current_location(first_tap_location);
+  generator_->set_current_screen_location(first_tap_location);
   generator_->PressTouchId(1);
   generator_->ReleaseTouchId(1);
   gfx::Point second_tap_location(15, 16);
-  generator_->set_current_location(second_tap_location);
+  generator_->set_current_screen_location(second_tap_location);
   generator_->PressTouchId(1);
   // Advance to the finger passing through.
   AdvanceSimulatedTimePastTapDelay();
@@ -843,7 +843,7 @@
   SwitchTouchExplorationMode(true);
   // Tap at one location, and get a mouse move event.
   gfx::Point tap_location(11, 12);
-  generator_->set_current_location(tap_location);
+  generator_->set_current_screen_location(tap_location);
   generator_->PressTouch();
   generator_->ReleaseTouch();
   AdvanceSimulatedTimePastTapDelay();
@@ -861,11 +861,11 @@
   // at the location of the tap, not at the location of the double-tap.
   // There should be a time delay between the touch press and release.
   gfx::Point first_tap_location(33, 34);
-  generator_->set_current_location(first_tap_location);
+  generator_->set_current_screen_location(first_tap_location);
   generator_->PressTouch();
   generator_->ReleaseTouch();
   gfx::Point second_tap_location(23, 24);
-  generator_->set_current_location(second_tap_location);
+  generator_->set_current_screen_location(second_tap_location);
   generator_->PressTouch();
   // Advance to the finger passing through, and then to the longpress timeout.
   AdvanceSimulatedTimePastTapDelay();
@@ -894,7 +894,7 @@
 
   // Tap once to simulate a mouse moved event.
   gfx::Point initial_location(11, 12);
-  generator_->set_current_location(initial_location);
+  generator_->set_current_screen_location(initial_location);
   generator_->PressTouch();
   AdvanceSimulatedTimePastTapDelay();
   ClearCapturedEvents();
@@ -907,7 +907,7 @@
   // Allow time to pass within the grace period of releasing before
   // tapping again.
   gfx::Point final_location(33, 34);
-  generator_->set_current_location(final_location);
+  generator_->set_current_screen_location(final_location);
   simulated_clock_.Advance(base::TimeDelta::FromMilliseconds(250));
   generator_->PressTouch();
   generator_->ReleaseTouch();
@@ -936,7 +936,7 @@
   // touch exploration event. The double-tap should be discarded, and no events
   // should be generated at all.
   gfx::Point double_tap_location(33, 34);
-  generator_->set_current_location(double_tap_location);
+  generator_->set_current_screen_location(double_tap_location);
   generator_->PressTouch();
   generator_->ReleaseTouch();
   generator_->PressTouch();
@@ -1712,7 +1712,7 @@
 TEST_F(TouchExplorationTest, TwoFingerTap) {
   SwitchTouchExplorationMode(true);
 
-  generator_->set_current_location(gfx::Point(101, 102));
+  generator_->set_current_screen_location(gfx::Point(101, 102));
   generator_->PressTouchId(1);
   EXPECT_FALSE(IsInTwoFingerTapState());
 
@@ -1847,7 +1847,7 @@
 
   // Motion starting in exclusion bounds is passed-through unchanged.
   {
-    generator_->set_current_location(in_pt);
+    generator_->set_current_screen_location(in_pt);
     generator_->PressTouchId(0);
     AdvanceSimulatedTimePastPotentialTapDelay();
     generator_->MoveTouchId(out_mv_pt, 0);
@@ -1863,7 +1863,7 @@
 
   // Complete motion outside exclusion is rewritten.
   {
-    generator_->set_current_location(out_pt);
+    generator_->set_current_screen_location(out_pt);
     generator_->PressTouchId(0);
     AdvanceSimulatedTimePastTapDelay();
     generator_->MoveTouchId(out_mv_pt, 0);
@@ -1882,7 +1882,7 @@
   // events are discarded unless they end the motion.
   {
     // finger 0 down outside, moves inside.
-    generator_->set_current_location(out_pt);
+    generator_->set_current_screen_location(out_pt);
     generator_->PressTouchId(0);
     AdvanceSimulatedTimePastTapDelay();
     generator_->MoveTouchId(out_mv_pt, 0);
@@ -1894,7 +1894,7 @@
     ClearCapturedEvents();
 
     // finger 1 down inside, moves outside
-    generator_->set_current_location(in_pt);
+    generator_->set_current_screen_location(in_pt);
     generator_->PressTouchId(1);
     generator_->MoveTouchId(out_mv_pt, 1);
     generator_->ReleaseTouchId(1);
@@ -1924,7 +1924,7 @@
   // The user has to have previously selected something.
   SetTouchAccessibilityAnchorPoint(tap_location);
 
-  generator_->set_current_location(tap_location);
+  generator_->set_current_screen_location(tap_location);
   generator_->PressTouchId(1);
   generator_->ReleaseTouchId(1);
   AdvanceSimulatedTimePastTapDelay();
@@ -1938,7 +1938,7 @@
 
   gfx::Point out_tap_location(tap_location.x(), lift_activation.bottom() + 20);
   SetTouchAccessibilityAnchorPoint(out_tap_location);
-  generator_->set_current_location(out_tap_location);
+  generator_->set_current_screen_location(out_tap_location);
   generator_->PressTouchId(1);
   generator_->ReleaseTouchId(1);
   AdvanceSimulatedTimePastTapDelay();
diff --git a/ash/app_launch_unittest.cc b/ash/app_launch_unittest.cc
index 138d22e..da7c53d 100644
--- a/ash/app_launch_unittest.cc
+++ b/ash/app_launch_unittest.cc
@@ -7,9 +7,12 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/run_loop.h"
-#include "services/service_manager/public/cpp/service_test.h"
+#include "base/test/scoped_task_environment.h"
+#include "services/service_manager/public/cpp/test/test_service.h"
+#include "services/service_manager/public/cpp/test/test_service_manager.h"
 #include "services/ws/public/mojom/constants.mojom.h"
 #include "services/ws/public/mojom/window_server_test.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/views/layout/layout_provider.h"
 
@@ -20,17 +23,24 @@
   std::move(callback).Run();
 }
 
-class AppLaunchTest : public service_manager::test::ServiceTest {
+class AppLaunchTest : public testing::Test {
  public:
-  AppLaunchTest() : ServiceTest("ash_unittests") {}
+  AppLaunchTest()
+      : test_service_(
+            test_service_manager_.RegisterTestInstance("ash_unittests")) {}
   ~AppLaunchTest() override = default;
 
+ protected:
+  service_manager::Connector* connector() { return test_service_.connector(); }
+
  private:
   void SetUp() override {
     base::CommandLine::ForCurrentProcess()->AppendSwitch("use-test-config");
-    ServiceTest::SetUp();
   }
 
+  base::test::ScopedTaskEnvironment task_environment_;
+  service_manager::TestServiceManager test_service_manager_;
+  service_manager::TestService test_service_;
   views::LayoutProvider layout_provider_;
 
   DISALLOW_COPY_AND_ASSIGN(AppLaunchTest);
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index f0805cf..41fe604 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -39,9 +39,18 @@
 
 namespace ash {
 
+namespace {
+
+bool IsTabletMode() {
+  return Shell::Get()
+      ->tablet_mode_controller()
+      ->IsTabletModeWindowManagerEnabled();
+}
+
+}  // namespace
+
 AppListControllerImpl::AppListControllerImpl()
-    : presenter_(std::make_unique<AppListPresenterDelegateImpl>(this)),
-      is_home_launcher_enabled_(app_list_features::IsHomeLauncherEnabled()) {
+    : presenter_(std::make_unique<AppListPresenterDelegateImpl>(this)) {
   model_.AddObserver(this);
 
   SessionController* session_controller = Shell::Get()->session_controller();
@@ -57,8 +66,7 @@
   Shell::Get()->AddShellObserver(this);
   keyboard::KeyboardController::Get()->AddObserver(this);
 
-  if (is_home_launcher_enabled_ &&
-      app_list_features::IsHomeLauncherGesturesEnabled()) {
+  if (app_list_features::IsHomeLauncherGesturesEnabled()) {
     home_launcher_gesture_handler_ =
         std::make_unique<HomeLauncherGestureHandler>(this);
   }
@@ -373,7 +381,7 @@
 
 void AppListControllerImpl::OnActiveUserPrefServiceChanged(
     PrefService* /* pref_service */) {
-  if (!IsHomeLauncherEnabledInTabletMode()) {
+  if (!IsTabletMode()) {
     DismissAppList();
     return;
   }
@@ -436,7 +444,7 @@
     int y_position_in_screen,
     float background_opacity) {
   // Avoid changing app list opacity and position when homecher is enabled.
-  if (IsHomeLauncherEnabledInTabletMode())
+  if (IsTabletMode())
     return;
   presenter_.UpdateYPositionAndOpacity(y_position_in_screen,
                                        background_opacity);
@@ -445,14 +453,14 @@
 void AppListControllerImpl::EndDragFromShelf(
     app_list::AppListViewState app_list_state) {
   // Avoid dragging app list when homecher is enabled.
-  if (IsHomeLauncherEnabledInTabletMode())
+  if (IsTabletMode())
     return;
   presenter_.EndDragFromShelf(app_list_state);
 }
 
 void AppListControllerImpl::ProcessMouseWheelEvent(
     const ui::MouseWheelEvent& event) {
-  presenter_.ProcessMouseWheelOffset(event.offset().y());
+  presenter_.ProcessMouseWheelOffset(event.offset());
 }
 
 void AppListControllerImpl::ToggleAppList(
@@ -485,7 +493,7 @@
 }
 
 void AppListControllerImpl::OnOverviewModeStarting() {
-  if (!IsHomeLauncherEnabledInTabletMode()) {
+  if (!IsTabletMode()) {
     DismissAppList();
     return;
   }
@@ -501,7 +509,7 @@
 }
 
 void AppListControllerImpl::OnOverviewModeEnding() {
-  if (!IsHomeLauncherEnabledInTabletMode())
+  if (!IsTabletMode())
     return;
 
   // Animate the launcher if overview mode is sliding out. Let
@@ -519,7 +527,7 @@
 
 void AppListControllerImpl::OnOverviewModeEndingAnimationComplete(
     bool canceled) {
-  if (!IsHomeLauncherEnabledInTabletMode() || canceled)
+  if (!IsTabletMode() || canceled)
     return;
 
   presenter_.ScheduleOverviewModeAnimation(/*start=*/false,
@@ -532,9 +540,6 @@
     presenter_.GetView()->OnTabletModeChanged(true);
   }
 
-  if (!is_home_launcher_enabled_)
-    return;
-
   // Show the app list if the tablet mode starts.
   ShowHomeLauncher();
 }
@@ -543,8 +548,6 @@
   if (IsVisible())
     presenter_.GetView()->OnTabletModeChanged(false);
 
-  if (!is_home_launcher_enabled_)
-    return;
   // Dismiss the app list if the tablet mode ends.
   DismissAppList();
 }
@@ -592,7 +595,7 @@
   // To avoid crashes, we must ensure that the Home Launcher shown status is as
   // expected if it's enabled and we're still in tablet mode.
   // https://crbug.com/900956.
-  const bool should_be_shown = IsHomeLauncherEnabledInTabletMode();
+  const bool should_be_shown = IsTabletMode();
   if (should_be_shown == GetTargetVisibility())
     return;
 
@@ -602,12 +605,6 @@
     DismissAppList();
 }
 
-bool AppListControllerImpl::IsHomeLauncherEnabledInTabletMode() const {
-  return is_home_launcher_enabled_ && Shell::Get()
-                                          ->tablet_mode_controller()
-                                          ->IsTabletModeWindowManagerEnabled();
-}
-
 void AppListControllerImpl::Back() {
   presenter_.GetView()->Back();
 }
@@ -616,7 +613,7 @@
     int64_t display_id,
     app_list::AppListShowSource show_source,
     base::TimeTicks event_time_stamp) {
-  if (!IsHomeLauncherEnabledInTabletMode()) {
+  if (!IsTabletMode()) {
     ToggleAppList(display_id, show_source, event_time_stamp);
     return;
   }
@@ -671,7 +668,7 @@
 // Methods of |client_|:
 
 void AppListControllerImpl::StartAssistant() {
-  if (!IsHomeLauncherEnabledInTabletMode())
+  if (!IsTabletMode())
     DismissAppList();
 
   ash::Shell::Get()->assistant_controller()->ui_controller()->ShowUi(
@@ -714,7 +711,7 @@
   if (client_)
     client_->OpenSearchResult(result_id, event_flags);
 
-  if (IsHomeLauncherEnabledInTabletMode() && presenter_.IsVisible())
+  if (IsTabletMode() && presenter_.IsVisible())
     presenter_.GetView()->CloseOpenedPage();
 }
 
@@ -770,7 +767,7 @@
   if (client_)
     client_->ActivateItem(id, event_flags);
 
-  if (IsHomeLauncherEnabledInTabletMode() && presenter_.IsVisible())
+  if (IsTabletMode() && presenter_.IsVisible())
     presenter_.GetView()->CloseOpenedPage();
 }
 
@@ -924,7 +921,7 @@
 }
 
 void AppListControllerImpl::UpdateHomeLauncherVisibility() {
-  if (!IsHomeLauncherEnabledInTabletMode() || !presenter_.GetWindow())
+  if (!IsTabletMode() || !presenter_.GetWindow())
     return;
 
   const bool in_overview =
@@ -946,8 +943,7 @@
 }
 
 int64_t AppListControllerImpl::GetDisplayIdToShowAppListOn() {
-  if (IsHomeLauncherEnabledInTabletMode() &&
-      !Shell::Get()->display_manager()->IsInUnifiedMode()) {
+  if (IsTabletMode() && !Shell::Get()->display_manager()->IsInUnifiedMode()) {
     return display::Display::HasInternalDisplay()
                ? display::Display::InternalDisplayId()
                : display::Screen::GetScreen()->GetPrimaryDisplay().id();
@@ -959,7 +955,7 @@
 }
 
 void AppListControllerImpl::ShowHomeLauncher() {
-  DCHECK(IsHomeLauncherEnabledInTabletMode());
+  DCHECK(IsTabletMode());
 
   if (!Shell::Get()->session_controller()->IsActiveUserSessionStarted())
     return;
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h
index fa3f52b..923adde 100644
--- a/ash/app_list/app_list_controller_impl.h
+++ b/ash/app_list/app_list_controller_impl.h
@@ -216,9 +216,6 @@
 
   bool onscreen_keyboard_shown() const { return onscreen_keyboard_shown_; }
 
-  // Returns true if the home launcher is enabled in tablet mode.
-  bool IsHomeLauncherEnabledInTabletMode() const;
-
   // Performs the 'back' action for the active page.
   void Back();
 
@@ -270,9 +267,6 @@
   // Whether the on-screen keyboard is shown.
   bool onscreen_keyboard_shown_ = false;
 
-  // Whether the home launcher feature is enabled.
-  const bool is_home_launcher_enabled_;
-
   // Each time overview mode is exited, set this variable based on whether
   // overview mode is sliding out, so the home launcher knows what to do when
   // overview mode exit animations are finished.
diff --git a/ash/app_list/app_list_interactive_uitest.cc b/ash/app_list/app_list_interactive_uitest.cc
index 7f9768a..bb3d90d 100644
--- a/ash/app_list/app_list_interactive_uitest.cc
+++ b/ash/app_list/app_list_interactive_uitest.cc
@@ -42,7 +42,7 @@
   EXPECT_FALSE(presenter->GetTargetVisibility());
   EXPECT_EQ(0u, app_list_container->children().size());
   EXPECT_FALSE(app_list_button->is_showing_app_list());
-  generator.set_current_location(
+  generator.set_current_screen_location(
       app_list_button->GetBoundsInScreen().CenterPoint());
   generator.ClickLeftButton();
   // Flush the mojo message from Ash to Chrome to show the app list.
diff --git a/ash/app_list/app_list_presenter_delegate_impl.cc b/ash/app_list/app_list_presenter_delegate_impl.cc
index bced2e6..4d5edf4 100644
--- a/ash/app_list/app_list_presenter_delegate_impl.cc
+++ b/ash/app_list/app_list_presenter_delegate_impl.cc
@@ -80,16 +80,15 @@
   aura::Window* root_window = Shell::GetRootWindowForDisplayId(display_id);
 
   app_list::AppListView::InitParams params;
+  const bool is_tablet_mode = IsTabletMode();
   aura::Window* parent_window =
       RootWindowController::ForWindow(root_window)
-          ->GetContainer(IsHomeLauncherEnabledInTabletMode()
+          ->GetContainer(is_tablet_mode
                              ? kShellWindowId_AppListTabletModeContainer
                              : kShellWindowId_AppListContainer);
   params.parent = parent_window;
   params.initial_apps_page = current_apps_page;
-  params.is_tablet_mode = Shell::Get()
-                              ->tablet_mode_controller()
-                              ->IsTabletModeWindowManagerEnabled();
+  params.is_tablet_mode = is_tablet_mode;
   params.is_side_shelf = IsSideShelf(root_window);
 
   view->Initialize(params);
@@ -148,8 +147,10 @@
                                         view_->is_fullscreen());
 }
 
-bool AppListPresenterDelegateImpl::IsHomeLauncherEnabledInTabletMode() {
-  return controller_->IsHomeLauncherEnabledInTabletMode();
+bool AppListPresenterDelegateImpl::IsTabletMode() const {
+  return Shell::Get()
+      ->tablet_mode_controller()
+      ->IsTabletModeWindowManagerEnabled();
 }
 
 app_list::AppListViewDelegate*
@@ -238,8 +239,7 @@
 
   aura::Window* window = view_->GetWidget()->GetNativeView()->parent();
   if (!window->Contains(target) && !presenter_->CloseOpenedPage() &&
-      !app_list::switches::ShouldNotDismissOnBlur() &&
-      !IsHomeLauncherEnabledInTabletMode()) {
+      !app_list::switches::ShouldNotDismissOnBlur() && !IsTabletMode()) {
     presenter_->Dismiss(event->time_stamp());
   }
 }
diff --git a/ash/app_list/app_list_presenter_delegate_impl.h b/ash/app_list/app_list_presenter_delegate_impl.h
index 1240e49..dafd3ec 100644
--- a/ash/app_list/app_list_presenter_delegate_impl.h
+++ b/ash/app_list/app_list_presenter_delegate_impl.h
@@ -57,7 +57,7 @@
       aura::Window* root_window) override;
   base::TimeDelta GetVisibilityAnimationDuration(aura::Window* root_window,
                                                  bool is_visible) override;
-  bool IsHomeLauncherEnabledInTabletMode() override;
+  bool IsTabletMode() const override;
   app_list::AppListViewDelegate* GetAppListViewDelegate() override;
   bool GetOnScreenKeyboardShown() override;
   aura::Window* GetRootWindowForDisplayId(int64_t display_id) override;
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index 3617781..58b040b 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -6,11 +6,18 @@
 #include <memory>
 
 #include "ash/app_list/model/app_list_view_state.h"
+#include "ash/app_list/pagination_model.h"
 #include "ash/app_list/presenter/app_list_presenter_impl.h"
 #include "ash/app_list/test/app_list_test_helper.h"
+#include "ash/app_list/test/app_list_test_model.h"
+#include "ash/app_list/test/app_list_test_view_delegate.h"
 #include "ash/app_list/views/app_list_main_view.h"
 #include "ash/app_list/views/app_list_view.h"
+#include "ash/app_list/views/apps_container_view.h"
+#include "ash/app_list/views/apps_grid_view.h"
+#include "ash/app_list/views/contents_view.h"
 #include "ash/app_list/views/search_box_view.h"
+#include "ash/app_list/views/test/apps_grid_view_test_api.h"
 #include "ash/public/cpp/app_list/app_list_config.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
 #include "ash/public/cpp/app_list/app_list_switches.h"
@@ -134,29 +141,107 @@
   DISALLOW_COPY_AND_ASSIGN(AppListPresenterDelegateTest);
 };
 
+// Used to test app_list behavior with a populated apps_grid
+class PopulatedAppListTest : public AshTestBase,
+                             public testing::WithParamInterface<bool> {
+ public:
+  PopulatedAppListTest() = default;
+  ~PopulatedAppListTest() override = default;
+
+  void SetUp() override {
+    app_list::AppListView::SetShortAnimationForTesting(true);
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        keyboard::switches::kEnableVirtualKeyboard);
+    AshTestBase::SetUp();
+
+    // Make the display big enough to hold the app list.
+    UpdateDisplay("1024x768");
+
+    app_list_test_delegate_ =
+        std::make_unique<app_list::test::AppListTestViewDelegate>();
+
+    app_list_test_model_ = app_list_test_delegate_->GetTestModel();
+  }
+
+  void TearDown() override {
+    AshTestBase::TearDown();
+    app_list::AppListView::SetShortAnimationForTesting(false);
+  }
+
+ protected:
+  void CreateAndOpenAppList() {
+    app_list_view_ = new app_list::AppListView(app_list_test_delegate_.get());
+    app_list::AppListView::InitParams params;
+    params.parent = CurrentContext();
+    app_list_view_->Initialize(params);
+  }
+
+  void InitializeAppsGrid() {
+    if (!app_list_view_)
+      CreateAndOpenAppList();
+    apps_grid_view_ = app_list_view_->app_list_main_view()
+                          ->contents_view()
+                          ->GetAppsContainerView()
+                          ->apps_grid_view();
+    apps_grid_test_api_ =
+        std::make_unique<app_list::test::AppsGridViewTestApi>(apps_grid_view_);
+  }
+  gfx::Rect GetItemRectOnCurrentPageAt(int row, int col) const {
+    DCHECK_GT(app_list_test_model_->top_level_item_list()->item_count(), 0u);
+    return apps_grid_test_api_->GetItemTileRectOnCurrentPageAt(row, col);
+  }
+
+  app_list::test::AppListTestModel* app_list_test_model_ = nullptr;
+  std::unique_ptr<app_list::test::AppsGridViewTestApi> apps_grid_test_api_;
+  std::unique_ptr<app_list::test::AppListTestViewDelegate>
+      app_list_test_delegate_;
+  app_list::AppListView* app_list_view_ = nullptr;  // Owned by native widget.
+  app_list::AppsGridView* apps_grid_view_ =
+      nullptr;  // Owned by |app_list_view_|.
+};
+
 // Instantiate the Boolean which is used to toggle mouse and touch events in
 // the parameterized tests.
 INSTANTIATE_TEST_CASE_P(, AppListPresenterDelegateTest, testing::Bool());
 
-// Test a variety of behaviors in tablet mode when home launcher is not enabled.
-class AppListPresenterDelegateNonHomeLauncherTest
-    : public AppListPresenterDelegateTest {
- public:
-  AppListPresenterDelegateNonHomeLauncherTest() = default;
-  ~AppListPresenterDelegateNonHomeLauncherTest() override = default;
+TEST_F(PopulatedAppListTest, TappingAppsGridClosesVirtualKeyboard) {
+  InitializeAppsGrid();
+  app_list_test_model_->PopulateApps(2);
+  gfx::Point between_apps = GetItemRectOnCurrentPageAt(0, 0).right_center();
+  gfx::Point empty_space = GetItemRectOnCurrentPageAt(0, 2).CenterPoint();
 
-  // testing::Test:
-  void SetUp() override {
-    scoped_feature_list_.InitWithFeatures(
-        {}, {app_list_features::kEnableHomeLauncher});
-    AppListPresenterDelegateTest::SetUp();
-  }
+  ui::GestureEvent tap_between(between_apps.x(), between_apps.y(), 0,
+                               base::TimeTicks(),
+                               ui::GestureEventDetails(ui::ET_GESTURE_TAP));
+  ui::GestureEvent tap_outside(empty_space.x(), empty_space.y(), 0,
+                               base::TimeTicks(),
+                               ui::GestureEventDetails(ui::ET_GESTURE_TAP));
 
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
+  // Manually show the virtual keyboard.
+  auto* const keyboard_controller = keyboard::KeyboardController::Get();
+  keyboard_controller->ShowKeyboard(true /* locked */);
+  ASSERT_TRUE(keyboard::WaitUntilShown());
 
-  DISALLOW_COPY_AND_ASSIGN(AppListPresenterDelegateNonHomeLauncherTest);
-};
+  // Touch the apps_grid outside of any apps
+  apps_grid_view_->OnGestureEvent(&tap_outside);
+  // Expect that the event is ignored here and allowed to propogate to app_list
+  EXPECT_FALSE(tap_outside.handled());
+  // Hit the app_list with the same event
+  app_list_view_->OnGestureEvent(&tap_outside);
+  // Expect that the event is handled and the keyboard is closed.
+  EXPECT_TRUE(tap_outside.handled());
+  EXPECT_FALSE(keyboard_controller->IsKeyboardVisible());
+
+  // Reshow the VKeyboard
+  keyboard_controller->ShowKeyboard(true);
+  ASSERT_TRUE(keyboard::WaitUntilShown());
+
+  // Touch the apps_grid between two apps
+  apps_grid_view_->OnGestureEvent(&tap_between);
+  // Expect the event to be handled in the grid, and the keyboard to be closed.
+  EXPECT_TRUE(tap_between.handled());
+  EXPECT_FALSE(keyboard_controller->IsKeyboardVisible());
+}
 
 // Tests that app list hides when focus moves to a normal window.
 TEST_F(AppListPresenterDelegateTest, HideOnFocusOut) {
@@ -349,36 +434,6 @@
 }
 
 // Tests that the app list state responds correctly to tablet mode being
-// enabled while the app list is being shown.
-TEST_F(AppListPresenterDelegateNonHomeLauncherTest,
-       PeekingToFullscreenWhenTabletModeIsActive) {
-  // TODO(newcomer): Investigate mash failures crbug.com/726838
-  GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
-  GetAppListTestHelper()->CheckState(app_list::AppListViewState::PEEKING);
-  // Enable tablet mode, this should force the app list to switch to the
-  // fullscreen equivalent of the current state.
-  EnableTabletMode(true);
-  GetAppListTestHelper()->CheckState(
-      app_list::AppListViewState::FULLSCREEN_ALL_APPS);
-  // Disable tablet mode, the state of the app list should not change.
-  EnableTabletMode(false);
-  GetAppListTestHelper()->CheckState(
-      app_list::AppListViewState::FULLSCREEN_ALL_APPS);
-  // Enter text in the searchbox, the app list should transition to fullscreen
-  // search.
-  ui::test::EventGenerator* generator = GetEventGenerator();
-  generator->PressKey(ui::KeyboardCode::VKEY_0, 0);
-  GetAppListTestHelper()->CheckState(
-      app_list::AppListViewState::FULLSCREEN_SEARCH);
-
-  // Delete the text in the searchbox, the app list should transition to
-  // fullscreen all apps. generator->PressKey(ui::KeyboardCode::VKEY_BACK, 0);
-  generator->PressKey(ui::KeyboardCode::VKEY_BACK, 0);
-  GetAppListTestHelper()->CheckState(
-      app_list::AppListViewState::FULLSCREEN_ALL_APPS);
-}
-
-// Tests that the app list state responds correctly to tablet mode being
 // enabled while the app list is being shown with half launcher.
 TEST_F(AppListPresenterDelegateTest, HalfToFullscreenWhenTabletModeIsActive) {
   // TODO(newcomer): Investigate mash failures crbug.com/726838
@@ -472,49 +527,6 @@
   GetAppListTestHelper()->CheckVisibility(false);
 }
 
-// Tests that the app list view handles drag properly in tablet mode.
-TEST_F(AppListPresenterDelegateNonHomeLauncherTest,
-       AppListViewDragHandlerTabletModeFromAllApps) {
-  // TODO(newcomer): Investigate mash failures crbug.com/726838
-  EnableTabletMode(true);
-  GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
-  GetAppListTestHelper()->CheckState(
-      app_list::AppListViewState::FULLSCREEN_ALL_APPS);
-
-  ui::test::EventGenerator* generator = GetEventGenerator();
-  // Drag down.
-  generator->GestureScrollSequence(gfx::Point(0, 0), gfx::Point(0, 720),
-                                   base::TimeDelta::FromMilliseconds(100), 10);
-  GetAppListTestHelper()->WaitUntilIdle();
-  GetAppListTestHelper()->CheckState(app_list::AppListViewState::CLOSED);
-  GetAppListTestHelper()->CheckVisibility(false);
-}
-
-// Tests that the state of the app list changes properly with drag input from
-// fullscreen search.
-TEST_F(AppListPresenterDelegateNonHomeLauncherTest,
-       AppListViewDragHandlerTabletModeFromSearch) {
-  // TODO(newcomer): Investigate mash failures crbug.com/726838
-  EnableTabletMode(true);
-  GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
-  GetAppListTestHelper()->CheckState(
-      app_list::AppListViewState::FULLSCREEN_ALL_APPS);
-
-  // Type in the search box to transition to |FULLSCREEN_SEARCH|.
-  ui::test::EventGenerator* generator = GetEventGenerator();
-  generator->PressKey(ui::KeyboardCode::VKEY_0, 0);
-  GetAppListTestHelper()->WaitUntilIdle();
-  GetAppListTestHelper()->CheckState(
-      app_list::AppListViewState::FULLSCREEN_SEARCH);
-
-  // Drag down, this should close the app list.
-  generator->GestureScrollSequence(gfx::Point(0, 0), gfx::Point(0, 720),
-                                   base::TimeDelta::FromMilliseconds(100), 10);
-  GetAppListTestHelper()->WaitUntilIdle();
-  GetAppListTestHelper()->CheckState(app_list::AppListViewState::CLOSED);
-  GetAppListTestHelper()->CheckVisibility(false);
-}
-
 // Tests that the bottom shelf background is hidden when the app list is shown
 // in laptop mode.
 TEST_F(AppListPresenterDelegateTest,
@@ -1005,7 +1017,7 @@
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
   GetAppListTestHelper()->CheckState(app_list::AppListViewState::PEEKING);
 
-  GetAppListView()->HandleScroll(-30, ui::ET_MOUSEWHEEL);
+  GetAppListView()->HandleScroll(gfx::Vector2d(0, -30), ui::ET_MOUSEWHEEL);
 
   GetAppListTestHelper()->CheckState(
       app_list::AppListViewState::FULLSCREEN_ALL_APPS);
@@ -1088,6 +1100,13 @@
   EXPECT_LE(0, view->GetWidget()->GetNativeView()->bounds().y());
 }
 
+// Tests that no crash occurs after an attempt to show app list in an invalid
+// display.
+TEST_F(AppListPresenterDelegateTest, ShowInInvalidDisplay) {
+  GetAppListTestHelper()->ShowAndRunLoop(display::kInvalidDisplayId);
+  GetAppListTestHelper()->CheckState(app_list::AppListViewState::CLOSED);
+}
+
 // Test a variety of behaviors for home launcher (app list in tablet mode).
 class AppListPresenterDelegateHomeLauncherTest
     : public AppListPresenterDelegateTest {
@@ -1098,9 +1117,7 @@
   // testing::Test:
   void SetUp() override {
     scoped_feature_list_.InitWithFeatures(
-        {app_list_features::kEnableHomeLauncher,
-         app_list_features::kEnableBackgroundBlur},
-        {});
+        {app_list_features::kEnableBackgroundBlur}, {});
     AppListPresenterDelegateTest::SetUp();
     GetAppListTestHelper()->WaitUntilIdle();
   }
diff --git a/ash/app_list/home_launcher_gesture_handler.cc b/ash/app_list/home_launcher_gesture_handler.cc
index 7575ff0..8724b66 100644
--- a/ash/app_list/home_launcher_gesture_handler.cc
+++ b/ash/app_list/home_launcher_gesture_handler.cc
@@ -52,6 +52,12 @@
 // this ratio.
 constexpr float kWidthRatio = 0.8f;
 
+bool IsTabletMode() {
+  return Shell::Get()
+      ->tablet_mode_controller()
+      ->IsTabletModeWindowManagerEnabled();
+}
+
 // Checks if |window| can be hidden or shown with a gesture.
 bool CanProcessWindow(aura::Window* window,
                       HomeLauncherGestureHandler::Mode mode) {
@@ -68,7 +74,7 @@
     return false;
   }
 
-  if (!Shell::Get()->app_list_controller()->IsHomeLauncherEnabledInTabletMode())
+  if (!IsTabletMode())
     return false;
 
   if (window->type() == aura::client::WINDOW_TYPE_POPUP)
@@ -621,10 +627,7 @@
     return false;
   }
 
-  if (Shell::Get()
-          ->app_list_controller()
-          ->IsHomeLauncherEnabledInTabletMode() &&
-      overview_active && !split_view_active) {
+  if (IsTabletMode() && overview_active && !split_view_active) {
     DCHECK_EQ(Mode::kSlideUpToShow, mode);
     window_ = nullptr;
     return true;
diff --git a/ash/app_list/model/search/search_model.cc b/ash/app_list/model/search/search_model.cc
index 09c3fe6..1710c6a 100644
--- a/ash/app_list/model/search/search_model.cc
+++ b/ash/app_list/model/search/search_model.cc
@@ -101,4 +101,14 @@
   PublishResults(std::vector<std::unique_ptr<SearchResult>>());
 }
 
+void SearchModel::DeleteResultById(const std::string& id) {
+  for (size_t i = 0; i < results_->item_count(); ++i) {
+    SearchResult* result = results_->GetItemAt(i);
+    if (result->id() == id) {
+      results_->DeleteAt(i);
+      break;
+    }
+  }
+}
+
 }  // namespace app_list
diff --git a/ash/app_list/model/search/search_model.h b/ash/app_list/model/search/search_model.h
index f4b0720..2216bd2b 100644
--- a/ash/app_list/model/search/search_model.h
+++ b/ash/app_list/model/search/search_model.h
@@ -61,6 +61,9 @@
   // Deletes all search results. This is used in profile switches.
   void DeleteAllResults();
 
+  // Delete result by the given id.
+  void DeleteResultById(const std::string& id);
+
  private:
   std::unique_ptr<SearchBoxModel> search_box_;
   std::unique_ptr<SearchResults> results_;
diff --git a/ash/app_list/model/search/search_result.h b/ash/app_list/model/search/search_result.h
index 9a3acae..f3f7760 100644
--- a/ash/app_list/model/search/search_result.h
+++ b/ash/app_list/model/search/search_result.h
@@ -83,6 +83,13 @@
   const base::Optional<GURL>& query_url() const { return metadata_->query_url; }
   void set_query_url(const GURL& url) { metadata_->query_url = url; }
 
+  const base::Optional<std::string>& equivalent_result_id() const {
+    return metadata_->equivalent_result_id;
+  }
+  void set_equivalent_result_id(const std::string& equivalent_result_id) {
+    metadata_->equivalent_result_id = equivalent_result_id;
+  }
+
   const std::string& id() const { return metadata_->id; }
 
   double display_score() const { return metadata_->display_score; }
diff --git a/ash/app_list/presenter/app_list_presenter_delegate.h b/ash/app_list/presenter/app_list_presenter_delegate.h
index c55b057..4626968 100644
--- a/ash/app_list/presenter/app_list_presenter_delegate.h
+++ b/ash/app_list/presenter/app_list_presenter_delegate.h
@@ -62,8 +62,8 @@
       aura::Window* root_window,
       bool is_visible) = 0;
 
-  // Returns true if the home launcher is enabled in tablet mode.
-  virtual bool IsHomeLauncherEnabledInTabletMode() = 0;
+  // Returns true if tablet mode is enabled.
+  virtual bool IsTabletMode() const = 0;
 
   // Returns the view delegate, which will be passed into views so that views
   // can get access to Ash.
diff --git a/ash/app_list/presenter/app_list_presenter_impl.cc b/ash/app_list/presenter/app_list_presenter_impl.cc
index e012041..d8970b7 100644
--- a/ash/app_list/presenter/app_list_presenter_impl.cc
+++ b/ash/app_list/presenter/app_list_presenter_impl.cc
@@ -25,6 +25,7 @@
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/display/screen.h"
 #include "ui/display/types/display_constants.h"
+#include "ui/gfx/geometry/vector2d_conversions.h"
 #include "ui/gfx/presentation_feedback.h"
 #include "ui/keyboard/keyboard_controller.h"
 #include "ui/views/widget/widget.h"
@@ -119,13 +120,17 @@
   if (is_visible_) {
     // Launcher is always visible on the internal display when home launcher is
     // enabled in tablet mode.
-    if (display_id != GetDisplayId() &&
-        !delegate_->IsHomeLauncherEnabledInTabletMode()) {
+    if (display_id != GetDisplayId() && !delegate_->IsTabletMode()) {
       Dismiss(event_time_stamp);
     }
     return;
   }
 
+  if (!delegate_->GetRootWindowForDisplayId(display_id)) {
+    LOG(ERROR) << "Root window does not exist for display: " << display_id;
+    return;
+  }
+
   is_visible_ = true;
   RequestPresentationTime(display_id, event_time_stamp);
 
@@ -217,9 +222,10 @@
   }
 }
 
-void AppListPresenterImpl::ProcessMouseWheelOffset(int y_scroll_offset) {
+void AppListPresenterImpl::ProcessMouseWheelOffset(
+    const gfx::Vector2d& scroll_offset_vector) {
   if (view_)
-    view_->HandleScroll(y_scroll_offset, ui::ET_MOUSEWHEEL);
+    view_->HandleScroll(scroll_offset_vector, ui::ET_MOUSEWHEEL);
 }
 
 void AppListPresenterImpl::UpdateYPositionAndOpacityForHomeLauncher(
@@ -367,8 +373,7 @@
     aura::Window* applist_container = applist_window->parent();
     if (applist_container->Contains(lost_focus) &&
         (!gained_focus || !applist_container->Contains(gained_focus)) &&
-        !switches::ShouldNotDismissOnBlur() &&
-        !delegate_->IsHomeLauncherEnabledInTabletMode()) {
+        !switches::ShouldNotDismissOnBlur() && !delegate_->IsTabletMode()) {
       Dismiss(base::TimeTicks());
     }
     if (applist_container->Contains(gained_focus) &&
diff --git a/ash/app_list/presenter/app_list_presenter_impl.h b/ash/app_list/presenter/app_list_presenter_impl.h
index a928079..0281476 100644
--- a/ash/app_list/presenter/app_list_presenter_impl.h
+++ b/ash/app_list/presenter/app_list_presenter_impl.h
@@ -90,7 +90,7 @@
   void EndDragFromShelf(AppListViewState app_list_state);
 
   // Passes a MouseWheelEvent from the shelf to the AppListView.
-  void ProcessMouseWheelOffset(int y_scroll_offset);
+  void ProcessMouseWheelOffset(const gfx::Vector2d& scroll_offset_vector);
 
   // Updates the y position and opacity of the full screen app list. The changes
   // are slightly different than UpdateYPositionAndOpacity. If |callback| is non
diff --git a/ash/app_list/presenter/app_list_presenter_impl_unittest.cc b/ash/app_list/presenter/app_list_presenter_impl_unittest.cc
index fba8e6a..561b333 100644
--- a/ash/app_list/presenter/app_list_presenter_impl_unittest.cc
+++ b/ash/app_list/presenter/app_list_presenter_impl_unittest.cc
@@ -67,10 +67,10 @@
                                                  bool is_visible) override {
     return base::TimeDelta::FromMilliseconds(0);
   }
-  bool IsHomeLauncherEnabledInTabletMode() override { return false; }
+  bool IsTabletMode() const override { return false; }
   bool GetOnScreenKeyboardShown() override { return false; }
   aura::Window* GetRootWindowForDisplayId(int64_t display_id) override {
-    return nullptr;
+    return container_->GetRootWindow();
   }
   void OnVisibilityChanged(bool visible, aura::Window* root_window) override {}
   void OnTargetVisibilityChanged(bool visible) override {}
@@ -217,7 +217,8 @@
   // Press the left mouse button on the menu window, it should not close the
   // app list nor the context menu on this pointer event.
   ui::test::EventGenerator menu_event_generator(menu->GetRootWindow());
-  menu_event_generator.set_current_location(menu->GetBoundsInScreen().origin());
+  menu_event_generator.set_current_screen_location(
+      menu->GetBoundsInScreen().origin());
   menu_event_generator.PressLeftButton();
 
   // Check that the window and the app list are still open.
diff --git a/ash/app_list/views/app_list_folder_view.cc b/ash/app_list/views/app_list_folder_view.cc
index 0fcd0db..0056a92 100644
--- a/ash/app_list/views/app_list_folder_view.cc
+++ b/ash/app_list/views/app_list_folder_view.cc
@@ -577,7 +577,8 @@
   // Calculate the folder icon's bounds relative to AppsContainerView.
   gfx::RectF rect(activated_folder_item_view->GetIconBounds());
   ConvertRectToTarget(activated_folder_item_view, container_view_, &rect);
-  gfx::Rect icon_bounds_in_container = gfx::ToEnclosingRect(rect);
+  gfx::Rect icon_bounds_in_container =
+      container_view_->GetMirroredRect(gfx::ToEnclosingRect(rect));
 
   // The opened folder view's center should try to overlap with the folder
   // item's center while it must fit within the bounds of AppsContainerView and
diff --git a/ash/app_list/views/app_list_item_view.cc b/ash/app_list/views/app_list_item_view.cc
index 3fad61f..6cdaed4 100644
--- a/ash/app_list/views/app_list_item_view.cc
+++ b/ash/app_list/views/app_list_item_view.cc
@@ -20,7 +20,6 @@
 #include "build/build_config.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/gfx/animation/throb_animation.h"
@@ -408,7 +407,12 @@
 
 void AppListItemView::SetItemName(const base::string16& display_name,
                                   const base::string16& full_name) {
-  title_->SetText(display_name);
+  if (is_folder_ && display_name.empty()) {
+    title_->SetText(ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
+        IDS_APP_LIST_FOLDER_NAME_PLACEHOLDER));
+  } else {
+    title_->SetText(display_name);
+  }
 
   tooltip_text_ = display_name == full_name ? base::string16() : full_name;
 
@@ -462,24 +466,19 @@
 
   if (!apps_grid_view_->IsSelectedView(this))
     apps_grid_view_->ClearAnySelectedView();
-  int run_types = views::MenuRunner::HAS_MNEMONICS;
 
-  if (source_type == ui::MENU_SOURCE_TOUCH)
+  int run_types = views::MenuRunner::HAS_MNEMONICS |
+                  views::MenuRunner::USE_TOUCHABLE_LAYOUT |
+                  views::MenuRunner::FIXED_ANCHOR |
+                  views::MenuRunner::CONTEXT_MENU;
+
+  if (source_type == ui::MENU_SOURCE_TOUCH && touch_dragging_)
     run_types |= views::MenuRunner::SEND_GESTURE_EVENTS_TO_OWNER;
 
-  views::MenuAnchorPosition anchor_position = views::MENU_ANCHOR_TOPLEFT;
-  gfx::Rect anchor_rect = gfx::Rect(point, gfx::Size());
-
-  if (::features::IsTouchableAppContextMenuEnabled()) {
-    run_types |= views::MenuRunner::USE_TOUCHABLE_LAYOUT |
-                 views::MenuRunner::FIXED_ANCHOR |
-                 views::MenuRunner::CONTEXT_MENU;
-    anchor_position = views::MENU_ANCHOR_BUBBLE_TOUCHABLE_RIGHT;
-    anchor_rect = apps_grid_view_->GetIdealBounds(this);
-    // Anchor the menu to the same rect that is used for selection highlight.
-    AdaptBoundsForSelectionHighlight(&anchor_rect);
-    views::View::ConvertRectToScreen(apps_grid_view_, &anchor_rect);
-  }
+  gfx::Rect anchor_rect = apps_grid_view_->GetIdealBounds(this);
+  // Anchor the menu to the same rect that is used for selection highlight.
+  AdaptBoundsForSelectionHighlight(&anchor_rect);
+  views::View::ConvertRectToScreen(apps_grid_view_, &anchor_rect);
 
   context_menu_ = std::make_unique<AppListMenuModelAdapter>(
       item_weak_->GetMetadata()->id, this, source_type, this,
@@ -487,13 +486,16 @@
       base::BindOnce(&AppListItemView::OnMenuClosed,
                      weak_ptr_factory_.GetWeakPtr()));
   context_menu_->Build(std::move(menu));
-  context_menu_->Run(anchor_rect, anchor_position, run_types);
+  context_menu_->Run(anchor_rect, views::MENU_ANCHOR_BUBBLE_TOUCHABLE_RIGHT,
+                     run_types);
   apps_grid_view_->SetSelectedView(this);
 }
 
 void AppListItemView::ShowContextMenuForView(views::View* source,
                                              const gfx::Point& point,
                                              ui::MenuSourceType source_type) {
+  if (context_menu_ && context_menu_->IsShowingMenu())
+    return;
   // Prevent multiple requests for context menus before the current request
   // completes. If a second request is sent before the first one can respond,
   // the Chrome side delegate will become unresponsive
diff --git a/ash/app_list/views/app_list_main_view.cc b/ash/app_list/views/app_list_main_view.cc
index 9246e92..2d584a2 100644
--- a/ash/app_list/views/app_list_main_view.cc
+++ b/ash/app_list/views/app_list_main_view.cc
@@ -89,7 +89,7 @@
       wm::GetActivationClient(
           app_list_view_->GetWidget()->GetNativeView()->GetRootWindow())
           ->GetActiveWindow();
-  if (app_list_view_->IsHomeLauncherEnabledInTabletMode() && active_window)
+  if (app_list_view_->is_tablet_mode() && active_window)
     GetWidget()->ShowInactive();
   else
     GetWidget()->Show();
diff --git a/ash/app_list/views/app_list_main_view_unittest.cc b/ash/app_list/views/app_list_main_view_unittest.cc
index a6ec296..b63267e 100644
--- a/ash/app_list/views/app_list_main_view_unittest.cc
+++ b/ash/app_list/views/app_list_main_view_unittest.cc
@@ -313,7 +313,8 @@
   GetContentsView()->SetActiveState(ash::AppListState::kStateApps);
   GetContentsView()->Layout();
 
-  generator.set_current_location(item->GetBoundsInScreen().CenterPoint());
+  generator.set_current_screen_location(
+      item->GetBoundsInScreen().CenterPoint());
   generator.PressTouch();
   EXPECT_TRUE(item->is_highlighted());
 
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index 39c06c6..71ee9ae 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -41,7 +41,6 @@
 #include "ui/display/screen.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/insets.h"
-#include "ui/gfx/geometry/vector2d_conversions.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/path.h"
 #include "ui/gfx/skia_util.h"
@@ -291,7 +290,6 @@
           std::make_unique<TransitionAnimationObserver>(this)),
       state_animation_metrics_reporter_(
           std::make_unique<StateAnimationMetricsReporter>()),
-      is_home_launcher_enabled_(app_list_features::IsHomeLauncherEnabled()),
       is_new_style_launcher_enabled_(
           app_list_features::IsNewStyleLauncherEnabled()),
       weak_ptr_factory_(this) {
@@ -340,7 +338,7 @@
 
   // Tablet mode is enabled before the app list is shown, so apply the changes
   // that should occur upon entering the tablet mode here.
-  if (IsHomeLauncherEnabledInTabletMode())
+  if (is_tablet_mode())
     OnTabletModeChanged(is_tablet_mode_);
 
   UMA_HISTOGRAM_TIMES(kAppListCreationTimeHistogram,
@@ -405,8 +403,7 @@
     case ui::VKEY_BROWSER_BACK:
       // If the ContentsView does not handle the back action, then this is the
       // top level, so we close the app list.
-      if (!app_list_main_view_->contents_view()->Back() &&
-          !IsHomeLauncherEnabledInTabletMode()) {
+      if (!app_list_main_view_->contents_view()->Back() && !is_tablet_mode()) {
         Dismiss();
       }
       break;
@@ -441,7 +438,7 @@
   app_list_background_shield_->SetBoundsRect(app_list_background_shield_bounds);
   app_list_background_shield_->UpdateCornerRadius(kAppListBackgroundRadius);
   if (is_background_blur_enabled_ && app_list_background_shield_mask_ &&
-      !IsHomeLauncherEnabledInTabletMode() &&
+      !is_tablet_mode() &&
       app_list_background_shield_->layer()->size() !=
           app_list_background_shield_mask_->layer()->size()) {
     // Update the blur mask for the |app_list_background_shield_| with same
@@ -494,8 +491,7 @@
 void AppListView::InitContents(int initial_apps_page) {
   // The shield view that colors/blurs the background of the app list and
   // makes it transparent.
-  bool use_background_blur =
-      is_background_blur_enabled_ && !IsHomeLauncherEnabledInTabletMode();
+  bool use_background_blur = is_background_blur_enabled_ && !is_tablet_mode();
   app_list_background_shield_ = new AppListBackgroundShieldView(
       use_background_blur ? ui::LAYER_SOLID_COLOR : ui::LAYER_TEXTURED);
   app_list_background_shield_->layer()->SetOpacity(
@@ -617,7 +613,7 @@
       (event->IsMouseEvent() &&
        event->AsMouseEvent()->IsOnlyRightMouseButton())) {
     // Don't show menus on empty areas of the AppListView in clamshell mode.
-    if (!IsHomeLauncherEnabledInTabletMode())
+    if (!is_tablet_mode())
       return;
 
     // Home launcher is shown on top of wallpaper with trasparent background. So
@@ -631,7 +627,7 @@
   }
 
   if (!search_box_view_->is_search_box_active()) {
-    if (!IsHomeLauncherEnabledInTabletMode())
+    if (!is_tablet_mode())
       Dismiss();
     return;
   }
@@ -975,8 +971,10 @@
 }
 
 void AppListView::OnScrollEvent(ui::ScrollEvent* event) {
-  if (!HandleScroll(event->y_offset(), event->type()))
+  if (!HandleScroll(gfx::Vector2d(event->x_offset(), event->y_offset()),
+                    event->type())) {
     return;
+  }
 
   event->SetHandled();
   event->StopPropagation();
@@ -989,8 +987,7 @@
       HandleClickOrTap(event);
       break;
     case ui::ET_MOUSEWHEEL:
-      if (HandleScroll(event->AsMouseWheelEvent()->offset().y(),
-                       ui::ET_MOUSEWHEEL))
+      if (HandleScroll(event->AsMouseWheelEvent()->offset(), ui::ET_MOUSEWHEEL))
         event->SetHandled();
       break;
     default:
@@ -1024,7 +1021,7 @@
       }
 
       // Avoid scrolling events for the app list in tablet mode.
-      if (is_side_shelf_ || IsHomeLauncherEnabledInTabletMode())
+      if (is_side_shelf_ || is_tablet_mode())
         return;
       // There may be multiple scroll begin events in one drag because the
       // relative location of the finger and widget is almost unchanged and
@@ -1046,7 +1043,7 @@
       }
 
       // Avoid scrolling events for the app list in tablet mode.
-      if (is_side_shelf_ || IsHomeLauncherEnabledInTabletMode())
+      if (is_side_shelf_ || is_tablet_mode())
         return;
       SetIsInDrag(true);
       last_fling_velocity_ = event->details().scroll_y();
@@ -1066,7 +1063,7 @@
       if (!is_in_drag_)
         break;
       // Avoid scrolling events for the app list in tablet mode.
-      if (is_side_shelf_ || IsHomeLauncherEnabledInTabletMode())
+      if (is_side_shelf_ || is_tablet_mode())
         return;
       SetIsInDrag(false);
       EndDrag(event->location());
@@ -1074,8 +1071,7 @@
       break;
     }
     case ui::ET_MOUSEWHEEL: {
-      if (HandleScroll(event->AsMouseWheelEvent()->offset().y(),
-                       ui::ET_MOUSEWHEEL))
+      if (HandleScroll(event->AsMouseWheelEvent()->offset(), ui::ET_MOUSEWHEEL))
         event->SetHandled();
       break;
     }
@@ -1094,49 +1090,38 @@
   search_model_->SetTabletMode(started);
   GetAppsContainerView()->OnTabletModeChanged(started);
 
-  if (is_home_launcher_enabled_) {
-    if (!started) {
-      Dismiss();
-      return;
-    }
-
-    if (is_in_drag_) {
-      SetIsInDrag(false);
-      UpdateChildViewsYPositionAndOpacity();
-    }
-
-    // Set fullscreen state. When current state is fullscreen, we still need to
-    // set it again because app list may be in dragging.
-    SetState(app_list_state_ == AppListViewState::HALF ||
-                     app_list_state_ == AppListViewState::FULLSCREEN_SEARCH
-                 ? AppListViewState::FULLSCREEN_SEARCH
-                 : AppListViewState::FULLSCREEN_ALL_APPS);
-
-    // Put app list window in corresponding container based on whether the
-    // tablet mode is enabled.
-    aura::Window* window = GetWidget()->GetNativeWindow();
-    aura::Window* root_window = window->GetRootWindow();
-    aura::Window* parent_window = root_window->GetChildById(
-        ash::kShellWindowId_AppListTabletModeContainer);
-    if (parent_window && !parent_window->Contains(window))
-      parent_window->AddChild(window);
-
-    // Update background opacity.
-    app_list_background_shield_->layer()->SetOpacity(0.f);
-
-    // Update background blur.
-    if (is_background_blur_enabled_)
-      app_list_background_shield_->layer()->SetBackgroundBlur(0);
-
+  if (!started) {
+    Dismiss();
     return;
   }
 
-  if (is_tablet_mode_ && !is_fullscreen()) {
-    // Set |app_list_state_| to a tablet mode friendly state.
-    SetState(app_list_state_ == AppListViewState::PEEKING
-                 ? AppListViewState::FULLSCREEN_ALL_APPS
-                 : AppListViewState::FULLSCREEN_SEARCH);
+  if (is_in_drag_) {
+    SetIsInDrag(false);
+    UpdateChildViewsYPositionAndOpacity();
   }
+
+  // Set fullscreen state. When current state is fullscreen, we still need to
+  // set it again because app list may be in dragging.
+  SetState(app_list_state_ == AppListViewState::HALF ||
+                   app_list_state_ == AppListViewState::FULLSCREEN_SEARCH
+               ? AppListViewState::FULLSCREEN_SEARCH
+               : AppListViewState::FULLSCREEN_ALL_APPS);
+
+  // Put app list window in corresponding container based on whether the
+  // tablet mode is enabled.
+  aura::Window* window = GetWidget()->GetNativeWindow();
+  aura::Window* root_window = window->GetRootWindow();
+  aura::Window* parent_window =
+      root_window->GetChildById(ash::kShellWindowId_AppListTabletModeContainer);
+  if (parent_window && !parent_window->Contains(window))
+    parent_window->AddChild(window);
+
+  // Update background opacity.
+  app_list_background_shield_->layer()->SetOpacity(0.f);
+
+  // Update background blur.
+  if (is_background_blur_enabled_)
+    app_list_background_shield_->layer()->SetBackgroundBlur(0);
 }
 
 void AppListView::OnWallpaperColorsChanged() {
@@ -1144,16 +1129,20 @@
   search_box_view_->OnWallpaperColorsChanged();
 }
 
-bool AppListView::HandleScroll(int offset, ui::EventType type) {
+bool AppListView::HandleScroll(const gfx::Vector2d& offset,
+                               ui::EventType type) {
   // Ignore 0-offset events to prevent spurious dismissal, see crbug.com/806338
   // The system generates 0-offset ET_SCROLL_FLING_CANCEL events during simple
   // touchpad mouse moves. Those may be passed via mojo APIs and handled here.
-  if (offset == 0 || is_in_drag() || ShouldIgnoreScrollEvents())
+  if ((offset.y() == 0 && offset.x() == 0) || is_in_drag() ||
+      ShouldIgnoreScrollEvents()) {
     return false;
+  }
 
   if (app_list_state_ != AppListViewState::PEEKING &&
-      app_list_state_ != AppListViewState::FULLSCREEN_ALL_APPS)
+      app_list_state_ != AppListViewState::FULLSCREEN_ALL_APPS) {
     return false;
+  }
 
   // Let the Apps grid view handle the event first in FULLSCREEN_ALL_APPS.
   if (app_list_state_ == AppListViewState::FULLSCREEN_ALL_APPS) {
@@ -1167,8 +1156,8 @@
   // If the event is a mousewheel event, the offset is always large enough,
   // otherwise the offset must be larger than the scroll threshold.
   if (type == ui::ET_MOUSEWHEEL ||
-      abs(offset) > kAppListMinScrollToSwitchStates) {
-    if (offset > 0 && !IsHomeLauncherEnabledInTabletMode()) {
+      abs(offset.y()) > kAppListMinScrollToSwitchStates) {
+    if (offset.y() > 0 && !is_tablet_mode()) {
       Dismiss();
     } else {
       if (app_list_state_ == AppListViewState::FULLSCREEN_ALL_APPS)
@@ -1421,10 +1410,6 @@
   return display_bounds.height() - display.work_area().y() + display_bounds.y();
 }
 
-bool AppListView::IsHomeLauncherEnabledInTabletMode() const {
-  return is_tablet_mode_ && is_home_launcher_enabled_;
-}
-
 void AppListView::UpdateChildViewsYPositionAndOpacity() {
   if (app_list_state_ == AppListViewState::CLOSED)
     return;
diff --git a/ash/app_list/views/app_list_view.h b/ash/app_list/views/app_list_view.h
index 454f4d2..f0a4b2da 100644
--- a/ash/app_list/views/app_list_view.h
+++ b/ash/app_list/views/app_list_view.h
@@ -149,7 +149,7 @@
   void OnWallpaperColorsChanged();
 
   // Handles scroll events from various sources.
-  bool HandleScroll(int offset, ui::EventType type);
+  bool HandleScroll(const gfx::Vector2d& offset, ui::EventType type);
 
   // Changes the app list state.
   void SetState(AppListViewState new_state);
@@ -242,12 +242,6 @@
     onscreen_keyboard_shown_ = onscreen_keyboard_shown;
   }
 
-  // Returns true if the home launcher is enabled in tablet mode.
-  bool IsHomeLauncherEnabledInTabletMode() const;
-
-  // Returns true if the home_launcher feature is enabled.
-  bool is_home_launcher_enabled() const { return is_home_launcher_enabled_; }
-
   views::View* GetAppListBackgroundShieldForTest();
 
  private:
@@ -416,9 +410,6 @@
   // Whether the on-screen keyboard is shown.
   bool onscreen_keyboard_shown_ = false;
 
-  // Whether the home launcher feature is enabled.
-  const bool is_home_launcher_enabled_;
-
   // True if new style launcher feature is enabled.
   const bool is_new_style_launcher_enabled_;
 
diff --git a/ash/app_list/views/app_list_view_unittest.cc b/ash/app_list/views/app_list_view_unittest.cc
index c790602..14d8924 100644
--- a/ash/app_list/views/app_list_view_unittest.cc
+++ b/ash/app_list/views/app_list_view_unittest.cc
@@ -518,8 +518,8 @@
               focused_view());
 
     // Clean up
-    textfield->SetText(base::UTF8ToUTF16(""));
     textfield->RequestFocus();
+    textfield->SetText(base::UTF8ToUTF16(""));
   }
 
   AppListView* app_list_view() { return view_; }
@@ -610,42 +610,6 @@
                         AppListViewFocusTest,
                         testing::ValuesIn(kAppListViewFocusTestParams));
 
-// Test behaviors in tablet mode when homcher launcher feature is enabled.
-class AppListViewHomeLauncherTest : public AppListViewTest {
- public:
-  AppListViewHomeLauncherTest() = default;
-  ~AppListViewHomeLauncherTest() override = default;
-
-  void SetUp() override {
-    scoped_feature_list_.InitAndEnableFeature(
-        app_list_features::kEnableHomeLauncher);
-    AppListViewTest::SetUp();
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(AppListViewHomeLauncherTest);
-};
-
-// Test behaviors in tablet mode when homcher launcher feature is not enabled.
-class AppListViewNonHomeLauncherTest : public AppListViewTest {
- public:
-  AppListViewNonHomeLauncherTest() = default;
-  ~AppListViewNonHomeLauncherTest() override = default;
-
-  void SetUp() override {
-    scoped_feature_list_.InitAndDisableFeature(
-        app_list_features::kEnableHomeLauncher);
-    AppListViewTest::SetUp();
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(AppListViewNonHomeLauncherTest);
-};
-
 }  // namespace
 
 // Tests that the initial focus is on search box.
@@ -1432,7 +1396,7 @@
   delegate_->GetTestModel()->PopulateApps(kInitialItems);
   Show();
 
-  view_->HandleScroll(-30, ui::ET_MOUSEWHEEL);
+  view_->HandleScroll(gfx::Vector2d(0, -30), ui::ET_MOUSEWHEEL);
   EXPECT_EQ(AppListViewState::FULLSCREEN_ALL_APPS, view_->app_list_state());
 }
 
@@ -1441,7 +1405,7 @@
   delegate_->GetTestModel()->PopulateApps(kInitialItems);
   Show();
 
-  view_->HandleScroll(-30, ui::ET_SCROLL);
+  view_->HandleScroll(gfx::Vector2d(0, -30), ui::ET_SCROLL);
   EXPECT_EQ(AppListViewState::FULLSCREEN_ALL_APPS, view_->app_list_state());
 }
 
@@ -1632,39 +1596,6 @@
   ASSERT_EQ(AppListViewState::CLOSED, view_->app_list_state());
 }
 
-// Tests that escape works after leaving tablet mode from search.
-TEST_F(AppListViewNonHomeLauncherTest, LeaveTabletModeEscapeKeyToFullscreen) {
-  // Put into fullscreen using tablet mode.
-  Initialize(0, true, false);
-  views::Textfield* search_box =
-      view_->app_list_main_view()->search_box_view()->search_box();
-
-  Show();
-  search_box->SetText(base::string16());
-  search_box->InsertText(base::UTF8ToUTF16("nothing"));
-  view_->OnTabletModeChanged(false);
-  view_->AcceleratorPressed(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
-
-  ASSERT_EQ(AppListViewState::FULLSCREEN_ALL_APPS, view_->app_list_state());
-}
-
-// Tests that escape twice closes after leaving tablet mode from search.
-TEST_F(AppListViewNonHomeLauncherTest, LeaveTabletModeEscapeKeyTwiceToClosed) {
-  // Put into fullscreen using tablet mode.
-  Initialize(0, true, false);
-  views::Textfield* search_box =
-      view_->app_list_main_view()->search_box_view()->search_box();
-
-  Show();
-  search_box->SetText(base::string16());
-  search_box->InsertText(base::UTF8ToUTF16("nothing"));
-  view_->OnTabletModeChanged(false);
-  view_->AcceleratorPressed(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
-  view_->AcceleratorPressed(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
-
-  ASSERT_EQ(AppListViewState::CLOSED, view_->app_list_state());
-}
-
 // Tests that opening in peeking mode sets the correct height.
 TEST_P(AppListViewTest, OpenInPeekingCorrectHeight) {
   Initialize(0, false, false);
@@ -2074,34 +2005,8 @@
   EXPECT_TRUE(view_->GetWidget()->IsVisible());
 }
 
-// Tests that pressing escape when in tablet mode closes the app list.
-TEST_F(AppListViewNonHomeLauncherTest, EscapeKeyTabletModeFullscreenToClosed) {
-  // Put into fullscreen by using tablet mode.
-  Initialize(0, true, false);
-
-  Show();
-  view_->AcceleratorPressed(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
-
-  ASSERT_EQ(AppListViewState::CLOSED, view_->app_list_state());
-}
-
-// Tests that leaving tablet mode when in tablet search causes no change.
-TEST_F(AppListViewNonHomeLauncherTest, LeaveTabletModeNoChange) {
-  // Put into fullscreen using tablet mode.
-  Initialize(0, true, false);
-  views::Textfield* search_box =
-      view_->app_list_main_view()->search_box_view()->search_box();
-
-  Show();
-  search_box->SetText(base::string16());
-  search_box->InsertText(base::UTF8ToUTF16("something"));
-  view_->OnTabletModeChanged(false);
-
-  ASSERT_EQ(AppListViewState::FULLSCREEN_SEARCH, view_->app_list_state());
-}
-
 // Tests the back action in home launcher.
-TEST_F(AppListViewHomeLauncherTest, BackAction) {
+TEST_F(AppListViewTest, BackAction) {
   // Put into fullscreen using tablet mode.
   Initialize(0, true, false);
 
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index 9ca94f4..896e89f 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -1044,11 +1044,13 @@
   // If a tap/long-press occurs within a valid tile, it is usually a mistake and
   // should not close the launcher in clamshell mode. Otherwise, we should let
   // those events pass to the ancestor views.
-  if (!contents_view_->app_list_view()->IsHomeLauncherEnabledInTabletMode() &&
+  if (!contents_view_->app_list_view()->is_tablet_mode() &&
       (event->type() == ui::ET_GESTURE_TAP ||
        event->type() == ui::ET_GESTURE_LONG_PRESS)) {
-    if (EventIsBetweenOccupiedTiles(event))
+    if (EventIsBetweenOccupiedTiles(event)) {
+      contents_view_->app_list_view()->CloseKeyboardIfVisible();
       event->SetHandled();
+    }
     return;
   }
 
@@ -1061,7 +1063,7 @@
   // If the event is a scroll down in clamshell mode on the first page, don't
   // let |pagination_controller_| handle it. Unless it occurs in a folder.
   if (!folder_delegate_ && event->type() == ui::ET_GESTURE_SCROLL_BEGIN &&
-      !contents_view_->app_list_view()->IsHomeLauncherEnabledInTabletMode() &&
+      !contents_view_->app_list_view()->is_tablet_mode() &&
       pagination_model_.selected_page() == 0 &&
       event->details().scroll_y_hint() > 0) {
     return;
@@ -1077,8 +1079,7 @@
 }
 
 bool AppsGridView::OnMousePressed(const ui::MouseEvent& event) {
-  return !contents_view_->app_list_view()
-              ->IsHomeLauncherEnabledInTabletMode() &&
+  return !contents_view_->app_list_view()->is_tablet_mode() &&
          event.IsLeftMouseButton() && EventIsBetweenOccupiedTiles(&event);
 }
 
@@ -1185,16 +1186,15 @@
   return view;
 }
 
-bool AppsGridView::HandleScroll(int offset, ui::EventType type) {
+bool AppsGridView::HandleScroll(const gfx::Vector2d& offset,
+                                ui::EventType type) {
   // Bail on STATE_START or no apps page to make PaginationModel happy.
   if (contents_view_->GetActiveState() == ash::AppListState::kStateStart ||
       pagination_model_.total_pages() <= 0) {
     return false;
   }
 
-  const gfx::Vector2dF scroll_offset_vector(0, offset);
-  return pagination_controller_->OnScroll(
-      gfx::ToFlooredVector2d(scroll_offset_vector), type);
+  return pagination_controller_->OnScroll(offset, type);
 }
 
 void AppsGridView::EnsureViewVisible(const GridIndex& index) {
@@ -2030,9 +2030,10 @@
   }
 }
 
-bool AppsGridView::HandleScrollFromAppListView(int offset, ui::EventType type) {
+bool AppsGridView::HandleScrollFromAppListView(const gfx::Vector2d& offset,
+                                               ui::EventType type) {
   // Scroll up at first page in top level apps grid should close the launcher.
-  if (!folder_delegate_ && offset > 0 &&
+  if (!folder_delegate_ && offset.y() > 0 &&
       !pagination_model()->IsValidPageRelative(-1)) {
     return false;
   }
@@ -3026,7 +3027,7 @@
     const gfx::Rect& source_bounds) {
   // Calculate target bounds of dragged item.
   gfx::Rect target_bounds =
-      GetTargetIconRectInFolder(drag_item, folder_item_view);
+      GetMirroredRect(GetTargetIconRectInFolder(drag_item, folder_item_view));
 
   // Update folder icon.
   AppListFolderItem* folder_item =
diff --git a/ash/app_list/views/apps_grid_view.h b/ash/app_list/views/apps_grid_view.h
index e5bf72f..8915971 100644
--- a/ash/app_list/views/apps_grid_view.h
+++ b/ash/app_list/views/apps_grid_view.h
@@ -253,7 +253,8 @@
 
   // Passes scroll information from AppListView to the PaginationController,
   // returns true if this scroll would change pages.
-  bool HandleScrollFromAppListView(int offset, ui::EventType type);
+  bool HandleScrollFromAppListView(const gfx::Vector2d& offset,
+                                   ui::EventType type);
 
   // Returns the first app list item view in the selected page in the folder.
   AppListItemView* GetCurrentPageFirstItemViewInFolder();
@@ -332,7 +333,7 @@
   AppListItemView* CreateViewForItemAtIndex(size_t index);
 
   // Returns true if the event was handled by the pagination controller.
-  bool HandleScroll(int offset, ui::EventType type);
+  bool HandleScroll(const gfx::Vector2d& offset, ui::EventType type);
 
   // Ensures the view is visible. Note that if there is a running page
   // transition, this does nothing.
diff --git a/ash/app_list/views/apps_grid_view_unittest.cc b/ash/app_list/views/apps_grid_view_unittest.cc
index e76366a..0e7d587 100644
--- a/ash/app_list/views/apps_grid_view_unittest.cc
+++ b/ash/app_list/views/apps_grid_view_unittest.cc
@@ -592,6 +592,29 @@
   EXPECT_FALSE(apps_container_view->IsInFolderView());
 }
 
+TEST_F(AppsGridViewTest, TapsBetweenAppsWontCloseAppList) {
+  model_->PopulateApps(2);
+  gfx::Point between_apps = GetItemRectOnCurrentPageAt(0, 0).right_center();
+  gfx::Point empty_space = GetItemRectOnCurrentPageAt(0, 2).CenterPoint();
+
+  ui::GestureEvent tap_between(between_apps.x(), between_apps.y(), 0,
+                               base::TimeTicks(),
+                               ui::GestureEventDetails(ui::ET_GESTURE_TAP));
+  ui::GestureEvent tap_outside(empty_space.x(), empty_space.y(), 0,
+                               base::TimeTicks(),
+                               ui::GestureEventDetails(ui::ET_GESTURE_TAP));
+
+  // Taps between apps should be handled to prevent them from going into
+  // app_list
+  apps_grid_view_->OnGestureEvent(&tap_between);
+  EXPECT_TRUE(tap_between.handled());
+
+  // Taps outside of occupied tiles should not be handled, that they may close
+  // the app_list
+  apps_grid_view_->OnGestureEvent(&tap_outside);
+  EXPECT_FALSE(tap_outside.handled());
+}
+
 TEST_F(AppsGridViewTest, PageResetAfterOpenFolder) {
   const size_t kTotalItems = kMaxFolderPages * kMaxFolderItemsPerPage;
   model_->CreateAndPopulateFolderWithApps(kTotalItems);
diff --git a/ash/app_list/views/contents_view.cc b/ash/app_list/views/contents_view.cc
index f301603..a980d8d 100644
--- a/ash/app_list/views/contents_view.cc
+++ b/ash/app_list/views/contents_view.cc
@@ -311,7 +311,7 @@
 
   // Don't show |expand_arrow_view_| when the home launcher gestures are
   // disabled in tablet mode.
-  if (app_list_view_->IsHomeLauncherEnabledInTabletMode() &&
+  if (app_list_view_->is_tablet_mode() &&
       !app_list_features::IsHomeLauncherGesturesEnabled()) {
     expand_arrow_view_->layer()->SetOpacity(0);
     return;
@@ -436,7 +436,7 @@
           GetAppsContainerView()->apps_grid_view()->pagination_model();
       if (GetAppsContainerView()->IsInFolderView()) {
         GetAppsContainerView()->app_list_folder_view()->CloseFolderPage();
-      } else if (app_list_view_->IsHomeLauncherEnabledInTabletMode() &&
+      } else if (app_list_view_->is_tablet_mode() &&
                  pagination_model->total_pages() > 0 &&
                  pagination_model->selected_page() > 0) {
         pagination_model->SelectPage(
diff --git a/ash/app_list/views/folder_header_view.cc b/ash/app_list/views/folder_header_view.cc
index 7665d4f..488b390 100644
--- a/ash/app_list/views/folder_header_view.cc
+++ b/ash/app_list/views/folder_header_view.cc
@@ -43,6 +43,7 @@
   ~FolderNameView() override = default;
 
   void OnFocus() override {
+    SetText(base::UTF8ToUTF16(folder_header_view_->folder_item_->name()));
     SelectAll(false);
     Textfield::OnFocus();
   }
diff --git a/ash/app_list/views/search_box_view.cc b/ash/app_list/views/search_box_view.cc
index a4ba681c..41fa4b3 100644
--- a/ash/app_list/views/search_box_view.cc
+++ b/ash/app_list/views/search_box_view.cc
@@ -28,6 +28,7 @@
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "chromeos/chromeos_switches.h"
+#include "ui/base/ime/composition_text.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/chromeos/search_box/search_box_constants.h"
@@ -122,8 +123,7 @@
 void SearchBoxView::HandleSearchBoxEvent(ui::LocatedEvent* located_event) {
   if (located_event->type() == ui::ET_MOUSEWHEEL) {
     if (!app_list_view_->HandleScroll(
-            located_event->AsMouseWheelEvent()->offset().y(),
-            ui::ET_MOUSEWHEEL)) {
+            located_event->AsMouseWheelEvent()->offset(), ui::ET_MOUSEWHEEL)) {
       return;
     }
   }
@@ -362,7 +362,7 @@
 }
 
 void SearchBoxView::ProcessAutocomplete() {
-  if (!is_app_list_search_autocomplete_enabled_)
+  if (!ShouldProcessAutocomplete())
     return;
 
   SearchResult* const first_visible_result =
@@ -426,25 +426,14 @@
 }
 
 void SearchBoxView::AcceptAutocompleteText() {
-  if (!is_app_list_search_autocomplete_enabled_)
+  if (!ShouldProcessAutocomplete())
     return;
 
-  if (HasAutocompleteText())
-    ContentsChanged(search_box(), search_box()->text());
-}
-
-void SearchBoxView::AcceptOneCharInAutocompleteText() {
-  if (!is_app_list_search_autocomplete_enabled_)
-    return;
-
-  highlight_range_.set_start(highlight_range_.start() + 1);
-  highlight_range_.set_end(search_box()->text().length());
-  const base::string16 original_text = search_box()->text();
-  search_box()->SetText(
-      search_box()->text().substr(0, highlight_range_.start()));
-  ContentsChanged(search_box(), search_box()->text());
-  search_box()->SetText(original_text);
-  search_box()->SetSelectionRange(highlight_range_);
+  // Do not trigger another search here in case the user is left clicking to
+  // select existing autocomplete text. (This also matches omnibox behavior.)
+  DCHECK(HasAutocompleteText());
+  search_box()->ClearSelection();
+  ResetHighlightRange();
 }
 
 bool SearchBoxView::HasAutocompleteText() {
@@ -452,23 +441,28 @@
   // autocomplete or selected by the user. If the recorded autocomplete
   // |highlight_range_| matches the selection range, this text is suggested by
   // autocomplete.
-  return search_box()->GetSelectedRange() == highlight_range_ &&
+  return search_box()->GetSelectedRange().EqualsIgnoringDirection(
+             highlight_range_) &&
          highlight_range_.length() > 0;
 }
 
 void SearchBoxView::ClearAutocompleteText() {
-  if (!is_app_list_search_autocomplete_enabled_)
+  if (!ShouldProcessAutocomplete())
     return;
 
-  search_box()->SetText(
-      search_box()->text().substr(0, highlight_range_.start()));
+  // Avoid triggering subsequent query by temporarily setting controller to
+  // nullptr.
+  search_box()->set_controller(nullptr);
+  search_box()->ClearCompositionText();
+  search_box()->set_controller(this);
+  ResetHighlightRange();
 }
 
 void SearchBoxView::ContentsChanged(views::Textfield* sender,
                                     const base::string16& new_contents) {
   // Update autocomplete text highlight range to track user typed text.
-  if (is_app_list_search_autocomplete_enabled_)
-    highlight_range_.set_start(search_box()->text().length());
+  if (ShouldProcessAutocomplete())
+    ResetHighlightRange();
   search_box::SearchBoxViewBase::ContentsChanged(sender, new_contents);
   app_list_view_->SetStateFromSearchBoxView(
       IsSearchBoxTrimmedQueryEmpty(), true /*triggered_by_contents_change*/);
@@ -476,7 +470,7 @@
 
 void SearchBoxView::SetAutocompleteText(
     const base::string16& autocomplete_text) {
-  if (!is_app_list_search_autocomplete_enabled_)
+  if (!ShouldProcessAutocomplete())
     return;
 
   const base::string16& current_text = search_box()->text();
@@ -487,27 +481,37 @@
   if (autocomplete_text.length() == current_text.length())
     return;
 
-  const base::string16& user_typed_text =
-      current_text.substr(0, highlight_range_.start());
   const base::string16& highlighted_text =
       autocomplete_text.substr(highlight_range_.start());
-  search_box()->SetText(user_typed_text + highlighted_text);
+
+  // Don't set autocomplete text if the highlighted text is the same as before.
+  if (highlighted_text == search_box()->GetSelectedText())
+    return;
+
   highlight_range_.set_end(autocomplete_text.length());
-  search_box()->SelectRange(highlight_range_);
+  ui::CompositionText composition_text;
+  composition_text.text = highlighted_text;
+  composition_text.selection = gfx::Range(0, highlighted_text.length());
+
+  // Avoid triggering subsequent query by temporarily setting controller to
+  // nullptr.
+  search_box()->set_controller(nullptr);
+  search_box()->SetCompositionText(composition_text);
+  search_box()->set_controller(this);
 }
 
 bool SearchBoxView::HandleKeyEvent(views::Textfield* sender,
                                    const ui::KeyEvent& key_event) {
   if (search_box()->HasFocus() && is_search_box_active() &&
-      !search_box()->text().empty() &&
-      is_app_list_search_autocomplete_enabled_) {
+      !search_box()->text().empty() && ShouldProcessAutocomplete()) {
     // If the search box has no text in it currently, autocomplete should not
     // work.
     last_key_pressed_ = key_event.key_code();
     if (key_event.type() == ui::ET_KEY_PRESSED &&
         key_event.key_code() != ui::VKEY_BACK) {
-      if (key_event.key_code() == ui::VKEY_TAB) {
+      if (key_event.key_code() == ui::VKEY_TAB && HasAutocompleteText()) {
         AcceptAutocompleteText();
+        return true;
       } else if ((key_event.key_code() == ui::VKEY_UP ||
                   key_event.key_code() == ui::VKEY_DOWN ||
                   key_event.key_code() == ui::VKEY_LEFT ||
@@ -515,17 +519,6 @@
                  HasAutocompleteText()) {
         ClearAutocompleteText();
         return true;
-      } else {
-        const base::string16 pending_text = search_box()->GetSelectedText();
-        // Hitting the next key in the autocompete suggestion continues
-        // autocomplete suggestion. If the selected range doesn't match the
-        // recorded highlight range, the selection should be overwritten.
-        if (!pending_text.empty() &&
-            key_event.GetCharacter() == pending_text[0] &&
-            pending_text.length() == highlight_range_.length()) {
-          AcceptOneCharInAutocompleteText();
-          return true;
-        }
       }
     }
   }
@@ -557,7 +550,7 @@
                                      const ui::MouseEvent& mouse_event) {
   if (mouse_event.type() == ui::ET_MOUSEWHEEL) {
     return app_list_view_->HandleScroll(
-        (&mouse_event)->AsMouseWheelEvent()->offset().y(), ui::ET_MOUSEWHEEL);
+        (&mouse_event)->AsMouseWheelEvent()->offset(), ui::ET_MOUSEWHEEL);
   }
   if (mouse_event.type() == ui::ET_MOUSE_PRESSED)
     AcceptAutocompleteText();
@@ -610,6 +603,20 @@
   }
 }
 
+bool SearchBoxView::ShouldProcessAutocomplete() {
+  // IME sets composition text while the user is typing, so avoid handle
+  // autocomplete in this case to avoid conflicts.
+  return is_app_list_search_autocomplete_enabled_ &&
+         !(search_box()->IsIMEComposing() && highlight_range_.is_empty());
+}
+
+void SearchBoxView::ResetHighlightRange() {
+  DCHECK(ShouldProcessAutocomplete());
+  const uint32_t text_length = search_box()->text().length();
+  highlight_range_.set_start(text_length);
+  highlight_range_.set_end(text_length);
+}
+
 void SearchBoxView::SetupAssistantButton() {
   if (search_model_ && !search_model_->search_box()->show_assistant_button()) {
     return;
diff --git a/ash/app_list/views/search_box_view.h b/ash/app_list/views/search_box_view.h
index 5769503..dfbafed 100644
--- a/ash/app_list/views/search_box_view.h
+++ b/ash/app_list/views/search_box_view.h
@@ -92,6 +92,10 @@
   }
   ContentsView* contents_view() { return contents_view_; }
 
+  void set_highlight_range_for_test(const gfx::Range& range) {
+    highlight_range_ = range;
+  }
+
  private:
   // Gets the wallpaper prominent colors.
   void GetWallpaperProminentColors(
@@ -105,9 +109,6 @@
   // Notifies SearchBoxViewDelegate that the autocomplete text is valid.
   void AcceptAutocompleteText();
 
-  // Accepts one character in the autocomplete text and fires query.
-  void AcceptOneCharInAutocompleteText();
-
   // Returns true if there is currently an autocomplete suggestion in
   // search_box().
   bool HasAutocompleteText();
@@ -136,6 +137,12 @@
   void SearchEngineChanged() override;
   void ShowAssistantChanged() override;
 
+  // Returns true if the event to trigger autocomplete should be handled.
+  bool ShouldProcessAutocomplete();
+
+  // Clear highlight range.
+  void ResetHighlightRange();
+
   // The range of highlighted text for autocomplete.
   gfx::Range highlight_range_;
 
diff --git a/ash/app_list/views/search_box_view_unittest.cc b/ash/app_list/views/search_box_view_unittest.cc
index 249b2e1..50826b0 100644
--- a/ash/app_list/views/search_box_view_unittest.cc
+++ b/ash/app_list/views/search_box_view_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
+#include "ui/base/ime/composition_text.h"
 #include "ui/chromeos/search_box/search_box_constants.h"
 #include "ui/chromeos/search_box/search_box_view_delegate.h"
 #include "ui/events/base_event_utils.h"
@@ -307,22 +308,22 @@
   // expect only typed characters otherwise.
   void ExpectAutocompleteSuggestion(bool should_autocomplete) {
     if (should_autocomplete) {
-      // Search box autocomplete suggestion is accepted and reflected in Search
-      // Model.
-      EXPECT_EQ(view()->search_box()->text(),
+      // Search box autocomplete suggestion is accepted, but it should not
+      // trigger another query, thus it is not reflected in Search Model.
+      EXPECT_EQ(base::ASCIIToUTF16("hello world!"),
+                view()->search_box()->text());
+      EXPECT_EQ(base::ASCIIToUTF16("he"),
                 view_delegate()->GetSearchModel()->search_box()->text());
-      EXPECT_EQ(view()->search_box()->text(),
-                base::ASCIIToUTF16("hello world!"));
     } else {
       // Search box autocomplete suggestion is removed and is reflected in
       // SearchModel.
       EXPECT_EQ(view()->search_box()->text(),
                 view_delegate()->GetSearchModel()->search_box()->text());
-      EXPECT_EQ(view()->search_box()->text(), base::ASCIIToUTF16("he"));
+      EXPECT_EQ(base::ASCIIToUTF16("he"), view()->search_box()->text());
       // ProcessAutocomplete should be a no-op.
       view()->ProcessAutocomplete();
       // The autocomplete suggestion should still not be present.
-      EXPECT_EQ(view()->search_box()->text(), base::ASCIIToUTF16("he"));
+      EXPECT_EQ(base::ASCIIToUTF16("he"), view()->search_box()->text());
     }
   }
 
@@ -530,15 +531,16 @@
   KeyPress(ui::VKEY_H);
   KeyPress(ui::VKEY_E);
   view()->ProcessAutocomplete();
-  // Forward the next key in the autocomplete suggestion to HandleKeyEvent(). We
-  // use HandleKeyEvent() because KeyPress() will replace the existing
-  // highlighted text and add a repeat character.
-  ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_L, ui::EF_NONE);
-  static_cast<views::TextfieldController*>(view())->HandleKeyEvent(
-      view()->search_box(), event);
+
+  // After typing L, the highlighted text will be replaced by L.
+  KeyPress(ui::VKEY_L);
   base::string16 selected_text = view()->search_box()->GetSelectedText();
-  // The autocomplete text should be preserved after hitting the next key in the
-  // suggestion.
+  EXPECT_EQ(view()->search_box()->text(), base::ASCIIToUTF16("hel"));
+  EXPECT_EQ(base::ASCIIToUTF16(""), selected_text);
+
+  // After handling autocomplete, the highlighted text will show again.
+  view()->ProcessAutocomplete();
+  selected_text = view()->search_box()->GetSelectedText();
   EXPECT_EQ(view()->search_box()->text(), base::ASCIIToUTF16("hello world!"));
   EXPECT_EQ(base::ASCIIToUTF16("lo world!"), selected_text);
 }
@@ -567,5 +569,33 @@
                false);
 }
 
+// Tests that autocomplete is not handled if IME is using composition text.
+TEST_F(SearchBoxViewAutocompleteTest, SearchBoxAutocompletesNotHandledForIME) {
+  // Add a search result with a non-empty title field.
+  CreateSearchResult(ash::SearchResultDisplayType::kList, 1.0,
+                     base::ASCIIToUTF16("hello world!"), base::string16());
+
+  // Simulate uncomposited text. The autocomplete should be handled.
+  view()->search_box()->SetText(base::ASCIIToUTF16("he"));
+  view()->set_highlight_range_for_test(gfx::Range(2, 2));
+  view()->ProcessAutocomplete();
+
+  base::string16 selected_text = view()->search_box()->GetSelectedText();
+  EXPECT_EQ(view()->search_box()->text(), base::ASCIIToUTF16("hello world!"));
+  EXPECT_EQ(base::ASCIIToUTF16("llo world!"), selected_text);
+  view()->search_box()->SetText(base::string16());
+
+  // Simulate IME composition text. The autocomplete should not be handled.
+  ui::CompositionText composition_text;
+  composition_text.text = base::ASCIIToUTF16("he");
+  view()->search_box()->SetCompositionText(composition_text);
+  view()->set_highlight_range_for_test(gfx::Range(2, 2));
+  view()->ProcessAutocomplete();
+
+  selected_text = view()->search_box()->GetSelectedText();
+  EXPECT_EQ(view()->search_box()->text(), base::ASCIIToUTF16("he"));
+  EXPECT_EQ(base::ASCIIToUTF16(""), selected_text);
+}
+
 }  // namespace test
 }  // namespace app_list
diff --git a/ash/app_list/views/search_result_answer_card_view.cc b/ash/app_list/views/search_result_answer_card_view.cc
index 4beb79b..39d06a8 100644
--- a/ash/app_list/views/search_result_answer_card_view.cc
+++ b/ash/app_list/views/search_result_answer_card_view.cc
@@ -275,6 +275,11 @@
     if (!has_children()) {
       AddChildView(content_view);
       ExcludeCardFromEventHandling(contents_->GetView()->native_view());
+
+      if (search_result_->equivalent_result_id().has_value()) {
+        view_delegate_->GetSearchModel()->DeleteResultById(
+            search_result_->equivalent_result_id().value());
+      }
     }
     SetPreferredSize(content_view->GetPreferredSize());
     container_->Update();
diff --git a/ash/app_list/views/search_result_answer_card_view_unittest.cc b/ash/app_list/views/search_result_answer_card_view_unittest.cc
index 053199dc..2d09eb8 100644
--- a/ash/app_list/views/search_result_answer_card_view_unittest.cc
+++ b/ash/app_list/views/search_result_answer_card_view_unittest.cc
@@ -51,16 +51,18 @@
   }
 
  protected:
-  void SetUpSearchResult() {
+  std::unique_ptr<TestSearchResult> CreateAnswerCardResult() {
     const GURL kFakeQueryUrl = GURL("https://www.google.com/coac?q=fake");
-    SearchModel::SearchResults* results = GetResults();
-    std::unique_ptr<TestSearchResult> result =
-        std::make_unique<TestSearchResult>();
+    auto result = std::make_unique<TestSearchResult>();
     result->set_display_type(ash::SearchResultDisplayType::kCard);
     result->set_title(base::UTF8ToUTF16(kResultTitle));
     result->set_display_score(kDisplayScore);
     result->set_query_url(kFakeQueryUrl);
-    results->Add(std::move(result));
+    return result;
+  }
+
+  void SetUpSearchResult() {
+    GetResults()->Add(CreateAnswerCardResult());
 
     // Adding results will schedule Update().
     view_delegate_.fake_navigable_contents_factory()
@@ -103,6 +105,8 @@
     result_container_view_->child_at(0)->GetAccessibleNodeData(node_data);
   }
 
+  AppListTestViewDelegate& view_delegate() { return view_delegate_; }
+
  private:
   AppListTestViewDelegate view_delegate_;
 
@@ -147,5 +151,35 @@
   EXPECT_EQ(0, GetContainerScore());
 }
 
+TEST_F(SearchResultAnswerCardViewTest, RemoveEquivalent) {
+  // Ensures no results.
+  DeleteResult();
+
+  // Creates a result that will be removed later when answer card loads.
+  constexpr char kEquivalentResultId[] = "equivalent-id";
+  auto result = std::make_unique<TestSearchResult>();
+  result->set_result_id(kEquivalentResultId);
+  result->set_display_type(ash::SearchResultDisplayType::kList);
+  result->set_display_score(kDisplayScore);
+  GetResults()->Add(std::move(result));
+
+  // Creates an answer card result and associated with an equivalent result id.
+  result = CreateAnswerCardResult();
+  result->set_equivalent_result_id(kEquivalentResultId);
+  GetResults()->Add(std::move(result));
+
+  EXPECT_EQ(2u, GetResults()->item_count());
+  EXPECT_TRUE(
+      view_delegate().GetSearchModel()->FindSearchResult(kEquivalentResultId));
+
+  // Wait for the answer card result to load.
+  RunPendingMessages();
+
+  // Equivalent result should be removed.
+  EXPECT_EQ(1u, GetResults()->item_count());
+  EXPECT_FALSE(
+      view_delegate().GetSearchModel()->FindSearchResult(kEquivalentResultId));
+}
+
 }  // namespace test
 }  // namespace app_list
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 c6ee795b..4fb773c 100644
--- a/ash/app_list/views/search_result_tile_item_view.cc
+++ b/ash/app_list/views/search_result_tile_item_view.cc
@@ -24,7 +24,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/image/image_skia_operations.h"
@@ -367,27 +366,20 @@
   if (menu.empty() || (context_menu_ && context_menu_->IsShowingMenu()))
     return;
 
-  int run_types = views::MenuRunner::HAS_MNEMONICS;
-  views::MenuAnchorPosition anchor_position = views::MENU_ANCHOR_TOPLEFT;
-  gfx::Rect anchor_rect = gfx::Rect(point, gfx::Size());
-
-  if (::features::IsTouchableAppContextMenuEnabled()) {
-    anchor_position = views::MENU_ANCHOR_BUBBLE_TOUCHABLE_RIGHT;
-    run_types |= views::MenuRunner::USE_TOUCHABLE_LAYOUT |
-                 views::MenuRunner::CONTEXT_MENU |
-                 views::MenuRunner::FIXED_ANCHOR;
-    anchor_rect = source->GetBoundsInScreen();
-    // Anchor the menu to the same rect that is used for selection highlight.
-    anchor_rect.ClampToCenteredSize(
-        AppListConfig::instance().grid_focus_size());
-  }
+  gfx::Rect anchor_rect = source->GetBoundsInScreen();
+  // Anchor the menu to the same rect that is used for selection highlight.
+  anchor_rect.ClampToCenteredSize(AppListConfig::instance().grid_focus_size());
 
   context_menu_ = std::make_unique<AppListMenuModelAdapter>(
       item_->id(), this, source_type, this, GetAppType(),
       base::BindOnce(&SearchResultTileItemView::OnMenuClosed,
                      weak_ptr_factory_.GetWeakPtr()));
   context_menu_->Build(std::move(menu));
-  context_menu_->Run(anchor_rect, anchor_position, run_types);
+  context_menu_->Run(anchor_rect, views::MENU_ANCHOR_BUBBLE_TOUCHABLE_RIGHT,
+                     views::MenuRunner::HAS_MNEMONICS |
+                         views::MenuRunner::USE_TOUCHABLE_LAYOUT |
+                         views::MenuRunner::CONTEXT_MENU |
+                         views::MenuRunner::FIXED_ANCHOR);
   source->RequestFocus();
 }
 
diff --git a/ash/app_list/views/top_icon_animation_view.cc b/ash/app_list/views/top_icon_animation_view.cc
index fae69d1..538f4f0 100644
--- a/ash/app_list/views/top_icon_animation_view.cc
+++ b/ash/app_list/views/top_icon_animation_view.cc
@@ -73,7 +73,7 @@
   // Transform used for scaling down the icon and move it back inside to the
   // original folder icon. The transform's origin is this view's origin.
   gfx::Transform transform;
-  transform.Translate(scaled_rect_.x() - bounds().x(),
+  transform.Translate(scaled_rect_.x() - GetMirroredX(),
                       scaled_rect_.y() - bounds().y());
   transform.Scale(
       static_cast<double>(scaled_rect_.width()) / bounds().width(),
diff --git a/ash/ash_service_unittest.cc b/ash/ash_service_unittest.cc
index 67e32c4..23a34f3a 100644
--- a/ash/ash_service_unittest.cc
+++ b/ash/ash_service_unittest.cc
@@ -12,11 +12,14 @@
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
-#include "services/service_manager/public/cpp/service_test.h"
+#include "base/test/scoped_task_environment.h"
+#include "services/service_manager/public/cpp/test/test_service.h"
+#include "services/service_manager/public/cpp/test/test_service_manager.h"
 #include "services/ws/public/cpp/property_type_converters.h"
 #include "services/ws/public/mojom/window_manager.mojom.h"
 #include "services/ws/public/mojom/window_tree.mojom.h"
 #include "services/ws/public/mojom/window_tree_constants.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/env.h"
 #include "ui/aura/mus/property_converter.h"
@@ -66,24 +69,32 @@
   DISALLOW_COPY_AND_ASSIGN(WindowTreeClientDelegate);
 };
 
-class AshServiceTest : public service_manager::test::ServiceTest {
+class AshServiceTest : public testing::Test {
  public:
-  AshServiceTest() : service_manager::test::ServiceTest("ash_unittests") {}
+  AshServiceTest()
+      : test_service_(
+            test_service_manager_.RegisterTestInstance("ash_unittests")) {}
   ~AshServiceTest() override = default;
 
   // service_manager::test::ServiceTest:
   void SetUp() override {
-    service_manager::test::ServiceTest::SetUp();
     aura::test::EnvTestHelper().SetMode(aura::Env::Mode::MUS);
   }
+
   void TearDown() override {
     // Unset the screen installed by the test.
     display::Screen::SetScreenInstance(nullptr);
-    service_manager::test::ServiceTest::TearDown();
     aura::test::EnvTestHelper().SetMode(aura::Env::Mode::LOCAL);
   }
 
+ protected:
+  service_manager::Connector* connector() { return test_service_.connector(); }
+
  private:
+  base::test::ScopedTaskEnvironment task_environment_;
+  service_manager::TestServiceManager test_service_manager_;
+  service_manager::TestService test_service_;
+
   DISALLOW_COPY_AND_ASSIGN(AshServiceTest);
 };
 
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index c4e3cb41..5f2815f 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -1438,7 +1438,7 @@
         Hit <ph name="KEYBOARD_SHORTCUT">$1<ex>Control-Shift-Space</ex></ph> to switch keyboard layout.
       </message>
       <message name="IDS_ASH_LOGIN_ERROR_DETACHABLE_BASE_CHANGED" desc="Text for the error dialog shown on the lock screen when a detachable base different from the base last used by the currently active user is attached.">
-        A different keyboard has been connected since you last entered your password. It may be attempting to steal your keystrokes.
+        Another keyboard has connected to this device since you last signed in. Make sure you trust this keyboard before you use it.
       </message>
       <message name="IDS_ASH_LOGIN_POD_PASSWORD_FIELD_ACCESSIBLE_NAME" desc="Text to be spoken when the focus is set to the password field of a user pod on the sign-in screen.">
         Password for <ph name="USER_EMAIL_ADDRESS">$1<ex>john.doe@example.com</ex></ph>
diff --git a/ash/assistant/assistant_cache_controller.cc b/ash/assistant/assistant_cache_controller.cc
index 99490be..d1b49121 100644
--- a/ash/assistant/assistant_cache_controller.cc
+++ b/ash/assistant/assistant_cache_controller.cc
@@ -26,34 +26,34 @@
 // Conversation starters -------------------------------------------------------
 
 const base::Feature kConversationStartersFeature{
-    "ChromeOSAssistantConversationStarters", base::FEATURE_DISABLED_BY_DEFAULT};
+    "ChromeOSAssistantConversationStarters", base::FEATURE_ENABLED_BY_DEFAULT};
 
 constexpr base::FeatureParam<bool> kImBoredChipEnabled{
-    &kConversationStartersFeature, "im-bored-chip-enabled", false};
+    &kConversationStartersFeature, "im-bored-chip-enabled", true};
 
 constexpr base::FeatureParam<bool> kOpenFilesChipEnabled{
-    &kConversationStartersFeature, "open-files-chip-enabled", false};
+    &kConversationStartersFeature, "open-files-chip-enabled", true};
 
 constexpr base::FeatureParam<bool> kPlayMusicChipEnabled{
-    &kConversationStartersFeature, "play-music-chip-enabled", false};
+    &kConversationStartersFeature, "play-music-chip-enabled", true};
 
 constexpr base::FeatureParam<bool> kSendAnEmailChipEnabled{
-    &kConversationStartersFeature, "send-an-email-chip-enabled", false};
+    &kConversationStartersFeature, "send-an-email-chip-enabled", true};
 
 constexpr base::FeatureParam<bool> kSetAReminderChipEnabled{
-    &kConversationStartersFeature, "set-a-reminder-chip-enabled", false};
+    &kConversationStartersFeature, "set-a-reminder-chip-enabled", true};
 
 constexpr base::FeatureParam<bool> kWhatCanYouDoChipEnabled{
-    &kConversationStartersFeature, "what-can-you-do-chip-enabled", false};
+    &kConversationStartersFeature, "what-can-you-do-chip-enabled", true};
 
 constexpr base::FeatureParam<bool> kWhatsOnMyCalendarChipEnabled{
-    &kConversationStartersFeature, "whats-on-my-calendar-chip-enabled", false};
+    &kConversationStartersFeature, "whats-on-my-calendar-chip-enabled", true};
 
 constexpr base::FeatureParam<bool> kWhatsOnMyScreenChipEnabled{
-    &kConversationStartersFeature, "whats-on-my-screen-chip-enabled", false};
+    &kConversationStartersFeature, "whats-on-my-screen-chip-enabled", true};
 
 constexpr base::FeatureParam<bool> kWhatsTheWeatherChipEnabled{
-    &kConversationStartersFeature, "whats-the-weather-chip-enabled", false};
+    &kConversationStartersFeature, "whats-the-weather-chip-enabled", true};
 
 constexpr int kMaxNumOfConversationStarters = 3;
 
diff --git a/ash/assistant/assistant_controller.cc b/ash/assistant/assistant_controller.cc
index 6b8ebc5..e614446 100644
--- a/ash/assistant/assistant_controller.cc
+++ b/ash/assistant/assistant_controller.cc
@@ -99,23 +99,6 @@
   assistant_image_downloader_ = std::move(assistant_image_downloader);
 }
 
-// TODO(dmblack): Call SetAssistantSetup directly on AssistantSetupController
-// instead of going through AssistantController.
-void AssistantController::SetAssistantSetup(
-    mojom::AssistantSetupPtr assistant_setup) {
-  assistant_setup_ = std::move(assistant_setup);
-  assistant_setup_controller_->SetAssistantSetup(assistant_setup_.get());
-}
-
-// TODO(dmblack): Expose AssistantScreenContextController over mojo rather
-// than implementing RequestScreenshot here in AssistantController.
-void AssistantController::RequestScreenshot(
-    const gfx::Rect& rect,
-    RequestScreenshotCallback callback) {
-  assistant_screen_context_controller_->RequestScreenshot(rect,
-                                                          std::move(callback));
-}
-
 void AssistantController::OpenAssistantSettings() {
   // Launch Assistant settings via deeplink.
   OpenUrl(assistant::util::CreateAssistantSettingsDeepLink());
diff --git a/ash/assistant/assistant_controller.h b/ash/assistant/assistant_controller.h
index d08a335..852234a 100644
--- a/ash/assistant/assistant_controller.h
+++ b/ash/assistant/assistant_controller.h
@@ -27,7 +27,6 @@
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
 #include "services/content/public/mojom/navigable_contents_factory.mojom.h"
-#include "ui/gfx/geometry/rect.h"
 
 namespace ash {
 
@@ -65,15 +64,10 @@
 
   // mojom::AssistantController:
   // TODO(updowndota): Refactor Set() calls to use a factory pattern.
-  // TODO(dmblack): Expose RequestScreenshot(...) over mojo through
-  // AssistantScreenContextController.
   void SetAssistant(
       chromeos::assistant::mojom::AssistantPtr assistant) override;
   void SetAssistantImageDownloader(
       mojom::AssistantImageDownloaderPtr assistant_image_downloader) override;
-  void SetAssistantSetup(mojom::AssistantSetupPtr assistant_setup) override;
-  void RequestScreenshot(const gfx::Rect& rect,
-                         RequestScreenshotCallback callback) override;
   void OpenAssistantSettings() override;
 
   // AssistantControllerObserver:
@@ -146,7 +140,7 @@
 
   // The observer list should be initialized early so that sub-controllers may
   // register as observers during their construction.
-  base::ObserverList<AssistantControllerObserver>::Unchecked observers_;
+  base::ObserverList<AssistantControllerObserver> observers_;
 
   mojo::BindingSet<mojom::AssistantController> assistant_controller_bindings_;
 
@@ -158,8 +152,6 @@
 
   mojom::AssistantImageDownloaderPtr assistant_image_downloader_;
 
-  mojom::AssistantSetupPtr assistant_setup_;
-
   std::unique_ptr<AssistantCacheController> assistant_cache_controller_;
 
   std::unique_ptr<AssistantInteractionController>
diff --git a/ash/assistant/assistant_controller_observer.h b/ash/assistant/assistant_controller_observer.h
index 15c9552..3001112 100644
--- a/ash/assistant/assistant_controller_observer.h
+++ b/ash/assistant/assistant_controller_observer.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/macros.h"
+#include "base/observer_list_types.h"
 
 class GURL;
 
@@ -20,7 +21,9 @@
 }  // namespace util
 }  // namespace assistant
 
-class AssistantControllerObserver {
+// A checked observer which receives notification of changes to the
+// AssistantController.
+class AssistantControllerObserver : public base::CheckedObserver {
  public:
   // Invoked when the AssistantController has been fully constructed.
   virtual void OnAssistantControllerConstructed() {}
@@ -40,7 +43,7 @@
 
  protected:
   AssistantControllerObserver() = default;
-  virtual ~AssistantControllerObserver() = default;
+  ~AssistantControllerObserver() override = default;
 
   DISALLOW_COPY_AND_ASSIGN(AssistantControllerObserver);
 };
diff --git a/ash/assistant/assistant_interaction_controller.cc b/ash/assistant/assistant_interaction_controller.cc
index bf0ac72..aedd01eb 100644
--- a/ash/assistant/assistant_interaction_controller.cc
+++ b/ash/assistant/assistant_interaction_controller.cc
@@ -47,7 +47,6 @@
     AssistantController* assistant_controller)
     : assistant_controller_(assistant_controller),
       assistant_interaction_subscriber_binding_(this),
-      assistant_response_processor_(assistant_controller),
       weak_factory_(this) {
   AddModelObserver(this);
   assistant_controller_->AddObserver(this);
@@ -120,7 +119,7 @@
   }
 
   assistant_controller_->ui_controller()->ShowUi(AssistantSource::kDeepLink);
-  StartTextInteraction(query.value());
+  StartTextInteraction(query.value(), /*allow_tts=*/false);
 }
 
 void AssistantInteractionController::OnUiModeChanged(AssistantUiMode ui_mode) {
@@ -203,6 +202,28 @@
   StartMetalayerInteraction(/*region=*/rect);
 }
 
+void AssistantInteractionController::OnCommittedQueryChanged(
+    const AssistantQuery& assistant_query) {
+  std::string query;
+  switch (assistant_query.type()) {
+    case AssistantQueryType::kText: {
+      const auto* assistant_text_query =
+          static_cast<const AssistantTextQuery*>(&assistant_query);
+      query = assistant_text_query->text();
+      break;
+    }
+    case AssistantQueryType::kVoice: {
+      const auto* assistant_voice_query =
+          static_cast<const AssistantVoiceQuery*>(&assistant_query);
+      query = assistant_voice_query->high_confidence_speech();
+      break;
+    }
+    case AssistantQueryType::kNull:
+      break;
+  }
+  model_.query_history().Add(query);
+}
+
 void AssistantInteractionController::OnInteractionStateChanged(
     InteractionState interaction_state) {
   if (interaction_state != InteractionState::kActive)
@@ -369,7 +390,13 @@
   }
 
   // Otherwise, we will submit a simple text query using the suggestion text.
-  StartTextInteraction(suggestion->text);
+  // Note that a text query originating from a suggestion chip will carry
+  // forward the allowance/forbiddance of TTS from the previous response. This
+  // is because suggestion chips pressed after a voice query should continue to
+  // return TTS, as really the text interaction is just a continuation of the
+  // user's preceding voice interaction.
+  StartTextInteraction(suggestion->text, /*allow_tts=*/model_.response() &&
+                                             model_.response()->has_tts());
 }
 
 void AssistantInteractionController::OnSuggestionsResponse(
@@ -497,7 +524,7 @@
 void AssistantInteractionController::OnDialogPlateContentsCommitted(
     const std::string& text) {
   DCHECK(!text.empty());
-  StartTextInteraction(text);
+  StartTextInteraction(text, /*allow_tts=*/false);
 }
 
 bool AssistantInteractionController::HasUnprocessedPendingResponse() {
@@ -515,9 +542,15 @@
     return;
   }
 
+  // Bind an interface to a navigable contents factory that is needed for
+  // processing card elements.
+  content::mojom::NavigableContentsFactoryPtr contents_factory;
+  assistant_controller_->GetNavigableContentsFactory(
+      mojo::MakeRequest(&contents_factory));
+
   // Start processing.
-  assistant_response_processor_.Process(
-      *model_.pending_response(),
+  model_.pending_response()->Process(
+      std::move(contents_factory),
       base::BindOnce(
           &AssistantInteractionController::OnPendingResponseProcessed,
           weak_factory_.GetWeakPtr()));
@@ -581,12 +614,13 @@
 }
 
 void AssistantInteractionController::StartTextInteraction(
-    const std::string text) {
+    const std::string text,
+    bool allow_tts) {
   StopActiveInteraction(false);
 
   model_.SetPendingQuery(std::make_unique<AssistantTextQuery>(text));
 
-  assistant_->SendTextQuery(text);
+  assistant_->StartTextInteraction(text, allow_tts);
 }
 
 void AssistantInteractionController::StartVoiceInteraction() {
diff --git a/ash/assistant/assistant_interaction_controller.h b/ash/assistant/assistant_interaction_controller.h
index 72e7606..4ed1bda 100644
--- a/ash/assistant/assistant_interaction_controller.h
+++ b/ash/assistant/assistant_interaction_controller.h
@@ -11,7 +11,6 @@
 #include <vector>
 
 #include "ash/assistant/assistant_controller_observer.h"
-#include "ash/assistant/assistant_response_processor.h"
 #include "ash/assistant/model/assistant_interaction_model.h"
 #include "ash/assistant/model/assistant_interaction_model_observer.h"
 #include "ash/assistant/model/assistant_ui_model_observer.h"
@@ -26,7 +25,6 @@
 
 class AssistantController;
 class AssistantInteractionModelObserver;
-class AssistantResponseProcessor;
 
 class AssistantInteractionController
     : public chromeos::assistant::mojom::AssistantInteractionSubscriber,
@@ -67,6 +65,7 @@
   void OnInteractionStateChanged(InteractionState interaction_state) override;
   void OnInputModalityChanged(InputModality input_modality) override;
   void OnMicStateChanged(MicState mic_state) override;
+  void OnCommittedQueryChanged(const AssistantQuery& query) override;
   void OnResponseChanged(
       const std::shared_ptr<AssistantResponse>& response) override;
 
@@ -116,7 +115,7 @@
 
   void StartMetalayerInteraction(const gfx::Rect& region);
   void StartScreenContextInteraction();
-  void StartTextInteraction(const std::string text);
+  void StartTextInteraction(const std::string text, bool allow_tts);
   void StartVoiceInteraction();
   void StopActiveInteraction(bool cancel_conversation);
 
@@ -130,8 +129,6 @@
   mojo::Binding<chromeos::assistant::mojom::AssistantInteractionSubscriber>
       assistant_interaction_subscriber_binding_;
 
-  AssistantResponseProcessor assistant_response_processor_;
-
   AssistantInteractionModel model_;
 
   base::WeakPtrFactory<AssistantInteractionController> weak_factory_;
diff --git a/ash/assistant/assistant_response_processor.cc b/ash/assistant/assistant_response_processor.cc
deleted file mode 100644
index 6963f58..0000000
--- a/ash/assistant/assistant_response_processor.cc
+++ /dev/null
@@ -1,178 +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 "ash/assistant/assistant_response_processor.h"
-
-#include <algorithm>
-
-#include "ash/assistant/assistant_controller.h"
-#include "ash/assistant/model/assistant_response.h"
-#include "ash/assistant/model/assistant_ui_element.h"
-#include "ash/assistant/ui/assistant_ui_constants.h"
-#include "base/base64.h"
-#include "base/stl_util.h"
-
-namespace ash {
-
-namespace {
-
-// WebContents.
-constexpr char kDataUriPrefix[] = "data:text/html;base64,";
-
-}  // namespace
-
-// AssistantCardProcessor ------------------------------------------------------
-
-AssistantCardProcessor::AssistantCardProcessor(
-    AssistantController* assistant_controller,
-    AssistantResponseProcessor* assistant_response_processor,
-    AssistantCardElement* assistant_card_element)
-    : assistant_controller_(assistant_controller),
-      assistant_response_processor_(assistant_response_processor),
-      assistant_card_element_(assistant_card_element) {}
-
-AssistantCardProcessor::~AssistantCardProcessor() {
-  if (contents_)
-    contents_->RemoveObserver(this);
-}
-
-void AssistantCardProcessor::DidStopLoading() {
-  contents_->RemoveObserver(this);
-
-  // Transfer ownership of |contents_| to the card element and notify the
-  // response processor that we've finished processing.
-  assistant_card_element_->set_contents(std::move(contents_));
-  assistant_response_processor_->DidFinishProcessing(this);
-}
-
-void AssistantCardProcessor::Process() {
-  assistant_controller_->GetNavigableContentsFactory(
-      mojo::MakeRequest(&contents_factory_));
-
-  // TODO(dmblack): Find a better way of determining desired card size.
-  const int width_dip = kPreferredWidthDip - 2 * kUiElementHorizontalMarginDip;
-
-  // Configure parameters for the card.
-  auto contents_params = content::mojom::NavigableContentsParams::New();
-  contents_params->enable_view_auto_resize = true;
-  contents_params->auto_resize_min_size = gfx::Size(width_dip, 1);
-  contents_params->auto_resize_max_size = gfx::Size(width_dip, INT_MAX);
-  contents_params->suppress_navigations = true;
-
-  contents_ = std::make_unique<content::NavigableContents>(
-      contents_factory_.get(), std::move(contents_params));
-
-  // Observe |contents_| so that we are notified when loading is complete.
-  contents_->AddObserver(this);
-
-  // Navigate to the data URL which represents the card.
-  std::string encoded_html;
-  base::Base64Encode(assistant_card_element_->html(), &encoded_html);
-  contents_->Navigate(GURL(kDataUriPrefix + encoded_html));
-}
-
-// AssistantResponseProcessor::Task --------------------------------------------
-
-AssistantResponseProcessor::Task::Task(AssistantResponse& response,
-                                       ProcessCallback callback)
-    : response(response.GetWeakPtr()), callback(std::move(callback)) {}
-
-AssistantResponseProcessor::Task::~Task() = default;
-
-// AssistantResponseProcessor --------------------------------------------------
-
-AssistantResponseProcessor::AssistantResponseProcessor(
-    AssistantController* assistant_controller)
-    : assistant_controller_(assistant_controller), weak_factory_(this) {}
-
-AssistantResponseProcessor::~AssistantResponseProcessor() = default;
-
-void AssistantResponseProcessor::Process(AssistantResponse& response,
-                                         ProcessCallback callback) {
-  // We should only attempt to process responses that are unprocessed.
-  DCHECK_EQ(AssistantResponse::ProcessingState::kUnprocessed,
-            response.processing_state());
-
-  // Update processing state.
-  response.set_processing_state(
-      AssistantResponse::ProcessingState::kProcessing);
-
-  // We only support processing a single task at a time. As such, we should
-  // abort any task in progress before creating and starting a new one.
-  TryAbortingTask();
-
-  // Create a task.
-  task_.emplace(/*response=*/response,
-                /*callback=*/std::move(callback));
-
-  // Start processing UI elements.
-  for (const auto& ui_element : response.GetUiElements()) {
-    switch (ui_element->GetType()) {
-      case AssistantUiElementType::kCard:
-        // Create and start an element processor to process the card element.
-        task_->element_processors.push_back(
-            std::make_unique<AssistantCardProcessor>(
-                assistant_controller_, this,
-                static_cast<AssistantCardElement*>(ui_element.get())));
-        task_->element_processors.back()->Process();
-        break;
-      case AssistantUiElementType::kText:
-        // No processing necessary.
-        break;
-    }
-  }
-
-  // Try finishing. This will no-op if there are still UI elements being
-  // processed asynchronously.
-  TryFinishingTask();
-}
-
-void AssistantResponseProcessor::DidFinishProcessing(
-    const AssistantCardProcessor* card_processor) {
-  // If the response has been invalidated we should abort early.
-  if (!task_->response) {
-    TryAbortingTask();
-    return;
-  }
-
-  // Remove the finished element processor to indicate its completion.
-  base::EraseIf(task_->element_processors,
-                [card_processor](
-                    const std::unique_ptr<AssistantCardProcessor>& candidate) {
-                  return candidate.get() == card_processor;
-                });
-
-  // Try finishing. This will no-op if there are still UI elements being
-  // processed asynchronously.
-  TryFinishingTask();
-}
-
-void AssistantResponseProcessor::TryAbortingTask() {
-  if (!task_)
-    return;
-
-  // Invalidate weak pointers to prevent processing callbacks from running.
-  // Otherwise we might continue receiving card events for the aborted task.
-  weak_factory_.InvalidateWeakPtrs();
-
-  // Notify our callback and clean up any task related resources.
-  std::move(task_->callback).Run(/*success=*/false);
-  task_.reset();
-}
-
-void AssistantResponseProcessor::TryFinishingTask() {
-  // This method is a no-op if we are still processing.
-  if (!task_->element_processors.empty())
-    return;
-
-  // Update processing state.
-  task_->response->set_processing_state(
-      AssistantResponse::ProcessingState::kProcessed);
-
-  // Notify our callback and clean up any task related resources.
-  std::move(task_->callback).Run(/*success=*/true);
-  task_.reset();
-}
-
-}  // namespace ash
diff --git a/ash/assistant/assistant_response_processor.h b/ash/assistant/assistant_response_processor.h
deleted file mode 100644
index 05b757a..0000000
--- a/ash/assistant/assistant_response_processor.h
+++ /dev/null
@@ -1,131 +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 ASH_ASSISTANT_ASSISTANT_RESPONSE_PROCESSOR_H_
-#define ASH_ASSISTANT_ASSISTANT_RESPONSE_PROCESSOR_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/optional.h"
-#include "base/unguessable_token.h"
-#include "services/content/public/cpp/navigable_contents.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace ash {
-
-class AssistantController;
-class AssistantCardElement;
-class AssistantResponse;
-class AssistantResponseProcessor;
-enum class AssistantUiElementType;
-
-// AssistantCardProcessor ------------------------------------------------------
-
-// A UI element processor associated with a single card element that is
-// responsible for processing an Assistant card on behalf of an
-// AssistantResponseProcessor.
-class AssistantCardProcessor : public content::NavigableContentsObserver {
- public:
-  AssistantCardProcessor(
-      AssistantController* assistant_controller,
-      AssistantResponseProcessor* assistant_response_processor,
-      AssistantCardElement* assistant_card_element);
-  ~AssistantCardProcessor() override;
-
-  // content::NavigableContentsObserver:
-  void DidStopLoading() override;
-
-  // Starts processing the associated card element. Upon completion, this
-  // processor will call DidFinishProcessing on |assistant_response_processor_|.
-  void Process();
-
- private:
-  AssistantController* const assistant_controller_;
-  AssistantResponseProcessor* const assistant_response_processor_;
-  AssistantCardElement* const assistant_card_element_;
-
-  content::mojom::NavigableContentsFactoryPtr contents_factory_;
-  std::unique_ptr<content::NavigableContents> contents_;
-
-  DISALLOW_COPY_AND_ASSIGN(AssistantCardProcessor);
-};
-
-// AssistantResponseProcessor --------------------------------------------------
-
-// The AssistantResponseProcessor is responsible for performing any processing
-// steps necessary on an Assistant response before it is ready for presentation.
-class AssistantResponseProcessor {
- public:
-  using ProcessCallback = base::OnceCallback<void(bool)>;
-
-  explicit AssistantResponseProcessor(
-      AssistantController* assistant_controller);
-  ~AssistantResponseProcessor();
-
-  // Performs processing of the specified Assistant |response|. Upon completion
-  // of processing, |callback| is run indicating success or failure. Note that
-  // only one Assistant response may be processed at a time. Calling this method
-  // while another response is being processed will abort the previous task.
-  void Process(AssistantResponse& response, ProcessCallback callback);
-
-  // Invoked when the specified |card_processor| has finished processing.
-  void DidFinishProcessing(const AssistantCardProcessor* card_processor);
-
- private:
-  // Encapsulates a processing task for a given Assistant response. Upon task
-  // abort/completion, the associated callback should be run.
-  struct Task {
-   public:
-    Task(AssistantResponse& response, ProcessCallback callback);
-    ~Task();
-
-    // Weak pointer to the response being processed.
-    base::WeakPtr<AssistantResponse> response;
-
-    // Callback to be run on task abort/completion.
-    ProcessCallback callback;
-
-    // Vector of element processors that are processing the UI elements
-    // contained in |response|. When |element_processors| is empty, response
-    // processing is complete.
-    std::vector<std::unique_ptr<AssistantCardProcessor>> element_processors;
-  };
-
-  // Processes a card element as a part of the task identified by |task_id|.
-  void ProcessCardElement(AssistantCardElement* card_element);
-
-  // Invoked when a card element has completed processing. The event is
-  // associated with the task identified by |task_id| for the specified
-  // |card_element|.
-  void OnCardElementProcessed(
-      AssistantCardElement* card_element,
-      const base::Optional<base::UnguessableToken>& embed_token);
-
-  // Checks if the |task_| exists. If so, processing is aborted, the callback
-  // associated with the task is run and the task is cleaned up. Otherwise this
-  // is a no-op.
-  void TryAbortingTask();
-
-  // Checks if the |task_| is finished processing. If so, the callback
-  // associated with the task is run and the task is cleaned up. Otherwise this
-  // is a no-op.
-  void TryFinishingTask();
-
-  AssistantController* const assistant_controller_;  // Owned by Shell.
-
-  base::Optional<Task> task_;
-
-  base::WeakPtrFactory<AssistantResponseProcessor> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(AssistantResponseProcessor);
-};
-
-}  // namespace ash
-
-#endif  // ASH_ASSISTANT_ASSISTANT_RESPONSE_PROCESSOR_H_
diff --git a/ash/assistant/assistant_screen_context_controller.cc b/ash/assistant/assistant_screen_context_controller.cc
index c43b10d..1213759 100644
--- a/ash/assistant/assistant_screen_context_controller.cc
+++ b/ash/assistant/assistant_screen_context_controller.cc
@@ -47,7 +47,7 @@
 }
 
 void EncodeScreenshotAndRunCallback(
-    mojom::AssistantController::RequestScreenshotCallback callback,
+    mojom::AssistantScreenContextController::RequestScreenshotCallback callback,
     std::unique_ptr<ui::LayerTreeOwner> layer_owner,
     gfx::Image image) {
   base::PostTaskWithTraitsAndReplyWithResult(
@@ -129,6 +129,7 @@
 AssistantScreenContextController::AssistantScreenContextController(
     AssistantController* assistant_controller)
     : assistant_controller_(assistant_controller),
+      binding_(this),
       screen_context_request_factory_(this) {
   assistant_controller_->AddObserver(this);
 }
@@ -137,6 +138,11 @@
   assistant_controller_->RemoveObserver(this);
 }
 
+void AssistantScreenContextController::BindRequest(
+    mojom::AssistantScreenContextControllerRequest request) {
+  binding_.Bind(std::move(request));
+}
+
 void AssistantScreenContextController::SetAssistant(
     chromeos::assistant::mojom::Assistant* assistant) {
   assistant_ = assistant;
@@ -154,7 +160,8 @@
 
 void AssistantScreenContextController::RequestScreenshot(
     const gfx::Rect& rect,
-    mojom::AssistantController::RequestScreenshotCallback callback) {
+    mojom::AssistantScreenContextController::RequestScreenshotCallback
+        callback) {
   aura::Window* root_window = Shell::Get()->GetRootWindowForNewWindows();
 
   std::unique_ptr<ui::LayerTreeOwner> layer_owner =
diff --git a/ash/assistant/assistant_screen_context_controller.h b/ash/assistant/assistant_screen_context_controller.h
index 5919a4d..850d9ec 100644
--- a/ash/assistant/assistant_screen_context_controller.h
+++ b/ash/assistant/assistant_screen_context_controller.h
@@ -13,6 +13,7 @@
 #include "ash/assistant/model/assistant_ui_model_observer.h"
 #include "ash/public/interfaces/assistant_controller.mojom.h"
 #include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace ui {
@@ -25,13 +26,16 @@
 class AssistantScreenContextModelObserver;
 
 class ASH_EXPORT AssistantScreenContextController
-    : public AssistantControllerObserver,
+    : public ash::mojom::AssistantScreenContextController,
+      public AssistantControllerObserver,
       public AssistantUiModelObserver {
  public:
   explicit AssistantScreenContextController(
       AssistantController* assistant_controller);
   ~AssistantScreenContextController() override;
 
+  void BindRequest(mojom::AssistantScreenContextControllerRequest request);
+
   // Provides a pointer to the |assistant| owned by AssistantController.
   void SetAssistant(chromeos::assistant::mojom::Assistant* assistant);
 
@@ -42,12 +46,11 @@
   void AddModelObserver(AssistantScreenContextModelObserver* observer);
   void RemoveModelObserver(AssistantScreenContextModelObserver* observer);
 
-  // Requests a screenshot for the region defined by |rect| (given in DP). If
-  // an empty rect is supplied, the entire screen is captured. Upon screenshot
-  // completion, the specified |callback| is run.
+  // ash::mojom::AssistantScreenContextController:
   void RequestScreenshot(
       const gfx::Rect& rect,
-      mojom::AssistantController::RequestScreenshotCallback callback);
+      mojom::AssistantScreenContextController::RequestScreenshotCallback
+          callback) override;
 
   // AssistantControllerObserver:
   void OnAssistantControllerConstructed() override;
@@ -66,6 +69,8 @@
  private:
   AssistantController* const assistant_controller_;  // Owned by Shell.
 
+  mojo::Binding<mojom::AssistantScreenContextController> binding_;
+
   // Owned by AssistantController.
   chromeos::assistant::mojom::Assistant* assistant_ = nullptr;
 
diff --git a/ash/assistant/assistant_setup_controller.cc b/ash/assistant/assistant_setup_controller.cc
index d6ee356..27810bd 100644
--- a/ash/assistant/assistant_setup_controller.cc
+++ b/ash/assistant/assistant_setup_controller.cc
@@ -12,7 +12,7 @@
 
 AssistantSetupController::AssistantSetupController(
     AssistantController* assistant_controller)
-    : assistant_controller_(assistant_controller) {
+    : assistant_controller_(assistant_controller), binding_(this) {
   assistant_controller_->AddObserver(this);
 }
 
@@ -20,9 +20,14 @@
   assistant_controller_->RemoveObserver(this);
 }
 
+void AssistantSetupController::BindRequest(
+    mojom::AssistantSetupControllerRequest request) {
+  binding_.Bind(std::move(request));
+}
+
 void AssistantSetupController::SetAssistantSetup(
-    mojom::AssistantSetup* assistant_setup) {
-  assistant_setup_ = assistant_setup;
+    mojom::AssistantSetupPtr assistant_setup) {
+  assistant_setup_ = std::move(assistant_setup);
 }
 
 void AssistantSetupController::OnDeepLinkReceived(
diff --git a/ash/assistant/assistant_setup_controller.h b/ash/assistant/assistant_setup_controller.h
index 74e5da3..b517122 100644
--- a/ash/assistant/assistant_setup_controller.h
+++ b/ash/assistant/assistant_setup_controller.h
@@ -10,21 +10,26 @@
 
 #include "ash/assistant/assistant_controller_observer.h"
 #include "ash/assistant/ui/main_stage/assistant_opt_in_view.h"
+#include "ash/public/interfaces/assistant_controller.mojom.h"
 #include "ash/public/interfaces/assistant_setup.mojom.h"
 #include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
 
 namespace ash {
 
 class AssistantController;
 
-class AssistantSetupController : public AssistantControllerObserver,
+class AssistantSetupController : public mojom::AssistantSetupController,
+                                 public AssistantControllerObserver,
                                  public AssistantOptInDelegate {
  public:
   explicit AssistantSetupController(AssistantController* assistant_controller);
   ~AssistantSetupController() override;
 
-  // Sets the controller's internal |assistant_setup_| reference.
-  void SetAssistantSetup(mojom::AssistantSetup* assistant_setup);
+  void BindRequest(mojom::AssistantSetupControllerRequest request);
+
+  // mojom::AssistantSetupController:
+  void SetAssistantSetup(mojom::AssistantSetupPtr assistant_setup) override;
 
   // AssistantControllerObserver:
   void OnDeepLinkReceived(
@@ -39,8 +44,10 @@
 
   AssistantController* const assistant_controller_;  // Owned by Shell.
 
-  mojom::AssistantSetup* assistant_setup_ =
-      nullptr;  // Owned by AssistantController.
+  mojo::Binding<mojom::AssistantSetupController> binding_;
+
+  // Interface to AssistantSetup in chrome/browser.
+  mojom::AssistantSetupPtr assistant_setup_;
 
   DISALLOW_COPY_AND_ASSIGN(AssistantSetupController);
 };
diff --git a/ash/assistant/model/assistant_cache_model.h b/ash/assistant/model/assistant_cache_model.h
index 98ccfd6..eabe1f2 100644
--- a/ash/assistant/model/assistant_cache_model.h
+++ b/ash/assistant/model/assistant_cache_model.h
@@ -44,7 +44,7 @@
 
   std::vector<AssistantSuggestionPtr> conversation_starters_;
 
-  base::ObserverList<AssistantCacheModelObserver>::Unchecked observers_;
+  base::ObserverList<AssistantCacheModelObserver> observers_;
 
   DISALLOW_COPY_AND_ASSIGN(AssistantCacheModel);
 };
diff --git a/ash/assistant/model/assistant_cache_model_observer.h b/ash/assistant/model/assistant_cache_model_observer.h
index 7c09ce3..6344c39 100644
--- a/ash/assistant/model/assistant_cache_model_observer.h
+++ b/ash/assistant/model/assistant_cache_model_observer.h
@@ -8,12 +8,14 @@
 #include <map>
 
 #include "base/macros.h"
+#include "base/observer_list_types.h"
 #include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
 
 namespace ash {
 
-// An observer which receives notification of changes to the Assistant cache.
-class AssistantCacheModelObserver {
+// A checked observer which receives notification of changes to the Assistant
+// cache.
+class AssistantCacheModelObserver : public base::CheckedObserver {
  public:
   using AssistantSuggestion = chromeos::assistant::mojom::AssistantSuggestion;
 
@@ -22,7 +24,7 @@
       const std::map<int, const AssistantSuggestion*>& conversation_starters) {}
 
  protected:
-  virtual ~AssistantCacheModelObserver() = default;
+  ~AssistantCacheModelObserver() override = default;
 };
 
 }  // namespace ash
diff --git a/ash/assistant/model/assistant_interaction_model.h b/ash/assistant/model/assistant_interaction_model.h
index 272a139..5132f1a 100644
--- a/ash/assistant/model/assistant_interaction_model.h
+++ b/ash/assistant/model/assistant_interaction_model.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "ash/assistant/model/assistant_query_history.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
 
@@ -119,6 +120,12 @@
   // Updates the speech level in dB.
   void SetSpeechLevel(float speech_level_db);
 
+  // Returns the reference to query history.
+  AssistantQueryHistory& query_history() { return query_history_; }
+
+  // Returns the const reference to query history.
+  const AssistantQueryHistory& query_history() const { return query_history_; }
+
  private:
   void NotifyInteractionStateChanged();
   void NotifyInputModalityChanged();
@@ -134,12 +141,13 @@
   InteractionState interaction_state_ = InteractionState::kInactive;
   InputModality input_modality_ = InputModality::kKeyboard;
   MicState mic_state_ = MicState::kClosed;
+  AssistantQueryHistory query_history_;
   std::unique_ptr<AssistantQuery> committed_query_;
   std::unique_ptr<AssistantQuery> pending_query_;
   std::unique_ptr<AssistantResponse> pending_response_;
   std::shared_ptr<AssistantResponse> response_;
 
-  base::ObserverList<AssistantInteractionModelObserver>::Unchecked observers_;
+  base::ObserverList<AssistantInteractionModelObserver> observers_;
 
   DISALLOW_COPY_AND_ASSIGN(AssistantInteractionModel);
 };
diff --git a/ash/assistant/model/assistant_interaction_model_observer.h b/ash/assistant/model/assistant_interaction_model_observer.h
index 10d43fe..1f02676 100644
--- a/ash/assistant/model/assistant_interaction_model_observer.h
+++ b/ash/assistant/model/assistant_interaction_model_observer.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/observer_list_types.h"
 
 namespace ash {
 
@@ -19,9 +20,9 @@
 enum class InteractionState;
 enum class MicState;
 
-// An observer which receives notification of changes to an Assistant
+// A checked observer which receives notification of changes to an Assistant
 // interaction.
-class AssistantInteractionModelObserver {
+class AssistantInteractionModelObserver : public base::CheckedObserver {
  public:
   // Invoked when the interaction state is changed.
   virtual void OnInteractionStateChanged(InteractionState interaction_state) {}
@@ -58,7 +59,7 @@
 
  protected:
   AssistantInteractionModelObserver() = default;
-  virtual ~AssistantInteractionModelObserver() = default;
+  ~AssistantInteractionModelObserver() override = default;
 
   DISALLOW_COPY_AND_ASSIGN(AssistantInteractionModelObserver);
 };
diff --git a/ash/assistant/model/assistant_query_history.cc b/ash/assistant/model/assistant_query_history.cc
new file mode 100644
index 0000000..80f8806
--- /dev/null
+++ b/ash/assistant/model/assistant_query_history.cc
@@ -0,0 +1,61 @@
+// 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/assistant/model/assistant_query_history.h"
+
+namespace ash {
+
+AssistantQueryHistory::AssistantQueryHistory(int capacity)
+    : capacity_(capacity) {
+  queries_.reserve(capacity);
+}
+
+AssistantQueryHistory::~AssistantQueryHistory() = default;
+
+std::unique_ptr<AssistantQueryHistory::Iterator>
+AssistantQueryHistory::GetIterator() const {
+  return std::make_unique<AssistantQueryHistory::Iterator>(queries_);
+}
+
+void AssistantQueryHistory::Add(const std::string& query) {
+  if (query.empty())
+    return;
+
+  if (static_cast<int>(queries_.size()) == capacity_)
+    queries_.pop_front();
+  queries_.push_back(query);
+}
+
+AssistantQueryHistory::Iterator::Iterator(
+    const base::circular_deque<std::string>& queries)
+    : queries_(queries), cur_pos_(queries_.size()) {}
+
+AssistantQueryHistory::Iterator::~Iterator() = default;
+
+base::Optional<std::string> AssistantQueryHistory::Iterator::Next() {
+  // queries_.size() is of type unsigned int and queries_.size() -1 will
+  // overflow if it is 0.
+  if (cur_pos_ + 1 >= queries_.size()) {
+    cur_pos_ = queries_.size();
+    return base::nullopt;
+  }
+  cur_pos_++;
+  return base::make_optional<std::string>(queries_[cur_pos_]);
+}
+
+base::Optional<std::string> AssistantQueryHistory::Iterator::Prev() {
+  if (queries_.size() == 0)
+    return base::nullopt;
+
+  if (cur_pos_ != 0)
+    cur_pos_--;
+
+  return base::make_optional<std::string>(queries_[cur_pos_]);
+}
+
+void AssistantQueryHistory::Iterator::ResetToLast() {
+  cur_pos_ = queries_.size();
+}
+
+}  // namespace ash
diff --git a/ash/assistant/model/assistant_query_history.h b/ash/assistant/model/assistant_query_history.h
new file mode 100644
index 0000000..71bedbb
--- /dev/null
+++ b/ash/assistant/model/assistant_query_history.h
@@ -0,0 +1,63 @@
+// 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_ASSISTANT_MODEL_ASSISTANT_QUERY_HISTORY_H_
+#define ASH_ASSISTANT_MODEL_ASSISTANT_QUERY_HISTORY_H_
+
+#include <memory>
+#include <string>
+
+#include "ash/ash_export.h"
+#include "base/containers/circular_deque.h"
+#include "base/macros.h"
+#include "base/optional.h"
+
+namespace ash {
+
+// Caches user query history.
+class ASH_EXPORT AssistantQueryHistory {
+ public:
+  class Iterator {
+   public:
+    Iterator(const base::circular_deque<std::string>& queries);
+    ~Iterator();
+
+    // Fetches the next query. If current is already the last query, or there is
+    // no query in history, returns nullopt.
+    base::Optional<std::string> Next();
+
+    // Fetches the previous query. If current is already the first query, return
+    // the first query. If there is no query in history, returns nullopt.
+    base::Optional<std::string> Prev();
+
+    // Resets to the last query. It also makes current iterator valid again if
+    // new queries are added to the underlying AssistantQueryHistory.
+    void ResetToLast();
+
+   private:
+    const base::circular_deque<std::string>& queries_;
+    size_t cur_pos_;
+
+    DISALLOW_COPY_AND_ASSIGN(Iterator);
+  };
+
+  AssistantQueryHistory(int capacity = 100);
+  ~AssistantQueryHistory();
+
+  // Gets the iterator of query history.
+  std::unique_ptr<Iterator> GetIterator() const;
+
+  // Adds a query to history. If it is empty, ignore it.
+  void Add(const std::string& query);
+
+ private:
+  const int capacity_;
+  base::circular_deque<std::string> queries_;
+
+  DISALLOW_COPY_AND_ASSIGN(AssistantQueryHistory);
+};
+
+}  // namespace ash
+
+#endif  // ASH_ASSISTANT_MODEL_ASSISTANT_QUERY_HISTORY_H_
diff --git a/ash/assistant/model/assistant_query_history_unittest.cc b/ash/assistant/model/assistant_query_history_unittest.cc
new file mode 100644
index 0000000..1e4ebaac
--- /dev/null
+++ b/ash/assistant/model/assistant_query_history_unittest.cc
@@ -0,0 +1,68 @@
+// 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/assistant/model/assistant_query_history.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash {
+
+// Assert that iterator Prev() or Next() does not crash on an empty history.
+TEST(AssistantQueryHistory, Empty) {
+  AssistantQueryHistory history(10);
+  auto it = history.GetIterator();
+  EXPECT_EQ(base::nullopt, it->Next());
+  EXPECT_EQ(base::nullopt, it->Prev());
+  EXPECT_EQ(base::nullopt, it->Next());
+  EXPECT_EQ(base::nullopt, it->Next());
+  EXPECT_EQ(base::nullopt, it->Next());
+  EXPECT_EQ(base::nullopt, it->Prev());
+}
+
+TEST(AssistantQueryHistory, Full) {
+  int size = 3;
+  AssistantQueryHistory history(size);
+  // Make more queries than history limit.
+  for (int i = 0; i <= size; i++)
+    history.Add(std::to_string(i));
+  auto it = history.GetIterator();
+  // Assert history only contains last 3 queries.
+  for (int i = size; i > 0; i--)
+    EXPECT_EQ(std::to_string(i), it->Prev().value());
+  EXPECT_EQ(std::to_string(1), it->Prev().value());
+  // Assert that iterate does not pass first query.
+  EXPECT_EQ(std::to_string(1), it->Prev().value());
+
+  // Make more queries than history limit again.
+  for (int i = 0; i <= size; i++)
+    history.Add(std::to_string(i + 4));
+  it->ResetToLast();
+  // Assert that history only contains last 3 queries.
+  for (int i = size; i > 0; i--)
+    EXPECT_EQ(std::to_string(i + 4), it->Prev());
+  EXPECT_EQ(std::to_string(5), it->Prev().value());
+  // Assert that iterate does not pass first query.
+  EXPECT_EQ(std::to_string(5), it->Prev().value());
+}
+
+TEST(AssistantQueryHistory, Add) {
+  AssistantQueryHistory history(10);
+  auto it = history.GetIterator();
+  EXPECT_EQ(base::nullopt, it->Next());
+  EXPECT_EQ(base::nullopt, it->Next());
+  EXPECT_EQ(base::nullopt, it->Next());
+  EXPECT_EQ(base::nullopt, it->Next());
+  history.Add("Query01");
+  history.Add("Query02");
+  it = history.GetIterator();
+  EXPECT_EQ(base::nullopt, it->Next());
+  EXPECT_EQ("Query02", it->Prev().value());
+  EXPECT_EQ("Query01", it->Prev().value());
+  EXPECT_EQ("Query01", it->Prev().value());
+  EXPECT_EQ("Query02", it->Next().value());
+  EXPECT_EQ(base::nullopt, it->Next());
+  EXPECT_EQ(base::nullopt, it->Next());
+}
+
+}  // namespace ash
diff --git a/ash/assistant/model/assistant_response.cc b/ash/assistant/model/assistant_response.cc
index 4ee2d76..ac899a1 100644
--- a/ash/assistant/model/assistant_response.cc
+++ b/ash/assistant/model/assistant_response.cc
@@ -8,7 +8,9 @@
 
 namespace ash {
 
-AssistantResponse::AssistantResponse() : weak_factory_(this) {}
+// AssistantResponse -----------------------------------------------------------
+
+AssistantResponse::AssistantResponse() = default;
 
 AssistantResponse::~AssistantResponse() = default;
 
@@ -50,8 +52,71 @@
   return suggestions;
 }
 
-base::WeakPtr<AssistantResponse> AssistantResponse::GetWeakPtr() {
-  return weak_factory_.GetWeakPtr();
+void AssistantResponse::Process(
+    content::mojom::NavigableContentsFactoryPtr contents_factory,
+    ProcessingCallback callback) {
+  processor_ = std::make_unique<Processor>(*this, std::move(contents_factory),
+                                           std::move(callback));
+  processor_->Process();
+}
+
+// AssistantResponse::Processor ------------------------------------------------
+
+AssistantResponse::Processor::Processor(
+    AssistantResponse& response,
+    content::mojom::NavigableContentsFactoryPtr contents_factory,
+    ProcessingCallback callback)
+    : response_(response),
+      contents_factory_(std::move(contents_factory)),
+      callback_(std::move(callback)) {}
+
+AssistantResponse::Processor::~Processor() {
+  if (callback_)
+    std::move(callback_).Run(/*success=*/false);
+}
+
+void AssistantResponse::Processor::Process() {
+  // Responses should only be processed once.
+  DCHECK_EQ(ProcessingState::kUnprocessed, response_.processing_state());
+  response_.set_processing_state(ProcessingState::kProcessing);
+
+  for (const auto& ui_element : response_.GetUiElements()) {
+    switch (ui_element->GetType()) {
+      case AssistantUiElementType::kCard:
+        ++processing_count_;
+        // Start asynchronous processing of the card element.
+        static_cast<AssistantCardElement*>(ui_element.get())
+            ->Process(contents_factory_.get(),
+                      base::BindOnce(
+                          &AssistantResponse::Processor::OnFinishedProcessing,
+                          base::Unretained(this)));
+        break;
+      case AssistantUiElementType::kText:
+        // No processing necessary.
+        break;
+    }
+  }
+
+  // If any elements are processing asynchronously this will no-op.
+  TryFinishing();
+}
+
+void AssistantResponse::Processor::OnFinishedProcessing(bool success) {
+  // We handle success/failure cases the same because failures will skipped in
+  // view handling. We decrement our |processing_count_| and attempt to finish
+  // response processing. This will no-op if elements are still processing.
+  --processing_count_;
+  TryFinishing();
+}
+
+void AssistantResponse::Processor::TryFinishing() {
+  // No-op if we are already finished or if elements are still processing.
+  if (!callback_ || processing_count_ > 0)
+    return;
+
+  // Notify processing success.
+  response_.set_processing_state(ProcessingState::kProcessed);
+  std::move(callback_).Run(/*success=*/true);
 }
 
 }  // namespace ash
\ No newline at end of file
diff --git a/ash/assistant/model/assistant_response.h b/ash/assistant/model/assistant_response.h
index de47078..f257a864 100644
--- a/ash/assistant/model/assistant_response.h
+++ b/ash/assistant/model/assistant_response.h
@@ -10,8 +10,8 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "base/memory/weak_ptr.h"
 #include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
+#include "services/content/public/cpp/navigable_contents.h"
 
 namespace ash {
 
@@ -20,16 +20,18 @@
 // Models a renderable Assistant response.
 class AssistantResponse {
  public:
+  using AssistantSuggestion = chromeos::assistant::mojom::AssistantSuggestion;
+  using AssistantSuggestionPtr =
+      chromeos::assistant::mojom::AssistantSuggestionPtr;
+
+  using ProcessingCallback = base::OnceCallback<void(bool)>;
+
   enum class ProcessingState {
     kUnprocessed,  // Response has not yet been processed.
     kProcessing,   // Response is currently being processed.
     kProcessed,    // Response has finished processing.
   };
 
-  using AssistantSuggestion = chromeos::assistant::mojom::AssistantSuggestion;
-  using AssistantSuggestionPtr =
-      chromeos::assistant::mojom::AssistantSuggestionPtr;
-
   AssistantResponse();
   ~AssistantResponse();
 
@@ -61,16 +63,48 @@
   bool has_tts() const { return has_tts_; }
   void set_has_tts(bool has_tts) { has_tts_ = has_tts; }
 
-  // Returns a weak pointer to this instance.
-  base::WeakPtr<AssistantResponse> GetWeakPtr();
+  // Invoke to begin processing the response. Upon completion, |callback| will
+  // be run to indicate success or failure.
+  void Process(content::mojom::NavigableContentsFactoryPtr contents_factory,
+               ProcessingCallback callback);
 
  private:
+  // Handles processing for an AssistantResponse.
+  class Processor {
+   public:
+    Processor(AssistantResponse& response,
+              content::mojom::NavigableContentsFactoryPtr contents_factory,
+              ProcessingCallback callback);
+    ~Processor();
+
+    // Invoke to begin processing.
+    void Process();
+
+   private:
+    // Event fired upon completion of a UI element's asynchronous processing.
+    // Once all asynchronous processing of UI elements has completed, the
+    // response itself has finished processing.
+    void OnFinishedProcessing(bool success);
+
+    // Attempts to successfully complete response processing. This will no-op
+    // if we have already finished or if elements are still processing.
+    void TryFinishing();
+
+    AssistantResponse& response_;
+    content::mojom::NavigableContentsFactoryPtr contents_factory_;
+    ProcessingCallback callback_;
+
+    int processing_count_ = 0;
+
+    DISALLOW_COPY_AND_ASSIGN(Processor);
+  };
+
   std::vector<std::unique_ptr<AssistantUiElement>> ui_elements_;
   std::vector<AssistantSuggestionPtr> suggestions_;
   ProcessingState processing_state_ = ProcessingState::kUnprocessed;
   bool has_tts_ = false;
 
-  base::WeakPtrFactory<AssistantResponse> weak_factory_;
+  std::unique_ptr<Processor> processor_;
 
   DISALLOW_COPY_AND_ASSIGN(AssistantResponse);
 };
diff --git a/ash/assistant/model/assistant_screen_context_model.h b/ash/assistant/model/assistant_screen_context_model.h
index a8b758c..93e27af 100644
--- a/ash/assistant/model/assistant_screen_context_model.h
+++ b/ash/assistant/model/assistant_screen_context_model.h
@@ -35,7 +35,7 @@
 
   ScreenContextRequestState request_state_ = ScreenContextRequestState::kIdle;
 
-  base::ObserverList<AssistantScreenContextModelObserver>::Unchecked observers_;
+  base::ObserverList<AssistantScreenContextModelObserver> observers_;
 
   DISALLOW_COPY_AND_ASSIGN(AssistantScreenContextModel);
 };
diff --git a/ash/assistant/model/assistant_screen_context_model_observer.h b/ash/assistant/model/assistant_screen_context_model_observer.h
index 2b814b6..a480798 100644
--- a/ash/assistant/model/assistant_screen_context_model_observer.h
+++ b/ash/assistant/model/assistant_screen_context_model_observer.h
@@ -6,12 +6,15 @@
 #define ASH_ASSISTANT_MODEL_ASSISTANT_SCREEN_CONTEXT_MODEL_OBSERVER_H_
 
 #include "base/macros.h"
+#include "base/observer_list_types.h"
 
 namespace ash {
 
 enum class ScreenContextRequestState;
 
-class AssistantScreenContextModelObserver {
+// A checked observer which receives notification of changes to the Assistant
+// screen context model state.
+class AssistantScreenContextModelObserver : public base::CheckedObserver {
  public:
   // Invoked when the screen context request state is changed.
   virtual void OnScreenContextRequestStateChanged(
@@ -19,7 +22,7 @@
 
  protected:
   AssistantScreenContextModelObserver() = default;
-  virtual ~AssistantScreenContextModelObserver() = default;
+  ~AssistantScreenContextModelObserver() override = default;
 
   DISALLOW_COPY_AND_ASSIGN(AssistantScreenContextModelObserver);
 };
diff --git a/ash/assistant/model/assistant_ui_element.cc b/ash/assistant/model/assistant_ui_element.cc
index 24fbe6e..b9762b59 100644
--- a/ash/assistant/model/assistant_ui_element.cc
+++ b/ash/assistant/model/assistant_ui_element.cc
@@ -4,8 +4,18 @@
 
 #include "ash/assistant/model/assistant_ui_element.h"
 
+#include "ash/assistant/ui/assistant_ui_constants.h"
+#include "base/base64.h"
+
 namespace ash {
 
+namespace {
+
+// Navigable contents.
+constexpr char kDataUriPrefix[] = "data:text/html;base64,";
+
+}  // namespace
+
 // AssistantCardElement --------------------------------------------------------
 
 AssistantCardElement::AssistantCardElement(const std::string& html,
@@ -16,4 +26,62 @@
 
 AssistantCardElement::~AssistantCardElement() = default;
 
+void AssistantCardElement::Process(
+    content::mojom::NavigableContentsFactory* contents_factory,
+    ProcessingCallback callback) {
+  processor_ =
+      std::make_unique<Processor>(*this, contents_factory, std::move(callback));
+  processor_->Process();
+}
+
+// AssistantCardElement::Processor ---------------------------------------------
+
+AssistantCardElement::Processor::Processor(
+    AssistantCardElement& card_element,
+    content::mojom::NavigableContentsFactory* contents_factory,
+    ProcessingCallback callback)
+    : card_element_(card_element),
+      contents_factory_(contents_factory),
+      callback_(std::move(callback)) {}
+
+AssistantCardElement::Processor::~Processor() {
+  if (contents_)
+    contents_->RemoveObserver(this);
+
+  if (callback_)
+    std::move(callback_).Run(/*success=*/false);
+}
+
+void AssistantCardElement::Processor::Process() {
+  // TODO(dmblack): Find a better way of determining desired card size.
+  const int width_dip = kPreferredWidthDip - 2 * kUiElementHorizontalMarginDip;
+
+  // Configure parameters for the card.
+  auto contents_params = content::mojom::NavigableContentsParams::New();
+  contents_params->enable_view_auto_resize = true;
+  contents_params->auto_resize_min_size = gfx::Size(width_dip, 1);
+  contents_params->auto_resize_max_size = gfx::Size(width_dip, INT_MAX);
+  contents_params->suppress_navigations = true;
+
+  contents_ = std::make_unique<content::NavigableContents>(
+      contents_factory_, std::move(contents_params));
+
+  // Observe |contents_| so that we are notified when loading is complete.
+  contents_->AddObserver(this);
+
+  // Navigate to the data URL which represents the card.
+  std::string encoded_html;
+  base::Base64Encode(card_element_.html(), &encoded_html);
+  contents_->Navigate(GURL(kDataUriPrefix + encoded_html));
+}
+
+void AssistantCardElement::Processor::DidStopLoading() {
+  contents_->RemoveObserver(this);
+
+  // Pass ownership of |contents_| to the card element that was being processed
+  // and notify our |callback_| of success.
+  card_element_.set_contents(std::move(contents_));
+  std::move(callback_).Run(/*success=*/true);
+}
+
 }  // namespace ash
diff --git a/ash/assistant/model/assistant_ui_element.h b/ash/assistant/model/assistant_ui_element.h
index 5492a35..4849163 100644
--- a/ash/assistant/model/assistant_ui_element.h
+++ b/ash/assistant/model/assistant_ui_element.h
@@ -44,6 +44,8 @@
 // An Assistant UI element that will be rendered as an HTML card.
 class AssistantCardElement : public AssistantUiElement {
  public:
+  using ProcessingCallback = base::OnceCallback<void(bool)>;
+
   explicit AssistantCardElement(const std::string& html,
                                 const std::string& fallback);
   ~AssistantCardElement() override;
@@ -59,12 +61,42 @@
     contents_ = std::move(contents);
   }
 
+  // Invoke to begin processing the card element. Upon completion, the specified
+  // |callback| will be run to indicate success or failure.
+  void Process(content::mojom::NavigableContentsFactory* contents_factory,
+               ProcessingCallback callback);
+
  private:
+  // Handles processing of an AssistantCardElement.
+  class Processor : public content::NavigableContentsObserver {
+   public:
+    Processor(AssistantCardElement& card_element,
+              content::mojom::NavigableContentsFactory* contents_factory,
+              ProcessingCallback callback);
+    ~Processor() override;
+
+    // Invoke to begin processing.
+    void Process();
+
+    // content::NavigableContentsObserver:
+    void DidStopLoading() override;
+
+   private:
+    AssistantCardElement& card_element_;
+    content::mojom::NavigableContentsFactory* const contents_factory_;
+    ProcessingCallback callback_;
+
+    std::unique_ptr<content::NavigableContents> contents_;
+
+    DISALLOW_COPY_AND_ASSIGN(Processor);
+  };
+
   const std::string html_;
   const std::string fallback_;
-
   std::unique_ptr<content::NavigableContents> contents_;
 
+  std::unique_ptr<Processor> processor_;
+
   DISALLOW_COPY_AND_ASSIGN(AssistantCardElement);
 };
 
diff --git a/ash/assistant/model/assistant_ui_model.h b/ash/assistant/model/assistant_ui_model.h
index 93ecfc7..6735e6c 100644
--- a/ash/assistant/model/assistant_ui_model.h
+++ b/ash/assistant/model/assistant_ui_model.h
@@ -86,7 +86,7 @@
 
   AssistantSource entry_point_ = AssistantSource::kUnspecified;
 
-  base::ObserverList<AssistantUiModelObserver>::Unchecked observers_;
+  base::ObserverList<AssistantUiModelObserver> observers_;
 
   // Usable work area for Assistant. Value is only meaningful when Assistant
   // UI exists.
diff --git a/ash/assistant/model/assistant_ui_model_observer.h b/ash/assistant/model/assistant_ui_model_observer.h
index ee7bad3..891de0a 100644
--- a/ash/assistant/model/assistant_ui_model_observer.h
+++ b/ash/assistant/model/assistant_ui_model_observer.h
@@ -6,6 +6,7 @@
 #define ASH_ASSISTANT_MODEL_ASSISTANT_UI_MODEL_OBSERVER_H_
 
 #include "base/macros.h"
+#include "base/observer_list_types.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace ash {
@@ -14,8 +15,9 @@
 enum class AssistantUiMode;
 enum class AssistantVisibility;
 
-// An observer which receives notification of changes to the Assistant UI model.
-class AssistantUiModelObserver {
+// A checked observer which receives notification of changes to the Assistant UI
+// model.
+class AssistantUiModelObserver : public base::CheckedObserver {
  public:
   // Invoked when the UI mode is changed.
   virtual void OnUiModeChanged(AssistantUiMode ui_mode) {}
@@ -34,7 +36,7 @@
 
  protected:
   AssistantUiModelObserver() = default;
-  virtual ~AssistantUiModelObserver() = default;
+  ~AssistantUiModelObserver() override = default;
 
   DISALLOW_COPY_AND_ASSIGN(AssistantUiModelObserver);
 };
diff --git a/ash/assistant/ui/assistant_web_view.cc b/ash/assistant/ui/assistant_web_view.cc
index d8a09b4..bf7b63e 100644
--- a/ash/assistant/ui/assistant_web_view.cc
+++ b/ash/assistant/ui/assistant_web_view.cc
@@ -182,7 +182,7 @@
   contents_->AddObserver(this);
 
   // Navigate to the url associated with the received deep link.
-  contents_->Navigate(assistant::util::GetWebUrl(type).value());
+  contents_->Navigate(assistant::util::GetWebUrl(type, params).value());
 }
 
 void AssistantWebView::DidAutoResizeView(const gfx::Size& new_size) {
diff --git a/ash/assistant/ui/dialog_plate/dialog_plate.cc b/ash/assistant/ui/dialog_plate/dialog_plate.cc
index b009bfd..7fa05ea6 100644
--- a/ash/assistant/ui/dialog_plate/dialog_plate.cc
+++ b/ash/assistant/ui/dialog_plate/dialog_plate.cc
@@ -69,7 +69,11 @@
               base::Unretained(this)),
           /*end_animation_callback=*/base::BindRepeating(
               &DialogPlate::OnAnimationEnded,
-              base::Unretained(this)))) {
+              base::Unretained(this)))),
+      query_history_iterator_(assistant_controller_->interaction_controller()
+                                  ->model()
+                                  ->query_history()
+                                  .GetIterator()) {
   InitLayout();
 
   // The Assistant controller indirectly owns the view hierarchy to which
@@ -109,30 +113,43 @@
 
 bool DialogPlate::HandleKeyEvent(views::Textfield* textfield,
                                  const ui::KeyEvent& key_event) {
-  if (key_event.key_code() != ui::KeyboardCode::VKEY_RETURN)
-    return false;
-
   if (key_event.type() != ui::EventType::ET_KEY_PRESSED)
     return false;
 
-  // In tablet mode the virtual keyboard should not be sticky, so we hide it
-  // when committing a query.
-  if (IsTabletMode())
-    textfield_->GetFocusManager()->ClearFocus();
+  switch (key_event.key_code()) {
+    case ui::KeyboardCode::VKEY_RETURN: {
+      // In tablet mode the virtual keyboard should not be sticky, so we hide it
+      // when committing a query.
+      if (IsTabletMode())
+        textfield_->GetFocusManager()->ClearFocus();
 
-  const base::StringPiece16& trimmed_text =
-      base::TrimWhitespace(textfield_->text(), base::TrimPositions::TRIM_ALL);
+      const base::StringPiece16& trimmed_text = base::TrimWhitespace(
+          textfield_->text(), base::TrimPositions::TRIM_ALL);
 
-  // Only non-empty trimmed text is consider a valid contents commit. Anything
-  // else will simply result in the DialogPlate being cleared.
-  if (!trimmed_text.empty()) {
-    for (DialogPlateObserver& observer : observers_)
-      observer.OnDialogPlateContentsCommitted(base::UTF16ToUTF8(trimmed_text));
+      // Only non-empty trimmed text is consider a valid contents commit.
+      // Anything else will simply result in the DialogPlate being cleared.
+      if (!trimmed_text.empty()) {
+        for (DialogPlateObserver& observer : observers_)
+          observer.OnDialogPlateContentsCommitted(
+              base::UTF16ToUTF8(trimmed_text));
+      }
+
+      textfield_->SetText(base::string16());
+
+      return true;
+    }
+    case ui::KeyboardCode::VKEY_UP:
+    case ui::KeyboardCode::VKEY_DOWN: {
+      DCHECK(query_history_iterator_);
+      auto opt_query = key_event.key_code() == ui::KeyboardCode::VKEY_UP
+                           ? query_history_iterator_->Prev()
+                           : query_history_iterator_->Next();
+      textfield_->SetText(base::UTF8ToUTF16(opt_query.value_or("")));
+      return true;
+    }
+    default:
+      return false;
   }
-
-  textfield_->SetText(base::string16());
-
-  return true;
 }
 
 void DialogPlate::OnInputModalityChanged(InputModality input_modality) {
@@ -219,6 +236,12 @@
   }
 }
 
+void DialogPlate::OnCommittedQueryChanged(
+    const AssistantQuery& committed_query) {
+  DCHECK(query_history_iterator_);
+  query_history_iterator_->ResetToLast();
+}
+
 void DialogPlate::OnUiVisibilityChanged(AssistantVisibility new_visibility,
                                         AssistantVisibility old_visibility,
                                         AssistantSource source) {
diff --git a/ash/assistant/ui/dialog_plate/dialog_plate.h b/ash/assistant/ui/dialog_plate/dialog_plate.h
index 53a0d86..e577ab3 100644
--- a/ash/assistant/ui/dialog_plate/dialog_plate.h
+++ b/ash/assistant/ui/dialog_plate/dialog_plate.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "ash/assistant/model/assistant_interaction_model_observer.h"
+#include "ash/assistant/model/assistant_query_history.h"
 #include "ash/assistant/model/assistant_ui_model_observer.h"
 #include "ash/assistant/ui/dialog_plate/action_view.h"
 #include "base/macros.h"
@@ -40,7 +41,7 @@
 
 // DialogPlateObserver ---------------------------------------------------------
 
-class DialogPlateObserver {
+class DialogPlateObserver : public base::CheckedObserver {
  public:
   // Invoked when the dialog plate button identified by |id| is pressed.
   virtual void OnDialogPlateButtonPressed(DialogPlateButtonId id) {}
@@ -49,7 +50,7 @@
   virtual void OnDialogPlateContentsCommitted(const std::string& text) {}
 
  protected:
-  virtual ~DialogPlateObserver() = default;
+  ~DialogPlateObserver() override = default;
 };
 
 // DialogPlate -----------------------------------------------------------------
@@ -87,6 +88,7 @@
 
   // AssistantInteractionModelObserver:
   void OnInputModalityChanged(InputModality input_modality) override;
+  void OnCommittedQueryChanged(const AssistantQuery& committed_query) override;
 
   // AssistantUiModelObserver:
   void OnUiVisibilityChanged(AssistantVisibility new_visibility,
@@ -120,8 +122,9 @@
   views::Textfield* textfield_;                      // Owned by view hierarchy.
 
   std::unique_ptr<ui::CallbackLayerAnimationObserver> animation_observer_;
+  std::unique_ptr<AssistantQueryHistory::Iterator> query_history_iterator_;
 
-  base::ObserverList<DialogPlateObserver>::Unchecked observers_;
+  base::ObserverList<DialogPlateObserver> observers_;
 
   DISALLOW_COPY_AND_ASSIGN(DialogPlate);
 };
diff --git a/ash/assistant/ui/main_stage/ui_element_container_view.h b/ash/assistant/ui/main_stage/ui_element_container_view.h
index ef7743d..c055789 100644
--- a/ash/assistant/ui/main_stage/ui_element_container_view.h
+++ b/ash/assistant/ui/main_stage/ui_element_container_view.h
@@ -10,7 +10,6 @@
 #include <utility>
 #include <vector>
 
-#include "ash/assistant/assistant_response_processor.h"
 #include "ash/assistant/model/assistant_interaction_model_observer.h"
 #include "ash/assistant/ui/base/assistant_scroll_view.h"
 #include "base/macros.h"
diff --git a/ash/assistant/util/deep_link_util.cc b/ash/assistant/util/deep_link_util.cc
index 9f7c648..ad6d7220 100644
--- a/ash/assistant/util/deep_link_util.cc
+++ b/ash/assistant/util/deep_link_util.cc
@@ -23,6 +23,7 @@
 
 // Supported deep link param keys. These values must be kept in sync with the
 // server. See more details at go/cros-assistant-deeplink.
+constexpr char kIdParamKey[] = "id";
 constexpr char kQueryParamKey[] = "q";
 constexpr char kPageParamKey[] = "page";
 constexpr char kRelaunchParamKey[] = "relaunch";
@@ -90,6 +91,7 @@
     DeepLinkParam param) {
   // Map of supported deep link params to their keys.
   static const std::map<DeepLinkParam, std::string> kDeepLinkParamKeys = {
+      {DeepLinkParam::kId, kIdParamKey},
       {DeepLinkParam::kPage, kPageParamKey},
       {DeepLinkParam::kQuery, kQueryParamKey},
       {DeepLinkParam::kRelaunch, kRelaunchParamKey}};
@@ -145,6 +147,17 @@
   return GetDeepLinkType(url) != DeepLinkType::kUnsupported;
 }
 
+GURL GetAssistantRemindersUrl(const base::Optional<std::string>& id) {
+  // TODO(b/113357196): Make these URLs configurable for development purposes.
+  static constexpr char kAssistantRemindersWebUrl[] =
+      "https://assistant.google.com/reminders/mainview";
+  static constexpr char kAssistantRemindersByIdWebUrl[] =
+      "https://assistant.google.com/reminders/id/";
+  return (id && !id.value().empty())
+             ? CreateLocalizedGURL(kAssistantRemindersByIdWebUrl + id.value())
+             : CreateLocalizedGURL(kAssistantRemindersWebUrl);
+}
+
 GURL GetChromeSettingsUrl(const base::Optional<std::string>& page) {
   static constexpr char kChromeSettingsUrl[] = "chrome://settings/";
 
@@ -161,13 +174,13 @@
 }
 
 base::Optional<GURL> GetWebUrl(const GURL& deep_link) {
-  return GetWebUrl(GetDeepLinkType(deep_link));
+  return GetWebUrl(GetDeepLinkType(deep_link), GetDeepLinkParams(deep_link));
 }
 
-base::Optional<GURL> GetWebUrl(DeepLinkType type) {
+base::Optional<GURL> GetWebUrl(
+    DeepLinkType type,
+    const std::map<std::string, std::string>& params) {
   // TODO(b/113357196): Make these URLs configurable for development purposes.
-  static constexpr char kAssistantRemindersWebUrl[] =
-      "https://assistant.google.com/reminders/mainview";
   static constexpr char kAssistantSettingsWebUrl[] =
       "https://assistant.google.com/settings/mainpage";
 
@@ -176,7 +189,8 @@
 
   switch (type) {
     case DeepLinkType::kReminders:
-      return CreateLocalizedGURL(kAssistantRemindersWebUrl);
+      return GetAssistantRemindersUrl(
+          GetDeepLinkParam(params, DeepLinkParam::kId));
     case DeepLinkType::kSettings:
       return CreateLocalizedGURL(kAssistantSettingsWebUrl);
     case DeepLinkType::kUnsupported:
diff --git a/ash/assistant/util/deep_link_util.h b/ash/assistant/util/deep_link_util.h
index 30f4f50..4400724 100644
--- a/ash/assistant/util/deep_link_util.h
+++ b/ash/assistant/util/deep_link_util.h
@@ -33,6 +33,7 @@
 
 // Enumeration of deep link parameters.
 enum class DeepLinkParam {
+  kId,
   kPage,
   kQuery,
   kRelaunch,
@@ -72,6 +73,10 @@
 // Returns true if the specified |url| is a deep link, false otherwise.
 ASH_EXPORT bool IsDeepLinkUrl(const GURL& url);
 
+// Returns the URL for the specified Assistant reminder |id|. If id is absent,
+// the returned URL will be for top-level Assistant Reminders.
+ASH_EXPORT GURL GetAssistantRemindersUrl(const base::Optional<std::string>& id);
+
 // Returns the URL for the specified Chrome Settings |page|. If page is absent
 // or not allowed, the URL will be for top-level Chrome Settings.
 ASH_EXPORT GURL GetChromeSettingsUrl(const base::Optional<std::string>& page);
@@ -81,10 +86,12 @@
 // IsWebDeepLink(GURL) API.
 ASH_EXPORT base::Optional<GURL> GetWebUrl(const GURL& deep_link);
 
-// Returns the web URL for a deep link of the specified |type|. A return value
-// will only be present if the deep link type is a web deep link type as
-// identified by the IsWebDeepLinkType(DeepLinkType) API.
-ASH_EXPORT base::Optional<GURL> GetWebUrl(DeepLinkType type);
+// Returns the web URL for a deep link of the specified |type| with the given
+// |params|. A return value will only be present if the deep link type is a web
+// deep link type as identified by the IsWebDeepLinkType(DeepLinkType) API.
+ASH_EXPORT base::Optional<GURL> GetWebUrl(
+    DeepLinkType type,
+    const std::map<std::string, std::string>& params);
 
 // Returns true if the specified |deep_link| is a web deep link.
 ASH_EXPORT bool IsWebDeepLink(const GURL& deep_link);
diff --git a/ash/assistant/util/deep_link_util_unittest.cc b/ash/assistant/util/deep_link_util_unittest.cc
index e4cacbe..2d4e034 100644
--- a/ash/assistant/util/deep_link_util_unittest.cc
+++ b/ash/assistant/util/deep_link_util_unittest.cc
@@ -262,6 +262,22 @@
     ASSERT_EQ(test_case.second, IsDeepLinkUrl(GURL(test_case.first)));
 }
 
+TEST_F(DeepLinkUnitTest, GetAssistantRemindersUrl) {
+  const std::map<base::Optional<std::string>, std::string> test_cases = {
+      // OK: Absent/empty id.
+      {base::nullopt,
+       "https://assistant.google.com/reminders/mainview?hl=en-US"},
+      {base::Optional<std::string>(std::string()),
+       "https://assistant.google.com/reminders/mainview?hl=en-US"},
+
+      // OK: Specified id.
+      {base::Optional<std::string>("123456"),
+       "https://assistant.google.com/reminders/id/123456?hl=en-US"}};
+
+  for (const auto& test_case : test_cases)
+    ASSERT_EQ(test_case.second, GetAssistantRemindersUrl(test_case.first));
+}
+
 TEST_F(DeepLinkUnitTest, GetChromeSettingsUrl) {
   const std::map<base::Optional<std::string>, std::string> test_cases = {
       // OK: Absent/empty page.
@@ -293,8 +309,8 @@
        GURL("https://assistant.google.com/settings/mainpage?hl=en-US")},
 
       // OK: Parameterized deep links.
-      {"googleassistant://reminders?param=true",
-       GURL("https://assistant.google.com/reminders/mainview?hl=en-US")},
+      {"googleassistant://reminders?id=123456",
+       GURL("https://assistant.google.com/reminders/id/123456?hl=en-US")},
       {"googleassistant://settings?param=true",
        GURL("https://assistant.google.com/settings/mainpage?hl=en-US")},
 
@@ -329,28 +345,51 @@
 }
 
 TEST_F(DeepLinkUnitTest, GetWebUrlByType) {
-  const std::map<DeepLinkType, base::Optional<GURL>> test_cases = {
+  using DeepLinkParams = std::map<std::string, std::string>;
+  using TestCase = std::pair<DeepLinkType, DeepLinkParams>;
+
+  // Creates a test case with a single parameter.
+  auto CreateTestCaseWithParam =
+      [](DeepLinkType type,
+         base::Optional<std::pair<std::string, std::string>> param =
+             base::nullopt) {
+        DeepLinkParams params;
+        if (param)
+          params.insert(param.value());
+        return std::make_pair(type, params);
+      };
+
+  // Creates a test case with no parameters.
+  auto CreateTestCase = [&CreateTestCaseWithParam](DeepLinkType type) {
+    return CreateTestCaseWithParam(type);
+  };
+
+  const std::map<TestCase, base::Optional<GURL>> test_cases = {
       // OK: Supported web deep link types.
-      {DeepLinkType::kReminders,
+      {CreateTestCase(DeepLinkType::kReminders),
        GURL("https://assistant.google.com/reminders/mainview?hl=en-US")},
-      {DeepLinkType::kSettings,
+      {CreateTestCaseWithParam(DeepLinkType::kReminders,
+                               std::make_pair("id", "123456")),
+       GURL("https://assistant.google.com/reminders/id/123456?hl=en-US")},
+      {CreateTestCase(DeepLinkType::kSettings),
        GURL("https://assistant.google.com/settings/mainpage?hl=en-US")},
 
       // FAIL: Non-web deep link types.
-      {DeepLinkType::kChromeSettings, base::nullopt},
-      {DeepLinkType::kFeedback, base::nullopt},
-      {DeepLinkType::kOnboarding, base::nullopt},
-      {DeepLinkType::kQuery, base::nullopt},
-      {DeepLinkType::kScreenshot, base::nullopt},
-      {DeepLinkType::kTaskManager, base::nullopt},
-      {DeepLinkType::kWhatsOnMyScreen, base::nullopt},
+      {CreateTestCase(DeepLinkType::kChromeSettings), base::nullopt},
+      {CreateTestCase(DeepLinkType::kFeedback), base::nullopt},
+      {CreateTestCase(DeepLinkType::kOnboarding), base::nullopt},
+      {CreateTestCase(DeepLinkType::kQuery), base::nullopt},
+      {CreateTestCase(DeepLinkType::kScreenshot), base::nullopt},
+      {CreateTestCase(DeepLinkType::kTaskManager), base::nullopt},
+      {CreateTestCase(DeepLinkType::kWhatsOnMyScreen), base::nullopt},
 
       // FAIL: Unsupported deep link types.
-      {DeepLinkType::kUnsupported, base::nullopt}};
+      {CreateTestCase(DeepLinkType::kUnsupported), base::nullopt}};
 
   for (const auto& test_case : test_cases) {
     const base::Optional<GURL>& expected = test_case.second;
-    const base::Optional<GURL> actual = GetWebUrl(test_case.first);
+    const base::Optional<GURL> actual = GetWebUrl(
+        /*type=*/test_case.first.first, /*params=*/test_case.first.second);
 
     // Assert |has_value| equivalence.
     ASSERT_EQ(expected, actual);
diff --git a/ash/components/fast_ink/fast_ink_view.cc b/ash/components/fast_ink/fast_ink_view.cc
index 37e41f7..7cc54632 100644
--- a/ash/components/fast_ink/fast_ink_view.cc
+++ b/ash/components/fast_ink/fast_ink_view.cc
@@ -154,6 +154,8 @@
     frame.metadata.begin_frame_ack.has_damage = true;
     frame.metadata.device_scale_factor =
         holder->last_frame_device_scale_factor_;
+    frame.metadata.local_surface_id_allocation_time =
+        holder->last_local_surface_id_allocation_time_;
     std::unique_ptr<viz::RenderPass> pass = viz::RenderPass::Create();
     pass->SetNew(1, gfx::Rect(holder->last_frame_size_in_pixels_),
                  gfx::Rect(holder->last_frame_size_in_pixels_),
@@ -189,6 +191,8 @@
     exported_resources_[resource_id] = std::move(resource);
     last_frame_size_in_pixels_ = frame.size_in_pixels();
     last_frame_device_scale_factor_ = frame.metadata.device_scale_factor;
+    last_local_surface_id_allocation_time_ =
+        frame.metadata.local_surface_id_allocation_time;
     frame_sink_->SubmitCompositorFrame(std::move(frame),
                                        /*show_hit_test_borders=*/false);
   }
@@ -268,6 +272,7 @@
       exported_resources_;
   gfx::Size last_frame_size_in_pixels_;
   float last_frame_device_scale_factor_ = 1.0f;
+  base::TimeTicks last_local_surface_id_allocation_time_;
   aura::Window* root_window_ = nullptr;
   bool delete_pending_ = false;
 
@@ -487,6 +492,8 @@
   frame.metadata.begin_frame_ack =
       viz::BeginFrameAck::CreateManualAckWithDamage();
   frame.metadata.device_scale_factor = device_scale_factor;
+  frame.metadata.local_surface_id_allocation_time =
+      widget_->GetNativeView()->GetLocalSurfaceIdAllocation().allocation_time();
 
   if (!presentation_callback_.is_null()) {
     // If overflow happens, we increase it again.
@@ -501,14 +508,14 @@
   float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
   gfx::RectF uv_crop(quad_rect);
   uv_crop.Scale(1.f / buffer_size_.width(), 1.f / buffer_size_.height());
-  texture_quad->SetNew(quad_state, quad_rect, quad_rect,
-                       /*needs_blending=*/true, transferable_resource.id,
-                       /*premultiplied_alpha=*/true, uv_crop.origin(),
-                       uv_crop.bottom_right(),
-                       /*background_color=*/SK_ColorTRANSPARENT, vertex_opacity,
-                       /*y_flipped=*/false,
-                       /*nearest_neighbor=*/false,
-                       /*secure_output_only=*/false);
+  texture_quad->SetNew(
+      quad_state, quad_rect, quad_rect,
+      /*needs_blending=*/true, transferable_resource.id,
+      /*premultiplied_alpha=*/true, uv_crop.origin(), uv_crop.bottom_right(),
+      /*background_color=*/SK_ColorTRANSPARENT, vertex_opacity,
+      /*y_flipped=*/false,
+      /*nearest_neighbor=*/false,
+      /*secure_output_only=*/false, ui::ProtectedVideoType::kClear);
   texture_quad->set_resource_size_in_pixels(transferable_resource.size);
   frame.resource_list.push_back(transferable_resource);
 
diff --git a/ash/components/quick_launch/main.cc b/ash/components/quick_launch/main.cc
index 4b379b61..83c4d3e 100644
--- a/ash/components/quick_launch/main.cc
+++ b/ash/components/quick_launch/main.cc
@@ -2,14 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/components/quick_launch/quick_launch_application.h"
 #include "services/service_manager/public/c/main.h"
-#include "services/service_manager/public/cpp/service_runner.h"
+#include "ash/components/quick_launch/quick_launch_application.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "services/service_manager/public/mojom/service.mojom.h"
 
 MojoResult ServiceMain(MojoHandle service_request_handle) {
-  quick_launch::QuickLaunchApplication* app =
-      new quick_launch::QuickLaunchApplication;
-  app->set_running_standalone(true);
-  service_manager::ServiceRunner runner(app);
-  return runner.Run(service_request_handle);
+  base::MessageLoop message_loop;
+  base::RunLoop run_loop;
+  quick_launch::QuickLaunchApplication service{
+      service_manager::mojom::ServiceRequest(mojo::MakeScopedHandle(
+          mojo::MessagePipeHandle(service_request_handle)))};
+  service.set_running_standalone(true);
+  service.set_termination_closure(run_loop.QuitClosure());
+  run_loop.Run();
+  return MOJO_RESULT_OK;
 }
diff --git a/ash/components/quick_launch/quick_launch_application.cc b/ash/components/quick_launch/quick_launch_application.cc
index 166b3ca..97e906a 100644
--- a/ash/components/quick_launch/quick_launch_application.cc
+++ b/ash/components/quick_launch/quick_launch_application.cc
@@ -14,11 +14,8 @@
 #include "mash/public/mojom/launchable.mojom.h"
 #include "services/catalog/public/mojom/catalog.mojom.h"
 #include "services/catalog/public/mojom/constants.mojom.h"
-#include "services/service_manager/public/c/main.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/cpp/service.h"
-#include "services/service_manager/public/cpp/service_context.h"
-#include "services/service_manager/public/cpp/service_runner.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/views/background.h"
@@ -155,7 +152,9 @@
 
 }  // namespace
 
-QuickLaunchApplication::QuickLaunchApplication() = default;
+QuickLaunchApplication::QuickLaunchApplication(
+    service_manager::mojom::ServiceRequest request)
+    : service_binding_(this, std::move(request)) {}
 
 QuickLaunchApplication::~QuickLaunchApplication() {
   if (window_)
@@ -164,21 +163,21 @@
 
 void QuickLaunchApplication::Quit() {
   window_ = nullptr;
-  context()->QuitNow();
+  Terminate();
 }
 
 void QuickLaunchApplication::OnStart() {
   // If AuraInit was unable to initialize there is no longer a peer connection.
   // The ServiceManager is in the process of shutting down, however we haven't
-  // been notified yet. Close our ServiceContext and shutdown.
+  // been notified yet. We just self-terminate in this case.
   views::AuraInit::InitParams params;
-  params.connector = context()->connector();
-  params.identity = context()->identity();
+  params.connector = service_binding_.GetConnector();
+  params.identity = service_binding_.identity();
   params.register_path_provider = running_standalone_;
   params.use_accessibility_host = true;
   aura_init_ = views::AuraInit::Create(params);
   if (!aura_init_) {
-    context()->QuitNow();
+    Terminate();
     return;
   }
 
@@ -186,10 +185,12 @@
   ash::ash_client::Init();
 
   catalog::mojom::CatalogPtr catalog;
-  context()->connector()->BindInterface(catalog::mojom::kServiceName, &catalog);
+  service_binding_.GetConnector()->BindInterface(catalog::mojom::kServiceName,
+                                                 &catalog);
 
   window_ = views::Widget::CreateWindowWithContextAndBounds(
-      new QuickLaunchUI(this, context()->connector(), std::move(catalog)),
+      new QuickLaunchUI(this, service_binding_.GetConnector(),
+                        std::move(catalog)),
       nullptr, gfx::Rect(10, 640, 0, 0));
   window_->GetNativeWindow()->GetHost()->window()->SetName("QuickLaunch");
   window_->Show();
diff --git a/ash/components/quick_launch/quick_launch_application.h b/ash/components/quick_launch/quick_launch_application.h
index 1dfb91d..d7a01c8 100644
--- a/ash/components/quick_launch/quick_launch_application.h
+++ b/ash/components/quick_launch/quick_launch_application.h
@@ -10,6 +10,7 @@
 #include "base/macros.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/public/cpp/service.h"
+#include "services/service_manager/public/cpp/service_binding.h"
 
 namespace views {
 class AuraInit;
@@ -20,7 +21,8 @@
 
 class QuickLaunchApplication : public service_manager::Service {
  public:
-  QuickLaunchApplication();
+  explicit QuickLaunchApplication(
+      service_manager::mojom::ServiceRequest request);
   ~QuickLaunchApplication() override;
 
   void Quit();
@@ -34,6 +36,8 @@
                        const std::string& interface_name,
                        mojo::ScopedMessagePipeHandle interface_pipe) override;
 
+  service_manager::ServiceBinding service_binding_;
+
   views::Widget* window_ = nullptr;
 
   service_manager::BinderRegistry registry_;
diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc
index 48de7e7..b8cb749 100644
--- a/ash/display/display_manager_unittest.cc
+++ b/ash/display/display_manager_unittest.cc
@@ -3037,7 +3037,8 @@
   // the destruction of the Unified host when we switched to mirror mode
   // asynchronously.
   auto* app_list_controller = Shell::Get()->app_list_controller();
-  EXPECT_TRUE(app_list_controller->IsHomeLauncherEnabledInTabletMode());
+  auto* tablet_mode_controller = Shell::Get()->tablet_mode_controller();
+  EXPECT_TRUE(tablet_mode_controller->IsTabletModeWindowManagerEnabled());
   EXPECT_TRUE(app_list_controller->IsVisible());
 
   // Exiting tablet mode should exit mirror mode and return back to Unified
@@ -3048,7 +3049,7 @@
   EXPECT_TRUE(display_manager()->IsInUnifiedMode());
 
   // Home Launcher should be dismissed.
-  EXPECT_FALSE(app_list_controller->IsHomeLauncherEnabledInTabletMode());
+  EXPECT_FALSE(tablet_mode_controller->IsTabletModeWindowManagerEnabled());
   EXPECT_FALSE(app_list_controller->IsVisible());
 }
 
diff --git a/ash/events/select_to_speak_event_handler_unittest.cc b/ash/events/select_to_speak_event_handler_unittest.cc
index 0e4cef1..7bfa88b 100644
--- a/ash/events/select_to_speak_event_handler_unittest.cc
+++ b/ash/events/select_to_speak_event_handler_unittest.cc
@@ -173,7 +173,7 @@
   EXPECT_FALSE(event_capturer_.last_key_event()->handled());
 
   gfx::Point click_location = gfx::Point(100, 12);
-  generator_->set_current_location(click_location);
+  generator_->set_current_screen_location(click_location);
   generator_->PressLeftButton();
   EXPECT_FALSE(event_capturer_.last_mouse_event());
 
@@ -196,7 +196,7 @@
 
   generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
   gfx::Point click_location = gfx::Point(100, 12);
-  generator_->set_current_location(click_location);
+  generator_->set_current_screen_location(click_location);
   generator_->PressLeftButton();
 
   EXPECT_EQ(click_location, GetDelegate()->last_mouse_event_location());
@@ -224,7 +224,7 @@
 
   generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
   gfx::Point click_location_px = gfx::Point(100, 12);
-  generator_->set_current_location(click_location_px);
+  generator_->set_current_screen_location(click_location_px);
   generator_->PressLeftButton();
   EXPECT_EQ(gfx::Point(click_location_px.x() / 2, click_location_px.y() / 2),
             GetDelegate()->last_mouse_event_location());
@@ -251,7 +251,7 @@
   generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
   generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
 
-  generator_->set_current_location(gfx::Point(100, 12));
+  generator_->set_current_screen_location(gfx::Point(100, 12));
   generator_->PressLeftButton();
   EXPECT_FALSE(event_capturer_.last_mouse_event());
 
@@ -290,7 +290,7 @@
   ASSERT_TRUE(event_capturer_.last_key_event());
   EXPECT_FALSE(event_capturer_.last_key_event()->handled());
 
-  generator_->set_current_location(gfx::Point(100, 12));
+  generator_->set_current_screen_location(gfx::Point(100, 12));
   generator_->PressLeftButton();
   EXPECT_FALSE(event_capturer_.last_mouse_event());
   EXPECT_TRUE(GetDelegate()->CapturedMouseEvent(ui::ET_MOUSE_PRESSED));
@@ -329,7 +329,7 @@
   ASSERT_TRUE(event_capturer_.last_key_event());
   EXPECT_FALSE(event_capturer_.last_key_event()->handled());
 
-  generator_->set_current_location(gfx::Point(100, 12));
+  generator_->set_current_screen_location(gfx::Point(100, 12));
   generator_->PressLeftButton();
   ASSERT_TRUE(event_capturer_.last_mouse_event());
   EXPECT_FALSE(event_capturer_.last_mouse_event()->handled());
@@ -435,7 +435,7 @@
 
   // Mouse event still captured.
   gfx::Point click_location = gfx::Point(100, 12);
-  generator_->set_current_location(click_location);
+  generator_->set_current_screen_location(click_location);
   generator_->PressLeftButton();
   EXPECT_FALSE(event_capturer_.last_mouse_event());
 
@@ -456,7 +456,7 @@
        CancelSearchKeyUpAfterEarlyInactiveStateChange) {
   generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
   gfx::Point click_location = gfx::Point(100, 12);
-  generator_->set_current_location(click_location);
+  generator_->set_current_screen_location(click_location);
   generator_->PressLeftButton();
   EXPECT_FALSE(event_capturer_.last_mouse_event());
   EXPECT_TRUE(GetDelegate()->CapturedMouseEvent(ui::ET_MOUSE_PRESSED));
@@ -479,7 +479,7 @@
 
 TEST_F(SelectToSpeakEventHandlerTest, SelectionRequestedWorksWithMouse) {
   gfx::Point click_location = gfx::Point(100, 12);
-  generator_->set_current_location(click_location);
+  generator_->set_current_screen_location(click_location);
 
   // Mouse events are let through normally before entering selecting state.
   // Another mouse event is let through normally.
@@ -521,7 +521,7 @@
 
 TEST_F(SelectToSpeakEventHandlerTest, SelectionRequestedWorksWithTouch) {
   gfx::Point touch_location = gfx::Point(100, 12);
-  generator_->set_current_location(touch_location);
+  generator_->set_current_screen_location(touch_location);
 
   // Mouse events are let through normally before entering selecting state.
   // Another mouse event is let through normally.
@@ -600,7 +600,7 @@
 TEST_F(SelectToSpeakEventHandlerTest, TrackingTouchIgnoresOtherTouchPointers) {
   gfx::Point touch_location = gfx::Point(100, 12);
   gfx::Point drag_location = gfx::Point(120, 32);
-  generator_->set_current_location(touch_location);
+  generator_->set_current_screen_location(touch_location);
   controller_->SetSelectToSpeakState(
       mojom::SelectToSpeakState::kSelectToSpeakStateSelecting);
 
diff --git a/ash/extended_desktop_unittest.cc b/ash/extended_desktop_unittest.cc
index 61d5943..d31752d 100644
--- a/ash/extended_desktop_unittest.cc
+++ b/ash/extended_desktop_unittest.cc
@@ -435,7 +435,7 @@
   ui::test::EventGenerator* generator = GetEventGenerator();
   generator->MoveMouseToCenterOf(r2_w1.get());
   EXPECT_EQ(gfx::Point(1060, 60).ToString(),
-            generator->current_location().ToString());
+            generator->current_screen_location().ToString());
 
   EventLocationHandler location_handler;
   r1_w1->AddPreTargetHandler(&location_handler);
@@ -467,7 +467,7 @@
   ui::test::EventGenerator* generator = GetEventGenerator();
   generator->MoveMouseToCenterOf(r2_w1.get());
   EXPECT_EQ(gfx::Point(560, 60).ToString(),
-            generator->current_location().ToString());
+            generator->current_screen_location().ToString());
 
   EventLocationHandler location_handler;
   r1_w1->AddPreTargetHandler(&location_handler);
@@ -499,7 +499,7 @@
   ui::test::EventGenerator* generator = GetEventGenerator();
   generator->MoveMouseToCenterOf(r2_w1.get());
   EXPECT_EQ(gfx::Point(1060, 60).ToString(),
-            generator->current_location().ToString());
+            generator->current_screen_location().ToString());
 
   EventLocationHandler location_handler;
   r1_w1->AddPreTargetHandler(&location_handler);
diff --git a/ash/keyboard/ash_keyboard_controller.cc b/ash/keyboard/ash_keyboard_controller.cc
index aa229330..376132a 100644
--- a/ash/keyboard/ash_keyboard_controller.cc
+++ b/ash/keyboard/ash_keyboard_controller.cc
@@ -14,6 +14,7 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/keyboard/keyboard_controller.h"
 #include "ui/keyboard/keyboard_ui.h"
+#include "ui/wm/core/coordinate_conversion.h"
 
 using keyboard::mojom::KeyboardConfig;
 using keyboard::mojom::KeyboardConfigPtr;
@@ -264,9 +265,22 @@
 
 void AshKeyboardController::OnKeyboardVisibleBoundsChanged(
     const gfx::Rect& bounds) {
-  observers_.ForAllPtrs([&bounds](mojom::KeyboardControllerObserver* observer) {
-    observer->OnKeyboardVisibleBoundsChanged(bounds);
-  });
+  // Pass the bounds in screen coordinates over mojo.
+  gfx::Rect screen_bounds = BoundsToScreen(bounds);
+  observers_.ForAllPtrs(
+      [&screen_bounds](mojom::KeyboardControllerObserver* observer) {
+        observer->OnKeyboardVisibleBoundsChanged(screen_bounds);
+      });
+}
+
+void AshKeyboardController::OnKeyboardWorkspaceOccludedBoundsChanged(
+    const gfx::Rect& bounds) {
+  // Pass the bounds in screen coordinates over mojo.
+  gfx::Rect screen_bounds = BoundsToScreen(bounds);
+  observers_.ForAllPtrs(
+      [&screen_bounds](mojom::KeyboardControllerObserver* observer) {
+        observer->OnKeyboardOccludedBoundsChanged(screen_bounds);
+      });
 }
 
 void AshKeyboardController::OnKeyboardEnableFlagsChanged(
@@ -285,4 +299,12 @@
       });
 }
 
+gfx::Rect AshKeyboardController::BoundsToScreen(const gfx::Rect& bounds) {
+  DCHECK(keyboard_controller_->GetKeyboardWindow());
+  gfx::Rect screen_bounds(bounds);
+  ::wm::ConvertRectToScreen(keyboard_controller_->GetKeyboardWindow(),
+                            &screen_bounds);
+  return screen_bounds;
+}
+
 }  // namespace ash
diff --git a/ash/keyboard/ash_keyboard_controller.h b/ash/keyboard/ash_keyboard_controller.h
index 94d109f..4bdef53 100644
--- a/ash/keyboard/ash_keyboard_controller.h
+++ b/ash/keyboard/ash_keyboard_controller.h
@@ -109,11 +109,15 @@
   void OnKeyboardConfigChanged() override;
   void OnKeyboardVisibilityStateChanged(bool is_visible) override;
   void OnKeyboardVisibleBoundsChanged(const gfx::Rect& bounds) override;
+  void OnKeyboardWorkspaceOccludedBoundsChanged(
+      const gfx::Rect& bounds) override;
   void OnKeyboardEnableFlagsChanged(
       std::set<keyboard::mojom::KeyboardEnableFlag>& keyboard_enable_flags)
       override;
   void OnKeyboardEnabledChanged(bool is_enabled) override;
 
+  gfx::Rect BoundsToScreen(const gfx::Rect& bounds);
+
   SessionController* session_controller_;  // unowned
   std::unique_ptr<keyboard::KeyboardController> keyboard_controller_;
   std::unique_ptr<VirtualKeyboardController> virtual_keyboard_controller_;
diff --git a/ash/keyboard/ash_keyboard_controller_unittest.cc b/ash/keyboard/ash_keyboard_controller_unittest.cc
index 650c52a..8667fe1 100644
--- a/ash/keyboard/ash_keyboard_controller_unittest.cc
+++ b/ash/keyboard/ash_keyboard_controller_unittest.cc
@@ -7,9 +7,11 @@
 #include <memory>
 #include <vector>
 
+#include "ash/public/cpp/test/test_keyboard_controller_observer.h"
 #include "ash/public/interfaces/keyboard_controller.mojom.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/test/ash_test_helper.h"
 #include "base/run_loop.h"
 #include "base/stl_util.h"
 #include "base/test/scoped_task_environment.h"
@@ -28,54 +30,10 @@
 
 namespace {
 
-class TestObserver : public mojom::KeyboardControllerObserver {
- public:
-  explicit TestObserver(mojom::KeyboardController* controller) {
-    mojom::KeyboardControllerObserverAssociatedPtrInfo ptr_info;
-    keyboard_controller_observer_binding_.Bind(mojo::MakeRequest(&ptr_info));
-    controller->AddObserver(std::move(ptr_info));
-  }
-  ~TestObserver() override = default;
-
-  // mojom::KeyboardControllerObserver:
-  void OnKeyboardEnableFlagsChanged(
-      const std::vector<keyboard::mojom::KeyboardEnableFlag>& flags) override {
-    enable_flags_ = flags;
-  }
-  void OnKeyboardEnabledChanged(bool enabled) override {
-    if (!enabled)
-      ++destroyed_count_;
-  }
-  void OnKeyboardVisibilityChanged(bool visible) override {}
-  void OnKeyboardVisibleBoundsChanged(const gfx::Rect& bounds) override {}
-  void OnKeyboardConfigChanged(KeyboardConfigPtr config) override {
-    config_ = *config;
-  }
-
-  const KeyboardConfig& config() const { return config_; }
-  void set_config(const KeyboardConfig& config) { config_ = config; }
-  const std::vector<keyboard::mojom::KeyboardEnableFlag>& enable_flags() const {
-    return enable_flags_;
-  }
-  int destroyed_count() const { return destroyed_count_; }
-
- private:
-  std::vector<keyboard::mojom::KeyboardEnableFlag> enable_flags_;
-  KeyboardConfig config_;
-  int destroyed_count_ = 0;
-  mojo::AssociatedBinding<mojom::KeyboardControllerObserver>
-      keyboard_controller_observer_binding_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(TestObserver);
-};
-
 class TestClient {
  public:
   explicit TestClient(service_manager::Connector* connector) {
     connector->BindInterface("test", &keyboard_controller_ptr_);
-
-    test_observer_ =
-        std::make_unique<TestObserver>(keyboard_controller_ptr_.get());
   }
 
   ~TestClient() = default;
@@ -181,7 +139,6 @@
     keyboard_controller_ptr_.FlushForTesting();
   }
 
-  TestObserver* test_observer() const { return test_observer_.get(); }
   int got_keyboard_config_count() const { return got_keyboard_config_count_; }
   const KeyboardConfig& keyboard_config() const { return keyboard_config_; }
 
@@ -199,7 +156,6 @@
   int got_keyboard_config_count_ = 0;
   KeyboardConfig keyboard_config_;
   mojom::KeyboardControllerPtr keyboard_controller_ptr_;
-  std::unique_ptr<TestObserver> test_observer_;
 };
 
 class TestContainerBehavior : public keyboard::ContainerBehavior {
@@ -296,7 +252,7 @@
     test_client_ = std::make_unique<TestClient>(connector_.get());
 
     // Set the initial observer config to the client (default) config.
-    test_client_->test_observer()->set_config(test_client()->keyboard_config());
+    test_observer()->set_config(test_client()->keyboard_config());
   }
 
   void TearDown() override {
@@ -313,6 +269,9 @@
     return Shell::Get()->ash_keyboard_controller()->keyboard_controller();
   }
   TestClient* test_client() { return test_client_.get(); }
+  TestKeyboardControllerObserver* test_observer() {
+    return ash_test_helper()->test_keyboard_controller_observer();
+  }
 
  private:
   std::unique_ptr<service_manager::Connector> connector_;
@@ -337,7 +296,7 @@
   KeyboardConfigPtr config =
       KeyboardConfig::New(test_client()->keyboard_config());
   // Set the observer config to the client (default) config.
-  test_client()->test_observer()->set_config(*config);
+  test_observer()->set_config(*config);
 
   // Change the keyboard config.
   bool old_auto_complete = config->auto_complete;
@@ -349,8 +308,7 @@
   EXPECT_NE(old_auto_complete, test_client()->keyboard_config().auto_complete);
 
   // Test that the test observer received the change.
-  EXPECT_NE(old_auto_complete,
-            test_client()->test_observer()->config().auto_complete);
+  EXPECT_NE(old_auto_complete, test_observer()->config().auto_complete);
 }
 
 TEST_F(AshKeyboardControllerTest, EnableFlags) {
@@ -361,7 +319,7 @@
       test_client()->GetEnableFlags();
   EXPECT_TRUE(
       base::ContainsValue(enable_flags, KeyboardEnableFlag::kExtensionEnabled));
-  EXPECT_EQ(enable_flags, test_client()->test_observer()->enable_flags());
+  EXPECT_EQ(enable_flags, test_observer()->enable_flags());
   EXPECT_TRUE(test_client()->IsKeyboardEnabled());
 
   // Set the enable override to disable the keyboard.
@@ -371,7 +329,7 @@
       base::ContainsValue(enable_flags, KeyboardEnableFlag::kExtensionEnabled));
   EXPECT_TRUE(
       base::ContainsValue(enable_flags, KeyboardEnableFlag::kPolicyDisabled));
-  EXPECT_EQ(enable_flags, test_client()->test_observer()->enable_flags());
+  EXPECT_EQ(enable_flags, test_observer()->enable_flags());
   EXPECT_FALSE(test_client()->IsKeyboardEnabled());
 
   // Clear the enable override; should enable the keyboard.
@@ -381,28 +339,28 @@
       base::ContainsValue(enable_flags, KeyboardEnableFlag::kExtensionEnabled));
   EXPECT_FALSE(
       base::ContainsValue(enable_flags, KeyboardEnableFlag::kPolicyDisabled));
-  EXPECT_EQ(enable_flags, test_client()->test_observer()->enable_flags());
+  EXPECT_EQ(enable_flags, test_observer()->enable_flags());
   EXPECT_TRUE(test_client()->IsKeyboardEnabled());
 }
 
 TEST_F(AshKeyboardControllerTest, RebuildKeyboardIfEnabled) {
-  EXPECT_EQ(0, test_client()->test_observer()->destroyed_count());
+  EXPECT_EQ(0, test_observer()->destroyed_count());
 
   // Enable the keyboard.
   test_client()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
-  EXPECT_EQ(0, test_client()->test_observer()->destroyed_count());
+  EXPECT_EQ(0, test_observer()->destroyed_count());
 
   // Enable the keyboard again; this should not reload the keyboard.
   test_client()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
-  EXPECT_EQ(0, test_client()->test_observer()->destroyed_count());
+  EXPECT_EQ(0, test_observer()->destroyed_count());
 
   // Rebuild the keyboard. This should destroy the previous keyboard window.
   test_client()->RebuildKeyboardIfEnabled();
-  EXPECT_EQ(1, test_client()->test_observer()->destroyed_count());
+  EXPECT_EQ(1, test_observer()->destroyed_count());
 
   // Disable the keyboard. The keyboard window should be destroyed.
   test_client()->ClearEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
-  EXPECT_EQ(2, test_client()->test_observer()->destroyed_count());
+  EXPECT_EQ(2, test_observer()->destroyed_count());
 }
 
 TEST_F(AshKeyboardControllerTest, ShowAndHideKeyboard) {
@@ -417,8 +375,8 @@
   test_client()->HideKeyboard();
   EXPECT_FALSE(keyboard_controller()->GetKeyboardWindow()->IsVisible());
 
-  // TODO(stevenjb): Also use TestObserver and IsKeyboardVisible to test
-  // visibility changes. https://crbug.com/849995.
+  // TODO(stevenjb): Also use TestKeyboardControllerObserver and
+  // IsKeyboardVisible to test visibility changes. https://crbug.com/849995.
 }
 
 TEST_F(AshKeyboardControllerTest, SetContainerType) {
diff --git a/ash/keyboard/test_keyboard_ui.cc b/ash/keyboard/test_keyboard_ui.cc
index 3a6b6c1..b052ab2 100644
--- a/ash/keyboard/test_keyboard_ui.cc
+++ b/ash/keyboard/test_keyboard_ui.cc
@@ -48,7 +48,5 @@
 }
 
 void TestKeyboardUI::ReloadKeyboardIfNeeded() {}
-void TestKeyboardUI::InitInsets(const gfx::Rect& keyboard_bounds) {}
-void TestKeyboardUI::ResetInsets() {}
 
 }  // namespace ash
diff --git a/ash/keyboard/test_keyboard_ui.h b/ash/keyboard/test_keyboard_ui.h
index 58f6772..a2ea0d5 100644
--- a/ash/keyboard/test_keyboard_ui.h
+++ b/ash/keyboard/test_keyboard_ui.h
@@ -31,8 +31,6 @@
   // Overridden from keyboard::KeyboardUI:
   ui::InputMethod* GetInputMethod() override;
   void ReloadKeyboardIfNeeded() override;
-  void InitInsets(const gfx::Rect& keyboard_bounds) override;
-  void ResetInsets() override;
 
   aura::test::TestWindowDelegate delegate_;
   std::unique_ptr<aura::Window> keyboard_window_;
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 16c33721..9b65fa79 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -697,7 +697,7 @@
 
     auth_error_bubble_->ShowErrorBubble(
         container, big_view->auth_user()->password_view() /*anchor_view*/,
-        LoginBubble::kFlagPersistent);
+        true /*show_persistently*/);
   }
 }
 
@@ -816,7 +816,7 @@
   label->SetEnabledColor(SK_ColorWHITE);
   warning_banner_bubble_->ShowErrorBubble(
       label, CurrentBigUserView()->auth_user()->password_view() /*anchor_view*/,
-      LoginBubble::kFlagPersistent);
+      true /*show_persistently*/);
 }
 
 void LockContentsView::OnHideWarningBanner() {
@@ -975,7 +975,7 @@
 
   detachable_base_error_bubble_->ShowErrorBubble(
       label, CurrentBigUserView()->auth_user()->password_view() /*anchor_view*/,
-      LoginBubble::kFlagPersistent);
+      true /*show_persistently*/);
 
   // Remove the focus from the password field, to make user less likely to enter
   // the password without seeing the warning about detachable base change.
@@ -1475,7 +1475,8 @@
   }
 
   // http://crbug/866790: After Supervised Users are deprecated, remove this.
-  if (big_user->basic_user_info->type == user_manager::USER_TYPE_SUPERVISED) {
+  if (ash::features::IsSupervisedUserDeprecationNoticeEnabled() &&
+      big_user->basic_user_info->type == user_manager::USER_TYPE_SUPERVISED) {
     base::string16 message = l10n_util::GetStringUTF16(
         IDS_ASH_LOGIN_POD_LEGACY_SUPERVISED_EXPIRATION_WARNING);
     // Shows supervised user deprecation message as a persistent error bubble.
@@ -1489,7 +1490,7 @@
     supervised_user_deprecation_bubble_->ShowErrorBubble(
         label,
         CurrentBigUserView()->auth_user()->password_view() /*anchor_view*/,
-        LoginBubble::kFlagPersistent);
+        true /*show_persistently*/);
   }
 
   // The new auth user might have different last used detachable base - make
@@ -1595,7 +1596,7 @@
 
   auth_error_bubble_->ShowErrorBubble(
       container, big_view->auth_user()->password_view() /*anchor_view*/,
-      LoginBubble::kFlagsNone);
+      false /*show_persistently*/);
 }
 
 void LockContentsView::OnEasyUnlockIconHovered() {
diff --git a/ash/login/ui/lock_contents_view_unittest.cc b/ash/login/ui/lock_contents_view_unittest.cc
index d35d155..045f61c 100644
--- a/ash/login/ui/lock_contents_view_unittest.cc
+++ b/ash/login/ui/lock_contents_view_unittest.cc
@@ -78,8 +78,8 @@
   // Build lock screen with 1 user.
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
 
@@ -125,8 +125,8 @@
 TEST_F(LockContentsViewUnitTest, SingleUserCentered) {
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
 
@@ -148,8 +148,8 @@
 TEST_F(LockContentsViewUnitTest, SingleUserCenteredNoteActionEnabled) {
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
 
@@ -172,8 +172,8 @@
   // Build lock screen.
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   LockContentsView::TestApi lock_contents(contents);
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
   display::test::DisplayManagerTestApi display_manager_test_api(
@@ -232,8 +232,8 @@
   // Build lock screen with three users.
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   LockContentsView::TestApi lock_contents(contents);
   SetUserCount(3);
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
@@ -283,8 +283,8 @@
   // Build lock screen with extra small layout (> 6 users).
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(9);
   ScrollableUsersListView* users_list =
       LockContentsView::TestApi(contents).users_list();
@@ -320,8 +320,8 @@
   // Build lock screen with small layout (3-6 users).
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(4);
   ScrollableUsersListView* users_list =
       LockContentsView::TestApi(contents).users_list();
@@ -377,7 +377,7 @@
   LockContentsView* contents =
       LockScreen::TestApi(LockScreen::Get()).contents_view();
   ASSERT_NE(nullptr, contents);
-  LoadUsers(9);
+  SetUserCount(9);
 
   // Users list in extra small layout should adjust its height to parent.
   ScrollableUsersListView* users_list =
@@ -399,7 +399,7 @@
   LockContentsView* contents =
       LockScreen::TestApi(LockScreen::Get()).contents_view();
   ASSERT_NE(nullptr, contents);
-  LoadUsers(4);
+  SetUserCount(4);
   ScrollableUsersListView* users_list =
       LockContentsView::TestApi(contents).users_list();
 
@@ -435,8 +435,8 @@
   // Build lock screen with two users.
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   LockContentsView::TestApi test_api(contents);
   SetUserCount(2);
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
@@ -481,8 +481,8 @@
   // Build lock screen with five users.
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   LockContentsView::TestApi lock_contents(contents);
   SetUserCount(5);
   ScrollableUsersListView::TestApi users_list(lock_contents.users_list());
@@ -525,8 +525,8 @@
 TEST_F(LockContentsViewUnitTest, NoteActionButtonVisibilityChanges) {
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
   SetWidget(CreateWidgetWithContent(contents));
 
@@ -537,20 +537,20 @@
   EXPECT_TRUE(note_action_button->visible());
 
   // In kLaunching state, the note action button should not be visible.
-  data_dispatcher()->SetLockScreenNoteState(mojom::TrayActionState::kLaunching);
+  DataDispatcher()->SetLockScreenNoteState(mojom::TrayActionState::kLaunching);
   EXPECT_FALSE(note_action_button->visible());
 
   // In kActive state, the note action button should not be visible.
-  data_dispatcher()->SetLockScreenNoteState(mojom::TrayActionState::kActive);
+  DataDispatcher()->SetLockScreenNoteState(mojom::TrayActionState::kActive);
   EXPECT_FALSE(note_action_button->visible());
 
   // When moved back to kAvailable state, the note action button should become
   // visible again.
-  data_dispatcher()->SetLockScreenNoteState(mojom::TrayActionState::kAvailable);
+  DataDispatcher()->SetLockScreenNoteState(mojom::TrayActionState::kAvailable);
   EXPECT_TRUE(note_action_button->visible());
 
   // In kNotAvailable state, the note action button should not be visible.
-  data_dispatcher()->SetLockScreenNoteState(
+  DataDispatcher()->SetLockScreenNoteState(
       mojom::TrayActionState::kNotAvailable);
   EXPECT_FALSE(note_action_button->visible());
 }
@@ -559,8 +559,8 @@
 TEST_F(LockContentsViewUnitTest, NoteActionButtonBounds) {
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
 
@@ -572,7 +572,7 @@
 
   // When the note action becomes available, the note action button should be
   // shown.
-  data_dispatcher()->SetLockScreenNoteState(mojom::TrayActionState::kAvailable);
+  DataDispatcher()->SetLockScreenNoteState(mojom::TrayActionState::kAvailable);
   EXPECT_TRUE(test_api.note_action()->visible());
 
   // Verify the bounds of the note action button are as expected.
@@ -585,7 +585,7 @@
 
   // If the note action is disabled again, the note action button should be
   // hidden.
-  data_dispatcher()->SetLockScreenNoteState(
+  DataDispatcher()->SetLockScreenNoteState(
       mojom::TrayActionState::kNotAvailable);
   EXPECT_FALSE(test_api.note_action()->visible());
 }
@@ -595,8 +595,8 @@
 TEST_F(LockContentsViewUnitTest, NoteActionButtonBoundsInitiallyAvailable) {
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
 
@@ -613,7 +613,7 @@
             test_api.note_action()->GetBoundsInScreen());
 
   // If the note action is disabled, the note action button should be hidden.
-  data_dispatcher()->SetLockScreenNoteState(
+  DataDispatcher()->SetLockScreenNoteState(
       mojom::TrayActionState::kNotAvailable);
   EXPECT_FALSE(test_api.note_action()->visible());
 }
@@ -622,8 +622,8 @@
 TEST_F(LockContentsViewUnitTest, SystemInfoViewBounds) {
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
 
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
@@ -634,8 +634,8 @@
 
   // Verify that the system info view becomes visible and it doesn't block the
   // note action button.
-  data_dispatcher()->SetSystemInfo(true /*show_if_hidden*/, "Best version ever",
-                                   "Asset ID: 6666", "Bluetooth adapter");
+  DataDispatcher()->SetSystemInfo(true /*show_if_hidden*/, "Best version ever",
+                                  "Asset ID: 6666", "Bluetooth adapter");
   EXPECT_TRUE(test_api.system_info()->visible());
   EXPECT_TRUE(test_api.note_action()->visible());
   gfx::Size note_action_size = test_api.note_action()->GetPreferredSize();
@@ -645,7 +645,7 @@
 
   // Verify that if the note action is disabled, the system info view moves to
   // the right to fill the empty space.
-  data_dispatcher()->SetLockScreenNoteState(
+  DataDispatcher()->SetLockScreenNoteState(
       mojom::TrayActionState::kNotAvailable);
   EXPECT_FALSE(test_api.note_action()->visible());
   EXPECT_LT(widget_bounds.right() -
@@ -657,8 +657,8 @@
 TEST_F(LockContentsViewUnitTest, AltVShowsHiddenSystemInfo) {
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
 
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
@@ -668,9 +668,8 @@
 
   // Verify that the system info view does not become visible when given data
   // but show is false.
-  data_dispatcher()->SetSystemInfo(false /*show_if_hidden*/,
-                                   "Best version ever", "Asset ID: 6666",
-                                   "Bluetooth adapter");
+  DataDispatcher()->SetSystemInfo(false /*show_if_hidden*/, "Best version ever",
+                                  "Asset ID: 6666", "Bluetooth adapter");
   EXPECT_FALSE(test_api.system_info()->visible());
 
   // Alt-V shows hidden system info.
@@ -689,16 +688,16 @@
 TEST_F(LockContentsViewUnitTest, ShowRevealsHiddenSystemInfo) {
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
 
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
   LockContentsView::TestApi test_api(contents);
 
   auto set_system_info = [&](bool show_if_hidden) {
-    data_dispatcher()->SetSystemInfo(show_if_hidden, "Best version ever",
-                                     "Asset ID: 6666", "Bluetooth adapter");
+    DataDispatcher()->SetSystemInfo(show_if_hidden, "Best version ever",
+                                    "Asset ID: 6666", "Bluetooth adapter");
   };
 
   // Start with hidden system info.
@@ -718,8 +717,8 @@
 TEST_F(LockContentsViewUnitTest, EasyUnlockForceTooltipCreatesTooltipWidget) {
   auto* lock = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
 
   SetUserCount(1);
   SetWidget(CreateWidgetWithContent(lock));
@@ -733,14 +732,14 @@
   auto icon = mojom::EasyUnlockIconOptions::New();
   icon->icon = mojom::EasyUnlockIconId::LOCKED;
   icon->autoshow_tooltip = false;
-  data_dispatcher()->ShowEasyUnlockIcon(users()[0]->basic_user_info->account_id,
-                                        icon);
+  DataDispatcher()->ShowEasyUnlockIcon(users()[0]->basic_user_info->account_id,
+                                       icon);
   EXPECT_FALSE(test_api.tooltip_bubble()->IsVisible());
 
   // Show icon with |autoshow_tooltip| set to true. Tooltip bubble is shown.
   icon->autoshow_tooltip = true;
-  data_dispatcher()->ShowEasyUnlockIcon(users()[0]->basic_user_info->account_id,
-                                        icon);
+  DataDispatcher()->ShowEasyUnlockIcon(users()[0]->basic_user_info->account_id,
+                                       icon);
   EXPECT_TRUE(test_api.tooltip_bubble()->IsVisible());
 }
 
@@ -749,8 +748,8 @@
   // Build lock screen with two users.
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(2);
   SetWidget(CreateWidgetWithContent(contents));
 
@@ -774,7 +773,7 @@
   auto enable_icon = [&](LoginBigUserView* view) {
     auto icon = mojom::EasyUnlockIconOptions::New();
     icon->icon = mojom::EasyUnlockIconId::LOCKED;
-    data_dispatcher()->ShowEasyUnlockIcon(
+    DataDispatcher()->ShowEasyUnlockIcon(
         view->GetCurrentUser()->basic_user_info->account_id, icon);
   };
 
@@ -782,7 +781,7 @@
   auto disable_icon = [&](LoginBigUserView* view) {
     auto icon = mojom::EasyUnlockIconOptions::New();
     icon->icon = mojom::EasyUnlockIconId::NONE;
-    data_dispatcher()->ShowEasyUnlockIcon(
+    DataDispatcher()->ShowEasyUnlockIcon(
         view->GetCurrentUser()->basic_user_info->account_id, icon);
   };
 
@@ -840,8 +839,8 @@
   // Build lock screen with a single user.
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
   SetWidget(CreateWidgetWithContent(contents));
 
@@ -873,8 +872,8 @@
   // Build lock screen with a single user.
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
   SetWidget(CreateWidgetWithContent(contents));
 
@@ -899,8 +898,8 @@
   // Build lock screen with a single user.
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLogin,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
   SetWidget(CreateWidgetWithContent(contents));
 
@@ -932,14 +931,14 @@
 
 TEST_F(LockContentsViewUnitTest, ErrorBubbleOnUntrustedDetachableBase) {
   auto fake_detachable_base_model =
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher());
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher());
   FakeLoginDetachableBaseModel* detachable_base_model =
       fake_detachable_base_model.get();
 
   // Build lock screen with 2 users.
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(), std::move(fake_detachable_base_model));
+      DataDispatcher(), std::move(fake_detachable_base_model));
   SetUserCount(2);
 
   const AccountId& kFirstUserAccountId =
@@ -1014,14 +1013,14 @@
 
 TEST_F(LockContentsViewUnitTest, ErrorBubbleForUnauthenticatedDetachableBase) {
   auto fake_detachable_base_model =
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher());
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher());
   FakeLoginDetachableBaseModel* detachable_base_model =
       fake_detachable_base_model.get();
 
   // Build lock screen with 2 users.
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(), std::move(fake_detachable_base_model));
+      DataDispatcher(), std::move(fake_detachable_base_model));
   SetUserCount(2);
 
   const AccountId& kFirstUserAccountId =
@@ -1077,14 +1076,14 @@
 TEST_F(LockContentsViewUnitTest,
        RemovingAttachedBaseHidesDetachableBaseNotification) {
   auto fake_detachable_base_model =
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher());
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher());
   FakeLoginDetachableBaseModel* detachable_base_model =
       fake_detachable_base_model.get();
 
   // Build lock screen with 2 users.
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(), std::move(fake_detachable_base_model));
+      DataDispatcher(), std::move(fake_detachable_base_model));
   SetUserCount(1);
 
   const AccountId& kUserAccountId = users()[0]->basic_user_info->account_id;
@@ -1113,14 +1112,14 @@
 
 TEST_F(LockContentsViewUnitTest, DetachableBaseErrorClearsAuthError) {
   auto fake_detachable_base_model =
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher());
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher());
   FakeLoginDetachableBaseModel* detachable_base_model =
       fake_detachable_base_model.get();
 
   // Build lock screen with a single user.
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(), std::move(fake_detachable_base_model));
+      DataDispatcher(), std::move(fake_detachable_base_model));
   SetUserCount(1);
 
   const AccountId& kUserAccountId = users()[0]->basic_user_info->account_id;
@@ -1164,14 +1163,14 @@
 
 TEST_F(LockContentsViewUnitTest, AuthErrorDoesNotRemoveDetachableBaseError) {
   auto fake_detachable_base_model =
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher());
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher());
   FakeLoginDetachableBaseModel* detachable_base_model =
       fake_detachable_base_model.get();
 
   // Build lock screen with a single user.
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(), std::move(fake_detachable_base_model));
+      DataDispatcher(), std::move(fake_detachable_base_model));
   SetUserCount(1);
 
   const AccountId& kUserAccountId = users()[0]->basic_user_info->account_id;
@@ -1231,7 +1230,7 @@
 
   // Add user who can use pin authentication.
   const std::string email = "user@domain.com";
-  LoadUser(email);
+  AddUserByEmail(email);
   contents->OnPinEnabledForUserChanged(AccountId::FromUserEmail(email), true);
   LoginBigUserView* big_view =
       LockContentsView::TestApi(contents).primary_big_view();
@@ -1259,7 +1258,7 @@
       LockScreen::TestApi(LockScreen::Get()).contents_view();
   ASSERT_NE(nullptr, contents);
 
-  LoadUsers(2);
+  SetUserCount(2);
 
   LoginAuthUserView::TestApi primary_user(
       LockContentsView::TestApi(contents).primary_big_view()->auth_user());
@@ -1305,7 +1304,7 @@
 
   // Add user who can use pin authentication.
   const std::string email = "user@domain.com";
-  LoadUser(email);
+  AddUserByEmail(email);
   contents->OnPinEnabledForUserChanged(AccountId::FromUserEmail(email), true);
   LoginBigUserView* big_view =
       LockContentsView::TestApi(contents).primary_big_view();
@@ -1343,8 +1342,8 @@
   // user.
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
   AddPublicAccountUsers(1);
   AddUsers(1);
@@ -1404,8 +1403,8 @@
   // users.
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
   AddPublicAccountUsers(2);
   AddUsers(2);
@@ -1530,8 +1529,8 @@
 TEST_F(LockContentsViewUnitTest, AuthUserSwapFocusesPassword) {
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   AddUsers(2);
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
 
@@ -1560,8 +1559,8 @@
 TEST_F(LockContentsViewUnitTest, TapOnAuthUserFocusesPassword) {
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
 
   auto do_test = [&](AuthTarget auth_target) {
@@ -1603,8 +1602,8 @@
 TEST_F(LockContentsViewUnitTest, UserListUserSwapFocusesPassword) {
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   LockContentsView::TestApi contents_test_api(contents);
   AddUsers(3);
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
@@ -1625,12 +1624,12 @@
 
 TEST_F(LockContentsViewUnitTest, BadDetachableBaseUnfocusesPasswordView) {
   auto fake_detachable_base_model =
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher());
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher());
   FakeLoginDetachableBaseModel* detachable_base_model =
       fake_detachable_base_model.get();
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(), std::move(fake_detachable_base_model));
+      DataDispatcher(), std::move(fake_detachable_base_model));
   SetUserCount(3);
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
 
@@ -1659,8 +1658,8 @@
   // users.
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   LockContentsView::TestApi lock_contents(contents);
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
   AddPublicAccountUsers(1);
@@ -1699,8 +1698,8 @@
 TEST_F(LockContentsViewUnitTest, OnUnlockAllowedForUserChanged) {
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
   SetWidget(CreateWidgetWithContent(contents));
 
@@ -1722,7 +1721,7 @@
   EXPECT_FALSE(disabled_auth_message->visible());
   // Setting auth disabled will hide the password field and the note action
   // button, and show the message.
-  data_dispatcher()->SetAuthEnabledForUser(
+  DataDispatcher()->SetAuthEnabledForUser(
       kFirstUserAccountId, false,
       base::Time::Now() + base::TimeDelta::FromHours(8));
   EXPECT_FALSE(note_action_button->visible());
@@ -1730,15 +1729,15 @@
   EXPECT_FALSE(pin_view->visible());
   EXPECT_TRUE(disabled_auth_message->visible());
   // Setting auth enabled will hide the message and show the password field.
-  data_dispatcher()->SetAuthEnabledForUser(kFirstUserAccountId, true,
-                                           base::nullopt);
+  DataDispatcher()->SetAuthEnabledForUser(kFirstUserAccountId, true,
+                                          base::nullopt);
   EXPECT_FALSE(note_action_button->visible());
   EXPECT_TRUE(password_view->visible());
   EXPECT_FALSE(pin_view->visible());
   EXPECT_FALSE(disabled_auth_message->visible());
 
   // Set auth disabled again.
-  data_dispatcher()->SetAuthEnabledForUser(
+  DataDispatcher()->SetAuthEnabledForUser(
       kFirstUserAccountId, false,
       base::Time::Now() + base::TimeDelta::FromHours(8));
   EXPECT_FALSE(note_action_button->visible());
@@ -1746,14 +1745,14 @@
   EXPECT_FALSE(pin_view->visible());
   EXPECT_TRUE(disabled_auth_message->visible());
   // Enable PIN. There's no UI change because auth is currently disabled.
-  data_dispatcher()->SetPinEnabledForUser(kFirstUserAccountId, true);
+  DataDispatcher()->SetPinEnabledForUser(kFirstUserAccountId, true);
   EXPECT_FALSE(note_action_button->visible());
   EXPECT_FALSE(password_view->visible());
   EXPECT_FALSE(pin_view->visible());
   EXPECT_TRUE(disabled_auth_message->visible());
   // Set auth enabled again. Both password field and PIN keyboard are shown.
-  data_dispatcher()->SetAuthEnabledForUser(kFirstUserAccountId, true,
-                                           base::nullopt);
+  DataDispatcher()->SetAuthEnabledForUser(kFirstUserAccountId, true,
+                                          base::nullopt);
   EXPECT_FALSE(note_action_button->visible());
   EXPECT_TRUE(password_view->visible());
   EXPECT_TRUE(pin_view->visible());
@@ -1763,8 +1762,8 @@
 TEST_F(LockContentsViewUnitTest, DisabledAuthMessageFocusBehavior) {
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
   SetWidget(CreateWidgetWithContent(contents));
 
@@ -1777,7 +1776,7 @@
   LoginUserView* user_view = auth_test_api.user_view();
 
   // The message is visible after disabling auth and it receives initial focus.
-  data_dispatcher()->SetAuthEnabledForUser(
+  DataDispatcher()->SetAuthEnabledForUser(
       kFirstUserAccountId, false,
       base::Time::Now() + base::TimeDelta::FromHours(8));
   EXPECT_TRUE(disabled_auth_message->visible());
@@ -1799,14 +1798,13 @@
   EXPECT_TRUE(HasFocusInAnyChildView(status_area));
 }
 
-class LockContentsViewPowerManagerUnitTest
-    : public LockContentsViewKeyboardUnitTest {
+class LockContentsViewPowerManagerUnitTest : public LockContentsViewUnitTest {
  public:
   void SetUp() override {
     chromeos::DBusThreadManager::GetSetterForTesting()->SetPowerManagerClient(
         std::make_unique<chromeos::FakePowerManagerClient>());
 
-    LockContentsViewKeyboardUnitTest::SetUp();
+    LockContentsViewUnitTest::SetUp();
   }
 };
 
@@ -1831,9 +1829,9 @@
 
 // Verifies that the password box for the active user is cleared if a suspend
 // event is received.
-TEST_F(LockContentsViewKeyboardUnitTest, PasswordClearedOnSuspend) {
+TEST_F(LockContentsViewUnitTest, PasswordClearedOnSuspend) {
   ASSERT_NO_FATAL_FAILURE(ShowLoginScreen());
-  LoadUsers(1);
+  AddUsers(1);
 
   LockScreen::TestApi lock_screen = LockScreen::TestApi(LockScreen::Get());
   LockContentsView* contents = lock_screen.contents_view();
@@ -1851,9 +1849,9 @@
   EXPECT_TRUE(textfield->text().empty());
 }
 
-TEST_F(LockContentsViewKeyboardUnitTest, ArrowNavSingleUser) {
+TEST_F(LockContentsViewUnitTest, ArrowNavSingleUser) {
   ASSERT_NO_FATAL_FAILURE(ShowLoginScreen());
-  LoadUsers(1);
+  SetUserCount(1);
   LockContentsView* lock_contents =
       LockScreen::TestApi(LockScreen::Get()).contents_view();
 
@@ -1869,10 +1867,10 @@
   EXPECT_TRUE(login_views_utils::HasFocusInAnyChildView(primary_big_view));
 }
 
-TEST_F(LockContentsViewKeyboardUnitTest, ArrowNavTwoUsers) {
+TEST_F(LockContentsViewUnitTest, ArrowNavTwoUsers) {
   ASSERT_NO_FATAL_FAILURE(ShowLoginScreen());
-  LoadUsers(1);
-  LoadPublicAccountUsers(1);
+  AddUsers(1);
+  AddPublicAccountUsers(1);
   LockContentsView::TestApi lock_contents = LockContentsView::TestApi(
       LockScreen::TestApi(LockScreen::Get()).contents_view());
 
@@ -1899,9 +1897,9 @@
   EXPECT_TRUE(login_views_utils::HasFocusInAnyChildView(primary_password_view));
 }
 
-TEST_F(LockContentsViewKeyboardUnitTest, ArrowNavThreeUsers) {
+TEST_F(LockContentsViewUnitTest, ArrowNavThreeUsers) {
   ASSERT_NO_FATAL_FAILURE(ShowLoginScreen());
-  LoadUsers(3);
+  SetUserCount(3);
   LockContentsView::TestApi lock_contents = LockContentsView::TestApi(
       LockScreen::TestApi(LockScreen::Get()).contents_view());
 
@@ -1932,9 +1930,9 @@
   EXPECT_TRUE(login_views_utils::HasFocusInAnyChildView(primary_password_view));
 }
 
-TEST_F(LockContentsViewKeyboardUnitTest, UserSwapFocusesBigView) {
+TEST_F(LockContentsViewUnitTest, UserSwapFocusesBigView) {
   ASSERT_NO_FATAL_FAILURE(ShowLoginScreen());
-  LoadUsers(3);
+  SetUserCount(3);
   LockContentsView::TestApi lock_contents = LockContentsView::TestApi(
       LockScreen::TestApi(LockScreen::Get()).contents_view());
 
@@ -1952,8 +1950,8 @@
 TEST_F(LockContentsViewUnitTest, PowerwashShortcutSendsMojoCall) {
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLogin,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
   SetWidget(CreateWidgetWithContent(contents));
 
@@ -1970,8 +1968,8 @@
 TEST_F(LockContentsViewUnitTest, UsersChangedRetainsExistingState) {
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(2);
   SetWidget(CreateWidgetWithContent(contents));
 
@@ -1980,7 +1978,7 @@
   AccountId primary_user = test_api.primary_big_view()
                                ->GetCurrentUser()
                                ->basic_user_info->account_id;
-  data_dispatcher()->SetPinEnabledForUser(primary_user, true);
+  DataDispatcher()->SetPinEnabledForUser(primary_user, true);
 
   // This user should be identical to the user we enabled PIN for.
   SetUserCount(1);
@@ -1995,8 +1993,8 @@
   // Build lock screen with a single user.
   auto* lock = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
   SetWidget(CreateWidgetWithContent(lock));
 
@@ -2009,15 +2007,15 @@
   EXPECT_FALSE(test_api.warning_banner_bubble()->IsVisible());
 
   // Verifies that a warning banner is shown by giving a non-empty message.
-  data_dispatcher()->ShowWarningBanner(base::ASCIIToUTF16("foo"));
+  DataDispatcher()->ShowWarningBanner(base::ASCIIToUTF16("foo"));
   EXPECT_TRUE(test_api.warning_banner_bubble()->IsVisible());
 
   // Verifies that a warning banner is hidden by HideWarningBanner().
-  data_dispatcher()->HideWarningBanner();
+  DataDispatcher()->HideWarningBanner();
   EXPECT_FALSE(test_api.warning_banner_bubble()->IsVisible());
 
   // Shows a warning banner again.
-  data_dispatcher()->ShowWarningBanner(base::ASCIIToUTF16("foo"));
+  DataDispatcher()->ShowWarningBanner(base::ASCIIToUTF16("foo"));
   EXPECT_TRUE(test_api.warning_banner_bubble()->IsVisible());
 
   // Attempt and fail user auth - an auth error is expected to be shown.
@@ -2043,12 +2041,12 @@
   // Build lock screen with one public account and one normal user.
   auto* lock = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   AddPublicAccountUsers(1);
   AddUsers(1);
   users()[1]->can_remove = true;
-  data_dispatcher()->NotifyUsers(users());
+  DataDispatcher()->NotifyUsers(users());
   SetWidget(CreateWidgetWithContent(lock));
 
   LockContentsView::TestApi test_api(lock);
@@ -2086,8 +2084,8 @@
   // Show lock screen with one normal user.
   auto* lock = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   AddUsers(1);
   SetWidget(CreateWidgetWithContent(lock));
 
@@ -2097,7 +2095,7 @@
       Shell::Get()->backlights_forced_off_setter()->backlights_forced_off());
 
   // Change fingerprint state; backlights remain forced off.
-  data_dispatcher()->SetFingerprintState(
+  DataDispatcher()->SetFingerprintState(
       users()[0]->basic_user_info->account_id,
       mojom::FingerprintState::DISABLED_FROM_ATTEMPTS);
   base::RunLoop().RunUntilIdle();
@@ -2117,8 +2115,8 @@
   // Show lock screen with one normal user.
   auto* lock = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   AddUsers(1);
   SetWidget(CreateWidgetWithContent(lock));
 
@@ -2129,7 +2127,7 @@
 
   // Validate a fingerprint authentication attempt resets backlights being
   // forced off.
-  data_dispatcher()->NotifyFingerprintAuthResult(
+  DataDispatcher()->NotifyFingerprintAuthResult(
       users()[0]->basic_user_info->account_id, false /*successful*/);
   base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(
@@ -2142,8 +2140,8 @@
   // Show lock screen but do *not* initialize any users.
   auto* lock = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetWidget(CreateWidgetWithContent(lock));
 
   // Nothing to validate except that there is no crash.
diff --git a/ash/login/ui/lock_screen_sanity_unittest.cc b/ash/login/ui/lock_screen_sanity_unittest.cc
index d18906a3..be51e0f 100644
--- a/ash/login/ui/lock_screen_sanity_unittest.cc
+++ b/ash/login/ui/lock_screen_sanity_unittest.cc
@@ -75,8 +75,8 @@
   // Build lock screen.
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
 
   // The lock screen requires at least one user.
   SetUserCount(1);
@@ -94,8 +94,8 @@
   // Build lock screen.
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
 
   // The lock screen requires at least one user.
   SetUserCount(1);
@@ -122,8 +122,8 @@
 
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
   LoginPasswordView::TestApi password_test_api =
@@ -179,8 +179,8 @@
   // Create lock screen.
   auto* lock = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(lock);
   views::View* shelf = Shelf::ForWindow(lock->GetWidget()->GetNativeWindow())
@@ -212,8 +212,8 @@
 
   auto* lock = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(lock);
   views::View* status_area =
@@ -243,8 +243,8 @@
 
   auto* lock = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(lock);
 
@@ -260,7 +260,7 @@
   LoginScreenController* controller = Shell::Get()->login_screen_controller();
 
   // Initialize lock screen action state.
-  data_dispatcher()->SetLockScreenNoteState(mojom::TrayActionState::kActive);
+  DataDispatcher()->SetLockScreenNoteState(mojom::TrayActionState::kActive);
 
   // Create and focus a lock screen app window.
   auto* lock_screen_app = new views::View();
@@ -315,8 +315,8 @@
       session_manager::SessionState::LOCKED);
   auto* lock = new LockContentsView(
       mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(lock);
 
@@ -325,7 +325,7 @@
                            ->GetContentsView();
 
   // Setup and focus a lock screen app.
-  data_dispatcher()->SetLockScreenNoteState(mojom::TrayActionState::kActive);
+  DataDispatcher()->SetLockScreenNoteState(mojom::TrayActionState::kActive);
   auto* lock_screen_app = new views::View();
   lock_screen_app->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
   std::unique_ptr<views::Widget> app_widget =
@@ -340,7 +340,7 @@
 
   // Move the lock screen note taking to available state (which happens when the
   // app session ends) - this should focus the lock screen.
-  data_dispatcher()->SetLockScreenNoteState(mojom::TrayActionState::kAvailable);
+  DataDispatcher()->SetLockScreenNoteState(mojom::TrayActionState::kAvailable);
   EXPECT_TRUE(VerifyFocused(lock));
 
   // Tab through the lock screen - the focus should eventually get to the shelf.
@@ -355,14 +355,14 @@
 
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
 
   // Add two users, the first of which can be removed.
   users().push_back(CreateUser("test1@test"));
   users()[0]->can_remove = true;
   users().push_back(CreateUser("test2@test"));
-  data_dispatcher()->NotifyUsers(users());
+  DataDispatcher()->NotifyUsers(users());
 
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
 
diff --git a/ash/login/ui/lock_window_unittest.cc b/ash/login/ui/lock_window_unittest.cc
index 5296834..667d4e8 100644
--- a/ash/login/ui/lock_window_unittest.cc
+++ b/ash/login/ui/lock_window_unittest.cc
@@ -21,7 +21,7 @@
       LockScreen::TestApi(LockScreen::Get()).contents_view();
   ASSERT_NE(nullptr, lock_contents);
 
-  LoadUsers(1);
+  SetUserCount(1);
 
   LoginBigUserView* auth_view =
       MakeLockContentsViewTestApi(lock_contents).primary_big_view();
diff --git a/ash/login/ui/login_base_bubble_view.cc b/ash/login/ui/login_base_bubble_view.cc
index efe6305..69813f7 100644
--- a/ash/login/ui/login_base_bubble_view.cc
+++ b/ash/login/ui/login_base_bubble_view.cc
@@ -50,6 +50,10 @@
   return nullptr;
 }
 
+bool LoginBaseBubbleView::IsPersistent() const {
+  return false;
+}
+
 void LoginBaseBubbleView::OnBeforeBubbleWidgetInit(
     views::Widget::InitParams* params,
     views::Widget* widget) const {
diff --git a/ash/login/ui/login_base_bubble_view.h b/ash/login/ui/login_base_bubble_view.h
index 4f89b1d..f8aa76e 100644
--- a/ash/login/ui/login_base_bubble_view.h
+++ b/ash/login/ui/login_base_bubble_view.h
@@ -25,6 +25,9 @@
   // Returns the button responsible for opening this bubble.
   virtual LoginButton* GetBubbleOpener() const;
 
+  // Returns whether or not this bubble should show persistently.
+  virtual bool IsPersistent() const;
+
   // views::BubbleDialogDelegateView:
   void OnBeforeBubbleWidgetInit(views::Widget::InitParams* params,
                                 views::Widget* widget) const override;
diff --git a/ash/login/ui/login_bubble.cc b/ash/login/ui/login_bubble.cc
index aae18d7..8d2a734 100644
--- a/ash/login/ui/login_bubble.cc
+++ b/ash/login/ui/login_bubble.cc
@@ -96,8 +96,10 @@
  public:
   LoginErrorBubbleView(views::View* content,
                        views::View* anchor_view,
-                       aura::Window* container)
-      : LoginBaseBubbleView(anchor_view, container) {
+                       aura::Window* container,
+                       bool show_persistently)
+      : LoginBaseBubbleView(anchor_view, container),
+        show_persistently_(show_persistently) {
     set_anchor_view_insets(
         gfx::Insets(kAnchorViewErrorBubbleVerticalSpacingDp, 0));
 
@@ -125,6 +127,9 @@
 
   ~LoginErrorBubbleView() override = default;
 
+  // LoginBaseBubbleView:
+  bool IsPersistent() const override { return show_persistently_; }
+
   // views::View:
   const char* GetClassName() const override { return "LoginErrorBubbleView"; }
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
@@ -132,6 +137,8 @@
   }
 
  private:
+  bool show_persistently_;
+
   DISALLOW_COPY_AND_ASSIGN(LoginErrorBubbleView);
 };
 
@@ -453,14 +460,14 @@
 
 void LoginBubble::ShowErrorBubble(views::View* content,
                                   views::View* anchor_view,
-                                  uint32_t flags) {
+                                  bool show_persistently) {
   if (bubble_view_)
     CloseImmediately();
 
-  flags_ = flags;
   aura::Window* menu_container = Shell::GetContainer(
       Shell::GetPrimaryRootWindow(), kShellWindowId_MenuContainer);
-  bubble_view_ = new LoginErrorBubbleView(content, anchor_view, menu_container);
+  bubble_view_ = new LoginErrorBubbleView(content, anchor_view, menu_container,
+                                          show_persistently);
 
   Show();
 }
@@ -477,7 +484,6 @@
   if (bubble_view_)
     CloseImmediately();
 
-  flags_ = kFlagsNone;
   bubble_view_ = new LoginUserMenuView(
       this, username, email, type, is_owner, anchor_view, bubble_opener,
       show_remove_user, std::move(on_remove_user_warning_shown),
@@ -496,7 +502,6 @@
   if (bubble_view_)
     CloseImmediately();
 
-  flags_ = kFlagsNone;
   bubble_view_ = new LoginTooltipView(message, anchor_view);
   Show();
 }
@@ -505,7 +510,6 @@
   if (bubble_view_)
     CloseImmediately();
 
-  flags_ = kFlagsNone;
   const bool had_focus =
       menu->GetBubbleOpener() && menu->GetBubbleOpener()->HasFocus();
 
@@ -579,7 +583,7 @@
   if (bubble_view_->GetWidget()->IsActive())
     return;
 
-  if (!(flags_ & kFlagPersistent)) {
+  if (!bubble_view_->IsPersistent()) {
     Close();
   }
 }
@@ -603,7 +607,7 @@
   if (gained_focus && bubble_window->Contains(gained_focus))
     return;
 
-  if (!(flags_ & kFlagPersistent))
+  if (!bubble_view_->IsPersistent())
     Close();
 }
 
@@ -645,7 +649,7 @@
       return;
   }
 
-  if (!(flags_ & kFlagPersistent))
+  if (!bubble_view_->IsPersistent())
     Close();
 }
 
@@ -694,7 +698,6 @@
     bubble_view_->GetWidget()->Close();
   is_visible_ = false;
   bubble_view_ = nullptr;
-  flags_ = kFlagsNone;
 }
 
 void LoginBubble::EnsureBubbleInScreen() {
diff --git a/ash/login/ui/login_bubble.h b/ash/login/ui/login_bubble.h
index 56b6b9c..db3619c 100644
--- a/ash/login/ui/login_bubble.h
+++ b/ash/login/ui/login_bubble.h
@@ -40,12 +40,6 @@
 
   static const int kUserMenuRemoveUserButtonIdForTest;
 
-  // Flags passed to ShowErrorBubble().
-  static constexpr uint32_t kFlagsNone = 0;
-  // If set, the shown error bubble will not be closed due to an unrelated user
-  // action - e.g. the bubble will not be closed if the user starts typing.
-  static constexpr uint32_t kFlagPersistent = 1 << 0;
-
   LoginBubble();
   ~LoginBubble() override;
 
@@ -53,7 +47,7 @@
   // |anchor_view| is the anchor for placing the bubble view.
   void ShowErrorBubble(views::View* content,
                        views::View* anchor_view,
-                       uint32_t flags);
+                       bool show_persistently);
 
   // Shows a user menu bubble.
   // |anchor_view| is the anchor for placing the bubble view.
@@ -128,9 +122,6 @@
   // Repositions the bubble view if it extends too far right or down.
   void EnsureBubbleInScreen();
 
-  // Flags passed to ShowErrorBubble().
-  uint32_t flags_ = kFlagsNone;
-
   LoginBaseBubbleView* bubble_view_ = nullptr;
 
   // The status of bubble after animation ends.
diff --git a/ash/login/ui/login_bubble_unittest.cc b/ash/login/ui/login_bubble_unittest.cc
index 07fee4b..0e51846 100644
--- a/ash/login/ui/login_bubble_unittest.cc
+++ b/ash/login/ui/login_bubble_unittest.cc
@@ -288,7 +288,7 @@
 
   EXPECT_FALSE(bubble_->IsVisible());
   views::Label* error_text = new views::Label(base::ASCIIToUTF16("Error text"));
-  bubble_->ShowErrorBubble(error_text, container_, LoginBubble::kFlagsNone);
+  bubble_->ShowErrorBubble(error_text, container_, false /*show_persistently*/);
   EXPECT_TRUE(bubble_->IsVisible());
 
   // Verifies that key event on a view other than error closes the error bubble.
@@ -302,7 +302,7 @@
 
   EXPECT_FALSE(bubble_->IsVisible());
   views::Label* error_text = new views::Label(base::ASCIIToUTF16("Error text"));
-  bubble_->ShowErrorBubble(error_text, container_, LoginBubble::kFlagsNone);
+  bubble_->ShowErrorBubble(error_text, container_, false /*show_persistently*/);
   EXPECT_TRUE(bubble_->IsVisible());
 
   // Verifies that mouse event on the bubble itself won't close the bubble.
@@ -322,7 +322,7 @@
 
   EXPECT_FALSE(bubble_->IsVisible());
   views::Label* error_text = new views::Label(base::ASCIIToUTF16("Error text"));
-  bubble_->ShowErrorBubble(error_text, container_, LoginBubble::kFlagsNone);
+  bubble_->ShowErrorBubble(error_text, container_, false /*show_persistently*/);
   EXPECT_TRUE(bubble_->IsVisible());
 
   // Verifies that gesture event on the bubble itself won't close the bubble.
@@ -340,8 +340,7 @@
 
   EXPECT_FALSE(bubble_->IsVisible());
   views::Label* error_text = new views::Label(base::ASCIIToUTF16("Error text"));
-  bubble_->ShowErrorBubble(error_text, container_,
-                           LoginBubble::kFlagPersistent);
+  bubble_->ShowErrorBubble(error_text, container_, true /*show_persistently*/);
   EXPECT_TRUE(bubble_->IsVisible());
 
   // Verifies that mouse event on the bubble itself won't close the bubble.
diff --git a/ash/login/ui/login_keyboard_test_base.cc b/ash/login/ui/login_keyboard_test_base.cc
index a2ef470..fed9442 100644
--- a/ash/login/ui/login_keyboard_test_base.cc
+++ b/ash/login/ui/login_keyboard_test_base.cc
@@ -5,7 +5,6 @@
 #include "ash/login/ui/login_keyboard_test_base.h"
 
 #include "ash/keyboard/ash_keyboard_controller.h"
-#include "ash/login/login_screen_controller.h"
 #include "ash/login/mock_login_screen_client.h"
 #include "ash/login/ui/lock_screen.h"
 #include "ash/login/ui/login_test_utils.h"
@@ -29,19 +28,15 @@
 void LoginKeyboardTestBase::SetUp() {
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       keyboard::switches::kEnableVirtualKeyboard);
-  AshTestBase::SetUp();
-
-  login_controller_ = Shell::Get()->login_screen_controller();
-  ASSERT_NE(nullptr, login_controller_);
+  LoginTestBase::SetUp();
 
   Shell::Get()->ash_keyboard_controller()->ActivateKeyboard();
 }
 
 void LoginKeyboardTestBase::TearDown() {
   Shell::Get()->ash_keyboard_controller()->DeactivateKeyboard();
-  if (ash::LockScreen::HasInstance())
-    ash::LockScreen::Get()->Destroy();
-  AshTestBase::TearDown();
+
+  LoginTestBase::TearDown();
 }
 
 void LoginKeyboardTestBase::ShowKeyboard() {
@@ -68,57 +63,4 @@
       ->GetBoundsInScreen();
 }
 
-void LoginKeyboardTestBase::ShowLockScreen() {
-  GetSessionControllerClient()->SetSessionState(
-      session_manager::SessionState::LOCKED);
-  // The lock screen can't be shown without a wallpaper.
-  Shell::Get()->wallpaper_controller()->ShowDefaultWallpaperForTesting();
-
-  base::Optional<bool> result;
-  login_controller_->ShowLockScreen(base::BindOnce(
-      [](base::Optional<bool>* result, bool did_show) { *result = did_show; },
-      &result));
-  base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(result.has_value());
-  ASSERT_EQ(*result, true);
-}
-
-void LoginKeyboardTestBase::ShowLoginScreen() {
-  GetSessionControllerClient()->SetSessionState(
-      session_manager::SessionState::LOGIN_PRIMARY);
-  // The login screen can't be shown without a wallpaper.
-  Shell::Get()->wallpaper_controller()->ShowDefaultWallpaperForTesting();
-
-  base::Optional<bool> result;
-  login_controller_->ShowLoginScreen(base::BindOnce(
-      [](base::Optional<bool>* result, bool did_show) { *result = did_show; },
-      &result));
-  base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(result.has_value());
-  ASSERT_EQ(*result, true);
-}
-
-void LoginKeyboardTestBase::LoadUsers(int count) {
-  for (int i = 0; i < count; ++i) {
-    std::string email =
-        base::StrCat({"user", std::to_string(i), "@domain.com "});
-    users_.push_back(CreateUser(email));
-  }
-  ash::LockScreen::Get()->data_dispatcher()->NotifyUsers(users_);
-}
-
-void LoginKeyboardTestBase::LoadPublicAccountUsers(int count) {
-  for (int i = 0; i < count; ++i) {
-    std::string email =
-        base::StrCat({"publicuser", std::to_string(i), "@domain.com"});
-    users_.push_back(CreatePublicAccountUser(email));
-  }
-  ash::LockScreen::Get()->data_dispatcher()->NotifyUsers(users_);
-}
-
-void LoginKeyboardTestBase::LoadUser(const std::string& email) {
-  users_.push_back(CreateUser(email));
-  ash::LockScreen::Get()->data_dispatcher()->NotifyUsers(users_);
-}
-
 }  // namespace ash
diff --git a/ash/login/ui/login_keyboard_test_base.h b/ash/login/ui/login_keyboard_test_base.h
index 36801a9..75a31e1 100644
--- a/ash/login/ui/login_keyboard_test_base.h
+++ b/ash/login/ui/login_keyboard_test_base.h
@@ -7,16 +7,15 @@
 
 #include <memory>
 
+#include "ash/login/ui/login_test_base.h"
 #include "ash/public/interfaces/login_user_info.mojom.h"
 #include "ash/test/ash_test_base.h"
 
 namespace ash {
 
-class LoginScreenController;
-
 // Base test fixture for testing the views-based login and lock screens with
 // virtual keyboard.
-class LoginKeyboardTestBase : public AshTestBase {
+class LoginKeyboardTestBase : public LoginTestBase {
  public:
   LoginKeyboardTestBase();
   ~LoginKeyboardTestBase() override;
@@ -33,29 +32,11 @@
   // Returns bounds of the keyboard in screen coordinate space.
   gfx::Rect GetKeyboardBoundsInScreen() const;
 
-  // Shows lock screen. Asserts that lock screen is shown. To stop execution of
-  // the test on failed assertion use ASSERT_NO_FATAL_FAILURE macro.
-  void ShowLockScreen();
-
-  // Shows login screen. Asserts that login screen is shown. To stop execution
-  // of the test on failed assertion use ASSERT_NO_FATAL_FAILURE macro.
-  void ShowLoginScreen();
-
-  // Loads the number of test users specified by |count|.
-  void LoadUsers(int count);
-
-  // Loads the number of test public account users specified by |count|.
-  void LoadPublicAccountUsers(int count);
-
-  // Loads user with the specified |email|.
-  void LoadUser(const std::string& email);
-
   // AshTestBase:
   void SetUp() override;
   void TearDown() override;
 
  private:
-  LoginScreenController* login_controller_ = nullptr;
   std::vector<mojom::LoginUserInfoPtr> users_;
 
   DISALLOW_COPY_AND_ASSIGN(LoginKeyboardTestBase);
diff --git a/ash/login/ui/login_test_base.cc b/ash/login/ui/login_test_base.cc
index e246616..eb668df 100644
--- a/ash/login/ui/login_test_base.cc
+++ b/ash/login/ui/login_test_base.cc
@@ -6,10 +6,14 @@
 
 #include <string>
 
+#include "ash/login/login_screen_controller.h"
+#include "ash/login/ui/lock_screen.h"
 #include "ash/login/ui/login_test_utils.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/interfaces/tray_action.mojom.h"
+#include "ash/session/test_session_controller_client.h"
 #include "ash/shell.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "base/strings/strcat.h"
 #include "services/ws/public/cpp/property_type_converters.h"
 #include "services/ws/public/mojom/window_manager.mojom.h"
@@ -42,6 +46,36 @@
 
 LoginTestBase::~LoginTestBase() = default;
 
+void LoginTestBase::ShowLockScreen() {
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::LOCKED);
+  // The lock screen can't be shown without a wallpaper.
+  Shell::Get()->wallpaper_controller()->ShowDefaultWallpaperForTesting();
+
+  base::Optional<bool> result;
+  Shell::Get()->login_screen_controller()->ShowLockScreen(base::BindOnce(
+      [](base::Optional<bool>* result, bool did_show) { *result = did_show; },
+      &result));
+  base::RunLoop().RunUntilIdle();
+  ASSERT_TRUE(result.has_value());
+  ASSERT_EQ(*result, true);
+}
+
+void LoginTestBase::ShowLoginScreen() {
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::LOGIN_PRIMARY);
+  // The login screen can't be shown without a wallpaper.
+  Shell::Get()->wallpaper_controller()->ShowDefaultWallpaperForTesting();
+
+  base::Optional<bool> result;
+  Shell::Get()->login_screen_controller()->ShowLoginScreen(base::BindOnce(
+      [](base::Optional<bool>* result, bool did_show) { *result = did_show; },
+      &result));
+  base::RunLoop().RunUntilIdle();
+  ASSERT_TRUE(result.has_value());
+  ASSERT_EQ(*result, true);
+}
+
 void LoginTestBase::SetWidget(std::unique_ptr<views::Widget> widget) {
   EXPECT_FALSE(widget_) << "SetWidget can only be called once.";
   widget_ = std::move(widget);
@@ -76,7 +110,7 @@
 
   users_.erase(users_.begin() + count, users_.end());
   // Notify any listeners that the user count has changed.
-  data_dispatcher_.NotifyUsers(users_);
+  DataDispatcher()->NotifyUsers(users_);
 }
 
 void LoginTestBase::AddUsers(size_t num_users) {
@@ -87,7 +121,12 @@
   }
 
   // Notify any listeners that the user count has changed.
-  data_dispatcher_.NotifyUsers(users_);
+  DataDispatcher()->NotifyUsers(users_);
+}
+
+void LoginTestBase::AddUserByEmail(const std::string& email) {
+  users_.push_back(CreateUser(email));
+  DataDispatcher()->NotifyUsers(users_);
 }
 
 void LoginTestBase::AddPublicAccountUsers(size_t num_public_accounts) {
@@ -98,12 +137,20 @@
   }
 
   // Notify any listeners that the user count has changed.
-  data_dispatcher_.NotifyUsers(users_);
+  DataDispatcher()->NotifyUsers(users_);
+}
+
+LoginDataDispatcher* LoginTestBase::DataDispatcher() {
+  return LockScreen::HasInstance() ? LockScreen::Get()->data_dispatcher()
+                                   : &data_dispatcher_;
 }
 
 void LoginTestBase::TearDown() {
   widget_.reset();
 
+  if (LockScreen::HasInstance())
+    LockScreen::Get()->Destroy();
+
   AshTestBase::TearDown();
 }
 
diff --git a/ash/login/ui/login_test_base.h b/ash/login/ui/login_test_base.h
index 298d32a..3da100d 100644
--- a/ash/login/ui/login_test_base.h
+++ b/ash/login/ui/login_test_base.h
@@ -26,6 +26,13 @@
   LoginTestBase();
   ~LoginTestBase() override;
 
+  // Shows a full Lock/Login screen. These methods are useful for when we want
+  // to test interactions between multiple lock screen components, or when some
+  // component needs to be able to talk directly to the lockscreen (e.g. getting
+  // the ScreenType).
+  void ShowLockScreen();
+  void ShowLoginScreen();
+
   // Sets the primary test widget. The widget can be retrieved using |widget()|.
   // This can be used to make a widget scoped to the whole test, e.g. if the
   // widget is created in a SetUp override.
@@ -37,24 +44,29 @@
   // shown.
   std::unique_ptr<views::Widget> CreateWidgetWithContent(views::View* content);
 
-  // Changes the active number of users. Fires an event on |data_dispatcher()|.
+  // Changes the active number of users. Fires an event on |DataDispatcher()|.
   void SetUserCount(size_t count);
 
   // Append number of |num_users| regular auth users.
   // Changes the active number of users. Fires an event on
-  // |data_dispatcher()|.
+  // |DataDispatcher()|.
   void AddUsers(size_t num_users);
 
+  // Add a single user with the specified |email|.
+  void AddUserByEmail(const std::string& email);
+
   // Append number of |num_public_accounts| public account users.
   // Changes the active number of users. Fires an event on
-  // |data_dispatcher()|.
+  // |DataDispatcher()|.
   void AddPublicAccountUsers(size_t num_public_accounts);
 
   std::vector<mojom::LoginUserInfoPtr>& users() { return users_; }
 
   const std::vector<mojom::LoginUserInfoPtr>& users() const { return users_; }
 
-  LoginDataDispatcher* data_dispatcher() { return &data_dispatcher_; }
+  // If the LockScreen is instantiated, returns its data dispatcher. Otherwise,
+  // returns a standalone instance.
+  LoginDataDispatcher* DataDispatcher();
 
   // AshTestBase:
   void TearDown() override;
diff --git a/ash/magnifier/partial_magnification_controller_unittest.cc b/ash/magnifier/partial_magnification_controller_unittest.cc
index 3d760ac9..18698dc 100644
--- a/ash/magnifier/partial_magnification_controller_unittest.cc
+++ b/ash/magnifier/partial_magnification_controller_unittest.cc
@@ -144,24 +144,24 @@
 
   // Move the pointer around, make sure the window follows it.
   event_generator->MoveTouch(gfx::Point(32, 32));
-  EXPECT_EQ(event_generator->current_location() + offset,
+  EXPECT_EQ(event_generator->current_screen_location() + offset,
             GetTestApi().GetWidgetOrigin());
 
   event_generator->MoveTouch(gfx::Point(0, 10));
-  EXPECT_EQ(event_generator->current_location() + offset,
+  EXPECT_EQ(event_generator->current_screen_location() + offset,
             GetTestApi().GetWidgetOrigin());
 
   event_generator->MoveTouch(gfx::Point(10, 0));
-  EXPECT_EQ(event_generator->current_location() + offset,
+  EXPECT_EQ(event_generator->current_screen_location() + offset,
             GetTestApi().GetWidgetOrigin());
 
   event_generator->ReleaseTouch();
 
   // Make sure the window is initially placed correctly.
-  event_generator->set_current_location(gfx::Point(50, 20));
+  event_generator->set_current_screen_location(gfx::Point(50, 20));
   EXPECT_FALSE(GetTestApi().is_active());
   event_generator->PressTouch();
-  EXPECT_EQ(event_generator->current_location() + offset,
+  EXPECT_EQ(event_generator->current_screen_location() + offset,
             GetTestApi().GetWidgetOrigin());
 }
 
diff --git a/ash/manifest.json b/ash/manifest.json
index 5be14b3..4e0cbe0 100644
--- a/ash/manifest.json
+++ b/ash/manifest.json
@@ -18,6 +18,8 @@
           "ash.mojom.AppListController",
           "ash.mojom.AshMessageCenterController",
           "ash.mojom.AssistantController",
+          "ash.mojom.AssistantScreenContextController",
+          "ash.mojom.AssistantSetupController",
           "ash.mojom.AssistantVolumeControl",
           "ash.mojom.CastConfig",
           "ash.mojom.CrosDisplayConfigController",
diff --git a/ash/media/media_notification_controller.cc b/ash/media/media_notification_controller.cc
index 3ddc2a2..dfa54cb 100644
--- a/ash/media/media_notification_controller.cc
+++ b/ash/media/media_notification_controller.cc
@@ -8,8 +8,10 @@
 #include "ash/media/media_notification_view.h"
 #include "ash/public/cpp/notification_utils.h"
 #include "base/strings/string16.h"
+#include "base/time/time.h"
 #include "services/media_session/public/mojom/constants.mojom.h"
 #include "services/media_session/public/mojom/media_controller.mojom.h"
+#include "services/media_session/public/mojom/media_session.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/gfx/image/image.h"
 #include "ui/message_center/public/cpp/notification.h"
@@ -20,8 +22,13 @@
 
 namespace ash {
 
+using media_session::mojom::MediaSessionAction;
+
 namespace {
 
+constexpr base::TimeDelta kDefaultSeekTime =
+    base::TimeDelta::FromSeconds(media_session::mojom::kDefaultSeekTimeSeconds);
+
 std::unique_ptr<message_center::MessageView> CreateCustomMediaNotificationView(
     const message_center::Notification& notification) {
   DCHECK_EQ(kMediaSessionNotificationCustomViewType,
@@ -134,19 +141,25 @@
     base::Optional<int> button_id) {
   DCHECK(button_id.has_value());
 
-  // TODO(beccahughes): Replace with MediaSessionAction enum when moved.
-  switch (*button_id) {
-    case 0:
+  switch (static_cast<MediaSessionAction>(*button_id)) {
+    case MediaSessionAction::kPreviousTrack:
       media_controller_ptr_->PreviousTrack();
       break;
-    case 1:
-      media_controller_ptr_->ToggleSuspendResume();
+    case MediaSessionAction::kSeekBackward:
+      media_controller_ptr_->Seek(kDefaultSeekTime * -1);
       break;
-    case 2:
+    case MediaSessionAction::kPlay:
+      media_controller_ptr_->Resume();
+      break;
+    case MediaSessionAction::kPause:
+      media_controller_ptr_->Suspend();
+      break;
+    case MediaSessionAction::kSeekForward:
+      media_controller_ptr_->Seek(kDefaultSeekTime);
+      break;
+    case MediaSessionAction::kNextTrack:
       media_controller_ptr_->NextTrack();
       break;
-    default:
-      NOTREACHED();
   }
 }
 
diff --git a/ash/media/media_notification_view.cc b/ash/media/media_notification_view.cc
index 98ea57c..15e7aa7 100644
--- a/ash/media/media_notification_view.cc
+++ b/ash/media/media_notification_view.cc
@@ -8,6 +8,7 @@
 #include "ash/media/media_notification_controller.h"
 #include "ash/shell.h"
 #include "components/vector_icons/vector_icons.h"
+#include "services/media_session/public/mojom/media_session.mojom.h"
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/public/cpp/message_center_constants.h"
 #include "ui/message_center/views/notification_control_buttons_view.h"
@@ -18,6 +19,8 @@
 
 namespace ash {
 
+using media_session::mojom::MediaSessionAction;
+
 namespace {
 
 // Dimensions.
@@ -61,10 +64,14 @@
       views::BoxLayout::CROSS_AXIS_ALIGNMENT_STRETCH);
   AddChildView(button_row_);
 
-  CreateMediaButton(vector_icons::kMediaPreviousTrackIcon);
+  CreateMediaButton(vector_icons::kMediaPreviousTrackIcon,
+                    MediaSessionAction::kPreviousTrack);
+  CreateMediaButton(vector_icons::kMediaSeekBackwardIcon,
+                    MediaSessionAction::kSeekBackward);
 
   // |play_pause_button_| toggles playback.
   play_pause_button_ = views::CreateVectorToggleImageButton(this);
+  play_pause_button_->set_tag(static_cast<int>(MediaSessionAction::kPlay));
   SkColor play_button_color = GetMediaNotificationColor(*play_pause_button_);
   views::SetImageFromVectorIcon(play_pause_button_,
                                 vector_icons::kPlayArrowIcon,
@@ -74,7 +81,10 @@
                                        kMediaButtonIconSize, play_button_color);
   button_row_->AddChildView(play_pause_button_);
 
-  CreateMediaButton(vector_icons::kMediaNextTrackIcon);
+  CreateMediaButton(vector_icons::kMediaSeekForwardIcon,
+                    MediaSessionAction::kSeekForward);
+  CreateMediaButton(vector_icons::kMediaNextTrackIcon,
+                    MediaSessionAction::kNextTrack);
 
   // TODO(beccahughes): Add remaining UI for notification.
 
@@ -129,15 +139,18 @@
                                           const ui::Event& event) {
   if (sender->parent() == button_row_) {
     message_center::MessageCenter::Get()->ClickOnNotificationButton(
-        notification_id(), sender->parent()->GetIndexOf(sender));
+        notification_id(), sender->tag());
   }
 }
 
 void MediaNotificationView::UpdateWithMediaSessionInfo(
     const media_session::mojom::MediaSessionInfoPtr& session_info) {
-  play_pause_button_->SetToggled(
-      session_info->playback_state ==
-      media_session::mojom::MediaPlaybackState::kPlaying);
+  bool playing = session_info->playback_state ==
+                 media_session::mojom::MediaPlaybackState::kPlaying;
+  play_pause_button_->SetToggled(playing);
+  play_pause_button_->set_tag(
+      playing ? static_cast<int>(MediaSessionAction::kPause)
+              : static_cast<int>(MediaSessionAction::kPlay));
 }
 
 void MediaNotificationView::UpdateControlButtonsVisibilityWithNotification(
@@ -150,8 +163,10 @@
   UpdateControlButtonsVisibility();
 }
 
-void MediaNotificationView::CreateMediaButton(const gfx::VectorIcon& icon) {
+void MediaNotificationView::CreateMediaButton(const gfx::VectorIcon& icon,
+                                              MediaSessionAction action) {
   views::ImageButton* button = views::CreateVectorImageButton(this);
+  button->set_tag(static_cast<int>(action));
   views::SetImageFromVectorIcon(button, icon, kMediaButtonIconSize,
                                 GetMediaNotificationColor(*button));
   button_row_->AddChildView(button);
diff --git a/ash/media/media_notification_view.h b/ash/media/media_notification_view.h
index 6cdc6af..93090a1 100644
--- a/ash/media/media_notification_view.h
+++ b/ash/media/media_notification_view.h
@@ -11,6 +11,10 @@
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/image_button.h"
 
+namespace media_session {
+enum class MediaSessionAction;
+}  // namespace media_session
+
 namespace message_center {
 class NotificationHeaderView;
 }  // namespace message_center
@@ -55,8 +59,10 @@
   void UpdateControlButtonsVisibilityWithNotification(
       const message_center::Notification& notification);
 
-  // Creates an image button with |icon| and adds it to |button_row_|.
-  void CreateMediaButton(const gfx::VectorIcon& icon);
+  // Creates an image button with |icon| and adds it to |button_row_|. When
+  // clicked it will trigger |action| on the sesssion.
+  void CreateMediaButton(const gfx::VectorIcon& icon,
+                         media_session::mojom::MediaSessionAction action);
 
   // View containing close and settings buttons.
   std::unique_ptr<message_center::NotificationControlButtonsView>
diff --git a/ash/media/media_notification_view_unittest.cc b/ash/media/media_notification_view_unittest.cc
index 14c9515..6a07987 100644
--- a/ash/media/media_notification_view_unittest.cc
+++ b/ash/media/media_notification_view_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "services/media_session/public/cpp/test/test_media_controller.h"
 #include "services/media_session/public/mojom/audio_focus.mojom.h"
+#include "services/media_session/public/mojom/media_session.mojom.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/message_center/message_center.h"
@@ -27,6 +28,7 @@
 
 namespace ash {
 
+using media_session::mojom::MediaSessionAction;
 using media_session::test::TestMediaController;
 
 namespace {
@@ -124,6 +126,17 @@
 
   views::View* button_row() const { return view_->button_row_; }
 
+  views::Button* GetButtonForAction(MediaSessionAction action) const {
+    for (int i = 0; i < button_row()->child_count(); ++i) {
+      views::Button* child = views::Button::AsButton(button_row()->child_at(i));
+
+      if (child->tag() == static_cast<int>(action))
+        return child;
+    }
+
+    return nullptr;
+  }
+
  private:
   std::unique_ptr<message_center::MessageView> CreateAndCaptureCustomView(
       const message_center::Notification& notification) {
@@ -156,7 +169,7 @@
   {
     gfx::Point cursor_location(-1, -1);
     views::View::ConvertPointToScreen(view(), &cursor_location);
-    GetEventGenerator()->MoveMouseTo(cursor_location.x(), cursor_location.y());
+    GetEventGenerator()->MoveMouseTo(cursor_location);
     GetEventGenerator()->SendMouseExit();
   }
 
@@ -168,7 +181,7 @@
   EXPECT_GT(button_row()->width(), 0);
   EXPECT_GT(button_row()->height(), 0);
 
-  EXPECT_EQ(3, button_row()->child_count());
+  EXPECT_EQ(5, button_row()->child_count());
 
   for (int i = 0; i < button_row()->child_count(); ++i) {
     const views::Button* child =
@@ -179,53 +192,108 @@
     EXPECT_EQ(kMediaButtonIconSize, child->width());
     EXPECT_EQ(kMediaButtonIconSize, child->height());
   }
+
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPreviousTrack));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kNextTrack));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekBackward));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekForward));
+
+  // |kPause| cannot be present if |kPlay| is.
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
 }
 
 TEST_F(MediaNotificationViewTest, NextTrackButtonClick) {
   EXPECT_EQ(0, media_controller()->next_track_count());
 
   gfx::Point cursor_location(1, 1);
-  views::View::ConvertPointToScreen(button_row()->child_at(2),
-                                    &cursor_location);
-  GetEventGenerator()->MoveMouseTo(cursor_location.x(), cursor_location.y());
+  views::View::ConvertPointToScreen(
+      GetButtonForAction(MediaSessionAction::kNextTrack), &cursor_location);
+  GetEventGenerator()->MoveMouseTo(cursor_location);
   GetEventGenerator()->ClickLeftButton();
   Shell::Get()->media_notification_controller()->FlushForTesting();
 
   EXPECT_EQ(1, media_controller()->next_track_count());
 }
 
-TEST_F(MediaNotificationViewTest, PlayPauseButtonClick) {
-  EXPECT_EQ(0, media_controller()->toggle_suspend_resume_count());
+TEST_F(MediaNotificationViewTest, PlayButtonClick) {
+  EXPECT_EQ(0, media_controller()->resume_count());
 
   gfx::Point cursor_location(1, 1);
-  views::View::ConvertPointToScreen(button_row()->child_at(1),
-                                    &cursor_location);
-  GetEventGenerator()->MoveMouseTo(cursor_location.x(), cursor_location.y());
+  views::View::ConvertPointToScreen(
+      GetButtonForAction(MediaSessionAction::kPlay), &cursor_location);
+  GetEventGenerator()->MoveMouseTo(cursor_location);
   GetEventGenerator()->ClickLeftButton();
   Shell::Get()->media_notification_controller()->FlushForTesting();
 
-  EXPECT_EQ(1, media_controller()->toggle_suspend_resume_count());
+  EXPECT_EQ(1, media_controller()->resume_count());
+}
+
+TEST_F(MediaNotificationViewTest, PauseButtonClick) {
+  EXPECT_EQ(0, media_controller()->suspend_count());
+
+  media_session::mojom::MediaSessionInfoPtr session_info(
+      media_session::mojom::MediaSessionInfo::New());
+  session_info->playback_state =
+      media_session::mojom::MediaPlaybackState::kPlaying;
+  Shell::Get()->media_notification_controller()->MediaSessionInfoChanged(
+      session_info.Clone());
+
+  gfx::Point cursor_location(1, 1);
+  views::View::ConvertPointToScreen(
+      GetButtonForAction(MediaSessionAction::kPause), &cursor_location);
+  GetEventGenerator()->MoveMouseTo(cursor_location);
+  GetEventGenerator()->ClickLeftButton();
+  Shell::Get()->media_notification_controller()->FlushForTesting();
+
+  EXPECT_EQ(1, media_controller()->suspend_count());
 }
 
 TEST_F(MediaNotificationViewTest, PreviousTrackButtonClick) {
   EXPECT_EQ(0, media_controller()->previous_track_count());
 
   gfx::Point cursor_location(1, 1);
-  views::View::ConvertPointToScreen(button_row()->child_at(0),
-                                    &cursor_location);
-  GetEventGenerator()->MoveMouseTo(cursor_location.x(), cursor_location.y());
+  views::View::ConvertPointToScreen(
+      GetButtonForAction(MediaSessionAction::kPreviousTrack), &cursor_location);
+  GetEventGenerator()->MoveMouseTo(cursor_location);
   GetEventGenerator()->ClickLeftButton();
   Shell::Get()->media_notification_controller()->FlushForTesting();
 
   EXPECT_EQ(1, media_controller()->previous_track_count());
 }
 
+TEST_F(MediaNotificationViewTest, SeekBackwardButtonClick) {
+  EXPECT_EQ(0, media_controller()->seek_backward_count());
+
+  gfx::Point cursor_location(1, 1);
+  views::View::ConvertPointToScreen(
+      GetButtonForAction(MediaSessionAction::kSeekBackward), &cursor_location);
+  GetEventGenerator()->MoveMouseTo(cursor_location);
+  GetEventGenerator()->ClickLeftButton();
+  Shell::Get()->media_notification_controller()->FlushForTesting();
+
+  EXPECT_EQ(1, media_controller()->seek_backward_count());
+}
+
+TEST_F(MediaNotificationViewTest, SeekForwardButtonClick) {
+  EXPECT_EQ(0, media_controller()->seek_forward_count());
+
+  gfx::Point cursor_location(1, 1);
+  views::View::ConvertPointToScreen(
+      GetButtonForAction(MediaSessionAction::kSeekForward), &cursor_location);
+  GetEventGenerator()->MoveMouseTo(cursor_location);
+  GetEventGenerator()->ClickLeftButton();
+  Shell::Get()->media_notification_controller()->FlushForTesting();
+
+  EXPECT_EQ(1, media_controller()->seek_forward_count());
+}
+
 TEST_F(MediaNotificationViewTest, ClickNotification) {
   EXPECT_EQ(0, media_controller()->toggle_suspend_resume_count());
 
   gfx::Point cursor_location(1, 1);
   views::View::ConvertPointToScreen(view(), &cursor_location);
-  GetEventGenerator()->MoveMouseTo(cursor_location.x(), cursor_location.y());
+  GetEventGenerator()->MoveMouseTo(cursor_location);
   GetEventGenerator()->ClickLeftButton();
   Shell::Get()->media_notification_controller()->FlushForTesting();
 
@@ -234,8 +302,8 @@
 
 TEST_F(MediaNotificationViewTest, PlayToggle_FromActiveSessionChanged) {
   {
-    views::ToggleImageButton* button =
-        static_cast<views::ToggleImageButton*>(button_row()->child_at(1));
+    views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
+        GetButtonForAction(MediaSessionAction::kPlay));
     ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
     EXPECT_FALSE(button->toggled_for_testing());
   }
@@ -258,16 +326,16 @@
   ShowNotificationAndCaptureView(std::move(session_info));
 
   {
-    views::ToggleImageButton* button =
-        static_cast<views::ToggleImageButton*>(button_row()->child_at(1));
+    views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
+        GetButtonForAction(MediaSessionAction::kPause));
     ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
     EXPECT_TRUE(button->toggled_for_testing());
   }
 }
 
 TEST_F(MediaNotificationViewTest, PlayToggle_FromObserver_Empty) {
-  views::ToggleImageButton* button =
-      static_cast<views::ToggleImageButton*>(button_row()->child_at(1));
+  views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
+      GetButtonForAction(MediaSessionAction::kPlay));
   ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
   EXPECT_FALSE(button->toggled_for_testing());
 
@@ -277,8 +345,8 @@
 }
 
 TEST_F(MediaNotificationViewTest, PlayToggle_FromObserver_PlaybackState) {
-  views::ToggleImageButton* button =
-      static_cast<views::ToggleImageButton*>(button_row()->child_at(1));
+  views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
+      GetButtonForAction(MediaSessionAction::kPlay));
   ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
   EXPECT_FALSE(button->toggled_for_testing());
 
diff --git a/ash/metrics/demo_session_metrics_recorder.cc b/ash/metrics/demo_session_metrics_recorder.cc
index fddc1e0..7bd4c84 100644
--- a/ash/metrics/demo_session_metrics_recorder.cc
+++ b/ash/metrics/demo_session_metrics_recorder.cc
@@ -37,6 +37,26 @@
 constexpr int kMaxPeriodsWithoutActivity =
     base::TimeDelta::FromSeconds(15) / kSamplePeriod;
 
+// Returns a package name for an ARC window. The returned value is unowned and
+// may be null.
+const std::string* GetPackageNameForArcWindow(const aura::Window* window) {
+  // Make sure this is an ARC app window.
+  DCHECK(static_cast<ash::AppType>(window->GetProperty(
+             aura::client::kAppType)) == ash::AppType::ARC_APP);
+  // We use a dedicated key for instead of kShelfIDKey for identifiying ARC++
+  // apps. The ShelfID app id isn't used to identify ARC++ apps since it's a
+  // hash of both the package name and the activity.
+  return static_cast<std::string*>(window->GetProperty(kArcPackageNameKey));
+}
+
+// Returns the app ID For a non-ARC window. This function crashes if the
+// window is an ARC window.
+std::string GetAppIdForWindow(const aura::Window* window) {
+  DCHECK(static_cast<ash::AppType>(window->GetProperty(
+             aura::client::kAppType)) != ash::AppType::ARC_APP);
+  return ShelfID::Deserialize(window->GetProperty(kShelfIDKey)).app_id;
+}
+
 // Maps a Chrome app ID to a DemoModeApp value for metrics.
 DemoModeApp GetAppFromAppId(const std::string& app_id) {
   // Each version of the Highlights app is bucketed into the same value.
@@ -91,17 +111,12 @@
   ash::AppType app_type =
       static_cast<ash::AppType>(window->GetProperty(aura::client::kAppType));
   if (app_type == ash::AppType::ARC_APP) {
-    // The ShelfID app id isn't used to identify ARC++ apps since it's a hash of
-    // both the package name and the activity.
-    const std::string* package_name =
-        static_cast<std::string*>(window->GetProperty(kArcPackageNameKey));
+    const std::string* package_name = GetPackageNameForArcWindow(window);
     return package_name ? GetAppFromPackageName(*package_name)
                         : DemoModeApp::kOtherArcApp;
   }
 
-  std::string app_id =
-      ShelfID::Deserialize(window->GetProperty(kShelfIDKey)).app_id;
-
+  std::string app_id = GetAppIdForWindow(window);
   // The Chrome "app" in the shelf is just the browser.
   if (app_id == extension_misc::kChromeAppId)
     return DemoModeApp::kBrowser;
@@ -136,13 +151,29 @@
     timer_ = std::make_unique<base::RepeatingTimer>();
 
   StartRecording();
+
+  // Listen for user activity events.
   observer_.Add(ui::UserActivityDetector::Get());
+
+  // Listen for Window activation events.
+  Shell::Get()->activation_client()->AddObserver(this);
 }
 
 DemoSessionMetricsRecorder::~DemoSessionMetricsRecorder() {
   // Report any remaining stored samples on exit. (If the user went idle, there
   // won't be any.)
   ReportSamples();
+
+  // Stop listening for window activation events.
+  Shell::Get()->activation_client()->RemoveObserver(this);
+
+  // Report the number of unique apps launched since the last
+  // time we reported, and when the demo session ends.  Only
+  // do this if there are any entries in the set, because an idle
+  // session that was shut down can result in erroneous
+  // sample stating that 0 unique apps were launched.
+  if (unique_apps_launched_.size() > 0)
+    ReportUniqueAppsLaunched();
 }
 
 void DemoSessionMetricsRecorder::OnUserActivity(const ui::Event* event) {
@@ -155,6 +186,33 @@
   periods_since_activity_ = 0;
 }
 
+void DemoSessionMetricsRecorder::OnWindowActivated(ActivationReason reason,
+                                                   aura::Window* gained_active,
+                                                   aura::Window* lost_active) {
+  if (gained_active == nullptr)
+    return;
+
+  // Don't count popup windows.
+  if (gained_active->type() != aura::client::WINDOW_TYPE_NORMAL)
+    return;
+
+  // Track unique apps opened.  There is no namespace collision between
+  // ARC apps and ChromeOS Apps because ARC app package names use a different
+  // naming scheme than ChromeOS Apps.
+  std::string unique_app_id;
+  ash::AppType app_type = static_cast<ash::AppType>(
+      gained_active->GetProperty(aura::client::kAppType));
+
+  if (app_type == ash::AppType::ARC_APP) {
+    const std::string* package_name = GetPackageNameForArcWindow(gained_active);
+    unique_app_id = *package_name;
+  } else {
+    unique_app_id = GetAppIdForWindow(gained_active);
+  }
+
+  unique_apps_launched_.insert(unique_app_id);
+}
+
 void DemoSessionMetricsRecorder::StartRecording() {
   timer_->Start(FROM_HERE, kSamplePeriod, this,
                 &DemoSessionMetricsRecorder::TakeSampleOrPause);
@@ -166,6 +224,10 @@
     // These samples were collected since the last user activity.
     unreported_samples_.clear();
     timer_->Stop();
+
+    // Since we are assuming that the user left, report how many
+    // unique apps have been launched since we last reported.
+    ReportUniqueAppsLaunched();
     return;
   }
 
@@ -186,4 +248,10 @@
   unreported_samples_.clear();
 }
 
+void DemoSessionMetricsRecorder::ReportUniqueAppsLaunched() {
+  UMA_HISTOGRAM_COUNTS_100("DemoMode.UniqueAppsLaunched",
+                           unique_apps_launched_.size());
+  unique_apps_launched_.clear();
+}
+
 }  // namespace ash
diff --git a/ash/metrics/demo_session_metrics_recorder.h b/ash/metrics/demo_session_metrics_recorder.h
index d21fbd0..7d69eed7 100644
--- a/ash/metrics/demo_session_metrics_recorder.h
+++ b/ash/metrics/demo_session_metrics_recorder.h
@@ -6,12 +6,15 @@
 #define ASH_METRICS_DEMO_SESSION_METRICS_RECORDER_H_
 
 #include <memory>
+#include <string>
 #include <vector>
 
 #include "ash/ash_export.h"
+#include "base/containers/flat_set.h"
 #include "base/macros.h"
 #include "base/scoped_observer.h"
 #include "ui/base/user_activity/user_activity_observer.h"
+#include "ui/wm/public/activation_change_observer.h"
 
 namespace base {
 class RepeatingTimer;
@@ -25,7 +28,9 @@
 
 // A metrics recorder for demo sessions that samples the active window's app or
 // window type. Only used when the device is in Demo Mode.
-class ASH_EXPORT DemoSessionMetricsRecorder : public ui::UserActivityObserver {
+class ASH_EXPORT DemoSessionMetricsRecorder
+    : public ui::UserActivityObserver,
+      public wm::ActivationChangeObserver {
  public:
   // These apps are preinstalled in Demo Mode. This list is not exhaustive, and
   // includes first- and third-party Chrome and ARC apps.
@@ -66,6 +71,11 @@
   // ui::UserActivityObserver:
   void OnUserActivity(const ui::Event* event) override;
 
+  // wm::ActivationChangeObserver:
+  void OnWindowActivated(ActivationReason reason,
+                         aura::Window* gained_active,
+                         aura::Window* lost_active) override;
+
  private:
   // Starts the timer for periodic sampling.
   void StartRecording();
@@ -77,10 +87,16 @@
   // Emits histograms for recorded samples.
   void ReportSamples();
 
+  // Emits histograms for the number of unique apps launched.
+  void ReportUniqueAppsLaunched();
+
   // Stores samples as they are collected. Report to UMA if we see user
   // activity soon after. Guaranteed not to grow too large.
   std::vector<DemoModeApp> unreported_samples_;
 
+  // Tracks the ids of apps that have been launched in Demo Mode.
+  base::flat_set<std::string> unique_apps_launched_;
+
   // How many periods have elapsed since the last user activity.
   int periods_since_activity_ = 0;
 
@@ -94,4 +110,4 @@
 
 }  // namespace ash
 
-#endif  // ASH_METRICS_POINTER_METRICS_RECORDER_H_
+#endif  // ASH_METRICS_DEMO_SESSION_METRICS_RECORDER_H_
diff --git a/ash/metrics/demo_session_metrics_recorder_unittest.cc b/ash/metrics/demo_session_metrics_recorder_unittest.cc
index ad50a8d..4cc775b 100644
--- a/ash/metrics/demo_session_metrics_recorder_unittest.cc
+++ b/ash/metrics/demo_session_metrics_recorder_unittest.cc
@@ -445,5 +445,84 @@
   histogram_tester_->ExpectTotalCount("DemoMode.ActiveApp", 0);
 }
 
+TEST_F(DemoSessionMetricsRecorderTest, UniqueAppsLaunchedOnIdle) {
+  // Activate each window twice.  Despite activating each twice,
+  // the count should only be incremented once per unique app.
+  auto chrome_app_window = CreateChromeAppWindow(extension_misc::kCameraAppId);
+  wm::ActivateWindow(chrome_app_window.get());
+  wm::DeactivateWindow(chrome_app_window.get());
+  wm::ActivateWindow(chrome_app_window.get());
+
+  auto chrome_browser_window =
+      CreateChromeAppWindow(extension_misc::kChromeAppId);
+  wm::ActivateWindow(chrome_browser_window.get());
+  wm::DeactivateWindow(chrome_browser_window.get());
+  wm::ActivateWindow(chrome_browser_window.get());
+
+  auto arc_window_1 = CreateArcWindow("com.google.Photos");
+  wm::ActivateWindow(arc_window_1.get());
+  wm::DeactivateWindow(arc_window_1.get());
+  wm::ActivateWindow(arc_window_1.get());
+
+  auto arc_window_2 = CreateArcWindow("com.google.Maps");
+  wm::ActivateWindow(arc_window_2.get());
+  wm::DeactivateWindow(arc_window_2.get());
+  wm::ActivateWindow(arc_window_2.get());
+
+  // Popup windows shouldn't be counted at all.
+  auto popup_window = CreatePopupWindow();
+  wm::ActivateWindow(popup_window.get());
+  wm::DeactivateWindow(popup_window.get());
+  wm::ActivateWindow(popup_window.get());
+
+  for (int i = 0; i < 20; i++)
+    FireTimer();
+
+  histogram_tester_->ExpectUniqueSample("DemoMode.UniqueAppsLaunched", 4, 1);
+}
+
+TEST_F(DemoSessionMetricsRecorderTest, UniqueAppsLaunchedOnDeletion) {
+  // Activate each window twice.  Despite activating each twice,
+  // the count should only be incremented once per unique app.
+  auto chrome_app_window = CreateChromeAppWindow(extension_misc::kCameraAppId);
+  wm::ActivateWindow(chrome_app_window.get());
+  wm::DeactivateWindow(chrome_app_window.get());
+  wm::ActivateWindow(chrome_app_window.get());
+
+  auto chrome_browser_window =
+      CreateChromeAppWindow(extension_misc::kChromeAppId);
+  wm::ActivateWindow(chrome_browser_window.get());
+  wm::DeactivateWindow(chrome_browser_window.get());
+  wm::ActivateWindow(chrome_browser_window.get());
+
+  auto arc_window_1 = CreateArcWindow("com.google.Photos");
+  wm::ActivateWindow(arc_window_1.get());
+  wm::DeactivateWindow(arc_window_1.get());
+  wm::ActivateWindow(arc_window_1.get());
+
+  auto arc_window_2 = CreateArcWindow("com.google.Maps");
+  wm::ActivateWindow(arc_window_2.get());
+  wm::DeactivateWindow(arc_window_2.get());
+  wm::ActivateWindow(arc_window_2.get());
+
+  // Popup windows shouldn't be counted at all.
+  auto popup_window = CreatePopupWindow();
+  wm::ActivateWindow(popup_window.get());
+  wm::DeactivateWindow(popup_window.get());
+  wm::ActivateWindow(popup_window.get());
+
+  DeleteMetricsRecorder();
+
+  histogram_tester_->ExpectUniqueSample("DemoMode.UniqueAppsLaunched", 4, 1);
+}
+
+TEST_F(DemoSessionMetricsRecorderTest, NoUniqueAppsLaunchedOnDeletion) {
+  DeleteMetricsRecorder();
+
+  // There should be no samples if the recorder is deleted with 0 unique apps
+  // launched.
+  histogram_tester_->ExpectTotalCount("DemoMode.ReportUniqueAppsLaunched", 0);
+}
+
 }  // namespace
 }  // namespace ash
diff --git a/ash/metrics/login_metrics_recorder_unittest.cc b/ash/metrics/login_metrics_recorder_unittest.cc
index f328ced..9cd67fd 100644
--- a/ash/metrics/login_metrics_recorder_unittest.cc
+++ b/ash/metrics/login_metrics_recorder_unittest.cc
@@ -85,8 +85,8 @@
 
   auto* contents = new LockContentsView(
       mojom::TrayActionState::kAvailable, LockScreen::ScreenType::kLock,
-      data_dispatcher(),
-      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   SetUserCount(1);
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
 
diff --git a/ash/mojo_interface_factory.cc b/ash/mojo_interface_factory.cc
index cea038b..28fa45c 100644
--- a/ash/mojo_interface_factory.cc
+++ b/ash/mojo_interface_factory.cc
@@ -11,6 +11,8 @@
 #include "ash/accessibility/accessibility_focus_ring_controller.h"
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/assistant/assistant_controller.h"
+#include "ash/assistant/assistant_screen_context_controller.h"
+#include "ash/assistant/assistant_setup_controller.h"
 #include "ash/cast_config_controller.h"
 #include "ash/display/ash_display_controller.h"
 #include "ash/display/cros_display_config.h"
@@ -87,6 +89,20 @@
   Shell::Get()->assistant_controller()->BindRequest(std::move(request));
 }
 
+void BindAssistantScreenContextControllerRequestOnMainThread(
+    mojom::AssistantScreenContextControllerRequest request) {
+  Shell::Get()
+      ->assistant_controller()
+      ->screen_context_controller()
+      ->BindRequest(std::move(request));
+}
+
+void BindAssistantSetupControllerRequestOnMainThread(
+    mojom::AssistantSetupControllerRequest request) {
+  Shell::Get()->assistant_controller()->setup_controller()->BindRequest(
+      std::move(request));
+}
+
 void BindAssistantVolumeControlRequestOnMainThread(
     mojom::AssistantVolumeControlRequest request) {
   Shell::Get()->assistant_controller()->BindRequest(std::move(request));
@@ -244,6 +260,13 @@
         base::BindRepeating(&BindAssistantControllerRequestOnMainThread),
         main_thread_task_runner);
     registry->AddInterface(
+        base::BindRepeating(
+            &BindAssistantScreenContextControllerRequestOnMainThread),
+        main_thread_task_runner);
+    registry->AddInterface(
+        base::BindRepeating(&BindAssistantSetupControllerRequestOnMainThread),
+        main_thread_task_runner);
+    registry->AddInterface(
         base::BindRepeating(&BindAssistantVolumeControlRequestOnMainThread),
         main_thread_task_runner);
   }
diff --git a/ash/multi_user/multi_user_window_manager.cc b/ash/multi_user/multi_user_window_manager.cc
index 214b028..3963a9a 100644
--- a/ash/multi_user/multi_user_window_manager.cc
+++ b/ash/multi_user/multi_user_window_manager.cc
@@ -10,6 +10,7 @@
 #include "ash/media_controller.h"
 #include "ash/multi_user/multi_user_window_manager.h"
 #include "ash/multi_user/multi_user_window_manager_delegate.h"
+#include "ash/multi_user/multi_user_window_manager_window_delegate.h"
 #include "ash/multi_user/user_switch_animator.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/session/session_controller.h"
@@ -17,10 +18,9 @@
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/auto_reset.h"
 #include "base/macros.h"
-#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/mus/window_mus.h"
+#include "ui/aura/mus/window_tree_client.h"
 #include "ui/aura/window.h"
-#include "ui/aura/window_event_dispatcher.h"
-#include "ui/aura/window_tree_host.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/events/event.h"
 #include "ui/views/mus/mus_client.h"
@@ -134,13 +134,12 @@
     animation_->CancelAnimation();
 
   // Remove all window observers.
-  WindowToEntryMap::iterator window = window_to_entry_.begin();
-  while (window != window_to_entry_.end()) {
+  while (!window_to_entry_.empty()) {
     // Explicitly remove this from window observer list since OnWindowDestroyed
     // no longer does that.
-    window->first->RemoveObserver(this);
-    OnWindowDestroyed(window->first);
-    window = window_to_entry_.begin();
+    aura::Window* window = window_to_entry_.begin()->first;
+    window->RemoveObserver(this);
+    OnWindowDestroyed(window);
   }
 
   Shell::Get()->session_controller()->RemoveObserver(this);
@@ -153,8 +152,11 @@
   return g_instance;
 }
 
-void MultiUserWindowManager::SetWindowOwner(aura::Window* window,
-                                            const AccountId& account_id) {
+void MultiUserWindowManager::SetWindowOwner(
+    aura::Window* window,
+    const AccountId& account_id,
+    bool show_for_current_user,
+    MultiUserWindowManagerWindowDelegate* window_delegate) {
   // Make sure the window is valid and there was no owner yet.
   DCHECK(window);
   DCHECK(account_id.is_valid());
@@ -162,10 +164,13 @@
   if (GetWindowOwner(window) == account_id)
     return;
   DCHECK(GetWindowOwner(window).empty());
-  window_to_entry_[window] = new WindowEntry(account_id);
+  std::unique_ptr<WindowEntry> window_entry_ptr =
+      std::make_unique<WindowEntry>(account_id, window_delegate);
+  WindowEntry* window_entry = window_entry_ptr.get();
+  window_to_entry_[window] = std::move(window_entry_ptr);
 
   // Remember the initial visibility of the window.
-  window_to_entry_[window]->set_show(window->IsVisible());
+  window_entry->set_show(window->IsVisible());
 
   // Add observers to track state changes.
   window->AddObserver(this);
@@ -173,8 +178,8 @@
 
   // Check if this window was created due to a user interaction. If it was,
   // transfer it to the current user.
-  if (window->GetProperty(aura::client::kCreatedByUserGesture))
-    window_to_entry_[window]->set_show_for_user(current_account_id_);
+  if (show_for_current_user)
+    window_entry->set_show_for_user(current_account_id_);
 
   // Add all transient children to our set of windows. Note that the function
   // will add the children but not the owner to the transient children map.
@@ -184,6 +189,14 @@
     SetWindowVisibility(window, false);
 }
 
+void MultiUserWindowManager::RemoveWindowDelegate(
+    MultiUserWindowManagerWindowDelegate* delegate) {
+  for (auto& pair : window_to_entry_) {
+    if (pair.second->delegate() == delegate)
+      pair.second->clear_delegate();
+  }
+}
+
 const AccountId& MultiUserWindowManager::GetWindowOwner(
     aura::Window* window) const {
   WindowToEntryMap::const_iterator it = window_to_entry_.find(window);
@@ -206,9 +219,8 @@
 }
 
 bool MultiUserWindowManager::AreWindowsSharedAmongUsers() const {
-  WindowToEntryMap::const_iterator it = window_to_entry_.begin();
-  for (; it != window_to_entry_.end(); ++it) {
-    if (it->second->owner() != it->second->show_for_user())
+  for (auto& window_pair : window_to_entry_) {
+    if (window_pair.second->owner() != window_pair.second->show_for_user())
       return true;
   }
   return false;
@@ -246,6 +258,9 @@
   // This needs to be set before the animation starts.
   current_account_id_ = account_id;
 
+  if (delegate_)
+    delegate_->OnWillSwitchActiveAccount(current_account_id_);
+
   // Here to avoid a very nasty race condition, we must destruct any previously
   // created animation before creating a new one. Otherwise, the newly
   // constructed will hide all windows of the old user in the first step of the
@@ -269,8 +284,6 @@
     return;
   }
   ::wm::TransientWindowManager::GetOrCreate(window)->RemoveObserver(this);
-  // Remove the window from the owners list.
-  delete window_to_entry_[window];
   window_to_entry_.erase(window);
 }
 
@@ -340,7 +353,7 @@
 }
 
 void MultiUserWindowManager::OnTabletModeStarted() {
-  for (auto entry : window_to_entry_)
+  for (auto& entry : window_to_entry_)
     Shell::Get()->tablet_mode_controller()->AddWindow(entry.first);
 }
 
@@ -372,23 +385,27 @@
   if (account_id != owner && minimized)
     return false;
 
-  WindowToEntryMap::iterator it = window_to_entry_.find(window);
-  it->second->set_show_for_user(account_id);
+  WindowEntry* window_entry = window_to_entry_[window].get();
+  window_entry->set_show_for_user(account_id);
 
   const bool teleported = !IsWindowOnDesktopOfUser(window, owner);
 
   // Show the window if the added user is the current one.
   if (account_id == current_account_id_) {
     // Only show the window if it should be shown according to its state.
-    if (it->second->show())
+    if (window_entry->show())
       SetWindowVisibility(window, true, kTeleportAnimationTime);
   } else {
     SetWindowVisibility(window, false, kTeleportAnimationTime);
   }
 
   // Notify entry change.
-  if (delegate_)
+  if (window_entry->delegate()) {
+    window_entry->delegate()->OnWindowOwnerEntryChanged(window, account_id,
+                                                        minimized, teleported);
+  } else if (delegate_) {
     delegate_->OnOwnerEntryChanged(window, account_id, minimized, teleported);
+  }
   return true;
 }
 
diff --git a/ash/multi_user/multi_user_window_manager.h b/ash/multi_user/multi_user_window_manager.h
index e336b5d..5d8e3f9 100644
--- a/ash/multi_user/multi_user_window_manager.h
+++ b/ash/multi_user/multi_user_window_manager.h
@@ -7,8 +7,6 @@
 
 #include <map>
 #include <memory>
-#include <set>
-#include <string>
 
 #include "ash/ash_export.h"
 #include "ash/session/session_observer.h"
@@ -23,6 +21,7 @@
 namespace ash {
 
 class MultiUserWindowManagerDelegate;
+class MultiUserWindowManagerWindowDelegate;
 class UserSwitchAnimator;
 
 // MultiUserWindowManager associates windows with users and ensures the
@@ -64,8 +63,16 @@
 
   // Associates a window with a particular account. This may result in hiding
   // |window|. This should *not* be called more than once with a different
-  // account.
-  void SetWindowOwner(aura::Window* window, const AccountId& account_id);
+  // account. If |show_for_current_user| is true, this sets the 'shown'
+  // account to the current account. If |delegate| is non-null, it is notified
+  // of changes rather than MultiUserWindowManagerDelegate.
+  void SetWindowOwner(aura::Window* window,
+                      const AccountId& account_id,
+                      bool show_for_current_user,
+                      MultiUserWindowManagerWindowDelegate* delegate = nullptr);
+
+  // Removes all references to |delegate|.
+  void RemoveWindowDelegate(MultiUserWindowManagerWindowDelegate* delegate);
 
   // Sets the 'shown' account for a window. See class description for details on
   // what the 'shown' account is. This function may trigger changing the active
@@ -102,8 +109,9 @@
  protected:
   class WindowEntry {
    public:
-    explicit WindowEntry(const AccountId& account_id)
-        : owner_(account_id), show_for_user_(account_id) {}
+    WindowEntry(const AccountId& account_id,
+                MultiUserWindowManagerWindowDelegate* delegate)
+        : owner_(account_id), show_for_user_(account_id), delegate_(delegate) {}
     ~WindowEntry() {}
 
     // Returns the owner of this window. This cannot be changed.
@@ -124,6 +132,9 @@
     // Sets if the window gets shown for the active user or not.
     void set_show(bool show) { show_ = show; }
 
+    void clear_delegate() { delegate_ = nullptr; }
+    MultiUserWindowManagerWindowDelegate* delegate() { return delegate_; }
+
    private:
     // The user id of the owner of this window.
     const AccountId owner_;
@@ -131,14 +142,16 @@
     // The user id of the user on which desktop the window gets shown.
     AccountId show_for_user_;
 
+    MultiUserWindowManagerWindowDelegate* delegate_;
+
     // True if the window should be visible for the user which shows the window.
     bool show_ = true;
 
     DISALLOW_COPY_AND_ASSIGN(WindowEntry);
   };
 
-  // TODO: make map to std::unique_ptr<WindowEntry>.
-  using WindowToEntryMap = std::map<aura::Window*, WindowEntry*>;
+  using WindowToEntryMap =
+      std::map<aura::Window*, std::unique_ptr<WindowEntry>>;
 
   const AccountId& GetWindowOwner(aura::Window* window) const;
 
diff --git a/ash/multi_user/multi_user_window_manager_delegate.h b/ash/multi_user/multi_user_window_manager_delegate.h
index 4df91f87..04597f7 100644
--- a/ash/multi_user/multi_user_window_manager_delegate.h
+++ b/ash/multi_user/multi_user_window_manager_delegate.h
@@ -20,6 +20,9 @@
   // Called when the owner of the window tracked by the manager is changed.
   // |was_minimized| is true if the window was minimized. |teleported| is true
   // if the window was not on the desktop of the current user.
+  // NOTE: when running this is not called for windows that are created through
+  // the WindowService. Windows created by the WindowService supply a
+  // MultiUserWindowManagerWindowDelegate.
   virtual void OnOwnerEntryChanged(aura::Window* window,
                                    const AccountId& account_id,
                                    bool was_minimized,
diff --git a/ash/multi_user/multi_user_window_manager_window_delegate.h b/ash/multi_user/multi_user_window_manager_window_delegate.h
new file mode 100644
index 0000000..5198ce1
--- /dev/null
+++ b/ash/multi_user/multi_user_window_manager_window_delegate.h
@@ -0,0 +1,36 @@
+// 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_MULTI_USER_MULTI_USER_WINDOW_MANAGER_WINDOW_DELEGATE_H_
+#define ASH_MULTI_USER_MULTI_USER_WINDOW_MANAGER_WINDOW_DELEGATE_H_
+
+#include "ash/ash_export.h"
+
+class AccountId;
+
+namespace aura {
+class Window;
+}  // namespace aura
+
+namespace ash {
+
+// Delegate associated with a single Window. This is used by windows created
+// by the WindowService.
+class ASH_EXPORT MultiUserWindowManagerWindowDelegate {
+ public:
+  // Called when the owner of the window tracked by the manager is changed.
+  // |was_minimized| is true if the window was minimized. |teleported| is true
+  // if the window was not on the desktop of the current user.
+  virtual void OnWindowOwnerEntryChanged(aura::Window* window,
+                                         const AccountId& account_id,
+                                         bool was_minimized,
+                                         bool teleported) {}
+
+ protected:
+  virtual ~MultiUserWindowManagerWindowDelegate() {}
+};
+
+}  // namespace ash
+
+#endif  // ASH_MULTI_USER_MULTI_USER_WINDOW_MANAGER_WINDOW_DELEGATE_H_
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index b1c277c..68b6f68 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -92,6 +92,8 @@
     "power_utils.h",
     "remote_shelf_item_delegate.cc",
     "remote_shelf_item_delegate.h",
+    "rounded_corner_decorator.cc",
+    "rounded_corner_decorator.h",
     "scale_utility.cc",
     "scale_utility.h",
     "session_types.h",
@@ -133,6 +135,7 @@
     "//skia/public/interfaces",
     "//ui/aura",
     "//ui/chromeos/strings",
+    "//ui/compositor_extra",
     "//ui/display",
     "//ui/events/devices",
     "//ui/message_center/public/cpp",
@@ -175,6 +178,7 @@
     "default_scale_factor_retriever_unittest.cc",
     "menu_utils_unittest.cc",
     "power_utils_unittest.cc",
+    "rounded_corner_decorator_unittest.cc",
     "shelf_model_unittest.cc",
     "shelf_struct_mojom_traits_unittest.cc",
   ]
@@ -184,6 +188,26 @@
     ":test_interfaces",
     "//base",
     "//testing/gtest",
+    "//ui/aura:test_support",
     "//ui/gfx:test_support",
   ]
 }
+
+source_set("test_support") {
+  sources = [
+    "immersive/immersive_fullscreen_controller_test_api.cc",
+    "immersive/immersive_fullscreen_controller_test_api.h",
+    "test/test_keyboard_controller_observer.cc",
+    "test/test_keyboard_controller_observer.h",
+  ]
+
+  deps = [
+    ":cpp",
+    "//base",
+    "//services/service_manager/public/cpp",
+    "//ui/aura",
+    "//ui/gfx",
+    "//ui/keyboard:mojom",
+    "//ui/views",
+  ]
+}
diff --git a/ash/public/cpp/app_list/app_list_features.cc b/ash/public/cpp/app_list/app_list_features.cc
index c4e8489..89d5482 100644
--- a/ash/public/cpp/app_list/app_list_features.cc
+++ b/ash/public/cpp/app_list/app_list_features.cc
@@ -20,8 +20,6 @@
     "EnablePlayStoreAppSearch", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kEnableAppDataSearch{"EnableAppDataSearch",
                                          base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kEnableHomeLauncher{"EnableHomeLauncher",
-                                        base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kEnableHomeLauncherGestures{
     "HomeLauncherGestures", base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kEnableSettingsShortcutSearch{
@@ -61,10 +59,6 @@
   return base::FeatureList::IsEnabled(kEnableAppDataSearch);
 }
 
-bool IsHomeLauncherEnabled() {
-  return base::FeatureList::IsEnabled(kEnableHomeLauncher);
-}
-
 bool IsHomeLauncherGesturesEnabled() {
   return base::FeatureList::IsEnabled(kEnableHomeLauncherGestures);
 }
diff --git a/ash/public/cpp/app_list/app_list_features.h b/ash/public/cpp/app_list/app_list_features.h
index 00b059c..18409b8 100644
--- a/ash/public/cpp/app_list/app_list_features.h
+++ b/ash/public/cpp/app_list/app_list_features.h
@@ -35,11 +35,6 @@
 // Enables in-app data search.
 ASH_PUBLIC_EXPORT extern const base::Feature kEnableAppDataSearch;
 
-// Enables the home launcher in tablet mode. In this mode, the launcher will be
-// always shown right on top of the wallpaper. Home button will minimize all
-// windows instead of toggling the launcher.
-ASH_PUBLIC_EXPORT extern const base::Feature kEnableHomeLauncher;
-
 // Enables using gestures to show or hide the home launcher.
 // TODO(crbug.com/872319): Remove this after the feature is launched.
 ASH_PUBLIC_EXPORT extern const base::Feature kEnableHomeLauncherGestures;
@@ -71,7 +66,6 @@
 bool ASH_PUBLIC_EXPORT IsBackgroundBlurEnabled();
 bool ASH_PUBLIC_EXPORT IsPlayStoreAppSearchEnabled();
 bool ASH_PUBLIC_EXPORT IsAppDataSearchEnabled();
-bool ASH_PUBLIC_EXPORT IsHomeLauncherEnabled();
 bool ASH_PUBLIC_EXPORT IsHomeLauncherGesturesEnabled();
 bool ASH_PUBLIC_EXPORT IsSettingsShortcutSearchEnabled();
 bool ASH_PUBLIC_EXPORT IsAppsGridGapFeatureEnabled();
diff --git a/ash/public/cpp/ash_features.cc b/ash/public/cpp/ash_features.cc
index 7183df4..9cf3eff 100644
--- a/ash/public/cpp/ash_features.cc
+++ b/ash/public/cpp/ash_features.cc
@@ -46,6 +46,12 @@
 const base::Feature kNotificationScrollBar{"NotificationScrollBar",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kPipRoundedCorners{"PipRoundedCorners",
+                                       base::FEATURE_ENABLED_BY_DEFAULT};
+
+const base::Feature kSeparateNetworkIcons{"SeparateNetworkIcons",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kTrilinearFiltering{"TrilinearFiltering",
                                         base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -57,6 +63,9 @@
 const base::Feature kUseBluetoothSystemInAsh{"UseBluetoothSystemInAsh",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kSupervisedUserDeprecationNotice{
+    "SupervisedUserDeprecationNotice", base::FEATURE_ENABLED_BY_DEFAULT};
+
 bool IsDockedMagnifierEnabled() {
   return base::FeatureList::IsEnabled(kDockedMagnifier);
 }
@@ -90,6 +99,14 @@
   return base::FeatureList::IsEnabled(kNotificationScrollBar);
 }
 
+bool IsPipRoundedCornersEnabled() {
+  return base::FeatureList::IsEnabled(kPipRoundedCorners);
+}
+
+bool IsSeparateNetworkIconsEnabled() {
+  return base::FeatureList::IsEnabled(kSeparateNetworkIcons);
+}
+
 bool IsTrilinearFilteringEnabled() {
   static bool use_trilinear_filtering =
       base::FeatureList::IsEnabled(kTrilinearFiltering);
@@ -105,5 +122,9 @@
          base::FeatureList::IsEnabled(kViewsLogin);
 }
 
+bool IsSupervisedUserDeprecationNoticeEnabled() {
+  return base::FeatureList::IsEnabled(kSupervisedUserDeprecationNotice);
+}
+
 }  // namespace features
 }  // namespace ash
diff --git a/ash/public/cpp/ash_features.h b/ash/public/cpp/ash_features.h
index 96b16001..c3572ce 100644
--- a/ash/public/cpp/ash_features.h
+++ b/ash/public/cpp/ash_features.h
@@ -63,6 +63,13 @@
 // Enables notification scroll bar in UnifiedSystemTray.
 ASH_PUBLIC_EXPORT extern const base::Feature kNotificationScrollBar;
 
+// Enables rounded corners for the Picture-in-picture window.
+ASH_PUBLIC_EXPORT extern const base::Feature kPipRoundedCorners;
+
+// Enables displaying separate network icons for different networks types.
+// https://crbug.com/902409
+ASH_PUBLIC_EXPORT extern const base::Feature kSeparateNetworkIcons;
+
 // Enables trilinear filtering.
 ASH_PUBLIC_EXPORT extern const base::Feature kTrilinearFiltering;
 
@@ -75,6 +82,9 @@
 // Enables using the BluetoothSystem Mojo interface for Bluetooth operations.
 ASH_PUBLIC_EXPORT extern const base::Feature kUseBluetoothSystemInAsh;
 
+// Enables the Supervised User Deprecation notices.
+ASH_PUBLIC_EXPORT extern const base::Feature kSupervisedUserDeprecationNotice;
+
 ASH_PUBLIC_EXPORT bool IsDockedMagnifierEnabled();
 
 ASH_PUBLIC_EXPORT bool IsKeyboardShortcutViewerAppEnabled();
@@ -91,10 +101,16 @@
 
 ASH_PUBLIC_EXPORT bool IsNotificationScrollBarEnabled();
 
+ASH_PUBLIC_EXPORT bool IsPipRoundedCornersEnabled();
+
+ASH_PUBLIC_EXPORT bool IsSeparateNetworkIconsEnabled();
+
 ASH_PUBLIC_EXPORT bool IsTrilinearFilteringEnabled();
 
 ASH_PUBLIC_EXPORT bool IsViewsLoginEnabled();
 
+ASH_PUBLIC_EXPORT bool IsSupervisedUserDeprecationNoticeEnabled();
+
 }  // namespace features
 }  // namespace ash
 
diff --git a/ash/public/cpp/rounded_corner_decorator.cc b/ash/public/cpp/rounded_corner_decorator.cc
new file mode 100644
index 0000000..2cd7a57
--- /dev/null
+++ b/ash/public/cpp/rounded_corner_decorator.cc
@@ -0,0 +1,97 @@
+// 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/public/cpp/rounded_corner_decorator.h"
+
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/window.h"
+#include "ui/compositor/paint_recorder.h"
+#include "ui/compositor_extra/shadow.h"
+#include "ui/wm/core/shadow_controller.h"
+
+namespace ash {
+
+RoundedCornerDecorator::RoundedCornerDecorator(aura::Window* shadow_window,
+                                               aura::Window* layer_window,
+                                               ui::Layer* layer,
+                                               int radius)
+    : layer_window_(layer_window), layer_(layer), radius_(radius) {
+  layer_window_->AddObserver(this);
+  layer_->AddObserver(this);
+  Update(layer_->size());
+
+  // Update the shadow if necessary.
+  ui::Shadow* shadow = wm::ShadowController::GetShadowForWindow(shadow_window);
+  if (shadow)
+    shadow->SetRoundedCornerRadius(radius_);
+}
+
+RoundedCornerDecorator::~RoundedCornerDecorator() {
+  Shutdown();
+}
+
+bool RoundedCornerDecorator::IsValid() {
+  return !!layer_;
+}
+
+void RoundedCornerDecorator::OnPaintLayer(const ui::PaintContext& context) {
+  cc::PaintFlags flags;
+  flags.setAlpha(255);
+  flags.setAntiAlias(true);
+  flags.setStyle(cc::PaintFlags::kFill_Style);
+
+  SkScalar radii[8] = {radius_, radius_,   // top-left
+                       radius_, radius_,   // top-right
+                       radius_, radius_,   // bottom-right
+                       radius_, radius_};  // bottom-left
+  SkPath path;
+  const gfx::Size size = mask_layer_->size();
+  path.addRoundRect(gfx::RectToSkRect(gfx::Rect(size)), radii);
+
+  ui::PaintRecorder recorder(context, size);
+  recorder.canvas()->DrawPath(path, flags);
+}
+
+void RoundedCornerDecorator::LayerDestroyed(ui::Layer* layer) {
+  Shutdown();
+}
+
+void RoundedCornerDecorator::OnWindowBoundsChanged(
+    aura::Window* window,
+    const gfx::Rect& old_bounds,
+    const gfx::Rect& new_bounds,
+    ui::PropertyChangeReason reason) {
+  Update(new_bounds.size());
+}
+
+void RoundedCornerDecorator::OnWindowDestroying(aura::Window* window) {
+  Shutdown();
+}
+
+void RoundedCornerDecorator::Update(const gfx::Size& size) {
+  DCHECK(layer_window_);
+  DCHECK(layer_);
+  if (!mask_layer_) {
+    mask_layer_ = std::make_unique<ui::Layer>(ui::LAYER_TEXTURED);
+    mask_layer_->set_delegate(this);
+    mask_layer_->SetFillsBoundsOpaquely(false);
+    layer_->SetMaskLayer(mask_layer_.get());
+  }
+  mask_layer_->SetBounds(gfx::Rect(size));
+}
+
+void RoundedCornerDecorator::Shutdown() {
+  if (!IsValid())
+    return;
+
+  if (layer_->layer_mask_layer() == mask_layer_.get())
+    layer_->SetMaskLayer(nullptr);
+  mask_layer_.reset();
+  layer_->RemoveObserver(this);
+  layer_window_->RemoveObserver(this);
+  layer_ = nullptr;
+  layer_window_ = nullptr;
+}
+
+}  // namespace ash
diff --git a/ash/public/cpp/rounded_corner_decorator.h b/ash/public/cpp/rounded_corner_decorator.h
new file mode 100644
index 0000000..00b72d1
--- /dev/null
+++ b/ash/public/cpp/rounded_corner_decorator.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 ASH_PUBLIC_CPP_ROUNDED_CORNER_DECORATOR_H_
+#define ASH_PUBLIC_CPP_ROUNDED_CORNER_DECORATOR_H_
+
+#include <memory>
+
+#include "ash/public/cpp/ash_public_export.h"
+#include "ui/aura/window_observer.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_delegate.h"
+#include "ui/compositor/layer_observer.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace ash {
+
+constexpr int kPipRoundedCornerRadius = 8;
+
+// Applies rounded corners to the given layer, and modifies the shadow of
+// the given window to be rounded.
+class ASH_PUBLIC_EXPORT RoundedCornerDecorator : public ui::LayerDelegate,
+                                                 public ui::LayerObserver,
+                                                 public aura::WindowObserver {
+ public:
+  RoundedCornerDecorator(aura::Window* shadow_window,
+                         aura::Window* layer_window,
+                         ui::Layer* layer,
+                         int radius);
+  ~RoundedCornerDecorator() override;
+
+  // Returns true if the rounded corner decorator is still applied to a valid
+  // layer.
+  bool IsValid();
+
+  // ui::LayerDelegate:
+  void OnPaintLayer(const ui::PaintContext& context) override;
+  void OnDeviceScaleFactorChanged(float old_device_scale_factor,
+                                  float new_device_scale_factor) override {}
+
+  // ui::LayerObserver:
+  void LayerDestroyed(ui::Layer* layer) override;
+
+  // aura::WindowObserver:
+  void OnWindowBoundsChanged(aura::Window* window,
+                             const gfx::Rect& old_bounds,
+                             const gfx::Rect& new_bounds,
+                             ui::PropertyChangeReason reason) override;
+  void OnWindowDestroying(aura::Window* window) override;
+
+ private:
+  void Update(const gfx::Size& size);
+  void Shutdown();
+
+  aura::Window* layer_window_;
+  ui::Layer* layer_;
+  std::unique_ptr<ui::Layer> mask_layer_;
+  int radius_;
+
+  DISALLOW_COPY_AND_ASSIGN(RoundedCornerDecorator);
+};
+
+}  // namespace ash
+
+#endif  // ASH_PUBLIC_CPP_ROUNDED_CORNER_DECORATOR_H_
diff --git a/ash/public/cpp/rounded_corner_decorator_unittest.cc b/ash/public/cpp/rounded_corner_decorator_unittest.cc
new file mode 100644
index 0000000..473d2d8
--- /dev/null
+++ b/ash/public/cpp/rounded_corner_decorator_unittest.cc
@@ -0,0 +1,58 @@
+// 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/public/cpp/rounded_corner_decorator.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/aura/test/aura_test_base.h"
+#include "ui/aura/test/test_windows.h"
+#include "ui/aura/window.h"
+
+namespace ash {
+
+typedef aura::test::AuraTestBase RoundedCornerDecoratorTest;
+
+// Test that the decorator doesn't try to apply itself to destroyed layers.
+TEST_F(RoundedCornerDecoratorTest, RoundedCornerMaskProperlyInvalidatesItself) {
+  std::unique_ptr<aura::Window> window(aura::test::CreateTestWindowWithBounds(
+      gfx::Rect(100, 100, 100, 100), root_window()));
+  auto decorator = std::make_unique<ash::RoundedCornerDecorator>(
+      window.get(), window.get(), window->layer(), 4);
+
+  // Confirm a mask layer exists and the decorator is valid.
+  EXPECT_TRUE(window->layer());
+  EXPECT_TRUE(window->layer()->layer_mask_layer());
+  EXPECT_TRUE(decorator->IsValid());
+
+  // Destroy window.
+  window.reset();
+
+  // Existing layer was destroyed, so the decorator should no longer be valid.
+  EXPECT_FALSE(decorator->IsValid());
+}
+
+// Test that mask layer changes bounds with the window it is applied to.
+TEST_F(RoundedCornerDecoratorTest,
+       RoundedCornerMaskChangesBoundsOnWindowBoundsChange) {
+  std::unique_ptr<aura::Window> window(aura::test::CreateTestWindowWithBounds(
+      gfx::Rect(100, 100, 100, 100), root_window()));
+  auto decorator = std::make_unique<ash::RoundedCornerDecorator>(
+      window.get(), window.get(), window->layer(), 4);
+
+  // Make sure the mask layer has the correct bounds and exists.
+  ASSERT_TRUE(window->layer()->layer_mask_layer());
+  EXPECT_EQ(gfx::Rect(0, 0, 100, 100),
+            window->layer()->layer_mask_layer()->bounds());
+
+  // Change the bounds of the window. Set zero duration animations to apply
+  // changes immediately.
+  window->SetBounds(gfx::Rect(0, 0, 150, 150));
+
+  // Make sure the mask layer's bounds are also changed.
+  EXPECT_EQ(gfx::Rect(0, 0, 150, 150).ToString(), window->bounds().ToString());
+  EXPECT_EQ(window->layer()->layer_mask_layer()->bounds().ToString(),
+            window->bounds().ToString());
+}
+
+}  // namespace ash
diff --git a/ash/public/cpp/test/test_keyboard_controller_observer.cc b/ash/public/cpp/test/test_keyboard_controller_observer.cc
new file mode 100644
index 0000000..2419fb2
--- /dev/null
+++ b/ash/public/cpp/test/test_keyboard_controller_observer.cc
@@ -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.
+
+#include "ash/public/cpp/test/test_keyboard_controller_observer.h"
+
+#include "base/run_loop.h"
+
+namespace ash {
+
+TestKeyboardControllerObserver::TestKeyboardControllerObserver(
+    mojom::KeyboardController* controller)
+    : controller_(controller) {
+  keyboard_controller_observer_binding_.Bind(
+      mojo::MakeRequestAssociatedWithDedicatedPipe(&ptr_));
+  controller_->AddObserver(ptr_.PassInterface());
+}
+
+TestKeyboardControllerObserver::~TestKeyboardControllerObserver() = default;
+
+void TestKeyboardControllerObserver::OnKeyboardEnableFlagsChanged(
+    const std::vector<keyboard::mojom::KeyboardEnableFlag>& flags) {
+  enable_flags_ = flags;
+}
+
+void TestKeyboardControllerObserver::OnKeyboardEnabledChanged(bool enabled) {
+  if (!enabled)
+    ++destroyed_count_;
+}
+
+void TestKeyboardControllerObserver::OnKeyboardConfigChanged(
+    keyboard::mojom::KeyboardConfigPtr config) {
+  config_ = *config;
+}
+
+void TestKeyboardControllerObserver::OnKeyboardVisibilityChanged(bool visible) {
+}
+
+void TestKeyboardControllerObserver::OnKeyboardVisibleBoundsChanged(
+    const gfx::Rect& bounds) {}
+
+void TestKeyboardControllerObserver::OnKeyboardOccludedBoundsChanged(
+    const gfx::Rect& bounds) {}
+
+}  // namespace ash
diff --git a/ash/public/cpp/test/test_keyboard_controller_observer.h b/ash/public/cpp/test/test_keyboard_controller_observer.h
new file mode 100644
index 0000000..868a1de
--- /dev/null
+++ b/ash/public/cpp/test/test_keyboard_controller_observer.h
@@ -0,0 +1,56 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_PUBLIC_CPP_TEST_TEST_KEYBOARD_CONTROLLER_OBSERVER_H_
+#define ASH_PUBLIC_CPP_TEST_TEST_KEYBOARD_CONTROLLER_OBSERVER_H_
+
+#include "ash/public/interfaces/keyboard_controller.mojom.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+
+namespace ash {
+
+// mojom::KeyboardControllerObserver implementation for tests. This class
+// implements a test client observer for tests running with the Window Service.
+
+class TestKeyboardControllerObserver
+    : public mojom::KeyboardControllerObserver {
+ public:
+  explicit TestKeyboardControllerObserver(
+      mojom::KeyboardController* controller);
+  ~TestKeyboardControllerObserver() override;
+
+  // mojom::KeyboardControllerObserver
+  void OnKeyboardEnableFlagsChanged(
+      const std::vector<keyboard::mojom::KeyboardEnableFlag>& flags) override;
+  void OnKeyboardEnabledChanged(bool enabled) override;
+  void OnKeyboardConfigChanged(
+      keyboard::mojom::KeyboardConfigPtr config) override;
+  void OnKeyboardVisibilityChanged(bool visible) override;
+  void OnKeyboardVisibleBoundsChanged(const gfx::Rect& bounds) override;
+  void OnKeyboardOccludedBoundsChanged(const gfx::Rect& bounds) override;
+
+  const keyboard::mojom::KeyboardConfig& config() const { return config_; }
+  void set_config(const keyboard::mojom::KeyboardConfig& config) {
+    config_ = config;
+  }
+  const std::vector<keyboard::mojom::KeyboardEnableFlag>& enable_flags() const {
+    return enable_flags_;
+  }
+  int destroyed_count() const { return destroyed_count_; }
+
+ private:
+  mojom::KeyboardController* controller_;
+  mojom::KeyboardControllerObserverAssociatedPtr ptr_;
+  std::vector<keyboard::mojom::KeyboardEnableFlag> enable_flags_;
+  keyboard::mojom::KeyboardConfig config_;
+  int destroyed_count_ = 0;
+  mojo::AssociatedBinding<mojom::KeyboardControllerObserver>
+      keyboard_controller_observer_binding_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(TestKeyboardControllerObserver);
+};
+
+}  // namespace ash
+
+#endif  // ASH_PUBLIC_CPP_TEST_TEST_KEYBOARD_CONTROLLER_OBSERVER_H_
diff --git a/ash/public/cpp/window_animation_types.h b/ash/public/cpp/window_animation_types.h
index 244fa65..a2a7558 100644
--- a/ash/public/cpp/window_animation_types.h
+++ b/ash/public/cpp/window_animation_types.h
@@ -21,9 +21,9 @@
   // Window slides down from above screen to show and, meanwhile, home launcher
   // slides down off screen.
   WINDOW_VISIBILITY_ANIMATION_TYPE_SLIDE_DOWN,
-  // Animate a window out of the closest side of the screen.
-  // This is for hiding only, and does not do anything for showing.
-  WINDOW_VISIBILITY_ANIMATION_TYPE_SLIDE_OUT,
+  // Animate a window out of the closest side of the screen. Fade in if it
+  // re-appears.
+  WINDOW_VISIBILITY_ANIMATION_TYPE_FADE_IN_SLIDE_OUT,
 };
 
 }  // namespace wm
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn
index d9c4b81..d44aa88 100644
--- a/ash/public/interfaces/BUILD.gn
+++ b/ash/public/interfaces/BUILD.gn
@@ -42,6 +42,7 @@
     "login_user_info.mojom",
     "media.mojom",
     "menu.mojom",
+    "multi_user_window_manager.mojom",
     "new_window.mojom",
     "night_light_controller.mojom",
     "note_taking_controller.mojom",
diff --git a/ash/public/interfaces/app_list.mojom b/ash/public/interfaces/app_list.mojom
index 2838360..c11efda8 100644
--- a/ash/public/interfaces/app_list.mojom
+++ b/ash/public/interfaces/app_list.mojom
@@ -67,6 +67,12 @@
                              // (e.g. displaying inline web contents) is
                              // dependent on the result type.
 
+  string? equivalent_result_id;  // An optional id that identifies an equivalent
+                                 // result to this result. Answer card result
+                                 // has this set to remove the equivalent
+                                 // omnibox search-what-you-typed result when
+                                 // there is an answer card for the query.
+
   gfx.mojom.ImageSkia? icon;  // The icon of this result.
   gfx.mojom.ImageSkia? chip_icon;  // The icon of this result in a smaller
                                    // dimension to be rendered in suggestion
diff --git a/ash/public/interfaces/assistant_controller.mojom b/ash/public/interfaces/assistant_controller.mojom
index 906f037..6eb95bc 100644
--- a/ash/public/interfaces/assistant_controller.mojom
+++ b/ash/public/interfaces/assistant_controller.mojom
@@ -22,14 +22,26 @@
   SetAssistantImageDownloader(
     AssistantImageDownloader assistant_image_downloader);
 
-  // Provides an interface to the |assistant_setup| owned by AssistantClient.
-  SetAssistantSetup(AssistantSetup assistant_setup);
-
-  // Requests screenshot of specified |rect| region and returns the screenshot
-  // encoded in JPEG format. If |rect| is empty, it returns fullscreen
-  // screenshot.
-  RequestScreenshot(gfx.mojom.Rect rect) => (array<uint8> screenshot);
-
   // Opens Google Assistant settings.
   OpenAssistantSettings();
 };
+
+// Interface to the AssistantScreenContextController which is owned by the
+// AssistantController. Currently used by the Assistant service to request
+// screenshots.
+interface AssistantScreenContextController {
+  // Requests a screenshot of the region enclosed by |rect| and returns the
+  // screenshot encoded in JPEG format. If |rect| is empty, it returns a
+  // fullscreen screenshot.
+  RequestScreenshot(gfx.mojom.Rect rect) => (array<uint8> screenshot);
+};
+
+// Interface to the AssistantSetupController which is owned by the
+// AssistantController. Currently used by AssistantSetup in chrome/browser
+// to provide an interface to itself. This is used for triggering a runtime
+// onboarding flow.
+interface AssistantSetupController {
+  // Provides an interface to the |assistant_setup| owned by AssistantClient
+  // in chrome/browser.
+  SetAssistantSetup(AssistantSetup assistant_setup);
+};
\ No newline at end of file
diff --git a/ash/public/interfaces/keyboard_controller.mojom b/ash/public/interfaces/keyboard_controller.mojom
index 348b5bd..3b7a10a 100644
--- a/ash/public/interfaces/keyboard_controller.mojom
+++ b/ash/public/interfaces/keyboard_controller.mojom
@@ -34,8 +34,13 @@
   // field is focused or blurred, or the user hides the keyboard.
   OnKeyboardVisibilityChanged(bool visible);
 
-  // Called when the keyboard bounds change.
-  OnKeyboardVisibleBoundsChanged(gfx.mojom.Rect new_bounds);
+  // Called when the keyboard bounds change. |screen_bounds| is in screen
+  // coordinates.
+  OnKeyboardVisibleBoundsChanged(gfx.mojom.Rect screen_bounds);
+
+  // Called when the keyboard occluded bounds change. |screen_bounds| is in
+  // screen coordinates.
+  OnKeyboardOccludedBoundsChanged(gfx.mojom.Rect screen_bounds);
 };
 
 interface KeyboardController {
diff --git a/ash/public/interfaces/multi_user_window_manager.mojom b/ash/public/interfaces/multi_user_window_manager.mojom
new file mode 100644
index 0000000..dca7a2f
--- /dev/null
+++ b/ash/public/interfaces/multi_user_window_manager.mojom
@@ -0,0 +1,34 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module ash.mojom;
+
+import "components/account_id/interfaces/account_id.mojom";
+
+// Used to assign windows to user accounts so that ash shows the appropriate set
+// of windows based on the active user.
+interface MultiUserWindowManager {
+  SetClient(associated MultiUserWindowManagerClient client);
+
+  // Associates a window with an account. If |show_for_current_user| is true,
+  // the window is associated with |account_id|, but is shown for the currently
+  // active user.
+  SetWindowOwner(uint64 window_id,
+                 signin.mojom.AccountId account_id,
+                 bool show_for_current_user);
+
+  // Shows a previously registered window for the specified account.
+  ShowWindowForUser(uint64 window_id,
+                    signin.mojom.AccountId account_id);
+};
+
+interface MultiUserWindowManagerClient {
+  // Called when the owner of a window supplied to SetWindowOwner() changes.
+  // |was_minimized| is true if the window was minimized. |teleported| is true
+  // if the window was not on the desktop of the current user.
+  OnWindowOwnerEntryChanged(uint64 window_id,
+                            signin.mojom.AccountId account_id,
+                            bool was_minimized,
+                            bool teleported);
+};
diff --git a/ash/public/interfaces/shell_test_api.mojom b/ash/public/interfaces/shell_test_api.mojom
index c4c0b17..bab9b46 100644
--- a/ash/public/interfaces/shell_test_api.mojom
+++ b/ash/public/interfaces/shell_test_api.mojom
@@ -16,11 +16,14 @@
   // controller. In tablet mode, enables the virtual keyboard.
   EnableVirtualKeyboard() => ();
 
-  // 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) => ();
+  // Tells the SplitViewController to snap the given window to the left or
+  // right. The client name is used to find the client's WindowTree.
+  SnapWindowInSplitView(string client_name, uint64 window_id, bool left) => ();
 
   // Fullscreens the active window, as if the user had pressed the hardware
   // fullscreen button.
   ToggleFullscreen() => ();
+
+  // Enters or exits overview mode.
+  ToggleOverviewMode() => ();
 };
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index c8ffab9..46ecafb 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -72,7 +72,6 @@
 #include "ui/aura/window_observer.h"
 #include "ui/aura/window_tracker.h"
 #include "ui/base/models/menu_model.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/compositor/layer.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/events/event_utils.h"
@@ -632,20 +631,17 @@
   UMA_HISTOGRAM_ENUMERATION("Apps.ContextMenuShowSource.Desktop", source_type,
                             ui::MENU_SOURCE_TYPE_LAST);
 
-  int run_types = views::MenuRunner::CONTEXT_MENU;
-  views::MenuAnchorPosition anchor_position = views::MENU_ANCHOR_TOPLEFT;
-  if (::features::IsTouchableAppContextMenuEnabled()) {
-    run_types |= views::MenuRunner::USE_TOUCHABLE_LAYOUT |
-                 views::MenuRunner::FIXED_ANCHOR;
-    anchor_position = views::MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE;
-  }
   menu_runner_ = std::make_unique<views::MenuRunner>(
-      menu_model_.get(), run_types,
+      menu_model_.get(),
+      views::MenuRunner::CONTEXT_MENU |
+          views::MenuRunner::USE_TOUCHABLE_LAYOUT |
+          views::MenuRunner::FIXED_ANCHOR,
       base::Bind(&RootWindowController::OnMenuClosed, base::Unretained(this),
                  base::TimeTicks::Now()));
   menu_runner_->RunMenuAt(wallpaper_widget_controller()->GetWidget(), nullptr,
                           gfx::Rect(location_in_screen, gfx::Size()),
-                          anchor_position, source_type);
+                          views::MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE,
+                          source_type);
 }
 
 void RootWindowController::HideContextMenu() {
diff --git a/ash/root_window_controller_unittest.cc b/ash/root_window_controller_unittest.cc
index c28d208..41adab8 100644
--- a/ash/root_window_controller_unittest.cc
+++ b/ash/root_window_controller_unittest.cc
@@ -687,12 +687,11 @@
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         keyboard::switches::kEnableVirtualKeyboard);
     AshTestBase::SetUp();
-    keyboard::SetTouchKeyboardEnabled(true);
-    Shell::Get()->EnableKeyboard();
+    SetTouchKeyboardEnabled(true);
   }
 
   void TearDown() override {
-    keyboard::SetTouchKeyboardEnabled(false);
+    SetTouchKeyboardEnabled(false);
     AshTestBase::TearDown();
   }
 
diff --git a/ash/session/test_session_controller_client.cc b/ash/session/test_session_controller_client.cc
index 9660f3f..1e783be 100644
--- a/ash/session/test_session_controller_client.cc
+++ b/ash/session/test_session_controller_client.cc
@@ -184,6 +184,7 @@
 
 void TestSessionControllerClient::RequestSignOut() {
   Reset();
+  ++request_sign_out_count_;
 }
 
 void TestSessionControllerClient::SwitchActiveUser(
diff --git a/ash/session/test_session_controller_client.h b/ash/session/test_session_controller_client.h
index cc466c2..07cfb39 100644
--- a/ash/session/test_session_controller_client.h
+++ b/ash/session/test_session_controller_client.h
@@ -47,6 +47,8 @@
     use_lower_case_user_id_ = value;
   }
 
+  int request_sign_out_count() const { return request_sign_out_count_; }
+
   // Helpers to set SessionController state.
   void SetCanLockScreen(bool can_lock);
   void SetShouldLockScreenAutomatically(bool should_lock);
@@ -100,6 +102,7 @@
   mojom::SessionInfoPtr session_info_;
 
   bool use_lower_case_user_id_ = true;
+  int request_sign_out_count_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(TestSessionControllerClient);
 };
diff --git a/ash/shelf/app_list_button.cc b/ash/shelf/app_list_button.cc
index 33eaae9..9946c64 100644
--- a/ash/shelf/app_list_button.cc
+++ b/ash/shelf/app_list_button.cc
@@ -23,6 +23,7 @@
 #include "ash/shell_state.h"
 #include "ash/system/tray/tray_popup_utils.h"
 #include "ash/voice_interaction/voice_interaction_controller.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/command_line.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
@@ -54,10 +55,10 @@
          chromeos::switches::IsAssistantEnabled();
 }
 
-bool IsHomeLauncherShown() {
+bool IsTabletMode() {
   return Shell::Get()
-      ->app_list_controller()
-      ->IsHomeLauncherEnabledInTabletMode();
+      ->tablet_mode_controller()
+      ->IsTabletModeWindowManagerEnabled();
 }
 
 }  // namespace
@@ -99,7 +100,7 @@
 void AppListButton::OnAppListShown() {
   // Do not show a highlight in tablet mode since the "homecher" view is always
   // open in the background.
-  if (!IsHomeLauncherShown())
+  if (!IsTabletMode())
     AnimateInkDrop(views::InkDropState::ACTIVATED, nullptr);
   is_showing_app_list_ = true;
   shelf_->UpdateAutoHideState();
@@ -124,7 +125,7 @@
         assistant_overlay_->EndAnimation();
         assistant_animation_delay_timer_->Stop();
       }
-      if (IsHomeLauncherShown())
+      if (IsTabletMode())
         AnimateInkDrop(views::InkDropState::ACTION_TRIGGERED, event);
       ImageButton::OnGestureEvent(event);
       return;
@@ -137,8 +138,7 @@
             base::Bind(&AppListButton::StartVoiceInteractionAnimation,
                        base::Unretained(this)));
       }
-      if (!Shell::Get()->app_list_controller()->IsVisible() ||
-          IsHomeLauncherShown()) {
+      if (!Shell::Get()->app_list_controller()->IsVisible() || IsTabletMode()) {
         AnimateInkDrop(views::InkDropState::ACTION_PENDING, event);
       }
       ImageButton::OnGestureEvent(event);
diff --git a/ash/shelf/shelf.cc b/ash/shelf/shelf.cc
index 23910e7..7a28a41 100644
--- a/ash/shelf/shelf.cc
+++ b/ash/shelf/shelf.cc
@@ -45,7 +45,7 @@
         event, static_cast<aura::Window*>(event->target()));
   }
   void OnGestureEvent(ui::GestureEvent* event) override {
-    shelf_layout_manager_->ProcessGestureEventOnWindow(
+    shelf_layout_manager_->ProcessGestureEventOfAutoHideShelf(
         event, static_cast<aura::Window*>(event->target()));
   }
 
@@ -95,6 +95,10 @@
   shelf_widget_.reset();
 }
 
+bool Shelf::IsVisible() const {
+  return shelf_layout_manager_->IsVisible();
+}
+
 aura::Window* Shelf::GetWindow() {
   return shelf_widget_->GetNativeWindow();
 }
diff --git a/ash/shelf/shelf.h b/ash/shelf/shelf.h
index 7a21553..0c4c2c2 100644
--- a/ash/shelf/shelf.h
+++ b/ash/shelf/shelf.h
@@ -59,6 +59,11 @@
     return shelf_layout_manager_;
   }
 
+  // Returns true if the shelf is visible. Shelf can be visible in 1)
+  // SHELF_VISIBLE or 2) SHELF_AUTO_HIDE but in SHELF_AUTO_HIDE_SHOWN. See
+  // details in ShelfLayoutManager::IsVisible.
+  bool IsVisible() const;
+
   ShelfWidget* shelf_widget() { return shelf_widget_.get(); }
 
   // Returns the window showing the shelf.
diff --git a/ash/shelf/shelf_application_menu_model.cc b/ash/shelf/shelf_application_menu_model.cc
index 76aabbbc9..dcaf03d 100644
--- a/ash/shelf/shelf_application_menu_model.cc
+++ b/ash/shelf/shelf_application_menu_model.cc
@@ -11,7 +11,6 @@
 
 #include "ash/public/cpp/shelf_item_delegate.h"
 #include "base/metrics/histogram_macros.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/gfx/image/image.h"
 
@@ -28,11 +27,7 @@
     std::vector<mojom::MenuItemPtr> items,
     ShelfItemDelegate* delegate)
     : ui::SimpleMenuModel(this), items_(std::move(items)), delegate_(delegate) {
-  if (!features::IsTouchableAppContextMenuEnabled())
-    AddSeparator(ui::SPACING_SEPARATOR);
   AddItem(kInvalidCommandId, title);
-  if (!features::IsTouchableAppContextMenuEnabled())
-    AddSeparator(ui::SPACING_SEPARATOR);
 
   for (size_t i = 0; i < items_.size(); i++) {
     mojom::MenuItem* item = items_[i].get();
diff --git a/ash/shelf/shelf_button.cc b/ash/shelf/shelf_button.cc
index c446ff9..d0951b1d 100644
--- a/ash/shelf/shelf_button.cc
+++ b/ash/shelf/shelf_button.cc
@@ -605,16 +605,20 @@
 void ShelfButton::OnGestureEvent(ui::GestureEvent* event) {
   switch (event->type()) {
     case ui::ET_GESTURE_TAP_DOWN:
-      AddState(STATE_HOVERED);
-      drag_timer_.Start(
-          FROM_HERE, base::TimeDelta::FromMilliseconds(kDragTimeThresholdMs),
-          base::Bind(&ShelfButton::OnTouchDragTimer, base::Unretained(this)));
-      ripple_activation_timer_.Start(
-          FROM_HERE,
-          base::TimeDelta::FromMilliseconds(kInkDropRippleActivationTimeMs),
-          base::Bind(&ShelfButton::OnRippleTimer, base::Unretained(this)));
-      GetInkDrop()->AnimateToState(views::InkDropState::ACTION_PENDING);
-      event->SetHandled();
+      if (shelf_view_->shelf()->IsVisible()) {
+        AddState(STATE_HOVERED);
+        drag_timer_.Start(
+            FROM_HERE, base::TimeDelta::FromMilliseconds(kDragTimeThresholdMs),
+            base::BindRepeating(&ShelfButton::OnTouchDragTimer,
+                                base::Unretained(this)));
+        ripple_activation_timer_.Start(
+            FROM_HERE,
+            base::TimeDelta::FromMilliseconds(kInkDropRippleActivationTimeMs),
+            base::BindRepeating(&ShelfButton::OnRippleTimer,
+                                base::Unretained(this)));
+        GetInkDrop()->AnimateToState(views::InkDropState::ACTION_PENDING);
+        event->SetHandled();
+      }
       break;
     case ui::ET_GESTURE_END:
       drag_timer_.Stop();
diff --git a/ash/shelf/shelf_context_menu_model.cc b/ash/shelf/shelf_context_menu_model.cc
index 384a29cd..9a40037 100644
--- a/ash/shelf/shelf_context_menu_model.cc
+++ b/ash/shelf/shelf_context_menu_model.cc
@@ -25,7 +25,6 @@
 #include "base/numerics/safe_conversions.h"
 #include "components/prefs/pref_service.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/controls/menu/menu_config.h"
@@ -62,11 +61,6 @@
                                   ->tablet_mode_controller()
                                   ->IsTabletModeWindowManagerEnabled();
 
-  // When touchable app context menus are enabled in tablet mode, shelf
-  // alignment option is not shown.
-  const bool skip_clamshell_only_options =
-      features::IsTouchableAppContextMenuEnabled() && is_tablet_mode;
-
   const views::MenuConfig& menu_config = views::MenuConfig::instance();
 
   // In fullscreen, the shelf is either hidden or auto-hidden, depending on
@@ -78,7 +72,6 @@
     const bool is_autohide_set =
         GetShelfAutoHideBehaviorPref(prefs, display_id) ==
         SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
-    if (features::IsTouchableAppContextMenuEnabled()) {
       auto_hide->type = ui::MenuModel::TYPE_COMMAND;
       auto_hide->image = gfx::CreateVectorIcon(
           is_autohide_set ? kAlwaysShowShelfIcon : kAutoHideIcon,
@@ -86,20 +79,17 @@
       auto_hide->label = GetStringUTF16(
           is_autohide_set ? IDS_ASH_SHELF_CONTEXT_MENU_ALWAYS_SHOW_SHELF
                           : IDS_ASH_SHELF_CONTEXT_MENU_AUTO_HIDE);
-    } else {
-      auto_hide->label = GetStringUTF16(IDS_ASH_SHELF_CONTEXT_MENU_AUTO_HIDE);
-      auto_hide->type = ui::MenuModel::TYPE_CHECK;
-      auto_hide->checked = is_autohide_set;
-    }
+
     auto_hide->command_id = ShelfContextMenuModel::MENU_AUTO_HIDE;
     auto_hide->enabled = true;
     menu->push_back(std::move(auto_hide));
   }
 
-  // Only allow shelf alignment modifications by the owner or user.
+  // Only allow shelf alignment modifications by the owner or user. In tablet
+  // mode, the shelf alignment option is not shown.
   LoginStatus status = Shell::Get()->session_controller()->login_status();
   if ((status == LoginStatus::USER || status == LoginStatus::OWNER) &&
-      !skip_clamshell_only_options) {
+      !is_tablet_mode) {
     const ShelfAlignment alignment = GetShelfAlignmentPref(prefs, display_id);
     mojom::MenuItemPtr alignment_menu(mojom::MenuItem::New());
     alignment_menu->type = ui::MenuModel::TYPE_SUBMENU;
@@ -107,11 +97,9 @@
     alignment_menu->label = GetStringUTF16(IDS_ASH_SHELF_CONTEXT_MENU_POSITION);
     alignment_menu->submenu = MenuItemList();
     alignment_menu->enabled = !is_tablet_mode;
-    if (features::IsTouchableAppContextMenuEnabled()) {
-      alignment_menu->image = gfx::CreateVectorIcon(
-          kShelfPositionIcon, menu_config.touchable_icon_size,
-          menu_config.touchable_icon_color);
-    }
+    alignment_menu->image = gfx::CreateVectorIcon(
+        kShelfPositionIcon, menu_config.touchable_icon_size,
+        menu_config.touchable_icon_color);
 
     mojom::MenuItemPtr left(mojom::MenuItem::New());
     left->type = ui::MenuModel::TYPE_RADIO;
@@ -146,11 +134,9 @@
     wallpaper->command_id = ShelfContextMenuModel::MENU_CHANGE_WALLPAPER;
     wallpaper->label = GetStringUTF16(IDS_AURA_SET_DESKTOP_WALLPAPER);
     wallpaper->enabled = true;
-    if (features::IsTouchableAppContextMenuEnabled()) {
-      wallpaper->image =
-          gfx::CreateVectorIcon(kWallpaperIcon, menu_config.touchable_icon_size,
-                                menu_config.touchable_icon_color);
-    }
+    wallpaper->image =
+        gfx::CreateVectorIcon(kWallpaperIcon, menu_config.touchable_icon_size,
+                              menu_config.touchable_icon_color);
     menu->push_back(std::move(wallpaper));
   }
 }
@@ -165,7 +151,7 @@
       delegate_(delegate),
       display_id_(display_id) {
   // Append shelf settings and wallpaper items if no shelf item was selected.
-  if (!features::IsTouchableAppContextMenuEnabled() || !delegate)
+  if (!delegate)
     AddLocalMenuItems(&menu_items_, display_id);
   menu_utils::PopulateMenuFromMojoMenuItems(this, this, menu_items_,
                                             &submenus_);
diff --git a/ash/shelf/shelf_controller.h b/ash/shelf/shelf_controller.h
index 31bb3b2..453e46f 100644
--- a/ash/shelf/shelf_controller.h
+++ b/ash/shelf/shelf_controller.h
@@ -102,7 +102,7 @@
   // Changes to the local ShelfModel should not be reported during this time.
   bool applying_remote_shelf_model_changes_ = false;
 
-  // Whether touchable context menus have been enabled for app icons on the
+  // Whether notification indicators have been enabled for app icons in the
   // shelf.
   const bool is_notification_indicator_enabled_;
 
diff --git a/ash/shelf/shelf_controller_unittest.cc b/ash/shelf/shelf_controller_unittest.cc
index 819322b..35010bf 100644
--- a/ash/shelf/shelf_controller_unittest.cc
+++ b/ash/shelf/shelf_controller_unittest.cc
@@ -209,27 +209,26 @@
   EXPECT_FALSE(controller->model()->items()[index].image.isNull());
 }
 
-class ShelfControllerTouchableContextMenuTest : public AshTestBase {
+class ShelfControllerNotificationIndicatorTest : public AshTestBase {
  public:
-  ShelfControllerTouchableContextMenuTest() = default;
-  ~ShelfControllerTouchableContextMenuTest() override = default;
+  ShelfControllerNotificationIndicatorTest() = default;
+  ~ShelfControllerNotificationIndicatorTest() override = default;
 
   void SetUp() override {
-    scoped_feature_list_.InitWithFeatures(
-        {features::kTouchableAppContextMenu, features::kNotificationIndicator},
-        {});
+    scoped_feature_list_.InitWithFeatures({features::kNotificationIndicator},
+                                          {});
     AshTestBase::SetUp();
   }
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 
-  DISALLOW_COPY_AND_ASSIGN(ShelfControllerTouchableContextMenuTest);
+  DISALLOW_COPY_AND_ASSIGN(ShelfControllerNotificationIndicatorTest);
 };
 
 // Tests that the ShelfController keeps the ShelfModel updated on new
 // notifications.
-TEST_F(ShelfControllerTouchableContextMenuTest, HasNotificationBasic) {
+TEST_F(ShelfControllerNotificationIndicatorTest, HasNotificationBasic) {
   ShelfController* controller = Shell::Get()->shelf_controller();
   const std::string app_id("app_id");
   ShelfItem item;
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index 093bdd7..ff75529 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -95,10 +95,10 @@
       ->IsTabletModeWindowManagerEnabled();
 }
 
-// TODO(sammiequon): This should be the same as IsTabletModeEnabled once home
-// launcher flag is removed.
 bool IsHomeLauncherEnabledInTabletMode() {
-  return app_list_features::IsHomeLauncherEnabled() && IsTabletModeEnabled();
+  // Home launcher flag is enabled by default and removed, so this is the same
+  // as checking tablet mode state.
+  return IsTabletModeEnabled();
 }
 
 }  // namespace
@@ -349,14 +349,24 @@
   }
 }
 
-void ShelfLayoutManager::ProcessGestureEventOnWindow(ui::GestureEvent* event,
-                                                     aura::Window* target) {
+void ShelfLayoutManager::ProcessGestureEventOfAutoHideShelf(
+    ui::GestureEvent* event,
+    aura::Window* target) {
+  const bool is_shelf_window = IsShelfWindow(target);
   // Skip event processing if shelf widget is fully visible to let the default
   // event dispatching do its work.
-  if (IsVisible() || in_shutdown_)
+  if (IsVisible() || in_shutdown_) {
+    // Tap outside of the AUTO_HIDE_SHOWN shelf should hide it.
+    if (!is_shelf_window && !IsStatusAreaWindow(target) &&
+        visibility_state() == SHELF_AUTO_HIDE &&
+        state_.auto_hide_state == SHELF_AUTO_HIDE_SHOWN &&
+        event->type() == ui::ET_GESTURE_TAP) {
+      UpdateAutoHideState();
+    }
     return;
+  }
 
-  if (IsShelfWindow(target)) {
+  if (is_shelf_window) {
     ui::GestureEvent event_in_screen(*event);
     gfx::Point location_in_screen(event->location());
     ::wm::ConvertPointToScreen(target, &location_in_screen);
@@ -394,13 +404,6 @@
 
   if (gesture_drag_status_ != GESTURE_DRAG_IN_PROGRESS &&
       gesture_drag_status_ != GESTURE_DRAG_APPLIST_IN_PROGRESS) {
-    if ((shelf_->GetVisibilityState() == SHELF_AUTO_HIDE &&
-         shelf_->GetAutoHideState() == SHELF_AUTO_HIDE_HIDDEN) ||
-        shelf_->GetVisibilityState() == SHELF_HIDDEN) {
-      // Do not allow children to handle events while the shelf is hidden.
-      return true;
-    }
-
     return false;
   }
 
@@ -996,8 +999,9 @@
     return SHELF_AUTO_HIDE_SHOWN;
 
   if (shelf_widget_->status_area_widget() &&
-      shelf_widget_->status_area_widget()->ShouldShowShelf())
+      shelf_widget_->status_area_widget()->ShouldShowShelf()) {
     return SHELF_AUTO_HIDE_SHOWN;
+  }
 
   if (shelf_widget_->IsShowingContextMenu())
     return SHELF_AUTO_HIDE_SHOWN;
@@ -1049,8 +1053,12 @@
 
   gfx::Point cursor_position_in_screen =
       display::Screen::GetScreen()->GetCursorScreenPoint();
-  if (shelf_region.Contains(cursor_position_in_screen))
+  // Cursor is invisible in talbet mode and plug in an external mouse in tablet
+  // mode will switch to clamshell mode.
+  if (shelf_region.Contains(cursor_position_in_screen) &&
+      !IsTabletModeEnabled()) {
     return SHELF_AUTO_HIDE_SHOWN;
+  }
 
   // When the shelf is auto hidden and the shelf is on the boundary between two
   // displays, it is hard to trigger showing the shelf. For instance, if a
@@ -1240,6 +1248,10 @@
   if (is_app_list_visible_ && !IsHomeLauncherEnabledInTabletMode())
     return false;
 
+  // Also disable shelf drags until the overflow shelf is closed.
+  if (shelf_widget_->IsShowingOverflowBubble())
+    return false;
+
   gesture_drag_status_ = GESTURE_DRAG_IN_PROGRESS;
   gesture_drag_auto_hide_state_ = visibility_state() == SHELF_AUTO_HIDE
                                       ? auto_hide_state()
@@ -1388,6 +1400,11 @@
   if (!IsVisible())
     return false;
 
+  // Do not show the fullscreen app list until the overflow bubble has been
+  // closed.
+  if (shelf_widget_->IsShowingOverflowBubble())
+    return false;
+
   // If app list is already opened, swiping up on the shelf should keep the app
   // list opened.
   if (is_app_list_visible_)
diff --git a/ash/shelf/shelf_layout_manager.h b/ash/shelf/shelf_layout_manager.h
index 3fdb91c..bd4aae2 100644
--- a/ash/shelf/shelf_layout_manager.h
+++ b/ash/shelf/shelf_layout_manager.h
@@ -113,9 +113,10 @@
   // Updates the auto-hide state for mouse events.
   void UpdateAutoHideForMouseEvent(ui::MouseEvent* event, aura::Window* target);
 
-  // Process the gesture events on |target|.
-  void ProcessGestureEventOnWindow(ui::GestureEvent* event,
-                                   aura::Window* target);
+  // Called by AutoHideEventHandler to process the gesture events when shelf is
+  // auto hide.
+  void ProcessGestureEventOfAutoHideShelf(ui::GestureEvent* event,
+                                          aura::Window* target);
 
   ShelfVisibilityState visibility_state() const {
     return state_.visibility_state;
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index a961b70..43aa2c2 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -13,7 +13,6 @@
 #include "ash/app_list/home_launcher_gesture_handler.h"
 #include "ash/app_list/test/app_list_test_helper.h"
 #include "ash/focus_cycler.h"
-#include "ash/public/cpp/app_list/app_list_features.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h"
 #include "ash/public/cpp/shell_window_ids.h"
@@ -36,7 +35,7 @@
 #include "ash/wm/lock_state_controller.h"
 #include "ash/wm/overview/window_selector_controller.h"
 #include "ash/wm/splitview/split_view_controller.h"
-#include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_event.h"
@@ -807,23 +806,6 @@
             GetShelfWidget()->GetWindowBoundsInScreen().ToString());
 }
 
-class ShelfLayoutManagerNonHomeLauncherTest : public ShelfLayoutManagerTest {
- public:
-  ShelfLayoutManagerNonHomeLauncherTest() = default;
-  ~ShelfLayoutManagerNonHomeLauncherTest() override = default;
-
-  void SetUp() override {
-    scoped_feature_list_.InitWithFeatures(
-        {}, {app_list_features::kEnableHomeLauncher});
-    ShelfLayoutManagerTest::SetUp();
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(ShelfLayoutManagerNonHomeLauncherTest);
-};
-
 // Makes sure SetVisible updates work area and widget appropriately.
 TEST_F(ShelfLayoutManagerTest, SetVisible) {
   ShelfWidget* shelf_widget = GetShelfWidget();
@@ -996,6 +978,13 @@
   generator->MoveMouseTo(1, display_bottom - 1);
   UpdateAutoHideStateNow();
   EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
+
+  // Switch to tablet mode should hide the AUTO_HIDE_SHOWN shelf even the mouse
+  // cursor is inside the shelf area.
+  EXPECT_FALSE(TabletModeControllerTestApi().IsTabletModeStarted());
+  TabletModeControllerTestApi().EnterTabletMode();
+  EXPECT_TRUE(TabletModeControllerTestApi().IsTabletModeStarted());
+  EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
 }
 
 // Test the behavior of the shelf when it is auto hidden and it is on the
@@ -1740,128 +1729,6 @@
   GetAppListTestHelper()->CheckVisibility(false);
 }
 
-TEST_F(ShelfLayoutManagerNonHomeLauncherTest,
-       SwipingUpOnShelfInTabletModeForFullscreenAppList) {
-  // Animations triggered by immersive mode cause this test to fail.
-  ImmersiveFullscreenControllerTestApi::GlobalAnimationDisabler
-      animation_disabler;
-  Shell* shell = Shell::Get();
-  shell->tablet_mode_controller()->EnableTabletModeWindowManager(true);
-  Shelf* shelf = GetPrimaryShelf();
-  GetShelfLayoutManager()->LayoutShelf();
-  EXPECT_EQ(SHELF_ALIGNMENT_BOTTOM, shelf->alignment());
-  EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_NEVER, shelf->auto_hide_behavior());
-  EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState());
-
-  // Note: A window must be visible in order to hide the shelf. The test will
-  // make the window fullscreened, so make the window resizeable and
-  // maximizable.
-  std::unique_ptr<aura::Window> window(
-      AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)));
-  window->SetProperty(aura::client::kResizeBehaviorKey,
-                      ws::mojom::kResizeBehaviorCanResize |
-                          ws::mojom::kResizeBehaviorCanMaximize);
-  wm::ActivateWindow(window.get());
-
-  ui::test::EventGenerator* generator = GetEventGenerator();
-  constexpr base::TimeDelta kTimeDelta = base::TimeDelta::FromMilliseconds(100);
-  constexpr int kNumScrollSteps = 4;
-
-  // Starts the drag from the center of the shelf's bottom.
-  gfx::Rect shelf_widget_bounds = GetVisibleShelfWidgetBoundsInScreen();
-  gfx::Point shelf_bottom_center =
-      gfx::Point(shelf_widget_bounds.x() + shelf_widget_bounds.width() / 2,
-                 shelf_widget_bounds.bottom() - 1);
-  gfx::Point point_to_fullscreen(shelf_bottom_center);
-  point_to_fullscreen.Offset(
-      0, -(ShelfLayoutManager::kAppListDragSnapToFullscreenThreshold + 10));
-
-  generator->GestureScrollSequence(shelf_bottom_center, point_to_fullscreen,
-                                   kTimeDelta, kNumScrollSteps);
-  GetAppListTestHelper()->WaitUntilIdle();
-  GetAppListTestHelper()->CheckVisibility(true);
-  GetAppListTestHelper()->CheckState(
-      app_list::AppListViewState::FULLSCREEN_ALL_APPS);
-  EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState());
-
-  // Closing the app list.
-  GetAppListTestHelper()->DismissAndRunLoop();
-  GetAppListTestHelper()->CheckVisibility(false);
-  GetAppListTestHelper()->CheckState(app_list::AppListViewState::CLOSED);
-
-  // Swiping up less or equal to the threshold should dismiss the app list.
-  gfx::Point point_to_no_fullscreen(shelf_bottom_center);
-  point_to_no_fullscreen.Offset(
-      0, -(ShelfLayoutManager::kAppListDragSnapToFullscreenThreshold - 10));
-
-  // TODO(minch): investigate failure without EnableMaximizeMode again here.
-  // http://crbug.com/746481.
-  shell->tablet_mode_controller()->EnableTabletModeWindowManager(true);
-  generator->GestureScrollSequence(shelf_bottom_center, point_to_no_fullscreen,
-                                   kTimeDelta, kNumScrollSteps);
-  GetAppListTestHelper()->WaitUntilIdle();
-  GetAppListTestHelper()->CheckVisibility(false);
-  GetAppListTestHelper()->CheckState(app_list::AppListViewState::CLOSED);
-  GetShelfLayoutManager()->UpdateVisibilityState();
-  EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState());
-
-  // Swiping down on the shelf should do nothing as always shown shelf can not
-  // be dragged down to hide.
-  gfx::Point shelf_top_center =
-      gfx::Point(shelf_widget_bounds.x() + shelf_widget_bounds.width() / 2,
-                 shelf_widget_bounds.y());
-  generator->GestureScrollSequence(shelf_top_center, shelf_bottom_center,
-                                   kTimeDelta, kNumScrollSteps);
-  EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState());
-
-  // Verify that the shelf can still enter auto hide if the window requests to
-  // be fullscreened.
-  wm::WindowState* window_state = wm::GetWindowState(window.get());
-  const wm::WMEvent event(wm::WM_EVENT_TOGGLE_FULLSCREEN);
-  window_state->OnWMEvent(&event);
-  window_state->SetHideShelfWhenFullscreen(false);
-  window->SetProperty(kImmersiveIsActive, true);
-  GetShelfLayoutManager()->UpdateVisibilityState();
-  EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
-  EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
-  EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_NEVER, shelf->auto_hide_behavior());
-
-  // Swiping up should show the shelf but not the app list if shelf is hidden.
-  generator->GestureScrollSequence(shelf_bottom_center, point_to_fullscreen,
-                                   kTimeDelta, kNumScrollSteps);
-  GetAppListTestHelper()->WaitUntilIdle();
-  GetAppListTestHelper()->CheckVisibility(false);
-  GetAppListTestHelper()->CheckState(app_list::AppListViewState::CLOSED);
-  EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
-  EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
-  EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_NEVER, shelf->auto_hide_behavior());
-
-  // Swiping down should hide the shelf.
-  generator->GestureScrollSequence(shelf_top_center, shelf_bottom_center,
-                                   kTimeDelta, kNumScrollSteps);
-  EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
-  EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
-  EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_NEVER, shelf->auto_hide_behavior());
-
-  // Verify that after toggling fullscreen to off, the shelf is visible.
-  window_state->OnWMEvent(&event);
-  EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState());
-
-  // Minimize the visible window, the shelf should be shown if there are no
-  // visible windows, even in auto-hide mode.
-  window_state->Minimize();
-  EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState());
-  EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_NEVER, shelf->auto_hide_behavior());
-
-  // Swiping up on the shelf in this state should open the app list.
-  generator->GestureScrollSequence(shelf_bottom_center, point_to_fullscreen,
-                                   kTimeDelta, kNumScrollSteps);
-  GetAppListTestHelper()->WaitUntilIdle();
-  GetAppListTestHelper()->CheckVisibility(true);
-  GetAppListTestHelper()->CheckState(
-      app_list::AppListViewState::FULLSCREEN_ALL_APPS);
-}
-
 TEST_F(ShelfLayoutManagerTest,
        SwipingUpOnShelfInLaptopModeForFullscreenAppList) {
   Shelf* shelf = GetPrimaryShelf();
@@ -2343,7 +2210,7 @@
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
 
   // Transition to tablet mode.
-  Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(true);
+  TabletModeControllerTestApi().EnterTabletMode();
 
   // |window| should be maximized, and the shelf background should match the
   // maximized state.
@@ -2354,7 +2221,7 @@
 // Test the background color for split view mode.
 TEST_F(ShelfLayoutManagerTest, ShelfBackgroundColorInSplitView) {
   // Split view is only enabled in tablet mode.
-  Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(true);
+  TabletModeControllerTestApi().EnterTabletMode();
 
   std::unique_ptr<aura::Window> window1(CreateTestWindow());
   window1->SetProperty(aura::client::kResizeBehaviorKey,
@@ -2487,7 +2354,7 @@
 // gesture handler to handle.
 TEST_F(ShelfLayoutManagerTest, HomeLauncherGestureHandler) {
   // Home launcher is only available in tablet mode.
-  Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(true);
+  TabletModeControllerTestApi().EnterTabletMode();
 
   // Home launcher gesture handler needs at least one window.
   std::unique_ptr<aura::Window> window =
@@ -2547,6 +2414,63 @@
   EXPECT_FALSE(gesture_handler->window());
 }
 
+// Tests that tap outside of the AUTO_HIDE_SHOWN shelf should hide it.
+TEST_F(ShelfLayoutManagerTest, TapOutsideOfAutoHideShownShelf) {
+  views::Widget* widget = CreateTestWidget();
+  Shelf* shelf = GetPrimaryShelf();
+  ui::test::EventGenerator* generator = GetEventGenerator();
+
+  shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
+  ShelfLayoutManager* layout_manager = GetShelfLayoutManager();
+  layout_manager->LayoutShelf();
+  EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
+  EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
+
+  aura::Window* window = widget->GetNativeWindow();
+  gfx::Rect window_bounds = window->GetBoundsInScreen();
+  gfx::Rect display_bounds =
+      display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
+  const gfx::Point start(display_bounds.bottom_center());
+  const gfx::Point end(start + gfx::Vector2d(0, -80));
+  const base::TimeDelta kTimeDelta = base::TimeDelta::FromMilliseconds(100);
+  const int kNumScrollSteps = 4;
+  // Swipe up to show the auto-hide shelf.
+  generator->GestureScrollSequence(start, end, kTimeDelta, kNumScrollSteps);
+  EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
+
+  // Tap outside the window and AUTO_HIDE_SHOWN shelf should hide the shelf.
+  gfx::Point tap_location =
+      window_bounds.bottom_right() + gfx::Vector2d(10, 10);
+  generator->GestureTapAt(tap_location);
+  EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
+
+  // Swipe up to show the auto-hide shelf again.
+  generator->GestureScrollSequence(start, end, kTimeDelta, kNumScrollSteps);
+  EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
+
+  // Tap inside the AUTO_HIDE_SHOWN shelf should not hide the shelf.
+  gfx::Rect shelf_bounds = GetShelfWidget()->GetWindowBoundsInScreen();
+  tap_location = gfx::Point(shelf_bounds.CenterPoint());
+  generator->GestureTapAt(tap_location);
+  EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
+
+  // Tap inside the window should still hide the shelf.
+  tap_location = window_bounds.origin() + gfx::Vector2d(10, 10);
+  generator->GestureTapAt(tap_location);
+  EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
+
+  // Swipe up to show the auto-hide shelf again.
+  generator->GestureScrollSequence(start, end, kTimeDelta, kNumScrollSteps);
+  EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
+
+  // Tap the system tray that inside the status area should not hide the shelf
+  // but open the systrem tray bubble.
+  generator->GestureTapAt(
+      GetPrimaryUnifiedSystemTray()->GetBoundsInScreen().CenterPoint());
+  EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
+  EXPECT_TRUE(GetPrimaryUnifiedSystemTray()->IsBubbleShown());
+}
+
 class ShelfLayoutManagerKeyboardTest : public AshTestBase {
  public:
   ShelfLayoutManagerKeyboardTest() = default;
diff --git a/ash/shelf/shelf_tooltip_manager.cc b/ash/shelf/shelf_tooltip_manager.cc
index 3f41ada..7b451eb 100644
--- a/ash/shelf/shelf_tooltip_manager.cc
+++ b/ash/shelf/shelf_tooltip_manager.cc
@@ -127,6 +127,8 @@
   // The code below handles mouse move events within the shelf window.
   if (event->type() != ui::ET_MOUSE_MOVED ||
       event->target() != shelf_view_->GetWidget()->GetNativeWindow()) {
+    // Don't show delayed tooltips if the mouse is being active elsewhere.
+    timer_.Stop();
     return;
   }
 
diff --git a/ash/shelf/shelf_tooltip_manager_unittest.cc b/ash/shelf/shelf_tooltip_manager_unittest.cc
index af4d66df..4432653 100644
--- a/ash/shelf/shelf_tooltip_manager_unittest.cc
+++ b/ash/shelf/shelf_tooltip_manager_unittest.cc
@@ -170,7 +170,7 @@
   // Should hide for touch events in the shelf.
   tooltip_manager_->ShowTooltip(shelf_view_->GetAppListButton());
   ASSERT_TRUE(tooltip_manager_->IsVisible());
-  generator->set_current_location(shelf_bounds.CenterPoint());
+  generator->set_current_screen_location(shelf_bounds.CenterPoint());
   generator->PressTouch();
   EXPECT_FALSE(tooltip_manager_->IsVisible());
 
@@ -187,7 +187,7 @@
   // Should hide for touches outside the shelf.
   tooltip_manager_->ShowTooltip(shelf_view_->GetAppListButton());
   ASSERT_TRUE(tooltip_manager_->IsVisible());
-  generator->set_current_location(gfx::Point());
+  generator->set_current_screen_location(gfx::Point());
   generator->PressTouch();
   EXPECT_FALSE(tooltip_manager_->IsVisible());
   generator->ReleaseTouch();
@@ -195,7 +195,7 @@
   // Should hide for touch events on the tooltip.
   tooltip_manager_->ShowTooltip(shelf_view_->GetAppListButton());
   ASSERT_TRUE(tooltip_manager_->IsVisible());
-  generator->set_current_location(
+  generator->set_current_screen_location(
       GetTooltip()->GetWindowBoundsInScreen().CenterPoint());
   generator->PressTouch();
   EXPECT_FALSE(tooltip_manager_->IsVisible());
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 79f0af5..15f8ba6 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -1622,19 +1622,14 @@
 gfx::Rect ShelfView::GetMenuAnchorRect(const views::View& source,
                                        const gfx::Point& location,
                                        bool context_menu) const {
-  const bool for_item = ShelfItemForView(&source);
+  // Application menus for items are anchored on the icon bounds.
+  if (ShelfItemForView(&source) || !context_menu)
+    return source.GetBoundsInScreen();
+
   const gfx::Rect shelf_bounds_in_screen =
       is_overflow_mode()
           ? owner_overflow_bubble_->bubble_view()->GetBubbleBounds()
           : GetBoundsInScreen();
-  const gfx::Rect& source_bounds_in_screen = source.GetBoundsInScreen();
-  // Application menus and touchable menus for items are anchored on the icon
-  // bounds.
-  if ((features::IsTouchableAppContextMenuEnabled() && for_item) ||
-      !context_menu) {
-    return source_bounds_in_screen;
-  }
-
   gfx::Point origin;
   switch (shelf_->alignment()) {
     case SHELF_ALIGNMENT_BOTTOM:
@@ -1648,31 +1643,7 @@
       origin = gfx::Point(shelf_bounds_in_screen.x(), location.y());
       break;
   }
-  return gfx::Rect(origin,
-                   for_item ? source_bounds_in_screen.size() : gfx::Size());
-}
-
-views::MenuAnchorPosition ShelfView::GetMenuAnchorPosition(
-    bool for_item,
-    bool context_menu) const {
-  if (features::IsTouchableAppContextMenuEnabled()) {
-    return shelf_->IsHorizontalAlignment()
-               ? views::MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE
-               : views::MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT;
-  }
-  if (!context_menu) {
-    switch (shelf_->alignment()) {
-      case SHELF_ALIGNMENT_BOTTOM:
-      case SHELF_ALIGNMENT_BOTTOM_LOCKED:
-        return views::MENU_ANCHOR_BUBBLE_ABOVE;
-      case SHELF_ALIGNMENT_LEFT:
-        return views::MENU_ANCHOR_BUBBLE_RIGHT;
-      case SHELF_ALIGNMENT_RIGHT:
-        return views::MENU_ANCHOR_BUBBLE_LEFT;
-    }
-  }
-  return shelf_->IsHorizontalAlignment() ? views::MENU_ANCHOR_FIXED_BOTTOMCENTER
-                                         : views::MENU_ANCHOR_FIXED_SIDECENTER;
+  return gfx::Rect(origin, gfx::Size());
 }
 
 gfx::Rect ShelfView::GetBoundsForDragInsertInScreen() {
@@ -1807,14 +1778,6 @@
 }
 
 void ShelfView::OnGestureEvent(ui::GestureEvent* event) {
-  // Do not forward events to |shelf_| (which forwards events to the shelf
-  // layout manager) as we do not want gestures on the overflow to open the app
-  // list for example.
-  if (is_overflow_mode()) {
-    main_shelf_->overflow_bubble()->bubble_view()->ProcessGestureEvent(*event);
-    event->StopPropagation();
-    return;
-  }
 
   // Convert the event location from current view to screen, since swiping up on
   // the shelf can open the fullscreen app list. Updating the bounds of the app
@@ -1824,6 +1787,13 @@
   event->set_location(location_in_screen);
   if (shelf_->ProcessGestureEvent(*event))
     event->StopPropagation();
+  else if (is_overflow_mode()) {
+    // If the event hasn't been processed and the overflow shelf is showing,
+    // let the bubble process the event.
+    main_shelf_->overflow_bubble()->bubble_view()->ProcessGestureEvent(*event);
+    event->StopPropagation();
+    return;
+  }
 }
 
 bool ShelfView::OnMouseWheel(const ui::MouseWheelEvent& event) {
@@ -2092,16 +2062,12 @@
   closing_event_time_ = base::TimeTicks();
 
   // NOTE: If you convert to HAS_MNEMONICS be sure to update menu building code.
-  int run_types = 0;
+  int run_types = views::MenuRunner::USE_TOUCHABLE_LAYOUT;
   if (context_menu) {
     run_types |=
         views::MenuRunner::CONTEXT_MENU | views::MenuRunner::FIXED_ANCHOR;
   }
 
-  // Only use the touchable layout if the menu is for an app.
-  if (features::IsTouchableAppContextMenuEnabled())
-    run_types |= views::MenuRunner::USE_TOUCHABLE_LAYOUT;
-
   const ShelfItem* item = ShelfItemForView(source);
   // Only selected shelf items with context menu opened can be dragged.
   if (context_menu && item && ShelfButtonIsInDrag(item->type, source) &&
@@ -2115,7 +2081,10 @@
       base::BindOnce(&ShelfView::OnMenuClosed, base::Unretained(this), source));
   shelf_menu_model_adapter_->Run(
       GetMenuAnchorRect(*source, click_point, context_menu),
-      GetMenuAnchorPosition(item, context_menu), run_types);
+      shelf_->IsHorizontalAlignment()
+          ? views::MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE
+          : views::MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT,
+      run_types);
 }
 
 void ShelfView::OnMenuClosed(views::View* source) {
@@ -2130,8 +2099,12 @@
 
   shelf_menu_model_adapter_.reset();
 
-  // Auto-hide or alignment might have changed, but only for this shelf.
-  shelf_->UpdateVisibilityState();
+  const bool is_in_drag = item && ShelfButtonIsInDrag(item->type, source);
+  // Update the shelf visibility since auto-hide or alignment might have
+  // changes, but don't update if shelf item is being dragged. Since shelf
+  // should be kept as visible during shelf item drag even menu is closed.
+  if (!is_in_drag)
+    shelf_->UpdateVisibilityState();
 }
 
 void ShelfView::OnBoundsAnimatorProgressed(views::BoundsAnimator* animator) {
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index d08640c..ebcf1e3 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -381,13 +381,6 @@
                               const gfx::Point& location,
                               bool context_menu) const;
 
-  // Gets the menu anchor position for a menu. |for_item| is true if the menu is
-  // for an item on the shelf, or false if the menu is for the shelf view
-  // itself, |context_menu| is whether the menu will be an application menu or
-  // context menu, and |touch_menu| is whether the menu was initiated by touch.
-  views::MenuAnchorPosition GetMenuAnchorPosition(bool for_item,
-                                                  bool context_menu) const;
-
   // Overridden from views::View:
   gfx::Size CalculatePreferredSize() const override;
   void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index 2c41ca7..bd3c702 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -276,7 +276,7 @@
       flag_warning->SetVisible(false);
     }
 
-    // The bounds should be big enough for 4 buttons + overflow chevron.
+    // The bounds should be big enough for 4 buttons + overflow button.
     ASSERT_GE(shelf_view_->width(), 500);
 
     test_api_.reset(new ShelfViewTestAPI(shelf_view_));
@@ -560,7 +560,7 @@
     gfx::Point center_point_of_drag_item = GetButtonCenter(drag_button);
 
     ui::test::EventGenerator* generator = GetEventGenerator();
-    generator->set_current_location(center_point_of_drag_item);
+    generator->set_current_screen_location(center_point_of_drag_item);
     // Rip an item off this source shelf.
     generator->PressLeftButton();
     gfx::Point rip_off_point(center_point_of_drag_item.x(), 0);
@@ -1195,7 +1195,7 @@
   // Verify that dragging an app off the shelf will trigger the app getting
   // ripped off, unless the distance is less than |kRipOffDistance|.
   gfx::Point first_app_location = GetButtonCenter(GetButtonByID(first_app_id));
-  generator->set_current_location(first_app_location);
+  generator->set_current_screen_location(first_app_location);
   generator->PressLeftButton();
   // Drag the mouse to just off the shelf.
   generator->MoveMouseBy(0, -ShelfConstants::shelf_size() / 2 - 1);
@@ -1221,7 +1221,7 @@
       GetButtonCenter(GetButtonByID(second_app_id));
   gfx::Point overflow_app_location = GetButtonCenter(
       test_api_for_overflow.GetButton(model_->ItemIndexByID(overflow_app_id)));
-  generator->set_current_location(second_app_location);
+  generator->set_current_screen_location(second_app_location);
   generator->PressLeftButton();
   generator->MoveMouseTo(overflow_app_location);
   EXPECT_TRUE(test_api_->IsRippedOffFromShelf());
@@ -1232,7 +1232,7 @@
   // Verify that when an app from the overflow shelf is dragged to a location on
   // the main shelf, it is ripped off.
   ASSERT_TRUE(test_api_->IsShowingOverflowBubble());
-  generator->set_current_location(overflow_app_location);
+  generator->set_current_screen_location(overflow_app_location);
   generator->PressLeftButton();
   generator->MoveMouseTo(second_app_location);
   EXPECT_TRUE(test_api_for_overflow.IsRippedOffFromShelf());
@@ -1261,7 +1261,7 @@
   EXPECT_TRUE(IsAppPinned(GetItemId(index)));
 
   gfx::Point app_location = GetButtonCenter(GetButtonByID(id));
-  generator->set_current_location(app_location);
+  generator->set_current_screen_location(app_location);
   generator->PressLeftButton();
   generator->MoveMouseBy(0, -ShelfConstants::shelf_size() / 2 - 1);
   EXPECT_FALSE(test_api_->IsRippedOffFromShelf());
@@ -1716,7 +1716,8 @@
   UpdateDisplay("800x600,800x600");
   ASSERT_EQ(2U, Shell::GetAllRootWindows().size());
 
-  Shelf* secondary_shelf = Shelf::ForWindow(Shell::GetAllRootWindows()[1]);
+  aura::Window* root_window = Shell::GetAllRootWindows()[1];
+  Shelf* secondary_shelf = Shelf::ForWindow(root_window);
 
   secondary_shelf->SetAlignment(SHELF_ALIGNMENT_LEFT);
   ASSERT_EQ(SHELF_ALIGNMENT_LEFT, secondary_shelf->alignment());
@@ -1729,13 +1730,13 @@
 
   // Fetch the start point of dragging.
   gfx::Point start_point = button->GetBoundsInScreen().CenterPoint();
-  ::wm::ConvertPointFromScreen(secondary_shelf->GetWindow(), &start_point);
-  ui::test::EventGenerator generator(Shell::GetAllRootWindows()[1],
-                                     start_point);
+  gfx::Point end_point = start_point + gfx::Vector2d(400, 0);
+  ::wm::ConvertPointFromScreen(root_window, &start_point);
+  ui::test::EventGenerator generator(root_window, start_point);
 
   // Rip off the browser item.
   generator.PressLeftButton();
-  generator.MoveMouseTo(start_point.x() + 400, start_point.y());
+  generator.MoveMouseTo(end_point);
   test_api_for_secondary_shelf_view.RunMessageLoopUntilAnimationsDone();
   EXPECT_TRUE(test_api_for_secondary_shelf_view.IsRippedOffFromShelf());
 }
@@ -1759,6 +1760,53 @@
                                           true /* cancel */);
 }
 
+// Checks drag-reorder items within the overflow shelf.
+TEST_F(ShelfViewTest, TestDragWithinOverflow) {
+  // Prepare the overflow and open it.
+  AddButtonsUntilOverflow();
+  // Add a couple more to make sure we have things to drag.
+  AddAppShortcut();
+  AddAppShortcut();
+  test_api_->ShowOverflowBubble();
+  ShelfView* overflow_shelf_view =
+      shelf_view_->overflow_bubble()->bubble_view()->shelf_view();
+  ASSERT_TRUE(test_api_->IsShowingOverflowBubble());
+
+  ShelfViewTestAPI overflow_api(overflow_shelf_view);
+
+  // We are going to drag the first item in the overflow (A) onto the last
+  // one (B).
+  int item_a_initial_index = overflow_api.GetFirstVisibleIndex();
+  int item_b_initial_index = overflow_api.GetLastVisibleIndex();
+  ShelfID item_a = GetItemId(item_a_initial_index);
+  ShelfID item_b = GetItemId(item_b_initial_index);
+  ShelfButton* item_a_button = overflow_api.GetButton(item_a_initial_index);
+  ShelfButton* item_b_button = overflow_api.GetButton(item_b_initial_index);
+  gfx::Point drag_point = GetButtonCenter(item_a_button);
+  gfx::Point drop_point = GetButtonCenter(item_b_button);
+
+  ui::test::EventGenerator* generator = GetEventGenerator();
+  generator->set_current_screen_location(drag_point);
+  EXPECT_EQ(nullptr, overflow_shelf_view->drag_view());
+
+  // TODO(manucornet): Test the same thing with only touches.
+  generator->PressLeftButton();
+
+  generator->MoveMouseTo(drop_point);
+  EXPECT_NE(nullptr, overflow_shelf_view->drag_view());
+  generator->ReleaseLeftButton();
+  overflow_api.RunMessageLoopUntilAnimationsDone();
+
+  // Now, item A should be the last item, and item B should be just before it.
+  ShelfID new_first_visible_item =
+      GetItemId(overflow_api.GetFirstVisibleIndex());
+  EXPECT_NE(item_a, new_first_visible_item);
+  EXPECT_EQ(item_a, GetItemId(overflow_api.GetLastVisibleIndex()));
+  EXPECT_EQ(item_b, GetItemId(overflow_api.GetLastVisibleIndex() - 1));
+
+  test_api_->HideOverflowBubble();
+}
+
 // Checks creating app shortcut for an opened platform app in overflow bubble
 // should be invisible to the shelf. See crbug.com/605793.
 TEST_F(ShelfViewTest, CheckOverflowStatusPinOpenedAppToShelf) {
@@ -1846,13 +1894,13 @@
   test_api_->ShowOverflowBubble();
 
   // Make sure the point we chose is not on the shelf or its overflow bubble.
-  ASSERT_FALSE(
-      shelf_view_->GetBoundsInScreen().Contains(generator->current_location()));
+  ASSERT_FALSE(shelf_view_->GetBoundsInScreen().Contains(
+      generator->current_screen_location()));
   ASSERT_FALSE(test_api_->overflow_bubble()
                    ->bubble_view()
                    ->shelf_view()
                    ->GetBoundsInScreen()
-                   .Contains(generator->current_location()));
+                   .Contains(generator->current_screen_location()));
   generator->PressLeftButton();
   EXPECT_FALSE(test_api_->IsShowingOverflowBubble());
   generator->ReleaseLeftButton();
@@ -1861,7 +1909,7 @@
   // bubble is opened, the overflow bubble will close.
   EXPECT_FALSE(test_api_->IsShowingOverflowBubble());
   test_api_->ShowOverflowBubble();
-  generator->set_current_location(GetButtonCenter(first_app_id));
+  generator->set_current_screen_location(GetButtonCenter(first_app_id));
   generator->ClickLeftButton();
   EXPECT_FALSE(test_api_->IsShowingOverflowBubble());
 
@@ -1873,7 +1921,8 @@
       test_api_->overflow_bubble()->bubble_view()->shelf_view());
   ShelfButton* button_on_overflow_shelf =
       test_api_for_overflow.GetButton(model_->ItemIndexByID(overflow_app_id2));
-  generator->set_current_location(GetButtonCenter(button_on_overflow_shelf));
+  generator->set_current_screen_location(
+      GetButtonCenter(button_on_overflow_shelf));
   generator->ClickLeftButton();
   EXPECT_FALSE(test_api_->IsShowingOverflowBubble());
 
@@ -1881,7 +1930,7 @@
   // bubble.
   EXPECT_FALSE(test_api_->IsShowingOverflowBubble());
   test_api_->ShowOverflowBubble();
-  generator->set_current_location(GetButtonCenter(first_app_id));
+  generator->set_current_screen_location(GetButtonCenter(first_app_id));
   generator->DragMouseTo(GetButtonCenter(second_app_id));
   EXPECT_TRUE(test_api_->IsShowingOverflowBubble());
   test_api_->HideOverflowBubble();
@@ -1896,7 +1945,8 @@
       test_api_for_overflow2.GetButton(model_->ItemIndexByID(overflow_app_id1));
   ShelfButton* button_on_overflow_shelf1 =
       test_api_for_overflow2.GetButton(model_->ItemIndexByID(overflow_app_id2));
-  generator->set_current_location(GetButtonCenter(button_on_overflow_shelf));
+  generator->set_current_screen_location(
+      GetButtonCenter(button_on_overflow_shelf));
   generator->DragMouseTo(GetButtonCenter(button_on_overflow_shelf1));
   EXPECT_TRUE(test_api_->IsShowingOverflowBubble());
 }
@@ -1930,7 +1980,7 @@
   // The shelf items should animate if they are moved within the shelf, either
   // by swapping or if the items need to be rearranged due to an item getting
   // ripped off.
-  generator->set_current_location(GetButtonCenter(first_app_id));
+  generator->set_current_screen_location(GetButtonCenter(first_app_id));
   generator->DragMouseTo(GetButtonCenter(second_app_id));
   generator->DragMouseBy(0, 50);
   test_api_->RunMessageLoopUntilAnimationsDone();
@@ -2085,69 +2135,88 @@
   EXPECT_FALSE(shelf_view_->drag_view());
 }
 
-struct TouchableAppContextMenuTestParams {
-  TouchableAppContextMenuTestParams(bool enable_touchable_app_context_menu,
-                                    bool context_menu)
-      : enable_touchable_app_context_menu(enable_touchable_app_context_menu),
-        context_menu(context_menu) {}
-  // Whether to enable the touchable app context menu feature.
-  bool enable_touchable_app_context_menu;
-  // Whether the menu is shown as an application or context menu.
-  bool context_menu;
-};
+// Tests that shelf items in always shown shelf can be dragged through gesture
+// events after context menu is shown.
+TEST_F(ShelfViewTest, DragAppAfterContextMenuIsShownInAlwaysShownShelf) {
+  ASSERT_EQ(SHELF_VISIBLE, GetPrimaryShelf()->GetVisibilityState());
+  ui::test::EventGenerator* generator = GetEventGenerator();
+  const ShelfID first_app_id = AddAppShortcut();
+  const ShelfID second_app_id = AddAppShortcut();
+  const int last_index = model_->items().size() - 1;
+  ASSERT_TRUE(last_index >= 0);
 
-class ShelfViewTouchableContextMenuTest
-    : public ShelfViewTest,
-      public testing::WithParamInterface<TouchableAppContextMenuTestParams> {
+  const gfx::Point start = GetButtonCenter(first_app_id);
+  // Drag the app long enough to ensure the drag can be triggered.
+  const gfx::Point end(start.x() + 100, start.y());
+  generator->set_current_screen_location(start);
+
+  // Add |STATE_DRAGGING| state to emulate the gesture drag after context menu
+  // is shown.
+  GetButtonByID(first_app_id)->AddState(ShelfButton::STATE_DRAGGING);
+  generator->GestureScrollSequence(start, end,
+                                   base::TimeDelta::FromMilliseconds(100), 3);
+
+  // |first_add_id| has been moved to the end of the items in the shelf.
+  EXPECT_EQ(first_app_id, model_->items()[last_index].id);
+}
+
+// Tests that shelf items in AUTO_HIDE_SHOWN shelf can be dragged through
+// gesture events after context menu is shown.
+TEST_F(ShelfViewTest, DragAppAfterContextMenuIsShownInAutoHideShelf) {
+  ui::test::EventGenerator* generator = GetEventGenerator();
+  const ShelfID first_app_id = AddAppShortcut();
+  const ShelfID second_app_id = AddAppShortcut();
+  const int last_index = model_->items().size() - 1;
+
+  Shelf* shelf = GetPrimaryShelf();
+  std::unique_ptr<views::Widget> widget = CreateTestWidget();
+  widget->Show();
+  shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
+  EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
+  EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
+
+  shelf->shelf_widget()->GetFocusCycler()->RotateFocus(FocusCycler::FORWARD);
+  EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
+
+  const gfx::Point start = GetButtonCenter(first_app_id);
+  // Drag the app long enough to ensure the drag can be triggered.
+  const gfx::Point end = gfx::Point(start.x() + 100, start.y());
+  generator->set_current_screen_location(start);
+
+  // Add |STATE_DRAGGING| state to emulate the gesture drag after context menu
+  // is shown.
+  GetButtonByID(first_app_id)->AddState(ShelfButton::STATE_DRAGGING);
+  generator->GestureScrollSequence(start, end,
+                                   base::TimeDelta::FromMilliseconds(100), 3);
+
+  // |first_add_id| has been moved to the end of the items in the shelf.
+  EXPECT_EQ(first_app_id, model_->items()[last_index].id);
+}
+
+// Tests that the app list button does not show a context menu on right click.
+TEST_F(ShelfViewTest, AppListButtonDoesNotShowContextMenu) {
+  ui::test::EventGenerator* generator = GetEventGenerator();
+  const AppListButton* app_list_button = shelf_view_->GetAppListButton();
+  generator->MoveMouseTo(app_list_button->GetBoundsInScreen().CenterPoint());
+  generator->PressRightButton();
+  EXPECT_FALSE(test_api_->CloseMenu());
+}
+// Test class that tests both context and application menus.
+class ShelfViewMenuTest : public ShelfViewTest,
+                          public testing::WithParamInterface<bool> {
  public:
-  ShelfViewTouchableContextMenuTest() = default;
-  ~ShelfViewTouchableContextMenuTest() override = default;
+  ShelfViewMenuTest() = default;
+  ~ShelfViewMenuTest() override = default;
 
-  void SetUp() override {
-    // If the test is parameterized, respect the parameter. Otherwise enable
-    // touchable app context menus by default.
-    const bool enable_touchable_app_context_menu =
-        testing::UnitTest::GetInstance()->current_test_info()->value_param()
-            ? GetParam().enable_touchable_app_context_menu
-            : true;
-    std::vector<base::Feature> enabled_features = {
-        features::kNotificationIndicator};
-    if (enable_touchable_app_context_menu)
-      enabled_features.push_back(features::kTouchableAppContextMenu);
-    scoped_feature_list_.InitWithFeatures(enabled_features, {});
-    ShelfViewTest::SetUp();
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(ShelfViewTouchableContextMenuTest);
+  DISALLOW_COPY_AND_ASSIGN(ShelfViewMenuTest);
 };
 
-INSTANTIATE_TEST_CASE_P(
-    TouchableDisabledAppMenu,
-    ShelfViewTouchableContextMenuTest,
-    ::testing::Values(TouchableAppContextMenuTestParams(false, false)));
-
-INSTANTIATE_TEST_CASE_P(
-    TouchableEnabledAppMenu,
-    ShelfViewTouchableContextMenuTest,
-    ::testing::Values(TouchableAppContextMenuTestParams(true, false)));
-
-INSTANTIATE_TEST_CASE_P(
-    TouchableDisabledContextMenu,
-    ShelfViewTouchableContextMenuTest,
-    ::testing::Values(TouchableAppContextMenuTestParams(false, true)));
-
-INSTANTIATE_TEST_CASE_P(
-    TouchableEnabledContextMenu,
-    ShelfViewTouchableContextMenuTest,
-    ::testing::Values(TouchableAppContextMenuTestParams(true, true)));
+INSTANTIATE_TEST_CASE_P(, ShelfViewMenuTest, testing::Bool());
 
 // Tests that menu anchor points are aligned with the shelf button bounds.
-TEST_P(ShelfViewTouchableContextMenuTest, ShelfViewMenuAnchorPoint) {
+TEST_P(ShelfViewMenuTest, ShelfViewMenuAnchorPoint) {
   const ShelfButton* shelf_button = GetButtonByID(AddApp());
-  const bool context_menu = GetParam().context_menu;
+  const bool context_menu = GetParam();
   EXPECT_EQ(ash::ShelfAlignment::SHELF_ALIGNMENT_BOTTOM,
             GetPrimaryShelf()->alignment());
 
@@ -2160,13 +2229,8 @@
   // Test for left shelf.
   GetPrimaryShelf()->SetAlignment(ash::ShelfAlignment::SHELF_ALIGNMENT_LEFT);
 
-  int expected_x = shelf_button->GetBoundsInScreen().x();
-  // Left shelf context menus when TouchableAppContextMenu is disabled anchor
-  // off of the right edge of the shelf.
-  if (context_menu && !features::IsTouchableAppContextMenuEnabled())
-    expected_x = shelf_button->GetBoundsInScreen().right();
   EXPECT_EQ(
-      expected_x,
+      shelf_button->GetBoundsInScreen().x(),
       test_api_->GetMenuAnchorRect(*shelf_button, gfx::Point(), context_menu)
           .x());
 
@@ -2179,19 +2243,27 @@
           .x());
 }
 
-// Tests that the app list button does not show a context menu on right click
-// when touchable app context menus are enabled.
-TEST_F(ShelfViewTouchableContextMenuTest, AppListButtonDoesNotShowContextMenu) {
-  ui::test::EventGenerator* generator = GetEventGenerator();
-  const AppListButton* app_list_button = shelf_view_->GetAppListButton();
-  generator->MoveMouseTo(app_list_button->GetBoundsInScreen().CenterPoint());
-  generator->PressRightButton();
-  EXPECT_FALSE(test_api_->CloseMenu());
-}
+// Test class that enables notification indicators.
+class NotificationIndicatorTest : public ShelfViewTest {
+ public:
+  NotificationIndicatorTest() = default;
+  ~NotificationIndicatorTest() override = default;
+
+  void SetUp() override {
+    scoped_feature_list_.InitWithFeatures({features::kNotificationIndicator},
+                                          {});
+    ShelfViewTest::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(NotificationIndicatorTest);
+};
 
 // Tests that an item has a notification indicator when it recieves a
 // notification.
-TEST_F(ShelfViewTouchableContextMenuTest, AddedItemHasNotificationIndicator) {
+TEST_F(NotificationIndicatorTest, AddedItemHasNotificationIndicator) {
   const ShelfID id_0 = AddApp();
   const std::string notification_id_0("notification_id_0");
   const ShelfButton* button_0 = GetButtonByID(id_0);
@@ -2231,7 +2303,7 @@
 
 // Tests that the notification indicator is active until all notifications have
 // been removed.
-TEST_F(ShelfViewTouchableContextMenuTest,
+TEST_F(NotificationIndicatorTest,
        NotificationIndicatorStaysActiveUntilNotificationsAreGone) {
   const ShelfID app = AddApp();
   const ShelfButton* button = GetButtonByID(app);
@@ -2962,7 +3034,7 @@
 // on it.
 TEST_F(OverflowButtonInkDropTest, TouchActivate) {
   ui::test::EventGenerator* generator = GetEventGenerator();
-  generator->set_current_location(GetScreenPointInsideOverflowButton());
+  generator->set_current_screen_location(GetScreenPointInsideOverflowButton());
 
   generator->PressTouch();
   EXPECT_EQ(views::InkDropState::ACTION_PENDING,
@@ -2983,7 +3055,7 @@
 // down on it and drags it out of the button bounds.
 TEST_F(OverflowButtonInkDropTest, TouchDragOut) {
   ui::test::EventGenerator* generator = GetEventGenerator();
-  generator->set_current_location(GetScreenPointInsideOverflowButton());
+  generator->set_current_screen_location(GetScreenPointInsideOverflowButton());
 
   generator->PressTouch();
   EXPECT_EQ(views::InkDropState::ACTION_PENDING,
@@ -3010,7 +3082,7 @@
 // down on it and drags it out of the button bounds and back.
 TEST_F(OverflowButtonInkDropTest, TouchDragOutAndBack) {
   ui::test::EventGenerator* generator = GetEventGenerator();
-  generator->set_current_location(GetScreenPointInsideOverflowButton());
+  generator->set_current_screen_location(GetScreenPointInsideOverflowButton());
 
   generator->PressTouch();
   EXPECT_EQ(views::InkDropState::ACTION_PENDING,
@@ -3172,7 +3244,7 @@
 // and the user taps on it.
 TEST_F(OverflowButtonActiveInkDropTest, TouchDeactivate) {
   ui::test::EventGenerator* generator = GetEventGenerator();
-  generator->set_current_location(GetScreenPointInsideOverflowButton());
+  generator->set_current_screen_location(GetScreenPointInsideOverflowButton());
 
   generator->PressTouch();
   EXPECT_EQ(views::InkDropState::ACTIVATED,
@@ -3193,7 +3265,7 @@
 // and the user taps down on it and drags it out of the button bounds.
 TEST_F(OverflowButtonActiveInkDropTest, TouchDragOut) {
   ui::test::EventGenerator* generator = GetEventGenerator();
-  generator->set_current_location(GetScreenPointInsideOverflowButton());
+  generator->set_current_screen_location(GetScreenPointInsideOverflowButton());
 
   generator->PressTouch();
   EXPECT_EQ(views::InkDropState::ACTIVATED,
@@ -3221,7 +3293,7 @@
 // back.
 TEST_F(OverflowButtonActiveInkDropTest, TouchDragOutAndBack) {
   ui::test::EventGenerator* generator = GetEventGenerator();
-  generator->set_current_location(GetScreenPointInsideOverflowButton());
+  generator->set_current_screen_location(GetScreenPointInsideOverflowButton());
 
   generator->PressTouch();
   EXPECT_EQ(views::InkDropState::ACTIVATED,
diff --git a/ash/shelf/shelf_widget_unittest.cc b/ash/shelf/shelf_widget_unittest.cc
index 200a537..086a2ee 100644
--- a/ash/shelf/shelf_widget_unittest.cc
+++ b/ash/shelf/shelf_widget_unittest.cc
@@ -566,7 +566,7 @@
   ASSERT_TRUE(keyboard::WaitUntilShown());
 
   ui::test::EventGenerator* generator = GetEventGenerator();
-  generator->set_current_location(
+  generator->set_current_screen_location(
       GetShelfWidget()->GetWindowBoundsInScreen().CenterPoint());
   generator->ClickLeftButton();
 
@@ -579,7 +579,7 @@
   ASSERT_TRUE(keyboard::WaitUntilShown());
 
   ui::test::EventGenerator* generator = GetEventGenerator();
-  generator->set_current_location(
+  generator->set_current_screen_location(
       GetShelfWidget()->GetWindowBoundsInScreen().CenterPoint());
   generator->PressTouch();
 
@@ -592,7 +592,7 @@
   ASSERT_TRUE(keyboard::WaitUntilShown());
 
   ui::test::EventGenerator* generator = GetEventGenerator();
-  generator->set_current_location(
+  generator->set_current_screen_location(
       GetShelfWidget()->GetWindowBoundsInScreen().CenterPoint());
 
   generator->ClickLeftButton();
diff --git a/ash/shelf/shelf_window_watcher_item_delegate.cc b/ash/shelf/shelf_window_watcher_item_delegate.cc
index de2c219..d8edade 100644
--- a/ash/shelf/shelf_window_watcher_item_delegate.cc
+++ b/ash/shelf/shelf_window_watcher_item_delegate.cc
@@ -17,7 +17,6 @@
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/events/event_constants.h"
 #include "ui/wm/core/window_animations.h"
 
@@ -70,11 +69,6 @@
   close->label = l10n_util::GetStringUTF16(IDS_CLOSE);
   close->enabled = true;
   items.push_back(std::move(close));
-  if (!features::IsTouchableAppContextMenuEnabled()) {
-    ash::mojom::MenuItemPtr separator(ash::mojom::MenuItem::New());
-    separator->type = ui::MenuModel::TYPE_SEPARATOR;
-    items.push_back(std::move(separator));
-  }
   std::move(callback).Run(std::move(items));
 }
 
diff --git a/ash/shelf/window_preview.cc b/ash/shelf/window_preview.cc
index 911344a..5c532a2 100644
--- a/ash/shelf/window_preview.cc
+++ b/ash/shelf/window_preview.cc
@@ -112,12 +112,12 @@
   if (!mirror_->bounds().Contains(event.location()))
     return false;
 
-  aura::Window* target = mirror_->target();
-  if (target) {
+  aura::Window* source = mirror_->source();
+  if (source) {
     // The window might have been closed in the mean time.
     // TODO: Use WindowObserver to listen to when previewed windows are
     // being closed and remove this condition.
-    wm::ActivateWindow(target);
+    wm::ActivateWindow(source);
 
     // This will have the effect of deleting this view.
     delegate_->OnPreviewActivated(this);
@@ -128,14 +128,14 @@
 void WindowPreview::ButtonPressed(views::Button* sender,
                                   const ui::Event& event) {
   // The close button was pressed.
-  aura::Window* target = mirror_->target();
+  aura::Window* source = mirror_->source();
 
   // The window might have been closed in the mean time.
   // TODO: Use WindowObserver to listen to when previewed windows are
   // being closed and remove this condition.
-  if (!target)
+  if (!source)
     return;
-  wm::CloseWidgetForWindow(target);
+  wm::CloseWidgetForWindow(source);
 
   // This will have the effect of deleting this view.
   delegate_->OnPreviewDismissed(this);
diff --git a/ash/shell/content/client/shell_main_delegate.cc b/ash/shell/content/client/shell_main_delegate.cc
index 0d4234b..f7f3fda 100644
--- a/ash/shell/content/client/shell_main_delegate.cc
+++ b/ash/shell/content/client/shell_main_delegate.cc
@@ -26,9 +26,11 @@
 namespace shell {
 namespace {
 
-std::unique_ptr<service_manager::Service> CreateQuickLaunch() {
+std::unique_ptr<service_manager::Service> CreateQuickLaunch(
+    service_manager::mojom::ServiceRequest request) {
   logging::SetLogPrefix("quick");
-  return std::make_unique<quick_launch::QuickLaunchApplication>();
+  return std::make_unique<quick_launch::QuickLaunchApplication>(
+      std::move(request));
 }
 
 std::unique_ptr<service_manager::Service> CreateShortcutViewer() {
@@ -55,11 +57,6 @@
   void RegisterServices(StaticServiceMap* services) override {
     {
       service_manager::EmbeddedServiceInfo info;
-      info.factory = base::BindRepeating(&CreateQuickLaunch);
-      (*services)[quick_launch::mojom::kServiceName] = info;
-    }
-    {
-      service_manager::EmbeddedServiceInfo info;
       info.factory = base::BindRepeating(&CreateShortcutViewer);
       (*services)[shortcut_viewer::mojom::kServiceName] = info;
     }
@@ -75,6 +72,15 @@
     }
   }
 
+  std::unique_ptr<service_manager::Service> HandleServiceRequest(
+      const std::string& service_name,
+      service_manager::mojom::ServiceRequest request) override {
+    if (service_name == quick_launch::mojom::kServiceName)
+      return CreateQuickLaunch(std::move(request));
+
+    return nullptr;
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(ShellContentUtilityClient);
 };
diff --git a/ash/shell_test_api.cc b/ash/shell_test_api.cc
index b23260c..91483180 100644
--- a/ash/shell_test_api.cc
+++ b/ash/shell_test_api.cc
@@ -11,6 +11,7 @@
 #include "ash/shell.h"
 #include "ash/system/power/backlights_forced_off_setter.h"
 #include "ash/system/power/power_button_controller.h"
+#include "ash/wm/overview/window_selector_controller.h"
 #include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/ws/window_service_owner.h"
@@ -88,6 +89,7 @@
 
 void ShellTestApi::SnapWindowInSplitView(const std::string& client_name,
                                          ws::Id window_id,
+                                         bool left,
                                          SnapWindowInSplitViewCallback cb) {
   auto* window_service = shell_->window_service_owner()->window_service();
   aura::Window* window = nullptr;
@@ -98,8 +100,9 @@
     }
   }
   DCHECK(window);
-  shell_->split_view_controller()->SnapWindow(window,
-                                              ash::SplitViewController::LEFT);
+  shell_->split_view_controller()->SnapWindow(
+      window,
+      left ? ash::SplitViewController::LEFT : ash::SplitViewController::RIGHT);
   shell_->split_view_controller()->FlushForTesting();
   std::move(cb).Run();
 }
@@ -109,4 +112,9 @@
   std::move(cb).Run();
 }
 
+void ShellTestApi::ToggleOverviewMode(ToggleOverviewModeCallback cb) {
+  shell_->window_selector_controller()->ToggleOverview();
+  std::move(cb).Run();
+}
+
 }  // namespace ash
diff --git a/ash/shell_test_api.h b/ash/shell_test_api.h
index 44a6a879..0b4117c 100644
--- a/ash/shell_test_api.h
+++ b/ash/shell_test_api.h
@@ -57,8 +57,10 @@
   void EnableVirtualKeyboard(EnableVirtualKeyboardCallback cb) override;
   void SnapWindowInSplitView(const std::string& client_name,
                              ws::Id window_id,
+                             bool left,
                              SnapWindowInSplitViewCallback cb) override;
   void ToggleFullscreen(ToggleFullscreenCallback cb) override;
+  void ToggleOverviewMode(ToggleOverviewModeCallback cb) override;
 
  private:
   Shell* shell_;  // not owned
diff --git a/ash/strings/ash_strings_am.xtb b/ash/strings/ash_strings_am.xtb
index 051055a..4677887 100644
--- a/ash/strings/ash_strings_am.xtb
+++ b/ash/strings/ash_strings_am.xtb
@@ -31,6 +31,7 @@
 <translation id="1351937230027495976">ምናሌ ሰብስብ</translation>
 <translation id="1383876407941801731">ፍለጋ </translation>
 <translation id="1419738280318246476">የማሳወቅ እርምጃውን ለማከናወን መሣሪያን ይክፈቱ</translation>
+<translation id="1455242230282523554">የቋንቋ ቅንብሮችን አሳይ</translation>
 <translation id="1467432559032391204">ግራ</translation>
 <translation id="1484102317210609525"><ph name="DEVICE_NAME" /> (HDMI/DP)</translation>
 <translation id="1520303207432623762">{NUM_APPS,plural, =1{የማሳወቂያ ቅንብሮችን አሳይ። ማሳወቂያዎች ለአንድ መተግበሪያ ጠፍተዋል}one{የማሳወቂያ ቅንብሮችን አሳይ። ማሳወቂያዎች ለ# መተግበሪያዎች ጠፍተዋል}other{የማሳወቂያ ቅንብሮችን አሳይ። ማሳወቂያዎች ለ# መተግበሪያዎች ጠፍተዋል}}</translation>
@@ -38,7 +39,6 @@
 <translation id="1537254971476575106">የሙሉ ማያ ገጽ ማጉያ</translation>
 <translation id="15373452373711364">ትልቅ የመዳፊት ጠቋሚ</translation>
 <translation id="1550523713251050646">ለተጨማሪ አማራጮች ጠቅ ያድርጉ</translation>
-<translation id="1567387640189251553">የእርስዎን የይለፍ ቃል ለመጨረሻ ጊዜ ካስገቡ ወዲህ የተለየ የቁልፍ ሰሌዳ ተገናኝቷል። የእርስዎን የቁልፍ ጭረቶች ለመስረቅ እየሞከረ ሊሆን ይችላል።</translation>
 <translation id="1570871743947603115">ብሉቱዝን አብራ ወይም አጥፋ። <ph name="STATE_TEXT" /></translation>
 <translation id="1608626060424371292">ይህን ተጠቃሚ አስወግድ</translation>
 <translation id="1621499497873603021">ባትሪ ባዶ እስኪሆን ድረስ የቀረው ጊዜ፣ <ph name="TIME_LEFT" /></translation>
@@ -156,6 +156,7 @@
 <translation id="3413817803639110246">ገና ምንም የሚታይ ነገር የለም</translation>
 <translation id="3445925074670675829">USB-C መሣሪያ</translation>
 <translation id="3454224730401036106">የእርስዎ ግንኙነት ደህንነቱ ይበልጥ ወደተጠበቀ አውታረ መረብ ቀይሯል</translation>
+<translation id="3465223694362104965">ባለፈው በመለያ ከገቡ ወዲህ ወደዚህ መሣሪያ ሌላ የቁልፍ ሰሌዳ ተገናኝቷል። ከመጠቀምዎ በፊት ይህን የቁልፍ ሰሌዳ እንደሚያምኑት ያረጋግጡ።</translation>
 <translation id="3477079411857374384">Control-Shift-Space</translation>
 <translation id="3510164367642747937">የመዳፊት ጠቋሚን አድምቅ</translation>
 <translation id="3510503721818156981">Chromebook ከአዲስ ስልክ ጋር ተገናኝቷል</translation>
@@ -261,6 +262,7 @@
 <translation id="5571066253365925590">ብሉቱዝ ነቅቷል</translation>
 <translation id="5597451508971090205"><ph name="SHORT_WEEKDAY" />፣ <ph name="DATE" /></translation>
 <translation id="5600837773213129531">የሚነገር ግብረመልስን ለማሰናከል Ctrl + Alt + Z ይጫኑ።</translation>
+<translation id="5626283214046138476">የእርስዎ ቁጥጥር የሚደረግበት ተጠቃሚ መለያ በቅርቡ የአገልግሎት ጊዜው ያበቃል።</translation>
 <translation id="5648021990716966815">የማይክሮፎን መሰኪያ</translation>
 <translation id="5669267381087807207">በማግበር ላይ</translation>
 <translation id="5673434351075758678">ከ«<ph name="FROM_LOCALE" />» ወደ «<ph name="TO_LOCALE" />» የእርስዎን ቅንብሮች ከማመሳሰል በኋላ።</translation>
diff --git a/ash/strings/ash_strings_ar.xtb b/ash/strings/ash_strings_ar.xtb
index 14572b7..5a0130a 100644
--- a/ash/strings/ash_strings_ar.xtb
+++ b/ash/strings/ash_strings_ar.xtb
@@ -31,6 +31,7 @@
 <translation id="1351937230027495976">تصغير القائمة</translation>
 <translation id="1383876407941801731">البحث</translation>
 <translation id="1419738280318246476">فتح قفل الجهاز لتنفيذ إجراء الإشعارات</translation>
+<translation id="1455242230282523554">عرض إعدادات اللغة</translation>
 <translation id="1467432559032391204">اليسار</translation>
 <translation id="1484102317210609525"><ph name="DEVICE_NAME" /> (HDMI/DP)</translation>
 <translation id="1520303207432623762">{NUM_APPS,plural, =1{عرض إعدادات الإشعارات. تم إيقاف الإشعارات لتطبيق واحد}zero{عرض إعدادات الإشعارات. تم إيقاف الإشعارات لـ # تطبيق}two{عرض إعدادات الإشعارات. تم إيقاف الإشعارات لتطبيقين (#)}few{عرض إعدادات الإشعارات. تم إيقاف الإشعارات لـ # تطبيقات}many{عرض إعدادات الإشعارات. تم إيقاف الإشعارات لـ # تطبيقًا}other{عرض إعدادات الإشعارات. تم إيقاف الإشعارات لـ # تطبيق}}</translation>
@@ -38,7 +39,6 @@
 <translation id="1537254971476575106">مكبّر بملء الشاشة</translation>
 <translation id="15373452373711364">مؤشر الماوس الكبير</translation>
 <translation id="1550523713251050646">انقر للحصول على المزيد من الخيارات</translation>
-<translation id="1567387640189251553">تم توصيل لوحة مفاتيح مختلفة منذ آخر إدخال لكلمة المرور، وقد يكون ذلك محاولةً لسرقة ضغطات المفاتيح.</translation>
 <translation id="1570871743947603115">تبديل البلوتوث. <ph name="STATE_TEXT" /></translation>
 <translation id="1608626060424371292">إزالة هذا المستخدم</translation>
 <translation id="1621499497873603021">الوقت المتبقي حتى تصبح البطارية فارغة <ph name="TIME_LEFT" /></translation>
@@ -79,7 +79,7 @@
 <translation id="2220572644011485463">رقم التعريف الشخصي أو كلمة المرور</translation>
 <translation id="225680501294068881">جارٍ البحث عن أجهزة...</translation>
 <translation id="2268130516524549846">تم إيقاف البلوتوث</translation>
-<translation id="2268813581635650749">خروج الجميع</translation>
+<translation id="2268813581635650749">خروج من كل الحسابات</translation>
 <translation id="2292698582925480719">مقياس العرض</translation>
 <translation id="2302092602801625023">‏تتم إدارة هذا الحساب من خلال Family Link</translation>
 <translation id="2303600792989757991">نظرة عامة لنافذة التبديل</translation>
@@ -156,6 +156,7 @@
 <translation id="3413817803639110246">ليس لديك شيء للمشاهدة حتى الآن</translation>
 <translation id="3445925074670675829">‏جهاز USB-C</translation>
 <translation id="3454224730401036106">تم تبديل الاتصال إلى شبكة أكثر أمانًا.</translation>
+<translation id="3465223694362104965">تم ربط لوحة مفاتيح أخرى بهذا الجهاز منذ آخر مرة سجَّلت الدخول فيها. يُرجى التأكُّد من أن لوحة المفاتيح هذه موثوقة قبل استخدامها.</translation>
 <translation id="3477079411857374384">Control-Shift-Space</translation>
 <translation id="3510164367642747937">تمييز مؤشر الماوس</translation>
 <translation id="3510503721818156981">‏تم ربط جهاز Chromebook بهاتف جديد</translation>
@@ -261,6 +262,7 @@
 <translation id="5571066253365925590">تم تفعيل البلوتوث</translation>
 <translation id="5597451508971090205"><ph name="SHORT_WEEKDAY" />، <ph name="DATE" /></translation>
 <translation id="5600837773213129531">‏اضغط على Ctrl + Alt + Z لإيقاف التعليقات والملاحظات المنطوقة</translation>
+<translation id="5626283214046138476">ستنتهي صلاحية حساب المستخدم تحت الإشراف قريبًا.</translation>
 <translation id="5648021990716966815">مقبس الميكروفون</translation>
 <translation id="5669267381087807207">تفعيل</translation>
 <translation id="5673434351075758678">من "<ph name="FROM_LOCALE" />" إلى "<ph name="TO_LOCALE" />" بعد مزامنة الإعدادات.</translation>
diff --git a/ash/strings/ash_strings_bg.xtb b/ash/strings/ash_strings_bg.xtb
index 17ed640..4c3ce7e 100644
--- a/ash/strings/ash_strings_bg.xtb
+++ b/ash/strings/ash_strings_bg.xtb
@@ -31,6 +31,7 @@
 <translation id="1351937230027495976">Свиване на мен