diff --git a/.gn b/.gn
index 3a439c9b..8fb99ce 100644
--- a/.gn
+++ b/.gn
@@ -357,6 +357,8 @@
   "//third_party/blanketjs/*",
 
   # "//third_party/blink/*",  # Errors: https://crbug.com/800764
+  "//third_party/blink/public/*",
+
   #"//third_party/breakpad/*",  # Small errors.
   "//third_party/boringssl/*",
   "//third_party/bouncycastle/*",
@@ -497,7 +499,6 @@
   "//third_party/markdown/*",
   "//third_party/markupsafe/*",
   "//third_party/material_design_icons/*",
-  "//third_party/mesa/*",
   "//third_party/mesa_headers/*",
   "//third_party/metrics_proto/*",
   "//third_party/minigbm/*",
diff --git a/AUTHORS b/AUTHORS
index 5123c7e..80d5c38 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -96,6 +96,7 @@
 Arunoday Sarkar <a.sarkar.arun@gmail.com>
 Arunprasad Rajkumar <ararunprasad@gmail.com>
 Arunprasad Rajkumar <arurajku@cisco.com>
+Asami Doi <d0iasm.pub@gmail.com>
 Ashish Kumar Gupta <guptaag@amazon.com>
 Ashlin Joseph <ashlin.j@samsung.com>
 Asish Singh <asish.singh@samsung.com>
@@ -420,6 +421,7 @@
 John Yani <vanuan@gmail.com>
 John Yoo <nearbyh13@gmail.com>
 Johnson Lin <johnson.lin@intel.com>
+Jon Kunkee <jkunkee@microsoft.com>
 Jonathan Frazer <listedegarde@gmail.com>
 Jonathan Garbee <jonathan@garbee.me>
 Jonathan Hacker <jhacker@arcanefour.com>
@@ -506,6 +508,7 @@
 Kyung Yeol Kim <chitacan@gmail.com>
 Kyungtae Kim <ktf.kim@samsung.com>
 Kyungyoung Heo <bbvch13531@gmail.com>
+Lalit Chandivade <lalit.chandivade@einfochips.com>
 Laszlo Gombos <l.gombos@samsung.com>
 Laszlo Radanyi <bekkra@gmail.com>
 Lauren Yeun Kim <lauren.yeun.kim@gmail.com>
@@ -819,6 +822,7 @@
 Soojung Choi <crystal2840@gmail.com>
 Soorya R <soorya.r@samsung.com>
 Soren Dreijer <dreijerbit@gmail.com>
+Sreerenj Balachandran <sreerenj.balachandran@intel.com>
 Srirama Chandra Sekhar Mogali <srirama.m@samsung.com>
 Staphany Park <stapark008@gmail.com>
 Stephen Searles <stephen.searles@gmail.com>
diff --git a/BUILD.gn b/BUILD.gn
index e4d8f64d..46bcfd07 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -105,7 +105,6 @@
       "//chrome/test:sync_integration_tests",
       "//chrome/test/chromedriver:chromedriver_unittests",
       "//components/subresource_filter/tools:subresource_filter_tools",
-      "//components/sync/tools:sync_listen_notifications",
       "//components/zucchini:zucchini",
       "//components/zucchini:zucchini_unittests",
       "//gpu/gles2_conform_support:gles2_conform_test",
@@ -299,6 +298,7 @@
       "//chrome/android/webapk/shell_apk:webapk",
       "//chrome/test/vr/perf:motopho_latency_test",
       "//components/invalidation/impl:components_invalidation_impl_junit_tests",
+      "//components/journey:journey_info_fetcher",
       "//components/policy/android:components_policy_junit_tests",
       "//components/signin/core/browser/android:components_signin_junit_tests",
       "//content/public/android:content_junit_tests",
@@ -803,6 +803,7 @@
   group("chrome_official_builder_no_unittests") {
     deps = [
       "//chrome/common/win:eventlog_provider",
+      "//chrome/credential_provider",
       "//chrome/installer/gcapi",
       "//chrome/installer/mini_installer",
       "//cloud_print",
@@ -1017,7 +1018,7 @@
     if (is_win) {
       data += [
         "//third_party/apache-win32/",
-        "//third_party/perl/",
+        "//third_party/perl/perl/",
       ]
     }
 
diff --git a/DEPS b/DEPS
index b138676..4a61048 100644
--- a/DEPS
+++ b/DEPS
@@ -116,19 +116,19 @@
   # 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': '88e15bdaf539b43ff7eaeec882ab9c64df7fe49f',
+  'skia_revision': '73b4a1f572546d46213c07eb65073940a0983221',
   # 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': '31b2546b348e864539ade15897eac971b3c0e402',
+  'v8_revision': '9a0639762e7ada6acab150e3097d4ded62caf9de',
   # 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': 'f78187ab77127de42555afe0ad410bebde6ac6a5',
+  'swarming_revision': '7f463e66e1c4bb830ef3de9d046aa227d28e1b00',
   # 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': '5d2dfa46c05c8c661304922ce1e1e7d7b045a342',
+  'angle_revision': '778bf09deea44363f5b9aa5a8795e264936099c3',
   # 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.
@@ -136,11 +136,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '4169b31090dbe7d48b3e053c571c12414089bf28',
+  'swiftshader_revision': '8f20452b0219dece19a9875bd11c04437a7e3cd4',
   # 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': 'aab0b516aa87a0d2cb9c0eca27ce2afbe2a30344',
+  'pdfium_revision': 'cf927b1f0823b51a519fcec6f1919b092a58918e',
   # 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 +148,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': '384d0eaf1930af1ebc47eda751f0c78dfcba1c03',
+  'boringssl_revision': 'f241a59dcca617c5b9d9880a8a9fd92996a654be',
   # 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.
@@ -176,7 +176,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '5d5091665700495b0d212cc504fd48530565eecf',
+  'catapult_revision': 'b81a9c76c982129c6ec624367bcb0c0dca4a49d3',
   # 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 +192,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': '8acb73aa3e5f28938b54d35c1a6d40e02399d799',
+  'feed_revision': '107851225e86b1ab6a222ce8ca7ac3729652273a',
   # 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 +224,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': '1e9fc1aac1074b4d48c7f48af30ddeb2b768d745',
+  'spv_tools_revision': 'cd22b3155708d7922524e7f52551e0794b3c1d62',
   # 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': 'a2c529b5dda18838ab4b52f816acfebd774eaab3',
+  'spv_headers_revision': '7cb43009d543e90698dd300eb26dfd6d9a9bb100',
   # 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 +240,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': '2960ec33667a11bb4445dfe8154aef133c741afe',
+  'dawn_revision': '69b44ee6a1269afb5c7622189de236af0c4ec2a7',
 }
 
 # Only these hosts are allowed for dependencies in this DEPS file.
@@ -347,7 +347,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'a367adfe25465864acf7ab7396216a9e4c1cf00b',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '9f4ad51008e503ca15bdcd33077418a45f4b2824',
       'condition': 'checkout_ios',
   },
 
@@ -408,15 +408,15 @@
       'packages': [
         {
           'package': 'infra/tools/luci/isolate/${{platform}}',
-          'version': 'git_revision:bc125484b8513898f17bc2501ac5e95330f44a3b',
+          'version': 'git_revision:b4183943efee5d2a2d444c8551877ae96b78d8b2',
         },
         {
           'package': 'infra/tools/luci/isolated/${{platform}}',
-          'version': 'git_revision:bc125484b8513898f17bc2501ac5e95330f44a3b',
+          'version': 'git_revision:b4183943efee5d2a2d444c8551877ae96b78d8b2',
         },
         {
           'package': 'infra/tools/luci/swarming/${{platform}}',
-          'version': 'git_revision:bc125484b8513898f17bc2501ac5e95330f44a3b',
+          'version': 'git_revision:b4183943efee5d2a2d444c8551877ae96b78d8b2',
         },
       ],
       'dep_type': 'cipd',
@@ -486,7 +486,7 @@
   },
 
   'src/third_party/android_tools': {
-      'url': Var('chromium_git') + '/android_tools.git' + '@' + '130499e25286f4d56acafa252fee09f3cc595c49',
+      'url': Var('chromium_git') + '/android_tools.git' + '@' + '6fecaa542f73dd5aeed170d9a4cf340159b42976',
       'condition': 'checkout_android_native_support',
   },
 
@@ -655,7 +655,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '63924982b3fdaf3c313e0052fe0c07dae5e4628a',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'a91e38e61cfb76fe278df270fe866890fdc31ac3',
       'condition': 'checkout_linux',
   },
 
@@ -670,7 +670,7 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '693e848cded93bf487a75519e185bc8b73cc1bce',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'eb9630f2312fe23c4b9678f666ff45c3c6df949c',
       'condition': 'checkout_linux',
   },
 
@@ -680,7 +680,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '6edb632ff8cdbf65569eb80a9ed8f6d841854a2b',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'c6ffd7af7d84ec354a92a0dc11613ec23cf82c60',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -761,7 +761,7 @@
   },
 
   'src/third_party/googletest/src':
-    Var('chromium_git') + '/external/github.com/google/googletest.git' + '@' + '2e68926a9d4929e9289373cd49e40ddcb9a628f7',
+    Var('chromium_git') + '/external/github.com/google/googletest.git' + '@' + '879ac092fde0a19e1b3a61b2546b2a422b1528bc',
 
   # GNU binutils assembler for x86-32.
   'src/third_party/gnu_binutils': {
@@ -868,7 +868,7 @@
     Var('chromium_git') + '/external/libaddressinput.git' + '@' + 'd7ed8e2f3f35ce9a3aafdfdc48745ceab66e7229',
 
   'src/third_party/libaom/source/libaom': {
-    'url': Var('aomedia_git') + '/aom.git' + '@' +  '35171107616f564a4c18dd75eead6b4393c1f6bd',
+    'url': Var('aomedia_git') + '/aom.git' + '@' +  '716f2896c6babc140a6a774c21653c3f4d04d9de',
     'condition': 'checkout_libaom',
   },
 
@@ -908,7 +908,7 @@
   },
 
   'src/third_party/libvpx/source/libvpx':
-    Var('chromium_git') + '/webm/libvpx.git' + '@' +  'c66fe1a8930cf1ad43b38dd7cb88f8ae0139b0b2',
+    Var('chromium_git') + '/webm/libvpx.git' + '@' +  '4a8c248744500f9caf00588ca312efce5659e45e',
 
   'src/third_party/libwebm/source':
     Var('chromium_git') + '/webm/libwebm.git' + '@' + 'e4931ebc0a816458c18a6734e91a4d1b5acd5c56',
@@ -946,7 +946,7 @@
 
   # Minizip library. Used on Chrome OS.
   'src/third_party/minizip/src': {
-      'url': Var('chromium_git') + '/external/github.com/nmoinvaz/minizip' + '@' + 'c47090678d687742eddc60d07c5430279af416d8',
+      'url': Var('chromium_git') + '/external/github.com/nmoinvaz/minizip' + '@' + '4d4c9db5b019e71b4a40fb41ab21fb47de12ae69',
       'condition': 'checkout_linux',
   },
 
@@ -963,7 +963,7 @@
 
   'src/third_party/nasm': {
       'url': Var('chromium_git') + '/chromium/deps/nasm.git' + '@' +
-      '20920a85609b0608d4c9c80b525196d07183b289'
+      'a0a6951e259bd347c133969740348bb5ebb468c4'
   },
 
   'src/third_party/netty-tcnative/src': {
@@ -1014,7 +1014,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '606ed681f4ea01e065a9db72a50184c6470b2eb2',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'acedb7bc166fb805a037507253e935f3ea0fd0f9',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1163,10 +1163,10 @@
     Var('chromium_git') + '/external/selenium/py.git' + '@' + '5fd78261a75fe08d27ca4835fb6c5ce4b42275bd',
 
   'src/third_party/webgl/src':
-    Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '0d55c887e92b645f6effe753528323ab2ffd94c2',
+    Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '5b6cbd789b9b91b4e46dde883c9f2ecb31eddade',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'e769ed90c3599a131166d5a61625c1b64318fb91',
+    Var('webrtc_git') + '/src.git' + '@' + '179a3923b9e402f427728d52b3024a3de1696a66',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1197,10 +1197,21 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c458532c0628bb1da619aff19c050d3685f258b4',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@a73c9823f6c5659ceaf3b7539da381369fa209e2',
     'condition': 'checkout_src_internal',
   },
 
+  'src/third_party/google_android_play_core': {
+      'packages': [
+          {
+              'package': 'chromium/third_party/android_deps/libs/com_google_android_play_core_verification',
+              'version': 'version:1.3.6-cr1',
+          },
+      ],
+      'condition': 'checkout_android',
+      'dep_type': 'cipd',
+  },
+
   # === ANDROID_DEPS Generated Code Start ===
   # Generated by //tools/android/roll/android_deps/fetch_all.py
   'src/third_party/android_deps/libs/android_arch_core_common': {
@@ -1709,17 +1720,6 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/android_deps/libs/com_google_android_play_core': {
-      'packages': [
-          {
-              'package': 'chromium/third_party/android_deps/libs/com_google_android_play_core',
-              'version': 'version:1.3.5-cr0',
-          },
-      ],
-      'condition': 'checkout_android',
-      'dep_type': 'cipd',
-  },
-
   'src/third_party/android_deps/libs/com_google_code_findbugs_jsr305': {
       'packages': [
           {
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index c0aec6c..2484e48 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -2717,70 +2717,6 @@
   return results
 
 
-def _CheckForRiskyJsArrowFunction(line_number, line):
-  if ' => ' in line:
-    return "line %d, is using an => (arrow) function\n %s\n" % (
-        line_number, line)
-  return ''
-
-
-def _CheckForRiskyJsConstLet(input_api, line_number, line):
-  if input_api.re.match('^\s*(const|let)\s', line):
-    return "line %d, is using const/let keyword\n %s\n" % (
-        line_number, line)
-  return ''
-
-
-def _CheckForRiskyJsFeatures(input_api, output_api):
-  maybe_ios_js = [r"^(ios|components|ui\/webui\/resources)\/.+\.js$"]
-  # 'ui/webui/resources/cr_components are not allowed on ios'
-  not_ios_filter = (r".*ui\/webui\/resources\/cr_components.*", )
-  file_filter = lambda f: input_api.FilterSourceFile(f, white_list=maybe_ios_js,
-                                                     black_list=not_ios_filter)
-  results = []
-  for f in input_api.AffectedFiles(file_filter=file_filter):
-    arrow_error_lines = []
-    const_let_error_lines = []
-    for lnum, line in f.ChangedContents():
-      arrow_error_lines += filter(None, [
-        _CheckForRiskyJsArrowFunction(lnum, line),
-      ])
-
-      const_let_error_lines += filter(None, [
-        _CheckForRiskyJsConstLet(input_api, lnum, line),
-      ])
-
-    if arrow_error_lines:
-      arrow_error_lines = map(
-          lambda e: "%s:%s" % (f.LocalPath(), e), arrow_error_lines)
-      results.append(
-          output_api.PresubmitPromptWarning('\n'.join(arrow_error_lines + [
-"""
-Use of => (arrow) operator detected in:
-%s
-Please ensure your code does not run on iOS9 (=> (arrow) does not work there).
-https://chromium.googlesource.com/chromium/src/+/master/styleguide/web/es6.md#Arrow-Functions
-""" % f.LocalPath()
-          ])))
-
-    if const_let_error_lines:
-      const_let_error_lines = map(
-          lambda e: "%s:%s" % (f.LocalPath(), e), const_let_error_lines)
-      results.append(
-          output_api.PresubmitPromptWarning('\n'.join(const_let_error_lines + [
-"""
-Use of const/let keywords detected in:
-%s
-Please ensure your code does not run on iOS9 because const/let is not fully
-supported.
-https://chromium.googlesource.com/chromium/src/+/master/styleguide/web/es6.md#let-Block_Scoped-Variables
-https://chromium.googlesource.com/chromium/src/+/master/styleguide/web/es6.md#const-Block_Scoped-Constants
-""" % f.LocalPath()
-          ])))
-
-  return results
-
-
 def _CheckForRelativeIncludes(input_api, output_api):
   # Need to set the sys.path so PRESUBMIT_test.py runs properly
   import sys
@@ -3119,7 +3055,6 @@
   results.extend(_CheckJavaStyle(input_api, output_api))
   results.extend(_CheckIpcOwners(input_api, output_api))
   results.extend(_CheckUselessForwardDeclarations(input_api, output_api))
-  results.extend(_CheckForRiskyJsFeatures(input_api, output_api))
   results.extend(_CheckForRelativeIncludes(input_api, output_api))
   results.extend(_CheckWATCHLISTS(input_api, output_api))
   results.extend(input_api.RunTests(
diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py
index 0c23bb9..a04f742 100755
--- a/PRESUBMIT_test.py
+++ b/PRESUBMIT_test.py
@@ -1391,54 +1391,6 @@
     self.assertEqual(4, len(warnings))
 
 
-class RiskyJsTest(unittest.TestCase):
-  def testArrowWarnInIos9Code(self):
-    mock_input_api = MockInputApi()
-    mock_output_api = MockOutputApi()
-
-    mock_input_api.files = [
-      MockAffectedFile('components/blah.js', ["shouldn't use => here"]),
-    ]
-    warnings = PRESUBMIT._CheckForRiskyJsFeatures(
-        mock_input_api, mock_output_api)
-    self.assertEqual(1, len(warnings))
-
-    mock_input_api.files = [
-      MockAffectedFile('ios/blee.js', ['might => break folks']),
-    ]
-    warnings = PRESUBMIT._CheckForRiskyJsFeatures(
-        mock_input_api, mock_output_api)
-    self.assertEqual(1, len(warnings))
-
-    mock_input_api.files = [
-      MockAffectedFile('ui/webui/resources/blarg.js', ['on => iOS9']),
-    ]
-    warnings = PRESUBMIT._CheckForRiskyJsFeatures(
-        mock_input_api, mock_output_api)
-    self.assertEqual(1, len(warnings))
-
-  def testArrowsAllowedInChromeCode(self):
-    mock_input_api = MockInputApi()
-    mock_input_api.files = [
-      MockAffectedFile('chrome/browser/resources/blah.js', 'arrow => OK here'),
-    ]
-    warnings = PRESUBMIT._CheckForRiskyJsFeatures(
-        mock_input_api, MockOutputApi())
-    self.assertEqual(0, len(warnings))
-
-  def testConstLetWarningIos9Code(self):
-    mock_input_api = MockInputApi()
-    mock_output_api = MockOutputApi()
-
-    mock_input_api.files = [
-      MockAffectedFile('components/blah.js', [" const foo = 'bar';"]),
-      MockAffectedFile('ui/webui/resources/blah.js', [" let foo = 3;"]),
-    ]
-    warnings = PRESUBMIT._CheckForRiskyJsFeatures(
-        mock_input_api, mock_output_api)
-    self.assertEqual(2, len(warnings))
-
-
 class RelativeIncludesTest(unittest.TestCase):
   def testThirdPartyNotWebKitIgnored(self):
     mock_input_api = MockInputApi()
diff --git a/android_webview/browser/aw_autofill_client.cc b/android_webview/browser/aw_autofill_client.cc
index 56869279..9ffa0ad 100644
--- a/android_webview/browser/aw_autofill_client.cc
+++ b/android_webview/browser/aw_autofill_client.cc
@@ -84,7 +84,7 @@
   return nullptr;
 }
 
-autofill::StrikeDatabase* AwAutofillClient::GetStrikeDatabase() {
+autofill::LegacyStrikeDatabase* AwAutofillClient::GetLegacyStrikeDatabase() {
   return nullptr;
 }
 
diff --git a/android_webview/browser/aw_autofill_client.h b/android_webview/browser/aw_autofill_client.h
index 29c3809..c48665c 100644
--- a/android_webview/browser/aw_autofill_client.h
+++ b/android_webview/browser/aw_autofill_client.h
@@ -21,9 +21,9 @@
 class CardUnmaskDelegate;
 class CreditCard;
 class FormStructure;
+class LegacyStrikeDatabase;
 class MigratableCreditCard;
 class PersonalDataManager;
-class StrikeDatabase;
 }
 
 namespace content {
@@ -66,7 +66,7 @@
   identity::IdentityManager* GetIdentityManager() override;
   autofill::payments::PaymentsClient* GetPaymentsClient() override;
   autofill::FormDataImporter* GetFormDataImporter() override;
-  autofill::StrikeDatabase* GetStrikeDatabase() override;
+  autofill::LegacyStrikeDatabase* GetLegacyStrikeDatabase() override;
   ukm::UkmRecorder* GetUkmRecorder() override;
   ukm::SourceId GetUkmSourceId() override;
   autofill::AddressNormalizer* GetAddressNormalizer() override;
diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc
index cdb1b4a..d7164ad 100644
--- a/android_webview/browser/aw_browser_context.cc
+++ b/android_webview/browser/aw_browser_context.cc
@@ -213,10 +213,6 @@
   return context_storage_path_;
 }
 
-base::FilePath AwBrowserContext::GetCachePath() const {
-  return GetCacheDir();
-}
-
 bool AwBrowserContext::IsOffTheRecord() const {
   // Android WebView does not support off the record profile yet.
   return false;
diff --git a/android_webview/browser/aw_browser_context.h b/android_webview/browser/aw_browser_context.h
index 7dcbf5f..ab0346c 100644
--- a/android_webview/browser/aw_browser_context.h
+++ b/android_webview/browser/aw_browser_context.h
@@ -95,7 +95,6 @@
 
   // content::BrowserContext implementation.
   base::FilePath GetPath() const override;
-  base::FilePath GetCachePath() const override;
   bool IsOffTheRecord() const override;
   content::ResourceContext* GetResourceContext() override;
   content::DownloadManagerDelegate* GetDownloadManagerDelegate() override;
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index 16efdcf..81acbd4 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -193,6 +193,21 @@
     autofill::mojom::PasswordManagerDriverRequest request,
     content::RenderFrameHost* render_frame_host) {}
 
+// TODO(timvolodine): consider refactoring this into common utility method.
+void OnReceivedErrorOnUIThread(
+    const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter,
+    const AwWebResourceRequest& request) {
+  AwContentsClientBridge* client =
+      AwContentsClientBridge::FromWebContentsGetter(web_contents_getter);
+  if (!client) {
+    DLOG(WARNING) << "client is null, onReceivedError dropped for "
+                  << request.url;
+    return;
+  }
+  client->OnReceivedError(request, net::ERR_UNKNOWN_URL_SCHEME,
+                          false /*safebrowsing_hit*/);
+}
+
 }  // anonymous namespace
 
 // TODO(yirui): can use similar logic as in PrependToAcceptLanguagesIfNecessary
@@ -791,10 +806,24 @@
     content::NavigationUIData* navigation_data,
     bool is_main_frame,
     ui::PageTransition page_transition,
-    bool has_user_gesture) {
-  // The AwURLRequestJobFactory implementation should ensure this method never
-  // gets called.
-  NOTREACHED();
+    bool has_user_gesture,
+    const std::string& method,
+    const net::HttpRequestHeaders& headers) {
+  if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+    AwWebResourceRequest aw_resource_request(url.spec(), method, is_main_frame,
+                                             has_user_gesture, headers);
+    aw_resource_request.is_renderer_initiated =
+        ui::PageTransitionIsWebTriggerable(
+            static_cast<ui::PageTransition>(page_transition));
+    base::PostTaskWithTraits(
+        FROM_HERE, {BrowserThread::UI},
+        base::BindOnce(&OnReceivedErrorOnUIThread, web_contents_getter,
+                       std::move(aw_resource_request)));
+  } else {
+    // The AwURLRequestJobFactory implementation should ensure this method never
+    // gets called when Network Service is not enabled.
+    NOTREACHED();
+  }
   return false;
 }
 
diff --git a/android_webview/browser/aw_content_browser_client.h b/android_webview/browser/aw_content_browser_client.h
index c7d169b..afafb94 100644
--- a/android_webview/browser/aw_content_browser_client.h
+++ b/android_webview/browser/aw_content_browser_client.h
@@ -200,7 +200,9 @@
       content::NavigationUIData* navigation_data,
       bool is_main_frame,
       ui::PageTransition page_transition,
-      bool has_user_gesture) override;
+      bool has_user_gesture,
+      const std::string& method,
+      const net::HttpRequestHeaders& headers) override;
   void RegisterOutOfProcessServices(OutOfProcessServiceMap* services) override;
   bool ShouldIsolateErrorPage(bool in_main_frame) override;
   bool ShouldEnableStrictSiteIsolation() override;
diff --git a/android_webview/browser/aw_contents_io_thread_client.cc b/android_webview/browser/aw_contents_io_thread_client.cc
index c3d337f3..936a948 100644
--- a/android_webview/browser/aw_contents_io_thread_client.cc
+++ b/android_webview/browser/aw_contents_io_thread_client.cc
@@ -17,6 +17,7 @@
 #include "base/containers/flat_set.h"
 #include "base/lazy_instance.h"
 #include "base/synchronization/lock.h"
+#include "base/threading/scoped_blocking_call.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
@@ -342,7 +343,7 @@
 std::unique_ptr<AwWebResourceResponse> RunShouldInterceptRequest(
     const AwWebResourceRequest& request,
     JavaObjectWeakGlobalRef ref) {
-  base::AssertBlockingAllowedDeprecated();
+  base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK);
 
   JNIEnv* env = AttachCurrentThread();
   base::android::ScopedJavaLocalRef<jobject> obj = ref.get(env);
diff --git a/android_webview/browser/aw_feature_list.cc b/android_webview/browser/aw_feature_list.cc
index b49f524..982e8ab 100644
--- a/android_webview/browser/aw_feature_list.cc
+++ b/android_webview/browser/aw_feature_list.cc
@@ -24,6 +24,7 @@
 // in other locations in the code base (e.g. content/, components/, etc).
 const base::Feature* kFeaturesExposedToJava[] = {
     &features::kWebViewConnectionlessSafeBrowsing,
+    &features::kWebViewPageStartedOnCommit,
 };
 
 const base::Feature* FindFeatureExposedToJava(const std::string& feature_name) {
@@ -47,6 +48,11 @@
 const base::Feature kWebViewConnectionlessSafeBrowsing{
     "WebViewConnectionlessSafeBrowsing", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Kill switch for feature to call onPageFinished for browser-initiated
+// navigations when the navigation commits.
+const base::Feature kWebViewPageStartedOnCommit{
+    "WebViewPageStartedOnCommit", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Whether the application package name is logged in UMA.
 const base::Feature kWebViewUmaLogAppPackageName{
     "WebViewUmaLogAppPackageName", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/android_webview/browser/aw_feature_list.h b/android_webview/browser/aw_feature_list.h
index aaa60eb..8f800a7 100644
--- a/android_webview/browser/aw_feature_list.h
+++ b/android_webview/browser/aw_feature_list.h
@@ -15,6 +15,7 @@
 
 // Alphabetical:
 extern const base::Feature kWebViewConnectionlessSafeBrowsing;
+extern const base::Feature kWebViewPageStartedOnCommit;
 extern const base::Feature kWebViewUmaLogAppPackageName;
 
 }  // namespace features
diff --git a/android_webview/browser/aw_metrics_service_client.cc b/android_webview/browser/aw_metrics_service_client.cc
index 70d1371..d4062684 100644
--- a/android_webview/browser/aw_metrics_service_client.cc
+++ b/android_webview/browser/aw_metrics_service_client.cc
@@ -32,6 +32,7 @@
 #include "components/metrics/metrics_pref_names.h"
 #include "components/metrics/metrics_service.h"
 #include "components/metrics/metrics_state_manager.h"
+#include "components/metrics/net/network_metrics_provider.h"
 #include "components/metrics/ui/screen_info_metrics_provider.h"
 #include "components/metrics/url_constants.h"
 #include "components/metrics/version_utils.h"
@@ -39,6 +40,7 @@
 #include "components/version_info/android/channel_getter.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/network_service_instance.h"
 
 namespace android_webview {
 
@@ -167,7 +169,8 @@
 
   metrics_service_->RegisterMetricsProvider(
       std::unique_ptr<metrics::MetricsProvider>(
-          new metrics::NetworkMetricsProvider));
+          new metrics::NetworkMetricsProvider(
+              content::CreateNetworkConnectionTrackerAsyncGetter())));
 
   metrics_service_->RegisterMetricsProvider(
       std::unique_ptr<metrics::MetricsProvider>(
@@ -237,7 +240,7 @@
 }
 
 metrics::SystemProfileProto::Channel AwMetricsServiceClient::GetChannel() {
-  return metrics::AsProtobufChannel(version_info::GetChannel());
+  return metrics::AsProtobufChannel(version_info::android::GetChannel());
 }
 
 std::string AwMetricsServiceClient::GetVersionString() {
diff --git a/android_webview/browser/aw_variations_service_client.cc b/android_webview/browser/aw_variations_service_client.cc
index af22333..8f1c0fe 100644
--- a/android_webview/browser/aw_variations_service_client.cc
+++ b/android_webview/browser/aw_variations_service_client.cc
@@ -6,7 +6,7 @@
 
 #include "android_webview/common/aw_channel.h"
 #include "base/bind.h"
-#include "base/threading/thread_restrictions.h"
+#include "base/threading/scoped_blocking_call.h"
 #include "build/build_config.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
@@ -18,7 +18,7 @@
 // Gets the version number to use for variations seed simulation. Must be called
 // on a thread where IO is allowed.
 base::Version GetVersionForSimulation() {
-  base::AssertBlockingAllowedDeprecated();
+  base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK);
   return version_info::GetVersion();
 }
 
diff --git a/android_webview/browser/aw_web_contents_delegate.cc b/android_webview/browser/aw_web_contents_delegate.cc
index f74aef0..428a722 100644
--- a/android_webview/browser/aw_web_contents_delegate.cc
+++ b/android_webview/browser/aw_web_contents_delegate.cc
@@ -338,9 +338,12 @@
       file_info->display_name = display_name_str[i];
     files.push_back(FileChooserFileInfo::NewNativeFile(std::move(file_info)));
   }
+  base::FilePath base_dir;
   FileChooserParams::Mode mode;
   if (mode_flags & kFileChooserModeOpenFolder) {
     mode = FileChooserParams::Mode::kUploadFolder;
+    // We'd like to set |base_dir| to a folder which a user selected. But it's
+    // impossible with WebChromeClient API in the current Android.
   } else if (mode_flags & kFileChooserModeOpenMultiple) {
     mode = FileChooserParams::Mode::kOpenMultiple;
   } else {
@@ -348,7 +351,7 @@
   }
   DVLOG(0) << "File Chooser result: mode = " << mode
            << ", file paths = " << base::JoinString(file_path_str, ":");
-  listener->FileSelected(std::move(files), mode);
+  listener->FileSelected(std::move(files), base_dir, mode);
 }
 
 }  // namespace android_webview
diff --git a/android_webview/browser/hardware_renderer.cc b/android_webview/browser/hardware_renderer.cc
index 730e062..cccd77c 100644
--- a/android_webview/browser/hardware_renderer.cc
+++ b/android_webview/browser/hardware_renderer.cc
@@ -184,11 +184,9 @@
                               last_submitted_layer_tree_frame_sink_id_);
 }
 
-void HardwareRenderer::DidPresentCompositorFrame(
-    uint32_t presentation_token,
-    const gfx::PresentationFeedback& feedback) {}
-
-void HardwareRenderer::OnBeginFrame(const viz::BeginFrameArgs& args) {
+void HardwareRenderer::OnBeginFrame(
+    const viz::BeginFrameArgs& args,
+    const base::flat_map<uint32_t, gfx::PresentationFeedback>& feedbacks) {
   // TODO(tansell): Hook this up.
 }
 
diff --git a/android_webview/browser/hardware_renderer.h b/android_webview/browser/hardware_renderer.h
index 083ee09..4271045 100644
--- a/android_webview/browser/hardware_renderer.h
+++ b/android_webview/browser/hardware_renderer.h
@@ -51,10 +51,9 @@
   // viz::mojom::CompositorFrameSinkClient implementation.
   void DidReceiveCompositorFrameAck(
       const std::vector<viz::ReturnedResource>& resources) override;
-  void DidPresentCompositorFrame(
-      uint32_t presentation_token,
-      const gfx::PresentationFeedback& feedback) override;
-  void OnBeginFrame(const viz::BeginFrameArgs& args) override;
+  void OnBeginFrame(const viz::BeginFrameArgs& args,
+                    const base::flat_map<uint32_t, gfx::PresentationFeedback>&
+                        feedbacks) override;
   void ReclaimResources(
       const std::vector<viz::ReturnedResource>& resources) override;
   void OnBeginFramePausedChanged(bool paused) override;
diff --git a/android_webview/browser/net/aw_url_request_context_getter.cc b/android_webview/browser/net/aw_url_request_context_getter.cc
index 6a46c25..d34ffae 100644
--- a/android_webview/browser/net/aw_url_request_context_getter.cc
+++ b/android_webview/browser/net/aw_url_request_context_getter.cc
@@ -25,7 +25,7 @@
 #include "base/files/file_path.h"
 #include "base/path_service.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/sys_info.h"
+#include "base/system/sys_info.h"
 #include "base/task/post_task.h"
 #include "build/build_config.h"
 #include "components/network_session_configurator/common/network_switches.h"
diff --git a/android_webview/browser/surfaces_instance.cc b/android_webview/browser/surfaces_instance.cc
index 89f331d..9e6a653 100644
--- a/android_webview/browser/surfaces_instance.cc
+++ b/android_webview/browser/surfaces_instance.cc
@@ -220,7 +220,9 @@
   return child_ranges;
 }
 
-void SurfacesInstance::OnBeginFrame(const viz::BeginFrameArgs& args) {}
+void SurfacesInstance::OnBeginFrame(
+    const viz::BeginFrameArgs& args,
+    const base::flat_map<uint32_t, gfx::PresentationFeedback>& feedbacks) {}
 
 void SurfacesInstance::ReclaimResources(
     const std::vector<viz::ReturnedResource>& resources) {
diff --git a/android_webview/browser/surfaces_instance.h b/android_webview/browser/surfaces_instance.h
index 52d2a70..570defe 100644
--- a/android_webview/browser/surfaces_instance.h
+++ b/android_webview/browser/surfaces_instance.h
@@ -60,9 +60,8 @@
 
   // viz::DisplayClient overrides.
   void DisplayOutputSurfaceLost() override;
-  void DisplayWillDrawAndSwap(
-      bool will_draw_and_swap,
-      const viz::RenderPassList& render_passes) override {}
+  void DisplayWillDrawAndSwap(bool will_draw_and_swap,
+                              viz::RenderPassList* render_passes) override {}
   void DisplayDidDrawAndSwap() override {}
   void DisplayDidReceiveCALayerParams(
       const gfx::CALayerParams& ca_layer_params) override {}
@@ -73,10 +72,9 @@
   // viz::mojom::CompositorFrameSinkClient implementation.
   void DidReceiveCompositorFrameAck(
       const std::vector<viz::ReturnedResource>& resources) override;
-  void DidPresentCompositorFrame(
-      uint32_t presentation_token,
-      const gfx::PresentationFeedback& feedback) override {}
-  void OnBeginFrame(const viz::BeginFrameArgs& args) override;
+  void OnBeginFrame(const viz::BeginFrameArgs& args,
+                    const base::flat_map<uint32_t, gfx::PresentationFeedback>&
+                        feedbacks) override;
   void OnBeginFramePausedChanged(bool paused) override;
   void ReclaimResources(
       const std::vector<viz::ReturnedResource>& resources) override;
diff --git a/android_webview/common/aw_channel.cc b/android_webview/common/aw_channel.cc
index ece22c8..b585040 100644
--- a/android_webview/common/aw_channel.cc
+++ b/android_webview/common/aw_channel.cc
@@ -11,7 +11,7 @@
 using version_info::Channel;
 
 Channel GetChannelOrStable() {
-  Channel channel = version_info::GetChannel();
+  Channel channel = version_info::android::GetChannel();
   return channel == Channel::UNKNOWN ? Channel::STABLE : channel;
 }
 
diff --git a/android_webview/common/devtools_instrumentation.h b/android_webview/common/devtools_instrumentation.h
index 31f1170..abb95dc 100644
--- a/android_webview/common/devtools_instrumentation.h
+++ b/android_webview/common/devtools_instrumentation.h
@@ -12,22 +12,23 @@
 namespace devtools_instrumentation {
 
 namespace internal {
-const char kCategory[] = "Java,devtools,disabled-by-default-devtools.timeline";
+constexpr const char* Category() {
+  // Declared as a constexpr function to have an external linkage and to be
+  // known at compile-time.
+  return "Java,devtools,disabled-by-default-devtools.timeline";
+}
 const char kEmbedderCallback[] = "EmbedderCallback";
 const char kCallbackNameArgument[] = "callbackName";
 }  // namespace internal
 
 class ScopedEmbedderCallbackTask {
  public:
-  ScopedEmbedderCallbackTask(const char* callback_name) {
-    TRACE_EVENT_BEGIN1(internal::kCategory,
-                       internal::kEmbedderCallback,
-                       internal::kCallbackNameArgument,
-                       callback_name);
+  explicit ScopedEmbedderCallbackTask(const char* callback_name) {
+    TRACE_EVENT_BEGIN1(internal::Category(), internal::kEmbedderCallback,
+                       internal::kCallbackNameArgument, callback_name);
   }
   ~ScopedEmbedderCallbackTask() {
-    TRACE_EVENT_END0(internal::kCategory,
-                     internal::kEmbedderCallback);
+    TRACE_EVENT_END0(internal::Category(), internal::kEmbedderCallback);
   }
 
  private:
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
index 3168066..3016955 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
@@ -225,6 +225,7 @@
         }
     }
 
+    @SuppressWarnings("NoContextGetApplicationContext")
     private void initialize(WebViewDelegate webViewDelegate) {
         long startTime = SystemClock.elapsedRealtime();
         try (ScopedSysTraceEvent e1 =
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 648039d..85b9d55 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -105,6 +105,7 @@
 import java.util.Map;
 import java.util.WeakHashMap;
 import java.util.concurrent.Callable;
+import java.util.regex.Pattern;
 
 /**
  * Exposes the native AwContents class, and together these classes wrap the WebContents
@@ -138,6 +139,17 @@
     public static final String DATA_URI_HISTOGRAM_NAME =
             "Android.WebView.LoadUrl.DataUriHasOctothorpe";
 
+    @VisibleForTesting
+    public static final String DATA_BASE_URL_SCHEME_HISTOGRAM_NAME =
+            "Android.WebView.LoadDataWithBaseUrl.BaseUrl";
+
+    @VisibleForTesting
+    public static final String LOAD_URL_SCHEME_HISTOGRAM_NAME = "Android.WebView.LoadUrl.UrlScheme";
+
+    // Permit any number of slashes, since chromium seems to canonicalize bad values.
+    private static final Pattern sFileAndroidAssetPattern =
+            Pattern.compile("^file:/*android_(asset|res).*");
+
     private static class ForceAuxiliaryBitmapRendering {
         private static final boolean sResult = lazyCheck();
         private static boolean lazyCheck() {
@@ -145,8 +157,8 @@
         }
     }
 
-    // Used to record the UMA histogram WebView.LoadDataWithBaseUrl.HistoryUrl. Since these values
-    // are persisted to logs, they should never be renumbered nor reused.
+    // Used to record the UMA histogram Android.WebView.LoadDataWithBaseUrl.HistoryUrl. Since these
+    // values are persisted to logs, they should never be renumbered nor reused.
     @IntDef({HistoryUrl.EMPTY, HistoryUrl.BASEURL, HistoryUrl.DIFFERENT, HistoryUrl.COUNT})
     @interface HistoryUrl {
         int EMPTY = 0;
@@ -155,6 +167,32 @@
         int COUNT = 3;
     }
 
+    // Used to record the UMA histogram Android.WebView.LoadDataWithBaseUrl.UrlScheme. Since these
+    // values are persisted to logs, they should never be renumbered nor reused.
+    @VisibleForTesting
+    @IntDef({UrlScheme.EMPTY, UrlScheme.UNKNOWN_SCHEME, UrlScheme.HTTP_SCHEME,
+            UrlScheme.HTTPS_SCHEME, UrlScheme.FILE_SCHEME, UrlScheme.FTP_SCHEME,
+            UrlScheme.DATA_SCHEME, UrlScheme.JAVASCRIPT_SCHEME, UrlScheme.ABOUT_SCHEME,
+            UrlScheme.CHROME_SCHEME, UrlScheme.BLOB_SCHEME, UrlScheme.CONTENT_SCHEME,
+            UrlScheme.INTENT_SCHEME, UrlScheme.FILE_ANDROID_ASSET_SCHEME})
+    public @interface UrlScheme {
+        int EMPTY = 0;
+        int UNKNOWN_SCHEME = 1;
+        int HTTP_SCHEME = 2;
+        int HTTPS_SCHEME = 3;
+        int FILE_SCHEME = 4;
+        int FTP_SCHEME = 5;
+        int DATA_SCHEME = 6;
+        int JAVASCRIPT_SCHEME = 7;
+        int ABOUT_SCHEME = 8;
+        int CHROME_SCHEME = 9;
+        int BLOB_SCHEME = 10;
+        int CONTENT_SCHEME = 11;
+        int INTENT_SCHEME = 12;
+        int FILE_ANDROID_ASSET_SCHEME = 13; // Covers android_asset and android_res URLs
+        int COUNT = 14;
+    }
+
     /**
      * WebKit hit test related data structure. These are used to implement
      * getHitTestResult, requestFocusNodeHref, requestImageRef methods in WebView.
@@ -611,7 +649,7 @@
             // The shouldOverrideUrlLoading call might have resulted in posting messages to the
             // UI thread. Using sendMessage here (instead of calling onPageStarted directly)
             // will allow those to run in order.
-            if (!navigationParams.isRendererInitiated) {
+            if (!AwFeatureList.pageStartedOnCommitEnabled(navigationParams.isRendererInitiated)) {
                 mContentsClient.getCallbackHelper().postOnPageStarted(navigationParams.url);
             }
             return false;
@@ -1656,6 +1694,16 @@
                 "Android.WebView.LoadDataWithBaseUrl.HistoryUrl", value, HistoryUrl.COUNT);
     }
 
+    private static void recordBaseUrl(@UrlScheme int value) {
+        RecordHistogram.recordEnumeratedHistogram(
+                DATA_BASE_URL_SCHEME_HISTOGRAM_NAME, value, UrlScheme.COUNT);
+    }
+
+    private static void recordLoadUrlScheme(@UrlScheme int value) {
+        RecordHistogram.recordEnumeratedHistogram(
+                LOAD_URL_SCHEME_HISTOGRAM_NAME, value, UrlScheme.COUNT);
+    }
+
     /**
      * WebView.loadData.
      */
@@ -1669,6 +1717,37 @@
                 fixupData(data), fixupMimeType(mimeType), isBase64Encoded(encoding)));
     }
 
+    private @UrlScheme int schemeForUrl(String url) {
+        if (url == null || url.equals(ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL)) {
+            return (UrlScheme.EMPTY);
+        } else if (url.startsWith("http:")) {
+            return (UrlScheme.HTTP_SCHEME);
+        } else if (url.startsWith("https:")) {
+            return (UrlScheme.HTTPS_SCHEME);
+        } else if (sFileAndroidAssetPattern.matcher(url).matches()) {
+            return (UrlScheme.FILE_ANDROID_ASSET_SCHEME);
+        } else if (url.startsWith("file:")) {
+            return (UrlScheme.FILE_SCHEME);
+        } else if (url.startsWith("ftp:")) {
+            return (UrlScheme.FTP_SCHEME);
+        } else if (url.startsWith("data:")) {
+            return (UrlScheme.DATA_SCHEME);
+        } else if (url.startsWith("javascript:")) {
+            return (UrlScheme.JAVASCRIPT_SCHEME);
+        } else if (url.startsWith("about:")) {
+            return (UrlScheme.ABOUT_SCHEME);
+        } else if (url.startsWith("chrome:")) {
+            return (UrlScheme.CHROME_SCHEME);
+        } else if (url.startsWith("blob:")) {
+            return (UrlScheme.BLOB_SCHEME);
+        } else if (url.startsWith("content:")) {
+            return (UrlScheme.CONTENT_SCHEME);
+        } else if (url.startsWith("intent:")) {
+            return (UrlScheme.INTENT_SCHEME);
+        }
+        return (UrlScheme.UNKNOWN_SCHEME);
+    }
+
     /**
      * WebView.loadDataWithBaseURL.
      */
@@ -1691,6 +1770,8 @@
             recordHistoryUrl(HistoryUrl.DIFFERENT);
         }
 
+        recordBaseUrl(schemeForUrl(baseUrl));
+
         if (baseUrl.startsWith("data:")) {
             // We record only for this branch, because the other branch assumes unencoded content.
             if (data != null && data.contains("#")) {
@@ -1735,6 +1816,12 @@
      */
     @VisibleForTesting
     public void loadUrl(LoadUrlParams params) {
+        if (params.getBaseUrl() == null) {
+            // Don't record the URL if this was loaded via loadDataWithBaseURL(). That API is
+            // tracked separately under Android.WebView.LoadDataWithBaseUrl.BaseUrl.
+            recordLoadUrlScheme(schemeForUrl(params.getUrl()));
+        }
+
         if (params.getLoadUrlType() == LoadURLType.DATA && !params.isBaseUrlDataScheme()) {
             // This allows data URLs with a non-data base URL access to file:///android_asset/ and
             // file:///android_res/ URLs. If AwSettings.getAllowFileAccess permits, it will also
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java b/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java
index 2c2aef4..ad3a5c4 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java
@@ -301,7 +301,8 @@
             } else {
                 error.errorCode = ErrorCodeConversionHelper.convertErrorCode(error.errorCode);
             }
-            if (request.isMainFrame && isRendererInitiated) {
+            if (request.isMainFrame
+                    && AwFeatureList.pageStartedOnCommitEnabled(isRendererInitiated)) {
                 mClient.getCallbackHelper().postOnPageStarted(request.url);
             }
             mClient.getCallbackHelper().postOnReceivedError(request, error);
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java b/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java
index 8237019..0c85343 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java
@@ -99,17 +99,18 @@
         nativeSetSafeBrowsingWhitelist(urlArray, callback);
     }
 
+    @SuppressWarnings("NoContextGetApplicationContext")
     public static void initSafeBrowsing(Context context, final Callback<Boolean> callback) {
         // Wrap the callback to make sure we always invoke it on the UI thread, as guaranteed by the
         // API.
-        final Context appContext = context.getApplicationContext();
         Callback<Boolean> wrapperCallback = b -> {
             if (callback != null) {
                 ThreadUtils.runOnUiThread(() -> callback.onResult(b));
             }
         };
 
-        PlatformServiceBridge.getInstance().warmUpSafeBrowsing(appContext, wrapperCallback);
+        PlatformServiceBridge.getInstance().warmUpSafeBrowsing(
+                context.getApplicationContext(), wrapperCallback);
     }
 
     public static Uri getSafeBrowsingPrivacyPolicyUrl() {
diff --git a/android_webview/java/src/org/chromium/android_webview/AwFeatureList.java b/android_webview/java/src/org/chromium/android_webview/AwFeatureList.java
index 6bbfe0c..829c7a8 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwFeatureList.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwFeatureList.java
@@ -4,6 +4,10 @@
 
 package org.chromium.android_webview;
 
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+
+import org.chromium.base.ContextUtils;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.MainDex;
 
@@ -16,6 +20,36 @@
     // Do not instantiate this class.
     private AwFeatureList() {}
 
+    private static final String GMS_PACKAGE = "com.google.android.gms";
+
+    private static Boolean sPageStartedOnCommitForBrowserNavigations;
+
+    private static boolean computePageStartedOnCommitForBrowserNavigations() {
+        if (!nativeIsEnabled(WEBVIEW_PAGE_STARTED_ON_COMMIT)) return false;
+        if (GMS_PACKAGE.equals(ContextUtils.getApplicationContext().getPackageName())) {
+            try {
+                PackageInfo gmsPackage =
+                        ContextUtils.getApplicationContext().getPackageManager().getPackageInfo(
+                                GMS_PACKAGE, 0);
+                return gmsPackage.versionCode >= 15000000;
+            } catch (PackageManager.NameNotFoundException e) {
+            }
+            return false;
+        }
+        return true;
+    }
+
+    public static boolean pageStartedOnCommitEnabled(boolean isRendererInitiated) {
+        // Always enable for renderer-initiated navigations.
+        if (isRendererInitiated) return true;
+        if (sPageStartedOnCommitForBrowserNavigations != null) {
+            return sPageStartedOnCommitForBrowserNavigations;
+        }
+        sPageStartedOnCommitForBrowserNavigations =
+                computePageStartedOnCommitForBrowserNavigations();
+        return sPageStartedOnCommitForBrowserNavigations;
+    }
+
     /**
      * Returns whether the specified feature is enabled or not.
      *
@@ -32,6 +66,7 @@
     // Alphabetical:
     public static final String WEBVIEW_CONNECTIONLESS_SAFE_BROWSING =
             "WebViewConnectionlessSafeBrowsing";
+    public static final String WEBVIEW_PAGE_STARTED_ON_COMMIT = "WebViewPageStartedOnCommit";
 
     private static native boolean nativeIsEnabled(String featureName);
 }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwPicture.java b/android_webview/java/src/org/chromium/android_webview/AwPicture.java
index c8c8d3b..8993543 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwPicture.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwPicture.java
@@ -68,7 +68,6 @@
         nativeDraw(mNativeAwPicture, canvas);
     }
 
-    @Override
     @SuppressWarnings("deprecation")
     public void writeToStream(OutputStream stream) {
         unsupportedOperation();
diff --git a/android_webview/java/src/org/chromium/android_webview/AwSettings.java b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
index d5fbe20..912d436 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwSettings.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
@@ -4,6 +4,7 @@
 
 package org.chromium.android_webview;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Build;
@@ -166,6 +167,7 @@
         EventHandler() {
         }
 
+        @SuppressLint("HandlerLeak")
         void bindUiThread() {
             if (mHandler != null) return;
             mHandler = new Handler(ThreadUtils.getUiThreadLooper()) {
diff --git a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
index ad829f6..46e9b1e 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
@@ -179,6 +179,7 @@
     }
 
     @Override
+    @SuppressLint("HandlerLeak")
     public void showRepostFormWarningDialog() {
         // TODO(mkosiba) We should be using something akin to the JsResultReceiver as the
         // callback parameter (instead of WebContents) and implement a way of converting
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 0e0200a..853423c 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java
@@ -107,7 +107,8 @@
         if (client != null) {
             // OnPageStarted is not called for fragment navigations.
             // Error page is handled by AwContentsClientBridge.onReceivedError.
-            if (!isFragmentNavigation && !isErrorPage && isRendererInitiated) {
+            if (!isFragmentNavigation && !isErrorPage
+                    && AwFeatureList.pageStartedOnCommitEnabled(isRendererInitiated)) {
                 client.getCallbackHelper().postOnPageStarted(url);
             }
 
diff --git a/android_webview/java/src/org/chromium/android_webview/CleanupReference.java b/android_webview/java/src/org/chromium/android_webview/CleanupReference.java
index 6242bcf..fb326ef 100644
--- a/android_webview/java/src/org/chromium/android_webview/CleanupReference.java
+++ b/android_webview/java/src/org/chromium/android_webview/CleanupReference.java
@@ -4,6 +4,7 @@
 
 package org.chromium.android_webview;
 
+import android.annotation.SuppressLint;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -81,6 +82,7 @@
      * This is lazily initialized as ThreadUtils.getUiThreadLooper() may not be
      * set yet early in startup.
      */
+    @SuppressLint("HandlerLeak")
     private static class LazyHolder {
         static final Handler sHandler = new Handler(ThreadUtils.getUiThreadLooper()) {
             @Override
diff --git a/android_webview/java/src/org/chromium/android_webview/ResourcesContextWrapperFactory.java b/android_webview/java/src/org/chromium/android_webview/ResourcesContextWrapperFactory.java
index 638e947..da6d957 100644
--- a/android_webview/java/src/org/chromium/android_webview/ResourcesContextWrapperFactory.java
+++ b/android_webview/java/src/org/chromium/android_webview/ResourcesContextWrapperFactory.java
@@ -82,6 +82,7 @@
         }
 
         @Override
+        @SuppressWarnings("NoContextGetApplicationContext")
         public Context getApplicationContext() {
             if (mApplicationContext == null) {
                 Context appCtx = getBaseContext().getApplicationContext();
diff --git a/android_webview/java/src/org/chromium/android_webview/services/AwMinidumpUploadJobService.java b/android_webview/java/src/org/chromium/android_webview/services/AwMinidumpUploadJobService.java
index 188fb20c..f6856a6 100644
--- a/android_webview/java/src/org/chromium/android_webview/services/AwMinidumpUploadJobService.java
+++ b/android_webview/java/src/org/chromium/android_webview/services/AwMinidumpUploadJobService.java
@@ -15,6 +15,7 @@
 // OBS: This class needs to be public to be started from android.app.ActivityThread.
 public class AwMinidumpUploadJobService extends MinidumpUploadJobService {
     @Override
+    @SuppressWarnings("NoContextGetApplicationContext")
     public void onCreate() {
         super.onCreate();
         ServiceInit.init(getApplicationContext());
diff --git a/android_webview/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java b/android_webview/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java
index c34f025..55cbd2c 100644
--- a/android_webview/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java
+++ b/android_webview/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java
@@ -150,6 +150,7 @@
     }
 
     @Override
+    @SuppressWarnings("NoContextGetApplicationContext")
     public void onCreate() {
         super.onCreate();
         ServiceInit.init(getApplicationContext());
diff --git a/android_webview/java/src/org/chromium/android_webview/services/CrashReceiverService.java b/android_webview/java/src/org/chromium/android_webview/services/CrashReceiverService.java
index 43e6af9..330d61f 100644
--- a/android_webview/java/src/org/chromium/android_webview/services/CrashReceiverService.java
+++ b/android_webview/java/src/org/chromium/android_webview/services/CrashReceiverService.java
@@ -35,6 +35,7 @@
     private boolean mIsCopying;
 
     @Override
+    @SuppressWarnings("NoContextGetApplicationContext")
     public void onCreate() {
         super.onCreate();
         ServiceInit.init(getApplicationContext());
diff --git a/android_webview/java/src/org/chromium/android_webview/services/VariationsSeedServer.java b/android_webview/java/src/org/chromium/android_webview/services/VariationsSeedServer.java
index 7428722..bd9c059 100644
--- a/android_webview/java/src/org/chromium/android_webview/services/VariationsSeedServer.java
+++ b/android_webview/java/src/org/chromium/android_webview/services/VariationsSeedServer.java
@@ -30,6 +30,7 @@
     }
 
     @Override
+    @SuppressWarnings("NoContextGetApplicationContext")
     public void onCreate() {
         super.onCreate();
         ServiceInit.init(getApplicationContext());
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
index cb0bdbc..90b31d16 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
@@ -903,4 +903,71 @@
         // |loadUrl| doesn't allow a null url, so it is not necessary to check that for this API.
         // See http://crbug.com/864708.
     }
+
+    @Test
+    @Feature({"AndroidWebView"})
+    @SmallTest
+    public void testLoadUrlRecordsScheme_http() throws Throwable {
+        // No need to spin up a web server, since we don't care if the load ever succeeds.
+        final String httpUrlWithNoRealPage = "http://some.origin/some/path.html";
+        loadUrlAndCheckScheme(httpUrlWithNoRealPage, AwContents.UrlScheme.HTTP_SCHEME);
+    }
+
+    @Test
+    @Feature({"AndroidWebView"})
+    @SmallTest
+    public void testLoadUrlRecordsScheme_javascript() throws Throwable {
+        loadUrlAndCheckScheme(
+                "javascript:console.log('message')", AwContents.UrlScheme.JAVASCRIPT_SCHEME);
+    }
+
+    @Test
+    @Feature({"AndroidWebView"})
+    @SmallTest
+    public void testLoadUrlRecordsScheme_fileAndroidAsset() throws Throwable {
+        loadUrlAndCheckScheme("file:///android_asset/some/asset/page.html",
+                AwContents.UrlScheme.FILE_ANDROID_ASSET_SCHEME);
+    }
+
+    @Test
+    @Feature({"AndroidWebView"})
+    @SmallTest
+    public void testLoadUrlRecordsScheme_fileRegular() throws Throwable {
+        loadUrlAndCheckScheme("file:///some/path/on/disk.html", AwContents.UrlScheme.FILE_SCHEME);
+    }
+
+    @Test
+    @Feature({"AndroidWebView"})
+    @SmallTest
+    public void testLoadUrlRecordsScheme_data() throws Throwable {
+        loadUrlAndCheckScheme(
+                "data:text/html,<html><body>foo</body></html>", AwContents.UrlScheme.DATA_SCHEME);
+    }
+
+    @Test
+    @Feature({"AndroidWebView"})
+    @SmallTest
+    public void testLoadUrlRecordsScheme_blank() throws Throwable {
+        loadUrlAndCheckScheme("about:blank", AwContents.UrlScheme.EMPTY);
+    }
+
+    private void loadUrlAndCheckScheme(String url, @AwContents.UrlScheme int expectedSchemeEnum)
+            throws Throwable {
+        AwTestContainerView testView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
+        final AwContents awContents = testView.getAwContents();
+
+        Assert.assertEquals(0,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        AwContents.LOAD_URL_SCHEME_HISTOGRAM_NAME));
+        // Note: we use async because not all loads emit onPageFinished. This relies on the UMA
+        // metric being logged in the synchronous part of loadUrl().
+        mActivityTestRule.loadUrlAsync(awContents, url);
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        AwContents.LOAD_URL_SCHEME_HISTOGRAM_NAME));
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        AwContents.LOAD_URL_SCHEME_HISTOGRAM_NAME, expectedSchemeEnum));
+    }
 }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageStartedTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageStartedTest.java
index ab516e7..06d91f4 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageStartedTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageStartedTest.java
@@ -285,19 +285,21 @@
         int hangingRequestCount = mHangingRequestCallbackHelper.getCallCount();
         mActivityTestRule.loadUrlAsync(mAwContents, mHangingUrl);
 
-        // onPageStarted should be called, but not onPageFinished.
+        // onPageStarted and onPageFinished should not be called yet.
         mContentsClient.getOnLoadResourceHelper().waitForCallback(onLoadResourceCount);
         mHangingRequestCallbackHelper.waitForCallback(hangingRequestCount);
-        mContentsClient.getOnPageStartedHelper().waitForCallback(pageStartedCount);
         Assert.assertEquals(
                 mHangingUrl, mContentsClient.getOnLoadResourceHelper().getLastLoadedResource());
-        Assert.assertEquals(mHangingUrl, mContentsClient.getOnPageStartedHelper().getUrl());
+        Assert.assertEquals(
+                pageStartedCount, mContentsClient.getOnPageStartedHelper().getCallCount());
         Assert.assertEquals(
                 pageFinishedCount, mContentsClient.getOnPageFinishedHelper().getCallCount());
 
         // Release request on server. Should get onPageStarted/Finished after.
         mHangingRequestSemaphore.release();
+        mContentsClient.getOnPageStartedHelper().waitForCallback(pageStartedCount);
         mContentsClient.getOnPageFinishedHelper().waitForCallback(pageFinishedCount);
+        Assert.assertEquals(mHangingUrl, mContentsClient.getOnPageStartedHelper().getUrl());
         Assert.assertEquals(mHangingUrl, mContentsClient.getOnPageFinishedHelper().getUrl());
     }
 
@@ -356,24 +358,26 @@
         int hangingRequestCount = mHangingRequestCallbackHelper.getCallCount();
         mActivityTestRule.loadUrlAsync(mAwContents, mRedirectToHangingUrl);
 
-        // onPageStarted should be called, but not onPageFinished.
+        // onPageStarted and onPageFinished should not be called yet.
         mContentsClient.getShouldOverrideUrlLoadingHelper().waitForCallback(
                 shouldOverrideUrlLoadingCount);
         mContentsClient.getOnLoadResourceHelper().waitForCallback(onLoadResourceCount);
         mHangingRequestCallbackHelper.waitForCallback(hangingRequestCount);
-        mContentsClient.getOnPageStartedHelper().waitForCallback(pageStartedCount);
         Assert.assertEquals(mHangingUrl,
                 mContentsClient.getShouldOverrideUrlLoadingHelper()
                         .getShouldOverrideUrlLoadingUrl());
         Assert.assertEquals(mRedirectToHangingUrl,
                 mContentsClient.getOnLoadResourceHelper().getLastLoadedResource());
-        Assert.assertEquals(mHangingUrl, mContentsClient.getOnPageStartedHelper().getUrl());
+        Assert.assertEquals(
+                pageStartedCount, mContentsClient.getOnPageStartedHelper().getCallCount());
         Assert.assertEquals(
                 pageFinishedCount, mContentsClient.getOnPageFinishedHelper().getCallCount());
 
         // Release request on server. Should get onPageStarted/Finished after.
         mHangingRequestSemaphore.release();
+        mContentsClient.getOnPageStartedHelper().waitForCallback(pageStartedCount);
         mContentsClient.getOnPageFinishedHelper().waitForCallback(pageFinishedCount);
+        Assert.assertEquals(mHangingUrl, mContentsClient.getOnPageStartedHelper().getUrl());
         Assert.assertEquals(mHangingUrl, mContentsClient.getOnPageFinishedHelper().getUrl());
     }
 }
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 7d18a65..f80de4ad 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
@@ -19,6 +19,7 @@
 import org.chromium.android_webview.AwCookieManager;
 import org.chromium.android_webview.AwSettings;
 import org.chromium.android_webview.test.util.CommonResources;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.content_public.browser.WebContents;
@@ -68,6 +69,7 @@
     private static final String SCRIPT_LOADED = "Loaded";
     private static final String SCRIPT_NOT_LOADED = "Not loaded";
     private static final String SCRIPT_JS = "script_was_loaded = true;";
+    private static final String SIMPLE_HTML = "<html><body></body></html>";
 
     private String getScriptFileTestPageHtml(final String scriptUrl) {
         return "<html>"
@@ -507,4 +509,66 @@
         Assert.assertEquals(
                 CommonResources.ABOUT_TITLE, mActivityTestRule.getTitleOnUiThread(mAwContents));
     }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testBaseUrlMetrics_empty() throws Throwable {
+        loadContentAndCheckMetrics(null, AwContents.UrlScheme.EMPTY);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testBaseUrlMetrics_data() throws Throwable {
+        loadContentAndCheckMetrics("data:text/html", AwContents.UrlScheme.DATA_SCHEME);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testBaseUrlMetrics_http() throws Throwable {
+        loadContentAndCheckMetrics("http://www.google.com/", AwContents.UrlScheme.HTTP_SCHEME);
+    }
+
+    private void loadContentAndCheckMetrics(String baseUrl, int expectedSchemeEnum)
+            throws Throwable {
+        Assert.assertEquals(0,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        AwContents.DATA_BASE_URL_SCHEME_HISTOGRAM_NAME));
+        loadDataWithBaseUrlSync(SIMPLE_HTML, "text/html", false, baseUrl, null);
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        AwContents.DATA_BASE_URL_SCHEME_HISTOGRAM_NAME));
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        AwContents.DATA_BASE_URL_SCHEME_HISTOGRAM_NAME, expectedSchemeEnum));
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testWindowOriginForHttpSchemeUrl() throws Throwable {
+        String baseUri = "https://google.com";
+        AwSettings contentSettings = mActivityTestRule.getAwSettingsOnUiThread(mAwContents);
+        contentSettings.setJavaScriptEnabled(true);
+        loadDataWithBaseUrlSync("", "text/html", false, baseUri, null);
+        Assert.assertEquals("\"https://google.com\"",
+                mActivityTestRule.executeJavaScriptAndWaitForResult(
+                        mAwContents, mContentsClient, "window.origin;"));
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testWindowOriginForCustomSchemeUrl() throws Throwable {
+        String baseUri = "x-thread://-86516399/2465766146407674724";
+        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\"",
+                mActivityTestRule.executeJavaScriptAndWaitForResult(
+                        mAwContents, mContentsClient, "window.origin;"));
+    }
 }
diff --git a/android_webview/lib/aw_main_delegate.cc b/android_webview/lib/aw_main_delegate.cc
index 06275a9..e722cc1 100644
--- a/android_webview/lib/aw_main_delegate.cc
+++ b/android_webview/lib/aw_main_delegate.cc
@@ -232,11 +232,14 @@
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
 
-  const std::string locale = command_line.GetSwitchValueASCII(switches::kLang);
-  base::i18n::SetICUDefaultLocale(locale);
-
   std::string process_type =
       command_line.GetSwitchValueASCII(switches::kProcessType);
+  const bool is_browser_process = process_type.empty();
+  if (!is_browser_process) {
+    base::i18n::SetICUDefaultLocale(
+        command_line.GetSwitchValueASCII(switches::kLang));
+  }
+
   int crash_signal_fd = -1;
   if (process_type == switches::kRendererProcess) {
     auto* global_descriptors = base::GlobalDescriptors::GetInstance();
@@ -260,7 +263,7 @@
     crash_signal_fd =
         global_descriptors->Get(kAndroidWebViewCrashSignalDescriptor);
   }
-  if (process_type.empty()) {
+  if (is_browser_process) {
     if (command_line.HasSwitch(switches::kWebViewSandboxedRenderer)) {
       process_type = breakpad::kBrowserProcessType;
     } else {
diff --git a/android_webview/renderer/aw_content_renderer_client.cc b/android_webview/renderer/aw_content_renderer_client.cc
index 5be63fb9..0235457 100644
--- a/android_webview/renderer/aw_content_renderer_client.cc
+++ b/android_webview/renderer/aw_content_renderer_client.cc
@@ -195,15 +195,12 @@
     content::RenderFrame* render_frame,
     const blink::WebURLRequest& failed_request,
     const blink::WebURLError& error,
-    std::string* error_html,
-    base::string16* error_description) {
+    std::string* error_html) {
   std::string err;
   if (error.reason() == net::ERR_TEMPORARILY_THROTTLED)
     err = kThrottledErrorDescription;
   else
     err = net::ErrorToString(error.reason());
-  if (error_description)
-    *error_description = base::ASCIIToUTF16(err);
 
   if (!error_html)
     return;
@@ -300,7 +297,8 @@
   // TODO(crbug.com/806394): Use a WebView-specific service for SpellCheckHost
   // and SafeBrowsing, instead of |content_browser|.
   RenderThread::Get()->GetConnector()->BindInterface(
-      service_manager::Identity(content::mojom::kBrowserServiceName),
+      service_manager::ServiceFilter::ByName(
+          content::mojom::kBrowserServiceName),
       interface_name, std::move(interface_pipe));
 }
 
diff --git a/android_webview/renderer/aw_content_renderer_client.h b/android_webview/renderer/aw_content_renderer_client.h
index e15b497..9c6906e 100644
--- a/android_webview/renderer/aw_content_renderer_client.h
+++ b/android_webview/renderer/aw_content_renderer_client.h
@@ -40,8 +40,7 @@
   void PrepareErrorPage(content::RenderFrame* render_frame,
                         const blink::WebURLRequest& failed_request,
                         const blink::WebURLError& error,
-                        std::string* error_html,
-                        base::string16* error_description) override;
+                        std::string* error_html) override;
   unsigned long long VisitedLinkHash(const char* canonical_url,
                                      size_t length) override;
   bool IsLinkVisited(unsigned long long link_hash) override;
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 dcf3b56..566535b 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
@@ -74,6 +74,20 @@
     }
 
     /**
+     * Plural version of {@link #createInvocationHandlerFor(Object)}.
+     */
+    @TargetApi(Build.VERSION_CODES.KITKAT)
+    public static InvocationHandler[] createInvocationHandlersForArray(final Object[] delegates) {
+        if (delegates == null) return null;
+
+        InvocationHandler[] handlers = new InvocationHandler[delegates.length];
+        for (int i = 0; i < handlers.length; i++) {
+            handlers[i] = createInvocationHandlerFor(delegates[i]);
+        }
+        return handlers;
+    }
+
+    /**
      * Assuming that the given InvocationHandler was created in the current classloader and is an
      * InvocationHandlerWithDelegateGetter, return the object the InvocationHandler delegates its
      * method calls to.
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebMessagePortAdapter.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebMessagePortAdapter.java
index c111f10..cb05918 100644
--- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebMessagePortAdapter.java
+++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebMessagePortAdapter.java
@@ -96,12 +96,12 @@
             MessagePort[] messagePorts) {
         if (messagePorts == null) return null;
 
-        InvocationHandler[] ports = new InvocationHandler[messagePorts.length];
+        SupportLibWebMessagePortAdapter[] ports =
+                new SupportLibWebMessagePortAdapter[messagePorts.length];
         for (int i = 0; i < messagePorts.length; i++) {
-            ports[i] = BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(
-                    new SupportLibWebMessagePortAdapter(messagePorts[i]));
+            ports[i] = new SupportLibWebMessagePortAdapter(messagePorts[i]);
         }
-        return ports;
+        return BoundaryInterfaceReflectionUtil.createInvocationHandlersForArray(ports);
     }
 
     public static MessagePort[] toMessagePorts(InvocationHandler[] webMessagePorts) {
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebkitToCompatConverterAdapter.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebkitToCompatConverterAdapter.java
index 53f119d..f2f77b0 100644
--- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebkitToCompatConverterAdapter.java
+++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebkitToCompatConverterAdapter.java
@@ -4,6 +4,7 @@
 
 package org.chromium.support_lib_glue;
 
+import android.annotation.SuppressLint;
 import android.webkit.SafeBrowsingResponse;
 import android.webkit.ServiceWorkerWebSettings;
 import android.webkit.WebMessagePort;
@@ -48,6 +49,7 @@
     }
 
     // ServiceWorkerWebSettingsBoundaryInterface
+    @SuppressLint("NewApi")
     @Override
     public InvocationHandler convertServiceWorkerSettings(
             /* ServiceWorkerWebSettings */ Object serviceWorkerWebSettings) {
@@ -66,6 +68,7 @@
         return new ServiceWorkerSettingsAdapter(supportLibWebSettings.getAwServiceWorkerSettings());
     }
 
+    @SuppressLint("NewApi")
     @Override
     public /* SupportLibWebResourceError */ InvocationHandler convertWebResourceError(
             /* WebResourceError */ Object webResourceError) {
@@ -83,6 +86,7 @@
         return new WebResourceErrorAdapter(supportLibError.getAwWebResourceError());
     }
 
+    @SuppressLint("NewApi")
     @Override
     public /* SupportLibSafeBrowsingResponse */ InvocationHandler convertSafeBrowsingResponse(
             /* SafeBrowsingResponse */ Object safeBrowsingResponse) {
@@ -102,6 +106,7 @@
                 supportLibResponse.getAwSafeBrowsingResponseCallback());
     }
 
+    @SuppressLint("NewApi")
     @Override
     public /* SupportLibWebMessagePort */ InvocationHandler convertWebMessagePort(
             /* WebMessagePort */ Object webMessagePort) {
diff --git a/android_webview/test/BUILD.gn b/android_webview/test/BUILD.gn
index 1a21e3a..89ab61a 100644
--- a/android_webview/test/BUILD.gn
+++ b/android_webview/test/BUILD.gn
@@ -17,23 +17,26 @@
   ]
 }
 
-group("webview_cts_tests") {
-  _py_files = read_file("//android_webview/tools/run_cts.pydeps", "list lines")
-  deps = [
-    "//android_webview:system_webview_apk",
-  ]
+if (public_android_sdk) {
+  group("webview_cts_tests") {
+    _py_files =
+        read_file("//android_webview/tools/run_cts.pydeps", "list lines")
+    deps = [
+      "//android_webview:system_webview_apk",
+    ]
 
-  data_deps = [
-    "//build/android:logdog_wrapper_py",
-    "//build/android:test_runner_py",
-  ]
+    data_deps = [
+      "//build/android:logdog_wrapper_py",
+      "//build/android:test_runner_py",
+    ]
 
-  # Filter out comments.
-  set_sources_assignment_filter([ "#*" ])
-  sources = _py_files
+    # Filter out comments.
+    set_sources_assignment_filter([ "#*" ])
+    sources = _py_files
 
-  data = sources
-  data += [ "//android_webview/tools/cts_config/" ]
+    data = sources
+    data += [ "//android_webview/tools/cts_config/" ]
+  }
 }
 
 android_apk("webview_instrumentation_apk") {
diff --git a/android_webview/tools/automated_ui_tests/java/res/layout/edittext_webview.xml b/android_webview/tools/automated_ui_tests/java/res/layout/edittext_webview.xml
index 5f5a7e7d..a242010 100644
--- a/android_webview/tools/automated_ui_tests/java/res/layout/edittext_webview.xml
+++ b/android_webview/tools/automated_ui_tests/java/res/layout/edittext_webview.xml
@@ -15,6 +15,7 @@
         android:layout_width="match_parent"
         android:layout_height="280dp" />
     <EditText
+        tools:ignore="LabelFor"
         android:id="@+id/edittext"
         android:inputType="text"
         android:layout_below="@+id/webview"
diff --git a/android_webview/tools/cts_config/webview_cts_gcs_path.json b/android_webview/tools/cts_config/webview_cts_gcs_path.json
index 5d90979..f178a9f 100644
--- a/android_webview/tools/cts_config/webview_cts_gcs_path.json
+++ b/android_webview/tools/cts_config/webview_cts_gcs_path.json
@@ -37,10 +37,51 @@
       {
         "apk": "android-cts/repository/testcases/CtsWebkitTestCases.apk",
         "excludes": [
-        {
-          "match": "android.webkit.cts.WebViewClientTest#testShouldOverrideUrlLoadingOnCreateWindow",
-          "_bug_id": "crbug.com/896857"
-        }]
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testDoUpdateVisitedHistory",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testLoadPage",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testOnFormResubmission",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testOnReceivedError",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testOnReceivedErrorForSubresource",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testOnReceivedHttpAuthRequest",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testOnReceivedHttpError",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testOnReceivedLoginRequest",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testOnScaleChanged",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testShouldOverrideUrlLoading",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testShouldOverrideUrlLoadingOnCreateWindow",
+            "_bug_id": "crbug.com/896022"
+          }
+        ]
       },
       {
         "apk": "android-cts/repository/testcases/CtsWidgetTestCases.apk",
@@ -58,10 +99,51 @@
       {
         "apk": "android-cts/repository/testcases/CtsWebkitTestCases.apk",
         "excludes": [
-        {
-          "match": "android.webkit.cts.WebViewClientTest#testShouldOverrideUrlLoadingOnCreateWindow",
-          "_bug_id": "crbug.com/896857"
-        }]
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testDoUpdateVisitedHistory",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testLoadPage",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testOnFormResubmission",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testOnReceivedError",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testOnReceivedErrorForSubresource",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testOnReceivedHttpAuthRequest",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testOnReceivedHttpError",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testOnReceivedLoginRequest",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testOnScaleChanged",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testShouldOverrideUrlLoading",
+            "_bug_id": "crbug.com/896022"
+          },
+          {
+            "match": "android.webkit.cts.WebViewClientTest#testShouldOverrideUrlLoadingOnCreateWindow",
+            "_bug_id": "crbug.com/896022"
+          }
+        ]
       },
       {
         "apk": "android-cts/repository/testcases/CtsWidgetTestCases.apk",
diff --git a/android_webview/tools/system_webview_shell/apk/res/layout/activity_webview_browser.xml b/android_webview/tools/system_webview_shell/apk/res/layout/activity_webview_browser.xml
index 01f8b29..48dcdd41 100644
--- a/android_webview/tools/system_webview_shell/apk/res/layout/activity_webview_browser.xml
+++ b/android_webview/tools/system_webview_shell/apk/res/layout/activity_webview_browser.xml
@@ -15,7 +15,9 @@
         android:orientation="horizontal"
         android:layout_width="match_parent"
         android:layout_height="wrap_content">
+        <!-- TODO(crbug.com/900912): Fix and remove lint ignore -->
         <EditText
+            tools:ignore="Autofill"
             android:id="@+id/url_field"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
diff --git a/android_webview/tools/system_webview_shell/test/data/webexposed/global-interface-listing-expected.txt b/android_webview/tools/system_webview_shell/test/data/webexposed/global-interface-listing-expected.txt
index b6b480c..fa40462 100644
--- a/android_webview/tools/system_webview_shell/test/data/webexposed/global-interface-listing-expected.txt
+++ b/android_webview/tools/system_webview_shell/test/data/webexposed/global-interface-listing-expected.txt
@@ -3556,6 +3556,7 @@
     getter ondevicechange
     method constructor
     method enumerateDevices
+    method getDisplayMedia
     method getSupportedConstraints
     method getUserMedia
     setter ondevicechange
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 711dce4..ec018be 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -48,6 +48,11 @@
     "frame/wide_frame_view.h",
     "login/ui/lock_window.h",
     "magnifier/magnification_controller.h",
+
+    # TODO: move MultiUserWindowManager (and delegate) to sources:
+    # https://crbug.com/756085
+    "multi_user/multi_user_window_manager.h",
+    "multi_user/multi_user_window_manager_delegate.h",
     "new_window_controller.h",
     "root_window_controller.h",
     "screenshot_delegate.h",
@@ -181,7 +186,6 @@
     "assistant/model/assistant_query.h",
     "assistant/model/assistant_response.cc",
     "assistant/model/assistant_response.h",
-    "assistant/model/assistant_response_observer.h",
     "assistant/model/assistant_screen_context_model.cc",
     "assistant/model/assistant_screen_context_model.h",
     "assistant/model/assistant_screen_context_model_observer.h",
@@ -519,6 +523,9 @@
     "multi_device_setup/multi_device_notification_presenter.h",
     "multi_profile_uma.cc",
     "multi_profile_uma.h",
+    "multi_user/multi_user_window_manager.cc",
+    "multi_user/user_switch_animator.cc",
+    "multi_user/user_switch_animator.h",
     "network_connect_delegate_mus.cc",
     "network_connect_delegate_mus.h",
     "new_window_controller.cc",
@@ -708,8 +715,14 @@
     "system/keyboard_brightness/unified_keyboard_brightness_slider_controller.cc",
     "system/keyboard_brightness/unified_keyboard_brightness_slider_controller.h",
     "system/keyboard_brightness_control_delegate.h",
+    "system/locale/locale_detailed_view.cc",
+    "system/locale/locale_detailed_view.h",
+    "system/locale/locale_feature_pod_controller.cc",
+    "system/locale/locale_feature_pod_controller.h",
     "system/locale/locale_notification_controller.cc",
     "system/locale/locale_notification_controller.h",
+    "system/locale/unified_locale_detailed_view_controller.cc",
+    "system/locale/unified_locale_detailed_view_controller.h",
     "system/message_center/arc_notification_manager_delegate_impl.cc",
     "system/message_center/arc_notification_manager_delegate_impl.h",
     "system/message_center/ash_message_center_lock_screen_controller.cc",
@@ -763,6 +776,8 @@
     "system/network/network_icon_animation.cc",
     "system/network/network_icon_animation.h",
     "system/network/network_icon_animation_observer.h",
+    "system/network/network_icon_purger.cc",
+    "system/network/network_icon_purger.h",
     "system/network/network_info.cc",
     "system/network/network_info.h",
     "system/network/network_list.cc",
@@ -948,8 +963,6 @@
     "system/tray/tri_view.cc",
     "system/tray/tri_view.h",
     "system/tray/view_click_listener.h",
-    "system/tray_drag_controller.cc",
-    "system/tray_drag_controller.h",
     "system/unified/collapse_button.cc",
     "system/unified/collapse_button.h",
     "system/unified/detailed_view_controller.h",
@@ -1743,6 +1756,7 @@
     "system/flag_warning/flag_warning_tray_unittest.cc",
     "system/ime/ime_feature_pod_controller_unittest.cc",
     "system/ime_menu/ime_menu_tray_unittest.cc",
+    "system/locale/locale_feature_pod_controller_unittest.cc",
     "system/message_center/arc/arc_notification_content_view_unittest.cc",
     "system/message_center/arc/arc_notification_manager_unittest.cc",
     "system/message_center/arc/arc_notification_view_unittest.cc",
@@ -1825,6 +1839,7 @@
     "wm/fullscreen_window_finder_unittest.cc",
     "wm/gestures/overview_gesture_handler_unittest.cc",
     "wm/immersive_fullscreen_controller_unittest.cc",
+    "wm/immersive_gesture_drag_handler_unittest.cc",
     "wm/lock_action_handler_layout_manager_unittest.cc",
     "wm/lock_layout_manager_unittest.cc",
     "wm/lock_state_controller_unittest.cc",
@@ -2104,6 +2119,8 @@
     "lock_screen_action/lock_screen_action_background_view_test_api.h",
     "lock_screen_action/test_lock_screen_action_background_controller.cc",
     "lock_screen_action/test_lock_screen_action_background_controller.h",
+    "login/login_screen_test_api.cc",
+    "login/login_screen_test_api.h",
     "metrics/task_switch_time_tracker_test_api.cc",
     "metrics/task_switch_time_tracker_test_api.h",
     "metrics/time_to_first_present_recorder_test_api.cc",
@@ -2194,6 +2211,7 @@
 
   public_deps = [
     "//ash",
+    "//services/service_manager/public/cpp/test:test_support",
     "//testing/gtest",
     "//third_party/blink/public:blink_headers",
     "//ui/display:display_manager_test_api",
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc
index 106d5b1..af9d9d7 100644
--- a/ash/accelerators/accelerator_controller.cc
+++ b/ash/accelerators/accelerator_controller.cc
@@ -70,7 +70,7 @@
 #include "base/stl_util.h"
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/sys_info.h"
+#include "base/system/sys_info.h"
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power_manager_client.h"
diff --git a/ash/accelerators/debug_commands.cc b/ash/accelerators/debug_commands.cc
index 41554aa..d966698 100644
--- a/ash/accelerators/debug_commands.cc
+++ b/ash/accelerators/debug_commands.cc
@@ -115,7 +115,9 @@
 }
 
 void HandleShowQuickLaunch() {
-  Shell::Get()->connector()->StartService(quick_launch::mojom::kServiceName);
+  // TODO(https://crbug.com/904148): This should not use |WarmService()|.
+  Shell::Get()->connector()->WarmService(service_manager::ServiceFilter::ByName(
+      quick_launch::mojom::kServiceName));
 }
 
 gfx::ImageSkia CreateWallpaperImage(SkColor fill, SkColor rect) {
diff --git a/ash/accessibility/accessibility_controller.cc b/ash/accessibility/accessibility_controller.cc
index 3f7d9bf..f579344 100644
--- a/ash/accessibility/accessibility_controller.cc
+++ b/ash/accessibility/accessibility_controller.cc
@@ -263,6 +263,9 @@
                                   static_cast<int>(kDefaultAutoclickEventType));
     registry->RegisterBooleanPref(
         prefs::kAccessibilityAutoclickRevertToLeftClick, true);
+    registry->RegisterIntegerPref(
+        prefs::kAccessibilityAutoclickMovementThreshold,
+        kDefaultAutoclickMovementThreshold);
     registry->RegisterBooleanPref(prefs::kAccessibilityCaretHighlightEnabled,
                                   false);
     registry->RegisterBooleanPref(prefs::kAccessibilityCursorHighlightEnabled,
@@ -309,6 +312,8 @@
   registry->RegisterForeignPref(prefs::kAccessibilityAutoclickEventType);
   registry->RegisterForeignPref(
       prefs::kAccessibilityAutoclickRevertToLeftClick);
+  registry->RegisterForeignPref(
+      prefs::kAccessibilityAutoclickMovementThreshold);
   registry->RegisterForeignPref(prefs::kAccessibilityCaretHighlightEnabled);
   registry->RegisterForeignPref(prefs::kAccessibilityCursorHighlightEnabled);
   registry->RegisterForeignPref(prefs::kAccessibilityDictationEnabled);
@@ -838,6 +843,11 @@
           &AccessibilityController::UpdateAutoclickRevertToLeftClickFromPref,
           base::Unretained(this)));
   pref_change_registrar_->Add(
+      prefs::kAccessibilityAutoclickMovementThreshold,
+      base::BindRepeating(
+          &AccessibilityController::UpdateAutoclickMovementThresholdFromPref,
+          base::Unretained(this)));
+  pref_change_registrar_->Add(
       prefs::kAccessibilityCaretHighlightEnabled,
       base::BindRepeating(
           &AccessibilityController::UpdateCaretHighlightFromPref,
@@ -896,6 +906,7 @@
   UpdateAutoclickDelayFromPref();
   UpdateAutoclickEventTypeFromPref();
   UpdateAutoclickRevertToLeftClickFromPref();
+  UpdateAutoclickMovementThresholdFromPref();
   UpdateCaretHighlightFromPref();
   UpdateCursorHighlightFromPref();
   UpdateDictationFromPref();
@@ -962,6 +973,15 @@
       revert_to_left_click);
 }
 
+void AccessibilityController::UpdateAutoclickMovementThresholdFromPref() {
+  DCHECK(active_user_prefs_);
+  int movement_threshold = active_user_prefs_->GetInteger(
+      prefs::kAccessibilityAutoclickMovementThreshold);
+
+  Shell::Get()->autoclick_controller()->set_movement_threshold(
+      movement_threshold);
+}
+
 void AccessibilityController::UpdateCaretHighlightFromPref() {
   DCHECK(active_user_prefs_);
   const bool enabled = active_user_prefs_->GetBoolean(
diff --git a/ash/accessibility/accessibility_controller.h b/ash/accessibility/accessibility_controller.h
index 2a48336..9b58e97 100644
--- a/ash/accessibility/accessibility_controller.h
+++ b/ash/accessibility/accessibility_controller.h
@@ -192,6 +192,7 @@
   void UpdateAutoclickDelayFromPref();
   void UpdateAutoclickEventTypeFromPref();
   void UpdateAutoclickRevertToLeftClickFromPref();
+  void UpdateAutoclickMovementThresholdFromPref();
   void UpdateCaretHighlightFromPref();
   void UpdateCursorHighlightFromPref();
   void UpdateDictationFromPref();
diff --git a/ash/app_launch_unittest.cc b/ash/app_launch_unittest.cc
index a675e1c..138d22e 100644
--- a/ash/app_launch_unittest.cc
+++ b/ash/app_launch_unittest.cc
@@ -42,11 +42,16 @@
   if (features::IsSingleProcessMash())
     return;
 
-  connector()->StartService(mojom::kServiceName);
-  connector()->StartService(quick_launch::mojom::kServiceName);
+  // TODO(https://crbug.com/904148): These should not use |WarmService()|.
+  connector()->WarmService(
+      service_manager::ServiceFilter::ByName(mojom::kServiceName));
+  connector()->WarmService(service_manager::ServiceFilter::ByName(
+      quick_launch::mojom::kServiceName));
 
   ws::mojom::WindowServerTestPtr test_interface;
-  connector()->BindInterface(ws::mojom::kServiceName, &test_interface);
+  connector()->BindInterface(
+      service_manager::ServiceFilter::ByName(ws::mojom::kServiceName),
+      mojo::MakeRequest(&test_interface));
 
   base::RunLoop run_loop;
   bool success = false;
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index f55ce09..f0805cf 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -17,7 +17,6 @@
 #include "ash/app_list/views/search_box_view.h"
 #include "ash/assistant/assistant_controller.h"
 #include "ash/assistant/assistant_ui_controller.h"
-#include "ash/public/cpp/app_list/answer_card_contents_registry.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/session/session_controller.h"
@@ -42,21 +41,9 @@
 
 AppListControllerImpl::AppListControllerImpl()
     : presenter_(std::make_unique<AppListPresenterDelegateImpl>(this)),
-      is_home_launcher_enabled_(app_list_features::IsHomeLauncherEnabled()),
-      voice_interaction_binding_(this) {
+      is_home_launcher_enabled_(app_list_features::IsHomeLauncherEnabled()) {
   model_.AddObserver(this);
 
-  // Create only for non-mash. Mash uses window tree embed API to get a
-  // token to map answer card contents.
-  //
-  // TODO(https://crbug.com/894987): This is now only used (as a singleton) by
-  // assistant UI code to display its answer card contents. It can be removed
-  // once that code is ported to use Content Service.
-  if (!::features::IsUsingWindowService()) {
-    answer_card_contents_registry_ =
-        std::make_unique<app_list::AnswerCardContentsRegistry>();
-  }
-
   SessionController* session_controller = Shell::Get()->session_controller();
   session_controller->AddObserver(this);
 
@@ -76,17 +63,18 @@
         std::make_unique<HomeLauncherGestureHandler>(this);
   }
 
-  mojom::VoiceInteractionObserverPtr ptr;
-  voice_interaction_binding_.Bind(mojo::MakeRequest(&ptr));
-  Shell::Get()->voice_interaction_controller()->AddObserver(std::move(ptr));
+  Shell::Get()->voice_interaction_controller()->AddLocalObserver(this);
+  Shell::Get()->window_tree_host_manager()->AddObserver(this);
 }
 
 AppListControllerImpl::~AppListControllerImpl() {
+  Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
   keyboard::KeyboardController::Get()->RemoveObserver(this);
   Shell::Get()->RemoveShellObserver(this);
   Shell::Get()->wallpaper_controller()->RemoveObserver(this);
   Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
   Shell::Get()->session_controller()->RemoveObserver(this);
+  Shell::Get()->voice_interaction_controller()->RemoveLocalObserver(this);
   model_.RemoveObserver(this);
 }
 
@@ -547,15 +535,8 @@
   if (!is_home_launcher_enabled_)
     return;
 
-  SessionController const* session_controller =
-      Shell::Get()->session_controller();
-  if (session_controller && !session_controller->IsActiveUserSessionStarted())
-    return;
-
   // Show the app list if the tablet mode starts.
-  Show(GetDisplayIdToShowAppListOn(), app_list::kTabletMode, base::TimeTicks());
-  UpdateHomeLauncherVisibility();
-  Shelf::ForWindow(presenter_.GetWindow())->MaybeUpdateShelfBackground();
+  ShowHomeLauncher();
 }
 
 void AppListControllerImpl::OnTabletModeEnded() {
@@ -600,6 +581,27 @@
   UpdateAssistantVisibility();
 }
 
+void AppListControllerImpl::OnDisplayConfigurationChanged() {
+  // Entering tablet mode triggers a display configuration change when we
+  // automatically switch to mirror mode. Switching to mirror mode happens
+  // asynchronously (see DisplayConfigurationObserver::OnTabletModeStarted()).
+  // This may result in the removal of a window tree host, as in the example of
+  // switching to tablet mode while Unified Desktop mode is on; the Unified host
+  // will be destroyed and the Home Launcher (which was created earlier when we
+  // entered tablet mode) will be dismissed.
+  // 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();
+  if (should_be_shown == GetTargetVisibility())
+    return;
+
+  if (should_be_shown)
+    ShowHomeLauncher();
+  else
+    DismissAppList();
+}
+
 bool AppListControllerImpl::IsHomeLauncherEnabledInTabletMode() const {
   return is_home_launcher_enabled_ && Shell::Get()
                                           ->tablet_mode_controller()
@@ -807,8 +809,7 @@
       return home_launcher_gesture_handler_->OnScrollEvent(
           screen_location, event->details().scroll_y());
     case ui::ET_GESTURE_END:
-      return home_launcher_gesture_handler_->OnReleaseEvent(
-          screen_location, /*out_dragged_down=*/nullptr);
+      return home_launcher_gesture_handler_->OnReleaseEvent(screen_location);
     default:
       break;
   }
@@ -945,7 +946,8 @@
 }
 
 int64_t AppListControllerImpl::GetDisplayIdToShowAppListOn() {
-  if (IsHomeLauncherEnabledInTabletMode()) {
+  if (IsHomeLauncherEnabledInTabletMode() &&
+      !Shell::Get()->display_manager()->IsInUnifiedMode()) {
     return display::Display::HasInternalDisplay()
                ? display::Display::InternalDisplayId()
                : display::Screen::GetScreen()->GetPrimaryDisplay().id();
@@ -956,4 +958,15 @@
       .id();
 }
 
+void AppListControllerImpl::ShowHomeLauncher() {
+  DCHECK(IsHomeLauncherEnabledInTabletMode());
+
+  if (!Shell::Get()->session_controller()->IsActiveUserSessionStarted())
+    return;
+
+  Show(GetDisplayIdToShowAppListOn(), app_list::kTabletMode, base::TimeTicks());
+  UpdateHomeLauncherVisibility();
+  Shelf::ForWindow(presenter_.GetWindow())->MaybeUpdateShelfBackground();
+}
+
 }  // namespace ash
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h
index 732e544..fa3f52b 100644
--- a/ash/app_list/app_list_controller_impl.h
+++ b/ash/app_list/app_list_controller_impl.h
@@ -16,6 +16,7 @@
 #include "ash/app_list/model/search/search_model.h"
 #include "ash/app_list/presenter/app_list_presenter_impl.h"
 #include "ash/ash_export.h"
+#include "ash/display/window_tree_host_manager.h"
 #include "ash/public/cpp/app_list/app_list_constants.h"
 #include "ash/public/cpp/assistant/default_voice_interaction_observer.h"
 #include "ash/public/interfaces/app_list.mojom.h"
@@ -29,10 +30,6 @@
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
 #include "ui/keyboard/keyboard_controller_observer.h"
 
-namespace app_list {
-class AnswerCardContentsRegistry;
-}  // namespace app_list
-
 namespace ui {
 class MouseWheelEvent;
 }  // namespace ui
@@ -53,7 +50,8 @@
       public TabletModeObserver,
       public keyboard::KeyboardControllerObserver,
       public WallpaperControllerObserver,
-      public DefaultVoiceInteractionObserver {
+      public DefaultVoiceInteractionObserver,
+      public WindowTreeHostManager::Observer {
  public:
   using AppListItemMetadataPtr = mojom::AppListItemMetadataPtr;
   using SearchResultMetadataPtr = mojom::SearchResultMetadataPtr;
@@ -213,6 +211,9 @@
   void OnAssistantFeatureAllowedChanged(
       mojom::AssistantAllowedState state) override;
 
+  // WindowTreeHostManager::Observer:
+  void OnDisplayConfigurationChanged() override;
+
   bool onscreen_keyboard_shown() const { return onscreen_keyboard_shown_; }
 
   // Returns true if the home launcher is enabled in tablet mode.
@@ -245,6 +246,9 @@
 
   int64_t GetDisplayIdToShowAppListOn();
 
+  // Shows the home launcher in tablet mode.
+  void ShowHomeLauncher();
+
   base::string16 last_raw_query_;
 
   mojom::AppListClientPtr client_;
@@ -259,12 +263,6 @@
   // Bindings for the AppListController interface.
   mojo::BindingSet<mojom::AppListController> bindings_;
 
-  // TODO(https://crbug.com/894987): Remove this once assistant UI is converted
-  // to use Content Service, as there will then be no more consumers of
-  // AnswerCardContentsRegistry.
-  std::unique_ptr<app_list::AnswerCardContentsRegistry>
-      answer_card_contents_registry_;
-
   // Owned pointer to the object which handles gestures related to the home
   // launcher.
   std::unique_ptr<HomeLauncherGestureHandler> home_launcher_gesture_handler_;
@@ -287,8 +285,6 @@
   // Whether we're currently in a window dragging process.
   bool in_window_dragging_ = false;
 
-  mojo::Binding<mojom::VoiceInteractionObserver> voice_interaction_binding_;
-
   DISALLOW_COPY_AND_ASSIGN(AppListControllerImpl);
 };
 
diff --git a/ash/app_list/app_list_interactive_uitest.cc b/ash/app_list/app_list_interactive_uitest.cc
index dae9bca..7f9768a 100644
--- a/ash/app_list/app_list_interactive_uitest.cc
+++ b/ash/app_list/app_list_interactive_uitest.cc
@@ -32,7 +32,7 @@
 
   aura::Window* app_list_container =
       root_window->GetChildById(ash::kShellWindowId_AppListContainer);
-  ui::test::EventGenerator generator(shelf_widget->GetNativeWindow());
+  ui::test::EventGenerator generator(root_window);
 
   // Click the app list button to show the app list.
   ash::Shell* shell = ash::Shell::Get();
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index 72a5fe1..3617781 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -48,7 +48,7 @@
 #include "ui/display/test/display_manager_test_api.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/keyboard/keyboard_controller.h"
-#include "ui/keyboard/keyboard_switches.h"
+#include "ui/keyboard/public/keyboard_switches.h"
 #include "ui/keyboard/test/keyboard_test_util.h"
 #include "ui/views/controls/textfield/textfield.h"
 
diff --git a/ash/app_list/home_launcher_gesture_handler.cc b/ash/app_list/home_launcher_gesture_handler.cc
index 53129af..7575ff0 100644
--- a/ash/app_list/home_launcher_gesture_handler.cc
+++ b/ash/app_list/home_launcher_gesture_handler.cc
@@ -74,6 +74,7 @@
   if (window->type() == aura::client::WINDOW_TYPE_POPUP)
     return false;
 
+  // Do not process if |window| is not the root of a transient tree.
   if (::wm::GetTransientParent(window))
     return false;
 
@@ -181,7 +182,6 @@
     return false;
 
   mode_ = mode;
-  initial_event_location_ = location;
   last_event_location_ = base::make_optional(location);
 
   UpdateWindows(0.0, /*animate=*/false);
@@ -198,11 +198,6 @@
 
   last_event_location_ = base::make_optional(location);
   last_scroll_y_ = scroll_y;
-  if (mode_ == Mode::kSlideUpToShow &&
-      (*last_event_location_ - initial_event_location_).y() > 0) {
-    UpdateWindows(0.0, /*animate=*/false);
-    return true;
-  }
 
   DCHECK(display_.is_valid());
   UpdateWindows(GetHeightInWorkAreaAsRatio(location, display_.work_area()),
@@ -210,8 +205,7 @@
   return true;
 }
 
-bool HomeLauncherGestureHandler::OnReleaseEvent(const gfx::Point& location,
-                                                bool* out_dragged_down) {
+bool HomeLauncherGestureHandler::OnReleaseEvent(const gfx::Point& location) {
   if (IsAnimating())
     return false;
 
@@ -228,11 +222,6 @@
   }
 
   last_event_location_ = base::make_optional(location);
-  if (out_dragged_down) {
-    DCHECK_EQ(mode_, Mode::kSlideUpToShow);
-    *out_dragged_down =
-        (*last_event_location_ - initial_event_location_).y() > 0;
-  }
   AnimateToFinalState();
   return true;
 }
@@ -578,6 +567,14 @@
       return true;
   }
 
+  if (Shell::Get()->window_selector_controller()->IsSelecting() &&
+      Shell::Get()
+          ->window_selector_controller()
+          ->window_selector()
+          ->IsWindowGridAnimating()) {
+    return true;
+  }
+
   return false;
 }
 
diff --git a/ash/app_list/home_launcher_gesture_handler.h b/ash/app_list/home_launcher_gesture_handler.h
index 112d6736..f0ec847 100644
--- a/ash/app_list/home_launcher_gesture_handler.h
+++ b/ash/app_list/home_launcher_gesture_handler.h
@@ -53,7 +53,7 @@
   // was not processed.
   bool OnPressEvent(Mode mode, const gfx::Point& location);
   bool OnScrollEvent(const gfx::Point& location, float scroll_y);
-  bool OnReleaseEvent(const gfx::Point& location, bool* out_dragged_down);
+  bool OnReleaseEvent(const gfx::Point& location);
 
   // Cancel a current drag and animates the items to their final state based on
   // |last_event_location_|.
@@ -158,8 +158,6 @@
   // hidden so the home launcher is visible when swiping up.
   std::vector<aura::Window*> hidden_windows_;
 
-  gfx::Point initial_event_location_;
-
   // Tracks the location of the last received event in screen coordinates. Empty
   // if there is currently no window being processed.
   base::Optional<gfx::Point> last_event_location_;
diff --git a/ash/app_list/home_launcher_gesture_handler_unittest.cc b/ash/app_list/home_launcher_gesture_handler_unittest.cc
index 962b98c..6c5e3b0 100644
--- a/ash/app_list/home_launcher_gesture_handler_unittest.cc
+++ b/ash/app_list/home_launcher_gesture_handler_unittest.cc
@@ -141,14 +141,14 @@
   // Tests that flinging down in this mode will keep the window visible.
   DoPress(Mode::kSlideUpToShow);
   GetGestureHandler()->OnScrollEvent(gfx::Point(0, 300), 10.f);
-  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 300), nullptr);
+  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 300));
   ASSERT_TRUE(window->IsVisible());
 
   // Tests that flinging up in this mode will hide the window and show the
   // home launcher.
   DoPress(Mode::kSlideUpToShow);
   GetGestureHandler()->OnScrollEvent(gfx::Point(0, 300), -10.f);
-  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 300), nullptr);
+  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 300));
   EXPECT_FALSE(window->IsVisible());
 }
 
@@ -164,13 +164,13 @@
   // Tests that flinging up in this mode will not show the mru window.
   DoPress(Mode::kSlideDownToHide);
   GetGestureHandler()->OnScrollEvent(gfx::Point(0, 100), -10.f);
-  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 100), nullptr);
+  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 100));
   ASSERT_FALSE(window->IsVisible());
 
   // Tests that flinging down in this mode will show the mru window.
   DoPress(Mode::kSlideDownToHide);
   GetGestureHandler()->OnScrollEvent(gfx::Point(0, 100), 10.f);
-  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 100), nullptr);
+  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 100));
   EXPECT_TRUE(window->IsVisible());
 }
 
@@ -185,12 +185,6 @@
   GetGestureHandler()->OnPressEvent(Mode::kSlideUpToShow, gfx::Point(0, 400));
   GetGestureHandler()->OnScrollEvent(gfx::Point(0, 420), 1.f);
   EXPECT_EQ(gfx::Transform(), window->transform());
-
-  // Tests that OnReleaseEvent returns true when checking if the release point
-  // is below the press point.
-  bool released_below;
-  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 420), &released_below);
-  EXPECT_TRUE(released_below);
 }
 
 // Tests that the home launcher gestures work with overview mode as expected.
@@ -221,7 +215,7 @@
 
   // Tests that after releasing at below the halfway point, we remain in
   // overview mode.
-  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 300), nullptr);
+  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 300));
   EXPECT_TRUE(controller->IsSelecting());
   EXPECT_EQ(window1_initial_translation,
             window1->transform().To2dTranslation().y());
@@ -231,7 +225,7 @@
   // Tests that after releasing on the bottom half, overview mode has been
   // exited, and the two windows have been minimized to show the home launcher.
   DoPress(Mode::kSlideUpToShow);
-  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 100), nullptr);
+  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 100));
   EXPECT_FALSE(controller->IsSelecting());
   EXPECT_TRUE(wm::GetWindowState(window1.get())->IsMinimized());
   EXPECT_TRUE(wm::GetWindowState(window2.get())->IsMinimized());
@@ -268,7 +262,7 @@
 
   // Tests that after releasing at below the halfway point, we remain in
   // both splitview and overview mode.
-  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 300), nullptr);
+  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 300));
   EXPECT_EQ(window1->transform(), gfx::Transform());
   EXPECT_EQ(window2_initial_translation,
             window2->transform().To2dTranslation().y());
@@ -278,7 +272,7 @@
   // Tests that after releasing on the bottom half, overivew and splitview have
   // both been exited, and both windows are minimized to show the home launcher.
   DoPress(Mode::kSlideUpToShow);
-  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 100), nullptr);
+  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 100));
   EXPECT_FALSE(window_selector_controller->IsSelecting());
   EXPECT_FALSE(split_view_controller->IsSplitViewModeActive());
   EXPECT_TRUE(wm::GetWindowState(window1.get())->IsMinimized());
@@ -314,7 +308,7 @@
 
   // Tests that after releasing at below the halfway point, we remain in
   // splitview.
-  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 300), nullptr);
+  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 300));
   EXPECT_EQ(window1->transform(), gfx::Transform());
   EXPECT_EQ(window2->transform(), gfx::Transform());
   EXPECT_TRUE(split_view_controller->IsSplitViewModeActive());
@@ -322,7 +316,7 @@
   // Tests that after releasing on the bottom half, splitview has been ended,
   // and the two windows have been minimized to show the home launcher.
   DoPress(Mode::kSlideUpToShow);
-  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 100), nullptr);
+  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 100));
   EXPECT_FALSE(split_view_controller->IsSplitViewModeActive());
   EXPECT_TRUE(wm::GetWindowState(window1.get())->IsMinimized());
   EXPECT_TRUE(wm::GetWindowState(window2.get())->IsMinimized());
@@ -399,7 +393,7 @@
   EXPECT_NE(1.f, window1->layer()->opacity());
 
   // Tests the transform and opacity have returned to the identity and 1.
-  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 300), nullptr);
+  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 300));
   EXPECT_EQ(gfx::Transform(), window1->transform());
   EXPECT_EQ(1.f, window1->layer()->opacity());
 
@@ -426,7 +420,7 @@
   ASSERT_FALSE(window3->IsVisible());
 
   // Test that |window1| is minimized on release.
-  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 100), nullptr);
+  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 100));
   EXPECT_TRUE(wm::GetWindowState(window1.get())->IsMinimized());
 
   // The rest of the windows remain invisible, to show the home launcher.
@@ -460,7 +454,7 @@
 
   // Tests that after releasing on the bottom half, the transient child reverts
   // to its original values.
-  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 300), nullptr);
+  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 300));
   EXPECT_EQ(1.0f, child->layer()->opacity());
   EXPECT_EQ(gfx::Transform(), child->transform());
 }
@@ -514,7 +508,7 @@
   EXPECT_FALSE(GetGestureHandler()->transient_descendants_values_.empty());
 
   // Tests that after a drag, the variables are either null or empty.
-  GetGestureHandler()->OnReleaseEvent(gfx::Point(10, 10), nullptr);
+  GetGestureHandler()->OnReleaseEvent(gfx::Point(10, 10));
   EXPECT_FALSE(GetGestureHandler()->window());
   EXPECT_FALSE(GetGestureHandler()->last_event_location_);
   EXPECT_EQ(Mode::kNone, GetGestureHandler()->mode_);
diff --git a/ash/app_list/model/app_list_model_unittest.cc b/ash/app_list/model/app_list_model_unittest.cc
index 526c393..92acbea 100644
--- a/ash/app_list/model/app_list_model_unittest.cc
+++ b/ash/app_list/model/app_list_model_unittest.cc
@@ -220,18 +220,7 @@
   }
 }
 
-class AppListModelFolderTest : public AppListModelTest {
- public:
-  AppListModelFolderTest() {}
-  ~AppListModelFolderTest() override {}
-
-  // testing::Test overrides:
-  void SetUp() override { AppListModelTest::SetUp(); }
-  void TearDown() override { AppListModelTest::TearDown(); }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(AppListModelFolderTest);
-};
+using AppListModelFolderTest = AppListModelTest;
 
 TEST_F(AppListModelFolderTest, FolderItem) {
   AppListFolderItem* folder = new AppListFolderItem("folder1");
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 ad5b74f..fba8e6a 100644
--- a/ash/app_list/presenter/app_list_presenter_impl_unittest.cc
+++ b/ash/app_list/presenter/app_list_presenter_impl_unittest.cc
@@ -216,7 +216,7 @@
 
   // 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);
+  ui::test::EventGenerator menu_event_generator(menu->GetRootWindow());
   menu_event_generator.set_current_location(menu->GetBoundsInScreen().origin());
   menu_event_generator.PressLeftButton();
 
diff --git a/ash/app_list/views/app_list_item_view.cc b/ash/app_list/views/app_list_item_view.cc
index e183387..3fad61f 100644
--- a/ash/app_list/views/app_list_item_view.cc
+++ b/ash/app_list/views/app_list_item_view.cc
@@ -381,8 +381,11 @@
 }
 
 void AppListItemView::CancelContextMenu() {
-  if (context_menu_)
-    context_menu_->Cancel();
+  if (!context_menu_)
+    return;
+
+  menu_close_initiated_from_drag_ = true;
+  context_menu_->Cancel();
 }
 
 void AppListItemView::OnDragEnded() {
@@ -555,7 +558,7 @@
                           flags);
   }
 
-  int preview_circle_radius = GetPreviewCircleRadius();
+  const int preview_circle_radius = GetPreviewCircleRadius();
   if (!preview_circle_radius)
     return;
 
@@ -799,6 +802,14 @@
 }
 
 void AppListItemView::OnMenuClosed() {
+  if (!menu_close_initiated_from_drag_) {
+    // If the menu was not closed due to a drag sequence(e.g. multi touch) reset
+    // the drag state.
+    SetState(STATE_NORMAL);
+    SetTouchDragging(false);
+  }
+
+  menu_close_initiated_from_drag_ = false;
   OnBlur();
 }
 
@@ -841,6 +852,7 @@
   SetUIState(UI_STATE_DRAGGING);
 }
 
+// static
 gfx::Rect AppListItemView::GetIconBoundsForTargetViewBounds(
     const gfx::Rect& target_bounds,
     const gfx::Size& icon_size) {
@@ -850,6 +862,7 @@
   return rect;
 }
 
+// static
 gfx::Rect AppListItemView::GetTitleBoundsForTargetViewBounds(
     const gfx::Rect& target_bounds,
     const gfx::Size& title_size) {
@@ -861,6 +874,7 @@
   return rect;
 }
 
+// static
 gfx::Rect AppListItemView::GetProgressBarBoundsForTargetViewBounds(
     const gfx::Rect& target_bounds,
     const gfx::Size& progress_bar_size) {
diff --git a/ash/app_list/views/app_list_item_view.h b/ash/app_list/views/app_list_item_view.h
index 6586ef0..261ef06 100644
--- a/ash/app_list/views/app_list_item_view.h
+++ b/ash/app_list/views/app_list_item_view.h
@@ -260,6 +260,10 @@
   bool is_installing_ = false;
   bool is_highlighted_ = false;
 
+  // Whether |context_menu_| was cancelled as the result of a continuous drag
+  // gesture.
+  bool menu_close_initiated_from_drag_ = false;
+
   base::string16 tooltip_text_;
 
   // A timer to defer showing drag UI when mouse is pressed.
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index 08a1cf6..9ca94f4 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -738,6 +738,16 @@
                                  drag_source_bounds);
   }
 
+  if (!cancel) {
+    // Select the page where dragged item is dropped. Avoid doing so when the
+    // dragged item ends up in a folder.
+    const int model_index = GetModelIndexOfItem(drag_item);
+    if (model_index < view_model_.view_size()) {
+      pagination_model_.SelectPage(GetIndexFromModelIndex(model_index).page,
+                                   false /* animate */);
+    }
+  }
+
   StopPageFlipTimer();
 }
 
@@ -2201,9 +2211,6 @@
   view_model_.Move(current_model_index, target_model_index);
   item_list_->AddObserver(this);
 
-  if (pagination_model_.selected_page() != target.page)
-    pagination_model_.SelectPage(target.page, false);
-
   RecordAppMovingTypeMetrics(folder_delegate_ ? kReorderInFolder
                                               : kReorderInTopLevel);
 }
diff --git a/ash/app_list/views/folder_header_view.cc b/ash/app_list/views/folder_header_view.cc
index fa92920..7665d4f 100644
--- a/ash/app_list/views/folder_header_view.cc
+++ b/ash/app_list/views/folder_header_view.cc
@@ -69,18 +69,20 @@
               IDS_APP_LIST_FOLDER_NAME_PLACEHOLDER)),
       delegate_(delegate),
       folder_name_visible_(true) {
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  gfx::FontList font_list = rb.GetFontList(ui::ResourceBundle::MediumFont);
   folder_name_view_->set_placeholder_text_color(kFolderTitleHintTextColor);
   folder_name_view_->set_placeholder_text(folder_name_placeholder_text_);
   folder_name_view_->SetBorder(views::NullBorder());
 
   // Make folder name font size 14px.
-  folder_name_view_->SetFontList(font_list.DeriveWithSizeDelta(-1));
+  folder_name_view_->SetFontList(
+      ui::ResourceBundle::GetSharedInstance().GetFontListWithDelta(2));
   folder_name_view_->SetBackgroundColor(SK_ColorTRANSPARENT);
   folder_name_view_->SetTextColor(kFolderNameColor);
   folder_name_view_->set_controller(this);
   AddChildView(folder_name_view_);
+
+  SetPaintToLayer();
+  layer()->SetFillsBoundsOpaquely(false);
 }
 
 FolderHeaderView::~FolderHeaderView() {
diff --git a/ash/app_menu/notification_menu_view_unittest.cc b/ash/app_menu/notification_menu_view_unittest.cc
index 00ce257..3098a15 100644
--- a/ash/app_menu/notification_menu_view_unittest.cc
+++ b/ash/app_menu/notification_menu_view_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "ash/app_menu/notification_item_view.h"
 #include "ash/app_menu/notification_menu_view_test_api.h"
+#include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -18,6 +19,7 @@
 #include "ui/views/test/views_test_base.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget_delegate.h"
+#include "ui/views/widget/widget_utils.h"
 
 namespace ash {
 
@@ -173,7 +175,7 @@
 
   void DispatchGesture(const ui::GestureEventDetails& details) {
     ui::test::EventGenerator generator(
-        notification_menu_view_->GetWidget()->GetNativeWindow());
+        GetRootWindow(notification_menu_view_->GetWidget()));
 
     ui::GestureEvent event(
         0,
@@ -346,6 +348,7 @@
   EXPECT_EQ(-200.f, GetSlideAmount());
   // Release the gesture, the notification should slide out.
   EndScroll();
+  base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, mock_notification_menu_controller()->slide_out_count_);
   EXPECT_EQ(0, mock_notification_menu_controller()->activation_count_);
 }
diff --git a/ash/ash_service_unittest.cc b/ash/ash_service_unittest.cc
index 67a0c2c..67e32c4 100644
--- a/ash/ash_service_unittest.cc
+++ b/ash/ash_service_unittest.cc
@@ -105,8 +105,6 @@
 
   WindowTreeClientDelegate window_tree_delegate;
 
-  connector()->StartService(mojom::kServiceName);
-
   // Connect to mus and create a new top level window. The request goes to
   // |ash|, but is async.
   std::unique_ptr<aura::WindowTreeClient> client =
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 639b3d0..c4e3cb41 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -543,6 +543,15 @@
       <message name="IDS_ASH_STATUS_TRAY_NOTIFICATIONS_DO_NOT_DISTURB_SUBLABEL" desc="The sub label text shown under do not disturb button when all notifications are disabled.">
         Do not disturb
       </message>
+      <message name="IDS_ASH_STATUS_TRAY_LOCALE" desc="The label used for locale button in system tray bubble.">
+        Language
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_LOCALE_TITLE" desc="The title shown in the locale detailed view.">
+        Languages
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_LOCALE_TOOLTIP" desc="The tooltip text in the tray to show the locale menu.">
+        Show language settings
+      </message>
 
       <!-- Status tray supervised users. -->
       <message name="IDS_ASH_STATUS_TRAY_SUPERVISED_LABEL" desc="Label shown instead of email for supervised users">
@@ -1449,6 +1458,9 @@
       <message name="IDS_ASH_LOGIN_POD_MENU_REMOVE_ITEM_ACCESSIBLE_NAME" desc="Text to be spoken when 'Remove user' item is selected on the pod menu.">
         Remove this user
       </message>
+      <message name="IDS_ASH_LOGIN_POD_LEGACY_SUPERVISED_EXPIRATION_WARNING" desc="Text shown as a warning that supervised users will be deprecated.">
+        Your supervised user account is expiring soon.
+      </message>
       <message name="IDS_ASH_LOGIN_POD_LEGACY_SUPERVISED_USER_REMOVE_WARNING" desc="Text shown as a warning when attempting to remove legacy supervised user.">
         All files and local data associated with the supervised user will be permanently deleted once this supervised user is removed. Visited websites and settings for this supervised user may still be visible by the manager at <ph name="MANAGEMENT_URL">$1<ex>www.example.com</ex></ph>.
       </message>
@@ -1462,7 +1474,7 @@
         Managed by <ph name="DOMAIN">$1<ex>yourdomain.com</ex></ph>
       </message>
       <message name="IDS_ASH_LOGIN_PUBLIC_ACCOUNT_MONITORING_WARNING" desc="Template for text shown in the public account user pod, informing the user that this is a public, managed account.">
-        The administrator of this device has access to all activity, including passwords and communications.
+        The device admin may monitor your browsing activity.
       </message>
       <message name="IDS_ASH_LOGIN_PUBLIC_ACCOUNT_SIGNOUT_REMINDER" desc="Text shown in the public account user pod, reminding the user to log out.">
         Your internet session will be cleared when you sign out. <ph name="LEARN_MORE">$1<ex>Learn more</ex></ph>
diff --git a/ash/ash_strings_grd/IDS_ASH_LOGIN_POD_LEGACY_SUPERVISED_EXPIRATION_WARNING.png.sha1 b/ash/ash_strings_grd/IDS_ASH_LOGIN_POD_LEGACY_SUPERVISED_EXPIRATION_WARNING.png.sha1
new file mode 100644
index 0000000..fc3c1d0
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_LOGIN_POD_LEGACY_SUPERVISED_EXPIRATION_WARNING.png.sha1
@@ -0,0 +1 @@
+09c28d1841db0b6658f032506e3f42b4a63c6c40
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_LOCALE_TOOLTIP.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_LOCALE_TOOLTIP.png.sha1
new file mode 100644
index 0000000..57bbb79
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_LOCALE_TOOLTIP.png.sha1
@@ -0,0 +1 @@
+488707bc8fbcee8f16adab3eea0910f58cf4e505
\ No newline at end of file
diff --git a/ash/assistant/assistant_cache_controller.cc b/ash/assistant/assistant_cache_controller.cc
index 03f2beb..99490be 100644
--- a/ash/assistant/assistant_cache_controller.cc
+++ b/ash/assistant/assistant_cache_controller.cc
@@ -63,20 +63,15 @@
 
 AssistantCacheController::AssistantCacheController(
     AssistantController* assistant_controller)
-    : assistant_controller_(assistant_controller),
-      voice_interaction_binding_(this) {
+    : assistant_controller_(assistant_controller) {
   UpdateConversationStarters();
-
   assistant_controller_->AddObserver(this);
-
-  // Bind to observe changes to screen context preference.
-  mojom::VoiceInteractionObserverPtr ptr;
-  voice_interaction_binding_.Bind(mojo::MakeRequest(&ptr));
-  Shell::Get()->voice_interaction_controller()->AddObserver(std::move(ptr));
+  Shell::Get()->voice_interaction_controller()->AddLocalObserver(this);
 }
 
 AssistantCacheController::~AssistantCacheController() {
   assistant_controller_->RemoveObserver(this);
+  Shell::Get()->voice_interaction_controller()->RemoveLocalObserver(this);
 }
 
 void AssistantCacheController::AddModelObserver(
diff --git a/ash/assistant/assistant_cache_controller.h b/ash/assistant/assistant_cache_controller.h
index 3864adc..c675be1 100644
--- a/ash/assistant/assistant_cache_controller.h
+++ b/ash/assistant/assistant_cache_controller.h
@@ -11,7 +11,6 @@
 #include "ash/public/cpp/assistant/default_voice_interaction_observer.h"
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "base/macros.h"
-#include "mojo/public/cpp/bindings/binding.h"
 
 namespace ash {
 
@@ -49,8 +48,6 @@
 
   AssistantController* const assistant_controller_;  // Owned by Shell.
 
-  mojo::Binding<mojom::VoiceInteractionObserver> voice_interaction_binding_;
-
   AssistantCacheModel model_;
 
   DISALLOW_COPY_AND_ASSIGN(AssistantCacheController);
diff --git a/ash/assistant/assistant_controller.cc b/ash/assistant/assistant_controller.cc
index ed3f1a1..6b8ebc5 100644
--- a/ash/assistant/assistant_controller.cc
+++ b/ash/assistant/assistant_controller.cc
@@ -23,7 +23,8 @@
 #include "ash/voice_interaction/voice_interaction_controller.h"
 #include "base/bind.h"
 #include "base/memory/scoped_refptr.h"
-#include "base/unguessable_token.h"
+#include "services/content/public/mojom/constants.mojom.h"
+#include "services/service_manager/public/cpp/connector.h"
 
 namespace ash {
 
@@ -40,11 +41,8 @@
       assistant_setup_controller_(
           std::make_unique<AssistantSetupController>(this)),
       assistant_ui_controller_(std::make_unique<AssistantUiController>(this)),
-      voice_interaction_binding_(this),
       weak_factory_(this) {
-  mojom::VoiceInteractionObserverPtr ptr;
-  voice_interaction_binding_.Bind(mojo::MakeRequest(&ptr));
-  Shell::Get()->voice_interaction_controller()->AddObserver(std::move(ptr));
+  Shell::Get()->voice_interaction_controller()->AddLocalObserver(this);
   chromeos::CrasAudioHandler::Get()->AddAudioObserver(this);
   AddObserver(this);
 
@@ -56,6 +54,7 @@
 
   chromeos::CrasAudioHandler::Get()->RemoveAudioObserver(this);
   Shell::Get()->accessibility_controller()->RemoveObserver(this);
+  Shell::Get()->voice_interaction_controller()->RemoveLocalObserver(this);
   RemoveObserver(this);
 }
 
@@ -108,11 +107,6 @@
   assistant_setup_controller_->SetAssistantSetup(assistant_setup_.get());
 }
 
-void AssistantController::SetWebContentsManager(
-    mojom::WebContentsManagerPtr web_contents_manager) {
-  web_contents_manager_ = std::move(web_contents_manager);
-}
-
 // TODO(dmblack): Expose AssistantScreenContextController over mojo rather
 // than implementing RequestScreenshot here in AssistantController.
 void AssistantController::RequestScreenshot(
@@ -127,50 +121,6 @@
   OpenUrl(assistant::util::CreateAssistantSettingsDeepLink());
 }
 
-void AssistantController::ManageWebContents(
-    const base::UnguessableToken& id_token,
-    mojom::ManagedWebContentsParamsPtr params,
-    mojom::WebContentsManager::ManageWebContentsCallback callback) {
-  DCHECK(web_contents_manager_);
-
-  const mojom::UserSession* user_session =
-      Shell::Get()->session_controller()->GetUserSession(0);
-
-  if (!user_session) {
-    LOG(WARNING) << "Unable to retrieve active user session.";
-    std::move(callback).Run(base::nullopt);
-    return;
-  }
-
-  // Supply account ID.
-  params->account_id = user_session->user_info->account_id;
-
-  // Specify that we will handle top level browser requests.
-  ash::mojom::ManagedWebContentsOpenUrlDelegatePtr ptr;
-  web_contents_open_url_delegate_bindings_.AddBinding(this,
-                                                      mojo::MakeRequest(&ptr));
-  params->open_url_delegate_ptr_info = ptr.PassInterface();
-
-  web_contents_manager_->ManageWebContents(id_token, std::move(params),
-                                           std::move(callback));
-}
-
-void AssistantController::ReleaseWebContents(
-    const base::UnguessableToken& id_token) {
-  web_contents_manager_->ReleaseWebContents(id_token);
-}
-
-void AssistantController::ReleaseWebContents(
-    const std::vector<base::UnguessableToken>& id_tokens) {
-  web_contents_manager_->ReleaseAllWebContents(id_tokens);
-}
-
-void AssistantController::NavigateWebContentsBack(
-    const base::UnguessableToken& id_token,
-    mojom::WebContentsManager::NavigateWebContentsBackCallback callback) {
-  web_contents_manager_->NavigateWebContentsBack(id_token, std::move(callback));
-}
-
 void AssistantController::DownloadImage(
     const GURL& url,
     mojom::AssistantImageDownloader::DownloadCallback callback) {
@@ -229,34 +179,6 @@
   }
 }
 
-void AssistantController::ShouldOpenUrlFromTab(
-    const GURL& url,
-    WindowOpenDisposition disposition,
-    ash::mojom::ManagedWebContentsOpenUrlDelegate::ShouldOpenUrlFromTabCallback
-        callback) {
-  // We always handle deep links ourselves.
-  if (assistant::util::IsDeepLinkUrl(url)) {
-    std::move(callback).Run(/*should_open=*/false);
-    NotifyDeepLinkReceived(url);
-    return;
-  }
-
-  AssistantUiMode ui_mode = assistant_ui_controller_->model()->ui_mode();
-
-  // When in main UI mode, WebContents should not navigate as they are hosting
-  // Assistant cards. Instead, we route navigation attempts to the browser. We
-  // also respect open |disposition| to launch in the browser if appropriate.
-  if (ui_mode == AssistantUiMode::kMainUi ||
-      disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB) {
-    std::move(callback).Run(/*should_open=*/false);
-    OpenUrl(url);
-    return;
-  }
-
-  // In all other cases WebContents should be able to navigate freely.
-  std::move(callback).Run(/*should_open=*/true);
-}
-
 void AssistantController::SetVolume(int volume, bool user_initiated) {
   volume = std::min(100, volume);
   volume = std::max(volume, 0);
@@ -311,6 +233,29 @@
   NotifyUrlOpened(url, from_server);
 }
 
+void AssistantController::GetNavigableContentsFactory(
+    content::mojom::NavigableContentsFactoryRequest request) {
+  const mojom::UserSession* user_session =
+      Shell::Get()->session_controller()->GetUserSession(0);
+
+  if (!user_session) {
+    LOG(WARNING) << "Unable to retrieve active user session.";
+    return;
+  }
+
+  const base::Optional<base::Token>& service_instance_group =
+      user_session->user_info->service_instance_group;
+  if (!service_instance_group) {
+    LOG(ERROR) << "Unable to retrieve service instance group.";
+    return;
+  }
+
+  Shell::Get()->connector()->BindInterface(
+      service_manager::ServiceFilter::ByNameInGroup(
+          content::mojom::kServiceName, *service_instance_group),
+      std::move(request));
+}
+
 void AssistantController::NotifyConstructed() {
   for (AssistantControllerObserver& observer : observers_)
     observer.OnAssistantControllerConstructed();
diff --git a/ash/assistant/assistant_controller.h b/ash/assistant/assistant_controller.h
index 73d81cf..d08a335 100644
--- a/ash/assistant/assistant_controller.h
+++ b/ash/assistant/assistant_controller.h
@@ -19,7 +19,6 @@
 #include "ash/public/interfaces/assistant_setup.mojom.h"
 #include "ash/public/interfaces/assistant_volume_control.mojom.h"
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
-#include "ash/public/interfaces/web_contents_manager.mojom.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
@@ -27,12 +26,9 @@
 #include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
 #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 base {
-class UnguessableToken;
-}  // namespace base
-
 namespace ash {
 
 class AssistantCacheController;
@@ -45,7 +41,6 @@
 class ASH_EXPORT AssistantController
     : public mojom::AssistantController,
       public AssistantControllerObserver,
-      public mojom::ManagedWebContentsOpenUrlDelegate,
       public DefaultVoiceInteractionObserver,
       public mojom::AssistantVolumeControl,
       public chromeos::CrasAudioHandler::AudioObserver,
@@ -61,30 +56,6 @@
   void AddObserver(AssistantControllerObserver* observer);
   void RemoveObserver(AssistantControllerObserver* observer);
 
-  // Requests that WebContents, uniquely identified by |id_token|, be created
-  // and managed according to the specified |params|. When the WebContents is
-  // ready for embedding, the supplied |callback| is run with an embed token. In
-  // the event that an error occurs, the supplied callback will still be run but
-  // no embed token will be provided.
-  void ManageWebContents(
-      const base::UnguessableToken& id_token,
-      mojom::ManagedWebContentsParamsPtr params,
-      mojom::WebContentsManager::ManageWebContentsCallback callback);
-
-  // Releases resources for the WebContents uniquely identified by |id_token|.
-  void ReleaseWebContents(const base::UnguessableToken& id_token);
-
-  // Releases resources for any WebContents uniquely identified in
-  // |id_token_list|.
-  void ReleaseWebContents(const std::vector<base::UnguessableToken>& id_tokens);
-
-  // Navigates the WebContents uniquely identified by |id_token| back relative
-  // to the current history entry. The supplied |callback| will run specifying
-  // true if navigation occurred, false otherwise.
-  void NavigateWebContentsBack(
-      const base::UnguessableToken& id_token,
-      mojom::WebContentsManager::NavigateWebContentsBackCallback callback);
-
   // Downloads the image found at the specified |url|. On completion, the
   // supplied |callback| will be run with the downloaded image. If the download
   // attempt is unsuccessful, a NULL image is returned.
@@ -101,8 +72,6 @@
   void SetAssistantImageDownloader(
       mojom::AssistantImageDownloaderPtr assistant_image_downloader) override;
   void SetAssistantSetup(mojom::AssistantSetupPtr assistant_setup) override;
-  void SetWebContentsManager(
-      mojom::WebContentsManagerPtr web_contents_manager) override;
   void RequestScreenshot(const gfx::Rect& rect,
                          RequestScreenshotCallback callback) override;
   void OpenAssistantSettings() override;
@@ -112,13 +81,6 @@
       assistant::util::DeepLinkType type,
       const std::map<std::string, std::string>& params) override;
 
-  // mojom::ManagedWebContentsOpenUrlDelegate:
-  void ShouldOpenUrlFromTab(
-      const GURL& url,
-      WindowOpenDisposition disposition,
-      mojom::ManagedWebContentsOpenUrlDelegate::ShouldOpenUrlFromTabCallback
-          callback) override;
-
   // mojom::VolumeControl:
   void SetVolume(int volume, bool user_initiated) override;
   void SetMuted(bool muted) override;
@@ -135,6 +97,11 @@
   // to deep links which may cause deviation from this behavior.
   void OpenUrl(const GURL& url, bool from_server = false);
 
+  // Acquires a NavigableContentsFactory from the Content Service to allow
+  // Assistant to display embedded web contents.
+  void GetNavigableContentsFactory(
+      content::mojom::NavigableContentsFactoryRequest request);
+
   AssistantCacheController* cache_controller() {
     DCHECK(assistant_cache_controller_);
     return assistant_cache_controller_.get();
@@ -183,9 +150,6 @@
 
   mojo::BindingSet<mojom::AssistantController> assistant_controller_bindings_;
 
-  mojo::BindingSet<mojom::ManagedWebContentsOpenUrlDelegate>
-      web_contents_open_url_delegate_bindings_;
-
   mojo::Binding<mojom::AssistantVolumeControl>
       assistant_volume_control_binding_;
   mojo::InterfacePtrSet<mojom::VolumeObserver> volume_observer_;
@@ -196,8 +160,6 @@
 
   mojom::AssistantSetupPtr assistant_setup_;
 
-  mojom::WebContentsManagerPtr web_contents_manager_;
-
   std::unique_ptr<AssistantCacheController> assistant_cache_controller_;
 
   std::unique_ptr<AssistantInteractionController>
@@ -213,8 +175,6 @@
 
   std::unique_ptr<AssistantUiController> assistant_ui_controller_;
 
-  mojo::Binding<mojom::VoiceInteractionObserver> voice_interaction_binding_;
-
   base::WeakPtrFactory<AssistantController> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(AssistantController);
diff --git a/ash/assistant/assistant_interaction_controller.cc b/ash/assistant/assistant_interaction_controller.cc
index 96f6c24..bf0ac72 100644
--- a/ash/assistant/assistant_interaction_controller.cc
+++ b/ash/assistant/assistant_interaction_controller.cc
@@ -243,25 +243,6 @@
       assistant_controller_->ui_controller()->model()->entry_point());
 }
 
-void AssistantInteractionController::OnResponseDestroying(
-    AssistantResponse& response) {
-  response.RemoveObserver(this);
-
-  // We need to explicitly clean up resources owned by WebContentsManager for
-  // any card elements belonging to the response being destroyed.
-  std::vector<base::UnguessableToken> id_tokens;
-  for (const auto& ui_element : response.GetUiElements()) {
-    if (ui_element->GetType() == AssistantUiElementType::kCard) {
-      id_tokens.push_back(
-          static_cast<const AssistantCardElement*>(ui_element.get())
-              ->id_token());
-    }
-  }
-
-  if (!id_tokens.empty())
-    assistant_controller_->ReleaseWebContents(id_tokens);
-}
-
 void AssistantInteractionController::OnInteractionStarted(
     bool is_voice_interaction) {
   if (is_voice_interaction) {
@@ -299,10 +280,8 @@
     model_.SetMicState(MicState::kClosed);
   }
 
-  // Start caching a new Assistant response for the interaction. We observe the
-  // response so that we can receive notification of lifecycle change events.
+  // Start caching a new Assistant response for the interaction.
   model_.SetPendingResponse(std::make_unique<AssistantResponse>());
-  model_.pending_response()->AddObserver(this);
 }
 
 void AssistantInteractionController::OnInteractionFinished(
diff --git a/ash/assistant/assistant_interaction_controller.h b/ash/assistant/assistant_interaction_controller.h
index d1b8b86..72e7606 100644
--- a/ash/assistant/assistant_interaction_controller.h
+++ b/ash/assistant/assistant_interaction_controller.h
@@ -14,7 +14,6 @@
 #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_response_observer.h"
 #include "ash/assistant/model/assistant_ui_model_observer.h"
 #include "ash/assistant/ui/dialog_plate/dialog_plate.h"
 #include "ash/highlighter/highlighter_controller.h"
@@ -33,7 +32,6 @@
     : public chromeos::assistant::mojom::AssistantInteractionSubscriber,
       public AssistantControllerObserver,
       public AssistantInteractionModelObserver,
-      public AssistantResponseObserver,
       public AssistantUiModelObserver,
       public HighlighterController::Observer,
       public DialogPlateObserver {
@@ -72,9 +70,6 @@
   void OnResponseChanged(
       const std::shared_ptr<AssistantResponse>& response) override;
 
-  // AssistantResponseObserver:
-  void OnResponseDestroying(AssistantResponse& response) override;
-
   // AssistantUiModelObserver:
   void OnUiModeChanged(AssistantUiMode ui_mode) override;
   void OnUiVisibilityChanged(AssistantVisibility new_visibility,
diff --git a/ash/assistant/assistant_response_processor.cc b/ash/assistant/assistant_response_processor.cc
index abf3d1d..6963f58 100644
--- a/ash/assistant/assistant_response_processor.cc
+++ b/ash/assistant/assistant_response_processor.cc
@@ -4,11 +4,14 @@
 
 #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 {
 
@@ -19,6 +22,56 @@
 
 }  // 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,
@@ -57,8 +110,12 @@
   for (const auto& ui_element : response.GetUiElements()) {
     switch (ui_element->GetType()) {
       case AssistantUiElementType::kCard:
-        ProcessCardElement(
-            static_cast<AssistantCardElement*>(ui_element.get()));
+        // 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.
@@ -71,49 +128,20 @@
   TryFinishingTask();
 }
 
-void AssistantResponseProcessor::ProcessCardElement(
-    AssistantCardElement* card_element) {
-  // Encode the card HTML using base64.
-  std::string encoded_html;
-  base::Base64Encode(card_element->html(), &encoded_html);
-
-  // TODO(dmblack): Find a better way of determining desired card size.
-  const int width_dip = kPreferredWidthDip - 2 * kUiElementHorizontalMarginDip;
-
-  // Configure parameters for the card.
-  ash::mojom::ManagedWebContentsParamsPtr params(
-      ash::mojom::ManagedWebContentsParams::New());
-  params->url = GURL(kDataUriPrefix + encoded_html);
-  params->min_size_dip = gfx::Size(width_dip, 0);
-  params->max_size_dip = gfx::Size(width_dip, INT_MAX);
-
-  // Request an embed token for the card whose WebContents will be owned by
-  // WebContentsManager.
-  assistant_controller_->ManageWebContents(
-      card_element->id_token(), std::move(params),
-      base::BindOnce(&AssistantResponseProcessor::OnCardElementProcessed,
-                     weak_factory_.GetWeakPtr(), card_element));
-
-  // Increment |processing_count| to reflect the fact that a card element is
-  // being processed asynchronously.
-  ++task_->processing_count;
-}
-
-void AssistantResponseProcessor::OnCardElementProcessed(
-    AssistantCardElement* card_element,
-    const base::Optional<base::UnguessableToken>& embed_token) {
+void AssistantResponseProcessor::DidFinishProcessing(
+    const AssistantCardProcessor* card_processor) {
   // If the response has been invalidated we should abort early.
   if (!task_->response) {
     TryAbortingTask();
     return;
   }
 
-  // Save the |embed_token|.
-  card_element->set_embed_token(embed_token);
-
-  // Decrement |processing_count| to reflect the fact that a card element has
-  // finished being processed asynchronously.
-  --task_->processing_count;
+  // 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.
@@ -135,7 +163,7 @@
 
 void AssistantResponseProcessor::TryFinishingTask() {
   // This method is a no-op if we are still processing.
-  if (task_->processing_count > 0)
+  if (!task_->element_processors.empty())
     return;
 
   // Update processing state.
diff --git a/ash/assistant/assistant_response_processor.h b/ash/assistant/assistant_response_processor.h
index ae76975e..05b757a 100644
--- a/ash/assistant/assistant_response_processor.h
+++ b/ash/assistant/assistant_response_processor.h
@@ -14,6 +14,7 @@
 #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 {
@@ -21,8 +22,42 @@
 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 {
@@ -39,6 +74,9 @@
   // 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.
@@ -53,8 +91,10 @@
     // Callback to be run on task abort/completion.
     ProcessCallback callback;
 
-    // Count of UI elements that are being asynchronously processed.
-    int processing_count = 0;
+    // 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|.
diff --git a/ash/assistant/model/assistant_response.cc b/ash/assistant/model/assistant_response.cc
index e755773..4ee2d76 100644
--- a/ash/assistant/model/assistant_response.cc
+++ b/ash/assistant/model/assistant_response.cc
@@ -4,24 +4,13 @@
 
 #include "ash/assistant/model/assistant_response.h"
 
-#include "ash/assistant/model/assistant_response_observer.h"
 #include "ash/assistant/model/assistant_ui_element.h"
 
 namespace ash {
 
 AssistantResponse::AssistantResponse() : weak_factory_(this) {}
 
-AssistantResponse::~AssistantResponse() {
-  NotifyDestroying();
-}
-
-void AssistantResponse::AddObserver(AssistantResponseObserver* observer) {
-  observers_.AddObserver(observer);
-}
-
-void AssistantResponse::RemoveObserver(AssistantResponseObserver* observer) {
-  observers_.RemoveObserver(observer);
-}
+AssistantResponse::~AssistantResponse() = default;
 
 void AssistantResponse::AddUiElement(
     std::unique_ptr<AssistantUiElement> ui_element) {
@@ -65,9 +54,4 @@
   return weak_factory_.GetWeakPtr();
 }
 
-void AssistantResponse::NotifyDestroying() {
-  for (auto& observer : observers_)
-    observer.OnResponseDestroying(*this);
-}
-
 }  // 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 6a8cba7..de47078 100644
--- a/ash/assistant/model/assistant_response.h
+++ b/ash/assistant/model/assistant_response.h
@@ -11,12 +11,10 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/observer_list.h"
 #include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
 
 namespace ash {
 
-class AssistantResponseObserver;
 class AssistantUiElement;
 
 // Models a renderable Assistant response.
@@ -35,10 +33,6 @@
   AssistantResponse();
   ~AssistantResponse();
 
-  // Adds/removes the specified |observer|.
-  void AddObserver(AssistantResponseObserver* observer);
-  void RemoveObserver(AssistantResponseObserver* observer);
-
   // Adds the specified |ui_element| that should be rendered for the
   // interaction.
   void AddUiElement(std::unique_ptr<AssistantUiElement> ui_element);
@@ -71,15 +65,11 @@
   base::WeakPtr<AssistantResponse> GetWeakPtr();
 
  private:
-  void NotifyDestroying();
-
   std::vector<std::unique_ptr<AssistantUiElement>> ui_elements_;
   std::vector<AssistantSuggestionPtr> suggestions_;
   ProcessingState processing_state_ = ProcessingState::kUnprocessed;
   bool has_tts_ = false;
 
-  base::ObserverList<AssistantResponseObserver>::Unchecked observers_;
-
   base::WeakPtrFactory<AssistantResponse> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(AssistantResponse);
diff --git a/ash/assistant/model/assistant_response_observer.h b/ash/assistant/model/assistant_response_observer.h
deleted file mode 100644
index 4439018e..0000000
--- a/ash/assistant/model/assistant_response_observer.h
+++ /dev/null
@@ -1,29 +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_MODEL_ASSISTANT_RESPONSE_OBSERVER_H_
-#define ASH_ASSISTANT_MODEL_ASSISTANT_RESPONSE_OBSERVER_H_
-
-#include "base/macros.h"
-
-namespace ash {
-
-class AssistantResponse;
-
-// An observer which receives notification of changes to an Assistant response.
-class AssistantResponseObserver {
- public:
-  // Invoked when the specified |response| is being destroyed.
-  virtual void OnResponseDestroying(AssistantResponse& response) {}
-
- protected:
-  AssistantResponseObserver() = default;
-  virtual ~AssistantResponseObserver() = default;
-
-  DISALLOW_COPY_AND_ASSIGN(AssistantResponseObserver);
-};
-
-}  // namespace ash
-
-#endif  // ASH_ASSISTANT_MODEL_ASSISTANT_RESPONSE_OBSERVER_H_
diff --git a/ash/assistant/model/assistant_ui_element.cc b/ash/assistant/model/assistant_ui_element.cc
index 8c5d3fd..24fbe6e 100644
--- a/ash/assistant/model/assistant_ui_element.cc
+++ b/ash/assistant/model/assistant_ui_element.cc
@@ -12,8 +12,7 @@
                                            const std::string& fallback)
     : AssistantUiElement(AssistantUiElementType::kCard),
       html_(html),
-      fallback_(fallback),
-      id_token_(base::UnguessableToken::Create()) {}
+      fallback_(fallback) {}
 
 AssistantCardElement::~AssistantCardElement() = default;
 
diff --git a/ash/assistant/model/assistant_ui_element.h b/ash/assistant/model/assistant_ui_element.h
index 67c9df2..5492a35 100644
--- a/ash/assistant/model/assistant_ui_element.h
+++ b/ash/assistant/model/assistant_ui_element.h
@@ -5,11 +5,11 @@
 #ifndef ASH_ASSISTANT_MODEL_ASSISTANT_UI_ELEMENT_H_
 #define ASH_ASSISTANT_MODEL_ASSISTANT_UI_ELEMENT_H_
 
+#include <memory>
 #include <string>
 
 #include "base/macros.h"
-#include "base/optional.h"
-#include "base/unguessable_token.h"
+#include "services/content/public/cpp/navigable_contents.h"
 
 namespace ash {
 
@@ -52,22 +52,18 @@
 
   const std::string& fallback() const { return fallback_; }
 
-  const base::UnguessableToken& id_token() const { return id_token_; }
+  const content::NavigableContents* contents() const { return contents_.get(); }
+  content::NavigableContents* contents() { return contents_.get(); }
 
-  const base::Optional<base::UnguessableToken>& embed_token() const {
-    return embed_token_;
-  }
-
-  void set_embed_token(
-      const base::Optional<base::UnguessableToken>& embed_token) {
-    embed_token_ = embed_token;
+  void set_contents(std::unique_ptr<content::NavigableContents> contents) {
+    contents_ = std::move(contents);
   }
 
  private:
   const std::string html_;
   const std::string fallback_;
-  base::UnguessableToken id_token_;
-  base::Optional<base::UnguessableToken> embed_token_ = base::nullopt;
+
+  std::unique_ptr<content::NavigableContents> contents_;
 
   DISALLOW_COPY_AND_ASSIGN(AssistantCardElement);
 };
diff --git a/ash/assistant/ui/assistant_web_view.cc b/ash/assistant/ui/assistant_web_view.cc
index 3655afa..d8a09b4 100644
--- a/ash/assistant/ui/assistant_web_view.cc
+++ b/ash/assistant/ui/assistant_web_view.cc
@@ -11,10 +11,8 @@
 #include "ash/assistant/assistant_ui_controller.h"
 #include "ash/assistant/ui/assistant_ui_constants.h"
 #include "ash/assistant/util/deep_link_util.h"
-#include "ash/public/cpp/app_list/answer_card_contents_registry.h"
-#include "ash/public/interfaces/web_contents_manager.mojom.h"
 #include "base/callback.h"
-#include "base/unguessable_token.h"
+#include "services/content/public/cpp/navigable_contents_view.h"
 #include "ui/aura/window.h"
 #include "ui/compositor/layer.h"
 #include "ui/display/display.h"
@@ -28,12 +26,12 @@
 
 namespace {
 
-// ContentViewMaskPainter -------------------------------------------------
+// ContentsMaskPainter ---------------------------------------------------------
 
-class ContentViewMaskPainter : public views::Painter {
+class ContentsMaskPainter : public views::Painter {
  public:
-  ContentViewMaskPainter() = default;
-  ~ContentViewMaskPainter() override = default;
+  ContentsMaskPainter() = default;
+  ~ContentsMaskPainter() override = default;
 
   // views::Painter:
   gfx::Size GetMinimumSize() const override { return gfx::Size(); }
@@ -56,7 +54,7 @@
   }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(ContentViewMaskPainter);
+  DISALLOW_COPY_AND_ASSIGN(ContentsMaskPainter);
 };
 
 }  // namespace
@@ -64,8 +62,7 @@
 // AssistantWebView ------------------------------------------------------------
 
 AssistantWebView::AssistantWebView(AssistantController* assistant_controller)
-    : assistant_controller_(assistant_controller),
-      web_contents_request_factory_(this) {
+    : assistant_controller_(assistant_controller), weak_factory_(this) {
   InitLayout();
 
   assistant_controller_->AddObserver(this);
@@ -73,7 +70,8 @@
 
 AssistantWebView::~AssistantWebView() {
   assistant_controller_->RemoveObserver(this);
-  ReleaseWebContents();
+
+  RemoveContents();
 }
 
 const char* AssistantWebView::GetClassName() const {
@@ -101,38 +99,19 @@
   SchedulePaint();
 }
 
-void AssistantWebView::OnViewIsDeleting(views::View* view) {
-  DCHECK_EQ(content_view_, view);
-
-  // It's possible for |content_view_| to be deleted before AssistantWebView.
-  // When this happens, we need to perform clean up on |content_view_| before
-  // it is destroyed and clear references so that we don't try to perform
-  // clean up on the destroyed instance when destroying AssistantWebView.
-  content_view_->RemoveObserver(this);
-  content_view_ = nullptr;
-}
-
 void AssistantWebView::OnWindowBoundsChanged(aura::Window* window,
                                              const gfx::Rect& old_bounds,
                                              const gfx::Rect& new_bounds,
                                              ui::PropertyChangeReason reason) {
-  DCHECK_EQ(native_content_view_, window);
-
-  // The mask layer should always match the bounds of the native content view.
-  content_view_mask_->layer()->SetBounds(gfx::Rect(new_bounds.size()));
+  // The mask layer should always match the bounds of the contents' native view.
+  contents_mask_->layer()->SetBounds(gfx::Rect(new_bounds.size()));
 }
 
 void AssistantWebView::OnWindowDestroying(aura::Window* window) {
-  DCHECK_EQ(native_content_view_, window);
-
-  // It's possible for |native_content_view_| to be deleted before
-  // AssistantWebView. When this happens, we need to perform clean up on
-  // |native_content_view_| before it is destroyed and clear references so that
-  // we don't try to perform clean up on the destroyed instance when destroying
-  // AssistantWebView.
-  native_content_view_->RemoveObserver(this);
-  native_content_view_->layer()->SetMaskLayer(nullptr);
-  native_content_view_ = nullptr;
+  // It's possible for |window| to be deleted before AssistantWebView. When this
+  // happens, we need to perform clean up on |window| before it is destroyed.
+  window->RemoveObserver(this);
+  window->layer()->SetMaskLayer(nullptr);
 }
 
 void AssistantWebView::InitLayout() {
@@ -145,36 +124,33 @@
   caption_bar_->SetButtonVisible(CaptionButtonId::kMinimize, false);
   AddChildView(caption_bar_);
 
-  // Content mask.
-  // This is used to enforce corner radius on the contents' layer.
-  content_view_mask_ = views::Painter::CreatePaintedLayer(
-      std::make_unique<ContentViewMaskPainter>());
-  content_view_mask_->layer()->SetFillsBoundsOpaquely(false);
+  // Contents mask.
+  // This is used to enforce corner radius on the contents' native view layer.
+  contents_mask_ = views::Painter::CreatePaintedLayer(
+      std::make_unique<ContentsMaskPainter>());
+  contents_mask_->layer()->SetFillsBoundsOpaquely(false);
 }
 
 bool AssistantWebView::OnCaptionButtonPressed(CaptionButtonId id) {
-  CaptionBarDelegate* delegate = assistant_controller_->ui_controller();
-
   // We need special handling of the back button. When possible, the back button
-  // should navigate backwards in the managed WebContents' history stack.
-  if (id == CaptionButtonId::kBack && web_contents_id_token_.has_value()) {
-    assistant_controller_->NavigateWebContentsBack(
-        web_contents_id_token_.value(),
-        base::BindOnce(
-            [](CaptionBarDelegate* delegate, bool navigated) {
-              // If the WebContents' did not navigate it is because we are
-              // already at the first entry in the history stack and we cannot
-              // navigate back any further. In this case, we give back control
-              // to our primary caption button delegate.
-              if (!navigated)
-                delegate->OnCaptionButtonPressed(CaptionButtonId::kBack);
-            },
-            base::Unretained(delegate)));
+  // should navigate backwards in the web contents' history stack.
+  if (id == CaptionButtonId::kBack && contents_) {
+    contents_->GoBack(base::BindOnce(
+        [](const base::WeakPtr<AssistantWebView>& assistant_web_view,
+           bool success) {
+          // If we can't navigate back in the web contents' history stack we
+          // defer back to our primary caption button delegate.
+          if (!success && assistant_web_view) {
+            assistant_web_view->assistant_controller_->ui_controller()
+                ->OnCaptionButtonPressed(CaptionButtonId::kBack);
+          }
+        },
+        weak_factory_.GetWeakPtr()));
     return true;
   }
 
   // For all other buttons we defer to our primary caption button delegate.
-  return delegate->OnCaptionButtonPressed(id);
+  return assistant_controller_->ui_controller()->OnCaptionButtonPressed(id);
 }
 
 void AssistantWebView::OnDeepLinkReceived(
@@ -183,86 +159,88 @@
   if (!assistant::util::IsWebDeepLinkType(type))
     return;
 
-  ReleaseWebContents();
+  RemoveContents();
 
-  web_contents_id_token_ = base::UnguessableToken::Create();
+  assistant_controller_->GetNavigableContentsFactory(
+      mojo::MakeRequest(&contents_factory_));
 
-  gfx::Size preferred_size_dip =
+  const gfx::Size preferred_size =
       gfx::Size(kPreferredWidthDip,
                 kMaxHeightDip - caption_bar_->GetPreferredSize().height());
 
-  ash::mojom::ManagedWebContentsParamsPtr web_contents_params(
-      ash::mojom::ManagedWebContentsParams::New());
-  web_contents_params->url = GetWebUrl(type).value();
-  web_contents_params->min_size_dip = preferred_size_dip;
-  web_contents_params->max_size_dip = preferred_size_dip;
+  auto contents_params = content::mojom::NavigableContentsParams::New();
+  contents_params->enable_view_auto_resize = true;
+  contents_params->auto_resize_min_size = preferred_size;
+  contents_params->auto_resize_max_size = preferred_size;
+  contents_params->suppress_navigations = true;
 
-  assistant_controller_->ManageWebContents(
-      web_contents_id_token_.value(), std::move(web_contents_params),
-      base::BindOnce(&AssistantWebView::OnWebContentsReady,
-                     web_contents_request_factory_.GetWeakPtr()));
+  contents_ = std::make_unique<content::NavigableContents>(
+      contents_factory_.get(), std::move(contents_params));
+
+  // We observe |contents_| so that we can handle events from the underlying
+  // web contents.
+  contents_->AddObserver(this);
+
+  // Navigate to the url associated with the received deep link.
+  contents_->Navigate(assistant::util::GetWebUrl(type).value());
 }
 
-void AssistantWebView::OnWebContentsReady(
-    const base::Optional<base::UnguessableToken>& embed_token) {
-  // TODO(dmblack): In the event that rendering fails, we should show a useful
-  // message to the user (and perhaps close the UI).
-  if (!embed_token.has_value())
-    return;
-
-  // When web contents are rendered in process, the WebView associated with the
-  // returned |embed_token| is found in the AnswerCardContentsRegistry.
-  if (app_list::AnswerCardContentsRegistry::Get()) {
-    content_view_ = app_list::AnswerCardContentsRegistry::Get()->GetView(
-        embed_token.value());
-    content_view_->AddObserver(this);
-
-    // Cache a reference to the content's native view and observe it so that
-    // we can monitor changes to bounds and lifecycle.
-    native_content_view_ =
-        app_list::AnswerCardContentsRegistry::Get()->GetNativeView(
-            embed_token.value());
-    native_content_view_->AddObserver(this);
-
-    // Apply a mask layer to enforce corner radius. The mask bounds must always
-    // match the bounds of |native_content_view_|. We sync bounds prior to
-    // applying the mask to prevent a DCHECK failure in cc::Layer.
-    content_view_mask_->layer()->SetBounds(
-        gfx::Rect(native_content_view_->GetBoundsInScreen().size()));
-    native_content_view_->layer()->SetMaskLayer(content_view_mask_->layer());
-
-    AddChildView(content_view_);
-  }
-
-  // TODO(dmblack): Handle mash case.
+void AssistantWebView::DidAutoResizeView(const gfx::Size& new_size) {
+  contents_->GetView()->view()->SetPreferredSize(new_size);
 }
 
-void AssistantWebView::ReleaseWebContents() {
-  web_contents_request_factory_.InvalidateWeakPtrs();
+void AssistantWebView::DidStopLoading() {
+  AddChildView(contents_->GetView()->view());
 
-  if (!web_contents_id_token_.has_value())
+  gfx::NativeView native_view = contents_->GetView()->native_view();
+
+  // We apply a layer mask to the contents' native view to enforce our desired
+  // corner radius. We need to sync the bounds of mask layer with the bounds
+  // of the native view prior to application to prevent DCHECK failure.
+  contents_mask_->layer()->SetBounds(
+      gfx::Rect(native_view->GetBoundsInScreen().size()));
+  native_view->layer()->SetMaskLayer(contents_mask_->layer());
+
+  // We observe |native_view| to ensure we keep the mask layer bounds in sync
+  // with the native view layer's bounds across size changes.
+  native_view->AddObserver(this);
+}
+
+void AssistantWebView::DidSuppressNavigation(const GURL& url,
+                                             WindowOpenDisposition disposition,
+                                             bool from_user_gesture) {
+  if (!from_user_gesture)
     return;
 
-  if (content_view_) {
-    content_view_->RemoveObserver(this);
-    RemoveChildView(content_view_);
-
-    // In Mash, |content_view_| was owned by the view hierarchy prior to its
-    // removal. Otherwise the view is owned by the WebContentsManager.
-    if (!content_view_->owned_by_client())
-      delete content_view_;
-
-    content_view_ = nullptr;
+  // Deep links are always handled by AssistantController. If the |disposition|
+  // indicates a desire to open a new foreground tab, we also defer to the
+  // AssistantController so that it can open the |url| in the browser.
+  if (assistant::util::IsDeepLinkUrl(url) ||
+      disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB) {
+    assistant_controller_->OpenUrl(url);
+    return;
   }
 
-  if (native_content_view_) {
-    native_content_view_->RemoveObserver(this);
-    native_content_view_->layer()->SetMaskLayer(nullptr);
-    native_content_view_ = nullptr;
+  // Otherwise we'll allow our web contents to navigate freely.
+  contents_->Navigate(url);
+}
+
+void AssistantWebView::RemoveContents() {
+  if (!contents_)
+    return;
+
+  gfx::NativeView native_view = contents_->GetView()->native_view();
+  if (native_view) {
+    native_view->RemoveObserver(this);
+    native_view->layer()->SetMaskLayer(nullptr);
   }
 
-  assistant_controller_->ReleaseWebContents(web_contents_id_token_.value());
-  web_contents_id_token_.reset();
+  views::View* view = contents_->GetView()->view();
+  if (view)
+    RemoveChildView(view);
+
+  contents_->RemoveObserver(this);
+  contents_.reset();
 }
 
 }  // namespace ash
diff --git a/ash/assistant/ui/assistant_web_view.h b/ash/assistant/ui/assistant_web_view.h
index 3d5f4a7..338f047 100644
--- a/ash/assistant/ui/assistant_web_view.h
+++ b/ash/assistant/ui/assistant_web_view.h
@@ -13,13 +13,10 @@
 #include "ash/assistant/ui/caption_bar.h"
 #include "base/macros.h"
 #include "base/optional.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/content/public/cpp/navigable_contents.h"
 #include "ui/aura/window_observer.h"
 #include "ui/views/view.h"
-#include "ui/views/view_observer.h"
-
-namespace base {
-class UnguessableToken;
-}  // namespace base
 
 namespace ash {
 
@@ -27,13 +24,13 @@
 
 // AssistantWebView is a child of AssistantBubbleView which allows Assistant UI
 // to render remotely hosted content within its bubble. It provides a CaptionBar
-// for window level controls and a WebView/ServerRemoteViewHost for embedding
-// web contents.
+// for window level controls and embeds web contents with help from the Content
+// Service.
 class AssistantWebView : public views::View,
-                         public views::ViewObserver,
                          public aura::WindowObserver,
                          public AssistantControllerObserver,
-                         public CaptionBarDelegate {
+                         public CaptionBarDelegate,
+                         public content::NavigableContentsObserver {
  public:
   explicit AssistantWebView(AssistantController* assistant_controller);
   ~AssistantWebView() override;
@@ -44,9 +41,6 @@
   int GetHeightForWidth(int width) const override;
   void ChildPreferredSizeChanged(views::View* child) override;
 
-  // views::ViewObserver:
-  void OnViewIsDeleting(views::View* view) override;
-
   // views::WindowObserver:
   void OnWindowBoundsChanged(aura::Window* window,
                              const gfx::Rect& old_bounds,
@@ -62,32 +56,31 @@
       assistant::util::DeepLinkType type,
       const std::map<std::string, std::string>& params) override;
 
+  // content::NavigableContentsObserver:
+  void DidAutoResizeView(const gfx::Size& new_size) override;
+  void DidStopLoading() override;
+  void DidSuppressNavigation(const GURL& url,
+                             WindowOpenDisposition disposition,
+                             bool from_user_gesture) override;
+
  private:
   void InitLayout();
-  void OnWebContentsReady(
-      const base::Optional<base::UnguessableToken>& embed_token);
-  void ReleaseWebContents();
+  void RemoveContents();
 
   AssistantController* const assistant_controller_;  // Owned by Shell.
 
   CaptionBar* caption_bar_;  // Owned by view hierarchy.
 
-  // In Mash, |content_view_| is owned by the view hierarchy. Otherwise, the
-  // view is owned by the WebContentsManager.
-  views::View* content_view_ = nullptr;
-  gfx::NativeView native_content_view_ = nullptr;
+  content::mojom::NavigableContentsFactoryPtr contents_factory_;
+  std::unique_ptr<content::NavigableContents> contents_;
 
   // Our contents are drawn to a layer that is not masked by our widget's layer.
   // This causes our contents to ignore the corner radius that we have set on
   // the widget. To address this, we apply a separate layer mask to the
-  // contents' layer enforcing our desired corner radius.
-  std::unique_ptr<ui::LayerOwner> content_view_mask_;
+  // contents' native view layer enforcing our desired corner radius.
+  std::unique_ptr<ui::LayerOwner> contents_mask_;
 
-  // Uniquely identifies web contents owned by WebContentsManager.
-  base::Optional<base::UnguessableToken> web_contents_id_token_;
-
-  // Weak pointer factory used for web contents rendering requests.
-  base::WeakPtrFactory<AssistantWebView> web_contents_request_factory_;
+  base::WeakPtrFactory<AssistantWebView> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(AssistantWebView);
 };
diff --git a/ash/assistant/ui/caption_bar.cc b/ash/assistant/ui/caption_bar.cc
index 01b1861..1261e6e 100644
--- a/ash/assistant/ui/caption_bar.cc
+++ b/ash/assistant/ui/caption_bar.cc
@@ -13,7 +13,6 @@
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/strings/grit/ui_strings.h"
-#include "ui/views/background.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/widget/widget.h"
@@ -24,7 +23,7 @@
 
 // Appearance.
 constexpr int kCaptionButtonSizeDip = 32;
-constexpr int kPreferredHeightDip = 32;
+constexpr int kPreferredHeightDip = 48;
 constexpr int kVectorIconSizeDip = 12;
 
 // CaptionButton ---------------------------------------------------------------
diff --git a/ash/assistant/ui/main_stage/assistant_footer_view.cc b/ash/assistant/ui/main_stage/assistant_footer_view.cc
index 8c198c0..645d5b7 100644
--- a/ash/assistant/ui/main_stage/assistant_footer_view.cc
+++ b/ash/assistant/ui/main_stage/assistant_footer_view.cc
@@ -47,17 +47,14 @@
               base::Unretained(this)),
           /*animation_ended_callback=*/base::BindRepeating(
               &AssistantFooterView::OnAnimationEnded,
-              base::Unretained(this)))),
-      voice_interaction_observer_binding_(this) {
+              base::Unretained(this)))) {
   InitLayout();
-
-  // Observe voice interaction for changes to consent state.
-  mojom::VoiceInteractionObserverPtr ptr;
-  voice_interaction_observer_binding_.Bind(mojo::MakeRequest(&ptr));
-  Shell::Get()->voice_interaction_controller()->AddObserver(std::move(ptr));
+  Shell::Get()->voice_interaction_controller()->AddLocalObserver(this);
 }
 
-AssistantFooterView::~AssistantFooterView() = default;
+AssistantFooterView::~AssistantFooterView() {
+  Shell::Get()->voice_interaction_controller()->RemoveLocalObserver(this);
+}
 
 const char* AssistantFooterView::GetClassName() const {
   return "AssistantFooterView";
diff --git a/ash/assistant/ui/main_stage/assistant_footer_view.h b/ash/assistant/ui/main_stage/assistant_footer_view.h
index 8a02eac..3e31051 100644
--- a/ash/assistant/ui/main_stage/assistant_footer_view.h
+++ b/ash/assistant/ui/main_stage/assistant_footer_view.h
@@ -11,7 +11,6 @@
 #include "ash/public/cpp/assistant/default_voice_interaction_observer.h"
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "base/macros.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
 #include "ui/views/view.h"
 
 namespace ui {
@@ -51,9 +50,6 @@
 
   std::unique_ptr<ui::CallbackLayerAnimationObserver> animation_observer_;
 
-  mojo::Binding<mojom::VoiceInteractionObserver>
-      voice_interaction_observer_binding_;
-
   DISALLOW_COPY_AND_ASSIGN(AssistantFooterView);
 };
 
diff --git a/ash/assistant/ui/main_stage/assistant_main_stage.cc b/ash/assistant/ui/main_stage/assistant_main_stage.cc
index cff71fff..6574edf 100644
--- a/ash/assistant/ui/main_stage/assistant_main_stage.cc
+++ b/ash/assistant/ui/main_stage/assistant_main_stage.cc
@@ -292,7 +292,8 @@
   ui_element_container_->AddObserver(this);
   content_layout_container_->AddChildView(ui_element_container_);
 
-  layout_manager->SetFlexForView(ui_element_container_, 1);
+  layout_manager->SetFlexForView(ui_element_container_, 1,
+                                 /*use_min_size=*/true);
 
   // Footer.
   // Note that the |footer_| is placed within its own view container so that as
diff --git a/ash/assistant/ui/main_stage/ui_element_container_view.cc b/ash/assistant/ui/main_stage/ui_element_container_view.cc
index 2683a66..e963c7d 100644
--- a/ash/assistant/ui/main_stage/ui_element_container_view.cc
+++ b/ash/assistant/ui/main_stage/ui_element_container_view.cc
@@ -14,11 +14,10 @@
 #include "ash/assistant/ui/assistant_ui_constants.h"
 #include "ash/assistant/ui/main_stage/assistant_text_element_view.h"
 #include "ash/assistant/util/animation_util.h"
-#include "ash/public/cpp/app_list/answer_card_contents_registry.h"
 #include "ash/shell.h"
 #include "base/callback.h"
 #include "base/time/time.h"
-#include "base/unguessable_token.h"
+#include "services/content/public/cpp/navigable_contents_view.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/compositor/callback_layer_animation_observer.h"
@@ -89,14 +88,18 @@
 // aura::Window. The child widget's layer becomes the root of the card's layer
 // hierarchy.
 class CardElementViewHolder : public views::NativeViewHost,
-                              public views::ViewObserver {
+                              public views::ViewObserver,
+                              public content::NavigableContentsObserver {
  public:
-  explicit CardElementViewHolder(const AssistantCardElement* card_element)
-      : card_element_view_(app_list::AnswerCardContentsRegistry::Get()->GetView(
-            card_element->embed_token().value())) {
+  CardElementViewHolder(AssistantController* assistant_controller,
+                        AssistantCardElement* card_element,
+                        views::Widget* context)
+      : assistant_controller_(assistant_controller),
+        contents_(card_element->contents()) {
     views::Widget::InitParams params(views::Widget::InitParams::TYPE_CONTROL);
 
     params.name = GetClassName();
+    params.context = context->GetNativeWindow();
     params.delegate = new views::WidgetDelegateView();
     params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
     params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
@@ -106,17 +109,21 @@
 
     contents_view_ = params.delegate->GetContentsView();
     contents_view_->SetLayoutManager(std::make_unique<views::FillLayout>());
-    contents_view_->AddChildView(card_element_view_);
+    contents_view_->AddChildView(contents_->GetView()->view());
 
-    card_element_view_->AddObserver(this);
+    // We observe the |contents_| view to receive notification of preferred size
+    // changes and we observe |contents_| to receive events pertaining to the
+    // underlying web contents.
+    contents_->GetView()->view()->AddObserver(this);
+    contents_->AddObserver(this);
 
     // OverrideDescription() doesn't work. Only names are read automatically.
     GetViewAccessibility().OverrideName(card_element->fallback());
   }
 
   ~CardElementViewHolder() override {
-    if (card_element_view_)
-      card_element_view_->RemoveObserver(this);
+    contents_->GetView()->view()->RemoveObserver(this);
+    contents_->RemoveObserver(this);
   }
 
   // views::NativeViewHost:
@@ -165,21 +172,23 @@
     cursor_manager->UnlockCursor();
   }
 
-  // views::ViewObserver:
-  void OnViewIsDeleting(views::View* view) override {
-    DCHECK_EQ(card_element_view_, view);
-
-    // It's possible for |card_element_view_| to be destroyed before
-    // CardElementViewHolder. When this happens, we need to perform clean up
-    // prior to |card_element_view_| being destroyed and remove our cached
-    // reference to prevent additional clean up attempts on the destroyed
-    // instance when destroying CardElementViewHolder.
-    card_element_view_->RemoveObserver(this);
-    card_element_view_ = nullptr;
+  // content::NavigableContentsObserver:
+  void DidAutoResizeView(const gfx::Size& new_size) override {
+    contents_->GetView()->view()->SetPreferredSize(new_size);
   }
 
+  void DidSuppressNavigation(const GURL& url,
+                             WindowOpenDisposition disposition,
+                             bool from_user_gesture) override {
+    // We delegate navigation to the AssistantController so that it can apply
+    // special handling to deep links.
+    if (from_user_gesture)
+      assistant_controller_->OpenUrl(url);
+  }
+
+  // views::ViewObserver:
   void OnViewPreferredSizeChanged(views::View* view) override {
-    DCHECK_EQ(card_element_view_, view);
+    DCHECK_EQ(contents_->GetView()->view(), view);
 
     gfx::Size preferred_size = view->GetPreferredSize();
 
@@ -213,7 +222,10 @@
   }
 
  private:
-  views::View* card_element_view_;  // Owned by WebContentsManager.
+  AssistantController* const assistant_controller_;  // Owned by Shell.
+
+  // Owned by AssistantCardElement.
+  content::NavigableContents* const contents_;
 
   std::unique_ptr<views::Widget> child_widget_;
 
@@ -257,6 +269,19 @@
   return content_view()->GetHeightForWidth(width);
 }
 
+gfx::Size UiElementContainerView::GetMinimumSize() const {
+  // AssistantMainStage uses BoxLayout's flex property to grow/shrink
+  // UiElementContainerView to fill available space as needed. When height is
+  // shrunk to zero, as is temporarily the case during the initial container
+  // growth animation for the first Assistant response, UiElementContainerView
+  // will be laid out with zero width. We do not recover from this state until
+  // the next layout pass, which causes Assistant cards for the first response
+  // to be laid out with zero width. We work around this by imposing a minimum
+  // height restriction of 1 dip that is factored into BoxLayout's flex
+  // calculations to make sure that our width is never being set to zero.
+  return gfx::Size(INT_MAX, 1);
+}
+
 void UiElementContainerView::OnContentsPreferredSizeChanged(
     views::View* content_view) {
   const int preferred_height = content_view->GetHeightForWidth(width());
@@ -397,51 +422,47 @@
 void UiElementContainerView::OnCardElementAdded(
     const AssistantCardElement* card_element) {
   // The card, for some reason, is not embeddable so we'll have to ignore it.
-  if (!card_element->embed_token().has_value())
+  if (!card_element->contents())
     return;
 
-  // When the card has been rendered in the same process, its view is
-  // available in the AnswerCardContentsRegistry's token-to-view map.
-  if (app_list::AnswerCardContentsRegistry::Get()) {
-    auto* view_holder = new CardElementViewHolder(card_element);
+  auto* view_holder = new CardElementViewHolder(
+      assistant_controller_, const_cast<AssistantCardElement*>(card_element),
+      /*context=*/GetWidget());
 
-    if (is_first_card_) {
-      is_first_card_ = false;
+  if (is_first_card_) {
+    is_first_card_ = false;
 
-      // The first card requires a top margin of |kFirstCardMarginTopDip|, but
-      // we need to account for child spacing because the first card is not
-      // necessarily the first UI element.
-      const int top_margin_dip = child_count() == 0
-                                     ? kFirstCardMarginTopDip
-                                     : kFirstCardMarginTopDip - kSpacingDip;
+    // The first card requires a top margin of |kFirstCardMarginTopDip|, but
+    // we need to account for child spacing because the first card is not
+    // necessarily the first UI element.
+    const int top_margin_dip = child_count() == 0
+                                   ? kFirstCardMarginTopDip
+                                   : kFirstCardMarginTopDip - kSpacingDip;
 
-      // We effectively create a top margin by applying an empty border.
-      view_holder->SetBorder(views::CreateEmptyBorder(top_margin_dip, 0, 0, 0));
-    }
-
-    content_view()->AddChildView(view_holder);
-    view_holder->Attach();
-
-    // Cache a reference to the attached native view host so that it can be
-    // detached prior to being removed from the view hierarchy and destroyed.
-    native_view_hosts_.push_back(view_holder);
-
-    // The view will be animated on its own layer, so we need to do some initial
-    // layer setup. We're going to fade the view in, so hide it. Note that we
-    // approximate 0% opacity by actually using 0.01%. We do this to workaround
-    // a DCHECK that requires aura::Windows to have a target opacity > 0% when
-    // shown. Because our window will be animated to full opacity from this
-    // value, it should be safe to circumnavigate this DCHECK.
-    view_holder->native_view()->layer()->SetFillsBoundsOpaquely(false);
-    view_holder->native_view()->layer()->SetOpacity(0.0001f);
-
-    // We cache the native view for use during animations and its desired
-    // opacity that we'll animate to while processing the next query response.
-    ui_element_views_.push_back(std::pair<ui::LayerOwner*, float>(
-        view_holder->native_view(), kCardElementAnimationFadeOutOpacity));
+    // We effectively create a top margin by applying an empty border.
+    view_holder->SetBorder(views::CreateEmptyBorder(top_margin_dip, 0, 0, 0));
   }
 
-  // TODO(dmblack): Handle Mash case.
+  content_view()->AddChildView(view_holder);
+  view_holder->Attach();
+
+  // Cache a reference to the attached native view host so that it can be
+  // detached prior to being removed from the view hierarchy and destroyed.
+  native_view_hosts_.push_back(view_holder);
+
+  // The view will be animated on its own layer, so we need to do some initial
+  // layer setup. We're going to fade the view in, so hide it. Note that we
+  // approximate 0% opacity by actually using 0.01%. We do this to workaround
+  // a DCHECK that requires aura::Windows to have a target opacity > 0% when
+  // shown. Because our window will be animated to full opacity from this
+  // value, it should be safe to circumnavigate this DCHECK.
+  view_holder->native_view()->layer()->SetFillsBoundsOpaquely(false);
+  view_holder->native_view()->layer()->SetOpacity(0.0001f);
+
+  // We cache the native view for use during animations and its desired
+  // opacity that we'll animate to while processing the next query response.
+  ui_element_views_.push_back(std::pair<ui::LayerOwner*, float>(
+      view_holder->native_view(), kCardElementAnimationFadeOutOpacity));
 }
 
 void UiElementContainerView::OnTextElementAdded(
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 f7b63bc..ef7743d 100644
--- a/ash/assistant/ui/main_stage/ui_element_container_view.h
+++ b/ash/assistant/ui/main_stage/ui_element_container_view.h
@@ -44,6 +44,7 @@
   const char* GetClassName() const override;
   gfx::Size CalculatePreferredSize() const override;
   int GetHeightForWidth(int width) const override;
+  gfx::Size GetMinimumSize() const override;
   void OnContentsPreferredSizeChanged(views::View* content_view) override;
   void PreferredSizeChanged() override;
 
diff --git a/ash/assistant/util/deep_link_util.cc b/ash/assistant/util/deep_link_util.cc
index 113f4b6..9f7c648 100644
--- a/ash/assistant/util/deep_link_util.cc
+++ b/ash/assistant/util/deep_link_util.cc
@@ -12,6 +12,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "net/base/escape.h"
+#include "net/base/url_util.h"
 #include "url/gurl.h"
 
 namespace ash {
@@ -40,8 +41,19 @@
 constexpr char kAssistantWhatsOnMyScreenPrefix[] =
     "googleassistant://whats-on-my-screen";
 
+// Helpers ---------------------------------------------------------------------
+
+// Returns a GURL for the specified |url| having set the locale query parameter.
+GURL CreateLocalizedGURL(const std::string& url) {
+  static constexpr char kLocaleParamKey[] = "hl";
+  return net::AppendOrReplaceQueryParameter(GURL(url), kLocaleParamKey,
+                                            base::i18n::GetConfiguredLocale());
+}
+
 }  // namespace
 
+// Utilities -------------------------------------------------------------------
+
 GURL CreateAssistantSettingsDeepLink() {
   return GURL(kAssistantSettingsPrefix);
 }
@@ -155,19 +167,18 @@
 base::Optional<GURL> GetWebUrl(DeepLinkType type) {
   // TODO(b/113357196): Make these URLs configurable for development purposes.
   static constexpr char kAssistantRemindersWebUrl[] =
-      "https://assistant.google.com/reminders/mainview?hl=";
+      "https://assistant.google.com/reminders/mainview";
   static constexpr char kAssistantSettingsWebUrl[] =
-      "https://assistant.google.com/settings/mainpage?hl=";
+      "https://assistant.google.com/settings/mainpage";
 
   if (!IsWebDeepLinkType(type))
     return base::nullopt;
 
   switch (type) {
     case DeepLinkType::kReminders:
-      return GURL(kAssistantRemindersWebUrl +
-                  base::i18n::GetConfiguredLocale());
+      return CreateLocalizedGURL(kAssistantRemindersWebUrl);
     case DeepLinkType::kSettings:
-      return GURL(kAssistantSettingsWebUrl + base::i18n::GetConfiguredLocale());
+      return CreateLocalizedGURL(kAssistantSettingsWebUrl);
     case DeepLinkType::kUnsupported:
     case DeepLinkType::kChromeSettings:
     case DeepLinkType::kFeedback:
diff --git a/ash/assistant/util/deep_link_util_unittest.cc b/ash/assistant/util/deep_link_util_unittest.cc
index 91fcb93..e4cacbe 100644
--- a/ash/assistant/util/deep_link_util_unittest.cc
+++ b/ash/assistant/util/deep_link_util_unittest.cc
@@ -16,16 +16,7 @@
 namespace assistant {
 namespace util {
 
-class DeepLinkUnitTest : public AshTestBase {
- protected:
-  DeepLinkUnitTest() = default;
-  ~DeepLinkUnitTest() override = default;
-
-  void SetUp() override { AshTestBase::SetUp(); }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DeepLinkUnitTest);
-};
+using DeepLinkUnitTest = AshTestBase;
 
 TEST_F(DeepLinkUnitTest, CreateAssistantSettingsDeepLink) {
   ASSERT_EQ(GURL("googleassistant://settings"),
@@ -176,7 +167,6 @@
     ASSERT_EQ(test_case.second, GetDeepLinkType(GURL(test_case.first)));
 }
 
-// TODO(dmblack): Implement.
 TEST_F(DeepLinkUnitTest, IsDeepLinkType) {
   const std::map<std::string, DeepLinkType> test_cases = {
       // OK: Supported deep link types.
@@ -294,59 +284,81 @@
     ASSERT_EQ(test_case.second, GetChromeSettingsUrl(test_case.first));
 }
 
-// TODO(dmblack): Assert actual web URLs when available.
 TEST_F(DeepLinkUnitTest, GetWebUrl) {
-  const std::map<std::string, bool> test_cases = {
+  const std::map<std::string, base::Optional<GURL>> test_cases = {
       // OK: Supported web deep links.
-      {"googleassistant://reminders", true},
-      {"googleassistant://settings", true},
+      {"googleassistant://reminders",
+       GURL("https://assistant.google.com/reminders/mainview?hl=en-US")},
+      {"googleassistant://settings",
+       GURL("https://assistant.google.com/settings/mainpage?hl=en-US")},
 
       // OK: Parameterized deep links.
-      {"googleassistant://reminders?param=true", true},
-      {"googleassistant://settings?param=true", true},
+      {"googleassistant://reminders?param=true",
+       GURL("https://assistant.google.com/reminders/mainview?hl=en-US")},
+      {"googleassistant://settings?param=true",
+       GURL("https://assistant.google.com/settings/mainpage?hl=en-US")},
 
       // FAIL: Deep links are case sensitive.
-      {"GOOGLEASSISTANT://REMINDERS", false},
-      {"GOOGLEASSISTANT://SETTINGS", false},
+      {"GOOGLEASSISTANT://REMINDERS", base::nullopt},
+      {"GOOGLEASSISTANT://SETTINGS", base::nullopt},
 
       // FAIL: Non-web deep links.
-      {"googleassistant://chrome-settings", false},
-      {"googleassistant://onboarding", false},
-      {"googleassistant://send-feedback", false},
-      {"googleassistant://send-query", false},
-      {"googleassistant://take-screenshot", false},
-      {"googleassistant://task-manager", false},
-      {"googleassistant://whats-on-my-screen", false},
+      {"googleassistant://chrome-settings", base::nullopt},
+      {"googleassistant://onboarding", base::nullopt},
+      {"googleassistant://send-feedback", base::nullopt},
+      {"googleassistant://send-query", base::nullopt},
+      {"googleassistant://take-screenshot", base::nullopt},
+      {"googleassistant://task-manager", base::nullopt},
+      {"googleassistant://whats-on-my-screen", base::nullopt},
 
       // FAIL: Non-deep link URLs.
-      {std::string(), false},
-      {"https://www.google.com/", false}};
+      {std::string(), base::nullopt},
+      {"https://www.google.com/", base::nullopt}};
 
-  for (const auto& test_case : test_cases)
-    ASSERT_EQ(test_case.second, GetWebUrl(GURL(test_case.first)).has_value());
+  for (const auto& test_case : test_cases) {
+    const base::Optional<GURL>& expected = test_case.second;
+    const base::Optional<GURL> actual = GetWebUrl(GURL(test_case.first));
+
+    // Assert |has_value| equivalence.
+    ASSERT_EQ(expected, actual);
+
+    // Assert |value| equivalence.
+    if (expected)
+      ASSERT_EQ(expected.value(), actual.value());
+  }
 }
 
-// TODO(dmblack): Assert actual web URLs when available.
 TEST_F(DeepLinkUnitTest, GetWebUrlByType) {
-  const std::map<DeepLinkType, bool> test_cases = {
+  const std::map<DeepLinkType, base::Optional<GURL>> test_cases = {
       // OK: Supported web deep link types.
-      {DeepLinkType::kReminders, true},
-      {DeepLinkType::kSettings, true},
+      {DeepLinkType::kReminders,
+       GURL("https://assistant.google.com/reminders/mainview?hl=en-US")},
+      {DeepLinkType::kSettings,
+       GURL("https://assistant.google.com/settings/mainpage?hl=en-US")},
 
       // FAIL: Non-web deep link types.
-      {DeepLinkType::kChromeSettings, false},
-      {DeepLinkType::kFeedback, false},
-      {DeepLinkType::kOnboarding, false},
-      {DeepLinkType::kQuery, false},
-      {DeepLinkType::kScreenshot, false},
-      {DeepLinkType::kTaskManager, false},
-      {DeepLinkType::kWhatsOnMyScreen, false},
+      {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},
 
       // FAIL: Unsupported deep link types.
-      {DeepLinkType::kUnsupported, false}};
+      {DeepLinkType::kUnsupported, base::nullopt}};
 
-  for (const auto& test_case : test_cases)
-    ASSERT_EQ(test_case.second, GetWebUrl(test_case.first).has_value());
+  for (const auto& test_case : test_cases) {
+    const base::Optional<GURL>& expected = test_case.second;
+    const base::Optional<GURL> actual = GetWebUrl(test_case.first);
+
+    // Assert |has_value| equivalence.
+    ASSERT_EQ(expected, actual);
+
+    // Assert |value| equivalence.
+    if (expected)
+      ASSERT_EQ(expected.value(), actual.value());
+  }
 }
 
 TEST_F(DeepLinkUnitTest, IsWebDeepLink) {
diff --git a/ash/autoclick/autoclick_controller.cc b/ash/autoclick/autoclick_controller.cc
index 8f90926..718c4ee 100644
--- a/ash/autoclick/autoclick_controller.cc
+++ b/ash/autoclick/autoclick_controller.cc
@@ -25,10 +25,6 @@
 
 namespace {
 
-// The threshold of mouse movement measured in DIP that will
-// initiate a new autoclick.
-const int kMovementThreshold = 20;
-
 bool IsModifierKey(const ui::KeyboardCode key_code) {
   return key_code == ui::VKEY_SHIFT || key_code == ui::VKEY_LSHIFT ||
          key_code == ui::VKEY_CONTROL || key_code == ui::VKEY_LCONTROL ||
@@ -47,10 +43,12 @@
     : enabled_(false),
       event_type_(kDefaultAutoclickEventType),
       revert_to_left_click_(true),
+      movement_threshold_(kDefaultAutoclickMovementThreshold),
       tap_down_target_(nullptr),
       delay_(GetDefaultAutoclickDelay()),
       mouse_event_flags_(ui::EF_NONE),
-      anchor_location_(-kMovementThreshold, -kMovementThreshold),
+      anchor_location_(-kDefaultAutoclickMovementThreshold,
+                       -kDefaultAutoclickMovementThreshold),
       autoclick_ring_handler_(std::make_unique<AutoclickRingHandler>()),
       drag_event_rewriter_(std::make_unique<AutoclickDragEventRewriter>()) {
   Shell::GetPrimaryRootWindow()->GetHost()->GetEventSource()->AddEventRewriter(
@@ -285,7 +283,6 @@
       // the events logged.
       if (DragInProgress())
         return;
-      LOG(ERROR) << "Recording a drag&drop";
       base::RecordAction(
           base::UserMetricsAction("Accessibility.Autoclick.DragAndDrop"));
       return;
@@ -314,7 +311,7 @@
     // 2. prevent the autoclick from ever occurring when the mouse
     //    arrives at the target.
     gfx::Vector2d delta = point_in_screen - anchor_location_;
-    if (delta.LengthSquared() >= kMovementThreshold * kMovementThreshold) {
+    if (delta.LengthSquared() >= movement_threshold_ * movement_threshold_) {
       anchor_location_ = point_in_screen;
       autoclick_timer_->Reset();
       autoclick_ring_handler_->StartGesture(delay_, anchor_location_,
diff --git a/ash/autoclick/autoclick_controller.h b/ash/autoclick/autoclick_controller.h
index 4636afff..8e027b0 100644
--- a/ash/autoclick/autoclick_controller.h
+++ b/ash/autoclick/autoclick_controller.h
@@ -57,6 +57,12 @@
     revert_to_left_click_ = revert_to_left_click;
   }
 
+  // Sets the movement threshold beyond which mouse movements cancel or begin
+  // a new Autoclick event.
+  void set_movement_threshold(int movement_threshold) {
+    movement_threshold_ = movement_threshold;
+  }
+
  private:
   void SetTapDownTarget(aura::Window* target);
   void CreateAutoclickRingWidget(const gfx::Point& point_in_screen);
@@ -83,6 +89,7 @@
   bool enabled_;
   mojom::AutoclickEventType event_type_;
   bool revert_to_left_click_;
+  int movement_threshold_;
   // The target window is observed by AutoclickController for the duration
   // of a autoclick gesture.
   aura::Window* tap_down_target_;
diff --git a/ash/autoclick/autoclick_unittest.cc b/ash/autoclick/autoclick_unittest.cc
index 6c002bc..5a59202 100644
--- a/ash/autoclick/autoclick_unittest.cc
+++ b/ash/autoclick/autoclick_unittest.cc
@@ -188,26 +188,44 @@
   aura::Window::Windows root_windows = Shell::GetAllRootWindows();
   EXPECT_EQ(2u, root_windows.size());
 
-  // Run test for the secondary display too to test fix for crbug.com/449870.
-  for (auto* root_window : root_windows) {
-    gfx::Point center = root_window->GetBoundsInScreen().CenterPoint();
+  // Try at a couple different thresholds.
+  for (int movement_threshold = 10; movement_threshold < 50;
+       movement_threshold += 10) {
+    GetAutoclickController()->set_movement_threshold(movement_threshold);
 
-    GetAutoclickController()->SetEnabled(true);
-    GetEventGenerator()->MoveMouseTo(center);
-    EXPECT_EQ(2u, WaitForMouseEvents().size());
+    // Run test for the secondary display too to test fix for crbug.com/449870.
+    for (auto* root_window : root_windows) {
+      gfx::Point center = root_window->GetBoundsInScreen().CenterPoint();
 
-    // Small mouse movements should not trigger an autoclick.
-    GetEventGenerator()->MoveMouseTo(center + gfx::Vector2d(1, 1));
-    EXPECT_EQ(0u, WaitForMouseEvents().size());
-    GetEventGenerator()->MoveMouseTo(center + gfx::Vector2d(2, 2));
-    EXPECT_EQ(0u, WaitForMouseEvents().size());
-    GetEventGenerator()->MoveMouseTo(center);
-    EXPECT_EQ(0u, WaitForMouseEvents().size());
+      GetAutoclickController()->SetEnabled(true);
+      GetEventGenerator()->MoveMouseTo(center);
+      EXPECT_EQ(2u, WaitForMouseEvents().size());
 
-    // A large mouse movement should trigger an autoclick.
-    GetEventGenerator()->MoveMouseTo(center + gfx::Vector2d(100, 100));
-    EXPECT_EQ(2u, WaitForMouseEvents().size());
+      // Small mouse movements should not trigger an autoclick, i.e. movements
+      // within the radius of the movement_threshold.
+      GetEventGenerator()->MoveMouseTo(
+          center + gfx::Vector2d(std::sqrt(movement_threshold) - 1,
+                                 std::sqrt(movement_threshold) - 1));
+      EXPECT_EQ(0u, WaitForMouseEvents().size());
+      GetEventGenerator()->MoveMouseTo(
+          center + gfx::Vector2d(movement_threshold - 1, 0));
+      EXPECT_EQ(0u, WaitForMouseEvents().size());
+      GetEventGenerator()->MoveMouseTo(
+          center + gfx::Vector2d(0, -movement_threshold + 1));
+      EXPECT_EQ(0u, WaitForMouseEvents().size());
+      GetEventGenerator()->MoveMouseTo(center);
+      EXPECT_EQ(0u, WaitForMouseEvents().size());
+
+      // A larger mouse movement should trigger an autoclick.
+      GetEventGenerator()->MoveMouseTo(
+          center +
+          gfx::Vector2d(movement_threshold + 1, movement_threshold + 1));
+      EXPECT_EQ(2u, WaitForMouseEvents().size());
+    }
   }
+
+  // Reset to default threshold.
+  GetAutoclickController()->set_movement_threshold(20);
 }
 
 TEST_F(AutoclickTest, SingleKeyModifier) {
diff --git a/ash/bluetooth_devices_observer.cc b/ash/bluetooth_devices_observer.cc
index e301b12..7b7ce1d 100644
--- a/ash/bluetooth_devices_observer.cc
+++ b/ash/bluetooth_devices_observer.cc
@@ -10,11 +10,16 @@
 namespace ash {
 
 BluetoothDevicesObserver::BluetoothDevicesObserver(
-    const DeviceChangedCallback& device_changed_callback)
-    : device_changed_callback_(device_changed_callback), weak_factory_(this) {
-  device::BluetoothAdapterFactory::GetAdapter(
-      base::Bind(&BluetoothDevicesObserver::InitializeOnAdapterReady,
-                 weak_factory_.GetWeakPtr()));
+    const AdapterOrDeviceChangedCallback& device_changed_callback)
+    : adapter_or_device_changed_callback_(device_changed_callback),
+      weak_factory_(this) {
+  if (device::BluetoothAdapterFactory::IsBluetoothSupported()) {
+    device::BluetoothAdapterFactory::GetAdapter(
+        base::Bind(&BluetoothDevicesObserver::InitializeOnAdapterReady,
+                   weak_factory_.GetWeakPtr()));
+  } else {
+    adapter_or_device_changed_callback_.Run(/*device=*/nullptr);
+  }
 }
 
 BluetoothDevicesObserver::~BluetoothDevicesObserver() {
@@ -22,9 +27,21 @@
     bluetooth_adapter_->RemoveObserver(this);
 }
 
+void BluetoothDevicesObserver::AdapterPresentChanged(
+    device::BluetoothAdapter* adapter,
+    bool present) {
+  adapter_or_device_changed_callback_.Run(/*device=*/nullptr);
+}
+
+void BluetoothDevicesObserver::AdapterPoweredChanged(
+    device::BluetoothAdapter* adapter,
+    bool powered) {
+  adapter_or_device_changed_callback_.Run(/*device=*/nullptr);
+}
+
 void BluetoothDevicesObserver::DeviceChanged(device::BluetoothAdapter* adapter,
                                              device::BluetoothDevice* device) {
-  device_changed_callback_.Run(device);
+  adapter_or_device_changed_callback_.Run(device);
 }
 
 void BluetoothDevicesObserver::InitializeOnAdapterReady(
@@ -35,8 +52,11 @@
 
 bool BluetoothDevicesObserver::IsConnectedBluetoothDevice(
     const ui::InputDevice& input_device) const {
-  if (!bluetooth_adapter_ || !bluetooth_adapter_->IsPowered())
+  if (!bluetooth_adapter_ || !bluetooth_adapter_->IsPresent() ||
+      !bluetooth_adapter_->IsInitialized() ||
+      !bluetooth_adapter_->IsPowered()) {
     return false;
+  }
 
   // Since there is no map from an InputDevice to a BluetoothDevice. We just
   // comparing their vendor id and product id to guess a match.
diff --git a/ash/bluetooth_devices_observer.h b/ash/bluetooth_devices_observer.h
index 5612ec1c..3b8a7bb 100644
--- a/ash/bluetooth_devices_observer.h
+++ b/ash/bluetooth_devices_observer.h
@@ -20,14 +20,20 @@
 // bluetooth device changes.
 class BluetoothDevicesObserver : public device::BluetoothAdapter::Observer {
  public:
-  using DeviceChangedCallback =
+  // Note |device| can be nullptr here if only the bluetooth adapter status
+  // changes.
+  using AdapterOrDeviceChangedCallback =
       base::RepeatingCallback<void(device::BluetoothDevice* device)>;
 
   explicit BluetoothDevicesObserver(
-      const DeviceChangedCallback& device_changed_callback);
+      const AdapterOrDeviceChangedCallback& device_changed_callback);
   ~BluetoothDevicesObserver() override;
 
   // device::BluetoothAdapter::Observer:
+  void AdapterPresentChanged(device::BluetoothAdapter* adapter,
+                             bool present) override;
+  void AdapterPoweredChanged(device::BluetoothAdapter* adapter,
+                             bool powered) override;
   void DeviceChanged(device::BluetoothAdapter* adapter,
                      device::BluetoothDevice* device) override;
 
@@ -50,8 +56,9 @@
   // device change event.
   scoped_refptr<device::BluetoothAdapter> bluetooth_adapter_;
 
-  // Callback function to be called when a bluetooth device status changes.
-  DeviceChangedCallback device_changed_callback_;
+  // Callback function to be called when the bluetooth adapter's status or a
+  // bluetooth device's status changes.
+  AdapterOrDeviceChangedCallback adapter_or_device_changed_callback_;
 
   base::WeakPtrFactory<BluetoothDevicesObserver> weak_factory_;
 
diff --git a/ash/components/strings/ash_components_strings_da.xtb b/ash/components/strings/ash_components_strings_da.xtb
index 48a6c32..060234f 100644
--- a/ash/components/strings/ash_components_strings_da.xtb
+++ b/ash/components/strings/ash_components_strings_da.xtb
@@ -96,7 +96,7 @@
 <translation id="4556221320735744018">Se Hjælp til tastaturgenveje</translation>
 <translation id="4628718545549558538">Åbn statusområdet (hvor dit kontobillede vises)</translation>
 <translation id="4642092649622328492">Tag et delvist screenshot</translation>
-<translation id="4698850295812410683">Vis penneværktøjer</translation>
+<translation id="4698850295812410683">Vis styluspenværktøjer</translation>
 <translation id="4801989101741319327">Flyt til slutningen af det næste ord</translation>
 <translation id="4916163929714267752">Åbn linket i et nyt vindue</translation>
 <translation id="5030659775136592441">Vis bogmærkeadministrator</translation>
diff --git a/ash/components/tap_visualizer/tap_visualizer_app_unittest.cc b/ash/components/tap_visualizer/tap_visualizer_app_unittest.cc
index 9de9eff..2ff7794 100644
--- a/ash/components/tap_visualizer/tap_visualizer_app_unittest.cc
+++ b/ash/components/tap_visualizer/tap_visualizer_app_unittest.cc
@@ -59,7 +59,6 @@
     // Create a MusClient using the AuraTestBase's TestWindowTreeClient,
     // which does not connect to a window service.
     views::MusClient::InitParams params;
-    params.create_cursor_factory = false;
     params.create_wm_state = false;
     params.window_tree_client = window_tree_client_impl();
     mus_client_ = std::make_unique<views::MusClient>(params);
diff --git a/ash/cursor_unittest.cc b/ash/cursor_unittest.cc
index e6d9736..ac4a0ea 100644
--- a/ash/cursor_unittest.cc
+++ b/ash/cursor_unittest.cc
@@ -26,7 +26,7 @@
 
   // Make sure the WindowTreeClient receives events.
   EXPECT_EQ(0U, GetTestWindowTreeClient()->input_events().size());
-  ui::test::EventGenerator generator(window.get());
+  ui::test::EventGenerator generator(window->GetRootWindow());
   generator.MoveMouseToInHost(50, 50);
   ASSERT_EQ(1U, GetTestWindowTreeClient()->input_events().size());
   EXPECT_EQ(ui::EventType::ET_MOUSE_MOVED,
@@ -74,7 +74,7 @@
   EXPECT_TRUE(embed_root->IsVisible());
 
   // Now put the cursor over it and the previously set cursor should be used.
-  ui::test::EventGenerator generator(toplevel.get());
+  ui::test::EventGenerator generator(toplevel->GetRootWindow());
   generator.MoveMouseToInHost(50, 50);
   EXPECT_EQ(ui::CursorType::kHelp,
             ash::Shell::Get()->cursor_manager()->GetCursor().native_type());
@@ -91,7 +91,7 @@
   std::unique_ptr<aura::Window> window =
       CreateTestWindow(gfx::Rect(0, 0, 100, 100));
   EXPECT_EQ(0U, GetTestWindowTreeClient()->input_events().size());
-  ui::test::EventGenerator generator(window.get());
+  ui::test::EventGenerator generator(window->GetRootWindow());
   generator.MoveMouseToInHost(50, 50);
 
   // Set a custom cursor.
diff --git a/ash/display/display_configuration_observer.cc b/ash/display/display_configuration_observer.cc
index a749111..a324894 100644
--- a/ash/display/display_configuration_observer.cc
+++ b/ash/display/display_configuration_observer.cc
@@ -60,8 +60,6 @@
   // how to handle this scenario, and we shouldn't save this state.
   // https://crbug.com/733092.
   save_preference_ = false;
-  // TODO(oshima): Mirroring won't work with 3+ displays:
-  // https://crbug.com/737667.
   display::DisplayManager* display_manager = Shell::Get()->display_manager();
   was_in_mirror_mode_ = display_manager->IsInMirrorMode();
   display_manager->SetMirrorMode(display::MirrorMode::kNormal, base::nullopt);
diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc
index b30f9b1..48de7e7 100644
--- a/ash/display/display_manager_unittest.cc
+++ b/ash/display/display_manager_unittest.cc
@@ -5,6 +5,7 @@
 #include "ui/display/manager/display_manager.h"
 
 #include "ash/accelerators/accelerator_commands.h"
+#include "ash/app_list/app_list_controller_impl.h"
 #include "ash/display/cursor_window_controller.h"
 #include "ash/display/display_configuration_controller.h"
 #include "ash/display/display_util.h"
@@ -3011,6 +3012,46 @@
                          .id());
 }
 
+TEST_F(DisplayManagerTest, UnifiedDesktopTabletMode) {
+  // Don't check root window destruction in unified mode.
+  Shell::GetPrimaryRootWindow()->RemoveObserver(this);
+
+  UpdateDisplay("400x300,800x800");
+  RunAllPendingInMessageLoop();
+
+  // Set the first display as internal display so that the tablet mode can be
+  // enabled.
+  display::test::DisplayManagerTestApi(display_manager())
+      .SetFirstDisplayAsInternalDisplay();
+
+  display_manager()->SetUnifiedDesktopEnabled(true);
+  EXPECT_TRUE(display_manager()->IsInUnifiedMode());
+
+  // Turn on tablet mode, expect that we switch to mirror mode without any
+  // crashes.
+  Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(true);
+  RunAllPendingInMessageLoop();
+  EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
+
+  // The Home Launcher should be created and shown, not dismissed as a result of
+  // 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());
+  EXPECT_TRUE(app_list_controller->IsVisible());
+
+  // Exiting tablet mode should exit mirror mode and return back to Unified
+  // mode.
+  Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(false);
+  RunAllPendingInMessageLoop();
+  EXPECT_FALSE(display_manager()->IsInSoftwareMirrorMode());
+  EXPECT_TRUE(display_manager()->IsInUnifiedMode());
+
+  // Home Launcher should be dismissed.
+  EXPECT_FALSE(app_list_controller->IsHomeLauncherEnabledInTabletMode());
+  EXPECT_FALSE(app_list_controller->IsVisible());
+}
+
 TEST_F(DisplayManagerTest, DockMode) {
   const int64_t internal_id = 1;
   const int64_t external_id = 2;
diff --git a/ash/display/display_prefs.cc b/ash/display/display_prefs.cc
index 65bacf5..25a4016 100644
--- a/ash/display/display_prefs.cc
+++ b/ash/display/display_prefs.cc
@@ -15,7 +15,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
-#include "base/sys_info.h"
+#include "base/system/sys_info.h"
 #include "base/values.h"
 #include "chromeos/chromeos_switches.h"
 #include "components/prefs/pref_registry_simple.h"
diff --git a/ash/display/display_util.cc b/ash/display/display_util.cc
index 22f8bb3..17142c6ab 100644
--- a/ash/display/display_util.cc
+++ b/ash/display/display_util.cc
@@ -19,7 +19,7 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/sys_info.h"
+#include "base/system/sys_info.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/ash/display/mirror_window_controller_unittest.cc b/ash/display/mirror_window_controller_unittest.cc
index 7477f7e..cfa4832 100644
--- a/ash/display/mirror_window_controller_unittest.cc
+++ b/ash/display/mirror_window_controller_unittest.cc
@@ -27,6 +27,7 @@
 namespace ash {
 
 namespace {
+
 display::ManagedDisplayInfo CreateDisplayInfo(int64_t id,
                                               const gfx::Rect& bounds,
                                               float scale = 1.f) {
@@ -49,7 +50,6 @@
         ::switches::kEnableSoftwareMirroring);
     AshTestBase::SetUp();
   }
-  void TearDown() override { AshTestBase::TearDown(); }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MirrorOnBootTest);
diff --git a/ash/display/window_tree_host_manager_unittest.cc b/ash/display/window_tree_host_manager_unittest.cc
index 791f9ee..b47f370 100644
--- a/ash/display/window_tree_host_manager_unittest.cc
+++ b/ash/display/window_tree_host_manager_unittest.cc
@@ -1287,13 +1287,14 @@
 
   ui::test::EventGenerator generator(root_windows[0]);
   generator.MoveMouseToInHost(0, 0);
-  EXPECT_EQ("0,375", event_handler.GetLocationAndReset());
+  // The mouse location must be inside the root bounds in dp.
+  EXPECT_EQ("0,374", event_handler.GetLocationAndReset());
   generator.MoveMouseToInHost(599, 0);
   EXPECT_EQ("0,0", event_handler.GetLocationAndReset());
   generator.MoveMouseToInHost(599, 399);
   EXPECT_EQ("249,0", event_handler.GetLocationAndReset());
   generator.MoveMouseToInHost(0, 399);
-  EXPECT_EQ("249,375", event_handler.GetLocationAndReset());
+  EXPECT_EQ("249,374", event_handler.GetLocationAndReset());
 
   UpdateDisplay("600x400*2/u@0.8");
   display1 = display::Screen::GetScreen()->GetPrimaryDisplay();
@@ -1303,13 +1304,13 @@
   EXPECT_EQ(0.8f, GetStoredZoomScale(display1.id()));
 
   generator.MoveMouseToInHost(0, 0);
-  EXPECT_EQ("375,250", event_handler.GetLocationAndReset());
+  EXPECT_EQ("374,249", event_handler.GetLocationAndReset());
   generator.MoveMouseToInHost(599, 0);
-  EXPECT_EQ("0,250", event_handler.GetLocationAndReset());
+  EXPECT_EQ("0,249", event_handler.GetLocationAndReset());
   generator.MoveMouseToInHost(599, 399);
   EXPECT_EQ("0,0", event_handler.GetLocationAndReset());
   generator.MoveMouseToInHost(0, 399);
-  EXPECT_EQ("375,0", event_handler.GetLocationAndReset());
+  EXPECT_EQ("374,0", event_handler.GetLocationAndReset());
 
   UpdateDisplay("600x400*2/l@0.8");
   display1 = display::Screen::GetScreen()->GetPrimaryDisplay();
@@ -1319,9 +1320,9 @@
   EXPECT_EQ(0.8f, GetStoredZoomScale(display1.id()));
 
   generator.MoveMouseToInHost(0, 0);
-  EXPECT_EQ("250,0", event_handler.GetLocationAndReset());
+  EXPECT_EQ("249,0", event_handler.GetLocationAndReset());
   generator.MoveMouseToInHost(599, 0);
-  EXPECT_EQ("250,374", event_handler.GetLocationAndReset());
+  EXPECT_EQ("249,374", event_handler.GetLocationAndReset());
   generator.MoveMouseToInHost(599, 399);
   EXPECT_EQ("0,374", event_handler.GetLocationAndReset());
   generator.MoveMouseToInHost(0, 399);
@@ -1468,10 +1469,10 @@
 
   aura::Env* env = Shell::Get()->aura_env();
 
-  ui::test::EventGenerator generator(root_windows[0]);
+  ui::test::EventGenerator generator_on_2nd(root_windows[1]);
 
   // Set the initial position.
-  generator.MoveMouseToInHost(350, 150);
+  generator_on_2nd.MoveMouseToInHost(150, 150);
   EXPECT_EQ("350,150", env->last_mouse_location().ToString());
 
   // A mouse pointer will stay in the 2nd display.
@@ -1494,7 +1495,8 @@
   EXPECT_EQ("150,150", env->last_mouse_location().ToString());
 
   // Move the mouse pointer to the bottom of 1st display.
-  generator.MoveMouseToInHost(150, 290);
+  ui::test::EventGenerator generator_on_1st(root_windows[0]);
+  generator_on_1st.MoveMouseToInHost(150, 290);
   EXPECT_EQ("150,290", env->last_mouse_location().ToString());
 
   // The mouse pointer is now on 2nd display.
diff --git a/ash/drag_drop/drag_drop_tracker_unittest.cc b/ash/drag_drop/drag_drop_tracker_unittest.cc
index 74f16f9..fb8c75d 100644
--- a/ash/drag_drop/drag_drop_tracker_unittest.cc
+++ b/ash/drag_drop/drag_drop_tracker_unittest.cc
@@ -19,8 +19,6 @@
 
 class DragDropTrackerTest : public AshTestBase {
  public:
-  void SetUp() override { AshTestBase::SetUp(); }
-
   aura::Window* CreateTestWindow(const gfx::Rect& bounds) {
     static int window_id = 0;
     return CreateTestWindowInShellWithDelegate(
@@ -174,4 +172,4 @@
   EXPECT_EQ(original11.flags(), converted11->flags());
 }
 
-}  // namespace aura
+}  // namespace ash
diff --git a/ash/highlighter/highlighter_controller_unittest.cc b/ash/highlighter/highlighter_controller_unittest.cc
index 6b01127d..8aad7da 100644
--- a/ash/highlighter/highlighter_controller_unittest.cc
+++ b/ash/highlighter/highlighter_controller_unittest.cc
@@ -421,8 +421,9 @@
   constexpr float display_scales[] = {1.f, 1.5f, 2.0f};
 
   for (size_t i = 0; i < sizeof(display_scales) / sizeof(float); ++i) {
-    std::string display_spec =
-        base::StringPrintf("1000x1000*%.2f", display_scales[i]);
+    // 2nd display is for offscreen test.
+    std::string display_spec = base::StringPrintf(
+        "1000x1000*%.2f,500x1000*%.2f", display_scales[i], display_scales[i]);
     SCOPED_TRACE(display_spec);
     UpdateDisplayAndWaitForCompositingEnded(display_spec);
 
@@ -462,11 +463,11 @@
     EXPECT_TRUE(controller_test_api_->HandleSelectionCalled());
     EXPECT_TRUE(screen.Contains(controller_test_api_->selection()));
 
-    // Horizontal stroke completely offscreen.
+    // Vertical stroke completely offscreen.
     controller_test_api_->ResetSelection();
-    event_generator->MoveTouch(gfx::Point(0, -100));
+    event_generator->MoveTouch(gfx::Point(1100, 100));
     event_generator->PressTouch();
-    event_generator->MoveTouch(gfx::Point(1000, -100));
+    event_generator->MoveTouch(gfx::Point(1100, 500));
     event_generator->ReleaseTouch();
     controller_test_api_->SimulateInterruptedStrokeTimeout();
     EXPECT_FALSE(controller_test_api_->HandleSelectionCalled());
diff --git a/ash/host/ash_window_tree_host.cc b/ash/host/ash_window_tree_host.cc
index 30d5b3f..8ab4b34 100644
--- a/ash/host/ash_window_tree_host.cc
+++ b/ash/host/ash_window_tree_host.cc
@@ -12,7 +12,7 @@
 #include "ash/host/ash_window_tree_host_unified.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "base/command_line.h"
-#include "base/sys_info.h"
+#include "base/system/sys_info.h"
 #include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window_tree_host.h"
diff --git a/ash/keyboard/ash_keyboard_controller.cc b/ash/keyboard/ash_keyboard_controller.cc
index c81bf0e..aa229330 100644
--- a/ash/keyboard/ash_keyboard_controller.cc
+++ b/ash/keyboard/ash_keyboard_controller.cc
@@ -113,6 +113,14 @@
   UpdateEnableFlag(was_enabled);
 }
 
+void AshKeyboardController::GetEnableFlags(GetEnableFlagsCallback callback) {
+  const std::set<keyboard::mojom::KeyboardEnableFlag>& keyboard_enable_flags =
+      keyboard_controller_->keyboard_enable_flags();
+  std::vector<keyboard::mojom::KeyboardEnableFlag> flags(
+      keyboard_enable_flags.begin(), keyboard_enable_flags.end());
+  std::move(callback).Run(std::move(flags));
+}
+
 void AshKeyboardController::ReloadKeyboardIfNeeded() {
   keyboard_controller_->Reload();
 }
@@ -148,6 +156,35 @@
   }
 }
 
+void AshKeyboardController::SetContainerType(
+    keyboard::mojom::ContainerType container_type,
+    const base::Optional<gfx::Rect>& target_bounds,
+    SetContainerTypeCallback callback) {
+  keyboard_controller_->SetContainerType(container_type, target_bounds,
+                                         std::move(callback));
+}
+
+void AshKeyboardController::SetKeyboardLocked(bool locked) {
+  keyboard_controller_->set_keyboard_locked(locked);
+}
+
+void AshKeyboardController::SetOccludedBounds(
+    const std::vector<gfx::Rect>& bounds) {
+  // TODO(https://crbug.com/826617): Support occluded bounds with multiple
+  // rectangles.
+  keyboard_controller_->SetOccludedBounds(bounds.empty() ? gfx::Rect()
+                                                         : bounds[0]);
+}
+
+void AshKeyboardController::SetHitTestBounds(
+    const std::vector<gfx::Rect>& bounds) {
+  keyboard_controller_->SetHitTestBounds(bounds);
+}
+
+void AshKeyboardController::SetDraggableArea(const gfx::Rect& bounds) {
+  keyboard_controller_->SetDraggableArea(bounds);
+}
+
 void AshKeyboardController::OnSessionStateChanged(
     session_manager::SessionState state) {
   if (!keyboard_controller_->IsKeyboardEnableRequested())
@@ -232,6 +269,15 @@
   });
 }
 
+void AshKeyboardController::OnKeyboardEnableFlagsChanged(
+    std::set<keyboard::mojom::KeyboardEnableFlag>& keyboard_enable_flags) {
+  std::vector<keyboard::mojom::KeyboardEnableFlag> flags(
+      keyboard_enable_flags.begin(), keyboard_enable_flags.end());
+  observers_.ForAllPtrs([&flags](mojom::KeyboardControllerObserver* observer) {
+    observer->OnKeyboardEnableFlagsChanged(flags);
+  });
+}
+
 void AshKeyboardController::OnKeyboardEnabledChanged(bool is_enabled) {
   observers_.ForAllPtrs(
       [is_enabled](mojom::KeyboardControllerObserver* observer) {
diff --git a/ash/keyboard/ash_keyboard_controller.h b/ash/keyboard/ash_keyboard_controller.h
index 9ec8234..94d109f 100644
--- a/ash/keyboard/ash_keyboard_controller.h
+++ b/ash/keyboard/ash_keyboard_controller.h
@@ -64,11 +64,19 @@
   void IsKeyboardEnabled(IsKeyboardEnabledCallback callback) override;
   void SetEnableFlag(keyboard::mojom::KeyboardEnableFlag flag) override;
   void ClearEnableFlag(keyboard::mojom::KeyboardEnableFlag flag) override;
+  void GetEnableFlags(GetEnableFlagsCallback callback) override;
   void ReloadKeyboardIfNeeded() override;
   void RebuildKeyboardIfEnabled() override;
   void IsKeyboardVisible(IsKeyboardVisibleCallback callback) override;
   void ShowKeyboard() override;
   void HideKeyboard(mojom::HideReason reason) override;
+  void SetContainerType(keyboard::mojom::ContainerType container_type,
+                        const base::Optional<gfx::Rect>& target_bounds,
+                        SetContainerTypeCallback callback) override;
+  void SetKeyboardLocked(bool locked) override;
+  void SetOccludedBounds(const std::vector<gfx::Rect>& bounds) override;
+  void SetHitTestBounds(const std::vector<gfx::Rect>& bounds) override;
+  void SetDraggableArea(const gfx::Rect& bounds) override;
   void AddObserver(
       mojom::KeyboardControllerObserverAssociatedPtrInfo observer) override;
 
@@ -101,6 +109,9 @@
   void OnKeyboardConfigChanged() override;
   void OnKeyboardVisibilityStateChanged(bool is_visible) override;
   void OnKeyboardVisibleBoundsChanged(const gfx::Rect& bounds) override;
+  void OnKeyboardEnableFlagsChanged(
+      std::set<keyboard::mojom::KeyboardEnableFlag>& keyboard_enable_flags)
+      override;
   void OnKeyboardEnabledChanged(bool is_enabled) override;
 
   SessionController* session_controller_;  // unowned
diff --git a/ash/keyboard/ash_keyboard_controller_unittest.cc b/ash/keyboard/ash_keyboard_controller_unittest.cc
index 59f22fe..650c52a 100644
--- a/ash/keyboard/ash_keyboard_controller_unittest.cc
+++ b/ash/keyboard/ash_keyboard_controller_unittest.cc
@@ -5,15 +5,19 @@
 #include "ash/keyboard/ash_keyboard_controller.h"
 
 #include <memory>
+#include <vector>
+
 #include "ash/public/interfaces/keyboard_controller.mojom.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "base/run_loop.h"
+#include "base/stl_util.h"
 #include "base/test/scoped_task_environment.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/window.h"
+#include "ui/keyboard/container_behavior.h"
 #include "ui/keyboard/keyboard_controller.h"
 
 using keyboard::mojom::KeyboardConfig;
@@ -27,13 +31,17 @@
 class TestObserver : public mojom::KeyboardControllerObserver {
  public:
   explicit TestObserver(mojom::KeyboardController* controller) {
-    ash::mojom::KeyboardControllerObserverAssociatedPtrInfo ptr_info;
+    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_;
@@ -44,11 +52,18 @@
     config_ = *config;
   }
 
-  KeyboardConfig config_;
-  int destroyed_count_ = 0;
+  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:
-  mojo::AssociatedBinding<ash::mojom::KeyboardControllerObserver>
+  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);
@@ -57,69 +72,118 @@
 class TestClient {
  public:
   explicit TestClient(service_manager::Connector* connector) {
-    connector->BindInterface("test", &keyboard_controller_);
+    connector->BindInterface("test", &keyboard_controller_ptr_);
 
-    test_observer_ = std::make_unique<TestObserver>(keyboard_controller_.get());
+    test_observer_ =
+        std::make_unique<TestObserver>(keyboard_controller_ptr_.get());
   }
 
   ~TestClient() = default;
 
   bool IsKeyboardEnabled() {
-    keyboard_controller_->IsKeyboardEnabled(base::BindOnce(
+    keyboard_controller_ptr_->IsKeyboardEnabled(base::BindOnce(
         &TestClient::OnIsKeyboardEnabled, base::Unretained(this)));
-    keyboard_controller_.FlushForTesting();
+    keyboard_controller_ptr_.FlushForTesting();
     return is_enabled_;
   }
 
   void GetKeyboardConfig() {
-    keyboard_controller_->GetKeyboardConfig(base::BindOnce(
+    keyboard_controller_ptr_->GetKeyboardConfig(base::BindOnce(
         &TestClient::OnGetKeyboardConfig, base::Unretained(this)));
-    keyboard_controller_.FlushForTesting();
+    keyboard_controller_ptr_.FlushForTesting();
   }
 
   void SetKeyboardConfig(KeyboardConfigPtr config) {
-    keyboard_controller_->SetKeyboardConfig(std::move(config));
-    keyboard_controller_.FlushForTesting();
+    keyboard_controller_ptr_->SetKeyboardConfig(std::move(config));
+    keyboard_controller_ptr_.FlushForTesting();
   }
 
   void SetEnableFlag(KeyboardEnableFlag flag) {
-    keyboard_controller_->SetEnableFlag(flag);
-    keyboard_controller_.FlushForTesting();
+    keyboard_controller_ptr_->SetEnableFlag(flag);
+    keyboard_controller_ptr_.FlushForTesting();
   }
 
   void ClearEnableFlag(KeyboardEnableFlag flag) {
-    keyboard_controller_->ClearEnableFlag(flag);
-    keyboard_controller_.FlushForTesting();
+    keyboard_controller_ptr_->ClearEnableFlag(flag);
+    keyboard_controller_ptr_.FlushForTesting();
+  }
+
+  std::vector<keyboard::mojom::KeyboardEnableFlag> GetEnableFlags() {
+    std::vector<keyboard::mojom::KeyboardEnableFlag> enable_flags;
+    base::RunLoop run_loop;
+    keyboard_controller_ptr_->GetEnableFlags(base::BindOnce(
+        [](std::vector<keyboard::mojom::KeyboardEnableFlag>* enable_flags,
+           base::OnceClosure callback,
+           const std::vector<keyboard::mojom::KeyboardEnableFlag>& flags) {
+          *enable_flags = flags;
+          std::move(callback).Run();
+        },
+        &enable_flags, run_loop.QuitClosure()));
+    run_loop.Run();
+    return enable_flags;
   }
 
   void RebuildKeyboardIfEnabled() {
-    keyboard_controller_->RebuildKeyboardIfEnabled();
-    keyboard_controller_.FlushForTesting();
+    keyboard_controller_ptr_->RebuildKeyboardIfEnabled();
+    keyboard_controller_ptr_.FlushForTesting();
   }
 
   bool IsKeyboardVisible() {
-    keyboard_controller_->IsKeyboardVisible(base::BindOnce(
+    keyboard_controller_ptr_->IsKeyboardVisible(base::BindOnce(
         &TestClient::OnIsKeyboardVisible, base::Unretained(this)));
-    keyboard_controller_.FlushForTesting();
+    keyboard_controller_ptr_.FlushForTesting();
     return is_visible_;
   }
 
   void ShowKeyboard() {
-    keyboard_controller_->ShowKeyboard();
-    keyboard_controller_.FlushForTesting();
+    keyboard_controller_ptr_->ShowKeyboard();
+    keyboard_controller_ptr_.FlushForTesting();
   }
 
   void HideKeyboard() {
-    keyboard_controller_->HideKeyboard(ash::mojom::HideReason::kUser);
-    keyboard_controller_.FlushForTesting();
+    keyboard_controller_ptr_->HideKeyboard(mojom::HideReason::kUser);
+    keyboard_controller_ptr_.FlushForTesting();
+  }
+
+  bool SetContainerType(keyboard::mojom::ContainerType container_type,
+                        const base::Optional<gfx::Rect>& target_bounds) {
+    bool result;
+    base::RunLoop run_loop;
+    keyboard_controller_ptr_->SetContainerType(
+        container_type, target_bounds,
+        base::BindOnce(
+            [](bool* result_ptr, base::OnceClosure callback, bool result) {
+              *result_ptr = result;
+              std::move(callback).Run();
+            },
+            &result, run_loop.QuitClosure()));
+    run_loop.Run();
+    return result;
+  }
+
+  void SetKeyboardLocked(bool locked) {
+    keyboard_controller_ptr_->SetKeyboardLocked(locked);
+    keyboard_controller_ptr_.FlushForTesting();
+  }
+
+  void SetOccludedBounds(const std::vector<gfx::Rect>& bounds) {
+    keyboard_controller_ptr_->SetOccludedBounds(bounds);
+    keyboard_controller_ptr_.FlushForTesting();
+  }
+
+  void SetHitTestBounds(const std::vector<gfx::Rect>& bounds) {
+    keyboard_controller_ptr_->SetHitTestBounds(bounds);
+    keyboard_controller_ptr_.FlushForTesting();
+  }
+
+  void SetDraggableArea(const gfx::Rect& bounds) {
+    keyboard_controller_ptr_->SetDraggableArea(bounds);
+    keyboard_controller_ptr_.FlushForTesting();
   }
 
   TestObserver* test_observer() const { return test_observer_.get(); }
-
-  bool is_enabled_ = false;
-  bool is_visible_ = false;
-  int got_keyboard_config_count_ = 0;
-  KeyboardConfig keyboard_config_;
+  int got_keyboard_config_count() const { return got_keyboard_config_count_; }
+  const KeyboardConfig& keyboard_config() const { return keyboard_config_; }
 
  private:
   void OnIsKeyboardEnabled(bool enabled) { is_enabled_ = enabled; }
@@ -130,10 +194,83 @@
     keyboard_config_ = *config;
   }
 
-  mojom::KeyboardControllerPtr keyboard_controller_;
+  bool is_enabled_ = false;
+  bool is_visible_ = false;
+  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 {
+ public:
+  TestContainerBehavior() : keyboard::ContainerBehavior(nullptr){};
+  ~TestContainerBehavior() override = default;
+
+  // keyboard::ContainerBehavior
+  void DoShowingAnimation(
+      aura::Window* window,
+      ui::ScopedLayerAnimationSettings* animation_settings) override {}
+
+  void DoHidingAnimation(
+      aura::Window* window,
+      wm::ScopedHidingAnimationSettings* animation_settings) override {}
+
+  void InitializeShowAnimationStartingState(aura::Window* container) override {}
+
+  gfx::Rect AdjustSetBoundsRequest(
+      const gfx::Rect& display_bounds,
+      const gfx::Rect& requested_bounds_in_screen_coords) override {
+    return gfx::Rect();
+  }
+
+  void SetCanonicalBounds(aura::Window* container,
+                          const gfx::Rect& display_bounds) override {}
+
+  bool IsOverscrollAllowed() const override { return true; }
+
+  bool IsDragHandle(const gfx::Vector2d& offset,
+                    const gfx::Size& keyboard_size) const override {
+    return false;
+  }
+
+  void SavePosition(const gfx::Rect& keyboard_bounds_in_screen,
+                    const gfx::Size& screen_size) override {}
+
+  bool HandlePointerEvent(const ui::LocatedEvent& event,
+                          const display::Display& current_display) override {
+    return false;
+  }
+
+  keyboard::mojom::ContainerType GetType() const override { return type_; }
+
+  bool TextBlurHidesKeyboard() const override { return false; }
+
+  void SetOccludedBounds(const gfx::Rect& bounds) override {
+    occluded_bounds_ = bounds;
+  }
+
+  gfx::Rect GetOccludedBounds(
+      const gfx::Rect& visual_bounds_in_window) const override {
+    return occluded_bounds_;
+  }
+
+  bool OccludedBoundsAffectWorkspaceLayout() const override { return false; }
+
+  void SetDraggableArea(const gfx::Rect& rect) override {
+    draggable_area_ = rect;
+  }
+
+  const gfx::Rect& occluded_bounds() const { return occluded_bounds_; }
+  const gfx::Rect& draggable_area() const { return draggable_area_; }
+
+ private:
+  keyboard::mojom::ContainerType type_ =
+      keyboard::mojom::ContainerType::kFullWidth;
+  gfx::Rect occluded_bounds_;
+  gfx::Rect draggable_area_;
+};
+
 class AshKeyboardControllerTest : public AshTestBase {
  public:
   AshKeyboardControllerTest() = default;
@@ -149,7 +286,8 @@
 
     service_manager::Connector::TestApi test_api(connector_.get());
     test_api.OverrideBinderForTesting(
-        service_manager::Identity("test"), mojom::KeyboardController::Name_,
+        service_manager::ServiceFilter::ByName("test"),
+        mojom::KeyboardController::Name_,
         base::BindRepeating(
             &AshKeyboardControllerTest::AddKeyboardControllerBinding,
             base::Unretained(this)));
@@ -158,7 +296,7 @@
     test_client_ = std::make_unique<TestClient>(connector_.get());
 
     // Set the initial observer config to the client (default) config.
-    test_client_->test_observer()->config_ = test_client()->keyboard_config_;
+    test_client_->test_observer()->set_config(test_client()->keyboard_config());
   }
 
   void TearDown() override {
@@ -187,7 +325,7 @@
 
 TEST_F(AshKeyboardControllerTest, GetKeyboardConfig) {
   test_client()->GetKeyboardConfig();
-  EXPECT_EQ(1, test_client()->got_keyboard_config_count_);
+  EXPECT_EQ(1, test_client()->got_keyboard_config_count());
 }
 
 TEST_F(AshKeyboardControllerTest, SetKeyboardConfig) {
@@ -195,11 +333,11 @@
   test_client()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
 
   test_client()->GetKeyboardConfig();
-  EXPECT_EQ(1, test_client()->got_keyboard_config_count_);
+  EXPECT_EQ(1, test_client()->got_keyboard_config_count());
   KeyboardConfigPtr config =
-      KeyboardConfig::New(test_client()->keyboard_config_);
+      KeyboardConfig::New(test_client()->keyboard_config());
   // Set the observer config to the client (default) config.
-  test_client()->test_observer()->config_ = *config;
+  test_client()->test_observer()->set_config(*config);
 
   // Change the keyboard config.
   bool old_auto_complete = config->auto_complete;
@@ -208,65 +346,157 @@
 
   // Test that the config changes.
   test_client()->GetKeyboardConfig();
-  EXPECT_NE(old_auto_complete, test_client()->keyboard_config_.auto_complete);
+  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);
+            test_client()->test_observer()->config().auto_complete);
 }
 
-TEST_F(AshKeyboardControllerTest, Enabled) {
+TEST_F(AshKeyboardControllerTest, EnableFlags) {
   EXPECT_FALSE(test_client()->IsKeyboardEnabled());
   // Enable the keyboard.
   test_client()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
+  std::vector<keyboard::mojom::KeyboardEnableFlag> enable_flags =
+      test_client()->GetEnableFlags();
+  EXPECT_TRUE(
+      base::ContainsValue(enable_flags, KeyboardEnableFlag::kExtensionEnabled));
+  EXPECT_EQ(enable_flags, test_client()->test_observer()->enable_flags());
   EXPECT_TRUE(test_client()->IsKeyboardEnabled());
 
   // Set the enable override to disable the keyboard.
   test_client()->SetEnableFlag(KeyboardEnableFlag::kPolicyDisabled);
+  enable_flags = test_client()->GetEnableFlags();
+  EXPECT_TRUE(
+      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_FALSE(test_client()->IsKeyboardEnabled());
 
   // Clear the enable override; should enable the keyboard.
   test_client()->ClearEnableFlag(KeyboardEnableFlag::kPolicyDisabled);
+  enable_flags = test_client()->GetEnableFlags();
+  EXPECT_TRUE(
+      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_TRUE(test_client()->IsKeyboardEnabled());
 }
 
 TEST_F(AshKeyboardControllerTest, RebuildKeyboardIfEnabled) {
-  EXPECT_EQ(0, test_client()->test_observer()->destroyed_count_);
+  EXPECT_EQ(0, test_client()->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_client()->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_client()->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_client()->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_client()->test_observer()->destroyed_count());
 }
 
 TEST_F(AshKeyboardControllerTest, ShowAndHideKeyboard) {
   // Enable the keyboard. This will create the keyboard window but not show it.
   test_client()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
-  base::RunLoop().RunUntilIdle();
   ASSERT_TRUE(keyboard_controller()->GetKeyboardWindow());
   EXPECT_FALSE(keyboard_controller()->GetKeyboardWindow()->IsVisible());
 
   test_client()->ShowKeyboard();
-  base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(keyboard_controller()->GetKeyboardWindow()->IsVisible());
 
   test_client()->HideKeyboard();
-  base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(keyboard_controller()->GetKeyboardWindow()->IsVisible());
 
   // TODO(stevenjb): Also use TestObserver and IsKeyboardVisible to test
   // visibility changes. https://crbug.com/849995.
 }
 
+TEST_F(AshKeyboardControllerTest, SetContainerType) {
+  // Enable the keyboard.
+  test_client()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
+  const auto default_behavior = keyboard::mojom::ContainerType::kFullWidth;
+  EXPECT_EQ(default_behavior, keyboard_controller()->GetActiveContainerType());
+
+  gfx::Rect target_bounds(0, 0, 10, 10);
+  // Set the container type to kFloating.
+  EXPECT_TRUE(test_client()->SetContainerType(
+      keyboard::mojom::ContainerType::kFloating, target_bounds));
+  EXPECT_EQ(keyboard::mojom::ContainerType::kFloating,
+            keyboard_controller()->GetActiveContainerType());
+  // Ensure that the window size is correct (position is determined by Ash).
+  EXPECT_EQ(
+      target_bounds.size(),
+      keyboard_controller()->GetKeyboardWindow()->GetTargetBounds().size());
+
+  // Set the container type to kFullscreen.
+  EXPECT_TRUE(test_client()->SetContainerType(
+      keyboard::mojom::ContainerType::kFullscreen, base::nullopt));
+  EXPECT_EQ(keyboard::mojom::ContainerType::kFullscreen,
+            keyboard_controller()->GetActiveContainerType());
+
+  // Setting the container type to the current type should fail.
+  EXPECT_FALSE(test_client()->SetContainerType(
+      keyboard::mojom::ContainerType::kFullscreen, base::nullopt));
+  EXPECT_EQ(keyboard::mojom::ContainerType::kFullscreen,
+            keyboard_controller()->GetActiveContainerType());
+}
+
+TEST_F(AshKeyboardControllerTest, SetKeyboardLocked) {
+  ASSERT_FALSE(keyboard_controller()->keyboard_locked());
+  test_client()->SetKeyboardLocked(true);
+  EXPECT_TRUE(keyboard_controller()->keyboard_locked());
+  test_client()->SetKeyboardLocked(false);
+  EXPECT_FALSE(keyboard_controller()->keyboard_locked());
+}
+
+TEST_F(AshKeyboardControllerTest, SetOccludedBounds) {
+  // Enable the keyboard.
+  test_client()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
+
+  // Override the container behavior.
+  auto scoped_behavior = std::make_unique<TestContainerBehavior>();
+  TestContainerBehavior* behavior = scoped_behavior.get();
+  keyboard_controller()->set_container_behavior_for_test(
+      std::move(scoped_behavior));
+
+  gfx::Rect bounds(10, 20, 30, 40);
+  test_client()->SetOccludedBounds({bounds});
+  EXPECT_EQ(bounds, behavior->occluded_bounds());
+}
+
+TEST_F(AshKeyboardControllerTest, SetHitTestBounds) {
+  // Enable the keyboard.
+  test_client()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
+  ASSERT_FALSE(keyboard_controller()->GetKeyboardWindow()->targeter());
+
+  // Setting the hit test bounds should set a WindowTargeter.
+  test_client()->SetHitTestBounds({gfx::Rect(10, 20, 30, 40)});
+  ASSERT_TRUE(keyboard_controller()->GetKeyboardWindow()->targeter());
+}
+
+TEST_F(AshKeyboardControllerTest, SetDraggableArea) {
+  // Enable the keyboard.
+  test_client()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
+
+  // Override the container behavior.
+  auto scoped_behavior = std::make_unique<TestContainerBehavior>();
+  TestContainerBehavior* behavior = scoped_behavior.get();
+  keyboard_controller()->set_container_behavior_for_test(
+      std::move(scoped_behavior));
+
+  gfx::Rect bounds(10, 20, 30, 40);
+  test_client()->SetDraggableArea(bounds);
+  EXPECT_EQ(bounds, behavior->draggable_area());
+}
+
 }  // namespace ash
diff --git a/ash/keyboard/virtual_keyboard_controller.cc b/ash/keyboard/virtual_keyboard_controller.cc
index 08bc9a4..36010e8 100644
--- a/ash/keyboard/virtual_keyboard_controller.cc
+++ b/ash/keyboard/virtual_keyboard_controller.cc
@@ -24,8 +24,8 @@
 #include "ui/events/devices/input_device_manager.h"
 #include "ui/events/devices/touchscreen_device.h"
 #include "ui/keyboard/keyboard_controller.h"
-#include "ui/keyboard/keyboard_switches.h"
 #include "ui/keyboard/keyboard_util.h"
+#include "ui/keyboard/public/keyboard_switches.h"
 
 namespace ash {
 namespace {
@@ -103,9 +103,10 @@
 
   keyboard::KeyboardController::Get()->AddObserver(this);
 
-  bluetooth_devices_observer_ = std::make_unique<BluetoothDevicesObserver>(
-      base::BindRepeating(&VirtualKeyboardController::UpdateBluetoothDevice,
-                          base::Unretained(this)));
+  bluetooth_devices_observer_ =
+      std::make_unique<BluetoothDevicesObserver>(base::BindRepeating(
+          &VirtualKeyboardController::OnBluetoothAdapterOrDeviceChanged,
+          base::Unretained(this)));
 }
 
 VirtualKeyboardController::~VirtualKeyboardController() {
@@ -301,16 +302,15 @@
     Shell::Get()->EnableKeyboard();
 }
 
-void VirtualKeyboardController::UpdateBluetoothDevice(
+void VirtualKeyboardController::OnBluetoothAdapterOrDeviceChanged(
     device::BluetoothDevice* device) {
   // We only care about keyboard type bluetooth device change.
-  if (device->GetDeviceType() != device::BluetoothDeviceType::KEYBOARD &&
-      device->GetDeviceType() !=
+  if (!device ||
+      device->GetDeviceType() == device::BluetoothDeviceType::KEYBOARD ||
+      device->GetDeviceType() ==
           device::BluetoothDeviceType::KEYBOARD_MOUSE_COMBO) {
-    return;
+    UpdateDevices();
   }
-
-  UpdateDevices();
 }
 
 }  // namespace ash
diff --git a/ash/keyboard/virtual_keyboard_controller.h b/ash/keyboard/virtual_keyboard_controller.h
index 0583ea4..fcaa6a0 100644
--- a/ash/keyboard/virtual_keyboard_controller.h
+++ b/ash/keyboard/virtual_keyboard_controller.h
@@ -73,9 +73,9 @@
   // Force enable the keyboard and show it, even in laptop mode.
   void ForceShowKeyboard();
 
-  // Callback function of |bluetooth_devices_observer_|. Called when |device|
-  // changes.
-  void UpdateBluetoothDevice(device::BluetoothDevice* device);
+  // Callback function of |bluetooth_devices_observer_|. Called when the
+  // bluetooth adapter or |device| changes.
+  void OnBluetoothAdapterOrDeviceChanged(device::BluetoothDevice* device);
 
   // True if an external keyboard is connected.
   bool has_external_keyboard_;
diff --git a/ash/keyboard/virtual_keyboard_controller_unittest.cc b/ash/keyboard/virtual_keyboard_controller_unittest.cc
index edc26ef..56ba22e8 100644
--- a/ash/keyboard/virtual_keyboard_controller_unittest.cc
+++ b/ash/keyboard/virtual_keyboard_controller_unittest.cc
@@ -23,7 +23,7 @@
 #include "ui/display/test/display_manager_test_api.h"
 #include "ui/events/devices/input_device.h"
 #include "ui/events/devices/touchscreen_device.h"
-#include "ui/keyboard/keyboard_switches.h"
+#include "ui/keyboard/public/keyboard_switches.h"
 #include "ui/keyboard/test/keyboard_test_util.h"
 
 using keyboard::mojom::KeyboardEnableFlag;
diff --git a/ash/keyboard/virtual_keyboard_unittest.cc b/ash/keyboard/virtual_keyboard_unittest.cc
index 9970e31..6136050 100644
--- a/ash/keyboard/virtual_keyboard_unittest.cc
+++ b/ash/keyboard/virtual_keyboard_unittest.cc
@@ -8,8 +8,8 @@
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/keyboard/keyboard_controller.h"
-#include "ui/keyboard/keyboard_switches.h"
 #include "ui/keyboard/keyboard_util.h"
+#include "ui/keyboard/public/keyboard_switches.h"
 #include "ui/keyboard/test/keyboard_test_util.h"
 
 namespace ash {
@@ -112,8 +112,9 @@
   // Change the container behavior, which should reset the hit test bounds to
   // the whole keyboard window.
   keyboard_controller->HideKeyboardExplicitlyBySystem();
-  keyboard_controller->SetContainerType(keyboard::ContainerType::FLOATING,
-                                        base::nullopt, base::DoNothing());
+  keyboard_controller->SetContainerType(
+      keyboard::mojom::ContainerType::kFloating, base::nullopt,
+      base::DoNothing());
   keyboard_controller->ShowKeyboard(false);
 
   // (0, 0) should no longer pass through the keyboard window.
diff --git a/ash/login/login_screen_controller.cc b/ash/login/login_screen_controller.cc
index ab10dec..1dca8ab 100644
--- a/ash/login/login_screen_controller.cc
+++ b/ash/login/login_screen_controller.cc
@@ -495,8 +495,8 @@
   Shelf* shelf = Shelf::ForWindow(Shell::Get()->GetPrimaryRootWindow());
   // Tell the focus direction to the status area or the shelf so they can focus
   // the correct child view.
-  if (reverse && !ShelfWidget::IsUsingViewsShelf()) {
-    if (Shell::GetPrimaryRootWindowController()->IsSystemTrayVisible())
+  if (reverse || !ShelfWidget::IsUsingViewsShelf()) {
+    if (!Shell::GetPrimaryRootWindowController()->IsSystemTrayVisible())
       return;
     shelf->GetStatusAreaWidget()
         ->status_area_widget_delegate()
diff --git a/ash/login/login_screen_test_api.cc b/ash/login/login_screen_test_api.cc
new file mode 100644
index 0000000..b7c216c
--- /dev/null
+++ b/ash/login/login_screen_test_api.cc
@@ -0,0 +1,62 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/login/login_screen_test_api.h"
+
+#include <memory>
+#include <utility>
+
+#include "ash/login/ui/lock_contents_view.h"
+#include "ash/login/ui/lock_screen.h"
+#include "ash/login/ui/lock_window.h"
+#include "ash/login/ui/login_auth_user_view.h"
+#include "ash/login/ui/login_big_user_view.h"
+#include "ash/login/ui/login_password_view.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "ui/views/controls/textfield/textfield.h"
+
+namespace ash {
+
+// static
+void LoginScreenTestApi::BindRequest(mojom::LoginScreenTestApiRequest request) {
+  mojo::MakeStrongBinding(std::make_unique<LoginScreenTestApi>(),
+                          std::move(request));
+}
+
+LoginScreenTestApi::LoginScreenTestApi() = default;
+
+LoginScreenTestApi::~LoginScreenTestApi() = default;
+
+void LoginScreenTestApi::IsLockShown(IsLockShownCallback callback) {
+  std::move(callback).Run(
+      LockScreen::HasInstance() && LockScreen::Get()->is_shown() &&
+      LockScreen::Get()->screen_type() == LockScreen::ScreenType::kLock);
+}
+
+void LoginScreenTestApi::SubmitPassword(const AccountId& account_id,
+                                        const std::string& password,
+                                        SubmitPasswordCallback callback) {
+  // It'd be better to generate keyevents dynamically and dispatch them instead
+  // of reaching into the views structure, but at the time of writing I could
+  // not find a good way to do this. If you know of a way feel free to change
+  // this code.
+  LockScreen::TestApi lock_screen_test(LockScreen::Get());
+  LockContentsView::TestApi lock_contents_test(
+      lock_screen_test.contents_view());
+  LoginAuthUserView::TestApi auth_test(
+      lock_contents_test.primary_big_view()->auth_user());
+  LoginPasswordView::TestApi password_test(auth_test.password_view());
+
+  // For the time being, only the primary user is supported. To support multiple
+  // users this API needs to search all user views for the associated user and
+  // potentially activate that user so it is showing its password field.
+  CHECK_EQ(account_id,
+           auth_test.user_view()->current_user()->basic_user_info->account_id);
+
+  password_test.SubmitPassword(password);
+
+  std::move(callback).Run();
+}
+
+}  // namespace ash
diff --git a/ash/login/login_screen_test_api.h b/ash/login/login_screen_test_api.h
new file mode 100644
index 0000000..4b60034
--- /dev/null
+++ b/ash/login/login_screen_test_api.h
@@ -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.
+
+#ifndef ASH_LOGIN_LOGIN_SCREEN_TEST_API_H_
+#define ASH_LOGIN_LOGIN_SCREEN_TEST_API_H_
+
+#include "ash/public/interfaces/login_screen_test_api.mojom.h"
+#include "base/macros.h"
+#include "components/account_id/account_id.h"
+
+namespace ash {
+
+// Allows tests to access private state of the login/lock screens.
+class LoginScreenTestApi : public mojom::LoginScreenTestApi {
+ public:
+  // Creates and binds an instance from a remote request (e.g. from chrome).
+  static void BindRequest(mojom::LoginScreenTestApiRequest request);
+
+  LoginScreenTestApi();
+  ~LoginScreenTestApi() override;
+
+  // mojom::LoginScreen:
+  void IsLockShown(IsLockShownCallback callback) override;
+  void SubmitPassword(const AccountId& account_id,
+                      const std::string& password,
+                      SubmitPasswordCallback callback) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LoginScreenTestApi);
+};
+
+}  // namespace ash
+
+#endif  // ASH_LOGIN_LOGIN_SCREEN_TEST_API_H_
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 23471be..16c33721 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -102,7 +102,7 @@
 class AuthErrorLearnMoreButton : public views::Button,
                                  public views::ButtonListener {
  public:
-  AuthErrorLearnMoreButton(LoginBubble* parent_bubble)
+  explicit AuthErrorLearnMoreButton(LoginBubble* parent_bubble)
       : views::Button(this), parent_bubble_(parent_bubble) {
     SetLayoutManager(std::make_unique<views::FillLayout>());
     auto* label =
@@ -115,6 +115,8 @@
     label->SetFontList(base_font_list.Derive(0, gfx::Font::FontStyle::NORMAL,
                                              gfx::Font::Weight::NORMAL));
     AddChildView(label);
+
+    SetAccessibleName(l10n_util::GetStringUTF16(IDS_ASH_LEARN_MORE));
   }
 
   void ButtonPressed(Button* sender, const ui::Event& event) override {
@@ -309,6 +311,11 @@
   return view_->warning_banner_bubble_.get();
 }
 
+LoginBubble* LockContentsView::TestApi::supervised_user_deprecation_bubble()
+    const {
+  return view_->supervised_user_deprecation_bubble_.get();
+}
+
 views::View* LockContentsView::TestApi::system_info() const {
   return view_->system_info_;
 }
@@ -351,6 +358,7 @@
   Shell::Get()->system_tray_notifier()->AddSystemTrayFocusObserver(this);
   keyboard::KeyboardController::Get()->AddObserver(this);
   auth_error_bubble_ = std::make_unique<LoginBubble>();
+  supervised_user_deprecation_bubble_ = std::make_unique<LoginBubble>();
   detachable_base_error_bubble_ = std::make_unique<LoginBubble>();
   tooltip_bubble_ = std::make_unique<LoginBubble>();
   warning_banner_bubble_ = std::make_unique<LoginBubble>();
@@ -1447,13 +1455,14 @@
 }
 
 void LockContentsView::OnBigUserChanged() {
-  const AccountId new_big_user =
-      CurrentBigUserView()->GetCurrentUser()->basic_user_info->account_id;
+  const mojom::LoginUserInfoPtr& big_user =
+      CurrentBigUserView()->GetCurrentUser();
+  const AccountId big_user_account_id = big_user->basic_user_info->account_id;
 
   CurrentBigUserView()->RequestFocus();
 
-  Shell::Get()->login_screen_controller()->OnFocusPod(new_big_user);
-  UpdateEasyUnlockIconForUser(new_big_user);
+  Shell::Get()->login_screen_controller()->OnFocusPod(big_user_account_id);
+  UpdateEasyUnlockIconForUser(big_user_account_id);
 
   if (unlock_attempt_ > 0) {
     // Times a password was incorrectly entered until user gives up (change
@@ -1465,6 +1474,24 @@
     unlock_attempt_ = 0;
   }
 
+  // http://crbug/866790: After Supervised Users are deprecated, remove this.
+  if (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.
+    views::Label* label =
+        new views::Label(message, views::style::CONTEXT_MESSAGE_BOX_BODY_TEXT,
+                         views::style::STYLE_PRIMARY);
+    label->SetMultiLine(true);
+    label->SetAutoColorReadabilityEnabled(false);
+    label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    label->SetEnabledColor(SK_ColorWHITE);
+    supervised_user_deprecation_bubble_->ShowErrorBubble(
+        label,
+        CurrentBigUserView()->auth_user()->password_view() /*anchor_view*/,
+        LoginBubble::kFlagPersistent);
+  }
+
   // The new auth user might have different last used detachable base - make
   // sure the detachable base pairing error is updated if needed.
   OnDetachableBasePairingStatusChanged(
diff --git a/ash/login/ui/lock_contents_view.h b/ash/login/ui/lock_contents_view.h
index 6807ed3..e569d87 100644
--- a/ash/login/ui/lock_contents_view.h
+++ b/ash/login/ui/lock_contents_view.h
@@ -84,6 +84,7 @@
     LoginBubble* auth_error_bubble() const;
     LoginBubble* detachable_base_error_bubble() const;
     LoginBubble* warning_banner_bubble() const;
+    LoginBubble* supervised_user_deprecation_bubble() const;
     views::View* system_info() const;
     LoginExpandedPublicAccountView* expanded_view() const;
     views::View* main_view() const;
@@ -384,6 +385,9 @@
   // Bubble for displaying warning banner message.
   std::unique_ptr<LoginBubble> warning_banner_bubble_;
 
+  // Bubble for displaying supervised user deprecation message.
+  std::unique_ptr<LoginBubble> supervised_user_deprecation_bubble_;
+
   int unlock_attempt_ = 0;
 
   // Whether a lock screen app is currently active (i.e. lock screen note action
diff --git a/ash/login/ui/login_base_bubble_view.cc b/ash/login/ui/login_base_bubble_view.cc
index 00daf98..efe6305 100644
--- a/ash/login/ui/login_base_bubble_view.cc
+++ b/ash/login/ui/login_base_bubble_view.cc
@@ -26,6 +26,10 @@
 }  // namespace
 
 LoginBaseBubbleView::LoginBaseBubbleView(views::View* anchor_view)
+    : LoginBaseBubbleView(anchor_view, nullptr) {}
+
+LoginBaseBubbleView::LoginBaseBubbleView(views::View* anchor_view,
+                                         aura::Window* parent_window)
     : BubbleDialogDelegateView(anchor_view, views::BubbleBorder::NONE) {
   set_margins(gfx::Insets(kBubbleTopMarginDp, kBubbleHorizontalMarginDp,
                           kBubbleBottomMarginDp, kBubbleHorizontalMarginDp));
@@ -36,22 +40,26 @@
   // Layer rendering is needed for animation.
   SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
+
+  set_parent_window(parent_window);
 }
 
 LoginBaseBubbleView::~LoginBaseBubbleView() = default;
 
+LoginButton* LoginBaseBubbleView::GetBubbleOpener() const {
+  return nullptr;
+}
+
 void LoginBaseBubbleView::OnBeforeBubbleWidgetInit(
     views::Widget::InitParams* params,
     views::Widget* widget) const {
-  // Login bubbles must always be associated with the lock screen container,
-  // otherwise they may not show up as other containers are hidden.
-  //
-  // params->parent may be already set if the bubble has an anchor view.
-
-  // Shell may be null in tests.
+  // This case only gets called if the bubble has no anchor and no parent
+  // container was specified. In this case, the parent container should default
+  // to MenuContainer, so that login bubbles are visible over the shelf and
+  // virtual keyboard. Shell may be null in tests.
   if (!params->parent && Shell::HasInstance()) {
     params->parent = Shell::GetContainer(Shell::GetPrimaryRootWindow(),
-                                         kShellWindowId_LockScreenContainer);
+                                         kShellWindowId_MenuContainer);
   }
 }
 
diff --git a/ash/login/ui/login_base_bubble_view.h b/ash/login/ui/login_base_bubble_view.h
index 48b8f9a..4f89b1d 100644
--- a/ash/login/ui/login_base_bubble_view.h
+++ b/ash/login/ui/login_base_bubble_view.h
@@ -6,6 +6,7 @@
 #define ASH_LOGIN_UI_LOGIN_BASE_BUBBLE_VIEW_H_
 
 #include "ash/ash_export.h"
+#include "ash/login/ui/login_button.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/view.h"
 
@@ -14,9 +15,16 @@
 // Base bubble view for login screen bubbles.
 class ASH_EXPORT LoginBaseBubbleView : public views::BubbleDialogDelegateView {
  public:
+  // Without specifying a parent_window, the bubble will default to being in the
+  // same container as anchor_view.
   explicit LoginBaseBubbleView(views::View* anchor_view);
+  explicit LoginBaseBubbleView(views::View* anchor_view,
+                               gfx::NativeView parent_window);
   ~LoginBaseBubbleView() override;
 
+  // Returns the button responsible for opening this bubble.
+  virtual LoginButton* GetBubbleOpener() 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 0be80ec..aae18d7 100644
--- a/ash/login/ui/login_bubble.cc
+++ b/ash/login/ui/login_bubble.cc
@@ -15,6 +15,7 @@
 #include "ash/login/ui/non_accessible_view.h"
 #include "ash/login/ui/views_utils.h"
 #include "ash/public/cpp/ash_constants.h"
+#include "ash/public/cpp/shell_window_ids.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -93,8 +94,10 @@
 
 class LoginErrorBubbleView : public LoginBaseBubbleView {
  public:
-  LoginErrorBubbleView(views::View* content, views::View* anchor_view)
-      : LoginBaseBubbleView(anchor_view) {
+  LoginErrorBubbleView(views::View* content,
+                       views::View* anchor_view,
+                       aura::Window* container)
+      : LoginBaseBubbleView(anchor_view, container) {
     set_anchor_view_insets(
         gfx::Insets(kAnchorViewErrorBubbleVerticalSpacingDp, 0));
 
@@ -182,11 +185,13 @@
                     user_manager::UserType type,
                     bool is_owner,
                     views::View* anchor_view,
+                    LoginButton* bubble_opener_,
                     bool show_remove_user,
                     base::OnceClosure on_remove_user_warning_shown,
                     base::OnceClosure on_remove_user_requested)
       : LoginBaseBubbleView(anchor_view),
         bubble_(bubble),
+        bubble_opener_(bubble_opener_),
         on_remove_user_warning_shown_(std::move(on_remove_user_warning_shown)),
         on_remove_user_requested_(std::move(on_remove_user_requested)) {
     // This view has content the user can interact with if the remove user
@@ -240,8 +245,12 @@
       username_label_->SetMaxLines(1);
       container->AddChildView(username_label_);
       add_space(container, kBubbleBetweenChildSpacingDp);
-      container->AddChildView(CreateLabel(
-          email, SkColorSetA(SK_ColorWHITE, kSubMessageColorAlpha)));
+      views::Label* email_label =
+          CreateLabel(email, SkColorSetA(SK_ColorWHITE, kSubMessageColorAlpha));
+      // Do not change these two lines for the same reasons as above.
+      email_label->SetMultiLine(true);
+      email_label->SetMaxLines(1);
+      container->AddChildView(email_label);
     }
 
     // Remove user.
@@ -318,6 +327,9 @@
 
   ~LoginUserMenuView() override = default;
 
+  // LoginBaseBubbleView:
+  LoginButton* GetBubbleOpener() const override { return bubble_opener_; }
+
   // views::View:
   const char* GetClassName() const override { return "LoginUserMenuView"; }
   gfx::Size CalculatePreferredSize() const override {
@@ -377,6 +389,7 @@
 
  private:
   LoginBubble* bubble_ = nullptr;
+  LoginButton* bubble_opener_ = nullptr;
   base::OnceClosure on_remove_user_warning_shown_;
   base::OnceClosure on_remove_user_requested_;
   views::View* remove_user_confirm_data_ = nullptr;
@@ -445,7 +458,9 @@
     CloseImmediately();
 
   flags_ = flags;
-  bubble_view_ = new LoginErrorBubbleView(content, anchor_view);
+  aura::Window* menu_container = Shell::GetContainer(
+      Shell::GetPrimaryRootWindow(), kShellWindowId_MenuContainer);
+  bubble_view_ = new LoginErrorBubbleView(content, anchor_view, menu_container);
 
   Show();
 }
@@ -463,12 +478,12 @@
     CloseImmediately();
 
   flags_ = kFlagsNone;
-  bubble_opener_ = bubble_opener;
-  bubble_view_ = new LoginUserMenuView(this, username, email, type, is_owner,
-                                       anchor_view, show_remove_user,
-                                       std::move(on_remove_user_warning_shown),
-                                       std::move(on_remove_user_requested));
-  bool had_focus = bubble_opener_->HasFocus();
+  bubble_view_ = new LoginUserMenuView(
+      this, username, email, type, is_owner, anchor_view, bubble_opener,
+      show_remove_user, std::move(on_remove_user_warning_shown),
+      std::move(on_remove_user_requested));
+  bool had_focus = bubble_view_->GetBubbleOpener() &&
+                   bubble_view_->GetBubbleOpener()->HasFocus();
   Show();
   if (had_focus) {
     // Try to focus the bubble view only if the bubble opener was focused.
@@ -486,14 +501,13 @@
   Show();
 }
 
-void LoginBubble::ShowSelectionMenu(LoginMenuView* menu,
-                                    LoginButton* bubble_opener) {
+void LoginBubble::ShowSelectionMenu(LoginMenuView* menu) {
   if (bubble_view_)
     CloseImmediately();
 
   flags_ = kFlagsNone;
-  bubble_opener_ = bubble_opener;
-  const bool had_focus = bubble_opener_->HasFocus();
+  const bool had_focus =
+      menu->GetBubbleOpener() && menu->GetBubbleOpener()->HasFocus();
 
   // Transfer the ownership of |menu| to bubble widget.
   bubble_view_ = menu;
@@ -557,7 +571,8 @@
 
   // If current focus view is the button view, don't process the event here,
   // let the button logic handle the event and determine show/hide behavior.
-  if (bubble_opener_ && bubble_opener_->HasFocus())
+  if (bubble_view_->GetBubbleOpener() &&
+      bubble_view_->GetBubbleOpener()->HasFocus())
     return;
 
   // If |bubble_view_| is interactive do not close it.
@@ -623,8 +638,9 @@
 
   // If the user clicks on the button view, don't process the event here,
   // let the button logic handle the event and determine show/hide behavior.
-  if (bubble_opener_) {
-    gfx::Rect bubble_opener_bounds = bubble_opener_->GetBoundsInScreen();
+  if (bubble_view_->GetBubbleOpener()) {
+    gfx::Rect bubble_opener_bounds =
+        bubble_view_->GetBubbleOpener()->GetBoundsInScreen();
     if (bubble_opener_bounds.Contains(screen_location))
       return;
   }
@@ -637,10 +653,11 @@
   if (!bubble_view_ || is_visible_ == visible)
     return;
 
-  if (bubble_opener_) {
-    bubble_opener_->AnimateInkDrop(visible ? views::InkDropState::ACTIVATED
-                                           : views::InkDropState::DEACTIVATED,
-                                   nullptr /*event*/);
+  if (bubble_view_->GetBubbleOpener()) {
+    bubble_view_->GetBubbleOpener()->AnimateInkDrop(
+        visible ? views::InkDropState::ACTIVATED
+                : views::InkDropState::DEACTIVATED,
+        nullptr /*event*/);
   }
 
   ui::Layer* layer = bubble_view_->layer();
@@ -676,7 +693,6 @@
   if (!widget_already_closing)
     bubble_view_->GetWidget()->Close();
   is_visible_ = false;
-  bubble_opener_ = nullptr;
   bubble_view_ = nullptr;
   flags_ = kFlagsNone;
 }
diff --git a/ash/login/ui/login_bubble.h b/ash/login/ui/login_bubble.h
index 48bb440..56b6b9c 100644
--- a/ash/login/ui/login_bubble.h
+++ b/ash/login/ui/login_bubble.h
@@ -74,7 +74,7 @@
   void ShowTooltip(const base::string16& message, views::View* anchor_view);
 
   // Shows a selection menu.
-  void ShowSelectionMenu(LoginMenuView* menu, LoginButton* bubble_opener);
+  void ShowSelectionMenu(LoginMenuView* menu);
 
   // Schedule animation for closing the bubble.
   // The bubble widget will be closed when the animation is ended.
@@ -133,9 +133,6 @@
 
   LoginBaseBubbleView* bubble_view_ = nullptr;
 
-  // A button that could open/close the bubble.
-  LoginButton* bubble_opener_ = nullptr;
-
   // The status of bubble after animation ends.
   bool is_visible_ = false;
 
diff --git a/ash/login/ui/login_bubble_unittest.cc b/ash/login/ui/login_bubble_unittest.cc
index 8b7cc6e..07fee4b 100644
--- a/ash/login/ui/login_bubble_unittest.cc
+++ b/ash/login/ui/login_bubble_unittest.cc
@@ -106,9 +106,9 @@
   }
 
   void ShowSelectionMenu(const LoginMenuView::OnSelect& on_select) {
-    LoginMenuView* view =
-        new LoginMenuView(PopulateMenuItems(), container_, on_select);
-    bubble_->ShowSelectionMenu(view, bubble_opener_);
+    LoginMenuView* view = new LoginMenuView(PopulateMenuItems(), container_,
+                                            bubble_opener_, on_select);
+    bubble_->ShowSelectionMenu(view);
   }
 
   // Owned by test widget view hierarchy.
@@ -427,14 +427,14 @@
   EXPECT_FALSE(bubble_->IsVisible());
 }
 
-TEST_F(LoginBubbleTest, LongUserNameLaidOutCorrectly) {
+TEST_F(LoginBubbleTest, LongUserNameAndEmailLaidOutCorrectly) {
   ui::test::EventGenerator* generator = GetEventGenerator();
 
   EXPECT_FALSE(bubble_->IsVisible());
 
   bubble_->ShowUserMenu(
       base::UTF8ToUTF16("NedHasAReallyLongName StarkHasAReallyLongName"),
-      base::UTF8ToUTF16("reallylonggaianame@gmail.com"),
+      base::UTF8ToUTF16("reallyreallyextralonggaianame@gmail.com"),
       user_manager::UserType::USER_TYPE_REGULAR, false /*is_owner*/, container_,
       bubble_opener_, true /*show_remove_user*/, base::OnceClosure(),
       base::OnceClosure());
diff --git a/ash/login/ui/login_expanded_public_account_view.cc b/ash/login/ui/login_expanded_public_account_view.cc
index 700c5da..47b017a 100644
--- a/ash/login/ui/login_expanded_public_account_view.cc
+++ b/ash/login/ui/login_expanded_public_account_view.cc
@@ -18,7 +18,6 @@
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "base/strings/utf_string_conversions.h"
-#include "components/vector_icons/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/paint_vector_icon.h"
@@ -75,7 +74,6 @@
 
 constexpr char kMonitoringWarningClassName[] = "MonitoringWarning";
 constexpr int kSpacingBetweenMonitoringWarningIconAndLabelDp = 8;
-constexpr int kMonitoringWarningIconSizeDp = 20;
 
 views::Label* CreateLabel(const base::string16& text, SkColor color) {
   auto* label = new views::Label(text);
@@ -181,8 +179,7 @@
   DISALLOW_COPY_AND_ASSIGN(SelectionButtonView);
 };
 
-// Container for the device monitoring warning.  Contains the warning icon on
-// the left side, and text on the right side.
+// Container for the device monitoring warning.
 class MonitoringWarningView : public NonAccessibleView {
  public:
   MonitoringWarningView() : NonAccessibleView(kMonitoringWarningClassName) {
@@ -190,13 +187,6 @@
         views::BoxLayout::kHorizontal, gfx::Insets(),
         kSpacingBetweenMonitoringWarningIconAndLabelDp));
 
-    views::ImageView* image = new views::ImageView();
-    image->SetImage(gfx::CreateVectorIcon(
-        vector_icons::kWarningIcon, kMonitoringWarningIconSizeDp, SK_ColorRED));
-    image->SetPreferredSize(
-        gfx::Size(kMonitoringWarningIconSizeDp, kMonitoringWarningIconSizeDp));
-    AddChildView(image);
-
     const base::string16 label_text = l10n_util::GetStringUTF16(
         IDS_ASH_LOGIN_PUBLIC_ACCOUNT_MONITORING_WARNING);
     views::Label* label = CreateLabel(label_text, SK_ColorWHITE);
@@ -368,10 +358,10 @@
       if (!language_menu_->IsVisible() && !language_items_.empty()) {
         LoginMenuView* view = new LoginMenuView(
             language_items_, language_selection_ /*anchor_view*/,
+            language_selection_ /*bubble_opener*/,
             base::BindRepeating(&RightPaneView::OnLanguageSelected,
                                 weak_factory_.GetWeakPtr()));
-        language_menu_->ShowSelectionMenu(
-            view, language_selection_ /*bubble_opener*/);
+        language_menu_->ShowSelectionMenu(view);
       } else {
         language_menu_->Close();
       }
@@ -379,10 +369,10 @@
       if (!keyboard_menu_->IsVisible() && !keyboard_items_.empty()) {
         LoginMenuView* view = new LoginMenuView(
             keyboard_items_, keyboard_selection_ /*anchor_view*/,
+            keyboard_selection_ /*bubble_opener*/,
             base::BindRepeating(&RightPaneView::OnKeyboardSelected,
                                 weak_factory_.GetWeakPtr()));
-        keyboard_menu_->ShowSelectionMenu(
-            view, keyboard_selection_ /*bubble_opener*/);
+        keyboard_menu_->ShowSelectionMenu(view);
       } else {
         keyboard_menu_->Close();
       }
diff --git a/ash/login/ui/login_keyboard_test_base.cc b/ash/login/ui/login_keyboard_test_base.cc
index 7d8c7fe..a2ef470 100644
--- a/ash/login/ui/login_keyboard_test_base.cc
+++ b/ash/login/ui/login_keyboard_test_base.cc
@@ -16,8 +16,8 @@
 #include "base/command_line.h"
 #include "base/strings/strcat.h"
 #include "ui/keyboard/keyboard_controller.h"
-#include "ui/keyboard/keyboard_switches.h"
 #include "ui/keyboard/keyboard_ui.h"
+#include "ui/keyboard/public/keyboard_switches.h"
 #include "ui/keyboard/test/keyboard_test_util.h"
 
 namespace ash {
diff --git a/ash/login/ui/login_menu_view.cc b/ash/login/ui/login_menu_view.cc
index 3913e6f..7e2f49b 100644
--- a/ash/login/ui/login_menu_view.cc
+++ b/ash/login/ui/login_menu_view.cc
@@ -123,8 +123,9 @@
 
 LoginMenuView::LoginMenuView(const std::vector<Item>& items,
                              views::View* anchor_view,
+                             LoginButton* opener,
                              const OnSelect& on_select)
-    : LoginBaseBubbleView(anchor_view), on_select_(on_select) {
+    : LoginBaseBubbleView(anchor_view), opener_(opener), on_select_(on_select) {
   set_can_activate(true);
   set_margins(gfx::Insets());
   set_color(kMenuBackgroundColor);
@@ -194,6 +195,10 @@
   return current_index;
 }
 
+LoginButton* LoginMenuView::GetBubbleOpener() const {
+  return opener_;
+}
+
 void LoginMenuView::OnFocus() {
   // Forward the focus to the selected child view.
   contents_->child_at(selected_index_)->RequestFocus();
diff --git a/ash/login/ui/login_menu_view.h b/ash/login/ui/login_menu_view.h
index adb2e45..8bea9c2 100644
--- a/ash/login/ui/login_menu_view.h
+++ b/ash/login/ui/login_menu_view.h
@@ -7,6 +7,7 @@
 
 #include "ash/ash_export.h"
 #include "ash/login/ui/login_base_bubble_view.h"
+#include "ash/login/ui/login_button.h"
 #include "base/callback.h"
 #include "ui/views/view.h"
 
@@ -46,12 +47,16 @@
 
   LoginMenuView(const std::vector<Item>& items,
                 views::View* anchor_view,
+                LoginButton* opener_,
                 const OnSelect& on_select);
   ~LoginMenuView() override;
 
   void OnHighLightChange(int item_index, bool by_selection);
   int FindNextItem(bool reverse);
 
+  // LoginBaseBubbleView:
+  LoginButton* GetBubbleOpener() const override;
+
   // views::View:
   void OnFocus() override;
   bool OnKeyPressed(const ui::KeyEvent& event) override;
@@ -64,6 +69,8 @@
   // Owned by ScrollView.
   views::View* contents_ = nullptr;
 
+  LoginButton* opener_ = nullptr;
+
   const OnSelect on_select_;
   int selected_index_ = 0;
 
diff --git a/ash/login/ui/login_password_view.cc b/ash/login/ui/login_password_view.cc
index bd3b53a..ffbd5d7 100644
--- a/ash/login/ui/login_password_view.cc
+++ b/ash/login/ui/login_password_view.cc
@@ -304,6 +304,12 @@
 
 LoginPasswordView::TestApi::~TestApi() = default;
 
+void LoginPasswordView::TestApi::SubmitPassword(const std::string& password) {
+  view_->textfield_->SetText(base::ASCIIToUTF16(password));
+  view_->UpdateUiState();
+  view_->SubmitPassword();
+}
+
 views::Textfield* LoginPasswordView::TestApi::textfield() const {
   return view_->textfield_;
 }
diff --git a/ash/login/ui/login_password_view.h b/ash/login/ui/login_password_view.h
index 536c3c4..d68c5db 100644
--- a/ash/login/ui/login_password_view.h
+++ b/ash/login/ui/login_password_view.h
@@ -48,6 +48,8 @@
     explicit TestApi(LoginPasswordView* view);
     ~TestApi();
 
+    void SubmitPassword(const std::string& password);
+
     views::Textfield* textfield() const;
     views::View* submit_button() const;
     views::View* easy_unlock_icon() const;
diff --git a/ash/magnifier/docked_magnifier_controller_unittest.cc b/ash/magnifier/docked_magnifier_controller_unittest.cc
index 56c5198..6530e76 100644
--- a/ash/magnifier/docked_magnifier_controller_unittest.cc
+++ b/ash/magnifier/docked_magnifier_controller_unittest.cc
@@ -131,6 +131,14 @@
               gfx::ToFlooredPoint(point_of_interest_in_root_f));
   }
 
+  void TouchPoint(const gfx::Point& touch_point_in_screen) {
+    // TODO(oshima): Currently touch event doesn't update the
+    // event dispatcher in the event generator. Fix it and use
+    // touch event insteead.
+    auto* generator = GetEventGenerator();
+    generator->GestureTapAt(touch_point_in_screen);
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 
@@ -446,7 +454,7 @@
   // Generate some touch events in both displays and expect the magnifier
   // viewport moves accordingly.
   gfx::Point touch_point(200, 350);
-  GetEventGenerator()->PressMoveAndReleaseTouchTo(touch_point);
+  TouchPoint(touch_point);
   const views::Widget* viewport_widget =
       controller()->GetViewportWidgetForTesting();
   EXPECT_EQ(root_windows[0], viewport_widget->GetNativeView()->GetRootWindow());
@@ -454,7 +462,8 @@
 
   // Touch a new point in the other display.
   touch_point = gfx::Point(900, 200);
-  GetEventGenerator()->PressMoveAndReleaseTouchTo(touch_point);
+  TouchPoint(touch_point);
+
   // New viewport widget is created in the second display.
   ASSERT_NE(viewport_widget, controller()->GetViewportWidgetForTesting());
   viewport_widget = controller()->GetViewportWidgetForTesting();
diff --git a/ash/manifest.json b/ash/manifest.json
index bde87ca..5be14b3 100644
--- a/ash/manifest.json
+++ b/ash/manifest.json
@@ -49,6 +49,7 @@
         ],
         // Test-only interfaces.
         "test": [
+          "ash.mojom.LoginScreenTestApi",
           "ash.mojom.ShelfTestApi",
           "ash.mojom.ShellTestApi",
           "ash.mojom.StatusAreaWidgetTestApi",
@@ -69,6 +70,7 @@
         "*": [ "accessibility", "app" ],
         "ash_pref_connector": [ "pref_connector" ],
         "catalog": [ "directory" ],
+        "content": [ "navigation" ],
         "device": [
           "device:bluetooth_system",
           "device:fingerprint"
diff --git a/ash/media/media_notification_controller.cc b/ash/media/media_notification_controller.cc
index c65181a..3ddc2a2 100644
--- a/ash/media/media_notification_controller.cc
+++ b/ash/media/media_notification_controller.cc
@@ -67,13 +67,19 @@
 
 MediaNotificationController::~MediaNotificationController() = default;
 
-void MediaNotificationController::OnFocusGained(
-    media_session::mojom::MediaSessionInfoPtr session_info,
-    media_session::mojom::AudioFocusType type) {
+void MediaNotificationController::OnActiveSessionChanged(
+    media_session::mojom::AudioFocusRequestStatePtr session) {
+  // Hide the notification if the active session is null.
+  if (session.is_null()) {
+    message_center::MessageCenter::Get()->RemoveNotification(
+        kMediaSessionNotificationId, false);
+    return;
+  }
+
   if (IsMediaSessionNotificationVisible())
     return;
 
-  session_info_ = std::move(session_info);
+  session_info_ = std::move(session->session_info);
 
   std::unique_ptr<message_center::Notification> notification =
       ash::CreateSystemNotification(
@@ -101,15 +107,6 @@
       std::move(notification));
 }
 
-void MediaNotificationController::OnFocusLost(
-    media_session::mojom::MediaSessionInfoPtr session_info) {
-  if (!IsMediaSessionNotificationVisible())
-    return;
-
-  message_center::MessageCenter::Get()->RemoveNotification(
-      kMediaSessionNotificationId, false);
-}
-
 void MediaNotificationController::MediaSessionInfoChanged(
     media_session::mojom::MediaSessionInfoPtr session_info) {
   session_info_ = std::move(session_info);
diff --git a/ash/media/media_notification_controller.h b/ash/media/media_notification_controller.h
index 130bd23..927f19b3 100644
--- a/ash/media/media_notification_controller.h
+++ b/ash/media/media_notification_controller.h
@@ -34,9 +34,11 @@
 
   // media_session::mojom::AudioFocusObserver:
   void OnFocusGained(media_session::mojom::MediaSessionInfoPtr session_info,
-                     media_session::mojom::AudioFocusType type) override;
+                     media_session::mojom::AudioFocusType type) override {}
   void OnFocusLost(
-      media_session::mojom::MediaSessionInfoPtr session_info) override;
+      media_session::mojom::MediaSessionInfoPtr session_info) override {}
+  void OnActiveSessionChanged(
+      media_session::mojom::AudioFocusRequestStatePtr session) override;
 
   // media_session::mojom::MediaSessionObserver:
   void MediaSessionInfoChanged(
diff --git a/ash/media/media_notification_controller_unittest.cc b/ash/media/media_notification_controller_unittest.cc
index 5e34807..3fc8739 100644
--- a/ash/media/media_notification_controller_unittest.cc
+++ b/ash/media/media_notification_controller_unittest.cc
@@ -17,8 +17,7 @@
 
 namespace ash {
 
-using media_session::mojom::AudioFocusType;
-using media_session::mojom::MediaSessionInfo;
+using media_session::mojom::AudioFocusRequestState;
 
 namespace {
 
@@ -56,43 +55,43 @@
   DISALLOW_COPY_AND_ASSIGN(MediaNotificationControllerTest);
 };
 
-TEST_F(MediaNotificationControllerTest, OnFocusGainedLost) {
+TEST_F(MediaNotificationControllerTest, OnActiveSessionChanged) {
   EXPECT_FALSE(IsMediaNotificationShown());
   EXPECT_EQ(0, GetVisibleNotificationCount());
   EXPECT_EQ(0, GetPopupNotificationCount());
 
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      MediaSessionInfo::New(), AudioFocusType::kGain);
+  Shell::Get()->media_notification_controller()->OnActiveSessionChanged(
+      AudioFocusRequestState::New());
   EXPECT_TRUE(IsMediaNotificationShown());
   EXPECT_EQ(1, GetVisibleNotificationCount());
   EXPECT_EQ(0, GetPopupNotificationCount());
 
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      MediaSessionInfo::New(), AudioFocusType::kGain);
+  Shell::Get()->media_notification_controller()->OnActiveSessionChanged(
+      AudioFocusRequestState::New());
   EXPECT_TRUE(IsMediaNotificationShown());
   EXPECT_EQ(1, GetVisibleNotificationCount());
   EXPECT_EQ(0, GetPopupNotificationCount());
 
-  Shell::Get()->media_notification_controller()->OnFocusLost(
-      MediaSessionInfo::New());
+  Shell::Get()->media_notification_controller()->OnActiveSessionChanged(
+      nullptr);
   EXPECT_FALSE(IsMediaNotificationShown());
   EXPECT_EQ(0, GetVisibleNotificationCount());
   EXPECT_EQ(0, GetPopupNotificationCount());
 }
 
-TEST_F(MediaNotificationControllerTest, OnFocusLost_Noop) {
+TEST_F(MediaNotificationControllerTest, OnActiveSessionChanged_Noop) {
   EXPECT_FALSE(IsMediaNotificationShown());
 
-  Shell::Get()->media_notification_controller()->OnFocusLost(
-      MediaSessionInfo::New());
+  Shell::Get()->media_notification_controller()->OnActiveSessionChanged(
+      nullptr);
   EXPECT_FALSE(IsMediaNotificationShown());
 }
 
 TEST_F(MediaNotificationControllerTest, NotificationHasCustomViewType) {
   EXPECT_FALSE(IsMediaNotificationShown());
 
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      MediaSessionInfo::New(), AudioFocusType::kGain);
+  Shell::Get()->media_notification_controller()->OnActiveSessionChanged(
+      AudioFocusRequestState::New());
   message_center::Notification* notification =
       message_center::MessageCenter::Get()->FindVisibleNotificationById(
           kMediaSessionNotificationId);
diff --git a/ash/media/media_notification_view_unittest.cc b/ash/media/media_notification_view_unittest.cc
index ff521b2..14c9515 100644
--- a/ash/media/media_notification_view_unittest.cc
+++ b/ash/media/media_notification_view_unittest.cc
@@ -78,8 +78,11 @@
             base::Unretained(this)));
 
     // Show the notification.
-    Shell::Get()->media_notification_controller()->OnFocusGained(
-        std::move(session_info), media_session::mojom::AudioFocusType::kGain);
+    media_session::mojom::AudioFocusRequestStatePtr session(
+        media_session::mojom::AudioFocusRequestState::New());
+    session->session_info = std::move(session_info);
+    Shell::Get()->media_notification_controller()->OnActiveSessionChanged(
+        std::move(session));
 
     message_center::Notification* notification =
         message_center::MessageCenter::Get()->FindVisibleNotificationById(
@@ -229,7 +232,7 @@
   EXPECT_EQ(0, media_controller()->toggle_suspend_resume_count());
 }
 
-TEST_F(MediaNotificationViewTest, PlayToggle_FromFocusGain) {
+TEST_F(MediaNotificationViewTest, PlayToggle_FromActiveSessionChanged) {
   {
     views::ToggleImageButton* button =
         static_cast<views::ToggleImageButton*>(button_row()->child_at(1));
@@ -237,8 +240,8 @@
     EXPECT_FALSE(button->toggled_for_testing());
   }
 
-  Shell::Get()->media_notification_controller()->OnFocusLost(
-      media_session::mojom::MediaSessionInfo::New());
+  Shell::Get()->media_notification_controller()->OnActiveSessionChanged(
+      nullptr);
 
   // Disable the tray and run the loop to make sure that the existing view is
   // destroyed.
diff --git a/ash/metrics/login_metrics_recorder_unittest.cc b/ash/metrics/login_metrics_recorder_unittest.cc
index ba84451..f328ced 100644
--- a/ash/metrics/login_metrics_recorder_unittest.cc
+++ b/ash/metrics/login_metrics_recorder_unittest.cc
@@ -46,8 +46,6 @@
     histogram_tester_.reset(new base::HistogramTester());
   }
 
-  void TearDown() override { LoginTestBase::TearDown(); }
-
  protected:
   void EnableTabletMode(bool enable) {
     Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(
diff --git a/ash/metrics/user_metrics_action.h b/ash/metrics/user_metrics_action.h
index b06188d..870c273 100644
--- a/ash/metrics/user_metrics_action.h
+++ b/ash/metrics/user_metrics_action.h
@@ -80,10 +80,6 @@
   UMA_TRAY_OVERVIEW,
   UMA_TRAY_SETTINGS,
   UMA_TRAY_SHUT_DOWN,
-  UMA_TRAY_SWIPE_TO_CLOSE_SUCCESSFUL,
-  UMA_TRAY_SWIPE_TO_CLOSE_UNSUCCESSFUL,
-  UMA_TRAY_SWIPE_TO_OPEN_SUCCESSFUL,
-  UMA_TRAY_SWIPE_TO_OPEN_UNSUCCESSFUL,
 
   // DEPRECATED: Do not add new values. See top of file.
 };
diff --git a/ash/metrics/user_metrics_recorder.cc b/ash/metrics/user_metrics_recorder.cc
index 44b9a93..dbacabd 100644
--- a/ash/metrics/user_metrics_recorder.cc
+++ b/ash/metrics/user_metrics_recorder.cc
@@ -426,18 +426,6 @@
     case UMA_TRAY_SHUT_DOWN:
       RecordAction(UserMetricsAction("Tray_ShutDown"));
       break;
-    case UMA_TRAY_SWIPE_TO_CLOSE_SUCCESSFUL:
-      RecordAction(UserMetricsAction("Tray_SwipeToClose_Successful"));
-      break;
-    case UMA_TRAY_SWIPE_TO_CLOSE_UNSUCCESSFUL:
-      RecordAction(UserMetricsAction("Tray_SwipeToClose_Unsuccessful"));
-      break;
-    case UMA_TRAY_SWIPE_TO_OPEN_SUCCESSFUL:
-      RecordAction(UserMetricsAction("Tray_SwipeToOpen_Successful"));
-      break;
-    case UMA_TRAY_SWIPE_TO_OPEN_UNSUCCESSFUL:
-      RecordAction(UserMetricsAction("Tray_SwipeToOpen_Unsuccessful"));
-      break;
   }
 }
 
diff --git a/ash/mojo_test_interface_factory.cc b/ash/mojo_test_interface_factory.cc
index ac73874..1d8f737 100644
--- a/ash/mojo_test_interface_factory.cc
+++ b/ash/mojo_test_interface_factory.cc
@@ -6,7 +6,9 @@
 
 #include <utility>
 
+#include "ash/login/login_screen_test_api.h"
 #include "ash/metrics/time_to_first_present_recorder_test_api.h"
+#include "ash/public/interfaces/login_screen_test_api.mojom.h"
 #include "ash/public/interfaces/shelf_test_api.mojom.h"
 #include "ash/public/interfaces/shell_test_api.mojom.h"
 #include "ash/public/interfaces/status_area_widget_test_api.mojom.h"
@@ -26,6 +28,11 @@
 // These functions aren't strictly necessary, but exist to make threading and
 // arguments clearer.
 
+void BindLoginScreenTestApiOnMainThread(
+    mojom::LoginScreenTestApiRequest request) {
+  LoginScreenTestApi::BindRequest(std::move(request));
+}
+
 void BindShelfTestApiOnMainThread(mojom::ShelfTestApiRequest request) {
   ShelfTestApi::BindRequest(std::move(request));
 }
@@ -54,6 +61,8 @@
 void RegisterInterfaces(
     service_manager::BinderRegistry* registry,
     scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner) {
+  registry->AddInterface(base::Bind(&BindLoginScreenTestApiOnMainThread),
+                         main_thread_task_runner);
   registry->AddInterface(base::Bind(&BindShelfTestApiOnMainThread),
                          main_thread_task_runner);
   registry->AddInterface(base::Bind(&BindShellTestApiOnMainThread),
diff --git a/ash/multi_device_setup/multi_device_notification_presenter.cc b/ash/multi_device_setup/multi_device_notification_presenter.cc
index e142b88..cda53ae 100644
--- a/ash/multi_device_setup/multi_device_notification_presenter.cc
+++ b/ash/multi_device_setup/multi_device_notification_presenter.cc
@@ -226,15 +226,17 @@
   if (!user_session)
     return;
 
-  std::string service_user_id = user_session->user_info->service_user_id;
+  base::Optional<base::Token> service_instance_group =
+      user_session->user_info->service_instance_group;
 
-  // Cannot proceed if there is no service user ID.
-  if (service_user_id.empty())
+  // Cannot proceed if there is no known service instance group.
+  if (!service_instance_group)
     return;
 
   connector_->BindInterface(
-      service_manager::Identity(
-          chromeos::multidevice_setup::mojom::kServiceName, service_user_id),
+      service_manager::ServiceFilter::ByNameInGroup(
+          chromeos::multidevice_setup::mojom::kServiceName,
+          *service_instance_group),
       &multidevice_setup_ptr_);
 
   // Add this object as the delegate of the MultiDeviceSetup Service.
diff --git a/ash/multi_device_setup/multi_device_notification_presenter_unittest.cc b/ash/multi_device_setup/multi_device_notification_presenter_unittest.cc
index 5fc03b6..4460775 100644
--- a/ash/multi_device_setup/multi_device_notification_presenter_unittest.cc
+++ b/ash/multi_device_setup/multi_device_notification_presenter_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/token.h"
 #include "chromeos/services/multidevice_setup/public/cpp/fake_multidevice_setup.h"
 #include "chromeos/services/multidevice_setup/public/mojom/constants.mojom.h"
 #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
@@ -32,8 +33,8 @@
 const char kTestUserEmail[] = "test@example.com";
 const char kTestHostDeviceName[] = "Test Device";
 
-// Note: Must be formatted as a GUID.
-const char kTestServiceUserId[] = "01234567-89ab-cdef-0123-456789abcdef";
+const base::Token kTestServiceInstanceGroup{0x0123456789abcdefull,
+                                            0xfedcba9876543210ull};
 
 class TestMessageCenter : public message_center::FakeMessageCenter {
  public:
@@ -135,9 +136,9 @@
         std::make_unique<chromeos::multidevice_setup::FakeMultiDeviceSetup>();
     service_manager::Connector::TestApi test_api(connector_.get());
     test_api.OverrideBinderForTesting(
-        service_manager::Identity(
+        service_manager::ServiceFilter::ByNameInGroup(
             chromeos::multidevice_setup::mojom::kServiceName,
-            kTestServiceUserId),
+            kTestServiceInstanceGroup),
         chromeos::multidevice_setup::mojom::MultiDeviceSetup::Name_,
         base::BindRepeating(
             &chromeos::multidevice_setup::FakeMultiDeviceSetup::BindHandle,
@@ -163,7 +164,7 @@
     test_session_client->AddUserSession(
         kTestUserEmail, user_manager::USER_TYPE_REGULAR,
         true /* enable_settings */, true /* provide_pref_service */,
-        false /* is_new_profile */, kTestServiceUserId);
+        false /* is_new_profile */, kTestServiceInstanceGroup);
     test_session_client->SetSessionState(session_manager::SessionState::ACTIVE);
     test_session_client->SwitchActiveUser(
         AccountId::FromUserEmail(kTestUserEmail));
diff --git a/ash/multi_user/OWNERS b/ash/multi_user/OWNERS
new file mode 100644
index 0000000..0debac9
--- /dev/null
+++ b/ash/multi_user/OWNERS
@@ -0,0 +1,2 @@
+skuhne@chromium.org
+
diff --git a/ash/multi_user/multi_user_window_manager.cc b/ash/multi_user/multi_user_window_manager.cc
new file mode 100644
index 0000000..214b028
--- /dev/null
+++ b/ash/multi_user/multi_user_window_manager.cc
@@ -0,0 +1,539 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/multi_user/multi_user_window_manager.h"
+
+#include <set>
+#include <vector>
+
+#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/user_switch_animator.h"
+#include "ash/public/cpp/shell_window_ids.h"
+#include "ash/session/session_controller.h"
+#include "ash/shell.h"
+#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/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"
+#include "ui/wm/core/transient_window_manager.h"
+#include "ui/wm/core/window_animations.h"
+#include "ui/wm/core/window_util.h"
+
+namespace ash {
+namespace {
+
+// The animation time for a single window that is fading in / out.
+constexpr base::TimeDelta kAnimationTime =
+    base::TimeDelta::FromMilliseconds(100);
+
+// The animation time for the fade in and / or out when switching users.
+constexpr base::TimeDelta kUserFadeTime =
+    base::TimeDelta::FromMilliseconds(110);
+
+// The animation time in ms for a window which get teleported to another screen.
+constexpr base::TimeDelta kTeleportAnimationTime =
+    base::TimeDelta::FromMilliseconds(300);
+
+MultiUserWindowManager* g_instance = nullptr;
+
+bool HasSystemModalTransientChildWindow(aura::Window* window) {
+  if (window == nullptr)
+    return false;
+
+  aura::Window* system_modal_container = window->GetRootWindow()->GetChildById(
+      ash::kShellWindowId_SystemModalContainer);
+  if (window->parent() == system_modal_container)
+    return true;
+
+  for (aura::Window* transient_child : ::wm::GetTransientChildren(window)) {
+    if (HasSystemModalTransientChildWindow(transient_child))
+      return true;
+  }
+  return false;
+}
+
+mojom::WallpaperUserInfoPtr WallpaperUserInfoForAccount(
+    const AccountId& account_id) {
+  DCHECK(account_id.is_valid());
+  mojom::WallpaperUserInfoPtr wallpaper_user_info =
+      mojom::WallpaperUserInfo::New();
+  SessionController* session_controller = Shell::Get()->session_controller();
+  for (const mojom::UserSessionPtr& user_session :
+       session_controller->GetUserSessions()) {
+    if (user_session->user_info->account_id == account_id) {
+      wallpaper_user_info->account_id = account_id;
+      wallpaper_user_info->type = user_session->user_info->type;
+      wallpaper_user_info->is_ephemeral = user_session->user_info->is_ephemeral;
+      wallpaper_user_info->has_gaia_account =
+          user_session->user_info->has_gaia_account;
+      return wallpaper_user_info;
+    }
+  }
+  NOTREACHED();
+  return wallpaper_user_info;
+}
+
+}  // namespace
+
+// A class to temporarily change the animation properties for a window.
+class AnimationSetter {
+ public:
+  AnimationSetter(aura::Window* window, base::TimeDelta animation_time)
+      : window_(window),
+        previous_animation_type_(
+            ::wm::GetWindowVisibilityAnimationType(window_)),
+        previous_animation_time_(
+            ::wm::GetWindowVisibilityAnimationDuration(*window_)) {
+    ::wm::SetWindowVisibilityAnimationType(
+        window_, ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
+    ::wm::SetWindowVisibilityAnimationDuration(window_, animation_time);
+  }
+
+  ~AnimationSetter() {
+    ::wm::SetWindowVisibilityAnimationType(window_, previous_animation_type_);
+    ::wm::SetWindowVisibilityAnimationDuration(window_,
+                                               previous_animation_time_);
+  }
+
+ private:
+  // The window which gets used.
+  aura::Window* window_;
+
+  // Previous animation type.
+  const int previous_animation_type_;
+
+  // Previous animation time.
+  const base::TimeDelta previous_animation_time_;
+
+  DISALLOW_COPY_AND_ASSIGN(AnimationSetter);
+};
+
+MultiUserWindowManager::MultiUserWindowManager(
+    MultiUserWindowManagerDelegate* delegate,
+    const AccountId& account_id)
+    : delegate_(delegate), current_account_id_(account_id) {
+  g_instance = this;
+  Shell::Get()->tablet_mode_controller()->AddObserver(this);
+  Shell::Get()->session_controller()->AddObserver(this);
+}
+
+MultiUserWindowManager::~MultiUserWindowManager() {
+  // When the MultiUserWindowManager gets destroyed, ash::Shell is mostly gone.
+  // As such we should not try to finalize any outstanding user animations.
+  // Note that the destruction of the object can be done later.
+  if (animation_.get())
+    animation_->CancelAnimation();
+
+  // Remove all window observers.
+  WindowToEntryMap::iterator window = window_to_entry_.begin();
+  while (window != window_to_entry_.end()) {
+    // 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();
+  }
+
+  Shell::Get()->session_controller()->RemoveObserver(this);
+  Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
+  g_instance = nullptr;
+}
+
+// static
+MultiUserWindowManager* MultiUserWindowManager::Get() {
+  return g_instance;
+}
+
+void MultiUserWindowManager::SetWindowOwner(aura::Window* window,
+                                            const AccountId& account_id) {
+  // Make sure the window is valid and there was no owner yet.
+  DCHECK(window);
+  DCHECK(account_id.is_valid());
+
+  if (GetWindowOwner(window) == account_id)
+    return;
+  DCHECK(GetWindowOwner(window).empty());
+  window_to_entry_[window] = new WindowEntry(account_id);
+
+  // Remember the initial visibility of the window.
+  window_to_entry_[window]->set_show(window->IsVisible());
+
+  // Add observers to track state changes.
+  window->AddObserver(this);
+  ::wm::TransientWindowManager::GetOrCreate(window)->AddObserver(this);
+
+  // 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_);
+
+  // 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.
+  AddTransientOwnerRecursive(window, window);
+
+  if (!IsWindowOnDesktopOfUser(window, current_account_id_))
+    SetWindowVisibility(window, false);
+}
+
+const AccountId& MultiUserWindowManager::GetWindowOwner(
+    aura::Window* window) const {
+  WindowToEntryMap::const_iterator it = window_to_entry_.find(window);
+  return it != window_to_entry_.end() ? it->second->owner() : EmptyAccountId();
+}
+
+void MultiUserWindowManager::ShowWindowForUser(aura::Window* window,
+                                               const AccountId& account_id) {
+  const AccountId previous_owner(GetUserPresentingWindow(window));
+  if (!ShowWindowForUserIntern(window, account_id))
+    return;
+  // The window switched to a new desktop and we have to switch to that desktop,
+  // but only when it was on the visible desktop and the the target is not the
+  // visible desktop.
+  if (account_id == current_account_id_ ||
+      previous_owner != current_account_id_)
+    return;
+
+  Shell::Get()->session_controller()->SwitchActiveUser(account_id);
+}
+
+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())
+      return true;
+  }
+  return false;
+}
+
+bool MultiUserWindowManager::IsWindowOnDesktopOfUser(
+    aura::Window* window,
+    const AccountId& account_id) const {
+  const AccountId& presenting_user = GetUserPresentingWindow(window);
+  return (!presenting_user.is_valid()) || presenting_user == account_id;
+}
+
+const AccountId& MultiUserWindowManager::GetUserPresentingWindow(
+    aura::Window* window) const {
+  WindowToEntryMap::const_iterator it = window_to_entry_.find(window);
+  // If the window is not owned by anyone it is shown on all desktops and we
+  // return the empty string.
+  if (it == window_to_entry_.end())
+    return EmptyAccountId();
+  // Otherwise we ask the object for its desktop.
+  return it->second->show_for_user();
+}
+
+void MultiUserWindowManager::OnActiveUserSessionChanged(
+    const AccountId& account_id) {
+  // MultiUserWindowManager is created with an account before the change has
+  // potentially made it to SessionController. This means MultiUserWindowManager
+  // may be notified of a switch to the current user. Ignore this. Ignoring this
+  // is especially important in tests, which may be impacted by running the
+  // animation (when the animation closes, observers are notified, which may
+  // have side effects in downstream code).
+  if (account_id == current_account_id_)
+    return;
+
+  // This needs to be set before the animation starts.
+  current_account_id_ = 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
+  // animation only to be reshown again by the destructor of the old animation.
+  animation_.reset();
+  animation_ = std::make_unique<UserSwitchAnimator>(
+      this, WallpaperUserInfoForAccount(current_account_id_),
+      GetAdjustedAnimationTime(kUserFadeTime));
+
+  // Call RequestCaptureState here instead of having MediaClient observe
+  // ActiveUserChanged because it must happen after
+  // MultiUserWindowManager is notified.
+  Shell::Get()->media_controller()->RequestCaptureState();
+}
+
+void MultiUserWindowManager::OnWindowDestroyed(aura::Window* window) {
+  if (GetWindowOwner(window).empty()) {
+    // This must be a window in the transient chain - remove it and its
+    // children from the owner.
+    RemoveTransientOwnerRecursive(window);
+    return;
+  }
+  ::wm::TransientWindowManager::GetOrCreate(window)->RemoveObserver(this);
+  // Remove the window from the owners list.
+  delete window_to_entry_[window];
+  window_to_entry_.erase(window);
+}
+
+void MultiUserWindowManager::OnWindowVisibilityChanging(aura::Window* window,
+                                                        bool visible) {
+  // This command gets called first and immediately when show or hide gets
+  // called. We remember here the desired state for restoration IF we were
+  // not ourselves issuing the call.
+  // Note also that using the OnWindowVisibilityChanged callback cannot be
+  // used for this.
+  if (suppress_visibility_changes_)
+    return;
+
+  WindowToEntryMap::iterator it = window_to_entry_.find(window);
+  // If the window is not owned by anyone it is shown on all desktops.
+  if (it != window_to_entry_.end()) {
+    // Remember what was asked for so that we can restore this when the user's
+    // desktop gets restored.
+    it->second->set_show(visible);
+  } else {
+    TransientWindowToVisibility::iterator it =
+        transient_window_to_visibility_.find(window);
+    if (it != transient_window_to_visibility_.end())
+      it->second = visible;
+  }
+}
+
+void MultiUserWindowManager::OnWindowVisibilityChanged(aura::Window* window,
+                                                       bool visible) {
+  if (suppress_visibility_changes_)
+    return;
+
+  // Don't allow to make the window visible if it shouldn't be.
+  if (visible && !IsWindowOnDesktopOfUser(window, current_account_id_)) {
+    SetWindowVisibility(window, false);
+    return;
+  }
+  aura::Window* owned_parent = GetOwningWindowInTransientChain(window);
+  if (owned_parent && owned_parent != window && visible &&
+      !IsWindowOnDesktopOfUser(owned_parent, current_account_id_))
+    SetWindowVisibility(window, false);
+}
+
+void MultiUserWindowManager::OnTransientChildAdded(
+    aura::Window* window,
+    aura::Window* transient_window) {
+  if (!GetWindowOwner(window).empty()) {
+    AddTransientOwnerRecursive(transient_window, window);
+    return;
+  }
+  aura::Window* owned_parent =
+      GetOwningWindowInTransientChain(transient_window);
+  if (!owned_parent)
+    return;
+
+  AddTransientOwnerRecursive(transient_window, owned_parent);
+}
+
+void MultiUserWindowManager::OnTransientChildRemoved(
+    aura::Window* window,
+    aura::Window* transient_window) {
+  // Remove the transient child if the window itself is owned, or one of the
+  // windows in its transient parents chain.
+  if (!GetWindowOwner(window).empty() ||
+      GetOwningWindowInTransientChain(window))
+    RemoveTransientOwnerRecursive(transient_window);
+}
+
+void MultiUserWindowManager::OnTabletModeStarted() {
+  for (auto entry : window_to_entry_)
+    Shell::Get()->tablet_mode_controller()->AddWindow(entry.first);
+}
+
+void MultiUserWindowManager::SetAnimationSpeedForTest(
+    MultiUserWindowManager::AnimationSpeed speed) {
+  animation_speed_ = speed;
+}
+
+bool MultiUserWindowManager::IsAnimationRunningForTest() {
+  return animation_ && !animation_->IsAnimationFinished();
+}
+
+const AccountId& MultiUserWindowManager::GetCurrentUserForTest() const {
+  return current_account_id_;
+}
+
+bool MultiUserWindowManager::ShowWindowForUserIntern(
+    aura::Window* window,
+    const AccountId& account_id) {
+  // If there is either no owner, or the owner is the current user, no action
+  // is required.
+  const AccountId& owner = GetWindowOwner(window);
+  if ((!owner.is_valid()) ||
+      (owner == account_id && IsWindowOnDesktopOfUser(window, account_id)))
+    return false;
+
+  bool minimized = ::wm::WindowStateIs(window, ui::SHOW_STATE_MINIMIZED);
+  // Check that we are not trying to transfer ownership of a minimized window.
+  if (account_id != owner && minimized)
+    return false;
+
+  WindowToEntryMap::iterator it = window_to_entry_.find(window);
+  it->second->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())
+      SetWindowVisibility(window, true, kTeleportAnimationTime);
+  } else {
+    SetWindowVisibility(window, false, kTeleportAnimationTime);
+  }
+
+  // Notify entry change.
+  if (delegate_)
+    delegate_->OnOwnerEntryChanged(window, account_id, minimized, teleported);
+  return true;
+}
+
+void MultiUserWindowManager::SetWindowVisibility(
+    aura::Window* window,
+    bool visible,
+    base::TimeDelta animation_time) {
+  if (window->IsVisible() == visible)
+    return;
+
+  // Hiding a system modal dialog should not be allowed. Instead we switch to
+  // the user which is showing the system modal window.
+  // Note that in some cases (e.g. unit test) windows might not have a root
+  // window.
+  if (!visible && window->GetRootWindow()) {
+    if (HasSystemModalTransientChildWindow(window)) {
+      // The window is system modal and we need to find the parent which owns
+      // it so that we can switch to the desktop accordingly.
+      AccountId account_id = GetUserPresentingWindow(window);
+      if (!account_id.is_valid()) {
+        aura::Window* owning_window = GetOwningWindowInTransientChain(window);
+        DCHECK(owning_window);
+        account_id = GetUserPresentingWindow(owning_window);
+        DCHECK(account_id.is_valid());
+      }
+      Shell::Get()->session_controller()->SwitchActiveUser(account_id);
+      return;
+    }
+  }
+
+  // To avoid that these commands are recorded as any other commands, we are
+  // suppressing any window entry changes while this is going on.
+  base::AutoReset<bool> suppressor(&suppress_visibility_changes_, true);
+
+  if (visible)
+    ShowWithTransientChildrenRecursive(window, animation_time);
+  else
+    SetWindowVisible(window, false, animation_time);
+}
+
+void MultiUserWindowManager::ShowWithTransientChildrenRecursive(
+    aura::Window* window,
+    base::TimeDelta animation_time) {
+  for (aura::Window* transient_child : ::wm::GetTransientChildren(window))
+    ShowWithTransientChildrenRecursive(transient_child, animation_time);
+
+  // We show all children which were not explicitly hidden.
+  TransientWindowToVisibility::iterator it =
+      transient_window_to_visibility_.find(window);
+  if (it == transient_window_to_visibility_.end() || it->second)
+    SetWindowVisible(window, true, animation_time);
+}
+
+aura::Window* MultiUserWindowManager::GetOwningWindowInTransientChain(
+    aura::Window* window) const {
+  if (!GetWindowOwner(window).empty())
+    return nullptr;
+  aura::Window* parent = ::wm::GetTransientParent(window);
+  while (parent) {
+    if (!GetWindowOwner(parent).empty())
+      return parent;
+    parent = ::wm::GetTransientParent(parent);
+  }
+  return nullptr;
+}
+
+void MultiUserWindowManager::AddTransientOwnerRecursive(
+    aura::Window* window,
+    aura::Window* owned_parent) {
+  // First add all child windows.
+  for (aura::Window* transient_child : ::wm::GetTransientChildren(window))
+    AddTransientOwnerRecursive(transient_child, owned_parent);
+
+  // If this window is the owned window, we do not have to handle it again.
+  if (window == owned_parent)
+    return;
+
+  // Remember the current visibility.
+  DCHECK(transient_window_to_visibility_.find(window) ==
+         transient_window_to_visibility_.end());
+  transient_window_to_visibility_[window] = window->IsVisible();
+
+  // Add observers to track state changes.
+  window->AddObserver(this);
+  ::wm::TransientWindowManager::GetOrCreate(window)->AddObserver(this);
+
+  // Hide the window if it should not be shown. Note that this hide operation
+  // will hide recursively this and all children - but we have already collected
+  // their initial view state.
+  if (!IsWindowOnDesktopOfUser(owned_parent, current_account_id_))
+    SetWindowVisibility(window, false, kAnimationTime);
+}
+
+void MultiUserWindowManager::RemoveTransientOwnerRecursive(
+    aura::Window* window) {
+  // First remove all child windows.
+  for (aura::Window* transient_child : ::wm::GetTransientChildren(window))
+    RemoveTransientOwnerRecursive(transient_child);
+
+  // Find from transient window storage the visibility for the given window,
+  // set the visibility accordingly and delete the window from the map.
+  TransientWindowToVisibility::iterator visibility_item =
+      transient_window_to_visibility_.find(window);
+  DCHECK(visibility_item != transient_window_to_visibility_.end());
+
+  window->RemoveObserver(this);
+  ::wm::TransientWindowManager::GetOrCreate(window)->RemoveObserver(this);
+
+  bool unowned_view_state = visibility_item->second;
+  transient_window_to_visibility_.erase(visibility_item);
+  if (unowned_view_state && !window->IsVisible()) {
+    // To prevent these commands from being recorded as any other commands, we
+    // are suppressing any window entry changes while this is going on.
+    // Instead of calling SetWindowVisible, only show gets called here since all
+    // dependents have been shown previously already.
+    base::AutoReset<bool> suppressor(&suppress_visibility_changes_, true);
+    window->Show();
+  }
+}
+
+void MultiUserWindowManager::SetWindowVisible(aura::Window* window,
+                                              bool visible,
+                                              base::TimeDelta animation_time) {
+  // The TabletModeWindowManager will not handle invisible windows since they
+  // are not user activatable. Since invisible windows are not being tracked,
+  // we tell it to maximize / track this window now before it gets shown, to
+  // reduce animation jank from multiple resizes.
+  if (visible)
+    Shell::Get()->tablet_mode_controller()->AddWindow(window);
+
+  AnimationSetter animation_setter(window,
+                                   GetAdjustedAnimationTime(animation_time));
+  if (visible)
+    window->Show();
+  else
+    window->Hide();
+}
+
+base::TimeDelta MultiUserWindowManager::GetAdjustedAnimationTime(
+    base::TimeDelta default_time) const {
+  return animation_speed_ == ANIMATION_SPEED_NORMAL
+             ? default_time
+             : (animation_speed_ == ANIMATION_SPEED_FAST
+                    ? base::TimeDelta::FromMilliseconds(10)
+                    : base::TimeDelta());
+}
+
+}  // namespace ash
diff --git a/ash/multi_user/multi_user_window_manager.h b/ash/multi_user/multi_user_window_manager.h
new file mode 100644
index 0000000..e336b5d
--- /dev/null
+++ b/ash/multi_user/multi_user_window_manager.h
@@ -0,0 +1,237 @@
+// 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_H_
+#define ASH_MULTI_USER_MULTI_USER_WINDOW_MANAGER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "ash/ash_export.h"
+#include "ash/session/session_observer.h"
+#include "ash/wm/tablet_mode/tablet_mode_observer.h"
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "components/account_id/account_id.h"
+#include "ui/aura/window_observer.h"
+#include "ui/wm/core/transient_window_observer.h"
+
+namespace ash {
+
+class MultiUserWindowManagerDelegate;
+class UserSwitchAnimator;
+
+// MultiUserWindowManager associates windows with users and ensures the
+// appropriate set of windows are visible at the right time.
+// MultiUserWindowManager must be explicitly told about the windows to manage.
+// This is done by way of SetWindowOwner().
+//
+// Each window may be associated with two accounts. The owning account (the
+// account supplied to SetWindowOwner()), and an account the window is shown
+// with when the account is active. Typically the 'shown' account and 'owning'
+// account are the same, but the user may choose to show a window from an other
+// account, in which case the 'shown' account changes.
+//
+// Note:
+// - aura::Window::Hide() is currently hiding the window and all owned transient
+//   children. However aura::Window::Show() is only showing the window itself.
+//   To address that, all transient children (and their children) are remembered
+//   in |transient_window_to_visibility_| and monitored to keep track of the
+//   visibility changes from the owning user. This way the visibility can be
+//   changed back to its requested state upon showing by us - or when the window
+//   gets detached from its current owning parent.
+class ASH_EXPORT MultiUserWindowManager : public SessionObserver,
+                                          public aura::WindowObserver,
+                                          public ::wm::TransientWindowObserver,
+                                          public TabletModeObserver {
+ public:
+  // The speed which should be used to perform animations.
+  enum AnimationSpeed {
+    ANIMATION_SPEED_NORMAL,   // The normal animation speed.
+    ANIMATION_SPEED_FAST,     // Unit test speed which test animations.
+    ANIMATION_SPEED_DISABLED  // Unit tests which do not require animations.
+  };
+
+  MultiUserWindowManager(MultiUserWindowManagerDelegate* delegate,
+                         const AccountId& account_id);
+  ~MultiUserWindowManager() override;
+
+  static MultiUserWindowManager* Get();
+
+  // 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);
+
+  // 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
+  // user. When the window is minimized, the 'shown' account is reset to the
+  // 'owning' account.
+  void ShowWindowForUser(aura::Window* window, const AccountId& account_id);
+
+  // SessionObserver:
+  void OnActiveUserSessionChanged(const AccountId& account_id) override;
+
+  // WindowObserver overrides:
+  void OnWindowDestroyed(aura::Window* window) override;
+  void OnWindowVisibilityChanging(aura::Window* window, bool visible) override;
+  void OnWindowVisibilityChanged(aura::Window* window, bool visible) override;
+
+  // TransientWindowObserver overrides:
+  void OnTransientChildAdded(aura::Window* window,
+                             aura::Window* transient) override;
+  void OnTransientChildRemoved(aura::Window* window,
+                               aura::Window* transient) override;
+
+  // TabletModeObserver:
+  void OnTabletModeStarted() override;
+
+  // Disable any animations for unit tests.
+  void SetAnimationSpeedForTest(AnimationSpeed speed);
+
+  // Returns true when a user switch animation is running. For unit tests.
+  bool IsAnimationRunningForTest();
+
+  // Returns the current user for unit tests.
+  const AccountId& GetCurrentUserForTest() const;
+
+ protected:
+  class WindowEntry {
+   public:
+    explicit WindowEntry(const AccountId& account_id)
+        : owner_(account_id), show_for_user_(account_id) {}
+    ~WindowEntry() {}
+
+    // Returns the owner of this window. This cannot be changed.
+    const AccountId& owner() const { return owner_; }
+
+    // Returns the user for which this should be shown.
+    const AccountId& show_for_user() const { return show_for_user_; }
+
+    // Returns if the window should be shown for the "show user" or not.
+    bool show() const { return show_; }
+
+    // Set the user which will display the window on the owned desktop. If
+    // an empty user id gets passed the owner will be used.
+    void set_show_for_user(const AccountId& account_id) {
+      show_for_user_ = account_id.is_valid() ? account_id : owner_;
+    }
+
+    // Sets if the window gets shown for the active user or not.
+    void set_show(bool show) { show_ = show; }
+
+   private:
+    // The user id of the owner of this window.
+    const AccountId owner_;
+
+    // The user id of the user on which desktop the window gets shown.
+    AccountId show_for_user_;
+
+    // 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*>;
+
+  const AccountId& GetWindowOwner(aura::Window* window) const;
+
+  // Returns true if at least one window's 'owner' account differs from its
+  // 'shown' account. In other words, a window from one account is shown with
+  // windows from another account.
+  bool AreWindowsSharedAmongUsers() const;
+
+  // Returns true if the 'shown' owner of |window| is |account_id|.
+  bool IsWindowOnDesktopOfUser(aura::Window* window,
+                               const AccountId& account_id) const;
+
+  // Returns the 'shown' owner.
+  const AccountId& GetUserPresentingWindow(aura::Window* window) const;
+
+  // Show a window for a user without switching the user.
+  // Returns true when the window moved to a new desktop.
+  bool ShowWindowForUserIntern(aura::Window* window,
+                               const AccountId& account_id);
+
+  // Show / hide the given window. Note: By not doing this within the functions,
+  // this allows to either switching to different ways to show/hide and / or to
+  // distinguish state changes performed by this class vs. state changes
+  // performed by the others. Note furthermore that system modal dialogs will
+  // not get hidden. We will switch instead to the owners desktop.
+  // The |animation_time| is the time the animation should take, an empty value
+  // switches instantly.
+  void SetWindowVisibility(aura::Window* window,
+                           bool visible,
+                           base::TimeDelta animation_time = base::TimeDelta());
+
+  const WindowToEntryMap& window_to_entry() { return window_to_entry_; }
+
+ private:
+  friend class MultiUserWindowManagerChromeOSTest;
+  friend class UserSwitchAnimator;
+
+  using TransientWindowToVisibility = base::flat_map<aura::Window*, bool>;
+
+  // Show the window and its transient children. However - if a transient child
+  // was turned invisible by some other operation, it will stay invisible.
+  // |animation_time| is the amount of time to animate.
+  void ShowWithTransientChildrenRecursive(aura::Window* window,
+                                          base::TimeDelta animation_time);
+
+  // Find the first owned window in the chain.
+  // Returns NULL when the window itself is owned.
+  aura::Window* GetOwningWindowInTransientChain(aura::Window* window) const;
+
+  // A |window| and its children were attached as transient children to an
+  // |owning_parent| and need to be registered. Note that the |owning_parent|
+  // itself will not be registered, but its children will.
+  void AddTransientOwnerRecursive(aura::Window* window,
+                                  aura::Window* owning_parent);
+
+  // A window and its children were removed from its parent and can be
+  // unregistered.
+  void RemoveTransientOwnerRecursive(aura::Window* window);
+
+  // Animate a |window| to be |visible| over a time of |animation_time|.
+  void SetWindowVisible(aura::Window* window,
+                        bool visible,
+                        base::TimeDelta aimation_time);
+
+  // Returns the time for an animation.
+  base::TimeDelta GetAdjustedAnimationTime(base::TimeDelta default_time) const;
+
+  MultiUserWindowManagerDelegate* delegate_;
+
+  // A lookup to see to which user the given window belongs to, where and if it
+  // should get shown.
+  WindowToEntryMap window_to_entry_;
+
+  // A map which remembers for owned transient windows their own visibility.
+  TransientWindowToVisibility transient_window_to_visibility_;
+
+  // The currently selected active user. It is used to find the proper
+  // visibility state in various cases. The state is stored here instead of
+  // being read from the user manager to be in sync while a switch occurs.
+  AccountId current_account_id_;
+
+  // Suppress changes to the visibility flag while we are changing it ourselves.
+  bool suppress_visibility_changes_ = false;
+
+  // The speed which is used to perform any animations.
+  AnimationSpeed animation_speed_ = ANIMATION_SPEED_NORMAL;
+
+  // The animation between users.
+  std::unique_ptr<UserSwitchAnimator> animation_;
+
+  DISALLOW_COPY_AND_ASSIGN(MultiUserWindowManager);
+};
+
+}  // namespace ash
+
+#endif  // ASH_MULTI_USER_MULTI_USER_WINDOW_MANAGER_H_
diff --git a/ash/multi_user/multi_user_window_manager_delegate.h b/ash/multi_user/multi_user_window_manager_delegate.h
new file mode 100644
index 0000000..4df91f87
--- /dev/null
+++ b/ash/multi_user/multi_user_window_manager_delegate.h
@@ -0,0 +1,45 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_MULTI_USER_MULTI_USER_WINDOW_MANAGER_DELEGATE_H_
+#define ASH_MULTI_USER_MULTI_USER_WINDOW_MANAGER_DELEGATE_H_
+
+#include "ash/ash_export.h"
+
+class AccountId;
+
+namespace aura {
+class Window;
+}  // namespace aura
+
+namespace ash {
+
+class ASH_EXPORT MultiUserWindowManagerDelegate {
+ 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 OnOwnerEntryChanged(aura::Window* window,
+                                   const AccountId& account_id,
+                                   bool was_minimized,
+                                   bool teleported) {}
+
+  // Called when the active account changes. This is followed by
+  // OnTransitionUserShelfToNewAccount() and OnDidSwitchActiveAccount().
+  virtual void OnWillSwitchActiveAccount(const AccountId& account_id) {}
+
+  // Called at the time when the user's shelf should transition to the account
+  // supplied to OnWillSwitchActiveAccount().
+  virtual void OnTransitionUserShelfToNewAccount() {}
+
+  // Called when the active account change is complete.
+  virtual void OnDidSwitchActiveAccount() {}
+
+ protected:
+  virtual ~MultiUserWindowManagerDelegate() {}
+};
+
+}  // namespace ash
+
+#endif  // ASH_MULTI_USER_MULTI_USER_WINDOW_MANAGER_DELEGATE_H_
diff --git a/ash/multi_user/user_switch_animator.cc b/ash/multi_user/user_switch_animator.cc
new file mode 100644
index 0000000..54a2a3d
--- /dev/null
+++ b/ash/multi_user/user_switch_animator.cc
@@ -0,0 +1,366 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/multi_user/user_switch_animator.h"
+
+#include "ash/multi_user/multi_user_window_manager.h"
+#include "ash/multi_user/multi_user_window_manager_delegate.h"
+#include "ash/shell.h"
+#include "ash/wallpaper/wallpaper_controller.h"
+#include "ash/wm/mru_window_tracker.h"
+#include "ash/wm/window_positioner.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/compositor/layer_animation_observer.h"
+#include "ui/compositor/layer_tree_owner.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/display/display.h"
+#include "ui/wm/core/window_util.h"
+#include "ui/wm/public/activation_client.h"
+
+namespace ash {
+namespace {
+
+// The minimal possible animation time for animations which should happen
+// "instantly".
+constexpr base::TimeDelta kMinimalAnimationTime =
+    base::TimeDelta::FromMilliseconds(1);
+
+// logic while the user gets switched.
+class UserChangeActionDisabler {
+ public:
+  UserChangeActionDisabler() {
+    WindowPositioner::DisableAutoPositioning(true);
+    Shell::Get()->mru_window_tracker()->SetIgnoreActivations(true);
+  }
+
+  ~UserChangeActionDisabler() {
+    WindowPositioner::DisableAutoPositioning(false);
+    Shell::Get()->mru_window_tracker()->SetIgnoreActivations(false);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(UserChangeActionDisabler);
+};
+
+// Defines an animation watcher for the 'hide' animation of the first maximized
+// window we encounter while looping through the old user's windows. This is
+// to observe the end of the animation so that we can destruct the old detached
+// layer of the window.
+class MaximizedWindowAnimationWatcher : public ui::ImplicitAnimationObserver {
+ public:
+  explicit MaximizedWindowAnimationWatcher(
+      std::unique_ptr<ui::LayerTreeOwner> old_layer)
+      : old_layer_(std::move(old_layer)) {}
+
+  // ui::ImplicitAnimationObserver:
+  void OnImplicitAnimationsCompleted() override { delete this; }
+
+ private:
+  std::unique_ptr<ui::LayerTreeOwner> old_layer_;
+
+  DISALLOW_COPY_AND_ASSIGN(MaximizedWindowAnimationWatcher);
+};
+
+// Modifies the given |window_list| such that the most-recently used window (if
+// any, and if it exists in |window_list|) will be the last window in the list.
+void PutMruWindowLast(std::vector<aura::Window*>* window_list) {
+  DCHECK(window_list);
+  auto it = std::find_if(
+      window_list->begin(), window_list->end(),
+      [](aura::Window* window) { return wm::IsActiveWindow(window); });
+  if (it == window_list->end())
+    return;
+  // Move the active window to the end of the list.
+  aura::Window* active_window = *it;
+  window_list->erase(it);
+  window_list->push_back(active_window);
+}
+
+}  // namespace
+
+UserSwitchAnimator::UserSwitchAnimator(
+    MultiUserWindowManager* owner,
+    mojom::WallpaperUserInfoPtr wallpaper_user_info,
+    base::TimeDelta animation_speed)
+    : owner_(owner),
+      wallpaper_user_info_(std::move(wallpaper_user_info)),
+      new_account_id_(wallpaper_user_info_->account_id),
+      animation_speed_(animation_speed),
+      animation_step_(ANIMATION_STEP_HIDE_OLD_USER),
+      screen_cover_(GetScreenCover(NULL)),
+      windows_by_account_id_() {
+  BuildUserToWindowsListMap();
+  AdvanceUserTransitionAnimation();
+
+  if (animation_speed_.is_zero()) {
+    FinalizeAnimation();
+  } else {
+    user_changed_animation_timer_.reset(new base::RepeatingTimer());
+    user_changed_animation_timer_->Start(
+        FROM_HERE, animation_speed_,
+        base::BindRepeating(&UserSwitchAnimator::AdvanceUserTransitionAnimation,
+                            base::Unretained(this)));
+  }
+}
+
+UserSwitchAnimator::~UserSwitchAnimator() {
+  FinalizeAnimation();
+}
+
+// static
+bool UserSwitchAnimator::CoversScreen(aura::Window* window) {
+  // Full screen covers the screen naturally. Since a normal window can have the
+  // same size as the work area, we only compare the bounds against the work
+  // area.
+  if (wm::WindowStateIs(window, ui::SHOW_STATE_FULLSCREEN))
+    return true;
+  gfx::Rect bounds = window->GetBoundsInScreen();
+  gfx::Rect work_area =
+      display::Screen::GetScreen()->GetDisplayNearestWindow(window).work_area();
+  bounds.Intersect(work_area);
+  return work_area == bounds;
+}
+
+void UserSwitchAnimator::AdvanceUserTransitionAnimation() {
+  DCHECK_NE(animation_step_, ANIMATION_STEP_ENDED);
+
+  TransitionWallpaper(animation_step_);
+  TransitionUserShelf(animation_step_);
+  TransitionWindows(animation_step_);
+
+  // Advance to the next step.
+  switch (animation_step_) {
+    case ANIMATION_STEP_HIDE_OLD_USER:
+      animation_step_ = ANIMATION_STEP_SHOW_NEW_USER;
+      break;
+    case ANIMATION_STEP_SHOW_NEW_USER:
+      animation_step_ = ANIMATION_STEP_FINALIZE;
+      break;
+    case ANIMATION_STEP_FINALIZE:
+      user_changed_animation_timer_.reset();
+      animation_step_ = ANIMATION_STEP_ENDED;
+      if (owner_->delegate_)
+        owner_->delegate_->OnDidSwitchActiveAccount();
+      break;
+    case ANIMATION_STEP_ENDED:
+      NOTREACHED();
+      break;
+  }
+}
+
+void UserSwitchAnimator::CancelAnimation() {
+  animation_step_ = ANIMATION_STEP_ENDED;
+}
+
+void UserSwitchAnimator::FinalizeAnimation() {
+  user_changed_animation_timer_.reset();
+  while (ANIMATION_STEP_ENDED != animation_step_)
+    AdvanceUserTransitionAnimation();
+}
+
+void UserSwitchAnimator::TransitionWallpaper(AnimationStep animation_step) {
+  WallpaperController* wallpaper_controller =
+      Shell::Get()->wallpaper_controller();
+
+  // Handle the wallpaper switch.
+  if (animation_step == ANIMATION_STEP_HIDE_OLD_USER) {
+    // Set the wallpaper cross dissolve animation duration to our complete
+    // animation cycle for a fade in and fade out.
+    base::TimeDelta duration =
+        animation_speed_ * (NO_USER_COVERS_SCREEN == screen_cover_ ? 2 : 0);
+    wallpaper_controller->SetAnimationDuration(
+        duration > kMinimalAnimationTime ? duration : kMinimalAnimationTime);
+    if (screen_cover_ != NEW_USER_COVERS_SCREEN) {
+      DCHECK(wallpaper_user_info_);
+      wallpaper_controller->ShowUserWallpaper(std::move(wallpaper_user_info_));
+      wallpaper_user_id_for_test_ =
+          (NO_USER_COVERS_SCREEN == screen_cover_ ? "->" : "") +
+          new_account_id_.Serialize();
+    }
+  } else if (animation_step == ANIMATION_STEP_FINALIZE) {
+    // Revert the wallpaper cross dissolve animation duration back to the
+    // default.
+    if (screen_cover_ == NEW_USER_COVERS_SCREEN) {
+      DCHECK(wallpaper_user_info_);
+      wallpaper_controller->ShowUserWallpaper(std::move(wallpaper_user_info_));
+    }
+
+    // Coming here the wallpaper user id is the final result. No matter how we
+    // got here.
+    wallpaper_user_id_for_test_ = new_account_id_.Serialize();
+    wallpaper_controller->SetAnimationDuration(base::TimeDelta());
+  }
+}
+
+void UserSwitchAnimator::TransitionUserShelf(AnimationStep animation_step) {
+  if (animation_step != ANIMATION_STEP_SHOW_NEW_USER)
+    return;
+
+  if (owner_->delegate_)
+    owner_->delegate_->OnTransitionUserShelfToNewAccount();
+}
+
+void UserSwitchAnimator::TransitionWindows(AnimationStep animation_step) {
+  // Disable the window position manager and the MRU window tracker temporarily.
+  UserChangeActionDisabler disabler;
+
+  // Animation duration.
+  base::TimeDelta duration = base::TimeDelta::FromMilliseconds(
+      std::max(kMinimalAnimationTime.InMilliseconds(),
+               2 * animation_speed_.InMilliseconds()));
+
+  switch (animation_step) {
+    case ANIMATION_STEP_HIDE_OLD_USER: {
+      // Hide the old users.
+      for (auto& user_pair : windows_by_account_id_) {
+        auto& show_for_account_id = user_pair.first;
+        if (show_for_account_id == new_account_id_)
+          continue;
+
+        bool found_foreground_maximized_window = false;
+
+        // We hide the windows such that the MRU window is the last one to be
+        // hidden, at which point all other windows have already been hidden,
+        // and hence the FocusController will not be able to find a next
+        // activateable window to restore focus to, and so we don't change
+        // window order (crbug.com/424307).
+        PutMruWindowLast(&(user_pair.second));
+        for (auto* window : user_pair.second) {
+          // Minimized visiting windows (minimized windows with an owner
+          // different than that of the for_show_account_id) should retrun to
+          // their
+          // original owners' desktops.
+          MultiUserWindowManager::WindowToEntryMap::const_iterator itr =
+              owner_->window_to_entry().find(window);
+          DCHECK(itr != owner_->window_to_entry().end());
+          if (show_for_account_id != itr->second->owner() &&
+              wm::WindowStateIs(window, ui::SHOW_STATE_MINIMIZED)) {
+            owner_->ShowWindowForUserIntern(window, itr->second->owner());
+            wm::Unminimize(window);
+            continue;
+          }
+
+          if (!found_foreground_maximized_window && CoversScreen(window) &&
+              screen_cover_ == BOTH_USERS_COVER_SCREEN) {
+            // Maximized windows should be hidden, but visually kept visible
+            // in order to prevent showing the background while the animation is
+            // in progress. Therefore we detach the old layer and recreate fresh
+            // ones. The old layers will be destructed at the animation step
+            // |ANIMATION_STEP_FINALIZE|.
+            // old_layers_.push_back(wm::RecreateLayers(window));
+            // We only want to do this for the first (foreground) maximized
+            // window we encounter.
+            found_foreground_maximized_window = true;
+            std::unique_ptr<ui::LayerTreeOwner> old_layer =
+                wm::RecreateLayers(window);
+            window->layer()->parent()->StackAtBottom(old_layer->root());
+            ui::ScopedLayerAnimationSettings settings(
+                window->layer()->GetAnimator());
+            settings.AddObserver(
+                new MaximizedWindowAnimationWatcher(std::move(old_layer)));
+            // Call SetWindowVisibility() within the scope of |settings| so that
+            // MaximizedWindowAnimationWatcher is notified when the animation
+            // completes.
+            owner_->SetWindowVisibility(window, false, duration);
+          } else {
+            owner_->SetWindowVisibility(window, false, duration);
+          }
+        }
+      }
+
+      // Show new user.
+      auto new_user_itr = windows_by_account_id_.find(new_account_id_);
+      if (new_user_itr == windows_by_account_id_.end())
+        return;
+
+      for (auto* window : new_user_itr->second) {
+        auto entry = owner_->window_to_entry().find(window);
+        DCHECK(entry != owner_->window_to_entry().end());
+
+        if (entry->second->show())
+          owner_->SetWindowVisibility(window, true, duration);
+      }
+
+      break;
+    }
+    case ANIMATION_STEP_SHOW_NEW_USER: {
+      // In order to make the animation look better, we had to move the code
+      // that shows the new user to the previous step. Hence, we do nothing
+      // here.
+      break;
+    }
+    case ANIMATION_STEP_FINALIZE: {
+      // Reactivate the MRU window of the new user.
+      aura::Window::Windows mru_list =
+          Shell::Get()->mru_window_tracker()->BuildMruWindowList();
+      if (!mru_list.empty()) {
+        aura::Window* window = mru_list[0];
+        if (owner_->IsWindowOnDesktopOfUser(window, new_account_id_) &&
+            !wm::WindowStateIs(window, ui::SHOW_STATE_MINIMIZED)) {
+          // Several unit tests come here without an activation client.
+          wm::ActivationClient* client =
+              wm::GetActivationClient(window->GetRootWindow());
+          if (client)
+            client->ActivateWindow(window);
+        }
+      }
+
+      break;
+    }
+    case ANIMATION_STEP_ENDED:
+      NOTREACHED();
+      break;
+  }
+}
+
+UserSwitchAnimator::TransitioningScreenCover UserSwitchAnimator::GetScreenCover(
+    aura::Window* root_window) {
+  TransitioningScreenCover cover = NO_USER_COVERS_SCREEN;
+  for (auto& pair : owner_->window_to_entry()) {
+    aura::Window* window = pair.first;
+    if (root_window && window->GetRootWindow() != root_window)
+      continue;
+    if (window->IsVisible() && CoversScreen(window)) {
+      if (cover == NEW_USER_COVERS_SCREEN)
+        return BOTH_USERS_COVER_SCREEN;
+      else
+        cover = OLD_USER_COVERS_SCREEN;
+    } else if (owner_->IsWindowOnDesktopOfUser(window, new_account_id_) &&
+               CoversScreen(window)) {
+      if (cover == OLD_USER_COVERS_SCREEN)
+        return BOTH_USERS_COVER_SCREEN;
+      else
+        cover = NEW_USER_COVERS_SCREEN;
+    }
+  }
+  return cover;
+}
+
+void UserSwitchAnimator::BuildUserToWindowsListMap() {
+  // This is to be called only at the time this animation is constructed.
+  DCHECK(windows_by_account_id_.empty());
+
+  // For each unique parent window, we enumerate its children windows, and
+  // for each child if it's in the |window_to_entry()| map, we add it to the
+  // |windows_by_account_id_| map.
+  // This gives us a list of windows per each user that is in the same order
+  // they were created in their parent windows.
+  std::set<aura::Window*> parent_windows;
+  auto& window_to_entry_map = owner_->window_to_entry();
+  for (auto& window_entry_pair : window_to_entry_map) {
+    aura::Window* parent_window = window_entry_pair.first->parent();
+    if (parent_windows.find(parent_window) == parent_windows.end()) {
+      parent_windows.insert(parent_window);
+      for (auto* child_window : parent_window->children()) {
+        auto itr = window_to_entry_map.find(child_window);
+        if (itr != window_to_entry_map.end()) {
+          windows_by_account_id_[itr->second->show_for_user()].push_back(
+              child_window);
+        }
+      }
+    }
+  }
+}
+
+}  // namespace ash
diff --git a/ash/multi_user/user_switch_animator.h b/ash/multi_user/user_switch_animator.h
new file mode 100644
index 0000000..2f7ba50b5
--- /dev/null
+++ b/ash/multi_user/user_switch_animator.h
@@ -0,0 +1,136 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_MULTI_USER_USER_SWITCH_ANIMATOR_H_
+#define ASH_MULTI_USER_USER_SWITCH_ANIMATOR_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "ash/ash_export.h"
+#include "ash/public/interfaces/wallpaper.mojom.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "components/account_id/account_id.h"
+#include "ui/aura/window.h"
+
+namespace ash {
+
+class MultiUserWindowManager;
+
+// A class which performs transitions animations between users. Upon creation,
+// the animation gets started and upon destruction the animation gets finished
+// if not done yet.
+// Specifying |animation_disabled| upon creation will perform the transition
+// without visible animations.
+class ASH_EXPORT UserSwitchAnimator {
+ public:
+  // The animation step for the user change animation.
+  enum AnimationStep {
+    ANIMATION_STEP_HIDE_OLD_USER,  // Hiding the old user (and shelf).
+    ANIMATION_STEP_SHOW_NEW_USER,  // Show the shelf of the new user.
+    ANIMATION_STEP_FINALIZE,       // All animations are done - final cleanup.
+    ANIMATION_STEP_ENDED           // The animation has ended.
+  };
+
+  // Creates a UserSwitchAnimator to animate between the current user and
+  // |user_info|.
+  UserSwitchAnimator(MultiUserWindowManager* owner,
+                     mojom::WallpaperUserInfoPtr user_info,
+                     base::TimeDelta animation_speed);
+  ~UserSwitchAnimator();
+
+  // Check if a window is covering the entire work area of the screen it is on.
+  static bool CoversScreen(aura::Window* window);
+
+  bool IsAnimationFinished() { return animation_step_ == ANIMATION_STEP_ENDED; }
+
+  // Returns the user id for which the wallpaper is currently shown.
+  // If a wallpaper is transitioning to B it will be returned as "->B".
+  const std::string& wallpaper_user_id_for_test() {
+    return wallpaper_user_id_for_test_;
+  }
+
+  // Advances the user switch animation to the next step. It reads the current
+  // step from |animation_step_| and increments it thereafter. When
+  // |ANIMATION_STEP_FINALIZE| gets executed, the animation is finished and the
+  // timer (if one exists) will get destroyed.
+  void AdvanceUserTransitionAnimation();
+
+  // When the system is shutting down, the animation can be stopped without
+  // ending it.
+  void CancelAnimation();
+
+ private:
+  // The window configuration of screen covering windows before an animation.
+  enum TransitioningScreenCover {
+    NO_USER_COVERS_SCREEN,   // No window covers the entire screen.
+    OLD_USER_COVERS_SCREEN,  // The current user has at least one window
+                             // covering the entire screen.
+    NEW_USER_COVERS_SCREEN,  // The user which becomes active has at least one
+                             // window covering the entire screen.
+    BOTH_USERS_COVER_SCREEN  // Both users have at least one window each
+                             // covering the entire screen.
+  };
+
+  // Finalizes the animation and ends the timer (if there is one).
+  void FinalizeAnimation();
+
+  // Execute the user wallpaper animations for |animation_step|.
+  void TransitionWallpaper(AnimationStep animtion_step);
+
+  // Update the shelf for |animation_step|.
+  void TransitionUserShelf(AnimationStep animtion_step);
+
+  // Execute the window animations for |animation_step|.
+  void TransitionWindows(AnimationStep animation_step);
+
+  // Check if a window is maximized / fullscreen / covering the entire screen.
+  // If a |root_window| is given, the screen coverage of that root_window is
+  // tested, otherwise all screens.
+  TransitioningScreenCover GetScreenCover(aura::Window* root_window);
+
+  // Builds the map that a user ID to the list of windows that should be shown
+  // for this user. This operation happens once upon the construction of this
+  // animation.
+  void BuildUserToWindowsListMap();
+
+  // The owning window manager.
+  MultiUserWindowManager* owner_;
+
+  // Contains the wallpaper configuration for the user switching to. This is
+  // passed to the WallpaperController at the right time.
+  mojom::WallpaperUserInfoPtr wallpaper_user_info_;
+
+  // The new user to set.
+  AccountId new_account_id_;
+
+  // The animation speed in ms. If 0, animations are disabled.
+  base::TimeDelta animation_speed_;
+
+  // The next animation step for AdvanceUserTransitionAnimation().
+  AnimationStep animation_step_;
+
+  // The screen cover status before the animation has started.
+  const TransitioningScreenCover screen_cover_;
+
+  // Mapping users IDs to the list of windows to show for these users.
+  typedef std::map<AccountId, aura::Window::Windows> UserToWindowsMap;
+  UserToWindowsMap windows_by_account_id_;
+
+  // A timer which watches to executes the second part of a "user changed"
+  // animation. Note that this timer exists only during such an animation.
+  std::unique_ptr<base::RepeatingTimer> user_changed_animation_timer_;
+
+  // For unit tests: Check which wallpaper was set.
+  std::string wallpaper_user_id_for_test_;
+
+  DISALLOW_COPY_AND_ASSIGN(UserSwitchAnimator);
+};
+
+}  // namespace ash
+
+#endif  // ASH_MULTI_USER_USER_SWITCH_ANIMATOR_H_
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index f9881a6..b1c277c 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -9,8 +9,6 @@
   sources = [
     "accelerators.cc",
     "accelerators.h",
-    "app_list/answer_card_contents_registry.cc",
-    "app_list/answer_card_contents_registry.h",
     "app_list/app_list_config.cc",
     "app_list/app_list_config.h",
     "app_list/app_list_constants.cc",
diff --git a/ash/public/cpp/app_list/answer_card_contents_registry.cc b/ash/public/cpp/app_list/answer_card_contents_registry.cc
deleted file mode 100644
index 1a9399f..0000000
--- a/ash/public/cpp/app_list/answer_card_contents_registry.cc
+++ /dev/null
@@ -1,67 +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/public/cpp/app_list/answer_card_contents_registry.h"
-
-#include "base/logging.h"
-
-namespace app_list {
-
-namespace {
-
-AnswerCardContentsRegistry* instance = nullptr;
-
-}  // namespace
-
-AnswerCardContentsRegistry::AnswerCardContentsRegistry() {
-  DCHECK(!instance);
-  instance = this;
-}
-
-AnswerCardContentsRegistry::~AnswerCardContentsRegistry() {
-  DCHECK_EQ(this, instance);
-  instance = nullptr;
-}
-
-// static
-AnswerCardContentsRegistry* AnswerCardContentsRegistry::Get() {
-  return instance;
-}
-
-base::UnguessableToken AnswerCardContentsRegistry::Register(
-    views::View* contents_view,
-    gfx::NativeView contents_native_view) {
-  const base::UnguessableToken token = base::UnguessableToken::Create();
-  contents_map_[token] = {contents_view, contents_native_view};
-  return token;
-}
-
-void AnswerCardContentsRegistry::Unregister(
-    const base::UnguessableToken& token) {
-  auto it = contents_map_.find(token);
-  if (it == contents_map_.end())
-    return;
-
-  contents_map_.erase(it);
-}
-
-views::View* AnswerCardContentsRegistry::GetView(
-    const base::UnguessableToken& token) {
-  auto it = contents_map_.find(token);
-  if (it == contents_map_.end())
-    return nullptr;
-
-  return it->second.view;
-}
-
-gfx::NativeView AnswerCardContentsRegistry::GetNativeView(
-    const base::UnguessableToken& token) {
-  auto it = contents_map_.find(token);
-  if (it == contents_map_.end())
-    return nullptr;
-
-  return it->second.native_view;
-}
-
-}  // namespace app_list
diff --git a/ash/public/cpp/app_list/answer_card_contents_registry.h b/ash/public/cpp/app_list/answer_card_contents_registry.h
deleted file mode 100644
index d0de858..0000000
--- a/ash/public/cpp/app_list/answer_card_contents_registry.h
+++ /dev/null
@@ -1,60 +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_PUBLIC_CPP_APP_LIST_ANSWER_CARD_CONTENTS_REGISTRY_H_
-#define ASH_PUBLIC_CPP_APP_LIST_ANSWER_CARD_CONTENTS_REGISTRY_H_
-
-#include <map>
-
-#include "ash/public/cpp/ash_public_export.h"
-#include "base/macros.h"
-#include "base/unguessable_token.h"
-#include "ui/gfx/native_widget_types.h"
-
-namespace views {
-class View;
-}
-
-namespace app_list {
-
-// Helper class to provide a token to answer card view map when app list UI code
-// runs in the same process of answer card provider (classic ash, or mash before
-// UI code is migrated to ash). In such environment, chrome creates and owns an
-// instance of this class. AnswerCardContents registers answer card contents
-// with it and get a token back. App list UI code gets the token and use it to
-// look up the view to show. On mash after the UI code migration, this class
-// will NOT be used at all. App list UI code would use the token to embed the
-// answer card content directly.
-class ASH_PUBLIC_EXPORT AnswerCardContentsRegistry {
- public:
-  AnswerCardContentsRegistry();
-  ~AnswerCardContentsRegistry();
-
-  static AnswerCardContentsRegistry* Get();
-
-  // Register content with a View and its relevant NativeView.
-  base::UnguessableToken Register(views::View* contents_view,
-                                  gfx::NativeView contents_native_view);
-
-  // Unregister and release the associated resources.
-  void Unregister(const base::UnguessableToken& token);
-
-  // Get the view for the given token. Return nullptr for unknown token.
-  views::View* GetView(const base::UnguessableToken& token);
-  gfx::NativeView GetNativeView(const base::UnguessableToken& token);
-
- private:
-  struct Entry {
-    views::View* view;
-    gfx::NativeView native_view;
-  };
-
-  std::map<base::UnguessableToken, Entry> contents_map_;
-
-  DISALLOW_COPY_AND_ASSIGN(AnswerCardContentsRegistry);
-};
-
-}  // namespace app_list
-
-#endif  // ASH_PUBLIC_CPP_APP_LIST_ANSWER_CARD_CONTENTS_REGISTRY_H_
diff --git a/ash/public/cpp/app_list/app_list_config.cc b/ash/public/cpp/app_list/app_list_config.cc
index 452b59a..5e08383 100644
--- a/ash/public/cpp/app_list/app_list_config.cc
+++ b/ash/public/cpp/app_list/app_list_config.cc
@@ -69,7 +69,8 @@
     grid_icon_dimension_ = 64;
     grid_icon_bottom_padding_ = 24;
     grid_title_top_padding_ = 82;
-    grid_title_width_ = 96;
+    grid_title_horizontal_padding_ = 0;
+    grid_title_width_ = grid_tile_width_;
     grid_focus_dimension_ = 80;
     grid_focus_corner_radius_ = 12;
     suggestion_chip_icon_dimension_ = 16;
diff --git a/ash/public/cpp/app_list/app_list_constants.cc b/ash/public/cpp/app_list/app_list_constants.cc
index 491f76b..bfd8716 100644
--- a/ash/public/cpp/app_list/app_list_constants.cc
+++ b/ash/public/cpp/app_list/app_list_constants.cc
@@ -36,7 +36,7 @@
 // it includes top 24px padding and bottom 56px padding.
 const int kHorizontalPagePreferredHeight = 623;
 
-const SkColor kFolderTitleColor = SkColorSetRGB(0x33, 0x33, 0x33);
+const SkColor kFolderTitleColor = gfx::kGoogleGrey700;
 const SkColor kFolderTitleHintTextColor = SkColorSetRGB(0xA0, 0xA0, 0xA0);
 // Color of the folder bubble shadow.
 const SkColor kFolderShadowColor = SkColorSetRGB(0xBF, 0xBF, 0xBF);
diff --git a/ash/public/cpp/app_list/app_list_features.cc b/ash/public/cpp/app_list/app_list_features.cc
index f46880a3..c4e8489 100644
--- a/ash/public/cpp/app_list/app_list_features.cc
+++ b/ash/public/cpp/app_list/app_list_features.cc
@@ -35,7 +35,7 @@
 const base::Feature kEnableZeroStateSuggestions{
     "EnableZeroStateSuggestions", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kEnableAppListSearchAutocomplete{
-    "EnableAppListSearchAutocomplete", base::FEATURE_ENABLED_BY_DEFAULT};
+    "EnableAppListSearchAutocomplete", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kEnableAppSearchResultRanker{
     "EnableAppSearchResultRanker", base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/ash/public/cpp/ash_constants.h b/ash/public/cpp/ash_constants.h
index b9d5cf6..d5018b4 100644
--- a/ash/public/cpp/ash_constants.h
+++ b/ash/public/cpp/ash_constants.h
@@ -43,6 +43,10 @@
 constexpr mojom::AutoclickEventType kDefaultAutoclickEventType =
     mojom::AutoclickEventType::kLeftClick;
 
+// The default threshold of mouse movement, measured in DIP, that will initiate
+// a new autoclick.
+constexpr int kDefaultAutoclickMovementThreshold = 20;
+
 // The default frame color.
 constexpr SkColor kDefaultFrameColor = SkColorSetRGB(0xFD, 0xFE, 0xFF);
 
diff --git a/ash/public/cpp/ash_features.cc b/ash/public/cpp/ash_features.cc
index 47ec75a..7183df4 100644
--- a/ash/public/cpp/ash_features.cc
+++ b/ash/public/cpp/ash_features.cc
@@ -46,9 +46,6 @@
 const base::Feature kNotificationScrollBar{"NotificationScrollBar",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kOverviewSwipeToClose{"OverviewSwipeToClose",
-                                          base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kTrilinearFiltering{"TrilinearFiltering",
                                         base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/ash/public/cpp/ash_features.h b/ash/public/cpp/ash_features.h
index 25f14c4..96b16001 100644
--- a/ash/public/cpp/ash_features.h
+++ b/ash/public/cpp/ash_features.h
@@ -63,11 +63,6 @@
 // Enables notification scroll bar in UnifiedSystemTray.
 ASH_PUBLIC_EXPORT extern const base::Feature kNotificationScrollBar;
 
-// Enables swipe to close in overview mode.
-// TODO(sammiequon): Remove this after the feature is fully launched.
-// https://crbug.com/828646.
-ASH_PUBLIC_EXPORT extern const base::Feature kOverviewSwipeToClose;
-
 // Enables trilinear filtering.
 ASH_PUBLIC_EXPORT extern const base::Feature kTrilinearFiltering;
 
diff --git a/ash/public/cpp/ash_pref_names.cc b/ash/public/cpp/ash_pref_names.cc
index 4a3ed9d..dc0ffbd 100644
--- a/ash/public/cpp/ash_pref_names.cc
+++ b/ash/public/cpp/ash_pref_names.cc
@@ -57,6 +57,10 @@
 // another event type action, or whether it should stay as the other event type.
 const char kAccessibilityAutoclickRevertToLeftClick[] =
     "settings.a11y.autoclick_revert_to_left_click";
+// The default threshold of mouse movement, measured in DIP, that will initiate
+// a new autoclick.
+const char kAccessibilityAutoclickMovementThreshold[] =
+    "settings.a11y.autoclick_movement_threshold";
 // A boolean pref which determines whether caret highlighting is enabled.
 const char kAccessibilityCaretHighlightEnabled[] =
     "settings.a11y.caret_highlight";
diff --git a/ash/public/cpp/ash_pref_names.h b/ash/public/cpp/ash_pref_names.h
index d0edd49..3eda609 100644
--- a/ash/public/cpp/ash_pref_names.h
+++ b/ash/public/cpp/ash_pref_names.h
@@ -25,6 +25,7 @@
 ASH_PUBLIC_EXPORT extern const char kAccessibilityAutoclickDelayMs[];
 ASH_PUBLIC_EXPORT extern const char kAccessibilityAutoclickEventType[];
 ASH_PUBLIC_EXPORT extern const char kAccessibilityAutoclickRevertToLeftClick[];
+ASH_PUBLIC_EXPORT extern const char kAccessibilityAutoclickMovementThreshold[];
 ASH_PUBLIC_EXPORT extern const char kAccessibilityCaretHighlightEnabled[];
 ASH_PUBLIC_EXPORT extern const char kAccessibilityCursorHighlightEnabled[];
 ASH_PUBLIC_EXPORT extern const char kAccessibilityFocusHighlightEnabled[];
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn
index e86d917..d9c4b81 100644
--- a/ash/public/interfaces/BUILD.gn
+++ b/ash/public/interfaces/BUILD.gn
@@ -60,7 +60,6 @@
     "volume.mojom",
     "vpn_list.mojom",
     "wallpaper.mojom",
-    "web_contents_manager.mojom",
     "window_pin_type.mojom",
     "window_properties.mojom",
     "window_state_type.mojom",
@@ -99,6 +98,7 @@
   testonly = true
   disable_variants = true
   sources = [
+    "login_screen_test_api.mojom",
     "shelf_test_api.mojom",
     "shell_test_api.mojom",
     "status_area_widget_test_api.mojom",
@@ -106,6 +106,7 @@
     "time_to_first_present_recorder_test_api.mojom",
   ]
   deps = [
+    "//components/account_id/interfaces",
     "//mojo/public/mojom/base",
     "//ui/gfx/geometry/mojo",
   ]
diff --git a/ash/public/interfaces/assistant_controller.mojom b/ash/public/interfaces/assistant_controller.mojom
index 428ba39..906f037 100644
--- a/ash/public/interfaces/assistant_controller.mojom
+++ b/ash/public/interfaces/assistant_controller.mojom
@@ -5,7 +5,6 @@
 module ash.mojom;
 
 import "ash/public/interfaces/assistant_image_downloader.mojom";
-import "ash/public/interfaces/web_contents_manager.mojom";
 import "ash/public/interfaces/assistant_setup.mojom";
 import "chromeos/services/assistant/public/mojom/assistant.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
@@ -23,10 +22,6 @@
   SetAssistantImageDownloader(
     AssistantImageDownloader assistant_image_downloader);
 
-  // Provides an interface to the |web_contents_manager| owned by
-  // AssistantClient.
-  SetWebContentsManager(WebContentsManager web_contents_manager);
-
   // Provides an interface to the |assistant_setup| owned by AssistantClient.
   SetAssistantSetup(AssistantSetup assistant_setup);
 
diff --git a/ash/public/interfaces/keyboard_controller.mojom b/ash/public/interfaces/keyboard_controller.mojom
index 75f93cf..348b5bd 100644
--- a/ash/public/interfaces/keyboard_controller.mojom
+++ b/ash/public/interfaces/keyboard_controller.mojom
@@ -6,7 +6,7 @@
 
 import "ui/gfx/geometry/mojo/geometry.mojom";
 import "ui/keyboard/public/keyboard_config.mojom";
-import "ui/keyboard/public/keyboard_enable_flag.mojom";
+import "ui/keyboard/public/keyboard_controller_types.mojom";
 
 enum HideReason {
   // Hide requested by an explicit user action.
@@ -18,6 +18,9 @@
 };
 
 interface KeyboardControllerObserver {
+  // Called when a keyboard enable flag changes.
+  OnKeyboardEnableFlagsChanged(array<keyboard.mojom.KeyboardEnableFlag> flags);
+
   // Called when the keyboard is enabled or disabled. If ReloadKeyboard() is
   // called or other code enables the keyboard while already enabled, this will
   // be called twice, once when the keyboard is disabled and again when it is
@@ -53,6 +56,9 @@
   // changes, enables or disables the keyboard to match the new state.
   ClearEnableFlag(keyboard.mojom.KeyboardEnableFlag flag);
 
+  // Gets the current set of keyboard enable flags.
+  GetEnableFlags() => (array<keyboard.mojom.KeyboardEnableFlag> flags);
+
   // Reloads the virtual keyboard if it is enabled and the URL has changed, e.g.
   // the focus has switched from one type of field to another.
   ReloadKeyboardIfNeeded();
@@ -71,6 +77,25 @@
   // Hides the virtual keyboard if it is visible.
   HideKeyboard(HideReason reason);
 
+  // Sets the keyboard container type. If non empty, |target_bounds| provides
+  // the container size. Returns whether the transition succeeded once the
+  // container type changes (or fails to change).
+  SetContainerType(keyboard.mojom.ContainerType container_type,
+                   gfx.mojom.Rect? target_bounds) => (bool result);
+
+  // If |locked| is true, the keyboard remains visible even when no window has
+  // input focus.
+  SetKeyboardLocked(bool locked);
+
+  // Sets the regions of the keyboard window that occlude whatever is behind it.
+  SetOccludedBounds(array<gfx.mojom.Rect> bounds);
+
+  // Sets the regions of the keyboard window where events should be handled.
+  SetHitTestBounds(array<gfx.mojom.Rect> bounds);
+
+  // Sets the region of the keyboard window that can be used as a drag handle.
+  SetDraggableArea(gfx.mojom.Rect bounds);
+
   // Adds a KeyboardControllerObserver.
   AddObserver(associated KeyboardControllerObserver observer);
 };
diff --git a/ash/public/interfaces/login_screen_test_api.mojom b/ash/public/interfaces/login_screen_test_api.mojom
new file mode 100644
index 0000000..8aca8d8
--- /dev/null
+++ b/ash/public/interfaces/login_screen_test_api.mojom
@@ -0,0 +1,16 @@
+// 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";
+
+// Provides a high-level test API for controlling the login/lock screen.
+interface LoginScreenTestApi {
+  // Returns true if the lock screen is currently being shown.
+  IsLockShown() => (bool is_shown);
+
+  // Submit |password| for |account_id|.
+  SubmitPassword(signin.mojom.AccountId account_id, string password) => ();
+};
diff --git a/ash/public/interfaces/system_tray.mojom b/ash/public/interfaces/system_tray.mojom
index b5cbdfd..e517a32 100644
--- a/ash/public/interfaces/system_tray.mojom
+++ b/ash/public/interfaces/system_tray.mojom
@@ -7,6 +7,15 @@
 import "ash/public/interfaces/update.mojom";
 import "mojo/public/mojom/base/string16.mojom";
 
+// The locale info to show in the locale feature detailed view.
+struct LocaleInfo {
+  // This ISO code of the locale.
+  string iso_code;
+
+  // The display name of the locale.
+  mojo_base.mojom.String16 display_name;
+};
+
 // Allows clients (e.g. Chrome browser) to control the ash system tray menu.
 interface SystemTray {
   // Sets the client interface.
@@ -33,6 +42,10 @@
   // tracing is running.
   SetPerformanceTracingIconVisible(bool visible);
 
+  // Sets the list of supported UI locales. |current_locale_iso_code| refers to
+  // the locale currently used by the UI.
+  SetLocaleList(array<LocaleInfo> locale_list, string current_locale_iso_code);
+
   // Shows an icon in the system tray or a notification on the unified system
   // menu indicating that a software update is available. Once shown, the icon
   // or the notification persists until reboot.
@@ -166,4 +179,8 @@
 
   // Attempts to restart the system for update.
   RequestRestartForUpdate();
+
+  // Sets the UI locale to |locale_iso_code| and exit the session to take
+  // effect.
+  SetLocaleAndExit(string locale_iso_code);
 };
diff --git a/ash/public/interfaces/user_info.mojom b/ash/public/interfaces/user_info.mojom
index 06aa244..c40bb51 100644
--- a/ash/public/interfaces/user_info.mojom
+++ b/ash/public/interfaces/user_info.mojom
@@ -5,6 +5,7 @@
 module ash.mojom;
 
 import "components/account_id/interfaces/account_id.mojom";
+import "mojo/public/mojom/base/token.mojom";
 import "ui/gfx/image/mojo/image.mojom";
 
 // Matches user_manager::UserType.
@@ -49,7 +50,7 @@
 struct UserInfo {
   UserType type;
   signin.mojom.AccountId account_id;
-  string service_user_id;
+  mojo_base.mojom.Token? service_instance_group;
   string display_name;
   string display_email;
   UserAvatar avatar;
@@ -61,4 +62,6 @@
   bool is_ephemeral;
   // True if the user is also the device owner.
   bool is_device_owner;
+  // True if the user has a gaia account.
+  bool has_gaia_account;
 };
diff --git a/ash/public/interfaces/web_contents_manager.mojom b/ash/public/interfaces/web_contents_manager.mojom
deleted file mode 100644
index 65037d9..0000000
--- a/ash/public/interfaces/web_contents_manager.mojom
+++ /dev/null
@@ -1,67 +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.
-
-module ash.mojom;
-
-import "components/account_id/interfaces/account_id.mojom";
-import "mojo/public/mojom/base/unguessable_token.mojom";
-import "ui/base/mojo/window_open_disposition.mojom";
-import "ui/gfx/geometry/mojo/geometry.mojom";
-import "url/mojom/url.mojom";
-
-// Interface for a class which is responsible for managing a WebContents in
-// chrome/browser and owning associated resources until they are explicity
-// released using the provided APIs.
-interface WebContentsManager {
-  // Creates and manages WebContents as defined by the specified |params|. Any
-  // call to ManageWebContents should be paired with a corresponding call to
-  // ReleaseWebContents when the resources are no longer needed. To this end,
-  // the caller must supply an identifier by which to uniquely identify
-  // WebContents resources. When the rendered content is ready for embedding,
-  // the supplied callback will be run providing an unguessable |embed_token|.
-  // In the event of failure, the callback will still be run but no
-  // |embed_token| will be provided.
-  ManageWebContents(mojo_base.mojom.UnguessableToken id_token,
-    ManagedWebContentsParams params)
-      => (mojo_base.mojom.UnguessableToken? embed_token);
-
-  // Releases any resources associated with the WebContents uniquely identified
-  // by |id_token|.
-  ReleaseWebContents(mojo_base.mojom.UnguessableToken id_token);
-
-  // Releases any resources associated with any WebContents uniquely identified
-  // by one of the specified |id_tokens|.
-  ReleaseAllWebContents(array<mojo_base.mojom.UnguessableToken> id_tokens);
-
-  // Navigates the WebContents uniquely identified by |id_token| back relative
-  // to the current history entry. The callback returns true if the WebContents
-  // were navigated, false otherwise.
-  NavigateWebContentsBack(mojo_base.mojom.UnguessableToken id_token)
-    => (bool navigated);
-};
-
-// Defines parameters for a managed WebContents.
-struct ManagedWebContentsParams {
-  // The account identifier for the profile.
-  signin.mojom.AccountId account_id;
-  // The initial URL.
-  url.mojom.Url url;
-  // The minimum desired size. Omitting defaults to (1, 1).
-  gfx.mojom.Size? min_size_dip;
-  // The maximum desired size. Omitting defaults to (INT_MAX, INT_MAX).
-  gfx.mojom.Size? max_size_dip;
-  // An optional delegate to handle top level browser requests. If omitted, top
-  // level browser requests will be handled according to default behavior.
-  ManagedWebContentsOpenUrlDelegate? open_url_delegate_ptr_info;
-};
-
-// Interface for a delegate to handle top level browser requests for a
-// managed WebContents.
-interface ManagedWebContentsOpenUrlDelegate {
-  // Invoked on a top level browser request to navigate to |url| with the
-  // specified window open |disposition|. Return false to consume the
-  // navigation attempt or true to allow it to continue.
-  ShouldOpenUrlFromTab(url.mojom.Url url,
-    ui.mojom.WindowOpenDisposition disposition) => (bool should_open);
-};
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn
index 6a57c93e..6a8aa48 100644
--- a/ash/resources/vector_icons/BUILD.gn
+++ b/ash/resources/vector_icons/BUILD.gn
@@ -214,6 +214,7 @@
     "unified_menu_info.icon",
     "unified_menu_keyboard_brightness.icon",
     "unified_menu_keyboard.icon",
+    "unified_menu_locale.icon",
     "unified_menu_lock.icon",
     "unified_menu_managed.icon",
     "unified_menu_more.icon",
diff --git a/ash/resources/vector_icons/unified_menu_locale.icon b/ash/resources/vector_icons/unified_menu_locale.icon
new file mode 100644
index 0000000..bb70f4c
--- /dev/null
+++ b/ash/resources/vector_icons/unified_menu_locale.icon
@@ -0,0 +1,65 @@
+// 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.
+
+CANVAS_DIMENSIONS, 24,
+MOVE_TO, 11.99f, 2,
+CUBIC_TO, 6.47f, 2, 2, 6.48f, 2, 12,
+R_CUBIC_TO, 0, 5.52f, 4.47f, 10, 9.99f, 10,
+CUBIC_TO, 17.52f, 22, 22, 17.52f, 22, 12,
+CUBIC_TO_SHORTHAND, 17.52f, 2, 11.99f, 2,
+CLOSE,
+R_MOVE_TO, 6.93f, 6,
+R_H_LINE_TO, -2.95f,
+R_CUBIC_TO, -0.32f, -1.25f, -0.78f, -2.45f, -1.38f, -3.56f,
+R_CUBIC_TO, 1.84f, 0.63f, 3.37f, 1.91f, 4.33f, 3.56f,
+CLOSE,
+MOVE_TO, 12, 4.04f,
+R_CUBIC_TO, 0.83f, 1.2f, 1.48f, 2.53f, 1.91f, 3.96f,
+R_H_LINE_TO, -3.82f,
+R_CUBIC_TO, 0.43f, -1.43f, 1.08f, -2.76f, 1.91f, -3.96f,
+CLOSE,
+MOVE_TO, 4.26f, 14,
+CUBIC_TO, 4.1f, 13.36f, 4, 12.69f, 4, 12,
+R_CUBIC_TO, 0, -0.69f, 0.1f, -1.36f, 0.26f, -2,
+R_H_LINE_TO, 3.38f,
+R_CUBIC_TO, -0.08f, 0.66f, -0.14f, 1.32f, -0.14f, 2,
+R_CUBIC_TO, 0, 0.68f, 0.06f, 1.34f, 0.14f, 2,
+H_LINE_TO, 4.26f,
+CLOSE,
+R_MOVE_TO, 0.82f, 2,
+R_H_LINE_TO, 2.95f,
+R_CUBIC_TO, 0.32f, 1.25f, 0.78f, 2.45f, 1.38f, 3.56f,
+R_CUBIC_TO, -1.84f, -0.63f, -3.37f, -1.9f, -4.33f, -3.56f,
+CLOSE,
+R_MOVE_TO, 2.95f, -8,
+H_LINE_TO, 5.08f,
+R_CUBIC_TO, 0.96f, -1.66f, 2.49f, -2.93f, 4.33f, -3.56f,
+CUBIC_TO, 8.81f, 5.55f, 8.35f, 6.75f, 8.03f, 8,
+CLOSE,
+MOVE_TO, 12, 19.96f,
+R_CUBIC_TO, -0.83f, -1.2f, -1.48f, -2.53f, -1.91f, -3.96f,
+R_H_LINE_TO, 3.82f,
+R_CUBIC_TO, -0.43f, 1.43f, -1.08f, 2.76f, -1.91f, 3.96f,
+CLOSE,
+MOVE_TO, 14.34f, 14,
+H_LINE_TO, 9.66f,
+R_CUBIC_TO, -0.09f, -0.66f, -0.16f, -1.32f, -0.16f, -2,
+R_CUBIC_TO, 0, -0.68f, 0.07f, -1.35f, 0.16f, -2,
+R_H_LINE_TO, 4.68f,
+R_CUBIC_TO, 0.09f, 0.65f, 0.16f, 1.32f, 0.16f, 2,
+R_CUBIC_TO, 0, 0.68f, -0.07f, 1.34f, -0.16f, 2,
+CLOSE,
+R_MOVE_TO, 0.25f, 5.56f,
+R_CUBIC_TO, 0.6f, -1.11f, 1.06f, -2.31f, 1.38f, -3.56f,
+R_H_LINE_TO, 2.95f,
+R_CUBIC_TO, -0.96f, 1.65f, -2.49f, 2.93f, -4.33f, 3.56f,
+CLOSE,
+MOVE_TO, 16.36f, 14,
+R_CUBIC_TO, 0.08f, -0.66f, 0.14f, -1.32f, 0.14f, -2,
+R_CUBIC_TO, 0, -0.68f, -0.06f, -1.34f, -0.14f, -2,
+R_H_LINE_TO, 3.38f,
+R_CUBIC_TO, 0.16f, 0.64f, 0.26f, 1.31f, 0.26f, 2,
+R_CUBIC_TO, 0, 0.69f, -0.1f, 1.36f, -0.26f, 2,
+R_H_LINE_TO, -3.38f,
+CLOSE
\ No newline at end of file
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index 1153f9f..c8ffab9 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -66,6 +66,7 @@
 #include "ui/aura/client/drag_drop_client.h"
 #include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/client/window_types.h"
+#include "ui/aura/null_window_targeter.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_observer.h"
@@ -259,6 +260,80 @@
   return window->owned_by_parent();
 }
 
+class RootWindowTargeter : public aura::WindowTargeter {
+ public:
+  RootWindowTargeter() = default;
+  ~RootWindowTargeter() override = default;
+
+ protected:
+  aura::Window* FindTargetForLocatedEvent(aura::Window* window,
+                                          ui::LocatedEvent* event) override {
+    if (!window->parent() && !window->bounds().Contains(event->location()) &&
+        IsEventInsideDisplayForTelemetryHack(window, event)) {
+      auto* dispatcher = window->GetHost()->dispatcher();
+      bool has_capture_target = !!dispatcher->mouse_pressed_handler() ||
+                                !!aura::client::GetCaptureWindow(window);
+
+      // Make sure that event location is within the root window bounds if
+      // 1) mouse event isn't captured.
+      // 2) A mouse is clicked without movement and capture.
+      //
+      // The event can be outside on some scale factor due to rounding, or due
+      // to not well calibrated a touch screen, or Detect this situation and
+      // adjust the location.
+      bool bounded_click = ShouldConstrainMouseClick(event, has_capture_target);
+      if (!has_capture_target || bounded_click) {
+        gfx::Point new_location =
+            FitPointToBounds(event->location(), window->bounds());
+        // Do not change |location_f|. It's used to compute pixel position and
+        // such client should know what they're doing.
+        event->set_location(new_location);
+        event->set_root_location(new_location);
+      }
+    }
+    return aura::WindowTargeter::FindTargetForLocatedEvent(window, event);
+  }
+
+  // Stop-gap workaround for telemetry tests that send events far outside of the
+  // display (e.g. 512, -4711). Fix the test and remove this (crbgu.com/904623).
+  bool IsEventInsideDisplayForTelemetryHack(aura::Window* window,
+                                            ui::LocatedEvent* event) {
+    constexpr int ExtraMarginForTelemetryTest = -10;
+    gfx::Rect bounds = window->bounds();
+    bounds.Inset(ExtraMarginForTelemetryTest, ExtraMarginForTelemetryTest);
+    return bounds.Contains(event->location());
+  }
+
+ private:
+  // Returns true if the mouse event should be constrainted.
+  bool ShouldConstrainMouseClick(ui::LocatedEvent* event,
+                                 bool has_capture_target) {
+    if (event->type() == ui::ET_MOUSE_PRESSED && !has_capture_target) {
+      last_mouse_event_type_ = ui::ET_MOUSE_PRESSED;
+      return true;
+    }
+    if (last_mouse_event_type_ == ui::ET_MOUSE_PRESSED &&
+        event->type() == ui::ET_MOUSE_RELEASED && has_capture_target) {
+      last_mouse_event_type_ = ui::ET_UNKNOWN;
+      return true;
+    }
+    // For other cases, reset the state
+    if (event->type() != ui::ET_MOUSE_CAPTURE_CHANGED)
+      last_mouse_event_type_ = ui::ET_UNKNOWN;
+    return false;
+  }
+
+  gfx::Point FitPointToBounds(const gfx::Point p, const gfx::Rect& bounds) {
+    return gfx::Point(
+        std::min(std::max(bounds.x(), p.x()), bounds.right() - 1),
+        std::min(std::max(bounds.y(), p.y()), bounds.bottom() - 1));
+  }
+
+  ui::EventType last_mouse_event_type_ = ui::ET_UNKNOWN;
+
+  DISALLOW_COPY_AND_ASSIGN(RootWindowTargeter);
+};
+
 }  // namespace
 
 // static
@@ -429,6 +504,9 @@
 }
 
 void RootWindowController::Shutdown() {
+  auto targeter = GetRootWindow()->SetEventTargeter(
+      std::make_unique<aura::NullWindowTargeter>());
+
   touch_exploration_manager_.reset();
 
   ResetRootForNewWindowsIfNecessary();
@@ -447,6 +525,12 @@
   system_wallpaper_.reset();
   lock_screen_action_background_controller_.reset();
   aura::client::SetScreenPositionClient(root_window, nullptr);
+
+  // The targeter may still on the stack, so delete it later.
+  if (targeter) {
+    base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
+                                                    std::move(targeter));
+  }
 }
 
 void RootWindowController::CloseChildWindows() {
@@ -616,6 +700,9 @@
   aura::Window* root_window = GetRootWindow();
   Shell* shell = Shell::Get();
   shell->InitRootWindow(root_window);
+  auto old_targeter =
+      root_window->SetEventTargeter(std::make_unique<RootWindowTargeter>());
+  DCHECK(!old_targeter);
 
   CreateContainers();
   CreateSystemWallpaper(root_window_type);
diff --git a/ash/root_window_controller_unittest.cc b/ash/root_window_controller_unittest.cc
index 098b82c..c28d208 100644
--- a/ash/root_window_controller_unittest.cc
+++ b/ash/root_window_controller_unittest.cc
@@ -37,9 +37,9 @@
 #include "ui/events/test/event_generator.h"
 #include "ui/events/test/test_event_handler.h"
 #include "ui/keyboard/keyboard_controller.h"
-#include "ui/keyboard/keyboard_switches.h"
 #include "ui/keyboard/keyboard_ui.h"
 #include "ui/keyboard/keyboard_util.h"
+#include "ui/keyboard/public/keyboard_switches.h"
 #include "ui/keyboard/test/keyboard_test_util.h"
 #include "ui/views/controls/menu/menu_controller.h"
 #include "ui/views/widget/widget.h"
@@ -748,26 +748,31 @@
 
   aura::Window* contents_window =
       keyboard::KeyboardController::Get()->GetKeyboardWindow();
-  contents_window->SetBounds(gfx::Rect());
   contents_window->Show();
+  keyboard::KeyboardController::Get()->ShowKeyboard(false);
 
   // Make sure no pending mouse events in the queue.
   RunAllPendingInMessageLoop();
 
+  // TODO(oshima|yhanada): This simply make sure that targeting logic works, but
+  // doesn't mean it'll deliver the event to the target. Fix this to make this
+  // more reliable.
   ui::test::TestEventHandler handler;
   root_window->AddPreTargetHandler(&handler);
 
   ui::test::EventGenerator event_generator(root_window, contents_window);
   event_generator.ClickLeftButton();
-  int expected_mouse_presses = 1;
-  EXPECT_EQ(expected_mouse_presses, handler.num_mouse_events() / 2);
+  EXPECT_EQ(2, handler.num_mouse_events());
 
   for (int block_reason = FIRST_BLOCK_REASON;
        block_reason < NUMBER_OF_BLOCK_REASONS; ++block_reason) {
+    SCOPED_TRACE(base::StringPrintf("Reason: %d", block_reason));
     BlockUserSession(static_cast<UserSessionBlockReason>(block_reason));
+    handler.Reset();
     event_generator.ClickLeftButton();
-    expected_mouse_presses++;
-    EXPECT_EQ(expected_mouse_presses, handler.num_mouse_events() / 2);
+    // Click may generate CAPTURE_CHANGED event so make sure it's more than
+    // 2 (press,release);
+    EXPECT_LE(2, handler.num_mouse_events());
     UnblockUserSession();
   }
   root_window->RemovePreTargetHandler(&handler);
diff --git a/ash/session/test_session_controller_client.cc b/ash/session/test_session_controller_client.cc
index c37a8ba..9660f3f 100644
--- a/ash/session/test_session_controller_client.cc
+++ b/ash/session/test_session_controller_client.cc
@@ -138,15 +138,17 @@
     bool enable_settings,
     bool provide_pref_service,
     bool is_new_profile,
-    const std::string& service_user_id) {
-  auto account_id = AccountId::FromUserEmail(GetUserIdFromEmail(display_email));
+    const base::Optional<base::Token>& service_instance_group) {
+  auto account_id = AccountId::FromUserEmail(
+      use_lower_case_user_id_ ? GetUserIdFromEmail(display_email)
+                              : display_email);
   mojom::UserSessionPtr session = mojom::UserSession::New();
   session->session_id = ++fake_session_id_;
   session->user_info = mojom::UserInfo::New();
   session->user_info->avatar = mojom::UserAvatar::New();
   session->user_info->type = user_type;
   session->user_info->account_id = account_id;
-  session->user_info->service_user_id = service_user_id;
+  session->user_info->service_instance_group = service_instance_group;
   session->user_info->display_name = "Über tray Über tray Über tray Über tray";
   session->user_info->display_email = display_email;
   session->user_info->is_ephemeral = false;
diff --git a/ash/session/test_session_controller_client.h b/ash/session/test_session_controller_client.h
index 671454c5..cc466c2 100644
--- a/ash/session/test_session_controller_client.h
+++ b/ash/session/test_session_controller_client.h
@@ -11,6 +11,8 @@
 
 #include "ash/public/interfaces/session_controller.mojom.h"
 #include "base/macros.h"
+#include "base/optional.h"
+#include "base/token.h"
 #include "components/user_manager/user_type.h"
 #include "mojo/public/cpp/bindings/binding.h"
 
@@ -41,6 +43,10 @@
   // Sets up the default state of SessionController.
   void Reset();
 
+  void set_use_lower_case_user_id(bool value) {
+    use_lower_case_user_id_ = value;
+  }
+
   // Helpers to set SessionController state.
   void SetCanLockScreen(bool can_lock);
   void SetShouldLockScreenAutomatically(bool should_lock);
@@ -66,7 +72,8 @@
       bool enable_settings = true,
       bool provide_pref_service = true,
       bool is_new_profile = false,
-      const std::string& service_user_id = std::string());
+      const base::Optional<base::Token>& service_instance_group =
+          base::nullopt);
 
   // Creates a test PrefService and associates it with the user.
   void ProvidePrefServiceForUser(const AccountId& account_id);
@@ -92,6 +99,8 @@
   int fake_session_id_ = 0;
   mojom::SessionInfoPtr session_info_;
 
+  bool use_lower_case_user_id_ = true;
+
   DISALLOW_COPY_AND_ASSIGN(TestSessionControllerClient);
 };
 
diff --git a/ash/shelf/app_list_button.cc b/ash/shelf/app_list_button.cc
index 7ef526d..33eaae9 100644
--- a/ash/shelf/app_list_button.cc
+++ b/ash/shelf/app_list_button.cc
@@ -68,17 +68,14 @@
     : ShelfControlButton(),
       listener_(listener),
       shelf_view_(shelf_view),
-      shelf_(shelf),
-      voice_interaction_binding_(this) {
+      shelf_(shelf) {
   DCHECK(listener_);
   DCHECK(shelf_view_);
   DCHECK(shelf_);
   Shell::Get()->AddShellObserver(this);
   Shell::Get()->session_controller()->AddObserver(this);
 
-  mojom::VoiceInteractionObserverPtr ptr;
-  voice_interaction_binding_.Bind(mojo::MakeRequest(&ptr));
-  Shell::Get()->voice_interaction_controller()->AddObserver(std::move(ptr));
+  Shell::Get()->voice_interaction_controller()->AddLocalObserver(this);
   SetAccessibleName(
       l10n_util::GetStringUTF16(IDS_ASH_SHELF_APP_LIST_LAUNCHER_TITLE));
   set_notify_action(Button::NOTIFY_ON_PRESS);
@@ -96,6 +93,7 @@
 AppListButton::~AppListButton() {
   Shell::Get()->RemoveShellObserver(this);
   Shell::Get()->session_controller()->RemoveObserver(this);
+  Shell::Get()->voice_interaction_controller()->RemoveLocalObserver(this);
 }
 
 void AppListButton::OnAppListShown() {
diff --git a/ash/shelf/app_list_button.h b/ash/shelf/app_list_button.h
index 2b9df96f..9a0bcd2 100644
--- a/ash/shelf/app_list_button.h
+++ b/ash/shelf/app_list_button.h
@@ -14,7 +14,6 @@
 #include "ash/shelf/shelf_control_button.h"
 #include "ash/shell_observer.h"
 #include "base/macros.h"
-#include "mojo/public/cpp/bindings/binding.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/views/controls/button/image_button.h"
 
@@ -98,8 +97,6 @@
   std::unique_ptr<base::OneShotTimer> assistant_animation_hide_delay_timer_;
   base::TimeTicks voice_interaction_start_timestamp_;
 
-  mojo::Binding<mojom::VoiceInteractionObserver> voice_interaction_binding_;
-
   DISALLOW_COPY_AND_ASSIGN(AppListButton);
 };
 
diff --git a/ash/shelf/login_shelf_view.cc b/ash/shelf/login_shelf_view.cc
index 0ec15e7..d3945a9 100644
--- a/ash/shelf/login_shelf_view.cc
+++ b/ash/shelf/login_shelf_view.cc
@@ -193,10 +193,7 @@
                         public ui::SimpleMenuModel::Delegate {
  public:
   KioskAppsButton(const base::string16& text, const gfx::ImageSkia& image)
-      : MenuButton(text, this, true), ui::SimpleMenuModel(this) {
-    // We don't want a menu marker for the apps button.
-    set_menu_marker(&empty_menu_marker_);
-
+      : MenuButton(text, this), ui::SimpleMenuModel(this) {
     SetFocusBehavior(FocusBehavior::ALWAYS);
     SetFocusPainter(views::Painter::CreateSolidFocusPainter(
         kFocusBorderColor, kFocusBorderThickness, gfx::InsetsF()));
@@ -324,8 +321,6 @@
  private:
   std::unique_ptr<views::MenuRunner> menu_runner_;
   std::vector<mojom::KioskAppInfoPtr> kiosk_apps_;
-  // Passed to set_menu_marker to remove menu marker
-  gfx::ImageSkia empty_menu_marker_;
   bool is_launch_enabled_ = true;
 
   DISALLOW_COPY_AND_ASSIGN(KioskAppsButton);
@@ -398,8 +393,12 @@
     // Focus should leave the system tray.
     Shell::Get()->system_tray_notifier()->NotifyFocusOut(reverse);
 
-    // If OOBE dialog is showing, it will take focus.
-    Shell::Get()->login_screen_controller()->FocusOobeDialog();
+    // If the dialog is hidden, let views handle the focus automatically.
+    // Otherwise, forward a focus request to the OOBE dialog.
+    if (dialog_state_ != mojom::OobeDialogState::HIDDEN &&
+        dialog_state_ != mojom::OobeDialogState::NONE) {
+      Shell::Get()->login_screen_controller()->FocusOobeDialog();
+    }
   } else {
     // Focus goes to status area.
     Shelf::ForWindow(GetWidget()->GetNativeWindow())
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index 7d5e072..093bdd7 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -1318,16 +1318,8 @@
 
   HomeLauncherGestureHandler* home_launcher_handler =
       Shell::Get()->app_list_controller()->home_launcher_gesture_handler();
-  bool dragged_down;
   if (home_launcher_handler &&
-      home_launcher_handler->OnReleaseEvent(gesture_in_screen.location(),
-                                            &dragged_down)) {
-    if (dragged_down && visibility_state() == SHELF_AUTO_HIDE) {
-      DCHECK_EQ(SHELF_AUTO_HIDE_SHOWN, gesture_drag_auto_hide_state_);
-      gesture_drag_auto_hide_state_ = SHELF_AUTO_HIDE_HIDDEN;
-      gesture_drag_status_ = GESTURE_DRAG_COMPLETE_IN_PROGRESS;
-      UpdateVisibilityState();
-    }
+      home_launcher_handler->OnReleaseEvent(gesture_in_screen.location())) {
     gesture_drag_status_ = GESTURE_DRAG_NONE;
     return;
   }
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 9a6d801..a961b70 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -9,6 +9,8 @@
 
 #include "ash/accelerators/accelerator_controller.h"
 #include "ash/accelerators/accelerator_table.h"
+#include "ash/app_list/app_list_controller_impl.h"
+#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"
@@ -147,6 +149,17 @@
   virtual ~ShelfDragCallback() = default;
 
   void ProcessScroll(ui::EventType type, const gfx::Vector2dF& delta) {
+    ProcessScrollInternal(type, delta, true);
+  }
+
+  void ProcessScrollNoBoundsCheck(ui::EventType type,
+                                  const gfx::Vector2dF& delta) {
+    ProcessScrollInternal(type, delta, false);
+  }
+
+  void ProcessScrollInternal(ui::EventType type,
+                             const gfx::Vector2dF& delta,
+                             bool bounds_check) {
     if (GetShelfLayoutManager()->visibility_state() == SHELF_HIDDEN)
       return;
 
@@ -196,23 +209,30 @@
       }
     } else {
       // The shelf is invisible at the start of the drag.
-      if (increasing_drag) {
+      if (increasing_drag && bounds_check) {
+        constexpr float kEpsilon = 1.f;
         // Moving the shelf into the screen.
         if (std::abs(scroll_delta) < shelf_size) {
           // Tests that the shelf sticks with the touch point during the drag
           // until the shelf is completely visible.
           if (SHELF_ALIGNMENT_BOTTOM == shelf->alignment()) {
-            EXPECT_EQ(shelf_bounds.y(), auto_hidden_shelf_bounds_.y() +
-                                            kHiddenShelfInScreenPortion -
-                                            std::abs(scroll_delta));
+            EXPECT_NEAR(shelf_bounds.y(),
+                        auto_hidden_shelf_bounds_.y() +
+                            kHiddenShelfInScreenPortion -
+                            std::abs(scroll_delta),
+                        kEpsilon);
           } else if (SHELF_ALIGNMENT_LEFT == shelf->alignment()) {
-            EXPECT_EQ(shelf_bounds.x(), auto_hidden_shelf_bounds_.x() -
-                                            kHiddenShelfInScreenPortion +
-                                            std::abs(scroll_delta));
+            EXPECT_NEAR(shelf_bounds.x(),
+                        auto_hidden_shelf_bounds_.x() -
+                            kHiddenShelfInScreenPortion +
+                            std::abs(scroll_delta),
+                        kEpsilon);
           } else if (SHELF_ALIGNMENT_RIGHT == shelf->alignment()) {
-            EXPECT_EQ(shelf_bounds.x(), auto_hidden_shelf_bounds_.x() +
-                                            kHiddenShelfInScreenPortion -
-                                            std::abs(scroll_delta));
+            EXPECT_NEAR(shelf_bounds.x(),
+                        auto_hidden_shelf_bounds_.x() +
+                            kHiddenShelfInScreenPortion -
+                            std::abs(scroll_delta),
+                        kEpsilon);
           }
         } else {
           // Tests that after the shelf is completely visible, the shelf starts
@@ -331,7 +351,14 @@
     return widget;
   }
 
-  void RunGestureDragTests(gfx::Vector2d);
+  void RunGestureDragTests(const gfx::Point& shown, const gfx::Point& hidden);
+
+  gfx::Rect GetVisibleShelfWidgetBoundsInScreen() {
+    gfx::Rect bounds = GetShelfWidget()->GetWindowBoundsInScreen();
+    bounds.Intersect(
+        display::Screen::GetScreen()->GetPrimaryDisplay().bounds());
+    return bounds;
+  }
 
   // Turn on the lock screen.
   void LockScreen() {
@@ -405,7 +432,13 @@
   DISALLOW_COPY_AND_ASSIGN(ShelfLayoutManagerTest);
 };
 
-void ShelfLayoutManagerTest::RunGestureDragTests(gfx::Vector2d delta) {
+void ShelfLayoutManagerTest::RunGestureDragTests(
+    const gfx::Point& edge_to_hide,
+    const gfx::Point& edge_to_show) {
+  ui::test::EventGenerator* generator = GetEventGenerator();
+  display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
+  generator->MoveMouseTo(display.bounds().CenterPoint());
+
   Shelf* shelf = GetPrimaryShelf();
   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
 
@@ -434,19 +467,17 @@
   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
   layout_manager->LayoutShelf();
 
-  ui::test::EventGenerator* generator = GetEventGenerator();
   const int kNumScrollSteps = 4;
   ShelfDragCallback handler(shelf_hidden, shelf_shown);
 
-  // Swipe up on the always shown shelf should not change any state.
-  gfx::Point start = GetShelfWidget()->GetWindowBoundsInScreen().CenterPoint();
-  gfx::Point end = start + delta;
-
   // Swipe down on the always shown shelf should not auto-hide it.
-  generator->GestureScrollSequenceWithCallback(
-      start, end, kTimeDelta, kNumScrollSteps,
-      base::Bind(&ShelfDragCallback::ProcessScroll,
-                 base::Unretained(&handler)));
+  {
+    SCOPED_TRACE("SWIPE_DOWN_ALWAYS_SHOWN");
+    generator->GestureScrollSequenceWithCallback(
+        edge_to_hide, edge_to_show, kTimeDelta, kNumScrollSteps,
+        base::BindRepeating(&ShelfDragCallback::ProcessScroll,
+                            base::Unretained(&handler)));
+  }
   EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_NEVER, shelf->auto_hide_behavior());
   EXPECT_EQ(window_bounds_with_shelf.ToString(), window->bounds().ToString());
@@ -465,13 +496,15 @@
   EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_NEVER, shelf->auto_hide_behavior());
 
   // Swiping up should show the shelf if shelf is hidden in fullscreen mode.
-  generator->GestureScrollSequence(end, start, kTimeDelta, kNumScrollSteps);
+  generator->GestureScrollSequence(edge_to_hide, edge_to_show, kTimeDelta,
+                                   kNumScrollSteps);
   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(start, end, kTimeDelta, kNumScrollSteps);
+  generator->GestureScrollSequence(edge_to_show, edge_to_hide, 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());
@@ -493,10 +526,13 @@
   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
 
   // Swipe up the auto-hide shelf should show it.
-  generator->GestureScrollSequenceWithCallback(
-      end, start, kTimeDelta, kNumScrollSteps,
-      base::Bind(&ShelfDragCallback::ProcessScroll,
-                 base::Unretained(&handler)));
+  {
+    SCOPED_TRACE("SWIPE_UP_AUTO_HIDE_SHOW");
+    generator->GestureScrollSequenceWithCallback(
+        edge_to_hide, edge_to_show, kTimeDelta, kNumScrollSteps,
+        base::BindRepeating(&ShelfDragCallback::ProcessScroll,
+                            base::Unretained(&handler)));
+  }
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
   // Gesture drag should not change the auto hide behavior of shelf, even though
@@ -509,14 +545,18 @@
             GetShelfWidget()->GetWindowBoundsInScreen().ToString());
 
   // Swipe down very little. It shouldn't change any state.
-  end = start + delta;
+  gfx::Point new_point(edge_to_show);
+  gfx::Vector2d diff = edge_to_hide - edge_to_show;
+  new_point.Offset(diff.x() * 3 / 10, diff.y() * 3 / 10);
+  /*
   if (shelf->IsHorizontalAlignment())
     end.set_y(start.y() + shelf_shown.height() * 3 / 10);
   else if (SHELF_ALIGNMENT_LEFT == shelf->alignment())
     end.set_x(start.x() - shelf_shown.width() * 3 / 10);
   else if (SHELF_ALIGNMENT_RIGHT == shelf->alignment())
     end.set_x(start.x() + shelf_shown.width() * 3 / 10);
-  generator->GestureScrollSequence(start, end, kTimeDelta, 5);
+  */
+  generator->GestureScrollSequence(edge_to_show, new_point, kTimeDelta, 5);
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
   EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
@@ -524,12 +564,13 @@
   EXPECT_EQ(shelf_shown.ToString(),
             GetShelfWidget()->GetWindowBoundsInScreen().ToString());
 
-  // Swipe down to hide the shelf.
-  end = start + delta;
-  generator->GestureScrollSequenceWithCallback(
-      start, end, kTimeDelta, kNumScrollSteps,
-      base::Bind(&ShelfDragCallback::ProcessScroll,
-                 base::Unretained(&handler)));
+  {
+    SCOPED_TRACE("SWIPE_DOWN_AUTO_HIDE_1");
+    generator->GestureScrollSequenceWithCallback(
+        edge_to_show, edge_to_hide, kTimeDelta, kNumScrollSteps,
+        base::BindRepeating(&ShelfDragCallback::ProcessScroll,
+                            base::Unretained(&handler)));
+  }
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
   EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
@@ -538,7 +579,7 @@
             GetShelfWidget()->GetWindowBoundsInScreen().ToString());
 
   // Swipe up in extended hit region to show it.
-  gfx::Point extended_start = start;
+  gfx::Point extended_start = edge_to_show;
   if (shelf->IsHorizontalAlignment())
     extended_start.set_y(GetShelfWidget()->GetWindowBoundsInScreen().y() - 1);
   else if (SHELF_ALIGNMENT_LEFT == shelf->alignment())
@@ -546,11 +587,14 @@
                          1);
   else if (SHELF_ALIGNMENT_RIGHT == shelf->alignment())
     extended_start.set_x(GetShelfWidget()->GetWindowBoundsInScreen().x() - 1);
-  end = extended_start - delta;
-  generator->GestureScrollSequenceWithCallback(
-      extended_start, end, kTimeDelta, kNumScrollSteps,
-      base::Bind(&ShelfDragCallback::ProcessScroll,
-                 base::Unretained(&handler)));
+  {
+    SCOPED_TRACE("SWIPE_UP_EXTENDED_HIT");
+    generator->GestureScrollSequenceWithCallback(
+        extended_start, edge_to_show, kTimeDelta, kNumScrollSteps,
+        base::BindRepeating(&ShelfDragCallback::ProcessScroll,
+                            base::Unretained(&handler)));
+  }
+
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
   EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
@@ -559,11 +603,13 @@
             GetShelfWidget()->GetWindowBoundsInScreen().ToString());
 
   // Swipe down again to hide.
-  end = start + delta;
-  generator->GestureScrollSequenceWithCallback(
-      start, end, kTimeDelta, kNumScrollSteps,
-      base::Bind(&ShelfDragCallback::ProcessScroll,
-                 base::Unretained(&handler)));
+  {
+    SCOPED_TRACE("SWIPE_DOWN_AUTO_HIDE_2");
+    generator->GestureScrollSequenceWithCallback(
+        edge_to_show, edge_to_hide, kTimeDelta, kNumScrollSteps,
+        base::BindRepeating(&ShelfDragCallback::ProcessScroll,
+                            base::Unretained(&handler)));
+  }
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
   EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
@@ -573,31 +619,27 @@
 
   // Swipe up outside the hit area. This should not change anything.
   gfx::Point outside_start =
-      gfx::Point((GetShelfWidget()->GetWindowBoundsInScreen().x() +
-                  GetShelfWidget()->GetWindowBoundsInScreen().right()) /
-                     2,
-                 GetShelfWidget()->GetWindowBoundsInScreen().y() - 50);
-  end = outside_start + delta;
-  generator->GestureScrollSequence(outside_start, end, kTimeDelta,
-                                   kNumScrollSteps);
+      GetShelfWidget()->GetWindowBoundsInScreen().top_center();
+  outside_start.set_y(outside_start.y() - 50);
+  gfx::Vector2d delta = edge_to_hide - edge_to_show;
+  generator->GestureScrollSequence(outside_start, outside_start + delta,
+                                   kTimeDelta, kNumScrollSteps);
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
   EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
   EXPECT_EQ(window_bounds_with_noshelf.ToString(), window->bounds().ToString());
   EXPECT_EQ(shelf_hidden.ToString(),
             GetShelfWidget()->GetWindowBoundsInScreen().ToString());
-
   // Swipe up from below the shelf where a bezel would be, this should show the
   // shelf.
-  gfx::Point below_start = start;
+  gfx::Point below_start = edge_to_hide;
   if (shelf->IsHorizontalAlignment())
-    below_start.set_y(GetShelfWidget()->GetWindowBoundsInScreen().bottom() + 1);
+    below_start.set_y(GetShelfWidget()->GetWindowBoundsInScreen().bottom() - 1);
   else if (SHELF_ALIGNMENT_LEFT == shelf->alignment())
-    below_start.set_x(GetShelfWidget()->GetWindowBoundsInScreen().x() - 1);
+    below_start.set_x(GetShelfWidget()->GetWindowBoundsInScreen().x());
   else if (SHELF_ALIGNMENT_RIGHT == shelf->alignment())
-    below_start.set_x(GetShelfWidget()->GetWindowBoundsInScreen().right() + 1);
-  end = below_start - delta;
-  generator->GestureScrollSequence(below_start, end, kTimeDelta,
+    below_start.set_x(GetShelfWidget()->GetWindowBoundsInScreen().right() - 1);
+  generator->GestureScrollSequence(below_start, edge_to_show, kTimeDelta,
                                    kNumScrollSteps);
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
@@ -607,11 +649,13 @@
             GetShelfWidget()->GetWindowBoundsInScreen().ToString());
 
   // Swipe down again to hide.
-  end = start + delta;
-  generator->GestureScrollSequenceWithCallback(
-      start, end, kTimeDelta, kNumScrollSteps,
-      base::Bind(&ShelfDragCallback::ProcessScroll,
-                 base::Unretained(&handler)));
+  {
+    SCOPED_TRACE("SWIPE_DOWN_AUTO_HIDE_3");
+    generator->GestureScrollSequenceWithCallback(
+        edge_to_show, edge_to_hide, kTimeDelta, kNumScrollSteps,
+        base::BindRepeating(&ShelfDragCallback::ProcessScroll,
+                            base::Unretained(&handler)));
+  }
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
   EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
@@ -632,11 +676,15 @@
             window_bounds_fullscreen.ToString());
 
   // Swipe up. This should show the shelf.
-  end = below_start - delta;
-  generator->GestureScrollSequenceWithCallback(
-      below_start, end, kTimeDelta, kNumScrollSteps,
-      base::Bind(&ShelfDragCallback::ProcessScroll,
-                 base::Unretained(&handler)));
+  {
+    SCOPED_TRACE("SWIPE_UP_AUTO_HIDE_1");
+    // Do not check bounds because the events outside of the bounds
+    // will be clipped.
+    generator->GestureScrollSequenceWithCallback(
+        below_start, edge_to_show, kTimeDelta, kNumScrollSteps,
+        base::BindRepeating(&ShelfDragCallback::ProcessScrollNoBoundsCheck,
+                            base::Unretained(&handler)));
+  }
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
   EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
@@ -645,11 +693,13 @@
   EXPECT_EQ(window_bounds_fullscreen.ToString(), window->bounds().ToString());
 
   // Swipe down to hide the shelf.
-  end = start + delta;
-  generator->GestureScrollSequenceWithCallback(
-      start, end, kTimeDelta, kNumScrollSteps,
-      base::Bind(&ShelfDragCallback::ProcessScroll,
-                 base::Unretained(&handler)));
+  {
+    SCOPED_TRACE("SWIPE_DOWN_AUTO_HIDE_4");
+    generator->GestureScrollSequenceWithCallback(
+        edge_to_show, edge_to_hide, kTimeDelta, kNumScrollSteps,
+        base::BindRepeating(&ShelfDragCallback::ProcessScroll,
+                            base::Unretained(&handler)));
+  }
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
   EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
@@ -666,14 +716,16 @@
   EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
 
   // Swipe-up. This should not change anything.
-  end = start - delta;
-  generator->GestureScrollSequenceWithCallback(
-      below_start, end, kTimeDelta, kNumScrollSteps,
-      base::Bind(&ShelfDragCallback::ProcessScroll,
-                 base::Unretained(&handler)));
-  EXPECT_EQ(SHELF_HIDDEN, shelf->GetVisibilityState());
-  EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
-  EXPECT_EQ(window_bounds_fullscreen.ToString(), window->bounds().ToString());
+  {
+    SCOPED_TRACE("SWIPE_UP_NO_CHANGE");
+    generator->GestureScrollSequenceWithCallback(
+        below_start, edge_to_show, kTimeDelta, kNumScrollSteps,
+        base::BindRepeating(&ShelfDragCallback::ProcessScroll,
+                            base::Unretained(&handler)));
+    EXPECT_EQ(SHELF_HIDDEN, shelf->GetVisibilityState());
+    EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
+    EXPECT_EQ(window_bounds_fullscreen.ToString(), window->bounds().ToString());
+  }
 
   // Minimize actually, otherwise further event may be affected since widget
   // is fullscreen status.
@@ -688,11 +740,13 @@
 
   // Swipe-down to hide. This should have no effect because there are no visible
   // windows.
-  end = start + delta;
-  generator->GestureScrollSequenceWithCallback(
-      start, end, kTimeDelta, kNumScrollSteps,
-      base::Bind(&ShelfDragCallback::ProcessScroll,
-                 base::Unretained(&handler)));
+  {
+    SCOPED_TRACE("SWIPE_DOWN_AUTO_HIDE_5");
+    generator->GestureScrollSequenceWithCallback(
+        edge_to_show, edge_to_hide, kTimeDelta, kNumScrollSteps,
+        base::BindRepeating(&ShelfDragCallback::ProcessScroll,
+                            base::Unretained(&handler)));
+  }
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
   EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
@@ -706,11 +760,15 @@
   // Swipe up on the shelf. This should show the shelf but should not change the
   // auto-hide behavior, since auto-hide behavior can only be changed through
   // context menu of the shelf.
-  end = below_start - delta;
-  generator->GestureScrollSequenceWithCallback(
-      below_start, end, kTimeDelta, kNumScrollSteps,
-      base::Bind(&ShelfDragCallback::ProcessScroll,
-                 base::Unretained(&handler)));
+  {
+    SCOPED_TRACE("SWIPE_UP_AUTO_HIDE_2");
+    // Do not check bounds because the events outside of the bounds
+    // will be clipped.
+    generator->GestureScrollSequenceWithCallback(
+        below_start, edge_to_show, kTimeDelta, kNumScrollSteps,
+        base::BindRepeating(&ShelfDragCallback::ProcessScrollNoBoundsCheck,
+                            base::Unretained(&handler)));
+  }
   EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
   EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
   EXPECT_EQ(shelf_shown.ToString(),
@@ -722,11 +780,13 @@
 
   // Swipe-down to hide. This should have no effect because there are no visible
   // windows.
-  end = start + delta;
-  generator->GestureScrollSequenceWithCallback(
-      start, end, kTimeDelta, kNumScrollSteps,
-      base::Bind(&ShelfDragCallback::ProcessScroll,
-                 base::Unretained(&handler)));
+  {
+    SCOPED_TRACE("SWIPE_DOWN_AUTO_HIDE_6");
+    generator->GestureScrollSequenceWithCallback(
+        edge_to_show, edge_to_hide, kTimeDelta, kNumScrollSteps,
+        base::BindRepeating(&ShelfDragCallback::ProcessScroll,
+                            base::Unretained(&handler)));
+  }
   EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
   EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
   EXPECT_EQ(shelf_shown.ToString(),
@@ -734,11 +794,13 @@
 
   // Swipe up again on AUTO_HIDE_SHOWN shelf shouldn't change any state.
   // Swipe up on auto-hide shown shelf should still keep shelf shown.
-  end = start - delta;
-  generator->GestureScrollSequenceWithCallback(
-      start, end, kTimeDelta, kNumScrollSteps,
-      base::Bind(&ShelfDragCallback::ProcessScroll,
-                 base::Unretained(&handler)));
+  {
+    SCOPED_TRACE("SWIPE_UP_AUTO_HIDE_4");
+    generator->GestureScrollSequenceWithCallback(
+        edge_to_hide, edge_to_show, kTimeDelta, kNumScrollSteps,
+        base::BindRepeating(&ShelfDragCallback::ProcessScroll,
+                            base::Unretained(&handler)));
+  }
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
   EXPECT_EQ(shelf_shown.ToString(),
@@ -1541,21 +1603,28 @@
   {
     SCOPED_TRACE("BOTTOM");
     shelf->SetAlignment(SHELF_ALIGNMENT_BOTTOM);
-    RunGestureDragTests(gfx::Vector2d(0, 120));
+    gfx::Rect shelf_bounds = GetVisibleShelfWidgetBoundsInScreen();
+    gfx::Point bottom_center = shelf_bounds.bottom_center();
+    bottom_center.Offset(0, -1);  // Make sure the point is inside shelf.
+    RunGestureDragTests(bottom_center, shelf_bounds.top_center());
     GetAppListTestHelper()->WaitUntilIdle();
   }
-
   {
     SCOPED_TRACE("LEFT");
     shelf->SetAlignment(SHELF_ALIGNMENT_LEFT);
-    RunGestureDragTests(gfx::Vector2d(-120, 0));
+    gfx::Rect shelf_bounds = GetVisibleShelfWidgetBoundsInScreen();
+    gfx::Point right_center = shelf_bounds.right_center();
+    right_center.Offset(-1, 0);  // Make sure the point is inside shelf.
+    RunGestureDragTests(shelf_bounds.left_center(), right_center);
     GetAppListTestHelper()->WaitUntilIdle();
   }
-
   {
     SCOPED_TRACE("RIGHT");
     shelf->SetAlignment(SHELF_ALIGNMENT_RIGHT);
-    RunGestureDragTests(gfx::Vector2d(120, 0));
+    gfx::Rect shelf_bounds = GetVisibleShelfWidgetBoundsInScreen();
+    gfx::Point right_center = shelf_bounds.right_center();
+    right_center.Offset(-1, 0);  // Make sure the point is inside shelf.
+    RunGestureDragTests(right_center, shelf_bounds.left_center());
     GetAppListTestHelper()->WaitUntilIdle();
   }
 }
@@ -1570,9 +1639,7 @@
 
   // Starts the drag from the center of the shelf's bottom.
   gfx::Rect shelf_widget_bounds = GetShelfWidget()->GetWindowBoundsInScreen();
-  gfx::Point start =
-      gfx::Point(shelf_widget_bounds.x() + shelf_widget_bounds.width() / 2,
-                 shelf_widget_bounds.bottom());
+  gfx::Point start = shelf_widget_bounds.bottom_center();
 
   // Fling up that exceeds the velocity threshold should show the fullscreen app
   // list.
@@ -1701,16 +1768,16 @@
   constexpr int kNumScrollSteps = 4;
 
   // Starts the drag from the center of the shelf's bottom.
-  gfx::Rect shelf_widget_bounds = GetShelfWidget()->GetWindowBoundsInScreen();
-  gfx::Point start =
+  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());
-  gfx::Vector2d delta;
+                 shelf_widget_bounds.bottom() - 1);
+  gfx::Point point_to_fullscreen(shelf_bottom_center);
+  point_to_fullscreen.Offset(
+      0, -(ShelfLayoutManager::kAppListDragSnapToFullscreenThreshold + 10));
 
-  // Swiping up more than the threshold should show the app list.
-  delta.set_y(ShelfLayoutManager::kAppListDragSnapToFullscreenThreshold + 10);
-  gfx::Point end = start - delta;
-  generator->GestureScrollSequence(start, end, kTimeDelta, kNumScrollSteps);
+  generator->GestureScrollSequence(shelf_bottom_center, point_to_fullscreen,
+                                   kTimeDelta, kNumScrollSteps);
   GetAppListTestHelper()->WaitUntilIdle();
   GetAppListTestHelper()->CheckVisibility(true);
   GetAppListTestHelper()->CheckState(
@@ -1723,12 +1790,15 @@
   GetAppListTestHelper()->CheckState(app_list::AppListViewState::CLOSED);
 
   // Swiping up less or equal to the threshold should dismiss the app list.
-  delta.set_y(ShelfLayoutManager::kAppListDragSnapToFullscreenThreshold - 10);
-  end = start - delta;
+  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(start, end, kTimeDelta, kNumScrollSteps);
+  generator->GestureScrollSequence(shelf_bottom_center, point_to_no_fullscreen,
+                                   kTimeDelta, kNumScrollSteps);
   GetAppListTestHelper()->WaitUntilIdle();
   GetAppListTestHelper()->CheckVisibility(false);
   GetAppListTestHelper()->CheckState(app_list::AppListViewState::CLOSED);
@@ -1737,8 +1807,11 @@
 
   // Swiping down on the shelf should do nothing as always shown shelf can not
   // be dragged down to hide.
-  end = start + delta;
-  generator->GestureScrollSequence(start, end, kTimeDelta, kNumScrollSteps);
+  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
@@ -1754,7 +1827,8 @@
   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(end, start, kTimeDelta, kNumScrollSteps);
+  generator->GestureScrollSequence(shelf_bottom_center, point_to_fullscreen,
+                                   kTimeDelta, kNumScrollSteps);
   GetAppListTestHelper()->WaitUntilIdle();
   GetAppListTestHelper()->CheckVisibility(false);
   GetAppListTestHelper()->CheckState(app_list::AppListViewState::CLOSED);
@@ -1763,7 +1837,8 @@
   EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_NEVER, shelf->auto_hide_behavior());
 
   // Swiping down should hide the shelf.
-  generator->GestureScrollSequence(start, end, kTimeDelta, kNumScrollSteps);
+  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());
@@ -1779,9 +1854,8 @@
   EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_NEVER, shelf->auto_hide_behavior());
 
   // Swiping up on the shelf in this state should open the app list.
-  delta.set_y(ShelfLayoutManager::kAppListDragSnapToFullscreenThreshold + 10);
-  end = start - delta;
-  generator->GestureScrollSequence(start, end, kTimeDelta, kNumScrollSteps);
+  generator->GestureScrollSequence(shelf_bottom_center, point_to_fullscreen,
+                                   kTimeDelta, kNumScrollSteps);
   GetAppListTestHelper()->WaitUntilIdle();
   GetAppListTestHelper()->CheckVisibility(true);
   GetAppListTestHelper()->CheckState(
@@ -2409,6 +2483,70 @@
   EXPECT_EQ(gfx::Point(500, 400), status_area_bounds.bottom_right());
 }
 
+// Tests that the shelf forwards the appropriate events to the home launcher
+// gesture handler to handle.
+TEST_F(ShelfLayoutManagerTest, HomeLauncherGestureHandler) {
+  // Home launcher is only available in tablet mode.
+  Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(true);
+
+  // Home launcher gesture handler needs at least one window.
+  std::unique_ptr<aura::Window> window =
+      AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400));
+  wm::ActivateWindow(window.get());
+
+  const gfx::Point shelf_center =
+      GetVisibleShelfWidgetBoundsInScreen().CenterPoint();
+
+  // Helper to create a scroll event for this test.
+  auto create_scroll_event = [&shelf_center](ui::EventType type,
+                                             float scroll_y) {
+    ui::GestureEventDetails details =
+        type == ui::ET_GESTURE_SCROLL_END
+            ? ui::GestureEventDetails(type)
+            : ui::GestureEventDetails(type, 0, scroll_y);
+    return ui::GestureEvent(shelf_center.x(), shelf_center.y(), 0,
+                            base::TimeTicks(), details);
+  };
+
+  // The home launcher gesture handler should not be handling any window
+  // initially.
+  ShelfLayoutManager* manager = GetShelfLayoutManager();
+  HomeLauncherGestureHandler* gesture_handler =
+      Shell::Get()->app_list_controller()->home_launcher_gesture_handler();
+  ASSERT_TRUE(gesture_handler);
+  ASSERT_FALSE(gesture_handler->window());
+
+  // Tests that after scrolling up on the shelf, the home launcher gesture
+  // handler will be acting on |window|.
+  manager->ProcessGestureEvent(
+      create_scroll_event(ui::ET_GESTURE_SCROLL_BEGIN, -1.f));
+  EXPECT_EQ(window.get(), gesture_handler->window());
+
+  // Tests that since the initial scroll event was scrolled up, the home
+  // launcher gesture handler will continue to act on |window| regardless of
+  // direction of scroll updates.
+  manager->ProcessGestureEvent(
+      create_scroll_event(ui::ET_GESTURE_SCROLL_UPDATE, -1.f));
+  EXPECT_EQ(window.get(), gesture_handler->window());
+  manager->ProcessGestureEvent(
+      create_scroll_event(ui::ET_GESTURE_SCROLL_UPDATE, 1.f));
+  EXPECT_EQ(window.get(), gesture_handler->window());
+
+  // End the scroll.
+  manager->ProcessGestureEvent(
+      create_scroll_event(ui::ET_GESTURE_SCROLL_END, 1.f));
+  ASSERT_FALSE(gesture_handler->window());
+
+  // Tests that if the initial scroll event is directed downwards, the home
+  // launcher gesture handler will not act on |window|.
+  manager->ProcessGestureEvent(
+      create_scroll_event(ui::ET_GESTURE_SCROLL_BEGIN, 1.f));
+  EXPECT_FALSE(gesture_handler->window());
+  manager->ProcessGestureEvent(
+      create_scroll_event(ui::ET_GESTURE_SCROLL_UPDATE, -1.f));
+  EXPECT_FALSE(gesture_handler->window());
+}
+
 class ShelfLayoutManagerKeyboardTest : public AshTestBase {
  public:
   ShelfLayoutManagerKeyboardTest() = default;
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index dae9315b..79f0af5 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -374,6 +374,7 @@
   // Add the background view behind the app list and back buttons first, so
   // that other views will appear above it.
   back_and_app_list_background_ = new views::View();
+  back_and_app_list_background_->set_can_process_events_within_subtree(false);
   back_and_app_list_background_->SetBackground(
       CreateBackgroundFromPainter(views::Painter::CreateSolidRoundRectPainter(
           kShelfControlPermanentHighlightBackground,
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index 4220099..38c8fcd 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -44,7 +44,7 @@
 
 constexpr int kShelfRoundedCornerRadius = 28;
 constexpr int kShelfBlurRadius = 10;
-constexpr float kShelfBlurQuality = 0.25f;
+constexpr float kShelfBlurQuality = 0.33f;
 
 // Return the first or last focusable child of |root|.
 views::View* FindFirstOrLastFocusableChild(views::View* root,
diff --git a/ash/shelf/shelf_widget_unittest.cc b/ash/shelf/shelf_widget_unittest.cc
index e58bd22..200a537 100644
--- a/ash/shelf/shelf_widget_unittest.cc
+++ b/ash/shelf/shelf_widget_unittest.cc
@@ -26,8 +26,8 @@
 #include "ui/events/event_utils.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/keyboard/keyboard_controller.h"
-#include "ui/keyboard/keyboard_switches.h"
 #include "ui/keyboard/keyboard_util.h"
+#include "ui/keyboard/public/keyboard_switches.h"
 #include "ui/keyboard/test/keyboard_test_util.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
@@ -551,8 +551,9 @@
     // These tests only apply to the floating virtual keyboard, as it is the
     // only case where both the virtual keyboard and the shelf are visible.
     const gfx::Rect keyboard_bounds(0, 0, 1, 1);
-    keyboard_controller()->SetContainerType(keyboard::ContainerType::FLOATING,
-                                            keyboard_bounds, base::DoNothing());
+    keyboard_controller()->SetContainerType(
+        keyboard::mojom::ContainerType::kFloating, keyboard_bounds,
+        base::DoNothing());
   }
 
   keyboard::KeyboardController* keyboard_controller() {
diff --git a/ash/shell.cc b/ash/shell.cc
index 54d24ed..74bed48 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -160,7 +160,7 @@
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
-#include "base/sys_info.h"
+#include "base/system/sys_info.h"
 #include "base/trace_event/trace_event.h"
 #include "chromeos/chromeos_features.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
@@ -675,7 +675,6 @@
       system_tray_notifier_(std::make_unique<SystemTrayNotifier>()),
       vpn_list_(std::make_unique<VpnList>()),
       window_cycle_controller_(std::make_unique<WindowCycleController>()),
-      window_selector_controller_(std::make_unique<WindowSelectorController>()),
       display_configurator_(std::make_unique<display::DisplayConfigurator>()),
       display_output_protection_(std::make_unique<DisplayOutputProtection>(
           display_configurator_.get())),
@@ -1063,7 +1062,9 @@
       std::make_unique<::wm::FocusController>(new wm::AshFocusRules());
   focus_controller_->AddObserver(this);
 
-  screen_position_controller_.reset(new ScreenPositionController);
+  window_selector_controller_ = std::make_unique<WindowSelectorController>();
+
+  screen_position_controller_ = std::make_unique<ScreenPositionController>();
 
   // Must be before CreatePrimaryHost().
   window_service_owner_ =
@@ -1174,12 +1175,7 @@
         std::make_unique<DockedMagnifierController>();
   }
 
-  viz::mojom::VideoDetectorObserverPtr observer;
-  video_detector_ =
-      std::make_unique<VideoDetector>(mojo::MakeRequest(&observer));
-  aura_env_->context_factory_private()
-      ->GetHostFrameSinkManager()
-      ->AddVideoDetectorObserver(std::move(observer));
+  video_detector_ = std::make_unique<VideoDetector>();
 
   tooltip_controller_.reset(new views::corewm::TooltipController(
       std::unique_ptr<views::corewm::Tooltip>(new views::corewm::TooltipAura)));
@@ -1254,9 +1250,11 @@
   // |connector_| is null in unit tests.
   if (connector_ &&
       base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kShowTaps)) {
-    // The show taps feature is a separate mojo app.
+    // The show taps feature is a separate service.
     // TODO(jamescook): Make this work in ash_shell_with_content.
-    connector_->StartService(tap_visualizer::mojom::kServiceName);
+    // TODO(https://crbug.com/904148): This should not use |WarmService()|.
+    connector_->WarmService(service_manager::ServiceFilter::ByName(
+        tap_visualizer::mojom::kServiceName));
   }
 
   if (!::features::IsMultiProcessMash()) {
diff --git a/ash/shell/ash_content_browser_manifest_overlay.json b/ash/shell/ash_content_browser_manifest_overlay.json
index 7598a0c..52d105d 100644
--- a/ash/shell/ash_content_browser_manifest_overlay.json
+++ b/ash/shell/ash_content_browser_manifest_overlay.json
@@ -4,7 +4,8 @@
   "interface_provider_specs": {
     "service_manager:connector": {
       "requires": {
-        "shortcut_viewer_app": [ "shortcut_viewer" ]
+        "shortcut_viewer_app": [ "shortcut_viewer" ],
+        "device": [ "device:fingerprint" ]
       }
     }
   }
diff --git a/ash/shell/content/client/shell_browser_main_parts.cc b/ash/shell/content/client/shell_browser_main_parts.cc
index df62cdf..192a89f 100644
--- a/ash/shell/content/client/shell_browser_main_parts.cc
+++ b/ash/shell/content/client/shell_browser_main_parts.cc
@@ -85,14 +85,16 @@
   chromeos::PowerPolicyController::Initialize(
       chromeos::DBusThreadManager::Get()->GetPowerManagerClient());
 
+  service_manager::Connector* const connector =
+      content::ServiceManagerConnection::GetForProcess()->GetConnector();
+
   ui::MaterialDesignController::Initialize();
   ash::ShellInitParams init_params;
   init_params.delegate = std::make_unique<ash::shell::ShellDelegateImpl>();
   init_params.context_factory = content::GetContextFactory();
   init_params.context_factory_private = content::GetContextFactoryPrivate();
   init_params.gpu_interface_provider = content::CreateGpuInterfaceProvider();
-  init_params.connector =
-      content::ServiceManagerConnection::GetForProcess()->GetConnector();
+  init_params.connector = connector;
   ash::Shell::CreateInstance(std::move(init_params));
 
   // Initialize session controller client and create fake user sessions. The
@@ -111,19 +113,17 @@
 
   ash::Shell::GetPrimaryRootWindow()->GetHost()->Show();
 
-  content::ServiceManagerConnection::GetForProcess()
-      ->GetConnector()
-      ->StartService(test_ime_driver::mojom::kServiceName);
-  content::ServiceManagerConnection::GetForProcess()
-      ->GetConnector()
-      ->StartService(quick_launch::mojom::kServiceName);
-  content::ServiceManagerConnection::GetForProcess()
-      ->GetConnector()
-      ->StartService(tap_visualizer::mojom::kServiceName);
+  // TODO(https://crbug.com/904148): These should not use |WarmService()|.
+  connector->WarmService(service_manager::ServiceFilter::ByName(
+      test_ime_driver::mojom::kServiceName));
+  connector->WarmService(service_manager::ServiceFilter::ByName(
+      quick_launch::mojom::kServiceName));
+  connector->WarmService(service_manager::ServiceFilter::ByName(
+      tap_visualizer::mojom::kServiceName));
   shortcut_viewer::mojom::ShortcutViewerPtr shortcut_viewer;
-  content::ServiceManagerConnection::GetForProcess()
-      ->GetConnector()
-      ->BindInterface(shortcut_viewer::mojom::kServiceName, &shortcut_viewer);
+  connector->BindInterface(service_manager::ServiceFilter::ByName(
+                               shortcut_viewer::mojom::kServiceName),
+                           mojo::MakeRequest(&shortcut_viewer));
   shortcut_viewer->Toggle(base::TimeTicks::Now());
   ash::Shell::Get()->InitWaylandServer(nullptr);
 }
diff --git a/ash/shell_unittest.cc b/ash/shell_unittest.cc
index 0847f1a..19b9828 100644
--- a/ash/shell_unittest.cc
+++ b/ash/shell_unittest.cc
@@ -45,8 +45,8 @@
 #include "ui/events/test/test_event_handler.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/keyboard/keyboard_controller.h"
-#include "ui/keyboard/keyboard_switches.h"
 #include "ui/keyboard/keyboard_util.h"
+#include "ui/keyboard/public/keyboard_switches.h"
 #include "ui/views/controls/menu/menu_controller.h"
 #include "ui/views/controls/menu/menu_runner.h"
 #include "ui/views/widget/widget.h"
diff --git a/ash/shutdown_controller.cc b/ash/shutdown_controller.cc
index cf5f466..205102f 100644
--- a/ash/shutdown_controller.cc
+++ b/ash/shutdown_controller.cc
@@ -12,7 +12,7 @@
 #include "ash/wm/lock_state_controller.h"
 #include "base/metrics/user_metrics.h"
 #include "base/strings/stringprintf.h"
-#include "base/sys_info.h"
+#include "base/system/sys_info.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power_manager_client.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
diff --git a/ash/sticky_keys/sticky_keys_unittest.cc b/ash/sticky_keys/sticky_keys_unittest.cc
index 24309ad..bff297d 100644
--- a/ash/sticky_keys/sticky_keys_unittest.cc
+++ b/ash/sticky_keys/sticky_keys_unittest.cc
@@ -28,8 +28,6 @@
     root_window_ = target_->GetRootWindow();
   }
 
-  void TearDown() override { AshTestBase::TearDown(); }
-
   virtual void OnShortcutPressed() {
     if (target_) {
       delete target_;
diff --git a/ash/strings/ash_strings_am.xtb b/ash/strings/ash_strings_am.xtb
index 78aebb0a..051055a 100644
--- a/ash/strings/ash_strings_am.xtb
+++ b/ash/strings/ash_strings_am.xtb
@@ -81,6 +81,7 @@
 <translation id="2268130516524549846">ብሉቱዝ ተሰናክሏል</translation>
 <translation id="2268813581635650749">ሁሉንም ዘግተህ ውጣ</translation>
 <translation id="2292698582925480719">ልኬትን አሳይ</translation>
+<translation id="2302092602801625023">ይህ መለያ በFamily Link የሚተዳደር ነው</translation>
 <translation id="2303600792989757991">የመስኮት አጠቃላይ እይታን ቀያይር</translation>
 <translation id="2339073806695260576">ማስታወሻ ለመያዝ፣ ቅጽበታዊ ገጽ እይታን ለማንሳት፣ ሌዘር ጠቋሚውን ወይም ማጉያ መነጽሩን ለመጠቀም በመደርደሪያው ላይ ያለውን የስታይለስ አዝራሩን መታ ያድርጉ።</translation>
 <translation id="2341729377289034582">ወደ አቀባዊ ተቆልፏል</translation>
@@ -140,7 +141,9 @@
 <translation id="315116470104423982">የተንቀሳቃሽ ስልክ ውሂብ</translation>
 <translation id="3151786313568798007">አቀማመጥ</translation>
 <translation id="3153444934357957346">በአንድ ጊዜ ብዙ መግባት ላይ እስከ <ph name="MULTI_PROFILE_USER_LIMIT" /> መለያዎች ድረስ ብቻ ነው ሊኖርዎት የሚችለው።</translation>
+<translation id="3154351730702813399">የመሣሪያው አስተዳዳሪ የአሰሳ እንቅስቃሴዎን ሊከታተል ይችላል።</translation>
 <translation id="3202010236269062730">{NUM_DEVICES,plural, =1{ከአንድ መሣሪያ ጋር ተገናኝቷል}one{ከ# መሣሪያዎች ጋር ተገናኝቷል}other{ከ# መሣሪያዎች ጋር ተገናኝቷል}}</translation>
+<translation id="32244657276328332">ወደ አውታረ መረብ አልተገናኘም</translation>
 <translation id="3236488194889173876">ምንም የተንቀሳቃሽ ስልክ አውታረ መረብ የለም</translation>
 <translation id="3255483164551725916">ምን ማድረግ ይችላሉ?</translation>
 <translation id="3294437725009624529">እንግዳ</translation>
@@ -171,6 +174,7 @@
 <translation id="3783640748446814672">alt</translation>
 <translation id="3784455785234192852">ቆልፍ</translation>
 <translation id="3798670284305777884">ድምጽ ማጉያ (ውስጣዊ)</translation>
+<translation id="3799080171973636491">የሙሉ ማያ ገጽ ማጉያውን አቋራጭ ተጭነዋል። ሊያበሩት ይፈልጋሉ?</translation>
 <translation id="380165613292957338">ጤና ይስጥልኝ፣ እንዴት ልርዳዎት?</translation>
 <translation id="383629559565718788">የቁልፍ ሰሌዳ ቅንብሮችን አሳይ</translation>
 <translation id="3846575436967432996">ምንም የአውታረ መረብ መረጃ አይገኝም</translation>
@@ -188,6 +192,7 @@
 <translation id="4065525899979931964">{NUM_APPS,plural, =1{ቅናሽ ለአንድ መተግበሪያ}one{ቅናሽ ለ# መተግበሪያዎች}other{ቅናሽ ለ# መተግበሪያዎች}}</translation>
 <translation id="4072264167173457037">መካከለኛ ሲግናል</translation>
 <translation id="4146833061457621061">Play ሙዚቃ</translation>
+<translation id="4181841719683918333">ቋንቋዎች</translation>
 <translation id="4217571870635786043">በቃል ማስጻፍ</translation>
 <translation id="4261870227682513959">የማሳወቂያ ቅንብሮችን አሳይ። ማሳወቂያዎች ጠፍተዋል</translation>
 <translation id="4269883910223712419">የዚህ መሣሪያ የሚከተሉትን የማድረግ ችሎታ አለው፦</translation>
@@ -195,12 +200,14 @@
 <translation id="4279490309300973883">በማንጸባረቅ ላይ</translation>
 <translation id="4292681942966152062"><ph name="NETWORK_NAME" />ን በማግበር ላይ</translation>
 <translation id="4321179778687042513">ctrl</translation>
+<translation id="4321776623976362024">የከፍተኛ ንጽጽር የቁልፍ ሰሌዳ አቋራጩን ተጭነዋል። ሊያበሩት ይፈልጋሉ?</translation>
 <translation id="4331809312908958774">Chrome ስርዓተ ክወና</translation>
 <translation id="4338109981321384717">ማጉያ መነጽር</translation>
 <translation id="4351433414020964307">ረዳት በመጫን ላይ ነው...</translation>
 <translation id="435527878592612277">የእርስዎን ፎቶ ይምረጡ</translation>
 <translation id="4378551569595875038">በመገናኘት ላይ...</translation>
 <translation id="4379531060876907730">እነዚህ የእርስዎ የስታይለስ መሣሪያዎች ናቸው</translation>
+<translation id="4389184120735010762">የተተከለ የማጉያ የቁልፍ ሰሌዳ አቋራጩን ተጭነዋል። ሊያበሩት ይፈልጋሉ?</translation>
 <translation id="4421231901400348175">የማያ ገጽዎን ቁጥጥር በርቀት እርዳታ በኩል ለ<ph name="HELPER_NAME" /> በማጋራት ላይ።</translation>
 <translation id="4430019312045809116">ድምፅ</translation>
 <translation id="4450893287417543264">ዳግም አታሳይ</translation>
@@ -216,7 +223,6 @@
 <translation id="4628757576491864469">መሣሪያዎች</translation>
 <translation id="4659419629803378708">ChromeVox ነቅቷል</translation>
 <translation id="4734965478015604180">አግድማዊ</translation>
-<translation id="476166673298332917">የዚህ መሣሪያ አስተዳዳሪ የይለፍ ቃላት እና የተግባቦት ጨምሮ የሁሉንም እንቅስቃሴ መዳረሻ አለው።</translation>
 <translation id="4774338217796918551">ነገ <ph name="COME_BACK_TIME" /> ላይ ተመልሰው ይምጡ።</translation>
 <translation id="4776917500594043016">የ<ph name="USER_EMAIL_ADDRESS" /> ይለፍ ቃል</translation>
 <translation id="4778095205580009397">የGoogle ረዳቱ በማሳያ ክፍለ-ጊዜ ውስጥ አይገኝም።</translation>
@@ -239,6 +245,7 @@
 <translation id="5207949376430453814">የጽሑፍ ድፋቱን አድምቅ</translation>
 <translation id="5222676887888702881">ዘግተህ ውጣ</translation>
 <translation id="523505283826916779">የተደራሽነት ቅንብሮች</translation>
+<translation id="5302048478445481009">ቋንቋ</translation>
 <translation id="5313326810920013265">የብሉቱዝ ቅንብሮች</translation>
 <translation id="5331975486040154427">USB-C መሣሪያ (የግራ ጎን ኋላ ወደብ)</translation>
 <translation id="5379115545237091094">በጣም ብዙ ሙከራዎች</translation>
@@ -390,6 +397,7 @@
 <translation id="7886169021410746335">የግላዊነት ቅንብሮችን ያስተካክሉ</translation>
 <translation id="7886277072580235377">የእርስዎ የበይነመረብ ክፍለ-ጊዜ ዘግተው ሲወጡ ይጸዳል። <ph name="LEARN_MORE" /></translation>
 <translation id="788781083998633524">ኢሜይል ላክ</translation>
+<translation id="7897375687985782769">የማያ ገጽ ማሽከርከሪያ የቁልፍ ሰሌዳ አቋራጩን ተጭነውታል። ማያ ገጹን ማሽከርከር ይፈልጋሉ?</translation>
 <translation id="7904094684485781019">የዚህ መለያ አስተዳዳሪ ባለብዙ መለያ መግባትን ከልክሏል።</translation>
 <translation id="7933084174919150729">Google ረዳቱ ለዋናው መገለጫ ብቻ የሚገኝ ነው።</translation>
 <translation id="79341161159229895">መለያው በ<ph name="FIRST_PARENT_EMAIL" /> እና <ph name="SECOND_PARENT_EMAIL" /> ነው የሚቀናበረው</translation>
@@ -455,6 +463,7 @@
 <translation id="8921624153894383499">የGoogle ረዳቱ ይህን ቋንቋ አይናገርም።</translation>
 <translation id="8938800817013097409">USB-C መሣሪያ (የቀኝ ወደብ ከኋላ በኩል)</translation>
 <translation id="8940956008527784070">ባትሪ ዝቅተኛ ነው (<ph name="PERCENTAGE" />%)</translation>
+<translation id="8990809378771970590"><ph name="IME_NAME" />ን በመጠቀም ላይ</translation>
 <translation id="899350903320462459">የማሳወቅ እርምጃውን ለማከናወን መሣሪያን እንደ <ph name="LOGIN_ID" /> ይክፈቱ</translation>
 <translation id="8995603266996330174">የተቀናበረው በ<ph name="DOMAIN" /></translation>
 <translation id="9029474291399787231">የAdobe Flash Player ዝማኔ ይገኛል</translation>
diff --git a/ash/strings/ash_strings_ar.xtb b/ash/strings/ash_strings_ar.xtb
index 7c88b97..14572b7 100644
--- a/ash/strings/ash_strings_ar.xtb
+++ b/ash/strings/ash_strings_ar.xtb
@@ -81,6 +81,7 @@
 <translation id="2268130516524549846">تم إيقاف البلوتوث</translation>
 <translation id="2268813581635650749">خروج الجميع</translation>
 <translation id="2292698582925480719">مقياس العرض</translation>
+<translation id="2302092602801625023">‏تتم إدارة هذا الحساب من خلال Family Link</translation>
 <translation id="2303600792989757991">نظرة عامة لنافذة التبديل</translation>
 <translation id="2339073806695260576">انقر على زر قلم الشاشة على الرف لتدوين ملاحظة أو للحصول على لقطة شاشة أو لاستخدام مؤشر الليزر أو العدسة المكبرة.</translation>
 <translation id="2341729377289034582">تم القفل على الوضع العمودي</translation>
@@ -140,7 +141,9 @@
 <translation id="315116470104423982">بيانات الجوال</translation>
 <translation id="3151786313568798007">الاتجاه</translation>
 <translation id="3153444934357957346">يمكنك الحصول فقط على ما يصل إلى <ph name="MULTI_PROFILE_USER_LIMIT" /> حساب في الدخول المتعدد.</translation>
+<translation id="3154351730702813399">قد يراقب مشرف الجهاز نشاط التصفح.</translation>
 <translation id="3202010236269062730">{NUM_DEVICES,plural, =1{تم الاتصال بجهاز واحد}zero{تم الاتصال بـ # جهاز}two{تم الاتصال بجهازين (#)}few{تم الاتصال بـ # أجهزة}many{تم الاتصال بـ # جهازًا}other{تم الاتصال بـ # جهاز}}</translation>
+<translation id="32244657276328332">لا يتوفّر اتصال بالشبكة</translation>
 <translation id="3236488194889173876">ليست هناك شبكة جوّال متاحة</translation>
 <translation id="3255483164551725916">ما الإجراء المطلوب؟</translation>
 <translation id="3294437725009624529">ضيف</translation>
@@ -171,6 +174,7 @@
 <translation id="3783640748446814672">alt</translation>
 <translation id="3784455785234192852">قفل</translation>
 <translation id="3798670284305777884">سماعة (داخلية)</translation>
+<translation id="3799080171973636491">لقد ضغطت على اختصار لوحة المفاتيح للمُكبِّر بملء الشاشة. هل ترغب في تفعيله؟</translation>
 <translation id="380165613292957338">مرحبًا، كيف يمكنني مساعدتك؟</translation>
 <translation id="383629559565718788">عرض إعدادات لوحة المفاتيح</translation>
 <translation id="3846575436967432996">لا توجد معلومات متاحة حول الشبكة</translation>
@@ -188,6 +192,7 @@
 <translation id="4065525899979931964">{NUM_APPS,plural, =1{الإشعارات متوقفة لتطبيق واحد}zero{الإشعارات متوقفة لـ # تطبيق}two{الإشعارات متوقفة لتطبيقَين (#)}few{الإشعارات متوقفة لـ # تطبيقات}many{الإشعارات متوقفة لـ # تطبيقًا}other{الإشعارات متوقفة لـ # تطبيق}}</translation>
 <translation id="4072264167173457037">إشارة متوسطة</translation>
 <translation id="4146833061457621061">تشغيل الموسيقى</translation>
+<translation id="4181841719683918333">اللغات</translation>
 <translation id="4217571870635786043">إملاء</translation>
 <translation id="4261870227682513959">عرض إعدادات الإشعارات. تم إيقاف الإشعارات</translation>
 <translation id="4269883910223712419">يمتلك مشرف هذا الجهاز القدرة على ما يلي:</translation>
@@ -195,12 +200,14 @@
 <translation id="4279490309300973883">النسخ المطابق</translation>
 <translation id="4292681942966152062">جارٍ تفعيل <ph name="NETWORK_NAME" /></translation>
 <translation id="4321179778687042513">ctrl</translation>
+<translation id="4321776623976362024">لقد ضغطت على اختصار لوحة المفاتيح للتباين العالي. هل ترغب في تفعيله؟</translation>
 <translation id="4331809312908958774">Chrome OS</translation>
 <translation id="4338109981321384717">العدسة المكبرة</translation>
 <translation id="4351433414020964307">جارٍ تحميل المساعد...</translation>
 <translation id="435527878592612277">اختيار صورتك</translation>
 <translation id="4378551569595875038">جارٍ الاتصال...</translation>
 <translation id="4379531060876907730">هذه هي أدوات قلم الشاشة</translation>
+<translation id="4389184120735010762">لقد ضغطت على اختصار لوحة المفاتيح للمُكبِّر الذي تم إرساؤه. هل ترغب في تفعيله؟</translation>
 <translation id="4421231901400348175">مشاركة التحكم في شاشتك مع <ph name="HELPER_NAME" /> عن طريق المساعدة عن بُعد.</translation>
 <translation id="4430019312045809116">مستوى الصوت</translation>
 <translation id="4450893287417543264">عدم الإظهار مرة أخرى</translation>
@@ -216,7 +223,6 @@
 <translation id="4628757576491864469">الأجهزة</translation>
 <translation id="4659419629803378708">‏تم تفعيل ChromeVox</translation>
 <translation id="4734965478015604180">أفقي</translation>
-<translation id="476166673298332917">يمكن لمشرف هذا الجهاز الوصول إلى جميع الأنشطة، بما في ذلك كلمات المرور والاتصالات.</translation>
 <translation id="4774338217796918551">يمكنك العودة غدًا في <ph name="COME_BACK_TIME" />.</translation>
 <translation id="4776917500594043016">كلمة مرور <ph name="USER_EMAIL_ADDRESS" /></translation>
 <translation id="4778095205580009397">‏لا يتوفَّر "مساعد Google" في جلسة تجريبية.</translation>
@@ -239,6 +245,7 @@
 <translation id="5207949376430453814">تمييز علامة إقحام النص</translation>
 <translation id="5222676887888702881">الخروج</translation>
 <translation id="523505283826916779">إعدادات إمكانية الدخول</translation>
+<translation id="5302048478445481009">اللغة</translation>
 <translation id="5313326810920013265">إعدادات البلوتوث</translation>
 <translation id="5331975486040154427">‏جهاز USB-C (المنفذ الخلفي الأيسر)</translation>
 <translation id="5379115545237091094">محاولات كثيرة جدًا</translation>
@@ -391,6 +398,7 @@
 <translation id="7886169021410746335">ضبط إعدادات الخصوصية</translation>
 <translation id="7886277072580235377">سيتم محو جلسة الإنترنت عند تسجيل الخروج. <ph name="LEARN_MORE" /></translation>
 <translation id="788781083998633524">إرسال رسالة إلكترونية</translation>
+<translation id="7897375687985782769">لقد ضغطت على اختصار لوحة المفاتيح لتدوير الشاشة. هل ترغب في تدوير الشاشة؟</translation>
 <translation id="7904094684485781019">لقد حظر مشرف هذا الحساب إمكانية الدخول المتعدد.</translation>
 <translation id="7933084174919150729">‏لا يتوفر مساعد Google إلا للملف الشخصي الأساسي.</translation>
 <translation id="79341161159229895">يُدير <ph name="FIRST_PARENT_EMAIL" /> و<ph name="SECOND_PARENT_EMAIL" /> الحساب.</translation>
@@ -456,6 +464,7 @@
 <translation id="8921624153894383499">‏لا يتحدث مساعد Google هذه اللغة.</translation>
 <translation id="8938800817013097409">‏جهاز USB-C (المنفذ الأيمن في الخلف)</translation>
 <translation id="8940956008527784070">طاقة البطارية منخفضة (<ph name="PERCENTAGE" />%)</translation>
+<translation id="8990809378771970590">استخدام <ph name="IME_NAME" /></translation>
 <translation id="899350903320462459">فتح قفل الجهاز بواسطة <ph name="LOGIN_ID" /> لتنفيذ إجراء الإشعارات</translation>
 <translation id="8995603266996330174">مدار بواسطة <ph name="DOMAIN" /></translation>
 <translation id="9029474291399787231">‏هناك تحديث جديد لتطبيق Adobe Flash Player</translation>
diff --git a/ash/strings/ash_strings_bg.xtb b/ash/strings/ash_strings_bg.xtb
index c7ebaef..17ed640 100644
--- a/ash/strings/ash_strings_bg.xtb
+++ b/ash/strings/ash_strings_bg.xtb
@@ -81,6 +81,7 @@
 <translation id="2268130516524549846">Bluetooth е деактивиран</translation>
 <translation id="2268813581635650749">Изход за всички</translation>
 <translation id="2292698582925480719">Мащаб на показване</translation>
+<translation id="2302092602801625023">Този профил се управлява от Family Link</translation>
 <translation id="2303600792989757991">Превключване на общия преглед на прозорците</translation>
 <translation id="2339073806695260576">Докоснете бутона с икона на писалка в лавицата, за да създадете бележка, да направите екранна снимка или да използвате лазерната показалка или лупата.</translation>
 <translation id="2341729377289034582">Заключено във вертикална ориентация</translation>
@@ -140,7 +141,9 @@
 <translation id="315116470104423982">Мобилни данни</translation>
 <translation id="3151786313568798007">Ориентация</translation>
 <translation id="3153444934357957346">Можете да използвате най-много <ph name="MULTI_PROFILE_USER_LIMIT" /> профила с функцията за централизиран вход.</translation>
+<translation id="3154351730702813399">Администраторът на устройството може да наблюдава активността ви при сърфиране.</translation>
 <translation id="3202010236269062730">{NUM_DEVICES,plural, =1{Установена е връзка с едно устройство}other{Установена е връзка с # устройства}}</translation>
+<translation id="32244657276328332">Не е установена връзка с мрежа</translation>
 <translation id="3236488194889173876">Няма мобилни мрежи</translation>
 <translation id="3255483164551725916">„What can you do?“ („Какво можеш да правиш?“)</translation>
 <translation id="3294437725009624529">Гост</translation>
@@ -171,6 +174,7 @@
 <translation id="3783640748446814672">alt</translation>
 <translation id="3784455785234192852">Заключване</translation>
 <translation id="3798670284305777884">Високоговорител (вътрешен)</translation>
+<translation id="3799080171973636491">Използвахте клавишната комбинация за увеличаване на целия екран. Искате ли да включите функцията?</translation>
 <translation id="380165613292957338">Здравейте, как мога да помогна?</translation>
 <translation id="383629559565718788">Показване на настройките на клавиатурата</translation>
 <translation id="3846575436967432996">Не е налице информация за мрежата</translation>
@@ -188,6 +192,7 @@
 <translation id="4065525899979931964">{NUM_APPS,plural, =1{Изключено за едно приложение}other{Изключено за # приложения}}</translation>
 <translation id="4072264167173457037">умерен сигнал</translation>
 <translation id="4146833061457621061">„Play music“ („Пусни музика“)</translation>
+<translation id="4181841719683918333">Езици</translation>
 <translation id="4217571870635786043">Диктуване</translation>
 <translation id="4261870227682513959">Показване на настройките за известия. Известията са изключени</translation>
 <translation id="4269883910223712419">Администраторът на това устройство може:</translation>
@@ -195,12 +200,14 @@
 <translation id="4279490309300973883">Дублиране</translation>
 <translation id="4292681942966152062">Мрежата се активира (<ph name="NETWORK_NAME" />)</translation>
 <translation id="4321179778687042513">ctrl</translation>
+<translation id="4321776623976362024">Използвахте клавишната комбинация за режима на висок контраст. Искате ли да го включите?</translation>
 <translation id="4331809312908958774">Chrome OS</translation>
 <translation id="4338109981321384717">Лупа</translation>
 <translation id="4351433414020964307">Асистент се зарежда...</translation>
 <translation id="435527878592612277">Изберете своя снимка</translation>
 <translation id="4378551569595875038">Установява се връзка...</translation>
 <translation id="4379531060876907730">Това са инструментите ви за писане</translation>
+<translation id="4389184120735010762">Използвахте клавишната комбинация за лупата в прикрепен режим. Искате ли да включите функцията?</translation>
 <translation id="4421231901400348175">Споделяте с/ъс <ph name="HELPER_NAME" /> контрола върху екрана си чрез отдалечено съдействие.</translation>
 <translation id="4430019312045809116">Звук</translation>
 <translation id="4450893287417543264">Да не се показва отново</translation>
@@ -216,7 +223,6 @@
 <translation id="4628757576491864469">Устройства</translation>
 <translation id="4659419629803378708">Активирахте ChromeVox</translation>
 <translation id="4734965478015604180">Хоризонтална ориентация</translation>
-<translation id="476166673298332917">Администраторът на това устройство има достъп до цялата активност, включително паролите и комуникациите.</translation>
 <translation id="4774338217796918551">Заповядайте отново утре в <ph name="COME_BACK_TIME" />.</translation>
 <translation id="4776917500594043016">Парола за <ph name="USER_EMAIL_ADDRESS" /></translation>
 <translation id="4778095205580009397">Google Асистент не е налице по време на демонстрационна сесия.</translation>
@@ -239,6 +245,7 @@
 <translation id="5207949376430453814">Открояване на точката на вмъкване в текста</translation>
 <translation id="5222676887888702881">Изход</translation>
 <translation id="523505283826916779">Настройки за достъпност</translation>
+<translation id="5302048478445481009">Език</translation>
 <translation id="5313326810920013265">Настройки за Bluetooth</translation>
 <translation id="5331975486040154427">Устройство с USB-C (задният ляв порт)</translation>
 <translation id="5379115545237091094">Твърде много опити</translation>
@@ -390,6 +397,7 @@
 <translation id="7886169021410746335">да коригира настройките за поверителност.</translation>
 <translation id="7886277072580235377">Сесията ви за интернет ще бъде изчистена, когато излезете от профила си. <ph name="LEARN_MORE" /></translation>
 <translation id="788781083998633524">„Send an email“ („Изпрати имейл“)</translation>
+<translation id="7897375687985782769">Използвахте клавишната комбинация за завъртане на екрана. Искате ли да промените ориентацията му?</translation>
 <translation id="7904094684485781019">Администраторът на този профил е забранил централизирания вход.</translation>
 <translation id="7933084174919150729">Google Асистент е налице само за основния потребителски профил.</translation>
 <translation id="79341161159229895">Профилът се управлява от <ph name="FIRST_PARENT_EMAIL" /> и <ph name="SECOND_PARENT_EMAIL" /></translation>
@@ -455,6 +463,7 @@
 <translation id="8921624153894383499">Google Асистент не поддържа този език.</translation>
 <translation id="8938800817013097409">Устройство с USB-C (десният порт на гърба)</translation>
 <translation id="8940956008527784070">Батерията е изтощена (<ph name="PERCENTAGE" />%)</translation>
+<translation id="8990809378771970590">Използвате <ph name="IME_NAME" /></translation>
 <translation id="899350903320462459">Отключете устройството като <ph name="LOGIN_ID" />, за да се изпълни действието от известието</translation>
 <translation id="8995603266996330174">Управлявано от <ph name="DOMAIN" /></translation>
 <translation id="9029474291399787231">Налице е актуализация на Adobe Flash Player</translation>
diff --git a/ash/strings/ash_strings_bn.xtb b/ash/strings/ash_strings_bn.xtb
index fa9a8cc..addfd9a 100644
--- a/ash/strings/ash_strings_bn.xtb
+++ b/ash/strings/ash_strings_bn.xtb
@@ -81,6 +81,7 @@
 <translation id="2268130516524549846">ব্লুটুথ অক্ষমিত</translation>
 <translation id="2268813581635650749">সবগুলি প্রস্থান করুন</translation>
 <translation id="2292698582925480719">স্কেল প্রদর্শন করুন</translation>
+<translation id="2302092602801625023">এই অ্যাকাউন্টটি Family Link দ্বারা ম্যানেজ করা</translation>
 <translation id="2303600792989757991">উইন্ডোর ওভারভিউ টগল করুন</translation>
 <translation id="2339073806695260576">একটি নোট, স্ক্রিনশট নিতে, লেজার পয়েন্টার বা ম্যাগনিফিকেশনের কাচ ব্যবহার করার জন্য শেল্ফের স্টাইলাস বোতামটি ট্যাপ করুন।</translation>
 <translation id="2341729377289034582">স্ক্রিনটি পোর্ট্রেট মোডে লক করা আছে</translation>
@@ -140,7 +141,9 @@
 <translation id="315116470104423982">মোবাইল ডেটা</translation>
 <translation id="3151786313568798007">সজ্জা</translation>
 <translation id="3153444934357957346">একধিক সাইন-ইনে আপনার কেবলমাত্র <ph name="MULTI_PROFILE_USER_LIMIT" />টি পর্যন্ত অ্যাকাউন্ট থাকতে পারে।</translation>
+<translation id="3154351730702813399">ডিভাইস প্রশাসক আপনার ব্রাউজিং অ্যাক্টিভিটি পর্যবেক্ষণ করতে পারেন।</translation>
 <translation id="3202010236269062730">{NUM_DEVICES,plural, =1{একটি ডিভাইসে কানেক্ট রয়েছে}one{#টি ডিভাইসে কানেক্ট রয়েছে}other{#টি ডিভাইসে কানেক্ট রয়েছে}}</translation>
+<translation id="32244657276328332">কোনও নেটওয়ার্কে কানেক্ট নেই</translation>
 <translation id="3236488194889173876">কোনো মোবাইল নেটওয়ার্ক উপলব্ধ নেই</translation>
 <translation id="3255483164551725916">আপনি কি করতে পারেন?</translation>
 <translation id="3294437725009624529">অতিথি</translation>
@@ -171,6 +174,7 @@
 <translation id="3783640748446814672">alt</translation>
 <translation id="3784455785234192852">লক করুন</translation>
 <translation id="3798670284305777884">স্পিকার (অভ্যন্তরীণ)</translation>
+<translation id="3799080171973636491">ফুল-স্ক্রিন ম্যাগনিফায়ারের জন্য আপনি কীবোর্ড শর্টকাট টিপেছেন। আপনি কি এটি চালু করতে চান?</translation>
 <translation id="380165613292957338">হাই, আমি কীভাবে সাহায্য করতে পারি?</translation>
 <translation id="383629559565718788">কীবোর্ড সেটিংস দেখান</translation>
 <translation id="3846575436967432996">কোনো নেটওয়ার্ক সংক্রান্ত তথ্য উপলব্ধ নেই</translation>
@@ -188,6 +192,7 @@
 <translation id="4065525899979931964">{NUM_APPS,plural, =1{একটি অ্যাপের জন্য বন্ধ আছে}one{#টি অ্যাপের জন্য বন্ধ আছে}other{#টি অ্যাপের জন্য বন্ধ আছে}}</translation>
 <translation id="4072264167173457037">মাঝারি সিগন্যাল</translation>
 <translation id="4146833061457621061">মিউজিক চালান</translation>
+<translation id="4181841719683918333">ভাষাসমূহ</translation>
 <translation id="4217571870635786043">ডিক্টেশন</translation>
 <translation id="4261870227682513959">বিজ্ঞপ্তি সেটিংস দেখান। বিজ্ঞপ্তি বন্ধ আছে</translation>
 <translation id="4269883910223712419">এই ডিভাইসের প্রশাসক এগুলি করতে পারেন:</translation>
@@ -195,12 +200,14 @@
 <translation id="4279490309300973883">অনুকরণ করা হচ্ছে</translation>
 <translation id="4292681942966152062"><ph name="NETWORK_NAME" /> চালু করা হচ্ছে</translation>
 <translation id="4321179778687042513">ctrl</translation>
+<translation id="4321776623976362024">উচ্চ কনট্রাস্টের জন্য আপনি কীবোর্ড শর্টকাট টিপেছেন। আপনি কি এটি চালু করতে চান?</translation>
 <translation id="4331809312908958774">Chrome OS</translation>
 <translation id="4338109981321384717">আতস কাচ</translation>
 <translation id="4351433414020964307">সহায়ক লোড হচ্ছে...</translation>
 <translation id="435527878592612277">আপনার ফটো বেছে নিন</translation>
 <translation id="4378551569595875038">সংযুক্ত হচ্ছে...</translation>
 <translation id="4379531060876907730">এগুলি হল আপনার স্টাইলাস টুল</translation>
+<translation id="4389184120735010762">ডক করা ম্যাগনিফায়ারের জন্য আপনি কীবোর্ড শর্টকাট টিপেছেন। আপনি কি এটি চালু করতে চান?</translation>
 <translation id="4421231901400348175">রিমোট সহায়কের মাধ্যমে <ph name="HELPER_NAME" />-এর সাথে আপনার স্ক্রিন নিয়ন্ত্রন ভাগ করুন৷</translation>
 <translation id="4430019312045809116">ভলিউম</translation>
 <translation id="4450893287417543264">আর দেখতে চাই না</translation>
@@ -216,7 +223,6 @@
 <translation id="4628757576491864469">ডিভাইস</translation>
 <translation id="4659419629803378708">ChromeVox সক্ষম করা আছে</translation>
 <translation id="4734965478015604180">অনুভূমিক</translation>
-<translation id="476166673298332917">পাসওয়ার্ড এবং যোগাযোগ সহ সমস্ত অ্যাক্টিভিটির অ্যাক্সেস এই ডিভাইসের অ্যাডমিনিস্ট্রেটরের কাছে আছে।</translation>
 <translation id="4774338217796918551">আগামীকাল <ph name="COME_BACK_TIME" />-এ আবার ব্যবহার করতে পারবেন।</translation>
 <translation id="4776917500594043016"><ph name="USER_EMAIL_ADDRESS" />-এর জন্য পাসওয়ার্ড</translation>
 <translation id="4778095205580009397">একটি ডেমো সেশনে Google অ্যাসিস্ট্যান্ট উপলভ্য নয়।</translation>
@@ -239,6 +245,7 @@
 <translation id="5207949376430453814">পাঠ্যের কার্সার হাইলাইট করুন</translation>
 <translation id="5222676887888702881">প্রস্থান করুন</translation>
 <translation id="523505283826916779">অ্যাক্সেসযোগ্যতার সেটিংস</translation>
+<translation id="5302048478445481009">ভাষা</translation>
 <translation id="5313326810920013265">ব্লুটুথ সেটিংস</translation>
 <translation id="5331975486040154427">USB-C ডিভাইস (বাঁ দিকের পিছনের পোর্ট)</translation>
 <translation id="5379115545237091094">অনেক বেশি চেষ্টা করেছেন</translation>
@@ -391,6 +398,7 @@
 <translation id="7886169021410746335">গোপনীয়তা সেটিংস অ্যাডজাস্ট করুন</translation>
 <translation id="7886277072580235377">আপনি সাইন-আউট করলে আপনার ইন্টারনেট সেশন মুছে ফেলা হবে। <ph name="LEARN_MORE" /></translation>
 <translation id="788781083998633524">একটি ইমেল পাঠান</translation>
+<translation id="7897375687985782769">স্ক্রিন ঘোরানোর জন্য আপনি কীবোর্ড শর্টকাট টিপেছেন। আপনি কি স্ক্রিন ঘোরাতে চান?</translation>
 <translation id="7904094684485781019">এই অ্যাকাউন্টের অ্যাডমিনিস্ট্রেটর একাধিক সাইন-ইন অননুমোদিত করেছেন৷</translation>
 <translation id="7933084174919150729">Google সহায়ক শুধুমাত্র প্রাথমিক প্রোফাইলেই ব্যবহার করা যায়।</translation>
 <translation id="79341161159229895">অ্যাকাউন্টটি <ph name="FIRST_PARENT_EMAIL" /> এবং <ph name="SECOND_PARENT_EMAIL" /> পরিচালনা করছেন</translation>
@@ -456,6 +464,7 @@
 <translation id="8921624153894383499">Google সহায়ক এই ভাষায় কথা বলে না।</translation>
 <translation id="8938800817013097409">USB-C ডিভাইস (পিছনের ডান পোর্ট)</translation>
 <translation id="8940956008527784070">(<ph name="PERCENTAGE" />%) কম ব্যাটারি</translation>
+<translation id="8990809378771970590"><ph name="IME_NAME" /> ব্যবহার করা হচ্ছে</translation>
 <translation id="899350903320462459">বিজ্ঞপ্তি সম্পর্কিত অ্যাকশনের জন্য <ph name="LOGIN_ID" /> হিসেবে ডিভাইস আনলক করুন</translation>
 <translation id="8995603266996330174">পরিচালনা করেছেন <ph name="DOMAIN" /> </translation>
 <translation id="9029474291399787231">Adobe Flash Player-এর আপডেট উপলভ্য</translation>
diff --git a/ash/strings/ash_strings_ca.xtb b/ash/strings/ash_strings_ca.xtb
index 8b20993..c159652 100644
--- a/ash/strings/ash_strings_ca.xtb
+++ b/ash/strings/ash_strings_ca.xtb
@@ -81,6 +81,7 @@
 <translation id="2268130516524549846">S'ha desactivat el Bluetooth</translation>
 <translation id="2268813581635650749">Tanca la sessió de tots els usuaris</translation>
 <translation id="2292698582925480719">Escala de visualització</translation>
+<translation id="2302092602801625023">Family Link gestiona aquest compte</translation>
 <translation id="2303600792989757991">Commuta la visió general de la finestra</translation>
 <translation id="2339073806695260576">Toca el botó del llapis òptic al prestatge per escriure una nota, fer una captura de pantalla o bé utilitzar-lo com a punter làser o lupa.</translation>
 <translation id="2341729377289034582">Bloquejada en vertical</translation>
@@ -140,7 +141,9 @@
 <translation id="315116470104423982">Dades mòbils</translation>
 <translation id="3151786313568798007">Orientació</translation>
 <translation id="3153444934357957346">Només pots tenir <ph name="MULTI_PROFILE_USER_LIMIT" /> comptes com a màxim en un inici de sessió múltiple.</translation>
+<translation id="3154351730702813399">És possible que l'administrador del dispositiu supervisi l'activitat de navegació.</translation>
 <translation id="3202010236269062730">{NUM_DEVICES,plural, =1{Connectat a un dispositiu}other{Connectat a # dispositius}}</translation>
+<translation id="32244657276328332">No hi ha connexió a la xarxa</translation>
 <translation id="3236488194889173876">No hi ha cap xarxa mòbil disponible</translation>
 <translation id="3255483164551725916">Què pots fer?</translation>
 <translation id="3294437725009624529">Convidat</translation>
@@ -171,6 +174,7 @@
 <translation id="3783640748446814672">alt</translation>
 <translation id="3784455785234192852">Bloqueja</translation>
 <translation id="3798670284305777884">Altaveu (intern)</translation>
+<translation id="3799080171973636491">Has premut la tecla de drecera de la lupa de pantalla completa. Vols activar-la?</translation>
 <translation id="380165613292957338">Hola, en què et puc ajudar?</translation>
 <translation id="383629559565718788">Mostra la configuració del teclat</translation>
 <translation id="3846575436967432996">No hi ha informació de xarxa disponible</translation>
@@ -188,6 +192,7 @@
 <translation id="4065525899979931964">{NUM_APPS,plural, =1{Desactivades en una aplicació}other{Desactivades en # aplicacions}}</translation>
 <translation id="4072264167173457037">Senyal mitjà</translation>
 <translation id="4146833061457621061">Reprodueix música</translation>
+<translation id="4181841719683918333">Idiomes</translation>
 <translation id="4217571870635786043">Dictat</translation>
 <translation id="4261870227682513959">Mostra la configuració de notificacions. Les notificacions estan desactivades.</translation>
 <translation id="4269883910223712419">L'administrador d'aquest dispositiu pot dur a terme les accions següents:</translation>
@@ -195,12 +200,14 @@
 <translation id="4279490309300973883">S'està creant una rèplica</translation>
 <translation id="4292681942966152062">S'està activant <ph name="NETWORK_NAME" /></translation>
 <translation id="4321179778687042513">ctrl</translation>
+<translation id="4321776623976362024">Has premut la tecla de drecera de l'alt contrast. Vols activar-lo?</translation>
 <translation id="4331809312908958774">Chrome OS</translation>
 <translation id="4338109981321384717">Lupa</translation>
 <translation id="4351433414020964307">L'Assistent s'està carregant...</translation>
 <translation id="435527878592612277">Selecciona la teva foto</translation>
 <translation id="4378551569595875038">S'està connectant...</translation>
 <translation id="4379531060876907730">Aquestes són les eines del llapis òptic</translation>
+<translation id="4389184120735010762">Has premut la tecla de drecera de la lupa acoblada. Vols activar-la?</translation>
 <translation id="4421231901400348175">Es comparteix el control de la pantalla amb <ph name="HELPER_NAME" /> mitjançant l'Assistència remota.</translation>
 <translation id="4430019312045809116">Volum</translation>
 <translation id="4450893287417543264">No ho tornis a mostrar</translation>
@@ -216,7 +223,6 @@
 <translation id="4628757576491864469">Dispositius</translation>
 <translation id="4659419629803378708">S'ha activat ChromeVox</translation>
 <translation id="4734965478015604180">Horitzontal</translation>
-<translation id="476166673298332917">L'administrador d'aquest dispositiu té accés a tota l'activitat, incloses les contrasenyes i les comunicacions.</translation>
 <translation id="4774338217796918551">Pots tornar demà a les <ph name="COME_BACK_TIME" />.</translation>
 <translation id="4776917500594043016">Contrasenya per a <ph name="USER_EMAIL_ADDRESS" /></translation>
 <translation id="4778095205580009397">L'Assistent de Google no està disponible en les sessions de demostració.</translation>
@@ -239,6 +245,7 @@
 <translation id="5207949376430453814">Ressalta la marca d'inserció de text</translation>
 <translation id="5222676887888702881">Tanca la sessió</translation>
 <translation id="523505283826916779">Configuració d'accessibilitat</translation>
+<translation id="5302048478445481009">Idioma</translation>
 <translation id="5313326810920013265">Configuració del Bluetooth</translation>
 <translation id="5331975486040154427">Dispositiu USB-C (port posterior esquerre)</translation>
 <translation id="5379115545237091094">Massa intents</translation>
@@ -390,6 +397,7 @@
 <translation id="7886169021410746335">Ajustar la configuració de privadesa</translation>
 <translation id="7886277072580235377">Les dades de la sessió a Internet s'esborraran en tancar-la. <ph name="LEARN_MORE" /></translation>
 <translation id="788781083998633524">Envia un correu electrònic</translation>
+<translation id="7897375687985782769">Has premut la tecla de drecera de la rotació de la pantalla. Vols girar la pantalla?</translation>
 <translation id="7904094684485781019">L'administrador d'aquest compte no ha permès l'inici de sessió múltiple.</translation>
 <translation id="7933084174919150729">L'Assistent de Google només està disponible per al perfil principal.</translation>
 <translation id="79341161159229895">Compte gestionat per <ph name="FIRST_PARENT_EMAIL" /> i <ph name="SECOND_PARENT_EMAIL" /></translation>
@@ -455,6 +463,7 @@
 <translation id="8921624153894383499">L'Assistent de Google no parla aquest idioma.</translation>
 <translation id="8938800817013097409">Dispositiu USB-C (port posterior dret)</translation>
 <translation id="8940956008527784070">Bateria baixa (<ph name="PERCENTAGE" />%)</translation>
+<translation id="8990809378771970590">S'està utilitzant <ph name="IME_NAME" /></translation>
 <translation id="899350903320462459">Desbloqueja el dispositiu com a <ph name="LOGIN_ID" /> per dur a terme l'acció que indica la notificació</translation>
 <translation id="8995603266996330174">Gestionat per <ph name="DOMAIN" /></translation>
 <translation id="9029474291399787231">Hi ha disponible una actualització d'Adobe Flash Player</translation>
diff --git a/ash/strings/ash_strings_cs.xtb b/ash/strings/ash_strings_cs.xtb
index e7e218e..a4b344d 100644
--- a/ash/strings/ash_strings_cs.xtb
+++ b/ash/strings/ash_strings_cs.xtb
@@ -81,6 +81,7 @@
 <translation id="2268130516524549846">Rozhraní Bluetooth deaktivováno</translation>
 <translation id="2268813581635650749">Odhlásit vše</translation>
 <translation id="2292698582925480719">Měřítko displeje</translation>
+<translation id="2302092602801625023">Tento účet spravuje služba Family Link</translation>
 <translation id="2303600792989757991">Přepnutí přehledu okna</translation>
 <translation id="2339073806695260576">Klepnutím na tlačítko dotykového pera na poličce můžete pořídit poznámku nebo snímek obrazovky, případně použít laserové ukazovátko nebo lupu.</translation>
 <translation id="2341729377289034582">Uzamknuto na výšku</translation>
@@ -140,7 +141,9 @@
 <translation id="315116470104423982">Mobilní datové přenosy</translation>
 <translation id="3151786313568798007">Orientace</translation>
 <translation id="3153444934357957346">Počet účtů, které lze v rámci vícenásobného přihlášení používat, je omezen na <ph name="MULTI_PROFILE_USER_LIMIT" />.</translation>
+<translation id="3154351730702813399">Správce zařízení může sledovat vaši aktivitu při procházení.</translation>
 <translation id="3202010236269062730">{NUM_DEVICES,plural, =1{Připojeno k zařízení}few{Připojeno k # zařízením}many{Připojeno k # zařízení}other{Připojeno k # zařízením}}</translation>
+<translation id="32244657276328332">Nejste připojeni k síti</translation>
 <translation id="3236488194889173876">Není k dispozici žádná mobilní síť</translation>
 <translation id="3255483164551725916">Co umíš?</translation>
 <translation id="3294437725009624529">Host</translation>
@@ -171,6 +174,7 @@
 <translation id="3783640748446814672">alt</translation>
 <translation id="3784455785234192852">Uzamknout</translation>
 <translation id="3798670284305777884">Reproduktor (interní)</translation>
+<translation id="3799080171973636491">Stiskli jste klávesovou zkratku pro celoobrazovkovou lupu. Chcete ji zapnout?</translation>
 <translation id="380165613292957338">Jak vám mohu pomoci?</translation>
 <translation id="383629559565718788">Zobrazit nastavení klávesnice</translation>
 <translation id="3846575436967432996">Informace o síti nejsou k dispozici</translation>
@@ -188,6 +192,7 @@
 <translation id="4065525899979931964">{NUM_APPS,plural, =1{Vypnuto pro aplikaci}few{Vypnuto pro # aplikace}many{Vypnuto pro # aplikace}other{Vypnuto pro # aplikací}}</translation>
 <translation id="4072264167173457037">Střední signál</translation>
 <translation id="4146833061457621061">Přehrát hudbu</translation>
+<translation id="4181841719683918333">Jazyky</translation>
 <translation id="4217571870635786043">Diktování</translation>
 <translation id="4261870227682513959">Zobrazit nastavení oznámení. Oznámení jsou vypnutá</translation>
 <translation id="4269883910223712419">Administrátor zařízení má následující možnosti:</translation>
@@ -195,12 +200,14 @@
 <translation id="4279490309300973883">Zrcadlení</translation>
 <translation id="4292681942966152062">Aktivace sítě <ph name="NETWORK_NAME" /></translation>
 <translation id="4321179778687042513">ctrl</translation>
+<translation id="4321776623976362024">Stiskli jste klávesovou zkratku pro vysoký kontrast. Chcete ho zapnout?</translation>
 <translation id="4331809312908958774">Chrome OS</translation>
 <translation id="4338109981321384717">Lupa</translation>
 <translation id="4351433414020964307">Asistent se načítá...</translation>
 <translation id="435527878592612277">Vyberte svou fotografii</translation>
 <translation id="4378551569595875038">Připojování...</translation>
 <translation id="4379531060876907730">Toto jsou nástroje pro dotykové pero</translation>
+<translation id="4389184120735010762">Stiskli jste klávesovou zkratku pro zadokovanou lupu. Chcete ji zapnout?</translation>
 <translation id="4421231901400348175">Ovládání obrazovky je prostřednictvím Vzdálené pomoci sdíleno s uživatelem <ph name="HELPER_NAME" />.</translation>
 <translation id="4430019312045809116">Hlasitost</translation>
 <translation id="4450893287417543264">Tuto zprávu již nezobrazovat</translation>
@@ -216,7 +223,6 @@
 <translation id="4628757576491864469">Zařízení</translation>
 <translation id="4659419629803378708">Funkce ChromeVox je zapnutá</translation>
 <translation id="4734965478015604180">Na šířku</translation>
-<translation id="476166673298332917">Administrátor tohoto zařízení má přístup k veškeré aktivitě včetně hesel a komunikace.</translation>
 <translation id="4774338217796918551">Vrať se v <ph name="COME_BACK_TIME" />.</translation>
 <translation id="4776917500594043016">Heslo pro účet <ph name="USER_EMAIL_ADDRESS" /></translation>
 <translation id="4778095205580009397">Asistent Google není v ukázkové relaci k dispozici.</translation>
@@ -239,6 +245,7 @@
 <translation id="5207949376430453814">Zvýraznit textový kurzor</translation>
 <translation id="5222676887888702881">Odhlásit se</translation>
 <translation id="523505283826916779">Nastavení usnadnění přístupu</translation>
+<translation id="5302048478445481009">Jazyk</translation>
 <translation id="5313326810920013265">Nastavení Bluetooth</translation>
 <translation id="5331975486040154427">Zařízení USB Type-C (levý zadní port)</translation>
 <translation id="5379115545237091094">Příliš mnoho pokusů</translation>
@@ -390,6 +397,7 @@
 <translation id="7886169021410746335">Upravit nastavení soukromí</translation>
 <translation id="7886277072580235377">Když se odhlásíte, vaše relace internetu se vymaže. <ph name="LEARN_MORE" /></translation>
 <translation id="788781083998633524">Poslat e‑mail</translation>
+<translation id="7897375687985782769">Stiskli jste klávesovou zkratku pro otočení obrazovky. Chcete otočit obrazovku?</translation>
 <translation id="7904094684485781019">Správce tohoto účtu zakázal vícenásobné přihlášení.</translation>
 <translation id="7933084174919150729">Asistent Google je k dispozici pouze pro primární profil.</translation>
 <translation id="79341161159229895">Správci účtu: <ph name="FIRST_PARENT_EMAIL" /> a <ph name="SECOND_PARENT_EMAIL" /></translation>
@@ -455,6 +463,7 @@
 <translation id="8921624153894383499">Tímto jazykem Asistent Google nehovoří.</translation>
 <translation id="8938800817013097409">Zařízení USB Type-C (pravý zadní port)</translation>
 <translation id="8940956008527784070">Slabá baterie (<ph name="PERCENTAGE" /> %)</translation>
+<translation id="8990809378771970590">Metoda zadávání: <ph name="IME_NAME" /></translation>
 <translation id="899350903320462459">Chcete-li provést akci s oznámením, odemkněte zařízení jako <ph name="LOGIN_ID" /></translation>
 <translation id="8995603266996330174">Správce: <ph name="DOMAIN" /></translation>
 <translation id="9029474291399787231">Je k dispozici aktualizace přehrávače Adobe Flash Player</translation>
diff --git a/ash/strings/ash_strings_da.xtb b/ash/strings/ash_strings_da.xtb
index 202347b..bca7c43 100644
--- a/ash/strings/ash_strings_da.xtb
+++ b/ash/strings/ash_strings_da.xtb
@@ -81,10 +81,11 @@
 <translation id="2268130516524549846">Bluetooth er deaktiveret</translation>
 <translation id="2268813581635650749">Log alle ud</translation>
 <translation id="2292698582925480719">Visningsskala</translation>
+<translation id="2302092602801625023">Denne konto administreres af Family Link</translation>
 <translation id="2303600792989757991">Skift vinduesoversigt</translation>
 <translation id="2339073806695260576">Tryk på knappen for styluspen på hylden for at skrive en note, tage et screenshot eller bruge lasermarkøren og luppen.</translation>
 <translation id="2341729377289034582">Altid lodret</translation>
-<translation id="2352467521400612932">Penneindstillinger</translation>
+<translation id="2352467521400612932">Indstillinger for styluspen</translation>
 <translation id="2354174487190027830">Aktiverer <ph name="NAME" /></translation>
 <translation id="2359808026110333948">Fortsæt</translation>
 <translation id="2365393535144473978">Bluetooth aktiveres, når du aktiverer mobildata.</translation>
@@ -140,13 +141,15 @@
 <translation id="315116470104423982">Mobildata</translation>
 <translation id="3151786313568798007">Retning</translation>
 <translation id="3153444934357957346">Du kan kun have op til <ph name="MULTI_PROFILE_USER_LIMIT" /> konti i samlet login fra flere konti.</translation>
+<translation id="3154351730702813399">Administratoren af enheden kan overvåge din browseraktivitet.</translation>
 <translation id="3202010236269062730">{NUM_DEVICES,plural, =1{Forbundet til en enhed}one{Forbundet til # enhed}other{Forbundet til # enheder}}</translation>
+<translation id="32244657276328332">Der er ingen netværksforbindelse</translation>
 <translation id="3236488194889173876">Der er ingen tilgængelige mobilnetværk</translation>
 <translation id="3255483164551725916">Hvad kan du gøre?</translation>
 <translation id="3294437725009624529">Gæst</translation>
 <translation id="332587331255250389">Udskift batteriet</translation>
 <translation id="3351879221545518001">Du caster i øjeblikket skærmen.</translation>
-<translation id="3364721542077212959">Penneværktøjer</translation>
+<translation id="3364721542077212959">Styluspenværktøjer</translation>
 <translation id="3368922792935385530">Tilsluttet</translation>
 <translation id="3371140690572404006">USB-C-enhed (porten foran i højre side)</translation>
 <translation id="3400357268283240774">Yderligere indstillinger</translation>
@@ -171,6 +174,7 @@
 <translation id="3783640748446814672">alt</translation>
 <translation id="3784455785234192852">Lås</translation>
 <translation id="3798670284305777884">Højttaler (indbygget)</translation>
+<translation id="3799080171973636491">Du trykkede på tastaturgenvejen for forstørrelse af fuld skærm. Vil du aktivere indstillingen?</translation>
 <translation id="380165613292957338">Hej, hvad kan jeg hjælpe med?</translation>
 <translation id="383629559565718788">Vis tastaturindstillinger</translation>
 <translation id="3846575436967432996">Der er ingen tilgængelige netværksoplysninger</translation>
@@ -188,6 +192,7 @@
 <translation id="4065525899979931964">{NUM_APPS,plural, =1{Deaktiveret for én app}one{Deaktiveret for # app}other{Deaktiveret for # apps}}</translation>
 <translation id="4072264167173457037">Middel signal</translation>
 <translation id="4146833061457621061">Spil musik</translation>
+<translation id="4181841719683918333">Sprog</translation>
 <translation id="4217571870635786043">Diktering</translation>
 <translation id="4261870227682513959">Vis indstillinger for underretninger. Underretninger er slået fra</translation>
 <translation id="4269883910223712419">Administratoren af denne enhed kan gøre følgende:</translation>
@@ -195,12 +200,14 @@
 <translation id="4279490309300973883">Spejling</translation>
 <translation id="4292681942966152062">Aktiverer <ph name="NETWORK_NAME" /></translation>
 <translation id="4321179778687042513">ctrl</translation>
+<translation id="4321776623976362024">Du trykkede på tastaturgenvejen for høj kontrast. Vil du aktivere indstillingen?</translation>
 <translation id="4331809312908958774">Chrome OS</translation>
 <translation id="4338109981321384717">Forstørrelsesglas</translation>
 <translation id="4351433414020964307">Assistenten indlæses...</translation>
 <translation id="435527878592612277">Vælg dit billede</translation>
 <translation id="4378551569595875038">Opretter forbindelse...</translation>
-<translation id="4379531060876907730">Dette er dine penneværktøjer</translation>
+<translation id="4379531060876907730">Dette er dine styluspenværktøjer</translation>
+<translation id="4389184120735010762">Du trykkede på tastaturgenvejen for det fastgjorte lupvindue. Vil du aktivere indstillingen?</translation>
 <translation id="4421231901400348175">Skærmdeling med <ph name="HELPER_NAME" /> via Fjernsupport.</translation>
 <translation id="4430019312045809116">Lydstyrke</translation>
 <translation id="4450893287417543264">Vis ikke igen</translation>
@@ -216,7 +223,6 @@
 <translation id="4628757576491864469">Enheder</translation>
 <translation id="4659419629803378708">ChromeVox er aktiveret</translation>
 <translation id="4734965478015604180">Vandret</translation>
-<translation id="476166673298332917">Administratoren af denne enhed har adgang til al aktivitet, herunder adgangskoder og kommunikation.</translation>
 <translation id="4774338217796918551">Kom tilbage i morgen kl. <ph name="COME_BACK_TIME" />.</translation>
 <translation id="4776917500594043016">Adgangskode for <ph name="USER_EMAIL_ADDRESS" /></translation>
 <translation id="4778095205580009397">Google Assistent er ikke tilgængelig i en demosession.</translation>
@@ -239,6 +245,7 @@
 <translation id="5207949376430453814">Fremhæv tekstmarkør</translation>
 <translation id="5222676887888702881">Log ud</translation>
 <translation id="523505283826916779">Indstillinger for hjælpefunktioner</translation>
+<translation id="5302048478445481009">Sprog</translation>
 <translation id="5313326810920013265">Indstillinger for Bluetooth</translation>
 <translation id="5331975486040154427">USB-C-enhed (porten bagpå i venstre side)</translation>
 <translation id="5379115545237091094">For mange forsøg</translation>
@@ -297,7 +304,7 @@
 <translation id="6259254695169772643">Vælg ved hjælp af din styluspen</translation>
 <translation id="6267036997247669271"><ph name="NAME" />: Aktiverer...</translation>
 <translation id="6284232397434400372">Opløsningen blev ændret</translation>
-<translation id="6297287540776456956">Brug pennen til at vælge et område</translation>
+<translation id="6297287540776456956">Brug styluspennen til at vælge et område</translation>
 <translation id="6310121235600822547"><ph name="DISPLAY_NAME" /> er roteret til <ph name="ROTATION" /></translation>
 <translation id="6344698853700675736">Slå Nattelys til/fra</translation>
 <translation id="6376931439017688372">Bluetooth er slået til</translation>
@@ -391,6 +398,7 @@
 <translation id="7886169021410746335">Juster privatlivsindstillinger</translation>
 <translation id="7886277072580235377">Din internetsession ryddes, når du logger ud. <ph name="LEARN_MORE" /></translation>
 <translation id="788781083998633524">Send en mail</translation>
+<translation id="7897375687985782769">Du trykkede på tastaturgenvejen for skærmrotation. Vil du rotere skærmen?</translation>
 <translation id="7904094684485781019">Administratoren for denne konto tillader ikke samlet login fra flere konti.</translation>
 <translation id="7933084174919150729">Google Assistent kan kun bruges via den primære profil.</translation>
 <translation id="79341161159229895">Kontoen administreres af <ph name="FIRST_PARENT_EMAIL" /> og <ph name="SECOND_PARENT_EMAIL" /></translation>
@@ -456,6 +464,7 @@
 <translation id="8921624153894383499">Google-assistenten taler ikke dette sprog.</translation>
 <translation id="8938800817013097409">USB-C-enhed (porten bagpå i højre side)</translation>
 <translation id="8940956008527784070">Batteriniveauet er lavt (<ph name="PERCENTAGE" /> %)</translation>
+<translation id="8990809378771970590"><ph name="IME_NAME" /> anvendes</translation>
 <translation id="899350903320462459">Lås enheden op som <ph name="LOGIN_ID" /> for at udføre underretningshandlingen</translation>
 <translation id="8995603266996330174">Administreres af <ph name="DOMAIN" /></translation>
 <translation id="9029474291399787231">Der er en tilgængelig Adobe Flash Player-opdatering</translation>
diff --git a/ash/strings/ash_strings_de.xtb b/ash/strings/ash_strings_de.xtb
index 3b5649e9..b9efd88 100644
--- a/ash/strings/ash_strings_de.xtb
+++ b/ash/strings/ash_strings_de.xtb
@@ -81,6 +81,7 @@
 <translation id="2268130516524549846">Bluetooth deaktiviert</translation>
 <translation id="2268813581635650749">Alle abmelden</translation>
 <translation id="2292698582925480719">Displaymaßstab</translation>
+<translation id="2302092602801625023">Dieses Konto wird über Family Link verwaltet</translation>
 <translation id="2303600792989757991">Fensterübersicht umschalten</translation>
 <translation id="2339073806695260576">Tippen Sie auf die Eingabestift-Schaltfläche in der Ablage, um Notizen oder Screenshots zu erstellen, den Laserpointer oder die Vergrößerungsfunktion zu verwenden.</translation>
 <translation id="2341729377289034582">Nur vertikale Ausrichtung</translation>
@@ -140,7 +141,9 @@
 <translation id="315116470104423982">Mobilfunk</translation>
 <translation id="3151786313568798007">Ausrichtung</translation>
 <translation id="3153444934357957346">Bei der Mehrfachanmeldung sind maximal <ph name="MULTI_PROFILE_USER_LIMIT" /> Konten zulässig.</translation>
+<translation id="3154351730702813399">Der Geräteadministrator überwacht unter Umständen Ihre Browseraktivitäten.</translation>
 <translation id="3202010236269062730">{NUM_DEVICES,plural, =1{Mit einem Gerät verbunden}other{Mit # Geräten verbunden}}</translation>
+<translation id="32244657276328332">Keine Netzwerkverbindung</translation>
 <translation id="3236488194889173876">Kein Mobilfunknetz verfügbar</translation>
 <translation id="3255483164551725916">Was kannst du alles tun?</translation>
 <translation id="3294437725009624529">Gast</translation>
@@ -171,6 +174,7 @@
 <translation id="3783640748446814672">Alt</translation>
 <translation id="3784455785234192852">Sperren</translation>
 <translation id="3798670284305777884">Lautsprecher (intern)</translation>
+<translation id="3799080171973636491">Sie haben die Tastenkombination für den Modus "Vollbildlupe" gedrückt. Möchten Sie ihn aktivieren?</translation>
 <translation id="380165613292957338">Hallo, wie kann ich dir helfen?</translation>
 <translation id="383629559565718788">Tastatureinstellungen anzeigen</translation>
 <translation id="3846575436967432996">Keine Netzwerkinformationen verfügbar</translation>
@@ -188,6 +192,7 @@
 <translation id="4065525899979931964">{NUM_APPS,plural, =1{Für eine App deaktiviert}other{Für # Apps deaktiviert}}</translation>
 <translation id="4072264167173457037">Durchschnittliches Signal</translation>
 <translation id="4146833061457621061">Musik abspielen</translation>
+<translation id="4181841719683918333">Sprachen</translation>
 <translation id="4217571870635786043">Spracheingabe</translation>
 <translation id="4261870227682513959">Benachrichtigungseinstellungen anzeigen. Benachrichtigungen sind deaktiviert.</translation>
 <translation id="4269883910223712419">Der Administrator dieses Geräts kann Folgendes tun:</translation>
@@ -195,12 +200,14 @@
 <translation id="4279490309300973883">Spiegelung</translation>
 <translation id="4292681942966152062"><ph name="NETWORK_NAME" /> wird aktiviert</translation>
 <translation id="4321179778687042513">Strg</translation>
+<translation id="4321776623976362024">Sie haben die Tastenkombination für den Modus "Hoher Kontrast" gedrückt. Möchten Sie ihn aktivieren?</translation>
 <translation id="4331809312908958774">Chrome OS</translation>
 <translation id="4338109981321384717">Lupe</translation>
 <translation id="4351433414020964307">Assistant wird geladen…</translation>
 <translation id="435527878592612277">Foto auswählen</translation>
 <translation id="4378551569595875038">Verbindung wird hergestellt...</translation>
 <translation id="4379531060876907730">Dies sind Ihre Eingabestift-Tools</translation>
+<translation id="4389184120735010762">Sie haben die Tastenkombination für den Modus "Angedockte Lupe" gedrückt. Möchten Sie ihn aktivieren?</translation>
 <translation id="4421231901400348175">Sie teilen sich die Bildschirmsteuerung mit <ph name="HELPER_NAME" /> per Remote-Unterstützung.</translation>
 <translation id="4430019312045809116">Lautstärke</translation>
 <translation id="4450893287417543264">Nicht mehr anzeigen</translation>
@@ -216,7 +223,6 @@
 <translation id="4628757576491864469">Geräte</translation>
 <translation id="4659419629803378708">ChromeVox aktiviert</translation>
 <translation id="4734965478015604180">Horizontal</translation>
-<translation id="476166673298332917">Der Administrator dieses Geräts hat Zugriff auf sämtliche Aktivitäten, einschließlich Passwörtern und Kommunikation.</translation>
 <translation id="4774338217796918551">Du darfst das Gerät um <ph name="COME_BACK_TIME" /> wieder verwenden.</translation>
 <translation id="4776917500594043016">Passwort für <ph name="USER_EMAIL_ADDRESS" /></translation>
 <translation id="4778095205580009397">In Demositzungen ist Google Assistant nicht verfügbar.</translation>
@@ -239,6 +245,7 @@
 <translation id="5207949376430453814">Textcursor hervorheben</translation>
 <translation id="5222676887888702881">Abmelden</translation>
 <translation id="523505283826916779">Zugänglichkeitseinstellungen</translation>
+<translation id="5302048478445481009">Sprache</translation>
 <translation id="5313326810920013265">Bluetooth-Einstellungen</translation>
 <translation id="5331975486040154427">USB-C-Gerät (Port hinten links)</translation>
 <translation id="5379115545237091094">Zu viele Versuche</translation>
@@ -390,6 +397,7 @@
 <translation id="7886169021410746335">Datenschutzeinstellungen anpassen</translation>
 <translation id="7886277072580235377">Ihre Internetsitzung wird gelöscht, sobald Sie sich abmelden. <ph name="LEARN_MORE" /></translation>
 <translation id="788781083998633524">E-Mails senden</translation>
+<translation id="7897375687985782769">Sie haben die Tastenkombination zum Drehen des Bildschirms gedrückt. Möchten Sie den Bildschirm drehen?</translation>
 <translation id="7904094684485781019">Der Administrator dieses Kontos hat keine Mehrfachanmeldung zugelassen.</translation>
 <translation id="7933084174919150729">Google Assistant ist nur für das Hauptprofil verfügbar.</translation>
 <translation id="79341161159229895">Konto verwaltet von <ph name="FIRST_PARENT_EMAIL" /> und <ph name="SECOND_PARENT_EMAIL" /></translation>
@@ -455,6 +463,7 @@
 <translation id="8921624153894383499">Google Assistant spricht diese Sprache nicht.</translation>
 <translation id="8938800817013097409">USB-C-Gerät (rechter Port hinten)</translation>
 <translation id="8940956008527784070">Niedriger Akkustand (<ph name="PERCENTAGE" /> %)</translation>
+<translation id="8990809378771970590"><ph name="IME_NAME" /> wird verwendet</translation>
 <translation id="899350903320462459">Wenn Sie die Benachrichtigungsaktion ausführen möchten, entsperren Sie das Gerät als <ph name="LOGIN_ID" /></translation>
 <translation id="8995603266996330174">Verwaltet von <ph name="DOMAIN" /></translation>
 <translation id="9029474291399787231">Update für den Adobe Flash Player verfügbar</translation>
diff --git a/ash/strings/ash_strings_el.xtb b/ash/strings/ash_strings_el.xtb
index bb4e387..935677f 100644
--- a/ash/strings/ash_strings_el.xtb
+++ b/ash/strings/ash_strings_el.xtb
@@ -81,6 +81,7 @@
 <translation id="2268130516524549846">Το Bluetooth έχει απενεργοποιηθεί</translation>
 <translation id="2268813581635650749">Αποσύνδεση όλων</translation>
 <translation id="2292698582925480719">Κλίμακα προβολής</translation>
+<translation id="2302092602801625023">Αυτός ο λογαριασμός είναι διαχειριζόμενος από το Family Link</translation>
 <translation id="2303600792989757991">Επισκόπηση εναλλαγής παραθύρων</translation>
 <translation id="2339073806695260576">Πατήστε το κουμπί με τη γραφίδα στο ράφι, για να δημιουργήσετε μια σημείωση, ένα στιγμιότυπο οθόνης και να χρησιμοποιήσετε τον δείκτη λέιζερ ή τον μεγεθυντικό φακό.</translation>
 <translation id="2341729377289034582">Κλειδωμένη σε κάθετη</translation>
@@ -140,7 +141,9 @@
 <translation id="315116470104423982">Δεδομένα κινητής τηλεφωνίας</translation>
 <translation id="3151786313568798007">Προσανατολισμός</translation>
 <translation id="3153444934357957346">Μπορείτε να χρησιμοποιήσετε έως <ph name="MULTI_PROFILE_USER_LIMIT" /> λογαριασμούς στη λειτουργία σύνδεσης σε πολλούς λογαριασμούς.</translation>
+<translation id="3154351730702813399">Ο διαχειριστής της συσκευής μπορεί να παρακολουθεί τη δραστηριότητα περιήγησής σας.</translation>
 <translation id="3202010236269062730">{NUM_DEVICES,plural, =1{Συνδέθηκε σε κάποια συσκευή}other{Συνδέθηκε σε # συσκευές}}</translation>
+<translation id="32244657276328332">Δεν έχει συνδεθεί σε κάποιο δίκτυο</translation>
 <translation id="3236488194889173876">Δεν υπάρχουν διαθέσιμα δίκτυα κινητής τηλεφωνίας</translation>
 <translation id="3255483164551725916">Τι μπορείς να κάνεις;</translation>
 <translation id="3294437725009624529">Επισκέπτης</translation>
@@ -171,6 +174,7 @@
 <translation id="3783640748446814672">alt</translation>
 <translation id="3784455785234192852">Κλείδωμα</translation>
 <translation id="3798670284305777884">Ηχείο (εσωτερικό)</translation>
+<translation id="3799080171973636491">Πατήσατε τη συντόμευση πληκτρολογίου για τον μεγεθυντικό φακό πλήρους οθόνης. Θέλετε να τον ενεργοποιήσετε;</translation>
 <translation id="380165613292957338">Γεια, πώς μπορώ να βοηθήσω;</translation>
 <translation id="383629559565718788">Εμφάνιση ρυθμίσεων πληκτρολογίου</translation>
 <translation id="3846575436967432996">Δεν υπάρχουν διαθέσιμες πληροφορίες δικτύου</translation>
@@ -188,6 +192,7 @@
 <translation id="4065525899979931964">{NUM_APPS,plural, =1{Απενεργοποιημένες για κάποια εφαρμογή}other{Απενεργοποιημένες για # εφαρμογές}}</translation>
 <translation id="4072264167173457037">Μεσαίο σήμα</translation>
 <translation id="4146833061457621061">Αναπαραγωγή μουσικής</translation>
+<translation id="4181841719683918333">Γλώσσες</translation>
 <translation id="4217571870635786043">Υπαγόρευση</translation>
 <translation id="4261870227682513959">Εμφάνιση ρυθμίσεων ειδοποιήσεων. Οι ειδοποιήσεις είναι απενεργοποιημένες.</translation>
 <translation id="4269883910223712419">Ο διαχειριστής αυτής της συσκευής μπορεί να εκτελεί τις ακόλουθες ενέργειες:</translation>
@@ -195,12 +200,14 @@
 <translation id="4279490309300973883">Κατοπτρισμός</translation>
 <translation id="4292681942966152062">Ενεργοποίηση δικτύου <ph name="NETWORK_NAME" /></translation>
 <translation id="4321179778687042513">ctrl</translation>
+<translation id="4321776623976362024">Πατήσατε τη συντόμευση πληκτρολογίου για την υψηλή αντίθεση. Θέλετε να την ενεργοποιήσετε;</translation>
 <translation id="4331809312908958774">Chrome OS</translation>
 <translation id="4338109981321384717">Μεγεθυντικός φακός</translation>
 <translation id="4351433414020964307">Γίνεται φόρτωση του Βοηθού…</translation>
 <translation id="435527878592612277">Επιλέξτε τη φωτογραφία σας</translation>
 <translation id="4378551569595875038">Σύνδεση…</translation>
 <translation id="4379531060876907730">Αυτά είναι τα εργαλεία γραφίδας σας</translation>
+<translation id="4389184120735010762">Πατήσατε τη συντόμευση πληκτρολογίου για τον μεγεθυντικό φακό σε παράθυρο. Θέλετε να τον ενεργοποιήσετε;</translation>
 <translation id="4421231901400348175">Κοινόχρηστος έλεγχος της οθόνης σας με το χρήστη <ph name="HELPER_NAME" /> μέσω της απομακρυσμένης βοήθειας.</translation>
 <translation id="4430019312045809116">Ένταση</translation>
 <translation id="4450893287417543264">Να μην εμφανιστεί ξανά</translation>
@@ -216,7 +223,6 @@
 <translation id="4628757576491864469">Συσκευές</translation>
 <translation id="4659419629803378708">Το ChromeVox ενεργοποιήθηκε</translation>
 <translation id="4734965478015604180">Οριζόντια περιστροφή</translation>
-<translation id="476166673298332917">Ο διαχειριστής αυτής της συσκευής έχει πρόσβαση σε όλη τη δραστηριότητα, συμπεριλαμβανομένων των κωδικών πρόσβασης και της επικοινωνίας.</translation>
 <translation id="4774338217796918551">Επιστρέψτε ξανά αύριο στις <ph name="COME_BACK_TIME" />.</translation>
 <translation id="4776917500594043016">Κωδικός πρόσβασης για <ph name="USER_EMAIL_ADDRESS" /></translation>
 <translation id="4778095205580009397">Ο Βοηθός Google δεν είναι διαθέσιμος σε περίοδο λειτουργίας επίδειξης.</translation>
@@ -239,6 +245,7 @@
 <translation id="5207949376430453814">Επισήμανση δρομέα κειμένου</translation>
 <translation id="5222676887888702881">Έξοδος</translation>
 <translation id="523505283826916779">Ρυθμίσεις προσβασιμότητας</translation>
+<translation id="5302048478445481009">Γλώσσα</translation>
 <translation id="5313326810920013265">Ρυθμίσεις Bluetooth</translation>
 <translation id="5331975486040154427">Συσκευή USB-C (πίσω αριστερή θύρα)</translation>
 <translation id="5379115545237091094">Πάρα πολλές προσπάθειες</translation>
@@ -390,6 +397,7 @@
 <translation id="7886169021410746335">Προσαρμογή ρυθμίσεων απορρήτου</translation>
 <translation id="7886277072580235377">Η περίοδος σύνδεσής σας στο διαδίκτυο θα διαγραφεί μόλις αποσυνδεθείτε. <ph name="LEARN_MORE" /></translation>
 <translation id="788781083998633524">Αποστολή μηνύματος ηλεκτρονικού ταχυδρομείου</translation>
+<translation id="7897375687985782769">Πατήσατε τη συντόμευση πληκτρολογίου για την περιστροφή οθόνης. Θέλετε να περιστρέψετε την οθόνη;</translation>
 <translation id="7904094684485781019">Ο διαχειριστής αυτού του λογαριασμού δεν έχει επιτρέψει τις πολλαπλές συνδέσεις.</translation>
 <translation id="7933084174919150729">Ο Βοηθός Google είναι διαθέσιμος μόνο για το κύριο προφίλ.</translation>
 <translation id="79341161159229895">Ο λογαριασμός είναι διαχειριζόμενος από τους χρήστες <ph name="FIRST_PARENT_EMAIL" /> και <ph name="SECOND_PARENT_EMAIL" /></translation>
@@ -455,6 +463,7 @@
 <translation id="8921624153894383499">Ο Βοηθός Google δεν είναι διαθέσιμος σε αυτήν τη γλώσσα.</translation>
 <translation id="8938800817013097409">Συσκευή USB-C (πίσω δεξιά θύρα)</translation>
 <translation id="8940956008527784070">Χαμηλή στάθμη μπαταρίας (<ph name="PERCENTAGE" />%)</translation>
+<translation id="8990809378771970590">Χρήση <ph name="IME_NAME" /></translation>
 <translation id="899350903320462459">Ξεκλειδώστε τη συσκευή ως <ph name="LOGIN_ID" /> για εκτέλεση της ενέργειας ειδοποίησης</translation>
 <translation id="8995603266996330174">Έγινε διαχείριση από <ph name="DOMAIN" /></translation>
 <translation id="9029474291399787231">Υπάρχει διαθέσιμη ενημέρωση για το Adobe Flash Player</translation>
diff --git a/ash/strings/ash_strings_en-GB.xtb b/ash/strings/ash_strings_en-GB.xtb
index c5682fb..f36f377 100644
--- a/ash/strings/ash_strings_en-GB.xtb
+++ b/ash/strings/ash_strings_en-GB.xtb
@@ -81,6 +81,7 @@
 <translation id="2268130516524549846">Bluetooth disabled</translation>
 <translation id="2268813581635650749">Sign out all</translation>
 <translation id="2292698582925480719">Display scale</translation>
+<translation id="2302092602801625023">This account is managed by Family Link</translation>
 <translation id="2303600792989757991">Toggle window overview</translation>
 <translation id="2339073806695260576">Tap the stylus button on the shelf to take a note, screenshot, use the laser pointer or magnifying glass.</translation>
 <translation id="2341729377289034582">Locked to vertical</translation>
@@ -140,7 +141,9 @@
 <translation id="315116470104423982">Mobile data</translation>
 <translation id="3151786313568798007">Orientation</translation>
 <translation id="3153444934357957346">You can only have up to <ph name="MULTI_PROFILE_USER_LIMIT" /> accounts in multiple sign-in.</translation>
+<translation id="3154351730702813399">The device admin may monitor your browsing activity.</translation>
 <translation id="3202010236269062730">{NUM_DEVICES,plural, =1{Connected to a device}other{Connected to # devices}}</translation>
+<translation id="32244657276328332">Not connected to network</translation>
 <translation id="3236488194889173876">No mobile network available</translation>
 <translation id="3255483164551725916">What can you do?</translation>
 <translation id="3294437725009624529">Guest</translation>
@@ -171,6 +174,7 @@
 <translation id="3783640748446814672">alt</translation>
 <translation id="3784455785234192852">Lock</translation>
 <translation id="3798670284305777884">Speaker (internal)</translation>
+<translation id="3799080171973636491">You pressed the keyboard shortcut for the full-screen magnifier. Do you want to turn it on?</translation>
 <translation id="380165613292957338">Hi, how can I help?</translation>
 <translation id="383629559565718788">Show keyboard settings</translation>
 <translation id="3846575436967432996">No network information available</translation>
@@ -188,6 +192,7 @@
 <translation id="4065525899979931964">{NUM_APPS,plural, =1{Off for an app}other{Off for # apps}}</translation>
 <translation id="4072264167173457037">Medium signal</translation>
 <translation id="4146833061457621061">Play music</translation>
+<translation id="4181841719683918333">Languages</translation>
 <translation id="4217571870635786043">Dictation</translation>
 <translation id="4261870227682513959">Show notification settings. Notifications are off</translation>
 <translation id="4269883910223712419">The admin of this device has the ability to:</translation>
@@ -195,12 +200,14 @@
 <translation id="4279490309300973883">Mirroring</translation>
 <translation id="4292681942966152062">Activating <ph name="NETWORK_NAME" /></translation>
 <translation id="4321179778687042513">ctrl</translation>
+<translation id="4321776623976362024">You pressed the keyboard shortcut for high contrast. Do you want to turn it on?</translation>
 <translation id="4331809312908958774">Chrome OS</translation>
 <translation id="4338109981321384717">Magnifying glass</translation>
 <translation id="4351433414020964307">Assistant is loading...</translation>
 <translation id="435527878592612277">Select your photo</translation>
 <translation id="4378551569595875038">Connecting...</translation>
 <translation id="4379531060876907730">These are your stylus tools</translation>
+<translation id="4389184120735010762">You pressed the keyboard shortcut for the docked magnifier. Do you want to turn it on?</translation>
 <translation id="4421231901400348175">Sharing control of your screen with <ph name="HELPER_NAME" /> via Remote Assistance.</translation>
 <translation id="4430019312045809116">volume</translation>
 <translation id="4450893287417543264">Don't show again</translation>
@@ -216,7 +223,6 @@
 <translation id="4628757576491864469">Devices</translation>
 <translation id="4659419629803378708">ChromeVox enabled</translation>
 <translation id="4734965478015604180">Horizontal</translation>
-<translation id="476166673298332917">The administrator of this device has access to all activity, including passwords and communications.</translation>
 <translation id="4774338217796918551">Come back tomorrow at <ph name="COME_BACK_TIME" />.</translation>
 <translation id="4776917500594043016">Password for <ph name="USER_EMAIL_ADDRESS" /></translation>
 <translation id="4778095205580009397">The Google Assistant is not available in a demo session.</translation>
@@ -239,6 +245,7 @@
 <translation id="5207949376430453814">Highlight text caret</translation>
 <translation id="5222676887888702881">Sign out</translation>
 <translation id="523505283826916779">Accessibility settings</translation>
+<translation id="5302048478445481009">Language</translation>
 <translation id="5313326810920013265">Bluetooth settings</translation>
 <translation id="5331975486040154427">USB-C device (left side back port)</translation>
 <translation id="5379115545237091094">Too many attempts</translation>
@@ -390,6 +397,7 @@
 <translation id="7886169021410746335">Adjust privacy settings</translation>
 <translation id="7886277072580235377">Your Internet session will be cleared when you sign out. <ph name="LEARN_MORE" /></translation>
 <translation id="788781083998633524">Send an email</translation>
+<translation id="7897375687985782769">You pressed the keyboard shortcut for screen rotation. Do you want to rotate the screen?</translation>
 <translation id="7904094684485781019">The administrator for this account has disallowed multiple sign-in.</translation>
 <translation id="7933084174919150729">The Google Assistant is only available for primary profile.</translation>
 <translation id="79341161159229895">Account managed by <ph name="FIRST_PARENT_EMAIL" /> and <ph name="SECOND_PARENT_EMAIL" /></translation>
@@ -455,6 +463,7 @@
 <translation id="8921624153894383499">The Google Assistant doesn’t speak this language.</translation>
 <translation id="8938800817013097409">USB-C device (right port in the back)</translation>
 <translation id="8940956008527784070">Battery low (<ph name="PERCENTAGE" />%)</translation>
+<translation id="8990809378771970590">Using <ph name="IME_NAME" /></translation>
 <translation id="899350903320462459">Unlock device as <ph name="LOGIN_ID" /> to perform the notification action</translation>
 <translation id="8995603266996330174">Managed by <ph name="DOMAIN" /></translation>
 <translation id="9029474291399787231">Adobe Flash Player update available</translation>
diff --git a/ash/strings/ash_strings_es-419.xtb b/ash/strings/ash_strings_es-419.xtb
index ac10716..76f640d 100644
--- a/ash/strings/ash_strings_es-419.xtb
+++ b/ash/strings/ash_strings_es-419.xtb
@@ -81,6 +81,7 @@
 <translation id="2268130516524549846">Bluetooth desactivado</translation>
 <translation id="2268813581635650749">Salir de todo</translation>
 <translation id="2292698582925480719">Escala de visualización</translation>
+<translation id="2302092602801625023">Esta cuenta está administrada por Family Link</translation>
 <translation id="2303600792989757991">Activar vista general de ventanas</translation>
 <translation id="2339073806695260576">Presiona el botón de la pluma stylus en la biblioteca para tomar notas y capturas de pantalla, y usar el puntero láser o la lupa.</translation>
 <translation id="2341729377289034582">Se bloqueó en posición vertical</translation>
@@ -140,7 +141,9 @@
 <translation id="315116470104423982">Datos móviles</translation>
 <translation id="3151786313568798007">Orientación</translation>
 <translation id="3153444934357957346">Puedes acceder a un máximo de <ph name="MULTI_PROFILE_USER_LIMIT" /> cuentas con acceso múltiple.</translation>
+<translation id="3154351730702813399">Es posible que el administrador de dispositivos supervise tu actividad de navegación.</translation>
 <translation id="3202010236269062730">{NUM_DEVICES,plural, =1{Conectado a un dispositivo}other{Conectado a # dispositivos}}</translation>
+<translation id="32244657276328332">Sin conexión a la red</translation>
 <translation id="3236488194889173876">No hay redes móviles disponibles</translation>
 <translation id="3255483164551725916">¿Qué puedes hacer?</translation>
 <translation id="3294437725009624529">Invitado</translation>
@@ -172,6 +175,7 @@
 <translation id="3783640748446814672">alt</translation>
 <translation id="3784455785234192852">Bloquear</translation>
 <translation id="3798670284305777884">Altavoz (interno)</translation>
+<translation id="3799080171973636491">Presionaste la combinación de teclas para activar la lupa de pantalla completa. ¿Quieres activarla?</translation>
 <translation id="380165613292957338">Hola, ¿cómo puedo ayudarte?</translation>
 <translation id="383629559565718788">Mostrar la configuración del teclado</translation>
 <translation id="3846575436967432996">No hay información de red disponible.</translation>
@@ -189,6 +193,7 @@
 <translation id="4065525899979931964">{NUM_APPS,plural, =1{Desactivadas para una app}other{Desactivadas para # apps}}</translation>
 <translation id="4072264167173457037">Señal media</translation>
 <translation id="4146833061457621061">Reproducir música</translation>
+<translation id="4181841719683918333">Idiomas</translation>
 <translation id="4217571870635786043">Dictado</translation>
 <translation id="4261870227682513959">Mostrar la configuración de las notificaciones: Están desactivadas</translation>
 <translation id="4269883910223712419">El administrador de este dispositivo puede hacer lo siguiente:</translation>
@@ -196,12 +201,14 @@
 <translation id="4279490309300973883">Duplicando</translation>
 <translation id="4292681942966152062">Activando la red <ph name="NETWORK_NAME" /></translation>
 <translation id="4321179778687042513">ctrl</translation>
+<translation id="4321776623976362024">Presionaste la combinación de teclas para activar el modo de contraste alto. ¿Quieres activarlo?</translation>
 <translation id="4331809312908958774">SO Chrome</translation>
 <translation id="4338109981321384717">Lupa</translation>
 <translation id="4351433414020964307">El Asistente se está cargando…</translation>
 <translation id="435527878592612277">Seleccionar tu foto</translation>
 <translation id="4378551569595875038">Conectando…</translation>
 <translation id="4379531060876907730">Estas son las herramientas de la pluma stylus</translation>
+<translation id="4389184120735010762">Presionaste la combinación de teclas para activar la lupa con vista acoplada. ¿Quieres activarla?</translation>
 <translation id="4421231901400348175">Se está compartiendo el control de la pantalla con <ph name="HELPER_NAME" /> mediante la Asistencia remota.</translation>
 <translation id="4430019312045809116">Volumen</translation>
 <translation id="4450893287417543264">No volver a mostrar</translation>
@@ -217,7 +224,6 @@
 <translation id="4628757576491864469">Dispositivos</translation>
 <translation id="4659419629803378708">Se habilitó ChromeVox</translation>
 <translation id="4734965478015604180">Horizontal</translation>
-<translation id="476166673298332917">El administrador de este dispositivo tiene acceso a toda la actividad, incluidas las contraseñas y comunicaciones.</translation>
 <translation id="4774338217796918551">Regresa mañana a las <ph name="COME_BACK_TIME" />.</translation>
 <translation id="4776917500594043016">Contraseña para <ph name="USER_EMAIL_ADDRESS" /></translation>
 <translation id="4778095205580009397">El Asistente de Google no está disponible en la sesión de demostración.</translation>
@@ -240,6 +246,7 @@
 <translation id="5207949376430453814">Destacar el cursor de texto</translation>
 <translation id="5222676887888702881">Salir</translation>
 <translation id="523505283826916779">Configuración de accesibilidad</translation>
+<translation id="5302048478445481009">Idioma</translation>
 <translation id="5313326810920013265">Configuración de Bluetooth</translation>
 <translation id="5331975486040154427">Dispositivo USB-C (puerto lateral izquierdo trasero)</translation>
 <translation id="5379115545237091094">Realizaste demasiados intentos</translation>
@@ -391,6 +398,7 @@
 <translation id="7886169021410746335">Ajustar la configuración de privacidad</translation>
 <translation id="7886277072580235377">Cuando salgas, se borrarán los datos de tu sesión de Internet. <ph name="LEARN_MORE" /></translation>
 <translation id="788781083998633524">Enviar un correo electrónico</translation>
+<translation id="7897375687985782769">Presionaste la combinación de teclas para girar la pantalla. ¿Quieres llevar a cabo esta acción?</translation>
 <translation id="7904094684485781019">El administrador de esta cuenta inhabilitó el acceso múltiple.</translation>
 <translation id="7933084174919150729">El Asistente de Google solo está disponible para el perfil principal.</translation>
 <translation id="79341161159229895">Cuenta administrada por <ph name="FIRST_PARENT_EMAIL" /> y <ph name="SECOND_PARENT_EMAIL" /></translation>
@@ -456,6 +464,7 @@
 <translation id="8921624153894383499">El Asistente de Google no habla este idioma</translation>
 <translation id="8938800817013097409">Dispositivo USB-C (puerto derecho en la parte posterior)</translation>
 <translation id="8940956008527784070">Batería baja (<ph name="PERCENTAGE" />%)</translation>
+<translation id="8990809378771970590"><ph name="IME_NAME" /> en uso</translation>
 <translation id="899350903320462459">Desbloquea el dispositivo como <ph name="LOGIN_ID" /> para realizar la acción con las notificaciones</translation>
 <translation id="8995603266996330174">Administrado por <ph name="DOMAIN" /></translation>
 <translation id="9029474291399787231">La actualización de Adobe Flash Player está disponible</translation>
diff --git a/ash/strings/ash_strings_es.xtb b/ash/strings/ash_strings_es.xtb
index ee2e97b..804b842 100644
--- a/ash/strings/ash_strings_es.xtb
+++ b/ash/strings/ash_strings_es.xtb
@@ -81,6 +81,7 @@
 <translation id="2268130516524549846">Bluetooth inhabilitado</translation>
 <translation id="2268813581635650749">Cerrar todas las sesiones</translation>
 <translation id="2292698582925480719">Escala de pantalla</translation>
+<translation id="2302092602801625023">Esta cuenta se gestiona con Family Link</translation>
 <translation id="2303600792989757991">Activar descripción general de ventanas</translation>
 <translation id="2339073806695260576">Toca el botón del lápiz óptico situado en la estantería para tomar una nota, hacer una captura de pantalla o utilizar el puntero láser o la lupa.</translation>
 <translation id="2341729377289034582">Bloqueada en vertical</translation>
@@ -140,7 +141,9 @@
 <translation id="315116470104423982">Redes móviles</translation>
 <translation id="3151786313568798007">Orientación</translation>
 <translation id="3153444934357957346">Solo puedes tener un máximo de <ph name="MULTI_PROFILE_USER_LIMIT" /> cuentas en el inicio de sesión múltiple.</translation>
+<translation id="3154351730702813399">Es posible que el administrador del dispositivo supervise tu actividad de navegación.</translation>
 <translation id="3202010236269062730">{NUM_DEVICES,plural, =1{Conectado a 1 dispositivo}other{Conectado a # dispositivos}}</translation>
+<translation id="32244657276328332">Sin conexión a la red</translation>
 <translation id="3236488194889173876">No hay ninguna red móvil disponible</translation>
 <translation id="3255483164551725916">¿Qué puedes hacer?</translation>
 <translation id="3294437725009624529">Invitado</translation>
@@ -172,6 +175,7 @@
 <translation id="3783640748446814672">alt</translation>
 <translation id="3784455785234192852">Bloquear</translation>
 <translation id="3798670284305777884">Altavoz (interno)</translation>
+<translation id="3799080171973636491">Has pulsado la combinación de teclas que activa la lupa de pantalla completa. ¿Quieres activarla?</translation>
 <translation id="380165613292957338">Hola, ¿cómo puedo ayudarte?</translation>
 <translation id="383629559565718788">Mostrar configuración de teclado</translation>
 <translation id="3846575436967432996">No hay información de red disponible.</translation>
@@ -189,6 +193,7 @@
 <translation id="4065525899979931964">{NUM_APPS,plural, =1{Desactivadas para una aplicación}other{Desactivadas para # aplicaciones}}</translation>
 <translation id="4072264167173457037">Señal media</translation>
 <translation id="4146833061457621061">Pon música</translation>
+<translation id="4181841719683918333">Idiomas</translation>
 <translation id="4217571870635786043">Dictado</translation>
 <translation id="4261870227682513959">Muestra la configuración de notificaciones. Las notificaciones están desactivadas.</translation>
 <translation id="4269883910223712419">El administrador de este dispositivo puede hacer lo siguiente:</translation>
@@ -196,12 +201,14 @@
 <translation id="4279490309300973883">Duplicando</translation>
 <translation id="4292681942966152062">Activando <ph name="NETWORK_NAME" /></translation>
 <translation id="4321179778687042513">ctrl</translation>
+<translation id="4321776623976362024">Has pulsado la combinación de teclas que activa el contraste alto. ¿Quieres activarlo?</translation>
 <translation id="4331809312908958774">Chrome OS</translation>
 <translation id="4338109981321384717">Lupa</translation>
 <translation id="4351433414020964307">El Asistente se está cargando...</translation>
 <translation id="435527878592612277">Selecciona una foto</translation>
 <translation id="4378551569595875038">Conectando...</translation>
 <translation id="4379531060876907730">Estas son las herramientas del lápiz óptico</translation>
+<translation id="4389184120735010762">Has pulsado la combinación de teclas que activa la lupa fijada. ¿Quieres activarla?</translation>
 <translation id="4421231901400348175">Compartiendo control de la pantalla con <ph name="HELPER_NAME" /> a través de Asistencia remota.</translation>
 <translation id="4430019312045809116">Volumen</translation>
 <translation id="4450893287417543264">No volver a mostrar</translation>
@@ -217,7 +224,6 @@
 <translation id="4628757576491864469">Dispositivos</translation>
 <translation id="4659419629803378708">ChromeVox habilitado</translation>
 <translation id="4734965478015604180">Horizontal</translation>
-<translation id="476166673298332917">El administrador de este dispositivo tiene acceso a toda la actividad, incluidas las contraseñas y las comunicaciones.</translation>
 <translation id="4774338217796918551">Vuelve mañana a las <ph name="COME_BACK_TIME" />.</translation>
 <translation id="4776917500594043016">Contraseña de <ph name="USER_EMAIL_ADDRESS" /></translation>
 <translation id="4778095205580009397">El Asistente de Google no está disponible en las sesiones de demostración.</translation>
@@ -240,6 +246,7 @@
 <translation id="5207949376430453814">Resaltar el símbolo de intercalación del texto</translation>
 <translation id="5222676887888702881">Cerrar sesión</translation>
 <translation id="523505283826916779">Configuración de accesibilidad</translation>
+<translation id="5302048478445481009">Idioma</translation>
 <translation id="5313326810920013265">Configuración de Bluetooth</translation>
 <translation id="5331975486040154427">Dispositivo USB tipo C (puerto trasero izquierdo)</translation>
 <translation id="5379115545237091094">Demasiados intentos</translation>
@@ -391,6 +398,7 @@
 <translation id="7886169021410746335">Ajustar la configuración de privacidad</translation>
 <translation id="7886277072580235377">Tu sesión de Internet se borrará cuando la cierres. <ph name="LEARN_MORE" /></translation>
 <translation id="788781083998633524">Enviar un correo electrónico</translation>
+<translation id="7897375687985782769">Has pulsado la combinación de teclas que gira la pantalla. ¿Quieres girar la pantalla?</translation>
 <translation id="7904094684485781019">El administrador de esta cuenta ha inhabilitado el inicio de sesión múltiple.</translation>
 <translation id="7933084174919150729">El Asistente de Google solo está disponible en el perfil principal.</translation>
 <translation id="79341161159229895">Cuenta gestionada por <ph name="FIRST_PARENT_EMAIL" /> y <ph name="SECOND_PARENT_EMAIL" /></translation>
@@ -456,6 +464,7 @@
 <translation id="8921624153894383499">El Asistente de Google no habla este idioma.</translation>
 <translation id="8938800817013097409">Dispositivo USB tipo C (puerto derecho situado en la parte trasera)</translation>
 <translation id="8940956008527784070">Poca batería (<ph name="PERCENTAGE" />%)</translation>
+<translation id="8990809378771970590">Estás usando <ph name="IME_NAME" /></translation>
 <translation id="899350903320462459">Desbloquea el dispositivo como <ph name="LOGIN_ID" /> para hacer lo que indica la notificación</translation>
 <translation id="8995603266996330174">Administrado por <ph name="DOMAIN" /></translation>
 <translation id="9029474291399787231">Actualización de Adobe Flash Player disponible</translation>
diff --git a/ash/strings/ash_strings_et.xtb b/ash/strings/ash_strings_et.xtb
index c5dd3bbe..157b056 100644
--- a/ash/strings/ash_strings_et.xtb
+++ b/ash/strings/ash_strings_et.xtb
@@ -81,6 +81,7 @@
 <translation id="2268130516524549846">Bluetooth on keelatud</translation>
 <translation id="2268813581635650749">Logi kõik kasutajad välja</translation>
 <translation id="2292698582925480719">Ekraani skaala</translation>
+<translation id="2302092602801625023">Kontot haldab Family Link</translation>
 <translation id="2303600792989757991">Akna ülevaate sisse- ja väljalülitamine</translation>
 <translation id="2339073806695260576">Puudutage riiulil elektronpliiatsi nuppu, et teha märkmeid, jäädvustada ekraanipilt, kasutada laserkursorit või suurendusklaasi.</translation>
 <translation id="2341729377289034582">Lukustatud vertikaalasendisse</translation>
@@ -140,7 +141,9 @@
 <translation id="315116470104423982">Mobiilne andmeside</translation>
 <translation id="3151786313568798007">Suund</translation>
 <translation id="3153444934357957346">Saate korraga sisse logida kuni <ph name="MULTI_PROFILE_USER_LIMIT" /> kontole.</translation>
+<translation id="3154351730702813399">Seadme administraator võib jälgida teie sirvimistegevust.</translation>
 <translation id="3202010236269062730">{NUM_DEVICES,plural, =1{Ühendatud ühe seadmega}other{Ühendatud # seadmega}}</translation>
+<translation id="32244657276328332">Võrguga ühendamata</translation>
 <translation id="3236488194889173876">Ühtegi mobiilsidevõrku pole saadaval</translation>
 <translation id="3255483164551725916">Mida sa teha oskad?</translation>
 <translation id="3294437725009624529">Külaline</translation>
@@ -172,6 +175,7 @@
 <translation id="3783640748446814672">alt</translation>
 <translation id="3784455785234192852">Lukusta</translation>
 <translation id="3798670284305777884">Kõlar (sisemine)</translation>
+<translation id="3799080171973636491">Vajutasite täisekraani luubi otseteed. Kas soovite luubi sisse lülitada?</translation>
 <translation id="380165613292957338">Tere! Kuidas saan aidata?</translation>
 <translation id="383629559565718788">Kuvab klaviatuuriseaded</translation>
 <translation id="3846575436967432996">Võrguteave ei ole saadaval</translation>
@@ -189,6 +193,7 @@
 <translation id="4065525899979931964">{NUM_APPS,plural, =1{Rakenduse puhul välja lülitatud}other{# rakenduse puhul välja lülitatud}}</translation>
 <translation id="4072264167173457037">Keskmine signaal</translation>
 <translation id="4146833061457621061">Esita muusikat</translation>
+<translation id="4181841719683918333">Keeled</translation>
 <translation id="4217571870635786043">Dikteerimine</translation>
 <translation id="4261870227682513959">Kuvab märguandeseaded. Märguanded on välja lülitatud</translation>
 <translation id="4269883910223712419">Selle seadme administraator saab teha järgmist.</translation>
@@ -196,12 +201,14 @@
 <translation id="4279490309300973883">Peegeldamine</translation>
 <translation id="4292681942966152062">Võrgu <ph name="NETWORK_NAME" /> aktiveerimine</translation>
 <translation id="4321179778687042513">ctrl</translation>
+<translation id="4321776623976362024">Vajutasite suure kontrastsusega režiimi otseteed. Kas soovite režiimi sisse lülitada?</translation>
 <translation id="4331809312908958774">Chrome OS</translation>
 <translation id="4338109981321384717">Suurendusklaas</translation>
 <translation id="4351433414020964307">Assistenti laaditakse …</translation>
 <translation id="435527878592612277">Valige foto</translation>
 <translation id="4378551569595875038">Ühendamine ...</translation>
 <translation id="4379531060876907730">Need on teie elektronpliiatsi tööriistad</translation>
+<translation id="4389184120735010762">Vajutasite dokitud luubi otseteed. Kas soovite luubi sisse lülitada?</translation>
 <translation id="4421231901400348175">Ekraani juhtimise jagamine isikuga <ph name="HELPER_NAME" /> kaugabi kaudu.</translation>
 <translation id="4430019312045809116">Helitugevus</translation>
 <translation id="4450893287417543264">Ära kuva uuesti</translation>
@@ -217,7 +224,6 @@
 <translation id="4628757576491864469">Seadmed</translation>
 <translation id="4659419629803378708">ChromeVox on lubatud</translation>
 <translation id="4734965478015604180">Horisontaalne</translation>
-<translation id="476166673298332917">Seadme aministraatoril on juurdepääs kõigile tegevustele, sh paroolidele ja sidele.</translation>
 <translation id="4774338217796918551">Tulge tagasi homme kell <ph name="COME_BACK_TIME" />.</translation>
 <translation id="4776917500594043016">Kasutaja <ph name="USER_EMAIL_ADDRESS" /> parool</translation>
 <translation id="4778095205580009397">Google'i assistent ei ole demoseansi puhul saadaval.</translation>
@@ -240,6 +246,7 @@
 <translation id="5207949376430453814">Tekstisisestusmärgi esiletõstmine</translation>
 <translation id="5222676887888702881">Logi välja</translation>
 <translation id="523505283826916779">Juurdepääsuseaded</translation>
+<translation id="5302048478445481009">Keel</translation>
 <translation id="5313326810920013265">Bluetoothi seaded</translation>
 <translation id="5331975486040154427">C-tüüpi USB-seade (tagumine vasakpoolne port)</translation>
 <translation id="5379115545237091094">Liiga palju katseid</translation>
@@ -391,6 +398,7 @@
 <translation id="7886169021410746335">Privaatsusseadete kohandamine</translation>
 <translation id="7886277072580235377">Teie Interneti-seanss kustutatakse väljalogimisel. <ph name="LEARN_MORE" /></translation>
 <translation id="788781083998633524">Saada meil</translation>
+<translation id="7897375687985782769">Vajutasite ekraanikuva pööramise otseteed. Kas soovite ekraanikuva pöörata?</translation>
 <translation id="7904094684485781019">Selle konto administraator on mitmesse kontosse sisselogimise keelanud.</translation>
 <translation id="7933084174919150729">Google'i assistent on saadaval ainult peamisel profiilil.</translation>
 <translation id="79341161159229895">Kontohaldurid: <ph name="FIRST_PARENT_EMAIL" /> ja <ph name="SECOND_PARENT_EMAIL" /></translation>
@@ -456,6 +464,7 @@
 <translation id="8921624153894383499">Google'i assistent ei räägi selles keeles.</translation>
 <translation id="8938800817013097409">C-tüüpi USB-seade (parempoolne port taga)</translation>
 <translation id="8940956008527784070">Aku tühjeneb (<ph name="PERCENTAGE" />%)</translation>
+<translation id="8990809378771970590">Kasutusel: <ph name="IME_NAME" /></translation>
 <translation id="899350903320462459">Märguandetoimingu tegemiseks avage seade kontoga <ph name="LOGIN_ID" /></translation>
 <translation id="8995603266996330174">Haldaja: <ph name="DOMAIN" /></translation>
 <translation id="9029474291399787231">Saadaval on Adobe Flash Playeri värskendus</translation>
diff --git a/ash/strings/ash_strings_fa.xtb b/ash/strings/ash_strings_fa.xtb
index 709ddc3..ea3149a 100644
--- a/ash/strings/ash_strings_fa.xtb
+++ b/ash/strings/ash_strings_fa.xtb
@@ -81,6 +81,7 @@
 <translation id="2268130516524549846">بلوتوث غیرفعال است</translation>
 <translation id="2268813581635650749">خروج همه از سیستم</translation>
 <translation id="2292698582925480719">مقیاس نمایش</translation>
+<translation id="2302092602801625023">‏این حساب با Family Link مدیریت می‌شود</translation>
 <translation id="2303600792989757991">مروری کلی بر پنجره تغییر حالت</translation>
 <translation id="2339073806695260576">برای یادداشت‌برداری، گرفتن عکس صفحه‌نمایش، استفاده از اشاره‌گر لیزری یا ذره‌بین روی دکمه قلم در قفسه ضربه بزنید.</translation>
 <translation id="2341729377289034582">قفل درحالت عمودی</translation>
@@ -140,7 +141,9 @@
 <translation id="315116470104423982">داده‌های تلفن همراه</translation>
 <translation id="3151786313568798007">جهت‌‌</translation>
 <translation id="3153444934357957346">در ورود چندگانه به سیستم، می‌توانید حداکثر از <ph name="MULTI_PROFILE_USER_LIMIT" /> حساب استفاده کنید.</translation>
+<translation id="3154351730702813399">سرپرست دستگاه ممکن است فعالیت مرور شما را پایش کند</translation>
 <translation id="3202010236269062730">{NUM_DEVICES,plural, =1{متصل به یک دستگاه.}one{متصل به # دستگاه}other{متصل به # دستگاه}}</translation>
+<translation id="32244657276328332">به شبکه متصل نیست</translation>
 <translation id="3236488194889173876">هیچ شبکه تلفن همراهی دردسترس نیست</translation>
 <translation id="3255483164551725916">‏What can you do?‎ (چه کاری می‌توانی انجام دهی؟)</translation>
 <translation id="3294437725009624529">مهمان</translation>
@@ -171,6 +174,7 @@
 <translation id="3783640748446814672">دگرساز</translation>
 <translation id="3784455785234192852">قفل</translation>
 <translation id="3798670284305777884">بلندگو (داخلی)</translation>
+<translation id="3799080171973636491">میان‌بر صفحه‌کلید مربوط به ذره‌بین تمام‌صفحه را فشار دادید. می‌خواهید آن را روشن کنید؟</translation>
 <translation id="380165613292957338">سلام، چه کمکی می‌توانم بکنم؟</translation>
 <translation id="383629559565718788">نمایش تنظیمات صفحه‌کلید</translation>
 <translation id="3846575436967432996">اطلاعات شبکه در دسترس نیست</translation>
@@ -188,6 +192,7 @@
 <translation id="4065525899979931964">{NUM_APPS,plural, =1{خاموش برای یک برنامه}one{خاموش برای # برنامه}other{خاموش برای # برنامه}}</translation>
 <translation id="4072264167173457037">سیگنال متوسط</translation>
 <translation id="4146833061457621061">‏Play music (موسیقی پخش کن)</translation>
+<translation id="4181841719683918333">زبان‌ها</translation>
 <translation id="4217571870635786043">املا</translation>
 <translation id="4261870227682513959">نمایش تنظیمات اعلان. اعلان‌ها خاموش است</translation>
 <translation id="4269883910223712419">سرپرست این دستگاه می‌تواند:</translation>
@@ -195,6 +200,7 @@
 <translation id="4279490309300973883">بازتاب می‌شود</translation>
 <translation id="4292681942966152062">درحال فعال کردن <ph name="NETWORK_NAME" /></translation>
 <translation id="4321179778687042513">مهار</translation>
+<translation id="4321776623976362024">میان‌بر صفحه‌کلید مربوط به کنتراست بالا را فشار دادید. می‌خواهید آن را روشن کنید؟</translation>
 <translation id="4331809312908958774">Chrome OS
 </translation>
 <translation id="4338109981321384717">ذره‌بین</translation>
@@ -202,6 +208,7 @@
 <translation id="435527878592612277">انتخاب عکس</translation>
 <translation id="4378551569595875038">در حال اتصال...</translation>
 <translation id="4379531060876907730">این‌ها ابزارهای قلم شما هستند</translation>
+<translation id="4389184120735010762">میان‌بر صفحه‌کلید مربوط به ذره‌بین متصل را فشار دادید. می‌خواهید آن را روشن کنید؟</translation>
 <translation id="4421231901400348175">هم‌رسانی کنترل صفحه‌تان با <ph name="HELPER_NAME" /> از طریق راهنمایی ازراه‌دور.</translation>
 <translation id="4430019312045809116">میزان صدا</translation>
 <translation id="4450893287417543264">دیگر نشان داده نشود</translation>
@@ -217,7 +224,6 @@
 <translation id="4628757576491864469">دستگاه‌ها</translation>
 <translation id="4659419629803378708">‏ChromeVox فعال شد</translation>
 <translation id="4734965478015604180">افقی</translation>
-<translation id="476166673298332917">سرپرست این سیستم به همه فعالیت‌ها، ازجمله گذرواژه‌ها و ارتباطات دسترسی دارد.</translation>
 <translation id="4774338217796918551">فردا ساعت <ph name="COME_BACK_TIME" /> برگردید.</translation>
 <translation id="4776917500594043016">گذرواژه <ph name="USER_EMAIL_ADDRESS" /></translation>
 <translation id="4778095205580009397">‏«دستیار Google» در جلسه نمایشی در دسترس نیست.</translation>
@@ -240,6 +246,7 @@
 <translation id="5207949376430453814">برجسته کردن هشتک نوشتار</translation>
 <translation id="5222676887888702881">خروج از سیستم</translation>
 <translation id="523505283826916779">تنظیمات دسترس‌پذیری</translation>
+<translation id="5302048478445481009">زبان</translation>
 <translation id="5313326810920013265">تنظیمات بلوتوث</translation>
 <translation id="5331975486040154427">‏دستگاه USB-C (درگاه عقب سمت چپ)</translation>
 <translation id="5379115545237091094">تلاش‌های ناموفق زیادی انجام شده است</translation>
@@ -391,6 +398,7 @@
 <translation id="7886169021410746335">تنظیمات حریم خصوصی را تنظیم کند</translation>
 <translation id="7886277072580235377">وقتی از سیستم خارج شوید، جلسه اینترنت پاک خواهد شد. <ph name="LEARN_MORE" /></translation>
 <translation id="788781083998633524">‏Send an email (ایمیل ارسال کن)</translation>
+<translation id="7897375687985782769">میان‌بر صفحه‌کلید مربوط به چرخش صفحه را فشار دادید. می‌خواهید صفحه را بچرخانید؟</translation>
 <translation id="7904094684485781019">سرپرست این حساب اجازه ورود چندگانه به سیستم را نمی‌دهد.</translation>
 <translation id="7933084174919150729">‏«دستیار Google» فقط برای نمایه اصلی دردسترس است.</translation>
 <translation id="79341161159229895">حساب تحت مدیریت <ph name="FIRST_PARENT_EMAIL" /> و <ph name="SECOND_PARENT_EMAIL" /> است</translation>
@@ -456,6 +464,7 @@
 <translation id="8921624153894383499">‏«دستیار Google» به این زبان صحبت نمی‌کند.</translation>
 <translation id="8938800817013097409">‏دستگاه USB-C (درگاه عقب سمت راست)</translation>
 <translation id="8940956008527784070">باتری ضعیف است (<ph name="PERCENTAGE" />٪)</translation>
+<translation id="8990809378771970590">درحال استفاده از <ph name="IME_NAME" /></translation>
 <translation id="899350903320462459">به‌عنوان <ph name="LOGIN_ID" />، قفل دستگاه را باز کنید تا کنش اعلان اجرا شود</translation>
 <translation id="8995603266996330174">مدیریت شده توسط <ph name="DOMAIN" /></translation>
 <translation id="9029474291399787231">‏به‌روزرسانی Adobe Flash Player دردسترس است</translation>
diff --git a/ash/strings/ash_strings_fi.xtb b/ash/strings/ash_strings_fi.xtb
index 0d14038..7152630 100644
--- a/ash/strings/ash_strings_fi.xtb
+++ b/ash/strings/ash_strings_fi.xtb
@@ -81,6 +81,7 @@
 <translation id="2268130516524549846">Bluetooth pois käytöstä</translation>
 <translation id="2268813581635650749">Kirjaa kaikki ulos</translation>
 <translation id="2292698582925480719">Näytön skaalaus</translation>
+<translation id="2302092602801625023">Tiliä ylläpidetään Family Linkissä</translation>
 <translation id="2303600792989757991">Vaihda ikkunan yleiskatsausta</translation>
 <translation id="2339073806695260576">Napauttamalla hyllyn näyttökynäpainiketta voit tehdä muistiinpanoja, tallentaa kuvakaappauksen ja käyttää laserosoitinta tai suurennuslasia.</translation>
 <translation id="2341729377289034582">Lukittu pystysuuntaiseksi</translation>
@@ -140,7 +141,9 @@
 <translation id="315116470104423982">Mobiilitiedonsiirto</translation>
 <translation id="3151786313568798007">Suunta</translation>
 <translation id="3153444934357957346">Voit lisätä enintään <ph name="MULTI_PROFILE_USER_LIMIT" /> tiliä useille tileille kirjautumista varten.</translation>
+<translation id="3154351730702813399">Laitteen järjestelmänvalvoja saattaa seurata selaustoimintaasi.</translation>
 <translation id="3202010236269062730">{NUM_DEVICES,plural, =1{Yhdistetty 1 laitteeseen}other{Yhdistetty # laitteeseen}}</translation>
+<translation id="32244657276328332">Ei verkkoyhteyttä</translation>
 <translation id="3236488194889173876">Mobiiliverkkoja ei ole käytettävissä</translation>
 <translation id="3255483164551725916">Mitä osaat tehdä?</translation>
 <translation id="3294437725009624529">Vieras</translation>
@@ -171,6 +174,7 @@
 <translation id="3783640748446814672">alt</translation>
 <translation id="3784455785234192852">Lukitse</translation>
 <translation id="3798670284305777884">Kaiutin (sisäinen)</translation>
+<translation id="3799080171973636491">Painoit koko näytön suurennuksen pikanäppäintä. Haluatko ottaa sen käyttöön?</translation>
 <translation id="380165613292957338">Hei. Kuinka voin auttaa?</translation>
 <translation id="383629559565718788">Näytä näppäimistöasetukset</translation>
 <translation id="3846575436967432996">Verkon tietoja ei saatavilla</translation>
@@ -188,6 +192,7 @@
 <translation id="4065525899979931964">{NUM_APPS,plural, =1{Pois käytöstä sovelluksessa}other{Pois käytöstä # sovelluksessa}}</translation>
 <translation id="4072264167173457037">Keskitasoinen signaali</translation>
 <translation id="4146833061457621061">Soita musiikkia</translation>
+<translation id="4181841719683918333">Kielet</translation>
 <translation id="4217571870635786043">Sanelu</translation>
 <translation id="4261870227682513959">Näytä ilmoitusasetukset. Ilmoitukset on poistettu käytöstä.</translation>
 <translation id="4269883910223712419">Tämän laitteen ylläpitäjä voi</translation>
@@ -195,6 +200,7 @@
 <translation id="4279490309300973883">Peilaus päällä</translation>
 <translation id="4292681942966152062">Aktivoidaan <ph name="NETWORK_NAME" /></translation>
 <translation id="4321179778687042513">ctrl</translation>
+<translation id="4321776623976362024">Painoit suuren kontrastin pikanäppäintä. Haluatko ottaa sen käyttöön?</translation>
 <translation id="4331809312908958774">Chrome OS
 </translation>
 <translation id="4338109981321384717">Suurennuslasi</translation>
@@ -202,6 +208,7 @@
 <translation id="435527878592612277">Valitse valokuva</translation>
 <translation id="4378551569595875038">Yhdistetään…</translation>
 <translation id="4379531060876907730">Tässä ovat näyttökynätyökalusi</translation>
+<translation id="4389184120735010762">Painoit kiinnitetyn suurennuksen pikanäppäintä. Haluatko ottaa sen käyttöön?</translation>
 <translation id="4421231901400348175">Näyttösi hallinta jaetaan käyttäjän <ph name="HELPER_NAME" /> kanssa Etätuen kautta.</translation>
 <translation id="4430019312045809116">Äänenvoimakkuus</translation>
 <translation id="4450893287417543264">Älä näytä uudelleen</translation>
@@ -217,7 +224,6 @@
 <translation id="4628757576491864469">Laitteet</translation>
 <translation id="4659419629803378708">ChromeVox käytössä</translation>
 <translation id="4734965478015604180">Vaakasuora</translation>
-<translation id="476166673298332917">Tämän laitteen järjestelmänvalvojalla on pääsy kaikkeen toimintaan, mukaan lukien salasanoihin ja viestintään.</translation>
 <translation id="4774338217796918551">Palaa huomenna kello <ph name="COME_BACK_TIME" />.</translation>
 <translation id="4776917500594043016">Osoitteen <ph name="USER_EMAIL_ADDRESS" /> salasana</translation>
 <translation id="4778095205580009397">Google Assistantia ei voi käyttää demo-käyttökerralla.</translation>
@@ -240,6 +246,7 @@
 <translation id="5207949376430453814">Korosta tekstikursori</translation>
 <translation id="5222676887888702881">Kirjaudu ulos</translation>
 <translation id="523505283826916779">Käytettävyysasetukset</translation>
+<translation id="5302048478445481009">Kieli</translation>
 <translation id="5313326810920013265">Bluetooth-asetukset</translation>
 <translation id="5331975486040154427">C-tyypin USB-laite (vasemman sivun taaimmainen portti)</translation>
 <translation id="5379115545237091094">Liian monta yritystä</translation>
@@ -393,6 +400,7 @@
 <translation id="7886169021410746335">muuttaa tietosuoja-asetuksia</translation>
 <translation id="7886277072580235377">Internetin käyttökertasi tyhjennetään kirjautuessasi ulos. <ph name="LEARN_MORE" /></translation>
 <translation id="788781083998633524">Lähetä sähköposti</translation>
+<translation id="7897375687985782769">Painoit näytön kääntämisen pikanäppäintä. Haluatko kääntää näytön?</translation>
 <translation id="7904094684485781019">Tämän tilin järjestelmänvalvoja on estänyt useisiin tileihin kirjautumisen.</translation>
 <translation id="7933084174919150729">Google Assistant on käytettävissä vain ensisijaisella profiililla.</translation>
 <translation id="79341161159229895"><ph name="FIRST_PARENT_EMAIL" /> ja <ph name="SECOND_PARENT_EMAIL" /> hallinnoivat tiliä</translation>
@@ -458,6 +466,7 @@
 <translation id="8921624153894383499">Google Assistant ei ole käytettävissä tällä kielellä.</translation>
 <translation id="8938800817013097409">C-tyypin USB-laite (oikeanpuoleinen takaportti)</translation>
 <translation id="8940956008527784070">Akku vähissä (<ph name="PERCENTAGE" /> %)</translation>
+<translation id="8990809378771970590"><ph name="IME_NAME" /> käytössä</translation>
 <translation id="899350903320462459">Avaa laitteen lukitus tunnuksella <ph name="LOGIN_ID" />, niin voit käyttää ilmoitustoimintoa</translation>
 <translation id="8995603266996330174">Hallinnoija: <ph name="DOMAIN" /></translation>
 <translation id="9029474291399787231">Adobe Flash Playerin päivitys saatavilla</translation>
diff --git a/ash/strings/ash_strings_fil.xtb b/ash/strings/ash_strings_fil.xtb
index b07daac..e6d263d 100644
--- a/ash/strings/ash_strings_fil.xtb
+++ b/ash/strings/ash_strings_fil.xtb
@@ -81,6 +81,7 @@
 <translation id="2268130516524549846">Hindi pinagana ang Bluetooth</translation>
 <translation id="2268813581635650749">I-sign out ang lahat</translation>
 <translation id="2292698582925480719">Scale ng display</translation>
+<translation id="2302092602801625023">Pinapamahalaan ng Family Link ang account na ito</translation>
 <translation id="2303600792989757991">I-toggle ang window ng pangkalahatang-ideya</translation>
 <translation id="2339073806695260576">I-tap ang button na stylus sa shelf para magsulat ng tala, mag-screenshot, gamitin ang laser pointer, o magnifying glass.</translation>
 <translation id="2341729377289034582">Naka-lock sa vertical</translation>
@@ -140,7 +141,9 @@
 <translation id="315116470104423982">Data sa mobile</translation>
 <translation id="3151786313568798007">Oryentasyon</translation>
 <translation id="3153444934357957346">Maaari ka lang magkaroon ng hanggang <ph name="MULTI_PROFILE_USER_LIMIT" /> (na) account sa maraming pag-sign in.</translation>
+<translation id="3154351730702813399">Maaaring subaybayan ng admin ng device ang iyong aktibidad sa pag-browse.</translation>
 <translation id="3202010236269062730">{NUM_DEVICES,plural, =1{Nakakonekta sa isang device}one{Nakakonekta sa # device}other{Nakakonekta sa # na device}}</translation>
+<translation id="32244657276328332">Hindi nakakonekta sa network</translation>
 <translation id="3236488194889173876">Walang available na mobile network</translation>
 <translation id="3255483164551725916">Ano'ng maaari mong gawin?</translation>
 <translation id="3294437725009624529">Bisita</translation>
@@ -171,6 +174,7 @@
 <translation id="3783640748446814672">alt</translation>
 <translation id="3784455785234192852">I-lock</translation>
 <translation id="3798670284305777884">Speaker (internal)</translation>
+<translation id="3799080171973636491">Napindot mo ang keyboard shortcut para sa full-screen magnifier. Gusto mo ba itong i-on?</translation>
 <translation id="380165613292957338">Kumusta, ano ang maitutulong ko?</translation>
 <translation id="383629559565718788">Ipakita ang mga setting ng keyboard</translation>
 <translation id="3846575436967432996">Walang available na impormasyon sa network</translation>
@@ -188,6 +192,7 @@
 <translation id="4065525899979931964">{NUM_APPS,plural, =1{Naka-off para sa isang app}one{Naka-off para sa # app}other{Naka-off para sa # na app}}</translation>
 <translation id="4072264167173457037">Katamtaman ang signal</translation>
 <translation id="4146833061457621061">Mag-play ng musika</translation>
+<translation id="4181841719683918333">Mga Wika</translation>
 <translation id="4217571870635786043">Pagdidikta</translation>
 <translation id="4261870227682513959">Ipakita ang mga setting ng notification. Naka-off ang mga notification</translation>
 <translation id="4269883910223712419">May kakayahan ang admin ng device na ito na:</translation>
@@ -195,12 +200,14 @@
 <translation id="4279490309300973883">Nagmi-mirror</translation>
 <translation id="4292681942966152062">Ina-activate ang <ph name="NETWORK_NAME" /></translation>
 <translation id="4321179778687042513">ctrl</translation>
+<translation id="4321776623976362024">Napindot mo ang keyboard shortcut para sa mataas na contrast. Gusto mo ba itong i-on?</translation>
 <translation id="4331809312908958774">Chrome OS</translation>
 <translation id="4338109981321384717">Magnifying glass</translation>
 <translation id="4351433414020964307">Naglo-load ang Assistant...</translation>
 <translation id="435527878592612277">Piliin ang iyong larawan</translation>
 <translation id="4378551569595875038">Kumokonekta...</translation>
 <translation id="4379531060876907730">Ito ang iyong mga stylus tool</translation>
+<translation id="4389184120735010762">Napindot mo ang keyboard shortcut para sa naka-dock na magnifier. Gusto mo ba itong i-on?</translation>
 <translation id="4421231901400348175">Pagbabahagi ng kontrol sa iyong screen gamit ang <ph name="HELPER_NAME" /> sa pamamagitan ng Remote Assistance.</translation>
 <translation id="4430019312045809116">Volume</translation>
 <translation id="4450893287417543264">Huwag ipakitang muli</translation>
@@ -216,7 +223,6 @@
 <translation id="4628757576491864469">Mga Device</translation>
 <translation id="4659419629803378708">Na-enable ang ChromeVox</translation>
 <translation id="4734965478015604180">Horizontal</translation>
-<translation id="476166673298332917">May access ang administrator ng device na ito sa lahat ng aktibidad, kabilang ang mga password at komunikasyon.</translation>
 <translation id="4774338217796918551">Bumalik nang <ph name="COME_BACK_TIME" />.</translation>
 <translation id="4776917500594043016">Password para sa <ph name="USER_EMAIL_ADDRESS" /></translation>
 <translation id="4778095205580009397">Hindi available ang Google Assistant sa demo session.</translation>
@@ -239,6 +245,7 @@
 <translation id="5207949376430453814">I-highlight ang text caret</translation>
 <translation id="5222676887888702881">Mag-sign out</translation>
 <translation id="523505283826916779">Mga setting ng accessibility</translation>
+<translation id="5302048478445481009">Wika</translation>
 <translation id="5313326810920013265">Mga setting ng Bluetooth</translation>
 <translation id="5331975486040154427">USB-C device (port sa kaliwang bahagi sa likod)</translation>
 <translation id="5379115545237091094">Masyadong maraming pagtatangka</translation>
@@ -390,6 +397,7 @@
 <translation id="7886169021410746335">Isaayos ang mga setting ng privacy</translation>
 <translation id="7886277072580235377">Maki-clear ang iyong session sa internet kapag nag-sign out ka. <ph name="LEARN_MORE" /></translation>
 <translation id="788781083998633524">Magpadala ng email</translation>
+<translation id="7897375687985782769">Napindot mo ang keyboard shortcut para sa pag-rotate ng screen. Gusto mo bang i-rotate ang screen?</translation>
 <translation id="7904094684485781019">Hindi pinayagan ng administrator para sa account na ito ang multiple na pag-sign in.</translation>
 <translation id="7933084174919150729">Available lang ang Google Assistant para sa pangunahing profile.</translation>
 <translation id="79341161159229895">Pinamamahalaan nina <ph name="FIRST_PARENT_EMAIL" /> at <ph name="SECOND_PARENT_EMAIL" /> ang account</translation>
@@ -455,6 +463,7 @@
 <translation id="8921624153894383499">Hindi nagsasalita ng ganitong wika ang Google Assistant.</translation>
 <translation id="8938800817013097409">USB-C device (kanang port sa likod)</translation>
 <translation id="8940956008527784070">Mahina na ang baterya (<ph name="PERCENTAGE" />%)</translation>
+<translation id="8990809378771970590">Gamit ang <ph name="IME_NAME" /></translation>
 <translation id="899350903320462459">I-unlock ang device bilang <ph name="LOGIN_ID" /> para maisagawa ang pagkilos sa notification</translation>
 <translation id="8995603266996330174">Pinamamahalaan ni <ph name="DOMAIN" /></translation>
 <translation id="9029474291399787231">Available ang update sa Adobe Flash Player</translation>
diff --git a/ash/strings/ash_strings_fr.xtb b/ash/strings/ash_strings_fr.xtb
index 1b9dbd8..cc95e86 100644
--- a/ash/strings/ash_strings_fr.xtb
+++ b/ash/strings/ash_strings_fr.xtb
@@ -81,6 +81,7 @@
 <translation id="2268130516524549846">Bluetooth désactivé</translation>
 <translation id="2268813581635650749">Déconnecter tous les utilisateurs</translation>
 <translation id="2292698582925480719">Échelle d'affichage</translation>
+<translation id="2302092602801625023">Ce compte est géré par Family Link</translation>
 <translation id="2303600792989757991">Activer/Désactiver la vue d'ensemble des fenêtres</translation>
 <translation id="2339073806695260576">Appuyez sur le bouton du stylet situé sur l'étagère pour prendre des notes, effectuer des captures d'écran, et utiliser le pointeur laser ou la loupe.</translation>
 <translation id="2341729377289034582">Verrouillée en position verticale</translation>
@@ -140,7 +141,9 @@
 <translation id="315116470104423982">Données mobiles</translation>
 <translation id="3151786313568798007">Orientation</translation>
 <translation id="3153444934357957346">Vous ne pouvez vous connecter qu'à <ph name="MULTI_PROFILE_USER_LIMIT" /> comptes au maximum dans le cadre de la connexion multicompte.</translation>
+<translation id="3154351730702813399">L'administrateur de cet appareil peut contrôler votre activité de navigation.</translation>
 <translation id="3202010236269062730">{NUM_DEVICES,plural, =1{Connecté à un appareil}one{Connecté à # appareil}other{Connecté à # appareils}}</translation>
+<translation id="32244657276328332">Non connecté à un réseau</translation>
 <translation id="3236488194889173876">Aucun réseau mobile disponible</translation>
 <translation id="3255483164551725916">Que sais-tu faire ?</translation>
 <translation id="3294437725009624529">Invité</translation>
@@ -171,6 +174,7 @@
 <translation id="3783640748446814672">alt</translation>
 <translation id="3784455785234192852">Verrouiller</translation>
 <translation id="3798670284305777884">Haut-parleur (interne)</translation>
+<translation id="3799080171973636491">Vous avez appuyé sur le raccourci clavier de la loupe plein écran. Voulez-vous l'activer ?</translation>
 <translation id="380165613292957338">Bonjour, comment puis-je vous aider ?</translation>