diff --git a/DEPS b/DEPS
index f77f08e6..fa4f820 100644
--- a/DEPS
+++ b/DEPS
@@ -168,11 +168,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'ad21d47cfa8d9adb2145216c5e514d978d649096',
+  'skia_revision': '7cb52cb5e67cd029e5ae7dc68d91eb8488072bf9',
   # 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': 'e8fc00fb9ea3d8e5e4fa7193d45afee764165a0e',
+  'v8_revision': 'a9e0cc38e4ce5d686dab1e1ac8bf5c742d1ce85f',
   # 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.
@@ -180,11 +180,11 @@
   # 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': '867f8c92ed1ac207f1ee99121eb44d48505ccaae',
+  'angle_revision': '3f647b1bf9a476adb1af5037b88ef7403eb73e31',
   # 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': '9da287fd0264892a8997426a2d7d154aae560de4',
+  'swiftshader_revision': '197a5725f2fecd3368433a1fbfc8249480da8955',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -299,7 +299,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.
-  'shaderc_revision': 'c9d5be6b01708bf7d8a8401a7edb98bf3ab82f17',
+  'shaderc_revision': 'e46010f01da9b74f63c77576ae3d91b900431073',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -818,7 +818,7 @@
   },
 
   'src/third_party/breakpad/breakpad':
-    Var('chromium_git') + '/breakpad/breakpad.git' + '@' + '5915ea929c43d61e77b68b64bc91d8709f5c9876',
+    Var('chromium_git') + '/breakpad/breakpad.git' + '@' + '792e6b2143bffd46c93e8f80c10d00d8289b8d73',
 
   'src/third_party/byte_buddy': {
       'packages': [
@@ -892,7 +892,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '79d4f9950680c588d4c3a2cc8595179f3adc90d4',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '9577daf6673d4af851bbd87a2b714f7e03cdb670',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -927,7 +927,7 @@
   },
 
   'src/third_party/ffmpeg':
-    Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + '83304c4e5287d20407dff656545e6dddd73566f8',
+    Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + 'fa04e15e1ad61052e85f42413d4b841d2a496ec0',
 
   'src/third_party/flac':
     Var('chromium_git') + '/chromium/deps/flac.git' + '@' + 'af862024c8c8fa0ae07ced05e89013d881b00596',
@@ -961,7 +961,7 @@
   },
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + 'f34cdc70ca1b4e741a9ea5a906f86d3593623356',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '37dcb894574e94a876c0165c4df96eeba68e6a5a',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1484,7 +1484,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'dd55f3ca8f2ea716ca917a4aaf36f0729fe902b1',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '229035db8cc676bb382f8734f9174df844e6a079',
+    Var('webrtc_git') + '/src.git' + '@' + '2bd2d85f65e83641ec4f6f5537a916eede4d366f',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -1551,7 +1551,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@ed16d1d29e2f891c14764d138dd019b68cf5f96e',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@d565d557945ea450b126f370b1a7b4d33518bf2b',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 371bf1e..edc07c6b 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -1320,6 +1320,9 @@
     r".*chrome[\\\/]android[\\\/]feed[\\\/]dummy[\\\/].*\.java",
 ]
 
+# List of image extensions that are used as resources in chromium.
+_IMAGE_EXTENSIONS = ['.svg', '.png', '.webp']
+
 # These paths contain test data and other known invalid JSON files.
 _KNOWN_INVALID_JSON_FILE_PATTERNS = [
     r'test[\\/]data[\\/]',
@@ -4079,6 +4082,36 @@
         long_text=long_text)]
 
 
+def _CheckNewImagesWarning(input_api, output_api):
+  """
+  Warns authors who add images into the repo to make sure their images are
+  optimized before committing.
+  """
+  images_added = False
+  image_paths = []
+  errors = []
+  filter_lambda = lambda x: input_api.FilterSourceFile(
+    x,
+    black_list=(('(?i).*test', r'.*\/junit\/')
+                + input_api.DEFAULT_BLACK_LIST),
+    white_list=[r'.*\/(drawable|mipmap)' ]
+  )
+  for f in input_api.AffectedFiles(
+      include_deletes=False, file_filter=filter_lambda):
+    local_path = f.LocalPath().lower()
+    if any(local_path.endswith(extension) for extension in _IMAGE_EXTENSIONS):
+      images_added = True
+      image_paths.append(f)
+  if images_added:
+    errors.append(output_api.PresubmitPromptWarning(
+        'It looks like you are trying to commit some images. If these are '
+        'non-test-only images, please make sure to read and apply the tips in '
+        'https://chromium.googlesource.com/chromium/src/+/HEAD/docs/speed/'
+        'binary_size/optimization_advice.md#optimizing-images\nThis check is '
+        'FYI only and will not block your CL on the CQ.', image_paths))
+  return errors
+
+
 def _AndroidSpecificOnUploadChecks(input_api, output_api):
   """Groups upload checks that target android code."""
   results = []
@@ -4091,6 +4124,7 @@
   results.extend(_CheckAndroidTestAnnotationUsage(input_api, output_api))
   results.extend(_CheckAndroidWebkitImports(input_api, output_api))
   results.extend(_CheckAndroidXmlStyle(input_api, output_api, True))
+  results.extend(_CheckNewImagesWarning(input_api, output_api))
   return results
 
 def _AndroidSpecificOnCommitChecks(input_api, output_api):
@@ -4604,7 +4638,6 @@
 
 
 def _CheckTranslationScreenshots(input_api, output_api):
-  PART_FILE_TAG = "part"
   import os
   import sys
   from io import StringIO
@@ -4612,47 +4645,11 @@
   try:
     old_sys_path = sys.path
     sys.path = sys.path + [input_api.os_path.join(
-          input_api.PresubmitLocalPath(), 'tools', 'grit')]
-    import grit.grd_reader
-    import grit.node.message
-    import grit.util
+          input_api.PresubmitLocalPath(), 'tools', 'translation')]
+    from helper import grd_helper
   finally:
     sys.path = old_sys_path
 
-  def _GetGrdMessages(grd_path_or_string, dir_path='.'):
-    """Load the grd file and return a dict of message ids to messages.
-
-    Ignores any nested grdp files pointed by <part> tag.
-    """
-    doc = grit.grd_reader.Parse(grd_path_or_string, dir_path,
-        stop_after=None, first_ids_file=None,
-        debug=False, defines={'_chromium': 1},
-        tags_to_ignore=set([PART_FILE_TAG]))
-    return {
-      msg.attrs['name']:msg for msg in doc.GetChildrenOfType(
-        grit.node.message.MessageNode)
-    }
-
-  def _GetGrdpMessagesFromString(grdp_string):
-    """Parses the contents of a grdp file given in grdp_string.
-
-    grd_reader can't parse grdp files directly. Instead, this creates a
-    temporary directory with a grd file pointing to the grdp file, and loads the
-    grd from there. Any nested grdp files (pointed by <part> tag) are ignored.
-    """
-    WRAPPER = """<?xml version="1.0" encoding="utf-8"?>
-    <grit latest_public_release="1" current_release="1">
-      <release seq="1">
-        <messages>
-          <part file="sub.grdp" />
-        </messages>
-      </release>
-    </grit>
-    """
-    with grit.util.TempDir({'main.grd': WRAPPER,
-                            'sub.grdp': grdp_string}) as temp_dir:
-      return _GetGrdMessages(temp_dir.GetPath('main.grd'), temp_dir.GetPath())
-
   new_or_added_paths = set(f.LocalPath()
       for f in input_api.AffectedFiles()
       if (f.Action() == 'A' or f.Action() == 'M'))
@@ -4713,18 +4710,18 @@
     new_id_to_msg_map = {}
     if file_path.endswith('.grdp'):
       if f.OldContents():
-        old_id_to_msg_map = _GetGrdpMessagesFromString(
+        old_id_to_msg_map = grd_helper.GetGrdpMessagesFromString(
           unicode('\n'.join(f.OldContents())))
       if f.NewContents():
-        new_id_to_msg_map = _GetGrdpMessagesFromString(
+        new_id_to_msg_map = grd_helper.GetGrdpMessagesFromString(
           unicode('\n'.join(f.NewContents())))
     else:
       if f.OldContents():
-        old_id_to_msg_map = _GetGrdMessages(
-          StringIO(unicode('\n'.join(f.OldContents()))))
+        old_id_to_msg_map = grd_helper.GetGrdMessages(
+          StringIO(unicode('\n'.join(f.OldContents()))), '.')
       if f.NewContents():
-        new_id_to_msg_map = _GetGrdMessages(
-          StringIO(unicode('\n'.join(f.NewContents()))))
+        new_id_to_msg_map = grd_helper.GetGrdMessages(
+          StringIO(unicode('\n'.join(f.NewContents()))), '.')
 
     # Compute added, removed and modified message IDs.
     old_ids = set(old_id_to_msg_map)
diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py
index 6455b91d..4d330335 100755
--- a/PRESUBMIT_test.py
+++ b/PRESUBMIT_test.py
@@ -2250,6 +2250,38 @@
     self.assertEqual(0, len(results))
 
 
+class NewImagesWarningTest(unittest.TestCase):
+  def testTruePositives(self):
+    mock_input_api = MockInputApi()
+    mock_input_api.files = [
+      MockFile('dir/android/res/drawable/foo.png', []),
+      MockFile('dir/android/res/drawable-v21/bar.svg', []),
+      MockFile('dir/android/res/mipmap-v21-en/baz.webp', []),
+      MockFile('dir/android/res_gshoe/drawable-mdpi/foobar.png', []),
+    ]
+
+    results = PRESUBMIT._CheckNewImagesWarning(mock_input_api, MockOutputApi())
+    self.assertEqual(1, len(results))
+    self.assertEqual(4, len(results[0].items))
+    self.assertTrue('foo.png' in results[0].items[0].LocalPath())
+    self.assertTrue('bar.svg' in results[0].items[1].LocalPath())
+    self.assertTrue('baz.webp' in results[0].items[2].LocalPath())
+    self.assertTrue('foobar.png' in results[0].items[3].LocalPath())
+
+  def testFalsePositives(self):
+    mock_input_api = MockInputApi()
+    mock_input_api.files = [
+      MockFile('dir/pngs/README.md', []),
+      MockFile('java/test/res/drawable/foo.png', []),
+      MockFile('third_party/blink/foo.png', []),
+      MockFile('dir/third_party/libpng/src/foo.cc', ['foobar']),
+      MockFile('dir/resources.webp/.gitignore', ['foo.png']),
+    ]
+
+    results = PRESUBMIT._CheckNewImagesWarning(mock_input_api, MockOutputApi())
+    self.assertEqual(0, len(results))
+
+
 class CheckUniquePtrTest(unittest.TestCase):
   def testTruePositivesNullptr(self):
     mock_input_api = MockInputApi()
@@ -2395,6 +2427,31 @@
            </grit>
         """.splitlines()
 
+  OLD_GRDP_CONTENTS = (
+    '<?xml version="1.0" encoding="utf-8"?>',
+      '<grit-part>',
+    '</grit-part>'
+  )
+
+  NEW_GRDP_CONTENTS1 = (
+    '<?xml version="1.0" encoding="utf-8"?>',
+      '<grit-part>',
+        '<message name="IDS_PART_TEST1">',
+          'Part string 1',
+        '</message>',
+    '</grit-part>')
+
+  NEW_GRDP_CONTENTS2 = (
+    '<?xml version="1.0" encoding="utf-8"?>',
+      '<grit-part>',
+        '<message name="IDS_PART_TEST1">',
+          'Part string 1',
+        '</message>',
+        '<message name="IDS_PART_TEST2">',
+          'Part string 2',
+      '</message>',
+    '</grit-part>')
+
   DO_NOT_UPLOAD_PNG_MESSAGE = ('Do not include actual screenshots in the '
                                'changelist. Run '
                                'tools/translate/upload_screenshots.py to '
@@ -2416,33 +2473,57 @@
         [x.LocalPath() for x in input_api.AffectedFiles(include_deletes=True)])
     return input_api
 
+  """ CL modified and added messages, but didn't add any screenshots."""
   def testNoScreenshots(self):
-    # CL modified and added messages, but didn't add any screenshots.
+    # No new strings (file contents same). Should not warn.
+    input_api = self.makeInputApi([
+      MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS1,
+                       self.NEW_GRD_CONTENTS1, action='M'),
+      MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS1,
+                       self.NEW_GRDP_CONTENTS1, action='M')])
+    warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
+                                                      MockOutputApi())
+    self.assertEqual(0, len(warnings))
+
+    # Add two new strings. Should have two warnings.
     input_api = self.makeInputApi([
       MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS2,
-                       self.OLD_GRD_CONTENTS, action='M')])
+                       self.NEW_GRD_CONTENTS1, action='M'),
+      MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS2,
+                       self.NEW_GRDP_CONTENTS1, action='M')])
     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
                                                       MockOutputApi())
     self.assertEqual(1, len(warnings))
     self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[0].message)
     self.assertEqual([
-        os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
-        os.path.join('test_grd', 'IDS_TEST2.png.sha1')
-    ], warnings[0].items)
+      os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
+      os.path.join('test_grd', 'IDS_TEST2.png.sha1')],
+                     warnings[0].items)
 
+    # Add four new strings. Should have four warnings.
     input_api = self.makeInputApi([
       MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS2,
-                       self.NEW_GRD_CONTENTS1, action='M')])
+                       self.OLD_GRD_CONTENTS, action='M'),
+      MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS2,
+                       self.OLD_GRDP_CONTENTS, action='M')])
     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
                                                       MockOutputApi())
     self.assertEqual(1, len(warnings))
     self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[0].message)
-    self.assertEqual([os.path.join('test_grd', 'IDS_TEST2.png.sha1')],
-                     warnings[0].items)
+    self.assertEqual([
+        os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
+        os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
+        os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
+        os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
+    ], warnings[0].items)
 
-
-  def testUnnecessaryScreenshots(self):
-    # CL added a single message and added the png file, but not the sha1 file.
+  def testPngAddedSha1NotAdded(self):
+    # CL added one new message in a grd file and added the png file associated
+    # with it, but did not add the corresponding sha1 file. This should warn
+    # twice:
+    # - Once for the added png file (because we don't want developers to upload
+    #   actual images)
+    # - Once for the missing .sha1 file
     input_api = self.makeInputApi([
         MockAffectedFile(
             'test.grd',
@@ -2462,38 +2543,59 @@
     self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png.sha1')],
                      warnings[1].items)
 
-    # CL added two messages, one has a png. Expect two messages:
-    # - One for the unnecessary png.
-    # - Another one for missing .sha1 files.
+    # CL added two messages (one in grd, one in grdp) and added the png files
+    # associated with the messages, but did not add the corresponding sha1
+    # files. This should warn twice:
+    # - Once for the added png files (because we don't want developers to upload
+    #   actual images)
+    # - Once for the missing .sha1 files
     input_api = self.makeInputApi([
+        # Modified files:
         MockAffectedFile(
             'test.grd',
-            self.NEW_GRD_CONTENTS2,
+            self.NEW_GRD_CONTENTS1,
             self.OLD_GRD_CONTENTS,
             action='M'),
         MockAffectedFile(
-            os.path.join('test_grd', 'IDS_TEST1.png'), 'binary', action='A')
+            'part.grdp',
+            self.NEW_GRDP_CONTENTS1,
+            self.OLD_GRDP_CONTENTS,
+            action='M'),
+        # Added files:
+        MockAffectedFile(
+            os.path.join('test_grd', 'IDS_TEST1.png'), 'binary', action='A'),
+        MockAffectedFile(
+            os.path.join('part_grdp', 'IDS_PART_TEST1.png'), 'binary',
+            action='A')
     ])
     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
                                                       MockOutputApi())
     self.assertEqual(2, len(warnings))
     self.assertEqual(self.DO_NOT_UPLOAD_PNG_MESSAGE, warnings[0].message)
-    self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png')],
+    self.assertEqual([os.path.join('part_grdp', 'IDS_PART_TEST1.png'),
+                      os.path.join('test_grd', 'IDS_TEST1.png')],
                      warnings[0].items)
     self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[1].message)
-    self.assertEqual([
-        os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
-        os.path.join('test_grd', 'IDS_TEST2.png.sha1')
-    ], warnings[1].items)
+    self.assertEqual([os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
+                      os.path.join('test_grd', 'IDS_TEST1.png.sha1')],
+                      warnings[1].items)
 
   def testScreenshotsWithSha1(self):
-    # CL added two messages and their corresponding .sha1 files. No warnings.
+    # CL added four messages (two each in a grd and grdp) and their
+    # corresponding .sha1 files. No warnings.
     input_api = self.makeInputApi([
+        # Modified files:
         MockAffectedFile(
             'test.grd',
             self.NEW_GRD_CONTENTS2,
             self.OLD_GRD_CONTENTS,
             action='M'),
+        MockAffectedFile(
+            'part.grdp',
+            self.NEW_GRDP_CONTENTS2,
+            self.OLD_GRDP_CONTENTS,
+            action='M'),
+        # Added files:
         MockFile(
             os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
             'binary',
@@ -2501,61 +2603,106 @@
         MockFile(
             os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
             'binary',
-            action='A')
+            action='A'),
+        MockFile(
+            os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
+            'binary',
+            action='A'),
+        MockFile(
+            os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
+            'binary',
+            action='A'),
     ])
     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
                                                       MockOutputApi())
     self.assertEqual([], warnings)
 
   def testScreenshotsRemovedWithSha1(self):
-    # Swap old contents with new contents, remove IDS_TEST1 and IDS_TEST2. The
-    # sha1 files associated with the messages should also be removed by the CL.
+    # Replace new contents with old contents in grd and grp files, removing
+    # IDS_TEST1, IDS_TEST2, IDS_PART_TEST1 and IDS_PART_TEST2.
+    # Should warn to remove the sha1 files associated with these strings.
     input_api = self.makeInputApi([
+        # Modified files:
         MockAffectedFile(
             'test.grd',
-            self.OLD_GRD_CONTENTS,
-            self.NEW_GRD_CONTENTS2,
+            self.OLD_GRD_CONTENTS, # new_contents
+            self.NEW_GRD_CONTENTS2, # old_contents
             action='M'),
-        MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'), 'binary', ""),
-        MockFile(os.path.join('test_grd', 'IDS_TEST2.png.sha1'), 'binary', "")
+        MockAffectedFile(
+            'part.grdp',
+            self.OLD_GRDP_CONTENTS, # new_contents
+            self.NEW_GRDP_CONTENTS2, # old_contents
+            action='M'),
+        # Unmodified files:
+        MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'), 'binary', ''),
+        MockFile(os.path.join('test_grd', 'IDS_TEST2.png.sha1'), 'binary', ''),
+        MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
+                 'binary', ''),
+        MockFile(os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
+                 'binary', '')
     ])
     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
                                                       MockOutputApi())
     self.assertEqual(1, len(warnings))
     self.assertEqual(self.REMOVE_SIGNATURES_MESSAGE, warnings[0].message)
     self.assertEqual([
+        os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
+        os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
         os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
         os.path.join('test_grd', 'IDS_TEST2.png.sha1')
     ], warnings[0].items)
 
-    # Same as above, but this time one of the .sha1 files is removed.
+    # Same as above, but this time one of the .sha1 files is also removed.
     input_api = self.makeInputApi([
+        # Modified files:
         MockAffectedFile(
             'test.grd',
-            self.OLD_GRD_CONTENTS,
-            self.NEW_GRD_CONTENTS2,
+            self.OLD_GRD_CONTENTS, # new_contents
+            self.NEW_GRD_CONTENTS2, # old_contents
             action='M'),
+        MockAffectedFile(
+            'part.grdp',
+            self.OLD_GRDP_CONTENTS, # new_contents
+            self.NEW_GRDP_CONTENTS2, # old_contents
+            action='M'),
+        # Unmodified files:
         MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'), 'binary', ''),
+        MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
+                 'binary', ''),
+        # Deleted files:
         MockAffectedFile(
             os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
             '',
             'old_contents',
+            action='D'),
+        MockAffectedFile(
+            os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
+            '',
+            'old_contents',
             action='D')
     ])
     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
                                                       MockOutputApi())
     self.assertEqual(1, len(warnings))
     self.assertEqual(self.REMOVE_SIGNATURES_MESSAGE, warnings[0].message)
-    self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png.sha1')],
-                     warnings[0].items)
+    self.assertEqual([os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
+                      os.path.join('test_grd', 'IDS_TEST1.png.sha1')
+                     ], warnings[0].items)
 
-    # Remove both sha1 files. No presubmit warnings.
+    # Remove all sha1 files. There should be no warnings.
     input_api = self.makeInputApi([
+        # Modified files:
         MockAffectedFile(
             'test.grd',
             self.OLD_GRD_CONTENTS,
             self.NEW_GRD_CONTENTS2,
             action='M'),
+        MockAffectedFile(
+            'part.grdp',
+            self.OLD_GRDP_CONTENTS,
+            self.NEW_GRDP_CONTENTS2,
+            action='M'),
+        # Deleted files:
         MockFile(
             os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
             'binary',
@@ -2563,6 +2710,14 @@
         MockFile(
             os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
             'binary',
+            action='D'),
+        MockFile(
+            os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
+            'binary',
+            action='D'),
+        MockFile(
+            os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
+            'binary',
             action='D')
     ])
     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
diff --git a/android_webview/browser/aw_browser_context_unittest.cc b/android_webview/browser/aw_browser_context_unittest.cc
index 2f967b5..802b2ff 100644
--- a/android_webview/browser/aw_browser_context_unittest.cc
+++ b/android_webview/browser/aw_browser_context_unittest.cc
@@ -17,6 +17,12 @@
 
 class AwBrowserContextTest : public testing::Test {
  protected:
+  // Runs before the first test
+  static void SetUpTestSuite() {
+    net::NetworkChangeNotifier::SetFactory(
+        new AwNetworkChangeNotifierFactory());
+  }
+
   void SetUp() override {
     mojo::core::Init();
     test_content_client_initializer_ =
@@ -25,8 +31,6 @@
     AwFeatureListCreator* aw_feature_list_creator = new AwFeatureListCreator();
     aw_feature_list_creator->CreateLocalState();
     browser_process_ = new AwBrowserProcess(aw_feature_list_creator);
-    net::NetworkChangeNotifier::SetFactory(
-        new AwNetworkChangeNotifierFactory());
   }
 
   void TearDown() override {
diff --git a/ash/accelerators/accelerator_controller_impl.cc b/ash/accelerators/accelerator_controller_impl.cc
index bcccb18..01ed19d 100644
--- a/ash/accelerators/accelerator_controller_impl.cc
+++ b/ash/accelerators/accelerator_controller_impl.cc
@@ -68,6 +68,7 @@
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/screen_pinning_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "ash/wm/tablet_mode/tablet_mode_window_manager.h"
 #include "ash/wm/window_cycle_controller.h"
 #include "ash/wm/window_positioning_utils.h"
 #include "ash/wm/window_state.h"
@@ -756,6 +757,12 @@
   accelerators::ToggleMinimized();
 }
 
+void HandleTopWindowMinimizeOnBack() {
+  base::RecordAction(
+      base::UserMetricsAction("Accel_Minimize_Top_Window_On_Back"));
+  WindowState::Get(TabletModeWindowManager::GetTopWindow())->Minimize();
+}
+
 void HandleShowImeMenuBubble() {
   base::RecordAction(UserMetricsAction("Accel_Show_Ime_Menu_Bubble"));
 
@@ -1674,6 +1681,8 @@
       return CanHandleWindowSnap();
     case FOCUS_PIP:
       return !!FindPipWidget();
+    case MINIMIZE_TOP_WINDOW_ON_BACK:
+      return TabletModeWindowManager::ShouldMinimizeTopWindowOnBack();
 
     // The following are always enabled.
     case BRIGHTNESS_DOWN:
@@ -2070,6 +2079,9 @@
     case WINDOW_MINIMIZE:
       HandleWindowMinimize();
       break;
+    case MINIMIZE_TOP_WINDOW_ON_BACK:
+      HandleTopWindowMinimizeOnBack();
+      break;
   }
 }
 
diff --git a/ash/accelerators/accelerator_table_unittest.cc b/ash/accelerators/accelerator_table_unittest.cc
index 5b26aedc..4629935 100644
--- a/ash/accelerators/accelerator_table_unittest.cc
+++ b/ash/accelerators/accelerator_table_unittest.cc
@@ -17,10 +17,10 @@
 namespace {
 
 // The number of non-Search-based accelerators.
-constexpr int kNonSearchAcceleratorsNum = 93;
+constexpr int kNonSearchAcceleratorsNum = 94;
 // The hash of non-Search-based accelerators. See HashAcceleratorData().
 constexpr char kNonSearchAcceleratorsHash[] =
-    "0be65cc67955b2320bef45ab7a9c68bb";
+    "6b85e809b6de996abea7e88c89215d0c";
 
 struct Cmp {
   bool operator()(const AcceleratorData& lhs,
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 09ea973..47be772 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -570,7 +570,7 @@
       <message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_VIRTUAL_KEYBOARD" desc="The label used in the accessibility menu of the system tray to toggle on/off the onscreen keyboard.">
         On-screen keyboard
       </message>
-      <message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SWITCH_ACCESS" desc="The label used in the accessibility menu of the system tray to toggle switch access, a feature that allows the entire device to be controlled with as few as 1 switch or button.">
+      <message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SWITCH_ACCESS" desc="The label used in the accessibility menu of the system tray to toggle Switch Access, a feature that allows the entire device to be controlled with as few as 1 switch or button.">
         Switch Access
       </message>
       <message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_ADDITIONAL_SETTINGS" desc="The sub-header label for additional setting in accessibility menu of system tray.">
@@ -1817,6 +1817,9 @@
       <message name="IDS_ASH_LOGIN_PARENT_ACCESS_NEXT_NUMBER_PROMPT" desc="Accessible prompt read when next access code input field has been focused. Asks user to enter next piece of the access code.">
         Next number
       </message>
+      <message name="IDS_ASH_LOGIN_SIGN_IN_REQUIRED_MESSAGE" desc="Message shown in the login screen when online sign-in is required.">
+        Sign in required
+      </message>
       <message name="IDS_ASH_LOGIN_SMART_CARD_SIGN_IN_MESSAGE" desc="Label for the button shown in the user pod that starts signing in via a smart card.">
         Sign in with smart card
       </message>
diff --git a/ash/ash_strings_grd/IDS_ASH_LOGIN_SIGN_IN_REQUIRED_MESSAGE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_LOGIN_SIGN_IN_REQUIRED_MESSAGE.png.sha1
new file mode 100644
index 0000000..c953213
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_LOGIN_SIGN_IN_REQUIRED_MESSAGE.png.sha1
@@ -0,0 +1 @@
+7cc1daa653d0cccf60f7d238cfe11025431c01a9
\ No newline at end of file
diff --git a/ash/home_screen/drag_window_from_shelf_controller.cc b/ash/home_screen/drag_window_from_shelf_controller.cc
index eb31b8a4..f95ef87 100644
--- a/ash/home_screen/drag_window_from_shelf_controller.cc
+++ b/ash/home_screen/drag_window_from_shelf_controller.cc
@@ -168,16 +168,17 @@
   if (overview_controller->InOverviewSession()) {
     const SplitViewController::SnapPosition snap_position =
         GetSnapPosition(location_in_screen);
-    SplitViewDragIndicators::WindowDraggingState window_dragging_state =
-        SplitViewDragIndicators::ComputeWindowDraggingState(
-            drag_started_,
-            SplitViewDragIndicators::WindowDraggingState::kFromShelf,
-            snap_position);
     OverviewSession* overview_session = overview_controller->overview_session();
-    overview_session->SetSplitViewDragIndicatorsWindowDraggingState(
-        window_dragging_state, location_in_screen);
+    overview_session->UpdateSplitViewDragIndicatorsWindowDraggingStates(
+        Shell::GetPrimaryRootWindow(), /*is_dragging=*/true,
+        SplitViewDragIndicators::WindowDraggingState::kFromShelf,
+        snap_position);
     overview_session->OnWindowDragContinued(
-        window_, gfx::PointF(location_in_screen), window_dragging_state);
+        window_, gfx::PointF(location_in_screen),
+        SplitViewDragIndicators::ComputeWindowDraggingState(
+            /*is_dragging=*/true,
+            SplitViewDragIndicators::WindowDraggingState::kFromShelf,
+            snap_position));
 
     if (snap_position != SplitViewController::NONE) {
       // If the dragged window is in snap preview area, make sure overview is
@@ -323,9 +324,10 @@
     ShowOverviewDuringOrAfterDrag();
 
     OverviewSession* overview_session = overview_controller->overview_session();
-    overview_session->SetSplitViewDragIndicatorsWindowDraggingState(
+    overview_session->UpdateSplitViewDragIndicatorsWindowDraggingStates(
+        Shell::GetPrimaryRootWindow(), /*is_dragging=*/false,
         SplitViewDragIndicators::WindowDraggingState::kNoDrag,
-        location_in_screen);
+        SplitViewController::NONE);
     overview_session->OnWindowDragEnded(
         window_, gfx::PointF(location_in_screen),
         should_drop_window_in_overview,
diff --git a/ash/home_screen/drag_window_from_shelf_controller_unittest.cc b/ash/home_screen/drag_window_from_shelf_controller_unittest.cc
index 3999704..341a13c 100644
--- a/ash/home_screen/drag_window_from_shelf_controller_unittest.cc
+++ b/ash/home_screen/drag_window_from_shelf_controller_unittest.cc
@@ -481,33 +481,25 @@
   OverviewController* overview_controller = Shell::Get()->overview_controller();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   OverviewSession* overview_session = overview_controller->overview_session();
-  SplitViewDragIndicators::WindowDraggingState window_dragging_state =
-      overview_session->split_view_drag_indicators()
-          ->current_window_dragging_state();
+  ASSERT_EQ(1u, overview_session->grid_list().size());
+  SplitViewDragIndicators* drag_indicators =
+      overview_session->grid_list()[0]->split_view_drag_indicators();
   EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kFromShelf,
-            window_dragging_state);
+            drag_indicators->current_window_dragging_state());
 
   Drag(gfx::Point(0, 200), 0.5f, 0.5f);
-  window_dragging_state = overview_session->split_view_drag_indicators()
-                              ->current_window_dragging_state();
   EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kToSnapLeft,
-            window_dragging_state);
+            drag_indicators->current_window_dragging_state());
 
   Drag(gfx::Point(0, 300), 0.5f, 0.5f);
-  window_dragging_state = overview_session->split_view_drag_indicators()
-                              ->current_window_dragging_state();
   EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kFromShelf,
-            window_dragging_state);
+            drag_indicators->current_window_dragging_state());
   Drag(gfx::Point(0, 200), 0.5f, 0.5f);
-  window_dragging_state = overview_session->split_view_drag_indicators()
-                              ->current_window_dragging_state();
   EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kToSnapLeft,
-            window_dragging_state);
+            drag_indicators->current_window_dragging_state());
   Drag(gfx::Point(200, 200), 0.5f, 0.5f);
-  window_dragging_state = overview_session->split_view_drag_indicators()
-                              ->current_window_dragging_state();
   EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kFromShelf,
-            window_dragging_state);
+            drag_indicators->current_window_dragging_state());
 
   EndDrag(shelf_bounds.CenterPoint(),
           /*velocity_y=*/base::nullopt);
diff --git a/ash/login/ui/login_auth_user_view.cc b/ash/login/ui/login_auth_user_view.cc
index 4c38440c..2cc6532 100644
--- a/ash/login/ui/login_auth_user_view.cc
+++ b/ash/login/ui/login_auth_user_view.cc
@@ -50,6 +50,7 @@
 #include "ui/views/border.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/button/md_text_button.h"
+#include "ui/views/controls/highlight_path_generator.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/layout/flex_layout.h"
@@ -80,9 +81,6 @@
 constexpr SkColor kChallengeResponseErrorColor =
     SkColorSetRGB(0xEE, 0x67, 0x5C);
 
-// The color of the online sign-in message text.
-constexpr SkColor kOnlineSignInMessageColor = SkColorSetRGB(0xE6, 0x7C, 0x73);
-
 // The color of the disabled auth message bubble when the color extracted from
 // wallpaper is transparent or invalid (i.e. color calculation fails or is
 // disabled).
@@ -121,6 +119,24 @@
 
 constexpr int kNonEmptyWidthDp = 1;
 
+// The color of the required online sign-in  message text.
+constexpr SkColor kSystemButtonMessageColor = SK_ColorBLACK;
+// The background color of the required online sign-in button.
+constexpr SkColor kSystemButtonBackgroundColor =
+    SkColorSetA(gfx::kGoogleRed300, SK_AlphaOPAQUE);
+
+constexpr int kUserInfoBubbleWidth = 192;
+constexpr int kUserInfoBubbleExternalPadding = 8;
+constexpr int kSystemButtonIconSize = 20;
+constexpr int kSystemButtonMarginTopBottomDp = 6;
+constexpr int kSystemButtonMarginLeftRightDp = 16;
+constexpr int kSystemButtonBorderRadius = 16;
+constexpr int kSystemButtonImageLabelSpacing = 8;
+constexpr int kSystemButtonMaxLabelWidthDp =
+    kUserInfoBubbleWidth - 2 * kUserInfoBubbleExternalPadding -
+    kSystemButtonIconSize - kSystemButtonImageLabelSpacing -
+    2 * kSystemButtonBorderRadius;
+
 // Returns an observer that will hide |view| when it fires. The observer will
 // delete itself after firing (by returning true). Make sure to call
 // |observer->SetActive()| after attaching it.
@@ -161,22 +177,69 @@
   DISALLOW_COPY_AND_ASSIGN(ClearPasswordAndHideAnimationObserver);
 };
 
-void DecorateOnlineSignInMessage(views::LabelButton* label_button) {
-  label_button->SetPaintToLayer();
-  label_button->layer()->SetFillsBoundsOpaquely(false);
-  label_button->SetImage(
-      views::Button::STATE_NORMAL,
-      CreateVectorIcon(kLockScreenAlertIcon, kOnlineSignInMessageColor));
-  label_button->SetTextSubpixelRenderingEnabled(false);
-  label_button->SetTextColor(views::Button::STATE_NORMAL,
-                             kOnlineSignInMessageColor);
-  label_button->SetTextColor(views::Button::STATE_HOVERED,
-                             kOnlineSignInMessageColor);
-  label_button->SetTextColor(views::Button::STATE_PRESSED,
-                             kOnlineSignInMessageColor);
-  label_button->SetBorder(views::CreateEmptyBorder(gfx::Insets(9, 0)));
+SkPath GetSystemButtonHighlightPath(const views::View* view) {
+  gfx::Rect rect(view->GetLocalBounds());
+  return SkPath().addRoundRect(gfx::RectToSkRect(rect),
+                               kSystemButtonBorderRadius,
+                               kSystemButtonBorderRadius);
 }
 
+class SystemButtonHighlightPathGenerator
+    : public views::HighlightPathGenerator {
+ public:
+  SystemButtonHighlightPathGenerator() = default;
+  SystemButtonHighlightPathGenerator(
+      const SystemButtonHighlightPathGenerator&) = delete;
+  SystemButtonHighlightPathGenerator& operator=(
+      const SystemButtonHighlightPathGenerator&) = delete;
+
+  // views::HighlightPathGenerator:
+  SkPath GetHighlightPath(const views::View* view) override {
+    return GetSystemButtonHighlightPath(view);
+  }
+};
+
+class SystemButton : public views::LabelButton {
+ public:
+  SystemButton(views::ButtonListener* listener, const base::string16& text)
+      : LabelButton(listener, text) {
+    SetImageLabelSpacing(kSystemButtonImageLabelSpacing);
+    label()->SetMultiLine(true);
+    label()->SetMaximumWidth(kSystemButtonMaxLabelWidthDp);
+    label()->SetFontList(
+        gfx::FontList().DeriveWithWeight(gfx::Font::Weight::MEDIUM));
+    SetPaintToLayer();
+    layer()->SetFillsBoundsOpaquely(false);
+    SetImage(views::Button::STATE_NORMAL,
+             CreateVectorIcon(kLockScreenAlertIcon, kSystemButtonMessageColor));
+    SetTextSubpixelRenderingEnabled(false);
+    SetTextColor(views::Button::STATE_NORMAL, kSystemButtonMessageColor);
+    SetTextColor(views::Button::STATE_HOVERED, kSystemButtonMessageColor);
+    SetTextColor(views::Button::STATE_PRESSED, kSystemButtonMessageColor);
+    views::HighlightPathGenerator::Install(
+        this, std::make_unique<SystemButtonHighlightPathGenerator>());
+  }
+
+  SystemButton(const SystemButton&) = delete;
+  SystemButton& operator=(const SystemButton&) = delete;
+  ~SystemButton() override = default;
+
+  // views::LabelButton:
+  void PaintButtonContents(gfx::Canvas* canvas) override {
+    cc::PaintFlags flags;
+    flags.setAntiAlias(true);
+    flags.setColor(kSystemButtonBackgroundColor);
+    flags.setStyle(cc::PaintFlags::kFill_Style);
+    canvas->DrawPath(GetSystemButtonHighlightPath(this), flags);
+  }
+
+  gfx::Insets GetInsets() const override {
+    return gfx::Insets(
+        kSystemButtonMarginTopBottomDp, kSystemButtonMarginLeftRightDp,
+        kSystemButtonMarginTopBottomDp, kSystemButtonMarginLeftRightDp);
+  }
+};
+
 // The label shown below the fingerprint icon.
 class FingerprintLabel : public views::Label {
  public:
@@ -775,10 +838,9 @@
       callbacks.on_easy_unlock_icon_hovered,
       callbacks.on_easy_unlock_icon_tapped);
 
-  auto online_sign_in_message = std::make_unique<views::LabelButton>(
-      this, base::UTF8ToUTF16(user.basic_user_info.display_name));
+  auto online_sign_in_message = std::make_unique<SystemButton>(
+      this, l10n_util::GetStringUTF16(IDS_ASH_LOGIN_SIGN_IN_REQUIRED_MESSAGE));
   online_sign_in_message_ = online_sign_in_message.get();
-  DecorateOnlineSignInMessage(online_sign_in_message_);
 
   auto disabled_auth_message = std::make_unique<DisabledAuthMessageView>();
   disabled_auth_message_ = disabled_auth_message.get();
@@ -1172,7 +1234,7 @@
   if (user_changed)
     password_view_->Clear();
   online_sign_in_message_->SetText(
-      base::UTF8ToUTF16(user.basic_user_info.display_name));
+      l10n_util::GetStringUTF16(IDS_ASH_LOGIN_SIGN_IN_REQUIRED_MESSAGE));
 }
 
 void LoginAuthUserView::SetFingerprintState(FingerprintState state) {
diff --git a/ash/public/cpp/accelerators.cc b/ash/public/cpp/accelerators.cc
index 11b1536..7b907f1 100644
--- a/ash/public/cpp/accelerators.cc
+++ b/ash/public/cpp/accelerators.cc
@@ -161,6 +161,7 @@
     {true, ui::VKEY_OEM_PLUS, ui::EF_ALT_DOWN, TOGGLE_MAXIMIZED},
     {true, ui::VKEY_BROWSER_FORWARD, ui::EF_CONTROL_DOWN, FOCUS_NEXT_PANE},
     {true, ui::VKEY_BROWSER_BACK, ui::EF_CONTROL_DOWN, FOCUS_PREVIOUS_PANE},
+    {true, ui::VKEY_BROWSER_BACK, ui::EF_NONE, MINIMIZE_TOP_WINDOW_ON_BACK},
 
     // Moving active window between displays shortcut.
     {true, ui::VKEY_M, ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN,
diff --git a/ash/public/cpp/accelerators.h b/ash/public/cpp/accelerators.h
index 6430d5d1..7072d53 100644
--- a/ash/public/cpp/accelerators.h
+++ b/ash/public/cpp/accelerators.h
@@ -114,6 +114,7 @@
   WINDOW_CYCLE_SNAP_LEFT,
   WINDOW_CYCLE_SNAP_RIGHT,
   WINDOW_MINIMIZE,
+  MINIMIZE_TOP_WINDOW_ON_BACK,
 
   // Debug accelerators are intentionally at the end, so that if you remove one
   // you don't need to update tests which check hashes of the ids.
diff --git a/ash/public/cpp/ash_features.cc b/ash/public/cpp/ash_features.cc
index 332c222..cb352d9 100644
--- a/ash/public/cpp/ash_features.cc
+++ b/ash/public/cpp/ash_features.cc
@@ -11,6 +11,9 @@
 namespace ash {
 namespace features {
 
+const base::Feature kAllowAmbientEQ{"AllowAmbientEQ",
+                                    base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kDockedMagnifier{"DockedMagnifier",
                                      base::FEATURE_ENABLED_BY_DEFAULT};
 
@@ -100,6 +103,10 @@
 const base::Feature kDragFromShelfToHomeOrOverview{
     "DragFromShelfToHomeOrOverview", base::FEATURE_ENABLED_BY_DEFAULT};
 
+bool IsAllowAmbientEQEnabled() {
+  return base::FeatureList::IsEnabled(kAllowAmbientEQ);
+}
+
 bool IsHideArcMediaNotificationsEnabled() {
   return base::FeatureList::IsEnabled(kMediaSessionNotification) &&
          base::FeatureList::IsEnabled(kHideArcMediaNotifications);
diff --git a/ash/public/cpp/ash_features.h b/ash/public/cpp/ash_features.h
index aad0db0e..d03bbba8 100644
--- a/ash/public/cpp/ash_features.h
+++ b/ash/public/cpp/ash_features.h
@@ -11,6 +11,10 @@
 namespace ash {
 namespace features {
 
+// Enables the UI to support Ambient EQ if the device supports it.
+// See https://crbug.com/1021193 for more details.
+ASH_PUBLIC_EXPORT extern const base::Feature kAllowAmbientEQ;
+
 // Enables the docked (a.k.a. picture-in-picture) magnifier.
 // TODO(afakhry): Remove this after the feature is fully launched.
 // https://crbug.com/709824.
@@ -129,6 +133,8 @@
 // launched.
 ASH_PUBLIC_EXPORT extern const base::Feature kDragFromShelfToHomeOrOverview;
 
+ASH_PUBLIC_EXPORT bool IsAllowAmbientEQEnabled();
+
 ASH_PUBLIC_EXPORT bool IsHideArcMediaNotificationsEnabled();
 
 ASH_PUBLIC_EXPORT bool IsKeyboardShortcutViewerAppEnabled();
diff --git a/ash/public/cpp/ash_pref_names.cc b/ash/public/cpp/ash_pref_names.cc
index c9b4443..df29324 100644
--- a/ash/public/cpp/ash_pref_names.cc
+++ b/ash/public/cpp/ash_pref_names.cc
@@ -80,7 +80,7 @@
 // A boolean pref which determines whether select-to-speak is enabled.
 const char kAccessibilitySelectToSpeakEnabled[] =
     "settings.a11y.select_to_speak";
-// A boolean pref which determines whether switch access is enabled.
+// A boolean pref which determines whether Switch Access is enabled.
 const char kAccessibilitySwitchAccessEnabled[] =
     "settings.a11y.switch_access.enabled";
 // A pref that stores the key code for the "select" action.
@@ -102,7 +102,7 @@
 const char kAccessibilitySwitchAccessPreviousSetting[] =
     "settings.a11y.switch_access.previous.setting";
 // A boolean pref which determines whether auto-scanning is enabled within
-// switch access.
+// Switch Access.
 const char kAccessibilitySwitchAccessAutoScanEnabled[] =
     "settings.a11y.switch_access.auto_scan.enabled";
 // An integer pref which determines time delay in ms before automatically
diff --git a/ash/resources/vector_icons/lock_screen_alert.icon b/ash/resources/vector_icons/lock_screen_alert.icon
index db94d704..2192048 100644
--- a/ash/resources/vector_icons/lock_screen_alert.icon
+++ b/ash/resources/vector_icons/lock_screen_alert.icon
@@ -2,40 +2,26 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-CANVAS_DIMENSIONS, 40,
-MOVE_TO, 1.67f, 35,
-LINE_TO, 38.33f, 35,
-LINE_TO, 20, 3.33f,
-LINE_TO, 1.67f, 35,
-CLOSE,
-MOVE_TO, 22, 30,
-LINE_TO, 18, 30,
-LINE_TO, 18, 26,
-LINE_TO, 22, 26,
-LINE_TO, 22, 30,
-CLOSE,
-MOVE_TO, 22, 23,
-LINE_TO, 18, 23,
-LINE_TO, 18, 16,
-LINE_TO, 22, 16,
-LINE_TO, 22, 23,
-CLOSE
-
 CANVAS_DIMENSIONS, 20,
-MOVE_TO, 0.83f, 17.5f,
-LINE_TO, 19.17f, 17.5f,
-LINE_TO, 10, 1.67f,
-LINE_TO, 0.83f, 17.5f,
+MOVE_TO, 10.87f, 2.5f,
+R_LINE_TO, 8, 14,
+ARC_TO, 1, 1, 0, 0, 1, 18, 18,
+H_LINE_TO, 2,
+R_ARC_TO, 1, 1, 0, 0, 1, -0.87f, -1.5f,
+R_LINE_TO, 8, -14,
+R_ARC_TO, 1, 1, 0, 0, 1, 1.74f, 0,
 CLOSE,
-MOVE_TO, 10.83f, 15,
-LINE_TO, 9.17f, 15,
-LINE_TO, 9.17f, 13.33f,
-LINE_TO, 10.83f, 13.33f,
-LINE_TO, 10.83f, 15,
+MOVE_TO, 10, 5.02f,
+LINE_TO, 3.72f, 16,
+R_H_LINE_TO, 12.55f,
 CLOSE,
-MOVE_TO, 10.83f, 11.67f,
-LINE_TO, 9.17f, 11.67f,
-LINE_TO, 9.17f, 8.33f,
-LINE_TO, 10.83f, 8.33f,
-LINE_TO, 10.83f, 11.67f,
+MOVE_TO, 11, 13,
+R_V_LINE_TO, 2,
+H_LINE_TO, 9,
+R_V_LINE_TO, -2,
+CLOSE,
+R_MOVE_TO, 0, -5,
+R_V_LINE_TO, 4,
+H_LINE_TO, 9,
+V_LINE_TO, 8,
 CLOSE
diff --git a/ash/shelf/back_button.cc b/ash/shelf/back_button.cc
index 4871082..22a0eb39 100644
--- a/ash/shelf/back_button.cc
+++ b/ash/shelf/back_button.cc
@@ -8,6 +8,8 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_focus_cycler.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/wm/tablet_mode/tablet_mode_window_manager.h"
+#include "ash/wm/window_state.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "ui/aura/window.h"
@@ -61,15 +63,20 @@
                                views::InkDrop* ink_drop) {
   base::RecordAction(base::UserMetricsAction("AppList_BackButtonPressed"));
 
-  // Send up event as well as down event as ARC++ clients expect this sequence.
-  // TODO: Investigate if we should be using the current modifiers.
-  aura::Window* root_window = GetWidget()->GetNativeWindow()->GetRootWindow();
-  ui::KeyEvent press_key_event(ui::ET_KEY_PRESSED, ui::VKEY_BROWSER_BACK,
-                               ui::EF_NONE);
-  ignore_result(root_window->GetHost()->SendEventToSink(&press_key_event));
-  ui::KeyEvent release_key_event(ui::ET_KEY_RELEASED, ui::VKEY_BROWSER_BACK,
+  if (TabletModeWindowManager::ShouldMinimizeTopWindowOnBack()) {
+    WindowState::Get(TabletModeWindowManager::GetTopWindow())->Minimize();
+  } else {
+    // Send up event as well as down event as ARC++ clients expect this
+    // sequence.
+    // TODO: Investigate if we should be using the current modifiers.
+    aura::Window* root_window = GetWidget()->GetNativeWindow()->GetRootWindow();
+    ui::KeyEvent press_key_event(ui::ET_KEY_PRESSED, ui::VKEY_BROWSER_BACK,
                                  ui::EF_NONE);
-  ignore_result(root_window->GetHost()->SendEventToSink(&release_key_event));
+    ignore_result(root_window->GetHost()->SendEventToSink(&press_key_event));
+    ui::KeyEvent release_key_event(ui::ET_KEY_RELEASED, ui::VKEY_BROWSER_BACK,
+                                   ui::EF_NONE);
+    ignore_result(root_window->GetHost()->SendEventToSink(&release_key_event));
+  }
 }
 
 }  // namespace ash
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index 8cc20ad..d9322c8 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -1079,6 +1079,10 @@
     return HotseatState::kShown;
 
   auto* app_list_controller = Shell::Get()->app_list_controller();
+  // If the app list controller is null, we are probably in the middle of
+  // a shutdown, let's not change the hotseat state.
+  if (!app_list_controller)
+    return hotseat_state();
   const auto* overview_controller = Shell::Get()->overview_controller();
   const bool in_overview =
       ((overview_controller && overview_controller->InOverviewSession()) ||
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index 26daf257..434b2cb 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -2178,7 +2178,8 @@
   // widget are created.
   EXPECT_TRUE(overview_grid->drop_target_widget());
   EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kFromOverview,
-            overview_session->split_view_drag_indicators()
+            overview_session->grid_list()[0]
+                ->split_view_drag_indicators()
                 ->current_window_dragging_state());
   // Now drop the window, and validate the indicators and the drop target were
   // removed.
@@ -2189,7 +2190,8 @@
   EXPECT_TRUE(overview_session->no_windows_widget_for_testing());
   EXPECT_FALSE(overview_grid->drop_target_widget());
   EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kNoDrag,
-            overview_session->split_view_drag_indicators()
+            overview_session->grid_list()[0]
+                ->split_view_drag_indicators()
                 ->current_window_dragging_state());
 }
 
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index ae51412..412543a 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -41,6 +41,7 @@
 #include "ash/wm/resize_shadow_controller.h"
 #include "ash/wm/splitview/split_view_constants.h"
 #include "ash/wm/splitview/split_view_divider.h"
+#include "ash/wm/splitview/split_view_utils.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_window_state.h"
 #include "ash/wm/window_util.h"
@@ -373,6 +374,10 @@
                            OverviewSession* overview_session)
     : root_window_(root_window),
       overview_session_(overview_session),
+      split_view_drag_indicators_(
+          ShouldAllowSplitView()
+              ? std::make_unique<SplitViewDragIndicators>(root_window)
+              : nullptr),
       bounds_(GetGridBoundsInScreen(root_window, /*divider_changed=*/false)) {
   for (auto* window : windows) {
     if (window->GetRootWindow() != root_window)
@@ -647,13 +652,11 @@
         overview_session_->window_drag_controller()->item()) {
       ignored_items.insert(overview_session_->window_drag_controller()->item());
     }
-    auto* split_view_drag_indicators =
-        overview_session_->split_view_drag_indicators();
     const gfx::Rect grid_bounds = GetGridBoundsInScreenForSplitview(
         root_window_,
-        split_view_drag_indicators
+        split_view_drag_indicators_
             ? base::make_optional(
-                  split_view_drag_indicators->current_window_dragging_state())
+                  split_view_drag_indicators_->current_window_dragging_state())
             : base::nullopt);
     SetBoundsAndUpdatePositions(grid_bounds, ignored_items, /*animate=*/true);
   }
@@ -713,7 +716,7 @@
 
   // Update the grid's bounds.
   const gfx::Rect wanted_grid_bounds = GetGridBoundsInScreenForSplitview(
-      dragged_window, base::make_optional(window_dragging_state));
+      root_window_, base::make_optional(window_dragging_state));
   if (bounds_ != wanted_grid_bounds) {
     SetBoundsAndUpdatePositions(wanted_grid_bounds,
                                 {GetOverviewItemContaining(dragged_window)},
@@ -721,6 +724,18 @@
   }
 }
 
+void OverviewGrid::SetSplitViewDragIndicatorsDraggedWindow(
+    aura::Window* dragged_window) {
+  DCHECK(split_view_drag_indicators_);
+  split_view_drag_indicators_->SetDraggedWindow(dragged_window);
+}
+
+void OverviewGrid::SetSplitViewDragIndicatorsWindowDraggingState(
+    SplitViewDragIndicators::WindowDraggingState window_dragging_state) {
+  DCHECK(split_view_drag_indicators_);
+  split_view_drag_indicators_->SetWindowDraggingState(window_dragging_state);
+}
+
 bool OverviewGrid::MaybeUpdateDesksWidgetBounds() {
   if (!desks_widget_)
     return false;
@@ -864,7 +879,7 @@
   // Update the grid bounds and reposition windows. Since the grid bounds might
   // be updated based on the preview area during drag, but the window finally
   // didn't be snapped to the preview area.
-  SetBoundsAndUpdatePositions(GetGridBoundsInScreenForSplitview(dragged_window),
+  SetBoundsAndUpdatePositions(GetGridBoundsInScreenForSplitview(root_window_),
                               /*ignored_items=*/{},
                               /*animate=*/true);
 }
@@ -903,6 +918,9 @@
 }
 
 void OverviewGrid::OnDisplayMetricsChanged() {
+  if (split_view_drag_indicators_)
+    split_view_drag_indicators_->OnDisplayBoundsChanged();
+
   // In case of split view mode, the grid bounds and item positions will be
   // updated in |OnSplitViewDividerPositionChanged|.
   if (SplitViewController::Get(root_window_)->InSplitViewMode())
@@ -1910,10 +1928,8 @@
   // Shift the widget down to make room for the splitview indicator guidance
   // when it's shown at the top of the screen when in portrait mode and no other
   // windows are snapped.
-  auto* split_view_drag_indicators =
-      overview_session_->split_view_drag_indicators();
-  if (split_view_drag_indicators &&
-      split_view_drag_indicators->current_window_dragging_state() ==
+  if (split_view_drag_indicators_ &&
+      split_view_drag_indicators_->current_window_dragging_state() ==
           SplitViewDragIndicators::WindowDraggingState::kFromOverview &&
       !IsCurrentScreenOrientationLandscape() &&
       !SplitViewController::Get(root_window_)->InSplitViewMode()) {
diff --git a/ash/wm/overview/overview_grid.h b/ash/wm/overview/overview_grid.h
index 840d563..437b474 100644
--- a/ash/wm/overview/overview_grid.h
+++ b/ash/wm/overview/overview_grid.h
@@ -146,6 +146,13 @@
       aura::Window* dragged_window,
       SplitViewDragIndicators::WindowDraggingState window_dragging_state);
 
+  // Sets the dragged window on |split_view_drag_indicators_|.
+  void SetSplitViewDragIndicatorsDraggedWindow(aura::Window* dragged_window);
+
+  // Sets the window dragging state on |split_view_drag_indicators_|.
+  void SetSplitViewDragIndicatorsWindowDraggingState(
+      SplitViewDragIndicators::WindowDraggingState window_dragging_state);
+
   // Updates the desks bar widget bounds if necessary.
   // Returns true if the desks widget's bounds have been updated.
   bool MaybeUpdateDesksWidgetBounds();
@@ -326,6 +333,10 @@
     return window_list_;
   }
 
+  SplitViewDragIndicators* split_view_drag_indicators() {
+    return split_view_drag_indicators_.get();
+  }
+
   const DesksBarView* desks_bar_view() const { return desks_bar_view_; }
 
   const gfx::Rect bounds() const { return bounds_; }
@@ -422,6 +433,10 @@
   // Vector containing all the windows in this grid.
   std::vector<std::unique_ptr<OverviewItem>> window_list_;
 
+  // The owner of the widget that displays split-view-related information. Null
+  // if split view is unsupported (see |ShouldAllowSplitView|).
+  std::unique_ptr<SplitViewDragIndicators> split_view_drag_indicators_;
+
   // Widget that contains the DeskBarView contents when the Virtual Desks
   // feature is enabled.
   std::unique_ptr<views::Widget> desks_widget_;
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index faa001b..c0f9888 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -110,9 +110,6 @@
   if (restore_focus_window_)
     restore_focus_window_->AddObserver(this);
 
-  if (ShouldAllowSplitView())
-    split_view_drag_indicators_ = std::make_unique<SplitViewDragIndicators>();
-
   aura::Window::Windows root_windows = Shell::GetAllRootWindows();
   std::sort(root_windows.begin(), root_windows.end(),
             [](const aura::Window* a, const aura::Window* b) {
@@ -323,16 +320,37 @@
 
 void OverviewSession::SetSplitViewDragIndicatorsDraggedWindow(
     aura::Window* dragged_window) {
-  DCHECK(split_view_drag_indicators_);
-  split_view_drag_indicators_->SetDraggedWindow(dragged_window);
+  for (std::unique_ptr<OverviewGrid>& grid : grid_list_)
+    grid->SetSplitViewDragIndicatorsDraggedWindow(dragged_window);
 }
 
-void OverviewSession::SetSplitViewDragIndicatorsWindowDraggingState(
-    SplitViewDragIndicators::WindowDraggingState window_dragging_state,
-    const gfx::Point& event_location) {
-  DCHECK(split_view_drag_indicators_);
-  split_view_drag_indicators_->SetWindowDraggingState(window_dragging_state,
-                                                      event_location);
+void OverviewSession::UpdateSplitViewDragIndicatorsWindowDraggingStates(
+    const aura::Window* root_window_being_dragged_in,
+    bool is_dragging,
+    SplitViewDragIndicators::WindowDraggingState non_snap_state,
+    SplitViewController::SnapPosition snap_position) {
+  using State = SplitViewDragIndicators::WindowDraggingState;
+  const State window_dragging_state_on_root_window_being_dragged_in =
+      SplitViewDragIndicators::ComputeWindowDraggingState(
+          is_dragging, non_snap_state, snap_position);
+  const State window_dragging_state_on_root_windows_not_being_dragged_in =
+      SplitViewDragIndicators::ComputeWindowDraggingState(
+          is_dragging, non_snap_state, SplitViewController::NONE);
+  for (std::unique_ptr<OverviewGrid>& grid : grid_list_) {
+    grid->SetSplitViewDragIndicatorsWindowDraggingState(
+        grid->root_window() == root_window_being_dragged_in
+            ? window_dragging_state_on_root_window_being_dragged_in
+            : window_dragging_state_on_root_windows_not_being_dragged_in);
+  }
+}
+
+void OverviewSession::RearrangeDuringDrag(aura::Window* dragged_window) {
+  for (std::unique_ptr<OverviewGrid>& grid : grid_list_) {
+    DCHECK(grid->split_view_drag_indicators());
+    grid->RearrangeDuringDrag(
+        dragged_window,
+        grid->split_view_drag_indicators()->current_window_dragging_state());
+  }
 }
 
 OverviewGrid* OverviewSession::GetGridWithRootWindow(
@@ -777,9 +795,6 @@
   GetGridWithRootWindow(Shell::GetRootWindowForDisplayId(display.id()))
       ->OnDisplayMetricsChanged();
 
-  if (split_view_drag_indicators_)
-    split_view_drag_indicators_->OnDisplayBoundsChanged();
-
   // The no windows widget is on the primary root window. If |display|
   // corresponds to another root window, then we are done.
   if (display.id() !=
diff --git a/ash/wm/overview/overview_session.h b/ash/wm/overview/overview_session.h
index 906c8a6..6a37dec 100644
--- a/ash/wm/overview/overview_session.h
+++ b/ash/wm/overview/overview_session.h
@@ -26,7 +26,6 @@
 #include "ui/wm/public/activation_change_observer.h"
 
 namespace gfx {
-class Point;
 class PointF;
 }  // namespace gfx
 
@@ -130,15 +129,23 @@
   // Activates |item's| window.
   void SelectWindow(OverviewItem* item);
 
-  // Sets the dragged window on |split_view_drag_indicators_|.
+  // Sets the dragged window on the split view drag indicators.
   void SetSplitViewDragIndicatorsDraggedWindow(aura::Window* dragged_window);
 
-  // Called to show or hide the split view drag indicators. This will do
-  // nothing if split view is not enabled. |event_location| is used to reparent
-  // |split_view_drag_indicators_|'s widget, if necessary.
-  void SetSplitViewDragIndicatorsWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState window_dragging_state,
-      const gfx::Point& event_location);
+  // This function sets the window dragging state on the split view drag
+  // indicators on every root window. On |root_window_being_dragged_in|, the
+  // state is determined by forwarding the other three arguments to
+  // |SplitViewDragIndicators::ComputeWindowDraggingState|. On other root
+  // windows, as snap previews are not appropriate, the state is determined
+  // similarly but with |SplitViewController::NONE| instead of |snap_position|.
+  void UpdateSplitViewDragIndicatorsWindowDraggingStates(
+      const aura::Window* root_window_being_dragged_in,
+      bool is_dragging,
+      SplitViewDragIndicators::WindowDraggingState non_snap_state,
+      SplitViewController::SnapPosition snap_position);
+
+  // See |OverviewGrid::RearrangeDuringDrag|.
+  void RearrangeDuringDrag(aura::Window* dragged_window);
 
   // Retrieves the window grid whose root window matches |root_window|. Returns
   // nullptr if the window grid is not found.
@@ -296,10 +303,6 @@
     is_shutting_down_ = is_shutting_down;
   }
 
-  SplitViewDragIndicators* split_view_drag_indicators() {
-    return split_view_drag_indicators_.get();
-  }
-
   const std::vector<std::unique_ptr<OverviewGrid>>& grid_list() const {
     return grid_list_;
   }
@@ -381,10 +384,6 @@
   // List of all the window overview grids, one for each root window.
   std::vector<std::unique_ptr<OverviewGrid>> grid_list_;
 
-  // The owner of the widget which displays splitview related information in
-  // overview mode. This will be nullptr if split view is not enabled.
-  std::unique_ptr<SplitViewDragIndicators> split_view_drag_indicators_;
-
   // The following variables are used for metric collection purposes. All of
   // them refer to this particular overview session and are not cumulative:
   // The time when overview was started.
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc
index 8097b99..3f992bf 100644
--- a/ash/wm/overview/overview_session_unittest.cc
+++ b/ash/wm/overview/overview_session_unittest.cc
@@ -4077,6 +4077,7 @@
     overview_session()->Drag(item1, gfx::PointF());
     EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kToSnapLeft,
               overview_session()
+                  ->grid_list()[0]
                   ->split_view_drag_indicators()
                   ->current_window_dragging_state());
     EXPECT_FALSE(window2->layer()->clip_rect().IsEmpty());
@@ -5669,7 +5670,8 @@
 }
 
 // Test dragging to snap an overview item on an external display.
-TEST_P(SplitViewOverviewSessionInClamshellTestMultiDisplayOnly, Dragging) {
+TEST_P(SplitViewOverviewSessionInClamshellTestMultiDisplayOnly,
+       DraggingOnExternalDisplay) {
   UpdateDisplay("800x600,800x600");
   aura::Window::Windows root_windows = Shell::GetAllRootWindows();
   ASSERT_EQ(2u, root_windows.size());
@@ -5684,7 +5686,7 @@
   SplitViewController* split_view_controller =
       SplitViewController::Get(root_windows[1]);
   SplitViewDragIndicators* indicators =
-      overview_session()->split_view_drag_indicators();
+      grid_on_root2->split_view_drag_indicators();
 
   Shell::Get()->cursor_manager()->SetDisplay(
       display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[1]));
@@ -5726,6 +5728,123 @@
             split_view_controller->state());
 }
 
+// Test dragging from one display to another.
+TEST_P(SplitViewOverviewSessionInClamshellTestMultiDisplayOnly,
+       MultiDisplayDragging) {
+  wm::CursorManager* cursor_manager = Shell::Get()->cursor_manager();
+  UpdateDisplay("800x600,800x600");
+  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
+  ASSERT_EQ(2u, root_windows.size());
+  const display::Display display_with_root1 =
+      display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[0]);
+  const display::Display display_with_root2 =
+      display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[1]);
+  const gfx::Rect bounds_within_root1(0, 0, 400, 400);
+  const gfx::Rect bounds_within_root2(800, 0, 400, 400);
+  std::unique_ptr<aura::Window> window1 = CreateTestWindow(bounds_within_root1);
+  std::unique_ptr<aura::Window> window2 = CreateTestWindow(bounds_within_root1);
+  std::unique_ptr<aura::Window> window3 = CreateTestWindow(bounds_within_root2);
+  ToggleOverview();
+  OverviewGrid* grid_on_root1 =
+      overview_session()->GetGridWithRootWindow(root_windows[0]);
+  OverviewGrid* grid_on_root2 =
+      overview_session()->GetGridWithRootWindow(root_windows[1]);
+  OverviewItem* item1 = grid_on_root1->GetOverviewItemContaining(window1.get());
+  SplitViewDragIndicators* indicators_on_root1 =
+      grid_on_root1->split_view_drag_indicators();
+  SplitViewDragIndicators* indicators_on_root2 =
+      grid_on_root2->split_view_drag_indicators();
+
+  ASSERT_EQ(display_with_root1.id(), cursor_manager->GetDisplay().id());
+  overview_session()->InitiateDrag(item1, item1->target_bounds().CenterPoint(),
+                                   /*is_touch_dragging=*/false);
+  EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kNoDrag,
+            indicators_on_root1->current_window_dragging_state());
+  EXPECT_EQ(display_with_root1.work_area(), grid_on_root1->bounds());
+  EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kNoDrag,
+            indicators_on_root2->current_window_dragging_state());
+  EXPECT_EQ(display_with_root2.work_area(), grid_on_root2->bounds());
+
+  const gfx::PointF root1_left_snap_point(0.f, 300.f);
+  overview_session()->Drag(item1, root1_left_snap_point);
+  EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kToSnapLeft,
+            indicators_on_root1->current_window_dragging_state());
+  EXPECT_EQ(
+      SplitViewController::Get(root_windows[0])
+          ->GetSnappedWindowBoundsInScreen(SplitViewController::RIGHT,
+                                           /*window_for_minimum_size=*/nullptr),
+      grid_on_root1->bounds());
+  EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kFromOverview,
+            indicators_on_root2->current_window_dragging_state());
+  EXPECT_EQ(display_with_root2.work_area(), grid_on_root2->bounds());
+
+  const gfx::PointF root1_middle_point(400.f, 300.f);
+  overview_session()->Drag(item1, root1_middle_point);
+  EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kFromOverview,
+            indicators_on_root1->current_window_dragging_state());
+  EXPECT_EQ(display_with_root1.work_area(), grid_on_root1->bounds());
+  EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kFromOverview,
+            indicators_on_root2->current_window_dragging_state());
+  EXPECT_EQ(display_with_root2.work_area(), grid_on_root2->bounds());
+
+  const gfx::PointF root1_right_snap_point(799.f, 300.f);
+  overview_session()->Drag(item1, root1_right_snap_point);
+  EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kToSnapRight,
+            indicators_on_root1->current_window_dragging_state());
+  EXPECT_EQ(
+      SplitViewController::Get(root_windows[0])
+          ->GetSnappedWindowBoundsInScreen(SplitViewController::LEFT,
+                                           /*window_for_minimum_size=*/nullptr),
+      grid_on_root1->bounds());
+  EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kFromOverview,
+            indicators_on_root2->current_window_dragging_state());
+  EXPECT_EQ(display_with_root2.work_area(), grid_on_root2->bounds());
+
+  const gfx::PointF root2_left_snap_point(800.f, 300.f);
+  cursor_manager->SetDisplay(display_with_root2);
+  overview_session()->Drag(item1, root2_left_snap_point);
+  EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kFromOverview,
+            indicators_on_root1->current_window_dragging_state());
+  EXPECT_EQ(display_with_root1.work_area(), grid_on_root1->bounds());
+  EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kToSnapLeft,
+            indicators_on_root2->current_window_dragging_state());
+  EXPECT_EQ(
+      SplitViewController::Get(root_windows[1])
+          ->GetSnappedWindowBoundsInScreen(SplitViewController::RIGHT,
+                                           /*window_for_minimum_size=*/nullptr),
+      grid_on_root2->bounds());
+
+  const gfx::PointF root2_right_snap_point(1599.f, 300.f);
+  overview_session()->Drag(item1, root2_right_snap_point);
+  EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kFromOverview,
+            indicators_on_root1->current_window_dragging_state());
+  EXPECT_EQ(display_with_root1.work_area(), grid_on_root1->bounds());
+  EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kToSnapRight,
+            indicators_on_root2->current_window_dragging_state());
+  EXPECT_EQ(
+      SplitViewController::Get(root_windows[1])
+          ->GetSnappedWindowBoundsInScreen(SplitViewController::LEFT,
+                                           /*window_for_minimum_size=*/nullptr),
+      grid_on_root2->bounds());
+
+  const gfx::PointF root2_middle_point(1200.f, 300.f);
+  overview_session()->Drag(item1, root2_middle_point);
+  EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kFromOverview,
+            indicators_on_root1->current_window_dragging_state());
+  EXPECT_EQ(display_with_root1.work_area(), grid_on_root1->bounds());
+  EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kFromOverview,
+            indicators_on_root2->current_window_dragging_state());
+  EXPECT_EQ(display_with_root2.work_area(), grid_on_root2->bounds());
+
+  overview_session()->CompleteDrag(item1, root2_middle_point);
+  EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kNoDrag,
+            indicators_on_root1->current_window_dragging_state());
+  EXPECT_EQ(display_with_root1.work_area(), grid_on_root1->bounds());
+  EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kNoDrag,
+            indicators_on_root2->current_window_dragging_state());
+  EXPECT_EQ(display_with_root2.work_area(), grid_on_root2->bounds());
+}
+
 // Verify that when in overview mode, the selector items unsnappable indicator
 // shows up when expected.
 TEST_P(SplitViewOverviewSessionInClamshellTestMultiDisplayOnly,
diff --git a/ash/wm/overview/overview_utils.cc b/ash/wm/overview/overview_utils.cc
index 5849c5c..4306ab6 100644
--- a/ash/wm/overview/overview_utils.cc
+++ b/ash/wm/overview/overview_utils.cc
@@ -17,6 +17,7 @@
 #include "ash/wm/overview/cleanup_animation_observer.h"
 #include "ash/wm/overview/delayed_animation_observer_impl.h"
 #include "ash/wm/overview/overview_controller.h"
+#include "ash/wm/overview/overview_grid.h"
 #include "ash/wm/overview/scoped_overview_animation_settings.h"
 #include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/splitview/split_view_utils.h"
@@ -220,12 +221,12 @@
 }
 
 // Get the grid bounds if a window is snapped in splitview, or what they will be
-// when snapped based on |indicator_state|.
+// when snapped based on |target_root| and |indicator_state|.
 gfx::Rect GetGridBoundsInScreenForSplitview(
-    aura::Window* window,
+    aura::Window* target_root,
     base::Optional<SplitViewDragIndicators::WindowDraggingState>
         window_dragging_state) {
-  auto* split_view_controller = SplitViewController::Get(window);
+  auto* split_view_controller = SplitViewController::Get(target_root);
   auto state = split_view_controller->state();
 
   // If we are in splitview mode already just use the given state, otherwise
@@ -252,7 +253,7 @@
           SplitViewController::LEFT, /*window_for_minimum_size=*/nullptr);
     default:
       return screen_util::
-          GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(window);
+          GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(target_root);
   }
 }
 
@@ -263,20 +264,23 @@
   auto* overview_session =
       Shell::Get()->overview_controller()->overview_session();
   DCHECK(overview_session);
-  DCHECK(overview_session->split_view_drag_indicators());
+  aura::Window* root_window = Shell::GetPrimaryRootWindow();
+  DCHECK(overview_session->GetGridWithRootWindow(root_window)
+             ->split_view_drag_indicators());
   // TODO(sammiequon): This does not work for drag from top as they have
   // different drag indicators object as regular overview.
-  auto window_dragging_state = overview_session->split_view_drag_indicators()
-                                   ->current_window_dragging_state();
-  if (!SplitViewController::Get(Shell::GetPrimaryRootWindow())
-           ->InSplitViewMode() &&
+  auto window_dragging_state =
+      overview_session->GetGridWithRootWindow(root_window)
+          ->split_view_drag_indicators()
+          ->current_window_dragging_state();
+  if (!SplitViewController::Get(root_window)->InSplitViewMode() &&
       SplitViewDragIndicators::GetSnapPosition(window_dragging_state) ==
           SplitViewController::NONE) {
     return base::nullopt;
   }
 
   return base::make_optional(gfx::RectF(GetGridBoundsInScreenForSplitview(
-      window, base::make_optional(window_dragging_state))));
+      root_window, base::make_optional(window_dragging_state))));
 }
 
 bool ShouldUseTabletModeGridLayout() {
diff --git a/ash/wm/overview/overview_utils.h b/ash/wm/overview/overview_utils.h
index 09b04e7..c82d102f 100644
--- a/ash/wm/overview/overview_utils.h
+++ b/ash/wm/overview/overview_utils.h
@@ -81,9 +81,9 @@
 void MaximizeIfSnapped(aura::Window* window);
 
 // Get the grid bounds if a window is snapped in splitview, or what they will be
-// when snapped based on |indicator_state|.
+// when snapped based on |target_root| and |indicator_state|.
 gfx::Rect GetGridBoundsInScreenForSplitview(
-    aura::Window* window,
+    aura::Window* target_root,
     base::Optional<SplitViewDragIndicators::WindowDraggingState>
         window_dragging_state = base::nullopt);
 
diff --git a/ash/wm/overview/overview_window_drag_controller.cc b/ash/wm/overview/overview_window_drag_controller.cc
index 5f72a3e2..7392f205 100644
--- a/ash/wm/overview/overview_window_drag_controller.cc
+++ b/ash/wm/overview/overview_window_drag_controller.cc
@@ -243,9 +243,10 @@
   if (should_allow_split_view_) {
     overview_session_->SetSplitViewDragIndicatorsDraggedWindow(
         item_->GetWindow());
-    overview_session_->SetSplitViewDragIndicatorsWindowDraggingState(
+    overview_session_->UpdateSplitViewDragIndicatorsWindowDraggingStates(
+        GetRootWindowBeingDraggedIn(), /*is_dragging=*/true,
         SplitViewDragIndicators::WindowDraggingState::kFromOverview,
-        gfx::ToRoundedPoint(location_in_screen));
+        SplitViewController::NONE);
     item_->HideCannotSnapWarning();
 
     // Update the split view divider bar status if necessary. If splitview is
@@ -338,8 +339,10 @@
     }
     item_->overview_grid()->RemoveDropTarget();
     if (should_allow_split_view_) {
-      overview_session_->SetSplitViewDragIndicatorsWindowDraggingState(
-          SplitViewDragIndicators::WindowDraggingState::kNoDrag, gfx::Point());
+      overview_session_->UpdateSplitViewDragIndicatorsWindowDraggingStates(
+          item_->overview_grid()->root_window(), /*is_dragging=*/false,
+          SplitViewDragIndicators::WindowDraggingState::kNoDrag,
+          SplitViewController::NONE);
       item_->UpdateCannotSnapWarningVisibility();
     }
   }
@@ -525,9 +528,10 @@
     // Update window grid bounds and |snap_position_| in case the screen
     // orientation was changed.
     UpdateDragIndicatorsAndOverviewGrid(location_in_screen);
-    overview_session_->SetSplitViewDragIndicatorsWindowDraggingState(
+    overview_session_->UpdateSplitViewDragIndicatorsWindowDraggingStates(
+        GetRootWindowBeingDraggedIn(), /*is_dragging=*/true,
         SplitViewDragIndicators::WindowDraggingState::kNoDrag,
-        rounded_screen_point);
+        SplitViewController::NONE);
     item_->UpdateCannotSnapWarningVisibility();
   }
 
@@ -586,15 +590,19 @@
     return;
 
   snap_position_ = GetSnapPosition(location_in_screen);
-  SplitViewDragIndicators::WindowDraggingState window_dragging_state =
-      SplitViewDragIndicators::ComputeWindowDraggingState(
-          /*is_dragging=*/true,
-          SplitViewDragIndicators::WindowDraggingState::kFromOverview,
-          snap_position_);
-  overview_session_->SetSplitViewDragIndicatorsWindowDraggingState(
-      window_dragging_state, gfx::ToRoundedPoint(location_in_screen));
-  item_->overview_grid()->RearrangeDuringDrag(item_->GetWindow(),
-                                              window_dragging_state);
+  overview_session_->UpdateSplitViewDragIndicatorsWindowDraggingStates(
+      GetRootWindowBeingDraggedIn(), /*is_dragging=*/true,
+      SplitViewDragIndicators::WindowDraggingState::kFromOverview,
+      snap_position_);
+  overview_session_->RearrangeDuringDrag(item_->GetWindow());
+}
+
+const aura::Window* OverviewWindowDragController::GetRootWindowBeingDraggedIn()
+    const {
+  return is_touch_dragging_
+             ? item_->root_window()
+             : Shell::GetRootWindowForDisplayId(
+                   Shell::Get()->cursor_manager()->GetDisplay().id());
 }
 
 gfx::Rect OverviewWindowDragController::GetWorkAreaOfDisplayBeingDraggedIn()
@@ -606,16 +614,6 @@
              : Shell::Get()->cursor_manager()->GetDisplay().work_area();
 }
 
-SplitViewController*
-OverviewWindowDragController::GetSplitViewControllerForDisplayBeingDraggedIn()
-    const {
-  return SplitViewController::Get(
-      is_touch_dragging_
-          ? item_->root_window()
-          : Shell::GetRootWindowForDisplayId(
-                Shell::Get()->cursor_manager()->GetDisplay().id()));
-}
-
 bool OverviewWindowDragController::ShouldUpdateDragIndicatorsOrSnap(
     const gfx::PointF& event_location) {
   auto snap_position = GetSnapPosition(event_location);
@@ -686,7 +684,7 @@
   // should show the preview window as soon as the window past the split divider
   // bar.
   SplitViewController* split_view_controller =
-      GetSplitViewControllerForDisplayBeingDraggedIn();
+      SplitViewController::Get(GetRootWindowBeingDraggedIn());
   if (split_view_controller->InSplitViewMode()) {
     const int position =
         gfx::ToRoundedInt(is_landscape ? location_in_screen.x() - area.x()
@@ -717,9 +715,9 @@
   aura::Window* window = item_->GetWindow();
   // TODO(crbug.com/970013): Properly implement the multi-display behavior which
   // involves reparenting |window| to put it on the destination display.
-  GetSplitViewControllerForDisplayBeingDraggedIn()->SnapWindow(
-      window, snap_position,
-      /*use_divider_spawn_animation=*/true);
+  SplitViewController::Get(GetRootWindowBeingDraggedIn())
+      ->SnapWindow(window, snap_position,
+                   /*use_divider_spawn_animation=*/true);
   item_ = nullptr;
   wm::ActivateWindow(window);
 }
diff --git a/ash/wm/overview/overview_window_drag_controller.h b/ash/wm/overview/overview_window_drag_controller.h
index edb99952..bcba1d8f 100644
--- a/ash/wm/overview/overview_window_drag_controller.h
+++ b/ash/wm/overview/overview_window_drag_controller.h
@@ -103,8 +103,8 @@
   void UpdateDragIndicatorsAndOverviewGrid(
       const gfx::PointF& location_in_screen);
 
+  const aura::Window* GetRootWindowBeingDraggedIn() const;
   gfx::Rect GetWorkAreaOfDisplayBeingDraggedIn() const;
-  SplitViewController* GetSplitViewControllerForDisplayBeingDraggedIn() const;
 
   // Dragged items should not attempt to update the indicators or snap if
   // the drag started in a snap region and has not been dragged pass the
diff --git a/ash/wm/overview/overview_window_drag_controller_unittest.cc b/ash/wm/overview/overview_window_drag_controller_unittest.cc
index df234f3..b441b42d 100644
--- a/ash/wm/overview/overview_window_drag_controller_unittest.cc
+++ b/ash/wm/overview/overview_window_drag_controller_unittest.cc
@@ -325,7 +325,7 @@
   }
 
   SplitViewDragIndicators* drag_indicators() {
-    return overview_session()->split_view_drag_indicators();
+    return overview_session()->grid_list()[0]->split_view_drag_indicators();
   }
 
   OverviewGrid* overview_grid() {
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
index c13892e..10c4d8b 100644
--- a/ash/wm/splitview/split_view_controller.cc
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -328,15 +328,9 @@
   DCHECK(window && CanSnapInSplitview(window));
   DCHECK_NE(snap_position, NONE);
   DCHECK(!is_resizing_);
+  DCHECK(!IsDividerAnimating());
   DCHECK_EQ(root_window_, window->GetRootWindow());
 
-  // This check detects the case that you try to snap a window while watching
-  // the divider snap animation. It also detects the case that you click a
-  // window in overview while tap dragging the divider (possible by using the
-  // emulator or chrome://flags/#force-tablet-mode).
-  if (IsDividerAnimating())
-    return;
-
   UpdateSnappingWindowTransformedBounds(window);
   RemoveWindowFromOverviewIfApplicable(window);
 
diff --git a/ash/wm/splitview/split_view_controller_unittest.cc b/ash/wm/splitview/split_view_controller_unittest.cc
index 0901fa63..1a314f8 100644
--- a/ash/wm/splitview/split_view_controller_unittest.cc
+++ b/ash/wm/splitview/split_view_controller_unittest.cc
@@ -1121,36 +1121,6 @@
   EXPECT_EQ(split_view_controller()->right_window(), window2.get());
 }
 
-// Verify that you cannot snap a window during the divider snap animation.
-TEST_P(SplitViewControllerTest, SnapWindowDuringDividerSnapAnimation) {
-  const gfx::Rect bounds(0, 0, 400, 400);
-  std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
-  std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
-  std::unique_ptr<aura::Window> window3(CreateWindow(bounds));
-  std::unique_ptr<aura::Window> window4(CreateWindow(bounds));
-
-  split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT);
-  split_view_controller()->SnapWindow(window2.get(),
-                                      SplitViewController::RIGHT);
-  ASSERT_EQ(split_view_controller()->left_window(), window1.get());
-  ASSERT_EQ(split_view_controller()->right_window(), window2.get());
-
-  // Drag the divider to trigger the snap animation.
-  const gfx::Point divider_center =
-      split_view_divider()
-          ->GetDividerBoundsInScreen(false /* is_dragging */)
-          .CenterPoint();
-  GetEventGenerator()->set_current_screen_location(divider_center);
-  GetEventGenerator()->DragMouseBy(20, 0);
-  ASSERT_TRUE(IsDividerAnimating());
-
-  split_view_controller()->SnapWindow(window3.get(), SplitViewController::LEFT);
-  split_view_controller()->SnapWindow(window4.get(),
-                                      SplitViewController::RIGHT);
-  EXPECT_EQ(split_view_controller()->left_window(), window1.get());
-  EXPECT_EQ(split_view_controller()->right_window(), window2.get());
-}
-
 // Verify that you cannot start dragging the divider during its snap animation.
 TEST_P(SplitViewControllerTest, StartDraggingDividerDuringSnapAnimation) {
   const gfx::Rect bounds(0, 0, 400, 400);
diff --git a/ash/wm/splitview/split_view_drag_indicators.cc b/ash/wm/splitview/split_view_drag_indicators.cc
index 11c5c78..368bc553 100644
--- a/ash/wm/splitview/split_view_drag_indicators.cc
+++ b/ash/wm/splitview/split_view_drag_indicators.cc
@@ -42,15 +42,15 @@
 constexpr float kOtherHighlightScreenPrimaryAxisRatio = 0.03f;
 
 // Creates the widget responsible for displaying the indicators.
-std::unique_ptr<views::Widget> CreateWidget() {
+std::unique_ptr<views::Widget> CreateWidget(aura::Window* root_window) {
   auto widget = std::make_unique<views::Widget>();
   views::Widget::InitParams params;
   params.type = views::Widget::InitParams::TYPE_POPUP;
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
   params.accept_events = false;
-  params.parent = Shell::GetContainer(Shell::Get()->GetPrimaryRootWindow(),
-                                      kShellWindowId_OverlayContainer);
+  params.parent =
+      Shell::GetContainer(root_window, kShellWindowId_OverlayContainer);
   widget->set_focus_on_creation(false);
   widget->Init(std::move(params));
   return widget;
@@ -612,9 +612,10 @@
   DISALLOW_COPY_AND_ASSIGN(SplitViewDragIndicatorsView);
 };
 
-SplitViewDragIndicators::SplitViewDragIndicators() {
+SplitViewDragIndicators::SplitViewDragIndicators(aura::Window* root_window) {
   indicators_view_ = new SplitViewDragIndicatorsView();
-  widget_ = CreateWidget();
+  widget_ = CreateWidget(root_window);
+  widget_->SetBounds(GetWorkAreaBoundsNoOverlapWithShelf(root_window));
   widget_->SetContentsView(indicators_view_);
   widget_->Show();
 }
@@ -635,22 +636,9 @@
 }
 
 void SplitViewDragIndicators::SetWindowDraggingState(
-    WindowDraggingState window_dragging_state,
-    const gfx::Point& event_location) {
+    WindowDraggingState window_dragging_state) {
   if (window_dragging_state == current_window_dragging_state_)
     return;
-
-  // Reparent the widget if needed.
-  aura::Window* target = ash::window_util::GetRootWindowAt(event_location);
-  aura::Window* root_window = target->GetRootWindow();
-  if (widget_->GetNativeView()->GetRootWindow() != root_window) {
-    views::Widget::ReparentNativeView(
-        widget_->GetNativeView(),
-        Shell::GetContainer(root_window, kShellWindowId_OverlayContainer));
-    widget_->SetContentsView(indicators_view_);
-  }
-  widget_->SetBounds(GetWorkAreaBoundsNoOverlapWithShelf(root_window));
-
   current_window_dragging_state_ = window_dragging_state;
   indicators_view_->OnWindowDraggingStateChanged(window_dragging_state);
 }
diff --git a/ash/wm/splitview/split_view_drag_indicators.h b/ash/wm/splitview/split_view_drag_indicators.h
index 08999fe6..bc350d88 100644
--- a/ash/wm/splitview/split_view_drag_indicators.h
+++ b/ash/wm/splitview/split_view_drag_indicators.h
@@ -85,12 +85,11 @@
       WindowDraggingState non_snap_state,
       SplitViewController::SnapPosition snap_position);
 
-  SplitViewDragIndicators();
+  SplitViewDragIndicators(aura::Window* root_window);
   ~SplitViewDragIndicators();
 
   void SetDraggedWindow(aura::Window* dragged_window);
-  void SetWindowDraggingState(WindowDraggingState window_dragging_state,
-                              const gfx::Point& event_location);
+  void SetWindowDraggingState(WindowDraggingState window_dragging_state);
   void OnDisplayBoundsChanged();
   bool GetIndicatorTypeVisibilityForTesting(IndicatorType type) const;
   gfx::Rect GetLeftHighlightViewBoundsForTesting() const;
diff --git a/ash/wm/splitview/split_view_drag_indicators_unittest.cc b/ash/wm/splitview/split_view_drag_indicators_unittest.cc
index 788930a..e0c90fb 100644
--- a/ash/wm/splitview/split_view_drag_indicators_unittest.cc
+++ b/ash/wm/splitview/split_view_drag_indicators_unittest.cc
@@ -66,7 +66,7 @@
     overview_session_ = Shell::Get()->overview_controller()->overview_session();
     ASSERT_TRUE(overview_session_);
     split_view_drag_indicators_ =
-        overview_session_->split_view_drag_indicators();
+        overview_session_->grid_list()[0]->split_view_drag_indicators();
   }
 
   SplitViewController* split_view_controller() {
@@ -368,8 +368,9 @@
 // Verify when the window dragging state changes, the expected indicators will
 // become visible or invisible.
 TEST_F(SplitViewDragIndicatorsTest, SplitViewDragIndicatorsVisibility) {
-  auto indicator = std::make_unique<SplitViewDragIndicators>();
   std::unique_ptr<aura::Window> dragged_window(CreateTestWindow());
+  auto indicator = std::make_unique<SplitViewDragIndicators>(
+      dragged_window->GetRootWindow());
   indicator->SetDraggedWindow(dragged_window.get());
 
   auto to_int = [](IndicatorType type) { return static_cast<int>(type); };
@@ -388,12 +389,10 @@
     }
   };
 
-  // Check each state has the correct views displayed. Pass and empty point as
-  // the location since there is no need to reparent the widget. Verify that
-  // nothing is shown in state
-  // |SplitViewDragIndicators::WindowDraggingState::kNoDrag|.
+  // Check each state has the correct views displayed. Verify that nothing is
+  // shown in state |SplitViewDragIndicators::WindowDraggingState::kNoDrag|.
   indicator->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kNoDrag, gfx::Point());
+      SplitViewDragIndicators::WindowDraggingState::kNoDrag);
   check_helper(indicator.get(), 0);
 
   const int all = to_int(IndicatorType::kLeftHighlight) |
@@ -403,35 +402,34 @@
   // Verify that everything is visible in state
   // |SplitViewDragIndicators::WindowDraggingState::kFromOverview|.
   indicator->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kFromOverview,
-      gfx::Point());
+      SplitViewDragIndicators::WindowDraggingState::kFromOverview);
   check_helper(indicator.get(), all);
 
   // Verify that only one highlight shows up for the snap states.
   indicator->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kToSnapLeft, gfx::Point());
+      SplitViewDragIndicators::WindowDraggingState::kToSnapLeft);
   check_helper(indicator.get(), to_int(IndicatorType::kLeftHighlight));
   indicator->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kToSnapRight, gfx::Point());
+      SplitViewDragIndicators::WindowDraggingState::kToSnapRight);
   check_helper(indicator.get(), to_int(IndicatorType::kRightHighlight));
 
   // Verify that only snap previews are shown for window dragging from shelf.
   indicator->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kNoDrag, gfx::Point());
+      SplitViewDragIndicators::WindowDraggingState::kNoDrag);
   indicator->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kFromShelf, gfx::Point());
+      SplitViewDragIndicators::WindowDraggingState::kFromShelf);
   check_helper(indicator.get(), 0);
   indicator->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kToSnapLeft, gfx::Point());
+      SplitViewDragIndicators::WindowDraggingState::kToSnapLeft);
   check_helper(indicator.get(), to_int(IndicatorType::kLeftHighlight));
   indicator->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kFromShelf, gfx::Point());
+      SplitViewDragIndicators::WindowDraggingState::kFromShelf);
   check_helper(indicator.get(), 0);
   indicator->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kToSnapRight, gfx::Point());
+      SplitViewDragIndicators::WindowDraggingState::kToSnapRight);
   check_helper(indicator.get(), to_int(IndicatorType::kRightHighlight));
   indicator->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kFromShelf, gfx::Point());
+      SplitViewDragIndicators::WindowDraggingState::kFromShelf);
   check_helper(indicator.get(), 0);
 
   ScreenOrientationControllerTestApi orientation_api(
@@ -442,14 +440,14 @@
   ASSERT_EQ(OrientationLockType::kLandscapePrimary,
             orientation_api.GetCurrentOrientation());
   indicator->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kNoDrag, gfx::Point());
+      SplitViewDragIndicators::WindowDraggingState::kNoDrag);
   indicator->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kFromTop, gfx::Point());
+      SplitViewDragIndicators::WindowDraggingState::kFromTop);
   check_helper(indicator.get(), all);
   indicator->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kToSnapRight, gfx::Point());
+      SplitViewDragIndicators::WindowDraggingState::kToSnapRight);
   indicator->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kFromTop, gfx::Point());
+      SplitViewDragIndicators::WindowDraggingState::kFromTop);
   check_helper(indicator.get(), all);
 
   const int right_with_text = to_int(IndicatorType::kRightHighlight) |
@@ -462,71 +460,15 @@
   ASSERT_EQ(OrientationLockType::kPortraitPrimary,
             orientation_api.GetCurrentOrientation());
   indicator->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kNoDrag, gfx::Point());
+      SplitViewDragIndicators::WindowDraggingState::kNoDrag);
   indicator->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kFromTop, gfx::Point());
+      SplitViewDragIndicators::WindowDraggingState::kFromTop);
   check_helper(indicator.get(), right_with_text);
   indicator->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kToSnapRight, gfx::Point());
+      SplitViewDragIndicators::WindowDraggingState::kToSnapRight);
   indicator->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kFromTop, gfx::Point());
+      SplitViewDragIndicators::WindowDraggingState::kFromTop);
   check_helper(indicator.get(), right_with_text);
 }
 
-// Verify that the split view drag indicators widget reparents when starting a
-// drag on a different display.
-TEST_F(SplitViewDragIndicatorsTest, SplitViewDragIndicatorsWidgetReparenting) {
-  // Add two displays and one window on each display.
-  UpdateDisplay("600x600,600x600");
-  // DisplayConfigurationObserver enables mirror mode when tablet mode is
-  // enabled. Disable mirror mode to test multiple displays.
-  display_manager()->SetMirrorMode(display::MirrorMode::kOff, base::nullopt);
-  base::RunLoop().RunUntilIdle();
-
-  auto root_windows = Shell::Get()->GetAllRootWindows();
-  ASSERT_EQ(2u, root_windows.size());
-
-  const gfx::Rect primary_screen_bounds(0, 0, 600, 600);
-  const gfx::Rect secondary_screen_bounds(600, 0, 600, 600);
-  auto primary_screen_window(CreateTestWindow(primary_screen_bounds));
-  auto secondary_screen_window(CreateTestWindow(secondary_screen_bounds));
-
-  ToggleOverview();
-
-  // Select an item on the primary display and verify the drag indicators
-  // widget's parent is the primary root window.
-  OverviewItem* item = GetOverviewItemForWindow(primary_screen_window.get());
-  gfx::PointF start_location(item->target_bounds().CenterPoint());
-  overview_session_->InitiateDrag(item, start_location,
-                                  /*is_touch_dragging=*/true);
-  overview_session_->Drag(item, gfx::PointF(100.f, start_location.y()));
-  EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kFromOverview,
-            window_dragging_state());
-  EXPECT_EQ(root_windows[0], overview_session_->split_view_drag_indicators()
-                                 ->widget_->GetNativeView()
-                                 ->GetRootWindow());
-  // Drag the item in a way that neither opens the window nor activates
-  // splitview mode.
-  overview_session_->Drag(item,
-                          gfx::PointF(primary_screen_bounds.CenterPoint()));
-  overview_session_->CompleteDrag(
-      item, gfx::PointF(primary_screen_bounds.CenterPoint()));
-  ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
-  ASSERT_FALSE(split_view_controller()->InSplitViewMode());
-
-  // Select an item on the secondary display and verify the indicators widget
-  // has reparented to the secondary root window.
-  item = GetOverviewItemForWindow(secondary_screen_window.get(), 1);
-  start_location = item->target_bounds().CenterPoint();
-  overview_session_->InitiateDrag(item, start_location,
-                                  /*is_touch_dragging=*/true);
-  overview_session_->Drag(item, gfx::PointF(800.f, start_location.y()));
-  EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kFromOverview,
-            window_dragging_state());
-  EXPECT_EQ(root_windows[1], overview_session_->split_view_drag_indicators()
-                                 ->widget_->GetNativeView()
-                                 ->GetRootWindow());
-  overview_session_->CompleteDrag(item, start_location);
-}
-
 }  // namespace ash
diff --git a/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc b/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
index 0479a1a..5dcd058 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
@@ -98,8 +98,8 @@
 TabletModeWindowDragDelegate::TabletModeWindowDragDelegate()
     : split_view_controller_(
           SplitViewController::Get(Shell::GetPrimaryRootWindow())),
-      split_view_drag_indicators_(std::make_unique<SplitViewDragIndicators>()) {
-}
+      split_view_drag_indicators_(std::make_unique<SplitViewDragIndicators>(
+          Shell::GetPrimaryRootWindow())) {}
 
 TabletModeWindowDragDelegate::~TabletModeWindowDragDelegate() {
   if (dragged_window_) {
@@ -250,8 +250,7 @@
           is_window_considered_moved_,
           SplitViewDragIndicators::WindowDraggingState::kFromTop,
           GetSnapPosition(location_in_screen));
-  split_view_drag_indicators_->SetWindowDraggingState(window_dragging_state,
-                                                      location_in_screen);
+  split_view_drag_indicators_->SetWindowDraggingState(window_dragging_state);
 
   if (GetOverviewSession()) {
     GetOverviewSession()->OnWindowDragContinued(dragged_window_,
@@ -284,8 +283,7 @@
   split_view_controller_->OnWindowDragEnded(dragged_window_, snap_position,
                                             location_in_screen);
   split_view_drag_indicators_->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kNoDrag,
-      location_in_screen);
+      SplitViewDragIndicators::WindowDraggingState::kNoDrag);
 
   // Reset the dragged window's window shadow elevation.
   ::wm::SetShadowElevation(dragged_window_, original_shadow_elevation_);
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager.cc b/ash/wm/tablet_mode/tablet_mode_window_manager.cc
index 0a7d2e0..fca1ac11 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager.cc
@@ -8,6 +8,7 @@
 
 #include "ash/display/screen_orientation_controller.h"
 #include "ash/public/cpp/app_types.h"
+#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_properties.h"
@@ -16,6 +17,7 @@
 #include "ash/screen_util.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
+#include "ash/shell_delegate.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/overview_controller.h"
@@ -233,6 +235,33 @@
   return windows.empty() ? nullptr : windows[0];
 }
 
+// static
+bool TabletModeWindowManager::ShouldMinimizeTopWindowOnBack() {
+  if (!features::IsSwipingFromLeftEdgeToGoBackEnabled())
+    return false;
+
+  Shell* shell = Shell::Get();
+  if (!shell->tablet_mode_controller()->InTabletMode())
+    return false;
+
+  aura::Window* window = GetTopWindow();
+  if (!window)
+    return false;
+
+  const int app_type = window->GetProperty(aura::client::kAppType);
+  if (app_type != static_cast<int>(AppType::BROWSER) &&
+      app_type != static_cast<int>(AppType::CHROME_APP)) {
+    return false;
+  }
+
+  WindowState* window_state = WindowState::Get(window);
+  if (!window_state || !window_state->CanMinimize())
+    return false;
+
+  // Minimize the window if it is at the bottom page.
+  return !shell->shell_delegate()->CanGoBack(window);
+}
+
 void TabletModeWindowManager::Init() {
   {
     ScopedObserveWindowAnimation scoped_observe(GetTopWindow(), this,
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager.h b/ash/wm/tablet_mode/tablet_mode_window_manager.h
index bf934f4..c263a6e 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager.h
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager.h
@@ -49,8 +49,13 @@
   TabletModeWindowManager();
   ~TabletModeWindowManager() override;
 
+  // Returns the top window on MRU window list, or null if the list
+  // is empty.
   static aura::Window* GetTopWindow();
 
+  // Returns whether the top window should be minimized on back action.
+  static bool ShouldMinimizeTopWindowOnBack();
+
   void Init();
 
   // Stops tracking windows and returns them to their clamshell mode state. Work
diff --git a/ash/wm/toplevel_window_event_handler.cc b/ash/wm/toplevel_window_event_handler.cc
index efcabe2..f00ca20 100644
--- a/ash/wm/toplevel_window_event_handler.cc
+++ b/ash/wm/toplevel_window_event_handler.cc
@@ -11,11 +11,11 @@
 #include "ash/public/cpp/ash_features.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
-#include "ash/shell_delegate.h"
 #include "ash/wm/back_gesture_affordance.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/resize_shadow_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "ash/wm/tablet_mode/tablet_mode_window_manager.h"
 #include "ash/wm/window_resizer.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_state_observer.h"
@@ -103,7 +103,7 @@
 }
 
 // True if we can start swiping from left edge to go to previous page.
-bool CanStartGoingBack(aura::Window* target) {
+bool CanStartGoingBack() {
   if (!features::IsSwipingFromLeftEdgeToGoBackEnabled())
     return false;
 
@@ -126,18 +126,6 @@
     return false;
   }
 
-  views::Widget* widget = views::Widget::GetTopLevelWidgetForNativeView(target);
-  if (!widget)
-    return false;
-
-  aura::Window* native_window = widget->GetNativeWindow();
-  const int app_type = native_window->GetProperty(aura::client::kAppType);
-  // No need to show the back gesture affordance and go back if the active
-  // browser web contents can not go back.
-  if (app_type == static_cast<int>(AppType::BROWSER) ||
-      app_type == static_cast<int>(AppType::CHROME_APP)) {
-    return Shell::Get()->shell_delegate()->CanGoBack(widget->GetNativeWindow());
-  }
   return true;
 }
 
@@ -896,7 +884,7 @@
 bool ToplevelWindowEventHandler::HandleGoingBackFromLeftEdge(
     ui::GestureEvent* event) {
   aura::Window* target = static_cast<aura::Window*>(event->target());
-  if (!CanStartGoingBack(target))
+  if (!CanStartGoingBack())
     return false;
 
   gfx::Point screen_location = event->location();
@@ -924,16 +912,20 @@
       if (back_gesture_affordance_->IsActivated() ||
           (event->type() == ui::ET_SCROLL_FLING_START &&
            event->details().velocity_x() >= kFlingVelocityForGoingBack)) {
-        aura::Window* root_window =
-            window_util::GetRootWindowAt(screen_location);
-        ui::KeyEvent press_key_event(ui::ET_KEY_PRESSED, ui::VKEY_BROWSER_BACK,
-                                     ui::EF_NONE);
-        ignore_result(
-            root_window->GetHost()->SendEventToSink(&press_key_event));
-        ui::KeyEvent release_key_event(ui::ET_KEY_RELEASED,
+        if (TabletModeWindowManager::ShouldMinimizeTopWindowOnBack()) {
+          WindowState::Get(TabletModeWindowManager::GetTopWindow())->Minimize();
+        } else {
+          aura::Window* root_window =
+              window_util::GetRootWindowAt(screen_location);
+          ui::KeyEvent press_key_event(ui::ET_KEY_PRESSED,
                                        ui::VKEY_BROWSER_BACK, ui::EF_NONE);
-        ignore_result(
-            root_window->GetHost()->SendEventToSink(&release_key_event));
+          ignore_result(
+              root_window->GetHost()->SendEventToSink(&press_key_event));
+          ui::KeyEvent release_key_event(ui::ET_KEY_RELEASED,
+                                         ui::VKEY_BROWSER_BACK, ui::EF_NONE);
+          ignore_result(
+              root_window->GetHost()->SendEventToSink(&release_key_event));
+        }
         back_gesture_affordance_->Complete();
       } else {
         back_gesture_affordance_->Abort();
diff --git a/base/mac/scoped_objc_class_swizzler.h b/base/mac/scoped_objc_class_swizzler.h
index e18e4ab..545ac55 100644
--- a/base/mac/scoped_objc_class_swizzler.h
+++ b/base/mac/scoped_objc_class_swizzler.h
@@ -31,7 +31,17 @@
   // Return a callable function pointer for the replaced method. To call this
   // from the replacing function, the first two arguments should be |self| and
   // |_cmd|. These are followed by the (variadic) method arguments.
-  IMP GetOriginalImplementation();
+  IMP GetOriginalImplementation() const;
+
+  // Invoke the original function directly, optionally with some arguments.
+  // Prefer this to hanging onto pointers to the original implementation
+  // function or to casting the result of GetOriginalImplementation() yourself.
+  template <typename Ret, typename... Args>
+  Ret InvokeOriginal(id receiver, SEL selector, Args... args) const {
+    auto func = reinterpret_cast<Ret (*)(id, SEL, Args...)>(
+        GetOriginalImplementation());
+    return func(receiver, selector, args...);
+  }
 
  private:
   // Delegated constructor.
diff --git a/base/mac/scoped_objc_class_swizzler.mm b/base/mac/scoped_objc_class_swizzler.mm
index 20e5c569..0065ed7 100644
--- a/base/mac/scoped_objc_class_swizzler.mm
+++ b/base/mac/scoped_objc_class_swizzler.mm
@@ -30,7 +30,7 @@
     method_exchangeImplementations(old_selector_impl_, new_selector_impl_);
 }
 
-IMP ScopedObjCClassSwizzler::GetOriginalImplementation() {
+IMP ScopedObjCClassSwizzler::GetOriginalImplementation() const {
   // Note that while the swizzle is in effect the "new" method is actually
   // pointing to the original implementation, since they have been swapped.
   return method_getImplementation(new_selector_impl_);
diff --git a/base/mac/scoped_objc_class_swizzler_unittest.mm b/base/mac/scoped_objc_class_swizzler_unittest.mm
index 79820a3f..3aa5e9e 100644
--- a/base/mac/scoped_objc_class_swizzler_unittest.mm
+++ b/base/mac/scoped_objc_class_swizzler_unittest.mm
@@ -92,9 +92,7 @@
     EXPECT_EQ(6, [object_one method]);
     EXPECT_EQ(7, [object_two method]);
 
-    IMP original = swizzler.GetOriginalImplementation();
-    id expected_result = reinterpret_cast<id>(3);
-    EXPECT_EQ(expected_result, original(object_one, @selector(method)));
+    EXPECT_EQ(3, swizzler.InvokeOriginal<int>(object_one, @selector(method)));
   }
 
   EXPECT_EQ(3, [object_one method]);
@@ -113,10 +111,8 @@
     EXPECT_EQ(20, [ObjCClassSwizzlerTestOne function]);
     EXPECT_EQ(10, [ObjCClassSwizzlerTestTwo function]);
 
-    IMP original = swizzler.GetOriginalImplementation();
-    id expected_result = reinterpret_cast<id>(10);
-    EXPECT_EQ(expected_result,
-              original([ObjCClassSwizzlerTestOne class], @selector(function)));
+    EXPECT_EQ(10, swizzler.InvokeOriginal<int>([ObjCClassSwizzlerTestOne class],
+                                               @selector(function)));
   }
 
   EXPECT_EQ(10, [ObjCClassSwizzlerTestOne function]);
@@ -135,9 +131,7 @@
         @selector(alternate));
     EXPECT_EQ(9, [object_one method]);
 
-    IMP original = swizzler.GetOriginalImplementation();
-    id expected_result = reinterpret_cast<id>(3);
-    EXPECT_EQ(expected_result, original(object_one, @selector(method)));
+    EXPECT_EQ(3, swizzler.InvokeOriginal<int>(object_one, @selector(method)));
   }
 
   EXPECT_EQ(3, [object_one method]);
@@ -155,9 +149,7 @@
         @selector(childAlternate));
     EXPECT_EQ(15, [child method]);
 
-    IMP original = swizzler.GetOriginalImplementation();
-    id expected_result = reinterpret_cast<id>(3);
-    EXPECT_EQ(expected_result, original(child, @selector(method)));
+    EXPECT_EQ(3, swizzler.InvokeOriginal<int>(child, @selector(method)));
   }
 
   EXPECT_EQ(3, [child method]);
diff --git a/build/config/fuchsia/tests.cmx b/build/config/fuchsia/tests.cmx
index 43ab020..3b616dd7 100644
--- a/build/config/fuchsia/tests.cmx
+++ b/build/config/fuchsia/tests.cmx
@@ -11,6 +11,7 @@
       "zero"
     ],
     "services": [
+      "fuchsia.accessibility.semantics.SemanticsManager",
       "fuchsia.device.NameProvider",
       "fuchsia.deprecatedtimezone.Timezone",
       "fuchsia.fonts.Provider",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index af896cd..b7e41ea 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1221,6 +1221,11 @@
   "java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerProperties.java",
   "java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerViewBinder.java",
   "java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerView.java",
+  "java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarCoordinator.java",
+  "java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarMediator.java",
+  "java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarProperties.java",
+  "java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarViewBinder.java",
+  "java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarView.java",
   "java/src/org/chromium/chrome/browser/payments/JourneyLogger.java",
   "java/src/org/chromium/chrome/browser/payments/PackageManagerDelegate.java",
   "java/src/org/chromium/chrome/browser/payments/PaymentApp.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index 69ca95b..3973aa1 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -91,7 +91,9 @@
   "javatests/src/org/chromium/chrome/browser/device_dialog/ItemChooserDialogTest.java",
   "javatests/src/org/chromium/chrome/browser/device_dialog/UsbChooserDialogTest.java",
   "javatests/src/org/chromium/chrome/browser/directactions/CloseTabDirectActionHandlerTest.java",
-  "javatests/src/org/chromium/chrome/browser/directactions/DirectActionAvailabilityTest.java",
+  "javatests/src/org/chromium/chrome/browser/directactions/DirectActionAvailabilityCustomTabTest.java",
+  "javatests/src/org/chromium/chrome/browser/directactions/DirectActionAvailabilityTabbedTest.java",
+  "javatests/src/org/chromium/chrome/browser/directactions/DirectActionAvailabilityWebappTest.java",
   "javatests/src/org/chromium/chrome/browser/directactions/DirectActionTestRule.java",
   "javatests/src/org/chromium/chrome/browser/directactions/DirectActionTestUtils.java",
   "javatests/src/org/chromium/chrome/browser/directactions/DirectActionsInActivityTest.java",
diff --git a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
index dfbf1c46..6150132d 100644
--- a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
+++ b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
@@ -162,7 +162,7 @@
                     @Override
                     public void willCloseTab(Tab tab, boolean animate) {
                         if (normalTabModel.getCount() <= 1
-                                || mPropertyModel.get(IS_SHOWING_OVERVIEW)) {
+                                && mPropertyModel.get(IS_SHOWING_OVERVIEW)) {
                             setTabCarouselVisibility(false);
                         }
                     }
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
index 6a35731c..81b2646 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
@@ -41,6 +41,7 @@
 import org.chromium.base.GarbageCollectionTestUtils;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
@@ -73,6 +74,7 @@
 import java.util.LinkedList;
 import java.util.List;
 
+// clang-format off
 /** Tests for the {@link StartSurfaceLayout} */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
@@ -80,7 +82,10 @@
         "force-fieldtrials=Study/Group"})
 @Restriction(
         {UiRestriction.RESTRICTION_TYPE_PHONE, Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE})
+@DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.M,
+        message = "https://crbug.com/1023833")
 public class StartSurfaceLayoutTest {
+    // clang-format on
     private static final String BASE_PARAMS = "force-fieldtrial-params="
             + "Study.Group:soft-cleanup-delay/0/cleanup-delay/0/skip-slow-zooming/false"
             + "/zooming-min-sdk-version/19/zooming-min-memory-mb/512";
@@ -132,7 +137,9 @@
                         .getCurrentTabModelFilter()::isTabModelRestored));
 
         assertEquals(0, mTabListDelegate.getBitmapFetchCountForTesting());
-        // Only skip thumbnail releasing assertion when "warm" (large soft-cleanup-delay).
+        // Only skip thumbnail releasing assertion when "warm" (large soft-cleanup-delay) or in
+        // RenderTest.
+        // TODO(wychen): figure out why thumbnails are not released in RenderTest.
         mSkipAssertThumbnailsAreReleased = false;
     }
 
@@ -146,14 +153,16 @@
     @Feature({"RenderTest"})
     @CommandLineFlags.Add({BASE_PARAMS})
     public void testRenderGrid_3WebTabs() throws InterruptedException, IOException {
+        mSkipAssertThumbnailsAreReleased = true;
+
         prepareTabs(3, 0, mUrl);
         TabUiTestHelper.enterTabSwitcher(mActivityTestRule.getActivity());
         TabUiTestHelper.clickFirstCardFromTabSwitcher(mActivityTestRule.getActivity());
 
         enterGTS();
-        mRenderTestRule.render(
-                mActivityTestRule.getActivity().findViewById(R.id.tab_list_view), "3_web_tabs");
-        leaveGTS();
+        mRenderTestRule.render(mActivityTestRule.getActivity().findViewById(
+                                       org.chromium.chrome.tab_ui.R.id.tab_list_view),
+                "3_web_tabs");
     }
 
     @Test
@@ -161,14 +170,16 @@
     @Feature({"RenderTest"})
     @CommandLineFlags.Add({BASE_PARAMS})
     public void testRenderGrid_10WebTabs() throws InterruptedException, IOException {
+        mSkipAssertThumbnailsAreReleased = true;
+
         prepareTabs(10, 0, mUrl);
         TabUiTestHelper.enterTabSwitcher(mActivityTestRule.getActivity());
         TabUiTestHelper.clickFirstCardFromTabSwitcher(mActivityTestRule.getActivity());
 
         enterGTS();
-        mRenderTestRule.render(
-                mActivityTestRule.getActivity().findViewById(R.id.tab_list_view), "10_web_tabs");
-        leaveGTS();
+        mRenderTestRule.render(mActivityTestRule.getActivity().findViewById(
+                                       org.chromium.chrome.tab_ui.R.id.tab_list_view),
+                "10_web_tabs");
     }
 
     @Test
@@ -176,6 +187,8 @@
     @Feature({"RenderTest"})
     @CommandLineFlags.Add({BASE_PARAMS})
     public void testRenderGrid_10WebTabs_InitialScroll() throws InterruptedException, IOException {
+        mSkipAssertThumbnailsAreReleased = true;
+
         prepareTabs(10, 0, mUrl);
         TabUiTestHelper.enterTabSwitcher(mActivityTestRule.getActivity());
         TabUiTestHelper.clickNthCardFromTabSwitcher(mActivityTestRule.getActivity(),
@@ -184,9 +197,9 @@
 
         enterGTS();
         // Make sure the grid tab switcher is scrolled down to show the selected tab.
-        mRenderTestRule.render(mActivityTestRule.getActivity().findViewById(R.id.tab_list_view),
+        mRenderTestRule.render(mActivityTestRule.getActivity().findViewById(
+                                       org.chromium.chrome.tab_ui.R.id.tab_list_view),
                 "10_web_tabs-select_last");
-        leaveGTS();
     }
 
     @Test
@@ -194,6 +207,8 @@
     @Feature({"RenderTest"})
     @CommandLineFlags.Add({BASE_PARAMS})
     public void testRenderGrid_Incognito() throws InterruptedException, IOException {
+        mSkipAssertThumbnailsAreReleased = true;
+
         // Prepare some incognito tabs and enter tab switcher.
         prepareTabs(1, 3, mUrl);
         assertTrue(mActivityTestRule.getActivity().getCurrentTabModel().isIncognito());
@@ -201,28 +216,9 @@
         TabUiTestHelper.clickFirstCardFromTabSwitcher(mActivityTestRule.getActivity());
 
         enterGTS();
-        mRenderTestRule.render(mActivityTestRule.getActivity().findViewById(R.id.tab_list_view),
+        mRenderTestRule.render(mActivityTestRule.getActivity().findViewById(
+                                       org.chromium.chrome.tab_ui.R.id.tab_list_view),
                 "3_incognito_web_tabs");
-        leaveGTS();
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"RenderTest"})
-    @CommandLineFlags.Add({BASE_PARAMS})
-    public void testRenderGrid_3IncognitoNTPs() throws InterruptedException, IOException {
-        // Prepare some incognito native tabs and enter tab switcher.
-        // NTP in incognito mode is chosen for its consistency in look, and we don't have to mock
-        // away the MV tiles, login promo, feed, etc.
-        prepareTabs(1, 3, null);
-        assertTrue(mActivityTestRule.getActivity().getCurrentTabModel().isIncognito());
-        TabUiTestHelper.enterTabSwitcher(mActivityTestRule.getActivity());
-        TabUiTestHelper.clickFirstCardFromTabSwitcher(mActivityTestRule.getActivity());
-
-        enterGTS();
-        mRenderTestRule.render(mActivityTestRule.getActivity().findViewById(R.id.tab_list_view),
-                "3_incognito_ntps");
-        leaveGTS();
     }
 
     @Test
@@ -468,7 +464,7 @@
                 waitForCaptureRateControl();
             }
             int count = getCaptureCount();
-            onView(withId(R.id.tab_list_view))
+            onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
                     .perform(RecyclerViewActions.actionOnItemAtPosition(targetIndex, click()));
             CriteriaHelper.pollUiThread(() -> {
                 boolean doneHiding =
@@ -591,16 +587,16 @@
     public void testIncognitoEnterGts() throws InterruptedException {
         prepareTabs(1, 1, null);
         enterGTS();
-        onView(withId(R.id.tab_list_view))
+        onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
                 .check(TabCountAssertion.havingTabCount(1));
 
-        onView(withId(R.id.tab_list_view))
+        onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
                 .perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
         CriteriaHelper.pollInstrumentationThread(
                 () -> !mActivityTestRule.getActivity().getLayoutManager().overviewVisible());
 
         enterGTS();
-        onView(withId(R.id.tab_list_view))
+        onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
                 .check(TabCountAssertion.havingTabCount(1));
     }
 
@@ -613,16 +609,16 @@
         // Prepare two incognito tabs and enter tab switcher.
         prepareTabs(1, 2, mUrl);
         enterGTS();
-        onView(withId(R.id.tab_list_view))
+        onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
                 .check(TabCountAssertion.havingTabCount(2));
 
         for (int i = 0; i < mRepeat; i++) {
             switchTabModel(false);
-            onView(withId(R.id.tab_list_view))
+            onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
                     .check(TabCountAssertion.havingTabCount(1));
 
             switchTabModel(true);
-            onView(withId(R.id.tab_list_view))
+            onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
                     .check(TabCountAssertion.havingTabCount(2));
         }
         leaveGTS();
diff --git a/chrome/android/features/start_surface/internal/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceMediatorUnitTest.java b/chrome/android/features/start_surface/internal/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceMediatorUnitTest.java
index e2a6ebe5..12ee98d 100644
--- a/chrome/android/features/start_surface/internal/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceMediatorUnitTest.java
+++ b/chrome/android/features/start_surface/internal/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceMediatorUnitTest.java
@@ -194,12 +194,16 @@
         StartSurfaceMediator mediator = createStartSurfaceMediator(SurfaceMode.SINGLE_PANE);
         verify(mNormalTabModel).addObserver(mTabModelObserverCaptor.capture());
 
-        doReturn(1).when(mNormalTabModel).getCount();
+        doReturn(2).when(mNormalTabModel).getCount();
         mediator.showOverview(false);
         assertThat(mPropertyModel.get(IS_SHOWING_OVERVIEW), equalTo(true));
         assertThat(mPropertyModel.get(IS_TAB_CAROUSEL_VISIBLE), equalTo(true));
 
-        doReturn(0).when(mNormalTabModel).getCount();
+        mTabModelObserverCaptor.getValue().willCloseTab(mock(Tab.class), false);
+        assertThat(mPropertyModel.get(IS_SHOWING_OVERVIEW), equalTo(true));
+        assertThat(mPropertyModel.get(IS_TAB_CAROUSEL_VISIBLE), equalTo(true));
+
+        doReturn(1).when(mNormalTabModel).getCount();
         mTabModelObserverCaptor.getValue().willCloseTab(mock(Tab.class), false);
         assertThat(mPropertyModel.get(IS_SHOWING_OVERVIEW), equalTo(true));
         assertThat(mPropertyModel.get(IS_TAB_CAROUSEL_VISIBLE), equalTo(false));
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
index 3f7a49e..163ce8e 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
@@ -326,9 +326,9 @@
 
     private void setupDialogSelectionEditor() {
         assert mTabSelectionEditorController != null;
-        TabSelectionEditorActionProvider actionProvider = new TabSelectionEditorActionProvider(
-                mTabModelSelector, mTabSelectionEditorController,
-                TabSelectionEditorActionProvider.TabSelectionEditorAction.UNGROUP);
+        TabSelectionEditorActionProvider actionProvider =
+                new TabSelectionEditorActionProvider(mTabSelectionEditorController,
+                        TabSelectionEditorActionProvider.TabSelectionEditorAction.UNGROUP);
 
         String actionButtonText =
                 mContext.getString(R.string.tab_grid_dialog_selection_mode_remove);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProvider.java
index ef66c6b..c53a8c1 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProvider.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProvider.java
@@ -21,42 +21,65 @@
  * Provider of actions for a list of selected tabs in {@link TabSelectionEditorMediator}.
  */
 class TabSelectionEditorActionProvider {
-    @IntDef({TabSelectionEditorAction.UNGROUP, TabSelectionEditorAction.GROUP})
+    @IntDef({TabSelectionEditorAction.UNDEFINED_ACTION, TabSelectionEditorAction.GROUP,
+            TabSelectionEditorAction.UNGROUP, TabSelectionEditorAction.CLOSE})
     @Retention(RetentionPolicy.SOURCE)
     @interface TabSelectionEditorAction {
-        int GROUP = 0;
-        int UNGROUP = 1;
-        int NUM_ENTRIES = 2;
+        int UNDEFINED_ACTION = 0;
+        int GROUP = 1;
+        int UNGROUP = 2;
+        int CLOSE = 3;
+        int NUM_ENTRIES = 4;
     }
-    private final TabModelSelector mTabModelSelector;
+
     private final TabSelectionEditorCoordinator
             .TabSelectionEditorController mTabSelectionEditorController;
-    private final int mAction;
+    private final @TabSelectionEditorAction int mAction;
 
-    TabSelectionEditorActionProvider(TabModelSelector tabModelSelector,
+    /**
+     * Construct {@link TabSelectionEditorActionProvider} with customized process selected tabs
+     * action.
+     * @see TabSelectionEditorActionProvider#processSelectedTabs(List, TabModelSelector).
+     */
+    TabSelectionEditorActionProvider() {
+        mTabSelectionEditorController = null;
+        mAction = TabSelectionEditorAction.UNDEFINED_ACTION;
+    }
+
+    /**
+     * Construct {@link TabSelectionEditorActionProvider} with defined
+     * {@link TabSelectionEditorAction}.
+     *
+     * @param tabSelectionEditorController Controller that associated with the TabSelectionEditor.
+     * @param action {@link TabSelectionEditorAction} to provide.
+     */
+    TabSelectionEditorActionProvider(
             TabSelectionEditorCoordinator.TabSelectionEditorController tabSelectionEditorController,
             @TabSelectionEditorAction int action) {
-        mTabModelSelector = tabModelSelector;
         mTabSelectionEditorController = tabSelectionEditorController;
         mAction = action;
     }
 
     /**
      * Defines how to process {@code selectedTabs} based on the {@link TabSelectionEditorAction}
-     * specified in the constructor.
+     * specified in the constructor. If {@link TabSelectionEditorAction} is not specified, the
+     * caller must override this method.
      *
      * @param selectedTabs The list of selected tabs to process.
+     * @param tabModelSelector {@link TabModelSelector} to use.
      */
-    void processSelectedTabs(List<Tab> selectedTabs) {
-        assert mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter()
-                        instanceof TabGroupModelFilter;
+    void processSelectedTabs(List<Tab> selectedTabs, TabModelSelector tabModelSelector) {
+        assert !(mAction == TabSelectionEditorAction.GROUP)
+                        && !(mAction == TabSelectionEditorAction.UNGROUP)
+                || tabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter()
+                                instanceof TabGroupModelFilter;
 
         switch (mAction) {
             case TabSelectionEditorAction.GROUP:
-                Tab destinationTab = getDestinationTab(selectedTabs);
+                Tab destinationTab = getDestinationTab(selectedTabs, tabModelSelector);
 
                 TabGroupModelFilter tabGroupModelFilter =
-                        (TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider()
+                        (TabGroupModelFilter) tabModelSelector.getTabModelFilterProvider()
                                 .getCurrentTabModelFilter();
                 tabGroupModelFilter.mergeListOfTabsToGroup(
                         selectedTabs, destinationTab, false, true);
@@ -67,28 +90,35 @@
                 break;
             case TabSelectionEditorAction.UNGROUP:
                 TabGroupModelFilter filter =
-                        (TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider()
+                        (TabGroupModelFilter) tabModelSelector.getTabModelFilterProvider()
                                 .getCurrentTabModelFilter();
                 for (Tab tab : selectedTabs) {
                     filter.moveTabOutOfGroup(tab.getId());
                 }
                 mTabSelectionEditorController.hide();
                 break;
+            case TabSelectionEditorAction.CLOSE:
+                tabModelSelector.getCurrentModel().closeMultipleTabs(selectedTabs, true);
+                mTabSelectionEditorController.hide();
+                break;
+            case TabSelectionEditorAction.UNDEFINED_ACTION:
             default:
-                assert false;
+                assert false : "TabSelectionEditorActionProvider must override"
+                               + "processSelectedTab() if mAction is not pre-defined with"
+                               + "TabSelectionEditorAction.";
         }
     }
 
     /**
      * @return The {@link Tab} that has the greatest index in TabModel among the given list of tabs.
      */
-    private Tab getDestinationTab(List<Tab> tabs) {
+    private Tab getDestinationTab(List<Tab> tabs, TabModelSelector tabModelSelector) {
         int greatestIndex = TabModel.INVALID_TAB_INDEX;
         for (int i = 0; i < tabs.size(); i++) {
             int index = TabModelUtils.getTabIndexById(
-                    mTabModelSelector.getCurrentModel(), tabs.get(i).getId());
+                    tabModelSelector.getCurrentModel(), tabs.get(i).getId());
             greatestIndex = Math.max(index, greatestIndex);
         }
-        return mTabModelSelector.getCurrentModel().getTabAt(greatestIndex);
+        return tabModelSelector.getCurrentModel().getTabAt(greatestIndex);
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java
index 3abfada..d523706d 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java
@@ -94,7 +94,7 @@
             }
 
             if (mActionProvider == null) return;
-            mActionProvider.processSelectedTabs(selectedTabs);
+            mActionProvider.processSelectedTabs(selectedTabs, mTabModelSelector);
         }
     };
 
@@ -163,8 +163,8 @@
         mTabModelSelector.addObserver(mTabModelSelectorObserver);
 
         // Default action for action button is to group selected tabs.
-        mActionProvider = new TabSelectionEditorActionProvider(mTabModelSelector, this,
-                TabSelectionEditorActionProvider.TabSelectionEditorAction.GROUP);
+        mActionProvider = new TabSelectionEditorActionProvider(
+                this, TabSelectionEditorActionProvider.TabSelectionEditorAction.GROUP);
 
         if (mPositionProvider != null) {
             mModel.set(TabSelectionEditorProperties.SELECTION_EDITOR_GLOBAL_LAYOUT_LISTENER,
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java
index 5a1a891f..9743294 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java
@@ -265,7 +265,7 @@
             Tab previousTab = previousTabModel.getTabAt(previousTabIndex);
 
             ChromeTabUtils.newTabFromMenu(InstrumentationRegistry.getInstrumentation(),
-                    rule.getActivity(), isIncognito, true);
+                    rule.getActivity(), isIncognito, url == null);
 
             if (url != null) rule.loadUrl(url);
 
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProviderUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProviderUnitTest.java
index 149c73b..68c0f96 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProviderUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProviderUnitTest.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.tasks.tab_management;
 
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -28,6 +29,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelFilterProvider;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
 import org.chromium.chrome.test.util.browser.Features;
@@ -36,6 +38,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Tests for {@link TabSelectionEditorActionProvider}.
@@ -97,11 +100,10 @@
     @Test
     public void testGroupAction() {
         TabSelectionEditorActionProvider tabSelectionEditorActionProvider =
-                new TabSelectionEditorActionProvider(mTabModelSelector,
-                        mTabSelectionEditorController,
+                new TabSelectionEditorActionProvider(mTabSelectionEditorController,
                         TabSelectionEditorActionProvider.TabSelectionEditorAction.GROUP);
         List<Tab> selectedTabs = new ArrayList<>(Arrays.asList(mTab1, mTab2));
-        tabSelectionEditorActionProvider.processSelectedTabs(selectedTabs);
+        tabSelectionEditorActionProvider.processSelectedTabs(selectedTabs, mTabModelSelector);
 
         verify(mTabGroupModelFilter)
                 .mergeListOfTabsToGroup(eq(selectedTabs), eq(mTab2), eq(false), eq(true));
@@ -111,17 +113,46 @@
     @Test
     public void testUngroupAction() {
         TabSelectionEditorActionProvider tabSelectionEditorActionProvider =
-                new TabSelectionEditorActionProvider(mTabModelSelector,
-                        mTabSelectionEditorController,
+                new TabSelectionEditorActionProvider(mTabSelectionEditorController,
                         TabSelectionEditorActionProvider.TabSelectionEditorAction.UNGROUP);
         List<Tab> selectedTabs = new ArrayList<>(Arrays.asList(mTab1, mTab2));
-        tabSelectionEditorActionProvider.processSelectedTabs(selectedTabs);
+        tabSelectionEditorActionProvider.processSelectedTabs(selectedTabs, mTabModelSelector);
 
         verify(mTabGroupModelFilter).moveTabOutOfGroup(TAB1_ID);
         verify(mTabGroupModelFilter).moveTabOutOfGroup(TAB2_ID);
         verify(mTabSelectionEditorController).hide();
     }
 
+    @Test
+    public void testCloseAction() {
+        TabSelectionEditorActionProvider tabSelectionEditorActionProvider =
+                new TabSelectionEditorActionProvider(mTabSelectionEditorController,
+                        TabSelectionEditorActionProvider.TabSelectionEditorAction.CLOSE);
+        List<Tab> selectedTabs = new ArrayList<>(Arrays.asList(mTab1, mTab2));
+        tabSelectionEditorActionProvider.processSelectedTabs(selectedTabs, mTabModelSelector);
+
+        verify(mTabModel).closeMultipleTabs(selectedTabs, true);
+        verify(mTabSelectionEditorController).hide();
+    }
+
+    @Test
+    public void testCustomizeAction() {
+        AtomicBoolean isProcessed = new AtomicBoolean();
+
+        TabSelectionEditorActionProvider tabSelectionEditorActionProvider =
+                new TabSelectionEditorActionProvider() {
+                    @Override
+                    void processSelectedTabs(
+                            List<Tab> selectedTabs, TabModelSelector tabModelSelector) {
+                        isProcessed.set(true);
+                    }
+                };
+        List<Tab> selectedTabs = new ArrayList<>(Arrays.asList(mTab1, mTab2));
+        tabSelectionEditorActionProvider.processSelectedTabs(selectedTabs, mTabModelSelector);
+
+        assertTrue(isProcessed.get());
+    }
+
     private Tab prepareTab(int id, String title) {
         Tab tab = mock(Tab.class);
         when(tab.getView()).thenReturn(mock(View.class));
diff --git a/chrome/android/java/res/layout/ephemeral_tab_toolbar.xml b/chrome/android/java/res/layout/ephemeral_tab_toolbar.xml
index 29eb4724..897848eb 100644
--- a/chrome/android/java/res/layout/ephemeral_tab_toolbar.xml
+++ b/chrome/android/java/res/layout/ephemeral_tab_toolbar.xml
@@ -23,16 +23,16 @@
         <RelativeLayout
             android:layout_width="match_parent"
             android:layout_height="62dp"
+            android:paddingStart="8dp"
             android:orientation="horizontal">
 
             <org.chromium.ui.widget.ChromeImageView
                 android:id="@+id/favicon"
                 android:layout_width="48dp"
                 android:layout_height="48dp"
-                android:layout_marginStart="8dp"
-                android:layout_marginEnd="8dp"
                 android:layout_marginTop="12dp"
                 android:padding="12dp"
+                android:layout_alignParentStart="true"
                 android:scaleType="fitCenter"
                 tools:ignore="ContentDescription" />
 
@@ -62,6 +62,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="16dp"
+                android:layout_marginStart="8dp"
                 android:layout_toStartOf="@id/open_in_new_tab"
                 android:layout_toEndOf="@id/favicon"
                 android:textAlignment="viewStart"
@@ -73,6 +74,7 @@
                 android:id="@+id/security_icon"
                 android:layout_width="16dp"
                 android:layout_height="16dp"
+                android:layout_marginStart="8dp"
                 android:layout_below="@id/ephemeral_tab_text"
                 android:layout_toEndOf="@id/favicon"
                 android:layout_marginTop="4dp"
diff --git a/chrome/android/java/res/layout/payment_handler_content.xml b/chrome/android/java/res/layout/payment_handler_content.xml
index a5defabf..b3af8a30 100644
--- a/chrome/android/java/res/layout/payment_handler_content.xml
+++ b/chrome/android/java/res/layout/payment_handler_content.xml
@@ -4,11 +4,7 @@
      found in the LICENSE file. -->
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:background="@color/sheet_bg_color"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
-    <org.chromium.chrome.browser.ui.widget.FadingShadowView
-        android:id="@+id/shadow"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/action_bar_shadow_height"/>
+
 </FrameLayout>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetCoordinator.java
index 6574914..b81a9dbe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetCoordinator.java
@@ -244,9 +244,10 @@
 
     @Override
     public void close(boolean animate) {
-        if (mBottomSheetController.get() == null) return;
-        if (!isHidden()) mBottomSheetController.get().hideContent(this, animate);
-        mBottomSheetController.get().removeObserver(mSheetObserver);
+        BottomSheetController controller = mBottomSheetController.get();
+        if (controller == null) return;
+        controller.hideContent(this, animate);
+        controller.removeObserver(mSheetObserver);
         mMediator.clear();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerCoordinator.java
index 9d412ba9..552d2a8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerCoordinator.java
@@ -8,6 +8,7 @@
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeVersionInfo;
 import org.chromium.chrome.browser.WebContentsFactory;
+import org.chromium.chrome.browser.payments.handler.toolbar.PaymentHandlerToolbarCoordinator;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 import org.chromium.components.embedder_support.view.ContentView;
 import org.chromium.content_public.browser.LoadUrlParams;
@@ -59,7 +60,11 @@
         bottomSheetController.addObserver(mediator);
         webContents.addObserver(mediator);
 
-        PaymentHandlerView view = new PaymentHandlerView(activity, webContents, webContentView);
+        PaymentHandlerToolbarCoordinator toolbarCoordinator = new PaymentHandlerToolbarCoordinator(
+                activity, webContents, () -> { mHider.run(); });
+        PaymentHandlerView view = new PaymentHandlerView(
+                activity, webContents, webContentView, toolbarCoordinator.getView());
+        assert toolbarCoordinator.getToolbarHeightPx() == view.getToolbarHeightPx();
         PropertyModelChangeProcessor changeProcessor =
                 PropertyModelChangeProcessor.create(model, view, PaymentHandlerViewBinder::bind);
         mHider = () -> {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java
index a1bf14f..7151d2c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java
@@ -23,6 +23,8 @@
         extends WebContentsObserver implements BottomSheetObserver {
     private final PropertyModel mModel;
     private final Runnable mHider;
+    /** Postfixed with "Ref" to distinguish from mWebContent in WebContentsObserver. */
+    private final WebContents mWebContentsRef;
 
     /**
      * Build a new mediator that handle events from outside the payment handler component.
@@ -35,28 +37,17 @@
     /* package */ PaymentHandlerMediator(
             PropertyModel model, Runnable hider, WebContents webContents) {
         super(webContents);
+        mWebContentsRef = webContents;
         mModel = model;
         mHider = hider;
     }
 
-    /**
-     * Hide the bottom-sheet if weak-ref of web-contents refers to null.
-     * @return Return true if the sheet is hidden.
-     */
-    private boolean hideSheetIfWebContentsNotExist() {
-        if (mWebContents != null) return false;
-        // TODO(maxlg): how to inform the service worker when the web-contents is missing.
-        mHider.run();
-        return true;
-    }
-
     // BottomSheetObserver:
     @Override
     public void onSheetStateChanged(@SheetState int newState) {
-        if (hideSheetIfWebContentsNotExist()) return;
         switch (newState) {
             case BottomSheetController.SheetState.HIDDEN:
-                ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindow(mWebContents.get());
+                ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindow(mWebContentsRef);
                 mHider.run();
                 break;
         }
@@ -88,28 +79,25 @@
     // WebContentsObserver:
     @Override
     public void didFinishLoad(long frameId, String validatedUrl, boolean isMainFrame) {
-        if (hideSheetIfWebContentsNotExist()) return;
-        if (!SslValidityChecker.isValidPageInPaymentHandlerWindow(mWebContents.get())) {
+        if (!SslValidityChecker.isValidPageInPaymentHandlerWindow(mWebContentsRef)) {
             ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindowForInsecureNavigation(
-                    mWebContents.get());
+                    mWebContentsRef);
             mHider.run();
         }
     }
 
     @Override
     public void didAttachInterstitialPage() {
-        if (hideSheetIfWebContentsNotExist()) return;
         ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindowForInsecureNavigation(
-                mWebContents.get());
+                mWebContentsRef);
         mHider.run();
     }
 
     @Override
     public void didFailLoad(
             boolean isMainFrame, int errorCode, String description, String failingUrl) {
-        if (hideSheetIfWebContentsNotExist()) return;
         // TODO(crbug.com/1017926): Respond to service worker with the net error.
-        ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindow(mWebContents.get());
+        ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindow(mWebContentsRef);
         mHider.run();
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerView.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerView.java
index a3454e2..dc64b1ef 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerView.java
@@ -11,13 +11,10 @@
 import android.view.ViewGroup.LayoutParams;
 import android.widget.FrameLayout;
 
-import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.thinwebview.ThinWebView;
 import org.chromium.chrome.browser.thinwebview.ThinWebViewFactory;
-import org.chromium.chrome.browser.ui.widget.FadingShadow;
-import org.chromium.chrome.browser.ui.widget.FadingShadowView;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
 import org.chromium.components.embedder_support.view.ContentView;
 import org.chromium.content_public.browser.WebContents;
@@ -25,12 +22,12 @@
 
 /** PaymentHandler UI. */
 /* package */ class PaymentHandlerView implements BottomSheetContent {
-    private final int mToolbarHeightPx;
     private final View mToolbarView;
     private final FrameLayout mContentView;
     private final ThinWebView mThinWebView;
     private final Handler mReflowHandler = new Handler();
     private final int mTabHeight;
+    private final int mToolbarHeightPx;
 
     /**
      * Construct the PaymentHandlerView.
@@ -39,13 +36,12 @@
      * @param webContents The web-content of the payment-handler web-app.
      * @param webContentView The {@link ContentView} that has been contructed with the web-content.
      */
-    /* package */ PaymentHandlerView(
-            ChromeActivity activity, WebContents webContents, ContentView webContentView) {
-        mToolbarHeightPx = activity.getResources().getDimensionPixelSize(
-                R.dimen.custom_tabs_control_container_height);
+    /* package */ PaymentHandlerView(ChromeActivity activity, WebContents webContents,
+            ContentView webContentView, View toolbarView) {
         mTabHeight = activity.getActivityTab().getHeight();
-
-        mToolbarView = LayoutInflater.from(activity).inflate(R.layout.custom_tabs_toolbar, null);
+        mToolbarView = toolbarView;
+        mToolbarHeightPx =
+                activity.getResources().getDimensionPixelSize(R.dimen.preview_tab_toolbar_height);
         mContentView = (FrameLayout) LayoutInflater.from(activity).inflate(
                 R.layout.payment_handler_content, null);
 
@@ -63,13 +59,10 @@
      */
     private void initContentView(ChromeActivity activity, ThinWebView thinWebView,
             WebContents webContents, ContentView webContentView) {
-        FadingShadowView shadow = mContentView.findViewById(R.id.shadow);
-        shadow.init(ApiCompatibilityUtils.getColor(
-                            activity.getResources(), R.color.toolbar_shadow_color),
-                FadingShadow.POSITION_TOP);
         assert webContentView.getParent() == null;
         thinWebView.attachWebContents(webContents, webContentView);
-        mContentView.setPadding(/*left=*/0, /*top=*/mToolbarHeightPx, /*right=*/0, /*bottom=*/0);
+        mContentView.setPadding(
+                /*left=*/0, /*top=*/mToolbarHeightPx, /*right=*/0, /*bottom=*/0);
         mContentView.addView(thinWebView.getView(), /*index=*/0);
     }
 
@@ -89,6 +82,10 @@
         mThinWebView.getView().setLayoutParams(params);
     }
 
+    /* package */ int getToolbarHeightPx() {
+        return mToolbarHeightPx;
+    }
+
     // BottomSheetContent:
     @Override
     public View getContentView() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarCoordinator.java
new file mode 100644
index 0000000..59fa48e
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarCoordinator.java
@@ -0,0 +1,61 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.payments.handler.toolbar;
+
+import android.content.Context;
+import android.view.View;
+
+import org.chromium.components.security_state.ConnectionSecurityLevel;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
+
+/**
+ * PaymentHandlerToolbar coordinator, which owns the component overall, i.e., creates other objects
+ * in the component and connects them. It decouples the implementation of this component from other
+ * components and acts as the point of contact between them. Any code in this component that needs
+ * to interact with another component does that through this coordinator.
+ */
+public class PaymentHandlerToolbarCoordinator {
+    private Runnable mHider;
+    private PaymentHandlerToolbarView mToolbarView;
+
+    /**
+     * Observer for the error of the payment handler toolbar.
+     */
+    public interface ErrorObserver {
+        /**
+         * Called when the UI gets an error
+         */
+        void onError();
+    }
+
+    /** Constructs the payment-handler toolbar component coordinator. */
+    public PaymentHandlerToolbarCoordinator(
+            Context context, WebContents webContents, ErrorObserver errorObserver) {
+        mToolbarView = new PaymentHandlerToolbarView(context);
+        PropertyModel model = new PropertyModel.Builder(PaymentHandlerToolbarProperties.ALL_KEYS)
+                                      .with(PaymentHandlerToolbarProperties.PROGRESS_VISIBLE, true)
+                                      .with(PaymentHandlerToolbarProperties.LOAD_PROGRESS, 0)
+                                      .with(PaymentHandlerToolbarProperties.SECURITY_ICON,
+                                              ConnectionSecurityLevel.NONE)
+                                      .build();
+        PaymentHandlerToolbarMediator mediator =
+                new PaymentHandlerToolbarMediator(model, webContents, errorObserver);
+        webContents.addObserver(mediator);
+        PropertyModelChangeProcessor changeProcessor = PropertyModelChangeProcessor.create(
+                model, mToolbarView, PaymentHandlerToolbarViewBinder::bind);
+    }
+
+    /** @return The height of the toolbar in px. */
+    public int getToolbarHeightPx() {
+        return mToolbarView.getToolbarHeightPx();
+    }
+
+    /** @return The toolbar of the PaymentHandler. */
+    public View getView() {
+        return mToolbarView.getView();
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarMediator.java
new file mode 100644
index 0000000..1fca49a
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarMediator.java
@@ -0,0 +1,134 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.payments.handler.toolbar;
+
+import android.os.Handler;
+import android.support.annotation.DrawableRes;
+
+import org.chromium.base.Log;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.payments.handler.toolbar.PaymentHandlerToolbarCoordinator.ErrorObserver;
+import org.chromium.chrome.browser.ssl.SecurityStateModel;
+import org.chromium.components.security_state.ConnectionSecurityLevel;
+import org.chromium.components.url_formatter.UrlFormatter;
+import org.chromium.content_public.browser.NavigationHandle;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.content_public.browser.WebContentsObserver;
+import org.chromium.ui.modelutil.PropertyModel;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * PaymentHandlerToolbar mediator, which is responsible for receiving events from the view and
+ * notifies the backend (the coordinator).
+ */
+/* package */ class PaymentHandlerToolbarMediator extends WebContentsObserver {
+    private static final String TAG = "PaymentHandlerTb";
+    /** The delay (four video frames - for 60Hz) after which the hide progress will be hidden. */
+    private static final long HIDE_PROGRESS_BAR_DELAY_MS = (1000 / 60) * 4;
+
+    private final PropertyModel mModel;
+    private final ErrorObserver mErrorObserver;
+    /** The handler to delay hiding the progress bar. */
+    private Handler mHideProgressBarHandler;
+    /** Postfixed with "Ref" to distinguish from mWebContent in WebContentsObserver. */
+    private final WebContents mWebContentsRef;
+
+    /**
+     * Build a new mediator that handle events from outside the payment handler toolbar component.
+     * @param model The {@link PaymentHandlerToolbarProperties} that holds all the view state for
+     *         the payment handler toolbar component.
+     * @param hider The callback to clean up the {@link ErrorObserver} when the sheet is
+     *         hidden.
+     * @param webContents The web-contents that loads the payment app.
+     */
+    /* package */ PaymentHandlerToolbarMediator(
+            PropertyModel model, WebContents webContents, ErrorObserver errorObserver) {
+        super(webContents);
+        mWebContentsRef = webContents;
+        mModel = model;
+        mErrorObserver = errorObserver;
+    }
+
+    // WebContentsObserver:
+    @Override
+    public void didFinishLoad(long frameId, String validatedUrl, boolean isMainFrame) {
+        // Hides the Progress Bar after a delay to make sure it is rendered for at least
+        // a few frames, otherwise its completion won't be visually noticeable.
+        mHideProgressBarHandler = new Handler();
+        mHideProgressBarHandler.postDelayed(() -> {
+            mModel.set(PaymentHandlerToolbarProperties.PROGRESS_VISIBLE, false);
+            mHideProgressBarHandler = null;
+        }, HIDE_PROGRESS_BAR_DELAY_MS);
+        return;
+    }
+
+    @Override
+    public void didFailLoad(
+            boolean isMainFrame, int errorCode, String description, String failingUrl) {
+        mModel.set(PaymentHandlerToolbarProperties.PROGRESS_VISIBLE, false);
+    }
+
+    @Override
+    public void didFinishNavigation(NavigationHandle navigation) {
+        if (navigation.hasCommitted() && navigation.isInMainFrame()) {
+            String url = navigation.getUrl();
+            String origin = UrlFormatter.formatUrlForSecurityDisplayOmitScheme(url);
+            try {
+                mModel.set(PaymentHandlerToolbarProperties.PROGRESS_VISIBLE, false);
+                mModel.set(PaymentHandlerToolbarProperties.ORIGIN, new URI(origin));
+            } catch (URISyntaxException e) {
+                Log.e(TAG, "Failed to instantiate URI with the origin \"%s\", whose url is \"%s\".",
+                        origin, url);
+                mErrorObserver.onError();
+                return;
+            }
+        }
+    }
+
+    @Override
+    public void titleWasSet(String title) {
+        mModel.set(PaymentHandlerToolbarProperties.TITLE, title);
+    }
+
+    @Override
+    public void loadProgressChanged(float progress) {
+        if (progress == 1.0) return;
+        // If the load restarts when the progress bar is waiting to hide, cancel the handler
+        // callbacks.
+        if (mHideProgressBarHandler != null) {
+            mHideProgressBarHandler.removeCallbacksAndMessages(null);
+            mHideProgressBarHandler = null;
+        }
+        mModel.set(PaymentHandlerToolbarProperties.PROGRESS_VISIBLE, true);
+        mModel.set(PaymentHandlerToolbarProperties.LOAD_PROGRESS, progress);
+    }
+
+    @DrawableRes
+    private static int getSecurityIconResource(@ConnectionSecurityLevel int securityLevel) {
+        switch (securityLevel) {
+            case ConnectionSecurityLevel.NONE:
+            case ConnectionSecurityLevel.WARNING:
+                return R.drawable.omnibox_info;
+            case ConnectionSecurityLevel.DANGEROUS:
+                return R.drawable.omnibox_not_secure_warning;
+            case ConnectionSecurityLevel.SECURE_WITH_POLICY_INSTALLED_CERT:
+            case ConnectionSecurityLevel.SECURE:
+            case ConnectionSecurityLevel.EV_SECURE:
+                return R.drawable.omnibox_https_valid;
+            default:
+                assert false;
+        }
+        return 0;
+    }
+
+    @Override
+    public void didChangeVisibleSecurityState() {
+        int securityLevel = SecurityStateModel.getSecurityLevelForWebContents(mWebContentsRef);
+        mModel.set(PaymentHandlerToolbarProperties.SECURITY_ICON,
+                getSecurityIconResource(securityLevel));
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarProperties.java
new file mode 100644
index 0000000..6b76baf
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarProperties.java
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.payments.handler.toolbar;
+
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableFloatPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
+
+import java.net.URI;
+
+/** PaymentHandlerToolbar UI properties, which fully describe the state of the UI. */
+/* package */ class PaymentHandlerToolbarProperties {
+    /* package */ static final WritableObjectPropertyKey<URI> ORIGIN =
+            new WritableObjectPropertyKey<>();
+
+    /* package */ static final WritableObjectPropertyKey<String> TITLE =
+            new WritableObjectPropertyKey<>();
+
+    /* package */ static final WritableFloatPropertyKey LOAD_PROGRESS =
+            new WritableFloatPropertyKey();
+
+    /* package */ static final WritableBooleanPropertyKey PROGRESS_VISIBLE =
+            new WritableBooleanPropertyKey();
+
+    /* package */ static final WritableIntPropertyKey SECURITY_ICON = new WritableIntPropertyKey();
+
+    /* package */ static final PropertyKey[] ALL_KEYS =
+            new PropertyKey[] {ORIGIN, TITLE, LOAD_PROGRESS, PROGRESS_VISIBLE, SECURITY_ICON};
+
+    // Prevent instantiation.
+    private PaymentHandlerToolbarProperties() {}
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarView.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarView.java
new file mode 100644
index 0000000..fecfd99
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarView.java
@@ -0,0 +1,63 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.payments.handler.toolbar;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ui.widget.FadingShadow;
+import org.chromium.chrome.browser.ui.widget.FadingShadowView;
+
+/** PaymentHandlerToolbar UI. */
+/* package */ class PaymentHandlerToolbarView {
+    private final int mToolbarHeightPx;
+    private final View mToolbarView;
+
+    /* package */ final TextView mOriginView;
+    /* package */ final TextView mTitleView;
+    /* package */ final ProgressBar mProgressBar;
+    /* package */ final ImageView mSecurityIconView;
+
+    /**
+     * Construct the PaymentHandlerToolbarView.
+     *
+     * @param context The context where the bottome-sheet should be shown.
+     */
+    /* package */ PaymentHandlerToolbarView(Context context) {
+        mToolbarHeightPx =
+                context.getResources().getDimensionPixelSize(R.dimen.preview_tab_toolbar_height);
+
+        mToolbarView = LayoutInflater.from(context).inflate(R.layout.ephemeral_tab_toolbar, null);
+        mOriginView = mToolbarView.findViewById(R.id.ephemeral_tab_caption);
+        mTitleView = mToolbarView.findViewById(R.id.ephemeral_tab_text);
+        mProgressBar = mToolbarView.findViewById(R.id.progress_bar);
+        mSecurityIconView = mToolbarView.findViewById(R.id.security_icon);
+
+        // These parts from ephemeral_tab_toolbar are not needed in this component.
+        mToolbarView.findViewById(R.id.open_in_new_tab).setVisibility(View.GONE);
+        mToolbarView.findViewById(R.id.favicon).setVisibility(View.GONE);
+
+        FadingShadowView shadow = mToolbarView.findViewById(R.id.shadow);
+        shadow.init(ApiCompatibilityUtils.getColor(
+                            context.getResources(), R.color.toolbar_shadow_color),
+                FadingShadow.POSITION_TOP);
+    }
+
+    /** @return The height of the toolbar in px. */
+    /* package */ int getToolbarHeightPx() {
+        return mToolbarHeightPx;
+    }
+
+    /** @return The layout of the PaymentHandlerToolbar. */
+    /* package */ View getView() {
+        return mToolbarView;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarViewBinder.java
new file mode 100644
index 0000000..263802f
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarViewBinder.java
@@ -0,0 +1,34 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.payments.handler.toolbar;
+
+import android.view.View;
+
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * PaymentHandlerToolbar view binder, which is stateless. It is called to bind a given model to a
+ * given view. Should contain as little business logic as possible.
+ */
+/* package */ class PaymentHandlerToolbarViewBinder {
+    /* package */ static void bind(
+            PropertyModel model, PaymentHandlerToolbarView view, PropertyKey propertyKey) {
+        if (PaymentHandlerToolbarProperties.ORIGIN == propertyKey) {
+            view.mOriginView.setText(model.get(PaymentHandlerToolbarProperties.ORIGIN).toString());
+        } else if (PaymentHandlerToolbarProperties.TITLE == propertyKey) {
+            view.mTitleView.setText(model.get(PaymentHandlerToolbarProperties.TITLE));
+        } else if (PaymentHandlerToolbarProperties.LOAD_PROGRESS == propertyKey) {
+            view.mProgressBar.setProgress(
+                    Math.round(model.get(PaymentHandlerToolbarProperties.LOAD_PROGRESS) * 100));
+        } else if (PaymentHandlerToolbarProperties.PROGRESS_VISIBLE == propertyKey) {
+            boolean visible = model.get(PaymentHandlerToolbarProperties.PROGRESS_VISIBLE);
+            view.mProgressBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+        } else if (PaymentHandlerToolbarProperties.SECURITY_ICON == propertyKey) {
+            int securityIconResource = model.get(PaymentHandlerToolbarProperties.SECURITY_ICON);
+            view.mSecurityIconView.setImageResource(securityIconResource);
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/themes/RadioButtonGroupThemePreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/themes/RadioButtonGroupThemePreference.java
index f0ed2a4d..eff92cb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/themes/RadioButtonGroupThemePreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/themes/RadioButtonGroupThemePreference.java
@@ -20,23 +20,28 @@
 import org.chromium.chrome.browser.night_mode.NightModeMetrics;
 import org.chromium.chrome.browser.preferences.themes.ThemePreferences.ThemeSetting;
 import org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription;
+import org.chromium.chrome.browser.ui.widget.RadioButtonWithDescriptionLayout;
 
 import java.util.ArrayList;
 import java.util.Collections;
 
 /**
  * A radio button group Preference used for Themes. Currently, it has 3 options: System default,
- * Light, and Dark.
+ * Light, and Dark. When an additional flag, DARKEN_WEBSITES_CHECKBOX_IN_THEMES_SETTING, is active
+ * there is an option added underneath the currently selected preference to allow website contents
+ * to be darkened (active for System default and Dark).
  */
 public class RadioButtonGroupThemePreference
         extends Preference implements RadioGroup.OnCheckedChangeListener {
     private @ThemeSetting int mSetting;
+    private RadioButtonWithDescription mSettingRadioButton;
+    private RadioButtonWithDescriptionLayout mGroup;
     private ArrayList<RadioButtonWithDescription> mButtons;
+
+    // Additional view that darkens website contents.
+    private LinearLayout mCheckboxContainer;
     private boolean mDarkenWebsitesEnabled;
     private CheckBox mCheckBox;
-    private LinearLayout mLayoutContainer;
-    private LinearLayout mCheckboxContainer;
-    private RadioGroup mRadioGroup;
 
     public RadioButtonGroupThemePreference(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -61,10 +66,9 @@
         super.onBindViewHolder(holder);
         mCheckboxContainer = (LinearLayout) holder.findViewById(R.id.checkbox_container);
         mCheckBox = (CheckBox) holder.findViewById(R.id.darken_websites);
-        mLayoutContainer = (LinearLayout) holder.itemView;
 
-        mRadioGroup = (RadioGroup) holder.findViewById(R.id.radio_button_layout);
-        mRadioGroup.setOnCheckedChangeListener(this);
+        mGroup = (RadioButtonWithDescriptionLayout) holder.findViewById(R.id.radio_button_layout);
+        mGroup.setOnCheckedChangeListener(this);
 
         mCheckboxContainer.setOnClickListener(x -> {
             mCheckBox.setChecked(!mCheckBox.isChecked());
@@ -86,20 +90,20 @@
         mButtons.set(
                 ThemeSetting.DARK, (RadioButtonWithDescription) holder.findViewById(R.id.dark));
 
-        mButtons.get(mSetting).setChecked(true);
+        mSettingRadioButton = mButtons.get(mSetting);
+        mSettingRadioButton.setChecked(true);
         positionCheckbox();
     }
 
     /**
-     * Remove and insert the checkbox to the view, based on the value of mSetting
+     * Remove and insert the checkbox to the view, based on the current theme preference.
      */
     private void positionCheckbox() {
         if (ChromeFeatureList.isEnabled(
                     ChromeFeatureList.DARKEN_WEBSITES_CHECKBOX_IN_THEMES_SETTING)) {
             if (mSetting == ThemeSetting.SYSTEM_DEFAULT || mSetting == ThemeSetting.DARK) {
-                mLayoutContainer.removeView(mCheckboxContainer);
+                mGroup.attachAccessoryView(mCheckboxContainer, mSettingRadioButton);
                 mCheckboxContainer.setVisibility(View.VISIBLE);
-                mLayoutContainer.addView(mCheckboxContainer, mSetting + 1);
             } else {
                 mCheckboxContainer.setVisibility(View.GONE);
             }
@@ -111,9 +115,12 @@
         for (int i = 0; i < ThemeSetting.NUM_ENTRIES; i++) {
             if (mButtons.get(i).isChecked()) {
                 mSetting = i;
+                mSettingRadioButton = mButtons.get(i);
                 break;
             }
         }
+        assert mSetting >= 0 && mSetting < ThemeSetting.NUM_ENTRIES : "No matching setting found.";
+
         positionCheckbox();
         callChangeListener(mSetting);
         NightModeMetrics.recordThemePreferencesChanged(mSetting);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/widget/OWNERS
index 6bc8c97..ff0a541 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/OWNERS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/OWNERS
@@ -1,4 +1,5 @@
 twellington@chromium.org
 
+per-file FeatureHighlightProvider.java=mdjones@chromium.org
 per-file ToolbarProgressBar*=mdjones@chromium.org
 per-file ScrimView.java=mdjones@chromium.org
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionAvailabilityCustomTabTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionAvailabilityCustomTabTest.java
new file mode 100644
index 0000000..be0cc52
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionAvailabilityCustomTabTest.java
@@ -0,0 +1,61 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.directactions;
+import static org.junit.Assert.assertThat;
+
+import android.annotation.TargetApi;
+import android.content.Intent;
+import android.os.Build;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+
+import org.hamcrest.Matchers;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.MinAndroidSdkLevel;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
+import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
+
+/**
+ * Tests the availability of core direct actions in different activities.
+ *
+ * <p>This tests both {@link DirectActionInitializer} and its integration with {@link
+ * ChromeActivity} and its different subclasses.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@DisableFeatures(ChromeFeatureList.AUTOFILL_ASSISTANT_DIRECT_ACTIONS)
+@MinAndroidSdkLevel(Build.VERSION_CODES.N)
+@TargetApi(24) // For java.util.function.Consumer.
+public class DirectActionAvailabilityCustomTabTest {
+    @Rule
+    public CustomTabActivityTestRule mCustomTabActivityTestRule = new CustomTabActivityTestRule();
+
+    @Rule
+    public DirectActionTestRule mDirectActionRule = new DirectActionTestRule();
+
+    @Test
+    @MediumTest
+    @Feature({"DirectActions"})
+    public void testCoreDirectActionInCustomTabActivity() throws Exception {
+        Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(
+                InstrumentationRegistry.getTargetContext(), "about:blank");
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
+
+        assertThat(
+                DirectActionTestUtils.setupActivityAndGetDirectAction(mCustomTabActivityTestRule),
+                Matchers.containsInAnyOrder(
+                        "go_back", "reload", "go_forward", "bookmark_this_page", "preferences"));
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionAvailabilityTabbedTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionAvailabilityTabbedTest.java
new file mode 100644
index 0000000..e98e6d2
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionAvailabilityTabbedTest.java
@@ -0,0 +1,64 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.directactions;
+import static org.junit.Assert.assertThat;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.support.test.filters.MediumTest;
+
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.MinAndroidSdkLevel;
+import org.chromium.base.test.util.RetryOnFailure;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
+
+/**
+ * Tests the availability of core direct actions in different activities.
+ *
+ * <p>This tests both {@link DirectActionInitializer} and its integration with {@link
+ * ChromeActivity} and its different subclasses.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@DisableFeatures(ChromeFeatureList.AUTOFILL_ASSISTANT_DIRECT_ACTIONS)
+@MinAndroidSdkLevel(Build.VERSION_CODES.N)
+@TargetApi(24) // For java.util.function.Consumer.
+public class DirectActionAvailabilityTabbedTest {
+    @Rule
+    public ChromeTabbedActivityTestRule mTabbedActivityTestRule =
+            new ChromeTabbedActivityTestRule();
+
+    @Rule
+    public DirectActionTestRule mDirectActionRule = new DirectActionTestRule();
+
+    @Before
+    public void setUp() throws Exception {
+        // Using OnBlank times out when waiting for NTP. Using null makes the test work.
+        mTabbedActivityTestRule.startMainActivityWithURL(null);
+    }
+
+    @Test
+    @MediumTest
+    @RetryOnFailure
+    @Feature({"DirectActions"})
+    public void testCoreDirectActionInTabbedActivity() throws Exception {
+        assertThat(DirectActionTestUtils.setupActivityAndGetDirectAction(mTabbedActivityTestRule),
+                Matchers.containsInAnyOrder("go_back", "reload", "go_forward", "bookmark_this_page",
+                        "downloads", "preferences", "open_history", "help", "new_tab", "close_tab",
+                        "close_all_tabs"));
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionAvailabilityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionAvailabilityTest.java
deleted file mode 100644
index 6f7f775..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionAvailabilityTest.java
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.directactions;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import android.annotation.TargetApi;
-import android.content.Intent;
-import android.os.Build;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
-
-import org.hamcrest.Matchers;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisabledTest;
-import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.MinAndroidSdkLevel;
-import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.ChromeFeatureList;
-import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
-import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
-import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.webapps.WebappActivityTestRule;
-import org.chromium.chrome.test.ChromeActivityTestRule;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
-import org.chromium.chrome.test.util.ChromeTabUtils;
-import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
-
-import java.util.List;
-
-/**
- * Tests the availability of core direct actions in different activities.
- *
- * <p>This tests both {@link DirectActionInitializer} and its integration with {@link
- * ChromeActivity} and its different subclasses.
- */
-@RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-@DisableFeatures(ChromeFeatureList.AUTOFILL_ASSISTANT_DIRECT_ACTIONS)
-@MinAndroidSdkLevel(Build.VERSION_CODES.N)
-@TargetApi(24) // For java.util.function.Consumer.
-public class DirectActionAvailabilityTest {
-    @Rule
-    public ChromeTabbedActivityTestRule mTabbedActivityTestRule =
-            new ChromeTabbedActivityTestRule();
-
-    @Rule
-    public CustomTabActivityTestRule mCustomTabActivityTestRule = new CustomTabActivityTestRule();
-
-    @Rule
-    public WebappActivityTestRule mWebAppActivityTestRule = new WebappActivityTestRule();
-
-    @Rule
-    public DirectActionTestRule mDirectActionRule = new DirectActionTestRule();
-
-    @Test
-    @MediumTest
-    @Feature({"DirectActions"})
-    public void testCoreDirectActionInTabbedActivity() throws Exception {
-        mTabbedActivityTestRule.startMainActivityOnBlankPage();
-
-        assertThat(setupActivityAndGetDirectAction(mTabbedActivityTestRule),
-                Matchers.containsInAnyOrder("go_back", "reload", "go_forward", "bookmark_this_page",
-                        "downloads", "preferences", "open_history", "help", "new_tab", "close_tab",
-                        "close_all_tabs"));
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"DirectActions"})
-    public void testCoreDirectActionInCustomTabActivity() throws Exception {
-        Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(
-                InstrumentationRegistry.getTargetContext(), "about:blank");
-        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
-
-        assertThat(setupActivityAndGetDirectAction(mCustomTabActivityTestRule),
-                Matchers.containsInAnyOrder(
-                        "go_back", "reload", "go_forward", "bookmark_this_page", "preferences"));
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"DirectActions"})
-    @DisabledTest(message = "https://crbug.com/1018183")
-    public void testCoreDirectActionInWebappActivity() throws Exception {
-        mWebAppActivityTestRule.startWebappActivity();
-
-        assertThat(setupActivityAndGetDirectAction(mWebAppActivityTestRule),
-                Matchers.containsInAnyOrder("go_back", "reload", "go_forward"));
-    }
-
-    private List<String> setupActivityAndGetDirectAction(ChromeActivityTestRule<?> rule)
-            throws Exception {
-        allowGoForward(rule);
-        return DirectActionTestUtils.callOnGetDirectActions(rule.getActivity());
-    }
-
-    /**
-     * Forces availability of the "go_forward" direct action on the current tab by loading another
-     * URL then navigating back to the current one.
-     *
-     * <p>The activity of the given rule must have been started and have loaded a page.
-     */
-    public static void allowGoForward(ChromeActivityTestRule<?> rule) throws Exception {
-        ChromeActivity activity = rule.getActivity();
-        String initialUrl = TestThreadUtils.runOnUiThreadBlocking(
-                () -> activity.getCurrentWebContents().getLastCommittedUrl());
-
-        // Any built-in page that is not about:blank and is reasonably cheap to render will do,
-        // here.
-        String visitedUrl = "chrome://version/";
-        assertThat(initialUrl, Matchers.not(Matchers.equalTo(visitedUrl)));
-        rule.loadUrl(visitedUrl);
-
-        Tab tab = activity.getTabModelSelector().getCurrentTab();
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> activity.getCurrentWebContents().getNavigationController().goBack());
-        ChromeTabUtils.waitForTabPageLoaded(tab, initialUrl);
-        assertTrue(tab.canGoForward());
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionAvailabilityWebappTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionAvailabilityWebappTest.java
new file mode 100644
index 0000000..f84dadd
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionAvailabilityWebappTest.java
@@ -0,0 +1,54 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.directactions;
+import static org.junit.Assert.assertThat;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.support.test.filters.MediumTest;
+
+import org.hamcrest.Matchers;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.MinAndroidSdkLevel;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.webapps.WebappActivityTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
+
+/**
+ * Tests the availability of core direct actions in different activities.
+ *
+ * <p>This tests both {@link DirectActionInitializer} and its integration with {@link
+ * ChromeActivity} and its different subclasses.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@DisableFeatures(ChromeFeatureList.AUTOFILL_ASSISTANT_DIRECT_ACTIONS)
+@MinAndroidSdkLevel(Build.VERSION_CODES.N)
+@TargetApi(24) // For java.util.function.Consumer.
+public class DirectActionAvailabilityWebappTest {
+    @Rule
+    public WebappActivityTestRule mWebAppActivityTestRule = new WebappActivityTestRule();
+
+    @Rule
+    public DirectActionTestRule mDirectActionRule = new DirectActionTestRule();
+
+    @Test
+    @MediumTest
+    @Feature({"DirectActions"})
+    public void testCoreDirectActionInWebappActivity() throws Exception {
+        mWebAppActivityTestRule.startWebappActivity();
+
+        assertThat(DirectActionTestUtils.setupActivityAndGetDirectAction(mWebAppActivityTestRule),
+                Matchers.containsInAnyOrder("go_back", "reload", "go_forward"));
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionTestUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionTestUtils.java
index d8e3de9..6ce50e24 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionTestUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionTestUtils.java
@@ -4,11 +4,19 @@
 
 package org.chromium.chrome.browser.directactions;
 
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
 import android.os.Bundle;
 import android.os.CancellationSignal;
 
+import org.hamcrest.Matchers;
+
 import org.chromium.base.Callback;
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.test.ChromeActivityTestRule;
+import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
 import java.util.ArrayList;
@@ -17,7 +25,7 @@
 /**
  * Utilities for writing tests that check for or perform direct actions on an activity.
  */
-public class DirectActionTestUtils {
+class DirectActionTestUtils {
     /** Perform a direct action, with the given name. */
     public static void callOnPerformDirectActions(
             ChromeActivity activity, String actionId, Callback<Bundle> callback) {
@@ -43,6 +51,41 @@
         return directActions;
     }
 
+    /**
+     * Sets the ChromeActivityTestRule by forcing "go_forward" to be available.
+     *
+     * <p>The activity of the given rule must have been started and have loaded a page.
+     */
+    static List<String> setupActivityAndGetDirectAction(ChromeActivityTestRule<?> rule)
+            throws Exception {
+        allowGoForward(rule);
+        return callOnGetDirectActions(rule.getActivity());
+    }
+
+    /**
+     * Forces availability of the "go_forward" direct action on the current tab by loading another
+     * URL then navigating back to the current one.
+     *
+     * <p>The activity of the given rule must have been started and have loaded a page.
+     */
+    static void allowGoForward(ChromeActivityTestRule<?> rule) throws Exception {
+        ChromeActivity activity = rule.getActivity();
+        String initialUrl = TestThreadUtils.runOnUiThreadBlocking(
+                () -> activity.getCurrentWebContents().getLastCommittedUrl());
+
+        // Any built-in page that is not about:blank and is reasonably cheap to render will do,
+        // here.
+        String visitedUrl = "chrome://version/";
+        assertThat(initialUrl, Matchers.not(Matchers.equalTo(visitedUrl)));
+        rule.loadUrl(visitedUrl);
+
+        Tab tab = activity.getTabModelSelector().getCurrentTab();
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> activity.getCurrentWebContents().getNavigationController().goBack());
+        ChromeTabUtils.waitForTabPageLoaded(tab, initialUrl);
+        assertTrue(tab.canGoForward());
+    }
+
     private DirectActionTestUtils() {
         // This is a utility class; it is not meant to be instantiated.
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/OWNERS b/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/OWNERS
new file mode 100644
index 0000000..829eb83
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/OWNERS
@@ -0,0 +1 @@
+file://components/autofill_assistant/OWNERS
\ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java
index 0df99d9..c8e1f4d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java
@@ -23,6 +23,7 @@
 import org.chromium.base.library_loader.LoadStatusRecorder.LoadLibraryStatus;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
@@ -150,6 +151,8 @@
     @Test
     @LargeTest
     @RetryOnFailure
+    @DisableIf.Build(sdk_is_greater_than = Build.VERSION_CODES.N_MR1,
+            message = "https://crbug.com/1023433")
     public void testStartWithURLRecorded() throws Exception {
         runAndWaitForPageLoadMetricsRecorded(
                 () -> mTabbedActivityTestRule.startMainActivityWithURL(mTestPage));
@@ -218,6 +221,8 @@
     @Test
     @LargeTest
     @RetryOnFailure
+    @DisableIf.Build(sdk_is_greater_than = Build.VERSION_CODES.N_MR1,
+            message = "https://crbug.com/1023433")
     public void testFromExternalAppRecorded() throws Exception {
         runAndWaitForPageLoadMetricsRecorded(
                 () -> mTabbedActivityTestRule.startMainActivityFromExternalApp(mTestPage, null));
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 58baeee..cd336c4 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -10255,6 +10255,32 @@
       Verify
     </message>
 
+    <!-- App pause prompt -->
+    <message name="IDS_APP_PAUSE_PROMPT_TITLE" desc="Titlebar of the app pause prompt window">
+      App paused
+    </message>
+
+    <message name="IDS_APP_PAUSE_HEADING_HOURS_AND_MINUTES" desc="Tells the user the app will be paused.">
+      The limit on "<ph name="APP_NAME">$1<ex>Google Photos</ex></ph>" that your parent set ran out. You can use it for <ph name="HOURS">$2<ex>2 hours</ex></ph> <ph name="MINUTES">$3<ex>30 minutes</ex></ph> tomorrow.
+    </message>
+    <message name="IDS_APP_PAUSE_HEADING_HOURS_ONLY" desc="Tells the user the app will be paused.">
+      The limit on "<ph name="APP_NAME">$1<ex>Google Photos</ex></ph>" that your parent set ran out. You can use it for <ph name="HOURS">$2<ex>2 hours</ex></ph> tomorrow.
+    </message>
+    <message name="IDS_APP_PAUSE_HEADING_MINUTES_ONLY" desc="Tells the user the app will be paused.">
+      The limit on "<ph name="APP_NAME">$1<ex>Google Photos</ex></ph>" that your parent set ran out. You can use it for <ph name="MINUTES">$2<ex>30 minutes</ex></ph> tomorrow.
+    </message>
+    <message name="IDS_APP_PAUSE_HEADING_HOURS" desc="Tells the user the app can be run how many hours. [ICU Syntax]">
+       {HOURS, plural,
+        =1 {# hour}
+        other {# hours}}
+    </message>
+    <message name="IDS_APP_PAUSE_HEADING_MINUTES" desc="Tells the user the app can be run how many minutes. [ICU Syntax]">
+       {MINUTES, plural,
+        =1 {# minute}
+        other {# minutes}}
+    </message>
+
+
   </messages>
 </release>
 </grit>
diff --git a/chrome/app/generated_resources_grd/IDS_APP_PAUSE_HEADING_HOURS.png.sha1 b/chrome/app/generated_resources_grd/IDS_APP_PAUSE_HEADING_HOURS.png.sha1
new file mode 100644
index 0000000..a90bf8d
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_APP_PAUSE_HEADING_HOURS.png.sha1
@@ -0,0 +1 @@
+b9d8a5bb225403e83014694d7c8693d652003cb8
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_APP_PAUSE_HEADING_HOURS_AND_MINUTES.png.sha1 b/chrome/app/generated_resources_grd/IDS_APP_PAUSE_HEADING_HOURS_AND_MINUTES.png.sha1
new file mode 100644
index 0000000..1024e26
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_APP_PAUSE_HEADING_HOURS_AND_MINUTES.png.sha1
@@ -0,0 +1 @@
+ac9f6657699ab90f5ed2c65d5de7b67e4fdc7be8
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_APP_PAUSE_HEADING_HOURS_ONLY.png.sha1 b/chrome/app/generated_resources_grd/IDS_APP_PAUSE_HEADING_HOURS_ONLY.png.sha1
new file mode 100644
index 0000000..5ae63e7
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_APP_PAUSE_HEADING_HOURS_ONLY.png.sha1
@@ -0,0 +1 @@
+8b21c8dff4948451dc44ca53f6c3ab4d4b676921
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_APP_PAUSE_HEADING_MINUTES.png.sha1 b/chrome/app/generated_resources_grd/IDS_APP_PAUSE_HEADING_MINUTES.png.sha1
new file mode 100644
index 0000000..a90bf8d
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_APP_PAUSE_HEADING_MINUTES.png.sha1
@@ -0,0 +1 @@
+b9d8a5bb225403e83014694d7c8693d652003cb8
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_APP_PAUSE_HEADING_MINUTES_ONLY.png.sha1 b/chrome/app/generated_resources_grd/IDS_APP_PAUSE_HEADING_MINUTES_ONLY.png.sha1
new file mode 100644
index 0000000..e4030fb6
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_APP_PAUSE_HEADING_MINUTES_ONLY.png.sha1
@@ -0,0 +1 @@
+19bdab026c688357ed9f0275d0b795fc6f0cb23a
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_APP_PAUSE_PROMPT_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_APP_PAUSE_PROMPT_TITLE.png.sha1
new file mode 100644
index 0000000..6b005f9
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_APP_PAUSE_PROMPT_TITLE.png.sha1
@@ -0,0 +1 @@
+5b713d24b0c6a493d8cbe52a3bb7e5127290b587
\ No newline at end of file
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 52132ed..2bfe199 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -511,11 +511,11 @@
     <message name="IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_OPTIONS_LABEL" desc="In the settings tab, the label for the button that opens the Options page for the Select-to-Speak feature.">
       Open select-to-speak settings
     </message>
-    <message name="IDS_SETTINGS_ACCESSIBILITY_SWITCH_ACCESS_DESCRIPTION" desc="In the settings tab, the text next to the checkbox to enable switch access (for users with limited motor control).">
-      Switch access (control the computer with just one or two switches)
+    <message name="IDS_SETTINGS_ACCESSIBILITY_SWITCH_ACCESS_DESCRIPTION" desc="In the settings tab, the text next to the checkbox to enable Switch Access (for users with limited motor control).">
+      Switch Access (control the computer with just one or two switches)
     </message>
     <message name="IDS_SETTINGS_ACCESSIBILITY_SWITCH_ACCESS_OPTIONS_LABEL" desc="In the settings tab, the label for the button that opens the Options page for the Switch Access feature.">
-      Switch access options
+      Switch Access options
     </message>
     <message name="IDS_SETTINGS_ACCESSIBILITY_TEXT_TO_SPEECH_HEADING" desc="In the settings tab, the heading for accessibility features that enable the computer to speak text from the computer screen.">
       Text-to-Speech
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 2745d67..62e8141 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -4124,6 +4124,10 @@
      FEATURE_VALUE_TYPE(features::kUseSkiaRenderer)},
 
 #if defined(OS_CHROMEOS)
+    {"allow-ambient-eq", flag_descriptions::kAllowAmbientEQName,
+     flag_descriptions::kAllowAmbientEQDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(ash::features::kAllowAmbientEQ)},
+
     {"allow-disable-mouse-acceleration",
      flag_descriptions::kAllowDisableMouseAccelerationName,
      flag_descriptions::kAllowDisableMouseAccelerationDescription, kOsCrOS,
diff --git a/chrome/browser/app_controller_mac_browsertest.mm b/chrome/browser/app_controller_mac_browsertest.mm
index bcc5c17..7b03e59 100644
--- a/chrome/browser/app_controller_mac_browsertest.mm
+++ b/chrome/browser/app_controller_mac_browsertest.mm
@@ -63,6 +63,11 @@
 
 using base::SysUTF16ToNSString;
 
+@interface AppController (ForTesting)
+- (void)getUrl:(NSAppleEventDescriptor*)event
+     withReply:(NSAppleEventDescriptor*)reply;
+@end
+
 namespace {
 
 GURL g_open_shortcut_url = GURL::EmptyGURL();
@@ -86,14 +91,7 @@
 void SendAppleEventToOpenUrlToAppController(const GURL& url) {
   AppController* controller =
       base::mac::ObjCCast<AppController>([NSApp delegate]);
-  Method get_url =
-      class_getInstanceMethod([controller class], @selector(getUrl:withReply:));
-
-  ASSERT_TRUE(get_url);
-
-  NSAppleEventDescriptor* shortcut_event = AppleEventToOpenUrl(url);
-
-  method_invoke(controller, get_url, shortcut_event, NULL);
+  [controller getUrl:AppleEventToOpenUrl(url) withReply:nullptr];
 }
 
 void RunClosureWhenProfileInitialized(const base::Closure& closure,
diff --git a/chrome/browser/apps/app_service/app_service_proxy.cc b/chrome/browser/apps/app_service/app_service_proxy.cc
index cc20b21..89b9ec3 100644
--- a/chrome/browser/apps/app_service/app_service_proxy.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy.cc
@@ -280,6 +280,50 @@
   uninstall_dialogs_.erase(it);
 }
 
+void AppServiceProxy::PauseApps(
+    const std::map<std::string, PauseData>& pause_data) {
+  if (!app_service_.is_connected())
+    return;
+
+  for (auto& data : pause_data) {
+    apps::mojom::AppType app_type = cache_.GetAppType(data.first);
+    constexpr bool kPaused = true;
+    UpdatePausedStatus(app_type, data.first, kPaused);
+
+    // TODO(crbug.com/1011235): Add the app running checking. If the app is not
+    // running, don't create the pause dialog, pause the app directly.
+    if (app_type != apps::mojom::AppType::kArc) {
+      // TODO(crbug.com/1011235): Add AppService interface to apply icon
+      // effects.
+      continue;
+    }
+
+    cache_.ForOneApp(data.first, [this, &data](const apps::AppUpdate& update) {
+      this->LoadIconForPauseDialog(update, data.second);
+    });
+  }
+}
+
+void AppServiceProxy::UnpauseApps(const std::set<std::string>& app_ids) {
+  if (!app_service_.is_connected())
+    return;
+
+  for (auto& app_id : app_ids) {
+    apps::mojom::AppType app_type = cache_.GetAppType(app_id);
+    constexpr bool kPaused = false;
+    UpdatePausedStatus(app_type, app_id, kPaused);
+
+    // TODO(crbug.com/1011235): Add AppService interface to recover icon
+    // effects.
+  }
+}
+
+void AppServiceProxy::OnPauseDialogClosed(apps::mojom::AppType app_type,
+                                          const std::string& app_id) {
+  // TODO(crbug.com/1011235): Add AppService interface to apply the icon effect
+  // and stop the running app.
+}
+
 void AppServiceProxy::OpenNativeSettings(const std::string& app_id) {
   if (app_service_.is_connected()) {
     cache_.ForOneApp(app_id, [this](const apps::AppUpdate& update) {
@@ -404,4 +448,49 @@
       std::make_unique<base::Value>(std::move(preferred_apps)));
 }
 
+void AppServiceProxy::LoadIconForPauseDialog(const apps::AppUpdate& update,
+                                             const PauseData& pause_data) {
+  apps::mojom::IconKeyPtr icon_key = update.IconKey();
+  constexpr bool kAllowPlaceholderIcon = false;
+  constexpr int32_t kPauseIconSize = 48;
+  LoadIconFromIconKey(
+      update.AppType(), update.AppId(), std::move(icon_key),
+      apps::mojom::IconCompression::kUncompressed, kPauseIconSize,
+      kAllowPlaceholderIcon,
+      base::BindOnce(&AppServiceProxy::OnLoadIconForPauseDialog,
+                     weak_ptr_factory_.GetWeakPtr(), update.AppType(),
+                     update.AppId(), update.Name(), pause_data));
+}
+
+void AppServiceProxy::OnLoadIconForPauseDialog(
+    apps::mojom::AppType app_type,
+    const std::string& app_id,
+    const std::string& app_name,
+    const PauseData& pause_data,
+    apps::mojom::IconValuePtr icon_value) {
+  if (icon_value->icon_compression !=
+      apps::mojom::IconCompression::kUncompressed) {
+    OnPauseDialogClosed(app_type, app_id);
+    return;
+  }
+
+  AppServiceProxy::CreatePauseDialog(
+      app_name, icon_value->uncompressed, pause_data,
+      base::BindOnce(&AppServiceProxy::OnPauseDialogClosed,
+                     weak_ptr_factory_.GetWeakPtr(), app_type, app_id));
+}
+
+void AppServiceProxy::UpdatePausedStatus(apps::mojom::AppType app_type,
+                                         const std::string& app_id,
+                                         bool paused) {
+  std::vector<apps::mojom::AppPtr> apps;
+  apps::mojom::AppPtr app = apps::mojom::App::New();
+  app->app_type = app_type;
+  app->app_id = app_id;
+  app->paused = (paused) ? apps::mojom::OptionalBool::kTrue
+                         : apps::mojom::OptionalBool::kFalse;
+  apps.push_back(std::move(app));
+  cache_.OnApps(std::move(apps));
+}
+
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/app_service_proxy.h b/chrome/browser/apps/app_service/app_service_proxy.h
index ca32f83..37b8a4c 100644
--- a/chrome/browser/apps/app_service/app_service_proxy.h
+++ b/chrome/browser/apps/app_service/app_service_proxy.h
@@ -5,13 +5,13 @@
 #ifndef CHROME_BROWSER_APPS_APP_SERVICE_APP_SERVICE_PROXY_H_
 #define CHROME_BROWSER_APPS_APP_SERVICE_APP_SERVICE_PROXY_H_
 
+#include <map>
 #include <memory>
 #include <set>
 
 #include "base/containers/unique_ptr_adapters.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/apps/app_service/uninstall_dialog.h"
 #include "chrome/services/app_service/public/cpp/app_registry_cache.h"
 #include "chrome/services/app_service/public/cpp/icon_cache.h"
 #include "chrome/services/app_service/public/cpp/icon_coalescer.h"
@@ -35,6 +35,12 @@
 namespace apps {
 
 class AppServiceImpl;
+class UninstallDialog;
+
+struct PauseData {
+  int hours;
+  int minutes;
+};
 
 // Singleton (per Profile) proxy and cache of an App Service's apps.
 //
@@ -46,6 +52,8 @@
                         public apps::IconLoader,
                         public apps::mojom::Subscriber {
  public:
+  using OnPauseDialogClosedCallback = base::OnceCallback<void()>;
+
   explicit AppServiceProxy(Profile* profile);
   ~AppServiceProxy() override;
 
@@ -92,6 +100,23 @@
                                bool clear_site_data,
                                bool report_abuse,
                                UninstallDialog* uninstall_dialog);
+
+  // Pauses apps. |pause_data|'s key is the app_id. |pause_data|'s PauseData
+  // is the time limit setting for the app, which is shown in the pause app
+  // dialog. AppService sets the paused status directly. If the app is running,
+  // AppService shows the pause app dialog. Otherwise, AppService applies the
+  // paused app icon effect directly.
+  void PauseApps(const std::map<std::string, PauseData>& pause_data);
+
+  // Unpauses the apps from the paused status. AppService sets the paused status
+  // as false directly and removes the paused app icon effect.
+  void UnpauseApps(const std::set<std::string>& app_ids);
+
+  // Called when the user clicks the 'OK' button of the pause app dialog.
+  // AppService stops the running app and applies the paused app icon effect.
+  void OnPauseDialogClosed(apps::mojom::AppType app_type,
+                           const std::string& app_id);
+
   void OpenNativeSettings(const std::string& app_id);
 
   void FlushMojoCallsForTesting();
@@ -170,6 +195,11 @@
     apps::IconLoader* overriding_icon_loader_for_testing_;
   };
 
+  static void CreatePauseDialog(const std::string& app_name,
+                                gfx::ImageSkia image,
+                                const PauseData& pause_data,
+                                OnPauseDialogClosedCallback pause_callback);
+
   void Initialize();
 
   void AddAppIconSource(Profile* profile);
@@ -184,6 +214,20 @@
                          apps::mojom::IntentFilterPtr intent_filter) override;
   void InitializePreferredApps(base::Value preferred_apps) override;
 
+  void LoadIconForPauseDialog(const apps::AppUpdate& update,
+                              const PauseData& pause_data);
+
+  // Callback invoked when the icon is loaded.
+  void OnLoadIconForPauseDialog(apps::mojom::AppType app_type,
+                                const std::string& app_id,
+                                const std::string& app_name,
+                                const PauseData& pause_data,
+                                apps::mojom::IconValuePtr icon_value);
+
+  void UpdatePausedStatus(apps::mojom::AppType app_type,
+                          const std::string& app_id,
+                          bool paused);
+
   // This proxy privately owns its instance of the App Service. This should not
   // be exposed except through the Mojo interface connected to |app_service_|.
   std::unique_ptr<apps::AppServiceImpl> app_service_impl_;
diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.cc b/chrome/browser/chromeos/accessibility/accessibility_manager.cc
index 7c44181..a811a55f 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_manager.cc
+++ b/chrome/browser/chromeos/accessibility/accessibility_manager.cc
@@ -919,8 +919,8 @@
   if (!command_line->HasSwitch(
           ::switches::kEnableExperimentalAccessibilitySwitchAccess)) {
     if (enabled) {
-      LOG(WARNING) << "Switch access enabled but experimental accessibility "
-                   << "switch access flag is not set.";
+      LOG(WARNING) << "Switch Access enabled but experimental accessibility "
+                   << "Switch Access flag is not set.";
     }
     return;
   }
diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.h b/chrome/browser/chromeos/accessibility/accessibility_manager.h
index 23d91d6b..dc8850d 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_manager.h
+++ b/chrome/browser/chromeos/accessibility/accessibility_manager.h
@@ -214,10 +214,10 @@
   // Called when the Select-to-Speak extension state has changed.
   void OnSelectToSpeakStateChanged(ash::SelectToSpeakState state);
 
-  // Invoked to enable or disable switch access.
+  // Invoked to enable or disable Switch Access.
   void SetSwitchAccessEnabled(bool enabled);
 
-  // Returns if switch access is enabled.
+  // Returns if Switch Access is enabled.
   bool IsSwitchAccessEnabled() const;
 
   // Returns true if a braille display is connected to the system, otherwise
diff --git a/chrome/browser/chromeos/child_accounts/parent_access_code/parent_access_service_browsertest.cc b/chrome/browser/chromeos/child_accounts/parent_access_code/parent_access_service_browsertest.cc
index 72848d60..a9885694 100644
--- a/chrome/browser/chromeos/child_accounts/parent_access_code/parent_access_service_browsertest.cc
+++ b/chrome/browser/chromeos/child_accounts/parent_access_code/parent_access_service_browsertest.cc
@@ -10,17 +10,15 @@
 #include "base/bind.h"
 #include "base/json/json_writer.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/child_accounts/child_account_test_utils.h"
 #include "chrome/browser/chromeos/child_accounts/parent_access_code/config_source.h"
 #include "chrome/browser/chromeos/child_accounts/parent_access_code/parent_access_service.h"
 #include "chrome/browser/chromeos/child_accounts/parent_access_code/parent_access_test_utils.h"
-#include "chrome/browser/chromeos/policy/login_policy_test_base.h"
 #include "chrome/browser/chromeos/policy/user_policy_test_helper.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/supervised_user/logged_in_user_mixin.h"
+#include "chrome/test/base/mixin_based_in_process_browser_test.h"
 #include "components/account_id/account_id.h"
-#include "components/policy/core/browser/browser_policy_connector.h"
-#include "components/policy/policy_constants.h"
 #include "components/prefs/pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -82,66 +80,52 @@
   DISALLOW_COPY_AND_ASSIGN(TestParentAccessServiceObserver);
 };
 
-class ParentAccessServiceTest : public policy::LoginPolicyTestBase {
+class ParentAccessServiceTest : public MixinBasedInProcessBrowserTest {
  public:
   ParentAccessServiceTest()
-      : test_observer_(
-            std::make_unique<TestParentAccessServiceObserver>(child_)) {}
+      : test_observer_(std::make_unique<TestParentAccessServiceObserver>(
+            logged_in_user_mixin_.GetAccountId())) {}
   ~ParentAccessServiceTest() override = default;
 
-  void SetUp() override {
-    // Recognize example.com (used by LoginPolicyTestBase) as non-enterprise
-    // account.
-    policy::BrowserPolicyConnector::SetNonEnterpriseDomainForTesting(
-        "example.com");
-
-    policy::LoginPolicyTestBase::SetUp();
-  }
-
   void SetUpOnMainThread() override {
     ASSERT_NO_FATAL_FAILURE(GetTestAccessCodeValues(&test_values_));
     ParentAccessService::Get().AddObserver(test_observer_.get());
-    policy::LoginPolicyTestBase::SetUpOnMainThread();
+    MixinBasedInProcessBrowserTest::SetUpOnMainThread();
+    logged_in_user_mixin_.SetUpOnMainThreadHelper(host_resolver(), this);
   }
 
   void TearDownOnMainThread() override {
     ParentAccessService::Get().RemoveObserver(test_observer_.get());
-    policy::LoginPolicyTestBase::TearDownOnMainThread();
-  }
-
-  std::string GetIdToken() const override {
-    return test::GetChildAccountOAuthIdToken();
+    MixinBasedInProcessBrowserTest::TearDownOnMainThread();
   }
 
  protected:
-  void LogInChild() {
-    SkipToLoginScreen();
-    LogIn(kAccountId, kAccountPassword, test::kChildAccountServiceFlags);
-  }
-
   // Updates the policy containing the Parent Access Code config.
   void UpdatePolicy(const base::DictionaryValue& dict) {
+    std::string config_string;
+    base::JSONWriter::Write(dict, &config_string);
+
+    logged_in_user_mixin_.GetUserPolicyMixin()
+        ->RequestPolicyUpdate()
+        ->policy_payload()
+        ->mutable_parentaccesscodeconfig()
+        ->set_value(config_string);
+
     const user_manager::UserManager* const user_manager =
         user_manager::UserManager::Get();
     EXPECT_TRUE(user_manager->GetActiveUser()->IsChild());
     Profile* child_profile =
         ProfileHelper::Get()->GetProfileByUser(user_manager->GetActiveUser());
 
-    std::string config_string;
-    base::JSONWriter::Write(dict, &config_string);
-
-    base::DictionaryValue policy;
-    policy.SetKey(policy::key::kParentAccessCodeConfig,
-                  base::Value(config_string));
-    user_policy_helper()->SetPolicyAndWait(
-        std::move(policy), base::DictionaryValue(), child_profile);
+    logged_in_user_mixin_.GetUserPolicyTestHelper()->RefreshPolicyAndWait(
+        child_profile);
   }
 
   // Performs |code| validation on ParentAccessService singleton using the
   // |validation time| and returns the result.
   bool ValidateAccessCode(const std::string& code, base::Time validation_time) {
-    return ParentAccessService::Get().ValidateParentAccessCode(child_, code,
-                                                               validation_time);
+    return ParentAccessService::Get().ValidateParentAccessCode(
+        logged_in_user_mixin_.GetAccountId(), code, validation_time);
   }
 
   // Checks if ParentAccessServiceObserver and ValidateParentAccessCodeCallback
@@ -152,8 +136,11 @@
     EXPECT_EQ(failure_count, test_observer_->validation_results_.failure_count);
   }
 
-  const AccountId child_ = AccountId::FromUserEmail(kAccountId);
   AccessCodeValues test_values_;
+  chromeos::LoggedInUserMixin logged_in_user_mixin_{
+      &mixin_host_,           LoggedInUserMixin::LogInType::kChild,
+      embedded_test_server(), true /*should_launch_browser*/,
+      base::nullopt,          false /*include_initial_user*/};
   std::unique_ptr<TestParentAccessServiceObserver> test_observer_;
 
  private:
@@ -161,8 +148,6 @@
 };
 
 IN_PROC_BROWSER_TEST_F(ParentAccessServiceTest, NoConfigAvailable) {
-  LogInChild();
-
   auto test_value = test_values_.begin();
   EXPECT_FALSE(ValidateAccessCode(test_value->second, test_value->first));
 
@@ -170,8 +155,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ParentAccessServiceTest, NoValidConfigAvailable) {
-  LogInChild();
-
   std::vector<AccessCodeConfig> old_configs;
   old_configs.emplace_back(GetInvalidTestConfig());
   UpdatePolicy(PolicyFromConfigs(GetInvalidTestConfig(), GetInvalidTestConfig(),
@@ -184,8 +167,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ParentAccessServiceTest, ValidationWithFutureConfig) {
-  LogInChild();
-
   std::vector<AccessCodeConfig> old_configs;
   old_configs.emplace_back(GetInvalidTestConfig());
   UpdatePolicy(PolicyFromConfigs(GetDefaultTestConfig(), GetInvalidTestConfig(),
@@ -198,8 +179,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ParentAccessServiceTest, ValidationWithCurrentConfig) {
-  LogInChild();
-
   std::vector<AccessCodeConfig> old_configs;
   old_configs.emplace_back(GetInvalidTestConfig());
   UpdatePolicy(PolicyFromConfigs(GetInvalidTestConfig(), GetDefaultTestConfig(),
@@ -212,8 +191,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ParentAccessServiceTest, ValidationWithOldConfig) {
-  LogInChild();
-
   std::vector<AccessCodeConfig> old_configs;
   old_configs.emplace_back(GetInvalidTestConfig());
   old_configs.emplace_back(GetDefaultTestConfig());
@@ -227,8 +204,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ParentAccessServiceTest, MultipleValidationAttempts) {
-  LogInChild();
-
   AccessCodeValues::iterator test_value = test_values_.begin();
 
   // No config - validation should fail.
@@ -251,8 +226,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ParentAccessServiceTest, NoObserver) {
-  LogInChild();
-
   ParentAccessService::Get().RemoveObserver(test_observer_.get());
 
   UpdatePolicy(
@@ -265,8 +238,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ParentAccessServiceTest, NoAccountId) {
-  LogInChild();
-
   ParentAccessService::Get().RemoveObserver(test_observer_.get());
 
   UpdatePolicy(
@@ -279,8 +250,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ParentAccessServiceTest, InvalidAccountId) {
-  LogInChild();
-
   ParentAccessService::Get().RemoveObserver(test_observer_.get());
 
   UpdatePolicy(
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index b723aad..2bc4f68 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -577,6 +577,11 @@
   // -- This used to be in ChromeBrowserMainParts::PreMainMessageLoopRun()
   // -- immediately before Profile creation().
 
+  // PreProfileInit() is not always called if no browser process is started
+  // (e.g. during some browser tests). Set a boolean so that we do not try to
+  // destroy singletons that are initialized here.
+  pre_profile_init_called_ = true;
+
   // Now that the file thread exists we can record our stats.
   BootTimesRecorder::Get()->RecordChromeMainStats();
   LoginEventRecorder::Get()->SetDelegate(BootTimesRecorder::Get());
@@ -954,6 +959,10 @@
 }
 
 // Shut down services before the browser process, etc are destroyed.
+// NOTE: This may get called without PreProfileInit() (or other
+// PreMainMessageLoopRun sub-stages) getting called, so be careful with
+// shutdown calls and test |pre_profile_init_called_| if necessary. See
+// crbug.com/702403 for details.
 void ChromeBrowserMainPartsChromeos::PostMainMessageLoopRun() {
   crostini_unsupported_action_notifier_.reset();
 
@@ -965,7 +974,8 @@
     lock_screen_apps_state_controller_->Shutdown();
 
   // This must be shut down before |arc_service_launcher_|.
-  NoteTakingHelper::Shutdown();
+  if (pre_profile_init_called_)
+    NoteTakingHelper::Shutdown();
 
   arc_service_launcher_->Shutdown();
 
@@ -979,7 +989,8 @@
   shutdown_policy_forwarder_.reset();
 
   // Destroy the application name notifier for Kiosk mode.
-  KioskModeIdleAppNameNotification::Shutdown();
+  if (pre_profile_init_called_)
+    KioskModeIdleAppNameNotification::Shutdown();
 
   // Shutdown the upgrade detector for Chrome OS. The upgrade detector
   // stops monitoring changes from the update engine.
@@ -1003,7 +1014,8 @@
   wake_on_wifi_manager_.reset();
   fast_transition_observer_.reset();
   network_throttling_observer_.reset();
-  ScreenLocker::ShutDownClass();
+  if (pre_profile_init_called_)
+    ScreenLocker::ShutDownClass();
   low_disk_notification_.reset();
   demo_mode_resources_remover_.reset();
   adaptive_screen_brightness_manager_.reset();
@@ -1021,10 +1033,10 @@
   if (LoginScreenExtensionUiHandler::Get(false /*can_create*/))
     LoginScreenExtensionUiHandler::Shutdown();
 
-  MagnificationManager::Shutdown();
-
-  audio::SoundsManager::Shutdown();
-
+  if (pre_profile_init_called_) {
+    MagnificationManager::Shutdown();
+    audio::SoundsManager::Shutdown();
+  }
   system::StatisticsProvider::GetInstance()->Shutdown();
 
   DemoSession::ShutDownIfInitialized();
@@ -1035,10 +1047,14 @@
   if (NetworkCertLoader::IsInitialized())
     NetworkCertLoader::Get()->set_is_shutting_down();
 
+  CHECK(g_browser_process);
+  CHECK(g_browser_process->platform_part());
+
   // Let the UserManager unregister itself as an observer of the CrosSettings
   // singleton before it is destroyed. This also ensures that the UserManager
   // has no URLRequest pending (see http://crbug.com/276659).
-  g_browser_process->platform_part()->user_manager()->Shutdown();
+  if (g_browser_process->platform_part()->user_manager())
+    g_browser_process->platform_part()->user_manager()->Shutdown();
 
   // Let the DeviceDisablingManager unregister itself as an observer of the
   // CrosSettings singleton before it is destroyed.
@@ -1052,7 +1068,8 @@
   KioskAppManager::Shutdown();
 
   // Make sure that there is no pending URLRequests.
-  UserSessionManager::GetInstance()->Shutdown();
+  if (pre_profile_init_called_)
+    UserSessionManager::GetInstance()->Shutdown();
 
   // Give BrowserPolicyConnectorChromeOS a chance to unregister any observers
   // on services that are going to be deleted later but before its Shutdown()
@@ -1063,15 +1080,19 @@
 
   // Shutdown the virtual keyboard UI before destroying ash::Shell or the
   // primary profile.
-  chrome_keyboard_controller_client_->Shutdown();
+  if (chrome_keyboard_controller_client_)
+    chrome_keyboard_controller_client_->Shutdown();
 
   // Must occur before BrowserProcessImpl::StartTearDown() destroys the
   // ProfileManager.
-  Profile* primary_user = ProfileManager::GetPrimaryUserProfile();
-  if (primary_user) {
-    // See startup_settings_cache::ReadAppLocale() comment for why we do this.
-    startup_settings_cache::WriteAppLocale(primary_user->GetPrefs()->GetString(
-        language::prefs::kApplicationLocale));
+  if (pre_profile_init_called_) {
+    Profile* primary_user = ProfileManager::GetPrimaryUserProfile();
+    if (primary_user) {
+      // See startup_settings_cache::ReadAppLocale() comment for why we do this.
+      startup_settings_cache::WriteAppLocale(
+          primary_user->GetPrefs()->GetString(
+              language::prefs::kApplicationLocale));
+    }
   }
 
   // Cleans up dbus services depending on ash.
@@ -1092,9 +1113,10 @@
   // |arc_service_launcher_| uses |scheduler_configuration_manager_|.
   scheduler_configuration_manager_.reset();
 
-  AccessibilityManager::Shutdown();
-
-  input_method::Shutdown();
+  if (pre_profile_init_called_) {
+    AccessibilityManager::Shutdown();
+    input_method::Shutdown();
+  }
 
   // Stops all in-flight OAuth2 token fetchers before the IO thread stops.
   DeviceOAuth2TokenServiceFactory::Shutdown();
@@ -1106,11 +1128,11 @@
 
   quirks::QuirksManager::Shutdown();
 
-  // Called after
-  // ChromeBrowserMainPartsLinux::PostMainMessageLoopRun() to be
-  // executed after execution of chrome::CloseAsh(), because some
-  // parts of WebUI depends on NetworkPortalDetector.
-  network_portal_detector::Shutdown();
+  // Called after ChromeBrowserMainPartsLinux::PostMainMessageLoopRun() (which
+  // calls chrome::CloseAsh()) because some parts of WebUI depend on
+  // NetworkPortalDetector.
+  if (pre_profile_init_called_)
+    network_portal_detector::Shutdown();
 
   g_browser_process->platform_part()->ShutdownSessionManager();
   // Ash needs to be closed before UserManager is destroyed.
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.h b/chrome/browser/chromeos/chrome_browser_main_chromeos.h
index 26e8a04..6e20fc02 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.h
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.h
@@ -174,6 +174,12 @@
       dark_resume_controller_;
 
   std::unique_ptr<SessionTerminationManager> session_termination_manager_;
+
+  // Set when PreProfileInit() is called. If PreMainMessageLoopRun() exits
+  // early, this will be false during PostMainMessageLoopRun(), etc.
+  // Used to prevent shutting down classes that were not initialized.
+  bool pre_profile_init_called_ = false;
+
   std::unique_ptr<policy::LockToSingleUserManager> lock_to_single_user_manager_;
   std::unique_ptr<WilcoDtcSupportdManager> wilco_dtc_supportd_manager_;
   std::unique_ptr<LoginScreenExtensionsLifetimeManager>
diff --git a/chrome/browser/chromeos/extensions/info_private_api.cc b/chrome/browser/chromeos/extensions/info_private_api.cc
index 0aaf258a..421770c73 100644
--- a/chrome/browser/chromeos/extensions/info_private_api.cc
+++ b/chrome/browser/chromeos/extensions/info_private_api.cc
@@ -110,7 +110,7 @@
 // Key which corresponds to the select-to-speak A11Y property in JS.
 const char kPropertySelectToSpeakEnabled[] = "a11ySelectToSpeakEnabled";
 
-// Key which corresponds to the switch access A11Y property in JS.
+// Key which corresponds to the Switch Access A11Y property in JS.
 const char kPropertySwitchAccessEnabled[] = "a11ySwitchAccessEnabled";
 
 // Key which corresponds to the send-function-keys property in JS.
diff --git a/chrome/browser/chromeos/login/saml/saml_browsertest.cc b/chrome/browser/chromeos/login/saml/saml_browsertest.cc
index d09bc4b1..5c3ae6b0 100644
--- a/chrome/browser/chromeos/login/saml/saml_browsertest.cc
+++ b/chrome/browser/chromeos/login/saml/saml_browsertest.cc
@@ -1616,7 +1616,7 @@
   ShowSAMLInterstitial();
   test::OobeJS().ExpectHiddenPath({"gaia-signin", "signin-frame-dialog"});
   test::OobeJS().ExpectHiddenPath({"gaia-signin", "offline-gaia"});
-  test::OobeJS().ExpectVisiblePath({"gaia-signin", "saml-interstitial"});
+  test::OobeJS().ExpectVisiblePath({"gaia-signin", "gaia-step-contents"});
 
   // Click the "change account" link on the SAML interstitial page.
   ClickChangeAccountOnSAMLInterstitialPage();
@@ -1626,7 +1626,7 @@
   test::OobeJS().ExpectHasClass("non-transparent",
                                 {"gaia-signin", "signin-frame-container"});
   test::OobeJS().ExpectHiddenPath({"gaia-signin", "offline-gaia"});
-  test::OobeJS().ExpectHiddenPath({"gaia-signin", "saml-interstitial"});
+  test::OobeJS().ExpectHiddenPath({"gaia-signin", "gaia-step-contents"});
 }
 
 // Tests that clicking "Next" in the SAML interstitial page successfully
diff --git a/chrome/browser/chromeos/policy/user_policy_test_helper.cc b/chrome/browser/chromeos/policy/user_policy_test_helper.cc
index da5eb108..8cf25b9 100644
--- a/chrome/browser/chromeos/policy/user_policy_test_helper.cc
+++ b/chrome/browser/chromeos/policy/user_policy_test_helper.cc
@@ -75,7 +75,10 @@
     const base::Value& recommended_policy,
     Profile* profile) {
   SetPolicy(mandatory_policy, recommended_policy);
+  RefreshPolicyAndWait(profile);
+}
 
+void UserPolicyTestHelper::RefreshPolicyAndWait(Profile* profile) {
   policy::ProfilePolicyConnector* const profile_connector =
       profile->GetProfilePolicyConnector();
   policy::PolicyService* const policy_service =
diff --git a/chrome/browser/chromeos/policy/user_policy_test_helper.h b/chrome/browser/chromeos/policy/user_policy_test_helper.h
index 2d7ec1b8..bcb60bfd 100644
--- a/chrome/browser/chromeos/policy/user_policy_test_helper.h
+++ b/chrome/browser/chromeos/policy/user_policy_test_helper.h
@@ -39,12 +39,15 @@
   // unnecessary to call this function.
   void WaitForInitialPolicy(Profile* profile);
 
-  // Update the policy test server with the given policy. Then refresh and wait
-  // for the new policy being applied to |profile|.
+  // Updates the policy test server with the given policy. Then calls
+  // RefreshPolicyAndWait().
   void SetPolicyAndWait(const base::Value& mandatory_policy,
                         const base::Value& recommended_policy,
                         Profile* profile);
 
+  // Refreshes and waits for the new policy being applied to |profile|.
+  void RefreshPolicyAndWait(Profile* profile);
+
  private:
   const std::string account_id_;
   chromeos::LocalPolicyTestServerMixin* local_policy_server_;
diff --git a/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc b/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
index 2eb864f..1175730 100644
--- a/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
+++ b/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
@@ -361,9 +361,9 @@
     }
     std::vector<std::string> ids{printer_ids.begin(), printer_ids.end()};
 
-    cups_wrapper_.QueryCups(ids,
-                            base::BindOnce(&CupsPrintJobManagerImpl::UpdateJobs,
-                                           weak_ptr_factory_.GetWeakPtr()));
+    cups_wrapper_.QueryCupsPrintJobs(
+        ids, base::BindOnce(&CupsPrintJobManagerImpl::UpdateJobs,
+                            weak_ptr_factory_.GetWeakPtr()));
   }
 
   // Process jobs from CUPS and perform notifications.
diff --git a/chrome/browser/chromeos/printing/cups_wrapper.cc b/chrome/browser/chromeos/printing/cups_wrapper.cc
index 78dc3ab..d3f9128 100644
--- a/chrome/browser/chromeos/printing/cups_wrapper.cc
+++ b/chrome/browser/chromeos/printing/cups_wrapper.cc
@@ -27,11 +27,14 @@
   Backend& operator=(const Backend&) = delete;
   ~Backend();
 
-  std::unique_ptr<QueryResult> QueryCups(
+  std::unique_ptr<QueryResult> QueryCupsPrintJobs(
       const std::vector<std::string>& printer_ids);
 
   void CancelJob(const std::string& printer_id, int job_id);
 
+  std::unique_ptr<::printing::PrinterStatus> QueryCupsPrinterStatus(
+      const std::string& printer_id);
+
  private:
   ::printing::CupsConnection cups_connection_;
 
@@ -47,7 +50,8 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
-std::unique_ptr<CupsWrapper::QueryResult> CupsWrapper::Backend::QueryCups(
+std::unique_ptr<CupsWrapper::QueryResult>
+CupsWrapper::Backend::QueryCupsPrintJobs(
     const std::vector<std::string>& printer_ids) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto result = std::make_unique<CupsWrapper::QueryResult>();
@@ -76,6 +80,17 @@
   }
 }
 
+std::unique_ptr<::printing::PrinterStatus>
+CupsWrapper::Backend::QueryCupsPrinterStatus(const std::string& printer_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto result = std::make_unique<::printing::PrinterStatus>();
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::MAY_BLOCK);
+  if (!cups_connection_.GetPrinterStatus(printer_id, result.get()))
+    return nullptr;
+  return result;
+}
+
 CupsWrapper::CupsWrapper()
     : backend_(std::make_unique<Backend>()),
       backend_task_runner_(base::CreateSequencedTaskRunner(
@@ -88,7 +103,7 @@
   backend_task_runner_->DeleteSoon(FROM_HERE, backend_.release());
 }
 
-void CupsWrapper::QueryCups(
+void CupsWrapper::QueryCupsPrintJobs(
     const std::vector<std::string>& printer_ids,
     base::OnceCallback<void(std::unique_ptr<CupsWrapper::QueryResult>)>
         callback) {
@@ -97,8 +112,8 @@
   // the same task runner.
   base::PostTaskAndReplyWithResult(
       backend_task_runner_.get(), FROM_HERE,
-      base::BindOnce(&Backend::QueryCups, base::Unretained(backend_.get()),
-                     printer_ids),
+      base::BindOnce(&Backend::QueryCupsPrintJobs,
+                     base::Unretained(backend_.get()), printer_ids),
       std::move(callback));
 }
 
@@ -112,4 +127,18 @@
                      printer_id, job_id));
 }
 
+void CupsWrapper::QueryCupsPrinterStatus(
+    const std::string& printer_id,
+    base::OnceCallback<void(std::unique_ptr<::printing::PrinterStatus>)>
+        callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // It's safe to pass unretained pointer here because we delete |backend_| on
+  // the same task runner.
+  base::PostTaskAndReplyWithResult(
+      backend_task_runner_.get(), FROM_HERE,
+      base::BindOnce(&Backend::QueryCupsPrinterStatus,
+                     base::Unretained(backend_.get()), printer_id),
+      std::move(callback));
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/cups_wrapper.h b/chrome/browser/chromeos/printing/cups_wrapper.h
index 2a58187..c23e57d 100644
--- a/chrome/browser/chromeos/printing/cups_wrapper.h
+++ b/chrome/browser/chromeos/printing/cups_wrapper.h
@@ -39,13 +39,20 @@
 
   // Queries CUPS for the current jobs for the given |printer_ids|. Passes
   // the result to |callback|.
-  void QueryCups(
+  void QueryCupsPrintJobs(
       const std::vector<std::string>& printer_ids,
       base::OnceCallback<void(std::unique_ptr<QueryResult>)> callback);
 
   // Cancels the print job on the blocking thread.
   void CancelJob(const std::string& printer_id, int job_id);
 
+  // Queries CUPS for the printer status for the given |printer_id|. Passes the
+  // result to |callback|.
+  void QueryCupsPrinterStatus(
+      const std::string& printer_id,
+      base::OnceCallback<void(std::unique_ptr<::printing::PrinterStatus>)>
+          callback);
+
  private:
   class Backend;
   // The |backend_| handles all communication with CUPS.
diff --git a/chrome/browser/chromeos/printing/printers_map.cc b/chrome/browser/chromeos/printing/printers_map.cc
index d63a389..f0e44a2 100644
--- a/chrome/browser/chromeos/printing/printers_map.cc
+++ b/chrome/browser/chromeos/printing/printers_map.cc
@@ -11,7 +11,8 @@
 
 bool IsProtocolSecure(const Printer& printer) {
   return !printer.HasNetworkProtocol() ||
-         printer.GetProtocol() == Printer::kIpps;
+         printer.GetProtocol() == Printer::kIpps ||
+         printer.GetProtocol() == Printer::kHttps;
 }
 
 }  // namespace
diff --git a/chrome/browser/chromeos/printing/printers_map_unittest.cc b/chrome/browser/chromeos/printing/printers_map_unittest.cc
index 1d6d46b..04bffc5 100644
--- a/chrome/browser/chromeos/printing/printers_map_unittest.cc
+++ b/chrome/browser/chromeos/printing/printers_map_unittest.cc
@@ -184,14 +184,19 @@
   http_printer.set_uri("http:printer");
   printers_map.Insert(PrinterClass::kDiscovered, http_printer);
 
-  // Only IPPS, IPPUSB, and USB printers are returned.
+  Printer https_printer = Printer("https");
+  https_printer.set_uri("https://printer");
+  printers_map.Insert(PrinterClass::kDiscovered, https_printer);
+
+  // Only HTTPS, IPPS, IPPUSB, and USB printers are returned.
   auto printers = printers_map.GetSecurePrinters();
 
-  EXPECT_EQ(3u, printers.size());
+  EXPECT_EQ(4u, printers.size());
 
   EXPECT_TRUE(IsPrinterInPrinters(printers, ipps_printer));
   EXPECT_TRUE(IsPrinterInPrinters(printers, usb_printer));
   EXPECT_TRUE(IsPrinterInPrinters(printers, ippusb_printer));
+  EXPECT_TRUE(IsPrinterInPrinters(printers, https_printer));
 
   EXPECT_FALSE(IsPrinterInPrinters(printers, ipp_printer));
   EXPECT_FALSE(IsPrinterInPrinters(printers, http_printer));
diff --git a/chrome/browser/client_hints/client_hints_browsertest.cc b/chrome/browser/client_hints/client_hints_browsertest.cc
index 4fad139..2db0583 100644
--- a/chrome/browser/client_hints/client_hints_browsertest.cc
+++ b/chrome/browser/client_hints/client_hints_browsertest.cc
@@ -11,6 +11,7 @@
 #include "base/run_loop.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
+#include "base/synchronization/lock.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_entropy_provider.h"
 #include "build/build_config.h"
@@ -405,10 +406,12 @@
   const GURL& redirect_url() const { return redirect_url_; }
 
   size_t count_user_agent_hint_headers_seen() const {
+    base::AutoLock lock(count_headers_lock_);
     return count_user_agent_hint_headers_seen_;
   }
 
   size_t count_client_hints_headers_seen() const {
+    base::AutoLock lock(count_headers_lock_);
     return count_client_hints_headers_seen_;
   }
 
@@ -429,6 +432,7 @@
 
   std::string intercept_iframe_resource_;
   bool intercept_to_http_equiv_iframe_ = false;
+  mutable base::Lock count_headers_lock_;
 
  private:
   // Intercepts only the main frame requests that contain
@@ -584,6 +588,7 @@
     for (size_t i = 0; i < blink::kClientHintsMappingsCount; ++i) {
       if (base::Contains(request.headers,
                          blink::kClientHintsHeaderMapping[i])) {
+        base::AutoLock lock(count_headers_lock_);
         // The user agent hint is special:
         if (std::string(blink::kClientHintsHeaderMapping[i]) == "sec-ch-ua") {
           count_user_agent_hint_headers_seen_++;
diff --git a/chrome/browser/extensions/external_provider_impl_unittest.cc b/chrome/browser/extensions/external_provider_impl_unittest.cc
index bbd2f29..c53d5db 100644
--- a/chrome/browser/extensions/external_provider_impl_unittest.cc
+++ b/chrome/browser/extensions/external_provider_impl_unittest.cc
@@ -139,6 +139,7 @@
               int(sizeof(prefs)));
     InitializeExtensionService(params);
     service_->updater()->Start();
+    content::RunAllTasksUntilIdle();
   }
 
   // ExtensionServiceTestBase overrides:
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index fcff95f..7f035fa 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -49,6 +49,11 @@
     "expiry_milestone": 82
   },
   {
+    "name": "allow-ambient-eq",
+    "owners": [ "zentaro", "baileyberro" ],
+    "expiry_milestone": 85
+  },
+  {
     "name": "allow-disable-mouse-acceleration",
     "owners": [ "zentaro" ],
     "expiry_milestone": 82
@@ -2175,7 +2180,7 @@
   {
     "name": "explore-sites",
     "owners": [ "chili", "dewittj" ],
-    "expiry_milestone": 83
+    "expiry_milestone": 85
   },
   {
     "name": "extension-apis",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index e73feb8..cdab5f7c 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3169,6 +3169,10 @@
     "Use the aggregated ML model to rank the non-app search results for "
     "non-empty queries.";
 
+const char kAllowAmbientEQName[] = "Show Ambient EQ UI";
+const char kAllowAmbientEQDescription[] =
+    "Shows the UI to enable Ambient EQ on devices that support it.";
+
 const char kAllowDisableMouseAccelerationName[] =
     "Allow disabling mouse acceleration";
 const char kAllowDisableMouseAccelerationDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index c2d0d3bf..465843f 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1867,6 +1867,9 @@
 extern const char kAggregatedMlSearchRankingName[];
 extern const char kAggregatedMlSearchRankingDescription[];
 
+extern const char kAllowAmbientEQName[];
+extern const char kAllowAmbientEQDescription[];
+
 extern const char kAllowDisableMouseAccelerationName[];
 extern const char kAllowDisableMouseAccelerationDescription[];
 
diff --git a/chrome/browser/lookalikes/lookalike_url_interstitial_page.cc b/chrome/browser/lookalikes/lookalike_url_interstitial_page.cc
index 758d3a00b..f9efcdca 100644
--- a/chrome/browser/lookalikes/lookalike_url_interstitial_page.cc
+++ b/chrome/browser/lookalikes/lookalike_url_interstitial_page.cc
@@ -90,8 +90,8 @@
   const base::string16 hostname =
       security_interstitials::common_string_util::GetFormattedHostName(
           request_url());
-  load_time_data->SetString("tabTitle",
-                            l10n_util::GetStringUTF16(IDS_LOOKALIKE_URL_TITLE));
+  load_time_data->SetString("tabTitle", l10n_util::GetStringFUTF16(
+                                            IDS_LOOKALIKE_URL_TITLE, hostname));
   load_time_data->SetString(
       "heading",
       l10n_util::GetStringFUTF16(IDS_LOOKALIKE_URL_HEADING, hostname));
diff --git a/chrome/browser/media/router/media_router_metrics.h b/chrome/browser/media/router/media_router_metrics.h
index 33292146..96fbfa2 100644
--- a/chrome/browser/media/router/media_router_metrics.h
+++ b/chrome/browser/media/router/media_router_metrics.h
@@ -22,7 +22,7 @@
 
 // NOTE: Do not renumber enums as that would confuse interpretation of
 // previously logged data. When making changes, also update the enum list
-// in tools/metrics/histograms/histograms.xml to keep it in sync.
+// in tools/metrics/histograms/enums.xml to keep it in sync.
 
 // NOTE: For metrics specific to the Media Router component extension, see
 // mojo/media_router_mojo_metrics.h.
@@ -33,9 +33,10 @@
   OVERFLOW_MENU = 1,
   CONTEXTUAL_MENU = 2,
   PAGE = 3,
+  APP_MENU = 4,
 
   // NOTE: Add entries only immediately above this line.
-  TOTAL_COUNT = 4
+  TOTAL_COUNT = 5
 };
 
 // The possible outcomes from a route creation response.
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index 2ce64c8..71b06f1a 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -584,24 +584,30 @@
   // Always enable the HTTP cache.
   network_context_params->http_cache_enabled = true;
 
+  network_context_params->http_auth_static_network_context_params =
+      network::mojom::HttpAuthStaticNetworkContextParams::New();
+
   // Allow/disallow ambient authentication with default credentials based on the
   // profile type.
   // TODO(https://crbug.com/458508): Allow this behavior to be controllable by
   // policy.
   if (profile_->IsGuestSession()) {
-    network_context_params->allow_default_credentials =
+    network_context_params->http_auth_static_network_context_params
+        ->allow_default_credentials =
         base::FeatureList::IsEnabled(
             features::kEnableAmbientAuthenticationInGuestSession)
             ? net::HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS
             : net::HttpAuthPreferences::DISALLOW_DEFAULT_CREDENTIALS;
   } else if (profile_->IsIncognitoProfile()) {
-    network_context_params->allow_default_credentials =
+    network_context_params->http_auth_static_network_context_params
+        ->allow_default_credentials =
         base::FeatureList::IsEnabled(
             features::kEnableAmbientAuthenticationInIncognito)
             ? net::HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS
             : net::HttpAuthPreferences::DISALLOW_DEFAULT_CREDENTIALS;
   } else {
-    network_context_params->allow_default_credentials =
+    network_context_params->http_auth_static_network_context_params
+        ->allow_default_credentials =
         net::HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS;
   }
 
diff --git a/chrome/browser/net/profile_network_context_service.h b/chrome/browser/net/profile_network_context_service.h
index 96a96b1..f634382 100644
--- a/chrome/browser/net/profile_network_context_service.h
+++ b/chrome/browser/net/profile_network_context_service.h
@@ -94,6 +94,8 @@
       ProfileNetworkContextServiceCertVerifierBuiltinFeaturePolicyTest,
       Test);
 
+  friend class AmbientAuthenticationTest;
+
   // Checks |quic_allowed_|, and disables QUIC if needed.
   void DisableQuicIfNotAllowed();
 
diff --git a/chrome/browser/net/profile_network_context_service_browsertest.cc b/chrome/browser/net/profile_network_context_service_browsertest.cc
index 0aee056..6270cafd 100644
--- a/chrome/browser/net/profile_network_context_service_browsertest.cc
+++ b/chrome/browser/net/profile_network_context_service_browsertest.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths_internal.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
@@ -37,6 +38,7 @@
 #include "content/public/test/simple_url_loader_test_helper.h"
 #include "mojo/public/cpp/system/data_pipe_utils.h"
 #include "net/base/load_flags.h"
+#include "net/http/http_auth_preferences.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
@@ -152,6 +154,64 @@
   EXPECT_TRUE(base::Contains(encodings, "br"));
 }
 
+enum class AmbientAuthenticationStateIncognito { ENABLED, DISABLED };
+
+class AmbientAuthenticationTest : public InProcessBrowserTest {
+ public:
+  explicit AmbientAuthenticationTest(
+      AmbientAuthenticationStateIncognito state =
+          AmbientAuthenticationStateIncognito::ENABLED) {
+    scoped_feature_list_.InitWithFeatureState(
+        features::kEnableAmbientAuthenticationInIncognito,
+        state == AmbientAuthenticationStateIncognito::ENABLED);
+  }
+
+  Profile* GetOffTheRecordProfile() {
+    Profile* regular_profile = browser()->profile();
+    Profile* off_the_record_profile = regular_profile->GetOffTheRecordProfile();
+    EXPECT_TRUE(regular_profile->HasOffTheRecordProfile());
+    return off_the_record_profile;
+  }
+
+  bool IsAmbientAuthAllowedForProfile(Profile* profile) {
+    ProfileNetworkContextService* profile_network_context_service =
+        ProfileNetworkContextServiceFactory::GetForContext(profile);
+    base::FilePath empty_relative_partition_path;
+    network::mojom::NetworkContextParamsPtr network_context_params_ptr =
+        profile_network_context_service->CreateNetworkContextParams(
+            /*in_memory=*/false, empty_relative_partition_path);
+    return network_context_params_ptr->http_auth_static_network_context_params
+               ->allow_default_credentials ==
+           net::HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS;
+  }
+
+ protected:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+class AmbientAuthenticationTestDisabled : public AmbientAuthenticationTest {
+ public:
+  explicit AmbientAuthenticationTestDisabled()
+      : AmbientAuthenticationTest(
+            AmbientAuthenticationStateIncognito::DISABLED) {}
+};
+
+IN_PROC_BROWSER_TEST_F(AmbientAuthenticationTest,
+                       AllowDefaultCredentialsRegular) {
+  EXPECT_TRUE(IsAmbientAuthAllowedForProfile(browser()->profile()));
+}
+
+IN_PROC_BROWSER_TEST_F(AmbientAuthenticationTest,
+                       AllowDefaultCredentialsIncognito) {
+  EXPECT_TRUE(IsAmbientAuthAllowedForProfile(GetOffTheRecordProfile()));
+}
+
+IN_PROC_BROWSER_TEST_F(AmbientAuthenticationTestDisabled,
+                       DisallowDefaultCredentialsIncognito) {
+  EXPECT_TRUE(IsAmbientAuthAllowedForProfile(browser()->profile()));
+  EXPECT_FALSE(IsAmbientAuthAllowedForProfile(GetOffTheRecordProfile()));
+}
+
 // Test subclass that adds switches::kDiskCacheDir and switches::kDiskCacheSize
 // to the command line, to make sure they're respected.
 class ProfileNetworkContextServiceDiskCacheBrowsertest
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
index 517b626..54b3b91 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
@@ -105,21 +105,6 @@
   return true;
 }
 
-// Returns the OptimizationGuideNavigationData for |navigation_handle| if the
-// OptimizationGuideWebContentsObserver is registered.
-OptimizationGuideNavigationData* GetNavigationDataForNavigationHandle(
-    content::NavigationHandle* navigation_handle) {
-  OptimizationGuideWebContentsObserver*
-      optimization_guide_web_contents_observer =
-          OptimizationGuideWebContentsObserver::FromWebContents(
-              navigation_handle->GetWebContents());
-  if (!optimization_guide_web_contents_observer)
-    return nullptr;
-
-  return optimization_guide_web_contents_observer
-      ->GetOrCreateOptimizationGuideNavigationData(navigation_handle);
-}
-
 // Returns the page hint for the navigation, if applicable. It will use the
 // cached page hint stored in |navigation_handle| if we have already done the
 // computation to find the page hint in a previous request to the hints manager.
@@ -129,7 +114,8 @@
     content::NavigationHandle* navigation_handle,
     const optimization_guide::proto::Hint* loaded_hint) {
   OptimizationGuideNavigationData* navigation_data =
-      GetNavigationDataForNavigationHandle(navigation_handle);
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle);
 
   // If we already know we had a page hint for the navigation, then just return
   // that.
@@ -588,7 +574,8 @@
   }
 
   OptimizationGuideNavigationData* navigation_data =
-      GetNavigationDataForNavigationHandle(navigation_handle);
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle);
   if (navigation_data) {
     bool has_hint = hint_cache_->HasHint(url.host());
     if (navigation_handle->HasCommitted()) {
@@ -788,7 +775,8 @@
 
   // Populate navigation data with hint information.
   OptimizationGuideNavigationData* navigation_data =
-      GetNavigationDataForNavigationHandle(navigation_handle);
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle);
   if (navigation_data) {
     navigation_data->set_has_hint_after_commit(has_hint_in_cache);
 
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
index b0879e8..4a7780a 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
@@ -353,17 +353,6 @@
     return navigation_handle;
   }
 
-  // Returns the optimization guide navigation data attached to
-  // |navigation_handle|.
-  OptimizationGuideNavigationData* GetOptimizationGuideNavigationData(
-      content::NavigationHandle* navigation_handle) {
-    OptimizationGuideWebContentsObserver* observer =
-        OptimizationGuideWebContentsObserver::FromWebContents(
-            navigation_handle->GetWebContents());
-    return observer->GetOrCreateOptimizationGuideNavigationData(
-        navigation_handle);
-  }
-
   OptimizationGuideHintsManager* hints_manager() const {
     return hints_manager_.get();
   }
@@ -737,7 +726,8 @@
                                       true, 1);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
@@ -761,7 +751,8 @@
                                       true, 1);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
   EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_after_commit());
@@ -785,7 +776,8 @@
                                       false, 1);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
   EXPECT_FALSE(navigation_data->has_hint_before_commit().value());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_after_commit());
@@ -808,7 +800,8 @@
   histogram_tester.ExpectTotalCount("OptimizationGuide.LoadedHint.Result", 0);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_after_commit());
@@ -1265,7 +1258,8 @@
             optimization_type_decision);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_after_commit());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
@@ -1309,7 +1303,8 @@
             optimization_type_decision);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_FALSE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
@@ -1356,7 +1351,8 @@
             optimization_type_decision);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_FALSE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
@@ -1403,7 +1399,8 @@
             optimization_type_decision);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_FALSE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
@@ -1450,7 +1447,8 @@
             optimization_type_decision);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_FALSE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
@@ -1498,7 +1496,8 @@
             optimization_type_decision);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_FALSE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
@@ -1542,7 +1541,8 @@
             optimization_type_decision);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
   EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
@@ -1581,7 +1581,8 @@
             optimization_type_decision);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
   EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
@@ -1621,7 +1622,8 @@
             optimization_type_decision);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
   EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
@@ -1659,7 +1661,8 @@
             optimization_type_decision);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
   EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
@@ -1698,7 +1701,8 @@
             optimization_type_decision);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
   EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
@@ -1723,7 +1727,8 @@
   // Purposely set the page hint to be null to show that we override the page
   // hint information from the navigation handle.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   navigation_data->set_page_hint(nullptr);
 
   optimization_guide::OptimizationTargetDecision optimization_target_decision;
@@ -2050,7 +2055,8 @@
             optimization_type_decision);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
   EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
@@ -2086,7 +2092,8 @@
             optimization_type_decision);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_FALSE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
@@ -2121,7 +2128,8 @@
       optimization_type_decision);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
@@ -2184,7 +2192,8 @@
             optimization_type_decision);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
   EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
@@ -2248,7 +2257,8 @@
             optimization_type_decision);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
-      GetOptimizationGuideNavigationData(navigation_handle.get());
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle.get());
   EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
   EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
index be6b6d7..4b02f36 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
@@ -10,7 +10,6 @@
 #include "chrome/browser/optimization_guide/optimization_guide_navigation_data.h"
 #include "chrome/browser/optimization_guide/optimization_guide_session_statistic.h"
 #include "chrome/browser/optimization_guide/optimization_guide_top_host_provider.h"
-#include "chrome/browser/optimization_guide/optimization_guide_web_contents_observer.h"
 #include "chrome/browser/optimization_guide/prediction/prediction_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/leveldb_proto/public/proto_database_provider.h"
@@ -48,18 +47,13 @@
     optimization_guide::proto::OptimizationTarget optimization_target,
     optimization_guide::OptimizationTargetDecision
         optimization_target_decision) {
-  OptimizationGuideWebContentsObserver*
-      optimization_guide_web_contents_observer =
-          OptimizationGuideWebContentsObserver::FromWebContents(
-              navigation_handle->GetWebContents());
-  if (!optimization_guide_web_contents_observer)
-    return;
-
   OptimizationGuideNavigationData* navigation_data =
-      optimization_guide_web_contents_observer
-          ->GetOrCreateOptimizationGuideNavigationData(navigation_handle);
-  navigation_data->SetDecisionForOptimizationTarget(
-      optimization_target, optimization_target_decision);
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle);
+  if (navigation_data) {
+    navigation_data->SetDecisionForOptimizationTarget(
+        optimization_target, optimization_target_decision);
+  }
 }
 
 // Logs the |optimization_type_decision| for |optimization_type| in the current
@@ -68,18 +62,13 @@
     content::NavigationHandle* navigation_handle,
     optimization_guide::proto::OptimizationType optimization_type,
     optimization_guide::OptimizationTypeDecision optimization_type_decision) {
-  OptimizationGuideWebContentsObserver*
-      optimization_guide_web_contents_observer =
-          OptimizationGuideWebContentsObserver::FromWebContents(
-              navigation_handle->GetWebContents());
-  if (!optimization_guide_web_contents_observer)
-    return;
-
   OptimizationGuideNavigationData* navigation_data =
-      optimization_guide_web_contents_observer
-          ->GetOrCreateOptimizationGuideNavigationData(navigation_handle);
-  navigation_data->SetDecisionForOptimizationType(optimization_type,
-                                                  optimization_type_decision);
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle);
+  if (navigation_data) {
+    navigation_data->SetDecisionForOptimizationType(optimization_type,
+                                                    optimization_type_decision);
+  }
 }
 
 // Returns the OptimizationGuideDecision from |optimization_target_decision|.
@@ -245,12 +234,6 @@
   if (prediction_manager_) {
     optimization_target_decision = prediction_manager_->ShouldTargetNavigation(
         navigation_handle, optimization_target);
-    if (optimization_guide::features::
-            ShouldOverrideOptimizationTargetDecisionForMetricsPurposes(
-                optimization_target)) {
-      optimization_target_decision = optimization_guide::
-          OptimizationTargetDecision::kModelPredictionHoldback;
-    }
   } else {
     DCHECK(hints_manager_);
     optimization_guide::OptimizationTypeDecision
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
index 260da5f..20edad6 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
@@ -817,57 +817,3 @@
                            kModelNotAvailableOnClient),
       1);
 }
-
-class OptimizationGuideKeyedServiceModelPredictionHoldbackBrowserTest
-    : public OptimizationGuideKeyedServiceBrowserTest {
- public:
-  OptimizationGuideKeyedServiceModelPredictionHoldbackBrowserTest() {
-    scoped_feature_list_.InitWithFeaturesAndParameters(
-        {base::test::ScopedFeatureList::FeatureAndParams(
-             optimization_guide::features::kOptimizationTargetPrediction,
-             {{"painful_page_load_metrics_only", "true"}}),
-         base::test::ScopedFeatureList::FeatureAndParams(
-             optimization_guide::features::kOptimizationHintsFetching, {{}})},
-        {});
-  }
-  ~OptimizationGuideKeyedServiceModelPredictionHoldbackBrowserTest() override =
-      default;
-
-  void SetUpCommandLine(base::CommandLine* cmd) override {
-    cmd->AppendSwitchASCII(optimization_guide::switches::kFetchHintsOverride,
-                           "whatever.com,somehost.com");
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(
-    OptimizationGuideKeyedServiceModelPredictionHoldbackBrowserTest,
-    ModelPredictionHoldbackOverridesActualTargetDecision) {
-  PushHintsComponentAndWaitForCompletion();
-  RegisterWithKeyedService();
-
-  ukm::TestAutoSetUkmRecorder ukm_recorder;
-  base::HistogramTester histogram_tester;
-
-  ui_test_utils::NavigateToURL(browser(), url_with_hints());
-
-  EXPECT_EQ(RetryForHistogramUntilCountReached(
-                histogram_tester, "OptimizationGuide.LoadedHint.Result", 1),
-            1);
-  // There should be a hint that matches this URL.
-  histogram_tester.ExpectUniqueSample("OptimizationGuide.LoadedHint.Result",
-                                      true, 1);
-  EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kFalse,
-            last_should_target_navigation_decision());
-  EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kTrue,
-            last_can_apply_optimization_decision());
-  EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kFalse,
-            last_consumer_decision());
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.TargetDecision.PainfulPageLoad",
-      static_cast<int>(optimization_guide::OptimizationTargetDecision::
-                           kModelPredictionHoldback),
-      1);
-}
diff --git a/chrome/browser/optimization_guide/optimization_guide_navigation_data.cc b/chrome/browser/optimization_guide/optimization_guide_navigation_data.cc
index 9b87b906..7309989 100644
--- a/chrome/browser/optimization_guide/optimization_guide_navigation_data.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_navigation_data.cc
@@ -8,7 +8,9 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/stringprintf.h"
+#include "chrome/browser/optimization_guide/optimization_guide_web_contents_observer.h"
 #include "components/optimization_guide/hints_processing_util.h"
+#include "content/public/browser/navigation_handle.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_source.h"
@@ -45,6 +47,10 @@
       serialized_hint_version_string_(other.serialized_hint_version_string_),
       optimization_type_decisions_(other.optimization_type_decisions_),
       optimization_target_decisions_(other.optimization_target_decisions_),
+      optimization_target_model_versions_(
+          other.optimization_target_model_versions_),
+      optimization_target_model_prediction_scores_(
+          other.optimization_target_model_prediction_scores_),
       has_hint_before_commit_(other.has_hint_before_commit_),
       has_hint_after_commit_(other.has_hint_after_commit_),
       was_host_covered_by_fetch_at_navigation_start_(
@@ -55,6 +61,20 @@
   }
 }
 
+// static
+OptimizationGuideNavigationData*
+OptimizationGuideNavigationData::GetFromNavigationHandle(
+    content::NavigationHandle* navigation_handle) {
+  OptimizationGuideWebContentsObserver*
+      optimization_guide_web_contents_observer =
+          OptimizationGuideWebContentsObserver::FromWebContents(
+              navigation_handle->GetWebContents());
+  if (!optimization_guide_web_contents_observer)
+    return nullptr;
+  return optimization_guide_web_contents_observer
+      ->GetOrCreateOptimizationGuideNavigationData(navigation_handle);
+}
+
 void OptimizationGuideNavigationData::RecordMetrics(bool has_committed) const {
   RecordHintCacheMatch(has_committed);
   RecordOptimizationTypeAndTargetDecisions();
@@ -130,37 +150,54 @@
 }
 
 void OptimizationGuideNavigationData::RecordOptimizationGuideUKM() const {
-  if (!serialized_hint_version_string_.has_value() ||
-      serialized_hint_version_string_.value().empty())
-    return;
-
-  // Deserialize the serialized version string into its protobuffer.
-  std::string binary_version_pb;
-  if (!base::Base64Decode(serialized_hint_version_string_.value(),
-                          &binary_version_pb))
-    return;
-
-  optimization_guide::proto::Version hint_version;
-  if (!hint_version.ParseFromString(binary_version_pb))
-    return;
-
-  // Record the UKM.
+  bool did_record_metric = false;
   ukm::SourceId ukm_source_id =
       ukm::ConvertToSourceId(navigation_id_, ukm::SourceIdType::NAVIGATION_ID);
   ukm::builders::OptimizationGuide builder(ukm_source_id);
 
-  bool did_record_metric = false;
-  if (hint_version.has_generation_timestamp() &&
-      hint_version.generation_timestamp().seconds() > 0) {
-    did_record_metric = true;
-    builder.SetHintGenerationTimestamp(
-        hint_version.generation_timestamp().seconds());
+  // Record model metrics.
+  for (const auto& optimization_target_model_version :
+       optimization_target_model_versions_) {
+    if (optimization_target_model_version.first ==
+        optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD) {
+      did_record_metric = true;
+      builder.SetPainfulPageLoadModelVersion(
+          optimization_target_model_version.second);
+    }
   }
-  if (hint_version.has_hint_source() &&
-      hint_version.hint_source() !=
-          optimization_guide::proto::HINT_SOURCE_UNKNOWN) {
-    did_record_metric = true;
-    builder.SetHintSource(static_cast<int>(hint_version.hint_source()));
+  for (const auto& optimization_target_model_prediction_score :
+       optimization_target_model_prediction_scores_) {
+    if (optimization_target_model_prediction_score.first ==
+        optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD) {
+      did_record_metric = true;
+      builder.SetPainfulPageLoadModelPredictionScore(static_cast<int64_t>(
+          100 * optimization_target_model_prediction_score.second));
+    }
+  }
+
+  // Record hint metrics.
+  if (serialized_hint_version_string_.has_value() &&
+      !serialized_hint_version_string_.value().empty()) {
+    // Deserialize the serialized version string into its protobuffer.
+    std::string binary_version_pb;
+    if (base::Base64Decode(serialized_hint_version_string_.value(),
+                           &binary_version_pb)) {
+      optimization_guide::proto::Version hint_version;
+      if (hint_version.ParseFromString(binary_version_pb)) {
+        if (hint_version.has_generation_timestamp() &&
+            hint_version.generation_timestamp().seconds() > 0) {
+          did_record_metric = true;
+          builder.SetHintGenerationTimestamp(
+              hint_version.generation_timestamp().seconds());
+        }
+        if (hint_version.has_hint_source() &&
+            hint_version.hint_source() !=
+                optimization_guide::proto::HINT_SOURCE_UNKNOWN) {
+          did_record_metric = true;
+          builder.SetHintSource(static_cast<int>(hint_version.hint_source()));
+        }
+      }
+    }
   }
 
   // Only record UKM if a metric was recorded.
@@ -199,3 +236,39 @@
     optimization_guide::OptimizationTargetDecision decision) {
   optimization_target_decisions_[optimization_target] = decision;
 }
+
+base::Optional<int64_t>
+OptimizationGuideNavigationData::GetModelVersionForOptimizationTarget(
+    optimization_guide::proto::OptimizationTarget optimization_target) const {
+  auto optimization_target_model_version_iter =
+      optimization_target_model_versions_.find(optimization_target);
+  if (optimization_target_model_version_iter ==
+      optimization_target_model_versions_.end())
+    return base::nullopt;
+  return optimization_target_model_version_iter->second;
+}
+
+void OptimizationGuideNavigationData::SetModelVersionForOptimizationTarget(
+    optimization_guide::proto::OptimizationTarget optimization_target,
+    int64_t model_version) {
+  optimization_target_model_versions_[optimization_target] = model_version;
+}
+
+base::Optional<double>
+OptimizationGuideNavigationData::GetModelPredictionScoreForOptimizationTarget(
+    optimization_guide::proto::OptimizationTarget optimization_target) const {
+  auto optimization_target_model_prediction_score_iter =
+      optimization_target_model_prediction_scores_.find(optimization_target);
+  if (optimization_target_model_prediction_score_iter ==
+      optimization_target_model_prediction_scores_.end())
+    return base::nullopt;
+  return optimization_target_model_prediction_score_iter->second;
+}
+
+void OptimizationGuideNavigationData::
+    SetModelPredictionScoreForOptimizationTarget(
+        optimization_guide::proto::OptimizationTarget optimization_target,
+        double model_prediction_score) {
+  optimization_target_model_prediction_scores_[optimization_target] =
+      model_prediction_score;
+}
diff --git a/chrome/browser/optimization_guide/optimization_guide_navigation_data.h b/chrome/browser/optimization_guide/optimization_guide_navigation_data.h
index 5f6b411..575544bb 100644
--- a/chrome/browser/optimization_guide/optimization_guide_navigation_data.h
+++ b/chrome/browser/optimization_guide/optimization_guide_navigation_data.h
@@ -26,6 +26,11 @@
 
   OptimizationGuideNavigationData(const OptimizationGuideNavigationData& other);
 
+  // Returns the OptimizationGuideNavigationData for |navigation_handle|. Will
+  // return nullptr if one cannot be created for it for any reason.
+  static OptimizationGuideNavigationData* GetFromNavigationHandle(
+      content::NavigationHandle* navigation_handle);
+
   // Records metrics based on data currently held in |this|. |has_committed|
   // indicates whether commit-time metrics should be recorded.
   void RecordMetrics(bool has_committed) const;
@@ -61,6 +66,23 @@
       optimization_guide::proto::OptimizationTarget optimization_target,
       optimization_guide::OptimizationTargetDecision decision);
 
+  // Returns the version of the model evaluated for |optimization_target|.
+  base::Optional<int64_t> GetModelVersionForOptimizationTarget(
+      optimization_guide::proto::OptimizationTarget optimization_target) const;
+  // Sets the |model_version| for |optimization_target|.
+  void SetModelVersionForOptimizationTarget(
+      optimization_guide::proto::OptimizationTarget optimization_target,
+      int64_t model_version);
+
+  // Returns the prediction score of the model evaluated for
+  // |optimization_target|.
+  base::Optional<double> GetModelPredictionScoreForOptimizationTarget(
+      optimization_guide::proto::OptimizationTarget optimization_target) const;
+  // Sets the |model_prediction_score| for |optimization_target|.
+  void SetModelPredictionScoreForOptimizationTarget(
+      optimization_guide::proto::OptimizationTarget optimization_target,
+      double model_prediction_score);
+
   // Whether the hint cache had a hint for the navigation before commit.
   base::Optional<bool> has_hint_before_commit() const {
     return has_hint_before_commit_;
@@ -127,6 +149,17 @@
                  optimization_guide::OptimizationTargetDecision>
       optimization_target_decisions_;
 
+  // The version of the painful page load model that was evaluated for the
+  // page load.
+  base::flat_map<optimization_guide::proto::OptimizationTarget, int64_t>
+      optimization_target_model_versions_;
+
+  // The score output after evaluating the painful page load model. If
+  // populated, this is 100x the fractional value output by the model
+  // evaluation.
+  base::flat_map<optimization_guide::proto::OptimizationTarget, double>
+      optimization_target_model_prediction_scores_;
+
   // Whether the hint cache had a hint for the navigation before commit.
   base::Optional<bool> has_hint_before_commit_;
 
diff --git a/chrome/browser/optimization_guide/optimization_guide_navigation_data_unittest.cc b/chrome/browser/optimization_guide/optimization_guide_navigation_data_unittest.cc
index e46a63fa..8b75578 100644
--- a/chrome/browser/optimization_guide/optimization_guide_navigation_data_unittest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_navigation_data_unittest.cc
@@ -434,6 +434,87 @@
 }
 
 TEST(OptimizationGuideNavigationDataTest,
+     RecordMetricsOptimizationTargetModelVersion) {
+  base::test::TaskEnvironment env;
+
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
+
+  OptimizationGuideNavigationData data(/*navigation_id=*/3);
+  data.SetModelVersionForOptimizationTarget(
+      optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, 2);
+  data.RecordMetrics(/*has_committed=*/false);
+
+  auto entries = ukm_recorder.GetEntriesByName(
+      ukm::builders::OptimizationGuide::kEntryName);
+  EXPECT_EQ(1u, entries.size());
+  auto* entry = entries[0];
+  EXPECT_TRUE(ukm_recorder.EntryHasMetric(
+      entry,
+      ukm::builders::OptimizationGuide::kPainfulPageLoadModelVersionName));
+  ukm_recorder.ExpectEntryMetric(
+      entry, ukm::builders::OptimizationGuide::kPainfulPageLoadModelVersionName,
+      2);
+}
+
+TEST(OptimizationGuideNavigationDataTest,
+     RecordMetricsModelVersionForOptimizationTargetHasNoCorrespondingUkm) {
+  base::test::TaskEnvironment env;
+
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
+
+  OptimizationGuideNavigationData data(/*navigation_id=*/3);
+  data.SetModelVersionForOptimizationTarget(
+      optimization_guide::proto::OPTIMIZATION_TARGET_UNKNOWN, 2);
+  data.RecordMetrics(/*has_committed=*/false);
+
+  // Make sure UKM not recorded for all empty values.
+  auto entries = ukm_recorder.GetEntriesByName(
+      ukm::builders::OptimizationGuide::kEntryName);
+  EXPECT_TRUE(entries.empty());
+}
+
+TEST(OptimizationGuideNavigationDataTest,
+     RecordMetricsOptimizationTargetModelPredictionScore) {
+  base::test::TaskEnvironment env;
+
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
+
+  OptimizationGuideNavigationData data(/*navigation_id=*/3);
+  data.SetModelPredictionScoreForOptimizationTarget(
+      optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, 0.123);
+  data.RecordMetrics(/*has_committed=*/false);
+
+  auto entries = ukm_recorder.GetEntriesByName(
+      ukm::builders::OptimizationGuide::kEntryName);
+  EXPECT_EQ(1u, entries.size());
+  auto* entry = entries[0];
+  EXPECT_TRUE(ukm_recorder.EntryHasMetric(
+      entry, ukm::builders::OptimizationGuide::
+                 kPainfulPageLoadModelPredictionScoreName));
+  ukm_recorder.ExpectEntryMetric(entry,
+                                 ukm::builders::OptimizationGuide::
+                                     kPainfulPageLoadModelPredictionScoreName,
+                                 12);
+}
+
+TEST(OptimizationGuideNavigationDataTest,
+     RecordMetricsModelPredicitonScoreOptimizationTargetHasNoCorrespondingUkm) {
+  base::test::TaskEnvironment env;
+
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
+
+  OptimizationGuideNavigationData data(/*navigation_id=*/3);
+  data.SetModelPredictionScoreForOptimizationTarget(
+      optimization_guide::proto::OPTIMIZATION_TARGET_UNKNOWN, 0.123);
+  data.RecordMetrics(/*has_committed=*/false);
+
+  // Make sure UKM not recorded for all empty values.
+  auto entries = ukm_recorder.GetEntriesByName(
+      ukm::builders::OptimizationGuide::kEntryName);
+  EXPECT_TRUE(entries.empty());
+}
+
+TEST(OptimizationGuideNavigationDataTest,
      RecordMetricsMultipleOptimizationTypes) {
   base::HistogramTester histogram_tester;
 
@@ -537,6 +618,14 @@
   EXPECT_EQ(base::nullopt, data->has_hint_before_commit());
   EXPECT_EQ(base::nullopt, data->has_hint_after_commit());
   EXPECT_FALSE(data->has_page_hint_value());
+  EXPECT_EQ(
+      base::nullopt,
+      data->GetModelVersionForOptimizationTarget(
+          optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD));
+  EXPECT_EQ(
+      base::nullopt,
+      data->GetModelPredictionScoreForOptimizationTarget(
+          optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD));
 
   data->set_serialized_hint_version_string("123abc");
   data->SetDecisionForOptimizationType(
@@ -552,6 +641,10 @@
   page_hint.set_page_pattern("pagepattern");
   data->set_page_hint(
       std::make_unique<optimization_guide::proto::PageHint>(page_hint));
+  data->SetModelVersionForOptimizationTarget(
+      optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, 123);
+  data->SetModelPredictionScoreForOptimizationTarget(
+      optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, 0.12);
 
   OptimizationGuideNavigationData data_copy(*data);
   EXPECT_EQ(3, data_copy.navigation_id());
@@ -567,4 +660,12 @@
   EXPECT_EQ("123abc", *(data_copy.serialized_hint_version_string()));
   EXPECT_TRUE(data_copy.has_page_hint_value());
   EXPECT_EQ("pagepattern", data_copy.page_hint()->page_pattern());
+  EXPECT_EQ(
+      123,
+      *data_copy.GetModelVersionForOptimizationTarget(
+          optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD));
+  EXPECT_EQ(
+      0.12,
+      *data_copy.GetModelPredictionScoreForOptimizationTarget(
+          optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD));
 }
diff --git a/chrome/browser/optimization_guide/optimization_guide_web_contents_observer.cc b/chrome/browser/optimization_guide/optimization_guide_web_contents_observer.cc
index 5eb5712..7baaf520 100644
--- a/chrome/browser/optimization_guide/optimization_guide_web_contents_observer.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_web_contents_observer.cc
@@ -19,6 +19,14 @@
 
 namespace {
 
+bool WasHostCoveredByFetch(content::NavigationHandle* navigation_handle) {
+  return optimization_guide::HintsFetcher::WasHostCoveredByFetch(
+      Profile::FromBrowserContext(
+          navigation_handle->GetWebContents()->GetBrowserContext())
+          ->GetPrefs(),
+      navigation_handle->GetURL().host());
+}
+
 // Records if the host for the current navigation was successfully
 // covered by a HintsFetch. HintsFetching must be enabled and only HTTPS
 // navigations are logged. Returns whether navigation was covered by fetch.
@@ -28,13 +36,7 @@
   if (!optimization_guide::features::IsHintsFetchingEnabled())
     return false;
 
-  bool was_host_covered_by_fetch =
-      optimization_guide::HintsFetcher::WasHostCoveredByFetch(
-          Profile::FromBrowserContext(
-              navigation_handle->GetWebContents()->GetBrowserContext())
-              ->GetPrefs(),
-          navigation_handle->GetURL().GetOrigin().host());
-
+  bool was_host_covered_by_fetch = WasHostCoveredByFetch(navigation_handle);
   UMA_HISTOGRAM_BOOLEAN(
       "OptimizationGuide.HintsFetcher.NavigationHostCoveredByFetch",
       was_host_covered_by_fetch);
@@ -86,8 +88,6 @@
   OptimizationGuideTopHostProvider::MaybeUpdateTopHostBlacklist(
       navigation_handle);
 
-  // Record the HintsFetcher coverage for the navigation, regardless if the
-  // keyed service is active or not.
   bool was_host_covered_by_fetch =
       RecordHintsFetcherCoverage(navigation_handle);
 
@@ -124,6 +124,16 @@
     content::NavigationHandle* navigation_handle) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
+  // If the navigation committed, this will cover if the race with the
+  // navigation was able to cover the navigation or not.
+  if (navigation_handle->HasCommitted() &&
+      navigation_handle->GetURL().SchemeIsHTTPOrHTTPS()) {
+    UMA_HISTOGRAM_BOOLEAN(
+        "OptimizationGuide.HintsFetcher.NavigationHostCoveredByFetch."
+        "AtCommit",
+        WasHostCoveredByFetch(navigation_handle));
+  }
+
   // Delete Optimization Guide information later, so that other
   // DidFinishNavigation methods can reliably use
   // GetOptimizationGuideNavigationData regardless of order of
diff --git a/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model.cc b/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model.cc
index e7ea347..121ba5b 100644
--- a/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model.cc
+++ b/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model.cc
@@ -123,14 +123,15 @@
 
 optimization_guide::OptimizationTargetDecision
 DecisionTreePredictionModel::Predict(
-    const base::flat_map<std::string, float>& model_features) {
+    const base::flat_map<std::string, float>& model_features,
+    double* prediction_score) {
   SEQUENCE_CHECKER(sequence_checker_);
 
-  double result = 0.0;
+  *prediction_score = 0.0;
   // TODO(mcrouse): Add metrics to record if the model evaluation fails.
-  if (!EvaluateModel(*model_.get(), model_features, &result))
+  if (!EvaluateModel(*model_.get(), model_features, prediction_score))
     return optimization_guide::OptimizationTargetDecision::kUnknown;
-  if (result > model_->threshold().value())
+  if (*prediction_score > model_->threshold().value())
     return optimization_guide::OptimizationTargetDecision::kPageLoadMatches;
   return optimization_guide::OptimizationTargetDecision::kPageLoadDoesNotMatch;
 }
diff --git a/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model.h b/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model.h
index c367a49..7fe0b051 100644
--- a/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model.h
+++ b/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model.h
@@ -30,7 +30,8 @@
 
   // PredictionModel implementation:
   optimization_guide::OptimizationTargetDecision Predict(
-      const base::flat_map<std::string, float>& model_features) override;
+      const base::flat_map<std::string, float>& model_features,
+      double* prediction_score) override;
 
  private:
   // Evaluates the provided model, either an ensemble or decision tree model,
diff --git a/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model_unittest.cc b/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model_unittest.cc
index 44b0600..7e9b4c0 100644
--- a/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model_unittest.cc
+++ b/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model_unittest.cc
@@ -83,10 +83,14 @@
   std::unique_ptr<PredictionModel> model =
       PredictionModel::Create(std::move(prediction_model), {"agg1"});
   EXPECT_TRUE(model);
+
+  double prediction_score;
   EXPECT_EQ(OptimizationTargetDecision::kPageLoadDoesNotMatch,
-            model->Predict({{"agg1", 1.0}}));
+            model->Predict({{"agg1", 1.0}}, &prediction_score));
+  EXPECT_EQ(4., prediction_score);
   EXPECT_EQ(OptimizationTargetDecision::kPageLoadMatches,
-            model->Predict({{"agg1", 2.0}}));
+            model->Predict({{"agg1", 2.0}}, &prediction_score));
+  EXPECT_EQ(8., prediction_score);
 }
 
 TEST(DecisionTreePredictionModel, InequalityLessThan) {
@@ -111,10 +115,14 @@
   std::unique_ptr<PredictionModel> model =
       PredictionModel::Create(std::move(prediction_model), {"agg1"});
   EXPECT_TRUE(model);
+
+  double prediction_score;
   EXPECT_EQ(OptimizationTargetDecision::kPageLoadDoesNotMatch,
-            model->Predict({{"agg1", 0.5}}));
+            model->Predict({{"agg1", 0.5}}, &prediction_score));
+  EXPECT_EQ(4., prediction_score);
   EXPECT_EQ(OptimizationTargetDecision::kPageLoadMatches,
-            model->Predict({{"agg1", 2.0}}));
+            model->Predict({{"agg1", 2.0}}, &prediction_score));
+  EXPECT_EQ(8., prediction_score);
 }
 
 TEST(DecisionTreePredictionModel, InequalityGreaterOrEqual) {
@@ -139,10 +147,14 @@
   std::unique_ptr<PredictionModel> model =
       PredictionModel::Create(std::move(prediction_model), {"agg1"});
   EXPECT_TRUE(model);
+
+  double prediction_score;
   EXPECT_EQ(OptimizationTargetDecision::kPageLoadMatches,
-            model->Predict({{"agg1", 0.5}}));
+            model->Predict({{"agg1", 0.5}}, &prediction_score));
+  EXPECT_EQ(8., prediction_score);
   EXPECT_EQ(OptimizationTargetDecision::kPageLoadDoesNotMatch,
-            model->Predict({{"agg1", 1.0}}));
+            model->Predict({{"agg1", 1.0}}, &prediction_score));
+  EXPECT_EQ(4., prediction_score);
 }
 
 TEST(DecisionTreePredictionModel, InequalityGreaterThan) {
@@ -167,10 +179,14 @@
   std::unique_ptr<PredictionModel> model =
       PredictionModel::Create(std::move(prediction_model), {"agg1"});
   EXPECT_TRUE(model);
+
+  double prediction_score;
   EXPECT_EQ(OptimizationTargetDecision::kPageLoadMatches,
-            model->Predict({{"agg1", 0.5}}));
+            model->Predict({{"agg1", 0.5}}, &prediction_score));
+  EXPECT_EQ(8., prediction_score);
   EXPECT_EQ(OptimizationTargetDecision::kPageLoadDoesNotMatch,
-            model->Predict({{"agg1", 2.0}}));
+            model->Predict({{"agg1", 2.0}}, &prediction_score));
+  EXPECT_EQ(4., prediction_score);
 }
 
 TEST(DecisionTreePredictionModel, MissingInequalityTest) {
@@ -416,10 +432,14 @@
   std::unique_ptr<PredictionModel> model =
       PredictionModel::Create(std::move(prediction_model), {"agg1"});
   EXPECT_TRUE(model);
+
+  double prediction_score;
   EXPECT_EQ(OptimizationTargetDecision::kPageLoadDoesNotMatch,
-            model->Predict({{"agg1", 1.0}}));
+            model->Predict({{"agg1", 1.0}}, &prediction_score));
+  EXPECT_EQ(4., prediction_score);
   EXPECT_EQ(OptimizationTargetDecision::kPageLoadMatches,
-            model->Predict({{"agg1", 2.0}}));
+            model->Predict({{"agg1", 2.0}}, &prediction_score));
+  EXPECT_EQ(8., prediction_score);
 }
 
 TEST(DecisionTreePredictionModel, EnsembleWithNoMembers) {
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager.cc b/chrome/browser/optimization_guide/prediction/prediction_manager.cc
index 8c71e890..480840b1 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_manager.cc
+++ b/chrome/browser/optimization_guide/prediction/prediction_manager.cc
@@ -12,6 +12,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/engagement/site_engagement_service.h"
+#include "chrome/browser/optimization_guide/optimization_guide_navigation_data.h"
 #include "chrome/browser/optimization_guide/optimization_guide_session_statistic.h"
 #include "chrome/browser/optimization_guide/prediction/prediction_model.h"
 #include "chrome/browser/optimization_guide/prediction/prediction_model_fetcher.h"
@@ -182,7 +183,28 @@
   base::flat_map<std::string, float> feature_map =
       BuildFeatureMap(navigation_handle, prediction_model->GetModelFeatures());
 
-  return prediction_model->Predict(feature_map);
+  double prediction_score = 0.0;
+  optimization_guide::OptimizationTargetDecision target_decision =
+      prediction_model->Predict(feature_map, &prediction_score);
+
+  OptimizationGuideNavigationData* navigation_data =
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          navigation_handle);
+  if (navigation_data) {
+    navigation_data->SetModelVersionForOptimizationTarget(
+        optimization_target, prediction_model->GetVersion());
+    navigation_data->SetModelPredictionScoreForOptimizationTarget(
+        optimization_target, prediction_score);
+  }
+
+  if (optimization_guide::features::
+          ShouldOverrideOptimizationTargetDecisionForMetricsPurposes(
+              optimization_target)) {
+    return optimization_guide::OptimizationTargetDecision::
+        kModelPredictionHoldback;
+  }
+
+  return target_decision;
 }
 
 void PredictionManager::OnEffectiveConnectionTypeChanged(
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager_unittest.cc b/chrome/browser/optimization_guide/prediction/prediction_manager_unittest.cc
index edda059a..7a978a78 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_manager_unittest.cc
+++ b/chrome/browser/optimization_guide/prediction/prediction_manager_unittest.cc
@@ -7,10 +7,14 @@
 #include <memory>
 
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
+#include "chrome/browser/optimization_guide/optimization_guide_navigation_data.h"
+#include "chrome/browser/optimization_guide/optimization_guide_web_contents_observer.h"
 #include "chrome/browser/optimization_guide/prediction/prediction_model.h"
 #include "chrome/browser/optimization_guide/prediction/prediction_model_fetcher.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/optimization_guide/optimization_guide_features.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "components/optimization_guide/top_host_provider.h"
 #include "content/public/test/mock_navigation_handle.h"
@@ -75,12 +79,15 @@
   ~TestPredictionModel() override = default;
 
   optimization_guide::OptimizationTargetDecision Predict(
-      const base::flat_map<std::string, float>& model_features) override {
+      const base::flat_map<std::string, float>& model_features,
+      double* prediction_score) override {
+    *prediction_score = 0.0;
     // Check to make sure the all model_features were provided.
     for (const auto& model_feature : GetModelFeatures()) {
       if (!model_features.contains(model_feature))
         return OptimizationTargetDecision::kUnknown;
     }
+    *prediction_score = 0.6;
     model_evaluated_ = true;
     return OptimizationTargetDecision::kPageLoadMatches;
   }
@@ -304,7 +311,8 @@
 }
 
 TEST_F(PredictionManagerTest, OptimizationTargetNotRegisteredForNavigation) {
-  content::MockNavigationHandle navigation_handle;
+  OptimizationGuideWebContentsObserver::CreateForWebContents(web_contents());
+  content::MockNavigationHandle navigation_handle(web_contents());
   navigation_handle.set_url(GURL("https://foo.com"));
 
   prediction_manager()->SetPredictionModelFetcherForTesting(
@@ -319,11 +327,24 @@
   EXPECT_EQ(OptimizationTargetDecision::kUnknown,
             prediction_manager()->ShouldTargetNavigation(
                 &navigation_handle, proto::OPTIMIZATION_TARGET_UNKNOWN));
+  // OptimizationGuideNavData should not be populated.
+  OptimizationGuideNavigationData* nav_data =
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          &navigation_handle);
+  EXPECT_FALSE(nav_data
+                   ->GetModelVersionForOptimizationTarget(
+                       optimization_guide::proto::OPTIMIZATION_TARGET_UNKNOWN)
+                   .has_value());
+  EXPECT_FALSE(nav_data
+                   ->GetModelPredictionScoreForOptimizationTarget(
+                       optimization_guide::proto::OPTIMIZATION_TARGET_UNKNOWN)
+                   .has_value());
 }
 
 TEST_F(PredictionManagerTest,
        NoPredictionModelForRegisteredOptimizationTarget) {
-  content::MockNavigationHandle navigation_handle;
+  OptimizationGuideWebContentsObserver::CreateForWebContents(web_contents());
+  content::MockNavigationHandle navigation_handle(web_contents());
   navigation_handle.set_url(GURL("https://foo.com"));
 
   prediction_manager()->RegisterOptimizationTargets(
@@ -332,10 +353,25 @@
       OptimizationTargetDecision::kModelNotAvailableOnClient,
       prediction_manager()->ShouldTargetNavigation(
           &navigation_handle, proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD));
+
+  // OptimizationGuideNavData should not be populated.
+  OptimizationGuideNavigationData* nav_data =
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          &navigation_handle);
+  EXPECT_FALSE(
+      nav_data
+          ->GetModelVersionForOptimizationTarget(
+              optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)
+          .has_value());
+  EXPECT_FALSE(
+      nav_data
+          ->GetModelPredictionScoreForOptimizationTarget(
+              optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)
+          .has_value());
 }
 
 TEST_F(PredictionManagerTest, EvaluatePredictionModel) {
-  content::MockNavigationHandle navigation_handle;
+  content::MockNavigationHandle navigation_handle(web_contents());
   navigation_handle.set_url(GURL("https://foo.com"));
 
   prediction_manager()->SetPredictionModelFetcherForTesting(
@@ -362,8 +398,6 @@
 }
 
 TEST_F(PredictionManagerTest, UpdateModelWithSameVersion) {
-  content::MockNavigationHandle navigation_handle;
-  navigation_handle.set_url(GURL("https://foo.com"));
   prediction_manager()->SetPredictionModelFetcherForTesting(
       BuildTestPredictionModelFetcher(
           PredictionModelFetcherEndState::kFetchFailed));
@@ -395,8 +429,9 @@
   EXPECT_EQ(3, stored_prediction_model->GetVersion());
 }
 
-TEST_F(PredictionManagerTest, UpdateModelForUnregisteredTarget) {
-  content::MockNavigationHandle navigation_handle;
+TEST_F(PredictionManagerTest, EvaluatePredictionModelPopulatesNavData) {
+  OptimizationGuideWebContentsObserver::CreateForWebContents(web_contents());
+  content::MockNavigationHandle navigation_handle(web_contents());
   navigation_handle.set_url(GURL("https://foo.com"));
 
   prediction_manager()->SetPredictionModelFetcherForTesting(
@@ -404,6 +439,82 @@
           PredictionModelFetcherEndState::
               kFetchSuccessWithModelsAndHostsModelFeatures));
 
+  prediction_manager()->RegisterOptimizationTargets(
+      {proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD});
+
+  EXPECT_TRUE(prediction_model_fetcher()->models_fetched());
+
+  EXPECT_EQ(
+      OptimizationTargetDecision::kPageLoadMatches,
+      prediction_manager()->ShouldTargetNavigation(
+          &navigation_handle, proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD));
+
+  TestPredictionModel* test_prediction_model =
+      static_cast<TestPredictionModel*>(
+          prediction_manager()->GetPredictionModelForTesting(
+              proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD));
+  EXPECT_TRUE(test_prediction_model);
+  EXPECT_TRUE(test_prediction_model->WasModelEvaluated());
+
+  OptimizationGuideNavigationData* nav_data =
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          &navigation_handle);
+  EXPECT_EQ(2, *nav_data->GetModelVersionForOptimizationTarget(
+                   proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD));
+  EXPECT_EQ(0.6, *nav_data->GetModelPredictionScoreForOptimizationTarget(
+                     proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD));
+}
+
+TEST_F(PredictionManagerTest,
+       EvaluatePredictionModelPopulatesNavDataEvenWithHoldback) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeaturesAndParameters(
+      {base::test::ScopedFeatureList::FeatureAndParams(
+          features::kOptimizationTargetPrediction,
+          {{"painful_page_load_metrics_only", "true"}})},
+      {});
+
+  OptimizationGuideWebContentsObserver::CreateForWebContents(web_contents());
+  content::MockNavigationHandle navigation_handle(web_contents());
+  navigation_handle.set_url(GURL("https://foo.com"));
+
+  prediction_manager()->SetPredictionModelFetcherForTesting(
+      BuildTestPredictionModelFetcher(
+          PredictionModelFetcherEndState::
+              kFetchSuccessWithModelsAndHostsModelFeatures));
+
+  prediction_manager()->RegisterOptimizationTargets(
+      {proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD});
+
+  EXPECT_TRUE(prediction_model_fetcher()->models_fetched());
+
+  EXPECT_EQ(
+      OptimizationTargetDecision::kModelPredictionHoldback,
+      prediction_manager()->ShouldTargetNavigation(
+          &navigation_handle, proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD));
+
+  TestPredictionModel* test_prediction_model =
+      static_cast<TestPredictionModel*>(
+          prediction_manager()->GetPredictionModelForTesting(
+              proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD));
+  EXPECT_TRUE(test_prediction_model);
+  EXPECT_TRUE(test_prediction_model->WasModelEvaluated());
+
+  OptimizationGuideNavigationData* nav_data =
+      OptimizationGuideNavigationData::GetFromNavigationHandle(
+          &navigation_handle);
+  EXPECT_EQ(2, *nav_data->GetModelVersionForOptimizationTarget(
+                   proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD));
+  EXPECT_EQ(0.6, *nav_data->GetModelPredictionScoreForOptimizationTarget(
+                     proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD));
+}
+
+TEST_F(PredictionManagerTest, UpdateModelForUnregisteredTarget) {
+  prediction_manager()->SetPredictionModelFetcherForTesting(
+      BuildTestPredictionModelFetcher(
+          PredictionModelFetcherEndState::
+              kFetchSuccessWithModelsAndHostsModelFeatures));
+
   prediction_manager()->RegisterOptimizationTargets({});
 
   EXPECT_FALSE(prediction_model_fetcher()->models_fetched());
@@ -422,7 +533,7 @@
 }
 
 TEST_F(PredictionManagerTest, UpdateModelWithUnsupportedOptimizationTarget) {
-  content::MockNavigationHandle navigation_handle;
+  content::MockNavigationHandle navigation_handle(web_contents());
   navigation_handle.set_url(GURL("https://foo.com"));
 
   prediction_manager()->SetPredictionModelFetcherForTesting(
@@ -456,8 +567,6 @@
 
 TEST_F(PredictionManagerTest, HasHostModelFeaturesForHost) {
   base::HistogramTester histogram_tester;
-  content::MockNavigationHandle navigation_handle;
-  navigation_handle.set_url(GURL("https://example1.com"));
 
   prediction_manager()->SetPredictionModelFetcherForTesting(
       BuildTestPredictionModelFetcher(
@@ -488,7 +597,7 @@
 
 TEST_F(PredictionManagerTest, NoHostModelFeaturesForHost) {
   base::HistogramTester histogram_tester;
-  content::MockNavigationHandle navigation_handle;
+  content::MockNavigationHandle navigation_handle(web_contents());
   navigation_handle.set_url(GURL("https://foo.com"));
 
   prediction_manager()->SetPredictionModelFetcherForTesting(
@@ -529,8 +638,6 @@
 
 TEST_F(PredictionManagerTest, UpdateHostModelFeaturesMissingHost) {
   base::HistogramTester histogram_tester;
-  content::MockNavigationHandle navigation_handle;
-  navigation_handle.set_url(GURL("https://foo.com"));
 
   prediction_manager()->SetPredictionModelFetcherForTesting(
       BuildTestPredictionModelFetcher(
@@ -553,8 +660,6 @@
 
 TEST_F(PredictionManagerTest, UpdateHostModelFeaturesNoFeature) {
   base::HistogramTester histogram_tester;
-  content::MockNavigationHandle navigation_handle;
-  navigation_handle.set_url(GURL("https://foo.com"));
 
   prediction_manager()->SetPredictionModelFetcherForTesting(
       BuildTestPredictionModelFetcher(
@@ -577,8 +682,6 @@
 
 TEST_F(PredictionManagerTest, UpdateHostModelFeaturesNoFeatureName) {
   base::HistogramTester histogram_tester;
-  content::MockNavigationHandle navigation_handle;
-  navigation_handle.set_url(GURL("https://foo.com"));
 
   prediction_manager()->SetPredictionModelFetcherForTesting(
       BuildTestPredictionModelFetcher(
@@ -603,8 +706,6 @@
 
 TEST_F(PredictionManagerTest, UpdateHostModelFeaturesDoubleValue) {
   base::HistogramTester histogram_tester;
-  content::MockNavigationHandle navigation_handle;
-  navigation_handle.set_url(GURL("https://foo.com"));
 
   prediction_manager()->SetPredictionModelFetcherForTesting(
       BuildTestPredictionModelFetcher(
@@ -632,8 +733,6 @@
 
 TEST_F(PredictionManagerTest, UpdateHostModelFeaturesIntValue) {
   base::HistogramTester histogram_tester;
-  content::MockNavigationHandle navigation_handle;
-  navigation_handle.set_url(GURL("https://foo.com"));
 
   prediction_manager()->SetPredictionModelFetcherForTesting(
       BuildTestPredictionModelFetcher(
@@ -663,8 +762,6 @@
 
 TEST_F(PredictionManagerTest, UpdateHostModelFeaturesUpdateDataInMap) {
   base::HistogramTester histogram_tester;
-  content::MockNavigationHandle navigation_handle;
-  navigation_handle.set_url(GURL("https://foo.com"));
 
   prediction_manager()->SetPredictionModelFetcherForTesting(
       BuildTestPredictionModelFetcher(
diff --git a/chrome/browser/optimization_guide/prediction/prediction_model.h b/chrome/browser/optimization_guide/prediction/prediction_model.h
index 24931f28..ada65f05 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_model.h
+++ b/chrome/browser/optimization_guide/prediction/prediction_model.h
@@ -33,9 +33,11 @@
       const base::flat_set<std::string>& host_model_features);
 
   // Returns the OptimizationTargetDecision by evaluating the |model_|
-  // using the provided |model_features|.
+  // using the provided |model_features|. |prediction_score| will be populated
+  // with the score output by the model.
   virtual optimization_guide::OptimizationTargetDecision Predict(
-      const base::flat_map<std::string, float>& model_features) = 0;
+      const base::flat_map<std::string, float>& model_features,
+      double* prediction_score) = 0;
 
   // Provide the version of the |model_| by |this|.
   int64_t GetVersion() const;
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 48604bb..737d430e 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/password_manager/account_storage/account_password_store_factory.h"
+#include "chrome/browser/password_manager/field_info_manager_factory.h"
 #include "chrome/browser/password_manager/password_store_factory.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/profiles/profile.h"
@@ -46,6 +47,7 @@
 #include "components/password_manager/content/browser/password_manager_log_router_factory.h"
 #include "components/password_manager/content/browser/password_requirements_service_factory.h"
 #include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
+#include "components/password_manager/core/browser/field_info_manager.h"
 #include "components/password_manager/core/browser/hsts_query.h"
 #include "components/password_manager/core/browser/http_auth_manager.h"
 #include "components/password_manager/core/browser/http_auth_manager_impl.h"
@@ -129,6 +131,7 @@
 using autofill::mojom::FocusedFieldType;
 using password_manager::BadMessageReason;
 using password_manager::ContentPasswordManagerDriverFactory;
+using password_manager::FieldInfoManager;
 using password_manager::PasswordManagerClientHelper;
 using password_manager::PasswordManagerDriver;
 using password_manager::PasswordManagerMetricsRecorder;
@@ -1104,6 +1107,10 @@
   return GetMainFrameURL() == chrome::kChromeSearchLocalNtpUrl;
 }
 
+FieldInfoManager* ChromePasswordManagerClient::GetFieldInfoManager() const {
+  return FieldInfoManagerFactory::GetForBrowserContext(profile_);
+}
+
 // static
 void ChromePasswordManagerClient::BindCredentialManager(
     mojo::PendingReceiver<blink::mojom::CredentialManager> receiver,
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.h b/chrome/browser/password_manager/chrome_password_manager_client.h
index 44f7baf..447dd78 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.h
+++ b/chrome/browser/password_manager/chrome_password_manager_client.h
@@ -153,6 +153,7 @@
   void NavigateToManagePasswordsPage(
       password_manager::ManagePasswordsReferrer referrer) override;
   bool IsNewTabPage() const override;
+  password_manager::FieldInfoManager* GetFieldInfoManager() const override;
 
   // autofill::mojom::PasswordGenerationDriver overrides.
   void AutomaticGenerationAvailable(
diff --git a/chrome/browser/policy/browser_dm_token_storage.cc b/chrome/browser/policy/browser_dm_token_storage.cc
index a6409781..e89d8d7 100644
--- a/chrome/browser/policy/browser_dm_token_storage.cc
+++ b/chrome/browser/policy/browser_dm_token_storage.cc
@@ -59,6 +59,17 @@
   return BrowserDMToken(Status::kEmpty, "");
 }
 
+BrowserDMToken::BrowserDMToken(const BrowserDMToken& other) = default;
+
+BrowserDMToken::BrowserDMToken(BrowserDMToken&& other) = default;
+
+BrowserDMToken& BrowserDMToken::operator=(const BrowserDMToken& other) =
+    default;
+
+BrowserDMToken& BrowserDMToken::operator=(BrowserDMToken&& other) = default;
+
+BrowserDMToken::~BrowserDMToken() = default;
+
 const std::string& BrowserDMToken::value() const {
   // TODO(domfc): Uncomment DCHECK(is_valid()) after migrating code.
   // DCHECK(is_valid());
diff --git a/chrome/browser/policy/browser_dm_token_storage.h b/chrome/browser/policy/browser_dm_token_storage.h
index 28a4671..cf456cc 100644
--- a/chrome/browser/policy/browser_dm_token_storage.h
+++ b/chrome/browser/policy/browser_dm_token_storage.h
@@ -44,16 +44,23 @@
   //    enrollment token is present.
   class BrowserDMToken {
    public:
+    static BrowserDMToken CreateValidToken(const std::string& value);
+    static BrowserDMToken CreateInvalidToken();
+    static BrowserDMToken CreateEmptyToken();
+
+    BrowserDMToken(const BrowserDMToken& other);
+    BrowserDMToken(BrowserDMToken&& other);
+
+    BrowserDMToken& operator=(const BrowserDMToken& other);
+    BrowserDMToken& operator=(BrowserDMToken&& other);
+    ~BrowserDMToken();
+
     const std::string& value() const;
 
     bool is_valid() const;
     bool is_invalid() const;
     bool is_empty() const;
 
-    static BrowserDMToken CreateValidToken(const std::string& value);
-    static BrowserDMToken CreateInvalidToken();
-    static BrowserDMToken CreateEmptyToken();
-
    private:
     enum class Status { kValid, kInvalid, kEmpty };
 
diff --git a/chrome/browser/previews/hints_fetcher_browsertest.cc b/chrome/browser/previews/hints_fetcher_browsertest.cc
index 362ad74..ccc891b3 100644
--- a/chrome/browser/previews/hints_fetcher_browsertest.cc
+++ b/chrome/browser/previews/hints_fetcher_browsertest.cc
@@ -1060,7 +1060,7 @@
             net::EFFECTIVE_CONNECTION_TYPE_2G);
 
     // Navigate to a host not in the seeded site engagement service; it
-    // should be recorded as not covered by the hints fetcher.
+    // should be recorded as covered by the hints fetcher due to the race.
     base::flat_set<std::string> expected_hosts_2g;
     std::string host_2g("https://unseenhost_2g.com/");
     expected_hosts_2g.insert(GURL(host_2g).host());
@@ -1079,10 +1079,21 @@
     RetryForHistogramUntilCountReached(
         histogram_tester, optimization_guide::kLoadedHintLocalHistogramString,
         IsOptimizationGuideKeyedServiceEnabled() ? 2 : 1);
+    if (IsOptimizationGuideKeyedServiceEnabled()) {
+      RetryForHistogramUntilCountReached(
+          histogram_tester,
+          "OptimizationGuide.HintsFetcher.NavigationHostCoveredByFetch."
+          "AtCommit",
+          1);
+      histogram_tester->ExpectUniqueSample(
+          "OptimizationGuide.HintsFetcher.NavigationHostCoveredByFetch."
+          "AtCommit",
+          true, 1);
+    }
   }
 
   // Change ECT to a high value. Hints should not be fetched at the time of
-  // navigation.
+  // navigation as the ECT fast so the fetcher should not race.
   {
     g_browser_process->network_quality_tracker()
         ->ReportEffectiveConnectionTypeForTesting(
@@ -1106,6 +1117,21 @@
     RetryForHistogramUntilCountReached(
         histogram_tester, optimization_guide::kLoadedHintLocalHistogramString,
         IsOptimizationGuideKeyedServiceEnabled() ? 2 : 1);
+    if (IsOptimizationGuideKeyedServiceEnabled()) {
+      RetryForHistogramUntilCountReached(
+          histogram_tester,
+          "OptimizationGuide.HintsFetcher.NavigationHostCoveredByFetch."
+          "AtCommit",
+          2);
+      histogram_tester->ExpectBucketCount(
+          "OptimizationGuide.HintsFetcher.NavigationHostCoveredByFetch."
+          "AtCommit",
+          true, 1);
+      histogram_tester->ExpectBucketCount(
+          "OptimizationGuide.HintsFetcher.NavigationHostCoveredByFetch."
+          "AtCommit",
+          false, 1);
+    }
   }
 
   // Change ECT back to a low value. Hints should be fetched at the time of
@@ -1135,6 +1161,22 @@
     RetryForHistogramUntilCountReached(
         histogram_tester, optimization_guide::kLoadedHintLocalHistogramString,
         IsOptimizationGuideKeyedServiceEnabled() ? 3 : 1);
+
+    if (IsOptimizationGuideKeyedServiceEnabled()) {
+      RetryForHistogramUntilCountReached(
+          histogram_tester,
+          "OptimizationGuide.HintsFetcher.NavigationHostCoveredByFetch."
+          "AtCommit",
+          3);
+      histogram_tester->ExpectBucketCount(
+          "OptimizationGuide.HintsFetcher.NavigationHostCoveredByFetch."
+          "AtCommit",
+          true, 2);
+      histogram_tester->ExpectBucketCount(
+          "OptimizationGuide.HintsFetcher.NavigationHostCoveredByFetch."
+          "AtCommit",
+          false, 1);
+    }
   }
 
   // Navigate again to a webpage with the
@@ -1173,6 +1215,22 @@
     RetryForHistogramUntilCountReached(
         histogram_tester, optimization_guide::kLoadedHintLocalHistogramString,
         IsOptimizationGuideKeyedServiceEnabled() ? 4 : 1);
+
+    if (IsOptimizationGuideKeyedServiceEnabled()) {
+      RetryForHistogramUntilCountReached(
+          histogram_tester,
+          "OptimizationGuide.HintsFetcher.NavigationHostCoveredByFetch."
+          "AtCommit",
+          4);
+      histogram_tester->ExpectBucketCount(
+          "OptimizationGuide.HintsFetcher.NavigationHostCoveredByFetch."
+          "AtCommit",
+          true, 3);
+      histogram_tester->ExpectBucketCount(
+          "OptimizationGuide.HintsFetcher.NavigationHostCoveredByFetch."
+          "AtCommit",
+          false, 1);
+    }
   }
 }
 
@@ -1425,4 +1483,10 @@
   RetryForHistogramUntilCountReached(
       histogram_tester, optimization_guide::kLoadedHintLocalHistogramString,
       IsOptimizationGuideKeyedServiceEnabled() ? 2 : 1);
+  // Only the SRP is navigated to and it should not be covered at navigation
+  // finish.
+  histogram_tester->ExpectUniqueSample(
+      "OptimizationGuide.HintsFetcher.NavigationHostCoveredByFetch."
+      "AtCommit",
+      false, 1);
 }
diff --git a/chrome/browser/profiles/profile.h b/chrome/browser/profiles/profile.h
index 756fa14..ff64a45 100644
--- a/chrome/browser/profiles/profile.h
+++ b/chrome/browser/profiles/profile.h
@@ -453,9 +453,9 @@
   static PrefStore* CreateExtensionPrefStore(Profile*,
                                              bool incognito_pref_store);
 
- private:
   void NotifyOffTheRecordProfileCreated(Profile* off_the_record);
 
+ private:
   bool restored_last_session_;
 
   // Used to prevent the notification that this Profile is destroyed from
diff --git a/chrome/browser/reputation/local_heuristics.cc b/chrome/browser/reputation/local_heuristics.cc
index 1c85d72..771154e1 100644
--- a/chrome/browser/reputation/local_heuristics.cc
+++ b/chrome/browser/reputation/local_heuristics.cc
@@ -19,12 +19,12 @@
 using MatchType = LookalikeUrlInterstitialPage::MatchType;
 
 const base::FeatureParam<bool> kEnableLookalikeTopSites{
-    &security_state::features::kSafetyTipUI, "topsites", false};
+    &security_state::features::kSafetyTipUI, "topsites", true};
 const base::FeatureParam<bool> kEnableLookalikeEditDistance{
-    &security_state::features::kSafetyTipUI, "editdistance", false};
+    &security_state::features::kSafetyTipUI, "editdistance", true};
 const base::FeatureParam<bool> kEnableLookalikeEditDistanceSiteEngagement{
     &security_state::features::kSafetyTipUI, "editdistance_siteengagement",
-    false};
+    true};
 
 }  // namespace
 
diff --git a/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.css b/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.css
index 8a3a998..84fd6bd 100644
--- a/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.css
+++ b/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.css
@@ -10,9 +10,3 @@
 setup-pin-keyboard {
   margin-top: 17px;
 }
-
-#done-illustration {
-  height: 264px;
-  margin-top: 38px; /* 64px from subtitle baseline. */
-  width: 264px;
-}
diff --git a/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.html b/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.html
index cb1394c..a6e4140 100644
--- a/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.html
+++ b/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.html
@@ -109,9 +109,10 @@
               class="focus-on-show"
               is-incognito-ui>
           </setup-pin-keyboard>
-          <img id="done-illustration" hidden="[[isStepHidden_(step_, 'done')]]"
-              srcset="images/pin_illustration_1x.svg 1x,
-                      images/pin_illustration_2x.svg 2x">
+        <img id="done-illustration" hidden="[[isStepHidden_(step_, 'done')]]"
+            class="oobe-illustration"
+            srcset="images/pin_illustration_1x.svg 1x,
+                    images/pin_illustration_2x.svg 2x">
       </div>
       <div slot="bottom-buttons" class="flex layout horizontal end-justified">
         <oobe-text-button id="setupSkipButton" on-tap="onSkipButton_"
diff --git a/chrome/browser/resources/chromeos/login/marketing_opt_in.html b/chrome/browser/resources/chromeos/login/marketing_opt_in.html
index 0d96b34..e8af493 100644
--- a/chrome/browser/resources/chromeos/login/marketing_opt_in.html
+++ b/chrome/browser/resources/chromeos/login/marketing_opt_in.html
@@ -11,9 +11,8 @@
     <link rel="stylesheet" href="oobe_flex_layout.css">
     <oobe-dialog id="marketingOptInOverviewDialog" role="dialog" has-buttons
         aria-label="[[i18nDynamic(locale, 'marketingOptInScreenTitle')]]">
-      <!-- TODO(https://crbug.com/852557): replace placeholder with real icon. -->
-      <hd-iron-icon slot="oobe-icon" icon1x="oobe-32:googleg"
-          icon2x="oobe-32:googleg">
+      <hd-iron-icon slot="oobe-icon" icon1x="oobe-32:checkmark"
+          icon2x="oobe-32:checkmark">
       </hd-iron-icon>
       <h1 slot="title">
         [[i18nDynamic(locale, 'marketingOptInScreenTitle')]]
@@ -23,8 +22,7 @@
       </div>
       <div slot="footer" class="layout vertical">
         <div class="marketing-option layout horizontal center">
-          <!-- TODO(https://crbug.com/852557): replace placeholder with real icon. -->
-          <hd-iron-icon icon1x="oobe-32:googleg" icon2x="oobe-64:googleg">
+          <hd-iron-icon icon1x="oobe-32:checkmark" icon2x="oobe-64:checkmark">
           </hd-iron-icon>
           <div id="playUpdatesOptionLabel" class="flex">
             [[i18nDynamic(locale, 'marketingOptInGetPlayUpdates')]]
@@ -34,8 +32,7 @@
           </cr-toggle>
         </div>
         <div class="marketing-option layout horizontal center">
-          <!-- TODO(https://crbug.com/852557): replace placeholder with real icon. -->
-          <hd-iron-icon icon1x="oobe-32:googleg" icon2x="oobe-64:googleg">
+          <hd-iron-icon icon1x="oobe-32:checkmark" icon2x="oobe-64:checkmark">
           </hd-iron-icon>
           <div id="chromebookUpdatesOptionLabel" class="flex">
             [[i18nRecursive(locale, 'marketingOptInGetChromebookUpdates',
diff --git a/chrome/browser/resources/chromeos/login/oobe_dialog_host.css b/chrome/browser/resources/chromeos/login/oobe_dialog_host.css
index 6ce229b27..19c1865 100644
--- a/chrome/browser/resources/chromeos/login/oobe_dialog_host.css
+++ b/chrome/browser/resources/chromeos/login/oobe_dialog_host.css
@@ -22,3 +22,9 @@
   border-radius: 4px;
   box-shadow: 0 24px 24px rgba(0, 0, 0, .24), 0 0 24px rgba(0, 0, 0, .12);
 }
+
+.oobe-illustration {
+  max-height: -webkit-fill-available;
+  max-width: -webkit-fill-available;
+  object-fit: contain;
+}
diff --git a/chrome/browser/resources/chromeos/login/oobe_flex_layout.css b/chrome/browser/resources/chromeos/login/oobe_flex_layout.css
index e2bc08d..26195752 100644
--- a/chrome/browser/resources/chromeos/login/oobe_flex_layout.css
+++ b/chrome/browser/resources/chromeos/login/oobe_flex_layout.css
@@ -116,9 +116,3 @@
   right: 0;
   top: 0;
 }
-
-.oobe-illustration {
-  max-height: -webkit-fill-available;
-  max-width: -webkit-fill-available;
-  object-fit: contain;
-}
diff --git a/chrome/browser/resources/chromeos/login/oobe_icons.html b/chrome/browser/resources/chromeos/login/oobe_icons.html
index 8853479..0c7b02f 100644
--- a/chrome/browser/resources/chromeos/login/oobe_icons.html
+++ b/chrome/browser/resources/chromeos/login/oobe_icons.html
@@ -53,6 +53,10 @@
         <path fill="#1A73E8" fill-rule="nonzero"d="M29 20V0H3v20H0v3h32v-3h-3zm-10.333 0h-5.334v-1.333h5.334V20zM26 16H6V3h20v13z"></path>
         <path fill="#1A73E8" d="M16.97 8.129L16 5l-.97 3.129H12l2.47 1.836L13.53 13 16 11.123 18.47 13l-.94-3.035L20 8.129z"></path>
       </g>
+      <g id="checkmark" fill="none" fill-rule="evenodd">
+        <path fill="#1A73E8" fill-rule="nonzero" d="M11.467 22.133l-5.6-5.6L4 18.4l7.467 7.467 16-16L25.6 8z"></path>
+        <path d="M0 0h32v32H0z"></path>
+      </g>
     </defs>
   </svg>
 </iron-iconset-svg>
@@ -105,6 +109,10 @@
         <path fill="#1A73E8" fill-rule="nonzero" d="M58 48V8H6v40H0v6h64v-6h-6zm-20.667 0H26.667v-2.667h10.666V48zM52 40H12V14h40v26z"></path>
         <path fill="#1A73E8" d="M33.94 24.258L32 18l-1.94 6.258H24l4.94 3.672L27.06 34 32 30.245 36.94 34l-1.88-6.07L40 24.258z"></path>
       </g>
+      <g  id="checkmark" fill="none" fill-rule="evenodd">
+        <path fill="#1A73E8" fill-rule="nonzero" d="M22.933 44.267l-11.2-11.2L8 36.8l14.933 14.933 32-32L51.2 16z"></path>
+        <path d="M0 0h64v64H0z"></path>
+      </g>
     </defs>
   </svg>
 </iron-iconset-svg>
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.css b/chrome/browser/resources/chromeos/login/screen_gaia_signin.css
index 71f2628..9d7bab8 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.css
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.css
@@ -78,15 +78,6 @@
   font-size: 13px;
 }
 
-:host(.whitelist-error) .step-contents,
-:host(.whitelist-error) .gaia-dialog {
-  visibility: hidden;
-}
-
-:host(.whitelist-error) .step-loading {
-  visibility: hidden;
-}
-
 .step-loading {
   align-items: center;
   bottom: 0;
@@ -109,14 +100,6 @@
   visibility: hidden;
 }
 
-:host(.whitelist-error) #gaia-whitelist-error {
-  visibility: visible;
-}
-
-:host(:not(.saml-interstitial)) #gaia-step-contents {
-  display: none;
-}
-
 :host(.saml) #signin-frame-container {
   padding-top: 44px;
 }
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.html b/chrome/browser/resources/chromeos/login/screen_gaia_signin.html
index a6dce40..27ec228 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.html
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.html
@@ -11,8 +11,7 @@
     <oobe-dialog id="signin-frame-dialog" class="gaia-dialog" role="dialog"
         has-buttons no-header no-footer-padding
         show-buttons-overlay="[[!navigationEnabled_]]"
-        hidden="[[!isSigninFrameDialogVisible_(screenMode_,
-                                               pinDialogParameters_)]]">
+        hidden="[[!eq_(step_, 'online-gaia')]]">
       <div slot="footer" class="flex layout vertical">
         <div id="signin-frame-container"
             class$="[[getSigninFrameContainerClass_(isLoadingUiShown_)]]">
@@ -40,31 +39,29 @@
       </div>
     </oobe-dialog>
     <offline-gaia id="offline-gaia" class="gaia-dialog"
-        hidden="[[!isOfflineGaiaVisible_(screenMode_, isLoadingUiShown_,
-                                         pinDialogParameters_)]]">
+        hidden="[[!eq_(step_, 'offline-gaia')]]">
     </offline-gaia>
     <offline-ad-login id="offline-ad-auth" class="gaia-dialog"
-        hidden="[[!isOfflineAdAuthVisible_(screenMode_, isLoadingUiShown_,
-                                           pinDialogParameters_)]]"
+        hidden="[[!eq_(step_, 'ad')]]"
         i18n-values="ad-welcome-message:loginWelcomeMessage">
     </offline-ad-login>
     <security-token-pin id="pinDialog" parameters="[[pinDialogParameters_]]"
-        hidden="[[!isPinDialogVisible_(pinDialogParameters_)]]"
+        hidden="[[!eq_(step_, 'pin')]]"
         on-cancel="onPinDialogCanceled_" on-completed="onPinDialogCompleted_">
     </security-token-pin>
-    <div id="gaia-step-contents" class="step-contents">
+    <div id="gaia-step-contents" class="step-contents"
+        hidden="[[!eq_(step_, 'saml-interstitial')]]">
       <div id="gaia-signin-form-container">
-        <saml-interstitial id="saml-interstitial" class="fit gaia-dialog"
-            hidden="[[!isSamlInterstitialVisible_(
-                screenMode_, isLoadingUiShown_, pinDialogParameters_)]]">
+        <saml-interstitial id="saml-interstitial" class="fit gaia-dialog">
         </saml-interstitial>
       </div>
     </div>
     <div id="gaia-loading" class="step-loading gaia-dialog"
-        hidden="[[!isLoadingUiShown_]]">
+        hidden="[[!eq_(step_, 'loading')]]">
       <throbber-notice i18n-values="text:gaiaLoading"></throbber-notice>
     </div>
     <notification-card id="gaia-whitelist-error" type="fail" class="gaia-dialog"
+        hidden="[[!eq_(step_, 'whitelist-error')]]"
         i18n-values="button-label:tryAgainButton;
                      link-label:learnMoreButton">
     </notification-card>
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
index 4ae3d091..8e7d7d8 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
@@ -42,16 +42,30 @@
 const BUBBLE_VERTICAL_PADDING = -213;
 
 /**
- * The modes this screen can be in.
+ * The authentication mode for the screen.
  * @enum {number}
  */
-const ScreenMode = {
+const AuthMode = {
   DEFAULT: 0,            // Default GAIA login flow.
   OFFLINE: 1,            // GAIA offline login.
   SAML_INTERSTITIAL: 2,  // Interstitial page before SAML redirection.
   AD_AUTH: 3             // Offline Active Directory login flow.
 };
 
+/**
+ * UI mode for the dialog.
+ * @enum {string}
+ */
+const DialogMode = {
+  GAIA: 'online-gaia',
+  OFFLINE_GAIA: 'offline-gaia',
+  OFFLINE_AD: 'ad',
+  LOADING: 'loading',
+  PIN_DIALOG: 'pin',
+  GAIA_WHITELIST_ERROR: 'whitelist-error',
+  SAML_INTERSTITIAL: 'saml-interstitial',
+};
+
 Polymer({
   is: 'gaia-signin',
 
@@ -74,11 +88,20 @@
      */
     screenMode_: {
       type: Number,
-      value: ScreenMode.DEFAULT,
+      value: AuthMode.DEFAULT,
       observer: 'screenModeChanged_',
     },
 
     /**
+     * Current step displayed.
+     * @private
+     */
+    step_: {
+      type: String,
+      value: DialogMode.GAIA,
+    },
+
+    /**
      * Whether the loading UI is shown.
      * @private
      */
@@ -88,6 +111,15 @@
     },
 
     /**
+     * Whether the loading whitelist error UI is shown.
+     * @private
+     */
+    isWhitelistErrorShown_: {
+      type: Boolean,
+      value: false,
+    },
+
+    /**
      * Whether the navigation controls are enabled.
      * @private
      */
@@ -155,6 +187,11 @@
     },
   },
 
+  observers: [
+    'refreshDialogStep_(screenMode_, pinDialogParameters_, isLoadingUiShown_,' +
+    'isWhitelistErrorShown_)'
+  ],
+
   /**
    * Saved authenticator load params.
    * @type {?string}
@@ -270,14 +307,14 @@
             return function(e) {
               let currentFrame = null;
               switch (that.screenMode_) {
-                case ScreenMode.DEFAULT:
-                case ScreenMode.SAML_INTERSTITIAL:
+                case AuthMode.DEFAULT:
+                case AuthMode.SAML_INTERSTITIAL:
                   currentFrame = that.authenticator_;
                   break;
-                case ScreenMode.OFFLINE:
+                case AuthMode.OFFLINE:
                   currentFrame = $that['offline-gaia'];
                   break;
-                case ScreenMode.AD_AUTH:
+                case AuthMode.AD_AUTH:
                   currentFrame = $that['offline-ad-auth'];
                   break;
               }
@@ -351,7 +388,7 @@
     // Register handlers for the saml interstitial page events.
     this.$['saml-interstitial'].addEventListener(
         'samlPageNextClicked', function() {
-          this.screenMode_ = ScreenMode.DEFAULT;
+          this.screenMode_ = AuthMode.DEFAULT;
           this.loadAuthenticator_(true /* doSamlRedirect */);
         }.bind(this));
     this.$['saml-interstitial'].addEventListener(
@@ -359,7 +396,7 @@
           // The user requests to change the account. We must clear the email
           // field of the auth params.
           this.authenticatorParams_.email = '';
-          this.screenMode_ = ScreenMode.DEFAULT;
+          this.screenMode_ = AuthMode.DEFAULT;
           this.loadAuthenticator_(false /* doSamlRedirect */);
         }.bind(this));
 
@@ -391,7 +428,7 @@
    */
   isAtTheBeginning_: function() {
     return !this.canGoBack_() && !this.isSaml_ &&
-        !this.classList.contains('whitelist-error') && !this.authCompleted_;
+        !this.isWhitelistErrorShown_ && !this.authCompleted_;
   },
 
   /**
@@ -427,8 +464,7 @@
    * @private
    */
   canGoBack_: function() {
-    const isWhitelistError = this.classList.contains('whitelist-error');
-    return this.lastBackMessageValue_ && !isWhitelistError &&
+    return this.lastBackMessageValue_ && !this.isWhitelistErrorShown_ &&
         !this.authCompleted_ && !this.isLoadingUiShown_ && !this.isSaml_;
   },
 
@@ -466,7 +502,7 @@
    * @private
    */
   isOffline_: function() {
-    return this.screenMode_ == ScreenMode.OFFLINE;
+    return this.screenMode_ == AuthMode.OFFLINE;
   },
 
   /**
@@ -496,7 +532,7 @@
   isSigninFrameDialogVisible_: function(screenMode, pinDialogParameters) {
     // See the comment in getSigninFrameContainerClass_() for the explanation on
     // why our element shouldn't be hidden during loading.
-    return screenMode == ScreenMode.DEFAULT && pinDialogParameters === null;
+    return screenMode == AuthMode.DEFAULT && pinDialogParameters === null;
   },
 
   /**
@@ -523,7 +559,7 @@
    */
   isOfflineGaiaVisible_: function(
       screenMode, isLoadingUiShown, pinDialogParameters) {
-    return screenMode == ScreenMode.OFFLINE && !isLoadingUiShown &&
+    return screenMode == AuthMode.OFFLINE && !isLoadingUiShown &&
         pinDialogParameters === null;
   },
 
@@ -537,7 +573,7 @@
    */
   isSamlInterstitialVisible_: function(
       screenMode, isLoadingUiShown, pinDialogParameters) {
-    return screenMode == ScreenMode.SAML_INTERSTITIAL && !isLoadingUiShown &&
+    return screenMode == AuthMode.SAML_INTERSTITIAL && !isLoadingUiShown &&
         pinDialogParameters === null;
   },
 
@@ -551,7 +587,7 @@
    */
   isOfflineAdAuthVisible_: function(
       screenMode, isLoadingUiShown, pinDialogParameters) {
-    return screenMode == ScreenMode.AD_AUTH && !isLoadingUiShown &&
+    return screenMode == AuthMode.AD_AUTH && !isLoadingUiShown &&
         pinDialogParameters === null;
   },
 
@@ -720,7 +756,7 @@
         behavior.onBeforeShow.call(this);
     });
 
-    this.screenMode_ = ScreenMode.DEFAULT;
+    this.screenMode_ = AuthMode.DEFAULT;
     this.isLoadingUiShown_ = true;
     chrome.send('loginUIStateChanged', ['gaia-signin', true]);
     Oobe.getInstance().setSigninUIState(SIGNIN_UI_STATE.GAIA_SIGNIN);
@@ -756,13 +792,13 @@
   /** @private */
   getActiveFrame_: function() {
     switch (this.screenMode_) {
-      case ScreenMode.DEFAULT:
+      case AuthMode.DEFAULT:
         return this.getSigninFrame_();
-      case ScreenMode.OFFLINE:
+      case AuthMode.OFFLINE:
         return this.$['offline-gaia'];
-      case ScreenMode.AD_AUTH:
+      case AuthMode.AD_AUTH:
         return this.$['offline-ad-auth'];
-      case ScreenMode.SAML_INTERSTITIAL:
+      case AuthMode.SAML_INTERSTITIAL:
         return this.$['saml-interstitial'];
     }
   },
@@ -794,8 +830,8 @@
   loadAuthExtension: function(data) {
     // Redirect the webview to the blank page in order to stop the SAML IdP
     // page from working in a background (see crbug.com/613245).
-    if (this.screenMode_ == ScreenMode.DEFAULT &&
-        data.screenMode != ScreenMode.DEFAULT) {
+    if (this.screenMode_ == AuthMode.DEFAULT &&
+        data.screenMode != AuthMode.DEFAULT) {
       this.authenticator_.resetWebview();
     }
 
@@ -824,7 +860,7 @@
         params[name] = data[name];
     }
 
-    params.doSamlRedirect = (this.screenMode_ == ScreenMode.SAML_INTERSTITIAL);
+    params.doSamlRedirect = (this.screenMode_ == AuthMode.SAML_INTERSTITIAL);
     params.menuGuestMode = data.guestSignin;
     params.menuKeyboardOptions = false;
     params.menuEnterpriseEnrollment =
@@ -836,19 +872,19 @@
     this.authenticatorParams_ = params;
 
     switch (this.screenMode_) {
-      case ScreenMode.DEFAULT:
+      case AuthMode.DEFAULT:
         this.loadAuthenticator_(false /* doSamlRedirect */);
         break;
 
-      case ScreenMode.OFFLINE:
+      case AuthMode.OFFLINE:
         this.loadOffline_(params);
         break;
 
-      case ScreenMode.AD_AUTH:
+      case AuthMode.AD_AUTH:
         this.loadAdAuth_(params);
         break;
 
-      case ScreenMode.SAML_INTERSTITIAL:
+      case AuthMode.SAML_INTERSTITIAL:
         this.$['saml-interstitial'].domain = data.enterpriseDisplayDomain;
         this.isLoadingUiShown_ = false;
         // This event is for the browser tests.
@@ -868,7 +904,7 @@
     const samlClass = 'saml-interstitial';
     const containedSamlClass = this.classList.contains(samlClass);
     this.classList.toggle(
-        samlClass, this.screenMode_ == ScreenMode.SAML_INTERSTITIAL);
+        samlClass, this.screenMode_ == AuthMode.SAML_INTERSTITIAL);
     if (Oobe.getInstance().currentScreen.id != 'gaia-signin')
       return;
     // Switching between signin-frame-dialog and gaia-step-contents
@@ -1232,7 +1268,7 @@
    * @private
    */
   onAuthCompleted_: function(credentials) {
-    if (this.screenMode_ == ScreenMode.AD_AUTH) {
+    if (this.screenMode_ == AuthMode.AD_AUTH) {
       this.email_ = credentials.username;
       chrome.send(
           'completeAdAuthentication',
@@ -1322,7 +1358,7 @@
    * Reloads extension frame.
    */
   doReload: function() {
-    if (this.screenMode_ != ScreenMode.DEFAULT)
+    if (this.screenMode_ != AuthMode.DEFAULT)
       return;
     this.authenticator_.reload();
     this.isLoadingUiShown_ = true;
@@ -1358,15 +1394,14 @@
   cancel: function() {
     this.clearVideoTimer_();
 
-    const isWhitelistError = this.classList.contains('whitelist-error');
     // TODO(crbug.com/470893): Figure out whether/which of these exit conditions
     // are useful.
-    if (this.screenMode_ == ScreenMode.SAML_INTERSTITIAL || isWhitelistError ||
-        this.authCompleted_) {
+    if (this.screenMode_ == AuthMode.SAML_INTERSTITIAL ||
+        this.isWhitelistErrorShown_ || this.authCompleted_) {
       return;
     }
 
-    if (this.screenMode_ == ScreenMode.AD_AUTH)
+    if (this.screenMode_ == AuthMode.AD_AUTH)
       chrome.send('cancelAdAuthentication');
 
     if (this.isClosable_())
@@ -1443,11 +1478,11 @@
       // To make animations correct, we need to make sure Gaia is completely
       // reloaded. Otherwise ChromeOS overlays hide and Gaia page is shown
       // somewhere in the middle of animations.
-      if (this.screenMode_ == ScreenMode.DEFAULT)
+      if (this.screenMode_ == AuthMode.DEFAULT)
         this.authenticator_.resetWebview();
     }
 
-    this.classList.toggle('whitelist-error', show);
+    this.isWhitelistErrorShown_ = show;
     this.isLoadingUiShown_ = !show;
 
     if (show)
@@ -1478,7 +1513,7 @@
    * @param {ACTIVE_DIRECTORY_ERROR_STATE} errorState
    */
   invalidateAd: function(username, errorState) {
-    if (this.screenMode_ != ScreenMode.AD_AUTH)
+    if (this.screenMode_ != AuthMode.AD_AUTH)
       return;
     const adAuthUI = this.getActiveFrame_();
     adAuthUI.userName = username;
@@ -1555,5 +1590,47 @@
     chrome.send('securityTokenPinEntered', [/*user_input=*/ e.detail]);
   },
 
+  /**
+   * Simple equality comparison function.
+   */
+  eq_: function(currentStep, expectedStep) {
+    return currentStep == expectedStep;
+  },
+
+  /**
+   * Updates current UI step based on internal state.
+   * @param {number} mode
+   * @param {OobeTypes.SecurityTokenPinDialogParameter} pinParams
+   * @param {boolean} isLoading
+   * @param {boolean} isWhitelistError
+   */
+  refreshDialogStep_: function(mode, pinParams, isLoading, isWhitelistError) {
+    if (pinParams !== null) {
+      this.step_ = DialogMode.PIN_DIALOG;
+      return;
+    }
+    if (isLoading) {
+      this.step_ = DialogMode.LOADING;
+      return;
+    }
+    if (isWhitelistError) {
+      this.step_ = DialogMode.GAIA_WHITELIST_ERROR;
+      return;
+    }
+    switch (mode) {
+      case AuthMode.DEFAULT:
+        this.step_ = DialogMode.GAIA;
+        break;
+      case AuthMode.SAML_INTERSTITIAL:
+        this.step_ = DialogMode.SAML_INTERSTITIAL;
+        break;
+      case AuthMode.OFFLINE:
+        this.step_ = DialogMode.OFFLINE_GAIA;
+        break;
+      case AuthMode.AD_AUTH:
+        this.step_ = DialogMode.OFFLINE_AD;
+        break;
+    }
+  },
 });
 })();
diff --git a/chrome/browser/resources/chromeos/switch_access/menu_panel.js b/chrome/browser/resources/chromeos/switch_access/menu_panel.js
index f1816108..ebea9667 100644
--- a/chrome/browser/resources/chromeos/switch_access/menu_panel.js
+++ b/chrome/browser/resources/chromeos/switch_access/menu_panel.js
@@ -15,7 +15,7 @@
     this.menuManager_;
 
     /**
-     * Reference to switch access.
+     * Reference to Switch Access.
      * @private {SwitchAccessInterface}
      */
     this.switchAccess_;
diff --git a/chrome/browser/resources/chromeos/switch_access/switch_access.js b/chrome/browser/resources/chromeos/switch_access/switch_access.js
index 4a89d32..fae8489 100644
--- a/chrome/browser/resources/chromeos/switch_access/switch_access.js
+++ b/chrome/browser/resources/chromeos/switch_access/switch_access.js
@@ -17,8 +17,6 @@
 
   /** @private */
   constructor() {
-    console.log('Switch access is enabled');
-
     /**
      * User commands.
      * @private {Commands}
diff --git a/chrome/browser/resources/local_ntp/externs.js b/chrome/browser/resources/local_ntp/externs.js
index bd71cf68..9ae815f 100644
--- a/chrome/browser/resources/local_ntp/externs.js
+++ b/chrome/browser/resources/local_ntp/externs.js
@@ -390,7 +390,10 @@
 window.chrome.embeddedSearch.searchBox.rtl;
 window.chrome.embeddedSearch.searchBox.startCapturingKeyStrokes;
 window.chrome.embeddedSearch.searchBox.stopCapturingKeyStrokes;
-/** @param {string} input */
+/**
+ * @param {string} input
+ * @param {boolean} preventInlineAutocomplete
+ */
 window.chrome.embeddedSearch.searchBox.queryAutocomplete;
 /** @param {boolean} clearResult */
 window.chrome.embeddedSearch.searchBox.stopAutocomplete;
diff --git a/chrome/browser/resources/local_ntp/local_ntp.js b/chrome/browser/resources/local_ntp/local_ntp.js
index a70ef1d..d68ea76 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.js
+++ b/chrome/browser/resources/local_ntp/local_ntp.js
@@ -268,7 +268,10 @@
  */
 let isDarkModeEnabled = false;
 
-/** Used to prevent inline autocompleting recently deleted output. */
+/**
+ * Used to prevent the default match from requiring inline autocompletion when
+ * the user is deleting text in the input.
+ */
 let isDeletingInput = false;
 
 /**
@@ -1167,13 +1170,9 @@
     selectMatchEl(assert($(IDS.REALBOX_MATCHES).firstElementChild));
   }
 
-  // If the user is deleting content, don't quickly re-suggest the same
-  // output.
-  if (!isDeletingInput) {
-    const first = result.matches[0];
-    if (first.allowedToBeDefaultMatch && first.inlineAutocompletion) {
-      updateRealboxOutput({inline: first.inlineAutocompletion});
-    }
+  const first = result.matches[0];
+  if (first.allowedToBeDefaultMatch && first.inlineAutocompletion) {
+    updateRealboxOutput({inline: first.inlineAutocompletion});
   }
 }
 
@@ -1542,7 +1541,8 @@
  */
 function queryAutocomplete(input) {
   lastInput = input;
-  window.chrome.embeddedSearch.searchBox.queryAutocomplete(input);
+  window.chrome.embeddedSearch.searchBox.queryAutocomplete(
+      input, isDeletingInput);
 }
 
 /**
diff --git a/chrome/browser/resources/tab_strip/tab.js b/chrome/browser/resources/tab_strip/tab.js
index 780b6dde..a5eb54e 100644
--- a/chrome/browser/resources/tab_strip/tab.js
+++ b/chrome/browser/resources/tab_strip/tab.js
@@ -121,8 +121,15 @@
     this.setAttribute('draggable', true);
     this.toggleAttribute('crashed_', tab.crashed);
 
-    if (!this.tab_ || this.tab_.title !== tab.title) {
+    if (tab.title) {
       this.titleTextEl_.textContent = tab.title;
+    } else if (
+        !tab.shouldHideThrobber &&
+        (tab.networkState === TabNetworkState.WAITING ||
+         tab.networkState === TabNetworkState.LOADING)) {
+      this.titleTextEl_.textContent = loadTimeData.getString('loadingTab');
+    } else {
+      this.titleTextEl_.textContent = loadTimeData.getString('defaultTabTitle');
     }
     this.titleTextEl_.setAttribute('aria-label', getAccessibleTitle(tab));
 
diff --git a/chrome/browser/resources/tab_strip/tab_swiper.js b/chrome/browser/resources/tab_strip/tab_swiper.js
index 91819e2..9815625 100644
--- a/chrome/browser/resources/tab_strip/tab_swiper.js
+++ b/chrome/browser/resources/tab_strip/tab_swiper.js
@@ -227,7 +227,7 @@
             (event.timeStamp - this.currentPointerDownEvent_.timeStamp)) >
         SWIPE_VELOCITY_THRESHOLD;
 
-    if (pixelsSwiped === SWIPE_FINISH_THRESHOLD_PX) {
+    if (Math.abs(pixelsSwiped) === SWIPE_FINISH_THRESHOLD_PX) {
       // The user has swiped the max amount of pixels to swipe and the animation
       // has already completed all its keyframes, so just fire the onfinish
       // events on the animation.
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
index 48892e0..fc348da 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
@@ -47,17 +47,11 @@
 
 namespace {
 
-const char** GetDMTokenForTestingStorage() {
-  static const char* dm_token_storage = "";
+BrowserDMToken* GetDMTokenForTestingStorage() {
+  static BrowserDMToken dm_token_storage = BrowserDMToken::CreateEmptyToken();
   return &dm_token_storage;
 }
 
-BrowserDMToken GetDMTokenForTesting() {
-  const char* dm_token = *GetDMTokenForTestingStorage();
-  return dm_token && dm_token[0] ? BrowserDMToken::CreateValidToken(dm_token)
-                                 : BrowserDMToken::CreateEmptyToken();
-}
-
 // Global pointer of factory function (RepeatingCallback) used to create
 // instances of DeepScanningDialogDelegate in tests.  !is_null() only in tests.
 DeepScanningDialogDelegate::Factory* GetFactoryStorage() {
@@ -405,7 +399,8 @@
 }
 
 // static
-void DeepScanningDialogDelegate::SetDMTokenForTesting(const char* dm_token) {
+void DeepScanningDialogDelegate::SetDMTokenForTesting(
+    const BrowserDMToken& dm_token) {
   *GetDMTokenForTestingStorage() = dm_token;
 }
 
@@ -482,7 +477,7 @@
 
 // static
 BrowserDMToken DeepScanningDialogDelegate::GetDMToken() {
-  auto dm_token = GetDMTokenForTesting();
+  BrowserDMToken* dm_token = GetDMTokenForTestingStorage();
 
 #if !defined(OS_CHROMEOS)
   // This is not compiled on chromeos because
@@ -490,13 +485,13 @@
   // policy::BrowserDMTokenStorage::Get()->RetrieveDMToken() does not return a
   // valid token either.  Once these are fixed the #if !defined can be removed.
 
-  if (dm_token.is_empty() &&
+  if (dm_token->is_empty() &&
       policy::ChromeBrowserCloudManagementController::IsEnabled()) {
-    dm_token = policy::BrowserDMTokenStorage::Get()->RetrieveBrowserDMToken();
+    return policy::BrowserDMTokenStorage::Get()->RetrieveBrowserDMToken();
   }
 #endif
 
-  return dm_token;
+  return *dm_token;
 }
 
 bool DeepScanningDialogDelegate::UploadData() {
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h
index c4d5694..00d0416 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h
@@ -163,7 +163,8 @@
   static void SetFactoryForTesting(Factory factory);
 
   // Overrides the DM token used for testing purposes.
-  static void SetDMTokenForTesting(const char* dm_token);
+  static void SetDMTokenForTesting(
+      const policy::BrowserDMTokenStorage::BrowserDMToken& dm_token);
 
   // Returns true if the given file type is supported for scanning.
   static bool FileTypeSupported(const bool for_malware_scan,
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_unittest.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_unittest.cc
index de9413f..5ec5a02 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_unittest.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_unittest.cc
@@ -39,6 +39,18 @@
 constexpr char kTestPortPatternUrl[] = "*:1234";
 constexpr char kTestQueryPatternUrl[] = "*?q=5678";
 
+class ScopedSetDMToken {
+ public:
+  explicit ScopedSetDMToken(
+      const policy::BrowserDMTokenStorage::BrowserDMToken& dm_token) {
+    DeepScanningDialogDelegate::SetDMTokenForTesting(dm_token);
+  }
+  ~ScopedSetDMToken() {
+    DeepScanningDialogDelegate::SetDMTokenForTesting(
+        policy::BrowserDMTokenStorage::BrowserDMToken::CreateEmptyToken());
+  }
+};
+
 class BaseTest : public testing::Test {
  public:
   BaseTest() : profile_manager_(TestingBrowserProcess::GetGlobal()) {
@@ -46,14 +58,14 @@
     profile_ = profile_manager_.CreateTestingProfile("test-user");
   }
 
-  void SetDMToken(const char* dm_token) {
-    DeepScanningDialogDelegate::SetDMTokenForTesting(dm_token);
+  void EnableFeature(const base::Feature& feature) {
+    scoped_feature_list_.Reset();
+    scoped_feature_list_.InitAndEnableFeature(feature);
   }
 
-  void EnableFeatures(const std::vector<base::Feature>& features) {
+  void DisableFeature(const base::Feature& feature) {
     scoped_feature_list_.Reset();
-    scoped_feature_list_.InitWithFeatures(features,
-                                          std::vector<base::Feature>());
+    scoped_feature_list_.InitAndDisableFeature(feature);
   }
 
   void SetDlpPolicy(CheckContentComplianceValues state) {
@@ -96,6 +108,10 @@
 using DeepScanningDialogDelegateIsEnabledTest = BaseTest;
 
 TEST_F(DeepScanningDialogDelegateIsEnabledTest, NoFeatureNoDMTokenNoPref) {
+  DisableFeature(kDeepScanningOfUploads);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateInvalidToken());
+
   DeepScanningDialogDelegate::Data data;
   EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(profile(), GURL(), &data));
   EXPECT_FALSE(data.do_dlp_scan);
@@ -103,7 +119,61 @@
 }
 
 TEST_F(DeepScanningDialogDelegateIsEnabledTest, NoDMTokenNoPref) {
-  EnableFeatures({kDeepScanningOfUploads});
+  EnableFeature(kDeepScanningOfUploads);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateInvalidToken());
+
+  DeepScanningDialogDelegate::Data data;
+  EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(profile(), GURL(), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+}
+
+TEST_F(DeepScanningDialogDelegateIsEnabledTest, NoDMToken) {
+  EnableFeature(kDeepScanningOfUploads);
+  SetDlpPolicy(CHECK_UPLOADS_AND_DOWNLOADS);
+  SetMalwarePolicy(SEND_UPLOADS_AND_DOWNLOADS);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateInvalidToken());
+
+  DeepScanningDialogDelegate::Data data;
+  EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(profile(), GURL(), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+}
+
+TEST_F(DeepScanningDialogDelegateIsEnabledTest, NoFeatureNoPref) {
+  DisableFeature(kDeepScanningOfUploads);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateValidToken(
+          kDmToken));
+
+  DeepScanningDialogDelegate::Data data;
+  EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(profile(), GURL(), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+}
+
+TEST_F(DeepScanningDialogDelegateIsEnabledTest, NoFeatureNoDMToken) {
+  DisableFeature(kDeepScanningOfUploads);
+  SetDlpPolicy(CHECK_UPLOADS_AND_DOWNLOADS);
+  SetMalwarePolicy(SEND_UPLOADS_AND_DOWNLOADS);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateInvalidToken());
+
+  DeepScanningDialogDelegate::Data data;
+  EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(profile(), GURL(), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+}
+
+TEST_F(DeepScanningDialogDelegateIsEnabledTest, NoFeature) {
+  DisableFeature(kDeepScanningOfUploads);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateValidToken(
+          kDmToken));
+  SetDlpPolicy(CHECK_UPLOADS_AND_DOWNLOADS);
+  SetMalwarePolicy(SEND_UPLOADS_AND_DOWNLOADS);
 
   DeepScanningDialogDelegate::Data data;
   EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(profile(), GURL(), &data));
@@ -112,8 +182,10 @@
 }
 
 TEST_F(DeepScanningDialogDelegateIsEnabledTest, DlpNoPref) {
-  EnableFeatures({kDeepScanningOfUploads});
-  SetDMToken(kDmToken);
+  EnableFeature(kDeepScanningOfUploads);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateValidToken(
+          kDmToken));
 
   DeepScanningDialogDelegate::Data data;
   EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(profile(), GURL(), &data));
@@ -122,8 +194,10 @@
 }
 
 TEST_F(DeepScanningDialogDelegateIsEnabledTest, DlpNoPref2) {
-  EnableFeatures({kDeepScanningOfUploads});
-  SetDMToken(kDmToken);
+  EnableFeature(kDeepScanningOfUploads);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateValidToken(
+          kDmToken));
   SetDlpPolicy(CHECK_NONE);
 
   DeepScanningDialogDelegate::Data data;
@@ -133,8 +207,10 @@
 }
 
 TEST_F(DeepScanningDialogDelegateIsEnabledTest, DlpNoPref3) {
-  EnableFeatures({kDeepScanningOfUploads});
-  SetDMToken(kDmToken);
+  EnableFeature(kDeepScanningOfUploads);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateValidToken(
+          kDmToken));
   SetDlpPolicy(CHECK_DOWNLOADS);
 
   DeepScanningDialogDelegate::Data data;
@@ -144,8 +220,10 @@
 }
 
 TEST_F(DeepScanningDialogDelegateIsEnabledTest, DlpEnabled) {
-  EnableFeatures({kDeepScanningOfUploads});
-  SetDMToken(kDmToken);
+  EnableFeature(kDeepScanningOfUploads);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateValidToken(
+          kDmToken));
   SetDlpPolicy(CHECK_UPLOADS);
 
   DeepScanningDialogDelegate::Data data;
@@ -155,8 +233,10 @@
 }
 
 TEST_F(DeepScanningDialogDelegateIsEnabledTest, DlpEnabled2) {
-  EnableFeatures({kDeepScanningOfUploads});
-  SetDMToken(kDmToken);
+  EnableFeature(kDeepScanningOfUploads);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateValidToken(
+          kDmToken));
   SetDlpPolicy(CHECK_UPLOADS_AND_DOWNLOADS);
 
   DeepScanningDialogDelegate::Data data;
@@ -167,8 +247,10 @@
 
 TEST_F(DeepScanningDialogDelegateIsEnabledTest, DlpDisabledByList) {
   GURL url(kTestUrl);
-  EnableFeatures({kDeepScanningOfUploads});
-  SetDMToken(kDmToken);
+  EnableFeature(kDeepScanningOfUploads);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateValidToken(
+          kDmToken));
   SetDlpPolicy(CHECK_UPLOADS);
   AddUrlToList(prefs::kURLsToNotCheckComplianceOfUploadedContent, url);
 
@@ -179,8 +261,10 @@
 }
 
 TEST_F(DeepScanningDialogDelegateIsEnabledTest, DlpDisabledByListWithPatterns) {
-  EnableFeatures({kDeepScanningOfUploads});
-  SetDMToken(kDmToken);
+  EnableFeature(kDeepScanningOfUploads);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateValidToken(
+          kDmToken));
   SetDlpPolicy(CHECK_UPLOADS);
   AddUrlToList(prefs::kURLsToNotCheckComplianceOfUploadedContent, kTestUrl);
   AddUrlToList(prefs::kURLsToNotCheckComplianceOfUploadedContent,
@@ -265,8 +349,10 @@
 }
 
 TEST_F(DeepScanningDialogDelegateIsEnabledTest, MalwareNoPref) {
-  EnableFeatures({kDeepScanningOfUploads});
-  SetDMToken(kDmToken);
+  EnableFeature(kDeepScanningOfUploads);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateValidToken(
+          kDmToken));
 
   DeepScanningDialogDelegate::Data data;
   EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(profile(), GURL(), &data));
@@ -275,8 +361,10 @@
 }
 
 TEST_F(DeepScanningDialogDelegateIsEnabledTest, MalwareNoPref2) {
-  EnableFeatures({kDeepScanningOfUploads});
-  SetDMToken(kDmToken);
+  EnableFeature(kDeepScanningOfUploads);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateValidToken(
+          kDmToken));
   SetMalwarePolicy(DO_NOT_SCAN);
 
   DeepScanningDialogDelegate::Data data;
@@ -286,8 +374,10 @@
 }
 
 TEST_F(DeepScanningDialogDelegateIsEnabledTest, MalwareNoPref4) {
-  EnableFeatures({kDeepScanningOfUploads});
-  SetDMToken(kDmToken);
+  EnableFeature(kDeepScanningOfUploads);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateValidToken(
+          kDmToken));
   SetMalwarePolicy(SEND_DOWNLOADS);
 
   DeepScanningDialogDelegate::Data data;
@@ -297,8 +387,10 @@
 }
 
 TEST_F(DeepScanningDialogDelegateIsEnabledTest, MalwareNoList) {
-  EnableFeatures({kDeepScanningOfUploads});
-  SetDMToken(kDmToken);
+  EnableFeature(kDeepScanningOfUploads);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateValidToken(
+          kDmToken));
   SetMalwarePolicy(SEND_UPLOADS);
 
   DeepScanningDialogDelegate::Data data;
@@ -308,8 +400,10 @@
 }
 
 TEST_F(DeepScanningDialogDelegateIsEnabledTest, MalwareNoList2) {
-  EnableFeatures({kDeepScanningOfUploads});
-  SetDMToken(kDmToken);
+  EnableFeature(kDeepScanningOfUploads);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateValidToken(
+          kDmToken));
   SetMalwarePolicy(SEND_UPLOADS_AND_DOWNLOADS);
 
   DeepScanningDialogDelegate::Data data;
@@ -320,8 +414,10 @@
 
 TEST_F(DeepScanningDialogDelegateIsEnabledTest, MalwareEnabled) {
   GURL url(kTestUrl);
-  EnableFeatures({kDeepScanningOfUploads});
-  SetDMToken(kDmToken);
+  EnableFeature(kDeepScanningOfUploads);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateValidToken(
+          kDmToken));
   SetMalwarePolicy(SEND_UPLOADS_AND_DOWNLOADS);
   AddUrlToList(prefs::kURLsToCheckForMalwareOfUploadedContent, url);
 
@@ -333,8 +429,10 @@
 
 TEST_F(DeepScanningDialogDelegateIsEnabledTest, NoScanInIncognito) {
   GURL url(kTestUrl);
-  EnableFeatures({kDeepScanningOfUploads});
-  SetDMToken(kDmToken);
+  EnableFeature(kDeepScanningOfUploads);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateValidToken(
+          kDmToken));
   SetDlpPolicy(CHECK_UPLOADS_AND_DOWNLOADS);
   SetMalwarePolicy(SEND_UPLOADS_AND_DOWNLOADS);
   AddUrlToList(prefs::kURLsToCheckForMalwareOfUploadedContent, url);
@@ -348,8 +446,10 @@
 }
 
 TEST_F(DeepScanningDialogDelegateIsEnabledTest, MalwareEnabledWithPatterns) {
-  EnableFeatures({kDeepScanningOfUploads});
-  SetDMToken(kDmToken);
+  EnableFeature(kDeepScanningOfUploads);
+  ScopedSetDMToken scoped_dm_token(
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateValidToken(
+          kDmToken));
   SetMalwarePolicy(SEND_UPLOADS_AND_DOWNLOADS);
   AddUrlToList(prefs::kURLsToCheckForMalwareOfUploadedContent, kTestUrl);
   AddUrlToList(prefs::kURLsToCheckForMalwareOfUploadedContent,
@@ -452,8 +552,7 @@
   void SetUp() override {
     BaseTest::SetUp();
 
-    EnableFeatures({kDeepScanningOfUploads});
-    SetDMToken(kDmToken);
+    EnableFeature(kDeepScanningOfUploads);
     SetDlpPolicy(CHECK_UPLOADS);
     SetMalwarePolicy(SEND_UPLOADS);
 
@@ -474,6 +573,9 @@
 
   base::RunLoop run_loop_;
   std::unique_ptr<content::WebContents> web_contents_;
+  ScopedSetDMToken scoped_dm_token_{
+      policy::BrowserDMTokenStorage::BrowserDMToken::CreateValidToken(
+          kDmToken)};
 
   // Paths in this map will be consider to have failed deep scan checks.
   // The actual failure response is given for each path.
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_browsertest.cc b/chrome/browser/sharing/click_to_call/click_to_call_browsertest.cc
index accc3da..8cfc46669 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_browsertest.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_browsertest.cc
@@ -27,6 +27,7 @@
 #include "components/policy/policy_constants.h"
 #include "components/prefs/pref_service.h"
 #include "components/sync/driver/profile_sync_service.h"
+#include "components/sync/driver/sync_driver_switches.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -130,6 +131,14 @@
 
 IN_PROC_BROWSER_TEST_F(ClickToCallBrowserTest,
                        ContextMenu_DevicesAvailable_SyncTurnedOff) {
+  if (base::FeatureList::IsEnabled(kSharingUseDeviceInfo) &&
+      base::FeatureList::IsEnabled(kSharingDeriveVapidKey) &&
+      base::FeatureList::IsEnabled(switches::kSyncDeviceInfoInTransportMode)) {
+    // Turning off sync will have no effect when Click to Call is available on
+    // sign-in.
+    return;
+  }
+
   Init(sync_pb::SharingSpecificFields::CLICK_TO_CALL,
        sync_pb::SharingSpecificFields::UNKNOWN);
   auto devices = sharing_service()->GetDeviceCandidates(
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_browsertest.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_browsertest.cc
index 57b0418..ce5374d 100644
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_browsertest.cc
+++ b/chrome/browser/sharing/shared_clipboard/shared_clipboard_browsertest.cc
@@ -11,6 +11,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
+#include "chrome/browser/sharing/features.h"
 #include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
 #include "chrome/browser/sharing/sharing_browsertest.h"
 #include "chrome/browser/sharing/sharing_constants.h"
@@ -18,6 +19,7 @@
 #include "chrome/browser/sharing/sharing_sync_preference.h"
 #include "chrome/browser/sync/test/integration/sessions_helper.h"
 #include "components/sync/driver/profile_sync_service.h"
+#include "components/sync/driver/sync_driver_switches.h"
 #include "url/gurl.h"
 
 namespace {
@@ -126,6 +128,14 @@
 }
 
 IN_PROC_BROWSER_TEST_F(SharedClipboardBrowserTest, ContextMenu_SyncTurnedOff) {
+  if (base::FeatureList::IsEnabled(kSharingUseDeviceInfo) &&
+      base::FeatureList::IsEnabled(kSharingDeriveVapidKey) &&
+      base::FeatureList::IsEnabled(switches::kSyncDeviceInfoInTransportMode)) {
+    // Turning off sync will have no effect when Shared Clipboard is available
+    // on sign-in.
+    return;
+  }
+
   Init(sync_pb::SharingSpecificFields::SHARED_CLIPBOARD,
        sync_pb::SharingSpecificFields::UNKNOWN);
   auto devices = sharing_service()->GetDeviceCandidates(
diff --git a/chrome/browser/sharing/sharing_service.cc b/chrome/browser/sharing/sharing_service.cc
index 066c79eb..0ea4d6f 100644
--- a/chrome/browser/sharing/sharing_service.cc
+++ b/chrome/browser/sharing/sharing_service.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/sharing/sharing_fcm_sender.h"
 #include "chrome/browser/sharing/sharing_message_handler.h"
 #include "chrome/browser/sharing/sharing_metrics.h"
+#include "chrome/browser/sharing/sharing_service_factory.h"
 #include "chrome/browser/sharing/sharing_sync_preference.h"
 #include "chrome/browser/sharing/sharing_utils.h"
 #include "chrome/browser/sharing/sms/sms_fetch_request_handler.h"
@@ -410,19 +411,35 @@
 }
 
 bool SharingService::IsSyncEnabled() const {
-  return sync_service_ &&
-         sync_service_->GetTransportState() ==
-             syncer::SyncService::TransportState::ACTIVE &&
-         sync_service_->GetActiveDataTypes().HasAll(GetRequiredSyncDataTypes());
+  if (!sync_service_)
+    return false;
+
+  bool is_sync_enabled =
+      sync_service_->GetTransportState() ==
+          syncer::SyncService::TransportState::ACTIVE &&
+      sync_service_->GetActiveDataTypes().HasAll(GetRequiredSyncDataTypes());
+  // TODO(crbug.com/1012226): Remove local sync check when we have dedicated
+  // Sharing data type.
+  if (base::FeatureList::IsEnabled(kSharingDeriveVapidKey))
+    is_sync_enabled &= !sync_service_->IsLocalSyncEnabled();
+  return is_sync_enabled;
 }
 
 bool SharingService::IsSyncDisabled() const {
-  return sync_service_ && (sync_service_->GetTransportState() ==
-                               syncer::SyncService::TransportState::DISABLED ||
-                           (sync_service_->GetTransportState() ==
-                                syncer::SyncService::TransportState::ACTIVE &&
-                            !sync_service_->GetActiveDataTypes().HasAll(
-                                GetRequiredSyncDataTypes())));
+  if (!sync_service_)
+    return false;
+
+  bool is_sync_disabled =
+      sync_service_->GetTransportState() ==
+          syncer::SyncService::TransportState::DISABLED ||
+      (sync_service_->GetTransportState() ==
+           syncer::SyncService::TransportState::ACTIVE &&
+       !sync_service_->GetActiveDataTypes().HasAll(GetRequiredSyncDataTypes()));
+  // TODO(crbug.com/1012226): Remove local sync check when we have dedicated
+  // Sharing data type.
+  if (base::FeatureList::IsEnabled(kSharingDeriveVapidKey))
+    is_sync_disabled |= sync_service_->IsLocalSyncEnabled();
+  return is_sync_disabled;
 }
 
 syncer::ModelTypeSet SharingService::GetRequiredSyncDataTypes() const {
diff --git a/chrome/browser/sharing/sharing_service_unittest.cc b/chrome/browser/sharing/sharing_service_unittest.cc
index abcd825..ccc774bf 100644
--- a/chrome/browser/sharing/sharing_service_unittest.cc
+++ b/chrome/browser/sharing/sharing_service_unittest.cc
@@ -566,6 +566,24 @@
             GetSharingService()->GetStateForTesting());
 }
 
+TEST_F(SharingServiceTest, DeviceUnregistrationLocalSyncEnabled) {
+  // Enable the registration feature and transport mode required features.
+  scoped_feature_list_.InitWithFeatures(
+      /*enabled_features=*/{kSharingDeviceRegistration, kSharingUseDeviceInfo,
+                            kSharingDeriveVapidKey},
+      /*disabled_features=*/{});
+  test_sync_service_.SetTransportState(
+      syncer::SyncService::TransportState::ACTIVE);
+  test_sync_service_.SetActiveDataTypes({syncer::DEVICE_INFO});
+  test_sync_service_.SetLocalSyncEnabled(true);
+
+  // Create new SharingService instance with sync disabled at constructor.
+  GetSharingService();
+  EXPECT_EQ(1, sharing_device_registration_->unregistration_attempts());
+  EXPECT_EQ(SharingService::State::DISABLED,
+            GetSharingService()->GetStateForTesting());
+}
+
 TEST_F(SharingServiceTest, DeviceRegisterAndUnregister) {
   // Enable the feature.
   scoped_feature_list_.InitAndEnableFeature(kSharingDeviceRegistration);
@@ -659,6 +677,29 @@
   GetSharingService();
 }
 
+TEST_F(SharingServiceTest, NoDevicesWhenLocalSyncEnabled) {
+  // Enable the registration feature and transport mode required features.
+  scoped_feature_list_.InitWithFeatures(
+      /*enabled_features=*/{kSharingDeviceRegistration, kSharingUseDeviceInfo,
+                            kSharingDeriveVapidKey},
+      /*disabled_features=*/{});
+  test_sync_service_.SetTransportState(
+      syncer::SyncService::TransportState::ACTIVE);
+  test_sync_service_.SetActiveDataTypes({syncer::DEVICE_INFO});
+  test_sync_service_.SetLocalSyncEnabled(true);
+
+  std::string id = base::GenerateGUID();
+  std::unique_ptr<syncer::DeviceInfo> device_info =
+      CreateFakeDeviceInfo(id, kDeviceName);
+  fake_device_info_sync_service.GetDeviceInfoTracker()->Add(device_info.get());
+
+  std::vector<std::unique_ptr<syncer::DeviceInfo>> candidates =
+      GetSharingService()->GetDeviceCandidates(
+          sync_pb::SharingSpecificFields::CLICK_TO_CALL);
+
+  ASSERT_EQ(0u, candidates.size());
+}
+
 TEST_F(SharingServiceTest, NoDevicesWhenSyncDisabled) {
   scoped_feature_list_.InitAndEnableFeature(kSharingDeviceRegistration);
   test_sync_service_.SetTransportState(
diff --git a/chrome/browser/supervised_user/logged_in_user_mixin.cc b/chrome/browser/supervised_user/logged_in_user_mixin.cc
index 77c4d21..d9a180a 100644
--- a/chrome/browser/supervised_user/logged_in_user_mixin.cc
+++ b/chrome/browser/supervised_user/logged_in_user_mixin.cc
@@ -46,8 +46,10 @@
                                                FakeGaiaMixin::kFakeUserGaiaId)),
             ConvertUserType(type)),
       login_manager_(mixin_host, GetInitialUsers(user_, include_initial_user)),
-      policy_server_(mixin_host),
-      user_policy_(mixin_host, user_.account_id, &policy_server_),
+      local_policy_server_(mixin_host),
+      user_policy_(mixin_host, user_.account_id, &local_policy_server_),
+      user_policy_helper_(user_.account_id.GetUserEmail(),
+                          &local_policy_server_),
       embedded_test_server_setup_(mixin_host, embedded_test_server),
       fake_gaia_(mixin_host, embedded_test_server) {
   // By default, LoginManagerMixin will set up user session manager not to
@@ -67,6 +69,10 @@
   // account.google.com requests would never reach fake GAIA server without
   // this.
   host_resolver->AddRule("*", "127.0.0.1");
+  // Call RequestPolicyUpdate() to set up policy, which prevents the call to
+  // LogInUser() below from hanging indefinitely when there's no initial user
+  // and wait_for_active_session is true.
+  GetUserPolicyMixin()->RequestPolicyUpdate();
   LogInUser(issue_any_scope_token, wait_for_active_session);
   // Set the private |browser_| member in InProcessBrowserTest.
   // Otherwise calls to InProcessBrowserTest::browser() returns null and leads
diff --git a/chrome/browser/supervised_user/logged_in_user_mixin.h b/chrome/browser/supervised_user/logged_in_user_mixin.h
index 98bdeff..f33e75dd 100644
--- a/chrome/browser/supervised_user/logged_in_user_mixin.h
+++ b/chrome/browser/supervised_user/logged_in_user_mixin.h
@@ -12,6 +12,7 @@
 #include "chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h"
 #include "chrome/browser/chromeos/login/test/login_manager_mixin.h"
 #include "chrome/browser/chromeos/login/test/user_policy_mixin.h"
+#include "chrome/browser/chromeos/policy/user_policy_test_helper.h"
 
 class AccountId;
 
@@ -99,19 +100,24 @@
   LoginManagerMixin* GetLoginManagerMixin() { return &login_manager_; }
 
   LocalPolicyTestServerMixin* GetLocalPolicyTestServerMixin() {
-    return &policy_server_;
+    return &local_policy_server_;
   }
 
   UserPolicyMixin* GetUserPolicyMixin() { return &user_policy_; }
 
+  policy::UserPolicyTestHelper* GetUserPolicyTestHelper() {
+    return &user_policy_helper_;
+  }
+
   const AccountId& GetAccountId() { return user_.account_id; }
 
  private:
   LoginManagerMixin::TestUserInfo user_;
   LoginManagerMixin login_manager_;
 
-  LocalPolicyTestServerMixin policy_server_;
+  LocalPolicyTestServerMixin local_policy_server_;
   UserPolicyMixin user_policy_;
+  policy::UserPolicyTestHelper user_policy_helper_;
 
   EmbeddedTestServerSetupMixin embedded_test_server_setup_;
   FakeGaiaMixin fake_gaia_;
diff --git a/chrome/browser/sync/test/integration/local_sync_test.cc b/chrome/browser/sync/test/integration/local_sync_test.cc
index 719d2bb..195658c 100644
--- a/chrome/browser/sync/test/integration/local_sync_test.cc
+++ b/chrome/browser/sync/test/integration/local_sync_test.cc
@@ -10,6 +10,8 @@
 #include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
+#include "chrome/browser/sharing/sharing_service.h"
+#include "chrome/browser/sharing/sharing_service_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
 #include "chrome/browser/ui/browser.h"
@@ -76,6 +78,9 @@
 
   // Verify certain features are disabled.
   EXPECT_FALSE(send_tab_to_self::IsUserSyncTypeActive(browser()->profile()));
+  EXPECT_EQ(SharingService::State::DISABLED,
+            SharingServiceFactory::GetForBrowserContext(browser()->profile())
+                ->GetStateForTesting());
 }
 #endif  // defined(OS_WIN)
 
diff --git a/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc b/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
index e7a1033..00af0583 100644
--- a/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
@@ -30,6 +30,9 @@
                                      syncer::SECURITY_EVENTS,
                                      syncer::AUTOFILL_WALLET_DATA);
   allowed_types.PutAll(syncer::ControlTypes());
+  if (base::FeatureList::IsEnabled(switches::kSyncDeviceInfoInTransportMode)) {
+    allowed_types.Put(syncer::DEVICE_INFO);
+  }
   return allowed_types;
 }
 
diff --git a/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc b/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
index c8b2e1a1..0e6441b 100644
--- a/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
@@ -27,6 +27,9 @@
                                      syncer::SECURITY_EVENTS,
                                      syncer::AUTOFILL_WALLET_DATA);
   allowed_types.PutAll(syncer::ControlTypes());
+  if (base::FeatureList::IsEnabled(switches::kSyncDeviceInfoInTransportMode)) {
+    allowed_types.Put(syncer::DEVICE_INFO);
+  }
   return allowed_types;
 }
 
diff --git a/chrome/browser/themes/browser_theme_pack.cc b/chrome/browser/themes/browser_theme_pack.cc
index eec91acb..b50bb721 100644
--- a/chrome/browser/themes/browser_theme_pack.cc
+++ b/chrome/browser/themes/browser_theme_pack.cc
@@ -1035,7 +1035,7 @@
   }
   if (theme_colors.empty())
     return;
-  provider->AddMixer()->AddSet({kColorSetCustomTheme, std::move(theme_colors)});
+  provider->AddMixer().AddSet({kColorSetCustomTheme, std::move(theme_colors)});
 }
 
 // private:
diff --git a/chrome/browser/themes/browser_theme_pack_unittest.cc b/chrome/browser/themes/browser_theme_pack_unittest.cc
index f315f5c..9638fd8 100644
--- a/chrome/browser/themes/browser_theme_pack_unittest.cc
+++ b/chrome/browser/themes/browser_theme_pack_unittest.cc
@@ -741,10 +741,10 @@
   // Tests to make sure that existing colors within the color provider are not
   // overwritten or lost in the absence of any user provided theme values.
   ui::ColorProvider provider;
-  provider.AddMixer()->AddSet({ui::kColorSetTest0,
-                               {{kColorToolbar, SK_ColorRED},
-                                {kColorOmniboxText, SK_ColorGREEN},
-                                {kColorOmniboxBackground, SK_ColorBLUE}}});
+  provider.AddMixer().AddSet({ui::kColorSetTest0,
+                              {{kColorToolbar, SK_ColorRED},
+                               {kColorOmniboxText, SK_ColorGREEN},
+                               {kColorOmniboxBackground, SK_ColorBLUE}}});
   theme_pack().AddCustomThemeColorMixers(&provider);
   EXPECT_EQ(SK_ColorRED, provider.GetColor(kColorToolbar));
   EXPECT_EQ(SK_ColorGREEN, provider.GetColor(kColorOmniboxText));
@@ -755,10 +755,10 @@
   // Tests to make sure that only provided theme values are replicated into the
   // color provider.
   ui::ColorProvider provider;
-  provider.AddMixer()->AddSet({ui::kColorSetTest0,
-                               {{kColorToolbar, SK_ColorRED},
-                                {kColorOmniboxText, SK_ColorGREEN},
-                                {kColorOmniboxBackground, SK_ColorBLUE}}});
+  provider.AddMixer().AddSet({ui::kColorSetTest0,
+                              {{kColorToolbar, SK_ColorRED},
+                               {kColorOmniboxText, SK_ColorGREEN},
+                               {kColorOmniboxBackground, SK_ColorBLUE}}});
   std::string color_json = R"({ "toolbar": [0, 20, 40],
                                 "omnibox_text": [60, 80, 100] })";
   LoadColorJSON(color_json);
@@ -772,10 +772,10 @@
   // Tests to make sure that all available colors are properly loaded into the
   // color provider.
   ui::ColorProvider provider;
-  provider.AddMixer()->AddSet({ui::kColorSetTest0,
-                               {{kColorToolbar, SK_ColorRED},
-                                {kColorOmniboxText, SK_ColorGREEN},
-                                {kColorOmniboxBackground, SK_ColorBLUE}}});
+  provider.AddMixer().AddSet({ui::kColorSetTest0,
+                              {{kColorToolbar, SK_ColorRED},
+                               {kColorOmniboxText, SK_ColorGREEN},
+                               {kColorOmniboxBackground, SK_ColorBLUE}}});
   std::string color_json = R"({ "toolbar": [0, 20, 40],
                                 "omnibox_text": [60, 80, 100],
                                 "omnibox_background": [120, 140, 160] })";
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index dc7f0e7..241bf4d 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2627,6 +2627,8 @@
       "views/apps/app_info_dialog/app_info_permissions_panel.h",
       "views/apps/app_info_dialog/app_info_summary_panel.cc",
       "views/apps/app_info_dialog/app_info_summary_panel.h",
+      "views/apps/app_pause_dialog_view.cc",
+      "views/apps/app_pause_dialog_view.h",
       "views/apps/app_uninstall_dialog_view.cc",
       "views/apps/app_uninstall_dialog_view.h",
       "views/apps/chrome_native_app_window_views.cc",
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescription.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescription.java
index 23182f4..aef401f 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescription.java
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescription.java
@@ -17,7 +17,6 @@
 import android.widget.RadioButton;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
-
 import java.util.List;
 
 /**
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayout.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayout.java
index aaf1f78c..39e0ada 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayout.java
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayout.java
@@ -7,6 +7,7 @@
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.RadioGroup;
 
 import androidx.annotation.Nullable;
@@ -16,11 +17,13 @@
 
 /**
  * Manages a group of exclusive RadioButtonWithDescriptions, automatically inserting a margin in
- * between the rows to prevent them from squishing together.
+ * between the rows to prevent them from squishing together. Has the option to set an accessory view
+ * on any given RadioButtonWithDescription. Only one accessory view per layout is supported.
  *
  * -------------------------------------------------
  * | O | MESSAGE #1                                |
  *        description_1                            |
+ *        [optional] accessory view                |
  * | O | MESSAGE #N                                |
  *        description_n                            |
  * -------------------------------------------------
@@ -63,6 +66,7 @@
     private final int mMarginBetweenRows;
     private final List<RadioButtonWithDescription> mRadioButtonsWithDescriptions;
     private OnCheckedChangeListener mOnCheckedChangeListener;
+    private View mAccessoryView;
 
     public RadioButtonWithDescriptionLayout(Context context) {
         this(context, null);
@@ -130,6 +134,31 @@
         updateMargins();
     }
 
+    private View removeAttachedAccessoryView(View view) {
+        // Remove the view from it's parent if it has one.
+        if (view.getParent() != null) {
+            ViewGroup previousGroup = (ViewGroup) view.getParent();
+            previousGroup.removeView(view);
+        }
+        return view;
+    }
+
+    /**
+     * Attach the given accessory view to the given RadioButtonWithDescription. The attachmentPoint
+     * must be a direct child of this.
+     *
+     * @param accessoryView The accessory view to be attached.
+     * @param attachmentPoint The RadioButtonWithDescription that the accessory view will be
+     *                        attached to.
+     */
+    public void attachAccessoryView(
+            View accessoryView, RadioButtonWithDescription attachmentPoint) {
+        removeAttachedAccessoryView(accessoryView);
+        int attachmentPointIndex = indexOfChild(attachmentPoint);
+        assert attachmentPointIndex >= 0 : "attachmentPoint view must a child of layout.";
+        addView(accessoryView, attachmentPointIndex + 1);
+    }
+
     /** Sets margins between each of the radio buttons. */
     private void updateMargins() {
         int childCount = getChildCount();
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayoutTest.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayoutTest.java
index 8b86081..9f9fdef 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayoutTest.java
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayoutTest.java
@@ -11,6 +11,7 @@
 import android.support.test.rule.UiThreadTestRule;
 import android.view.View;
 import android.view.ViewGroup.MarginLayoutParams;
+import android.widget.TextView;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -187,4 +188,51 @@
             Assert.assertEquals(i == 1, child.isChecked());
         }
     }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testAccessoryViewAdded() {
+        final RadioButtonWithDescriptionLayout layout =
+                new RadioButtonWithDescriptionLayout(mContext);
+
+        List<RadioButtonWithDescriptionLayout.Option> options = new ArrayList<>();
+        options.add(new RadioButtonWithDescriptionLayout.Option("a", "a_desc", null));
+        options.add(new RadioButtonWithDescriptionLayout.Option("b", "b_desc", null));
+        options.add(new RadioButtonWithDescriptionLayout.Option("c", "c_desc", null));
+        layout.addOptions(options);
+
+        RadioButtonWithDescription firstButton = (RadioButtonWithDescription) layout.getChildAt(0);
+        final TextView accessoryTextView = new TextView(mContext);
+        layout.attachAccessoryView(accessoryTextView, firstButton);
+        Assert.assertEquals(
+                "The accessory view should be right after the position of it's attachment host.",
+                accessoryTextView, layout.getChildAt(1));
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testAccessoryViewAddedThenReadded() {
+        final RadioButtonWithDescriptionLayout layout =
+                new RadioButtonWithDescriptionLayout(mContext);
+
+        List<RadioButtonWithDescriptionLayout.Option> options = new ArrayList<>();
+        options.add(new RadioButtonWithDescriptionLayout.Option("a", "a_desc", null));
+        options.add(new RadioButtonWithDescriptionLayout.Option("b", "b_desc", null));
+        options.add(new RadioButtonWithDescriptionLayout.Option("c", "c_desc", null));
+        layout.addOptions(options);
+
+        RadioButtonWithDescription firstButton = (RadioButtonWithDescription) layout.getChildAt(0);
+        RadioButtonWithDescription lastButton =
+                (RadioButtonWithDescription) layout.getChildAt(layout.getChildCount() - 1);
+        final TextView accessoryTextView = new TextView(mContext);
+        layout.attachAccessoryView(accessoryTextView, firstButton);
+        layout.attachAccessoryView(accessoryTextView, lastButton);
+        Assert.assertNotEquals(
+                "The accessory view shouldn't be in the first position it was inserted at.",
+                accessoryTextView, layout.getChildAt(1));
+        Assert.assertEquals("The accessory view should be at the new position it was placed at.",
+                accessoryTextView, layout.getChildAt(layout.getChildCount() - 1));
+    }
 }
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
index 76d6b13..ed420c6 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
@@ -228,13 +228,13 @@
   wallpaper_controller_client_.reset();
   vpn_list_forwarder_.reset();
 
-  // Initialized in PostProfileInit:
+  // Initialized in PostProfileInit (which may not get called in some tests).
   network_portal_notification_controller_.reset();
   display_settings_handler_.reset();
   media_client_.reset();
   login_screen_client_.reset();
 
-  // Initialized in PreProfileInit:
+  // Initialized in PreProfileInit (which may not get called in some tests).
   system_tray_client_.reset();
   session_controller_client_.reset();
   ime_controller_client_.reset();
@@ -245,8 +245,8 @@
   app_list_client_.reset();
   ash_shell_init_.reset();
   cast_config_controller_media_router_.reset();
-
-  chromeos::NetworkConnect::Shutdown();
+  if (chromeos::NetworkConnect::IsInitialized())
+    chromeos::NetworkConnect::Shutdown();
   network_connect_delegate_.reset();
 }
 
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc
index 0d4126c6..82b53694 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -58,7 +58,7 @@
   BrowserView* browser_view =
       BrowserView::GetBrowserViewForNativeWindow(window);
   if (!browser_view)
-    return true;
+    return false;
   content::WebContents* contents =
       browser_view->browser()->tab_strip_model()->GetActiveWebContents();
   if (!contents)
diff --git a/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc b/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc
index 8f30b7f..001e4af 100644
--- a/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc
+++ b/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc
@@ -23,15 +23,15 @@
 namespace {
 
 // The total number of Ash accelerators.
-constexpr int kAshAcceleratorsTotalNum = 109;
+constexpr int kAshAcceleratorsTotalNum = 110;
 // The hash of Ash accelerators.
-constexpr char kAshAcceleratorsHash[] = "8b62c1607085b5ca6a65ef43b97deac0";
+constexpr char kAshAcceleratorsHash[] = "1287cacd678f63ab151fbf25383ac19c";
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
 // Internal builds add an extra accelerator for the Feedback app.
 // The total number of Chrome accelerators (available on Chrome OS).
-constexpr int kChromeAcceleratorsTotalNum = 92;
+constexpr int kChromeAcceleratorsTotalNum = 93;
 // The hash of Chrome accelerators (available on Chrome OS).
-constexpr char kChromeAcceleratorsHash[] = "15a6c673dc62825b04e80036ad690514";
+constexpr char kChromeAcceleratorsHash[] = "73c842a72d77e7b9e69e92a6ee7900d3";
 #else
 // The total number of Chrome accelerators (available on Chrome OS).
 constexpr int kChromeAcceleratorsTotalNum = 92;
diff --git a/chrome/browser/ui/browser_focus_uitest.cc b/chrome/browser/ui/browser_focus_uitest.cc
index 52b84e0f..b532fde 100644
--- a/chrome/browser/ui/browser_focus_uitest.cc
+++ b/chrome/browser/ui/browser_focus_uitest.cc
@@ -600,7 +600,7 @@
 }
 
 // Tests that focus goes where expected when using reload on a crashed tab.
-#if (defined(OS_CHROMEOS) || defined(OS_LINUX)) && !defined(NDEBUG)
+#if defined(OS_CHROMEOS) || defined(OS_LINUX)
 // Hangy, http://crbug.com/50025.
 #define MAYBE_FocusOnReloadCrashedTab DISABLED_FocusOnReloadCrashedTab
 #else
diff --git a/chrome/browser/ui/cocoa/renderer_context_menu/render_view_context_menu_mac_cocoa.mm b/chrome/browser/ui/cocoa/renderer_context_menu/render_view_context_menu_mac_cocoa.mm
index 1be7698..d0ccb41 100644
--- a/chrome/browser/ui/cocoa/renderer_context_menu/render_view_context_menu_mac_cocoa.mm
+++ b/chrome/browser/ui/cocoa/renderer_context_menu/render_view_context_menu_mac_cocoa.mm
@@ -20,7 +20,7 @@
 
 namespace {
 
-IMP g_original_populatemenu_implementation = nullptr;
+base::mac::ScopedObjCClassSwizzler* g_populatemenu_swizzler = nullptr;
 
 // |g_filtered_entries_array| is only set during testing (see
 // +[ChromeSwizzleServicesMenuUpdater storeFilteredEntriesForTestingInArray:]).
@@ -98,8 +98,8 @@
   }
 
   // Pass the filtered array along to the _NSServicesMenuUpdater.
-  g_original_populatemenu_implementation(self, _cmd, menu, remainingEntries,
-                                         display);
+  g_populatemenu_swizzler->InvokeOriginal<void, NSMenu*, NSArray*, BOOL>(
+      self, _cmd, menu, remainingEntries, display);
 }
 
 + (void)storeFilteredEntriesForTestingInArray:(NSMutableArray*)array {
@@ -145,8 +145,7 @@
     Class swizzleClass = [ChromeSwizzleServicesMenuUpdater class];
     static base::NoDestructor<base::mac::ScopedObjCClassSwizzler>
         servicesMenuFilter(targetClass, swizzleClass, targetSelector);
-    g_original_populatemenu_implementation =
-        servicesMenuFilter->GetOriginalImplementation();
+    g_populatemenu_swizzler = servicesMenuFilter.get();
   });
 }
 
diff --git a/chrome/browser/ui/search/search_ipc_router.cc b/chrome/browser/ui/search/search_ipc_router.cc
index 64c39cd7..ab6aba1 100644
--- a/chrome/browser/ui/search/search_ipc_router.cc
+++ b/chrome/browser/ui/search/search_ipc_router.cc
@@ -441,6 +441,7 @@
 
 void SearchIPCRouter::QueryAutocomplete(
     const base::string16& input,
+    bool prevent_inline_autocomplete,
     chrome::mojom::EmbeddedSearch::QueryAutocompleteCallback callback) {
   if (!policy_->ShouldProcessQueryAutocomplete(is_active_tab_)) {
     std::move(callback).Run(chrome::mojom::AutocompleteResult::New(
@@ -449,7 +450,8 @@
     return;
   }
 
-  delegate_->QueryAutocomplete(input, std::move(callback));
+  delegate_->QueryAutocomplete(input, prevent_inline_autocomplete,
+                               std::move(callback));
 }
 
 void SearchIPCRouter::StopAutocomplete(bool clear_result) {
diff --git a/chrome/browser/ui/search/search_ipc_router.h b/chrome/browser/ui/search/search_ipc_router.h
index c737c15..900c22a 100644
--- a/chrome/browser/ui/search/search_ipc_router.h
+++ b/chrome/browser/ui/search/search_ipc_router.h
@@ -158,6 +158,7 @@
 
     virtual void QueryAutocomplete(
         const base::string16& input,
+        bool prevent_inline_autocomplete,
         chrome::mojom::EmbeddedSearch::QueryAutocompleteCallback callback) = 0;
 
     virtual void StopAutocomplete(bool clear_result) = 0;
@@ -317,6 +318,7 @@
   void ConfirmThemeChanges() override;
   void QueryAutocomplete(
       const base::string16& input,
+      bool prevent_inline_autocomplete,
       chrome::mojom::EmbeddedSearch::QueryAutocompleteCallback callback)
       override;
   void StopAutocomplete(bool clear_result) override;
diff --git a/chrome/browser/ui/search/search_ipc_router_unittest.cc b/chrome/browser/ui/search/search_ipc_router_unittest.cc
index d1278c5..621a6216 100644
--- a/chrome/browser/ui/search/search_ipc_router_unittest.cc
+++ b/chrome/browser/ui/search/search_ipc_router_unittest.cc
@@ -108,9 +108,10 @@
       void(uint8_t line,
            chrome::mojom::EmbeddedSearch::DeleteAutocompleteMatchCallback
                callback));
-  MOCK_METHOD2(
+  MOCK_METHOD3(
       QueryAutocomplete,
       void(const base::string16& input,
+           bool prevent_inline_autocomplete,
            chrome::mojom::EmbeddedSearch::QueryAutocompleteCallback callback));
   MOCK_METHOD1(StopAutocomplete, void(bool));
   MOCK_METHOD1(BlocklistPromo, void(const std::string& promo_id));
@@ -1079,13 +1080,13 @@
   NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
   SetupMockDelegateAndPolicy();
   MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
-  EXPECT_CALL(*mock_delegate(), QueryAutocomplete(_, _)).Times(0);
+  EXPECT_CALL(*mock_delegate(), QueryAutocomplete(_, _, _)).Times(0);
   EXPECT_CALL(*policy, ShouldProcessQueryAutocomplete(_))
       .Times(1)
       .WillOnce(Return(false));
 
   GetSearchIPCRouter().QueryAutocomplete(
-      base::string16(),
+      base::string16(), false,
       base::Bind([](chrome::mojom::AutocompleteResultPtr result) {}));
 }
 
diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc
index 485fc08..ce5afaf0 100644
--- a/chrome/browser/ui/search/search_tab_helper.cc
+++ b/chrome/browser/ui/search/search_tab_helper.cc
@@ -585,6 +585,7 @@
 
 void SearchTabHelper::QueryAutocomplete(
     const base::string16& input,
+    bool prevent_inline_autocomplete,
     chrome::mojom::EmbeddedSearch::QueryAutocompleteCallback callback) {
   if (!search::DefaultSearchProviderIsGoogle(profile())) {
     std::move(callback).Run(chrome::mojom::AutocompleteResult::New(
@@ -619,6 +620,8 @@
       input, metrics::OmniboxEventProto::NTP_REALBOX,
       ChromeAutocompleteSchemeClassifier(profile()));
   autocomplete_input.set_from_omnibox_focus(input.empty());
+  autocomplete_input.set_prevent_inline_autocomplete(
+      prevent_inline_autocomplete);
   autocomplete_controller_->Start(autocomplete_input);
 }
 
diff --git a/chrome/browser/ui/search/search_tab_helper.h b/chrome/browser/ui/search/search_tab_helper.h
index 124a611..bb06782 100644
--- a/chrome/browser/ui/search/search_tab_helper.h
+++ b/chrome/browser/ui/search/search_tab_helper.h
@@ -140,6 +140,7 @@
   void OnConfirmThemeChanges() override;
   void QueryAutocomplete(
       const base::string16& input,
+      bool prevent_inline_autocomplete,
       chrome::mojom::EmbeddedSearch::QueryAutocompleteCallback callback)
       override;
   void DeleteAutocompleteMatch(
diff --git a/chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop_unittest.cc b/chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop_unittest.cc
index a09e54d..1c7a4a3 100644
--- a/chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop_unittest.cc
+++ b/chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop_unittest.cc
@@ -67,7 +67,9 @@
         },
         scan_succeeds);
 
-    safe_browsing::DeepScanningDialogDelegate::SetDMTokenForTesting("dm_token");
+    safe_browsing::DeepScanningDialogDelegate::SetDMTokenForTesting(
+        policy::BrowserDMTokenStorage::BrowserDMToken::CreateValidToken(
+            "dm_token"));
     safe_browsing::DeepScanningDialogDelegate::SetFactoryForTesting(
         base::BindRepeating(
             &safe_browsing::FakeDeepScanningDialogDelegate::Create,
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc
index e0e5eca..7e662c3f 100644
--- a/chrome/browser/ui/toolbar/app_menu_model.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/media/router/media_router_feature.h"
+#include "chrome/browser/media/router/media_router_metrics.h"
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -443,6 +444,10 @@
       if (!uma_action_recorded_)
         UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.Cast", delta);
       LogMenuAction(MENU_ACTION_CAST);
+      // TODO(takumif): Look into moving this metrics logging to a single
+      // location, like MediaRouterDialogController::ShowMediaRouterDialog().
+      media_router::MediaRouterMetrics::RecordMediaRouterDialogOrigin(
+          media_router::MediaRouterDialogOpenOrigin::APP_MENU);
       break;
 
     // Edit menu.
diff --git a/chrome/browser/ui/views/accelerator_table_unittest.cc b/chrome/browser/ui/views/accelerator_table_unittest.cc
index 8427440..0c26fbb 100644
--- a/chrome/browser/ui/views/accelerator_table_unittest.cc
+++ b/chrome/browser/ui/views/accelerator_table_unittest.cc
@@ -64,7 +64,8 @@
     // 321568.
     if (ash_entry.action == ash::WINDOW_MINIMIZE ||
         ash_entry.action == ash::SHOW_TASK_MANAGER ||
-        ash_entry.action == ash::OPEN_GET_HELP)
+        ash_entry.action == ash::OPEN_GET_HELP ||
+        ash_entry.action == ash::MINIMIZE_TOP_WINDOW_ON_BACK)
       continue;
 
     // The following actions are duplicated in both ash and browser accelerator
diff --git a/chrome/browser/ui/views/apps/app_pause_dialog_view.cc b/chrome/browser/ui/views/apps/app_pause_dialog_view.cc
new file mode 100644
index 0000000..88aafe5
--- /dev/null
+++ b/chrome/browser/ui/views/apps/app_pause_dialog_view.cc
@@ -0,0 +1,106 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/apps/app_pause_dialog_view.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/browser/ui/views/chrome_typography.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/constrained_window/constrained_window_views.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/image/image_skia_operations.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/box_layout.h"
+
+// static
+void apps::AppServiceProxy::CreatePauseDialog(
+    const std::string& app_name,
+    gfx::ImageSkia image,
+    const apps::PauseData& pause_data,
+    apps::AppServiceProxy::OnPauseDialogClosedCallback closed_callback) {
+  constrained_window::CreateBrowserModalDialogViews(
+      new AppPauseDialogView(app_name, image, pause_data,
+                             std::move(closed_callback)),
+      nullptr)
+      ->Show();
+}
+
+AppPauseDialogView::AppPauseDialogView(
+    const std::string& app_name,
+    gfx::ImageSkia image,
+    const apps::PauseData& pause_data,
+    apps::AppServiceProxy::OnPauseDialogClosedCallback closed_callback)
+    : BubbleDialogDelegateView(nullptr, views::BubbleBorder::NONE),
+      closed_callback_(std::move(closed_callback)) {
+  ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
+  SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kHorizontal,
+      provider->GetDialogInsetsForContentType(views::TEXT, views::TEXT),
+      provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_HORIZONTAL)));
+
+  auto* icon_view = AddChildView(std::make_unique<views::ImageView>());
+  icon_view->SetImage(image);
+
+  base::string16 pause_hours = l10n_util::GetPluralStringFUTF16(
+      IDS_APP_PAUSE_HEADING_HOURS, pause_data.hours);
+  base::string16 pause_minutes = l10n_util::GetPluralStringFUTF16(
+      IDS_APP_PAUSE_HEADING_MINUTES, pause_data.minutes);
+
+  base::string16 heading_text;
+  if (pause_data.hours != 0 && pause_data.minutes != 0) {
+    heading_text = l10n_util::GetStringFUTF16(
+        IDS_APP_PAUSE_HEADING_HOURS_AND_MINUTES, base::UTF8ToUTF16(app_name),
+        pause_hours, pause_minutes);
+  } else if (pause_data.hours != 0) {
+    heading_text =
+        l10n_util::GetStringFUTF16(IDS_APP_PAUSE_HEADING_HOURS_ONLY,
+                                   base::UTF8ToUTF16(app_name), pause_hours);
+  } else if (pause_data.minutes != 0) {
+    heading_text =
+        l10n_util::GetStringFUTF16(IDS_APP_PAUSE_HEADING_MINUTES_ONLY,
+                                   base::UTF8ToUTF16(app_name), pause_minutes);
+  }
+
+  auto* label = AddChildView(std::make_unique<views::Label>(heading_text));
+  label->SetMultiLine(true);
+  label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+}
+
+AppPauseDialogView::~AppPauseDialogView() = default;
+
+bool AppPauseDialogView::Accept() {
+  std::move(closed_callback_).Run();
+  return true;
+}
+
+gfx::Size AppPauseDialogView::CalculatePreferredSize() const {
+  const int default_width = views::LayoutProvider::Get()->GetDistanceMetric(
+                                DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH) -
+                            margins().width();
+  return gfx::Size(default_width, GetHeightForWidth(default_width));
+}
+
+int AppPauseDialogView::GetDialogButtons() const {
+  return ui::DIALOG_BUTTON_OK;
+}
+
+ui::ModalType AppPauseDialogView::GetModalType() const {
+  return ui::MODAL_TYPE_SYSTEM;
+}
+
+base::string16 AppPauseDialogView::GetWindowTitle() const {
+  return l10n_util::GetStringUTF16(IDS_APP_PAUSE_PROMPT_TITLE);
+}
+
+bool AppPauseDialogView::ShouldShowCloseButton() const {
+  return false;
+}
diff --git a/chrome/browser/ui/views/apps/app_pause_dialog_view.h b/chrome/browser/ui/views/apps/app_pause_dialog_view.h
new file mode 100644
index 0000000..724828f
--- /dev/null
+++ b/chrome/browser/ui/views/apps/app_pause_dialog_view.h
@@ -0,0 +1,41 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_APPS_APP_PAUSE_DIALOG_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_APPS_APP_PAUSE_DIALOG_VIEW_H_
+
+#include "base/macros.h"
+
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
+
+namespace gfx {
+class ImageSkia;
+}
+
+// The app pause dialog. Once the user clicks the 'OK' button, this class calls
+// the callback to notify AppService, which pauses the app.
+class AppPauseDialogView : public views::BubbleDialogDelegateView {
+ public:
+  AppPauseDialogView(
+      const std::string& app_name,
+      gfx::ImageSkia image,
+      const apps::PauseData& pause_data,
+      apps::AppServiceProxy::OnPauseDialogClosedCallback callback);
+  ~AppPauseDialogView() override;
+
+  // views::BubbleDialogDelegateView:
+  bool Accept() override;
+  gfx::Size CalculatePreferredSize() const override;
+  int GetDialogButtons() const override;
+  ui::ModalType GetModalType() const override;
+  base::string16 GetWindowTitle() const override;
+  bool ShouldShowCloseButton() const override;
+
+ private:
+  // Callback when the dialog closes after the user has clicked the OK.
+  apps::AppServiceProxy::OnPauseDialogClosedCallback closed_callback_;
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_APPS_APP_PAUSE_DIALOG_VIEW_H_
diff --git a/chrome/browser/ui/views/omnibox/omnibox_color_mixer.cc b/chrome/browser/ui/views/omnibox/omnibox_color_mixer.cc
index 2a06c9f9..623d1b6 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_color_mixer.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_color_mixer.cc
@@ -13,55 +13,47 @@
 #include "ui/gfx/color_utils.h"
 
 void AddOmniboxColorMixer(ui::ColorProvider* provider, bool high_contrast) {
-  ui::ColorMixer* mixer = provider->AddMixer();
+  ui::ColorMixer& mixer = provider->AddMixer();
   const float minimum_contrast =
       high_contrast ? 6.0f : color_utils::kMinimumReadableContrastRatio;
 
   // Omnibox background colors.
-  mixer->AddRecipe(kColorOmniboxBackground)
-      .AddTransform(ui::GetResultingPaintColor(
-          ui::FromTransformInput(), ui::FromInputColor(kColorToolbar)));
-  mixer->AddRecipe(kColorOmniboxBackgroundHovered)
-      .AddTransform(ui::BlendTowardMaxContrast(
-          ui::FromResultColor(kColorOmniboxBackground), 0x0A));
+  mixer[kColorOmniboxBackground].AddTransform(ui::GetResultingPaintColor(
+      ui::FromTransformInput(), ui::FromInputColor(kColorToolbar)));
+  mixer[kColorOmniboxBackgroundHovered].AddTransform(ui::BlendTowardMaxContrast(
+      ui::FromResultColor(kColorOmniboxBackground), 0x0A));
 
   // Omnibox text colors.
-  mixer->AddRecipe(kColorOmniboxText)
-      .AddTransform(ui::GetResultingPaintColor(
-          ui::FromTransformInput(),
-          ui::FromResultColor(kColorOmniboxBackground)));
+  mixer[kColorOmniboxText].AddTransform(ui::GetResultingPaintColor(
+      ui::FromTransformInput(), ui::FromResultColor(kColorOmniboxBackground)));
   {
-    auto& selected_text =
-        mixer->AddRecipe(kColorOmniboxResultsTextSelected)
-            .AddTransform(ui::FromResultColor(kColorOmniboxText));
+    auto& selected_text = mixer[kColorOmniboxResultsTextSelected].AddTransform(
+        ui::FromResultColor(kColorOmniboxText));
     if (high_contrast)
       selected_text.AddTransform(ui::ContrastInvert(ui::FromTransformInput()));
   }
-  mixer->AddRecipe(kColorOmniboxSelectedKeyword)
-      .AddTransform(ui::SelectBasedOnDarkInput(
-          ui::FromResultColor(kColorOmniboxBackground),
-          ui::FromColor(gfx::kGoogleGrey100),
-          ui::FromResultColor(kColorOmniboxResultsUrl)));
+  mixer[kColorOmniboxSelectedKeyword].AddTransform(
+      ui::SelectBasedOnDarkInput(ui::FromResultColor(kColorOmniboxBackground),
+                                 ui::FromColor(gfx::kGoogleGrey100),
+                                 ui::FromResultColor(kColorOmniboxResultsUrl)));
 
   // Bubble outline colors.
-  mixer->AddRecipe(kColorOmniboxBubbleOutline)
-      .AddTransform(ui::SelectBasedOnDarkInput(
-          ui::FromResultColor(kColorOmniboxBackground),
-          ui::FromColor(gfx::kGoogleGrey100),
-          ui::FromColor(SkColorSetA(gfx::kGoogleGrey900, 0x24))));
-  mixer->AddRecipe(kColorOmniboxBubbleOutlineExperimentalKeywordMode)
-      .AddTransform(ui::FromResultColor(kColorOmniboxSelectedKeyword));
+  mixer[kColorOmniboxBubbleOutline].AddTransform(ui::SelectBasedOnDarkInput(
+      ui::FromResultColor(kColorOmniboxBackground),
+      ui::FromColor(gfx::kGoogleGrey100),
+      ui::FromColor(SkColorSetA(gfx::kGoogleGrey900, 0x24))));
+  mixer[kColorOmniboxBubbleOutlineExperimentalKeywordMode].AddTransform(
+      ui::FromResultColor(kColorOmniboxSelectedKeyword));
 
   // Results background colors.
-  mixer->AddRecipe(kColorOmniboxResultsBackground)
-      .AddTransform(
-          ui::GetColorWithMaxContrast(ui::FromResultColor(kColorOmniboxText)));
-  mixer->AddRecipe(kColorOmniboxResultsBackgroundHovered)
-      .AddTransform(ui::BlendTowardMaxContrast(
+  mixer[kColorOmniboxResultsBackground].AddTransform(
+      ui::GetColorWithMaxContrast(ui::FromResultColor(kColorOmniboxText)));
+  mixer[kColorOmniboxResultsBackgroundHovered].AddTransform(
+      ui::BlendTowardMaxContrast(
           ui::FromResultColor(kColorOmniboxResultsBackground),
           gfx::kGoogleGreyAlpha200));
-  mixer->AddRecipe(kColorOmniboxResultsBackgroundSelected)
-      .AddTransform(ui::BlendTowardMaxContrast(
+  mixer[kColorOmniboxResultsBackgroundSelected].AddTransform(
+      ui::BlendTowardMaxContrast(
           ui::GetColorWithMaxContrast(
               ui::FromResultColor(kColorOmniboxResultsTextSelected)),
           gfx::kGoogleGreyAlpha300));
@@ -74,12 +66,11 @@
           ui::DeriveDefaultIconColor(ui::FromResultColor(text_id)),
           ui::FromResultColor(background_id), base::nullopt, minimum_contrast);
     };
-    mixer->AddRecipe(kColorOmniboxResultsIcon)
-        .AddTransform(
-            results_icon(kColorOmniboxText, kColorOmniboxResultsBackground));
-    mixer->AddRecipe(kColorOmniboxResultsIconSelected)
-        .AddTransform(results_icon(kColorOmniboxResultsTextSelected,
-                                   kColorOmniboxResultsBackgroundSelected));
+    mixer[kColorOmniboxResultsIcon].AddTransform(
+        results_icon(kColorOmniboxText, kColorOmniboxResultsBackground));
+    mixer[kColorOmniboxResultsIconSelected].AddTransform(
+        results_icon(kColorOmniboxResultsTextSelected,
+                     kColorOmniboxResultsBackgroundSelected));
   }
 
   // Dimmed text colors.
@@ -95,16 +86,14 @@
                                   base::nullopt, minimum_contrast),
           minimum_contrast);
     };
-    mixer->AddRecipe(kColorOmniboxResultsTextDimmed)
-        .AddTransform(blend_with_clamped_contrast(
-            kColorOmniboxText, kColorOmniboxResultsBackgroundHovered));
-    mixer->AddRecipe(kColorOmniboxResultsTextDimmedSelected)
-        .AddTransform(blend_with_clamped_contrast(
-            kColorOmniboxResultsTextSelected,
-            kColorOmniboxResultsBackgroundSelected));
-    mixer->AddRecipe(kColorOmniboxTextDimmed)
-        .AddTransform(blend_with_clamped_contrast(
-            kColorOmniboxText, kColorOmniboxBackgroundHovered));
+    mixer[kColorOmniboxResultsTextDimmed].AddTransform(
+        blend_with_clamped_contrast(kColorOmniboxText,
+                                    kColorOmniboxResultsBackgroundHovered));
+    mixer[kColorOmniboxResultsTextDimmedSelected].AddTransform(
+        blend_with_clamped_contrast(kColorOmniboxResultsTextSelected,
+                                    kColorOmniboxResultsBackgroundSelected));
+    mixer[kColorOmniboxTextDimmed].AddTransform(blend_with_clamped_contrast(
+        kColorOmniboxText, kColorOmniboxBackgroundHovered));
   }
 
   // Results URL colors.
@@ -117,10 +106,10 @@
                                      ui::FromColor(gfx::kGoogleBlue900)),
           minimum_contrast);
     };
-    mixer->AddRecipe(kColorOmniboxResultsUrl)
-        .AddTransform(url_color(kColorOmniboxResultsBackgroundHovered));
-    mixer->AddRecipe(kColorOmniboxResultsUrlSelected)
-        .AddTransform(url_color(kColorOmniboxResultsBackgroundSelected));
+    mixer[kColorOmniboxResultsUrl].AddTransform(
+        url_color(kColorOmniboxResultsBackgroundHovered));
+    mixer[kColorOmniboxResultsUrlSelected].AddTransform(
+        url_color(kColorOmniboxResultsBackgroundSelected));
   }
 
   // Security chip colors.
@@ -136,12 +125,11 @@
                   ui::FromResultColor(kColorOmniboxBackgroundHovered),
                   base::nullopt, minimum_contrast));
         };
-    mixer->AddRecipe(kColorOmniboxSecurityChipDangerous)
-        .AddTransform(security_chip_color(ui::FromColor(gfx::kGoogleRed600)));
-    mixer->AddRecipe(kColorOmniboxSecurityChipSecure)
-        .AddTransform(security_chip_color(ui::DeriveDefaultIconColor(
-            ui::FromResultColor(kColorOmniboxText))));
+    mixer[kColorOmniboxSecurityChipDangerous].AddTransform(
+        security_chip_color(ui::FromColor(gfx::kGoogleRed600)));
+    mixer[kColorOmniboxSecurityChipSecure].AddTransform(security_chip_color(
+        ui::DeriveDefaultIconColor(ui::FromResultColor(kColorOmniboxText))));
   }
-  mixer->AddRecipe(kColorOmniboxSecurityChipDefault)
-      .AddTransform(ui::FromResultColor(kColorOmniboxSecurityChipSecure));
+  mixer[kColorOmniboxSecurityChipDefault].AddTransform(
+      ui::FromResultColor(kColorOmniboxSecurityChipSecure));
 }
diff --git a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc
index ae29293..f8c67aa 100644
--- a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc
@@ -167,7 +167,10 @@
         break;
       case UIStatus::kEnabled:
         feature_list_.InitWithFeaturesAndParameters(
-            {{security_state::features::kSafetyTipUI, {}},
+            {{security_state::features::kSafetyTipUI,
+              {{"topsites", "false"},
+               {"editdistance", "false"},
+               {"editdistance_siteengagement", "false"}}},
              {features::kLookalikeUrlNavigationSuggestionsUI,
               {{"topsites", "true"}}}},
             {});
@@ -610,9 +613,11 @@
   const GURL kLookalikeUrl = GetURL("googlé.sk");
   SetEngagementScore(browser(), kLookalikeUrl, kLowEngagement);
   NavigateToURL(browser(), kLookalikeUrl, WindowOpenDisposition::CURRENT_TAB);
-  histograms.ExpectBucketCount(
-      kHistogramName, security_state::SafetyTipStatus::kLookalike,
-      ui_status() == UIStatus::kEnabledWithAllFeatures ? 1 : 0);
+
+  // Record metrics for lookalike domains unless explicitly disabled.
+  histograms.ExpectBucketCount(kHistogramName,
+                               security_state::SafetyTipStatus::kLookalike,
+                               ui_status() == UIStatus::kEnabled ? 0 : 1);
   histograms.ExpectTotalCount(kHistogramName, 3);
 }
 
diff --git a/chrome/browser/ui/views/tabs/tab_group_header.cc b/chrome/browser/ui/views/tabs/tab_group_header.cc
index f904721..36b3e54 100644
--- a/chrome/browser/ui/views/tabs/tab_group_header.cc
+++ b/chrome/browser/ui/views/tabs/tab_group_header.cc
@@ -155,7 +155,7 @@
     title_->SetVisible(false);
 
     constexpr int kEmptyChipSize = 14;
-    const int y = (height() - kEmptyChipSize) / 2;
+    const int y = (GetLayoutConstant(TAB_HEIGHT) - kEmptyChipSize) / 2;
 
     title_chip_->SetBounds(TabGroupUnderline::GetStrokeInset(), y,
                            kEmptyChipSize, kEmptyChipSize);
@@ -184,7 +184,8 @@
     const int text_vertical_inset = 1;
     const int text_horizontal_inset = corner_radius + text_vertical_inset;
 
-    const int y = (height() - text_height) / 2 - text_vertical_inset;
+    const int y =
+        (GetLayoutConstant(TAB_HEIGHT) - text_height) / 2 - text_vertical_inset;
 
     title_chip_->SetBounds(TabGroupUnderline::GetStrokeInset(), y,
                            text_width + 2 * text_horizontal_inset,
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
index e790fc4..93f7e29 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
@@ -97,6 +97,16 @@
 #endif
 }
 
+std::unique_ptr<views::View> CreateAlertView(const TabAlertState& state) {
+  auto alert_state_label = std::make_unique<views::Label>(
+      base::string16(), CONTEXT_BODY_TEXT_LARGE, views::style::STYLE_PRIMARY);
+  alert_state_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  alert_state_label->SetMultiLine(true);
+  alert_state_label->SetVisible(true);
+  alert_state_label->SetText(chrome::GetTabAlertStateText(state));
+  return alert_state_label;
+}
+
 }  // namespace
 
 // static
@@ -635,19 +645,6 @@
   return ui::DIALOG_BUTTON_NONE;
 }
 
-std::unique_ptr<views::View> TabHoverCardBubbleView::CreateFootnoteView() {
-  if (!alert_state_.has_value())
-    return nullptr;
-
-  auto alert_state_label = std::make_unique<views::Label>(
-      base::string16(), CONTEXT_BODY_TEXT_LARGE, views::style::STYLE_PRIMARY);
-  alert_state_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  alert_state_label->SetMultiLine(true);
-  alert_state_label->SetVisible(true);
-  alert_state_label->SetText(chrome::GetTabAlertStateText(*alert_state_));
-  return alert_state_label;
-}
-
 void TabHoverCardBubbleView::Layout() {
   View::Layout();
   title_fade_label_->SetBoundsRect(title_label_->bounds());
@@ -727,8 +724,10 @@
   title_fade_label_->SetText(title_label_->GetText());
   title_label_->SetText(title);
 
-  if (alert_state_ != old_alert_state)
-    GetBubbleFrameView()->SetFootnoteView(CreateFootnoteView());
+  if (alert_state_ != old_alert_state) {
+    GetBubbleFrameView()->SetFootnoteView(
+        alert_state_.has_value() ? CreateAlertView(*alert_state_) : nullptr);
+  }
 
   domain_fade_label_->SetText(domain_label_->GetText());
   domain_label_->SetText(domain);
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h
index 281ceb0..bd1e8eaa 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h
@@ -54,7 +54,6 @@
   void OnWidgetVisibilityChanged(views::Widget* widget, bool visible) override;
   ax::mojom::Role GetAccessibleWindowRole() override;
   int GetDialogButtons() const override;
-  std::unique_ptr<views::View> CreateFootnoteView() override;
   void Layout() override;
 
   void set_last_mouse_exit_timestamp(
diff --git a/chrome/browser/ui/views/tabs/tab_style_views.cc b/chrome/browser/ui/views/tabs/tab_style_views.cc
index 350c1d5..fbe88956 100644
--- a/chrome/browser/ui/views/tabs/tab_style_views.cc
+++ b/chrome/browser/ui/views/tabs/tab_style_views.cc
@@ -75,6 +75,24 @@
   // effects and consider only the current tab's state.
   SeparatorOpacities GetSeparatorOpacities(bool for_layout) const;
 
+  // Returns a single separator's opacity based on whether it is the
+  // logically |leading| separator. |for_layout| has the same meaning as in
+  // GetSeparatorOpacities().
+  float GetSeparatorOpacity(bool for_layout, bool leading) const;
+
+  // Helper that returns an interpolated opacity if the tab or its neighbor
+  // |other_tab| is mid-hover-animation. Used in almost all cases when a
+  // separator is shown, since hovering is independent of tab state.
+  // |for_layout| has the same meaning as in GetSeparatorOpacities().
+  float GetHoverInterpolatedSeparatorOpacity(bool for_layout,
+                                             const Tab* other_tab) const;
+
+  // Helper that returns an interpolated opacity if the tab is
+  // mid-bounds-animation. Used only for the first and last tabs, since those
+  // are the primary cases where separator opacity is likely to change during
+  // a bounds animation.
+  float GetBoundsInterpolatedSeparatorOpacity() const;
+
   // Returns whether we shoould extend the hit test region for Fitts' Law.
   bool ShouldExtendHitTest() const;
 
@@ -536,73 +554,22 @@
 
 TabStyle::SeparatorOpacities GM2TabStyle::GetSeparatorOpacities(
     bool for_layout) const {
-  // Something should visually separate tabs from each other and any adjacent
-  // new tab button.  Normally, active and hovered tabs draw distinct shapes
-  // (via different background colors) and thus need no separators, while
-  // background tabs need separators between them.
-  float leading_opacity, trailing_opacity;
-  if (tab_->IsActive()) {
-    leading_opacity = trailing_opacity = 0;
-  } else {
-    const Tab* subsequent_tab = tab_->controller()->GetAdjacentTab(tab_, 1);
-    const Tab* previous_tab = tab_->controller()->GetAdjacentTab(tab_, -1);
+  // Adjacent slots should be visually separated from each other. This can be
+  // achieved in multiple ways:
+  //   - Contrasting background colors for tabs, due to:
+  //       - Active state
+  //       - Selected state
+  //       - Hovered state
+  //       - Theming (affected by all the above, plus the neutral state)
+  //   - Manually painting a separator.
+  // The separator should be the last resort, if none of the above states
+  // apply. It's also needed if multiple adjacent views are selected, in which
+  // case the uniform selected color does not provide enough contrast.
+  // In addition, separators should smoothly fade in and out between states,
+  // particularly during the hover animation.
 
-    // Fade out the intervening separator while this tab or an adjacent tab is
-    // hovered, which prevents sudden opacity changes when scrubbing the mouse
-    // across the tabstrip. If that adjacent tab is active, don't consider its
-    // hover animation value, otherwise the separator on this tab will disappear
-    // while that tab is being dragged.
-    auto adjacent_hover_value = [for_layout](const Tab* tab) {
-      if (for_layout || !tab || tab->IsActive())
-        return 0.f;
-      auto* tab_style = static_cast<const GM2TabStyle*>(tab->tab_style());
-      return float{tab_style->GetHoverAnimationValue()};
-    };
-    const float hover_value = GetHoverAnimationValue();
-    trailing_opacity =
-        1.f - std::max(hover_value, adjacent_hover_value(subsequent_tab));
-    leading_opacity =
-        1.f - std::max(hover_value, adjacent_hover_value(previous_tab));
-
-    if (tab_->IsSelected()) {
-      // Since this tab is selected, its shape will be visible against adjacent
-      // unselected tabs, so remove the separator in those cases.
-      if (previous_tab && !previous_tab->IsSelected())
-        leading_opacity = 0;
-      if (subsequent_tab && !subsequent_tab->IsSelected())
-        trailing_opacity = 0;
-    } else if (tab_->controller()->HasVisibleBackgroundTabShapes()) {
-      // Since this tab is unselected, adjacent selected tabs will normally
-      // paint atop it, covering the separator.  But if the user drags those
-      // selected tabs away, the exposed region looks like the window frame; and
-      // since background tab shapes are visible, there should be no separator.
-      // TODO(pkasting): https://crbug.com/876599  When a tab is animating
-      // into this gap, we should adjust its separator opacities as well.
-      if (previous_tab && previous_tab->IsSelected())
-        leading_opacity = 0;
-      if (subsequent_tab && subsequent_tab->IsSelected())
-        trailing_opacity = 0;
-    }
-  }
-
-  // For the first or (when tab shapes are visible) last tab in the strip, fade
-  // the leading or trailing separator based on how close to the target bounds
-  // this tab is.  In the steady state, this hides the leading separator; it
-  // fades out the separators as tabs animate into these positions, after they
-  // pass by the other tabs; and it snaps the separators to full visibility
-  // immediately when animating away from these positions, which seems
-  // desirable.
-  const gfx::Rect target_bounds =
-      tab_->controller()->GetTabAnimationTargetBounds(tab_);
-  const int tab_width = std::max(tab_->width(), target_bounds.width());
-  const float target_opacity =
-      float{std::min(std::abs(tab_->x() - target_bounds.x()), tab_width)} /
-      tab_width;
-  if (tab_->controller()->IsFirstVisibleTab(tab_))
-    leading_opacity = target_opacity;
-  if (tab_->controller()->IsLastVisibleTab(tab_) &&
-      tab_->controller()->HasVisibleBackgroundTabShapes())
-    trailing_opacity = target_opacity;
+  float leading_opacity = GetSeparatorOpacity(for_layout, true);
+  float trailing_opacity = GetSeparatorOpacity(for_layout, false);
 
   // Return the opacities in physical order, rather than logical.
   if (base::i18n::IsRTL())
@@ -610,6 +577,115 @@
   return {leading_opacity, trailing_opacity};
 }
 
+float GM2TabStyle::GetSeparatorOpacity(bool for_layout, bool leading) const {
+  // If the current tab is active, never show the separator.
+  if (tab_->IsActive())
+    return 0.0f;
+
+  const Tab* adjacent_tab =
+      tab_->controller()->GetAdjacentTab(tab_, leading ? -1 : 1);
+
+  const Tab* left_tab = leading ? adjacent_tab : tab_;
+  const Tab* right_tab = leading ? tab_ : adjacent_tab;
+  const bool adjacent_to_header =
+      right_tab && right_tab->group().has_value() &&
+      (!left_tab || left_tab->group() != right_tab->group());
+
+  // If the current tab is selected, default to hiding the separator. Only show
+  // the separator if it's adjacent to other selected tabs.
+  if (tab_->IsSelected()) {
+    // If the adjacent view is actually a group header, hide the separator since
+    // group headers currently cannot be selected.
+    // TODO(crbug.com/1017822): Update this if headers become selectable.
+    if (adjacent_to_header)
+      return 0.0f;
+
+    if (adjacent_tab && adjacent_tab->IsSelected())
+      return GetHoverInterpolatedSeparatorOpacity(for_layout, adjacent_tab);
+
+    return 0.0f;
+  }
+
+  // Otherwise, default to showing the separator, respecting the hover
+  // animation. Only hide the separator if it's in the first slot, or in
+  // certain cases if the tab has a visible background (see below).
+
+  // If the adjacent view is actually a group header, show the separator since
+  // the group header takes up a slot.
+  // TODO(crbug.com/1017822): Update this if headers become selectable.
+  if (adjacent_to_header)
+    return GetHoverInterpolatedSeparatorOpacity(for_layout, nullptr);
+
+  // If the tab has a visible background even when not selected or active, there
+  // are additional cases where the separators can be hidden.
+  if (tab_->controller()->HasVisibleBackgroundTabShapes()) {
+    // If the tab with a visible background is in an end slot, hide the
+    // separator because it doesn't need additional contrast with the tab strip
+    // or the new tab button. This value isn't interpolated like the others
+    // because the separator was likely already hidden: if it's animating into
+    // an end slot, then the tab was probably next to a selected dragging tab
+    // (see the condition below).
+    if (!adjacent_tab)
+      return 0.0f;
+
+    // If the adjacent tab is selected, any separator on the current tab will be
+    // "hidden" beneath the adjacent tab's background. Normally tabs will still
+    // have a separator, in case the adjacent tab is dragged away and it reveals
+    // an empty gap. However, tabs with visible backgrounds already have
+    // sufficient contrast against the empty gap, so this contingency isn't
+    // needed. Therefore, the separator is hidden only for tabs with visible
+    // backgrounds.
+    // TODO(crbug.com/876599): This value should be interpolated because the
+    // separator may be going from shown (the default) to hidden (when animating
+    // past an empty gap like this). This should behave similarly to
+    // GetBoundsInterpolatedSeparatorOpacity(), but not just for the end slots.
+    if (adjacent_tab->IsSelected())
+      return 0.0f;
+  }
+
+  // If the tab does not have a visible background and is in the first slot,
+  // make sure the opacity is interpolated correctly when it animates into
+  // position, since the separator is likely going from shown (the default) to
+  // hidden (in the first slot). See GetBoundsInterpolatedSeparatorOpacity().
+  if (!adjacent_tab && leading)
+    return GetBoundsInterpolatedSeparatorOpacity();
+
+  return GetHoverInterpolatedSeparatorOpacity(for_layout, adjacent_tab);
+}
+
+float GM2TabStyle::GetHoverInterpolatedSeparatorOpacity(
+    bool for_layout,
+    const Tab* other_tab) const {
+  // Fade out the intervening separator while this tab or an adjacent tab is
+  // hovered, which prevents sudden opacity changes when scrubbing the mouse
+  // across the tabstrip. If that adjacent tab is active, don't consider its
+  // hover animation value, otherwise the separator on this tab will disappear
+  // while that tab is being dragged.
+  auto adjacent_hover_value = [for_layout](const Tab* other_tab) {
+    if (for_layout || !other_tab || other_tab->IsActive())
+      return 0.0f;
+    auto* tab_style = static_cast<const GM2TabStyle*>(other_tab->tab_style());
+    return float{tab_style->GetHoverAnimationValue()};
+  };
+  const float hover_value = GetHoverAnimationValue();
+  return 1.0f - std::max(hover_value, adjacent_hover_value(other_tab));
+}
+
+float GM2TabStyle::GetBoundsInterpolatedSeparatorOpacity() const {
+  // When the bounds of a tab are animating, fade the separator based on how
+  // close to the target bounds this tab is. This function is only called
+  // when the target bounds are an end slot. That means this function will fade
+  // the separators in or out as a tab animtes into the end slot, but it will
+  // not be called if the tab is animating out of the end slot. In that case,
+  // the separator will snap to full opacity immediately, which is visually
+  // consistent with other bounds animations.
+  const gfx::Rect target_bounds =
+      tab_->controller()->GetTabAnimationTargetBounds(tab_);
+  const int tab_width = std::max(tab_->width(), target_bounds.width());
+  return float{std::min(std::abs(tab_->x() - target_bounds.x()), tab_width)} /
+         tab_width;
+}
+
 bool GM2TabStyle::ShouldExtendHitTest() const {
   const views::Widget* widget = tab_->GetWidget();
   return widget->IsMaximized() || widget->IsFullscreen();
diff --git a/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc b/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc
index 1c5b605..ed3cdc84 100644
--- a/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc
@@ -19,7 +19,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
 #include "base/values.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/chromeos/camera_presence_notifier.h"
 #include "chrome/browser/chromeos/login/users/avatar/user_image_manager.h"
@@ -39,7 +38,6 @@
 #include "components/user_manager/user_image/user_image.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_service.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/common/url_constants.h"
 #include "net/base/data_url.h"
diff --git a/chrome/browser/ui/webui/settings/profile_info_handler.cc b/chrome/browser/ui/webui/settings/profile_info_handler.cc
index 6e0af7ba..139f203 100644
--- a/chrome/browser/ui/webui/settings/profile_info_handler.cc
+++ b/chrome/browser/ui/webui/settings/profile_info_handler.cc
@@ -14,12 +14,10 @@
 #include "ui/base/webui/web_ui_util.h"
 
 #if defined(OS_CHROMEOS)
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/ui/webui/chromeos/user_image_source.h"
 #include "components/account_id/account_id.h"
 #include "components/user_manager/user_manager.h"
-#include "content/public/browser/notification_service.h"
 #else
 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
 #include "chrome/browser/profiles/profile_statistics.h"
diff --git a/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc b/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc
index ce95eecb..83923229 100644
--- a/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc
+++ b/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc
@@ -14,7 +14,6 @@
 #include "base/value_conversions.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/gaia_info_update_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_attributes_entry.h"
@@ -31,7 +30,6 @@
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_service.h"
 #include "content/public/browser/web_ui.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc
index f31d12ee..95ccbe20 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -17,7 +17,6 @@
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/content_settings/web_site_settings_uma_util.h"
 #include "chrome/browser/engagement/site_engagement_service.h"
@@ -28,7 +27,6 @@
 #include "chrome/browser/permissions/permission_manager.h"
 #include "chrome/browser/permissions/permission_uma_util.h"
 #include "chrome/browser/permissions/permission_util.h"
-#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/serial/serial_chooser_context.h"
 #include "chrome/browser/serial/serial_chooser_context_factory.h"
 #include "chrome/browser/ui/browser.h"
@@ -50,7 +48,6 @@
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_service.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/common/origin_util.h"
@@ -338,9 +335,7 @@
 
 SiteSettingsHandler::SiteSettingsHandler(Profile* profile,
                                          web_app::AppRegistrar& app_registrar)
-    : profile_(profile),
-      app_registrar_(app_registrar),
-      pref_change_registrar_(nullptr) {}
+    : profile_(profile), app_registrar_(app_registrar) {}
 
 SiteSettingsHandler::~SiteSettingsHandler() {
   if (cookies_tree_model_)
@@ -452,11 +447,6 @@
   if (profile_->HasOffTheRecordProfile())
     ObserveSourcesForProfile(profile_->GetOffTheRecordProfile());
 
-  notification_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED,
-                              content::NotificationService::AllSources());
-  notification_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
-                              content::NotificationService::AllSources());
-
   // Here we only subscribe to the HostZoomMap for the default storage partition
   // since we don't allow the user to manage the zoom levels for apps.
   // We're only interested in zoom-levels that are persisted, since the user
@@ -487,12 +477,12 @@
 void SiteSettingsHandler::OnJavascriptDisallowed() {
   observer_.RemoveAll();
   chooser_observer_.RemoveAll();
-  notification_registrar_.RemoveAll();
   host_zoom_map_subscription_.reset();
   pref_change_registrar_->Remove(prefs::kBlockAutoplayEnabled);
 #if defined(OS_CHROMEOS)
   pref_change_registrar_->Remove(prefs::kEnableDRM);
 #endif
+  observed_profiles_.RemoveAll();
 }
 
 void SiteSettingsHandler::OnGetUsageInfo() {
@@ -558,31 +548,16 @@
   }
 }
 
-void SiteSettingsHandler::Observe(int type,
-                                  const content::NotificationSource& source,
-                                  const content::NotificationDetails& details) {
-  switch (type) {
-    case chrome::NOTIFICATION_PROFILE_DESTROYED: {
-      Profile* profile = content::Source<Profile>(source).ptr();
-      if (!profile_->IsSameProfile(profile))
-        break;
-      SendIncognitoStatus(profile, /*was_destroyed=*/true);
+void SiteSettingsHandler::OnOffTheRecordProfileCreated(
+    Profile* off_the_record) {
+  FireWebUIListener("onIncognitoStatusChanged", base::Value(true));
+  ObserveSourcesForProfile(off_the_record);
+}
 
-      if (profile->IsOffTheRecord())
-        StopObservingSourcesForProfile(profile);
-      break;
-    }
-
-    case chrome::NOTIFICATION_PROFILE_CREATED: {
-      Profile* profile = content::Source<Profile>(source).ptr();
-      if (!profile_->IsSameProfile(profile))
-        break;
-      SendIncognitoStatus(profile, /*was_destroyed=*/false);
-
-      ObserveSourcesForProfile(profile);
-      break;
-    }
-  }
+void SiteSettingsHandler::OnProfileWillBeDestroyed(Profile* profile) {
+  if (profile->IsOffTheRecord())
+    FireWebUIListener("onIncognitoStatusChanged", base::Value(false));
+  StopObservingSourcesForProfile(profile);
 }
 
 void SiteSettingsHandler::OnChooserObjectPermissionChanged(
@@ -1207,22 +1182,8 @@
 void SiteSettingsHandler::HandleUpdateIncognitoStatus(
     const base::ListValue* args) {
   AllowJavascript();
-  SendIncognitoStatus(profile_, /*was_destroyed=*/false);
-}
-
-void SiteSettingsHandler::SendIncognitoStatus(Profile* profile,
-                                              bool was_destroyed) {
-  if (!IsJavascriptAllowed())
-    return;
-
-  // When an incognito profile is destroyed, it sends out the destruction
-  // message before destroying, so HasOffTheRecordProfile for profile_ won't
-  // return false until after the profile actually been destroyed.
-  bool incognito_enabled =
-      profile_->HasOffTheRecordProfile() &&
-      !(was_destroyed && profile == profile_->GetOffTheRecordProfile());
-
-  FireWebUIListener("onIncognitoStatusChanged", base::Value(incognito_enabled));
+  FireWebUIListener("onIncognitoStatusChanged",
+                    base::Value(profile_->HasOffTheRecordProfile()));
 }
 
 void SiteSettingsHandler::HandleFetchZoomLevels(const base::ListValue* args) {
@@ -1390,6 +1351,8 @@
   auto* serial_context = SerialChooserContextFactory::GetForProfile(profile);
   if (!chooser_observer_.IsObserving(serial_context))
     chooser_observer_.Add(serial_context);
+
+  observed_profiles_.Add(profile);
 }
 
 void SiteSettingsHandler::StopObservingSourcesForProfile(Profile* profile) {
@@ -1404,6 +1367,8 @@
   auto* serial_context = SerialChooserContextFactory::GetForProfile(profile);
   if (chooser_observer_.IsObserving(serial_context))
     chooser_observer_.Remove(serial_context);
+
+  observed_profiles_.Remove(profile);
 }
 
 void SiteSettingsHandler::TreeNodesAdded(ui::TreeModel* model,
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.h b/chrome/browser/ui/webui/settings/site_settings_handler.h
index 9e61c989..b60de36 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.h
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.h
@@ -14,18 +14,16 @@
 #include "base/scoped_observer.h"
 #include "chrome/browser/browsing_data/cookies_tree_model.h"
 #include "chrome/browser/permissions/chooser_context_base.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_observer.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "components/content_settings/core/browser/content_settings_observer.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "content/public/browser/host_zoom_map.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
 #include "ppapi/buildflags/buildflags.h"
 
-class Profile;
-
 class PrefChangeRegistrar;
 
 namespace base {
@@ -37,7 +35,7 @@
 // Chrome "ContentSettings" settings page UI handler.
 class SiteSettingsHandler : public SettingsPageUIHandler,
                             public content_settings::Observer,
-                            public content::NotificationObserver,
+                            public ProfileObserver,
                             public ChooserContextBase::PermissionObserver,
                             public CookiesTreeModel::Observer {
  public:
@@ -79,10 +77,9 @@
                                ContentSettingsType content_type,
                                const std::string& resource_identifier) override;
 
-  // content::NotificationObserver:
-  void Observe(int type,
-               const content::NotificationSource& source,
-               const content::NotificationDetails& details) override;
+  // ProfileObserver:
+  void OnOffTheRecordProfileCreated(Profile* off_the_record) override;
+  void OnProfileWillBeDestroyed(Profile* profile) override;
 
   // ChooserContextBase::PermissionObserver implementation:
   void OnChooserObjectPermissionChanged(
@@ -246,7 +243,7 @@
   Profile* profile_;
   web_app::AppRegistrar& app_registrar_;
 
-  content::NotificationRegistrar notification_registrar_;
+  ScopedObserver<Profile, ProfileObserver> observed_profiles_{this};
 
   // Keeps track of events related to zooming.
   std::unique_ptr<content::HostZoomMap::Subscription>
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
index 74ee718..e3aa490 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
@@ -19,7 +19,6 @@
 #include "chrome/browser/browsing_data/browsing_data_flash_lso_helper.h"
 #include "chrome/browser/browsing_data/mock_browsing_data_cookie_helper.h"
 #include "chrome/browser/browsing_data/mock_browsing_data_local_storage_helper.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/test_extension_system.h"
@@ -48,7 +47,6 @@
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "content/public/browser/navigation_controller.h"
-#include "content/public/browser/notification_service.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_web_ui.h"
@@ -396,10 +394,6 @@
   }
 
   virtual void DestroyIncognitoProfile() {
-    content::NotificationService::current()->Notify(
-        chrome::NOTIFICATION_PROFILE_DESTROYED,
-        content::Source<Profile>(static_cast<Profile*>(incognito_profile_)),
-        content::NotificationService::NoDetails());
     profile_.SetOffTheRecordProfile(nullptr);
     ASSERT_FALSE(profile_.HasOffTheRecordProfile());
     incognito_profile_ = nullptr;
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
index 76043fc..5c55d08 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
@@ -472,6 +472,8 @@
   static constexpr LocalizedString kStrings[] = {
       {"tabListTitle", IDS_ACCNAME_TAB_LIST},
       {"closeTab", IDS_ACCNAME_CLOSE},
+      {"defaultTabTitle", IDS_DEFAULT_TAB_TITLE},
+      {"loadingTab", IDS_TAB_LOADING_TITLE},
       {"tabCrashed", IDS_TAB_AX_LABEL_CRASHED_FORMAT},
       {"tabNetworkError", IDS_TAB_AX_LABEL_NETWORK_ERROR_FORMAT},
       {"audioPlaying", IDS_TAB_AX_LABEL_AUDIO_PLAYING_FORMAT},
diff --git a/chrome/chrome_cleaner/constants/chrome_cleaner_switches.cc b/chrome/chrome_cleaner/constants/chrome_cleaner_switches.cc
index be2b268..bfd5288 100644
--- a/chrome/chrome_cleaner/constants/chrome_cleaner_switches.cc
+++ b/chrome/chrome_cleaner/constants/chrome_cleaner_switches.cc
@@ -78,6 +78,10 @@
 // Specify the time to wait between logs upload retries, in minutes.
 const char kLogUploadRetryIntervalSwitch[] = "logs-upload-retry-interval";
 
+// The Mojo pipe token for IPC communication between the Software Reporter and
+// Chrome. Dropped in M80.
+const char kChromeMojoPipeTokenSwitch[] = "chrome-mojo-pipe-token";
+
 // Prevent the crash client from uploading crash reports.
 const char kNoCrashUploadSwitch[] = "no-crash-upload";
 
diff --git a/chrome/chrome_cleaner/constants/chrome_cleaner_switches.h b/chrome/chrome_cleaner/constants/chrome_cleaner_switches.h
index 644c626..48096c9 100644
--- a/chrome/chrome_cleaner/constants/chrome_cleaner_switches.h
+++ b/chrome/chrome_cleaner/constants/chrome_cleaner_switches.h
@@ -54,6 +54,13 @@
 extern const char kRunWithoutSandboxForTestingSwitch[];
 #endif
 
+// Deprecated switches that were set by older Chrome versions.
+// These must still be handled until we drop support for those versions.
+
+// The Mojo pipe token for IPC communication between the Software Reporter and
+// Chrome. Dropped in M80.
+extern const char kChromeMojoPipeTokenSwitch[];
+
 }  // namespace chrome_cleaner
 
 #endif  // CHROME_CHROME_CLEANER_CONSTANTS_CHROME_CLEANER_SWITCHES_H_
diff --git a/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface_unittest.cc b/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface_unittest.cc
index 8e2c1ede..494d513 100644
--- a/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface_unittest.cc
+++ b/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface_unittest.cc
@@ -327,6 +327,49 @@
   EXPECT_FALSE(base::PathExists(file_path));
 }
 
+TEST_F(CleanerSandboxInterfaceDeleteFileTest, QuotedPath) {
+  base::ScopedTempDir temp;
+  ASSERT_TRUE(temp.CreateUniqueTempDir());
+  base::FilePath file_path = temp.GetPath().Append(L"temp_file.exe");
+
+  ASSERT_TRUE(chrome_cleaner::CreateFileInFolder(
+      file_path.DirName(), file_path.BaseName().value().c_str()));
+
+  const base::FilePath quoted_path(L"\"" + file_path.value() + L"\"");
+
+  // RemoveNow should reject the file name because it starts with an invalid
+  // character. This needs to match the behaviour of SandboxOpenFileReadOnly,
+  // which is tested in ScannerSandboxInterface_OpenReadOnlyFile.BasicFile,
+  // since the same path could be passed to both.
+  chrome_cleaner::VerifyRemoveNowFailure(quoted_path, file_remover_.get());
+  EXPECT_TRUE(base::PathExists(file_path));
+}
+
+TEST_F(CleanerSandboxInterfaceDeleteFileTest, QuotedFilename) {
+  base::ScopedTempDir temp;
+  ASSERT_TRUE(temp.CreateUniqueTempDir());
+  base::FilePath file_path = temp.GetPath().Append(L"temp_file.exe");
+
+  ASSERT_TRUE(chrome_cleaner::CreateFileInFolder(
+      file_path.DirName(), file_path.BaseName().value().c_str()));
+
+  const base::FilePath quoted_path =
+      temp.GetPath().Append(L"\"temp_file.exe\"");
+
+  // RemoveNow should return true because the file name is valid, but refers to
+  // a file that already doesn't exist. The important thing is that the quotes
+  // aren't interpreted, which would cause '"temp_file.exe"' and
+  // 'temp_file.exe' to refer to the same thing.
+  //
+  // This needs to match the behaviour of SandboxOpenFileReadOnly, which is
+  // tested in ScannerSandboxInterface_OpenReadOnlyFile.BasicFile, since the
+  // same path could be passed to both. It would also be ok if both RemoveNow
+  // and OpenFileReadOnly interpreted the quotes, as long as their behaviour
+  // matches.
+  chrome_cleaner::VerifyRemoveNowSuccess(quoted_path, file_remover_.get());
+  EXPECT_TRUE(base::PathExists(file_path));
+}
+
 TEST_F(CleanerSandboxInterfaceDeleteFileTest, DeleteAlternativeStream) {
   base::ScopedTempDir temp;
   ASSERT_TRUE(temp.CreateUniqueTempDir());
diff --git a/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface_unittest.cc b/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface_unittest.cc
index b706537..156d7df 100644
--- a/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface_unittest.cc
+++ b/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface_unittest.cc
@@ -607,6 +607,24 @@
   handle = SandboxOpenReadOnlyFile(base::FilePath(path_with_space),
                                    FILE_ATTRIBUTE_NORMAL);
   EXPECT_TRUE(handle.IsValid());
+
+  // Make sure quotes aren't interpreted. The same path might be passed to
+  // SandboxDeleteFile, which doesn't interpret quotes.
+  const base::FilePath quoted_path(L"\"" + file_path.value() + L"\"");
+  handle = SandboxOpenReadOnlyFile(quoted_path, FILE_ATTRIBUTE_NORMAL);
+  EXPECT_FALSE(handle.IsValid())
+      << "SandboxOpenReadOnlyFile is interpreting quotes around path names; "
+         "this will cause problems if the same path is passed to DeleteFile "
+         "(see CleanerSandboxInterfaceDeleteFileTest.QuotedPath)";
+
+  const base::FilePath partly_quoted_path =
+      temp.GetPath().Append(L"\"temp_file.exe\"");
+  handle = SandboxOpenReadOnlyFile(partly_quoted_path, FILE_ATTRIBUTE_NORMAL);
+  EXPECT_FALSE(handle.IsValid())
+      << "SandboxOpenReadOnlyFile is interpreting quotes around path "
+         "components; this will cause problems if the same path is passed to "
+         "DeleteFile (see "
+         "CleanerSandboxInterfaceDeleteFileTest.QuotedFilename)";
 }
 
 TEST_F(ScannerSandboxInterface_OpenReadOnlyFile, NoFile) {
diff --git a/chrome/chrome_cleaner/ipc/BUILD.gn b/chrome/chrome_cleaner/ipc/BUILD.gn
index 83c6e79..be33dde 100644
--- a/chrome/chrome_cleaner/ipc/BUILD.gn
+++ b/chrome/chrome_cleaner/ipc/BUILD.gn
@@ -31,7 +31,7 @@
   deps = [
     ":mojo_task_runner",
     "//base",
-    "//components/chrome_cleaner/public/interfaces",
+    "//chrome/chrome_cleaner/mojom:chrome_prompt_interface",
     "//mojo/public/cpp/platform",
     "//mojo/public/cpp/system",
   ]
@@ -106,11 +106,11 @@
     "//base/test:test_support",
     "//chrome/chrome_cleaner:buildflags",
     "//chrome/chrome_cleaner/logging:common",
+    "//chrome/chrome_cleaner/mojom:chrome_prompt_interface",
     "//chrome/chrome_cleaner/mojom:mojo_sandbox_hooks_test_interface",
     "//chrome/chrome_cleaner/os:common_os",
     "//chrome/chrome_cleaner/test:test_util",
     "//components/chrome_cleaner/public/constants",
-    "//components/chrome_cleaner/public/interfaces",
     "//components/chrome_cleaner/public/proto",
     "//components/chrome_cleaner/public/proto:test_only_proto",
     "//components/chrome_cleaner/test:test_name_helper",
diff --git a/chrome/chrome_cleaner/ipc/mojo_chrome_prompt_ipc.h b/chrome/chrome_cleaner/ipc/mojo_chrome_prompt_ipc.h
index 36d7e90..996e1e3 100644
--- a/chrome/chrome_cleaner/ipc/mojo_chrome_prompt_ipc.h
+++ b/chrome/chrome_cleaner/ipc/mojo_chrome_prompt_ipc.h
@@ -14,7 +14,7 @@
 #include "base/strings/string16.h"
 #include "chrome/chrome_cleaner/ipc/chrome_prompt_ipc.h"
 #include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
-#include "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom.h"
+#include "chrome/chrome_cleaner/mojom/chrome_prompt.mojom.h"
 
 namespace chrome_cleaner {
 
diff --git a/chrome/chrome_cleaner/ipc/mojo_chrome_prompt_ipc_unittest.cc b/chrome/chrome_cleaner/ipc/mojo_chrome_prompt_ipc_unittest.cc
index 0d503b45..e4a9a1a 100644
--- a/chrome/chrome_cleaner/ipc/mojo_chrome_prompt_ipc_unittest.cc
+++ b/chrome/chrome_cleaner/ipc/mojo_chrome_prompt_ipc_unittest.cc
@@ -18,8 +18,8 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/chrome_cleaner/ipc/ipc_test_util.h"
 #include "chrome/chrome_cleaner/logging/scoped_logging.h"
+#include "chrome/chrome_cleaner/mojom/chrome_prompt.mojom.h"
 #include "chrome/chrome_cleaner/test/test_util.h"
-#include "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom.h"
 #include "components/chrome_cleaner/public/proto/chrome_prompt.pb.h"
 #include "components/chrome_cleaner/test/test_name_helper.h"
 #include "mojo/public/cpp/bindings/binding.h"
diff --git a/chrome/chrome_cleaner/mojom/BUILD.gn b/chrome/chrome_cleaner/mojom/BUILD.gn
index 96157e68..680e1ed 100644
--- a/chrome/chrome_cleaner/mojom/BUILD.gn
+++ b/chrome/chrome_cleaner/mojom/BUILD.gn
@@ -18,6 +18,26 @@
   }
 }
 
+chrome_cleaner_mojom("chrome_prompt_interface") {
+  sources = [
+    "chrome_prompt.mojom",
+  ]
+
+  deps = [
+    ":footprints_interface",
+  ]
+
+  # NOTE: We avoid scrambling message IDs here because these messages cross an
+  # IPC boundary to an external program built from a different source tree.
+  scramble_message_ids = false
+}
+
+chrome_cleaner_mojom("footprints_interface") {
+  sources = [
+    "footprints.mojom",
+  ]
+}
+
 chrome_cleaner_mojom("engine_sandbox_interface") {
   sources = [
     "cleaner_engine_requests.mojom",
@@ -29,7 +49,7 @@
     "windows_handle.mojom",
   ]
   deps = [
-    "//components/chrome_cleaner/public/interfaces",
+    ":footprints_interface",
     "//mojo/public/mojom/base",
   ]
 }
diff --git a/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom b/chrome/chrome_cleaner/mojom/chrome_prompt.mojom
similarity index 78%
rename from components/chrome_cleaner/public/interfaces/chrome_prompt.mojom
rename to chrome/chrome_cleaner/mojom/chrome_prompt.mojom
index f475fcdd..f799afe8 100644
--- a/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom
+++ b/chrome/chrome_cleaner/mojom/chrome_prompt.mojom
@@ -4,15 +4,23 @@
 
 module chrome_cleaner.mojom;
 
-// IMPORTANT NOTE: Avoid adding dependencies to typemapped .mojom files.
-// Enabling typemaps currently (as of July 2017) requires indirectly depending
-// on all existing typemap definitions. The Chrome Cleaner is built
-// independently from Chromium and would like to avoid these dependencies.
+// This interface was used to communicate between Chrome and the Chrome Cleanup
+// until M80, when it was replaced by
+// components/chrome_cleaner/public/proto/chrome_prompt.proto. It must be
+// supported until we drop support for Chrome M79 and earlier.
+
+// IMPORTANT NOTE: Avoid adding dependencies to typemapped .mojom files outside
+// the chrome/chrome_cleaner directory.  Enabling typemaps currently (as of
+// July 2017) requires indirectly depending on all existing typemap
+// definitions. The Chrome Cleaner is built independently from Chromium and
+// would like to avoid these dependencies.
 
 // Once it's possible to specify a limited subset of typemaps to use, be
 // careful not to add dependencies to [Native] mojo structures. The wire format
 // for [Native] structs is not guaranteed to be consistent between versions.
 
+import "chrome/chrome_cleaner/mojom/footprints.mojom";
+
 [Extensible]
 enum PromptAcceptance {
   UNSPECIFIED = 0,
@@ -27,21 +35,6 @@
   NUM_VALUES,
 };
 
-struct FilePath {
-  array<uint16> value;
-};
-
-struct RegistryKey {
-  // A string formatted for display to the user.
-  // Example: HKCU:32\software\unwanted_software
-  array<uint16> value;
-};
-
-struct ExtensionId {
-  // A 32-character extension ID.
-  array<uint16> value;
-};
-
 // Service provided by Chrome to prompt the user to start a cleanup if the
 // Chrome Cleanup Tool detects unwanted software on the system.
 interface ChromePrompt {
diff --git a/chrome/chrome_cleaner/mojom/cleaner_engine_requests.mojom b/chrome/chrome_cleaner/mojom/cleaner_engine_requests.mojom
index 3164121..a05dddbd 100644
--- a/chrome/chrome_cleaner/mojom/cleaner_engine_requests.mojom
+++ b/chrome/chrome_cleaner/mojom/cleaner_engine_requests.mojom
@@ -4,8 +4,8 @@
 
 module chrome_cleaner.mojom;
 
+import "chrome/chrome_cleaner/mojom/footprints.mojom";
 import "chrome/chrome_cleaner/mojom/string16_embedded_nulls.mojom";
-import "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom";
 import "mojo/public/mojom/base/string16.mojom";
 
 // Passes requests that can mutate the system from the low-privilege sandbox
diff --git a/chrome/chrome_cleaner/mojom/engine_file_requests.mojom b/chrome/chrome_cleaner/mojom/engine_file_requests.mojom
index beba962e..066b9f4 100644
--- a/chrome/chrome_cleaner/mojom/engine_file_requests.mojom
+++ b/chrome/chrome_cleaner/mojom/engine_file_requests.mojom
@@ -4,7 +4,7 @@
 
 module chrome_cleaner.mojom;
 
-import "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom";
+import "chrome/chrome_cleaner/mojom/footprints.mojom";
 
 // Handles returned by FindFirstFile aren't real handles, so we can't pass them
 // through mojo as handles, since they can't be duplicated.
diff --git a/chrome/chrome_cleaner/mojom/engine_requests.mojom b/chrome/chrome_cleaner/mojom/engine_requests.mojom
index 276805d0..b15bee4 100644
--- a/chrome/chrome_cleaner/mojom/engine_requests.mojom
+++ b/chrome/chrome_cleaner/mojom/engine_requests.mojom
@@ -4,9 +4,9 @@
 
 module chrome_cleaner.mojom;
 
+import "chrome/chrome_cleaner/mojom/footprints.mojom";
 import "chrome/chrome_cleaner/mojom/string16_embedded_nulls.mojom";
 import "chrome/chrome_cleaner/mojom/windows_handle.mojom";
-import "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom";
 import "mojo/public/mojom/base/process_id.mojom";
 import "mojo/public/mojom/base/string16.mojom";
 
diff --git a/chrome/chrome_cleaner/mojom/engine_sandbox.mojom b/chrome/chrome_cleaner/mojom/engine_sandbox.mojom
index 687549b..64636ab 100644
--- a/chrome/chrome_cleaner/mojom/engine_sandbox.mojom
+++ b/chrome/chrome_cleaner/mojom/engine_sandbox.mojom
@@ -4,11 +4,11 @@
 
 module chrome_cleaner.mojom;
 
+import "chrome/chrome_cleaner/mojom/footprints.mojom";
 import "chrome/chrome_cleaner/mojom/cleaner_engine_requests.mojom";
 import "chrome/chrome_cleaner/mojom/engine_requests.mojom";
 import "chrome/chrome_cleaner/mojom/engine_file_requests.mojom";
 import "chrome/chrome_cleaner/mojom/pup.mojom";
-import "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom";
 
 // All result_code parameters and return values in these interfaces are either
 // an EngineResultCode or, if higher than EngineResultCode::kEngineInternal, an
diff --git a/chrome/chrome_cleaner/mojom/footprints.mojom b/chrome/chrome_cleaner/mojom/footprints.mojom
new file mode 100644
index 0000000..a80607f6
--- /dev/null
+++ b/chrome/chrome_cleaner/mojom/footprints.mojom
@@ -0,0 +1,20 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module chrome_cleaner.mojom;
+
+struct FilePath {
+  array<uint16> value;
+};
+
+struct RegistryKey {
+  // A string formatted for display to the user.
+  // Example: HKCU:32\software\unwanted_software
+  array<uint16> value;
+};
+
+struct ExtensionId {
+  // A 32-character extension ID.
+  array<uint16> value;
+};
diff --git a/chrome/chrome_cleaner/mojom/pup.mojom b/chrome/chrome_cleaner/mojom/pup.mojom
index b0e7f53..3bab939 100644
--- a/chrome/chrome_cleaner/mojom/pup.mojom
+++ b/chrome/chrome_cleaner/mojom/pup.mojom
@@ -4,8 +4,7 @@
 
 module chrome_cleaner.mojom;
 
-// chrome_prompt included for the FilePath struct.
-import "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom";
+import "chrome/chrome_cleaner/mojom/footprints.mojom";
 
 // Typemapped to chrome_cleaner::UwS::TraceLocation enumeration from
 // chrome_cleaner/logging/proto/shared_data.proto.
diff --git a/chrome/chrome_cleaner/mojom/typemaps/DEPS b/chrome/chrome_cleaner/mojom/typemaps/DEPS
index b4c64d8..813ef00 100644
--- a/chrome/chrome_cleaner/mojom/typemaps/DEPS
+++ b/chrome/chrome_cleaner/mojom/typemaps/DEPS
@@ -1,4 +1,10 @@
 include_rules = [
+  # Unlike the rest of chrome/chrome_cleaner this might be built on non-Windows
+  # platforms because it's included from the global
+  # chromium_bindings_configuration.gni. So it needs macros from
+  # build/build_config.h to check the platform.
+  '+build',
+
   # Allow unit tests to set up a mojo embedder.
   '+mojo/core/embedder',
 ]
diff --git a/components/chrome_cleaner/public/typemaps/chrome_prompt.typemap b/chrome/chrome_cleaner/mojom/typemaps/footprints.typemap
similarity index 63%
rename from components/chrome_cleaner/public/typemaps/chrome_prompt.typemap
rename to chrome/chrome_cleaner/mojom/typemaps/footprints.typemap
index 3f6702b..2c15653 100644
--- a/components/chrome_cleaner/public/typemaps/chrome_prompt.typemap
+++ b/chrome/chrome_cleaner/mojom/typemaps/footprints.typemap
@@ -2,16 +2,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-mojom = "//components/chrome_cleaner/public/interfaces/chrome_prompt.mojom"
+mojom = "//chrome/chrome_cleaner/mojom/footprints.mojom"
 public_headers = [
   "//base/files/file_path.h",
   "//base/strings/string16.h",
 ]
-traits_headers = [
-  "//components/chrome_cleaner/public/typemaps/chrome_prompt_mojom_traits.h",
-]
+traits_headers =
+    [ "//chrome/chrome_cleaner/mojom/typemaps/footprints_mojom_traits.h" ]
 sources = [
-  "//components/chrome_cleaner/public/typemaps/chrome_prompt_mojom_traits.cc",
+  "//chrome/chrome_cleaner/mojom/typemaps/footprints_mojom_traits.cc",
 ]
 
 type_mappings = [
diff --git a/components/chrome_cleaner/public/typemaps/chrome_prompt_mojom_traits.cc b/chrome/chrome_cleaner/mojom/typemaps/footprints_mojom_traits.cc
similarity index 96%
rename from components/chrome_cleaner/public/typemaps/chrome_prompt_mojom_traits.cc
rename to chrome/chrome_cleaner/mojom/typemaps/footprints_mojom_traits.cc
index a55242c..165f744c 100644
--- a/components/chrome_cleaner/public/typemaps/chrome_prompt_mojom_traits.cc
+++ b/chrome/chrome_cleaner/mojom/typemaps/footprints_mojom_traits.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/chrome_cleaner/public/typemaps/chrome_prompt_mojom_traits.h"
+#include "chrome/chrome_cleaner/mojom/typemaps/footprints_mojom_traits.h"
 #include "build/build_config.h"
 
 namespace mojo {
diff --git a/components/chrome_cleaner/public/typemaps/chrome_prompt_mojom_traits.h b/chrome/chrome_cleaner/mojom/typemaps/footprints_mojom_traits.h
similarity index 78%
rename from components/chrome_cleaner/public/typemaps/chrome_prompt_mojom_traits.h
rename to chrome/chrome_cleaner/mojom/typemaps/footprints_mojom_traits.h
index e1b52e6b..f31aea93 100644
--- a/components/chrome_cleaner/public/typemaps/chrome_prompt_mojom_traits.h
+++ b/chrome/chrome_cleaner/mojom/typemaps/footprints_mojom_traits.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_CHROME_CLEANER_PUBLIC_TYPEMAPS_CHROME_PROMPT_MOJOM_TRAITS_H_
-#define COMPONENTS_CHROME_CLEANER_PUBLIC_TYPEMAPS_CHROME_PROMPT_MOJOM_TRAITS_H_
+#ifndef CHROME_CHROME_CLEANER_MOJOM_TYPEMAPS_FOOTPRINTS_MOJOM_TRAITS_H_
+#define CHROME_CHROME_CLEANER_MOJOM_TYPEMAPS_FOOTPRINTS_MOJOM_TRAITS_H_
 
 #include "base/containers/span.h"
 #include "base/files/file_path.h"
 #include "base/strings/string16.h"
-#include "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom.h"
+#include "chrome/chrome_cleaner/mojom/footprints.mojom.h"
 
 namespace mojo {
 
@@ -37,4 +37,4 @@
 
 }  // namespace mojo
 
-#endif  // COMPONENTS_CHROME_CLEANER_PUBLIC_TYPEMAPS_CHROME_PROMPT_MOJOM_TRAITS_H_
+#endif  // CHROME_CHROME_CLEANER_MOJOM_TYPEMAPS_FOOTPRINTS_MOJOM_TRAITS_H_
diff --git a/chrome/chrome_cleaner/mojom/typemaps/pup_mojom_traits.cc b/chrome/chrome_cleaner/mojom/typemaps/pup_mojom_traits.cc
index d6348a3..31a787d 100644
--- a/chrome/chrome_cleaner/mojom/typemaps/pup_mojom_traits.cc
+++ b/chrome/chrome_cleaner/mojom/typemaps/pup_mojom_traits.cc
@@ -5,7 +5,7 @@
 #include "chrome/chrome_cleaner/mojom/typemaps/pup_mojom_traits.h"
 
 #include "chrome/chrome_cleaner/logging/proto/shared_data.pb.h"
-#include "components/chrome_cleaner/public/typemaps/chrome_prompt_mojom_traits.h"
+#include "chrome/chrome_cleaner/mojom/typemaps/footprints_mojom_traits.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
 
 namespace mojo {
diff --git a/chrome/chrome_cleaner/mojom/typemaps/typemaps.gni b/chrome/chrome_cleaner/mojom/typemaps/typemaps.gni
index 3bbd409..c3a7042 100644
--- a/chrome/chrome_cleaner/mojom/typemaps/typemaps.gni
+++ b/chrome/chrome_cleaner/mojom/typemaps/typemaps.gni
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 typemaps = [
+  "//chrome/chrome_cleaner/mojom/typemaps/footprints.typemap",
   "//chrome/chrome_cleaner/mojom/typemaps/pup.typemap",
   "//chrome/chrome_cleaner/mojom/typemaps/string16_embedded_nulls.typemap",
   "//chrome/chrome_cleaner/mojom/typemaps/windows_handle.typemap",
diff --git a/chrome/common/extensions/api/accessibility_features.json b/chrome/common/extensions/api/accessibility_features.json
index 4311c073b..499a89a3 100644
--- a/chrome/common/extensions/api/accessibility_features.json
+++ b/chrome/common/extensions/api/accessibility_features.json
@@ -75,7 +75,7 @@
       },
       "switchAccess": {
         "$ref": "types.ChromeSetting",
-        "description": "<p><strong>ChromeOS only.</strong></p><p>Switch access. The value indicates whether the feature is enabled or not. <code>get()</code> requires <code>accessibilityFeatures.read</code> permission. <code>set()</code> and <code>clear()</code> require <code>accessibilityFeatures.modify</code> permission.</p>",
+        "description": "<p><strong>ChromeOS only.</strong></p><p>Switch Access. The value indicates whether the feature is enabled or not. <code>get()</code> requires <code>accessibilityFeatures.read</code> permission. <code>set()</code> and <code>clear()</code> require <code>accessibilityFeatures.modify</code> permission.</p>",
         "value": ["switchAccess", {"type": "boolean"}],
         "platforms": ["chromeos"]
       },
diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h
index 75717b3..42efd95 100644
--- a/chrome/common/extensions/extension_constants.h
+++ b/chrome/common/extensions/extension_constants.h
@@ -210,9 +210,9 @@
 // Path to preinstalled Select-to-speak extension (relative to
 // |chrome::DIR_RESOURCES|).
 extern const char kSelectToSpeakExtensionPath[];
-// The extension id of the Switch access extension.
+// The extension id of the Switch Access extension.
 extern const char kSwitchAccessExtensionId[];
-// Path to preinstalled Switch access extension (relative to
+// Path to preinstalled Switch Access extension (relative to
 // |chrome::DIR_RESOURCES|).
 extern const char kSwitchAccessExtensionPath[];
 // Name of the manifest file in an extension when a special manifest is used
diff --git a/chrome/common/search.mojom b/chrome/common/search.mojom
index 4c71ce0a..2d46ff96 100644
--- a/chrome/common/search.mojom
+++ b/chrome/common/search.mojom
@@ -181,8 +181,8 @@
   ConfirmThemeChanges();
 
   // Gets autocomplete matches from the browser.
-  QueryAutocomplete(mojo_base.mojom.String16 input) =>
-        (AutocompleteResult result);
+  QueryAutocomplete(mojo_base.mojom.String16 input,
+    bool prevent_inline_autocomplete) => (AutocompleteResult result);
 
   // Deletes the AutocompleteMatch in the current results by |line| number if
   // it is deletable. When a match is deleted, |matches| is populated with the
diff --git a/chrome/renderer/searchbox/searchbox.cc b/chrome/renderer/searchbox/searchbox.cc
index 5c270d29..f4fd1982 100644
--- a/chrome/renderer/searchbox/searchbox.cc
+++ b/chrome/renderer/searchbox/searchbox.cc
@@ -438,10 +438,12 @@
   embedded_search_service_->ConfirmThemeChanges();
 }
 
-void SearchBox::QueryAutocomplete(const base::string16& input) {
+void SearchBox::QueryAutocomplete(const base::string16& input,
+                                  bool prevent_inline_autocomplete) {
   embedded_search_service_->QueryAutocomplete(
-      input, base::BindOnce(&SearchBox::QueryAutocompleteResult,
-                            weak_ptr_factory_.GetWeakPtr()));
+      input, prevent_inline_autocomplete,
+      base::BindOnce(&SearchBox::QueryAutocompleteResult,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void SearchBox::DeleteAutocompleteMatch(uint8_t line) {
diff --git a/chrome/renderer/searchbox/searchbox.h b/chrome/renderer/searchbox/searchbox.h
index c4e0d4d1..d185c54 100644
--- a/chrome/renderer/searchbox/searchbox.h
+++ b/chrome/renderer/searchbox/searchbox.h
@@ -190,8 +190,11 @@
   void ConfirmThemeChanges();
 
   // Queries the autocomplete backend for realbox results for |input| as a
-  // search term. Handled by |QueryAutocompleteResult|.
-  void QueryAutocomplete(const base::string16& input);
+  // search term. |prevent_inline_autocomplete| is true if the result set should
+  // not require inline autocomplete for the default match. Handled by
+  // |QueryAutocompleteResult|.
+  void QueryAutocomplete(const base::string16& input,
+                         bool prevent_inline_autocomplete);
 
   // Deletes |AutocompleteMatch| by index of the result.
   void DeleteAutocompleteMatch(uint8_t line);
diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc
index fdaf0cc..faaa7601 100644
--- a/chrome/renderer/searchbox/searchbox_extension.cc
+++ b/chrome/renderer/searchbox/searchbox_extension.cc
@@ -593,7 +593,8 @@
   // Handlers for JS functions.
   static void DeleteAutocompleteMatch(int line);
   static void Paste(const std::string& text);
-  static void QueryAutocomplete(const base::string16& input);
+  static void QueryAutocomplete(const base::string16& input,
+                                bool prevent_inline_autocomplete);
   static void StopAutocomplete(bool clear_result);
   static void StartCapturingKeyStrokes();
   static void StopCapturingKeyStrokes();
@@ -660,11 +661,12 @@
 }
 
 // static
-void SearchBoxBindings::QueryAutocomplete(const base::string16& input) {
+void SearchBoxBindings::QueryAutocomplete(const base::string16& input,
+                                          bool prevent_inline_autocomplete) {
   SearchBox* search_box = GetSearchBoxForCurrentContext();
   if (!search_box)
     return;
-  search_box->QueryAutocomplete(input);
+  search_box->QueryAutocomplete(input, prevent_inline_autocomplete);
 }
 
 // static
diff --git a/chrome/test/base/interactive_test_utils_mac.mm b/chrome/test/base/interactive_test_utils_mac.mm
index d461e02..a6b4fc9 100644
--- a/chrome/test/base/interactive_test_utils_mac.mm
+++ b/chrome/test/base/interactive_test_utils_mac.mm
@@ -29,7 +29,9 @@
   // Callback for MockCrApplication.
   void ObserveSendEvent(NSEvent* event);
 
-  IMP original_send_event() const { return original_send_event_; }
+  void OriginalSendEvent(id receiver, SEL selector, NSEvent* event) {
+    scoped_swizzler_->InvokeOriginal<void, NSEvent*>(receiver, selector, event);
+  }
 
   void SendGlobalKeyEventsAndWait(int key_code, int modifier_flags);
 
@@ -39,7 +41,6 @@
                           bool key_down);
 
   std::unique_ptr<base::mac::ScopedObjCClassSwizzler> scoped_swizzler_;
-  IMP original_send_event_ = nullptr;
   base::ScopedCFTypeRef<CGEventSourceRef> event_source_;
   CGEventTapLocation event_tap_location_;
   base::RunLoop run_loop_;
@@ -62,7 +63,7 @@
 - (void)sendEvent:(NSEvent*)event {
   DCHECK(g_global_key_events_helper);
   g_global_key_events_helper->ObserveSendEvent(event);
-  g_global_key_events_helper->original_send_event()(self, _cmd, event);
+  g_global_key_events_helper->OriginalSendEvent(self, _cmd, event);
 }
 
 @end
@@ -78,7 +79,6 @@
   scoped_swizzler_ = std::make_unique<base::mac::ScopedObjCClassSwizzler>(
       [BrowserCrApplication class], [MockCrApplication class],
       @selector(sendEvent:));
-  original_send_event_ = scoped_swizzler_->GetOriginalImplementation();
 }
 
 SendGlobalKeyEventsHelper::~SendGlobalKeyEventsHelper() {
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index b013c79..95dbb48 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -545,6 +545,9 @@
     // must be initialized when the testing profile finishes its initialization.
     signin_util::EnsureUserSignoutAllowedIsInitializedForProfile(this);
   }
+
+  if (original_profile_)
+    original_profile_->NotifyOffTheRecordProfileCreated(this);
 }
 
 TestingProfile::~TestingProfile() {
diff --git a/chrome/test/chromedriver/js/get_element_location.js b/chrome/test/chromedriver/js/get_element_location.js
index dcdb2530..5397fb0 100644
--- a/chrome/test/chromedriver/js/get_element_location.js
+++ b/chrome/test/chromedriver/js/get_element_location.js
@@ -20,8 +20,8 @@
   var top = Math.max(0, rect.top);
   var bottom = Math.min(window.innerHeight, rect.bottom);
 
-  // Find the view boundry of the element by checking itself and all of its
-  // ancestor's boundry.
+  // Find the view boundary of the element by checking itself and all of its
+  // ancestor's boundary.
   while (element.parentElement != null &&
          element.parentElement != document.body &&
          element.parentElement.getClientRects().length > 0) {
@@ -30,21 +30,26 @@
     var overflowX = parentStyle.getPropertyValue("overflow-x");
     var overflowY = parentStyle.getPropertyValue("overflow-y");
     var parentRect = getParentRect(element);
-    if (overflow == "auto" || overflow == "scroll" || overflow == "hidden") {
-      left = Math.max(left, parentRect.left);
-      right = Math.min(right, parentRect.right);
-      top = Math.max(top, parentRect.top);
-      bottom = Math.min(bottom, parentRect.bottom);
-    } else {
-      if (overflowX == "auto" || overflowX == "scroll" ||
-          overflowX == "hidden") {
+    // Only consider about overflow cases when the parent area overlaps with
+    // the element's area.
+    if (parentRect.right > left && parentRect.bottom > top &&
+        right > parentRect.left && bottom > parentRect.top) {
+      if (overflow == "auto" || overflow == "scroll" || overflow == "hidden") {
         left = Math.max(left, parentRect.left);
         right = Math.min(right, parentRect.right);
-      }
-      if (overflowY == "auto" || overflowY == "scroll" ||
-          overflowY == "hidden") {
         top = Math.max(top, parentRect.top);
         bottom = Math.min(bottom, parentRect.bottom);
+      } else {
+        if (overflowX == "auto" || overflowX == "scroll" ||
+            overflowX == "hidden") {
+          left = Math.max(left, parentRect.left);
+          right = Math.min(right, parentRect.right);
+        }
+        if (overflowY == "auto" || overflowY == "scroll" ||
+            overflowY == "hidden") {
+          top = Math.max(top, parentRect.top);
+          bottom = Math.min(bottom, parentRect.bottom);
+        }
       }
     }
     element = element.parentElement;
@@ -52,7 +57,6 @@
 
   var x = 0.5 * (left + right);
   var y = 0.5 * (top + bottom);
-
   return [x, y, left, top];
 }
 
diff --git a/chrome/test/data/android/render_tests/StartSurfaceLayoutTest.3_incognito_ntps.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/StartSurfaceLayoutTest.3_incognito_ntps.Nexus_5-19.png.sha1
deleted file mode 100644
index 59c7543..0000000
--- a/chrome/test/data/android/render_tests/StartSurfaceLayoutTest.3_incognito_ntps.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-aa72b682f2a1056919067f870c576e0f9f531302
\ No newline at end of file
diff --git a/chrome/test/data/local_ntp/realbox_browsertest.js b/chrome/test/data/local_ntp/realbox_browsertest.js
index 0bcfc0bf..5b4ae7a5 100644
--- a/chrome/test/data/local_ntp/realbox_browsertest.js
+++ b/chrome/test/data/local_ntp/realbox_browsertest.js
@@ -84,7 +84,10 @@
       modifiers);
 };
 
-/** @type {!Array<string>} */
+/** @typedef {{input: string, preventInlineAutocomplete: bool}} */
+let AutocompleteQuery;
+
+/** @type {!Array<AutocompleteQuery>} */
 test.realbox.queries;
 
 /** @type {!Array<number>} */
@@ -108,8 +111,11 @@
       deleteAutocompleteMatch(line) {
         test.realbox.deletedLines.push(line);
       },
-      queryAutocomplete(query) {
-        test.realbox.queries.push(query);
+      queryAutocomplete(input, preventInlineAutocomplete) {
+        test.realbox.queries.push({
+          input: input,
+          preventInlineAutocomplete: preventInlineAutocomplete
+        });
       },
       stopAutocomplete(clearResult) {}
     },
@@ -146,14 +152,14 @@
   test.realbox.realboxEl.value = 'hello realbox';
   test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
   assertEquals(1, test.realbox.queries.length);
-  assertEquals('hello realbox', test.realbox.queries[0]);
+  assertEquals('hello realbox', test.realbox.queries[0].input);
 };
 
 test.realbox.testReplyWithMatches = function() {
   test.realbox.realboxEl.value = 'hello world';
   test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
   assertEquals(1, test.realbox.queries.length);
-  assertEquals('hello world', test.realbox.queries[0]);
+  assertEquals('hello world', test.realbox.queries[0].input);
 
   const matches = [test.realbox.getSearchMatch(), test.realbox.getUrlMatch()];
   chrome.embeddedSearch.searchBox.onqueryautocompletedone(
@@ -176,7 +182,7 @@
   test.realbox.realboxEl.value = 'hello ';
   test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
   assertEquals(1, test.realbox.queries.length);
-  assertEquals('hello ', test.realbox.queries[0]);
+  assertEquals('hello ', test.realbox.queries[0].input);
 
   const match = test.realbox.getSearchMatch({
     contents: 'hello ',
@@ -202,11 +208,14 @@
   assertEquals('world', realboxValue.substring(start, end));
 };
 
+// Ensures that deleting text from input informs the backend to prevent inline
+// autocompletion for the default match.
 test.realbox.testDeleteWithInlineAutocompletion = function() {
   test.realbox.realboxEl.value = 'supercal';
   test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
   assertEquals(1, test.realbox.queries.length);
-  assertEquals('supercal', test.realbox.queries[0]);
+  assertEquals('supercal', test.realbox.queries[0].input);
+  assertFalse(test.realbox.queries[0].preventInlineAutocomplete);
 
   const match = test.realbox.getSearchMatch({
     contents: 'supercal',
@@ -222,28 +231,18 @@
 
   test.realbox.realboxEl.value = 'superca';
   test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
+  assertEquals(2, test.realbox.queries.length);
+  assertEquals('superca', test.realbox.queries[1].input);
+  assertTrue(test.realbox.queries[1].preventInlineAutocomplete)
 
   match.contents = 'superca';
-  match.inlineAutocompletion = 'lifragilisticexpialidocious';
+  match.inlineAutocompletion = '';
   chrome.embeddedSearch.searchBox.onqueryautocompletedone({
     input: test.realbox.realboxEl.value,
     matches: [match],
   });
 
-  // Ensure that removing characters from input doesn't just inline autocomplete
-  // to the same contents on backspace/cut/etc.
   assertEquals('superca', test.realbox.realboxEl.value);
-
-  test.realbox.realboxEl.value = 'super';
-  test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
-
-  match.contents = 'super';
-  match.inlineAutocompletion = 'califragilisticexpialidocious';
-  chrome.embeddedSearch.searchBox.onqueryautocompletedone({
-    input: test.realbox.realboxEl.value,
-    matches: [match],
-  });
-  assertEquals('super', test.realbox.realboxEl.value);
 };
 
 test.realbox.testTypeInlineAutocompletion = function() {
@@ -857,7 +856,7 @@
   assertEquals(1, test.realbox.queries.length);
 
   chrome.embeddedSearch.searchBox.onqueryautocompletedone({
-    input: test.realbox.queries[0],
+    input: test.realbox.queries[0].input,
     matches: [test.realbox.getSearchMatch()],
   });
 
@@ -900,7 +899,7 @@
   assertEquals(1, test.realbox.queries.length);
 
   chrome.embeddedSearch.searchBox.onqueryautocompletedone({
-    input: test.realbox.queries[0],
+    input: test.realbox.queries[0].input,
     matches: [test.realbox.getSearchMatch()],
   });
 
diff --git a/chrome/test/data/webui/tab_strip/tab_test.js b/chrome/test/data/webui/tab_strip/tab_test.js
index ca159f5..d99d66f 100644
--- a/chrome/test/data/webui/tab_strip/tab_test.js
+++ b/chrome/test/data/webui/tab_strip/tab_test.js
@@ -27,6 +27,7 @@
 
   const strings = {
     closeTab: 'Close tab',
+    loadingTab: 'Loading...',
     tabCrashed: '$1 has crashed',
     tabNetworkError: '$1 has a network error',
   };
@@ -195,6 +196,18 @@
         tab.title, tabElement.shadowRoot.querySelector('#titleText').innerText);
   });
 
+  test('sets the loading title while loading', () => {
+    const loadingTabWithoutTitle = Object.assign({}, tab, {
+      networkState: TabNetworkState.WAITING,
+      shouldHideThrobber: false,
+    });
+    delete loadingTabWithoutTitle.title;
+    tabElement.tab = loadingTabWithoutTitle;
+    assertEquals(
+        strings['loadingTab'],
+        tabElement.shadowRoot.querySelector('#titleText').innerText);
+  });
+
   test('exposes the tab ID to an attribute', () => {
     tabElement.tab = Object.assign({}, tab, {id: 1001});
     assertEquals('1001', tabElement.getAttribute('data-tab-id'));
diff --git a/chromecast/BUILD.gn b/chromecast/BUILD.gn
index e482914..4b0f1eb 100644
--- a/chromecast/BUILD.gn
+++ b/chromecast/BUILD.gn
@@ -659,6 +659,9 @@
     android_manifest = "$root_gen_dir/cast_shell_manifest/AndroidManifest.xml"
     android_manifest_dep = "//chromecast/browser/android:cast_shell_manifest"
 
+    min_sdk_version = 21
+    target_sdk_version = 28
+
     shared_libraries = [ "//chromecast/android:libcast_shell_android" ]
 
     product_config_java_packages = [ "org.chromium.chromecast.shell" ]
diff --git a/chromecast/browser/android/apk/AndroidManifest.xml.jinja2 b/chromecast/browser/android/apk/AndroidManifest.xml.jinja2
index c857253..67297710 100644
--- a/chromecast/browser/android/apk/AndroidManifest.xml.jinja2
+++ b/chromecast/browser/android/apk/AndroidManifest.xml.jinja2
@@ -8,6 +8,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="org.chromium.chromecast.shell">
 
+    <uses-sdk android:minSdkVersion="21" />
+
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
diff --git a/chromecast/media/cma/backend/multizone_backend_unittest.cc b/chromecast/media/cma/backend/multizone_backend_unittest.cc
index 220bc4d..e144c41 100644
--- a/chromecast/media/cma/backend/multizone_backend_unittest.cc
+++ b/chromecast/media/cma/backend/multizone_backend_unittest.cc
@@ -368,7 +368,10 @@
             kMaxRenderingDelayErrorUs);
 }
 
-TEST_P(MultizoneBackendTest, RenderingDelay) {
+// TODO(b/143135800): The new mixer backend didn't calculate the rendering delay
+// correctly when playback rate changes. Temporary disable the test to unblock
+// PoG.
+TEST_P(MultizoneBackendTest, DISABLED_RenderingDelay) {
   const TestParams& params = GetParam();
   int sample_rate = testing::get<0>(params);
   float playback_rate = testing::get<1>(params);
@@ -380,7 +383,7 @@
   Start(playback_rate);
 }
 
-TEST_F(MultizoneBackendTest, RenderingDelayWithMultipleRateChanges) {
+TEST_F(MultizoneBackendTest, DISABLED_RenderingDelayWithMultipleRateChanges) {
   Initialize(48000 /* sample_rate */, 10 /* playback_rate_change_count */);
   AddEffectsStreams();
   Start(1.0f /* playback_rate */);
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 8722e24..6ce06d7 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -316,9 +316,22 @@
   return base::FeatureList::IsEnabled(kSplitSettingsSync);
 }
 
+bool ShouldDeprecateV1DeviceSync() {
+  return ShouldUseV2DeviceSync() &&
+         base::FeatureList::IsEnabled(
+             chromeos::features::kCryptAuthV1DeviceSyncDeprecate);
+}
+
 bool ShouldShowPlayStoreInDemoMode() {
   return base::FeatureList::IsEnabled(kShowPlayInDemoMode);
 }
 
+bool ShouldUseV2DeviceSync() {
+  return base::FeatureList::IsEnabled(
+             chromeos::features::kCryptAuthV2Enrollment) &&
+         base::FeatureList::IsEnabled(
+             chromeos::features::kCryptAuthV2DeviceSync);
+}
+
 }  // namespace features
 }  // namespace chromeos
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index 2d17f750..93a120b7 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -141,10 +141,13 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsParentalControlsSettingsEnabled();
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsSplitSettingsEnabled();
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsSplitSettingsSyncEnabled();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool ShouldDeprecateV1DeviceSync();
 
 // TODO(michaelpg): Remove after M71 branch to re-enable Play Store by default.
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool ShouldShowPlayStoreInDemoMode();
 
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool ShouldUseV2DeviceSync();
+
 // Keep alphabetized.
 
 }  // namespace features
diff --git a/chromeos/network/network_connect.cc b/chromeos/network/network_connect.cc
index a24406b..f14ef962 100644
--- a/chromeos/network/network_connect.cc
+++ b/chromeos/network/network_connect.cc
@@ -491,7 +491,12 @@
 void NetworkConnect::Shutdown() {
   CHECK(g_network_connect);
   delete g_network_connect;
-  g_network_connect = NULL;
+  g_network_connect = nullptr;
+}
+
+// static
+bool NetworkConnect::IsInitialized() {
+  return g_network_connect;
 }
 
 // static
diff --git a/chromeos/network/network_connect.h b/chromeos/network/network_connect.h
index c39887a..0efcf22f4e 100644
--- a/chromeos/network/network_connect.h
+++ b/chromeos/network/network_connect.h
@@ -60,7 +60,10 @@
   // Destroys the global NetworkConnect object.
   static void Shutdown();
 
-  // Returns the global NetworkConnect object if initialized or NULL.
+  // Returns true if the global NetworkConnect object is initialized.
+  static bool IsInitialized();
+
+  // Returns the global NetworkConnect object if initialized or null.
   static NetworkConnect* Get();
 
   virtual ~NetworkConnect();
diff --git a/chromeos/services/device_sync/device_sync_impl.cc b/chromeos/services/device_sync/device_sync_impl.cc
index 8b91dc2..1e1388ab 100644
--- a/chromeos/services/device_sync/device_sync_impl.cc
+++ b/chromeos/services/device_sync/device_sync_impl.cc
@@ -21,6 +21,7 @@
 #include "chromeos/services/device_sync/cryptauth_device_registry_impl.h"
 #include "chromeos/services/device_sync/cryptauth_enroller_factory_impl.h"
 #include "chromeos/services/device_sync/cryptauth_enrollment_manager_impl.h"
+#include "chromeos/services/device_sync/cryptauth_feature_status_setter_impl.h"
 #include "chromeos/services/device_sync/cryptauth_gcm_manager_impl.h"
 #include "chromeos/services/device_sync/cryptauth_key_registry_impl.h"
 #include "chromeos/services/device_sync/cryptauth_scheduler_impl.h"
@@ -89,13 +90,6 @@
   kMaxValue = kUnexpectedClientFeature
 };
 
-bool ShouldUseV2DeviceSync() {
-  return base::FeatureList::IsEnabled(
-             chromeos::features::kCryptAuthV2Enrollment) &&
-         base::FeatureList::IsEnabled(
-             chromeos::features::kCryptAuthV2DeviceSync);
-}
-
 DeviceSyncRequestFailureReason GetDeviceSyncRequestFailureReason(
     mojom::NetworkRequestResult failure_reason) {
   switch (failure_reason) {
@@ -246,7 +240,7 @@
 
 bool DeviceSyncImpl::PendingSetSoftwareFeatureRequest::IsFulfilled() const {
   const auto& synced_devices = remote_device_provider_->GetSyncedDevices();
-  const auto& devices_it =
+  const auto devices_it =
       std::find_if(synced_devices.begin(), synced_devices.end(),
                    [this](const auto& remote_device) {
                      return device_public_key_ == remote_device.public_key;
@@ -256,7 +250,7 @@
   if (devices_it == synced_devices.end())
     return false;
 
-  const auto& features_map_it =
+  const auto features_map_it =
       devices_it->software_features.find(software_feature_);
 
   // If the device does not contain an entry for |software_feature_|, the
@@ -292,11 +286,88 @@
     CryptAuthEnrollmentManagerImpl::RegisterPrefs(registry);
   }
 
-  if (ShouldUseV2DeviceSync()) {
+  if (features::ShouldUseV2DeviceSync()) {
     CryptAuthDeviceRegistryImpl::RegisterPrefs(registry);
   }
 }
 
+DeviceSyncImpl::PendingSetFeatureStatusRequest::PendingSetFeatureStatusRequest(
+    const std::string& device_instance_id,
+    multidevice::SoftwareFeature software_feature,
+    FeatureStatusChange status_change,
+    RemoteDeviceProvider* remote_device_provider,
+    SetFeatureStatusCallback callback)
+    : device_instance_id_(device_instance_id),
+      software_feature_(software_feature),
+      status_change_(status_change),
+      remote_device_provider_(remote_device_provider),
+      callback_(std::move(callback)) {
+  DCHECK(!device_instance_id.empty());
+}
+
+DeviceSyncImpl::PendingSetFeatureStatusRequest::
+    ~PendingSetFeatureStatusRequest() = default;
+
+bool DeviceSyncImpl::PendingSetFeatureStatusRequest::IsFulfilled() const {
+  // True if the device from the request is included in the synced-devices list.
+  bool is_requested_device_in_list = false;
+
+  // True if the feature from the request is enabled on the device from the
+  // request.
+  bool is_feature_enabled_for_requested_device = false;
+
+  // True if the feature from the request is enabled on any synced device other
+  // than the device from the request.
+  bool is_feature_enabled_for_any_other_device = false;
+
+  for (const multidevice::RemoteDevice& remote_device :
+       remote_device_provider_->GetSyncedDevices()) {
+    const auto it = remote_device.software_features.find(software_feature_);
+    bool is_feature_set_for_device =
+        it != remote_device.software_features.end();
+    bool is_feature_enabled_for_device =
+        is_feature_set_for_device &&
+        it->second == multidevice::SoftwareFeatureState::kEnabled;
+
+    if (device_instance_id_ == remote_device.instance_id) {
+      DCHECK(!is_requested_device_in_list);
+      is_requested_device_in_list = true;
+
+      // If the requested device does not contain an entry for
+      // |software_feature_|, the request is not fulfilled.
+      if (!is_feature_set_for_device)
+        return false;
+
+      is_feature_enabled_for_requested_device = is_feature_enabled_for_device;
+    } else {
+      is_feature_enabled_for_any_other_device =
+          is_feature_enabled_for_any_other_device ||
+          is_feature_enabled_for_device;
+    }
+  }
+
+  // If the requested device no longer exists, the request is not fulfilled.
+  if (!is_requested_device_in_list)
+    return false;
+
+  switch (status_change_) {
+    case FeatureStatusChange::kEnableExclusively:
+      return is_feature_enabled_for_requested_device &&
+             !is_feature_enabled_for_any_other_device;
+    case FeatureStatusChange::kEnableNonExclusively:
+      return is_feature_enabled_for_requested_device;
+    case FeatureStatusChange::kDisable:
+      return !is_feature_enabled_for_requested_device;
+  }
+}
+
+void DeviceSyncImpl::PendingSetFeatureStatusRequest::InvokeCallback(
+    mojom::NetworkRequestResult result) {
+  // Callback should only be invoked once.
+  DCHECK(callback_);
+  std::move(callback_).Run(result);
+}
+
 DeviceSyncImpl::DeviceSyncImpl(
     signin::IdentityManager* identity_manager,
     gcm::GCMDriver* gcm_driver,
@@ -370,7 +441,7 @@
 
   cryptauth_device_manager_->ForceSyncNow(cryptauth::INVOCATION_REASON_MANUAL);
 
-  if (ShouldUseV2DeviceSync()) {
+  if (features::ShouldUseV2DeviceSync()) {
     cryptauth_v2_device_manager_->ForceDeviceSyncNow(
         cryptauthv2::ClientMetadata::MANUAL, base::nullopt /* session_id */);
   }
@@ -441,6 +512,35 @@
       is_exclusive);
 }
 
+void DeviceSyncImpl::SetFeatureStatus(const std::string& device_instance_id,
+                                      multidevice::SoftwareFeature feature,
+                                      FeatureStatusChange status_change,
+                                      SetFeatureStatusCallback callback) {
+  DCHECK(!device_instance_id.empty());
+
+  if (status_ != Status::READY) {
+    PA_LOG(WARNING) << "DeviceSyncImpl::SetFeatureStatus() invoked before "
+                    << "initialization was complete. Cannot enable/disable "
+                    << "feature.";
+    std::move(callback).Run(
+        mojom::NetworkRequestResult::kServiceNotYetInitialized);
+    return;
+  }
+
+  auto request_id = base::UnguessableToken::Create();
+  id_to_pending_set_feature_status_request_map_.emplace(
+      request_id, std::make_unique<PendingSetFeatureStatusRequest>(
+                      device_instance_id, feature, status_change,
+                      remote_device_provider_.get(), std::move(callback)));
+
+  feature_status_setter_->SetFeatureStatus(
+      device_instance_id, feature, status_change,
+      base::BindOnce(&DeviceSyncImpl::OnSetFeatureStatusSuccess,
+                     weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&DeviceSyncImpl::OnSetFeatureStatusError,
+                     weak_ptr_factory_.GetWeakPtr(), request_id));
+}
+
 void DeviceSyncImpl::FindEligibleDevices(
     multidevice::SoftwareFeature software_feature,
     FindEligibleDevicesCallback callback) {
@@ -551,8 +651,8 @@
 
   // Iterate through pending SetSoftwareFeature() requests. If any of them have
   // been fulfilled, invoke their callbacks.
-  auto it = id_to_pending_set_software_feature_request_map_.begin();
-  while (it != id_to_pending_set_software_feature_request_map_.end()) {
+  for (auto it = id_to_pending_set_software_feature_request_map_.begin();
+       it != id_to_pending_set_software_feature_request_map_.end();) {
     if (!it->second->IsFulfilled()) {
       ++it;
       continue;
@@ -564,10 +664,27 @@
     it->second->InvokeCallback(mojom::NetworkRequestResult::kSuccess);
     it = id_to_pending_set_software_feature_request_map_.erase(it);
   }
+
+  // Iterate through pending SetFeatureStatus() requests. If any of them have
+  // been fulfilled, invoke their callbacks.
+  for (auto it = id_to_pending_set_feature_status_request_map_.begin();
+       it != id_to_pending_set_feature_status_request_map_.end();) {
+    if (!it->second->IsFulfilled()) {
+      ++it;
+      continue;
+    }
+
+    PA_LOG(VERBOSE) << "DeviceSyncImpl::OnSyncDeviceListChanged(): Feature "
+                    << "status updated via device sync; notifying success "
+                    << "callbacks.";
+    it->second->InvokeCallback(mojom::NetworkRequestResult::kSuccess);
+    it = id_to_pending_set_feature_status_request_map_.erase(it);
+  }
 }
 
 void DeviceSyncImpl::Shutdown() {
   software_feature_manager_.reset();
+  feature_status_setter_.reset();
   remote_device_provider_.reset();
   cryptauth_v2_device_manager_.reset();
   cryptauth_device_manager_.reset();
@@ -674,7 +791,7 @@
       clock_, cryptauth_client_factory_.get(), cryptauth_gcm_manager_.get(),
       profile_prefs_);
 
-  if (ShouldUseV2DeviceSync()) {
+  if (features::ShouldUseV2DeviceSync()) {
     cryptauth_device_registry_ =
         CryptAuthDeviceRegistryImpl::Factory::Get()->BuildInstance(
             profile_prefs_);
@@ -697,7 +814,7 @@
   // Now that enrollment has completed, the current device has been registered
   // with the CryptAuth back-end and can begin monitoring synced devices.
   cryptauth_device_manager_->Start();
-  if (ShouldUseV2DeviceSync()) {
+  if (features::ShouldUseV2DeviceSync()) {
     cryptauth_v2_device_manager_->Start();
   }
 
@@ -709,6 +826,11 @@
   software_feature_manager_ = SoftwareFeatureManagerImpl::Factory::NewInstance(
       cryptauth_client_factory_.get());
 
+  feature_status_setter_ =
+      CryptAuthFeatureStatusSetterImpl::Factory::Get()->BuildInstance(
+          client_app_metadata_provider_, cryptauth_client_factory_.get(),
+          cryptauth_gcm_manager_.get());
+
   status_ = Status::READY;
 
   PA_LOG(VERBOSE) << "DeviceSyncImpl: CryptAuth Enrollment is valid; service "
@@ -722,10 +844,10 @@
       << "DeviceSyncImpl::GetSyncedDeviceWithPublicKey() called before ready.";
 
   const auto& synced_devices = remote_device_provider_->GetSyncedDevices();
-  const auto& it = std::find_if(synced_devices.begin(), synced_devices.end(),
-                                [&public_key](const auto& remote_device) {
-                                  return public_key == remote_device.public_key;
-                                });
+  const auto it = std::find_if(synced_devices.begin(), synced_devices.end(),
+                               [&public_key](const auto& remote_device) {
+                                 return public_key == remote_device.public_key;
+                               });
 
   if (it == synced_devices.end())
     return base::nullopt;
@@ -740,7 +862,7 @@
   cryptauth_device_manager_->ForceSyncNow(
       cryptauth::INVOCATION_REASON_FEATURE_TOGGLED);
 
-  if (ShouldUseV2DeviceSync()) {
+  if (features::ShouldUseV2DeviceSync()) {
     cryptauth_v2_device_manager_->ForceDeviceSyncNow(
         cryptauthv2::ClientMetadata::FEATURE_TOGGLED,
         base::nullopt /* session_id */);
@@ -772,6 +894,36 @@
   id_to_pending_set_software_feature_request_map_.erase(it);
 }
 
+void DeviceSyncImpl::OnSetFeatureStatusSuccess() {
+  PA_LOG(VERBOSE) << "DeviceSyncImpl::OnSetFeatureStatusSuccess(): "
+                  << "Successfully completed SetFeatureStatus() call; "
+                  << "requesting force sync.";
+  cryptauth_device_manager_->ForceSyncNow(
+      cryptauth::INVOCATION_REASON_FEATURE_TOGGLED);
+
+  if (features::ShouldUseV2DeviceSync()) {
+    cryptauth_v2_device_manager_->ForceDeviceSyncNow(
+        cryptauthv2::ClientMetadata::FEATURE_TOGGLED,
+        base::nullopt /* session_id */);
+  }
+}
+
+void DeviceSyncImpl::OnSetFeatureStatusError(
+    const base::UnguessableToken& request_id,
+    NetworkRequestError error) {
+  auto it = id_to_pending_set_feature_status_request_map_.find(request_id);
+  if (it == id_to_pending_set_feature_status_request_map_.end()) {
+    PA_LOG(ERROR) << "DeviceSyncImpl::OnSetFeatureStatusError(): "
+                  << "Could not find request entry with ID " << request_id;
+    NOTREACHED();
+    return;
+  }
+
+  it->second->InvokeCallback(
+      mojo::ConvertTo<mojom::NetworkRequestResult>(error));
+  id_to_pending_set_feature_status_request_map_.erase(it);
+}
+
 void DeviceSyncImpl::OnFindEligibleDevicesSuccess(
     const base::RepeatingCallback<void(mojom::NetworkRequestResult,
                                        mojom::FindEligibleDevicesResponsePtr)>&
diff --git a/chromeos/services/device_sync/device_sync_impl.h b/chromeos/services/device_sync/device_sync_impl.h
index cc7ac50..517160d 100644
--- a/chromeos/services/device_sync/device_sync_impl.h
+++ b/chromeos/services/device_sync/device_sync_impl.h
@@ -14,6 +14,7 @@
 #include "chromeos/services/device_sync/cryptauth_enrollment_manager.h"
 #include "chromeos/services/device_sync/cryptauth_gcm_manager.h"
 #include "chromeos/services/device_sync/device_sync_base.h"
+#include "chromeos/services/device_sync/feature_status_change.h"
 #include "chromeos/services/device_sync/network_request_error.h"
 #include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
 #include "chromeos/services/device_sync/remote_device_provider.h"
@@ -44,6 +45,7 @@
 class CryptAuthClientFactory;
 class CryptAuthDeviceManager;
 class CryptAuthDeviceRegistry;
+class CryptAuthFeatureStatusSetter;
 class CryptAuthKeyRegistry;
 class CryptAuthScheduler;
 class CryptAuthV2DeviceManager;
@@ -100,6 +102,10 @@
       bool enabled,
       bool is_exclusive,
       SetSoftwareFeatureStateCallback callback) override;
+  void SetFeatureStatus(const std::string& device_instance_id,
+                        multidevice::SoftwareFeature feature,
+                        FeatureStatusChange status_change,
+                        SetFeatureStatusCallback callback) override;
   void FindEligibleDevices(multidevice::SoftwareFeature software_feature,
                            FindEligibleDevicesCallback callback) override;
   void GetDevicesActivityStatus(
@@ -147,6 +153,30 @@
     SetSoftwareFeatureStateCallback callback_;
   };
 
+  class PendingSetFeatureStatusRequest {
+   public:
+    PendingSetFeatureStatusRequest(
+        const std::string& device_instance_id,
+        multidevice::SoftwareFeature software_feature,
+        FeatureStatusChange status_change,
+        RemoteDeviceProvider* remote_device_provider,
+        SetFeatureStatusCallback callback);
+    ~PendingSetFeatureStatusRequest();
+
+    // True if the device and software feature status specified in the request
+    // agrees with the device data returned by CryptAuth.
+    bool IsFulfilled() const;
+
+    void InvokeCallback(mojom::NetworkRequestResult result);
+
+   private:
+    std::string device_instance_id_;
+    multidevice::SoftwareFeature software_feature_;
+    FeatureStatusChange status_change_;
+    RemoteDeviceProvider* remote_device_provider_;
+    SetFeatureStatusCallback callback_;
+  };
+
   DeviceSyncImpl(
       signin::IdentityManager* identity_manager,
       gcm::GCMDriver* gcm_driver,
@@ -174,6 +204,9 @@
   void OnSetSoftwareFeatureStateSuccess();
   void OnSetSoftwareFeatureStateError(const base::UnguessableToken& request_id,
                                       NetworkRequestError error);
+  void OnSetFeatureStatusSuccess();
+  void OnSetFeatureStatusError(const base::UnguessableToken& request_id,
+                               NetworkRequestError error);
   void OnFindEligibleDevicesSuccess(
       const base::RepeatingCallback<
           void(mojom::NetworkRequestResult,
@@ -213,6 +246,9 @@
   base::flat_map<base::UnguessableToken,
                  std::unique_ptr<PendingSetSoftwareFeatureRequest>>
       id_to_pending_set_software_feature_request_map_;
+  base::flat_map<base::UnguessableToken,
+                 std::unique_ptr<PendingSetFeatureStatusRequest>>
+      id_to_pending_set_feature_status_request_map_;
   base::flat_map<base::UnguessableToken, GetDevicesActivityStatusCallback>
       get_devices_activity_status_callbacks_;
 
@@ -231,6 +267,7 @@
   std::unique_ptr<CryptAuthDeviceManager> cryptauth_device_manager_;
   std::unique_ptr<RemoteDeviceProvider> remote_device_provider_;
   std::unique_ptr<SoftwareFeatureManager> software_feature_manager_;
+  std::unique_ptr<CryptAuthFeatureStatusSetter> feature_status_setter_;
   std::unique_ptr<CryptAuthDeviceActivityGetter>
       cryptauth_device_activity_getter_;
 
diff --git a/chromeos/services/device_sync/device_sync_service_unittest.cc b/chromeos/services/device_sync/device_sync_service_unittest.cc
index 22f192b4..3684e67d 100644
--- a/chromeos/services/device_sync/device_sync_service_unittest.cc
+++ b/chromeos/services/device_sync/device_sync_service_unittest.cc
@@ -31,6 +31,7 @@
 #include "chromeos/services/device_sync/device_sync_impl.h"
 #include "chromeos/services/device_sync/fake_cryptauth_device_manager.h"
 #include "chromeos/services/device_sync/fake_cryptauth_enrollment_manager.h"
+#include "chromeos/services/device_sync/fake_cryptauth_feature_status_setter.h"
 #include "chromeos/services/device_sync/fake_cryptauth_gcm_manager.h"
 #include "chromeos/services/device_sync/fake_cryptauth_scheduler.h"
 #include "chromeos/services/device_sync/fake_cryptauth_v2_device_manager.h"
@@ -133,6 +134,24 @@
   base::Closure on_delegate_call_closure_;
 };
 
+// Delegate which invokes the Closure provided to its constructor when a
+// delegate function is invoked.
+class FakeCryptAuthFeatureStatusSetterDelegate
+    : public FakeCryptAuthFeatureStatusSetter::Delegate {
+ public:
+  explicit FakeCryptAuthFeatureStatusSetterDelegate(
+      base::Closure on_delegate_call_closure)
+      : on_delegate_call_closure_(on_delegate_call_closure) {}
+
+  ~FakeCryptAuthFeatureStatusSetterDelegate() override = default;
+
+  // FakeCryptAuthFeatureStatusSetter::Delegate:
+  void OnSetFeatureStatusCalled() override { on_delegate_call_closure_.Run(); }
+
+ private:
+  base::Closure on_delegate_call_closure_;
+};
+
 class FakeCryptAuthGCMManagerFactory : public CryptAuthGCMManagerImpl::Factory {
  public:
   FakeCryptAuthGCMManagerFactory(gcm::FakeGCMDriver* fake_gcm_driver,
@@ -224,10 +243,8 @@
     : public CryptAuthDeviceRegistryImpl::Factory {
  public:
   explicit FakeCryptAuthDeviceRegistryFactory(
-      TestingPrefServiceSimple* test_pref_service,
-      bool using_v2_device_sync)
-      : test_pref_service_(test_pref_service),
-        using_v2_device_sync_(using_v2_device_sync) {}
+      TestingPrefServiceSimple* test_pref_service)
+      : test_pref_service_(test_pref_service) {}
 
   ~FakeCryptAuthDeviceRegistryFactory() override = default;
 
@@ -237,7 +254,7 @@
   // CryptAuthDeviceRegistryImpl::Factory:
   std::unique_ptr<CryptAuthDeviceRegistry> BuildInstance(
       PrefService* pref_service) override {
-    EXPECT_TRUE(using_v2_device_sync_);
+    EXPECT_TRUE(features::ShouldUseV2DeviceSync());
     EXPECT_EQ(test_pref_service_, pref_service);
 
     // Only one instance is expected to be created per test.
@@ -248,7 +265,6 @@
   }
 
   TestingPrefServiceSimple* test_pref_service_;
-  bool using_v2_device_sync_;
   FakeCryptAuthDeviceRegistry* instance_ = nullptr;
 };
 
@@ -266,10 +282,8 @@
     : public CryptAuthKeyRegistryImpl::Factory {
  public:
   explicit FakeCryptAuthKeyRegistryFactory(
-      TestingPrefServiceSimple* test_pref_service,
-      bool using_v2_enrollment)
-      : test_pref_service_(test_pref_service),
-        using_v2_enrollment_(using_v2_enrollment) {}
+      TestingPrefServiceSimple* test_pref_service)
+      : test_pref_service_(test_pref_service) {}
 
   ~FakeCryptAuthKeyRegistryFactory() override = default;
 
@@ -279,7 +293,7 @@
   // CryptAuthKeyRegistryImpl::Factory:
   std::unique_ptr<CryptAuthKeyRegistry> BuildInstance(
       PrefService* pref_service) override {
-    EXPECT_TRUE(using_v2_enrollment_);
+    EXPECT_TRUE(base::FeatureList::IsEnabled(features::kCryptAuthV2Enrollment));
     EXPECT_EQ(test_pref_service_, pref_service);
 
     // Only one instance is expected to be created per test.
@@ -290,17 +304,14 @@
   }
 
   TestingPrefServiceSimple* test_pref_service_;
-  bool using_v2_enrollment_;
   FakeCryptAuthKeyRegistry* instance_ = nullptr;
 };
 
 class FakeCryptAuthSchedulerFactory : public CryptAuthSchedulerImpl::Factory {
  public:
   explicit FakeCryptAuthSchedulerFactory(
-      TestingPrefServiceSimple* test_pref_service,
-      bool using_v2_enrollment)
-      : test_pref_service_(test_pref_service),
-        using_v2_enrollment_(using_v2_enrollment) {}
+      TestingPrefServiceSimple* test_pref_service)
+      : test_pref_service_(test_pref_service) {}
 
   ~FakeCryptAuthSchedulerFactory() override = default;
 
@@ -314,7 +325,7 @@
       base::Clock* clock,
       std::unique_ptr<base::OneShotTimer> enrollment_timer,
       std::unique_ptr<base::OneShotTimer> device_sync_timer) override {
-    EXPECT_TRUE(using_v2_enrollment_);
+    EXPECT_TRUE(base::FeatureList::IsEnabled(features::kCryptAuthV2Enrollment));
     EXPECT_EQ(test_pref_service_, pref_service);
 
     // Only one instance is expected to be created per test.
@@ -327,7 +338,6 @@
   }
 
   TestingPrefServiceSimple* test_pref_service_;
-  bool using_v2_enrollment_;
   FakeCryptAuthScheduler* instance_ = nullptr;
 };
 
@@ -339,14 +349,12 @@
       FakeCryptAuthDeviceRegistryFactory* fake_device_registry_factory,
       FakeCryptAuthKeyRegistryFactory* fake_key_registry_factory,
       FakeCryptAuthGCMManagerFactory* fake_gcm_manager_factory,
-      FakeCryptAuthSchedulerFactory* fake_scheduler_factory,
-      bool using_v2_device_sync)
+      FakeCryptAuthSchedulerFactory* fake_scheduler_factory)
       : fake_client_app_metadata_provider_(fake_client_app_metadata_provider),
         fake_device_registry_factory_(fake_device_registry_factory),
         fake_key_registry_factory_(fake_key_registry_factory),
         fake_gcm_manager_factory_(fake_gcm_manager_factory),
-        fake_scheduler_factory_(fake_scheduler_factory),
-        using_v2_device_sync_(using_v2_device_sync) {}
+        fake_scheduler_factory_(fake_scheduler_factory) {}
 
   ~FakeCryptAuthV2DeviceManagerFactory() override = default;
 
@@ -362,7 +370,7 @@
       CryptAuthGCMManager* gcm_manager,
       CryptAuthScheduler* scheduler,
       std::unique_ptr<base::OneShotTimer> timer) override {
-    EXPECT_TRUE(using_v2_device_sync_);
+    EXPECT_TRUE(features::ShouldUseV2DeviceSync());
     EXPECT_EQ(fake_client_app_metadata_provider_, client_app_metadata_provider);
     EXPECT_EQ(fake_device_registry_factory_->instance(), device_registry);
     EXPECT_EQ(fake_key_registry_factory_->instance(), key_registry);
@@ -383,7 +391,6 @@
   FakeCryptAuthKeyRegistryFactory* fake_key_registry_factory_ = nullptr;
   FakeCryptAuthGCMManagerFactory* fake_gcm_manager_factory_ = nullptr;
   FakeCryptAuthSchedulerFactory* fake_scheduler_factory_ = nullptr;
-  bool using_v2_device_sync_;
   FakeCryptAuthV2DeviceManager* instance_ = nullptr;
 };
 
@@ -393,12 +400,10 @@
   FakeCryptAuthEnrollmentManagerFactory(
       base::SimpleTestClock* simple_test_clock,
       FakeCryptAuthGCMManagerFactory* fake_cryptauth_gcm_manager_factory,
-      TestingPrefServiceSimple* test_pref_service,
-      bool using_v2_enrollment)
+      TestingPrefServiceSimple* test_pref_service)
       : simple_test_clock_(simple_test_clock),
         fake_cryptauth_gcm_manager_factory_(fake_cryptauth_gcm_manager_factory),
-        test_pref_service_(test_pref_service),
-        using_v2_enrollment_(using_v2_enrollment) {}
+        test_pref_service_(test_pref_service) {}
 
   ~FakeCryptAuthEnrollmentManagerFactory() override = default;
 
@@ -419,7 +424,8 @@
       const cryptauth::GcmDeviceInfo& device_info,
       CryptAuthGCMManager* gcm_manager,
       PrefService* pref_service) override {
-    EXPECT_FALSE(using_v2_enrollment_);
+    EXPECT_FALSE(
+        base::FeatureList::IsEnabled(features::kCryptAuthV2Enrollment));
     EXPECT_EQ(simple_test_clock_, clock);
     EXPECT_EQ(kTestGcmDeviceInfoLongDeviceId, device_info.long_device_id());
     EXPECT_EQ(fake_cryptauth_gcm_manager_factory_->instance(), gcm_manager);
@@ -440,7 +446,6 @@
   base::SimpleTestClock* simple_test_clock_;
   FakeCryptAuthGCMManagerFactory* fake_cryptauth_gcm_manager_factory_;
   TestingPrefServiceSimple* test_pref_service_;
-  bool using_v2_enrollment_;
   bool device_already_enrolled_in_cryptauth_ = false;
   FakeCryptAuthEnrollmentManager* instance_ = nullptr;
 };
@@ -454,16 +459,14 @@
       FakeCryptAuthGCMManagerFactory* fake_cryptauth_gcm_manager_factory,
       FakeCryptAuthSchedulerFactory* fake_cryptauth_scheduler_factory,
       TestingPrefServiceSimple* test_pref_service,
-      base::SimpleTestClock* simple_test_clock,
-      bool using_v2_enrollment)
+      base::SimpleTestClock* simple_test_clock)
       : fake_client_app_metadata_provider_(fake_client_app_metadata_provider),
         fake_cryptauth_key_registry_factory_(
             fake_cryptauth_key_registry_factory),
         fake_cryptauth_gcm_manager_factory_(fake_cryptauth_gcm_manager_factory),
         fake_cryptauth_scheduler_factory_(fake_cryptauth_scheduler_factory),
         test_pref_service_(test_pref_service),
-        simple_test_clock_(simple_test_clock),
-        using_v2_enrollment_(using_v2_enrollment) {}
+        simple_test_clock_(simple_test_clock) {}
 
   ~FakeCryptAuthV2EnrollmentManagerFactory() override = default;
 
@@ -485,7 +488,7 @@
       PrefService* pref_service,
       base::Clock* clock,
       std::unique_ptr<base::OneShotTimer> timer) override {
-    EXPECT_TRUE(using_v2_enrollment_);
+    EXPECT_TRUE(base::FeatureList::IsEnabled(features::kCryptAuthV2Enrollment));
     EXPECT_EQ(fake_client_app_metadata_provider_, client_app_metadata_provider);
     EXPECT_EQ(fake_cryptauth_key_registry_factory_->instance(), key_registry);
     EXPECT_EQ(fake_cryptauth_gcm_manager_factory_->instance(), gcm_manager);
@@ -511,7 +514,6 @@
   FakeCryptAuthSchedulerFactory* fake_cryptauth_scheduler_factory_;
   TestingPrefServiceSimple* test_pref_service_;
   base::SimpleTestClock* simple_test_clock_;
-  bool using_v2_enrollment_;
   bool device_already_enrolled_in_cryptauth_ = false;
   FakeCryptAuthEnrollmentManager* instance_ = nullptr;
 };
@@ -526,8 +528,7 @@
       FakeCryptAuthEnrollmentManagerFactory*
           fake_cryptauth_enrollment_manager_factory,
       FakeCryptAuthV2EnrollmentManagerFactory*
-          fake_cryptauth_v2_enrollment_manager_factory,
-      bool using_v2_enrollment)
+          fake_cryptauth_v2_enrollment_manager_factory)
       : initial_devices_(initial_devices),
         identity_manager_(identity_manager),
         fake_cryptauth_device_manager_factory_(
@@ -535,8 +536,7 @@
         fake_cryptauth_enrollment_manager_factory_(
             fake_cryptauth_enrollment_manager_factory),
         fake_cryptauth_v2_enrollment_manager_factory_(
-            fake_cryptauth_v2_enrollment_manager_factory),
-        using_v2_enrollment_(using_v2_enrollment) {}
+            fake_cryptauth_v2_enrollment_manager_factory) {}
 
   ~FakeRemoteDeviceProviderFactory() override = default;
 
@@ -550,7 +550,7 @@
     EXPECT_EQ(fake_cryptauth_device_manager_factory_->instance(),
               device_manager);
     EXPECT_EQ(identity_manager_->GetPrimaryAccountId(), user_id);
-    if (using_v2_enrollment_) {
+    if (base::FeatureList::IsEnabled(features::kCryptAuthV2Enrollment)) {
       EXPECT_EQ(fake_cryptauth_v2_enrollment_manager_factory_->instance()
                     ->GetUserPrivateKey(),
                 user_private_key);
@@ -579,7 +579,6 @@
       fake_cryptauth_enrollment_manager_factory_;
   FakeCryptAuthV2EnrollmentManagerFactory*
       fake_cryptauth_v2_enrollment_manager_factory_;
-  bool using_v2_enrollment_;
 
   FakeRemoteDeviceProvider* instance_ = nullptr;
 };
@@ -663,7 +662,6 @@
     } else {
       disabled_features.push_back(chromeos::features::kCryptAuthV2Enrollment);
     }
-    use_v2_enrollment_ = std::get<0>(GetParam());
 
     // Choose whether or not to enabled v2 DeviceSync feature flag based on the
     // second parameter provided by ::testing::TestWithParam<std::tuple<bool,
@@ -674,7 +672,6 @@
     } else {
       disabled_features.push_back(chromeos::features::kCryptAuthV2DeviceSync);
     }
-    use_v2_device_sync_ = std::get<0>(GetParam()) && std::get<1>(GetParam());
 
     scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
 
@@ -704,13 +701,13 @@
 
     fake_cryptauth_key_registry_factory_ =
         std::make_unique<FakeCryptAuthKeyRegistryFactory>(
-            test_pref_service_.get(), use_v2_enrollment_);
+            test_pref_service_.get());
     CryptAuthKeyRegistryImpl::Factory::SetFactoryForTesting(
         fake_cryptauth_key_registry_factory_.get());
 
     fake_cryptauth_scheduler_factory_ =
         std::make_unique<FakeCryptAuthSchedulerFactory>(
-            test_pref_service_.get(), use_v2_enrollment_);
+            test_pref_service_.get());
     CryptAuthSchedulerImpl::Factory::SetFactoryForTesting(
         fake_cryptauth_scheduler_factory_.get());
 
@@ -720,7 +717,7 @@
             fake_cryptauth_key_registry_factory_.get(),
             fake_cryptauth_gcm_manager_factory_.get(),
             fake_cryptauth_scheduler_factory_.get(), test_pref_service_.get(),
-            simple_test_clock_.get(), use_v2_enrollment_);
+            simple_test_clock_.get());
     CryptAuthV2EnrollmentManagerImpl::Factory::SetFactoryForTesting(
         fake_cryptauth_v2_enrollment_manager_factory_.get());
     // ---------- End: Only used for v2 Enrollment ----------
@@ -729,7 +726,7 @@
     fake_cryptauth_enrollment_manager_factory_ =
         std::make_unique<FakeCryptAuthEnrollmentManagerFactory>(
             simple_test_clock_.get(), fake_cryptauth_gcm_manager_factory_.get(),
-            test_pref_service_.get(), use_v2_enrollment_);
+            test_pref_service_.get());
     CryptAuthEnrollmentManagerImpl::Factory::SetInstanceForTesting(
         fake_cryptauth_enrollment_manager_factory_.get());
 
@@ -743,7 +740,7 @@
     // ---------- Begin: Only used for v2 DeviceSync ----------
     fake_cryptauth_device_registry_factory_ =
         std::make_unique<FakeCryptAuthDeviceRegistryFactory>(
-            test_pref_service_.get(), use_v2_device_sync_);
+            test_pref_service_.get());
     CryptAuthDeviceRegistryImpl::Factory::SetFactoryForTesting(
         fake_cryptauth_device_registry_factory_.get());
 
@@ -753,7 +750,7 @@
             fake_cryptauth_device_registry_factory_.get(),
             fake_cryptauth_key_registry_factory_.get(),
             fake_cryptauth_gcm_manager_factory_.get(),
-            fake_cryptauth_scheduler_factory_.get(), use_v2_device_sync_);
+            fake_cryptauth_scheduler_factory_.get());
     CryptAuthV2DeviceManagerImpl::Factory::SetFactoryForTesting(
         fake_cryptauth_v2_device_manager_factory_.get());
     // ---------- End: Only used for v2 DeviceSync ----------
@@ -763,8 +760,7 @@
             test_devices_, identity_test_environment_->identity_manager(),
             fake_cryptauth_device_manager_factory_.get(),
             fake_cryptauth_enrollment_manager_factory_.get(),
-            fake_cryptauth_v2_enrollment_manager_factory_.get(),
-            use_v2_enrollment_);
+            fake_cryptauth_v2_enrollment_manager_factory_.get());
     RemoteDeviceProviderImpl::Factory::SetInstanceForTesting(
         fake_remote_device_provider_factory_.get());
 
@@ -773,6 +769,11 @@
     SoftwareFeatureManagerImpl::Factory::SetInstanceForTesting(
         fake_software_feature_manager_factory_.get());
 
+    fake_feature_status_setter_factory_ =
+        std::make_unique<FakeCryptAuthFeatureStatusSetterFactory>();
+    CryptAuthFeatureStatusSetterImpl::Factory::SetFactoryForTesting(
+        fake_feature_status_setter_factory_.get());
+
     auto mock_timer = std::make_unique<base::MockOneShotTimer>();
     mock_timer_ = mock_timer.get();
 
@@ -807,7 +808,7 @@
     device_already_enrolled_in_cryptauth_ =
         device_already_enrolled_in_cryptauth;
 
-    if (use_v2_enrollment_) {
+    if (base::FeatureList::IsEnabled(features::kCryptAuthV2Enrollment)) {
       fake_cryptauth_v2_enrollment_manager_factory_
           ->set_device_already_enrolled_in_cryptauth(
               device_already_enrolled_in_cryptauth);
@@ -869,7 +870,7 @@
         expected_to_be_initialized,
         fake_cryptauth_device_manager_factory_->instance()->has_started());
 
-    if (use_v2_device_sync_) {
+    if (features::ShouldUseV2DeviceSync()) {
       EXPECT_EQ(
           expected_to_be_initialized,
           fake_cryptauth_v2_device_manager_factory_->instance()->has_started());
@@ -922,7 +923,7 @@
             ? CryptAuthDeviceManager::DeviceChangeResult::UNCHANGED
             : CryptAuthDeviceManager::DeviceChangeResult::CHANGED);
 
-    if (use_v2_device_sync_) {
+    if (features::ShouldUseV2DeviceSync()) {
       EXPECT_TRUE(v2_device_manager->IsDeviceSyncInProgress());
       v2_device_manager->FinishNextForcedDeviceSync(
           CryptAuthDeviceSyncResult(
@@ -972,7 +973,7 @@
   base::MockOneShotTimer* mock_timer() { return mock_timer_; }
 
   FakeCryptAuthEnrollmentManager* fake_cryptauth_enrollment_manager() {
-    return use_v2_enrollment_
+    return base::FeatureList::IsEnabled(features::kCryptAuthV2Enrollment)
                ? fake_cryptauth_v2_enrollment_manager_factory_->instance()
                : fake_cryptauth_enrollment_manager_factory_->instance();
   }
@@ -985,6 +986,18 @@
     return fake_software_feature_manager_factory_->instance();
   }
 
+  FakeCryptAuthFeatureStatusSetter* fake_feature_status_setter() {
+    if (fake_feature_status_setter_factory_->instances().empty())
+      return nullptr;
+
+    EXPECT_EQ(1u, fake_feature_status_setter_factory_->instances().size());
+    return fake_feature_status_setter_factory_->instances()[0];
+  }
+
+  const std::vector<mojom::NetworkRequestResult>& set_feature_status_results() {
+    return set_feature_status_results_;
+  }
+
   std::unique_ptr<mojom::NetworkRequestResult>
   GetLastSetSoftwareFeatureStateResponseAndReset() {
     return std::move(last_set_software_feature_state_response_);
@@ -1020,6 +1033,15 @@
     EXPECT_EQ(mojom::NetworkRequestResult::kServiceNotYetInitialized,
               *last_set_response);
 
+    // SetFeatureStatus() should return a struct with the special
+    // kErrorNotInitialized error code.
+    CallSetFeatureStatus(test_devices()[0].instance_id,
+                         multidevice::SoftwareFeature::kBetterTogetherHost,
+                         FeatureStatusChange::kEnableExclusively);
+    EXPECT_EQ(1u, set_feature_status_results_.size());
+    EXPECT_EQ(mojom::NetworkRequestResult::kServiceNotYetInitialized,
+              set_feature_status_results_[0]);
+
     // Likewise, FindEligibleDevices() should also return a struct with the same
     // error code.
     CallFindEligibleDevices(multidevice::SoftwareFeature::kBetterTogetherHost);
@@ -1143,6 +1165,34 @@
     fake_software_feature_manager_factory_->instance()->set_delegate(nullptr);
   }
 
+  void CallSetFeatureStatus(const std::string& device_instance_id,
+                            multidevice::SoftwareFeature software_feature,
+                            FeatureStatusChange status_change) {
+    base::RunLoop run_loop;
+
+    // If the feature setter has not yet been created, the service has not been
+    // initialized. SetFeatureStatus() is expected to respond synchronously with
+    // an error.
+    if (!fake_feature_status_setter()) {
+      device_sync_->SetFeatureStatus(
+          device_instance_id, software_feature, status_change,
+          base::Bind(
+              &DeviceSyncServiceTest::OnSetFeatureStatusCompletedSynchronously,
+              base::Unretained(this), run_loop.QuitClosure()));
+      run_loop.Run();
+      return;
+    }
+
+    FakeCryptAuthFeatureStatusSetterDelegate delegate(run_loop.QuitClosure());
+    fake_feature_status_setter()->set_delegate(&delegate);
+    device_sync_->SetFeatureStatus(
+        device_instance_id, software_feature, status_change,
+        base::Bind(&DeviceSyncServiceTest::OnSetFeatureStatusCompleted,
+                   base::Unretained(this)));
+    run_loop.Run();
+    fake_feature_status_setter()->set_delegate(nullptr);
+  }
+
   void CallFindEligibleDevices(multidevice::SoftwareFeature software_feature) {
     base::RunLoop run_loop;
     FakeSoftwareFeatureManager* manager = fake_software_feature_manager();
@@ -1230,6 +1280,17 @@
     std::move(quit_closure).Run();
   }
 
+  void OnSetFeatureStatusCompleted(mojom::NetworkRequestResult result_code) {
+    set_feature_status_results_.push_back(result_code);
+  }
+
+  void OnSetFeatureStatusCompletedSynchronously(
+      base::OnceClosure quit_closure,
+      mojom::NetworkRequestResult result_code) {
+    OnSetFeatureStatusCompleted(result_code);
+    std::move(quit_closure).Run();
+  }
+
   void OnFindEligibleDevicesCompleted(
       mojom::NetworkRequestResult result_code,
       mojom::FindEligibleDevicesResponsePtr response) {
@@ -1289,6 +1350,8 @@
       fake_remote_device_provider_factory_;
   std::unique_ptr<FakeSoftwareFeatureManagerFactory>
       fake_software_feature_manager_factory_;
+  std::unique_ptr<FakeCryptAuthFeatureStatusSetterFactory>
+      fake_feature_status_setter_factory_;
 
   std::unique_ptr<signin::IdentityTestEnvironment> identity_test_environment_;
   std::unique_ptr<gcm::FakeGCMDriver> fake_gcm_driver_;
@@ -1305,14 +1368,13 @@
                             mojom::FindEligibleDevicesResponsePtr>>
       last_find_eligible_devices_response_;
   base::Optional<mojom::DebugInfo> last_debug_info_result_;
+  std::vector<mojom::NetworkRequestResult> set_feature_status_results_;
 
   std::unique_ptr<FakeDeviceSyncObserver> fake_device_sync_observer_;
   std::unique_ptr<DeviceSyncBase> device_sync_;
 
   base::HistogramTester histogram_tester_;
 
-  bool use_v2_enrollment_;
-  bool use_v2_device_sync_;
   base::test::ScopedFeatureList scoped_feature_list_;
 
   DISALLOW_COPY_AND_ASSIGN(DeviceSyncServiceTest);
@@ -1624,6 +1686,172 @@
       "MultiDevice.DeviceSyncService.SetSoftwareFeatureState.Result", true, 0);
 }
 
+TEST_P(DeviceSyncServiceTest, SetFeatureStatus_Success) {
+  InitializeServiceSuccessfully();
+
+  EXPECT_EQ(0u, fake_feature_status_setter()->requests().size());
+
+  multidevice::RemoteDevice device_for_test = test_devices()[0];
+
+  // Exclusively enable kBetterTogetherHost for the device.
+  CallSetFeatureStatus(device_for_test.instance_id,
+                       multidevice::SoftwareFeature::kBetterTogetherHost,
+                       FeatureStatusChange::kEnableExclusively);
+  EXPECT_EQ(1u, fake_feature_status_setter()->requests().size());
+  EXPECT_EQ(multidevice::SoftwareFeature::kBetterTogetherHost,
+            fake_feature_status_setter()->requests()[0].feature);
+  EXPECT_EQ(FeatureStatusChange::kEnableExclusively,
+            fake_feature_status_setter()->requests()[0].status_change);
+  EXPECT_TRUE(fake_feature_status_setter()->requests()[0].success_callback);
+  EXPECT_TRUE(fake_feature_status_setter()->requests()[0].error_callback);
+
+  // The DeviceSyncImpl::SetFeatureStatus() callback has not yet been invoked.
+  EXPECT_TRUE(set_feature_status_results().empty());
+
+  // Now, invoke the CryptAuthFeatureStatusSetter success callback.
+  std::move(fake_feature_status_setter()->requests()[0].success_callback).Run();
+
+  // The DeviceSyncImpl::SetFeatureStatus() callback still has not yet been
+  // invoked since a device sync has not confirmed the feature state change yet.
+  EXPECT_TRUE(set_feature_status_results().empty());
+
+  // Simulate a sync which includes the device with the correct "enabled" state.
+  device_for_test
+      .software_features[multidevice::SoftwareFeature::kBetterTogetherHost] =
+      multidevice::SoftwareFeatureState::kEnabled;
+  SimulateSync(true /* success */, {device_for_test});
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_EQ(1u, set_feature_status_results().size());
+  EXPECT_EQ(mojom::NetworkRequestResult::kSuccess,
+            set_feature_status_results()[0]);
+}
+
+TEST_P(DeviceSyncServiceTest,
+       SetFeatureStatus_RequestSucceedsButDoesNotTakeEffect) {
+  InitializeServiceSuccessfully();
+
+  // Expected device feature states after SetFeatureStatus() calls:
+  // * Device 0 has kSmartLockHost disabled.
+  // * Device 1 has kSmartLockHost disabled.
+  // * Device 2 has kBetterTogetherHost enabled exclusively.
+  // * Device 3 has kInstantTetheringHost enabled.
+  // * Device 4 has kMessagesForWebHost disabled.
+  multidevice::RemoteDeviceList expected_remote_devices =
+      multidevice::CreateRemoteDeviceListForTest(5u);
+  expected_remote_devices[0]
+      .software_features[multidevice::SoftwareFeature::kSmartLockHost] =
+      multidevice::SoftwareFeatureState::kSupported;
+  expected_remote_devices[1]
+      .software_features[multidevice::SoftwareFeature::kSmartLockHost] =
+      multidevice::SoftwareFeatureState::kSupported;
+  expected_remote_devices[2]
+      .software_features[multidevice::SoftwareFeature::kBetterTogetherHost] =
+      multidevice::SoftwareFeatureState::kEnabled;
+  expected_remote_devices[3]
+      .software_features[multidevice::SoftwareFeature::kInstantTetheringHost] =
+      multidevice::SoftwareFeatureState::kEnabled;
+  expected_remote_devices[4]
+      .software_features[multidevice::SoftwareFeature::kMessagesForWebHost] =
+      multidevice::SoftwareFeatureState::kSupported;
+
+  CallSetFeatureStatus(expected_remote_devices[0].instance_id,
+                       multidevice::SoftwareFeature::kSmartLockHost,
+                       FeatureStatusChange::kDisable);
+  CallSetFeatureStatus(expected_remote_devices[1].instance_id,
+                       multidevice::SoftwareFeature::kSmartLockHost,
+                       FeatureStatusChange::kDisable);
+  CallSetFeatureStatus(expected_remote_devices[2].instance_id,
+                       multidevice::SoftwareFeature::kBetterTogetherHost,
+                       FeatureStatusChange::kEnableExclusively);
+  CallSetFeatureStatus(expected_remote_devices[3].instance_id,
+                       multidevice::SoftwareFeature::kInstantTetheringHost,
+                       FeatureStatusChange::kEnableNonExclusively);
+  CallSetFeatureStatus(expected_remote_devices[4].instance_id,
+                       multidevice::SoftwareFeature::kMessagesForWebHost,
+                       FeatureStatusChange::kDisable);
+  EXPECT_EQ(5u, fake_feature_status_setter()->requests().size());
+
+  // The DeviceSyncImpl::SetFeatureStatus() callbacks have not yet been invoked.
+  EXPECT_TRUE(set_feature_status_results().empty());
+
+  // Now, invoke the CryptAuthFeatureStatusSetter success callbacks.
+  std::move(fake_feature_status_setter()->requests()[0].success_callback).Run();
+  std::move(fake_feature_status_setter()->requests()[1].success_callback).Run();
+  std::move(fake_feature_status_setter()->requests()[2].success_callback).Run();
+  std::move(fake_feature_status_setter()->requests()[3].success_callback).Run();
+  std::move(fake_feature_status_setter()->requests()[4].success_callback).Run();
+
+  // The DeviceSyncImpl::SetFeatureStatus() callbacks still have not been
+  // invoked since a DeviceSync has not confirmed any of the requested feature
+  // state changes yet.
+  EXPECT_TRUE(set_feature_status_results().empty());
+
+  // Simulate a DeviceSync which returns unexpected device feature states:
+  // * Device 0 not in list of devices.
+  // * Device 1 missing kSmartLockHost entry in the feature list.
+  // * Device 2 has kBetterTogetherHost enabled but not exclusively since device
+  //   1 also has it enabled.
+  // * Device 3 does not have kInstantTetheringHost enabled.
+  // * Device 4 does not have kMessagesForWebHost disabled.
+  multidevice::RemoteDeviceList remote_devices_from_first_sync =
+      multidevice::CreateRemoteDeviceListForTest(5u);
+  remote_devices_from_first_sync[1]
+      .software_features[multidevice::SoftwareFeature::kBetterTogetherHost] =
+      multidevice::SoftwareFeatureState::kEnabled;
+  remote_devices_from_first_sync[2]
+      .software_features[multidevice::SoftwareFeature::kBetterTogetherHost] =
+      multidevice::SoftwareFeatureState::kEnabled;
+  remote_devices_from_first_sync[3]
+      .software_features[multidevice::SoftwareFeature::kInstantTetheringHost] =
+      multidevice::SoftwareFeatureState::kSupported;
+  remote_devices_from_first_sync[4]
+      .software_features[multidevice::SoftwareFeature::kMessagesForWebHost] =
+      multidevice::SoftwareFeatureState::kEnabled;
+  remote_devices_from_first_sync.erase(remote_devices_from_first_sync.begin());
+
+  SimulateSync(true /* success */, remote_devices_from_first_sync);
+  base::RunLoop().RunUntilIdle();
+
+  // The DeviceSyncImpl::SetFeatureStatus() callbacks still have not yet been
+  // invoked since the latest DeviceSync did not reflect the requested feature
+  // state changes.
+  EXPECT_EQ(0u, set_feature_status_results().size());
+
+  // Simulate a DeviceSync which returns the expected device feature states:
+  EXPECT_TRUE(CallForceSyncNow());
+  SimulateSync(true /* success */, expected_remote_devices);
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_EQ(5u, set_feature_status_results().size());
+  for (mojom::NetworkRequestResult result : set_feature_status_results())
+    EXPECT_EQ(mojom::NetworkRequestResult::kSuccess, result);
+}
+
+TEST_P(DeviceSyncServiceTest, SetFeatureStatus_Error) {
+  InitializeServiceSuccessfully();
+
+  multidevice::RemoteDevice device_for_test = test_devices()[0];
+
+  // Attempt to exclusively enable kBetterTogetherHost for the device.
+  CallSetFeatureStatus(device_for_test.instance_id,
+                       multidevice::SoftwareFeature::kBetterTogetherHost,
+                       FeatureStatusChange::kEnableExclusively);
+
+  // The DeviceSyncImpl::SetFeatureStatus() callback has not yet been invoked.
+  EXPECT_TRUE(set_feature_status_results().empty());
+
+  // Now, invoke the CryptAuthFeatureStatusSetter error callback.
+  std::move(fake_feature_status_setter()->requests()[0].error_callback)
+      .Run(NetworkRequestError::kBadRequest);
+
+  // The DeviceSyncImpl::SetFeatureStatus() callback is invoked with the same
+  // error code.
+  ASSERT_EQ(1u, set_feature_status_results().size());
+  EXPECT_EQ(mojom::NetworkRequestResult::kBadRequest,
+            set_feature_status_results()[0]);
+}
+
 TEST_P(DeviceSyncServiceTest, FindEligibleDevices) {
   InitializeServiceSuccessfully();
 
diff --git a/chromeos/services/device_sync/fake_device_sync.cc b/chromeos/services/device_sync/fake_device_sync.cc
index e028c44..933f2e4 100644
--- a/chromeos/services/device_sync/fake_device_sync.cc
+++ b/chromeos/services/device_sync/fake_device_sync.cc
@@ -39,6 +39,12 @@
   set_software_feature_state_callback_queue_.pop();
 }
 
+void FakeDeviceSync::InvokePendingSetFeatureStatusCallback(
+    mojom::NetworkRequestResult result_code) {
+  std::move(set_feature_status_callback_queue_.front()).Run(result_code);
+  set_feature_status_callback_queue_.pop();
+}
+
 void FakeDeviceSync::InvokePendingFindEligibleDevicesCallback(
     mojom::NetworkRequestResult result_code,
     mojom::FindEligibleDevicesResponsePtr find_eligible_devices_response_ptr) {
@@ -89,6 +95,13 @@
   set_software_feature_state_callback_queue_.push(std::move(callback));
 }
 
+void FakeDeviceSync::SetFeatureStatus(const std::string& device_instance_id,
+                                      multidevice::SoftwareFeature feature,
+                                      FeatureStatusChange status_change,
+                                      SetFeatureStatusCallback callback) {
+  set_feature_status_callback_queue_.push(std::move(callback));
+}
+
 void FakeDeviceSync::FindEligibleDevices(
     multidevice::SoftwareFeature software_feature,
     FindEligibleDevicesCallback callback) {
diff --git a/chromeos/services/device_sync/fake_device_sync.h b/chromeos/services/device_sync/fake_device_sync.h
index f7ab85f..493ed5d 100644
--- a/chromeos/services/device_sync/fake_device_sync.h
+++ b/chromeos/services/device_sync/fake_device_sync.h
@@ -41,6 +41,8 @@
           remote_devices);
   void InvokePendingSetSoftwareFeatureStateCallback(
       mojom::NetworkRequestResult result_code);
+  void InvokePendingSetFeatureStatusCallback(
+      mojom::NetworkRequestResult result_code);
   void InvokePendingFindEligibleDevicesCallback(
       mojom::NetworkRequestResult result_code,
       mojom::FindEligibleDevicesResponsePtr find_eligible_devices_response_ptr);
@@ -62,6 +64,10 @@
       bool enabled,
       bool is_exclusive,
       SetSoftwareFeatureStateCallback callback) override;
+  void SetFeatureStatus(const std::string& device_instance_id,
+                        multidevice::SoftwareFeature feature,
+                        FeatureStatusChange status_change,
+                        SetFeatureStatusCallback callback) override;
   void FindEligibleDevices(multidevice::SoftwareFeature software_feature,
                            FindEligibleDevicesCallback callback) override;
   void GetDebugInfo(GetDebugInfoCallback callback) override;
@@ -77,6 +83,7 @@
   std::queue<GetSyncedDevicesCallback> get_synced_devices_callback_queue_;
   std::queue<SetSoftwareFeatureStateCallback>
       set_software_feature_state_callback_queue_;
+  std::queue<SetFeatureStatusCallback> set_feature_status_callback_queue_;
   std::queue<FindEligibleDevicesCallback> find_eligible_devices_callback_queue_;
   std::queue<GetDevicesActivityStatusCallback>
       get_devices_activity_status_callback_queue_;
diff --git a/chromeos/services/device_sync/public/mojom/device_sync.mojom b/chromeos/services/device_sync/public/mojom/device_sync.mojom
index 9b572cd..283af3c 100644
--- a/chromeos/services/device_sync/public/mojom/device_sync.mojom
+++ b/chromeos/services/device_sync/public/mojom/device_sync.mojom
@@ -147,12 +147,26 @@
   // SoftwareFeature::EASY_UNLOCK_HOST and |enabled| = false, |public_key| is
   // ignored, because that combination of arguments disables EASY_UNLOCK_HOST on
   // all of the user's devices.
+  //
+  // TODO(https://crbug.com/1019206): Remove this function when v1 DeviceSync
+  // is deprecated.
   SetSoftwareFeatureState(
       string device_public_key,
       chromeos.multidevice.mojom.SoftwareFeature software_feature,
       bool enabled,
       bool is_exclusive) => (NetworkRequestResult result_code);
 
+  // Enables or disables |feature| for the device with Instance ID
+  // |device_instance_id|.
+  //
+  // This function can only affect devices using CryptAuth v2 DeviceSync since
+  // it requires an Instance ID. See SetSoftwareFeatureState() for the v1
+  // DeviceSync analog.
+  SetFeatureStatus(
+      string device_instance_id,
+      chromeos.multidevice.mojom.SoftwareFeature feature,
+      FeatureStatusChange status_change) => (NetworkRequestResult result_code);
+
   // Finds devices which are eligible for the given feature. When this function
   // is invoked, a network request will be sent to each eligible device which
   // instructs that device to enable BLE advertising; thus, this function can be
@@ -161,6 +175,9 @@
   // On success, this function returns a null error_code with a valid response
   // to the callback; on error, it returns a valid error_code string indicating
   // the reason for failure along with a null response.
+  //
+  // TODO(https://crbug.com/1019206): Remove this function when v1 DeviceSync
+  // is deprecated.
   FindEligibleDevices(
       chromeos.multidevice.mojom.SoftwareFeature software_feature) =>
           (NetworkRequestResult result_code,
diff --git a/components/.eslintrc.js b/components/.eslintrc.js
new file mode 100644
index 0000000..e5323a2b
--- /dev/null
+++ b/components/.eslintrc.js
@@ -0,0 +1,9 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module.exports = {
+  'rules': {
+    'no-restricted-properties': 'off',
+  },
+};
diff --git a/components/OWNERS b/components/OWNERS
index 26e9def..cbbf1d9 100644
--- a/components/OWNERS
+++ b/components/OWNERS
@@ -37,6 +37,10 @@
 per-file version_ui_strings.grdp=file://components/version_ui/OWNERS
 per-file web_contents_delegate_android_strings.grdp=file://components/embedder_support/android/delegate/OWNERS
 
+# For web_dev_style related changes.
+per-file .eslintrc.js=file://ui/webui/PLATFORM_OWNERS
+per-file PRESUBMIT.py=file://ui/webui/PLATFORM_OWNERS
+
 # Translation artifacts:
 per-file *.xtb=file://tools/translation/TRANSLATION_OWNERS
 
diff --git a/components/PRESUBMIT.py b/components/PRESUBMIT.py
new file mode 100644
index 0000000..80340ee9
--- /dev/null
+++ b/components/PRESUBMIT.py
@@ -0,0 +1,48 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  return _CommonChecks(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  return _CommonChecks(input_api, output_api)
+
+
+def _CheckSvgsOptimized(input_api, output_api):
+  results = []
+  try:
+    import sys
+    old_sys_path = sys.path[:]
+    cwd = input_api.PresubmitLocalPath()
+    sys.path += [input_api.os_path.join(cwd, '..', 'tools')]
+    from resources import svgo_presubmit
+    results += svgo_presubmit.CheckOptimized(input_api, output_api)
+  finally:
+    sys.path = old_sys_path
+  return results
+
+
+def _CheckWebDevStyle(input_api, output_api):
+  results = []
+  try:
+    import sys
+    old_sys_path = sys.path[:]
+    cwd = input_api.PresubmitLocalPath()
+    sys.path += [input_api.os_path.join(cwd, '..', 'tools')]
+    from web_dev_style import presubmit_support
+    results += presubmit_support.CheckStyle(input_api, output_api)
+  finally:
+    sys.path = old_sys_path
+  return results
+
+
+def _CommonChecks(input_api, output_api):
+  results = []
+  results += _CheckSvgsOptimized(input_api, output_api)
+  results += _CheckWebDevStyle(input_api, output_api)
+  results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api,
+                                                         check_js=True)
+  return results
diff --git a/components/arc/intent_helper/arc_intent_helper_bridge.cc b/components/arc/intent_helper/arc_intent_helper_bridge.cc
index 5c3a986b..c6dcbea 100644
--- a/components/arc/intent_helper/arc_intent_helper_bridge.cc
+++ b/components/arc/intent_helper/arc_intent_helper_bridge.cc
@@ -248,27 +248,6 @@
   ash::NewWindowDelegate::GetInstance()->CloseCameraApp();
 }
 
-void ArcIntentHelperBridge::HandleCameraResult(
-    uint32_t intent_id,
-    arc::mojom::CameraIntentAction action,
-    const std::vector<uint8_t>& data,
-    arc::mojom::IntentHelperInstance::HandleCameraResultCallback callback) {
-  auto* arc_service_manager = arc::ArcServiceManager::Get();
-  arc::mojom::IntentHelperInstance* instance = nullptr;
-  if (arc_service_manager) {
-    instance = ARC_GET_INSTANCE_FOR_METHOD(
-        arc_service_manager->arc_bridge_service()->intent_helper(),
-        HandleCameraResult);
-  }
-  if (!instance) {
-    LOG(ERROR) << "Failed to get instance for HandleCameraResult().";
-    std::move(callback).Run(false);
-    return;
-  }
-
-  instance->HandleCameraResult(intent_id, action, data, std::move(callback));
-}
-
 ArcIntentHelperBridge::GetResult ArcIntentHelperBridge::GetActivityIcons(
     const std::vector<ActivityName>& activities,
     OnIconsReadyCallback callback) {
@@ -310,6 +289,27 @@
   return observer_list_.HasObserver(observer);
 }
 
+void ArcIntentHelperBridge::HandleCameraResult(
+    uint32_t intent_id,
+    arc::mojom::CameraIntentAction action,
+    const std::vector<uint8_t>& data,
+    arc::mojom::IntentHelperInstance::HandleCameraResultCallback callback) {
+  auto* arc_service_manager = arc::ArcServiceManager::Get();
+  arc::mojom::IntentHelperInstance* instance = nullptr;
+  if (arc_service_manager) {
+    instance = ARC_GET_INSTANCE_FOR_METHOD(
+        arc_service_manager->arc_bridge_service()->intent_helper(),
+        HandleCameraResult);
+  }
+  if (!instance) {
+    LOG(ERROR) << "Failed to get instance for HandleCameraResult().";
+    std::move(callback).Run(false);
+    return;
+  }
+
+  instance->HandleCameraResult(intent_id, action, data, std::move(callback));
+}
+
 // static
 bool ArcIntentHelperBridge::IsIntentHelperPackage(
     const std::string& package_name) {
diff --git a/components/arc/intent_helper/arc_intent_helper_bridge.h b/components/arc/intent_helper/arc_intent_helper_bridge.h
index ad40a80d..9656417 100644
--- a/components/arc/intent_helper/arc_intent_helper_bridge.h
+++ b/components/arc/intent_helper/arc_intent_helper_bridge.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_ARC_INTENT_HELPER_ARC_INTENT_HELPER_BRIDGE_H_
 #define COMPONENTS_ARC_INTENT_HELPER_ARC_INTENT_HELPER_BRIDGE_H_
 
+#include <map>
 #include <memory>
 #include <set>
 #include <string>
@@ -60,6 +61,11 @@
   void AddObserver(ArcIntentHelperObserver* observer);
   void RemoveObserver(ArcIntentHelperObserver* observer);
   bool HasObserver(ArcIntentHelperObserver* observer) const;
+  void HandleCameraResult(
+      uint32_t intent_id,
+      arc::mojom::CameraIntentAction action,
+      const std::vector<uint8_t>& data,
+      arc::mojom::IntentHelperInstance::HandleCameraResultCallback callback);
 
   // mojom::IntentHelperHost
   void OnIconInvalidated(const std::string& package_name) override;
@@ -85,11 +91,6 @@
                        bool should_down_scale,
                        bool is_secure) override;
   void CloseCameraApp() override;
-  void HandleCameraResult(
-      uint32_t intent_id,
-      arc::mojom::CameraIntentAction action,
-      const std::vector<uint8_t>& data,
-      arc::mojom::IntentHelperInstance::HandleCameraResultCallback callback);
   void OnIntentFiltersUpdatedForPackage(
       const std::string& package_name,
       std::vector<IntentFilter> intent_filters) override;
diff --git a/components/bookmarks/browser/bookmark_model.cc b/components/bookmarks/browser/bookmark_model.cc
index 1e1c3fce..3dfde0e 100644
--- a/components/bookmarks/browser/bookmark_model.cc
+++ b/components/bookmarks/browser/bookmark_model.cc
@@ -205,7 +205,10 @@
   DCHECK(parent);
   size_t index = size_t{parent->GetIndexOf(node)};
   DCHECK_NE(size_t{-1}, index);
-  DCHECK(!is_permanent_node(node));
+
+  // Removing a permanent node is problematic and can cause crashes elsewhere
+  // that are difficult to trace back.
+  CHECK(!is_permanent_node(node)) << "for type " << node->type();
 
   for (BookmarkModelObserver& observer : observers_)
     observer.OnWillRemoveBookmarks(this, parent, index, node);
diff --git a/components/chrome_cleaner/public/constants/constants.cc b/components/chrome_cleaner/public/constants/constants.cc
index 98c0b89..345d481c 100644
--- a/components/chrome_cleaner/public/constants/constants.cc
+++ b/components/chrome_cleaner/public/constants/constants.cc
@@ -9,7 +9,6 @@
 // Command line switches.
 const char kChromeChannelSwitch[] = "chrome-channel";
 const char kChromeExePathSwitch[] = "chrome-exe-path";
-const char kChromeMojoPipeTokenSwitch[] = "chrome-mojo-pipe-token";
 const char kChromePromptSwitch[] = "chrome-prompt";
 const char kChromeReadHandleSwitch[] = "chrome-read-handle";
 const char kChromeWriteHandleSwitch[] = "chrome-write-handle";
diff --git a/components/chrome_cleaner/public/constants/constants.h b/components/chrome_cleaner/public/constants/constants.h
index 5beced3..1a1d84da 100644
--- a/components/chrome_cleaner/public/constants/constants.h
+++ b/components/chrome_cleaner/public/constants/constants.h
@@ -23,10 +23,6 @@
 // The path to Chrome's executable.
 extern const char kChromeExePathSwitch[];
 
-// The Mojo pipe token for IPC communication between the Software Reporter and
-// Chrome.
-extern const char kChromeMojoPipeTokenSwitch[];
-
 // Indicates that a cleaner run was started by Chrome.
 extern const char kChromePromptSwitch[];
 
diff --git a/components/chrome_cleaner/public/interfaces/BUILD.gn b/components/chrome_cleaner/public/interfaces/BUILD.gn
deleted file mode 100644
index fa6dda81..0000000
--- a/components/chrome_cleaner/public/interfaces/BUILD.gn
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright 2017 The Chromium Authors. All Rights Reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-# TODO(crbug.com/969139): This target uses a non-standard name because it is
-# mirrored at
-# https://chromium.googlesource.com/chromium/src/components/chrome_cleaner so
-# that it can be used from the internal Chrome Cleanup tool repo which is
-# released on a different schedule from Chrome. Coordinating the renaming from
-# interfaces -> mojom in the internal repo is tricky. Once crbug.com/969139 is
-# implemented we we will delete this interface anyway.
-mojom("interfaces") {
-  sources = [
-    "chrome_prompt.mojom",
-  ]
-
-  # NOTE: We avoid scrambling message IDs here because these messages cross an
-  # IPC boundary to an external program built from a different source tree.
-  scramble_message_ids = false
-}
diff --git a/components/chrome_cleaner/public/interfaces/OWNERS b/components/chrome_cleaner/public/interfaces/OWNERS
deleted file mode 100644
index 74b69f8..0000000
--- a/components/chrome_cleaner/public/interfaces/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-per-file *.mojom=set noparent
-per-file *.mojom=file://ipc/SECURITY_OWNERS
-
-# TEAM: security-dev@chromium.org
-# COMPONENT: UI>Browser>Preferences>Protector
diff --git a/components/chrome_cleaner/public/typemaps/DEPS b/components/chrome_cleaner/public/typemaps/DEPS
deleted file mode 100644
index b78e1d2..0000000
--- a/components/chrome_cleaner/public/typemaps/DEPS
+++ /dev/null
@@ -1,7 +0,0 @@
-# Allow the typemaps to access their dependencies.
-include_rules = [
-  '+base/containers/span.h',
-  '+base/files/file_path.h',
-  '+base/strings/string16.h',
-  '+build/build_config.h',
-]
diff --git a/components/crash/content/app/crashpad_android.cc b/components/crash/content/app/crashpad_android.cc
index 09191be..1183271d 100644
--- a/components/crash/content/app/crashpad_android.cc
+++ b/components/crash/content/app/crashpad_android.cc
@@ -287,8 +287,16 @@
 // adjacent to it.
 bool GetHandlerTrampoline(std::string* handler_trampoline,
                           std::string* handler_library) {
+  // The linker doesn't support loading executables passed on its command
+  // line until Q.
+  if (!base::android::BuildInfo::GetInstance()->is_at_least_q()) {
+    return false;
+  }
+
   Dl_info info;
-  if (dladdr(reinterpret_cast<void*>(&GetHandlerTrampoline), &info) == 0) {
+  if (dladdr(reinterpret_cast<void*>(&GetHandlerTrampoline), &info) == 0 ||
+      dlsym(dlopen(info.dli_fname, RTLD_NOLOAD | RTLD_LAZY),
+            "CrashpadHandlerMain") == nullptr) {
     return false;
   }
 
@@ -498,15 +506,8 @@
     }
 
     if (!base::PathExists(handler_path)) {
-      // The linker doesn't support loading executables passed on its command
-      // line until Q.
-      if (base::android::BuildInfo::GetInstance()->is_at_least_q()) {
-        bool found_library =
-            GetHandlerTrampoline(&handler_trampoline_, &handler_library_);
-        DCHECK(found_library);
-      } else {
-        use_java_handler_ = true;
-      }
+      use_java_handler_ =
+          !GetHandlerTrampoline(&handler_trampoline_, &handler_library_);
     }
 
     if (!dump_at_crash) {
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats_unittest.cc
index 27a95bf..850e7edc 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "base/values.h"
+#include "build/build_config.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
@@ -972,7 +973,13 @@
   VerifyDailyDataSavingContentLengthPrefLists(nullptr, 0, nullptr, 0, 0);
 }
 
-TEST_F(DataReductionProxyCompressionStatsTest, WeeklyAggregateDataUse) {
+// Aggregate metrics recording was disabled on Android x86 in crbug.com/865373.
+#if defined(OS_ANDROID) && defined(ARCH_CPU_X86)
+#define MAYBE_WeeklyAggregateDataUse DISABLED_WeeklyAggregateDataUse
+#else
+#define MAYBE_WeeklyAggregateDataUse WeeklyAggregateDataUse
+#endif
+TEST_F(DataReductionProxyCompressionStatsTest, MAYBE_WeeklyAggregateDataUse) {
   const int32_t kDataUseKB = 100;
   base::HistogramTester histogram_tester;
 
@@ -1021,7 +1028,14 @@
       data_use_measurement::DataUseUserData::MAIN_FRAME_HTML, 0);
 }
 
-TEST_F(DataReductionProxyCompressionStatsTest, AggregateDataUseForwardWeeks) {
+// Aggregate metrics recording was disabled on Android x86 in crbug.com/865373.
+#if defined(OS_ANDROID) && defined(ARCH_CPU_X86)
+#define MAYBE_AggregateDataUseForwardWeeks DISABLED_AggregateDataUseForwardWeeks
+#else
+#define MAYBE_AggregateDataUseForwardWeeks AggregateDataUseForwardWeeks
+#endif
+TEST_F(DataReductionProxyCompressionStatsTest,
+       MAYBE_AggregateDataUseForwardWeeks) {
   const int32_t kMainFrameKB = 100;
   const int32_t kNonMainFrameKB = 101;
   base::HistogramTester histogram_tester;
diff --git a/components/flags_ui/OWNERS b/components/flags_ui/OWNERS
index 6fc413e..f93ac61e 100644
--- a/components/flags_ui/OWNERS
+++ b/components/flags_ui/OWNERS
@@ -1,5 +1,6 @@
 file://ui/webui/PLATFORM_OWNERS
 
 asvitkine@chromium.org
+ellyjones@chromium.org
 
 # COMPONENT: UI>Browser>WebUI
diff --git a/components/gcm_driver/web_push_common.cc b/components/gcm_driver/web_push_common.cc
index 536b99bf..2ba54dc 100644
--- a/components/gcm_driver/web_push_common.cc
+++ b/components/gcm_driver/web_push_common.cc
@@ -21,4 +21,8 @@
   base::UmaHistogramCounts10000("GCM.SendWebPushMessagePayloadSize", size);
 }
 
+void LogSendWebPushMessageStatusCode(int status_code) {
+  base::UmaHistogramSparse("GCM.SendWebPushMessageStatusCode", status_code);
+}
+
 }  // namespace gcm
diff --git a/components/gcm_driver/web_push_common.h b/components/gcm_driver/web_push_common.h
index 743a87a7..e7d364d 100644
--- a/components/gcm_driver/web_push_common.h
+++ b/components/gcm_driver/web_push_common.h
@@ -43,6 +43,9 @@
 // web push message is sent.
 void LogSendWebPushMessagePayloadSize(int size);
 
+// Logs the network error or status code after a web push message is sent.
+void LogSendWebPushMessageStatusCode(int status_code);
+
 }  // namespace gcm
 
 #endif  // COMPONENTS_GCM_DRIVER_WEB_PUSH_COMMON_H_
diff --git a/components/gcm_driver/web_push_sender.cc b/components/gcm_driver/web_push_sender.cc
index b85e8543..67df65a6 100644
--- a/components/gcm_driver/web_push_sender.cc
+++ b/components/gcm_driver/web_push_sender.cc
@@ -179,30 +179,32 @@
     WebPushCallback callback,
     std::unique_ptr<std::string> response_body) {
   int net_error = url_loader->NetError();
-  if (net_error == net::ERR_INSUFFICIENT_RESOURCES) {
-    DLOG(ERROR) << "VAPID key invalid";
-    InvokeWebPushCallback(std::move(callback),
-                          SendWebPushMessageResult::kVapidKeyInvalid);
-    return;
-  }
-
   if (net_error != net::OK) {
-    DLOG(ERROR) << "Network Error: " << net_error;
-    InvokeWebPushCallback(std::move(callback),
-                          SendWebPushMessageResult::kNetworkError);
+    LogSendWebPushMessageStatusCode(net_error);
+    if (net_error == net::ERR_INSUFFICIENT_RESOURCES) {
+      DLOG(ERROR) << "VAPID key invalid";
+      InvokeWebPushCallback(std::move(callback),
+                            SendWebPushMessageResult::kVapidKeyInvalid);
+    } else {
+      DLOG(ERROR) << "Network Error: " << net_error;
+      InvokeWebPushCallback(std::move(callback),
+                            SendWebPushMessageResult::kNetworkError);
+    }
     return;
   }
 
-  scoped_refptr<net::HttpResponseHeaders> response_headers =
-      url_loader->ResponseInfo()->headers;
-  if (!url_loader->ResponseInfo() || !response_headers) {
+  if (!url_loader->ResponseInfo() || !url_loader->ResponseInfo()->headers) {
+    LogSendWebPushMessageStatusCode(net::OK);
     DLOG(ERROR) << "Response info not found";
     InvokeWebPushCallback(std::move(callback),
                           SendWebPushMessageResult::kServerError);
     return;
   }
 
+  scoped_refptr<net::HttpResponseHeaders> response_headers =
+      url_loader->ResponseInfo()->headers;
   int response_code = response_headers->response_code();
+  LogSendWebPushMessageStatusCode(response_code);
   if (response_code == net::HTTP_NOT_FOUND || response_code == net::HTTP_GONE) {
     DLOG(ERROR) << "Device no longer registered";
     InvokeWebPushCallback(std::move(callback),
@@ -244,7 +246,7 @@
 
   InvokeWebPushCallback(std::move(callback),
                         SendWebPushMessageResult::kSuccessful,
-                        location.substr(slash_pos + 1));
+                        /*message_id=*/location.substr(slash_pos + 1));
 }
 
 }  // namespace gcm
diff --git a/components/offline_pages/core/archive_validator_unittest.cc b/components/offline_pages/core/archive_validator_unittest.cc
index 20de231..f9ab9088 100644
--- a/components/offline_pages/core/archive_validator_unittest.cc
+++ b/components/offline_pages/core/archive_validator_unittest.cc
@@ -168,7 +168,8 @@
 }
 
 #if defined(OS_ANDROID)
-TEST_F(ArchiveValidatorTest, ValidateContentUri) {
+// Flaky. https://crbug.com/1022322
+TEST_F(ArchiveValidatorTest, DISABLED_ValidateContentUri) {
   base::FilePath content_uri_path = GetContentUriPathForTest();
   EXPECT_TRUE(ArchiveValidator::ValidateFile(
       content_uri_path, kSizeForTestContentUri, kExpectedDigestForContentUri));
diff --git a/components/omnibox/browser/autocomplete_result.cc b/components/omnibox/browser/autocomplete_result.cc
index 77717b9..90eeec8 100644
--- a/components/omnibox/browser/autocomplete_result.cc
+++ b/components/omnibox/browser/autocomplete_result.cc
@@ -201,6 +201,8 @@
   for (auto i(matches_.begin()); i != matches_.end(); ++i)
     i->ComputeStrippedDestinationURL(input, template_url_service);
 
+  CompareWithDemoteByType<AutocompleteMatch> comparing_object(
+      input.current_page_classification());
 #if !(defined(OS_ANDROID) || defined(OS_IOS))
   // Do not cull the tail suggestions for zero prefix query suggetions of
   // chromeOS launcher or NTP, since there won't be any default match in this
@@ -210,7 +212,7 @@
              metrics::OmniboxEventProto::CHROMEOS_APP_LIST ||
          BaseSearchProvider::IsNTPPage(input.current_page_classification())))) {
     // Wipe tail suggestions if not exclusive (minus default match).
-    MaybeCullTailSuggestions(&matches_);
+    MaybeCullTailSuggestions(&matches_, comparing_object);
   }
 #endif
   DemoteOnDeviceSearchSuggestions();
@@ -218,8 +220,6 @@
   DeduplicateMatches(input.current_page_classification(), &matches_);
 
   // Sort and trim to the most relevant GetMaxMatches() matches.
-  CompareWithDemoteByType<AutocompleteMatch> comparing_object(
-      input.current_page_classification());
   std::sort(matches_.begin(), matches_.end(), comparing_object);
 
   // Find the best match and rotate it to the front to become the default match.
@@ -530,7 +530,7 @@
   // the highest-relevance, allowed-to-be-default match while ignoring type
   // demotion, as we do when IsPreserveDefaultMatchScoreEnabled is true, we need
   // to explicitly find the highest relevance match rather than just accepting
-  // the first allowed-to-be--default match in the list.
+  // the first allowed-to-be-default match in the list.
   // The goal of this behavior is to ensure that in situations where the user
   // expects to see a commonly visited URL as the default match, the URL is not
   // suppressed by type demotion.
@@ -554,7 +554,7 @@
     return best;
   } else {
     return std::find_if(matches->begin(), matches->end(), [](const auto& m) {
-      return m.allowed_to_be_default_match;
+      return m.allowed_to_be_default_match && !m.IsSubMatch();
     });
   }
 }
@@ -797,19 +797,39 @@
 }
 
 // static
-void AutocompleteResult::MaybeCullTailSuggestions(ACMatches* matches) {
+void AutocompleteResult::MaybeCullTailSuggestions(
+    ACMatches* matches,
+    const CompareWithDemoteByType<AutocompleteMatch>& comparing_object) {
+  // This function implements the following logic:
+  // ('E' == 'There exists', '!E' == 'There does not exist')
+  // 1) !E default non-tail and E tail default? remove non-tails
+  // 2) !E any tails at all? do nothing
+  // 3) E default non-tail and other non-tails? remove tails
+  // 4) E default non-tail and no other non-tails? mark tails as non-default
+  // 5) E non-default non-tails? remove non-tails
   std::function<bool(const AutocompleteMatch&)> is_tail =
       [](const AutocompleteMatch& match) {
         return match.type == ACMatchType::SEARCH_SUGGEST_TAIL;
       };
-  auto non_tail_default = std::find_if(
-      matches->begin(), matches->end(), [&](const AutocompleteMatch& match) {
-        return match.allowed_to_be_default_match && !is_tail(match);
-      });
-  bool tail_default_exists = std::any_of(
-      matches->begin(), matches->end(), [&](const AutocompleteMatch& match) {
-        return match.allowed_to_be_default_match && is_tail(match);
-      });
+  auto default_non_tail = matches->end();
+  auto default_tail = matches->end();
+  bool other_non_tails = false, any_tails = false;
+  for (auto i = matches->begin(); i != matches->end(); ++i) {
+    if (comparing_object.GetDemotedRelevance(*i) == 0)
+      continue;
+    if (!is_tail(*i)) {
+      // We allow one default non-tail match. For non-default matches,
+      // don't consider if we'd remove them later.
+      if (default_non_tail == matches->end() && i->allowed_to_be_default_match)
+        default_non_tail = i;
+      else
+        other_non_tails = true;
+    } else {
+      any_tails = true;
+      if (default_tail == matches->end() && i->allowed_to_be_default_match)
+        default_tail = i;
+    }
+  }
   // If the only default matches are tail suggestions, let them remain and
   // instead remove the non-tail suggestions.  This is necessary because we do
   // not want to display tail suggestions mixed with other suggestions in the
@@ -818,39 +838,35 @@
   // default match--the non-tail ones much go.  This situation though is
   // unlikely, as we normally would expect the search-what-you-typed suggestion
   // as a default match (and that's a non-tail suggestion).
-  if (tail_default_exists && non_tail_default == matches->end()) {
-    base::EraseIf(*matches, [&is_tail](const AutocompleteMatch& match) {
-      return !is_tail(match);
-    });
+  // 1) above.
+  if (default_tail != matches->end() && default_non_tail == matches->end()) {
+    base::EraseIf(*matches, std::not1(is_tail));
     return;
   }
-  // Determine if there are both tail and non-tail matches, excluding the
-  // non-tail default match.
-  bool any_tail = false, any_non_tail = false;
-  for (auto i = matches->begin();
-       i != matches->end() && !(any_tail && any_non_tail); ++i) {
-    // We allow one default non-tail match.
-    if (i != non_tail_default) {
-      if (is_tail(*i))
-        any_tail = true;
-      else
-        any_non_tail = true;
-    }
-  }
+  // 2) above.
+  if (!any_tails)
+    return;
   // If both tail and non-tail matches, remove tail. Note that this can
   // remove the highest rated suggestions.
-  if (any_tail) {
-    if (any_non_tail) {
+  if (default_non_tail != matches->end()) {
+    // 3) above.
+    if (other_non_tails) {
       base::EraseIf(*matches, is_tail);
     } else {
-      // We want the non-tail default match to be first. Mark tail suggestions
-      // as not a legal default match, so that the default match will be moved
-      // up explicitly.
+      // 4) above.
+      // We want the non-tail default match to be placed first. Mark tail
+      // suggestions as not a legal default match, so that the default match
+      // will be moved up explicitly.
       for (auto& match : *matches) {
         if (is_tail(match))
           match.allowed_to_be_default_match = false;
       }
     }
+  } else if (other_non_tails && default_tail == matches->end()) {
+    // 5) above.
+    // If there are no defaults at all, but non-tail suggestions exist, remove
+    // the tail suggestions.
+    base::EraseIf(*matches, is_tail);
   }
 }
 
diff --git a/components/omnibox/browser/autocomplete_result.h b/components/omnibox/browser/autocomplete_result.h
index 9b67f558..3966c8b 100644
--- a/components/omnibox/browser/autocomplete_result.h
+++ b/components/omnibox/browser/autocomplete_result.h
@@ -193,7 +193,9 @@
   // If there are both tail and non-tail suggestions (ignoring one default
   // match), remove the tail suggestions.  If the only default matches are tail
   // suggestions, remove the non-tail suggestions.
-  static void MaybeCullTailSuggestions(ACMatches* matches);
+  static void MaybeCullTailSuggestions(
+      ACMatches* matches,
+      const CompareWithDemoteByType<AutocompleteMatch>& comparing_object);
 
   // Populates |provider_to_matches| from |matches_|. This AutocompleteResult
   // should not be used after the 'move' version.
diff --git a/components/omnibox/browser/autocomplete_result_unittest.cc b/components/omnibox/browser/autocomplete_result_unittest.cc
index a1ee48b5..e5c4952 100644
--- a/components/omnibox/browser/autocomplete_result_unittest.cc
+++ b/components/omnibox/browser/autocomplete_result_unittest.cc
@@ -524,6 +524,76 @@
   }
 }
 
+TEST_F(AutocompleteResultTest, SortAndCullZeroRelevanceSuggestions) {
+  // clang-format off
+  TestData data[] = {
+      {1, 1, 1000, true},   // A default non-tail suggestion.
+      {2, 1, 0,    true},   // A no-relevance default non-tail suggestion.
+      {3, 1, 1100, true},   // Default tail
+      {4, 1, 1000, false},  // Tail
+      {5, 1, 1300, false},  // Tail
+      {6, 1, 0,    false},  // No-relevance tail suggestion.
+  };
+  // clang-format on
+
+  ACMatches matches;
+  PopulateAutocompleteMatches(data, base::size(data), &matches);
+  for (size_t i = 2; i < base::size(data); ++i)
+    matches[i].type = AutocompleteMatchType::SEARCH_SUGGEST_TAIL;
+
+  AutocompleteInput input(base::ASCIIToUTF16("a"),
+                          metrics::OmniboxEventProto::OTHER,
+                          TestSchemeClassifier());
+  AutocompleteResult result;
+  result.AppendMatches(input, matches);
+  result.SortAndCull(input, template_url_service_.get());
+
+  EXPECT_EQ(4UL, result.size());
+  EXPECT_NE(AutocompleteMatchType::SEARCH_SUGGEST_TAIL,
+            result.match_at(0)->type);
+  EXPECT_TRUE(result.match_at(0)->allowed_to_be_default_match);
+  for (size_t i = 1; i < 4; ++i) {
+    EXPECT_EQ(AutocompleteMatchType::SEARCH_SUGGEST_TAIL,
+              result.match_at(i)->type);
+    EXPECT_FALSE(result.match_at(i)->allowed_to_be_default_match);
+  }
+}
+
+TEST_F(AutocompleteResultTest, SortAndCullZeroRelevanceDefaultMatches) {
+  // clang-format off
+  TestData data[] = {
+      {1, 1, 0,    true},   // A zero-relevance default non-tail suggestion.
+      {2, 1, 1100, true},   // Default tail
+      {3, 1, 1000, false},  // Tail
+      {4, 1, 1300, false},  // Tail
+      {5, 1, 0,    false},  // No-relevance tail suggestion.
+  };
+  // clang-format on
+
+  ACMatches matches;
+  PopulateAutocompleteMatches(data, base::size(data), &matches);
+  for (size_t i = 1; i < base::size(data); ++i)
+    matches[i].type = AutocompleteMatchType::SEARCH_SUGGEST_TAIL;
+
+  AutocompleteInput input(base::ASCIIToUTF16("a"),
+                          metrics::OmniboxEventProto::OTHER,
+                          TestSchemeClassifier());
+  AutocompleteResult result;
+  result.AppendMatches(input, matches);
+  result.SortAndCull(input, template_url_service_.get());
+
+  // It should ignore the first suggestion, despite it being marked as
+  // allowed to be default.
+  EXPECT_EQ(3UL, result.size());
+  EXPECT_TRUE(result.match_at(0)->allowed_to_be_default_match);
+  for (size_t i = 0; i < 3; ++i) {
+    EXPECT_EQ(AutocompleteMatchType::SEARCH_SUGGEST_TAIL,
+              result.match_at(i)->type);
+    if (i > 0)
+      EXPECT_FALSE(result.match_at(i)->allowed_to_be_default_match);
+  }
+}
+
 #endif
 
 TEST_F(AutocompleteResultTest, SortAndCullOnlyTailSuggestions) {
diff --git a/components/password_manager/core/browser/import/csv_password.cc b/components/password_manager/core/browser/import/csv_password.cc
index 0a78a697..a2faf716 100644
--- a/components/password_manager/core/browser/import/csv_password.cc
+++ b/components/password_manager/core/browser/import/csv_password.cc
@@ -35,12 +35,12 @@
 
 CSVPassword::~CSVPassword() = default;
 
-bool CSVPassword::Parse(PasswordForm* form) const {
+CSVPassword::Status CSVPassword::Parse(PasswordForm* form) const {
   // |map_| must be an (1) injective and (2) surjective (3) partial map. (3) is
   // enforced by its type, (2) is checked later in the code and (1) follows from
   // (2) and the following size() check.
   if (map_.size() != kLabelCount)
-    return false;
+    return Status::kSemanticError;
 
   size_t field_idx = 0;
   CSVFieldParser parser(row_);
@@ -51,14 +51,14 @@
   while (parser.HasMoreFields()) {
     base::StringPiece field;
     if (!parser.NextField(&field))
-      return false;
+      return Status::kSyntaxError;
     auto meaning_it = map_.find(field_idx++);
     if (meaning_it == map_.end())
       continue;
     switch (meaning_it->second) {
       case Label::kOrigin:
         if (!base::IsStringASCII(field))
-          return false;
+          return Status::kSyntaxError;
         origin = GURL(field);
         break;
       case Label::kUsername:
@@ -74,9 +74,9 @@
   // username is permitted to be an empty string, while password and origin are
   // not.
   if (!origin.is_valid() || !username_set || password.empty())
-    return false;
+    return Status::kSemanticError;
   if (!form)
-    return true;
+    return Status::kOK;
   // There is currently no way to import non-HTML credentials.
   form->scheme = PasswordForm::Scheme::kHtml;
   // GURL::GetOrigin() returns an empty GURL for Android credentials due
@@ -89,13 +89,13 @@
   form->origin = std::move(origin);
   form->username_value = Convert(username);
   form->password_value = Convert(password);
-  return true;
+  return Status::kOK;
 }
 
 PasswordForm CSVPassword::ParseValid() const {
   PasswordForm result;
-  bool success = Parse(&result);
-  DCHECK(success);
+  Status status = Parse(&result);
+  DCHECK_EQ(Status::kOK, status);
   return result;
 }
 
diff --git a/components/password_manager/core/browser/import/csv_password.h b/components/password_manager/core/browser/import/csv_password.h
index e85ff971..810f71a3 100644
--- a/components/password_manager/core/browser/import/csv_password.h
+++ b/components/password_manager/core/browser/import/csv_password.h
@@ -21,6 +21,9 @@
   enum class Label { kOrigin, kUsername, kPassword };
   using ColumnMap = base::flat_map<size_t, Label>;
 
+  // Status describes parsing errors.
+  enum class Status { kOK, kSyntaxError, kSemanticError };
+
   // Number of values in the Label enum.
   static constexpr size_t kLabelCount = 3;
 
@@ -32,10 +35,10 @@
   ~CSVPassword();
 
   // Returns whether the associated CSV row can be parsed successfully.
-  // If returning true and |form| is not null, it also stores the parsed result
-  // in |*form|. It does not return base::Optional<PasswordForm> for efficiency
-  // reasons in cases when the parsed form is not needed.
-  bool Parse(autofill::PasswordForm* form) const;
+  // If returning success and |form| is not null, it also stores the parsed
+  // result in |*form|. It does not return base::Optional<PasswordForm> for
+  // efficiency reasons in cases when the parsed form is not needed.
+  Status Parse(autofill::PasswordForm* form) const;
   // Convenience wrapper around Parse() for cases known to be correctly
   // parseable.
   autofill::PasswordForm ParseValid() const;
diff --git a/components/password_manager/core/browser/import/csv_password_iterator_unittest.cc b/components/password_manager/core/browser/import/csv_password_iterator_unittest.cc
index b9e727a..9024ddac 100644
--- a/components/password_manager/core/browser/import/csv_password_iterator_unittest.cc
+++ b/components/password_manager/core/browser/import/csv_password_iterator_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "components/password_manager/core/browser/import/csv_password.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace password_manager {
@@ -75,9 +76,10 @@
 
   CSVPasswordIterator check = iter;
   for (size_t i = 0; i < base::size(kExpectedPasswords); ++i) {
-    EXPECT_TRUE((check++)->Parse(nullptr)) << "on line " << i;
+    EXPECT_EQ(CSVPassword::Status::kOK, (check++)->Parse(nullptr))
+        << "on line " << i;
   }
-  EXPECT_FALSE(check->Parse(nullptr));
+  EXPECT_NE(CSVPassword::Status::kOK, check->Parse(nullptr));
 
   for (const base::StringPiece& expected_password : kExpectedPasswords) {
     PasswordForm result = (iter++)->ParseValid();
@@ -107,12 +109,13 @@
 
   CSVPasswordIterator check = iter;
   for (size_t i = 0; i + 1 < kLinesInBlob; ++i) {
-    EXPECT_FALSE((check++)->Parse(nullptr)) << "on line " << i;
+    EXPECT_NE(CSVPassword::Status::kOK, (check++)->Parse(nullptr))
+        << "on line " << i;
   }
   // Last line was not a failure.
-  EXPECT_TRUE((check++)->Parse(nullptr));
+  EXPECT_EQ(CSVPassword::Status::kOK, (check++)->Parse(nullptr));
   // After iterating over all lines, there is no more data to parse.
-  EXPECT_FALSE(check->Parse(nullptr));
+  EXPECT_NE(CSVPassword::Status::kOK, check->Parse(nullptr));
 }
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/import/csv_password_unittest.cc b/components/password_manager/core/browser/import/csv_password_unittest.cc
index 03a19ed..43c6420d 100644
--- a/components/password_manager/core/browser/import/csv_password_unittest.cc
+++ b/components/password_manager/core/browser/import/csv_password_unittest.cc
@@ -14,6 +14,8 @@
 
 using ::autofill::PasswordForm;
 
+using Status = CSVPassword::Status;
+
 TEST(CSVPassword, Construction) {
   CSVPassword::ColumnMap col_map = {
       {0, CSVPassword::Label::kOrigin},
@@ -39,6 +41,7 @@
   std::string signon_realm;
   std::string username;
   std::string password;
+  Status status = Status::kOK;
 };
 
 class TestCaseBuilder {
@@ -77,6 +80,11 @@
     return *this;
   }
 
+  TestCaseBuilder& Status(Status status) {
+    test_case_.status = status;
+    return *this;
+  }
+
   TestCase Build() { return std::move(test_case_); }
 
  private:
@@ -89,7 +97,7 @@
   const TestCase& test_case = GetParam();
   SCOPED_TRACE(test_case.name);
   CSVPassword csv_pwd(test_case.map, test_case.csv);
-  EXPECT_TRUE(csv_pwd.Parse(nullptr));
+  EXPECT_EQ(Status::kOK, csv_pwd.Parse(nullptr));
 
   PasswordForm result = csv_pwd.ParseValid();
 
@@ -173,7 +181,8 @@
 TEST_P(CSVPasswordTestFailure, Parse) {
   const TestCase& test_case = GetParam();
   SCOPED_TRACE(test_case.name);
-  EXPECT_FALSE(CSVPassword(test_case.map, test_case.csv).Parse(nullptr));
+  EXPECT_EQ(test_case.status,
+            CSVPassword(test_case.map, test_case.csv).Parse(nullptr));
 }
 
 INSTANTIATE_TEST_SUITE_P(
@@ -182,51 +191,60 @@
     ::testing::Values(TestCaseBuilder("no columns specified")
                           .Map({})
                           .CSV("http://example.com,user,password")
+                          .Status(Status::kSemanticError)
                           .Build(),
                       TestCaseBuilder("not ASCII")
                           .Map({{0, CSVPassword::Label::kOrigin},
                                 {1, CSVPassword::Label::kUsername},
                                 {2, CSVPassword::Label::kPassword}})
                           .CSV("http://example.com/Å™,user,password")
+                          .Status(Status::kSyntaxError)
                           .Build(),
                       TestCaseBuilder("no origin in map")
                           .Map({{1, CSVPassword::Label::kUsername},
                                 {2, CSVPassword::Label::kPassword}})
                           .CSV("http://example.com,user,password")
+                          .Status(Status::kSemanticError)
                           .Build(),
                       TestCaseBuilder("no username in map")
                           .Map({{0, CSVPassword::Label::kOrigin},
                                 {2, CSVPassword::Label::kPassword}})
                           .CSV("http://example.com,user,password")
+                          .Status(Status::kSemanticError)
                           .Build(),
                       TestCaseBuilder("no password in map")
                           .Map({{0, CSVPassword::Label::kOrigin},
                                 {1, CSVPassword::Label::kUsername}})
                           .CSV("http://example.com,user,password")
+                          .Status(Status::kSemanticError)
                           .Build(),
                       TestCaseBuilder("no origin in CSV")
                           .Map({{0, CSVPassword::Label::kUsername},
                                 {1, CSVPassword::Label::kPassword},
                                 {2, CSVPassword::Label::kOrigin}})
                           .CSV("user,password")
+                          .Status(Status::kSemanticError)
                           .Build(),
                       TestCaseBuilder("no username in CSV")
                           .Map({{0, CSVPassword::Label::kOrigin},
                                 {1, CSVPassword::Label::kPassword},
                                 {2, CSVPassword::Label::kUsername}})
                           .CSV("http://example.com,password")
+                          .Status(Status::kSemanticError)
                           .Build(),
                       TestCaseBuilder("no password in CSV")
                           .Map({{0, CSVPassword::Label::kOrigin},
                                 {1, CSVPassword::Label::kUsername},
                                 {2, CSVPassword::Label::kPassword}})
                           .CSV("http://example.com,user")
+                          .Status(Status::kSemanticError)
                           .Build(),
                       TestCaseBuilder("malformed CSV")
                           .Map({{0, CSVPassword::Label::kOrigin},
                                 {1, CSVPassword::Label::kUsername},
                                 {2, CSVPassword::Label::kPassword}})
                           .CSV("\"")
+                          .Status(Status::kSyntaxError)
                           .Build(),
                       TestCaseBuilder("map not injective")
                           .Map({{0, CSVPassword::Label::kOrigin},
@@ -234,6 +252,7 @@
                                 {2, CSVPassword::Label::kPassword},
                                 {3, CSVPassword::Label::kUsername}})
                           .CSV("http://example.com,user,pwd,user2")
+                          .Status(Status::kSemanticError)
                           .Build()));
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_manager_client.h b/components/password_manager/core/browser/password_manager_client.h
index 3e9414a2..aa0ce43 100644
--- a/components/password_manager/core/browser/password_manager_client.h
+++ b/components/password_manager/core/browser/password_manager_client.h
@@ -54,6 +54,7 @@
 
 namespace password_manager {
 
+class FieldInfoManager;
 class PasswordFeatureManager;
 class PasswordFormManagerForUI;
 class PasswordManager;
@@ -355,6 +356,9 @@
   // Returns true if the current page is to the new tab page.
   virtual bool IsNewTabPage() const = 0;
 
+  // Returns a FieldInfoManager associated with the current profile.
+  virtual FieldInfoManager* GetFieldInfoManager() const = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(PasswordManagerClient);
 };
diff --git a/components/password_manager/core/browser/stub_password_manager_client.cc b/components/password_manager/core/browser/stub_password_manager_client.cc
index f4a2d90..f17ea98 100644
--- a/components/password_manager/core/browser/stub_password_manager_client.cc
+++ b/components/password_manager/core/browser/stub_password_manager_client.cc
@@ -151,4 +151,8 @@
   return false;
 }
 
+FieldInfoManager* StubPasswordManagerClient::GetFieldInfoManager() const {
+  return nullptr;
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/stub_password_manager_client.h b/components/password_manager/core/browser/stub_password_manager_client.h
index 8ba8d18..c62b03d1 100644
--- a/components/password_manager/core/browser/stub_password_manager_client.h
+++ b/components/password_manager/core/browser/stub_password_manager_client.h
@@ -91,6 +91,7 @@
   scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
   bool IsIsolationForPasswordSitesEnabled() const override;
   bool IsNewTabPage() const override;
+  FieldInfoManager* GetFieldInfoManager() const override;
 
  private:
   const StubCredentialsFilter credentials_filter_;
diff --git a/components/policy/proto/chrome_device_policy.proto b/components/policy/proto/chrome_device_policy.proto
index 0eda211..e841ab6 100644
--- a/components/policy/proto/chrome_device_policy.proto
+++ b/components/policy/proto/chrome_device_policy.proto
@@ -12,7 +12,6 @@
 
 // Everything below this comment will be synchronized between client and server
 // repos ( go/cros-proto-sync ).
-// Please don't manually edit any lines below this comment.
 
 message DevicePolicyRefreshRateProto {
   // In milliseconds.
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index 51c9939..e67f9c9 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -10,7 +10,6 @@
 
 // Everything below this comment will be synchronized between client and server
 // repos ( go/cros-proto-sync ).
-// Please don't manually edit any lines below this comment.
 
 // This enum needs to be shared between DeviceRegisterRequest and
 // LicenseAvailability protos. With java_api_version 1, this means that enum
diff --git a/components/policy/proto/policy_common_definitions.proto b/components/policy/proto/policy_common_definitions.proto
index b5ffd71..676d9cd7 100644
--- a/components/policy/proto/policy_common_definitions.proto
+++ b/components/policy/proto/policy_common_definitions.proto
@@ -10,7 +10,6 @@
 
 // Everything below this comment will be synchronized between client and server
 // repos ( go/cros-proto-sync ).
-// Please don't manually edit any lines below this comment.
 
 message StringList {
   repeated string entries = 1;
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 0cd17f80..4d89975 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -13115,7 +13115,7 @@
         'type': 'array',
         'items': { 'type': 'string' },
       },
-      'supported_on': ['chrome.*:80-', 'chrome_os:80-'],
+      'supported_on': ['chrome.*:79-', 'chrome_os:79-'],
       'features': {
         'dynamic_refresh': False,
         'per_profile': True,
diff --git a/components/security_interstitials_strings.grdp b/components/security_interstitials_strings.grdp
index 903497d..9bae094 100644
--- a/components/security_interstitials_strings.grdp
+++ b/components/security_interstitials_strings.grdp
@@ -66,7 +66,7 @@
 
   <!-- Lookalike URL warning -->
   <message name="IDS_LOOKALIKE_URL_TITLE" desc="Tab title. Context: the requested URL might be trying to trick the user since it looks like a more popular URL. This interstitial points the user to the safe site instead.">
-    Did you mean?
+    Did you mean <ph name="DOMAIN">$1<ex>example.com</ex></ph>?
   </message>
   <message name="IDS_LOOKALIKE_URL_HEADING" desc="Large heading. Context: the error page that's shown when the requested URL might be trying to trick the user since it looks like a more popular URL. This interstitial points the user to the safe site instead.">
     Did you mean &lt;a href="#" id="dont-proceed-link"&gt;<ph name="DOMAIN">$1<ex>example.com</ex></ph>&lt;/a&gt;?
diff --git a/components/test/data/payments/PRESUBMIT.py b/components/test/data/payments/PRESUBMIT.py
deleted file mode 100644
index 8eae247..0000000
--- a/components/test/data/payments/PRESUBMIT.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-def _CommonChecks(input_api, output_api):
-  results = []
-  try:
-    import sys
-    old_sys_path = sys.path
-    cwd = input_api.PresubmitLocalPath()
-    sys.path += [input_api.os_path.join(cwd, '..', '..', '..', '..', 'tools')]
-    import web_dev_style.presubmit_support
-    results = web_dev_style.presubmit_support.CheckStyle(input_api, output_api)
-  finally:
-    sys.path = old_sys_path
-  return results
-
-def CheckChangeOnUpload(input_api, output_api):
-  return _CommonChecks(input_api, output_api)
-
-def CheckChangeOnCommit(input_api, output_api):
-  return _CommonChecks(input_api, output_api)
diff --git a/components/typemaps.gni b/components/typemaps.gni
index 879e7796..ca61b1f 100644
--- a/components/typemaps.gni
+++ b/components/typemaps.gni
@@ -5,7 +5,6 @@
 typemaps = [
   "//components/account_id/mojom/account_id.typemap",
   "//components/autofill/core/common/mojom/autofill_types.typemap",
-  "//components/chrome_cleaner/public/typemaps/chrome_prompt.typemap",
   "//components/content_capture/common/content_capture.typemap",
   "//components/content_settings/core/common/content_settings.typemap",
   "//components/nacl/common/nacl.typemap",
diff --git a/components/viz/common/gpu/DEPS b/components/viz/common/gpu/DEPS
index 5cbd28a..ba50fcbe2 100644
--- a/components/viz/common/gpu/DEPS
+++ b/components/viz/common/gpu/DEPS
@@ -7,7 +7,6 @@
   "+gpu/command_buffer",
   "+gpu/GLES2/gl2extchromium.h",
   "+gpu/vulkan",
-  "+third_party/dawn/src/include",
   "+third_party/khronos/GLES2/gl2.h",
   "+third_party/skia/include/gpu",
   "+third_party/vulkan/include",
diff --git a/components/viz/common/gpu/dawn_context_provider.cc b/components/viz/common/gpu/dawn_context_provider.cc
index d57ef4c..6c1f978 100644
--- a/components/viz/common/gpu/dawn_context_provider.cc
+++ b/components/viz/common/gpu/dawn_context_provider.cc
@@ -4,10 +4,11 @@
 
 #include "components/viz/common/gpu/dawn_context_provider.h"
 
+#include <dawn/dawn_proc.h>
+
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "build/build_config.h"
-#include "third_party/dawn/src/include/dawn/dawn_proc.h"
 
 namespace viz {
 
diff --git a/components/viz/common/gpu/dawn_context_provider.h b/components/viz/common/gpu/dawn_context_provider.h
index b1d81b1..dc4c53e 100644
--- a/components/viz/common/gpu/dawn_context_provider.h
+++ b/components/viz/common/gpu/dawn_context_provider.h
@@ -5,9 +5,10 @@
 #ifndef COMPONENTS_VIZ_COMMON_GPU_DAWN_CONTEXT_PROVIDER_H_
 #define COMPONENTS_VIZ_COMMON_GPU_DAWN_CONTEXT_PROVIDER_H_
 
+#include <dawn_native/DawnNative.h>
+
 #include "base/macros.h"
 #include "components/viz/common/viz_dawn_context_provider_export.h"
-#include "third_party/dawn/src/include/dawn_native/DawnNative.h"
 #include "third_party/skia/include/gpu/GrContext.h"
 #include "third_party/skia/include/gpu/dawn/GrDawnTypes.h"
 
diff --git a/components/viz/common/resources/DEPS b/components/viz/common/resources/DEPS
index 7fbfdb3..c98a60da 100644
--- a/components/viz/common/resources/DEPS
+++ b/components/viz/common/resources/DEPS
@@ -11,7 +11,6 @@
   "+mojo/public/cpp/system/buffer.h",
   "+mojo/public/cpp/system/platform_handle.h",
   "+skia/buildflags.h",
-  "+third_party/dawn/src/include",
   "+third_party/khronos/GLES2",
   "+third_party/skia",
   "+third_party/vulkan",
diff --git a/components/viz/common/resources/resource_format_utils.h b/components/viz/common/resources/resource_format_utils.h
index eb8748f9..33ebb5c 100644
--- a/components/viz/common/resources/resource_format_utils.h
+++ b/components/viz/common/resources/resource_format_utils.h
@@ -18,7 +18,7 @@
 #endif
 
 #if BUILDFLAG(SKIA_USE_DAWN)
-#include "third_party/dawn/src/include/dawn/dawncpp.h"  // nogncheck
+#include <dawn/dawncpp.h>
 #endif
 
 namespace viz {
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index e80a645..b3e1580a 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -683,6 +683,9 @@
                 render_pass_requires_scissor);
   FinishDrawingQuadList();
 
+  if (is_root_render_pass && overdraw_feedback_)
+    FlushOverdrawFeedback(render_pass_scissor_in_draw_space);
+
   if (render_pass->generate_mipmap)
     GenerateMipmap();
 }
diff --git a/components/viz/service/display/direct_renderer.h b/components/viz/service/display/direct_renderer.h
index c12b9d1d..b4021d6 100644
--- a/components/viz/service/display/direct_renderer.h
+++ b/components/viz/service/display/direct_renderer.h
@@ -206,6 +206,7 @@
   virtual void DoDrawQuad(const DrawQuad* quad,
                           const gfx::QuadF* clip_region) = 0;
   virtual void BeginDrawingFrame() = 0;
+  virtual void FlushOverdrawFeedback(const gfx::Rect& output_rect) {}
   virtual void FinishDrawingFrame() = 0;
   // If a pass contains a single tile draw quad and can be drawn without
   // a render pass (e.g. applying a filter directly to the tile quad)
diff --git a/components/viz/service/display/gl_renderer.cc b/components/viz/service/display/gl_renderer.cc
index cf811dc..3a601c8 100644
--- a/components/viz/service/display/gl_renderer.cc
+++ b/components/viz/service/display/gl_renderer.cc
@@ -2725,8 +2725,6 @@
   }
 
   swap_buffer_rect_.Union(current_frame()->root_damage_rect);
-  if (overdraw_feedback_)
-    FlushOverdrawFeedback(swap_buffer_rect_);
 
   if (use_swap_with_bounds_)
     swap_content_bounds_ = current_frame()->root_content_bounds;
@@ -2799,9 +2797,6 @@
     std::unique_ptr<CopyOutputRequest> request) {
   TRACE_EVENT0("viz", "GLRenderer::CopyDrawnRenderPass");
 
-  if (overdraw_feedback_)
-    FlushOverdrawFeedback(current_frame()->current_render_pass->output_rect);
-
   GLuint framebuffer_texture = 0;
   gfx::Size framebuffer_texture_size;
   if (current_framebuffer_texture_) {
diff --git a/components/viz/service/display/gl_renderer.h b/components/viz/service/display/gl_renderer.h
index ad7718b..2273f20 100644
--- a/components/viz/service/display/gl_renderer.h
+++ b/components/viz/service/display/gl_renderer.h
@@ -102,6 +102,7 @@
   void DoDrawQuad(const class DrawQuad*,
                   const gfx::QuadF* draw_region) override;
   void BeginDrawingFrame() override;
+  void FlushOverdrawFeedback(const gfx::Rect& output_rect) override;
   void FinishDrawingFrame() override;
   bool FlippedFramebuffer() const override;
   bool FlippedRootFramebuffer() const;
@@ -330,7 +331,7 @@
 
   // Setup/flush all pending overdraw feedback to framebuffer.
   void SetupOverdrawFeedback();
-  void FlushOverdrawFeedback(const gfx::Rect& output_rect);
+
   // Process overdraw feedback from query.
   void ProcessOverdrawFeedback(std::vector<int>* overdraw,
                                size_t num_expected_results,
diff --git a/components/viz/service/display/skia_output_surface.h b/components/viz/service/display/skia_output_surface.h
index ae7dbf5..f51730c 100644
--- a/components/viz/service/display/skia_output_surface.h
+++ b/components/viz/service/display/skia_output_surface.h
@@ -133,8 +133,10 @@
   // ScheduleDCLayers() will be called.
   virtual void SetEnableDCLayers(bool enable) = 0;
 
-  // Schedule drawing DC layer overlays at next SkiaSwapBuffers() call.
-  virtual void ScheduleDCLayers(std::vector<DCLayerOverlay> dc_layers) = 0;
+  // Schedule drawing DC layer overlays at next SkiaSwapBuffers() call. Waits on
+  // |sync_tokens| for the overlay textures to be ready before scheduling.
+  virtual void ScheduleDCLayers(std::vector<DCLayerOverlay> dc_layers,
+                                std::vector<gpu::SyncToken> sync_tokens) = 0;
 
   // Add context lost observer.
   virtual void AddContextLostObserver(ContextLostObserver* observer) = 0;
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index d110134..5ce9fd3e 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -2044,6 +2044,7 @@
   if (current_frame()->dc_layer_overlay_list.empty())
     return;
 
+  std::vector<gpu::SyncToken> sync_tokens;
   for (auto& dc_layer_overlay : current_frame()->dc_layer_overlay_list) {
     for (size_t i = 0; i < DCLayerOverlay::kNumResources; ++i) {
       ResourceId resource_id = dc_layer_overlay.resources[i];
@@ -2053,6 +2054,12 @@
       // Resources will be unlocked after the next call to SwapBuffers().
       auto* image_context =
           lock_set_for_external_use_->LockResource(resource_id, true);
+
+      // Sync tokens ensure the texture to be overlaid is available before
+      // scheduling it for display.
+      DCHECK(image_context->mailbox_holder().sync_token.HasData());
+      sync_tokens.push_back(image_context->mailbox_holder().sync_token);
+
       dc_layer_overlay.mailbox[i] = image_context->mailbox_holder().mailbox;
     }
     DCHECK(!dc_layer_overlay.mailbox[0].IsZero());
@@ -2060,7 +2067,8 @@
 
   has_locked_overlay_resources_ = true;
   skia_output_surface_->ScheduleDCLayers(
-      std::move(current_frame()->dc_layer_overlay_list));
+      std::move(current_frame()->dc_layer_overlay_list),
+      std::move(sync_tokens));
 }
 
 sk_sp<SkColorFilter> SkiaRenderer::GetColorFilter(const gfx::ColorSpace& src,
diff --git a/components/viz/service/display_embedder/DEPS b/components/viz/service/display_embedder/DEPS
index 38a3343..577b438e 100644
--- a/components/viz/service/display_embedder/DEPS
+++ b/components/viz/service/display_embedder/DEPS
@@ -31,7 +31,6 @@
   "+mojo/public/cpp/bindings",
   "+mojo/public/cpp/system",
   "+skia",
-  "+third_party/dawn/src/include",
   "+third_party/khronos/GLES2/gl2.h",
   "+third_party/khronos/GLES2/gl2ext.h",
   "+third_party/skia",
diff --git a/components/viz/service/display_embedder/skia_output_device_dawn.cc b/components/viz/service/display_embedder/skia_output_device_dawn.cc
index b2153b8..aa9b901f 100644
--- a/components/viz/service/display_embedder/skia_output_device_dawn.cc
+++ b/components/viz/service/display_embedder/skia_output_device_dawn.cc
@@ -9,9 +9,9 @@
 #include "components/viz/common/gpu/dawn_context_provider.h"
 
 #if defined(OS_WIN)
-#include "third_party/dawn/src/include/dawn_native/D3D12Backend.h"
+#include <dawn_native/D3D12Backend.h>
 #elif defined(OS_LINUX)
-#include "third_party/dawn/src/include/dawn_native/VulkanBackend.h"
+#include <dawn_native/VulkanBackend.h>
 #endif
 
 namespace viz {
diff --git a/components/viz/service/display_embedder/skia_output_device_dawn.h b/components/viz/service/display_embedder/skia_output_device_dawn.h
index a39722b..607f400 100644
--- a/components/viz/service/display_embedder/skia_output_device_dawn.h
+++ b/components/viz/service/display_embedder/skia_output_device_dawn.h
@@ -5,10 +5,11 @@
 #ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_DEVICE_DAWN_H_
 #define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_DEVICE_DAWN_H_
 
+#include <dawn/dawn_wsi.h>
+#include <dawn/dawncpp.h>
+#include <dawn_native/DawnNative.h>
+
 #include "components/viz/service/display_embedder/skia_output_device.h"
-#include "third_party/dawn/src/include/dawn/dawn_wsi.h"
-#include "third_party/dawn/src/include/dawn/dawncpp.h"
-#include "third_party/dawn/src/include/dawn_native/DawnNative.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
 #include "third_party/skia/include/gpu/GrBackendSurface.h"
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index 0deeecb..eda3bb4e 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -621,11 +621,12 @@
 }
 
 void SkiaOutputSurfaceImpl::ScheduleDCLayers(
-    std::vector<DCLayerOverlay> overlays) {
+    std::vector<DCLayerOverlay> overlays,
+    std::vector<gpu::SyncToken> sync_tokens) {
   auto task =
       base::BindOnce(&SkiaOutputSurfaceImplOnGpu::ScheduleDCLayers,
                      base::Unretained(impl_on_gpu_.get()), std::move(overlays));
-  ScheduleGpuTask(std::move(task), {});
+  ScheduleGpuTask(std::move(task), std::move(sync_tokens));
 }
 
 void SkiaOutputSurfaceImpl::SetCapabilitiesForTesting(
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.h b/components/viz/service/display_embedder/skia_output_surface_impl.h
index 8345677..4f03f5d 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.h
@@ -115,7 +115,8 @@
 
   void RemoveRenderPassResource(std::vector<RenderPassId> ids) override;
   void SetEnableDCLayers(bool enable) override;
-  void ScheduleDCLayers(std::vector<DCLayerOverlay> overlays) override;
+  void ScheduleDCLayers(std::vector<DCLayerOverlay> overlays,
+                        std::vector<gpu::SyncToken> sync_tokens) override;
   void CopyOutput(RenderPassId id,
                   const copy_output::RenderPassGeometry& geometry,
                   const gfx::ColorSpace& color_space,
diff --git a/components/viz/test/fake_skia_output_surface.h b/components/viz/test/fake_skia_output_surface.h
index cf291e2..8994312 100644
--- a/components/viz/test/fake_skia_output_surface.h
+++ b/components/viz/test/fake_skia_output_surface.h
@@ -90,7 +90,8 @@
       sk_sp<SkColorSpace> color_space) override;
   void RemoveRenderPassResource(std::vector<RenderPassId> ids) override;
   void SetEnableDCLayers(bool enable) override {}
-  void ScheduleDCLayers(std::vector<DCLayerOverlay> overlays) override {}
+  void ScheduleDCLayers(std::vector<DCLayerOverlay> overlays,
+                        std::vector<gpu::SyncToken> sync_tokens) override {}
   void CopyOutput(RenderPassId id,
                   const copy_output::RenderPassGeometry& geometry,
                   const gfx::ColorSpace& color_space,
diff --git a/content/browser/DEPS b/content/browser/DEPS
index 7205411c..d014086 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -116,7 +116,6 @@
   "+third_party/blink/public/platform/web_touch_event.h",
   "+third_party/blink/public/platform/web_text_input_type.h",
   "+third_party/blink/public/platform/mac/web_scrollbar_theme.h",
-  "+third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h",
   "+third_party/blink/public/platform/modules/service_worker/web_service_worker_error.h",
   "+third_party/blink/public/platform/modules/sms/sms_receiver.mojom.h",
   "+third_party/blink/public/public_buildflags.h",
diff --git a/content/browser/android/web_contents_observer_proxy.cc b/content/browser/android/web_contents_observer_proxy.cc
index 229d6184..1d5ed90 100644
--- a/content/browser/android/web_contents_observer_proxy.cc
+++ b/content/browser/android/web_contents_observer_proxy.cc
@@ -126,6 +126,11 @@
       jstring_error_description, jstring_url);
 }
 
+void WebContentsObserverProxy::DidChangeVisibleSecurityState() {
+  Java_WebContentsObserverProxy_didChangeVisibleSecurityState(
+      AttachCurrentThread(), java_observer_);
+}
+
 void WebContentsObserverProxy::DocumentAvailableInMainFrame() {
   JNIEnv* env = AttachCurrentThread();
   Java_WebContentsObserverProxy_documentAvailableInMainFrame(env,
diff --git a/content/browser/android/web_contents_observer_proxy.h b/content/browser/android/web_contents_observer_proxy.h
index dd72d644..ad9acb7 100644
--- a/content/browser/android/web_contents_observer_proxy.h
+++ b/content/browser/android/web_contents_observer_proxy.h
@@ -40,6 +40,7 @@
                    const GURL& validated_url,
                    int error_code,
                    const base::string16& error_description) override;
+  void DidChangeVisibleSecurityState() override;
   void DocumentAvailableInMainFrame() override;
   void DidFirstVisuallyNonEmptyPaint() override;
   void OnVisibilityChanged(content::Visibility visibility) override;
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index cd93395c..cc423e5 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -3190,7 +3190,13 @@
           params.started_from_context_menu, has_user_gesture,
           InitiatorCSPInfo(), std::vector<int>(), params.href_translate,
           false /* is_history_navigation_in_new_child_frame */,
-          params.input_start);
+          params.input_start,
+          // TODO(chenleihu): The value of frame policy should be set to a
+          // valid value here. Currently when we navigate a remote frame, the
+          // frame_policy value in common_params is not used to initialize
+          // container policy in document.cc.
+          // https://crbug.com/972089
+          base::nullopt /* frame policy */);
 
   mojom::CommitNavigationParamsPtr commit_params =
       mojom::CommitNavigationParams::New(
@@ -3327,7 +3333,8 @@
           *frame_entry, request_body, dest_url,
           blink::mojom::Referrer::New(dest_referrer.url, dest_referrer.policy),
           navigation_type, previews_state, navigation_start,
-          base::TimeTicks() /* input_start */);
+          base::TimeTicks() /* input_start */,
+          frame_tree_node->pending_frame_policy());
   common_params->is_history_navigation_in_new_child_frame =
       is_history_navigation_in_new_child_frame;
 
diff --git a/content/browser/frame_host/navigation_entry_impl.cc b/content/browser/frame_host/navigation_entry_impl.cc
index e7d7414..fb11d43 100644
--- a/content/browser/frame_host/navigation_entry_impl.cc
+++ b/content/browser/frame_host/navigation_entry_impl.cc
@@ -702,7 +702,8 @@
     mojom::NavigationType navigation_type,
     PreviewsState previews_state,
     base::TimeTicks navigation_start,
-    base::TimeTicks input_start) {
+    base::TimeTicks input_start,
+    const blink::FramePolicy& frame_policy) {
   NavigationDownloadPolicy download_policy;
   if (IsViewSourceMode())
     download_policy.SetDisallowed(NavigationDownloadType::kViewSource);
@@ -715,7 +716,8 @@
       post_body ? post_body : post_data_, base::Optional<SourceLocation>(),
       has_started_from_context_menu(), has_user_gesture(), InitiatorCSPInfo(),
       std::vector<int>(), std::string(),
-      false /* is_history_navigation_in_new_child_frame */, input_start);
+      false /* is_history_navigation_in_new_child_frame */, input_start,
+      frame_policy);
 }
 
 mojom::CommitNavigationParamsPtr
diff --git a/content/browser/frame_host/navigation_entry_impl.h b/content/browser/frame_host/navigation_entry_impl.h
index 97097c29..4921a37d 100644
--- a/content/browser/frame_host/navigation_entry_impl.h
+++ b/content/browser/frame_host/navigation_entry_impl.h
@@ -184,7 +184,8 @@
       mojom::NavigationType navigation_type,
       PreviewsState previews_state,
       base::TimeTicks navigation_start,
-      base::TimeTicks input_start);
+      base::TimeTicks input_start,
+      const blink::FramePolicy& frame_policy);
   mojom::CommitNavigationParamsPtr ConstructCommitNavigationParams(
       const FrameNavigationEntry& frame_entry,
       const GURL& original_url,
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 7da0bce4..7111efaa 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -821,7 +821,7 @@
           std::vector<int>() /* initiator_origin_trial_features */,
           std::string() /* href_translate */,
           false /* is_history_navigation_in_new_child_frame */,
-          base::TimeTicks::Now());
+          base::TimeTicks::Now(), base::nullopt /* frame policy */);
   mojom::CommitNavigationParamsPtr commit_params =
       mojom::CommitNavigationParams::New(
           params.origin, params.is_overriding_user_agent, params.redirects,
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 1fd6d7e..d490e6a 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -4826,7 +4826,8 @@
       base::TimeTicks::Now(), "GET", nullptr, base::Optional<SourceLocation>(),
       false /* started_from_context_menu */, false /* has_user_gesture */,
       InitiatorCSPInfo(), std::vector<int>(), std::string(),
-      false /* is_history_navigation_in_new_child_frame */, base::TimeTicks());
+      false /* is_history_navigation_in_new_child_frame */, base::TimeTicks(),
+      base::nullopt /* frame_policy */);
   CommitNavigation(nullptr /* navigation_request */, std::move(common_params),
                    CreateCommitNavigationParams(), nullptr /* response_head */,
                    mojo::ScopedDataPipeConsumerHandle(),
diff --git a/content/browser/frame_host/render_frame_host_manager_unittest.cc b/content/browser/frame_host/render_frame_host_manager_unittest.cc
index 7e46c7e..c082f24 100644
--- a/content/browser/frame_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc
@@ -397,7 +397,8 @@
             *frame_entry, request_body, frame_entry->url(),
             blink::mojom::Referrer::New(referrer.url, referrer.policy),
             navigate_type, PREVIEWS_UNSPECIFIED, base::TimeTicks::Now(),
-            base::TimeTicks::Now());
+            base::TimeTicks::Now(),
+            frame_tree_node->current_replication_state().frame_policy);
     mojom::CommitNavigationParamsPtr commit_params =
         entry->ConstructCommitNavigationParams(
             *frame_entry, common_params->url, frame_entry->committed_origin(),
@@ -2746,7 +2747,8 @@
           *frame_entry, nullptr, frame_entry->url(),
           blink::mojom::Referrer::New(referrer.url, referrer.policy),
           mojom::NavigationType::DIFFERENT_DOCUMENT, PREVIEWS_UNSPECIFIED,
-          base::TimeTicks::Now(), base::TimeTicks::Now());
+          base::TimeTicks::Now(), base::TimeTicks::Now(),
+          frame_tree_node->current_replication_state().frame_policy);
   mojom::CommitNavigationParamsPtr commit_params =
       entry.ConstructCommitNavigationParams(
           *frame_entry, common_params->url, frame_entry->committed_origin(),
diff --git a/content/browser/indexed_db/database_impl.cc b/content/browser/indexed_db/database_impl.cc
index 5b72934..0b32f351 100644
--- a/content/browser/indexed_db/database_impl.cc
+++ b/content/browser/indexed_db/database_impl.cc
@@ -20,7 +20,7 @@
 #include "content/browser/indexed_db/indexed_db_factory_impl.h"
 #include "content/browser/indexed_db/indexed_db_transaction.h"
 #include "content/browser/indexed_db/transaction_impl.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
 
 using blink::IndexedDBIndexKeys;
 using blink::IndexedDBKey;
@@ -186,7 +186,7 @@
                        blink::mojom::IDBDatabase::GetCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!connection_->IsConnected()) {
-    IndexedDBDatabaseError error(blink::kWebIDBDatabaseExceptionUnknownError,
+    IndexedDBDatabaseError error(blink::mojom::IDBException::kUnknownError,
                                  "Not connected.");
     std::move(callback).Run(blink::mojom::IDBDatabaseGetResult::NewErrorResult(
         blink::mojom::IDBError::New(error.code(), error.message())));
@@ -196,7 +196,7 @@
   IndexedDBTransaction* transaction =
       connection_->GetTransaction(transaction_id);
   if (!transaction) {
-    IndexedDBDatabaseError error(blink::kWebIDBDatabaseExceptionUnknownError,
+    IndexedDBDatabaseError error(blink::mojom::IDBException::kUnknownError,
                                  "Unknown transaction.");
     std::move(callback).Run(blink::mojom::IDBDatabaseGetResult::NewErrorResult(
         blink::mojom::IDBError::New(error.code(), error.message())));
@@ -225,7 +225,7 @@
                           blink::mojom::IDBDatabase::GetAllCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!connection_->IsConnected()) {
-    IndexedDBDatabaseError error(blink::kWebIDBDatabaseExceptionUnknownError,
+    IndexedDBDatabaseError error(blink::mojom::IDBException::kUnknownError,
                                  "Not connected.");
     std::move(callback).Run(
         blink::mojom::IDBDatabaseGetAllResult::NewErrorResult(
@@ -236,7 +236,7 @@
   IndexedDBTransaction* transaction =
       connection_->GetTransaction(transaction_id);
   if (!transaction) {
-    IndexedDBDatabaseError error(blink::kWebIDBDatabaseExceptionUnknownError,
+    IndexedDBDatabaseError error(blink::mojom::IDBException::kUnknownError,
                                  "Unknown transaction.");
     std::move(callback).Run(
         blink::mojom::IDBDatabaseGetAllResult::NewErrorResult(
@@ -245,7 +245,7 @@
   }
 
   if (!connection_->database()->IsObjectStoreIdInMetadata(object_store_id)) {
-    IndexedDBDatabaseError error(blink::kWebIDBDatabaseExceptionUnknownError,
+    IndexedDBDatabaseError error(blink::mojom::IDBException::kUnknownError,
                                  "Bad request");
     std::move(callback).Run(
         blink::mojom::IDBDatabaseGetAllResult::NewErrorResult(
@@ -330,7 +330,7 @@
     blink::mojom::IDBDatabase::OpenCursorCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!connection_->IsConnected()) {
-    IndexedDBDatabaseError error(blink::kWebIDBDatabaseExceptionUnknownError,
+    IndexedDBDatabaseError error(blink::mojom::IDBException::kUnknownError,
                                  "Not connected.");
     std::move(callback).Run(
         blink::mojom::IDBDatabaseOpenCursorResult::NewErrorResult(
@@ -341,7 +341,7 @@
   IndexedDBTransaction* transaction =
       connection_->GetTransaction(transaction_id);
   if (!transaction) {
-    IndexedDBDatabaseError error(blink::kWebIDBDatabaseExceptionUnknownError,
+    IndexedDBDatabaseError error(blink::mojom::IDBException::kUnknownError,
                                  "Unknown transaction.");
     std::move(callback).Run(
         blink::mojom::IDBDatabaseOpenCursorResult::NewErrorResult(
@@ -573,7 +573,7 @@
 
   connection_->AbortTransactionAndTearDownOnError(
       transaction,
-      IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionAbortError,
+      IndexedDBDatabaseError(blink::mojom::IDBException::kAbortError,
                              "Transaction aborted by user."));
 }
 
diff --git a/content/browser/indexed_db/indexed_db_callback_helpers.h b/content/browser/indexed_db/indexed_db_callback_helpers.h
index 607d4b53..b63fb725 100644
--- a/content/browser/indexed_db/indexed_db_callback_helpers.h
+++ b/content/browser/indexed_db/indexed_db_callback_helpers.h
@@ -11,7 +11,7 @@
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
 #include "content/browser/indexed_db/indexed_db_transaction.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-forward.h"
 #include "third_party/leveldatabase/env_chromium.h"
 
 // Since functions in this file use templates, they must be in a header file
@@ -34,9 +34,8 @@
 R AbortCallback(base::WeakPtr<IndexedDBTransaction> transaction) {
   if (transaction)
     transaction->IncrementNumErrorsSent();
-  IndexedDBDatabaseError error(
-      blink::kWebIDBDatabaseExceptionIgnorableAbortError,
-      "Backend aborted error");
+  IndexedDBDatabaseError error(blink::mojom::IDBException::kIgnorableAbortError,
+                               "Backend aborted error");
   return R::Struct::NewErrorResult(
       blink::mojom::IDBError::New(error.code(), error.message()));
 }
diff --git a/content/browser/indexed_db/indexed_db_connection.cc b/content/browser/indexed_db/indexed_db_connection.cc
index d970dd7..a58c70b6 100644
--- a/content/browser/indexed_db/indexed_db_connection.cc
+++ b/content/browser/indexed_db/indexed_db_connection.cc
@@ -16,7 +16,7 @@
 #include "content/browser/indexed_db/indexed_db_origin_state.h"
 #include "content/browser/indexed_db/indexed_db_tracing.h"
 #include "content/browser/indexed_db/indexed_db_transaction.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
 
 namespace content {
 
@@ -74,7 +74,7 @@
   callbacks_ = nullptr;
 
   // Finish up any transaction, in case there were any running.
-  IndexedDBDatabaseError error(blink::kWebIDBDatabaseExceptionUnknownError,
+  IndexedDBDatabaseError error(blink::mojom::IDBException::kUnknownError,
                                "Connection is closing.");
   leveldb::Status status;
   switch (error_handling) {
diff --git a/content/browser/indexed_db/indexed_db_connection.h b/content/browser/indexed_db/indexed_db_connection.h
index 4c0b381b..2d9d787 100644
--- a/content/browser/indexed_db/indexed_db_connection.h
+++ b/content/browser/indexed_db/indexed_db_connection.h
@@ -16,7 +16,7 @@
 #include "content/browser/indexed_db/indexed_db_database.h"
 #include "content/browser/indexed_db/indexed_db_execution_context_connection_tracker.h"
 #include "content/browser/indexed_db/indexed_db_origin_state_handle.h"
-#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-forward.h"
 
 namespace content {
 class IndexedDBDatabaseCallbacks;
diff --git a/content/browser/indexed_db/indexed_db_connection_coordinator.cc b/content/browser/indexed_db/indexed_db_connection_coordinator.cc
index 8cdd6eb..a056b4f 100644
--- a/content/browser/indexed_db/indexed_db_connection_coordinator.cc
+++ b/content/browser/indexed_db/indexed_db_connection_coordinator.cc
@@ -25,6 +25,7 @@
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_factory.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_transaction.h"
 #include "third_party/blink/public/common/indexeddb/indexeddb_metadata.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
 
 using base::ASCIIToUTF16;
 using base::NumberToString16;
@@ -152,7 +153,7 @@
               NumberToString16(pending_->version);
         }
         pending_->callbacks->OnError(IndexedDBDatabaseError(
-            blink::kWebIDBDatabaseExceptionUnknownError, message));
+            blink::mojom::IDBException::kUnknownError, message));
         state_ = RequestState::kError;
         return;
       }
@@ -199,7 +200,7 @@
       // Requested version is lower than current version - fail the request.
       DCHECK(!is_new_database);
       pending_->callbacks->OnError(IndexedDBDatabaseError(
-          blink::kWebIDBDatabaseExceptionVersionError,
+          blink::mojom::IDBException::kVersionError,
           ASCIIToUTF16("The requested version (") +
               NumberToString16(pending_->version) +
               ASCIIToUTF16(") is less than the existing version (") +
@@ -248,7 +249,7 @@
       connection_ptr_for_close_comparision_ = nullptr;
       if (!pending_->callbacks->is_complete()) {
         pending_->callbacks->OnError(
-            IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionAbortError,
+            IndexedDBDatabaseError(blink::mojom::IDBException::kAbortError,
                                    "The connection was closed."));
       }
       state_ = RequestState::kDone;
@@ -332,7 +333,7 @@
     } else {
       DCHECK_NE(pending_->version, db_->metadata_.version);
       pending_->callbacks->OnError(
-          IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionAbortError,
+          IndexedDBDatabaseError(blink::mojom::IDBException::kAbortError,
                                  "Version change transaction was aborted in "
                                  "upgradeneeded event handler."));
     }
@@ -344,7 +345,7 @@
     DCHECK(pending_);
     if (!pending_->callbacks->is_complete()) {
       pending_->callbacks->OnError(
-          IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionAbortError,
+          IndexedDBDatabaseError(blink::mojom::IDBException::kAbortError,
                                  "The connection was closed."));
     }
     if (state_ != RequestState::kError)
@@ -464,7 +465,7 @@
 
     if (!saved_leveldb_status_.ok()) {
       // TODO(jsbell): Consider including sanitized leveldb status message.
-      IndexedDBDatabaseError error(blink::kWebIDBDatabaseExceptionUnknownError,
+      IndexedDBDatabaseError error(blink::mojom::IDBException::kUnknownError,
                                    "Internal error deleting database.");
       callbacks_->OnError(error);
       state_ = RequestState::kError;
diff --git a/content/browser/indexed_db/indexed_db_cursor.cc b/content/browser/indexed_db/indexed_db_cursor.cc
index ebd9add4..5f8989d 100644
--- a/content/browser/indexed_db/indexed_db_cursor.cc
+++ b/content/browser/indexed_db/indexed_db_cursor.cc
@@ -20,7 +20,7 @@
 #include "content/browser/indexed_db/indexed_db_value.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
 
 using blink::IndexedDBKey;
 
@@ -33,12 +33,12 @@
 // back end; in that case the tx will already have sent an abort to the request
 // so this would be ignored.
 IndexedDBDatabaseError CreateCursorClosedError() {
-  return IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionUnknownError,
+  return IndexedDBDatabaseError(blink::mojom::IDBException::kUnknownError,
                                 "The cursor has been closed.");
 }
 
 IndexedDBDatabaseError CreateError(
-    uint16_t code,
+    blink::mojom::IDBException code,
     const char* message,
     base::WeakPtr<IndexedDBTransaction> transaction) {
   if (transaction)
@@ -113,7 +113,7 @@
 
     // CreateError() needs to be called before calling Close() so
     // |transaction_| is alive.
-    auto error = CreateError(blink::kWebIDBDatabaseExceptionUnknownError,
+    auto error = CreateError(blink::mojom::IDBException::kUnknownError,
                              "Error advancing cursor", transaction_);
     Close();
     std::move(callback).Run(blink::mojom::IDBCursorResult::NewErrorResult(
@@ -200,7 +200,7 @@
     // |transaction_| must be valid for CreateError(), so we can't call
     // Close() until after calling CreateError().
     IndexedDBDatabaseError error =
-        CreateError(blink::kWebIDBDatabaseExceptionUnknownError,
+        CreateError(blink::mojom::IDBException::kUnknownError,
                     "Error continuing cursor.", transaction_);
     Close();
     std::move(callback).Run(blink::mojom::IDBCursorResult::NewErrorResult(
@@ -294,7 +294,7 @@
       // |transaction_| must be valid for CreateError(), so we can't call
       // Close() until after calling CreateError().
       IndexedDBDatabaseError error =
-          CreateError(blink::kWebIDBDatabaseExceptionUnknownError,
+          CreateError(blink::mojom::IDBException::kUnknownError,
                       "Error continuing cursor.", transaction_);
       Close();
       std::move(callback).Run(blink::mojom::IDBCursorResult::NewErrorResult(
diff --git a/content/browser/indexed_db/indexed_db_cursor.h b/content/browser/indexed_db/indexed_db_cursor.h
index 79b75f4..f1915aca 100644
--- a/content/browser/indexed_db/indexed_db_cursor.h
+++ b/content/browser/indexed_db/indexed_db_cursor.h
@@ -17,7 +17,7 @@
 #include "content/browser/indexed_db/indexed_db_database.h"
 #include "content/browser/indexed_db/indexed_db_transaction.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
-#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-forward.h"
 
 namespace content {
 
diff --git a/content/browser/indexed_db/indexed_db_database.cc b/content/browser/indexed_db/indexed_db_database.cc
index 11f68e0..32e132f 100644
--- a/content/browser/indexed_db/indexed_db_database.cc
+++ b/content/browser/indexed_db/indexed_db_database.cc
@@ -52,12 +52,10 @@
 #include "third_party/blink/public/common/indexeddb/indexeddb_key_path.h"
 #include "third_party/blink/public/common/indexeddb/indexeddb_key_range.h"
 #include "third_party/blink/public/common/indexeddb/indexeddb_metadata.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
 #include "third_party/leveldatabase/env_chromium.h"
 #include "url/origin.h"
 
-using base::ASCIIToUTF16;
-using base::NumberToString16;
 using blink::IndexedDBDatabaseMetadata;
 using blink::IndexedDBIndexKeys;
 using blink::IndexedDBIndexMetadata;
@@ -70,14 +68,14 @@
 namespace content {
 namespace {
 
-IndexedDBDatabaseError CreateError(uint16_t code,
+IndexedDBDatabaseError CreateError(blink::mojom::IDBException code,
                                    const char* message,
                                    IndexedDBTransaction* transaction) {
   transaction->IncrementNumErrorsSent();
   return IndexedDBDatabaseError(code, message);
 }
 
-IndexedDBDatabaseError CreateError(uint16_t code,
+IndexedDBDatabaseError CreateError(blink::mojom::IDBException code,
                                    const base::string16& message,
                                    IndexedDBTransaction* transaction) {
   transaction->IncrementNumErrorsSent();
@@ -771,9 +769,8 @@
   IDB_TRACE1("IndexedDBDatabase::GetOperation", "txn.id", transaction->id());
 
   if (!IsObjectStoreIdAndMaybeIndexIdInMetadata(object_store_id, index_id)) {
-    IndexedDBDatabaseError error =
-        CreateError(blink::kWebIDBDatabaseExceptionUnknownError, "Bad request",
-                    transaction);
+    IndexedDBDatabaseError error = CreateError(
+        blink::mojom::IDBException::kUnknownError, "Bad request", transaction);
     std::move(callback).Run(blink::mojom::IDBDatabaseGetResult::NewErrorResult(
         blink::mojom::IDBError::New(error.code(), error.message())));
     return leveldb::Status::InvalidArgument(
@@ -790,8 +787,8 @@
   Status s = Status::OK();
   if (!dispatcher_host) {
     IndexedDBDatabaseError error =
-        CreateError(blink::kWebIDBDatabaseExceptionUnknownError,
-                    "Unknown error", transaction);
+        CreateError(blink::mojom::IDBException::kUnknownError, "Unknown error",
+                    transaction);
     std::move(callback).Run(blink::mojom::IDBDatabaseGetResult::NewErrorResult(
         blink::mojom::IDBError::New(error.code(), error.message())));
     return s;
@@ -826,7 +823,7 @@
 
     if (!s.ok()) {
       IndexedDBDatabaseError error =
-          CreateError(blink::kWebIDBDatabaseExceptionUnknownError,
+          CreateError(blink::mojom::IDBException::kUnknownError,
                       "Corruption detected, unable to continue", transaction);
       std::move(callback).Run(
           blink::mojom::IDBDatabaseGetResult::NewErrorResult(
@@ -851,7 +848,7 @@
                                   object_store_id, *key, &value);
     if (!s.ok()) {
       IndexedDBDatabaseError error =
-          CreateError(blink::kWebIDBDatabaseExceptionUnknownError,
+          CreateError(blink::mojom::IDBException::kUnknownError,
                       "Unknown error", transaction);
       std::move(callback).Run(
           blink::mojom::IDBDatabaseGetResult::NewErrorResult(
@@ -887,7 +884,7 @@
     if (!IndexedDBCallbacks::CreateAllBlobs(
             dispatcher_host->blob_storage_context(), std::move(value_blob))) {
       IndexedDBDatabaseError error =
-          CreateError(blink::kWebIDBDatabaseExceptionUnknownError,
+          CreateError(blink::mojom::IDBException::kUnknownError,
                       "Unknown error", transaction);
       std::move(callback).Run(
           blink::mojom::IDBDatabaseGetResult::NewErrorResult(
@@ -907,8 +904,8 @@
       *key, &primary_key);
   if (!s.ok()) {
     IndexedDBDatabaseError error =
-        CreateError(blink::kWebIDBDatabaseExceptionUnknownError,
-                    "Unknown error", transaction);
+        CreateError(blink::mojom::IDBException::kUnknownError, "Unknown error",
+                    transaction);
     std::move(callback).Run(blink::mojom::IDBDatabaseGetResult::NewErrorResult(
         blink::mojom::IDBError::New(error.code(), error.message())));
     return s;
@@ -931,8 +928,8 @@
                                 object_store_id, *primary_key, &value);
   if (!s.ok()) {
     IndexedDBDatabaseError error =
-        CreateError(blink::kWebIDBDatabaseExceptionUnknownError,
-                    "Unknown error", transaction);
+        CreateError(blink::mojom::IDBException::kUnknownError, "Unknown error",
+                    transaction);
     std::move(callback).Run(blink::mojom::IDBDatabaseGetResult::NewErrorResult(
         blink::mojom::IDBError::New(error.code(), error.message())));
     return s;
@@ -958,8 +955,8 @@
   if (!IndexedDBCallbacks::CreateAllBlobs(
           dispatcher_host->blob_storage_context(), std::move(value_blob))) {
     IndexedDBDatabaseError error =
-        CreateError(blink::kWebIDBDatabaseExceptionUnknownError,
-                    "Unknown error", transaction);
+        CreateError(blink::mojom::IDBException::kUnknownError, "Unknown error",
+                    transaction);
     std::move(callback).Run(blink::mojom::IDBDatabaseGetResult::NewErrorResult(
         blink::mojom::IDBError::New(error.code(), error.message())));
     return s;
@@ -987,9 +984,8 @@
   IDB_TRACE1("IndexedDBDatabase::GetAllOperation", "txn.id", transaction->id());
 
   if (!IsObjectStoreIdInMetadata(object_store_id)) {
-    IndexedDBDatabaseError error =
-        CreateError(blink::kWebIDBDatabaseExceptionUnknownError, "Bad request",
-                    transaction);
+    IndexedDBDatabaseError error = CreateError(
+        blink::mojom::IDBException::kUnknownError, "Bad request", transaction);
     std::move(callback).Run(
         blink::mojom::IDBDatabaseGetAllResult::NewErrorResult(
             blink::mojom::IDBError::New(error.code(), error.message())));
@@ -1006,8 +1002,8 @@
   Status s = Status::OK();
   if (!dispatcher_host) {
     IndexedDBDatabaseError error =
-        CreateError(blink::kWebIDBDatabaseExceptionUnknownError,
-                    "Unknown error", transaction);
+        CreateError(blink::mojom::IDBException::kUnknownError, "Unknown error",
+                    transaction);
     std::move(callback).Run(
         blink::mojom::IDBDatabaseGetAllResult::NewErrorResult(
             blink::mojom::IDBError::New(error.code(), error.message())));
@@ -1047,7 +1043,7 @@
   if (!s.ok()) {
     DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
     IndexedDBDatabaseError error =
-        CreateError(blink::kWebIDBDatabaseExceptionUnknownError,
+        CreateError(blink::mojom::IDBException::kUnknownError,
                     "Corruption detected, unable to continue", transaction);
     std::move(callback).Run(
         blink::mojom::IDBDatabaseGetAllResult::NewErrorResult(
@@ -1082,7 +1078,7 @@
     }
     if (!s.ok()) {
       IndexedDBDatabaseError error =
-          CreateError(blink::kWebIDBDatabaseExceptionUnknownError,
+          CreateError(blink::mojom::IDBException::kUnknownError,
                       "Seek failure, unable to continue", transaction);
       std::move(callback).Run(
           blink::mojom::IDBDatabaseGetAllResult::NewErrorResult(
@@ -1113,7 +1109,7 @@
       response_size += return_value.SizeEstimate();
     if (response_size > GetUsableMessageSizeInBytes()) {
       IndexedDBDatabaseError error =
-          CreateError(blink::kWebIDBDatabaseExceptionUnknownError,
+          CreateError(blink::mojom::IDBException::kUnknownError,
                       "Maximum IPC message size exceeded.", transaction);
       std::move(callback).Run(
           blink::mojom::IDBDatabaseGetAllResult::NewErrorResult(
@@ -1169,9 +1165,8 @@
   Status s = Status::OK();
 
   if (!IsObjectStoreIdInMetadata(params->object_store_id)) {
-    IndexedDBDatabaseError error =
-        CreateError(blink::kWebIDBDatabaseExceptionUnknownError, "Bad request",
-                    transaction);
+    IndexedDBDatabaseError error = CreateError(
+        blink::mojom::IDBException::kUnknownError, "Bad request", transaction);
     std::move(params->callback)
         .Run(blink::mojom::IDBTransactionPutResult::NewErrorResult(
             blink::mojom::IDBError::New(error.code(), error.message())));
@@ -1192,7 +1187,7 @@
     key_was_generated = true;
     if (!auto_inc_key->IsValid()) {
       IndexedDBDatabaseError error =
-          CreateError(blink::kWebIDBDatabaseExceptionConstraintError,
+          CreateError(blink::mojom::IDBException::kConstraintError,
                       "Maximum key generator value reached.", transaction);
       std::move(params->callback)
           .Run(blink::mojom::IDBTransactionPutResult::NewErrorResult(
@@ -1216,7 +1211,7 @@
       return found_status;
     if (found) {
       IndexedDBDatabaseError error =
-          CreateError(blink::kWebIDBDatabaseExceptionConstraintError,
+          CreateError(blink::mojom::IDBException::kConstraintError,
                       "Key already exists in the object store.", transaction);
       std::move(params->callback)
           .Run(blink::mojom::IDBTransactionPutResult::NewErrorResult(
@@ -1233,7 +1228,7 @@
       params->index_keys, &index_writers, &error_message, &obeys_constraints);
   if (!backing_store_success) {
     IndexedDBDatabaseError error =
-        CreateError(blink::kWebIDBDatabaseExceptionUnknownError,
+        CreateError(blink::mojom::IDBException::kUnknownError,
                     "Internal error: backing store error updating index keys.",
                     transaction);
     std::move(params->callback)
@@ -1243,8 +1238,8 @@
   }
   if (!obeys_constraints) {
     IndexedDBDatabaseError error =
-        CreateError(blink::kWebIDBDatabaseExceptionConstraintError,
-                    error_message, transaction);
+        CreateError(blink::mojom::IDBException::kConstraintError, error_message,
+                    transaction);
     std::move(params->callback)
         .Run(blink::mojom::IDBTransactionPutResult::NewErrorResult(
             blink::mojom::IDBError::New(error.code(), error.message())));
@@ -1316,7 +1311,7 @@
     return s;
   if (!found) {
     return transaction->Abort(IndexedDBDatabaseError(
-        blink::kWebIDBDatabaseExceptionUnknownError,
+        blink::mojom::IDBException::kUnknownError,
         "Internal error setting index keys for object store."));
   }
 
@@ -1332,12 +1327,12 @@
       false, index_keys, &index_writers, &error_message, &obeys_constraints);
   if (!backing_store_success) {
     return transaction->Abort(IndexedDBDatabaseError(
-        blink::kWebIDBDatabaseExceptionUnknownError,
+        blink::mojom::IDBException::kUnknownError,
         "Internal error: backing store error updating index keys."));
   }
   if (!obeys_constraints) {
     return transaction->Abort(IndexedDBDatabaseError(
-        blink::kWebIDBDatabaseExceptionConstraintError, error_message));
+        blink::mojom::IDBException::kConstraintError, error_message));
   }
 
   for (const auto& writer : index_writers) {
@@ -1371,7 +1366,7 @@
   Status s;
   if (!dispatcher_host) {
     IndexedDBDatabaseError error =
-        CreateError(blink::kWebIDBDatabaseExceptionUnknownError,
+        CreateError(blink::mojom::IDBException::kUnknownError,
                     "Dispatcher not connected.", transaction);
     std::move(params->callback)
         .Run(blink::mojom::IDBDatabaseOpenCursorResult::NewErrorResult(
@@ -1532,7 +1527,7 @@
     scoped_refptr<IndexedDBCallbacks> callbacks,
     IndexedDBTransaction* transaction) {
   if (!IsObjectStoreIdInMetadata(object_store_id)) {
-    callbacks->OnError(CreateError(blink::kWebIDBDatabaseExceptionDataError,
+    callbacks->OnError(CreateError(blink::mojom::IDBException::kDataError,
                                    "Object store id not valid.", transaction));
     return leveldb::Status::InvalidArgument("Invalid object_store_id.");
   }
@@ -1543,7 +1538,7 @@
       &current_number);
   if (!s.ok()) {
     callbacks->OnError(CreateError(
-        blink::kWebIDBDatabaseExceptionDataError,
+        blink::mojom::IDBException::kDataError,
         "Failed to get the current number of key generator.", transaction));
     return s;
   }
diff --git a/content/browser/indexed_db/indexed_db_database.h b/content/browser/indexed_db/indexed_db_database.h
index a4718bbb..0988ec9 100644
--- a/content/browser/indexed_db/indexed_db_database.h
+++ b/content/browser/indexed_db/indexed_db_database.h
@@ -38,7 +38,7 @@
 #include "content/common/content_export.h"
 #include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
-#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-forward.h"
 
 namespace blink {
 class IndexedDBKeyPath;
diff --git a/content/browser/indexed_db/indexed_db_database_error.cc b/content/browser/indexed_db/indexed_db_database_error.cc
index af0702e..649d834b 100644
--- a/content/browser/indexed_db/indexed_db_database_error.cc
+++ b/content/browser/indexed_db/indexed_db_database_error.cc
@@ -4,17 +4,20 @@
 
 #include "content/browser/indexed_db/indexed_db_database_error.h"
 
-namespace content {
+#include "base/strings/utf_string_conversions.h"
 
-IndexedDBDatabaseError::IndexedDBDatabaseError(int32_t code) : code_(code) {}
+namespace content {
 
 IndexedDBDatabaseError::IndexedDBDatabaseError() = default;
 
-IndexedDBDatabaseError::IndexedDBDatabaseError(int32_t code,
+IndexedDBDatabaseError::IndexedDBDatabaseError(blink::mojom::IDBException code)
+    : code_(code) {}
+
+IndexedDBDatabaseError::IndexedDBDatabaseError(blink::mojom::IDBException code,
                                                const char* message)
     : code_(code), message_(base::ASCIIToUTF16(message)) {}
 
-IndexedDBDatabaseError::IndexedDBDatabaseError(int32_t code,
+IndexedDBDatabaseError::IndexedDBDatabaseError(blink::mojom::IDBException code,
                                                const base::string16& message)
     : code_(code), message_(message) {}
 
diff --git a/content/browser/indexed_db/indexed_db_database_error.h b/content/browser/indexed_db/indexed_db_database_error.h
index 035209b..f07f397 100644
--- a/content/browser/indexed_db/indexed_db_database_error.h
+++ b/content/browser/indexed_db/indexed_db_database_error.h
@@ -8,28 +8,27 @@
 #include <stdint.h>
 
 #include "base/strings/string16.h"
-#include "base/strings/utf_string_conversions.h"
 #include "content/common/content_export.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-forward.h"
 
 namespace content {
 
 class CONTENT_EXPORT IndexedDBDatabaseError {
-  // TODO(dmurph): Move the WebIDBDatabaseException enum into mojo, and make
-  // the |code| type the mojo enum type.
  public:
   IndexedDBDatabaseError();
-  explicit IndexedDBDatabaseError(int32_t code);
-  IndexedDBDatabaseError(int32_t code, const char* message);
-  IndexedDBDatabaseError(int32_t code, const base::string16& message);
+  explicit IndexedDBDatabaseError(blink::mojom::IDBException code);
+  IndexedDBDatabaseError(blink::mojom::IDBException code, const char* message);
+  IndexedDBDatabaseError(blink::mojom::IDBException code,
+                         const base::string16& message);
   ~IndexedDBDatabaseError();
 
   IndexedDBDatabaseError& operator=(const IndexedDBDatabaseError& rhs);
 
-  int32_t code() const { return code_; }
+  blink::mojom::IDBException code() const { return code_; }
   const base::string16& message() const { return message_; }
 
  private:
-  int32_t code_ = 0;
+  blink::mojom::IDBException code_ = blink::mojom::IDBException::kNoError;
   base::string16 message_;
 };
 
diff --git a/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc b/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
index c8b8aa0..c255a684 100644
--- a/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
@@ -11,7 +11,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
-#include "base/strings/utf_offset_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/mock_callback.h"
@@ -41,7 +41,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
 #include "url/origin.h"
 
 using blink::IndexedDBDatabaseMetadata;
@@ -496,18 +495,18 @@
 
         EXPECT_CALL(
             put_callback,
-            Run(IsCallbackError(blink::kWebIDBDatabaseExceptionUnknownError)))
+            Run(IsCallbackError(blink::mojom::IDBException::kUnknownError)))
             .Times(1)
             .WillOnce(RunClosure(quit_closure2));
 
-        EXPECT_CALL(*connection->connection_callbacks,
-                    Abort(kTransactionId,
-                          blink::kWebIDBDatabaseExceptionUnknownError, _))
+        EXPECT_CALL(
+            *connection->connection_callbacks,
+            Abort(kTransactionId, blink::mojom::IDBException::kUnknownError, _))
             .Times(1)
             .WillOnce(RunClosure(quit_closure2));
 
         EXPECT_CALL(*connection->open_callbacks,
-                    Error(blink::kWebIDBDatabaseExceptionAbortError, _))
+                    Error(blink::mojom::IDBException::kAbortError, _))
             .Times(1)
             .WillOnce(RunClosure(std::move(quit_closure2)));
 
@@ -667,13 +666,13 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         ::testing::InSequence dummy;
 
-        EXPECT_CALL(*connection->connection_callbacks,
-                    Abort(kTransactionId,
-                          blink::kWebIDBDatabaseExceptionUnknownError, _))
+        EXPECT_CALL(
+            *connection->connection_callbacks,
+            Abort(kTransactionId, blink::mojom::IDBException::kUnknownError, _))
             .Times(1)
             .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(*connection->open_callbacks,
-                    Error(blink::kWebIDBDatabaseExceptionAbortError, _))
+                    Error(blink::mojom::IDBException::kAbortError, _))
             .Times(1)
             .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(*connection->connection_callbacks, ForcedClose())
@@ -743,13 +742,13 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         ::testing::InSequence dummy;
 
-        EXPECT_CALL(*connection->connection_callbacks,
-                    Abort(kTransactionId,
-                          blink::kWebIDBDatabaseExceptionUnknownError, _))
+        EXPECT_CALL(
+            *connection->connection_callbacks,
+            Abort(kTransactionId, blink::mojom::IDBException::kUnknownError, _))
             .Times(1)
             .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(*connection->open_callbacks,
-                    Error(blink::kWebIDBDatabaseExceptionAbortError, _))
+                    Error(blink::mojom::IDBException::kAbortError, _))
             .Times(1)
             .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(*connection->connection_callbacks, ForcedClose())
@@ -894,13 +893,13 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         ::testing::InSequence dummy;
 
-        EXPECT_CALL(*connection->connection_callbacks,
-                    Abort(kTransactionId,
-                          blink::kWebIDBDatabaseExceptionUnknownError, _))
+        EXPECT_CALL(
+            *connection->connection_callbacks,
+            Abort(kTransactionId, blink::mojom::IDBException::kUnknownError, _))
             .Times(1)
             .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(*connection->open_callbacks,
-                    Error(blink::kWebIDBDatabaseExceptionAbortError, _))
+                    Error(blink::mojom::IDBException::kAbortError, _))
             .Times(1)
             .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(*connection->connection_callbacks, ForcedClose())
@@ -971,13 +970,13 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         ::testing::InSequence dummy;
 
-        EXPECT_CALL(*connection->connection_callbacks,
-                    Abort(kTransactionId,
-                          blink::kWebIDBDatabaseExceptionUnknownError, _))
+        EXPECT_CALL(
+            *connection->connection_callbacks,
+            Abort(kTransactionId, blink::mojom::IDBException::kUnknownError, _))
             .Times(1)
             .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(*connection->open_callbacks,
-                    Error(blink::kWebIDBDatabaseExceptionAbortError, _))
+                    Error(blink::mojom::IDBException::kAbortError, _))
             .Times(1)
             .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(*connection->connection_callbacks, ForcedClose())
diff --git a/content/browser/indexed_db/indexed_db_factory_impl.cc b/content/browser/indexed_db/indexed_db_factory_impl.cc
index c477d56..a3744df 100644
--- a/content/browser/indexed_db/indexed_db_factory_impl.cc
+++ b/content/browser/indexed_db/indexed_db_factory_impl.cc
@@ -46,7 +46,7 @@
 #include "content/browser/indexed_db/indexed_db_tracing.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_database.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_factory.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
 #include "third_party/leveldatabase/env_chromium.h"
 
 using base::ASCIIToUTF16;
@@ -85,7 +85,7 @@
 
 IndexedDBDatabaseError CreateDefaultError() {
   return IndexedDBDatabaseError(
-      blink::kWebIDBDatabaseExceptionUnknownError,
+      blink::mojom::IDBException::kUnknownError,
       ASCIIToUTF16("Internal error opening backing store"
                    " for indexedDB.open."));
 }
@@ -200,7 +200,7 @@
       factory->backing_store_->db(),
       factory->backing_store_->origin_identifier(), &names_and_versions);
   if (!s.ok()) {
-    error = IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionUnknownError,
+    error = IndexedDBDatabaseError(blink::mojom::IDBException::kUnknownError,
                                    "Internal error opening backing store for "
                                    "indexedDB.databases().");
     callbacks->OnError(error);
@@ -243,7 +243,7 @@
       factory->backing_store_->db(),
       factory->backing_store_->origin_identifier(), &names);
   if (!s.ok()) {
-    error = IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionUnknownError,
+    error = IndexedDBDatabaseError(blink::mojom::IDBException::kUnknownError,
                                    "Internal error opening backing store for "
                                    "indexedDB.webkitGetDatabaseNames.");
     callbacks->OnError(error);
@@ -291,7 +291,7 @@
       std::make_unique<IndexedDBMetadataCoding>(), std::move(unique_identifier),
       factory->lock_manager());
   if (!database.get()) {
-    error = IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionUnknownError,
+    error = IndexedDBDatabaseError(blink::mojom::IDBException::kUnknownError,
                                    ASCIIToUTF16("Internal error creating "
                                                 "database backend for "
                                                 "indexedDB.open."));
@@ -357,7 +357,7 @@
       factory->backing_store()->db(),
       factory->backing_store()->origin_identifier(), &names);
   if (!s.ok()) {
-    error = IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionUnknownError,
+    error = IndexedDBDatabaseError(blink::mojom::IDBException::kUnknownError,
                                    "Internal error opening backing store for "
                                    "indexedDB.deleteDatabase.");
     callbacks->OnError(error);
@@ -382,7 +382,7 @@
       factory->lock_manager());
   if (!database.get()) {
     error = IndexedDBDatabaseError(
-        blink::kWebIDBDatabaseExceptionUnknownError,
+        blink::mojom::IDBException::kUnknownError,
         ASCIIToUTF16("Internal error creating database backend for "
                      "indexedDB.deleteDatabase."));
     callbacks->OnError(error);
@@ -718,7 +718,7 @@
     if (disk_full) {
       return {IndexedDBOriginStateHandle(), s,
               IndexedDBDatabaseError(
-                  blink::kWebIDBDatabaseExceptionQuotaError,
+                  blink::mojom::IDBException::kQuotaError,
                   ASCIIToUTF16("Encountered full disk while opening "
                                "backing store for indexedDB.open.")),
               data_loss_info, /*was_cold_open=*/true};
@@ -941,11 +941,10 @@
   if (status.IsCorruption()) {
     IndexedDBDatabaseError error =
         message != nullptr
-            ? IndexedDBDatabaseError(
-                  blink::kWebIDBDatabaseExceptionUnknownError, message)
-            : IndexedDBDatabaseError(
-                  blink::kWebIDBDatabaseExceptionUnknownError,
-                  base::ASCIIToUTF16(status.ToString()));
+            ? IndexedDBDatabaseError(blink::mojom::IDBException::kUnknownError,
+                                     message)
+            : IndexedDBDatabaseError(blink::mojom::IDBException::kUnknownError,
+                                     base::ASCIIToUTF16(status.ToString()));
     HandleBackingStoreCorruption(origin, error);
   } else {
     HandleBackingStoreFailure(origin);
diff --git a/content/browser/indexed_db/indexed_db_factory_unittest.cc b/content/browser/indexed_db/indexed_db_factory_unittest.cc
index 85e8234..ed93533 100644
--- a/content/browser/indexed_db/indexed_db_factory_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_factory_unittest.cc
@@ -41,7 +41,7 @@
 #include "storage/browser/test/mock_quota_manager_proxy.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -695,7 +695,7 @@
         error_called_(false) {}
   void OnError(const IndexedDBDatabaseError& error) override {
     error_called_ = true;
-    EXPECT_EQ(blink::kWebIDBDatabaseExceptionQuotaError, error.code());
+    EXPECT_EQ(blink::mojom::IDBException::kQuotaError, error.code());
   }
   bool error_called() const { return error_called_; }
 
diff --git a/content/browser/indexed_db/indexed_db_origin_state.cc b/content/browser/indexed_db/indexed_db_origin_state.cc
index d9833ecc..a2acd97 100644
--- a/content/browser/indexed_db/indexed_db_origin_state.cc
+++ b/content/browser/indexed_db/indexed_db_origin_state.cc
@@ -26,7 +26,7 @@
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_database.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_factory.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_transaction.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
 
 namespace content {
 namespace {
@@ -138,7 +138,7 @@
       if (connection) {
         leveldb::Status status =
             connection->AbortAllTransactions(IndexedDBDatabaseError(
-                blink::kWebIDBDatabaseExceptionUnknownError,
+                blink::mojom::IDBException::kUnknownError,
                 "Aborting all transactions for the origin."));
         if (!status.ok()) {
           // This call should delete this object.
diff --git a/content/browser/indexed_db/indexed_db_transaction.cc b/content/browser/indexed_db/indexed_db_transaction.cc
index 9492a77..f226243 100644
--- a/content/browser/indexed_db/indexed_db_transaction.cc
+++ b/content/browser/indexed_db/indexed_db_transaction.cc
@@ -20,7 +20,7 @@
 #include "content/browser/indexed_db/indexed_db_database.h"
 #include "content/browser/indexed_db/indexed_db_database_callbacks.h"
 #include "content/browser/indexed_db/indexed_db_tracing.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
 #include "third_party/leveldatabase/env_chromium.h"
 
 namespace content {
@@ -42,21 +42,21 @@
 };
 
 // Used for UMA metrics - do not change mappings.
-UmaIDBException ExceptionCodeToUmaEnum(uint16_t code) {
+UmaIDBException ExceptionCodeToUmaEnum(blink::mojom::IDBException code) {
   switch (code) {
-    case blink::kWebIDBDatabaseExceptionUnknownError:
+    case blink::mojom::IDBException::kUnknownError:
       return UmaIDBExceptionUnknownError;
-    case blink::kWebIDBDatabaseExceptionConstraintError:
+    case blink::mojom::IDBException::kConstraintError:
       return UmaIDBExceptionConstraintError;
-    case blink::kWebIDBDatabaseExceptionDataError:
+    case blink::mojom::IDBException::kDataError:
       return UmaIDBExceptionDataError;
-    case blink::kWebIDBDatabaseExceptionVersionError:
+    case blink::mojom::IDBException::kVersionError:
       return UmaIDBExceptionVersionError;
-    case blink::kWebIDBDatabaseExceptionAbortError:
+    case blink::mojom::IDBException::kAbortError:
       return UmaIDBExceptionAbortError;
-    case blink::kWebIDBDatabaseExceptionQuotaError:
+    case blink::mojom::IDBException::kQuotaError:
       return UmaIDBExceptionQuotaError;
-    case blink::kWebIDBDatabaseExceptionTimeoutError:
+    case blink::mojom::IDBException::kTimeoutError:
       return UmaIDBExceptionTimeoutError;
     default:
       NOTREACHED();
@@ -268,7 +268,7 @@
   switch (result) {
     case IndexedDBBackingStore::BlobWriteResult::kFailure: {
       leveldb::Status status = Abort(IndexedDBDatabaseError(
-          blink::kWebIDBDatabaseExceptionDataError, "Failed to write blobs."));
+          blink::mojom::IDBException::kDataError, "Failed to write blobs."));
       if (!status.ok())
         tear_down_callback_.Run(status);
       // The result is ignored.
@@ -318,7 +318,7 @@
   if (num_errors_sent_ != num_errors_handled_) {
     is_commit_pending_ = false;
     return Abort(
-        IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionUnknownError));
+        IndexedDBDatabaseError(blink::mojom::IDBException::kUnknownError));
   }
 
   state_ = COMMITTING;
@@ -428,12 +428,11 @@
     IndexedDBDatabaseError error;
     if (leveldb_env::IndicatesDiskFull(s)) {
       error = IndexedDBDatabaseError(
-          blink::kWebIDBDatabaseExceptionQuotaError,
+          blink::mojom::IDBException::kQuotaError,
           "Encountered disk full while committing transaction.");
     } else {
-      error =
-          IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionUnknownError,
-                                 "Internal error committing transaction.");
+      error = IndexedDBDatabaseError(blink::mojom::IDBException::kUnknownError,
+                                     "Internal error committing transaction.");
     }
     callbacks_->OnAbort(*this, error);
     if (database_)
@@ -525,7 +524,7 @@
 
 void IndexedDBTransaction::Timeout() {
   leveldb::Status result = Abort(IndexedDBDatabaseError(
-      blink::kWebIDBDatabaseExceptionTimeoutError,
+      blink::mojom::IDBException::kTimeoutError,
       base::ASCIIToUTF16("Transaction timed out due to inactivity.")));
   if (!result.ok())
     tear_down_callback_.Run(result);
diff --git a/content/browser/indexed_db/indexed_db_transaction.h b/content/browser/indexed_db/indexed_db_transaction.h
index be6369a..572eb76 100644
--- a/content/browser/indexed_db/indexed_db_transaction.h
+++ b/content/browser/indexed_db/indexed_db_transaction.h
@@ -28,7 +28,7 @@
 #include "content/browser/indexed_db/indexed_db_observer.h"
 #include "content/browser/indexed_db/indexed_db_task_helper.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
-#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-forward.h"
 
 namespace content {
 
diff --git a/content/browser/indexed_db/indexed_db_transaction_unittest.cc b/content/browser/indexed_db/indexed_db_transaction_unittest.cc
index 20e8cd6..502e09b 100644
--- a/content/browser/indexed_db/indexed_db_transaction_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_transaction_unittest.cc
@@ -29,7 +29,7 @@
 #include "content/browser/indexed_db/mock_indexed_db_factory.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
 
 namespace content {
 namespace indexed_db_transaction_unittest {
@@ -224,7 +224,7 @@
 
   // Clean up to avoid leaks.
   transaction->Abort(IndexedDBDatabaseError(
-      IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionAbortError,
+      IndexedDBDatabaseError(blink::mojom::IDBException::kAbortError,
                              "Transaction aborted by user.")));
   EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
@@ -438,9 +438,8 @@
 
   RunPostedTasks();
 
-  transaction->Abort(
-      IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionAbortError,
-                             "Transaction aborted by user."));
+  transaction->Abort(IndexedDBDatabaseError(
+      blink::mojom::IDBException::kAbortError, "Transaction aborted by user."));
   EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
   EXPECT_EQ(0, transaction->pending_preemptive_events_);
@@ -548,7 +547,7 @@
   // Abort the transaction, which should cancel the
   // |RegisterAndScheduleTransaction()| pending lock request.
   transaction->Abort(
-      IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionUnknownError));
+      IndexedDBDatabaseError(blink::mojom::IDBException::kUnknownError));
   EXPECT_EQ(transaction->state(), IndexedDBTransaction::FINISHED);
 
   // Clear |temp_lock_receiver| so we can test later that all locks have
diff --git a/content/browser/indexed_db/indexed_db_unittest.cc b/content/browser/indexed_db/indexed_db_unittest.cc
index af0da64..083e700f 100644
--- a/content/browser/indexed_db/indexed_db_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/bind_test_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/default_clock.h"
diff --git a/content/browser/indexed_db/mock_mojo_indexed_db_callbacks.h b/content/browser/indexed_db/mock_mojo_indexed_db_callbacks.h
index 620bd00..3fd3bb6 100644
--- a/content/browser/indexed_db/mock_mojo_indexed_db_callbacks.h
+++ b/content/browser/indexed_db/mock_mojo_indexed_db_callbacks.h
@@ -29,7 +29,9 @@
   mojo::PendingAssociatedRemote<blink::mojom::IDBCallbacks>
   CreateInterfacePtrAndBind();
 
-  MOCK_METHOD2(Error, void(int32_t code, const base::string16& message));
+  MOCK_METHOD2(Error,
+               void(blink::mojom::IDBException code,
+                    const base::string16& message));
 
   MOCK_METHOD1(SuccessNamesAndVersionsList,
                void(std::vector<blink::mojom::IDBNameAndVersionPtr> list));
diff --git a/content/browser/indexed_db/mock_mojo_indexed_db_database_callbacks.h b/content/browser/indexed_db/mock_mojo_indexed_db_database_callbacks.h
index bedff45..3947d49 100644
--- a/content/browser/indexed_db/mock_mojo_indexed_db_database_callbacks.h
+++ b/content/browser/indexed_db/mock_mojo_indexed_db_database_callbacks.h
@@ -28,7 +28,7 @@
   MOCK_METHOD2(VersionChange, void(int64_t old_version, int64_t new_version));
   MOCK_METHOD3(Abort,
                void(int64_t transaction_id,
-                    int32_t code,
+                    blink::mojom::IDBException code,
                     const base::string16& message));
   MOCK_METHOD1(Complete, void(int64_t transaction_id));
 
diff --git a/content/browser/indexed_db/transaction_impl.cc b/content/browser/indexed_db/transaction_impl.cc
index 0ff55b1c..d85f0b5 100644
--- a/content/browser/indexed_db/transaction_impl.cc
+++ b/content/browser/indexed_db/transaction_impl.cc
@@ -21,7 +21,7 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "storage/browser/blob/blob_storage_context.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
 
 namespace content {
 namespace {
@@ -29,7 +29,7 @@
 const char kInvalidBlobFilePath[] = "Blob file path is invalid";
 
 IndexedDBDatabaseError CreateBackendAbortError() {
-  return IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionAbortError,
+  return IndexedDBDatabaseError(blink::mojom::IDBException::kAbortError,
                                 "Backend aborted error");
 }
 
@@ -181,7 +181,7 @@
       return;
     }
     case IOHelper::LoadResultCode::kAbort: {
-      IndexedDBDatabaseError error(blink::kWebIDBDatabaseExceptionUnknownError,
+      IndexedDBDatabaseError error(blink::mojom::IDBException::kUnknownError,
                                    kInvalidBlobUuid);
       std::move(callback).Run(
           blink::mojom::IDBTransactionPutResult::NewErrorResult(
@@ -207,9 +207,8 @@
     }
     case IOHelper::LoadResultCode::kSuccess: {
       if (!transaction_) {
-        IndexedDBDatabaseError error(
-            blink::kWebIDBDatabaseExceptionUnknownError,
-            "Unknown transaction.");
+        IndexedDBDatabaseError error(blink::mojom::IDBException::kUnknownError,
+                                     "Unknown transaction.");
         std::move(callback).Run(
             blink::mojom::IDBTransactionPutResult::NewErrorResult(
                 blink::mojom::IDBError::New(error.code(), error.message())));
@@ -218,8 +217,8 @@
 
       IndexedDBConnection* connection = transaction_->connection();
       if (!connection->IsConnected()) {
-        IndexedDBDatabaseError error(
-            blink::kWebIDBDatabaseExceptionUnknownError, "Not connected.");
+        IndexedDBDatabaseError error(blink::mojom::IDBException::kUnknownError,
+                                     "Not connected.");
         std::move(callback).Run(
             blink::mojom::IDBTransactionPutResult::NewErrorResult(
                 blink::mojom::IDBError::New(error.code(), error.message())));
@@ -381,7 +380,7 @@
   } else {
     connection->AbortTransactionAndTearDownOnError(
         transaction_.get(),
-        IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionQuotaError));
+        IndexedDBDatabaseError(blink::mojom::IDBException::kQuotaError));
   }
 }
 
diff --git a/content/browser/resources/process/process_internals.css b/content/browser/resources/process/process_internals.css
index ba09677..c5b0b09 100644
--- a/content/browser/resources/process/process_internals.css
+++ b/content/browser/resources/process/process_internals.css
@@ -81,6 +81,12 @@
   z-index: 1;
 }
 
+@media(forced-colors) {

+  .content-header {

+    background: none;

+  }

+}
+
 .tree-label {
   user-select: text;
 }
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index d303c1b5..44871cb 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -1573,7 +1573,7 @@
           std::vector<int>() /* initiator_origin_trial_features */,
           std::string() /* href_translate */,
           false /* is_history_navigation_in_new_child_frame */,
-          base::TimeTicks());
+          base::TimeTicks(), base::nullopt /* frame_policy */);
   mojom::BeginNavigationParamsPtr begin_params =
       mojom::BeginNavigationParams::New(
           std::string() /* headers */, net::LOAD_NORMAL,
diff --git a/content/common/child_process_host_impl.cc b/content/common/child_process_host_impl.cc
index 13fd51a4..26cf172 100644
--- a/content/common/child_process_host_impl.cc
+++ b/content/common/child_process_host_impl.cc
@@ -31,6 +31,7 @@
 #include "ipc/ipc_channel_mojo.h"
 #include "ipc/ipc_logging.h"
 #include "ipc/message_filter.h"
+#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
 #include "services/resource_coordinator/public/mojom/memory_instrumentation/constants.mojom.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 
@@ -157,7 +158,8 @@
   BindInterface(IPC::mojom::ChannelBootstrap::Name_, std::move(pipe.handle1));
   channel_ = IPC::ChannelMojo::Create(
       std::move(pipe.handle0), IPC::Channel::MODE_SERVER, this,
-      base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get());
+      base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get(),
+      mojo::internal::MessageQuotaChecker::MaybeCreate());
   DCHECK(channel_);
 
   bool initialized = InitChannel();
diff --git a/content/common/navigation_params.mojom b/content/common/navigation_params.mojom
index e2761bd..e9fcc25 100644
--- a/content/common/navigation_params.mojom
+++ b/content/common/navigation_params.mojom
@@ -14,6 +14,8 @@
 import "services/network/public/mojom/url_response_head.mojom";
 import "third_party/blink/public/mojom/fetch/fetch_api_request.mojom";
 import "third_party/blink/public/mojom/referrer.mojom";
+import "third_party/blink/public/mojom/feature_policy/feature_policy.mojom";
+import "third_party/blink/public/mojom/frame/frame_policy.mojom";
 import "url/mojom/origin.mojom";
 import "url/mojom/url.mojom";
 
@@ -207,6 +209,20 @@
   // not always be set; it depends on the creator of the CommonNavigationParams
   // setting it.
   mojo_base.mojom.TimeTicks input_start;
+
+  // A snapshot value of frame policy(both sandbox flags and container policy)
+  // of the frame that is being navigated. The snapshot value is captured at the
+  // start of navigation.
+  // The value is set to null for top-level browser-initiated navigation.
+  // The value is also set to null when Ctrl-click was used to create a new tab
+  // and navigate, i.e. the document in new tab will inherit no frame_policy.
+  // For navigation created from NavigationControllerImpl::
+  // CreateNavigationRequestFromEntry which corresponds to history navigation,
+  // the value is set to current FrameTreeNode::pending_frame_policy in
+  // frame_tree_node. This behavior is currently undocumented and probably need
+  // further discussion. Another potential approach is to record frame policy
+  // value in NavigationEntry and reuse the historical value.
+  blink.mojom.FramePolicy? frame_policy;
 };
 
 // Provided by the browser -----------------------------------------------------
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java
index 1ac1beba..292e217 100644
--- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java
+++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java
@@ -128,6 +128,14 @@
 
     @Override
     @CalledByNative
+    public void didChangeVisibleSecurityState() {
+        for (mObserversIterator.rewind(); mObserversIterator.hasNext();) {
+            mObserversIterator.next().didChangeVisibleSecurityState();
+        }
+    }
+
+    @Override
+    @CalledByNative
     public void didFailLoad(
             boolean isMainFrame, int errorCode, String description, String failingUrl) {
         for (mObserversIterator.rewind(); mObserversIterator.hasNext();) {
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java b/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java
index aa90d260..d86fbbde 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java
@@ -82,6 +82,11 @@
     public void loadProgressChanged(float progress) {}
 
     /**
+     * Called when a page's visible security state has changed.
+     */
+    public void didChangeVisibleSecurityState() {}
+
+    /**
      * Called when an error occurs while loading a page and/or the page fails to load.
      * @param isMainFrame Whether the navigation occurred in main frame.
      * @param errorCode Error code for the occurring error.
diff --git a/content/public/test/content_browser_test_utils_mac.mm b/content/public/test/content_browser_test_utils_mac.mm
index 57fec6a..9683d45 100644
--- a/content/public/test/content_browser_test_utils_mac.mm
+++ b/content/public/test/content_browser_test_utils_mac.mm
@@ -163,7 +163,7 @@
 - (void)didAddSubview:(NSView*)view {
   content::RenderWidgetHostViewCocoaObserver::GetSwizzler(
       content::RenderWidgetHostViewCocoaObserver::kDidAddSubview)
-      ->GetOriginalImplementation()(self, _cmd, view);
+      ->InvokeOriginal<void, NSView*>(self, _cmd, view);
 
   content::RenderWidgetHostViewMac* rwhv_mac =
       content::GetRenderWidgetHostViewMac(self);
@@ -206,7 +206,8 @@
   content::RenderWidgetHostViewCocoaObserver::GetSwizzler(
       content::RenderWidgetHostViewCocoaObserver::
           kShowDefinitionForAttributedString)
-      ->GetOriginalImplementation()(self, _cmd, attrString, textBaselineOrigin);
+      ->InvokeOriginal<void, NSAttributedString*, NSPoint>(
+          self, _cmd, attrString, textBaselineOrigin);
 
   auto* rwhv_mac = content::GetRenderWidgetHostViewMac(self);
 
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc
index 374f186..e00990e 100644
--- a/content/public/test/render_view_test.cc
+++ b/content/public/test/render_view_test.cc
@@ -667,7 +667,8 @@
       base::TimeTicks::Now(), "GET", nullptr, base::Optional<SourceLocation>(),
       false /* started_from_context_menu */, false /* has_user_gesture */,
       InitiatorCSPInfo(), std::vector<int>(), std::string(),
-      false /* is_history_navigation_in_new_child_frame */, base::TimeTicks());
+      false /* is_history_navigation_in_new_child_frame */, base::TimeTicks(),
+      base::nullopt /* frame_policy */);
   RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_);
   TestRenderFrame* frame =
       static_cast<TestRenderFrame*>(impl->GetMainRenderFrame());
@@ -813,7 +814,8 @@
       base::TimeTicks::Now(), "GET", nullptr, base::Optional<SourceLocation>(),
       false /* started_from_context_menu */, false /* has_user_gesture */,
       InitiatorCSPInfo(), std::vector<int>(), std::string(),
-      false /* is_history_navigation_in_new_child_frame */, base::TimeTicks());
+      false /* is_history_navigation_in_new_child_frame */, base::TimeTicks(),
+      base::nullopt /* frame policy */);
   auto commit_params = CreateCommitNavigationParams();
   commit_params->page_state = state;
   commit_params->nav_entry_id = pending_offset + 1;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index a8cb25b..e76f0fc 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -597,7 +597,8 @@
                                  info->initiator_csp.self_source.value()))
                            : base::nullopt),
       initiator_origin_trial_features, info->href_translate.Latin1(),
-      is_history_navigation_in_new_child_frame, info->input_start);
+      is_history_navigation_in_new_child_frame, info->input_start,
+      info->frame_policy);
 }
 
 WebFrameLoadType NavigationTypeToLoadType(mojom::NavigationType navigation_type,
@@ -1010,6 +1011,8 @@
   navigation_params->appcache_host_id =
       commit_params.appcache_host_id.value_or(base::UnguessableToken());
 
+  navigation_params->frame_policy = common_params.frame_policy;
+
   if (common_params.navigation_type == mojom::NavigationType::RESTORE) {
     // We're doing a load of a page that was restored from the last session.
     // By default this prefers the cache over loading
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index f957828..62ca379 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1937,6 +1937,7 @@
     ":run_all_unittests",
     ":test_interfaces",
     ":test_support",
+    "//base",
     "//base/allocator:buildflags",
     "//base/test:test_support",
     "//base/third_party/dynamic_annotations",
diff --git a/docs/accessibility/overview.md b/docs/accessibility/overview.md
index d11a2f0..7ab969b 100644
--- a/docs/accessibility/overview.md
+++ b/docs/accessibility/overview.md
@@ -18,7 +18,7 @@
 * Screen readers for blind users that describe the screen using
   synthesized speech or braille
 * Voice control applications that let you speak to the computer,
-* Switch access that lets you control the computer with a small number
+* Switch Access that lets you control the computer with a small number
   of physical switches,
 * Magnifiers that magnify a portion of the screen, and often highlight the
   cursor and caret for easier viewing, and
diff --git a/docs/login/user_types.md b/docs/login/user_types.md
index 230b414..3e5f8c99 100644
--- a/docs/login/user_types.md
+++ b/docs/login/user_types.md
@@ -13,7 +13,6 @@
 
 Regular users that were registered using their GAIA account.
 
-
 ## Child users
 
 Users that logged in using
@@ -52,6 +51,8 @@
     *   If the test user is logged in using `LoginManagerMixin`, the injected
         `UserContext` has to have the refresh token matching the token passed to
         `FakeGaiaMixin`.
+*   Note that `LoggedInUserMixin` is a compound helper mixin that conveniently
+    packages the mixins mentioned above into an easy-to-use interface.
 
 ## Guest
 
@@ -62,7 +63,7 @@
 To test guest session state, use `GuestSessionMixin` - this will set up
 appropriate guest session flags.
 
-Testing guest user login is more complicated, as guest login required Chrome
+Testing guest user login is more complicated, as guest login requires Chrome
 restart. The test will require two parts:
 *   `PRE_BrowserTest` test that requests login
 *   `BrowserTest` that can test guest session state
@@ -70,4 +71,3 @@
 To properly set up and preserve Chrome flags between sessions runs, use
 `LoginManagerMixin`, and set it up using
 `LoginManagerMixin::set_session_restore_enabled()`
-
diff --git a/fuchsia/engine/BUILD.gn b/fuchsia/engine/BUILD.gn
index e1294b4..cead5da8 100644
--- a/fuchsia/engine/BUILD.gn
+++ b/fuchsia/engine/BUILD.gn
@@ -97,6 +97,7 @@
     "//third_party/fuchsia-sdk/sdk:ui_gfx",
     "//third_party/fuchsia-sdk/sdk:web",
     "//third_party/widevine/cdm:headers",
+    "//ui/accessibility",
     "//ui/aura",
     "//ui/base",
     "//ui/base/ime",
@@ -223,10 +224,15 @@
 source_set("browsertest_core") {
   testonly = true
   sources = [
+    "test/test_data.cc",
+    "test/test_data.h",
     "test/web_engine_browser_test.cc",
     "test/web_engine_browser_test.h",
     "test/web_engine_test_launcher.cc",
   ]
+  data = [
+    "test/data",
+  ]
   deps = [
     ":web_engine_core",
     "//content/public/browser",
@@ -240,15 +246,13 @@
 
 test("web_engine_browsertests") {
   sources = [
+    "browser/accessibility_bridge_browsertest.cc",
     "browser/content_directory_browsertest.cc",
     "browser/context_impl_browsertest.cc",
     "browser/frame_impl_browsertest.cc",
     "browser/media_browsertest.cc",
   ]
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
-  data = [
-    "test/data",
-  ]
   deps = [
     ":browsertest_core",
     ":switches",
@@ -260,6 +264,7 @@
     "//net:test_support",
     "//testing/gmock",
     "//testing/gtest",
+    "//third_party/fuchsia-sdk/sdk:accessibility_semantics",
     "//third_party/fuchsia-sdk/sdk:scenic_cpp",
     "//ui/ozone",
   ]
@@ -267,7 +272,6 @@
 
 test("web_engine_unittests") {
   sources = [
-    "browser/accessibility_bridge_unittest.cc",
     "browser/ax_tree_converter_unittest.cc",
     "browser/cookie_manager_impl_unittest.cc",
     "browser/frame_impl_unittest.cc",
@@ -288,7 +292,6 @@
     "//services/network/public/mojom",
     "//testing/gmock",
     "//testing/gtest",
-    "//third_party/fuchsia-sdk/sdk:accessibility_semantics",
     "//third_party/fuchsia-sdk/sdk:scenic_cpp",
     "//third_party/fuchsia-sdk/sdk:web",
   ]
diff --git a/fuchsia/engine/browser/accessibility_bridge.cc b/fuchsia/engine/browser/accessibility_bridge.cc
index b2f521c..e2e6ff3 100644
--- a/fuchsia/engine/browser/accessibility_bridge.cc
+++ b/fuchsia/engine/browser/accessibility_bridge.cc
@@ -4,18 +4,116 @@
 
 #include "fuchsia/engine/browser/accessibility_bridge.h"
 
+#include <lib/sys/cpp/component_context.h>
+#include <lib/ui/scenic/cpp/view_ref_pair.h>
+#include <sys/types.h>
+
+#include "base/fuchsia/fuchsia_logging.h"
 #include "base/logging.h"
+#include "fuchsia/engine/browser/ax_tree_converter.h"
+
+using fuchsia::accessibility::semantics::SemanticTree;
+
+namespace {
+
+constexpr uint32_t kSemanticNodeRootId = 0;
+
+// TODO(https://crbug.com/973095): Update this value based on average and
+// maximum sizes of serialized Semantic Nodes.
+constexpr size_t kMaxNodesPerUpdate = 16;
+
+// Template function to handle batching and sending of FIDL messages when
+// updating or deleting nodes.
+template <typename T>
+void SendBatches(std::vector<T> pending_items,
+                 base::RepeatingCallback<void(std::vector<T>)> callback) {
+  std::vector<T> nodes_to_send;
+  for (size_t i = 0; i < pending_items.size(); i++) {
+    nodes_to_send.push_back(std::move(pending_items.at(i)));
+    if (nodes_to_send.size() == kMaxNodesPerUpdate) {
+      callback.Run(std::move(nodes_to_send));
+      nodes_to_send.clear();
+    }
+  }
+
+  if (!nodes_to_send.empty()) {
+    callback.Run(std::move(nodes_to_send));
+  }
+}
+
+}  // namespace
 
 AccessibilityBridge::AccessibilityBridge(
     fuchsia::accessibility::semantics::SemanticsManagerPtr semantics_manager,
-    fuchsia::ui::views::ViewRef view_ref)
-    : binding_(this) {
+    fuchsia::ui::views::ViewRef view_ref,
+    content::WebContents* web_contents)
+    : binding_(this), web_contents_(web_contents) {
+  DCHECK(web_contents_);
+  Observe(web_contents_);
+  tree_.AddObserver(this);
+
   semantics_manager->RegisterViewForSemantics(
       std::move(view_ref), binding_.NewBinding(), tree_ptr_.NewRequest());
+  tree_ptr_.set_error_handler([](zx_status_t status) {
+    ZX_LOG_IF(ERROR, status != ZX_ERR_PEER_CLOSED, status)
+        << "Semantic Tree disconnected.";
+  });
 }
 
 AccessibilityBridge::~AccessibilityBridge() = default;
 
+void AccessibilityBridge::TryCommit() {
+  if (commit_inflight_ || (to_send_.empty() && to_delete_.empty()))
+    return;
+
+  if (!to_send_.empty()) {
+    SendBatches<fuchsia::accessibility::semantics::Node>(
+        std::move(to_send_),
+        base::BindRepeating(
+            [](SemanticTree* tree,
+               std::vector<fuchsia::accessibility::semantics::Node> nodes) {
+              tree->UpdateSemanticNodes(std::move(nodes));
+            },
+            base::Unretained(tree_ptr_.get())));
+  }
+
+  if (!to_delete_.empty()) {
+    SendBatches<uint32_t>(
+        std::move(to_delete_),
+        base::BindRepeating(
+            [](SemanticTree* tree, std::vector<uint32_t> nodes) {
+              tree->DeleteSemanticNodes(std::move(nodes));
+            },
+            base::Unretained(tree_ptr_.get())));
+  }
+
+  tree_ptr_->CommitUpdates(
+      fit::bind_member(this, &AccessibilityBridge::OnCommitComplete));
+  commit_inflight_ = true;
+}
+
+void AccessibilityBridge::OnCommitComplete() {
+  commit_inflight_ = false;
+}
+
+uint32_t AccessibilityBridge::ConvertToFuchsiaNodeId(int32_t ax_node_id) {
+  if (ax_node_id == root_id_) {
+    // On the Fuchsia side, the root node is indicated by id
+    // |kSemanticNodeRootId|, which is 0.
+    return kSemanticNodeRootId;
+  } else {
+    // AXNode ids are signed, Semantic Node ids are unsigned.
+    return bit_cast<uint32_t>(ax_node_id);
+  }
+}
+
+void AccessibilityBridge::AccessibilityEventReceived(
+    const content::AXEventNotificationDetails& details) {
+  for (const ui::AXTreeUpdate& update : details.updates) {
+    tree_.Unserialize(update);
+  }
+}
+
 void AccessibilityBridge::OnAccessibilityActionRequested(
     uint32_t node_id,
     fuchsia::accessibility::semantics::Action action,
@@ -31,5 +129,45 @@
 void AccessibilityBridge::OnSemanticsModeChanged(
     bool updates_enabled,
     OnSemanticsModeChangedCallback callback) {
-  NOTIMPLEMENTED();
+  if (updates_enabled) {
+    // The first call to AccessibilityEventReceived after this call will be the
+    // entire semantic tree.
+    web_contents_->EnableWebContentsOnlyAccessibilityMode();
+  } else {
+    // The SemanticsManager will clear all state in this case, which is mirrored
+    // here.
+    to_send_.clear();
+    to_delete_.clear();
+    commit_inflight_ = false;
+  }
+
+  // Notify the SemanticsManager that SemanticsMode has been updated.
+  callback();
+}
+
+void AccessibilityBridge::OnNodeWillBeDeleted(ui::AXTree* tree,
+                                              ui::AXNode* node) {
+  to_delete_.push_back(ConvertToFuchsiaNodeId(node->id()));
+  TryCommit();
+}
+
+void AccessibilityBridge::OnAtomicUpdateFinished(
+    ui::AXTree* tree,
+    bool root_changed,
+    const std::vector<ui::AXTreeObserver::Change>& changes) {
+  root_id_ = tree_.root()->id();
+  for (const ui::AXTreeObserver::Change& change : changes) {
+    // Reparent changes aren't included here because they consist of a delete
+    // and create change, which are already being handled.
+    if (change.type == ui::AXTreeObserver::NODE_CREATED ||
+        change.type == ui::AXTreeObserver::SUBTREE_CREATED ||
+        change.type == ui::AXTreeObserver::NODE_CHANGED) {
+      ui::AXNodeData ax_data = change.node->data();
+      if (change.node->id() == root_id_) {
+        ax_data.id = kSemanticNodeRootId;
+      }
+      to_send_.push_back(AXNodeDataToSemanticNode(ax_data));
+    }
+  }
+  TryCommit();
 }
diff --git a/fuchsia/engine/browser/accessibility_bridge.h b/fuchsia/engine/browser/accessibility_bridge.h
index b9d6120..50adc468 100644
--- a/fuchsia/engine/browser/accessibility_bridge.h
+++ b/fuchsia/engine/browser/accessibility_bridge.h
@@ -10,8 +10,19 @@
 #include <fuchsia/ui/views/cpp/fidl.h>
 #include <lib/fidl/cpp/binding.h>
 
+#include "base/callback.h"
 #include "base/macros.h"
+#include "content/public/browser/ax_event_notification_details.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/browser/web_contents_observer.h"
 #include "fuchsia/engine/web_engine_export.h"
+#include "ui/accessibility/ax_serializable_tree.h"
+#include "ui/accessibility/ax_tree_id.h"
+#include "ui/accessibility/ax_tree_observer.h"
+
+namespace content {
+class WebContents;
+}  // namespace content
 
 // This class is the intermediate for accessibility between Chrome and Fuchsia.
 // It handles registration to the Fuchsia Semantics Manager, translating events
@@ -21,14 +32,38 @@
 // View created by FrameImpl. This class refers to the View via the
 // caller-supplied ViewRef.
 class WEB_ENGINE_EXPORT AccessibilityBridge
-    : public fuchsia::accessibility::semantics::SemanticListener {
+    : public content::WebContentsObserver,
+      public fuchsia::accessibility::semantics::SemanticListener,
+      public ui::AXTreeObserver {
  public:
+  // |web_contents| is required to exist for the duration of |this|.
   AccessibilityBridge(
       fuchsia::accessibility::semantics::SemanticsManagerPtr semantics_manager,
-      fuchsia::ui::views::ViewRef view_ref);
+      fuchsia::ui::views::ViewRef view_ref,
+      content::WebContents* web_contents);
   ~AccessibilityBridge() final;
 
+  void set_semantic_tree_for_test(
+      fidl::InterfaceRequest<fuchsia::accessibility::semantics::SemanticTree>
+          tree_request);
+
  private:
+  FRIEND_TEST_ALL_PREFIXES(AccessibilityBridgeTest, OnSemanticsModeChanged);
+
+  // Handles batching of semantic nodes and committing them to the SemanticTree.
+  void TryCommit();
+
+  // Callback for SemanticTree::CommitUpdates.
+  void OnCommitComplete();
+
+  // Converts AXNode ids to Semantic Node ids, and handles special casing of the
+  // root.
+  uint32_t ConvertToFuchsiaNodeId(int32_t ax_node_id);
+
+  // content::WebContentsObserver implementation.
+  void AccessibilityEventReceived(
+      const content::AXEventNotificationDetails& details) override;
+
   // fuchsia::accessibility::semantics::SemanticListener implementation.
   void OnAccessibilityActionRequested(
       uint32_t node_id,
@@ -39,8 +74,24 @@
   void OnSemanticsModeChanged(bool updates_enabled,
                               OnSemanticsModeChangedCallback callback) final;
 
+  // ui::AXTreeObserver implementation.
+  void OnNodeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override;
+  void OnAtomicUpdateFinished(
+      ui::AXTree* tree,
+      bool root_changed,
+      const std::vector<ui::AXTreeObserver::Change>& changes) override;
+
   fuchsia::accessibility::semantics::SemanticTreePtr tree_ptr_;
   fidl::Binding<fuchsia::accessibility::semantics::SemanticListener> binding_;
+  content::WebContents* web_contents_;
+  ui::AXSerializableTree tree_;
+
+  std::vector<fuchsia::accessibility::semantics::Node> to_send_;
+  std::vector<uint32_t> to_delete_;
+  bool commit_inflight_ = false;
+
+  // The root id of |tree_|.
+  int32_t root_id_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(AccessibilityBridge);
 };
diff --git a/fuchsia/engine/browser/accessibility_bridge_browsertest.cc b/fuchsia/engine/browser/accessibility_bridge_browsertest.cc
new file mode 100644
index 0000000..65af13a
--- /dev/null
+++ b/fuchsia/engine/browser/accessibility_bridge_browsertest.cc
@@ -0,0 +1,244 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fuchsia/accessibility/semantics/cpp/fidl.h>
+#include <fuchsia/accessibility/semantics/cpp/fidl_test_base.h>
+#include <lib/sys/cpp/component_context.h>
+#include <lib/ui/scenic/cpp/view_token_pair.h>
+#include <zircon/types.h>
+
+#include "base/fuchsia/default_context.h"
+#include "base/fuchsia/scoped_service_binding.h"
+#include "base/fuchsia/service_directory_client.h"
+#include "base/logging.h"
+#include "base/test/bind_test_util.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "fuchsia/base/frame_test_util.h"
+#include "fuchsia/base/result_receiver.h"
+#include "fuchsia/base/test_navigation_listener.h"
+#include "fuchsia/engine/browser/accessibility_bridge.h"
+#include "fuchsia/engine/browser/frame_impl.h"
+#include "fuchsia/engine/test/test_data.h"
+#include "fuchsia/engine/test/web_engine_browser_test.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using fuchsia::accessibility::semantics::Node;
+using fuchsia::accessibility::semantics::SemanticListener;
+using fuchsia::accessibility::semantics::SemanticsManager;
+using fuchsia::accessibility::semantics::SemanticTree;
+
+namespace {
+
+const char kPage1Path[] = "/ax1.html";
+const char kPage2Path[] = "/batching.html";
+const char kPage1Title[] = "accessibility 1";
+const char kPage2Title[] = "lots of nodes!";
+const char kButtonName[] = "a button";
+const char kNodeName[] = "last node";
+const char kParagraphName[] = "a paragraph";
+const size_t kPage1NodeCount = 3;
+
+class FakeSemanticTree
+    : public fuchsia::accessibility::semantics::testing::SemanticTree_TestBase {
+ public:
+  FakeSemanticTree() = default;
+  ~FakeSemanticTree() override = default;
+
+  // fuchsia::accessibility::semantics::SemanticTree implementation.
+  void UpdateSemanticNodes(std::vector<Node> nodes) final {
+    for (auto& node : nodes) {
+      nodes_.push_back(std::move(node));
+    }
+  }
+
+  void DeleteSemanticNodes(std::vector<uint32_t> node_ids) final {
+    for (auto id : node_ids) {
+      for (uint i = 0; i < nodes_.size(); i++) {
+        if (nodes_.at(i).node_id() == id) {
+          nodes_.erase(nodes_.begin() + i);
+        }
+      }
+    }
+  }
+
+  void CommitUpdates(CommitUpdatesCallback callback) final {
+    if (on_commit_updates_) {
+      std::move(on_commit_updates_).Run();
+    }
+  }
+
+  void NotImplemented_(const std::string& name) final {
+    NOTIMPLEMENTED() << name;
+  }
+
+  void RunUntilNodeCountAtLeast(size_t count) {
+    DCHECK(!on_commit_updates_);
+
+    // Spin the RunLoop until the expected conditions are met.
+    while (nodes_.size() < count) {
+      base::RunLoop run_loop;
+      on_commit_updates_ = base::BindLambdaForTesting(
+          [this, count, quit = run_loop.QuitClosure()]() {
+            if (nodes_.size() >= count) {
+              quit.Run();
+            }
+          });
+      run_loop.Run();
+    }
+  }
+
+  bool HasNodeWithLabel(base::StringPiece name) {
+    for (auto& node : nodes_) {
+      if (node.has_attributes() && node.attributes().has_label() &&
+          node.attributes().label() == name) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+ private:
+  std::vector<Node> nodes_;
+  base::OnceClosure on_commit_updates_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeSemanticTree);
+};
+
+class FakeSemanticsManager : public fuchsia::accessibility::semantics::testing::
+                                 SemanticsManager_TestBase {
+ public:
+  FakeSemanticsManager() : semantic_tree_binding_(&semantic_tree_) {}
+  ~FakeSemanticsManager() override = default;
+
+  bool is_view_registered() const { return view_ref_.reference.is_valid(); }
+  bool is_listener_valid() const { return static_cast<bool>(listener_); }
+  FakeSemanticTree* semantic_tree() { return &semantic_tree_; }
+
+  // Directly call the listener to simulate Fuchsia requesting a change in
+  void SetSemanticsMode(bool is_enabled) {
+    listener_->OnSemanticsModeChanged(is_enabled, []() {});
+  }
+
+  // fuchsia::accessibility::semantics::SemanticsManager implementation.
+  void RegisterViewForSemantics(
+      fuchsia::ui::views::ViewRef view_ref,
+      fidl::InterfaceHandle<SemanticListener> listener,
+      fidl::InterfaceRequest<SemanticTree> semantic_tree_request) final {
+    view_ref_ = std::move(view_ref);
+    listener_ = listener.Bind();
+    semantic_tree_binding_.Bind(std::move(semantic_tree_request));
+  }
+
+  void NotImplemented_(const std::string& name) final {
+    NOTIMPLEMENTED() << name;
+  }
+
+ private:
+  fuchsia::ui::views::ViewRef view_ref_;
+  fuchsia::accessibility::semantics::SemanticListenerPtr listener_;
+  FakeSemanticTree semantic_tree_;
+  fidl::Binding<SemanticTree> semantic_tree_binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeSemanticsManager);
+};
+
+}  // namespace
+
+class AccessibilityBridgeTest : public cr_fuchsia::WebEngineBrowserTest {
+ public:
+  AccessibilityBridgeTest() : semantics_manager_binding_(&semantics_manager_) {
+    cr_fuchsia::WebEngineBrowserTest::set_test_server_root(
+        base::FilePath(cr_fuchsia::kTestServerRoot));
+  }
+
+  ~AccessibilityBridgeTest() override = default;
+
+  void SetUpOnMainThread() override {
+    fuchsia::accessibility::semantics::SemanticsManagerPtr
+        semantics_manager_ptr;
+    semantics_manager_binding_.Bind(semantics_manager_ptr.NewRequest());
+
+    frame_ptr_ =
+        cr_fuchsia::WebEngineBrowserTest::CreateFrame(&navigation_listener_);
+    frame_impl_ = context_impl()->GetFrameImplForTest(&frame_ptr_);
+    frame_impl_->set_semantics_manager_for_test(
+        std::move(semantics_manager_ptr));
+
+    // Call CreateView to trigger creation of accessibility bridge
+    auto view_tokens = scenic::NewViewTokenPair();
+    frame_ptr_->CreateView(std::move(view_tokens.first));
+    base::RunLoop().RunUntilIdle();
+  }
+
+ protected:
+  fuchsia::web::FramePtr frame_ptr_;
+  FrameImpl* frame_impl_;
+  FakeSemanticsManager semantics_manager_;
+  fidl::Binding<SemanticsManager> semantics_manager_binding_;
+  cr_fuchsia::TestNavigationListener navigation_listener_;
+
+  DISALLOW_COPY_AND_ASSIGN(AccessibilityBridgeTest);
+};
+
+// Test registration to the SemanticsManager and accessibility mode on
+// WebContents is set correctly..
+IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, RegisterViewRef) {
+  // Check that setup is successful.
+  EXPECT_TRUE(semantics_manager_.is_view_registered());
+  EXPECT_TRUE(semantics_manager_.is_listener_valid());
+
+  // Change the accessibility mode on the Fuchsia side and check that it is
+  // propagated correctly.
+  EXPECT_FALSE(frame_impl_->web_contents_for_test()
+                   ->IsWebContentsOnlyAccessibilityModeForTesting());
+  semantics_manager_.SetSemanticsMode(true);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(frame_impl_->web_contents_for_test()
+                  ->IsWebContentsOnlyAccessibilityModeForTesting());
+}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, CorrectDataSent) {
+  fuchsia::web::NavigationControllerPtr controller;
+  frame_ptr_->GetNavigationController(controller.NewRequest());
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL title1(embedded_test_server()->GetURL(kPage1Path));
+
+  semantics_manager_.SetSemanticsMode(true);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
+      controller.get(), fuchsia::web::LoadUrlParams(), title1.spec()));
+  navigation_listener_.RunUntilUrlAndTitleEquals(title1, kPage1Title);
+
+  // Check that the data values are correct in the FakeSemanticTree.
+  // TODO(fxb/18796): Test more fields once Chrome to Fuchsia conversions are
+  // available.
+  semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
+  EXPECT_TRUE(
+      semantics_manager_.semantic_tree()->HasNodeWithLabel(kPage1Title));
+  EXPECT_TRUE(
+      semantics_manager_.semantic_tree()->HasNodeWithLabel(kButtonName));
+  EXPECT_TRUE(
+      semantics_manager_.semantic_tree()->HasNodeWithLabel(kParagraphName));
+}
+
+// Batching is performed when the number of nodes to send or delete exceeds the
+// maximum, as set on the Fuchsia side. Check that all nodes are received by the
+// Semantic Tree when batching is performed.
+IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, DataSentWithBatching) {
+  fuchsia::web::NavigationControllerPtr controller;
+  frame_ptr_->GetNavigationController(controller.NewRequest());
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL title2(embedded_test_server()->GetURL(kPage2Path));
+
+  semantics_manager_.SetSemanticsMode(true);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
+      controller.get(), fuchsia::web::LoadUrlParams(), title2.spec()));
+  navigation_listener_.RunUntilUrlAndTitleEquals(title2, kPage2Title);
+
+  // Run until we expect more than a batch's worth of nodes to be present.
+  semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(50);
+  EXPECT_TRUE(semantics_manager_.semantic_tree()->HasNodeWithLabel(kNodeName));
+}
diff --git a/fuchsia/engine/browser/accessibility_bridge_unittest.cc b/fuchsia/engine/browser/accessibility_bridge_unittest.cc
deleted file mode 100644
index bb12bf2..0000000
--- a/fuchsia/engine/browser/accessibility_bridge_unittest.cc
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "fuchsia/engine/browser/accessibility_bridge.h"
-
-#include <fuchsia/accessibility/semantics/cpp/fidl.h>
-#include <fuchsia/accessibility/semantics/cpp/fidl_test_base.h>
-#include <lib/sys/cpp/component_context.h>
-#include <lib/ui/scenic/cpp/view_ref_pair.h>
-#include <zircon/types.h>
-
-#include "base/fuchsia/default_context.h"
-#include "base/fuchsia/scoped_service_binding.h"
-#include "base/fuchsia/service_directory_client.h"
-#include "base/logging.h"
-#include "base/test/task_environment.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using fuchsia::accessibility::semantics::SemanticListener;
-using fuchsia::accessibility::semantics::SemanticsManager;
-
-namespace {
-
-class FakeSemanticsManager : public fuchsia::accessibility::semantics::testing::
-                                 SemanticsManager_TestBase {
- public:
-  explicit FakeSemanticsManager() = default;
-  ~FakeSemanticsManager() override = default;
-
-  // fuchsia::accessibility::semantics::SemanticsManager implementation.
-  void RegisterViewForSemantics(
-      fuchsia::ui::views::ViewRef view_ref,
-      fidl::InterfaceHandle<SemanticListener> listener,
-      fidl::InterfaceRequest<fuchsia::accessibility::semantics::SemanticTree>
-          semantic_tree) final {
-    view_ref_ = std::move(view_ref);
-    listener_ = std::move(listener);
-    semantic_tree_ = std::move(semantic_tree);
-  }
-
-  bool is_view_registered() const { return view_ref_.reference.is_valid(); }
-
-  bool is_listener_handle_valid() const { return listener_.is_valid(); }
-
-  bool is_semantic_tree_handle_valid() const {
-    return semantic_tree_.is_valid();
-  }
-
-  void NotImplemented_(const std::string& name) final {
-    NOTIMPLEMENTED() << name;
-  }
-
- private:
-  fuchsia::ui::views::ViewRef view_ref_;
-  fidl::InterfaceHandle<SemanticListener> listener_;
-  fidl::InterfaceRequest<fuchsia::accessibility::semantics::SemanticTree>
-      semantic_tree_;
-
-  DISALLOW_COPY_AND_ASSIGN(FakeSemanticsManager);
-};
-
-}  // namespace
-
-class AccessibilityBridgeTest : public testing::Test {
- public:
-  AccessibilityBridgeTest()
-      : task_environment_(base::test::TaskEnvironment::MainThreadType::IO),
-        semantics_manager_binding_(&semantics_manager_) {}
-  ~AccessibilityBridgeTest() override = default;
-
-  void CreateAccessibilityBridge() {
-    auto view_ref_pair = scenic::ViewRefPair::New();
-    control_ref_ = std::move(view_ref_pair.control_ref);
-    fuchsia::accessibility::semantics::SemanticsManagerPtr
-        semantics_manager_ptr;
-    semantics_manager_binding_.Bind(semantics_manager_ptr.NewRequest());
-    accessibility_bridge_ = std::make_unique<AccessibilityBridge>(
-        std::move(semantics_manager_ptr), std::move(view_ref_pair.view_ref));
-  }
-
- protected:
-  base::test::SingleThreadTaskEnvironment task_environment_;
-  std::unique_ptr<AccessibilityBridge> accessibility_bridge_;
-  FakeSemanticsManager semantics_manager_;
-  fidl::Binding<fuchsia::accessibility::semantics::SemanticsManager>
-      semantics_manager_binding_;
-  fuchsia::ui::views::ViewRefControl control_ref_;
-
-  DISALLOW_COPY_AND_ASSIGN(AccessibilityBridgeTest);
-};
-
-// Test registration to the SemanticsManager.
-TEST_F(AccessibilityBridgeTest, RegisterViewRef) {
-  CreateAccessibilityBridge();
-  // Run loop so FIDL registration requests are processed.
-  task_environment_.RunUntilIdle();
-  EXPECT_TRUE(semantics_manager_.is_view_registered());
-  EXPECT_TRUE(semantics_manager_.is_listener_handle_valid());
-  EXPECT_TRUE(semantics_manager_.is_semantic_tree_handle_valid());
-}
diff --git a/fuchsia/engine/browser/frame_impl.cc b/fuchsia/engine/browser/frame_impl.cc
index 31be8d5..2f94199 100644
--- a/fuchsia/engine/browser/frame_impl.cc
+++ b/fuchsia/engine/browser/frame_impl.cc
@@ -4,10 +4,12 @@
 
 #include "fuchsia/engine/browser/frame_impl.h"
 
+#include <lib/sys/cpp/component_context.h>
 #include <lib/ui/scenic/cpp/view_ref_pair.h>
 #include <limits>
 
 #include "base/bind_helpers.h"
+#include "base/fuchsia/default_context.h"
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/json/json_writer.h"
 #include "base/strings/strcat.h"
@@ -24,6 +26,7 @@
 #include "content/public/common/was_activated_option.mojom.h"
 #include "fuchsia/base/mem_buffer_util.h"
 #include "fuchsia/base/message_port.h"
+#include "fuchsia/engine/browser/accessibility_bridge.h"
 #include "fuchsia/engine/browser/context_impl.h"
 #include "fuchsia/engine/browser/web_engine_devtools_controller.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
@@ -465,6 +468,27 @@
   properties.view_token = std::move(view_token);
   properties.view_ref_pair = scenic::ViewRefPair::New();
 
+  // Create a ViewRef and register it to the Fuchsia SemanticsManager.
+  fuchsia::ui::views::ViewRef accessibility_view_ref;
+  zx_status_t status = properties.view_ref_pair.view_ref.reference.duplicate(
+      ZX_RIGHT_SAME_RIGHTS, &accessibility_view_ref.reference);
+  if (status == ZX_OK) {
+    fuchsia::accessibility::semantics::SemanticsManagerPtr semantics_manager;
+    if (test_semantics_manager_ptr_) {
+      semantics_manager = std::move(test_semantics_manager_ptr_);
+    } else {
+      semantics_manager =
+          base::fuchsia::ComponentContextForCurrentProcess()
+              ->svc()
+              ->Connect<fuchsia::accessibility::semantics::SemanticsManager>();
+    }
+    accessibility_bridge_ = std::make_unique<AccessibilityBridge>(
+        std::move(semantics_manager), std::move(accessibility_view_ref),
+        web_contents_.get());
+  } else {
+    ZX_LOG(ERROR, status) << "zx_object_duplicate";
+  }
+
   window_tree_host_ =
       std::make_unique<FrameWindowTreeHost>(std::move(properties));
   window_tree_host_->InitHost();
diff --git a/fuchsia/engine/browser/frame_impl.h b/fuchsia/engine/browser/frame_impl.h
index 6e7f8c6..10b2953e 100644
--- a/fuchsia/engine/browser/frame_impl.h
+++ b/fuchsia/engine/browser/frame_impl.h
@@ -20,6 +20,7 @@
 #include "base/memory/platform_shared_memory_region.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "fuchsia/engine/browser/accessibility_bridge.h"
 #include "fuchsia/engine/browser/discarding_event_filter.h"
 #include "fuchsia/engine/browser/navigation_controller_impl.h"
 #include "fuchsia/engine/browser/url_request_rewrite_rules_manager.h"
@@ -57,6 +58,14 @@
       base::RepeatingCallback<void(base::StringPiece)> hook) {
     console_log_message_hook_ = std::move(hook);
   }
+  AccessibilityBridge* accessibility_bridge_for_test() const {
+    return accessibility_bridge_.get();
+  }
+  void set_semantics_manager_for_test(
+      fuchsia::accessibility::semantics::SemanticsManagerPtr
+          semantics_manager) {
+    test_semantics_manager_ptr_ = std::move(semantics_manager);
+  }
 
  private:
   FRIEND_TEST_ALL_PREFIXES(FrameImplTest, DelayedNavigationEventAck);
@@ -170,6 +179,9 @@
   const std::unique_ptr<content::WebContents> web_contents_;
   std::unique_ptr<wm::FocusController> focus_controller_;
   ContextImpl* const context_;
+  std::unique_ptr<AccessibilityBridge> accessibility_bridge_;
+  fuchsia::accessibility::semantics::SemanticsManagerPtr
+      test_semantics_manager_ptr_;
 
   DiscardingEventFilter discarding_event_filter_;
   NavigationControllerImpl navigation_controller_;
diff --git a/fuchsia/engine/browser/frame_impl_browsertest.cc b/fuchsia/engine/browser/frame_impl_browsertest.cc
index 28f64e13..af9c1c3 100644
--- a/fuchsia/engine/browser/frame_impl_browsertest.cc
+++ b/fuchsia/engine/browser/frame_impl_browsertest.cc
@@ -24,6 +24,7 @@
 #include "fuchsia/base/test_navigation_listener.h"
 #include "fuchsia/base/url_request_rewrite_test_util.h"
 #include "fuchsia/engine/browser/frame_impl.h"
+#include "fuchsia/engine/test/test_data.h"
 #include "fuchsia/engine/test/web_engine_browser_test.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
@@ -62,7 +63,6 @@
 const char kPage3Title[] = "websql not available";
 const char kDataUrl[] =
     "data:text/html;base64,PGI+SGVsbG8sIHdvcmxkLi4uPC9iPg==";
-const char kTestServerRoot[] = FILE_PATH_LITERAL("fuchsia/engine/test/data");
 const int64_t kOnLoadScriptId = 0;
 
 MATCHER_P(NavigationHandleUrlEquals,
@@ -100,7 +100,7 @@
   FrameImplTest()
       : run_timeout_(TestTimeouts::action_timeout(),
                      base::MakeExpectedNotRunClosure(FROM_HERE)) {
-    set_test_server_root(base::FilePath(kTestServerRoot));
+    set_test_server_root(base::FilePath(cr_fuchsia::kTestServerRoot));
   }
 
   ~FrameImplTest() = default;
diff --git a/fuchsia/engine/browser/media_browsertest.cc b/fuchsia/engine/browser/media_browsertest.cc
index 23fca26..96a13e5d 100644
--- a/fuchsia/engine/browser/media_browsertest.cc
+++ b/fuchsia/engine/browser/media_browsertest.cc
@@ -10,17 +10,17 @@
 #include "fuchsia/base/frame_test_util.h"
 #include "fuchsia/base/test_navigation_listener.h"
 #include "fuchsia/engine/switches.h"
+#include "fuchsia/engine/test/test_data.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
-const char kTestServerRoot[] = FILE_PATH_LITERAL("fuchsia/engine/test/data");
 
 class MediaTest : public cr_fuchsia::WebEngineBrowserTest {
  public:
   MediaTest()
       : run_timeout_(TestTimeouts::action_timeout(),
                      base::MakeExpectedNotRunClosure(FROM_HERE)) {
-    set_test_server_root(base::FilePath(kTestServerRoot));
+    set_test_server_root(base::FilePath(cr_fuchsia::kTestServerRoot));
   }
   ~MediaTest() override = default;
 
diff --git a/fuchsia/engine/test/data/ax1.html b/fuchsia/engine/test/data/ax1.html
new file mode 100644
index 0000000..5b7017d2
--- /dev/null
+++ b/fuchsia/engine/test/data/ax1.html
@@ -0,0 +1,7 @@
+<html>
+  <head><title>accessibility 1</title></head>
+  <body>
+    <button>a button</button>
+    <p>a paragraph</p>
+  </body>
+</html>
diff --git a/fuchsia/engine/test/data/batching.html b/fuchsia/engine/test/data/batching.html
new file mode 100644
index 0000000..4059eacd
--- /dev/null
+++ b/fuchsia/engine/test/data/batching.html
@@ -0,0 +1,99 @@
+<html>
+  <head><title>lots of nodes!</title></head>
+  <body>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>node</p>
+    <p>last node</p>
+  </body>
+</html>
diff --git a/fuchsia/engine/test/test_data.cc b/fuchsia/engine/test/test_data.cc
new file mode 100644
index 0000000..1ca9e32
--- /dev/null
+++ b/fuchsia/engine/test/test_data.cc
@@ -0,0 +1,13 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fuchsia/engine/test/test_data.h"
+
+#include "base/files/file_path.h"
+
+namespace cr_fuchsia {
+
+const char kTestServerRoot[] = FILE_PATH_LITERAL("fuchsia/engine/test/data");
+
+}  // namespace cr_fuchsia
\ No newline at end of file
diff --git a/fuchsia/engine/test/test_data.h b/fuchsia/engine/test/test_data.h
new file mode 100644
index 0000000..5a5618e
--- /dev/null
+++ b/fuchsia/engine/test/test_data.h
@@ -0,0 +1,15 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FUCHSIA_ENGINE_TEST_TEST_DATA_H_
+#define FUCHSIA_ENGINE_TEST_TEST_DATA_H_
+
+namespace cr_fuchsia {
+
+// Path to data used in browsertests.
+extern const char kTestServerRoot[];
+
+}  // namespace cr_fuchsia
+
+#endif  // FUCHSIA_ENGINE_TEST_TEST_DATA_H_
\ No newline at end of file
diff --git a/fuchsia/runners/web/web_runner.cmx b/fuchsia/runners/web/web_runner.cmx
index 50e3a7e4..af9afcc9 100644
--- a/fuchsia/runners/web/web_runner.cmx
+++ b/fuchsia/runners/web/web_runner.cmx
@@ -4,6 +4,7 @@
       "isolated-persistent-storage"
     ],
     "services": [
+      "fuchsia.accessibility.semantics.SemanticsManager",
       "fuchsia.deprecatedtimezone.Timezone",
       "fuchsia.device.NameProvider",
       "fuchsia.fonts.Provider",
diff --git a/gpu/ipc/client/gpu_channel_host.cc b/gpu/ipc/client/gpu_channel_host.cc
index 4b8c137..505affa4 100644
--- a/gpu/ipc/client/gpu_channel_host.cc
+++ b/gpu/ipc/client/gpu_channel_host.cc
@@ -21,6 +21,7 @@
 #include "gpu/ipc/common/gpu_watchdog_timeout.h"
 #include "ipc/ipc_channel_mojo.h"
 #include "ipc/ipc_sync_message.h"
+#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
 #include "url/gurl.h"
 
 using base::AutoLock;
@@ -274,11 +275,13 @@
 GpuChannelHost::Listener::Listener(
     mojo::ScopedMessagePipeHandle handle,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
-    : channel_(IPC::ChannelMojo::Create(std::move(handle),
-                                        IPC::Channel::MODE_CLIENT,
-                                        this,
-                                        io_task_runner,
-                                        base::ThreadTaskRunnerHandle::Get())) {
+    : channel_(IPC::ChannelMojo::Create(
+          std::move(handle),
+          IPC::Channel::MODE_CLIENT,
+          this,
+          io_task_runner,
+          base::ThreadTaskRunnerHandle::Get(),
+          mojo::internal::MessageQuotaChecker::MaybeCreate())) {
   DCHECK(channel_);
   DCHECK(io_task_runner->BelongsToCurrentThread());
   bool result = channel_->Connect();
diff --git a/headless/lib/browser/headless_request_context_manager.cc b/headless/lib/browser/headless_request_context_manager.cc
index f6349e2..f0586c8 100644
--- a/headless/lib/browser/headless_request_context_manager.cc
+++ b/headless/lib/browser/headless_request_context_manager.cc
@@ -230,9 +230,10 @@
   context_params->primary_network_context = is_system;
 
   // TODO(https://crbug.com/458508): Allow
-  // context_params->allow_default_credentials to be controllable by a flag.
-  context_params->allow_default_credentials =
-    net::HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS;
+  // context_params->http_auth_static_network_context_params->allow_default_credentials
+  // to be controllable by a flag.
+  context_params->http_auth_static_network_context_params =
+      ::network::mojom::HttpAuthStaticNetworkContextParams::New();
 
   if (!user_data_path_.empty()) {
     context_params->enable_encrypted_cookies = cookie_encryption_enabled_;
diff --git a/headless/test/data/protocol/emulation/virtual-time-fetch-keepalive-expected.txt b/headless/test/data/protocol/emulation/virtual-time-fetch-keepalive-expected.txt
new file mode 100644
index 0000000..6c5b42c
--- /dev/null
+++ b/headless/test/data/protocol/emulation/virtual-time-fetch-keepalive-expected.txt
@@ -0,0 +1,2 @@
+Tests that virtual time does not hang when a fetch is cancelled due to exceeded size of pending keepalive data.
+Request to https://test.com/index.html, type: Document
\ No newline at end of file
diff --git a/headless/test/data/protocol/emulation/virtual-time-fetch-keepalive.js b/headless/test/data/protocol/emulation/virtual-time-fetch-keepalive.js
new file mode 100644
index 0000000..55f3526
--- /dev/null
+++ b/headless/test/data/protocol/emulation/virtual-time-fetch-keepalive.js
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(async function(testRunner) {
+  const {page, session, dp} = await testRunner.startBlank(
+      'Tests that virtual time does not hang when a fetch is cancelled ' +
+      'due to exceeded size of pending keepalive data.');
+
+  const FetchHelper = await testRunner.loadScriptAbsolute(
+      '../fetch/resources/fetch-test.js');
+  const helper = new FetchHelper(testRunner, dp);
+  await helper.enable();
+
+  helper.onceRequest('https://test.com/index.html').fulfill(
+      FetchHelper.makeContentResponse(`<html></html>`));
+
+  await dp.Emulation.setVirtualTimePolicy({policy: 'pause'});
+  await dp.Emulation.setVirtualTimePolicy({
+      policy: 'pauseIfNetworkFetchesPending', budget: 5000,
+      waitForNavigation: true});
+  await dp.Page.navigate({url: 'https://test.com/index.html'});
+
+  helper.onceRequest('https://test.com/post').matched().then(() =>
+      testRunner.log('FAIL: this request should not come through!'));
+
+  await session.evaluateAsync(`
+      fetch('/post', {method: 'POST', body: '*'.repeat(64 * 1024 + 1),
+          keepalive: true})
+  `);
+  await dp.Emulation.onceVirtualTimeBudgetExpired();
+  testRunner.completeTest();
+})
diff --git a/headless/test/headless_protocol_browsertest.cc b/headless/test/headless_protocol_browsertest.cc
index 4d39ed2..65ca28b 100644
--- a/headless/test/headless_protocol_browsertest.cc
+++ b/headless/test/headless_protocol_browsertest.cc
@@ -248,6 +248,8 @@
                        "emulation/virtual-time-history-navigation.js")
 HEADLESS_PROTOCOL_TEST(VirtualTimeHistoryNavigationSameDoc,
                        "emulation/virtual-time-history-navigation-same-doc.js")
+HEADLESS_PROTOCOL_TEST(VirtualTimeFetchKeepalive,
+                       "emulation/virtual-time-fetch-keepalive.js")
 
 // http://crbug.com/633321
 #if defined(OS_ANDROID)
diff --git a/ios/build/bots/tests/common_tests.json b/ios/build/bots/tests/common_tests.json
index 42eb87b0..ad8a2931 100644
--- a/ios/build/bots/tests/common_tests.json
+++ b/ios/build/bots/tests/common_tests.json
@@ -25,7 +25,7 @@
       "app": "net_unittests"
     },
     {
-      "app": "ocmock_support_unittests"
+      "app": "ios_testing_unittests"
     },
     {
       "app": "services_unittests"
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index 944061c..70efeafa 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -61,11 +61,11 @@
     "//ios/chrome/browser/tabs",
     "//ios/chrome/common/app_group",
     "//ios/chrome/test:test_support",
-    "//ios/chrome/test/base",
     "//ios/public/provider/chrome/browser:browser",
     "//ios/public/provider/chrome/browser:test_support",
     "//ios/public/provider/chrome/browser/distribution",
     "//ios/public/provider/chrome/browser/distribution:test_support",
+    "//ios/testing:block_swizzler",
     "//ios/web/public/test:test",
     "//testing/gtest",
     "//third_party/ocmock",
diff --git a/ios/chrome/app/application_delegate/BUILD.gn b/ios/chrome/app/application_delegate/BUILD.gn
index 5b2e830..5c118e1 100644
--- a/ios/chrome/app/application_delegate/BUILD.gn
+++ b/ios/chrome/app/application_delegate/BUILD.gn
@@ -63,11 +63,11 @@
     "//ios/chrome/browser/web_state_list",
     "//ios/chrome/browser/web_state_list:test_support",
     "//ios/chrome/test:test_support",
-    "//ios/chrome/test/base",
     "//ios/chrome/test/ocmock",
     "//ios/public/provider/chrome/browser:test_support",
     "//ios/public/provider/chrome/browser/distribution",
     "//ios/public/provider/chrome/browser/user_feedback:test_support",
+    "//ios/testing:block_swizzler",
     "//ios/testing:ocmock_support",
     "//ios/web",
     "//ios/web/public/test",
diff --git a/ios/chrome/app/application_delegate/app_state_unittest.mm b/ios/chrome/app/application_delegate/app_state_unittest.mm
index 0b692c28..d876272 100644
--- a/ios/chrome/app/application_delegate/app_state_unittest.mm
+++ b/ios/chrome/app/application_delegate/app_state_unittest.mm
@@ -42,13 +42,13 @@
 #import "ios/chrome/browser/ui/main/test/stub_browser_interface_provider.h"
 #import "ios/chrome/browser/ui/safe_mode/safe_mode_coordinator.h"
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
-#import "ios/chrome/test/base/scoped_block_swizzler.h"
 #include "ios/chrome/test/block_cleanup_test.h"
 #include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_provider.h"
 #include "ios/public/provider/chrome/browser/distribution/app_distribution_provider.h"
 #include "ios/public/provider/chrome/browser/test_chrome_browser_provider.h"
 #include "ios/public/provider/chrome/browser/user_feedback/test_user_feedback_provider.h"
 #import "ios/testing/ocmock_complex_type_helper.h"
+#import "ios/testing/scoped_block_swizzler.h"
 #include "ios/web/public/test/web_task_environment.h"
 #include "ios/web/public/thread/web_task_traits.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
diff --git a/ios/chrome/app/application_delegate/metrics_mediator_unittest.mm b/ios/chrome/app/application_delegate/metrics_mediator_unittest.mm
index 01ac611..aab3c37b 100644
--- a/ios/chrome/app/application_delegate/metrics_mediator_unittest.mm
+++ b/ios/chrome/app/application_delegate/metrics_mediator_unittest.mm
@@ -19,8 +19,8 @@
 #import "ios/chrome/browser/ui/main/test/stub_browser_interface_provider.h"
 #import "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
-#import "ios/chrome/test/base/scoped_block_swizzler.h"
 #import "ios/chrome/test/ocmock/OCMockObject+BreakpadControllerTesting.h"
+#import "ios/testing/scoped_block_swizzler.h"
 #include "net/base/network_change_notifier.h"
 #include "testing/platform_test.h"
 #import "third_party/breakpad/breakpad/src/client/ios/BreakpadController.h"
diff --git a/ios/chrome/app/application_delegate/url_opener_unittest.mm b/ios/chrome/app/application_delegate/url_opener_unittest.mm
index c7fd39b..3be9dffb 100644
--- a/ios/chrome/app/application_delegate/url_opener_unittest.mm
+++ b/ios/chrome/app/application_delegate/url_opener_unittest.mm
@@ -16,7 +16,7 @@
 #import "ios/chrome/browser/ui/main/test/stub_browser_interface_provider.h"
 #import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
-#import "ios/chrome/test/base/scoped_block_swizzler.h"
+#import "ios/testing/scoped_block_swizzler.h"
 #include "ios/web/public/test/web_task_environment.h"
 #include "testing/gtest_mac.h"
 #include "testing/platform_test.h"
diff --git a/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm b/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm
index d63f2293..a6f478e 100644
--- a/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm
+++ b/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm
@@ -32,7 +32,7 @@
 #import "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_opener.h"
-#import "ios/chrome/test/base/scoped_block_swizzler.h"
+#import "ios/testing/scoped_block_swizzler.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #import "net/base/mac/url_conversions.h"
 #include "net/test/gtest_util.h"
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index fecc8a6..5ad1985 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -293,6 +293,12 @@
       <message name="IDS_IOS_AUTOFILL_EXP_YEAR" desc="Title of the field representing the expiration year of a credit card. [Length: 15em] [iOS only]">
         Expiration Year
       </message>
+      <message name="IDS_IOS_AUTOFILL_CARDHOLDER_NAME" desc="Tile of the field representing the cardholder name of a credit card. [Length: 15em] [iOS only]">
+        Cardholder Name
+      </message>
+      <message name="IDS_IOS_AUTOFILL_SAVE_CARD" desc="Title for the view for saving a credit card. [Length: 15em] [iOS only]">
+        Save Card
+      </message>
       <message name="IDS_IOS_AUTOFILL_FULLNAME" desc="Title of the field of a profile address representing the full name of the addressee. [Length: 15em] [iOS only]">
         Full Name
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_AUTOFILL_CARDHOLDER_NAME.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_AUTOFILL_CARDHOLDER_NAME.png.sha1
new file mode 100644
index 0000000..b45e7b9
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_AUTOFILL_CARDHOLDER_NAME.png.sha1
@@ -0,0 +1 @@
+64cccc843ea70fdf800d77ff72a1a9f1d69064d4
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_AUTOFILL_SAVE_CARD.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_AUTOFILL_SAVE_CARD.png.sha1
new file mode 100644
index 0000000..f381a06
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_AUTOFILL_SAVE_CARD.png.sha1
@@ -0,0 +1 @@
+56bf15e2f93bdb7b5b128f00f7027ca2c1a74319
\ No newline at end of file
diff --git a/ios/chrome/app/tab_opener_unittest.mm b/ios/chrome/app/tab_opener_unittest.mm
index c2575ed..8df077c 100644
--- a/ios/chrome/app/tab_opener_unittest.mm
+++ b/ios/chrome/app/tab_opener_unittest.mm
@@ -9,7 +9,7 @@
 #import "ios/chrome/app/application_delegate/url_opener.h"
 #include "ios/chrome/app/main_controller_private.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
-#import "ios/chrome/test/base/scoped_block_swizzler.h"
+#import "ios/testing/scoped_block_swizzler.h"
 #include "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
 
diff --git a/ios/chrome/browser/autofill/BUILD.gn b/ios/chrome/browser/autofill/BUILD.gn
index c26b4be..09d2f2eb 100644
--- a/ios/chrome/browser/autofill/BUILD.gn
+++ b/ios/chrome/browser/autofill/BUILD.gn
@@ -176,7 +176,7 @@
     "//ios/chrome/browser/web:test_support",
     "//ios/chrome/browser/web:web_internal",
     "//ios/chrome/browser/webdata_services",
-    "//ios/chrome/test/base",
+    "//ios/testing:block_swizzler",
     "//ios/web/public/deprecated",
     "//ios/web/public/js_messaging",
     "//ios/web/public/test",
diff --git a/ios/chrome/browser/crash_report/BUILD.gn b/ios/chrome/browser/crash_report/BUILD.gn
index 98fdf653..6d41c083 100644
--- a/ios/chrome/browser/crash_report/BUILD.gn
+++ b/ios/chrome/browser/crash_report/BUILD.gn
@@ -88,8 +88,8 @@
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/sessions:serialisation",
-    "//ios/chrome/test/base",
     "//ios/chrome/test/ocmock",
+    "//ios/testing:block_swizzler",
     "//ios/web/public/test",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/ios/chrome/browser/crash_report/breakpad_helper_unittest.mm b/ios/chrome/browser/crash_report/breakpad_helper_unittest.mm
index c76c190..309f4224 100644
--- a/ios/chrome/browser/crash_report/breakpad_helper_unittest.mm
+++ b/ios/chrome/browser/crash_report/breakpad_helper_unittest.mm
@@ -5,8 +5,8 @@
 #import "ios/chrome/browser/crash_report/breakpad_helper.h"
 #import "base/test/ios/wait_util.h"
 #include "ios/chrome/browser/crash_report/main_thread_freeze_detector.h"
-#import "ios/chrome/test/base/scoped_block_swizzler.h"
 #import "ios/chrome/test/ocmock/OCMockObject+BreakpadControllerTesting.h"
+#import "ios/testing/scoped_block_swizzler.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 #import "third_party/breakpad/breakpad/src/client/ios/BreakpadController.h"
diff --git a/ios/chrome/browser/passwords/ios_chrome_password_manager_client.h b/ios/chrome/browser/passwords/ios_chrome_password_manager_client.h
index c9d1560..a5f1b6f 100644
--- a/ios/chrome/browser/passwords/ios_chrome_password_manager_client.h
+++ b/ios/chrome/browser/passwords/ios_chrome_password_manager_client.h
@@ -135,6 +135,7 @@
   GetPasswordRequirementsService() override;
   bool IsIsolationForPasswordSitesEnabled() const override;
   bool IsNewTabPage() const override;
+  password_manager::FieldInfoManager* GetFieldInfoManager() const override;
 
  private:
   __weak id<PasswordManagerClientDelegate> delegate_;
diff --git a/ios/chrome/browser/passwords/ios_chrome_password_manager_client.mm b/ios/chrome/browser/passwords/ios_chrome_password_manager_client.mm
index 8c0f5db2..92742663 100644
--- a/ios/chrome/browser/passwords/ios_chrome_password_manager_client.mm
+++ b/ios/chrome/browser/passwords/ios_chrome_password_manager_client.mm
@@ -266,3 +266,8 @@
 bool IOSChromePasswordManagerClient::IsNewTabPage() const {
   return false;
 }
+
+password_manager::FieldInfoManager*
+IOSChromePasswordManagerClient::GetFieldInfoManager() const {
+  return nullptr;
+}
diff --git a/ios/chrome/browser/ui/alert_coordinator/BUILD.gn b/ios/chrome/browser/ui/alert_coordinator/BUILD.gn
index 3d4366b..70dc733 100644
--- a/ios/chrome/browser/ui/alert_coordinator/BUILD.gn
+++ b/ios/chrome/browser/ui/alert_coordinator/BUILD.gn
@@ -71,24 +71,3 @@
   ]
   libs = [ "UIKit.framework" ]
 }
-
-source_set("eg_tests") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  testonly = true
-  sources = [
-    "alert_coordinator_egtest.mm",
-  ]
-  deps = [
-    ":alert_coordinator",
-    "//base",
-    "//components/strings",
-    "//ios/chrome/browser/ui/util",
-    "//ios/chrome/test/earl_grey:test_support",
-    "//ios/testing/earl_grey:earl_grey_support",
-    "//ios/third_party/earl_grey:earl_grey+link",
-  ]
-  libs = [
-    "UIKit.framework",
-    "XCTest.framework",
-  ]
-}
diff --git a/ios/chrome/browser/ui/alert_coordinator/alert_coordinator_egtest.mm b/ios/chrome/browser/ui/alert_coordinator/alert_coordinator_egtest.mm
deleted file mode 100644
index 892f941..0000000
--- a/ios/chrome/browser/ui/alert_coordinator/alert_coordinator_egtest.mm
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import <EarlGrey/EarlGrey.h>
-#import <UIKit/UIKit.h>
-#import <XCTest/XCTest.h>
-
-#include "components/strings/grit/components_strings.h"
-#import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h"
-#import "ios/chrome/browser/ui/util/top_view_controller.h"
-#import "ios/chrome/test/earl_grey/chrome_matchers.h"
-#import "ios/chrome/test/earl_grey/chrome_test_case.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-NSString* kTitle = @"Foo Title";
-}  // namespace
-
-// Integration test for the alert coordinator using Earl Grey.
-@interface AlertCoordinatorTestCase : ChromeTestCase
-
-// Whether an alert is presented.
-- (BOOL)isPresentingAlert;
-
-@end
-
-@implementation AlertCoordinatorTestCase
-
-// Tests that if the alert coordinator is destroyed, the alert is dismissed.
-- (void)testDismissOnDestroy {
-  // TODO(crbug.com/754642): Remove TopPresentedViewControllerFrom().
-  UIViewController* topViewController =
-      top_view_controller::TopPresentedViewController();
-
-  AlertCoordinator* alertCoordinator =
-      [[AlertCoordinator alloc] initWithBaseViewController:topViewController
-                                                     title:kTitle
-                                                   message:nil];
-
-  [alertCoordinator start];
-
-  GREYAssertTrue([self isPresentingAlert], @"An alert should be presented");
-
-  alertCoordinator = nil;
-
-  GREYAssertFalse([self isPresentingAlert], @"The alert should be removed");
-}
-
-- (void)testNoInteractionActionAfterTap {
-  // TODO(crbug.com/754642): Remove TopPresentedViewControllerFrom().
-  UIViewController* topViewController =
-      top_view_controller::TopPresentedViewController();
-
-  AlertCoordinator* alertCoordinator =
-      [[AlertCoordinator alloc] initWithBaseViewController:topViewController
-                                                     title:kTitle
-                                                   message:nil];
-
-  __block BOOL blockCalled = NO;
-
-  [alertCoordinator setNoInteractionAction:^{
-    blockCalled = YES;
-  }];
-
-  [alertCoordinator start];
-
-  GREYAssertTrue([self isPresentingAlert], @"An alert should be presented");
-
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
-                                   IDS_OK)] performAction:grey_tap()];
-
-  GREYAssertFalse([self isPresentingAlert], @"The alert should be removed");
-
-  GREYAssertFalse(blockCalled,
-                  @"The noInteractionBlock should not have been called.");
-}
-
-- (BOOL)isPresentingAlert {
-  NSError* error = nil;
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(kTitle)]
-      assertWithMatcher:grey_sufficientlyVisible()
-                  error:&error];
-
-  return (error == nil);
-}
-
-@end
diff --git a/ios/chrome/browser/ui/alert_coordinator/alert_coordinator_unittest.mm b/ios/chrome/browser/ui/alert_coordinator/alert_coordinator_unittest.mm
index 28fc00ad..dd2d04d 100644
--- a/ios/chrome/browser/ui/alert_coordinator/alert_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/alert_coordinator/alert_coordinator_unittest.mm
@@ -228,6 +228,8 @@
   EXPECT_EQ(UIAlertActionStyleCancel, action.style);
 }
 
+// Tests that the |noInteractionAction| block is called for an alert coordinator
+// which is stopped before the user has interacted with it.
 TEST_F(AlertCoordinatorTest, NoInteractionActionTest) {
   // Setup.
   UIViewController* viewController = getViewController();
@@ -248,6 +250,8 @@
   EXPECT_TRUE(blockCalled);
 }
 
+// Tests that the |noInteractionAction| block is not called for an alert
+// coordinator which is dismissed with the cancel button.
 TEST_F(AlertCoordinatorTest, NoInteractionActionWithCancelTest) {
   // Setup.
   UIViewController* viewController = getViewController();
@@ -268,3 +272,26 @@
   // Test.
   EXPECT_FALSE(blockCalled);
 }
+
+// Tests that the alert coordinator is dismissed if destroyed without being
+// stopped.
+TEST_F(AlertCoordinatorTest, AlertDismissedOnDestroy) {
+  // Setup.
+  UIViewController* viewController = getViewController();
+  AlertCoordinator* alertCoordinator = getAlertCoordinator(viewController);
+
+  ASSERT_FALSE(alertCoordinator.isVisible);
+  ASSERT_EQ(nil, viewController.presentedViewController);
+
+  __block BOOL blockCalled = NO;
+
+  alertCoordinator.noInteractionAction = ^{
+    blockCalled = YES;
+  };
+
+  startAlertCoordinator();
+
+  alertCoordinator = nil;
+
+  EXPECT_FALSE(blockCalled);
+}
diff --git a/ios/chrome/browser/ui/collection_view/BUILD.gn b/ios/chrome/browser/ui/collection_view/BUILD.gn
index 801b3829..89eb06e 100644
--- a/ios/chrome/browser/ui/collection_view/BUILD.gn
+++ b/ios/chrome/browser/ui/collection_view/BUILD.gn
@@ -51,7 +51,7 @@
     "//base",
     "//ios/chrome/browser/ui/collection_view/cells",
     "//ios/chrome/test:test_support",
-    "//ios/chrome/test/base",
+    "//ios/testing:block_swizzler",
     "//ios/third_party/material_components_ios",
     "//testing/gtest",
   ]
diff --git a/ios/chrome/browser/ui/collection_view/collection_view_controller_unittest.mm b/ios/chrome/browser/ui/collection_view/collection_view_controller_unittest.mm
index 0d915ef..016e255ae 100644
--- a/ios/chrome/browser/ui/collection_view/collection_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/collection_view/collection_view_controller_unittest.mm
@@ -6,8 +6,8 @@
 
 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
-#import "ios/chrome/test/base/scoped_block_swizzler.h"
 #include "ios/chrome/test/block_cleanup_test.h"
+#import "ios/testing/scoped_block_swizzler.h"
 #import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index 341c069..bf2dd658 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -226,8 +226,8 @@
     "//ios/chrome/browser/url_loading",
     "//ios/chrome/browser/web_state_list",
     "//ios/chrome/browser/web_state_list:test_support",
-    "//ios/chrome/test/base",
     "//ios/public/provider/chrome/browser/ui",
+    "//ios/testing:block_swizzler",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
     "//testing/gtest",
@@ -259,7 +259,6 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/ui/toolbar/public:constants",
     "//ios/chrome/test:eg_test_support+eg2",
-    "//ios/chrome/test/base",
     "//ios/chrome/test/earl_grey:eg_test_support+eg2",
     "//ios/testing/earl_grey:eg_test_support+eg2",
     "//ios/third_party/earl_grey2:test_lib",
@@ -306,7 +305,6 @@
     "//ios/chrome/test:eg_test_support",
     "//ios/chrome/test:test_support",
     "//ios/chrome/test/app:test_support",
-    "//ios/chrome/test/base:base",
     "//ios/chrome/test/earl_grey:test_support",
     "//ios/testing/earl_grey:earl_grey_support",
     "//ios/third_party/earl_grey:earl_grey+link",
@@ -373,7 +371,7 @@
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/test/app:test_support",
-    "//ios/chrome/test/base",
+    "//ios/testing:block_swizzler",
     "//ios/testing/earl_grey:eg_app_support+eg2",
     "//ios/third_party/earl_grey2:app_framework+link",
     "//testing/gmock",
@@ -409,7 +407,7 @@
     "//ios/chrome/browser/ui/content_suggestions:content_suggestions_ui",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/test/app:test_support",
-    "//ios/chrome/test/base",
+    "//ios/testing:block_swizzler",
     "//ios/testing/earl_grey:earl_grey_support",
     "//ios/third_party/earl_grey:earl_grey+link",
     "//ios/web",
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
index 5b96bce..c6a1e9f 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
@@ -7,7 +7,7 @@
 #include <memory>
 
 #include "ios/chrome/browser/ui/util/ui_util.h"
-#import "ios/chrome/test/base/scoped_block_swizzler.h"
+#import "ios/testing/scoped_block_swizzler.h"
 #include "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
 
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_synchronizer_unittest.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_synchronizer_unittest.mm
index feb40ed..ab44a10 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_synchronizer_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_synchronizer_unittest.mm
@@ -8,7 +8,7 @@
 
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_controlling.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_header_controlling.h"
-#import "ios/chrome/test/base/scoped_block_swizzler.h"
+#import "ios/testing/scoped_block_swizzler.h"
 #include "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
 #import "third_party/ocmock/gtest_support.h"
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_provider_test_singleton.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_provider_test_singleton.mm
index 4094104d..a6d1a97a 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_provider_test_singleton.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_provider_test_singleton.mm
@@ -8,7 +8,7 @@
 
 #include "components/ntp_snippets/content_suggestion.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_test_utils.h"
-#include "ios/chrome/test/base/scoped_block_swizzler.h"
+#include "ios/testing/scoped_block_swizzler.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_save_card_coordinator.mm b/ios/chrome/browser/ui/infobars/coordinators/infobar_save_card_coordinator.mm
index a3576f97..3c019ef 100644
--- a/ios/chrome/browser/ui/infobars/coordinators/infobar_save_card_coordinator.mm
+++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_save_card_coordinator.mm
@@ -13,6 +13,8 @@
 #import "ios/chrome/browser/ui/infobars/infobar_container.h"
 #import "ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#include "ios/chrome/grit/ios_strings.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/image/image.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -154,10 +156,20 @@
 
   self.modalViewController =
       [[InfobarSaveCardTableViewController alloc] initWithModalDelegate:self];
-  // TODO(crbug.com/1014652): Replace with Modal specific text.
-  self.modalViewController.title = @"Save Credit Card";
+  self.modalViewController.title =
+      l10n_util::GetNSString(IDS_IOS_AUTOFILL_SAVE_CARD);
   self.modalViewController.cardIssuerIcon =
       NativeImage(self.saveCardInfoBarDelegate->issuer_icon_id());
+  self.modalViewController.cardNumber = [NSString
+      stringWithFormat:@"•••• %@",
+                       base::SysUTF16ToNSString(self.saveCardInfoBarDelegate
+                                                    ->card_last_four_digits())];
+  self.modalViewController.cardholderName =
+      base::SysUTF16ToNSString(self.saveCardInfoBarDelegate->cardholder_name());
+  self.modalViewController.expirationMonth = base::SysUTF16ToNSString(
+      self.saveCardInfoBarDelegate->expiration_date_month());
+  self.modalViewController.expirationYear = base::SysUTF16ToNSString(
+      self.saveCardInfoBarDelegate->expiration_date_year());
 
   return YES;
 }
diff --git a/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.h b/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.h
index 6dc253b..43c51a5 100644
--- a/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.h
+++ b/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.h
@@ -20,9 +20,21 @@
                                (ChromeTableViewControllerStyle)appBarStyle
     NS_UNAVAILABLE;
 
+// Cardholder name to be displayed.
+@property(nonatomic, copy) NSString* cardholderName;
+
 // Card Issuer icon image to be displayed.
 @property(nonatomic, strong) UIImage* cardIssuerIcon;
 
+// Card Number to be displayed.
+@property(nonatomic, copy) NSString* cardNumber;
+
+// Card Expiration Month to be displayed
+@property(nonatomic, copy) NSString* expirationMonth;
+
+// Card Expiration Year to be displayed.
+@property(nonatomic, copy) NSString* expirationYear;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_INFOBARS_MODALS_INFOBAR_SAVE_CARD_TABLE_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.mm b/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.mm
index f902e75c..ed106453 100644
--- a/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.mm
+++ b/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.mm
@@ -15,6 +15,8 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #import "ios/chrome/common/colors/semantic_color_names.h"
+#include "ios/chrome/grit/ios_strings.h"
+#include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -101,56 +103,54 @@
   TableViewModel* model = self.tableViewModel;
   [model addSectionWithIdentifier:SectionIdentifierContent];
 
-  // TODO(crbug.com/1014652): Use the real strings once they are exposed in the
-  // delegate.
-  TableViewTextEditItem* cardLastDigitsItem =
-      [self textEditItemWithType:ItemTypeCardExpireYear
-                   textFieldName:@"Card Number"
-                  textFieldValue:@"•••• 1234"
-                textFieldEnabled:NO];
+  TableViewTextEditItem* cardLastDigitsItem = [self
+      textEditItemWithType:ItemTypeCardExpireYear
+             textFieldName:l10n_util::GetNSString(IDS_IOS_AUTOFILL_CARD_NUMBER)
+            textFieldValue:self.cardNumber
+          textFieldEnabled:NO];
   cardLastDigitsItem.identifyingIcon = self.cardIssuerIcon;
   [model addItem:cardLastDigitsItem
       toSectionWithIdentifier:SectionIdentifierContent];
 
-  // TODO(crbug.com/1014652): Use the real strings once they are exposed in the
-  // delegate.
+  // TODO(crbug.com/1014652): Change textFieldEnabled to YES once editing its
+  // supported.
   TableViewTextEditItem* cardholderNameItem =
       [self textEditItemWithType:ItemTypeCardExpireYear
-                   textFieldName:@"Cardholder Name"
-                  textFieldValue:@"Sergio Collazos"
-                textFieldEnabled:YES];
+                   textFieldName:l10n_util::GetNSString(
+                                     IDS_IOS_AUTOFILL_CARDHOLDER_NAME)
+                  textFieldValue:self.cardholderName
+                textFieldEnabled:NO];
   [model addItem:cardholderNameItem
       toSectionWithIdentifier:SectionIdentifierContent];
 
-  // TODO(crbug.com/1014652): Use the real strings once they are exposed in the
-  // delegate.
-  TableViewTextEditItem* expireMonthItem =
-      [self textEditItemWithType:ItemTypeCardExpireYear
-                   textFieldName:@"Expiration Month"
-                  textFieldValue:@"07"
-                textFieldEnabled:YES];
+  // TODO(crbug.com/1014652): Change textFieldEnabled to YES once editing its
+  // supported.
+  TableViewTextEditItem* expireMonthItem = [self
+      textEditItemWithType:ItemTypeCardExpireYear
+             textFieldName:l10n_util::GetNSString(IDS_IOS_AUTOFILL_EXP_MONTH)
+            textFieldValue:self.expirationMonth
+          textFieldEnabled:NO];
   [model addItem:expireMonthItem
       toSectionWithIdentifier:SectionIdentifierContent];
 
-  // TODO(crbug.com/1014652): Use the real strings once they are exposed in the
-  // delegate.
-  TableViewTextEditItem* expireYearItem =
-      [self textEditItemWithType:ItemTypeCardExpireYear
-                   textFieldName:@"Expiration Year"
-                  textFieldValue:@"2020"
-                textFieldEnabled:YES];
+  // TODO(crbug.com/1014652): Change textFieldEnabled to YES once editing its
+  // supported.
+  TableViewTextEditItem* expireYearItem = [self
+      textEditItemWithType:ItemTypeCardExpireYear
+             textFieldName:l10n_util::GetNSString(IDS_IOS_AUTOFILL_EXP_YEAR)
+            textFieldValue:self.expirationYear
+          textFieldEnabled:NO];
   [model addItem:expireYearItem
       toSectionWithIdentifier:SectionIdentifierContent];
 
-  // TODO(crbug.com/1014652): Use the real strings once they are exposed in the
-  // delegate.
   TableViewTextButtonItem* saveCardButtonItem =
       [[TableViewTextButtonItem alloc] initWithType:ItemTypeCardSave];
   saveCardButtonItem.textAlignment = NSTextAlignmentNatural;
   // TODO(crbug.com/1014652): Implement TOS with Links. This might require a
   // separate item.
   saveCardButtonItem.text = @"TOS Agreement";
-  saveCardButtonItem.buttonText = @"Save Credit Card";
+  saveCardButtonItem.buttonText =
+      l10n_util::GetNSString(IDS_IOS_AUTOFILL_SAVE_CARD);
   saveCardButtonItem.enabled = YES;
   saveCardButtonItem.disableButtonIntrinsicWidth = YES;
   [model addItem:saveCardButtonItem
diff --git a/ios/chrome/browser/ui/infobars/presentation/infobar_modal_presentation_controller.mm b/ios/chrome/browser/ui/infobars/presentation/infobar_modal_presentation_controller.mm
index 5b185127..b02dbef 100644
--- a/ios/chrome/browser/ui/infobars/presentation/infobar_modal_presentation_controller.mm
+++ b/ios/chrome/browser/ui/infobars/presentation/infobar_modal_presentation_controller.mm
@@ -25,9 +25,14 @@
 @implementation InfobarModalPresentationController
 
 - (void)presentationTransitionWillBegin {
+  // Add a gesture recognizer to endEditing (thus hiding the keyboard) if a user
+  // taps outside the keyboard while one its being presented. Set
+  // cancelsTouchesInView to NO so the presented Modal can handle the gesture as
+  // well. (e.g. Selecting a row in a TableViewController.)
   UITapGestureRecognizer* tap =
       [[UITapGestureRecognizer alloc] initWithTarget:self.presentedView
                                               action:@selector(endEditing:)];
+  tap.cancelsTouchesInView = NO;
   [self.containerView addGestureRecognizer:tap];
 }
 
diff --git a/ios/chrome/browser/ui/list_model/BUILD.gn b/ios/chrome/browser/ui/list_model/BUILD.gn
index 9b6260b..44f2a899 100644
--- a/ios/chrome/browser/ui/list_model/BUILD.gn
+++ b/ios/chrome/browser/ui/list_model/BUILD.gn
@@ -28,7 +28,6 @@
     ":list_model",
     "//base",
     "//ios/chrome/test:test_support",
-    "//ios/chrome/test/base",
     "//testing/gtest",
   ]
 }
diff --git a/ios/chrome/browser/ui/main_content/BUILD.gn b/ios/chrome/browser/ui/main_content/BUILD.gn
index f996527..a3168782 100644
--- a/ios/chrome/browser/ui/main_content/BUILD.gn
+++ b/ios/chrome/browser/ui/main_content/BUILD.gn
@@ -62,7 +62,6 @@
     "//ios/chrome/browser/ui/main_content/test",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/browser/web_state_list",
-    "//ios/chrome/test/base",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
     "//testing/gtest",
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/BUILD.gn b/ios/chrome/browser/ui/omnibox/popup/shortcuts/BUILD.gn
index 115ae4e..0f0ebd0 100644
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/BUILD.gn
+++ b/ios/chrome/browser/ui/omnibox/popup/shortcuts/BUILD.gn
@@ -87,7 +87,6 @@
     "//ios/chrome/browser/ui/content_suggestions:content_suggestions_constant",
     "//ios/chrome/browser/ui/omnibox:omnibox_popup_shared",
     "//ios/chrome/test/app:test_support",
-    "//ios/chrome/test/base:base",
     "//ios/chrome/test/earl_grey:test_support",
     "//ios/testing/earl_grey:earl_grey_support",
     "//ios/third_party/earl_grey:earl_grey+link",
@@ -118,7 +117,6 @@
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/content_suggestions:content_suggestions_constant",
     "//ios/chrome/browser/ui/omnibox:omnibox_popup_shared",
-    "//ios/chrome/test/base",
     "//ios/chrome/test/earl_grey:eg_test_support+eg2",
     "//ios/testing/earl_grey:eg_test_support+eg2",
     "//ios/third_party/earl_grey2:test_lib",
diff --git a/ios/chrome/browser/ui/presenters/BUILD.gn b/ios/chrome/browser/ui/presenters/BUILD.gn
index 585e8e5..a167500 100644
--- a/ios/chrome/browser/ui/presenters/BUILD.gn
+++ b/ios/chrome/browser/ui/presenters/BUILD.gn
@@ -30,7 +30,6 @@
     ":presenters",
     "//base",
     "//base/test:test_support",
-    "//ios/chrome/test/base",
     "//testing/gtest",
   ]
 }
diff --git a/ios/chrome/browser/ui/safe_mode/BUILD.gn b/ios/chrome/browser/ui/safe_mode/BUILD.gn
index b2a04a3..e31b7a1 100644
--- a/ios/chrome/browser/ui/safe_mode/BUILD.gn
+++ b/ios/chrome/browser/ui/safe_mode/BUILD.gn
@@ -44,8 +44,8 @@
     "//ios/chrome/browser/ui/main",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/test/app:test_support",
-    "//ios/chrome/test/base",
     "//ios/chrome/test/earl_grey:test_support",
+    "//ios/testing:block_swizzler",
     "//ios/testing/earl_grey:earl_grey_support",
     "//ios/third_party/earl_grey:earl_grey+link",
   ]
@@ -109,8 +109,8 @@
     ":eg_test_support+eg2",
     "//base:base",
     "//ios/chrome/app/strings:ios_chromium_strings_grit",
-    "//ios/chrome/test/base:base",
     "//ios/chrome/test/earl_grey:eg_test_support+eg2",
+    "//ios/testing:block_swizzler",
     "//ios/testing/earl_grey:eg_test_support+eg2",
     "//ios/third_party/earl_grey2:test_lib",
   ]
@@ -129,8 +129,8 @@
     "//base",
     "//base/test:test_support",
     "//ios/chrome/browser/crash_report",
-    "//ios/chrome/test/base",
     "//ios/chrome/test/ocmock",
+    "//ios/testing:block_swizzler",
     "//testing/gtest",
     "//third_party/breakpad:client",
     "//third_party/ocmock",
diff --git a/ios/chrome/browser/ui/safe_mode/safe_mode_egtest.mm b/ios/chrome/browser/ui/safe_mode/safe_mode_egtest.mm
index 65c7083..ad98f27 100644
--- a/ios/chrome/browser/ui/safe_mode/safe_mode_egtest.mm
+++ b/ios/chrome/browser/ui/safe_mode/safe_mode_egtest.mm
@@ -8,7 +8,6 @@
 #include "base/mac/foundation_util.h"
 #include "ios/chrome/browser/ui/safe_mode/safe_mode_app_interface.h"
 #include "ios/chrome/grit/ios_chromium_strings.h"
-#import "ios/chrome/test/base/scoped_block_swizzler.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
@@ -16,6 +15,7 @@
 #import "ios/chrome/test/earl_grey/earl_grey_scoped_block_swizzler.h"
 #import "ios/testing/earl_grey/app_launch_manager.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
+#import "ios/testing/scoped_block_swizzler.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/ui/safe_mode/safe_mode_view_controller_unittest.mm b/ios/chrome/browser/ui/safe_mode/safe_mode_view_controller_unittest.mm
index 196f9e7a..ec23bb9 100644
--- a/ios/chrome/browser/ui/safe_mode/safe_mode_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/safe_mode/safe_mode_view_controller_unittest.mm
@@ -6,8 +6,8 @@
 #import "base/test/ios/wait_util.h"
 #import "ios/chrome/browser/crash_report/breakpad_helper.h"
 #import "ios/chrome/browser/crash_report/main_thread_freeze_detector.h"
-#import "ios/chrome/test/base/scoped_block_swizzler.h"
 #import "ios/chrome/test/ocmock/OCMockObject+BreakpadControllerTesting.h"
+#import "ios/testing/scoped_block_swizzler.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 #import "third_party/breakpad/breakpad/src/client/ios/BreakpadController.h"
diff --git a/ios/chrome/browser/ui/settings/password/passwords_settings_egtest.mm b/ios/chrome/browser/ui/settings/password/passwords_settings_egtest.mm
index 1458708..3acc0a8 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_settings_egtest.mm
@@ -647,6 +647,11 @@
 // Checks that deleting a saved password from password details view goes back
 // to the list-of-passwords view which doesn't display that form anymore.
 - (void)testSavedFormDeletionInDetailView {
+  // TODO(crbug.com/1023619): Enable the test on 13.2+ iPad once the bug is
+  // fixed.
+  if (base::ios::IsRunningOnOrLater(13, 2, 0) && [ChromeEarlGrey isIPadIdiom]) {
+    EARL_GREY_TEST_SKIPPED(@"Test disabled on iPad and iOS 13.2+");
+  }
   // Save form to be deleted later.
   SaveExamplePasswordForm();
 
@@ -698,6 +703,11 @@
 // goes back to the list-of-passwords view which doesn't display that form
 // anymore.
 - (void)testDuplicatedSavedFormDeletionInDetailView {
+  // TODO(crbug.com/1023619): Enable the test on 13.2+ iPad once the bug is
+  // fixed.
+  if (base::ios::IsRunningOnOrLater(13, 2, 0) && [ChromeEarlGrey isIPadIdiom]) {
+    EARL_GREY_TEST_SKIPPED(@"Test disabled on iPad and iOS 13.2+");
+  }
   // Save form to be deleted later.
   SaveExamplePasswordForm();
   // Save duplicate of the previously saved form to be deleted at the same time.
@@ -757,6 +767,11 @@
 // Checks that deleting a blacklisted form from password details view goes
 // back to the list-of-passwords view which doesn't display that form anymore.
 - (void)testBlacklistedFormDeletionInDetailView {
+  // TODO(crbug.com/1023619): Enable the test on 13.2+ iPad once the bug is
+  // fixed.
+  if (base::ios::IsRunningOnOrLater(13, 2, 0) && [ChromeEarlGrey isIPadIdiom]) {
+    EARL_GREY_TEST_SKIPPED(@"Test disabled on iPad and iOS 13.2+");
+  }
   // Save blacklisted form to be deleted later.
   PasswordForm blacklisted;
   blacklisted.origin = GURL("https://blacklisted.com");
@@ -809,6 +824,11 @@
 
 // Checks that deleting a password from password details can be cancelled.
 - (void)testCancelDeletionInDetailView {
+  // TODO(crbug.com/1023619): Enable the test on 13.2+ iPad once the bug is
+  // fixed.
+  if (base::ios::IsRunningOnOrLater(13, 2, 0) && [ChromeEarlGrey isIPadIdiom]) {
+    EARL_GREY_TEST_SKIPPED(@"Test disabled on iPad and iOS 13.2+");
+  }
   // Save form to be deleted later.
   SaveExamplePasswordForm();
 
@@ -1503,6 +1523,11 @@
 
 // Test export flow
 - (void)testExportFlow {
+  // TODO(crbug.com/1023619): Enable the test on 13.2+ iPad once the bug is
+  // fixed.
+  if (base::ios::IsRunningOnOrLater(13, 2, 0) && [ChromeEarlGrey isIPadIdiom]) {
+    EARL_GREY_TEST_SKIPPED(@"Test disabled on iPad and iOS 13.2+");
+  }
   // Saving a form is needed for exporting passwords.
   SaveExamplePasswordForm();
 
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index 55768c1..24ba864 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -295,7 +295,6 @@
     "//ios/chrome/common:unit_tests",
     "//ios/chrome/content_widget_extension:unit_tests",
     "//ios/chrome/search_widget_extension:unit_tests",
-    "//ios/chrome/test/base:unit_tests",
     "//ios/testing:http_server_bundle_data",
   ]
   if (ios_enable_firebase_sdk) {
diff --git a/ios/chrome/test/base/BUILD.gn b/ios/chrome/test/base/BUILD.gn
index 02f8dfe..438c6c1 100644
--- a/ios/chrome/test/base/BUILD.gn
+++ b/ios/chrome/test/base/BUILD.gn
@@ -5,25 +5,9 @@
 source_set("base") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
-  sources = [
-    "scoped_block_swizzler.h",
-    "scoped_block_swizzler.mm",
-  ]
-  deps = [
-    "//base",
-  ]
-}
-
-source_set("unit_tests") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  testonly = true
-  sources = [
-    "scoped_block_swizzler_unittest.mm",
-  ]
-  deps = [
-    ":base",
-    "//base",
-    "//testing/gtest",
+  sources = []
+  public_deps = [
+    "//ios/testing:block_swizzler",
   ]
 }
 
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index 0d3c33c..17b21d2 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -74,7 +74,6 @@
 chrome_ios_eg_test("ios_chrome_ui_egtests") {
   deps = [
     "//ios/chrome/browser/ui/activity_services:eg_tests",
-    "//ios/chrome/browser/ui/alert_coordinator:eg_tests",
     "//ios/chrome/browser/ui/browser_view:eg_tests",
     "//ios/chrome/browser/ui/dialogs:eg_tests",
     "//ios/chrome/browser/ui/download:eg_tests",
@@ -282,7 +281,7 @@
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/browser/web:tab_id_tab_helper",
     "//ios/chrome/test/app:test_support",
-    "//ios/chrome/test/base",
+    "//ios/testing:block_swizzler",
     "//ios/testing:verify_custom_webkit",
     "//ios/testing/earl_grey:earl_grey_support",
     "//ios/third_party/material_components_ios",
@@ -416,7 +415,7 @@
     "//ios/chrome/browser/ui/util:eg_app_support+eg2",
     "//ios/chrome/browser/web:tab_id_tab_helper",
     "//ios/chrome/test/app:test_support",
-    "//ios/chrome/test/base",
+    "//ios/testing:block_swizzler",
     "//ios/testing:nserror_support",
     "//ios/testing:verify_custom_webkit",
     "//ios/testing/earl_grey:eg_app_support+eg2",
diff --git a/ios/chrome/test/earl_grey/earl_grey_scoped_block_swizzler_app_interface.mm b/ios/chrome/test/earl_grey/earl_grey_scoped_block_swizzler_app_interface.mm
index 8dc9078..0485885 100644
--- a/ios/chrome/test/earl_grey/earl_grey_scoped_block_swizzler_app_interface.mm
+++ b/ios/chrome/test/earl_grey/earl_grey_scoped_block_swizzler_app_interface.mm
@@ -7,7 +7,7 @@
 #include <map>
 
 #include "base/logging.h"
-#include "ios/chrome/test/base/scoped_block_swizzler.h"
+#include "ios/testing/scoped_block_swizzler.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/testing/BUILD.gn b/ios/testing/BUILD.gn
index acc55d5..296bf38d 100644
--- a/ios/testing/BUILD.gn
+++ b/ios/testing/BUILD.gn
@@ -8,7 +8,7 @@
 group("all_tests") {
   testonly = true
   deps = [
-    ":ocmock_support_unittests",
+    ":ios_testing_unittests",
   ]
 }
 
@@ -36,6 +36,18 @@
   ]
 }
 
+source_set("block_swizzler") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  sources = [
+    "scoped_block_swizzler.h",
+    "scoped_block_swizzler.mm",
+  ]
+  deps = [
+    "//base",
+  ]
+}
+
 source_set("nserror_support") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
@@ -76,9 +88,10 @@
   ]
 }
 
-test("ocmock_support_unittests") {
+test("ios_testing_unittests") {
   configs += [ "//build/config/compiler:enable_arc" ]
   deps = [
+    ":block_swizzler",
     ":ocmock_support",
     "//base/test:run_all_unittests",
     "//base/test:test_support",
@@ -89,6 +102,7 @@
 
   sources = [
     "ocmock_complex_type_helper_unittest.mm",
+    "scoped_block_swizzler_unittest.mm",
   ]
 
   assert_no_deps = ios_assert_no_deps
diff --git a/ios/chrome/test/base/scoped_block_swizzler.h b/ios/testing/scoped_block_swizzler.h
similarity index 89%
rename from ios/chrome/test/base/scoped_block_swizzler.h
rename to ios/testing/scoped_block_swizzler.h
index ecfea71..c6c0abf7 100644
--- a/ios/chrome/test/base/scoped_block_swizzler.h
+++ b/ios/testing/scoped_block_swizzler.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-#ifndef IOS_CHROME_TEST_BASE_SCOPED_BLOCK_SWIZZLER_H_
-#define IOS_CHROME_TEST_BASE_SCOPED_BLOCK_SWIZZLER_H_
+#ifndef IOS_TESTING_SCOPED_BLOCK_SWIZZLER_H_
+#define IOS_TESTING_SCOPED_BLOCK_SWIZZLER_H_
 
 #include <objc/runtime.h>
 
@@ -37,4 +37,4 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedBlockSwizzler);
 };
 
-#endif  // IOS_CHROME_TEST_BASE_SCOPED_BLOCK_SWIZZLER_H_
+#endif  // IOS_TESTING_SCOPED_BLOCK_SWIZZLER_H_
diff --git a/ios/chrome/test/base/scoped_block_swizzler.mm b/ios/testing/scoped_block_swizzler.mm
similarity index 93%
rename from ios/chrome/test/base/scoped_block_swizzler.mm
rename to ios/testing/scoped_block_swizzler.mm
index 2f2bee3..998d0de0 100644
--- a/ios/chrome/test/base/scoped_block_swizzler.mm
+++ b/ios/testing/scoped_block_swizzler.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/chrome/test/base/scoped_block_swizzler.h"
+#include "ios/testing/scoped_block_swizzler.h"
 
 #include "base/logging.h"
 
diff --git a/ios/chrome/test/base/scoped_block_swizzler_unittest.mm b/ios/testing/scoped_block_swizzler_unittest.mm
similarity index 97%
rename from ios/chrome/test/base/scoped_block_swizzler_unittest.mm
rename to ios/testing/scoped_block_swizzler_unittest.mm
index 2af049c..2a8524a 100644
--- a/ios/chrome/test/base/scoped_block_swizzler_unittest.mm
+++ b/ios/testing/scoped_block_swizzler_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/chrome/test/base/scoped_block_swizzler.h"
+#include "ios/testing/scoped_block_swizzler.h"
 #include "base/mac/foundation_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
diff --git a/ios/web/web_view_only/wk_web_view_configuration_util_unittests.mm b/ios/web/web_view_only/wk_web_view_configuration_util_unittests.mm
index da65183..566a4699 100644
--- a/ios/web/web_view_only/wk_web_view_configuration_util_unittests.mm
+++ b/ios/web/web_view_only/wk_web_view_configuration_util_unittests.mm
@@ -34,4 +34,4 @@
   [web_controller() removeWebView];
 }
 
-}  // namespace
+}  // namespace web
diff --git a/ios/web_view/internal/passwords/web_view_password_manager_client.h b/ios/web_view/internal/passwords/web_view_password_manager_client.h
index 05cc5831..f8bb7e1 100644
--- a/ios/web_view/internal/passwords/web_view_password_manager_client.h
+++ b/ios/web_view/internal/passwords/web_view_password_manager_client.h
@@ -115,6 +115,7 @@
   scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
   bool IsIsolationForPasswordSitesEnabled() const override;
   bool IsNewTabPage() const override;
+  password_manager::FieldInfoManager* GetFieldInfoManager() const override;
 
  private:
   __weak id<CWVPasswordManagerClientDelegate> delegate_;
diff --git a/ios/web_view/internal/passwords/web_view_password_manager_client.mm b/ios/web_view/internal/passwords/web_view_password_manager_client.mm
index bb28e55..09b7cf0c 100644
--- a/ios/web_view/internal/passwords/web_view_password_manager_client.mm
+++ b/ios/web_view/internal/passwords/web_view_password_manager_client.mm
@@ -233,4 +233,9 @@
   return false;
 }
 
+password_manager::FieldInfoManager*
+WebViewPasswordManagerClient::GetFieldInfoManager() const {
+  return nullptr;
+}
+
 }  // namespace ios_web_view
diff --git a/ipc/ipc_channel_common.cc b/ipc/ipc_channel_common.cc
index 7afb12d..3a141172 100644
--- a/ipc/ipc_channel_common.cc
+++ b/ipc/ipc_channel_common.cc
@@ -5,6 +5,7 @@
 #include "build/build_config.h"
 #include "ipc/ipc_channel.h"
 #include "ipc/ipc_channel_mojo.h"
+#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 
 namespace IPC {
@@ -39,7 +40,8 @@
   return ChannelMojo::Create(
       mojo::ScopedMessagePipeHandle(channel_handle.mojo_handle),
       Channel::MODE_CLIENT, listener, ipc_task_runner,
-      base::ThreadTaskRunnerHandle::Get());
+      base::ThreadTaskRunnerHandle::Get(),
+      mojo::internal::MessageQuotaChecker::MaybeCreate());
 #endif
 }
 
@@ -55,7 +57,8 @@
   return ChannelMojo::Create(
       mojo::ScopedMessagePipeHandle(channel_handle.mojo_handle),
       Channel::MODE_SERVER, listener, ipc_task_runner,
-      base::ThreadTaskRunnerHandle::Get());
+      base::ThreadTaskRunnerHandle::Get(),
+      mojo::internal::MessageQuotaChecker::MaybeCreate());
 #endif
 }
 
diff --git a/ipc/ipc_channel_factory.cc b/ipc/ipc_channel_factory.cc
index 655c0eb..decaf831 100644
--- a/ipc/ipc_channel_factory.cc
+++ b/ipc/ipc_channel_factory.cc
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ipc/ipc_channel_factory.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "ipc/ipc_channel_factory.h"
 #include "ipc/ipc_channel_mojo.h"
+#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
 
 namespace IPC {
 
@@ -17,7 +18,10 @@
       ChannelHandle handle,
       Channel::Mode mode,
       const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner)
-      : handle_(handle), mode_(mode), ipc_task_runner_(ipc_task_runner) {}
+      : handle_(handle),
+        mode_(mode),
+        ipc_task_runner_(ipc_task_runner),
+        quota_checker_(mojo::internal::MessageQuotaChecker::MaybeCreate()) {}
 
   std::unique_ptr<Channel> BuildChannel(Listener* listener) override {
 #if defined(OS_NACL_SFI)
@@ -26,7 +30,7 @@
     DCHECK(handle_.is_mojo_channel_handle());
     return ChannelMojo::Create(
         mojo::ScopedMessagePipeHandle(handle_.mojo_handle), mode_, listener,
-        ipc_task_runner_, base::ThreadTaskRunnerHandle::Get());
+        ipc_task_runner_, base::ThreadTaskRunnerHandle::Get(), quota_checker_);
 #endif
   }
 
@@ -34,10 +38,16 @@
     return ipc_task_runner_;
   }
 
+  scoped_refptr<mojo::internal::MessageQuotaChecker> GetQuotaChecker()
+      override {
+    return quota_checker_;
+  }
+
  private:
   ChannelHandle handle_;
   Channel::Mode mode_;
   scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner_;
+  scoped_refptr<mojo::internal::MessageQuotaChecker> quota_checker_;
 
   DISALLOW_COPY_AND_ASSIGN(PlatformChannelFactory);
 };
diff --git a/ipc/ipc_channel_factory.h b/ipc/ipc_channel_factory.h
index 03d9626..8051cba 100644
--- a/ipc/ipc_channel_factory.h
+++ b/ipc/ipc_channel_factory.h
@@ -14,6 +14,12 @@
 #include "base/single_thread_task_runner.h"
 #include "ipc/ipc_channel.h"
 
+namespace mojo {
+namespace internal {
+class MessageQuotaChecker;
+}  // namespace internal
+}  // namespace mojo
+
 namespace IPC {
 
 // Encapsulates how a Channel is created. A ChannelFactory can be
@@ -31,6 +37,8 @@
   virtual ~ChannelFactory() { }
   virtual std::unique_ptr<Channel> BuildChannel(Listener* listener) = 0;
   virtual scoped_refptr<base::SingleThreadTaskRunner> GetIPCTaskRunner() = 0;
+  virtual scoped_refptr<mojo::internal::MessageQuotaChecker>
+  GetQuotaChecker() = 0;
 };
 
 }  // namespace IPC
diff --git a/ipc/ipc_channel_mojo.cc b/ipc/ipc_channel_mojo.cc
index 6e34266f..0affc26 100644
--- a/ipc/ipc_channel_mojo.cc
+++ b/ipc/ipc_channel_mojo.cc
@@ -27,6 +27,7 @@
 #include "ipc/ipc_mojo_handle_attachment.h"
 #include "ipc/native_handle_type_converters.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 
@@ -44,22 +45,30 @@
       : handle_(std::move(handle)),
         mode_(mode),
         ipc_task_runner_(ipc_task_runner),
-        proxy_task_runner_(proxy_task_runner) {}
+        proxy_task_runner_(proxy_task_runner),
+        quota_checker_(mojo::internal::MessageQuotaChecker::MaybeCreate()) {}
 
   std::unique_ptr<Channel> BuildChannel(Listener* listener) override {
     return ChannelMojo::Create(std::move(handle_), mode_, listener,
-                               ipc_task_runner_, proxy_task_runner_);
+                               ipc_task_runner_, proxy_task_runner_,
+                               quota_checker_);
   }
 
   scoped_refptr<base::SingleThreadTaskRunner> GetIPCTaskRunner() override {
     return ipc_task_runner_;
   }
 
+  scoped_refptr<mojo::internal::MessageQuotaChecker> GetQuotaChecker()
+      override {
+    return quota_checker_;
+  }
+
  private:
   mojo::ScopedMessagePipeHandle handle_;
   const Channel::Mode mode_;
   scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> proxy_task_runner_;
+  scoped_refptr<mojo::internal::MessageQuotaChecker> quota_checker_;
 
   DISALLOW_COPY_AND_ASSIGN(MojoChannelFactory);
 };
@@ -86,9 +95,11 @@
     Mode mode,
     Listener* listener,
     const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
-    const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner) {
+    const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner,
+    const scoped_refptr<mojo::internal::MessageQuotaChecker>& quota_checker) {
   return base::WrapUnique(new ChannelMojo(std::move(handle), mode, listener,
-                                          ipc_task_runner, proxy_task_runner));
+                                          ipc_task_runner, proxy_task_runner,
+                                          quota_checker));
 }
 
 // static
@@ -116,11 +127,12 @@
     Mode mode,
     Listener* listener,
     const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
-    const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner)
+    const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner,
+    const scoped_refptr<mojo::internal::MessageQuotaChecker>& quota_checker)
     : task_runner_(ipc_task_runner), pipe_(handle.get()), listener_(listener) {
   weak_ptr_ = weak_factory_.GetWeakPtr();
   bootstrap_ = MojoBootstrap::Create(std::move(handle), mode, ipc_task_runner,
-                                     proxy_task_runner);
+                                     proxy_task_runner, quota_checker);
 }
 
 void ChannelMojo::ForwardMessageFromThreadSafePtr(mojo::Message message) {
diff --git a/ipc/ipc_channel_mojo.h b/ipc/ipc_channel_mojo.h
index 3445708..49ba1a8 100644
--- a/ipc/ipc_channel_mojo.h
+++ b/ipc/ipc_channel_mojo.h
@@ -51,7 +51,8 @@
       Mode mode,
       Listener* listener,
       const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
-      const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner);
+      const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner,
+      const scoped_refptr<mojo::internal::MessageQuotaChecker>& quota_checker);
 
   // Create a factory object for ChannelMojo.
   // The factory is used to create Mojo-based ChannelProxy family.
@@ -101,7 +102,8 @@
       Mode mode,
       Listener* listener,
       const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
-      const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner);
+      const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner,
+      const scoped_refptr<mojo::internal::MessageQuotaChecker>& quota_checker);
 
   void ForwardMessageFromThreadSafePtr(mojo::Message message);
   void ForwardMessageWithResponderFromThreadSafePtr(
diff --git a/ipc/ipc_channel_proxy.cc b/ipc/ipc_channel_proxy.cc
index 54179c0..8b7729b 100644
--- a/ipc/ipc_channel_proxy.cc
+++ b/ipc/ipc_channel_proxy.cc
@@ -224,6 +224,9 @@
 
 // Called on the IPC::Channel thread
 void ChannelProxy::Context::OnSendMessage(std::unique_ptr<Message> message) {
+  if (quota_checker_)
+    quota_checker_->AfterMessagesDequeued(1);
+
   if (!channel_) {
     OnChannelClosed();
     return;
@@ -419,6 +422,9 @@
 }
 
 void ChannelProxy::Context::Send(Message* message) {
+  if (quota_checker_)
+    quota_checker_->BeforeMessagesEnqueued(1);
+
   ipc_task_runner()->PostTask(
       FROM_HERE, base::BindOnce(&ChannelProxy::Context::OnSendMessage, this,
                                 base::WrapUnique(message)));
@@ -497,6 +503,9 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!did_init_);
 
+  DCHECK(!context_->quota_checker_);
+  context_->quota_checker_ = factory->GetQuotaChecker();
+
   if (create_pipe_now) {
     // Create the channel immediately.  This effectively sets up the
     // low-level pipe so that the client can connect.  Without creating
diff --git a/ipc/ipc_channel_proxy.h b/ipc/ipc_channel_proxy.h
index 1260761..ab1d8d3c 100644
--- a/ipc/ipc_channel_proxy.h
+++ b/ipc/ipc_channel_proxy.h
@@ -27,6 +27,7 @@
 #include "mojo/public/cpp/bindings/associated_interface_ptr.h"
 #include "mojo/public/cpp/bindings/associated_interface_request.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
 #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
 #include "mojo/public/cpp/bindings/thread_safe_interface_ptr.h"
 
@@ -376,6 +377,9 @@
     std::unique_ptr<Channel> channel_;
     bool channel_connected_called_;
 
+    // The quota checker associated with this channel, if any.
+    scoped_refptr<mojo::internal::MessageQuotaChecker> quota_checker_;
+
     // Lock for |channel_| value. This is only relevant in the context of
     // thread-safe send.
     base::Lock channel_lifetime_lock_;
diff --git a/ipc/ipc_mojo_bootstrap.cc b/ipc/ipc_mojo_bootstrap.cc
index 39d29dd..47cd62ef 100644
--- a/ipc/ipc_mojo_bootstrap.cc
+++ b/ipc/ipc_mojo_bootstrap.cc
@@ -120,9 +120,11 @@
   ChannelAssociatedGroupController(
       bool set_interface_id_namespace_bit,
       const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
-      const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner)
+      const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner,
+      const scoped_refptr<mojo::internal::MessageQuotaChecker>& quota_checker)
       : task_runner_(ipc_task_runner),
         proxy_task_runner_(proxy_task_runner),
+        quota_checker_(quota_checker),
         set_interface_id_namespace_bit_(set_interface_id_namespace_bit),
         dispatcher_(this),
         control_message_handler_(this),
@@ -172,6 +174,8 @@
                    base::Unretained(this)));
     connector_->set_enforce_errors_from_incoming_receiver(false);
     connector_->SetWatcherHeapProfilerTag("IPC Channel");
+    if (quota_checker_)
+      connector_->SetMessageQuotaChecker(quota_checker_);
 
     // Don't let the Connector do any sort of queuing on our behalf. Individual
     // messages bound for the IPC::ChannelProxy thread (i.e. that vast majority
@@ -198,6 +202,9 @@
       base::AutoLock lock(outgoing_messages_lock_);
       std::swap(outgoing_messages, outgoing_messages_);
     }
+    if (quota_checker_ && outgoing_messages.size())
+      quota_checker_->AfterMessagesDequeued(outgoing_messages.size());
+
     for (auto& message : outgoing_messages)
       SendMessage(&message);
   }
@@ -243,6 +250,9 @@
     connector_.reset();
 
     base::AutoLock lock(outgoing_messages_lock_);
+    if (quota_checker_ && outgoing_messages_.size())
+      quota_checker_->AfterMessagesDequeued(outgoing_messages_.size());
+
     outgoing_messages_.clear();
   }
 
@@ -706,6 +716,8 @@
       if (!connector_ || paused_) {
         if (!shut_down_) {
           base::AutoLock lock(outgoing_messages_lock_);
+          if (quota_checker_)
+            quota_checker_->BeforeMessagesEnqueued(1);
           outgoing_messages_.emplace_back(std::move(*message));
         }
         return true;
@@ -983,6 +995,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
   const scoped_refptr<base::SingleThreadTaskRunner> proxy_task_runner_;
+  const scoped_refptr<mojo::internal::MessageQuotaChecker> quota_checker_;
   const bool set_interface_id_namespace_bit_;
   bool paused_ = false;
   std::unique_ptr<mojo::Connector> connector_;
@@ -1100,11 +1113,12 @@
     mojo::ScopedMessagePipeHandle handle,
     Channel::Mode mode,
     const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
-    const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner) {
+    const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner,
+    const scoped_refptr<mojo::internal::MessageQuotaChecker>& quota_checker) {
   return std::make_unique<MojoBootstrapImpl>(
-      std::move(handle),
-      new ChannelAssociatedGroupController(mode == Channel::MODE_SERVER,
-                                           ipc_task_runner, proxy_task_runner));
+      std::move(handle), new ChannelAssociatedGroupController(
+                             mode == Channel::MODE_SERVER, ipc_task_runner,
+                             proxy_task_runner, quota_checker));
 }
 
 }  // namespace IPC
diff --git a/ipc/ipc_mojo_bootstrap.h b/ipc/ipc_mojo_bootstrap.h
index 89262846..d231ab2c 100644
--- a/ipc/ipc_mojo_bootstrap.h
+++ b/ipc/ipc_mojo_bootstrap.h
@@ -19,6 +19,7 @@
 #include "ipc/ipc_listener.h"
 #include "mojo/public/cpp/bindings/associated_group.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 
@@ -42,7 +43,8 @@
       mojo::ScopedMessagePipeHandle handle,
       Channel::Mode mode,
       const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
-      const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner);
+      const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner,
+      const scoped_refptr<mojo::internal::MessageQuotaChecker>& quota_checker);
 
   // Start the handshake over the underlying message pipe.
   virtual void Connect(
diff --git a/ipc/ipc_mojo_bootstrap_unittest.cc b/ipc/ipc_mojo_bootstrap_unittest.cc
index 2b6dd97..d91d475 100644
--- a/ipc/ipc_mojo_bootstrap_unittest.cc
+++ b/ipc/ipc_mojo_bootstrap_unittest.cc
@@ -113,7 +113,7 @@
       IPC::MojoBootstrap::Create(
           helper_.StartChild("IPCMojoBootstrapTestClient"),
           IPC::Channel::MODE_SERVER, base::ThreadTaskRunnerHandle::Get(),
-          base::ThreadTaskRunnerHandle::Get()),
+          base::ThreadTaskRunnerHandle::Get(), nullptr),
       kTestServerPid);
 
   mojo::PendingAssociatedReceiver<IPC::mojom::Channel> receiver;
@@ -138,7 +138,7 @@
       IPC::MojoBootstrap::Create(
           std::move(mojo::core::test::MultiprocessTestHelper::primordial_pipe),
           IPC::Channel::MODE_CLIENT, base::ThreadTaskRunnerHandle::Get(),
-          base::ThreadTaskRunnerHandle::Get()),
+          base::ThreadTaskRunnerHandle::Get(), nullptr),
       kTestClientPid);
 
   mojo::PendingAssociatedReceiver<IPC::mojom::Channel> receiver;
@@ -159,7 +159,7 @@
       IPC::MojoBootstrap::Create(
           helper_.StartChild("IPCMojoBootstrapTestEmptyMessage"),
           IPC::Channel::MODE_SERVER, base::ThreadTaskRunnerHandle::Get(),
-          base::ThreadTaskRunnerHandle::Get()),
+          base::ThreadTaskRunnerHandle::Get(), nullptr),
       kTestServerPid);
 
   mojo::PendingAssociatedReceiver<IPC::mojom::Channel> receiver;
@@ -186,7 +186,7 @@
       IPC::MojoBootstrap::Create(
           std::move(mojo::core::test::MultiprocessTestHelper::primordial_pipe),
           IPC::Channel::MODE_CLIENT, base::ThreadTaskRunnerHandle::Get(),
-          base::ThreadTaskRunnerHandle::Get()),
+          base::ThreadTaskRunnerHandle::Get(), nullptr),
       kTestClientPid);
 
   mojo::PendingAssociatedReceiver<IPC::mojom::Channel> receiver;
diff --git a/ipc/ipc_test_base.cc b/ipc/ipc_test_base.cc
index 4194194..2030cb7 100644
--- a/ipc/ipc_test_base.cc
+++ b/ipc/ipc_test_base.cc
@@ -38,9 +38,10 @@
 }
 
 void IPCChannelMojoTestBase::CreateChannel(IPC::Listener* listener) {
-  channel_ = IPC::ChannelMojo::Create(
-      TakeHandle(), IPC::Channel::MODE_SERVER, listener,
-      base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get());
+  channel_ =
+      IPC::ChannelMojo::Create(TakeHandle(), IPC::Channel::MODE_SERVER,
+                               listener, base::ThreadTaskRunnerHandle::Get(),
+                               base::ThreadTaskRunnerHandle::Get(), nullptr);
 }
 
 bool IPCChannelMojoTestBase::ConnectChannel() {
@@ -64,9 +65,10 @@
 }
 
 void IpcChannelMojoTestClient::Connect(IPC::Listener* listener) {
-  channel_ = IPC::ChannelMojo::Create(
-      std::move(handle_), IPC::Channel::MODE_CLIENT, listener,
-      base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get());
+  channel_ =
+      IPC::ChannelMojo::Create(std::move(handle_), IPC::Channel::MODE_CLIENT,
+                               listener, base::ThreadTaskRunnerHandle::Get(),
+                               base::ThreadTaskRunnerHandle::Get(), nullptr);
   CHECK(channel_->Connect());
 }
 
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
index 2c44d3b..7682756e 100644
--- a/mojo/public/cpp/bindings/BUILD.gn
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -160,6 +160,8 @@
     "lib/interface_ptr_state.h",
     "lib/interface_serialization.h",
     "lib/message_dispatcher.cc",
+    "lib/message_quota_checker.cc",
+    "lib/message_quota_checker.h",
     "lib/multiplex_router.cc",
     "lib/multiplex_router.h",
     "lib/native_enum_data.h",
diff --git a/mojo/public/cpp/bindings/connector.h b/mojo/public/cpp/bindings/connector.h
index 761f06e7..03e6e68b 100644
--- a/mojo/public/cpp/bindings/connector.h
+++ b/mojo/public/cpp/bindings/connector.h
@@ -18,7 +18,6 @@
 #include "base/optional.h"
 #include "base/sequence_checker.h"
 #include "base/sequenced_task_runner.h"
-#include "mojo/public/c/system/quota.h"
 #include "mojo/public/cpp/bindings/connection_group.h"
 #include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/cpp/bindings/sequence_local_sync_event_watcher.h"
@@ -32,6 +31,9 @@
 }
 
 namespace mojo {
+namespace internal {
+class MessageQuotaChecker;
+}
 
 // The Connector class is responsible for performing read/write operations on a
 // MessagePipe. It writes messages it receives through the MessageReceiver
@@ -197,6 +199,10 @@
   // |tag| must be a const string literal.
   void SetWatcherHeapProfilerTag(const char* tag);
 
+  // Sets the quota checker.
+  void SetMessageQuotaChecker(
+      scoped_refptr<internal::MessageQuotaChecker> checker);
+
   // Allows testing environments to override the default serialization behavior
   // of newly constructed Connector instances. Must be called before any
   // Connector instances are constructed.
@@ -315,10 +321,8 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
-  // If this instance was selected for unread message measurement, contains
-  // the max send quota usage seen so far. If this instance was not selected
-  // contains MOJO_QUOTA_LIMIT_NONE as a sentinel value.
-  uint64_t max_unread_message_quota_used_ = MOJO_QUOTA_LIMIT_NONE;
+  // The quota checker associate with this connector, if any.
+  scoped_refptr<internal::MessageQuotaChecker> quota_checker_;
 
   base::Lock connected_lock_;
   bool connected_ = true;
diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc
index 5f77d2d..c9e84c9 100644
--- a/mojo/public/cpp/bindings/lib/connector.cc
+++ b/mojo/public/cpp/bindings/lib/connector.cc
@@ -8,8 +8,6 @@
 
 #include "base/bind.h"
 #include "base/compiler_specific.h"
-#include "base/debug/alias.h"
-#include "base/debug/dump_without_crashing.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/macros.h"
@@ -23,8 +21,10 @@
 #include "base/synchronization/lock.h"
 #include "base/threading/sequence_local_storage_slot.h"
 #include "base/trace_event/trace_event.h"
+#include "mojo/public/c/system/quota.h"
 #include "mojo/public/cpp/bindings/features.h"
 #include "mojo/public/cpp/bindings/lib/may_auto_lock.h"
+#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
 #include "mojo/public/cpp/bindings/lib/tracing_helper.h"
 #include "mojo/public/cpp/bindings/mojo_buildflags.h"
 #include "mojo/public/cpp/bindings/sync_handle_watcher.h"
@@ -56,62 +56,6 @@
   return enable;
 }
 
-const base::FeatureParam<int> kMojoRecordUnreadMessageCountSampleRate = {
-    &features::kMojoRecordUnreadMessageCount, "SampleRate",
-    100  // Sample 1% of Connectors by default.
-};
-
-const base::FeatureParam<int> kMojoRecordUnreadMessageCountQuotaValue = {
-    &features::kMojoRecordUnreadMessageCount, "QuotaValue",
-    100  // Use a 100 message quote by default.
-};
-
-const base::FeatureParam<int> kMojoRecordUnreadMessageCountCrashThreshold = {
-    &features::kMojoRecordUnreadMessageCount, "CrashThreshold",
-    0  // Set to zero to disable crash dumps by default.
-};
-
-int UnreadMessageCountQuota() {
-  static const bool enabled =
-      base::FeatureList::IsEnabled(features::kMojoRecordUnreadMessageCount);
-  if (!enabled)
-    return 0;
-
-  static const int sample_rate = kMojoRecordUnreadMessageCountSampleRate.Get();
-  if (base::RandInt(0, sample_rate - 1) != 0)
-    return 0;
-
-  static const int quota = kMojoRecordUnreadMessageCountQuotaValue.Get();
-  return quota;
-}
-
-// Disable inlining for this function to make sure it appears in the stack
-// trace on crash.
-NOINLINE void MaybeDumpWithoutCrashing(int quota_used) {
-  static const int crash_threshold =
-      kMojoRecordUnreadMessageCountCrashThreshold.Get();
-  if (crash_threshold == 0 || quota_used < crash_threshold)
-    return;
-
-  static bool have_crashed = false;
-  if (have_crashed)
-    return;
-
-  // Only crash once per process/per run. Note that this is slightly racy
-  // against concurrent quota overruns on multiple threads, but that's fine.
-  have_crashed = true;
-
-  // This is happening because the user of the interface implicated on the crash
-  // stack has queued up an unreasonable number of messages, namely
-  // |quota_used|.
-  base::debug::DumpWithoutCrashing();
-
-  // Defeat tail-call optimization and ensure these two variables are available
-  // on the stack.
-  base::debug::Alias(&crash_threshold);
-  base::debug::Alias(&quota_used);
-}
-
 }  // namespace
 
 // Used to efficiently maintain a doubly-linked list of all Connectors
@@ -220,25 +164,14 @@
   // Even though we don't have an incoming receiver, we still want to monitor
   // the message pipe to know if is closed or encounters an error.
   WaitToReadMore();
-
-  int unread_message_count_quota = UnreadMessageCountQuota();
-  if (unread_message_count_quota != 0) {
-    // This connector has been sampled to record the max unread message count.
-    // Note that setting the quota to N results in over-counting usage by up to
-    // N/2, in addition to overcounting due to message transit delays. As result
-    // it's best to treat the resulting metric as N-granular.
-    MojoResult rv = MojoSetQuota(message_pipe_.get().value(),
-                                 MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT,
-                                 unread_message_count_quota, nullptr);
-    if (rv == MOJO_RESULT_OK)
-      max_unread_message_quota_used_ = 0U;
-  }
 }
 
 Connector::~Connector() {
-  if (max_unread_message_quota_used_ != MOJO_QUOTA_LIMIT_NONE) {
+  if (quota_checker_) {
+    // Clear the message pipe handle in the checker.
+    quota_checker_->SetMessagePipe(MessagePipeHandle());
     UMA_HISTOGRAM_COUNTS_1M("Mojo.Connector.MaxUnreadMessageQuotaUsed",
-                            max_unread_message_quota_used_);
+                            quota_checker_->GetMaxQuotaUsage());
   }
 
   {
@@ -393,17 +326,9 @@
     DCHECK(dump_result);
   }
 #endif
-  if (max_unread_message_quota_used_ != MOJO_QUOTA_LIMIT_NONE) {
-    uint64_t limit = 0;
-    uint64_t usage = 0;
-    MojoResult rv = MojoQueryQuota(message_pipe_.get().value(),
-                                   MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT,
-                                   nullptr, &limit, &usage);
-    if (rv == MOJO_RESULT_OK && usage > max_unread_message_quota_used_) {
-      MaybeDumpWithoutCrashing(usage);
-      max_unread_message_quota_used_ = usage;
-    }
-  }
+
+  if (quota_checker_)
+    quota_checker_->BeforeWrite();
 
   MojoResult rv =
       WriteMessageNew(message_pipe_.get(), message->TakeMojoMessage(),
@@ -458,6 +383,14 @@
   }
 }
 
+void Connector::SetMessageQuotaChecker(
+    scoped_refptr<internal::MessageQuotaChecker> checker) {
+  DCHECK(checker && !quota_checker_);
+
+  quota_checker_ = std::move(checker);
+  quota_checker_->SetMessagePipe(message_pipe_.get());
+}
+
 // static
 void Connector::OverrideDefaultSerializationBehaviorForTesting(
     OutgoingSerializationMode outgoing_mode,
diff --git a/mojo/public/cpp/bindings/lib/message_quota_checker.cc b/mojo/public/cpp/bindings/lib/message_quota_checker.cc
new file mode 100644
index 0000000..f276506
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_quota_checker.cc
@@ -0,0 +1,192 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
+
+#include <algorithm>
+
+#include "base/debug/alias.h"
+#include "base/debug/dump_without_crashing.h"
+#include "base/logging.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/no_destructor.h"
+#include "base/rand_util.h"
+#include "base/synchronization/lock.h"
+#include "mojo/public/c/system/quota.h"
+#include "mojo/public/cpp/bindings/features.h"
+#include "mojo/public/cpp/bindings/mojo_buildflags.h"
+
+namespace mojo {
+namespace internal {
+namespace {
+
+const base::FeatureParam<int> kMojoRecordUnreadMessageCountSampleRate = {
+    &features::kMojoRecordUnreadMessageCount, "SampleRate",
+    100  // Sample 1% of Connectors by default. */
+};
+
+const base::FeatureParam<int> kMojoRecordUnreadMessageCountQuotaValue = {
+    &features::kMojoRecordUnreadMessageCount, "QuotaValue",
+    100  // Use a 100 message quote by default.
+};
+
+const base::FeatureParam<int> kMojoRecordUnreadMessageCountCrashThreshold = {
+    &features::kMojoRecordUnreadMessageCount, "CrashThreshold",
+    0  // Set to zero to disable crash dumps by default.
+};
+
+NOINLINE void MaybeDumpWithoutCrashing(size_t quota_used) {
+  static bool have_crashed = false;
+  if (have_crashed)
+    return;
+
+  // Only crash once per process/per run. Note that this is slightly racy
+  // against concurrent quota overruns on multiple threads, but that's fine.
+  have_crashed = true;
+
+  // This is happening because the user of the interface implicated on the crash
+  // stack has queued up an unreasonable number of messages, namely
+  // |quota_used|.
+  base::debug::DumpWithoutCrashing();
+
+  base::debug::Alias(&quota_used);
+}
+
+}  // namespace
+
+// static
+scoped_refptr<MessageQuotaChecker> MessageQuotaChecker::MaybeCreate() {
+  static const Configuration config = GetConfiguration();
+  return MaybeCreateImpl(config);
+}
+
+void MessageQuotaChecker::BeforeWrite() {
+  QuotaCheckImpl(0u);
+}
+
+void MessageQuotaChecker::BeforeMessagesEnqueued(size_t num) {
+  DCHECK_NE(num, 0u);
+  QuotaCheckImpl(num);
+}
+
+void MessageQuotaChecker::AfterMessagesDequeued(size_t num) {
+  base::AutoLock hold(lock_);
+  DCHECK_LE(num, consumed_quota_);
+  DCHECK_NE(num, 0u);
+
+  consumed_quota_ -= num;
+}
+
+size_t MessageQuotaChecker::GetMaxQuotaUsage() {
+  base::AutoLock hold(lock_);
+  return max_consumed_quota_;
+}
+
+void MessageQuotaChecker::SetMessagePipe(MessagePipeHandle message_pipe) {
+  base::AutoLock hold(lock_);
+  message_pipe_ = message_pipe;
+  if (!message_pipe_)
+    return;
+
+  MojoResult rv =
+      MojoSetQuota(message_pipe.value(), MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT,
+                   config_->unread_message_count_quota, nullptr);
+  DCHECK_EQ(MOJO_RESULT_OK, rv);
+}
+
+size_t MessageQuotaChecker::GetCurrentQuotaStatusForTesting() {
+  base::AutoLock hold(lock_);
+  return GetCurrentQuotaStatus();
+}
+
+// static
+MessageQuotaChecker::Configuration
+MessageQuotaChecker::GetConfigurationForTesting() {
+  return GetConfiguration();
+}
+
+// static
+scoped_refptr<MessageQuotaChecker> MessageQuotaChecker::MaybeCreateForTesting(
+    const Configuration& config) {
+  return MaybeCreateImpl(config);
+}
+
+MessageQuotaChecker::MessageQuotaChecker(const Configuration* config)
+    : config_(config) {}
+MessageQuotaChecker::~MessageQuotaChecker() = default;
+
+// static
+MessageQuotaChecker::Configuration MessageQuotaChecker::GetConfiguration() {
+  Configuration ret;
+
+  ret.is_enabled =
+      base::FeatureList::IsEnabled(features::kMojoRecordUnreadMessageCount);
+  ret.sample_rate = kMojoRecordUnreadMessageCountSampleRate.Get();
+
+  // Lower-bound the quota value to 100, which implies roughly 2% message
+  // overhead for sampled pipes.
+  constexpr int kMinQuotaValue = 100;
+  ret.unread_message_count_quota =
+      std::max(kMinQuotaValue, kMojoRecordUnreadMessageCountQuotaValue.Get());
+  ret.crash_threshold = kMojoRecordUnreadMessageCountCrashThreshold.Get();
+  ret.maybe_crash_function = &MaybeDumpWithoutCrashing;
+  return ret;
+}
+
+// static
+scoped_refptr<MessageQuotaChecker> MessageQuotaChecker::MaybeCreateImpl(
+    const Configuration& config) {
+  if (!config.is_enabled)
+    return nullptr;
+
+  if (base::RandInt(0, config.sample_rate - 1) != 0)
+    return nullptr;
+
+  return new MessageQuotaChecker(&config);
+}
+
+size_t MessageQuotaChecker::GetCurrentQuotaStatus() {
+  lock_.AssertAcquired();
+
+  size_t quota_status = consumed_quota_;
+  if (message_pipe_) {
+    uint64_t limit = 0;
+    uint64_t usage = 0;
+    MojoResult rv = MojoQueryQuota(message_pipe_.value(),
+                                   MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT,
+                                   nullptr, &limit, &usage);
+    if (rv == MOJO_RESULT_OK)
+      quota_status += usage;
+  }
+
+  return quota_status;
+}
+
+void MessageQuotaChecker::QuotaCheckImpl(size_t num_enqueued) {
+  bool new_max = false;
+  size_t quota_used = 0u;
+  {
+    base::AutoLock hold(lock_);
+
+    consumed_quota_ += num_enqueued;
+    quota_used = GetCurrentQuotaStatus();
+
+    // Account for the message that will be written.
+    if (!num_enqueued)
+      ++quota_used;
+
+    if (quota_used > max_consumed_quota_) {
+      max_consumed_quota_ = quota_used;
+      new_max = true;
+    }
+  }
+
+  if (new_max && config_->crash_threshold != 0 &&
+      quota_used >= config_->crash_threshold) {
+    config_->maybe_crash_function(quota_used);
+  }
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_quota_checker.h b/mojo/public/cpp/bindings/lib/message_quota_checker.h
new file mode 100644
index 0000000..499aeed
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_quota_checker.h
@@ -0,0 +1,103 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_QUOTA_CHECKER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_QUOTA_CHECKER_H_
+
+#include <stdint.h>
+
+#include "base/component_export.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+
+namespace mojo {
+namespace internal {
+
+// This class keeps track of how many messages are in-flight for a message pipe,
+// including messages that are posted or locally queued.
+//
+// Message pipe owners may have reason to implement their own mechanism for
+// queuing outgoing messages before writing them to a pipe. This class helps
+// with unread message quota monitoring in such cases, since Mojo's own
+// quota monitoring on the pipe cannot account for such external queues.
+// Callers are responsible for invoking  |BeforeMessagesEnqueued()| and
+// |AfterMessagesDequeued()| when making respective changes to their local
+// outgoing queue. Additionally, |BeforeWrite()| should be called immediately
+// before writing each message to the corresponding message pipe.
+//
+// Also note that messages posted to a different sequence with
+// |base::PostTask()| and the like, need to be treated as locally queued. Task
+// queues can grow arbitrarily long, and it's ideal to perform unread quota
+// checks before posting.
+//
+// Either |BeforeMessagesEnqueued()| or |BeforeWrite()| may cause the quota
+// to be exceeded, thus invoking the |maybe_crash_function| set in this
+// object's Configuration.
+class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) MessageQuotaChecker
+    : public base::RefCountedThreadSafe<MessageQuotaChecker> {
+ public:
+  // Returns a new instance if this invocation has been sampled for quota
+  // checking.
+  static scoped_refptr<MessageQuotaChecker> MaybeCreate();
+
+  // Call before writing a message to |message_pipe_|.
+  void BeforeWrite();
+
+  // Call before queueing |num| messages.
+  void BeforeMessagesEnqueued(size_t num);
+  // Call after de-queueing |num| messages.
+  void AfterMessagesDequeued(size_t num);
+
+  // Returns the high watermark of quota usage observed by this instance.
+  size_t GetMaxQuotaUsage();
+
+  // Set or unset the message pipe associated with this quota checker.
+  void SetMessagePipe(MessagePipeHandle message_pipe);
+
+  // Test support.
+  size_t GetCurrentQuotaStatusForTesting();
+  struct Configuration;
+  static Configuration GetConfigurationForTesting();
+  static scoped_refptr<MessageQuotaChecker> MaybeCreateForTesting(
+      const Configuration& config);
+
+ private:
+  friend class base::RefCountedThreadSafe<MessageQuotaChecker>;
+  explicit MessageQuotaChecker(const Configuration* config);
+  ~MessageQuotaChecker();
+  static Configuration GetConfiguration();
+  static scoped_refptr<MessageQuotaChecker> MaybeCreateImpl(
+      const Configuration& config);
+
+  size_t GetCurrentQuotaStatus();
+  void QuotaCheckImpl(size_t num_enqueued);
+
+  const Configuration* config_;
+
+  // Locks all local state.
+  base::Lock lock_;
+  // The locally consumed quota, e.g. the difference between the counts passed
+  // to |BeforeMessagesEnqueued()| and |BeforeMessagesDequeued()|.
+  size_t consumed_quota_ = 0u;
+  // The high watermark consumed quota observed.
+  size_t max_consumed_quota_ = 0u;
+  // The quota level that triggers a crash dump, or zero to disable crashing.
+  size_t crash_threshold_ = 0u;
+  // The message pipe this instance observes, if any.
+  MessagePipeHandle message_pipe_;
+};
+
+struct MessageQuotaChecker::Configuration {
+  bool is_enabled = false;
+  size_t sample_rate = 0u;
+  size_t unread_message_count_quota = 0u;
+  size_t crash_threshold = 0u;
+  void (*maybe_crash_function)(size_t quota_used);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_QUOTA_CHECKER_H_
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.cc b/mojo/public/cpp/bindings/lib/multiplex_router.cc
index c87cacc..7420cade 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.cc
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.cc
@@ -18,6 +18,7 @@
 #include "mojo/public/cpp/bindings/interface_endpoint_client.h"
 #include "mojo/public/cpp/bindings/interface_endpoint_controller.h"
 #include "mojo/public/cpp/bindings/lib/may_auto_lock.h"
+#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
 #include "mojo/public/cpp/bindings/sequence_local_sync_event_watcher.h"
 
 namespace mojo {
@@ -313,9 +314,9 @@
 MultiplexRouter::MultiplexRouter(
     ScopedMessagePipeHandle message_pipe,
     Config config,
-    bool set_interface_id_namesapce_bit,
+    bool set_interface_id_namespace_bit,
     scoped_refptr<base::SequencedTaskRunner> runner)
-    : set_interface_id_namespace_bit_(set_interface_id_namesapce_bit),
+    : set_interface_id_namespace_bit_(set_interface_id_namespace_bit),
       task_runner_(runner),
       dispatcher_(this),
       connector_(std::move(message_pipe),
@@ -342,6 +343,11 @@
       base::BindOnce(&MultiplexRouter::OnPipeConnectionError,
                      base::Unretained(this), false /* force_async_dispatch */));
 
+  scoped_refptr<internal::MessageQuotaChecker> quota_checker =
+      internal::MessageQuotaChecker::MaybeCreate();
+  if (quota_checker)
+    connector_.SetMessageQuotaChecker(std::move(quota_checker));
+
   std::unique_ptr<MessageHeaderValidator> header_validator =
       std::make_unique<MessageHeaderValidator>();
   header_validator_ = header_validator.get();
diff --git a/mojo/public/cpp/bindings/tests/BUILD.gn b/mojo/public/cpp/bindings/tests/BUILD.gn
index 4c2588b..34203da 100644
--- a/mojo/public/cpp/bindings/tests/BUILD.gn
+++ b/mojo/public/cpp/bindings/tests/BUILD.gn
@@ -29,6 +29,7 @@
     "map_unittest.cc",
     "message_queue.cc",
     "message_queue.h",
+    "message_quota_checker_unittest.cc",
     "message_unittest.cc",
     "multiplex_router_unittest.cc",
     "native_struct_unittest.cc",
diff --git a/mojo/public/cpp/bindings/tests/message_quota_checker_unittest.cc b/mojo/public/cpp/bindings/tests/message_quota_checker_unittest.cc
new file mode 100644
index 0000000..fd68005
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/message_quota_checker_unittest.cc
@@ -0,0 +1,189 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "mojo/public/c/system/quota.h"
+#include "mojo/public/cpp/bindings/features.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+class MessageQuotaCheckerTest : public testing::Test {
+ public:
+  MessageQuotaCheckerTest() {
+    EXPECT_EQ(nullptr, instance_);
+    instance_ = this;
+  }
+  ~MessageQuotaCheckerTest() override {
+    EXPECT_EQ(this, instance_);
+    instance_ = nullptr;
+  }
+
+ protected:
+  using MessageQuotaChecker = internal::MessageQuotaChecker;
+  using Configuration = MessageQuotaChecker::Configuration;
+
+  static void RecordDumpAttempt(size_t quota_used) {
+    ++instance_->num_dumps_;
+    instance_->last_dump_quota_used_ = quota_used;
+  }
+
+  size_t num_dumps_ = false;
+  size_t last_dump_quota_used_ = 0u;
+
+  static const Configuration enabled_config_;
+
+  static MessageQuotaCheckerTest* instance_;
+};
+
+const MessageQuotaCheckerTest::Configuration
+    MessageQuotaCheckerTest::enabled_config_ = {true, 1, 100, 200,
+                                                &RecordDumpAttempt};
+MessageQuotaCheckerTest* MessageQuotaCheckerTest::instance_ = nullptr;
+
+TEST_F(MessageQuotaCheckerTest, ReadsConfigurationFromFeatures) {
+  base::FieldTrialParams params;
+  params["SampleRate"] = "19";
+  // Quota value parameter below the minimum the checker will allow.
+  params["QuotaValue"] = "57";
+  params["CrashThreshold"] = "225";
+
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeatureWithParameters(
+      features::kMojoRecordUnreadMessageCount, params);
+
+  // Validate that the configuration reads from the feature configuration.
+  const MessageQuotaChecker::Configuration config =
+      MessageQuotaChecker::GetConfigurationForTesting();
+
+  EXPECT_TRUE(config.is_enabled);
+  EXPECT_EQ(19u, config.sample_rate);
+  EXPECT_EQ(100u, config.unread_message_count_quota);
+  EXPECT_EQ(225u, config.crash_threshold);
+  EXPECT_NE(nullptr, config.maybe_crash_function);
+}
+
+TEST_F(MessageQuotaCheckerTest, DisabledByDefault) {
+  const MessageQuotaChecker::Configuration config =
+      MessageQuotaChecker::GetConfigurationForTesting();
+  EXPECT_FALSE(config.is_enabled);
+
+  // Validate that no MessageQuoteCheckers are created in the default feature
+  // configuration. Run a bunch of iterations, as this function returns an
+  // instance randomly.
+  for (size_t i = 0; i < 1000; ++i)
+    ASSERT_EQ(nullptr, MessageQuotaChecker::MaybeCreate());
+}
+
+TEST_F(MessageQuotaCheckerTest, CreatesWhenEnabled) {
+  // Run a bunch of iterations, as this function returns an instance randomly.
+  for (size_t i = 0; i < 1000; ++i)
+    EXPECT_NE(nullptr,
+              MessageQuotaChecker::MaybeCreateForTesting(enabled_config_));
+}
+
+TEST_F(MessageQuotaCheckerTest, CountsRight) {
+  scoped_refptr<MessageQuotaChecker> checker =
+      MessageQuotaChecker::MaybeCreateForTesting(enabled_config_);
+
+  ASSERT_EQ(0u, checker->GetCurrentQuotaStatusForTesting());
+  ASSERT_EQ(0u, checker->GetMaxQuotaUsage());
+
+  checker->BeforeMessagesEnqueued(10);
+  ASSERT_EQ(10u, checker->GetCurrentQuotaStatusForTesting());
+  ASSERT_EQ(10u, checker->GetMaxQuotaUsage());
+
+  checker->AfterMessagesDequeued(5);
+  ASSERT_EQ(5u, checker->GetCurrentQuotaStatusForTesting());
+  ASSERT_EQ(10u, checker->GetMaxQuotaUsage());
+
+  ASSERT_EQ(0u, num_dumps_);
+}
+
+TEST_F(MessageQuotaCheckerTest, CountsMessagePipeAlso) {
+  MessagePipe pipe;
+  scoped_refptr<MessageQuotaChecker> checker =
+      MessageQuotaChecker::MaybeCreateForTesting(enabled_config_);
+
+  uint64_t limit = 0;
+  uint64_t usage = 0;
+  MojoResult rv = MojoQueryQuota(pipe.handle0.get().value(),
+                                 MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT, nullptr,
+                                 &limit, &usage);
+  ASSERT_EQ(MOJO_RESULT_OK, rv);
+  ASSERT_EQ(MOJO_QUOTA_LIMIT_NONE, limit);
+
+  checker->SetMessagePipe(pipe.handle0.get());
+
+  // Validate that the checker sets an unread message quota on the pipe, and
+  // that it clamps to the minimum of 100.
+  rv = MojoQueryQuota(pipe.handle0.get().value(),
+                      MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT, nullptr, &limit,
+                      &usage);
+  ASSERT_EQ(MOJO_RESULT_OK, rv);
+  ASSERT_EQ(100u, limit);
+
+  ASSERT_EQ(0u, checker->GetCurrentQuotaStatusForTesting());
+
+  const char kMessage[] = "hello";
+  for (size_t i = 0; i < 10; ++i) {
+    checker->BeforeWrite();
+    ASSERT_EQ(MOJO_RESULT_OK,
+              WriteMessageRaw(pipe.handle0.get(), kMessage, sizeof(kMessage),
+                              nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE));
+  }
+
+  ASSERT_EQ(10u, checker->GetMaxQuotaUsage());
+  ASSERT_EQ(10u, checker->GetCurrentQuotaStatusForTesting());
+
+  checker->BeforeMessagesEnqueued(10);
+  ASSERT_EQ(20u, checker->GetMaxQuotaUsage());
+  ASSERT_EQ(20u, checker->GetCurrentQuotaStatusForTesting());
+
+  ASSERT_EQ(0u, num_dumps_);
+}
+
+TEST_F(MessageQuotaCheckerTest, DumpsCoreOnOverrun) {
+  MessagePipe pipe;
+  scoped_refptr<MessageQuotaChecker> checker =
+      MessageQuotaChecker::MaybeCreateForTesting(enabled_config_);
+
+  // Queue up 100 messages.
+  checker->SetMessagePipe(pipe.handle0.get());
+  const char kMessage[] = "hello";
+  for (size_t i = 0; i < 100; ++i) {
+    checker->BeforeWrite();
+    ASSERT_EQ(MOJO_RESULT_OK,
+              WriteMessageRaw(pipe.handle0.get(), kMessage, sizeof(kMessage),
+                              nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE));
+  }
+
+  // The crash threshold is at 200 per the config, so shouldn't have attempted
+  // a core dump yet.
+  ASSERT_EQ(0u, num_dumps_);
+
+  checker->BeforeMessagesEnqueued(50);
+  ASSERT_EQ(0u, num_dumps_);
+
+  checker->BeforeMessagesEnqueued(50);
+  ASSERT_EQ(1u, num_dumps_);
+  ASSERT_EQ(200u, last_dump_quota_used_);
+
+  checker->BeforeWrite();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WriteMessageRaw(pipe.handle0.get(), kMessage, sizeof(kMessage),
+                            nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  ASSERT_EQ(2u, num_dumps_);
+  ASSERT_EQ(201u, last_dump_quota_used_);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/net/http/http_auth_controller.cc b/net/http/http_auth_controller.cc
index 0e11a5d..1ef08d8 100644
--- a/net/http/http_auth_controller.cc
+++ b/net/http/http_auth_controller.cc
@@ -142,15 +142,13 @@
     const NetworkIsolationKey& network_isolation_key,
     HttpAuthCache* http_auth_cache,
     HttpAuthHandlerFactory* http_auth_handler_factory,
-    HostResolver* host_resolver,
-    HttpAuthPreferences::DefaultCredentials allow_default_credentials)
+    HostResolver* host_resolver)
     : target_(target),
       auth_url_(auth_url),
       auth_origin_(auth_url.GetOrigin()),
       auth_path_(auth_url.path()),
       network_isolation_key_(network_isolation_key),
       embedded_identity_used_(false),
-      allow_default_credentials_(allow_default_credentials),
       default_credentials_used_(false),
       http_auth_cache_(http_auth_cache),
       http_auth_handler_factory_(http_auth_handler_factory),
@@ -539,12 +537,7 @@
   // infinite loop. We use default credentials after checking the auth cache so
   // that if single sign-on doesn't work, we won't try default credentials for
   // future transactions.
-  if (!default_credentials_used_ && handler_->AllowsDefaultCredentials() &&
-      // TODO(https://crbug.com/458508): Refactor |AllowsDefaultCredentials|
-      // to internally process |allow_default_credentials_| once it is
-      // passed along with the other |HttpAuthPreferences|.
-      allow_default_credentials_ ==
-          HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS) {
+  if (!default_credentials_used_ && handler_->AllowsDefaultCredentials()) {
     identity_.source = HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS;
     identity_.invalid = false;
     default_credentials_used_ = true;
diff --git a/net/http/http_auth_controller.h b/net/http/http_auth_controller.h
index 27a78f2..3a47c430 100644
--- a/net/http/http_auth_controller.h
+++ b/net/http/http_auth_controller.h
@@ -83,14 +83,12 @@
   //
   // * |allow_default_credentials| is used for determining if the current
   //       context allows ambient authentication using default credentials.
-  HttpAuthController(
-      HttpAuth::Target target,
-      const GURL& auth_url,
-      const NetworkIsolationKey& network_isolation_key,
-      HttpAuthCache* http_auth_cache,
-      HttpAuthHandlerFactory* http_auth_handler_factory,
-      HostResolver* host_resolver,
-      HttpAuthPreferences::DefaultCredentials allow_default_credentials);
+  HttpAuthController(HttpAuth::Target target,
+                     const GURL& auth_url,
+                     const NetworkIsolationKey& network_isolation_key,
+                     HttpAuthCache* http_auth_cache,
+                     HttpAuthHandlerFactory* http_auth_handler_factory,
+                     HostResolver* host_resolver);
 
   // Generate an authentication token for |target| if necessary. The return
   // value is a net error code. |OK| will be returned both in the case that
@@ -228,13 +226,6 @@
   // preventing an infinite auth restart loop.
   bool embedded_identity_used_;
 
-  // If the current context allows ambient authentication using default
-  // credentials.
-  // TODO(https://crbug.com/458508): Refactor |allow_default_credentials_|
-  // to be passed along with the other |HttpAuthPreferences|, rather then being
-  // passed directly to |HttpAuthController|.
-  HttpAuthPreferences::DefaultCredentials allow_default_credentials_;
-
   // True if default credentials have already been tried for this transaction
   // in response to an HTTP authentication challenge.
   bool default_credentials_used_;
diff --git a/net/http/http_auth_controller_unittest.cc b/net/http/http_auth_controller_unittest.cc
index d05f247..21df2ef 100644
--- a/net/http/http_auth_controller_unittest.cc
+++ b/net/http/http_auth_controller_unittest.cc
@@ -80,7 +80,7 @@
       base::MakeRefCounted<HttpAuthController>(
           HttpAuth::AUTH_PROXY, GURL("http://example.com"),
           NetworkIsolationKey(), &dummy_auth_cache, &auth_handler_factory,
-          host_resolver.get(), HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS));
+          host_resolver.get()));
   SSLInfo null_ssl_info;
   ASSERT_EQ(OK, controller->HandleAuthChallenge(headers, null_ssl_info, false,
                                                 false, net_log));
@@ -265,7 +265,7 @@
       base::MakeRefCounted<HttpAuthController>(
           HttpAuth::AUTH_SERVER, GURL("http://example.com"),
           NetworkIsolationKey(), &dummy_auth_cache, &auth_handler_factory,
-          host_resolver.get(), HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS));
+          host_resolver.get()));
   SSLInfo null_ssl_info;
   ASSERT_EQ(OK, controller->HandleAuthChallenge(headers, null_ssl_info, false,
                                                 false, dummy_log));
diff --git a/net/http/http_auth_preferences.cc b/net/http/http_auth_preferences.cc
index 15ab440..c0e4539 100644
--- a/net/http/http_auth_preferences.cc
+++ b/net/http/http_auth_preferences.cc
@@ -47,7 +47,8 @@
 
 bool HttpAuthPreferences::CanUseDefaultCredentials(
     const GURL& auth_origin) const {
-  return security_manager_->CanUseDefaultCredentials(auth_origin);
+  return allow_default_credentials_ == ALLOW_DEFAULT_CREDENTIALS &&
+         security_manager_->CanUseDefaultCredentials(auth_origin);
 }
 
 using DelegationType = HttpAuth::DelegationType;
@@ -63,6 +64,10 @@
   return DelegationType::kUnconstrained;
 }
 
+void HttpAuthPreferences::SetAllowDefaultCredentials(DefaultCredentials creds) {
+  allow_default_credentials_ = creds;
+}
+
 void HttpAuthPreferences::SetServerAllowlist(
     const std::string& server_allowlist) {
   std::unique_ptr<HttpAuthFilter> allowlist;
diff --git a/net/http/http_auth_preferences.h b/net/http/http_auth_preferences.h
index 8045bdd6..f59c36aa 100644
--- a/net/http/http_auth_preferences.h
+++ b/net/http/http_auth_preferences.h
@@ -79,6 +79,8 @@
 
   void SetDelegateAllowlist(const std::string& delegate_allowlist);
 
+  void SetAllowDefaultCredentials(DefaultCredentials creds);
+
 #if defined(OS_ANDROID)
   void set_auth_android_negotiate_account_type(
       const std::string& account_type) {
@@ -91,6 +93,8 @@
   bool negotiate_disable_cname_lookup_ = false;
   bool negotiate_enable_port_ = false;
 
+  DefaultCredentials allow_default_credentials_ = ALLOW_DEFAULT_CREDENTIALS;
+
 #if defined(OS_POSIX) || defined(OS_FUCHSIA)
   bool ntlm_v2_enabled_ = true;
 #endif
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index e40d233..bd3d44a2 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -93,8 +93,6 @@
       enable_quic(false),
       enable_quic_proxies_for_https_urls(false),
       disable_idle_sockets_close_on_memory_pressure(false),
-      allow_default_credentials(HttpAuthPreferences::DefaultCredentials::
-                                    DISALLOW_DEFAULT_CREDENTIALS),
       key_auth_cache_server_entries_by_network_isolation_key(false) {
   enable_early_data =
       base::FeatureList::IsEnabled(features::kEnableTLS13EarlyData);
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index 64039ac..e36e169b 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -27,7 +27,6 @@
 #include "net/base/host_port_pair.h"
 #include "net/base/net_export.h"
 #include "net/http/http_auth_cache.h"
-#include "net/http/http_auth_preferences.h"
 #include "net/http/http_stream_factory.h"
 #include "net/net_buildflags.h"
 #include "net/quic/quic_stream_factory.h"
@@ -150,10 +149,6 @@
     // If true, idle sockets won't be closed when memory pressure happens.
     bool disable_idle_sockets_close_on_memory_pressure;
 
-    // If authentication APIs that support ambient authentication are allowed
-    // to use the default credentials.
-    HttpAuthPreferences::DefaultCredentials allow_default_credentials;
-
     bool key_auth_cache_server_entries_by_network_isolation_key;
   };
 
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index dff329b..01f673874 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -886,8 +886,7 @@
     auth_controllers_[target] = base::MakeRefCounted<HttpAuthController>(
         target, AuthURL(target), request_->network_isolation_key,
         session_->http_auth_cache(), session_->http_auth_handler_factory(),
-        session_->host_resolver(),
-        session_->params().allow_default_credentials);
+        session_->host_resolver());
   return auth_controllers_[target]->MaybeGenerateAuthToken(request_,
                                                            io_callback_,
                                                            net_log_);
@@ -907,8 +906,7 @@
     auth_controllers_[target] = base::MakeRefCounted<HttpAuthController>(
         target, AuthURL(target), request_->network_isolation_key,
         session_->http_auth_cache(), session_->http_auth_handler_factory(),
-        session_->host_resolver(),
-        session_->params().allow_default_credentials);
+        session_->host_resolver());
     if (request_->load_flags & LOAD_DO_NOT_USE_EMBEDDED_IDENTITY)
       auth_controllers_[target]->DisableEmbeddedIdentity();
   }
diff --git a/net/http/http_proxy_client_socket_fuzzer.cc b/net/http/http_proxy_client_socket_fuzzer.cc
index 2b416158..9221e49 100644
--- a/net/http/http_proxy_client_socket_fuzzer.cc
+++ b/net/http/http_proxy_client_socket_fuzzer.cc
@@ -23,7 +23,6 @@
 #include "net/http/http_auth_handler_basic.h"
 #include "net/http/http_auth_handler_digest.h"
 #include "net/http/http_auth_handler_factory.h"
-#include "net/http/http_auth_preferences.h"
 #include "net/http/http_auth_scheme.h"
 #include "net/log/test_net_log.h"
 #include "net/socket/fuzzed_socket.h"
@@ -60,7 +59,7 @@
       base::MakeRefCounted<net::HttpAuthController>(
           net::HttpAuth::AUTH_PROXY, GURL("http://proxy:42/"),
           net::NetworkIsolationKey(), &auth_cache, &auth_handler_factory,
-          nullptr, net::HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS));
+          nullptr));
   // Determine if the HttpProxyClientSocket should be told the underlying socket
   // is HTTPS.
   net::HttpProxyClientSocket socket(
diff --git a/net/http/http_proxy_connect_job.cc b/net/http/http_proxy_connect_job.cc
index 13efec42..232484d 100644
--- a/net/http/http_proxy_connect_job.cc
+++ b/net/http/http_proxy_connect_job.cc
@@ -188,8 +188,7 @@
                     params_->network_isolation_key(),
                     common_connect_job_params->http_auth_cache,
                     common_connect_job_params->http_auth_handler_factory,
-                    host_resolver(),
-                    HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS)
+                    host_resolver())
               : nullptr) {}
 
 HttpProxyConnectJob::~HttpProxyConnectJob() {}
diff --git a/net/quic/quic_proxy_client_socket_unittest.cc b/net/quic/quic_proxy_client_socket_unittest.cc
index 8681f1f..576602c2 100644
--- a/net/quic/quic_proxy_client_socket_unittest.cc
+++ b/net/quic/quic_proxy_client_socket_unittest.cc
@@ -294,12 +294,11 @@
     sock_.reset(new QuicProxyClientSocket(
         std::move(stream_handle), std::move(session_handle_), user_agent_,
         endpoint_host_port_, net_log_.bound(),
-        new HttpAuthController(
-            HttpAuth::AUTH_PROXY,
-            GURL("https://" + proxy_host_port_.ToString()),
-            NetworkIsolationKey(), &http_auth_cache_,
-            http_auth_handler_factory_.get(), host_resolver_.get(),
-            HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS)));
+        new HttpAuthController(HttpAuth::AUTH_PROXY,
+                               GURL("https://" + proxy_host_port_.ToString()),
+                               NetworkIsolationKey(), &http_auth_cache_,
+                               http_auth_handler_factory_.get(),
+                               host_resolver_.get())));
 
     session_->StartReading();
   }
diff --git a/net/spdy/spdy_proxy_client_socket_unittest.cc b/net/spdy/spdy_proxy_client_socket_unittest.cc
index abe16d4..d63dbcc 100644
--- a/net/spdy/spdy_proxy_client_socket_unittest.cc
+++ b/net/spdy/spdy_proxy_client_socket_unittest.cc
@@ -17,7 +17,6 @@
 #include "net/base/test_completion_callback.h"
 #include "net/base/winsock_init.h"
 #include "net/dns/mock_host_resolver.h"
-#include "net/http/http_auth_preferences.h"
 #include "net/http/http_proxy_connect_job.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_response_info.h"
@@ -270,8 +269,7 @@
       new HttpAuthController(
           HttpAuth::AUTH_PROXY, GURL("https://" + proxy_host_port_.ToString()),
           NetworkIsolationKey(), session_->http_auth_cache(),
-          session_->http_auth_handler_factory(), session_->host_resolver(),
-          HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS));
+          session_->http_auth_handler_factory(), session_->host_resolver()));
 }
 
 scoped_refptr<IOBufferWithSize> SpdyProxyClientSocketTest::CreateBuffer(
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc
index 791b27c1..41b6396 100644
--- a/net/spdy/spdy_test_util_common.cc
+++ b/net/spdy/spdy_test_util_common.cc
@@ -344,7 +344,6 @@
       net_log(nullptr),
       disable_idle_sockets_close_on_memory_pressure(false),
       enable_early_data(false),
-      allow_default_credentials(HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS),
       key_auth_cache_server_entries_by_network_isolation_key(false) {
   http2_settings[spdy::SETTINGS_INITIAL_WINDOW_SIZE] =
       kDefaultInitialWindowSize;
@@ -399,7 +398,6 @@
   params.disable_idle_sockets_close_on_memory_pressure =
       session_deps->disable_idle_sockets_close_on_memory_pressure;
   params.enable_early_data = session_deps->enable_early_data;
-  params.allow_default_credentials = session_deps->allow_default_credentials;
   params.key_auth_cache_server_entries_by_network_isolation_key =
       session_deps->key_auth_cache_server_entries_by_network_isolation_key;
   return params;
diff --git a/net/spdy/spdy_test_util_common.h b/net/spdy/spdy_test_util_common.h
index 18c10087..514ea3b2 100644
--- a/net/spdy/spdy_test_util_common.h
+++ b/net/spdy/spdy_test_util_common.h
@@ -25,7 +25,6 @@
 #include "net/cert/cert_verifier.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/http/http_auth_handler_factory.h"
-#include "net/http/http_auth_preferences.h"
 #include "net/http/http_network_session.h"
 #include "net/http/http_response_info.h"
 #include "net/http/http_server_properties.h"
@@ -237,7 +236,6 @@
   NetLog* net_log;
   bool disable_idle_sockets_close_on_memory_pressure;
   bool enable_early_data;
-  HttpAuthPreferences::DefaultCredentials allow_default_credentials;
   bool key_auth_cache_server_entries_by_network_isolation_key;
 };
 
diff --git a/printing/backend/cups_connection.cc b/printing/backend/cups_connection.cc
index caa244c..79dd875 100644
--- a/printing/backend/cups_connection.cc
+++ b/printing/backend/cups_connection.cc
@@ -155,8 +155,8 @@
     temp_queues.emplace_back();
     QueueStatus* queue_status = &temp_queues.back();
 
-    if (!GetPrinterStatus(cups_http_.get(), id,
-                          &queue_status->printer_status)) {
+    if (!printing::GetPrinterStatus(cups_http_.get(), id,
+                                    &queue_status->printer_status)) {
       LOG(WARNING) << "Could not retrieve printer status for " << id;
       return false;
     }
@@ -178,6 +178,16 @@
   return true;
 }
 
+bool CupsConnection::GetPrinterStatus(const std::string& printer_id,
+                                      PrinterStatus* printer_status) {
+  if (!Connect()) {
+    LOG(ERROR) << "Could not establish connection to CUPS";
+    return false;
+  }
+  return printing::GetPrinterStatus(cups_http_.get(), printer_id,
+                                    printer_status);
+}
+
 std::string CupsConnection::server_name() const {
   return print_server_url_.host();
 }
diff --git a/printing/backend/cups_connection.h b/printing/backend/cups_connection.h
index 3fcb995..6d7f766 100644
--- a/printing/backend/cups_connection.h
+++ b/printing/backend/cups_connection.h
@@ -55,6 +55,11 @@
   bool GetJobs(const std::vector<std::string>& printer_ids,
                std::vector<QueueStatus>* jobs);
 
+  // Queries CUPS for printer status for |printer_id|.
+  // Returns true if the query was successful.
+  bool GetPrinterStatus(const std::string& printer_id,
+                        PrinterStatus* printer_status);
+
   std::string server_name() const;
 
   int last_error() const;
diff --git a/services/device/geolocation/wifi_data_provider_mac.mm b/services/device/geolocation/wifi_data_provider_mac.mm
index 83cae22..585208f 100644
--- a/services/device/geolocation/wifi_data_provider_mac.mm
+++ b/services/device/geolocation/wifi_data_provider_mac.mm
@@ -21,7 +21,13 @@
 #include "services/device/geolocation/wifi_data_provider_common.h"
 #include "services/device/geolocation/wifi_data_provider_manager.h"
 
+#if !defined(MAC_OS_X_VERSION_10_15)
+// This API is so deprecated that this symbol is no longer present at all in the
+// 10.15 SDK. For the moment, hack this functionality out entirely when building
+// with the 10.15 SDK.
+// https://crbug.com/1022821
 extern "C" NSString* const kCWScanKeyMerge;
+#endif
 
 @interface CWInterface (Private)
 - (NSArray*)scanForNetworksWithParameters:(NSDictionary*)params
@@ -47,7 +53,11 @@
   @autoreleasepool {  // Initialize the scan parameters with scan key merging
                       // disabled, so we get
     // every AP listed in the scan without any SSID de-duping logic.
+#if defined(MAC_OS_X_VERSION_10_15)
+    NSDictionary* params = @{};
+#else
     NSDictionary* params = @{kCWScanKeyMerge : @NO};
+#endif
 
     NSSet* supported_interfaces = [CWInterface interfaceNames];
     NSUInteger interface_error_count = 0;
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 160a006b..ad25f45 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -366,6 +366,12 @@
 
   origin_policy_manager_ = std::make_unique<OriginPolicyManager>(this);
 
+  if (params_->http_auth_static_network_context_params) {
+    http_auth_merged_preferences_.SetAllowDefaultCredentials(
+        params_->http_auth_static_network_context_params
+            ->allow_default_credentials);
+  }
+
   InitializeCorsParams();
 }
 
@@ -1573,6 +1579,40 @@
     std::move(callback).Run(base::nullopt);
 }
 
+const net::HttpAuthPreferences* NetworkContext::GetHttpAuthPreferences() const
+    noexcept {
+  return &http_auth_merged_preferences_;
+}
+
+void NetworkContext::OnHttpAuthDynamicParamsChanged(
+    const mojom::HttpAuthDynamicParams*
+        http_auth_dynamic_network_service_params) {
+  http_auth_merged_preferences_.SetServerAllowlist(
+      http_auth_dynamic_network_service_params->server_allowlist);
+  http_auth_merged_preferences_.SetDelegateAllowlist(
+      http_auth_dynamic_network_service_params->delegate_allowlist);
+  http_auth_merged_preferences_.set_delegate_by_kdc_policy(
+      http_auth_dynamic_network_service_params->delegate_by_kdc_policy);
+  http_auth_merged_preferences_.set_negotiate_disable_cname_lookup(
+      http_auth_dynamic_network_service_params->negotiate_disable_cname_lookup);
+  http_auth_merged_preferences_.set_negotiate_enable_port(
+      http_auth_dynamic_network_service_params->enable_negotiate_port);
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+  http_auth_merged_preferences_.set_ntlm_v2_enabled(
+      http_auth_dynamic_network_service_params->ntlm_v2_enabled);
+#endif
+
+#if defined(OS_ANDROID)
+  http_auth_merged_preferences_.set_auth_android_negotiate_account_type(
+      http_auth_dynamic_network_service_params->android_negotiate_account_type);
+#endif
+
+#if defined(OS_CHROMEOS)
+  http_auth_merged_preferences_.set_allow_gssapi_library_load(
+      http_auth_dynamic_network_service_params->allow_gssapi_library_load);
+#endif
+}
+
 URLRequestContextOwner NetworkContext::MakeURLRequestContext() {
   URLRequestContextBuilderMojo builder;
   const base::CommandLine* command_line =
@@ -1861,8 +1901,6 @@
       *base::CommandLine::ForCurrentProcess(), is_quic_force_disabled,
       params_->quic_user_agent_id, &session_params);
 
-  session_params.allow_default_credentials = params_->allow_default_credentials;
-
   session_params.disable_idle_sockets_close_on_memory_pressure =
       params_->disable_idle_sockets_close_on_memory_pressure;
 
diff --git a/services/network/network_context.h b/services/network/network_context.h
index 47d8a9e1..aed41d8 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -33,6 +33,7 @@
 #include "net/cert/cert_verify_result.h"
 #include "net/dns/dns_config_overrides.h"
 #include "net/dns/host_resolver.h"
+#include "net/http/http_auth_preferences.h"
 #include "services/network/cors/preflight_controller.h"
 #include "services/network/http_cache_data_counter.h"
 #include "services/network/http_cache_data_remover.h"
@@ -448,6 +449,14 @@
 
   bool IsCorsEnabled() const { return cors_enabled_; }
 
+  // The http_auth_dynamic_params_ would be used to populate
+  // the |http_auth_merged_preferences| of the given NetworkContext.
+  void OnHttpAuthDynamicParamsChanged(
+      const mojom::HttpAuthDynamicParams*
+          http_auth_dynamic_network_service_params);
+
+  const net::HttpAuthPreferences* GetHttpAuthPreferences() const;
+
  private:
   URLRequestContextOwner MakeURLRequestContext();
 
@@ -649,6 +658,13 @@
 
   std::unique_ptr<OriginPolicyManager> origin_policy_manager_;
 
+  // Each network context holds its own HttpAuthPreferences.
+  // The dynamic preferences of |NetworkService| and the static
+  // preferences from |NetworkContext| would be merged to
+  // `http_auth_merged_preferences_` which would then be used to create
+  // HttpAuthHandle via |NetworkContext::CreateHttpAuthHandlerFactory|.
+  net::HttpAuthPreferences http_auth_merged_preferences_;
+
   DISALLOW_COPY_AND_ASSIGN(NetworkContext);
 };
 
diff --git a/services/network/network_service.cc b/services/network/network_service.cc
index 6a121f6..4e3373f 100644
--- a/services/network/network_service.cc
+++ b/services/network/network_service.cc
@@ -329,6 +329,15 @@
   network_contexts_.insert(network_context);
   if (quic_disabled_)
     network_context->DisableQuic();
+
+  // The params may already be present, so we propagate it
+  // to this new network_context. When params gets changed
+  // via ConfigureHttpAuthPrefs method, we propagate the change
+  // to all NetworkContexts in |network_contexts_|
+  if (http_auth_dynamic_network_service_params_) {
+    network_context->OnHttpAuthDynamicParamsChanged(
+        http_auth_dynamic_network_service_params_.get());
+  }
 }
 
 void NetworkService::DeregisterNetworkContext(NetworkContext* network_context) {
@@ -453,37 +462,24 @@
 
 void NetworkService::SetUpHttpAuth(
     mojom::HttpAuthStaticParamsPtr http_auth_static_params) {
-  DCHECK(!http_auth_static_params_);
-  http_auth_static_params_ = std::move(http_auth_static_params);
+  DCHECK(!http_auth_static_network_service_params_);
+  DCHECK(network_contexts_.empty());
+  http_auth_static_network_service_params_ = std::move(http_auth_static_params);
 }
 
 void NetworkService::ConfigureHttpAuthPrefs(
     mojom::HttpAuthDynamicParamsPtr http_auth_dynamic_params) {
-  http_auth_preferences_.SetServerAllowlist(
-      http_auth_dynamic_params->server_allowlist);
-  http_auth_preferences_.SetDelegateAllowlist(
-      http_auth_dynamic_params->delegate_allowlist);
-  http_auth_preferences_.set_delegate_by_kdc_policy(
-      http_auth_dynamic_params->delegate_by_kdc_policy);
-  http_auth_preferences_.set_negotiate_disable_cname_lookup(
-      http_auth_dynamic_params->negotiate_disable_cname_lookup);
-  http_auth_preferences_.set_negotiate_enable_port(
-      http_auth_dynamic_params->enable_negotiate_port);
+  // We need to store it as a member variable because the method
+  // NetworkService::RegisterNetworkContext(NetworkContext *network_context)
+  // uses it to populate the HttpAuthPreferences of the incoming network_context
+  // with the latest dynamic params of the NetworkService.
+  http_auth_dynamic_network_service_params_ =
+      std::move(http_auth_dynamic_params);
 
-#if defined(OS_POSIX) || defined(OS_FUCHSIA)
-  http_auth_preferences_.set_ntlm_v2_enabled(
-      http_auth_dynamic_params->ntlm_v2_enabled);
-#endif
-
-#if defined(OS_ANDROID)
-  http_auth_preferences_.set_auth_android_negotiate_account_type(
-      http_auth_dynamic_params->android_negotiate_account_type);
-#endif
-
-#if defined(OS_CHROMEOS)
-  http_auth_preferences_.set_allow_gssapi_library_load(
-      http_auth_dynamic_params->allow_gssapi_library_load);
-#endif
+  for (NetworkContext* network_context : network_contexts_) {
+    network_context->OnHttpAuthDynamicParamsChanged(
+        http_auth_dynamic_network_service_params_.get());
+  }
 }
 
 void NetworkService::SetRawHeadersAccess(
@@ -662,21 +658,22 @@
 
 std::unique_ptr<net::HttpAuthHandlerFactory>
 NetworkService::CreateHttpAuthHandlerFactory(NetworkContext* network_context) {
-  if (!http_auth_static_params_) {
+  if (!http_auth_static_network_service_params_) {
     return net::HttpAuthHandlerFactory::CreateDefault(
-        &http_auth_preferences_
+        network_context->GetHttpAuthPreferences()
 #if defined(OS_ANDROID) && BUILDFLAG(USE_KERBEROS)
-        ,
+            ,
         base::BindRepeating(&CreateAuthSystem, network_context)
 #endif
     );
   }
 
   return net::HttpAuthHandlerRegistryFactory::Create(
-      &http_auth_preferences_, http_auth_static_params_->supported_schemes
+      network_context->GetHttpAuthPreferences(),
+      http_auth_static_network_service_params_->supported_schemes
 #if BUILDFLAG(USE_EXTERNAL_GSSAPI)
       ,
-      http_auth_static_params_->gssapi_library_name
+      http_auth_static_network_service_params_->gssapi_library_name
 #endif
 #if defined(OS_ANDROID) && BUILDFLAG(USE_KERBEROS)
       ,
diff --git a/services/network/network_service.h b/services/network/network_service.h
index 3fe829b..356c83e 100644
--- a/services/network/network_service.h
+++ b/services/network/network_service.h
@@ -27,7 +27,6 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/dns/dns_config.h"
-#include "net/http/http_auth_preferences.h"
 #include "net/log/net_log.h"
 #include "net/log/trace_net_log_observer.h"
 #include "services/network/keepalive_statistics_recorder.h"
@@ -273,11 +272,14 @@
   std::unique_ptr<net::HostResolverManager> host_resolver_manager_;
   std::unique_ptr<net::HostResolver::Factory> host_resolver_factory_;
   std::unique_ptr<NetworkUsageAccumulator> network_usage_accumulator_;
-
-  net::HttpAuthPreferences http_auth_preferences_;
-  mojom::HttpAuthStaticParamsPtr http_auth_static_params_;
   std::unique_ptr<HttpAuthCacheCopier> http_auth_cache_copier_;
 
+  // Members that would store the http auth network_service related params.
+  // These Params are later used by NetworkContext to create
+  // HttpAuthPreferences.
+  mojom::HttpAuthDynamicParamsPtr http_auth_dynamic_network_service_params_;
+  mojom::HttpAuthStaticParamsPtr http_auth_static_network_service_params_;
+
   // NetworkContexts created by CreateNetworkContext(). They call into the
   // NetworkService when their connection is closed so that it can delete
   // them.  It will also delete them when the NetworkService itself is torn
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index 80b5b4d..f7c195f 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -182,6 +182,13 @@
       pending_receiver<TrustedHeaderClient> header_client);
 };
 
+struct HttpAuthStaticNetworkContextParams {
+  // Whether authentication APIs that support fallback to the default account
+  // on the system can be used without specifying an account.
+  DefaultCredentials allow_default_credentials
+     = DefaultCredentials.ALLOW_DEFAULT_CREDENTIALS;
+};
+
 // Parameters for constructing a network context.
 struct NetworkContextParams {
   // Name used by memory tools to identify the context.
@@ -269,11 +276,6 @@
   // If true, idle sockets won't be closed when memory pressure happens.
   bool disable_idle_sockets_close_on_memory_pressure = false;
 
-  // Whether authentication APIs that support fallback to the default account
-  // on the system can be used without specifying an account.
-  DefaultCredentials allow_default_credentials =
-      DefaultCredentials.ALLOW_DEFAULT_CREDENTIALS;
-
   // SSL configuration. |initial_proxy_config| is the initial SSL configuration
   // to use. If nullptr, uses the default configuration. Updated SSL
   // configurations can be passed in via |ssl_config_client_receiver|.
@@ -440,6 +442,11 @@
   // regardless. This field should not be used if the ENABLE_REPORTING build
   // flag is false.
   mojo_base.mojom.FilePath? reporting_and_nel_store_path;
+
+  // The params are network context specific which will be merged with
+  // the dynamic preference from NetworkService side, to populate
+  // HttpAuthPreference.
+  HttpAuthStaticNetworkContextParams? http_auth_static_network_context_params;
 };
 
 struct NetworkConditions {
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 86a92329..8b6a411 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -54,12 +54,15 @@
   }
 
   defines = [
-    "SK_GL",
     "SK_HAS_PNG_LIBRARY",
     "SK_HAS_WEBP_LIBRARY",
     "SK_USER_CONFIG_HEADER=\"../../skia/config/SkUserConfig.h\"",
   ]
 
+  if (skia_use_gl) {
+    defines += [ "SK_GL" ]
+  }
+
   if (!is_ios) {
     defines += [ "SK_HAS_JPEG_LIBRARY" ]
     if (enable_skia_wuffs_gif) {
@@ -569,6 +572,9 @@
     sources += skia_gpu_sources
     sources += skia_null_gpu_sources
     sources += skia_sksl_gpu_sources
+    if (skia_use_gl) {
+      sources += skia_gl_gpu_sources
+    }
     if (enable_vulkan) {
       sources += skia_vk_sources
       sources += [
diff --git a/skia/features.gni b/skia/features.gni
index f8ee32ea..2a94ac2 100644
--- a/skia/features.gni
+++ b/skia/features.gni
@@ -5,4 +5,5 @@
 declare_args() {
   # Enable experimental SkiaRenderer Dawn backend.
   skia_use_dawn = false
+  skia_use_gl = true
 }
diff --git a/testing/buildbot/generate_buildbot_json.py b/testing/buildbot/generate_buildbot_json.py
index 7a21bcd..0a4e0fd 100755
--- a/testing/buildbot/generate_buildbot_json.py
+++ b/testing/buildbot/generate_buildbot_json.py
@@ -1096,7 +1096,9 @@
             # These waterfalls have their bot configs in a different repo.
             # so we don't know about their bot names.
             continue  # pragma: no cover
-          if waterfall['name'] in ['client.devtools-frontend.integration']:
+          if waterfall['name'] in [
+              'client.devtools-frontend.integration',
+              'tryserver.devtools-frontend']:
             continue  # pragma: no cover
           raise self.unknown_bot(bot_name, waterfall['name'])
 
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index d3dc0195..aca4359 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -2124,9 +2124,9 @@
     "label": "//third_party/opus:opus_tests",
     "type": "console_test_launcher",
   },
-  "ocmock_support_unittests": {
+  "ios_testing_unittests": {
     "args": [],
-    "label": "//ios/testing:ocmock_support_unittests",
+    "label": "//ios/testing:ios_testing_unittests",
     "type": "raw",
   },
   "origin_policy_parser_fuzzer": {
diff --git a/testing/buildbot/manage.py b/testing/buildbot/manage.py
index e0ad3aed..40e14354 100755
--- a/testing/buildbot/manage.py
+++ b/testing/buildbot/manage.py
@@ -115,7 +115,7 @@
   'ios_web_unittests',
   'ios_web_view_inttests',
   'ios_web_view_unittests',
-  'ocmock_support_unittests',
+  'ios_testing_unittests',
 
   # These are listed in Builders that are skipped for other reasons.
   'chrome_junit_tests',
diff --git a/testing/buildbot/tryserver.devtools-frontend.json b/testing/buildbot/tryserver.devtools-frontend.json
new file mode 100644
index 0000000..92b5ea3
--- /dev/null
+++ b/testing/buildbot/tryserver.devtools-frontend.json
@@ -0,0 +1,31 @@
+{
+  "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
+  "AAAAA2 See generate_buildbot_json.py to make changes": {},
+  "devtools_frontend_linux_blink_light_rel": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "--num-retries=3"
+        ],
+        "isolate_name": "blink_web_tests_exparchive",
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webkit_layout_tests",
+        "results_handler": "layout tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "shards": 12
+        }
+      }
+    ]
+  }
+}
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 73f7d3f3..2e26b2d 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -4463,6 +4463,19 @@
     },
   },
   {
+    'name': 'tryserver.devtools-frontend',
+    'machines': {
+      'devtools_frontend_linux_blink_light_rel': {
+        'mixins': [
+          'linux-xenial',
+        ],
+        'test_suites': {
+          'isolated_scripts': 'chromium_webkit_isolated_scripts',
+        },
+      },
+    }
+  },
+  {
     'name': 'tryserver.webrtc',
     'machines': {
       'android_chromium_compile': {
diff --git a/testing/scripts/OWNERS b/testing/scripts/OWNERS
index cf3d58aa..540926e6 100644
--- a/testing/scripts/OWNERS
+++ b/testing/scripts/OWNERS
@@ -9,5 +9,8 @@
 per-file check_static_initializers.py=thakis@chromium.org
 per-file check_static_initializers.py=thomasanderson@chromium.org
 
+per-file run-wpt_tests.py=lpz@chromium.org
+per-file run_wpt_tests.py=robertma@chromium.org
+
 # TEAM: infra-dev@chromium.org
 # COMPONENT: Infra>Client>Chrome
diff --git a/testing/scripts/run_wpt_tests.py b/testing/scripts/run_wpt_tests.py
index 5c3556af..e635ed8 100755
--- a/testing/scripts/run_wpt_tests.py
+++ b/testing/scripts/run_wpt_tests.py
@@ -72,8 +72,10 @@
             #"--no-manifest-update",
             "--manifest=../../third_party/blink/web_tests/external/"
                 "WPT_BASE_MANIFEST_6.json",
-            # Enable mach logging to aid debugging
-            "--log-mach=-"
+            # (crbug.com/1023835) The flags below are temporary to aid debugging
+            "--log-mach=-",
+            "--log-mach-verbose",
+            "--exclude=webdriver"
         ])
         return rest_args
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index c4b3f31..eaf5226 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1585,6 +1585,21 @@
             ]
         }
     ],
+    "CloudPolicyOverFcm": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "CloudPolicyOverFCM"
+                }
+            ]
+        }
+    ],
     "CompositorImageAnimation": [
         {
             "platforms": [
@@ -6707,10 +6722,30 @@
             ],
             "experiments": [
                 {
-                    "name": "UKM_Enabled",
+                    "name": "UMA_Enabled",
                     "enable_features": [
-                        "DemographicMetricsReporting",
-                        "UkmReportNoisedUserBirthYearAndGender"
+                        "DemographicMetricsReporting"
+                    ]
+                }
+            ]
+        }
+    ],
+    "UnidoOnSignIn": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "SharingDeriveVapidKey",
+                        "SharingUseDeviceInfo",
+                        "SyncDeviceInfoInTransportMode"
                     ]
                 }
             ]
diff --git a/components/chrome_cleaner/public/typemaps/OWNERS b/third_party/blink/common/frame/OWNERS
similarity index 79%
rename from components/chrome_cleaner/public/typemaps/OWNERS
rename to third_party/blink/common/frame/OWNERS
index 03fa9a5d..0f95157 100644
--- a/components/chrome_cleaner/public/typemaps/OWNERS
+++ b/third_party/blink/common/frame/OWNERS
@@ -1,7 +1,4 @@
-set noparent
-file://ipc/SECURITY_OWNERS
-
 per-file *_mojom_traits*.*=set noparent
 per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
 per-file *.typemap=set noparent
-per-file *.typemap=file://ipc/SECURITY_OWNERS
+per-file *.typemap=file://ipc/SECURITY_OWNERS
\ No newline at end of file
diff --git a/third_party/blink/common/frame/frame_policy.typemap b/third_party/blink/common/frame/frame_policy.typemap
new file mode 100644
index 0000000..cf90829a
--- /dev/null
+++ b/third_party/blink/common/frame/frame_policy.typemap
@@ -0,0 +1,16 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//third_party/blink/public/mojom/frame/frame_policy.mojom"
+public_headers = [
+  "//third_party/blink/public/common/frame/frame_policy.h",
+  "//third_party/blink/public/common/feature_policy/policy_value.h",
+  "//third_party/blink/public/common/frame/sandbox_flags.h",
+]
+traits_headers =
+    [ "//third_party/blink/common/frame/frame_policy_mojom_traits.h" ]
+sources = [
+  "//third_party/blink/common/frame/frame_policy_mojom_traits.cc",
+]
+type_mappings = [ "blink.mojom.FramePolicy=::blink::FramePolicy" ]
diff --git a/third_party/blink/common/frame/frame_policy_mojom_traits.cc b/third_party/blink/common/frame/frame_policy_mojom_traits.cc
new file mode 100644
index 0000000..fdc90788
--- /dev/null
+++ b/third_party/blink/common/frame/frame_policy_mojom_traits.cc
@@ -0,0 +1,19 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/common/frame/frame_policy_mojom_traits.h"
+
+namespace mojo {
+
+bool StructTraits<blink::mojom::FramePolicyDataView, blink::FramePolicy>::Read(
+    blink::mojom::FramePolicyDataView in,
+    blink::FramePolicy* out) {
+  out->allowed_to_download_without_user_activation =
+      in.allowed_to_download_without_user_activation();
+
+  return in.ReadSandboxFlags(&out->sandbox_flags) &&
+         in.ReadContainerPolicy(&out->container_policy);
+}
+
+}  // namespace mojo
diff --git a/third_party/blink/common/frame/frame_policy_mojom_traits.h b/third_party/blink/common/frame/frame_policy_mojom_traits.h
new file mode 100644
index 0000000..afe109a
--- /dev/null
+++ b/third_party/blink/common/frame/frame_policy_mojom_traits.h
@@ -0,0 +1,39 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_COMMON_FRAME_FRAME_POLICY_MOJOM_TRAITS_H_
+#define THIRD_PARTY_BLINK_COMMON_FRAME_FRAME_POLICY_MOJOM_TRAITS_H_
+
+#include "third_party/blink/common/feature_policy/feature_policy_mojom_traits.h"
+#include "third_party/blink/public/common/frame/frame_policy.h"
+#include "third_party/blink/public/mojom/frame/frame_policy.mojom-shared.h"
+
+namespace mojo {
+
+template <>
+class BLINK_COMMON_EXPORT
+    StructTraits<blink::mojom::FramePolicyDataView, blink::FramePolicy> {
+ public:
+  static bool allowed_to_download_without_user_activation(
+      const blink::FramePolicy& frame_policy) {
+    return frame_policy.allowed_to_download_without_user_activation;
+  }
+
+  static const std::vector<blink::ParsedFeaturePolicyDeclaration>&
+  container_policy(const blink::FramePolicy& frame_policy) {
+    return frame_policy.container_policy;
+  }
+
+  static blink::WebSandboxFlags sandbox_flags(
+      const blink::FramePolicy& frame_policy) {
+    return frame_policy.sandbox_flags;
+  }
+
+  static bool Read(blink::mojom::FramePolicyDataView in,
+                   blink::FramePolicy* out);
+};
+
+}  // namespace mojo
+
+#endif  // THIRD_PARTY_BLINK_COMMON_FRAME_FRAME_POLICY_MOJOM_TRAITS_H_
diff --git a/third_party/blink/common/typemaps.gni b/third_party/blink/common/typemaps.gni
index f0f89bd..b4bbb0de 100644
--- a/third_party/blink/common/typemaps.gni
+++ b/third_party/blink/common/typemaps.gni
@@ -5,6 +5,7 @@
 typemaps = [
   "//third_party/blink/common/bluetooth/web_bluetooth_device_id.typemap",
   "//third_party/blink/common/feature_policy/feature_policy.typemap",
+  "//third_party/blink/common/frame/frame_policy.typemap",
   "//third_party/blink/common/messaging/cloneable_message.typemap",
   "//third_party/blink/common/messaging/transferable_message.typemap",
 ]
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 7f2c31b..48820a1 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -126,7 +126,6 @@
     "platform/mac/web_sandbox_support.h",
     "platform/mac/web_scrollbar_theme.h",
     "platform/media/webmediaplayer_delegate.h",
-    "platform/modules/indexeddb/web_idb_database_exception.h",
     "platform/modules/mediastream/media_stream_audio_deliverer.h",
     "platform/modules/mediastream/media_stream_audio_track.h",
     "platform/modules/mediastream/media_stream_types.h",
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 6a78c29..502ccf7 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -53,6 +53,7 @@
     "filesystem/file_system.mojom",
     "frame/find_in_page.mojom",
     "frame/frame.mojom",
+    "frame/frame_policy.mojom",
     "frame/fullscreen.mojom",
     "frame/lifecycle.mojom",
     "frame/navigation_initiator.mojom",
diff --git a/third_party/blink/public/mojom/frame/frame_policy.mojom b/third_party/blink/public/mojom/frame/frame_policy.mojom
new file mode 100644
index 0000000..6f93e66
--- /dev/null
+++ b/third_party/blink/public/mojom/frame/frame_policy.mojom
@@ -0,0 +1,17 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module blink.mojom;
+
+import "third_party/blink/public/mojom/feature_policy/feature_policy.mojom";
+
+// This struct holds frame policy value that needs to be passed around between
+// renderer process and browser process during navigation. It is used as a data
+// member of CommonNavigationParams. For details, please refer to
+// navigation_params.mojom.
+struct FramePolicy {
+  blink.mojom.WebSandboxFlags sandbox_flags;
+  array<blink.mojom.ParsedFeaturePolicyDeclaration> container_policy;
+  bool allowed_to_download_without_user_activation = true;
+};
diff --git a/third_party/blink/public/mojom/indexeddb/indexeddb.mojom b/third_party/blink/public/mojom/indexeddb/indexeddb.mojom
index bc44681..745eadbef 100644
--- a/third_party/blink/public/mojom/indexeddb/indexeddb.mojom
+++ b/third_party/blink/public/mojom/indexeddb/indexeddb.mojom
@@ -147,6 +147,19 @@
   IOError,
 };
 
+// These values need to match core/dom/exception_code.h
+enum IDBException {
+  kNoError = 0,
+  kUnknownError = 28,
+  kConstraintError = 29,
+  kDataError = 30,
+  kVersionError = 33,
+  kAbortError = 20,
+  kIgnorableAbortError = 11,
+  kQuotaError = 22,
+  kTimeoutError = 23,
+};
+
 struct IDBIndexMetadata {
   int64 id;
   mojo_base.mojom.String16 name;
@@ -233,7 +246,7 @@
 // TODO(https://crbug.com/627484): Many of these could be replaced with
 // replies associated with particular messages.
 interface IDBCallbacks {
-  Error(int32 code, mojo_base.mojom.String16 message);
+  Error(IDBException code, mojo_base.mojom.String16 message);
 
   // Factory::GetDatabaseInfo
   SuccessNamesAndVersionsList(array<IDBNameAndVersion> value);
@@ -267,14 +280,14 @@
 interface IDBDatabaseCallbacks {
   ForcedClose();
   VersionChange(int64 old_version, int64 new_version);
-  Abort(int64 transaction_id, int32 code,
+  Abort(int64 transaction_id, IDBException code,
         mojo_base.mojom.String16 message);
   Complete(int64 transaction_id);
   Changes(IDBObserverChanges changes);
 };
 
 struct IDBError {
-  int32 error_code;
+  IDBException error_code;
   mojo_base.mojom.String16 error_message;
 };
 
diff --git a/third_party/blink/public/platform/modules/indexeddb/OWNERS b/third_party/blink/public/platform/modules/indexeddb/OWNERS
deleted file mode 100644
index a740487..0000000
--- a/third_party/blink/public/platform/modules/indexeddb/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-file://content/browser/indexed_db/OWNERS
-
-# TEAM: storage-dev@chromium.org
-# COMPONENT: Blink>Storage>IndexedDB
diff --git a/third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h b/third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h
deleted file mode 100644
index 32a2d63a..0000000
--- a/third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INDEXEDDB_WEB_IDB_DATABASE_EXCEPTION_H_
-#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INDEXEDDB_WEB_IDB_DATABASE_EXCEPTION_H_
-
-namespace blink {
-
-enum WebIDBDatabaseException {
-  kWebIDBDatabaseExceptionUnknownError = 28,
-  kWebIDBDatabaseExceptionConstraintError = 29,
-  kWebIDBDatabaseExceptionDataError = 30,
-  kWebIDBDatabaseExceptionVersionError = 33,
-  kWebIDBDatabaseExceptionAbortError = 20,
-  kWebIDBDatabaseExceptionIgnorableAbortError = 11,
-  kWebIDBDatabaseExceptionQuotaError = 22,
-  kWebIDBDatabaseExceptionTimeoutError = 23,
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INDEXEDDB_WEB_IDB_DATABASE_EXCEPTION_H_
diff --git a/third_party/blink/public/web/web_navigation_params.h b/third_party/blink/public/web/web_navigation_params.h
index d3e8c0b..6e7cfb8 100644
--- a/third_party/blink/public/web/web_navigation_params.h
+++ b/third_party/blink/public/web/web_navigation_params.h
@@ -14,6 +14,7 @@
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "services/network/public/mojom/ip_address_space.mojom-shared.h"
 #include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "third_party/blink/public/common/frame/frame_policy.h"
 #include "third_party/blink/public/common/navigation/triggering_event_info.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
@@ -148,6 +149,10 @@
   // The navigation initiator's address space.
   network::mojom::IPAddressSpace initiator_address_space =
       network::mojom::IPAddressSpace::kUnknown;
+
+  // The frame policy specified by the frame owner element.
+  // Should be base::nullopt for top level navigations
+  base::Optional<FramePolicy> frame_policy;
 };
 
 // This structure holds all information provided by the embedder that is
@@ -352,6 +357,10 @@
   // The base URL which will be set for the document to support relative path
   // subresource loading in unsigned bundled exchanges file.
   WebURL base_url_override_for_bundled_exchanges;
+
+  // The frame policy specified by the frame owner element.
+  // Should be base::nullopt for top level navigations
+  base::Optional<FramePolicy> frame_policy;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
index 6cd8d3f..6844f4a 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
@@ -414,7 +414,7 @@
       if (!image_data)
         return nullptr;
       DOMArrayBufferBase* pixel_buffer = image_data->BufferBase();
-      DCHECK_EQ(pixel_buffer->ByteLength(), byte_length);
+      DCHECK_EQ(pixel_buffer->DeprecatedByteLengthAsUnsigned(), byte_length);
       memcpy(pixel_buffer->Data(), pixels, byte_length);
       return image_data;
     }
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc
index 5c58c927..ac8e5462 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc
@@ -331,7 +331,7 @@
     WriteUint32(image_data->height());
     DOMArrayBufferBase* pixel_buffer = image_data->BufferBase();
     uint32_t pixel_buffer_length =
-        SafeCast<uint32_t>(pixel_buffer->ByteLength());
+        pixel_buffer->DeprecatedByteLengthAsUnsigned();
     WriteUint32(pixel_buffer_length);
     WriteRawBytes(pixel_buffer->Data(), pixel_buffer_length);
     return true;
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
index 981efb4bf..567640f2 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
@@ -794,8 +794,8 @@
       new_image_data->getColorSettings();
   EXPECT_EQ("p3", new_color_settings->colorSpace());
   EXPECT_EQ("float32", new_color_settings->storageFormat());
-  EXPECT_EQ(image_data->BufferBase()->ByteLength(),
-            new_image_data->BufferBase()->ByteLength());
+  EXPECT_EQ(image_data->BufferBase()->ByteLengthAsSizeT(),
+            new_image_data->BufferBase()->ByteLengthAsSizeT());
   EXPECT_EQ(200, static_cast<unsigned char*>(
                      new_image_data->BufferBase()->Data())[0]);
 }
@@ -850,7 +850,7 @@
       new_image_data->getColorSettings();
   EXPECT_EQ("p3", new_color_settings->colorSpace());
   EXPECT_EQ("float32", new_color_settings->storageFormat());
-  EXPECT_EQ(32u, new_image_data->BufferBase()->ByteLength());
+  EXPECT_EQ(32u, new_image_data->BufferBase()->ByteLengthAsSizeT());
   EXPECT_EQ(200, static_cast<unsigned char*>(
                      new_image_data->BufferBase()->Data())[0]);
 }
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
index c6fa496e7..7dfcc82 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
@@ -264,7 +264,7 @@
   if (DOMArrayBuffer* buffer =
           V8ArrayBuffer::ToImplWithTypeCheck(isolate, value.V8Value())) {
     vector.Assign(reinterpret_cast<const unsigned char*>(buffer->Data()),
-                  buffer->ByteLength());
+                  buffer->ByteLengthAsSizeT());
   }
   return vector;
 }
diff --git a/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc
index c6eb591f..deaa1c6 100644
--- a/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc
@@ -203,7 +203,7 @@
       return nullptr;
     }
     const char* start = static_cast<const char*>(buffer->Data());
-    size_t length = buffer->ByteLength();
+    size_t length = buffer->ByteLengthAsSizeT();
     return IDBKey::CreateBinary(SharedBuffer::Create(start, length));
   }
   if (value->IsArrayBufferView()) {
diff --git a/third_party/blink/renderer/core/css/font_face.cc b/third_party/blink/renderer/core/css/font_face.cc
index 1291405..c627029 100644
--- a/third_party/blink/renderer/core/css/font_face.cc
+++ b/third_party/blink/renderer/core/css/font_face.cc
@@ -142,7 +142,7 @@
   FontFace* font_face =
       MakeGarbageCollected<FontFace>(context, family, descriptors);
   font_face->InitCSSFontFace(static_cast<const unsigned char*>(source->Data()),
-                             source->ByteLength());
+                             source->ByteLengthAsSizeT());
   return font_face;
 }
 
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index cc66dbc..ca95f62 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -868,8 +868,22 @@
     }
 
     ParsedFeaturePolicy container_policy;
-    if (frame && frame->Owner())
-      container_policy = frame->Owner()->GetFramePolicy().container_policy;
+
+    if (frame && frame->Owner()) {
+      // TODO(chenleihu): Due to the data replication mechanism in
+      // multi-process site-per-process environment, the container_policy
+      // value in remote frame owner gets lazily updated only when the next
+      // navigation is initiated on the remote frame. We need a more robust way
+      // to enforce the validity of container_policy value.
+      // https://crbug.com/972089
+      if (frame->Owner()->IsRemote()) {
+        container_policy = frame->Owner()->GetFramePolicy().container_policy;
+      } else {
+        container_policy = initializer.GetFramePolicy()
+                               .value_or(FramePolicy())
+                               .container_policy;
+      }
+    }
 
     // TODO(icelland): This is problematic querying sandbox flags before
     // feature policy is initialized.
diff --git a/third_party/blink/renderer/core/dom/document_init.cc b/third_party/blink/renderer/core/dom/document_init.cc
index bc4df38..8dfbdd3d 100644
--- a/third_party/blink/renderer/core/dom/document_init.cc
+++ b/third_party/blink/renderer/core/dom/document_init.cc
@@ -275,4 +275,10 @@
   return *this;
 }
 
+DocumentInit& DocumentInit::WithFramePolicy(
+    const base::Optional<FramePolicy>& frame_policy) {
+  frame_policy_ = frame_policy;
+  return *this;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/document_init.h b/third_party/blink/renderer/core/dom/document_init.h
index 21d95304..b7e68d7 100644
--- a/third_party/blink/renderer/core/dom/document_init.h
+++ b/third_party/blink/renderer/core/dom/document_init.h
@@ -31,6 +31,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_DOCUMENT_INIT_H_
 
 #include "services/network/public/mojom/ip_address_space.mojom-shared.h"
+#include "third_party/blink/public/common/frame/frame_policy.h"
 #include "third_party/blink/public/platform/web_insecure_request_policy.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
@@ -139,6 +140,12 @@
     return content_security_policy_;
   }
 
+  DocumentInit& WithFramePolicy(
+      const base::Optional<FramePolicy>& frame_policy);
+  const base::Optional<FramePolicy>& GetFramePolicy() const {
+    return frame_policy_;
+  }
+
  private:
   DocumentInit(HTMLImportsController*);
 
@@ -205,6 +212,9 @@
 
   network::mojom::IPAddressSpace ip_address_space_ =
       network::mojom::IPAddressSpace::kUnknown;
+
+  // The frame policy snapshot from the beginning of navigation.
+  base::Optional<FramePolicy> frame_policy_ = base::nullopt;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/commands/editor_command.cc b/third_party/blink/renderer/core/editing/commands/editor_command.cc
index bc78d39d..93776a2 100644
--- a/third_party/blink/renderer/core/editing/commands/editor_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/editor_command.cc
@@ -1052,8 +1052,7 @@
   const VisibleSelection& selection =
       CreateVisibleSelection(frame.GetEditor().SelectionForCommand(event));
   return (selection.IsCaret() &&
-          (selection.IsContentEditable() ||
-           frame.GetSettings()->GetCaretBrowsingEnabled())) ||
+          (selection.IsContentEditable() || frame.IsCaretBrowsingEnabled())) ||
          selection.IsRange();
 }
 
@@ -1069,8 +1068,7 @@
   const VisibleSelection& selection =
       CreateVisibleSelection(frame.GetEditor().SelectionForCommand(event));
   return ((selection.IsCaret() &&
-           (selection.IsContentEditable() ||
-            frame.GetSettings()->GetCaretBrowsingEnabled())) ||
+           (selection.IsContentEditable() || frame.IsCaretBrowsingEnabled())) ||
           selection.IsRange()) &&
          !frame.GetEditor().Mark().IsNone();
 }
@@ -1104,7 +1102,7 @@
 static bool EnabledInEditableTextOrCaretBrowsing(LocalFrame& frame,
                                                  Event* event,
                                                  EditorCommandSource source) {
-  return frame.GetSettings()->GetCaretBrowsingEnabled() ||
+  return frame.IsCaretBrowsingEnabled() ||
          EnabledInEditableText(frame, event, source);
 }
 
diff --git a/third_party/blink/renderer/core/editing/commands/move_commands.cc b/third_party/blink/renderer/core/editing/commands/move_commands.cc
index b84a4707..f11ec20 100644
--- a/third_party/blink/renderer/core/editing/commands/move_commands.cc
+++ b/third_party/blink/renderer/core/editing/commands/move_commands.cc
@@ -47,7 +47,7 @@
 unsigned MoveCommands::VerticalScrollDistance(LocalFrame& frame) {
   const Element* focused_element = frame.GetDocument()->FocusedElement();
   if (!focused_element) {
-    if (frame.GetSettings()->GetCaretBrowsingEnabled()) {
+    if (frame.IsCaretBrowsingEnabled()) {
       focused_element = frame.GetDocument()->ActiveElement();
     }
 
@@ -63,8 +63,7 @@
     return 0;
   if (!(style->OverflowY() == EOverflow::kScroll ||
         style->OverflowY() == EOverflow::kAuto ||
-        HasEditableStyle(*focused_element) ||
-        frame.GetSettings()->GetCaretBrowsingEnabled()))
+        HasEditableStyle(*focused_element) || frame.IsCaretBrowsingEnabled()))
     return 0;
   const ScrollableArea& scrollable_area = *frame.View()->LayoutViewport();
   const int height = std::min<int>(layout_box.ClientHeight().ToInt(),
@@ -126,7 +125,7 @@
 }
 
 void MoveCommands::UpdateFocusForCaretBrowsing(LocalFrame& frame) {
-  if (!frame.GetSettings()->GetCaretBrowsingEnabled())
+  if (!frame.IsCaretBrowsingEnabled())
     return;
 
   SelectionInDOMTree selection = frame.Selection().GetSelectionInDOMTree();
@@ -160,7 +159,7 @@
 }
 
 void MoveCommands::UpdateSelectionForCaretBrowsing(LocalFrame& frame) {
-  if (!frame.GetSettings()->GetCaretBrowsingEnabled())
+  if (!frame.IsCaretBrowsingEnabled())
     return;
 
   if (frame.Selection().SelectionHasFocus())
diff --git a/third_party/blink/renderer/core/editing/frame_caret.cc b/third_party/blink/renderer/core/editing/frame_caret.cc
index c0aa701..8ef1bff 100644
--- a/third_party/blink/renderer/core/editing/frame_caret.cc
+++ b/third_party/blink/renderer/core/editing/frame_caret.cc
@@ -152,7 +152,7 @@
       caret_visibility_ == CaretVisibility::kVisible &&
       (IsEditablePosition(
            selection_editor_->ComputeVisibleSelectionInDOMTree().Start()) ||
-       frame_->GetSettings()->GetCaretBrowsingEnabled());
+       frame_->IsCaretBrowsingEnabled());
 
   display_item_client_->UpdateStyleAndLayoutIfNeeded(
       should_paint_caret ? CaretPosition() : PositionWithAffinity());
@@ -202,7 +202,7 @@
   } else {
     // Caret is not contained in editable content--see if caret browsing is
     // enabled. If it isn't, don't blink the caret.
-    if (!frame_->GetSettings()->GetCaretBrowsingEnabled())
+    if (!frame_->IsCaretBrowsingEnabled())
       return false;
   }
 
diff --git a/third_party/blink/renderer/core/editing/frame_selection.cc b/third_party/blink/renderer/core/editing/frame_selection.cc
index 6775a112..c14d3bf 100644
--- a/third_party/blink/renderer/core/editing/frame_selection.cc
+++ b/third_party/blink/renderer/core/editing/frame_selection.cc
@@ -260,9 +260,8 @@
     // |setFocusedNodeIfNeeded()| dispatches sync events "FocusOut" and
     // "FocusIn", |frame_| may associate to another document.
     if (!IsAvailable() || GetDocument() != current_document) {
-      // Once we get test case to reach here, we should change this
-      // if-statement to |DCHECK()|.
-      NOTREACHED();
+      // editing/selection/move-selection-detached-frame-crash.html reaches
+      // here. See http://crbug.com/1015710.
       return;
     }
   }
@@ -522,7 +521,7 @@
   DCHECK(!result ||
          (ComputeVisibleSelectionInDOMTree().IsCaret() &&
           (IsEditablePosition(ComputeVisibleSelectionInDOMTree().Start()) ||
-           frame_->GetSettings()->GetCaretBrowsingEnabled())));
+           frame_->IsCaretBrowsingEnabled())));
   return result;
 }
 
diff --git a/third_party/blink/renderer/core/editing/ime/edit_context.cc b/third_party/blink/renderer/core/editing/ime/edit_context.cc
index e17df07..526460a 100644
--- a/third_party/blink/renderer/core/editing/ime/edit_context.cc
+++ b/third_party/blink/renderer/core/editing/ime/edit_context.cc
@@ -24,7 +24,7 @@
 namespace blink {
 
 EditContext::EditContext(ScriptState* script_state, const EditContextInit* dict)
-    : ContextClient(ExecutionContext::From(script_state)) {
+    : ContextLifecycleObserver(ExecutionContext::From(script_state)) {
   DCHECK(IsMainThread());
 
   if (dict->hasText())
@@ -58,26 +58,30 @@
 }
 
 ExecutionContext* EditContext::GetExecutionContext() const {
-  return ContextClient::GetExecutionContext();
+  return ContextLifecycleObserver::GetExecutionContext();
+}
+
+bool EditContext::HasPendingActivity() const {
+  return GetExecutionContext() && HasEventListeners();
 }
 
 InputMethodController& EditContext::GetInputMethodController() const {
-  return ContextClient::GetFrame()->GetInputMethodController();
+  return ContextLifecycleObserver::GetFrame()->GetInputMethodController();
 }
 
 void EditContext::DispatchCompositionEndEvent(const String& text) {
   auto* event = MakeGarbageCollected<CompositionEvent>(
-      event_type_names::kCompositionend, ContextClient::GetFrame()->DomWindow(),
-      text);
+      event_type_names::kCompositionend,
+      ContextLifecycleObserver::GetFrame()->DomWindow(), text);
   DispatchEvent(*event);
 }
 
 bool EditContext::DispatchCompositionStartEvent(const String& text) {
   auto* event = MakeGarbageCollected<CompositionEvent>(
       event_type_names::kCompositionstart,
-      ContextClient::GetFrame()->DomWindow(), text);
+      ContextLifecycleObserver::GetFrame()->DomWindow(), text);
   DispatchEvent(*event);
-  if (!ContextClient::GetFrame())
+  if (!ContextLifecycleObserver::GetFrame())
     return false;
   return true;
 }
@@ -573,8 +577,9 @@
 }
 
 void EditContext::Trace(Visitor* visitor) {
-  EventTarget::Trace(visitor);
-  ScriptWrappable::Trace(visitor);
-  ContextClient::Trace(visitor);
+  ActiveScriptWrappable::Trace(visitor);
+  ContextLifecycleObserver::Trace(visitor);
+  EventTargetWithInlineData::Trace(visitor);
 }
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/ime/edit_context.h b/third_party/blink/renderer/core/editing/ime/edit_context.h
index 2cb9626..38cdc12c 100644
--- a/third_party/blink/renderer/core/editing/ime/edit_context.h
+++ b/third_party/blink/renderer/core/editing/ime/edit_context.h
@@ -11,10 +11,10 @@
 #include "third_party/blink/public/platform/web_text_input_type.h"
 #include "third_party/blink/public/web/web_ime_text_span.h"
 #include "third_party/blink/public/web/web_input_method_controller.h"
+#include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
-#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 
 namespace blink {
 
@@ -32,7 +32,8 @@
 // https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/master/EditContext/explainer.md
 
 class CORE_EXPORT EditContext final : public EventTargetWithInlineData,
-                                      public ContextClient,
+                                      public ActiveScriptWrappable<EditContext>,
+                                      public ContextLifecycleObserver,
                                       public WebInputMethodController {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(EditContext);
@@ -134,9 +135,12 @@
   void setInputPanelPolicy(const String& input_policy);
 
   // EventTarget overrides
-  bool HasEventTargetData() const { return has_event_target_data; }
   const AtomicString& InterfaceName() const override;
-  ExecutionContext* GetExecutionContext() const final;
+  ExecutionContext* GetExecutionContext() const override;
+
+  // ActiveScriptWrappable overrides.
+  bool HasPendingActivity() const override;
+
   void Trace(Visitor*) override;
 
   // WebInputMethodController overrides.
@@ -231,7 +235,6 @@
   ui::TextInputAction enter_key_hint_ = ui::TextInputAction::kEnter;
   EditContextInputPanelPolicy input_panel_policy_ =
       EditContextInputPanelPolicy::kManual;
-  bool has_event_target_data = false;
   WebRect control_bounds_;
   WebRect selection_bounds_;
   // This flag is set when the input method controller receives a
diff --git a/third_party/blink/renderer/core/editing/ime/edit_context.idl b/third_party/blink/renderer/core/editing/ime/edit_context.idl
index b3987e60..18bc5d0 100644
--- a/third_party/blink/renderer/core/editing/ime/edit_context.idl
+++ b/third_party/blink/renderer/core/editing/ime/edit_context.idl
@@ -10,6 +10,7 @@
     Constructor(optional EditContextInit options),
     ConstructorCallWith=ScriptState,
     Exposed=Window,
+    ActiveScriptWrappable,
     RuntimeEnabled=EditContext
 ] interface EditContext : EventTarget {
     void focus();
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
index 8c4c45d..0a7e97f 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
@@ -586,6 +586,10 @@
         devtools->NavigationInitiatorInfo(web_frame_->GetFrame());
   }
 
+  auto* owner = ToCoreFrame(web_frame_)->Owner();
+  navigation_info->frame_policy =
+      owner ? base::make_optional(owner->GetFramePolicy()) : base::nullopt;
+
   navigation_info->href_translate = href_translate;
 
   web_frame_->Client()->BeginNavigation(std::move(navigation_info));
diff --git a/third_party/blink/renderer/core/exported/web_array_buffer.cc b/third_party/blink/renderer/core/exported/web_array_buffer.cc
index c16c7413..bff770599 100644
--- a/third_party/blink/renderer/core/exported/web_array_buffer.cc
+++ b/third_party/blink/renderer/core/exported/web_array_buffer.cc
@@ -56,7 +56,7 @@
 
 unsigned WebArrayBuffer::ByteLength() const {
   if (!IsNull())
-    return private_->ByteLength();
+    return private_->DeprecatedByteLengthAsUnsigned();
   return 0;
 }
 
diff --git a/third_party/blink/renderer/core/exported/web_navigation_params.cc b/third_party/blink/renderer/core/exported/web_navigation_params.cc
index 78f495d1..df6f05c 100644
--- a/third_party/blink/renderer/core/exported/web_navigation_params.cc
+++ b/third_party/blink/renderer/core/exported/web_navigation_params.cc
@@ -42,6 +42,7 @@
   result->initiator_origin_trial_features =
       info.initiator_origin_trial_features;
   result->ip_address_space = info.initiator_address_space;
+  result->frame_policy = info.frame_policy;
   return result;
 }
 
diff --git a/third_party/blink/renderer/core/fetch/body.cc b/third_party/blink/renderer/core/fetch/body.cc
index 1ad4eb2..fcb6d67 100644
--- a/third_party/blink/renderer/core/fetch/body.cc
+++ b/third_party/blink/renderer/core/fetch/body.cc
@@ -186,7 +186,7 @@
       return ScriptPromise();
     }
   } else {
-    resolver->Resolve(DOMArrayBuffer::Create(0u, 1));
+    resolver->Resolve(DOMArrayBuffer::Create(size_t{0}, size_t{0}));
   }
   return promise;
 }
diff --git a/third_party/blink/renderer/core/fetch/body_stream_buffer_test.cc b/third_party/blink/renderer/core/fetch/body_stream_buffer_test.cc
index d171bd5..22adda11b 100644
--- a/third_party/blink/renderer/core/fetch/body_stream_buffer_test.cc
+++ b/third_party/blink/renderer/core/fetch/body_stream_buffer_test.cc
@@ -388,7 +388,7 @@
   EXPECT_FALSE(buffer->HasPendingActivity());
   ASSERT_TRUE(array_buffer);
   EXPECT_EQ("hello", String(static_cast<const char*>(array_buffer->Data()),
-                            array_buffer->ByteLength()));
+                            array_buffer->ByteLengthAsSizeT()));
 }
 
 TEST_F(BodyStreamBufferTest, LoadBodyStreamBufferAsBlob) {
diff --git a/third_party/blink/renderer/core/fetch/fetch_data_loader_test.cc b/third_party/blink/renderer/core/fetch/fetch_data_loader_test.cc
index 41ff1e8..69aa8773 100644
--- a/third_party/blink/renderer/core/fetch/fetch_data_loader_test.cc
+++ b/third_party/blink/renderer/core/fetch/fetch_data_loader_test.cc
@@ -356,7 +356,7 @@
 
   ASSERT_TRUE(array_buffer);
   ASSERT_EQ(kQuickBrownFoxLengthWithTerminatingNull,
-            array_buffer->ByteLength());
+            array_buffer->ByteLengthAsSizeT());
   EXPECT_STREQ(kQuickBrownFox, static_cast<const char*>(array_buffer->Data()));
 }
 
diff --git a/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.cc b/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.cc
index b150ffc..0ecffc8e 100644
--- a/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.cc
+++ b/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.cc
@@ -497,7 +497,8 @@
               UTF8Encoding().Encode(string, WTF::kNoUnencodables)))) {}
 
 FormDataBytesConsumer::FormDataBytesConsumer(DOMArrayBuffer* buffer)
-    : FormDataBytesConsumer(buffer->Data(), buffer->ByteLength()) {}
+    : FormDataBytesConsumer(buffer->Data(),
+                            buffer->DeprecatedByteLengthAsUnsigned()) {}
 
 FormDataBytesConsumer::FormDataBytesConsumer(DOMArrayBufferView* view)
     : FormDataBytesConsumer(view->BaseAddress(), view->byteLength()) {}
diff --git a/third_party/blink/renderer/core/fileapi/blob.cc b/third_party/blink/renderer/core/fileapi/blob.cc
index e3701368..835db406 100644
--- a/third_party/blink/renderer/core/fileapi/blob.cc
+++ b/third_party/blink/renderer/core/fileapi/blob.cc
@@ -157,7 +157,8 @@
   for (const auto& item : parts) {
     if (item.IsArrayBuffer()) {
       DOMArrayBuffer* array_buffer = item.GetAsArrayBuffer();
-      blob_data->AppendBytes(array_buffer->Data(), array_buffer->ByteLength());
+      blob_data->AppendBytes(array_buffer->Data(),
+                             array_buffer->ByteLengthAsSizeT());
     } else if (item.IsArrayBufferView()) {
       DOMArrayBufferView* array_buffer_view =
           item.GetAsArrayBufferView().View();
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index ab831fd..f9da3c56 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -492,6 +492,10 @@
   Frame::DidChangeVisibilityState();
 }
 
+bool LocalFrame::IsCaretBrowsingEnabled() const {
+  return GetSettings() ? GetSettings()->GetCaretBrowsingEnabled() : false;
+}
+
 void LocalFrame::HookBackForwardCacheEviction() {
   // Register a callback dispatched when JavaScript is executed on the frame.
   // The callback evicts the frame. If a frame is frozen by BackForwardCache,
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index 02a1b084..945356f 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -200,6 +200,9 @@
   // Returns ContentCaptureManager in LocalFrameRoot.
   ContentCaptureManager* GetContentCaptureManager();
 
+  // Returns the current state of caret browsing mode.
+  bool IsCaretBrowsingEnabled() const;
+
   // Activates the user activation states of the |LocalFrame| (provided it's
   // non-null) and all its ancestors.  Also creates a |UserGestureIndicator|
   // that contains a |UserGestureToken| with the given status.
diff --git a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc
index 13d97cc2..3f16848 100644
--- a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc
@@ -52,10 +52,16 @@
   // Define the UMA for the primary metric.
   primary_metric_.uma_counter.reset(
       new CustomCountHistogram("Blink.MainFrame.UpdateTime", 0, 10000000, 50));
+  primary_metric_.pre_fcp_uma_counter.reset(new CustomCountHistogram(
+      "Blink.MainFrame.UpdateTime.PreFCP", 0, 10000000, 50));
+  primary_metric_.post_fcp_uma_counter.reset(new CustomCountHistogram(
+      "Blink.MainFrame.UpdateTime.PostFCP", 0, 10000000, 50));
 
   // Set up the substrings to create the UMA names
   const String uma_preamble = "Blink.";
   const String uma_postscript = ".UpdateTime";
+  const String uma_prefcp_postscript = ".PreFCP";
+  const String uma_postfcp_postscript = ".PostFCP";
   const String uma_percentage_preamble = "Blink.MainFrame.";
   const String uma_percentage_postscript = "Ratio";
 
@@ -83,23 +89,34 @@
   for (unsigned i = 0; i < (unsigned)kCount; ++i) {
     const MetricInitializationData& metric_data = metrics_data()[i];
 
-    // Absolute records report the absolute time for each metric, both
-    // average and worst case. They have an associated UMA too that we
-    // own and allocate here.
+    // Absolute records report the absolute time for each metric per frame.
+    // They also aggregate the time spent in each stage between navigation
+    // (LocalFrameView resets) and First Contentful Paint.
+    // They have an associated UMA too that we own and allocate here.
     auto& absolute_record = absolute_metric_records_.emplace_back();
     absolute_record.reset();
-    StringBuilder uma_name;
-    uma_name.Append(uma_preamble);
-    uma_name.Append(metric_data.name);
-    uma_name.Append(uma_postscript);
     if (metric_data.has_uma) {
+      StringBuilder uma_name;
+      uma_name.Append(uma_preamble);
+      uma_name.Append(metric_data.name);
+      uma_name.Append(uma_postscript);
       absolute_record.uma_counter.reset(new CustomCountHistogram(
           uma_name.ToString().Utf8().c_str(), 0, 10000000, 50));
+      StringBuilder pre_fcp_uma_name;
+      pre_fcp_uma_name.Append(uma_name);
+      pre_fcp_uma_name.Append(uma_prefcp_postscript);
+      absolute_record.pre_fcp_uma_counter.reset(new CustomCountHistogram(
+          pre_fcp_uma_name.ToString().Utf8().c_str(), 0, 10000000, 50));
+      StringBuilder post_fcp_uma_name;
+      post_fcp_uma_name.Append(uma_name);
+      post_fcp_uma_name.Append(uma_postfcp_postscript);
+      absolute_record.post_fcp_uma_counter.reset(new CustomCountHistogram(
+          post_fcp_uma_name.ToString().Utf8().c_str(), 0, 10000000, 50));
     }
 
-    // Percentage records report the ratio of each metric to the primary metric,
-    // average and worst case. UMA counters are also associated with the
-    // ratios and we allocate and own them here.
+    // Percentage records report the ratio of each metric to the primary metric.
+    // UMA counters are also associated with the ratios and we allocate and own
+    // them here.
     auto& percentage_record = main_frame_percentage_records_.emplace_back();
     percentage_record.reset();
     for (auto bucket_substring : threshold_substrings) {
@@ -177,6 +194,10 @@
   if (!calls_to_next_forced_style_layout_uma_) {
     auto& record = absolute_metric_records_[kForcedStyleAndLayout];
     record.uma_counter->CountMicroseconds(duration);
+    if (is_before_fcp_)
+      record.pre_fcp_uma_counter->CountMicroseconds(duration);
+    else
+      record.post_fcp_uma_counter->CountMicroseconds(duration);
     calls_to_next_forced_style_layout_uma_ =
         base::RandInt(0, mean_calls_between_forced_style_layout_uma_ * 2);
   } else {
@@ -199,10 +220,16 @@
   // the signed 32 counter for number of events in a 30 minute period. So
   // randomly record with probability 1/100.
   if (record.uma_counter) {
-    if (metric_index == static_cast<size_t>(kForcedStyleAndLayout))
+    if (metric_index == static_cast<size_t>(kForcedStyleAndLayout)) {
       RecordForcedStyleLayoutUMA(duration);
-    else
+    } else {
       record.uma_counter->CountMicroseconds(duration);
+      if (is_before_fcp_) {
+        record.pre_fcp_uma_counter->CountMicroseconds(duration);
+      } else {
+        record.post_fcp_uma_counter->CountMicroseconds(duration);
+      }
+    }
   }
 
   // Only record ratios when inside a main frame.
@@ -230,6 +257,10 @@
 
   // Record UMA
   primary_metric_.uma_counter->CountMicroseconds(duration);
+  if (is_before_fcp_)
+    primary_metric_.pre_fcp_uma_counter->CountMicroseconds(duration);
+  else
+    primary_metric_.post_fcp_uma_counter->CountMicroseconds(duration);
 
   // Record primary time information
   primary_metric_.interval_duration = duration;
diff --git a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
index d0d399b..e21c5cd 100644
--- a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
+++ b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
@@ -259,6 +259,8 @@
  private:
   struct AbsoluteMetricRecord {
     std::unique_ptr<CustomCountHistogram> uma_counter;
+    std::unique_ptr<CustomCountHistogram> pre_fcp_uma_counter;
+    std::unique_ptr<CustomCountHistogram> post_fcp_uma_counter;
 
     // Accumulated at each sample, then reset with a call to
     // RecordEndOfFrameMetrics.
diff --git a/third_party/blink/renderer/core/html/canvas/image_data.cc b/third_party/blink/renderer/core/html/canvas/image_data.cc
index 8c21a4e..9cfd352 100644
--- a/third_party/blink/renderer/core/html/canvas/image_data.cc
+++ b/third_party/blink/renderer/core/html/canvas/image_data.cc
@@ -683,8 +683,8 @@
   if (pixel_format == kRGBA8CanvasPixelFormat &&
       storage_format == kUint8ClampedArrayStorageFormat) {
     DOMArrayBuffer* array_buffer = DOMArrayBuffer::Create(content);
-    return DOMUint8ClampedArray::Create(array_buffer, 0,
-                                        array_buffer->ByteLength());
+    return DOMUint8ClampedArray::Create(
+        array_buffer, 0, array_buffer->DeprecatedByteLengthAsUnsigned());
   }
 
   skcms_PixelFormat src_format = skcms_PixelFormat_RGBA_8888;
diff --git a/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js b/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
index 4cb0e033..d89a990 100644
--- a/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
+++ b/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
@@ -2502,10 +2502,11 @@
   ListView.prototype.onClick.call(this, event);
   var year = this.selectedRow + 1;
   if (this.selectedRow !== oldSelectedRow) {
-    var month = this.highlightedMonth ? this.highlightedMonth.month : 0;
+    // Always start with first month when changing the year.
+    const month = new Month(year, 0);
+    this.highlightMonth(month);
     this.dispatchEvent(
-        YearListView.EventTypeYearListViewDidSelectMonth, this,
-        new Month(year, month));
+        YearListView.EventTypeYearListViewDidSelectMonth, this, month);
     this.scrollView.scrollTo(this.selectedRow * YearListCell.GetHeight(), true);
   } else {
     var monthButton = enclosingNodeOrSelfWithClass(
diff --git a/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc b/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc
index b052acc1..927a786 100644
--- a/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc
@@ -69,6 +69,10 @@
   growth_limit_cap_ = growth_limit_cap;
 }
 
+void GridTrack::SetCachedTrackSize(const GridTrackSize& cached_track_size) {
+  cached_track_size_ = cached_track_size;
+}
+
 bool GridTrack::IsGrowthLimitBiggerThanBaseSize() const {
   return GrowthLimitIsInfinite() || growth_limit_ >= base_size_;
 }
@@ -195,8 +199,12 @@
     // We may need to estimate the grid area size before running the track
     // sizing algorithm in order to perform the pre-layout of orthogonal
     // items.
-    GridTrackSize track_size =
-        WasSetup() ? GetGridTrackSize(direction, track_position)
+    // We cannot use Tracks(direction)[track_position].CachedTrackSize()
+    // because Tracks(direction) is empty, since we are either performing
+    // pre-layout or are running the track sizing algorithm in the opposite
+    // direction and haven't run it in the desired direction yet.
+    const GridTrackSize& track_size =
+        WasSetup() ? CalculateGridTrackSize(direction, track_position)
                    : RawGridTrackSize(direction, track_position);
     GridLength max_track_size = track_size.MaxTrackBreadth();
     if (max_track_size.IsContentSized() || max_track_size.IsFlex() ||
@@ -394,7 +402,8 @@
         algorithm_.GetGrid().GridItemSpan(child, Direction());
     LayoutUnit max_breadth;
     for (const auto& track_position : span) {
-      GridTrackSize track_size = GetGridTrackSize(Direction(), track_position);
+      const GridTrackSize& track_size =
+          GetCachedGridTrackSize(Direction(), track_position);
       if (!track_size.HasFixedMaxTrackBreadth())
         return min_size;
       max_breadth += ValueForLength(track_size.MaxTrackBreadth().length(),
@@ -658,9 +667,10 @@
     // not to have to do that.
     flex_fraction = std::max(
         flex_fraction,
-        NormalizedFlexFraction(
-            all_tracks[track_index],
-            GetGridTrackSize(direction, track_index).MaxTrackBreadth().Flex()));
+        NormalizedFlexFraction(all_tracks[track_index],
+                               GetCachedGridTrackSize(direction, track_index)
+                                   .MaxTrackBreadth()
+                                   .Flex()));
   }
 
   const Grid& grid = algorithm_.GetGrid();
@@ -894,7 +904,7 @@
   return false;
 }
 
-GridTrackSize GridTrackSizingAlgorithm::GetGridTrackSize(
+GridTrackSize GridTrackSizingAlgorithm::CalculateGridTrackSize(
     GridTrackSizingDirection direction,
     size_t translated_index) const {
   DCHECK(WasSetup());
@@ -986,8 +996,9 @@
       direction_ == kForRows && !layout_grid_->CachedHasDefiniteLogicalHeight();
   size_t num_tracks = track_list.size();
   for (size_t i = 0; i < num_tracks; ++i) {
-    GridTrackSize track_size = GetGridTrackSize(direction_, i);
+    const GridTrackSize& track_size = CalculateGridTrackSize(direction_, i);
     GridTrack& track = track_list[i];
+    track.SetCachedTrackSize(track_size);
     track.SetBaseSize(InitialBaseSize(track_size));
     track.SetGrowthLimit(InitialGrowthLimit(track_size, track.BaseSize()));
     track.SetInfinitelyGrowable(false);
@@ -1019,7 +1030,8 @@
     LayoutBox& grid_item,
     GridTrack& track) {
   const size_t track_position = span.StartLine();
-  GridTrackSize track_size = GetGridTrackSize(direction_, track_position);
+  const GridTrackSize& track_size =
+      Tracks(direction_)[track_position].CachedTrackSize();
 
   if (track_size.HasMinContentMinTrackBreadth()) {
     track.SetBaseSize(
@@ -1049,9 +1061,10 @@
 
 bool GridTrackSizingAlgorithm::SpanningItemCrossesFlexibleSizedTracks(
     const GridSpan& span) const {
+  const Vector<GridTrack>& track_list = Tracks(direction_);
   for (const auto& track_position : span) {
     const GridTrackSize& track_size =
-        GetGridTrackSize(direction_, track_position);
+        track_list[track_position].CachedTrackSize();
     if (track_size.MinTrackBreadth().IsFlex() ||
         track_size.MaxTrackBreadth().IsFlex())
       return true;
@@ -1358,8 +1371,8 @@
     filtered_tracks.Shrink(0);
     LayoutUnit spanning_tracks_size;
     for (const auto& track_position : item_span) {
-      GridTrackSize track_size = GetGridTrackSize(direction_, track_position);
-      GridTrack& track = Tracks(direction_)[track_position];
+      GridTrack& track = all_tracks[track_position];
+      const GridTrackSize& track_size = track.CachedTrackSize();
       spanning_tracks_size +=
           TrackSizeForTrackSizeComputationPhase(phase, track, kForbidInfinity);
       if (!ShouldProcessTrackForTrackSizeComputationPhase(phase, track_size))
@@ -1399,12 +1412,13 @@
 }
 
 void GridTrackSizingAlgorithm::ResolveIntrinsicTrackSizes() {
+  Vector<GridTrack>& all_tracks = Tracks(direction_);
   Vector<GridItemWithSpan> items_sorted_by_increasing_span;
   if (grid_.HasGridItems()) {
     HashSet<LayoutBox*> items_set;
     for (const auto& track_index : content_sized_tracks_index_) {
       auto iterator = grid_.CreateIterator(direction_, track_index);
-      GridTrack& track = Tracks(direction_)[track_index];
+      GridTrack& track = all_tracks[track_index];
       while (auto* grid_item = iterator->NextGridItem()) {
         if (items_set.insert(grid_item).is_new_entry) {
           const GridSpan& span = grid_.GridItemSpan(*grid_item, direction_);
@@ -1440,7 +1454,7 @@
   }
 
   for (const auto& track_index : content_sized_tracks_index_) {
-    GridTrack& track = Tracks(direction_)[track_index];
+    GridTrack& track = all_tracks[track_index];
     if (track.GrowthLimit() == kInfinity)
       track.SetGrowthLimit(track.BaseSize());
   }
@@ -1487,7 +1501,7 @@
   double flex_factor_sum = 0;
   Vector<size_t, 8> flexible_tracks_indexes;
   for (const auto& track_index : tracks_span) {
-    GridTrackSize track_size = GetGridTrackSize(direction_, track_index);
+    const GridTrackSize& track_size = all_tracks[track_index].CachedTrackSize();
     if (!track_size.MaxTrackBreadth().IsFlex()) {
       left_over_space -= all_tracks[track_index].BaseSize();
     } else {
@@ -1527,7 +1541,7 @@
       continue;
     LayoutUnit base_size = tracks[index].BaseSize();
     double flex_factor =
-        GetGridTrackSize(direction_, index).MaxTrackBreadth().Flex();
+        tracks[index].CachedTrackSize().MaxTrackBreadth().Flex();
     // treating all such tracks as inflexible.
     if (base_size > hypothetical_factor_unit_size * flex_factor) {
       left_over_space -= base_size;
@@ -1557,7 +1571,7 @@
   const Vector<GridTrack>& all_tracks = Tracks(direction_);
   for (size_t i = 0; i < num_flex_tracks; ++i) {
     size_t track_index = flexible_sized_tracks_index_[i];
-    auto track_size = GetGridTrackSize(direction_, track_index);
+    const GridTrackSize& track_size = all_tracks[track_index].CachedTrackSize();
     DCHECK(track_size.MaxTrackBreadth().IsFlex());
     LayoutUnit old_base_size = all_tracks[track_index].BaseSize();
     LayoutUnit new_base_size = std::max(
@@ -1768,7 +1782,7 @@
 bool GridTrackSizingAlgorithm::TracksAreWiderThanMinTrackBreadth() const {
   const Vector<GridTrack>& all_tracks = Tracks(direction_);
   for (size_t i = 0; i < all_tracks.size(); ++i) {
-    GridTrackSize track_size = GetGridTrackSize(direction_, i);
+    const GridTrackSize& track_size = all_tracks[i].CachedTrackSize();
     if (InitialBaseSize(track_size) > all_tracks[i].BaseSize())
       return false;
   }
diff --git a/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.h b/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.h
index 6107043..72655cb 100644
--- a/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.h
+++ b/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.h
@@ -66,6 +66,12 @@
   }
   void SetGrowthLimitCap(base::Optional<LayoutUnit>);
 
+  const GridTrackSize CachedTrackSize() const {
+    DCHECK(cached_track_size_.has_value());
+    return cached_track_size_.value();
+  }
+  void SetCachedTrackSize(const GridTrackSize&);
+
  private:
   bool IsGrowthLimitBiggerThanBaseSize() const;
   void EnsureGrowthLimitIsBiggerThanBaseSize();
@@ -76,6 +82,7 @@
   LayoutUnit size_during_distribution_;
   base::Optional<LayoutUnit> growth_limit_cap_;
   bool infinitely_growable_;
+  base::Optional<GridTrackSize> cached_track_size_;
 };
 
 class GridTrackSizingAlgorithm final {
@@ -136,8 +143,8 @@
                                   GridTrackSizingDirection) const;
   bool IsRelativeSizedTrackAsAuto(const GridTrackSize&,
                                   GridTrackSizingDirection) const;
-  GridTrackSize GetGridTrackSize(GridTrackSizingDirection,
-                                 size_t translated_index) const;
+  GridTrackSize CalculateGridTrackSize(GridTrackSizingDirection,
+                                       size_t translated_index) const;
   GridTrackSize RawGridTrackSize(GridTrackSizingDirection,
                                  size_t translated_index) const;
 
@@ -318,9 +325,9 @@
     return algorithm_.AvailableSpace();
   }
 
-  GridTrackSize GetGridTrackSize(GridTrackSizingDirection direction,
-                                 size_t translated_index) const {
-    return algorithm_.GetGridTrackSize(direction, translated_index);
+  GridTrackSize GetCachedGridTrackSize(GridTrackSizingDirection direction,
+                                       size_t translated_index) const {
+    return algorithm_.Tracks(direction)[translated_index].CachedTrackSize();
   }
 
   // Helper functions
diff --git a/third_party/blink/renderer/core/layout/layout_block.cc b/third_party/blink/renderer/core/layout/layout_block.cc
index a6c43df..22199d3 100644
--- a/third_party/blink/renderer/core/layout/layout_block.cc
+++ b/third_party/blink/renderer/core/layout/layout_block.cc
@@ -159,46 +159,7 @@
 
 void LayoutBlock::StyleWillChange(StyleDifference diff,
                                   const ComputedStyle& new_style) {
-  const ComputedStyle* old_style = Style();
-
   SetIsAtomicInlineLevel(new_style.IsDisplayInlineType());
-
-  if (old_style && Parent()) {
-    bool old_style_contains_fixed_position = ComputeIsFixedContainer(old_style);
-    bool old_style_contains_absolute_position =
-        ComputeIsAbsoluteContainer(old_style);
-    bool new_style_contains_fixed_position =
-        ComputeIsFixedContainer(&new_style);
-    bool new_style_contains_absolute_position =
-        ComputeIsAbsoluteContainer(&new_style);
-
-    if ((old_style_contains_fixed_position &&
-         !new_style_contains_fixed_position) ||
-        (old_style_contains_absolute_position &&
-         !new_style_contains_absolute_position)) {
-      // Clear our positioned objects list. Our absolute and fixed positioned
-      // descendants will be inserted into our containing block's positioned
-      // objects list during layout.
-      RemovePositionedObjects(nullptr, kNewContainingBlock);
-    }
-    if (!old_style_contains_absolute_position &&
-        new_style_contains_absolute_position) {
-      // Remove our absolutely positioned descendants from their current
-      // containing block.
-      // They will be inserted into our positioned objects list during layout.
-      if (LayoutBlock* cb = ContainingBlockForAbsolutePosition())
-        cb->RemovePositionedObjects(this, kNewContainingBlock);
-    }
-    if (!old_style_contains_fixed_position &&
-        new_style_contains_fixed_position) {
-      // Remove our fixed positioned descendants from their current containing
-      // block.
-      // They will be inserted into our positioned objects list during layout.
-      if (LayoutBlock* cb = ContainingBlockForFixedPosition())
-        cb->RemovePositionedObjects(this, kNewContainingBlock);
-    }
-  }
-
   LayoutBox::StyleWillChange(diff, new_style);
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow.cc b/third_party/blink/renderer/core/layout/layout_block_flow.cc
index a3b11376..ea7cb84 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_flow.cc
@@ -1235,7 +1235,14 @@
     // Moving to a different page or column may mean that its height is
     // different.
     page_logical_height = PageLogicalHeightForOffset(new_logical_offset);
-    if (line_height > page_logical_height) {
+    // We need to insert a break now, either because there's no room for the
+    // line in the current column / page, or because we have determined that we
+    // need a break to satisfy widow requirements.
+    if (ShouldBreakAtLineToAvoidWidow() &&
+        LineBreakToAvoidWidow() == line_index) {
+      ClearShouldBreakAtLineToAvoidWidow();
+      SetDidBreakAtLineToAvoidWidow();
+    } else if (line_height > page_logical_height) {
       // Too tall to fit in one page / column. Give up. Don't push to the next
       // page / column.
       // TODO(mstensho): Get rid of this. This is just utter weirdness, but the
@@ -1246,14 +1253,6 @@
       return;
     }
 
-    // We need to insert a break now, either because there's no room for the
-    // line in the current column / page, or because we have determined that we
-    // need a break to satisfy widow requirements.
-    if (ShouldBreakAtLineToAvoidWidow() &&
-        LineBreakToAvoidWidow() == line_index) {
-      ClearShouldBreakAtLineToAvoidWidow();
-      SetDidBreakAtLineToAvoidWidow();
-    }
     if (ShouldSetStrutOnBlock(*this, line_box, logical_offset, line_index,
                               page_logical_height)) {
       // Note that when setting the strut on a block, it may be propagated to
diff --git a/third_party/blink/renderer/core/layout/layout_box_model_object.cc b/third_party/blink/renderer/core/layout/layout_box_model_object.cc
index d585612..fbaa0da 100644
--- a/third_party/blink/renderer/core/layout/layout_box_model_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_box_model_object.cc
@@ -329,9 +329,11 @@
     }
   }
 
-  if (old_style &&
-      (could_contain_fixed != CanContainFixedPositionObjects() ||
-       could_contain_absolute != CanContainAbsolutePositionObjects())) {
+  bool can_contain_fixed = CanContainFixedPositionObjects();
+  bool can_contain_absolute = CanContainAbsolutePositionObjects();
+
+  if (old_style && (could_contain_fixed != can_contain_fixed ||
+                    could_contain_absolute != can_contain_absolute)) {
     // If out of flow element containment changed, then we need to force a
     // subtree paint property update, since the children elements may now be
     // referencing a different container.
@@ -347,6 +349,32 @@
       Layer()->SetNeedsCompositingInputsUpdate();
   }
 
+  if (old_style && Parent()) {
+    LayoutBlock* block = FindNonAnonymousContainingBlock(this);
+
+    if ((could_contain_fixed && !can_contain_fixed) ||
+        (could_contain_absolute && !can_contain_absolute)) {
+      // Clear our positioned objects list. Our absolute and fixed positioned
+      // descendants will be inserted into our containing block's positioned
+      // objects list during layout.
+      block->RemovePositionedObjects(nullptr, kNewContainingBlock);
+    }
+    if (!could_contain_absolute && can_contain_absolute) {
+      // Remove our absolute positioned descendants from their current
+      // containing block.
+      // They will be inserted into our positioned objects list during layout.
+      if (LayoutBlock* cb = block->ContainingBlockForAbsolutePosition())
+        cb->RemovePositionedObjects(this, kNewContainingBlock);
+    }
+    if (!could_contain_fixed && can_contain_fixed) {
+      // Remove our fixed positioned descendants from their current containing
+      // block.
+      // They will be inserted into our positioned objects list during layout.
+      if (LayoutBlock* cb = block->ContainingBlockForFixedPosition())
+        cb->RemovePositionedObjects(this, kNewContainingBlock);
+    }
+  }
+
   if (Layer()) {
     Layer()->StyleDidChange(diff, old_style);
     if (had_layer && Layer()->IsSelfPaintingLayer() != layer_was_self_painting)
diff --git a/third_party/blink/renderer/core/layout/layout_inline.cc b/third_party/blink/renderer/core/layout/layout_inline.cc
index e011e82..aea0e46 100644
--- a/third_party/blink/renderer/core/layout/layout_inline.cc
+++ b/third_party/blink/renderer/core/layout/layout_inline.cc
@@ -311,23 +311,6 @@
     }
   }
 
-  bool old_style_is_containing_block = ComputeIsAbsoluteContainer(old_style);
-  bool new_style_is_containing_block = CanContainAbsolutePositionObjects();
-  // If we are changing to/from static, we need to reposition
-  // out-of-flow positioned descendants.
-  if (old_style_is_containing_block != new_style_is_containing_block) {
-    LayoutBlock* abs_containing_block = nullptr;
-    if (!old_style_is_containing_block) {
-      abs_containing_block = ContainingBlockForAbsolutePosition();
-    } else {
-      // When position was not static, containingBlockForAbsolutePosition
-      // for our children is our existing containingBlock.
-      abs_containing_block = FindNonAnonymousContainingBlock(this);
-    }
-    if (abs_containing_block)
-      abs_containing_block->RemovePositionedObjects(this, kNewContainingBlock);
-  }
-
   PropagateStyleToAnonymousChildren();
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
index 72623076..28cdeb5c 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
@@ -237,7 +237,7 @@
   }
 
   WritingMode GetWritingMode() const {
-    DCHECK_EQ(Type(), kText);
+    DCHECK(Type() == kText || Type() == kGeneratedText) << this;
     return Style().GetWritingMode();
   }
 
diff --git a/third_party/blink/renderer/core/layout/shapes/shape.cc b/third_party/blink/renderer/core/layout/shapes/shape.cc
index a84d787..1f296d6 100644
--- a/third_party/blink/renderer/core/layout/shapes/shape.cc
+++ b/third_party/blink/renderer/core/layout/shapes/shape.cc
@@ -285,8 +285,8 @@
     const IntRect& image_rect,
     const IntRect& margin_rect) {
   DOMArrayBuffer* array_buffer = DOMArrayBuffer::Create(contents);
-  DOMUint8ClampedArray* pixel_array =
-      DOMUint8ClampedArray::Create(array_buffer, 0, array_buffer->ByteLength());
+  DOMUint8ClampedArray* pixel_array = DOMUint8ClampedArray::Create(
+      array_buffer, 0, array_buffer->DeprecatedByteLengthAsUnsigned());
 
   unsigned pixel_array_offset = 3;  // Each pixel is four bytes: RGBA.
   uint8_t alpha_pixel_threshold = threshold * 255;
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index dd512c49..b323e07 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -173,6 +173,7 @@
   ip_address_space_ = params_->ip_address_space;
   grant_load_local_resources_ = params_->grant_load_local_resources;
   force_fetch_cache_mode_ = params_->force_fetch_cache_mode;
+  frame_policy_ = params_->frame_policy;
 
   WebNavigationTimings& timings = params_->navigation_timings;
   if (!timings.input_start.is_null())
@@ -1487,6 +1488,7 @@
           .WithSrcdocDocument(loading_srcdoc_)
           .WithBlockedByCSP(was_blocked_by_csp_)
           .WithGrantLoadLocalResources(grant_load_local_resources_)
+          .WithFramePolicy(frame_policy_)
           .WithNewRegistrationContext()
           .WithFeaturePolicyHeader(feature_policy.ToString())
           .WithOriginTrialsHeader(
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h
index 5c20f73..2cf5fdbf 100644
--- a/third_party/blink/renderer/core/loader/document_loader.h
+++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -415,6 +415,7 @@
       network::mojom::IPAddressSpace::kUnknown;
   bool grant_load_local_resources_ = false;
   base::Optional<blink::mojom::FetchCacheMode> force_fetch_cache_mode_;
+  base::Optional<FramePolicy> frame_policy_;
 
   // Params are saved in constructor and are cleared after StartLoading().
   // TODO(dgozman): remove once StartLoading is merged with constructor.
diff --git a/third_party/blink/renderer/core/loader/document_loader_test.cc b/third_party/blink/renderer/core/loader/document_loader_test.cc
index 06f85701..914a685 100644
--- a/third_party/blink/renderer/core/loader/document_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/document_loader_test.cc
@@ -8,6 +8,7 @@
 #include "base/auto_reset.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/frame/frame_owner_element_type.h"
+#include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-shared.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_url_loader_client.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
@@ -257,6 +258,31 @@
   EXPECT_EQ(KURL("https://example.com"), child_document->Loader()->Url());
 }
 
+TEST_F(DocumentLoaderSimTest, FramePolicyIntegrityOnNavigationCommit) {
+  SimRequest main_resource("https://example.com", "text/html");
+  SimRequest iframe_resource("https://example.com/foo.html", "text/html");
+  LoadURL("https://example.com");
+
+  main_resource.Write(R"(
+    <iframe id='frame1'></iframe>
+    <script>
+      const iframe = document.getElementById('frame1');
+      iframe.src = 'https://example.com/foo.html'; // navigation triggered
+      iframe.allow = "payment 'none'"; // should not take effect until the
+                                       // next navigation on iframe
+    </script>
+  )");
+
+  main_resource.Finish();
+  iframe_resource.Finish();
+
+  auto* child_frame = To<WebLocalFrameImpl>(MainFrame().FirstChild());
+  auto* child_document = child_frame->GetFrame()->GetDocument();
+
+  EXPECT_TRUE(child_document->IsFeatureEnabled(
+      blink::mojom::FeaturePolicyFeature::kPayment));
+}
+
 TEST_F(DocumentLoaderTest, CommitsDeferredOnSameOriginNavigation) {
   const KURL& requestor_url =
       KURL(NullURL(), "https://www.example.com/foo.html");
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index 7f07a8b..69d405e 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -206,6 +206,10 @@
 
   auto navigation_params = std::make_unique<WebNavigationParams>();
   navigation_params->url = KURL(g_empty_string);
+  navigation_params->frame_policy =
+      frame_->Owner() ? base::make_optional(frame_->Owner()->GetFramePolicy())
+                      : base::nullopt;
+
   provisional_document_loader_ = Client()->CreateDocumentLoader(
       frame_, kWebNavigationTypeOther, std::move(navigation_params),
       nullptr /* extra_data */);
diff --git a/third_party/blink/renderer/core/mathml/mathml_attribute_names.json5 b/third_party/blink/renderer/core/mathml/mathml_attribute_names.json5
index a50a993..7e4430d5 100644
--- a/third_party/blink/renderer/core/mathml/mathml_attribute_names.json5
+++ b/third_party/blink/renderer/core/mathml/mathml_attribute_names.json5
@@ -9,5 +9,8 @@
   data: [
     "definitionURL",
     "encoding",
+    "mathbackground",
+    "mathcolor",
+    "mathsize",
   ],
 }
diff --git a/third_party/blink/renderer/core/mathml/mathml_element.cc b/third_party/blink/renderer/core/mathml/mathml_element.cc
index 16cf2e5b..52bb3e3f 100644
--- a/third_party/blink/renderer/core/mathml/mathml_element.cc
+++ b/third_party/blink/renderer/core/mathml/mathml_element.cc
@@ -5,6 +5,8 @@
 #include "third_party/blink/renderer/core/mathml/mathml_element.h"
 
 #include "third_party/blink/renderer/bindings/core/v8/script_event_listener.h"
+#include "third_party/blink/renderer/core/css/css_property_name.h"
+#include "third_party/blink/renderer/core/css_value_keywords.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 
 namespace blink {
@@ -16,6 +18,57 @@
 
 MathMLElement::~MathMLElement() {}
 
+static inline bool IsValidDirAttribute(const AtomicString& value) {
+  return DeprecatedEqualIgnoringCase(value, "ltr") ||
+         DeprecatedEqualIgnoringCase(value, "rtl");
+}
+
+// Keywords from MathML3 and CSS font-size are skipped.
+static inline bool IsDisallowedMathSizeAttribute(const AtomicString& value) {
+  return DeprecatedEqualIgnoringCase(value, "medium") ||
+         value.EndsWith("large", kTextCaseASCIIInsensitive) ||
+         value.EndsWith("small", kTextCaseASCIIInsensitive) ||
+         DeprecatedEqualIgnoringCase(value, "smaller") ||
+         DeprecatedEqualIgnoringCase(value, "larger");
+}
+
+bool MathMLElement::IsPresentationAttribute(const QualifiedName& name) const {
+  // TODO(crbug.com/1023292, crbug.com/1023296): add support for display,
+  // displaystyle and scriptlevel.
+  if (name == html_names::kDirAttr || name == mathml_names::kMathsizeAttr ||
+      name == mathml_names::kMathcolorAttr ||
+      name == mathml_names::kMathbackgroundAttr)
+    return true;
+  return Element::IsPresentationAttribute(name);
+}
+
+void MathMLElement::CollectStyleForPresentationAttribute(
+    const QualifiedName& name,
+    const AtomicString& value,
+    MutableCSSPropertyValueSet* style) {
+  // TODO(crbug.com/1023292, crbug.com/1023296): add support for display,
+  // displaystyle and scriptlevel.
+  if (name == html_names::kDirAttr) {
+    if (IsValidDirAttribute(value)) {
+      AddPropertyToPresentationAttributeStyle(style, CSSPropertyID::kDirection,
+                                              value);
+    }
+  } else if (name == mathml_names::kMathsizeAttr) {
+    if (!IsDisallowedMathSizeAttribute(value)) {
+      AddPropertyToPresentationAttributeStyle(style, CSSPropertyID::kFontSize,
+                                              value);
+    }
+  } else if (name == mathml_names::kMathbackgroundAttr) {
+    AddPropertyToPresentationAttributeStyle(
+        style, CSSPropertyID::kBackgroundColor, value);
+  } else if (name == mathml_names::kMathcolorAttr) {
+    AddPropertyToPresentationAttributeStyle(style, CSSPropertyID::kColor,
+                                            value);
+  } else {
+    Element::CollectStyleForPresentationAttribute(name, value, style);
+  }
+}
+
 void MathMLElement::ParseAttribute(const AttributeModificationParams& param) {
   const AtomicString& event_name =
       HTMLElement::EventNameForAttributeName(param.name);
diff --git a/third_party/blink/renderer/core/mathml/mathml_element.h b/third_party/blink/renderer/core/mathml/mathml_element.h
index ac9176f..62d6290 100644
--- a/third_party/blink/renderer/core/mathml/mathml_element.h
+++ b/third_party/blink/renderer/core/mathml/mathml_element.h
@@ -26,6 +26,11 @@
   }
 
  private:
+  bool IsPresentationAttribute(const QualifiedName&) const final;
+  void CollectStyleForPresentationAttribute(const QualifiedName&,
+                                            const AtomicString&,
+                                            MutableCSSPropertyValueSet*) final;
+
   void ParseAttribute(const AttributeModificationParams&) final;
 
   bool IsMathMLElement() const =
diff --git a/third_party/blink/renderer/core/mojo/mojo_handle.cc b/third_party/blink/renderer/core/mojo/mojo_handle.cc
index 02038395..22e24a9 100644
--- a/third_party/blink/renderer/core/mojo/mojo_handle.cc
+++ b/third_party/blink/renderer/core/mojo/mojo_handle.cc
@@ -70,7 +70,7 @@
   if (buffer.IsArrayBuffer()) {
     DOMArrayBuffer* array = buffer.GetAsArrayBuffer();
     bytes = array->Data();
-    num_bytes = array->ByteLength();
+    num_bytes = array->ByteLengthAsSizeT();
   } else {
     DOMArrayBufferView* view = buffer.GetAsArrayBufferView().View();
     bytes = view->BaseAddress();
@@ -154,7 +154,7 @@
   if (buffer.IsArrayBuffer()) {
     DOMArrayBuffer* array = buffer.GetAsArrayBuffer();
     elements = array->Data();
-    num_bytes = array->ByteLength();
+    num_bytes = array->DeprecatedByteLengthAsUnsigned();
   } else {
     DOMArrayBufferView* view = buffer.GetAsArrayBufferView().View();
     elements = view->BaseAddress();
@@ -217,7 +217,7 @@
   if (buffer.IsArrayBuffer()) {
     DOMArrayBuffer* array = buffer.GetAsArrayBuffer();
     elements = array->Data();
-    num_bytes = array->ByteLength();
+    num_bytes = array->DeprecatedByteLengthAsUnsigned();
   } else {
     DOMArrayBufferView* view = buffer.GetAsArrayBufferView().View();
     elements = view->BaseAddress();
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_test.cc b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_test.cc
index b355c46..f4816f6 100644
--- a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_test.cc
@@ -1556,30 +1556,52 @@
     </div>
   )HTML");
 
-  // The initial count should be zero.
+  // The initial counts should be zero.
   histogram_tester.ExpectTotalCount("Blink.ScrollingCoordinator.UpdateTime", 0);
+  histogram_tester.ExpectTotalCount(
+      "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 0);
+  histogram_tester.ExpectTotalCount(
+      "Blink.ScrollingCoordinator.UpdateTime.PostFCP", 0);
 
-  // After an initial compositing update, we should have one scrolling update.
+  // After an initial compositing update, we should have one scrolling update
+  // recorded as PreFCP.
   ForceFullCompositingUpdate();
   histogram_tester.ExpectTotalCount("Blink.ScrollingCoordinator.UpdateTime", 1);
+  histogram_tester.ExpectTotalCount(
+      "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 1);
+  histogram_tester.ExpectTotalCount(
+      "Blink.ScrollingCoordinator.UpdateTime.PostFCP", 0);
 
   // An update with no scrolling changes should not cause a scrolling update.
   ForceFullCompositingUpdate();
   histogram_tester.ExpectTotalCount("Blink.ScrollingCoordinator.UpdateTime", 1);
+  histogram_tester.ExpectTotalCount(
+      "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 1);
+  histogram_tester.ExpectTotalCount(
+      "Blink.ScrollingCoordinator.UpdateTime.PostFCP", 0);
 
   // A change to background color does not need to cause a scrolling update but,
   // because hit test display items paint, we also cause a scrolling coordinator
-  // update when the background paints.
+  // update when the background paints. Also render some text to get past FCP.
   auto* background = GetFrame()->GetDocument()->getElementById("bg");
   background->removeAttribute(html_names::kStyleAttr);
+  background->SetInnerHTMLFromString("Some Text");
   ForceFullCompositingUpdate();
   histogram_tester.ExpectTotalCount("Blink.ScrollingCoordinator.UpdateTime", 2);
+  histogram_tester.ExpectTotalCount(
+      "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 1);
+  histogram_tester.ExpectTotalCount(
+      "Blink.ScrollingCoordinator.UpdateTime.PostFCP", 1);
 
   // Removing a scrollable area should cause a scrolling update.
   auto* scroller = GetFrame()->GetDocument()->getElementById("scroller");
   scroller->removeAttribute(html_names::kStyleAttr);
   ForceFullCompositingUpdate();
   histogram_tester.ExpectTotalCount("Blink.ScrollingCoordinator.UpdateTime", 3);
+  histogram_tester.ExpectTotalCount(
+      "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 1);
+  histogram_tester.ExpectTotalCount(
+      "Blink.ScrollingCoordinator.UpdateTime.PostFCP", 2);
 }
 
 TEST_P(ScrollingCoordinatorTest, NonCompositedNonFastScrollableRegion) {
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index fe6ea2c0..e208faa 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -2353,6 +2353,18 @@
   if (layer_->Size().IsEmpty())
     return false;
 
+  const auto* box = GetLayoutBox();
+
+  // Although trivial 3D transforms are not always a direct compositing reason
+  // (see CompositingReasonFinder::RequiresCompositingFor3DTransform), we treat
+  // them as one for composited scrolling. This is because of the amount of
+  // content that depends on this optimization, and because of the long-term
+  // desire to use composited scrolling whenever possible.
+  if (box->HasTransformRelatedProperty() &&
+      box->StyleRef().Has3DTransformOperation()) {
+    return true;
+  }
+
   if (!force_prefer_compositing_to_lcd_text &&
       !LayerNodeMayNeedCompositedScrolling(layer_)) {
     return false;
@@ -2362,7 +2374,6 @@
 
   // TODO(flackr): Allow integer transforms as long as all of the ancestor
   // transforms are also integer.
-  const LayoutBox* box = GetLayoutBox();
   bool background_supports_lcd_text =
       box->StyleRef().IsStackingContext() &&
       (box->GetBackgroundPaintLocation(
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
index aa29b73..ba3c97a7 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
@@ -4,7 +4,9 @@
 
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 
+#include "base/test/scoped_feature_list.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
@@ -1287,4 +1289,55 @@
   EXPECT_NE(paint_layer->GetScrollableArea()->Resizer(), nullptr);
 }
 
+class PaintLayerScrollableAreaCompositingTest
+    : public PaintLayerScrollableAreaTestBase,
+      public testing::WithParamInterface<unsigned>,
+      private ScopedCompositeAfterPaintForTest {
+ public:
+  PaintLayerScrollableAreaCompositingTest()
+      : ScopedCompositeAfterPaintForTest(GetParam() & kCompositeAfterPaint) {
+    if (GetParam() & kDoNotCompositeTrivial3D) {
+      scoped_feature_list_.InitAndEnableFeature(
+          blink::features::kDoNotCompositeTrivial3D);
+    } else {
+      scoped_feature_list_.InitAndDisableFeature(
+          blink::features::kDoNotCompositeTrivial3D);
+    }
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+INSTANTIATE_DO_NOT_COMPOSITE_TRIVIAL_3D_P(
+    PaintLayerScrollableAreaCompositingTest);
+
+// Test that a trivial 3D transform results in composited scrolling.
+TEST_P(PaintLayerScrollableAreaCompositingTest, CompositeWithTrivial3D) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      #scroller {
+        width: 100px;
+        height: 100px;
+        overflow: scroll;
+        transform: translateZ(0);
+      }
+      #scrolled {
+        width: 200px;
+        height: 200px;
+      }
+    </style>
+    <div id="scroller">
+      <div id="scrolled"></div>
+    </div>
+  )HTML");
+
+  LayoutBox* scroller = ToLayoutBox(GetLayoutObjectByElementId("scroller"));
+  PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea();
+  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+    EXPECT_TRUE(scrollable_area->NeedsCompositedScrolling());
+  const auto* properties = scroller->FirstFragment().PaintProperties();
+  EXPECT_TRUE(properties->ScrollTranslation()->HasDirectCompositingReasons());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/testing/internals.cc b/third_party/blink/renderer/core/testing/internals.cc
index 4e2b22f..0d2cc62 100644
--- a/third_party/blink/renderer/core/testing/internals.cc
+++ b/third_party/blink/renderer/core/testing/internals.cc
@@ -2795,7 +2795,7 @@
 scoped_refptr<SerializedScriptValue> Internals::deserializeBuffer(
     DOMArrayBuffer* buffer) const {
   return SerializedScriptValue::Create(static_cast<const char*>(buffer->Data()),
-                                       buffer->ByteLength());
+                                       buffer->ByteLengthAsSizeT());
 }
 
 DOMArrayBuffer* Internals::serializeWithInlineWasm(ScriptValue value) const {
diff --git a/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer.h b/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer.h
index af65667..0056be39 100644
--- a/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer.h
+++ b/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer.h
@@ -44,27 +44,27 @@
   USING_FAST_MALLOC(ArrayBuffer);
 
  public:
-  static inline scoped_refptr<ArrayBuffer> Create(unsigned num_elements,
-                                                  unsigned element_byte_size);
+  static inline scoped_refptr<ArrayBuffer> Create(size_t num_elements,
+                                                  size_t element_byte_size);
   static inline scoped_refptr<ArrayBuffer> Create(ArrayBuffer*);
   static inline scoped_refptr<ArrayBuffer> Create(const void* source,
                                                   size_t byte_length);
   static inline scoped_refptr<ArrayBuffer> Create(ArrayBufferContents&);
 
   static inline scoped_refptr<ArrayBuffer> CreateOrNull(
-      unsigned num_elements,
-      unsigned element_byte_size);
+      size_t num_elements,
+      size_t element_byte_size);
 
   // Only for use by DOMArrayBuffer::CreateUninitializedOrNull().
   static inline scoped_refptr<ArrayBuffer> CreateUninitializedOrNull(
-      unsigned num_elements,
-      unsigned element_byte_size);
+      size_t num_elements,
+      size_t element_byte_size);
 
   static inline scoped_refptr<ArrayBuffer> CreateShared(
-      unsigned num_elements,
-      unsigned element_byte_size);
+      size_t num_elements,
+      size_t element_byte_size);
   static inline scoped_refptr<ArrayBuffer> CreateShared(const void* source,
-                                                        unsigned byte_length);
+                                                        size_t byte_length);
 
   inline void* Data();
   inline const void* Data() const;
@@ -98,16 +98,16 @@
 
  private:
   static inline scoped_refptr<ArrayBuffer> Create(
-      unsigned num_elements,
-      unsigned element_byte_size,
+      size_t num_elements,
+      size_t element_byte_size,
       ArrayBufferContents::InitializationPolicy);
   static inline scoped_refptr<ArrayBuffer> CreateOrNull(
-      unsigned num_elements,
-      unsigned element_byte_size,
+      size_t num_elements,
+      size_t element_byte_size,
       ArrayBufferContents::InitializationPolicy);
   static inline scoped_refptr<ArrayBuffer> CreateShared(
-      unsigned num_elements,
-      unsigned element_byte_size,
+      size_t num_elements,
+      size_t element_byte_size,
       ArrayBufferContents::InitializationPolicy);
 
   inline unsigned ClampIndex(unsigned index) const;
@@ -117,8 +117,8 @@
   bool is_detached_;
 };
 
-scoped_refptr<ArrayBuffer> ArrayBuffer::Create(unsigned num_elements,
-                                               unsigned element_byte_size) {
+scoped_refptr<ArrayBuffer> ArrayBuffer::Create(size_t num_elements,
+                                               size_t element_byte_size) {
   return Create(num_elements, element_byte_size,
                 ArrayBufferContents::kZeroInitialize);
 }
@@ -146,23 +146,22 @@
   return base::AdoptRef(new ArrayBuffer(contents));
 }
 
-scoped_refptr<ArrayBuffer> ArrayBuffer::CreateOrNull(
-    unsigned num_elements,
-    unsigned element_byte_size) {
+scoped_refptr<ArrayBuffer> ArrayBuffer::CreateOrNull(size_t num_elements,
+                                                     size_t element_byte_size) {
   return CreateOrNull(num_elements, element_byte_size,
                       ArrayBufferContents::kZeroInitialize);
 }
 
 scoped_refptr<ArrayBuffer> ArrayBuffer::CreateUninitializedOrNull(
-    unsigned num_elements,
-    unsigned element_byte_size) {
+    size_t num_elements,
+    size_t element_byte_size) {
   return CreateOrNull(num_elements, element_byte_size,
                       ArrayBufferContents::kDontInitialize);
 }
 
 scoped_refptr<ArrayBuffer> ArrayBuffer::Create(
-    unsigned num_elements,
-    unsigned element_byte_size,
+    size_t num_elements,
+    size_t element_byte_size,
     ArrayBufferContents::InitializationPolicy policy) {
   ArrayBufferContents contents(num_elements, element_byte_size,
                                ArrayBufferContents::kNotShared, policy);
@@ -172,8 +171,8 @@
 }
 
 scoped_refptr<ArrayBuffer> ArrayBuffer::CreateOrNull(
-    unsigned num_elements,
-    unsigned element_byte_size,
+    size_t num_elements,
+    size_t element_byte_size,
     ArrayBufferContents::InitializationPolicy policy) {
   ArrayBufferContents contents(num_elements, element_byte_size,
                                ArrayBufferContents::kNotShared, policy);
@@ -182,15 +181,14 @@
   return base::AdoptRef(new ArrayBuffer(contents));
 }
 
-scoped_refptr<ArrayBuffer> ArrayBuffer::CreateShared(
-    unsigned num_elements,
-    unsigned element_byte_size) {
+scoped_refptr<ArrayBuffer> ArrayBuffer::CreateShared(size_t num_elements,
+                                                     size_t element_byte_size) {
   return CreateShared(num_elements, element_byte_size,
                       ArrayBufferContents::kZeroInitialize);
 }
 
 scoped_refptr<ArrayBuffer> ArrayBuffer::CreateShared(const void* source,
-                                                     unsigned byte_length) {
+                                                     size_t byte_length) {
   ArrayBufferContents contents(byte_length, 1, ArrayBufferContents::kShared,
                                ArrayBufferContents::kDontInitialize);
   CHECK(contents.DataShared());
@@ -200,8 +198,8 @@
 }
 
 scoped_refptr<ArrayBuffer> ArrayBuffer::CreateShared(
-    unsigned num_elements,
-    unsigned element_byte_size,
+    size_t num_elements,
+    size_t element_byte_size,
     ArrayBufferContents::InitializationPolicy policy) {
   ArrayBufferContents contents(num_elements, element_byte_size,
                                ArrayBufferContents::kShared, policy);
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc
index bf7452e..5dd94539 100644
--- a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc
+++ b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc
@@ -41,8 +41,8 @@
                               ArrayBufferContents& result) {
   DOMArrayBuffer* to_transfer = this;
   if (!IsDetachable(isolate)) {
-    to_transfer = DOMArrayBuffer::Create(Buffer()->Data(),
-                                         Buffer()->ByteLengthAsUnsigned());
+    to_transfer =
+        DOMArrayBuffer::Create(Buffer()->Data(), Buffer()->ByteLengthAsSizeT());
   }
 
   if (!to_transfer->Buffer()->Transfer(result))
@@ -59,8 +59,8 @@
 }
 
 DOMArrayBuffer* DOMArrayBuffer::CreateUninitializedOrNull(
-    unsigned num_elements,
-    unsigned element_byte_size) {
+    size_t num_elements,
+    size_t element_byte_size) {
   scoped_refptr<ArrayBuffer> buffer =
       ArrayBuffer::CreateUninitializedOrNull(num_elements, element_byte_size);
   if (!buffer)
@@ -78,7 +78,7 @@
   v8::Local<v8::Object> wrapper;
   {
     v8::Context::Scope context_scope(creation_context->CreationContext());
-    wrapper = v8::ArrayBuffer::New(isolate, Data(), ByteLength());
+    wrapper = v8::ArrayBuffer::New(isolate, Data(), ByteLengthAsSizeT());
   }
 
   return AssociateWithWrapper(isolate, wrapper_type_info, wrapper);
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h
index b3cb9e0..0b47bc1 100644
--- a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h
+++ b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h
@@ -20,11 +20,10 @@
   static DOMArrayBuffer* Create(scoped_refptr<ArrayBuffer> buffer) {
     return MakeGarbageCollected<DOMArrayBuffer>(std::move(buffer));
   }
-  static DOMArrayBuffer* Create(unsigned num_elements,
-                                unsigned element_byte_size) {
+  static DOMArrayBuffer* Create(size_t num_elements, size_t element_byte_size) {
     return Create(ArrayBuffer::Create(num_elements, element_byte_size));
   }
-  static DOMArrayBuffer* Create(const void* source, unsigned byte_length) {
+  static DOMArrayBuffer* Create(const void* source, size_t byte_length) {
     return Create(ArrayBuffer::Create(source, byte_length));
   }
   static DOMArrayBuffer* Create(ArrayBufferContents& contents) {
@@ -36,8 +35,8 @@
   // Only for use by XMLHttpRequest::responseArrayBuffer,
   // Internals::serializeObject, and
   // FetchDataLoaderAsArrayBuffer::OnStateChange.
-  static DOMArrayBuffer* CreateUninitializedOrNull(unsigned num_elements,
-                                                   unsigned element_byte_size);
+  static DOMArrayBuffer* CreateUninitializedOrNull(size_t num_elements,
+                                                   size_t element_byte_size);
 
   explicit DOMArrayBuffer(scoped_refptr<ArrayBuffer> buffer)
       : DOMArrayBufferBase(std::move(buffer)) {}
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer_base.h b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer_base.h
index fa42233d..150510b 100644
--- a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer_base.h
+++ b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer_base.h
@@ -21,7 +21,17 @@
 
   const void* Data() const { return Buffer()->Data(); }
   void* Data() { return Buffer()->Data(); }
-  unsigned ByteLength() const { return Buffer()->ByteLengthAsUnsigned(); }
+
+  size_t ByteLengthAsSizeT() const { return Buffer()->ByteLengthAsSizeT(); }
+
+  // This function is deprecated and should not be used. Use {ByteLengthAsSizeT}
+  // instead.
+  unsigned DeprecatedByteLengthAsUnsigned() const {
+    size_t size = ByteLengthAsSizeT();
+    CHECK_LE(size, static_cast<size_t>(std::numeric_limits<unsigned>::max()));
+    return static_cast<unsigned>(size);
+  }
+
   bool IsDetached() const { return Buffer()->IsDetached(); }
   bool IsShared() const { return Buffer()->IsShared(); }
 
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_array_piece.h b/third_party/blink/renderer/core/typed_arrays/dom_array_piece.h
index 98e910a4..a1ebc959c 100644
--- a/third_party/blink/renderer/core/typed_arrays/dom_array_piece.h
+++ b/third_party/blink/renderer/core/typed_arrays/dom_array_piece.h
@@ -42,7 +42,7 @@
                 InitWithUnionOption = kTreatNullAsNull);
 
   bool operator==(const DOMArrayBuffer& other) const {
-    return ByteLength() == other.ByteLength() &&
+    return ByteLength() == other.DeprecatedByteLengthAsUnsigned() &&
            memcmp(Data(), other.Data(), ByteLength()) == 0;
   }
 
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_shared_array_buffer.cc b/third_party/blink/renderer/core/typed_arrays/dom_shared_array_buffer.cc
index aeee1b2..2aaa4b83 100644
--- a/third_party/blink/renderer/core/typed_arrays/dom_shared_array_buffer.cc
+++ b/third_party/blink/renderer/core/typed_arrays/dom_shared_array_buffer.cc
@@ -14,8 +14,8 @@
   DCHECK(!DOMDataStore::ContainsWrapper(this, isolate));
 
   const WrapperTypeInfo* wrapper_type_info = this->GetWrapperTypeInfo();
-  v8::Local<v8::Object> wrapper =
-      v8::SharedArrayBuffer::New(isolate, Buffer()->DataShared(), ByteLength());
+  v8::Local<v8::Object> wrapper = v8::SharedArrayBuffer::New(
+      isolate, Buffer()->DataShared(), ByteLengthAsSizeT());
 
   return AssociateWithWrapper(isolate, wrapper_type_info, wrapper);
 }
diff --git a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
index dbaee70..cee86385b 100644
--- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
+++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
@@ -429,7 +429,7 @@
           binary_response_builder_->size(), 1);
       if (buffer) {
         bool result = binary_response_builder_->GetBytes(
-            buffer->Data(), static_cast<size_t>(buffer->ByteLength()));
+            buffer->Data(), buffer->ByteLengthAsSizeT());
         DCHECK(result);
         response_array_buffer_ = buffer;
       }
@@ -923,7 +923,7 @@
                           ExceptionState& exception_state) {
   NETWORK_DVLOG(1) << this << " send() ArrayBuffer " << body;
 
-  SendBytesData(body->Data(), body->ByteLength(), exception_state);
+  SendBytesData(body->Data(), body->ByteLengthAsSizeT(), exception_state);
 }
 
 void XMLHttpRequest::send(DOMArrayBufferView* body,
diff --git a/third_party/blink/renderer/modules/cache_storage/cache.cc b/third_party/blink/renderer/modules/cache_storage/cache.cc
index e4b240c..6447ca4c 100644
--- a/third_party/blink/renderer/modules/cache_storage/cache.cc
+++ b/third_party/blink/renderer/modules/cache_storage/cache.cc
@@ -458,9 +458,10 @@
 
     auto blob_data = std::make_unique<BlobData>();
     blob_data->SetContentType(mime_type_);
-    blob_data->AppendBytes(array_buffer->Data(), array_buffer->ByteLength());
+    blob_data->AppendBytes(array_buffer->Data(),
+                           array_buffer->ByteLengthAsSizeT());
     batch_operation->response->blob = BlobDataHandle::Create(
-        std::move(blob_data), array_buffer->ByteLength());
+        std::move(blob_data), array_buffer->ByteLengthAsSizeT());
 
     scoped_refptr<CachedMetadata> cached_metadata =
         GenerateFullCodeCache(array_buffer);
@@ -520,7 +521,7 @@
     return V8CodeCache::GenerateFullCodeCache(
         script_state_,
         text_decoder->Decode(static_cast<const char*>(array_buffer->Data()),
-                             array_buffer->ByteLength()),
+                             array_buffer->ByteLengthAsSizeT()),
         url_, text_decoder->Encoding(), opaque_mode_);
   }
 
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
index b16a090..9245e2cb 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -1632,7 +1632,7 @@
   ImageData* imageData = ImageData::Create(
       image_data_rect.Size(),
       NotShared<DOMUint8ClampedArray>(DOMUint8ClampedArray::Create(
-          array_buffer, 0, array_buffer->ByteLength())),
+          array_buffer, 0, array_buffer->DeprecatedByteLengthAsUnsigned())),
       color_settings);
 
   if (!IsPaint2D()) {
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc b/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc
index 9591363..6244ac5 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc
@@ -33,8 +33,8 @@
       DOMArrayBuffer* png_data) override {
     DCHECK(!IsMainThread());
     std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create(
-        SegmentReader::CreateFromSkData(
-            SkData::MakeWithoutCopy(png_data->Data(), png_data->ByteLength())),
+        SegmentReader::CreateFromSkData(SkData::MakeWithoutCopy(
+            png_data->Data(), png_data->ByteLengthAsSizeT())),
         true, ImageDecoder::kAlphaPremultiplied, ImageDecoder::kDefaultBitDepth,
         ColorBehavior::Tag());
     sk_sp<SkImage> image = nullptr;
@@ -77,7 +77,7 @@
 
     String wtf_string =
         String::FromUTF8(reinterpret_cast<const LChar*>(raw_data->Data()),
-                         raw_data->ByteLength());
+                         raw_data->ByteLengthAsSizeT());
     DCHECK(wtf_string.IsSafeToSendToAnotherThread());
     PostCrossThreadTask(
         *task_runner, FROM_HERE,
diff --git a/third_party/blink/renderer/modules/compression/deflate_transformer.cc b/third_party/blink/renderer/modules/compression/deflate_transformer.cc
index abc8186..65327f5 100644
--- a/third_party/blink/renderer/modules/compression/deflate_transformer.cc
+++ b/third_party/blink/renderer/modules/compression/deflate_transformer.cc
@@ -73,7 +73,7 @@
   DCHECK(buffer_source.IsArrayBuffer());
   const auto* array_buffer = buffer_source.GetAsArrayBuffer();
   const uint8_t* start = static_cast<const uint8_t*>(array_buffer->Data());
-  wtf_size_t length = array_buffer->ByteLength();
+  wtf_size_t length = array_buffer->DeprecatedByteLengthAsUnsigned();
   Deflate(start, length, IsFinished(false), controller, exception_state);
 
   return ScriptPromise::CastUndefined(script_state_);
diff --git a/third_party/blink/renderer/modules/compression/inflate_transformer.cc b/third_party/blink/renderer/modules/compression/inflate_transformer.cc
index ef9fa2b..fa873be 100644
--- a/third_party/blink/renderer/modules/compression/inflate_transformer.cc
+++ b/third_party/blink/renderer/modules/compression/inflate_transformer.cc
@@ -70,7 +70,7 @@
   DCHECK(buffer_source.IsArrayBuffer());
   const auto* array_buffer = buffer_source.GetAsArrayBuffer();
   const uint8_t* start = static_cast<const uint8_t*>(array_buffer->Data());
-  wtf_size_t length = array_buffer->ByteLength();
+  wtf_size_t length = array_buffer->DeprecatedByteLengthAsUnsigned();
   Inflate(start, length, IsFinished(false), controller, exception_state);
 
   return ScriptPromise::CastUndefined(script_state_);
diff --git a/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc b/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc
index c9274ba8..e0229ee 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc
+++ b/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc
@@ -173,7 +173,7 @@
     const blink::ArrayBufferOrArrayBufferView& buffer,
     unsigned length) {
   if (buffer.IsArrayBuffer() &&
-      (buffer.GetAsArrayBuffer()->ByteLength() != length)) {
+      (buffer.GetAsArrayBuffer()->DeprecatedByteLengthAsUnsigned() != length)) {
     return Vector<uint8_t>();
   }
 
@@ -193,7 +193,7 @@
   Vector<uint8_t> vector;
   if (buffer.IsArrayBuffer()) {
     vector.Append(static_cast<uint8_t*>(buffer.GetAsArrayBuffer()->Data()),
-                  buffer.GetAsArrayBuffer()->ByteLength());
+                  buffer.GetAsArrayBuffer()->DeprecatedByteLengthAsUnsigned());
   } else {
     DCHECK(buffer.IsArrayBufferView());
     vector.Append(static_cast<uint8_t*>(
diff --git a/third_party/blink/renderer/modules/encoding/text_decoder.cc b/third_party/blink/renderer/modules/encoding/text_decoder.cc
index 5dd63192..2b24a83 100644
--- a/third_party/blink/renderer/modules/encoding/text_decoder.cc
+++ b/third_party/blink/renderer/modules/encoding/text_decoder.cc
@@ -91,7 +91,7 @@
   DCHECK(input.IsArrayBuffer());
   const char* start =
       static_cast<const char*>(input.GetAsArrayBuffer()->Data());
-  uint32_t length = input.GetAsArrayBuffer()->ByteLength();
+  uint32_t length = input.GetAsArrayBuffer()->DeprecatedByteLengthAsUnsigned();
   return decode(start, length, options, exception_state);
 }
 
diff --git a/third_party/blink/renderer/modules/encoding/text_decoder_stream.cc b/third_party/blink/renderer/modules/encoding/text_decoder_stream.cc
index 77c5d7f..be34feb7 100644
--- a/third_party/blink/renderer/modules/encoding/text_decoder_stream.cc
+++ b/third_party/blink/renderer/modules/encoding/text_decoder_stream.cc
@@ -62,7 +62,7 @@
     DCHECK(bufferSource.IsArrayBuffer());
     const auto* array_buffer = bufferSource.GetAsArrayBuffer();
     const char* start = static_cast<const char*>(array_buffer->Data());
-    uint32_t length = array_buffer->ByteLength();
+    uint32_t length = array_buffer->DeprecatedByteLengthAsUnsigned();
     DecodeAndEnqueue(start, length, WTF::FlushBehavior::kDoNotFlush, controller,
                      exception_state);
 
diff --git a/third_party/blink/renderer/modules/encryptedmedia/media_key_session.cc b/third_party/blink/renderer/modules/encryptedmedia/media_key_session.cc
index c72276e1..f58ae9c9 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/media_key_session.cc
+++ b/third_party/blink/renderer/modules/encryptedmedia/media_key_session.cc
@@ -517,7 +517,7 @@
   // initializeNewSession() in Chromium will execute steps 10.1 to 10.9.
   session_->InitializeNewSession(
       init_data_type, static_cast<unsigned char*>(init_data_buffer->Data()),
-      init_data_buffer->ByteLength(), session_type_, result->Result());
+      init_data_buffer->ByteLengthAsSizeT(), session_type_, result->Result());
 
   // Remaining steps (10.10) executed in finishGenerateRequest(),
   // called when |result| is resolved.
@@ -732,7 +732,7 @@
 
   // update() in Chromium will execute steps 6.1 through 6.8.
   session_->Update(static_cast<unsigned char*>(sanitized_response->Data()),
-                   sanitized_response->ByteLength(), result->Result());
+                   sanitized_response->ByteLengthAsSizeT(), result->Result());
 
   // Last step (6.8.2 Resolve promise) will be done when |result| is resolved.
 }
diff --git a/third_party/blink/renderer/modules/encryptedmedia/media_key_status_map.cc b/third_party/blink/renderer/modules/encryptedmedia/media_key_status_map.cc
index a0655337..a81720d 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/media_key_status_map.cc
+++ b/third_party/blink/renderer/modules/encryptedmedia/media_key_status_map.cc
@@ -45,15 +45,15 @@
       return b->KeyId();
 
     // Compare the bytes.
-    int result =
-        memcmp(a->KeyId()->Data(), b->KeyId()->Data(),
-               std::min(a->KeyId()->ByteLength(), b->KeyId()->ByteLength()));
+    int result = memcmp(a->KeyId()->Data(), b->KeyId()->Data(),
+                        std::min(a->KeyId()->ByteLengthAsSizeT(),
+                                 b->KeyId()->ByteLengthAsSizeT()));
     if (result != 0)
       return result < 0;
 
     // KeyIds are equal to the shared length, so the shorter string is <.
-    DCHECK_NE(a->KeyId()->ByteLength(), b->KeyId()->ByteLength());
-    return a->KeyId()->ByteLength() < b->KeyId()->ByteLength();
+    DCHECK_NE(a->KeyId()->ByteLengthAsSizeT(), b->KeyId()->ByteLengthAsSizeT());
+    return a->KeyId()->ByteLengthAsSizeT() < b->KeyId()->ByteLengthAsSizeT();
   }
 
   virtual void Trace(blink::Visitor* visitor) { visitor->Trace(key_id_); }
diff --git a/third_party/blink/renderer/modules/encryptedmedia/media_keys.cc b/third_party/blink/renderer/modules/encryptedmedia/media_keys.cc
index 6197e7c..e243b5c 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/media_keys.cc
+++ b/third_party/blink/renderer/modules/encryptedmedia/media_keys.cc
@@ -323,7 +323,7 @@
   // 5.2 Use the cdm to process certificate.
   cdm->SetServerCertificate(
       static_cast<unsigned char*>(server_certificate->Data()),
-      server_certificate->ByteLength(), result->Result());
+      server_certificate->ByteLengthAsSizeT(), result->Result());
 
   // 5.3 If any of the preceding steps failed, reject promise with a
   //     new DOMException whose name is the appropriate error name.
diff --git a/third_party/blink/renderer/modules/hid/hid_device.cc b/third_party/blink/renderer/modules/hid/hid_device.cc
index d06e235..1c76028 100644
--- a/third_party/blink/renderer/modules/hid/hid_device.cc
+++ b/third_party/blink/renderer/modules/hid/hid_device.cc
@@ -37,7 +37,7 @@
   Vector<uint8_t> vector;
   if (buffer.IsArrayBuffer()) {
     vector.Append(static_cast<uint8_t*>(buffer.GetAsArrayBuffer()->Data()),
-                  buffer.GetAsArrayBuffer()->ByteLength());
+                  buffer.GetAsArrayBuffer()->DeprecatedByteLengthAsUnsigned());
   } else {
     vector.Append(static_cast<uint8_t*>(
                       buffer.GetAsArrayBufferView().View()->BaseAddress()),
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_database.cc b/third_party/blink/renderer/modules/indexeddb/idb_database.cc
index ba4706eb..3ff8a99 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_database.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_database.cc
@@ -31,7 +31,7 @@
 #include "base/atomic_sequence_num.h"
 #include "base/optional.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_idb_observer_callback.h"
@@ -613,19 +613,21 @@
   return ContextLifecycleObserver::GetExecutionContext();
 }
 
-STATIC_ASSERT_ENUM(kWebIDBDatabaseExceptionUnknownError,
+STATIC_ASSERT_ENUM(mojom::blink::IDBException::kNoError,
+                   DOMExceptionCode::kNoError);
+STATIC_ASSERT_ENUM(mojom::blink::IDBException::kUnknownError,
                    DOMExceptionCode::kUnknownError);
-STATIC_ASSERT_ENUM(kWebIDBDatabaseExceptionConstraintError,
+STATIC_ASSERT_ENUM(mojom::blink::IDBException::kConstraintError,
                    DOMExceptionCode::kConstraintError);
-STATIC_ASSERT_ENUM(kWebIDBDatabaseExceptionDataError,
+STATIC_ASSERT_ENUM(mojom::blink::IDBException::kDataError,
                    DOMExceptionCode::kDataError);
-STATIC_ASSERT_ENUM(kWebIDBDatabaseExceptionVersionError,
+STATIC_ASSERT_ENUM(mojom::blink::IDBException::kVersionError,
                    DOMExceptionCode::kVersionError);
-STATIC_ASSERT_ENUM(kWebIDBDatabaseExceptionAbortError,
+STATIC_ASSERT_ENUM(mojom::blink::IDBException::kAbortError,
                    DOMExceptionCode::kAbortError);
-STATIC_ASSERT_ENUM(kWebIDBDatabaseExceptionQuotaError,
+STATIC_ASSERT_ENUM(mojom::blink::IDBException::kQuotaError,
                    DOMExceptionCode::kQuotaExceededError);
-STATIC_ASSERT_ENUM(kWebIDBDatabaseExceptionTimeoutError,
+STATIC_ASSERT_ENUM(mojom::blink::IDBException::kTimeoutError,
                    DOMExceptionCode::kTimeoutError);
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_factory.cc b/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
index 82efc3f..c7b06b4c 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
@@ -95,7 +95,7 @@
   void SetState(base::WeakPtr<WebIDBCursorImpl> cursor,
                 int64_t transaction_id) override {}
 
-  void Error(int32_t code, const String& message) override {
+  void Error(mojom::blink::IDBException code, const String& message) override {
     if (!promise_resolver_)
       return;
 
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_factory_test.cc b/third_party/blink/renderer/modules/indexeddb/idb_factory_test.cc
index e9849e00..3696404 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_factory_test.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_factory_test.cc
@@ -105,7 +105,7 @@
   EXPECT_FALSE(on_fulfilled);
   EXPECT_FALSE(on_rejected);
 
-  callbacks->Error(0, String());
+  callbacks->Error(mojom::blink::IDBException::kNoError, String());
 
   EXPECT_FALSE(on_fulfilled);
   EXPECT_FALSE(on_rejected);
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_request_loader.cc b/third_party/blink/renderer/modules/indexeddb/idb_request_loader.cc
index 4b374f0..fced38c2 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_request_loader.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_request_loader.cc
@@ -4,7 +4,6 @@
 
 #include "third_party/blink/renderer/modules/indexeddb/idb_request_loader.h"
 
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
diff --git a/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.cc b/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.cc
index 9263a35..d9723ec 100644
--- a/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.cc
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.cc
@@ -31,7 +31,7 @@
 }
 
 void IndexedDBDatabaseCallbacksImpl::Abort(int64_t transaction_id,
-                                           int32_t code,
+                                           mojom::blink::IDBException code,
                                            const String& message) {
   callbacks_->OnAbort(
       transaction_id,
diff --git a/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.h b/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.h
index 702ea69..0a88c40 100644
--- a/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.h
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.h
@@ -26,7 +26,7 @@
   void ForcedClose() override;
   void VersionChange(int64_t old_version, int64_t new_version) override;
   void Abort(int64_t transaction_id,
-             int32_t code,
+             mojom::blink::IDBException code,
              const WTF::String& message) override;
   void Complete(int64_t transaction_id) override;
   void Changes(mojom::blink::IDBObserverChangesPtr changes) override;
diff --git a/third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.h b/third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.h
index c89042a..2fa7960c 100644
--- a/third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.h
+++ b/third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.h
@@ -26,7 +26,7 @@
 
   void SetState(base::WeakPtr<WebIDBCursorImpl>, int64_t);
 
-  MOCK_METHOD2(Error, void(int32_t, const String&));
+  MOCK_METHOD2(Error, void(mojom::blink::IDBException, const String&));
 
   void SuccessCursorContinue(
       std::unique_ptr<IDBKey>,
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc
index 25fc617..e517aa4 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc
@@ -33,7 +33,6 @@
 
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/modules/indexed_db_names.h"
@@ -104,7 +103,8 @@
   transaction_id_ = transaction_id;
 }
 
-void WebIDBCallbacksImpl::Error(int32_t code, const String& message) {
+void WebIDBCallbacksImpl::Error(mojom::blink::IDBException code,
+                                const String& message) {
   if (!request_)
     return;
 
@@ -112,7 +112,7 @@
   // destroys all pending tasks.  If our callback was queued with a task that
   // gets cleared, we'll get a signal with an IgnorableAbortError as the task is
   // torn down.  This means the error response can be safely ignored.
-  if (code == kWebIDBDatabaseExceptionIgnorableAbortError) {
+  if (code == mojom::blink::IDBException::kIgnorableAbortError) {
     Detach();
     return;
   }
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.h b/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.h
index 8ec637d..8c62e502 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.h
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.h
@@ -33,7 +33,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
-#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink-forward.h"
 #include "third_party/blink/renderer/core/probe/async_task_id.h"
 #include "third_party/blink/renderer/modules/indexeddb/web_idb_callbacks.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
@@ -63,7 +63,7 @@
                 int64_t transaction_id) override;
 
   // Pointers transfer ownership.
-  void Error(int32_t code, const String& message) override;
+  void Error(mojom::blink::IDBException code, const String& message) override;
   void SuccessNamesAndVersionsList(
       Vector<mojom::blink::IDBNameAndVersionPtr>) override;
   void SuccessStringList(const Vector<String>&) override;
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.cc
index e5605043..1bdee8792 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.cc
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.cc
@@ -8,7 +8,7 @@
 
 #include "base/single_thread_task_runner.h"
 #include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_key_range.h"
 #include "third_party/blink/renderer/modules/indexeddb/indexed_db_dispatcher.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -69,7 +69,7 @@
     callbacks.reset();
     return;
   } else if (result->is_empty()) {
-    callbacks->Error(blink::kWebIDBDatabaseExceptionUnknownError,
+    callbacks->Error(blink::mojom::IDBException::kUnknownError,
                      "Invalid response");
     callbacks.reset();
     return;
@@ -78,7 +78,7 @@
   if (result->get_values()->keys.size() != 1u ||
       result->get_values()->primary_keys.size() != 1u ||
       result->get_values()->values.size() != 1u) {
-    callbacks->Error(blink::kWebIDBDatabaseExceptionUnknownError,
+    callbacks->Error(blink::mojom::IDBException::kUnknownError,
                      "Invalid response");
     callbacks.reset();
     return;
@@ -153,7 +153,7 @@
     callbacks.reset();
     return;
   } else if (result->is_empty()) {
-    callbacks->Error(blink::kWebIDBDatabaseExceptionUnknownError,
+    callbacks->Error(blink::mojom::IDBException::kUnknownError,
                      "Invalid response");
     callbacks.reset();
     return;
@@ -162,7 +162,7 @@
   if (result->get_values()->keys.size() != 1u ||
       result->get_values()->primary_keys.size() != 1u ||
       result->get_values()->values.size() != 1u) {
-    callbacks->Error(blink::kWebIDBDatabaseExceptionUnknownError,
+    callbacks->Error(blink::mojom::IDBException::kUnknownError,
                      "Invalid response");
     callbacks.reset();
     return;
@@ -190,7 +190,7 @@
     callbacks.reset();
     return;
   } else if (result->is_empty()) {
-    callbacks->Error(blink::kWebIDBDatabaseExceptionUnknownError,
+    callbacks->Error(blink::mojom::IDBException::kUnknownError,
                      "Invalid response");
     callbacks.reset();
     return;
@@ -200,7 +200,7 @@
           result->get_values()->primary_keys.size() ||
       result->get_values()->keys.size() !=
           result->get_values()->values.size()) {
-    callbacks->Error(blink::kWebIDBDatabaseExceptionUnknownError,
+    callbacks->Error(blink::mojom::IDBException::kUnknownError,
                      "Invalid response");
     callbacks.reset();
     return;
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.cc
index ce509c8..b99decb0 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.cc
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.cc
@@ -6,7 +6,7 @@
 
 #include "base/format_macros.h"
 #include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_database_error.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_key_range.h"
 #include "third_party/blink/renderer/modules/indexeddb/indexed_db_blink_mojom_traits.h"
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_transaction_impl.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_transaction_impl.cc
index 15d5a1f..13b7d55 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_transaction_impl.cc
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_transaction_impl.cc
@@ -9,7 +9,7 @@
 
 #include "base/format_macros.h"
 #include "mojo/public/cpp/bindings/strong_associated_binding.h"
-#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_database_error.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_key_range.h"
@@ -64,7 +64,7 @@
       value->DataSize() + primary_key->SizeEstimate() + index_keys_size;
   if (arg_size >= max_put_value_size_) {
     callbacks->Error(
-        blink::kWebIDBDatabaseExceptionUnknownError,
+        mojom::blink::IDBException::kUnknownError,
         String::Format("The serialized keys and/or value are too large"
                        " (size=%" PRIuS " bytes, max=%" PRIuS " bytes).",
                        arg_size, max_put_value_size_));
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_transaction_impl.h b/third_party/blink/renderer/modules/indexeddb/web_idb_transaction_impl.h
index c49b629..b5787915 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_transaction_impl.h
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_transaction_impl.h
@@ -12,7 +12,7 @@
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
-#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink-forward.h"
 #include "third_party/blink/renderer/modules/indexeddb/web_idb_transaction.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 
diff --git a/third_party/blink/renderer/modules/mediasource/source_buffer.cc b/third_party/blink/renderer/modules/mediasource/source_buffer.cc
index e6e82ed..c303731 100644
--- a/third_party/blink/renderer/modules/mediasource/source_buffer.cc
+++ b/third_party/blink/renderer/modules/mediasource/source_buffer.cc
@@ -370,12 +370,12 @@
                                 ExceptionState& exception_state) {
   double media_time = GetMediaTime();
   DVLOG(2) << __func__ << " this=" << this << " media_time=" << media_time
-           << " size=" << data->ByteLength();
+           << " size=" << data->DeprecatedByteLengthAsUnsigned();
   // Section 3.2 appendBuffer()
   // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
   AppendBufferInternal(media_time,
                        static_cast<const unsigned char*>(data->Data()),
-                       data->ByteLength(), exception_state);
+                       data->DeprecatedByteLengthAsUnsigned(), exception_state);
 }
 
 void SourceBuffer::appendBuffer(NotShared<DOMArrayBufferView> data,
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index b6ee2a0..18907989 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -524,7 +524,6 @@
           "xr/xr_reference_space_event.idl",
           "xr/xr_render_state.idl",
           "xr/xr_rigid_transform.idl",
-          "xr/xr_hit_test_options.idl",
           "xr/xr_hit_test_result.idl",
           "xr/xr_hit_test_source.idl",
           "xr/xr_session.idl",
diff --git a/third_party/blink/renderer/modules/native_file_system/native_file_system_writer.cc b/third_party/blink/renderer/modules/native_file_system/native_file_system_writer.cc
index 7d9b73f..b140606 100644
--- a/third_party/blink/renderer/modules/native_file_system/native_file_system_writer.cc
+++ b/third_party/blink/renderer/modules/native_file_system/native_file_system_writer.cc
@@ -46,7 +46,8 @@
   Blob* blob = nullptr;
   if (data.IsArrayBuffer()) {
     DOMArrayBuffer* array_buffer = data.GetAsArrayBuffer();
-    blob_data->AppendBytes(array_buffer->Data(), array_buffer->ByteLength());
+    blob_data->AppendBytes(array_buffer->Data(),
+                           array_buffer->ByteLengthAsSizeT());
   } else if (data.IsArrayBufferView()) {
     DOMArrayBufferView* array_buffer_view = data.GetAsArrayBufferView().View();
     blob_data->AppendBytes(array_buffer_view->BaseAddress(),
diff --git a/third_party/blink/renderer/modules/nfc/ndef_message.cc b/third_party/blink/renderer/modules/nfc/ndef_message.cc
index 9891b136..642d5d8e 100644
--- a/third_party/blink/renderer/modules/nfc/ndef_message.cc
+++ b/third_party/blink/renderer/modules/nfc/ndef_message.cc
@@ -47,7 +47,7 @@
     WTF::Vector<uint8_t> payload_data;
     payload_data.Append(
         static_cast<uint8_t*>(source.GetAsArrayBuffer()->Data()),
-        source.GetAsArrayBuffer()->ByteLength());
+        source.GetAsArrayBuffer()->DeprecatedByteLengthAsUnsigned());
     NDEFMessage* message = MakeGarbageCollected<NDEFMessage>();
     message->records_.push_back(MakeGarbageCollected<NDEFRecord>(
         std::move(payload_data), "application/octet-stream"));
diff --git a/third_party/blink/renderer/modules/nfc/ndef_record.cc b/third_party/blink/renderer/modules/nfc/ndef_record.cc
index 11c673f..494763c5 100644
--- a/third_party/blink/renderer/modules/nfc/ndef_record.cc
+++ b/third_party/blink/renderer/modules/nfc/ndef_record.cc
@@ -46,7 +46,7 @@
     DOMArrayBuffer* array_buffer =
         V8ArrayBuffer::ToImpl(buffer_source.V8Value().As<v8::Object>());
     bytes.Append(static_cast<uint8_t*>(array_buffer->Data()),
-                 array_buffer->ByteLength());
+                 array_buffer->DeprecatedByteLengthAsUnsigned());
   } else if (buffer_source.V8Value()->IsArrayBufferView()) {
     DOMArrayBufferView* array_buffer_view =
         V8ArrayBufferView::ToImpl(buffer_source.V8Value().As<v8::Object>());
@@ -224,7 +224,7 @@
       V8ArrayBuffer::ToImpl(data.V8Value().As<v8::Object>());
   WTF::Vector<uint8_t> bytes;
   bytes.Append(static_cast<uint8_t*>(array_buffer->Data()),
-               array_buffer->ByteLength());
+               array_buffer->DeprecatedByteLengthAsUnsigned());
   return MakeGarbageCollected<NDEFRecord>(custom_type, std::move(bytes));
 }
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_data_channel.cc b/third_party/blink/renderer/modules/peerconnection/rtc_data_channel.cc
index 0a9bd28..ab22b49b 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_data_channel.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_data_channel.cc
@@ -376,7 +376,7 @@
     return;
   }
 
-  size_t data_length = data->ByteLength();
+  size_t data_length = data->ByteLengthAsSizeT();
   if (!data_length)
     return;
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.cc b/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.cc
index 1edb1af..bd25ad04 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.cc
@@ -158,9 +158,10 @@
             der_cert.data(), static_cast<unsigned int>(der_cert.size()));
         // Don't replace the certificate if it's unchanged.
         // Should have been "if (*dab_cert != *remote_certificates_[i])"
-        if (dab_cert->ByteLength() != remote_certificates_[i]->ByteLength() ||
+        if (dab_cert->ByteLengthAsSizeT() !=
+                remote_certificates_[i]->ByteLengthAsSizeT() ||
             memcmp(dab_cert->Data(), remote_certificates_[i]->Data(),
-                   dab_cert->ByteLength()) != 0) {
+                   dab_cert->ByteLengthAsSizeT()) != 0) {
           remote_certificates_[i] = dab_cert;
         }
       }
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.cc b/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.cc
index 0a68497..a369fbb 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.cc
@@ -155,7 +155,7 @@
       key_(key),
       certificates_(certificates),
       p2p_quic_transport_factory_(std::move(p2p_quic_transport_factory)) {
-  DCHECK_GT(key_->ByteLength(), 0u);
+  DCHECK_GT(key_->ByteLengthAsSizeT(), 0u);
   transport->ConnectConsumer(this);
 }
 
@@ -184,7 +184,7 @@
 }
 
 DOMArrayBuffer* RTCQuicTransport::getKey() const {
-  return DOMArrayBuffer::Create(key_->Data(), key_->ByteLength());
+  return DOMArrayBuffer::Create(key_->Data(), key_->ByteLengthAsSizeT());
 }
 
 void RTCQuicTransport::connect(ExceptionState& exception_state) {
@@ -196,7 +196,7 @@
   }
   start_reason_ = StartReason::kClientConnecting;
   std::string pre_shared_key(static_cast<const char*>(key_->Data()),
-                             key_->ByteLength());
+                             key_->ByteLengthAsSizeT());
   StartConnection(quic::Perspective::IS_CLIENT,
                   P2PQuicTransport::StartConfig(pre_shared_key));
 }
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport_test.cc
index bde32ac6..d99bc69e 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport_test.cc
@@ -79,7 +79,7 @@
 
 static base::span<uint8_t> SpanFromDOMArrayBuffer(DOMArrayBuffer* buffer) {
   return base::span<uint8_t>(static_cast<uint8_t*>(buffer->Data()),
-                             buffer->ByteLength());
+                             buffer->ByteLengthAsSizeT());
 }
 
 }  // namespace
@@ -444,7 +444,7 @@
       CreateQuicTransport(scope, ice_transport, {}, std::move(mock_transport));
   DOMArrayBuffer* key = quic_transport->getKey();
   std::string pre_shared_key(static_cast<const char*>(key->Data()),
-                             key->ByteLength());
+                             key->ByteLengthAsSizeT());
 
   EXPECT_CALL(*mock_transport_ptr, MockStart(_))
       .WillOnce(
@@ -659,7 +659,7 @@
       CreateQuicTransport(scope, ice_transport, {}, std::move(mock_transport));
   auto* key = quic_transport->getKey();
 
-  EXPECT_GE(key->ByteLength(), 16u);
+  EXPECT_GE(key->ByteLengthAsSizeT(), 16u);
 }
 
 // Test that stats are converted correctly to the RTCQuicTransportStats
diff --git a/third_party/blink/renderer/modules/presentation/presentation_connection.cc b/third_party/blink/renderer/modules/presentation/presentation_connection.cc
index fae2d02..9d1969b 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_connection.cc
+++ b/third_party/blink/renderer/modules/presentation/presentation_connection.cc
@@ -42,7 +42,7 @@
       WTF::Vector<uint8_t>());
   WTF::Vector<uint8_t>& data = message->get_data();
   data.Append(static_cast<const uint8_t*>(buffer->Data()),
-              buffer->ByteLength());
+              buffer->DeprecatedByteLengthAsUnsigned());
   return message;
 }
 
diff --git a/third_party/blink/renderer/modules/push_messaging/push_manager_test.cc b/third_party/blink/renderer/modules/push_messaging/push_manager_test.cc
index 54d6be8a..cfb1899 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_manager_test.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_manager_test.cc
@@ -37,9 +37,9 @@
 
   String application_server_key(
       reinterpret_cast<const char*>(output->applicationServerKey()->Data()),
-      output->applicationServerKey()->ByteLength());
+      output->applicationServerKey()->DeprecatedByteLengthAsUnsigned());
 
-  ASSERT_EQ(output->applicationServerKey()->ByteLength(),
+  ASSERT_EQ(output->applicationServerKey()->DeprecatedByteLengthAsUnsigned(),
             kApplicationServerKeyLength);
 
   ASSERT_EQ(reinterpret_cast<const char*>(sender_key),
diff --git a/third_party/blink/renderer/modules/push_messaging/push_message_data.cc b/third_party/blink/renderer/modules/push_messaging/push_message_data.cc
index 3ba9a309..f80d0307 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_message_data.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_message_data.cc
@@ -38,7 +38,8 @@
             : message_data.GetAsArrayBuffer();
 
     return MakeGarbageCollected<PushMessageData>(
-        static_cast<const char*>(buffer->Data()), buffer->ByteLength());
+        static_cast<const char*>(buffer->Data()),
+        buffer->DeprecatedByteLengthAsUnsigned());
   }
 
   if (message_data.IsUSVString()) {
diff --git a/third_party/blink/renderer/modules/push_messaging/push_messaging_client.cc b/third_party/blink/renderer/modules/push_messaging/push_messaging_client.cc
index 692dfc4..273922bd 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_messaging_client.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_messaging_client.cc
@@ -64,7 +64,7 @@
 
   // If a developer provided an application server key in |options|, skip
   // fetching the manifest.
-  if (!options->applicationServerKey()->ByteLength()) {
+  if (!options->applicationServerKey()->ByteLengthAsSizeT()) {
     ManifestManager* manifest_manager =
         ManifestManager::From(*GetSupplementable());
     manifest_manager->RequestManifest(
diff --git a/third_party/blink/renderer/modules/push_messaging/push_messaging_type_converters.cc b/third_party/blink/renderer/modules/push_messaging/push_messaging_type_converters.cc
index 8f85f0a..9452bd7 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_messaging_type_converters.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_messaging_type_converters.cc
@@ -20,7 +20,7 @@
   Vector<uint8_t> application_server_key;
   application_server_key.Append(
       reinterpret_cast<uint8_t*>(input->applicationServerKey()->Data()),
-      input->applicationServerKey()->ByteLength());
+      input->applicationServerKey()->DeprecatedByteLengthAsUnsigned());
 
   return blink::mojom::blink::PushSubscriptionOptions::New(
       input->userVisibleOnly(), application_server_key);
diff --git a/third_party/blink/renderer/modules/push_messaging/push_subscription.cc b/third_party/blink/renderer/modules/push_messaging/push_subscription.cc
index e22e0c0..afadd498 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_subscription.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_subscription.cc
@@ -27,7 +27,7 @@
 // based on the value of |buffer| while encoding, assuming a known length.
 String ToBase64URLWithoutPadding(DOMArrayBuffer* buffer) {
   String value = WTF::Base64URLEncode(static_cast<const char*>(buffer->Data()),
-                                      buffer->ByteLength());
+                                      buffer->DeprecatedByteLengthAsUnsigned());
   DCHECK_GT(value.length(), 0u);
 
   unsigned padding_to_remove = 0;
diff --git a/third_party/blink/renderer/modules/push_messaging/push_subscription_options.cc b/third_party/blink/renderer/modules/push_messaging/push_subscription_options.cc
index 1db027ac..ad43b565 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_subscription_options.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_subscription_options.cc
@@ -30,14 +30,15 @@
   if (application_server_key.IsArrayBuffer()) {
     input =
         static_cast<char*>(application_server_key.GetAsArrayBuffer()->Data());
-    length = application_server_key.GetAsArrayBuffer()->ByteLength();
+    length = application_server_key.GetAsArrayBuffer()
+                 ->DeprecatedByteLengthAsUnsigned();
   } else if (application_server_key.IsArrayBufferView()) {
     input = static_cast<char*>(
         application_server_key.GetAsArrayBufferView().View()->buffer()->Data());
     length = application_server_key.GetAsArrayBufferView()
                  .View()
                  ->buffer()
-                 ->ByteLength();
+                 ->DeprecatedByteLengthAsUnsigned();
   } else if (application_server_key.IsString()) {
     if (!Base64UnpaddedURLDecode(application_server_key.GetAsString(),
                                  decoded_application_server_key)) {
@@ -101,7 +102,7 @@
 bool PushSubscriptionOptions::IsApplicationServerKeyVapid() const {
   if (!application_server_key_)
     return false;
-  return application_server_key_->ByteLength() == 65 &&
+  return application_server_key_->ByteLengthAsSizeT() == 65 &&
          static_cast<uint8_t*>(application_server_key_->Data())[0] == 0x04;
 }
 
diff --git a/third_party/blink/renderer/modules/serial/serial_port_underlying_sink.cc b/third_party/blink/renderer/modules/serial/serial_port_underlying_sink.cc
index 9bc626b..6225708b 100644
--- a/third_party/blink/renderer/modules/serial/serial_port_underlying_sink.cc
+++ b/third_party/blink/renderer/modules/serial/serial_port_underlying_sink.cc
@@ -138,7 +138,7 @@
   if (buffer_source_.IsArrayBuffer()) {
     DOMArrayBuffer* array = buffer_source_.GetAsArrayBuffer();
     data = static_cast<const uint8_t*>(array->Data());
-    length = array->ByteLength();
+    length = array->DeprecatedByteLengthAsUnsigned();
   } else {
     DOMArrayBufferView* view = buffer_source_.GetAsArrayBufferView().View();
     data = static_cast<const uint8_t*>(view->BaseAddress());
diff --git a/third_party/blink/renderer/modules/webaudio/async_audio_decoder.cc b/third_party/blink/renderer/modules/webaudio/async_audio_decoder.cc
index 8cab445a..f84bdbce 100644
--- a/third_party/blink/renderer/modules/webaudio/async_audio_decoder.cc
+++ b/third_party/blink/renderer/modules/webaudio/async_audio_decoder.cc
@@ -73,7 +73,7 @@
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
   DCHECK(!IsMainThread());
   scoped_refptr<AudioBus> bus = CreateBusFromInMemoryAudioFile(
-      audio_data->Data(), audio_data->ByteLength(), false, sample_rate);
+      audio_data->Data(), audio_data->ByteLengthAsSizeT(), false, sample_rate);
 
   // Decoding is finished, but we need to do the callbacks on the main thread.
   // A reference to |*bus| is retained by base::OnceCallback and will be removed
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index 84a58f78..cbe8070 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -2003,7 +2003,7 @@
     SynthesizeGLError(GL_INVALID_VALUE, "bufferData", "no data");
     return;
   }
-  BufferDataImpl(target, data->ByteLength(), data->Data(), usage);
+  BufferDataImpl(target, data->ByteLengthAsSizeT(), data->Data(), usage);
 }
 
 void WebGLRenderingContextBase::bufferData(GLenum target,
@@ -2041,7 +2041,7 @@
   if (isContextLost())
     return;
   DCHECK(data);
-  BufferSubDataImpl(target, offset, data->ByteLength(), data->Data());
+  BufferSubDataImpl(target, offset, data->ByteLengthAsSizeT(), data->Data());
 }
 
 void WebGLRenderingContextBase::bufferSubData(
diff --git a/third_party/blink/renderer/modules/websockets/dom_websocket.cc b/third_party/blink/renderer/modules/websockets/dom_websocket.cc
index c3a3cd6..bea8bb2 100644
--- a/third_party/blink/renderer/modules/websockets/dom_websocket.cc
+++ b/third_party/blink/renderer/modules/websockets/dom_websocket.cc
@@ -354,15 +354,15 @@
     return;
   }
   if (common_.GetState() == kClosing || common_.GetState() == kClosed) {
-    UpdateBufferedAmountAfterClose(binary_data->ByteLength());
+    UpdateBufferedAmountAfterClose(binary_data->ByteLengthAsSizeT());
     return;
   }
   RecordSendTypeHistogram(kWebSocketSendTypeArrayBuffer);
   RecordSendMessageSizeHistogram(kWebSocketSendTypeArrayBuffer,
-                                 binary_data->ByteLength());
+                                 binary_data->ByteLengthAsSizeT());
   DCHECK(channel_);
-  buffered_amount_ += binary_data->ByteLength();
-  channel_->Send(*binary_data, 0, binary_data->ByteLength(),
+  buffered_amount_ += binary_data->ByteLengthAsSizeT();
+  channel_->Send(*binary_data, 0, binary_data->DeprecatedByteLengthAsUnsigned(),
                  base::OnceClosure());
 }
 
diff --git a/third_party/blink/renderer/modules/websockets/web_pepper_socket_impl.cc b/third_party/blink/renderer/modules/websockets/web_pepper_socket_impl.cc
index dba95e67..d799ed86 100644
--- a/third_party/blink/renderer/modules/websockets/web_pepper_socket_impl.cc
+++ b/third_party/blink/renderer/modules/websockets/web_pepper_socket_impl.cc
@@ -114,7 +114,8 @@
     return true;
 
   DOMArrayBuffer* array_buffer = web_array_buffer;
-  private_->Send(*array_buffer, 0, array_buffer->ByteLength(),
+  private_->Send(*array_buffer, 0,
+                 array_buffer->DeprecatedByteLengthAsUnsigned(),
                  base::OnceClosure());
   return true;
 }
diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc
index 0c77ba9..4650f8e 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc
+++ b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc
@@ -684,7 +684,7 @@
         CHECK(message->array_buffer);
         SendInternal(network::mojom::blink::WebSocketMessageType::BINARY,
                      static_cast<const char*>(message->array_buffer->Data()),
-                     message->array_buffer->ByteLength(),
+                     message->array_buffer->DeprecatedByteLengthAsUnsigned(),
                      &consumed_buffered_amount);
         break;
       case kMessageTypeClose: {
diff --git a/third_party/blink/renderer/modules/websockets/websocket_stream.cc b/third_party/blink/renderer/modules/websockets/websocket_stream.cc
index c33cdbc2..80014c8 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_stream.cc
+++ b/third_party/blink/renderer/modules/websockets/websocket_stream.cc
@@ -291,7 +291,8 @@
   auto* isolate = script_state->GetIsolate();
   if (v8chunk->IsArrayBuffer()) {
     DOMArrayBuffer* data = V8ArrayBuffer::ToImpl(v8chunk.As<v8::ArrayBuffer>());
-    SendArrayBuffer(script_state, data, 0, data->ByteLength(), resolver,
+    SendArrayBuffer(script_state, data, 0,
+                    data->DeprecatedByteLengthAsUnsigned(), resolver,
                     std::move(callback));
     return;
   }
diff --git a/third_party/blink/renderer/modules/webusb/usb_isochronous_in_transfer_result.h b/third_party/blink/renderer/modules/webusb/usb_isochronous_in_transfer_result.h
index 36c379e..ba72761 100644
--- a/third_party/blink/renderer/modules/webusb/usb_isochronous_in_transfer_result.h
+++ b/third_party/blink/renderer/modules/webusb/usb_isochronous_in_transfer_result.h
@@ -21,7 +21,8 @@
   static USBIsochronousInTransferResult* Create(
       DOMArrayBuffer* data,
       const HeapVector<Member<USBIsochronousInTransferPacket>>& packets) {
-    DOMDataView* data_view = DOMDataView::Create(data, 0, data->ByteLength());
+    DOMDataView* data_view =
+        DOMDataView::Create(data, 0, data->DeprecatedByteLengthAsUnsigned());
     return MakeGarbageCollected<USBIsochronousInTransferResult>(data_view,
                                                                 packets);
   }
diff --git a/third_party/blink/renderer/modules/xr/BUILD.gn b/third_party/blink/renderer/modules/xr/BUILD.gn
index ac6f487..04c1ca0 100644
--- a/third_party/blink/renderer/modules/xr/BUILD.gn
+++ b/third_party/blink/renderer/modules/xr/BUILD.gn
@@ -30,8 +30,6 @@
     "xr_grip_space.h",
     "xr_hit_result.cc",
     "xr_hit_result.h",
-    "xr_hit_test_options.cc",
-    "xr_hit_test_options.h",
     "xr_hit_test_result.cc",
     "xr_hit_test_result.h",
     "xr_hit_test_source.cc",
diff --git a/third_party/blink/renderer/modules/xr/xr_hit_test_options.cc b/third_party/blink/renderer/modules/xr/xr_hit_test_options.cc
deleted file mode 100644
index 73d366ff..0000000
--- a/third_party/blink/renderer/modules/xr/xr_hit_test_options.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/modules/xr/xr_hit_test_options.h"
-
-#include "third_party/blink/renderer/modules/xr/xr_hit_test_options_init.h"
-#include "third_party/blink/renderer/modules/xr/xr_ray.h"
-#include "third_party/blink/renderer/modules/xr/xr_space.h"
-
-namespace blink {
-
-XRHitTestOptions::XRHitTestOptions(XRHitTestOptionsInit* options_init) {
-  DCHECK(options_init);
-
-  space_ = options_init->space();
-
-  if (options_init->hasOffsetRay()) {
-    ray_ = options_init->offsetRay();
-  } else {
-    ray_ = MakeGarbageCollected<XRRay>();
-  }
-}
-
-void XRHitTestOptions::Trace(blink::Visitor* visitor) {
-  visitor->Trace(space_);
-  visitor->Trace(ray_);
-  ScriptWrappable::Trace(visitor);
-}
-
-XRSpace* XRHitTestOptions::space() const {
-  return space_;
-}
-
-XRRay* XRHitTestOptions::offsetRay() const {
-  return ray_;
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_hit_test_options.h b/third_party/blink/renderer/modules/xr/xr_hit_test_options.h
deleted file mode 100644
index 0c671b1..0000000
--- a/third_party/blink/renderer/modules/xr/xr_hit_test_options.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_HIT_TEST_OPTIONS_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_HIT_TEST_OPTIONS_H_
-
-#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
-
-namespace blink {
-
-class XRRay;
-class XRSpace;
-class XRHitTestOptionsInit;
-
-class XRHitTestOptions : public ScriptWrappable {
-  DEFINE_WRAPPERTYPEINFO();
-
- public:
-  explicit XRHitTestOptions(XRHitTestOptionsInit* options_init);
-
-  XRSpace* space() const;
-  XRRay* offsetRay() const;
-
-  void Trace(blink::Visitor* visitor) override;
-
- private:
-  Member<XRSpace> space_;
-  Member<XRRay> ray_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_HIT_TEST_OPTIONS_H_
diff --git a/third_party/blink/renderer/modules/xr/xr_hit_test_options.idl b/third_party/blink/renderer/modules/xr/xr_hit_test_options.idl
deleted file mode 100644
index 6f90679..0000000
--- a/third_party/blink/renderer/modules/xr/xr_hit_test_options.idl
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-[SecureContext, Exposed=Window, RuntimeEnabled=WebXRHitTest]
-interface XRHitTestOptions {
-  readonly attribute XRSpace space;
-  readonly attribute XRRay offsetRay;
-};
diff --git a/third_party/blink/renderer/modules/xr/xr_hit_test_result.cc b/third_party/blink/renderer/modules/xr/xr_hit_test_result.cc
index dd3bc4c2..9407db65 100644
--- a/third_party/blink/renderer/modules/xr/xr_hit_test_result.cc
+++ b/third_party/blink/renderer/modules/xr/xr_hit_test_result.cc
@@ -15,10 +15,6 @@
     : hit_test_source_(hit_test_source),
       pose_(std::make_unique<TransformationMatrix>(pose)) {}
 
-XRHitTestOptions* XRHitTestResult::hitTestOptions() const {
-  return hit_test_source_->hitTestOptions();
-}
-
 XRPose* XRHitTestResult::getPose(XRSpace* relative_to) {
   DCHECK(relative_to->MojoFromSpace());
 
diff --git a/third_party/blink/renderer/modules/xr/xr_hit_test_result.h b/third_party/blink/renderer/modules/xr/xr_hit_test_result.h
index 37526c0..3b38d7a 100644
--- a/third_party/blink/renderer/modules/xr/xr_hit_test_result.h
+++ b/third_party/blink/renderer/modules/xr/xr_hit_test_result.h
@@ -10,7 +10,6 @@
 namespace blink {
 
 class TransformationMatrix;
-class XRHitTestOptions;
 class XRHitTestSource;
 class XRPose;
 class XRSpace;
@@ -22,8 +21,6 @@
   XRHitTestResult(XRHitTestSource* hit_test_source,
                   const TransformationMatrix& pose);
 
-  XRHitTestOptions* hitTestOptions() const;
-
   XRPose* getPose(XRSpace* relative_to);
 
   void Trace(blink::Visitor* visitor) override;
diff --git a/third_party/blink/renderer/modules/xr/xr_hit_test_result.idl b/third_party/blink/renderer/modules/xr/xr_hit_test_result.idl
index 7c3cd75e..0019eaa 100644
--- a/third_party/blink/renderer/modules/xr/xr_hit_test_result.idl
+++ b/third_party/blink/renderer/modules/xr/xr_hit_test_result.idl
@@ -4,7 +4,5 @@
 
 [SecureContext, Exposed=Window, RuntimeEnabled=WebXRHitTest]
 interface XRHitTestResult {
-  readonly attribute XRHitTestOptions hitTestOptions;
-
    XRPose? getPose(XRSpace relative_to);
 };
diff --git a/third_party/blink/renderer/modules/xr/xr_hit_test_source.cc b/third_party/blink/renderer/modules/xr/xr_hit_test_source.cc
index 0cfdd0c..be628e3a 100644
--- a/third_party/blink/renderer/modules/xr/xr_hit_test_source.cc
+++ b/third_party/blink/renderer/modules/xr/xr_hit_test_source.cc
@@ -5,22 +5,16 @@
 #include "third_party/blink/renderer/modules/xr/xr_hit_test_source.h"
 
 #include "device/vr/public/mojom/vr_service.mojom-blink.h"
-#include "third_party/blink/renderer/modules/xr/xr_hit_test_options.h"
 #include "third_party/blink/renderer/modules/xr/xr_hit_test_result.h"
 
 namespace blink {
 
-XRHitTestSource::XRHitTestSource(uint64_t id, XRHitTestOptions* options)
-    : id_(id), options_(options) {}
+XRHitTestSource::XRHitTestSource(uint64_t id) : id_(id) {}
 
 uint64_t XRHitTestSource::id() const {
   return id_;
 }
 
-XRHitTestOptions* XRHitTestSource::hitTestOptions() const {
-  return options_;
-}
-
 HeapVector<Member<XRHitTestResult>> XRHitTestSource::Results() {
   HeapVector<Member<XRHitTestResult>> results;
 
@@ -41,9 +35,4 @@
   }
 }
 
-void XRHitTestSource::Trace(blink::Visitor* visitor) {
-  visitor->Trace(options_);
-  ScriptWrappable::Trace(visitor);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_hit_test_source.h b/third_party/blink/renderer/modules/xr/xr_hit_test_source.h
index 94af09d..41d6873a 100644
--- a/third_party/blink/renderer/modules/xr/xr_hit_test_source.h
+++ b/third_party/blink/renderer/modules/xr/xr_hit_test_source.h
@@ -14,19 +14,16 @@
 
 namespace blink {
 
-class XRHitTestOptions;
 class XRHitTestResult;
 
 class XRHitTestSource : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  XRHitTestSource(uint64_t id, XRHitTestOptions* options);
+  explicit XRHitTestSource(uint64_t id);
 
   uint64_t id() const;
 
-  XRHitTestOptions* hitTestOptions() const;
-
   // Returns a vector of XRHitTestResults that were obtained during last frame
   // update. This method is not exposed to JavaScript.
   HeapVector<Member<XRHitTestResult>> Results();
@@ -34,13 +31,9 @@
   void Update(const WTF::Vector<device::mojom::blink::XRHitResultPtr>&
                   hit_test_results);
 
-  void Trace(blink::Visitor*) override;
-
  private:
   const uint64_t id_;
 
-  Member<XRHitTestOptions> options_;
-
   Vector<std::unique_ptr<TransformationMatrix>> last_frame_results_;
 };
 
diff --git a/third_party/blink/renderer/modules/xr/xr_hit_test_source.idl b/third_party/blink/renderer/modules/xr/xr_hit_test_source.idl
index b05fbee1..68dd21c9 100644
--- a/third_party/blink/renderer/modules/xr/xr_hit_test_source.idl
+++ b/third_party/blink/renderer/modules/xr/xr_hit_test_source.idl
@@ -4,5 +4,4 @@
 
 [SecureContext, Exposed=Window, RuntimeEnabled=WebXRHitTest]
 interface XRHitTestSource {
-  readonly attribute XRHitTestOptions hitTestOptions;
 };
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc
index e23f9f2..b62141c 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.cc
+++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -31,7 +31,7 @@
 #include "third_party/blink/renderer/modules/xr/xr_frame.h"
 #include "third_party/blink/renderer/modules/xr/xr_frame_provider.h"
 #include "third_party/blink/renderer/modules/xr/xr_hit_result.h"
-#include "third_party/blink/renderer/modules/xr/xr_hit_test_options.h"
+#include "third_party/blink/renderer/modules/xr/xr_hit_test_options_init.h"
 #include "third_party/blink/renderer/modules/xr/xr_hit_test_source.h"
 #include "third_party/blink/renderer/modules/xr/xr_input_source_event.h"
 #include "third_party/blink/renderer/modules/xr/xr_input_sources_change_event.h"
@@ -631,12 +631,11 @@
 
   DCHECK(options_init);  // is this enforced by generated bindings?
 
-  XRHitTestOptions* options =
-      MakeGarbageCollected<XRHitTestOptions>(options_init);
-
   // 1. Grab the native origin from the passed in XRSpace.
   base::Optional<XRNativeOriginInformation> maybe_native_origin =
-      options->space()->NativeOrigin();
+      options_init && options_init->hasSpace()
+          ? options_init->space()->NativeOrigin()
+          : base::nullopt;
 
   if (!maybe_native_origin) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
@@ -648,13 +647,20 @@
   // should only matter for spaces whose transforms are not fully known on the
   // device (for example any space containing origin-offset).
   TransformationMatrix origin_from_space =
-      options->space()->OriginOffsetMatrix();
+      options_init->space()
+          ->OriginOffsetMatrix();  // Null checks not needed since native origin
+                                   // wouldn't be set if options_init or space()
+                                   // were null.
 
   DVLOG(3) << __func__
            << ": origin_from_space = " << origin_from_space.ToString(true);
 
   // Transformation from passed in pose to |space|.
-  auto space_from_ray = options->offsetRay()->RawMatrix();
+
+  XRRay* offsetRay = options_init && options_init->hasOffsetRay()
+                         ? options_init->offsetRay()
+                         : MakeGarbageCollected<XRRay>();
+  auto space_from_ray = offsetRay->RawMatrix();
   auto origin_from_ray = origin_from_space * space_from_ray;
 
   DVLOG(3) << __func__
@@ -681,7 +687,7 @@
   xr_->xrEnvironmentProviderRemote()->SubscribeToHitTest(
       maybe_native_origin->ToMojo(), std::move(ray_mojo),
       WTF::Bind(&XRSession::OnSubscribeToHitTestResult, WrapPersistent(this),
-                WrapPersistent(resolver), WrapPersistent(options)));
+                WrapPersistent(resolver)));
   request_hit_test_source_promises_.insert(resolver);
 
   return promise;
@@ -709,7 +715,6 @@
 
 void XRSession::OnSubscribeToHitTestResult(
     ScriptPromiseResolver* resolver,
-    XRHitTestOptions* options,
     device::mojom::SubscribeToHitTestResult result,
     uint64_t subscription_id) {
   DVLOG(2) << __func__ << ": result=" << result
@@ -725,7 +730,7 @@
   }
 
   XRHitTestSource* hit_test_source =
-      MakeGarbageCollected<XRHitTestSource>(subscription_id, options);
+      MakeGarbageCollected<XRHitTestSource>(subscription_id);
 
   hit_test_source_ids_to_hit_test_sources_.insert(subscription_id,
                                                   hit_test_source);
diff --git a/third_party/blink/renderer/modules/xr/xr_session.h b/third_party/blink/renderer/modules/xr/xr_session.h
index 4c574e0..40bafd7 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.h
+++ b/third_party/blink/renderer/modules/xr/xr_session.h
@@ -39,7 +39,6 @@
 class XRAnchor;
 class XRAnchorSet;
 class XRCanvasInputProvider;
-class XRHitTestOptions;
 class XRHitTestOptionsInit;
 class XRHitTestSource;
 class XRPlane;
@@ -311,7 +310,6 @@
 
   void OnSubscribeToHitTestResult(
       ScriptPromiseResolver* resolver,
-      XRHitTestOptions* options,
       device::mojom::SubscribeToHitTestResult result,
       uint64_t subscription_id);
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index 974909b..2dbb90bf 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -1908,7 +1908,6 @@
     const auto& request = resource->GetResourceRequest();
     ResourceResponse response;
 
-    resource->VirtualTimePauser().PauseVirtualTime();
     if (resource_load_observer_) {
       DCHECK(!IsDetached());
       resource_load_observer_->WillSendRequest(
@@ -1943,6 +1942,7 @@
     } else {
       non_blocking_loaders_.insert(loader);
     }
+    resource->VirtualTimePauser().PauseVirtualTime();
 
     StorePerformanceTimingInitiatorInformation(resource);
   }
diff --git a/third_party/blink/renderer/platform/testing/paint_test_configurations.h b/third_party/blink/renderer/platform/testing/paint_test_configurations.h
index d125cb30..00b0da0 100644
--- a/third_party/blink/renderer/platform/testing/paint_test_configurations.h
+++ b/third_party/blink/renderer/platform/testing/paint_test_configurations.h
@@ -15,6 +15,7 @@
   kCompositeAfterPaint = 1 << 0,
   kUnderInvalidationChecking = 1 << 1,
   kFastBorderRadius = 1 << 2,
+  kDoNotCompositeTrivial3D = 1 << 3,
 };
 
 class PaintTestConfigurations
@@ -51,6 +52,12 @@
   INSTANTIATE_TEST_SUITE_P(All, test_class,             \
                            ::testing::Values(0, kCompositeAfterPaint))
 
+#define INSTANTIATE_DO_NOT_COMPOSITE_TRIVIAL_3D_P(test_class)              \
+  INSTANTIATE_TEST_SUITE_P(                                                \
+      All, test_class,                                                     \
+      ::testing::Values(0, kCompositeAfterPaint, kDoNotCompositeTrivial3D, \
+                        kCompositeAfterPaint | kDoNotCompositeTrivial3D))
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_PAINT_TEST_CONFIGURATIONS_H_
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_metadata_builder_unittest.py b/third_party/blink/tools/blinkpy/w3c/wpt_metadata_builder_unittest.py
index 79b6c73..0088c9d 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_metadata_builder_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_metadata_builder_unittest.py
@@ -14,20 +14,29 @@
 from blinkpy.w3c.wpt_metadata_builder import WPTMetadataBuilder
 
 
-def _make_expectation(port, test_name, test_statuses):
-    """Creates an expectation object for a single test.
+def _make_expectation(port, test_path, test_statuses, test_names=[]):
+    """Creates an expectation object for a single test or directory.
 
     Args:
         port: the port to run against
-        test_name: the name of the test
+        test_path: the path to set expectations for
         test_status: the statuses of the test
+        test_names: a set of tests under the 'test_path'. Should be non-empty
+            when the 'test_path' is a directory instead of a single test
 
     Returns:
         An expectation object with the given test and statuses.
     """
     expectation_dict = OrderedDict()
-    expectation_dict["expectations"] = "Bug(test) %s [ %s ]" % (test_name, test_statuses)
-    return TestExpectations(port, tests=[test_name], expectations_dict=expectation_dict)
+    expectation_dict["expectations"] = "Bug(test) %s [ %s ]" % (test_path, test_statuses)
+
+    # When test_path is a dir, we expect test_names to be provided.
+    is_dir = test_path.endswith('/')
+    assert is_dir == bool(test_names)
+    if not is_dir:
+        test_names = [test_path]
+
+    return TestExpectations(port, tests=test_names, expectations_dict=expectation_dict)
 
 
 class WPTMetadataBuilderTest(unittest.TestCase):
@@ -86,10 +95,11 @@
 
     def test_skipped_directory(self):
         """A skipped WPT directory should get a dir-wide metadata file."""
-        test_name = "external/wpt/test_dir/"
-        expectations = _make_expectation(self.port, test_name, "SKIP")
+        test_dir = "external/wpt/test_dir/"
+        test_name = "external/wpt/test_dir/test.html"
+        expectations = _make_expectation(self.port, test_dir, "SKIP", test_names=[test_name])
         metadata_builder = WPTMetadataBuilder(expectations, self.port)
-        filename, contents = metadata_builder.get_metadata_filename_and_contents(test_name, 'SKIP')
+        filename, contents = metadata_builder.get_metadata_filename_and_contents(test_dir, 'SKIP')
         self.assertEqual(os.path.join("test_dir", "__dir__.ini"), filename)
         self.assertEqual("disabled: wpt_metadata_builder.py\n", contents)
 
diff --git a/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py b/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py
index 7fff95a..117d631 100644
--- a/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py
+++ b/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py
@@ -84,6 +84,77 @@
     set(s.upper() for s in _BUILD_TYPE_TOKEN_LIST)
 ]
 
+
+class AllTests(object):
+    """A class to query for path-prefix matches against the list of all tests."""
+    def __init__(self, list_of_tests):
+        self._tree = dict()
+        for test in list_of_tests:
+            assert not test.startswith('/')
+            assert not test.endswith('/')
+            assert test.replace('//', '/') == test
+            AllTests._add_path_to_tree(self._tree, test.split('/'))
+
+    def find_matching_tests(self, path_prefix):
+        assert not path_prefix.startswith('/')
+        assert path_prefix.replace('//', '/') == path_prefix
+
+        subtree = AllTests._find_subtree_with_prefix(self._tree, path_prefix.rstrip('/').split('/'))
+        if subtree is None:
+            # No match found.
+            return []
+        if not subtree:
+            # We found a leaf node, an exact match on |path_prefix|.
+            return [path_prefix]
+        return AllTests._all_paths_under_subtree(subtree, path_prefix)
+
+    @staticmethod
+    def _find_subtree_with_prefix(subtree, path_list):
+        # Reached the end of the path, we matched to this point in the
+        # dictionary tree.
+        if not path_list:
+            return subtree
+        path_component = path_list[0]
+        # A component of the path does not exist in the dictionary tree.
+        if not path_component in subtree:
+            return None
+        path_remainder = path_list[1:]
+        return AllTests._find_subtree_with_prefix(subtree[path_component], path_remainder)
+
+    @staticmethod
+    def _all_paths_under_subtree(subtree, prefix):
+        if not subtree:
+            return []
+
+        if prefix and not prefix.endswith('/'):
+            prefix = prefix + '/'
+
+        paths = []
+        for child_path, child_tree in subtree.iteritems():
+            if not child_tree:
+                paths.append(prefix + child_path)
+            else:
+                paths.extend(AllTests._all_paths_under_subtree(child_tree, prefix + child_path))
+        return paths
+
+    @staticmethod
+    def _add_path_to_tree(subtree, path_list):
+        # When |path_list| is empty, we reached the end of the path,
+        # so don't add anything more to |subtree|.
+        if not path_list:
+            # If subtree is not empty, then we have the same path listed
+            # twice in the initial list of tests.
+            assert len(subtree) == 0
+            return
+
+        path_component = path_list[0]
+        if not path_component in subtree:
+            subtree[path_component] = dict()
+
+        path_remainder = path_list[1:]
+        AllTests._add_path_to_tree(subtree[path_component], path_remainder)
+
+
 class TestExpectationParser(object):
     """Provides parsing facilities for lines in the test_expectation.txt file."""
 
@@ -101,12 +172,7 @@
         self._port = port
         self._test_configuration_converter = TestConfigurationConverter(
             set(port.all_test_configurations()), port.configuration_specifier_macros())
-
-        if all_tests:
-            self._all_tests = set(all_tests)
-        else:
-            self._all_tests = set()
-
+        self._all_tests = AllTests(all_tests) if all_tests else None
         self._is_lint_mode = is_lint_mode
 
     def parse(self, filename, expectations_string):
@@ -220,16 +286,7 @@
             expectation_line.matching_tests = [expectation_line.path]
             return
 
-        if not expectation_line.is_file:
-            # this is a test category, return all the tests of the category.
-            expectation_line.matching_tests = [test for test in self._all_tests if test.startswith(expectation_line.path)]
-            return
-
-        # this is a test file, do a quick check if it's in the
-        # full test suite.
-        if expectation_line.path in self._all_tests:
-            expectation_line.matching_tests.append(expectation_line.path)
-
+        expectation_line.matching_tests = self._all_tests.find_matching_tests(expectation_line.path)
 
 class TestExpectationLine(object):
     """Represents a line in test expectations file."""
diff --git a/third_party/blink/web_tests/FlagExpectations/layout-ng-fragment-item b/third_party/blink/web_tests/FlagExpectations/layout-ng-fragment-item
index 7b5f30b..17b55ef 100644
--- a/third_party/blink/web_tests/FlagExpectations/layout-ng-fragment-item
+++ b/third_party/blink/web_tests/FlagExpectations/layout-ng-fragment-item
@@ -1,6 +1,6 @@
 crbug.com/982194 accessibility/adjacent-continuations-cause-assertion-failure.html [ Failure ]
 crbug.com/982194 accessibility/aria-tables.html [ Failure ]
-crbug.com/982194 accessibility/bounds-calc.html [ Failure ]
+crbug.com/982194 accessibility/bounds-calc.html [ Failure Pass ]
 crbug.com/982194 accessibility/click-event.html [ Timeout ]
 crbug.com/982194 accessibility/first-letter-text-transform-causes-crash.html [ Failure ]
 crbug.com/982194 accessibility/first-letter-text-transform.html [ Failure ]
@@ -173,7 +173,7 @@
 crbug.com/982194 editing/selection/drag_with_unfocused_selection.html [ Failure ]
 crbug.com/982194 editing/selection/extend-selection-after-double-click.html [ Failure ]
 crbug.com/982194 editing/selection/first-letter-mouse-select-full-text.html [ Failure ]
-crbug.com/982194 editing/selection/focus-and-display-none.html [ Failure ]
+crbug.com/982194 editing/selection/focus-and-display-none.html [ Crash Failure ]
 crbug.com/982194 editing/selection/focus-crash.html [ Failure ]
 crbug.com/982194 editing/selection/inline-closest-leaf-child.html [ Failure ]
 crbug.com/982194 editing/selection/japanese-lr-selection.html [ Failure ]
@@ -397,7 +397,7 @@
 crbug.com/982194 external/wpt/css/css-writing-modes/vertical-alignment-vlr-025.xht [ Failure ]
 crbug.com/982194 external/wpt/css/css-writing-modes/vertical-alignment-vlr-027.xht [ Failure ]
 crbug.com/982194 external/wpt/css/css-writing-modes/wm-propagation-body-044.html [ Failure ]
-crbug.com/982194 external/wpt/css/cssom-view/DOMRectList.html [ Failure ]
+crbug.com/982194 external/wpt/css/cssom-view/DOMRectList.html [ Failure Pass ]
 crbug.com/982194 external/wpt/css/cssom-view/elementFromPoint-dynamic-anon-box.html [ Failure ]
 crbug.com/982194 external/wpt/css/cssom-view/elementFromPoint-list-001.html [ Failure ]
 crbug.com/982194 external/wpt/css/cssom-view/elementFromPoint-mixed-font-sizes.html [ Failure ]
@@ -694,7 +694,7 @@
 crbug.com/982194 fast/block/positioning/relative-overflow-replaced.html [ Failure ]
 crbug.com/982194 fast/block/positioning/vertical-lr/001.html [ Failure ]
 crbug.com/982194 fast/borders/border-image-inherits-with-border.html [ Failure Pass ]
-crbug.com/982194 fast/borders/border-image-outset-split-inline.html [ Failure ]
+crbug.com/982194 fast/borders/border-image-outset-split-inline.html [ Crash Failure ]
 crbug.com/982194 fast/borders/border-styles-split.html [ Failure ]
 crbug.com/982194 fast/borders/inline-mask-overlay-image-outset.html [ Failure ]
 crbug.com/982194 fast/borders/inline-mask-overlay-image.html [ Failure ]
@@ -793,14 +793,14 @@
 crbug.com/982194 fast/dom/HTMLAnchorElement/anchor-nodownload-set.html [ Timeout ]
 crbug.com/982194 fast/dom/HTMLAnchorElement/anchor-nodownload.html [ Timeout ]
 crbug.com/982194 fast/dom/HTMLAreaElement/area-download.html [ Timeout ]
-crbug.com/982194 fast/dom/Range/collapsed-range-bounding-client-rect.html [ Failure ]
-crbug.com/982194 fast/dom/Range/get-bounding-client-rect-empty-and-non-empty.html [ Failure ]
-crbug.com/982194 fast/dom/Range/getBoundingClientRect-getClientRects-relative-to-viewport.html [ Failure ]
-crbug.com/982194 fast/dom/Range/getBoundingClientRect-linebreak-character.html [ Failure ]
-crbug.com/982194 fast/dom/Range/getBoundingClientRect.html [ Failure ]
-crbug.com/982194 fast/dom/Range/getClientRects-character.html [ Failure ]
-crbug.com/982194 fast/dom/Range/getClientRects-leading-trailing-whitespaces.html [ Failure ]
-crbug.com/982194 fast/dom/Range/getClientRects.html [ Failure ]
+crbug.com/982194 fast/dom/Range/collapsed-range-bounding-client-rect.html [ Failure Pass ]
+crbug.com/982194 fast/dom/Range/get-bounding-client-rect-empty-and-non-empty.html [ Failure Pass ]
+crbug.com/982194 fast/dom/Range/getBoundingClientRect-getClientRects-relative-to-viewport.html [ Failure Pass ]
+crbug.com/982194 fast/dom/Range/getBoundingClientRect-linebreak-character.html [ Failure Pass ]
+crbug.com/982194 fast/dom/Range/getBoundingClientRect.html [ Failure Pass ]
+crbug.com/982194 fast/dom/Range/getClientRects-character.html [ Failure Pass ]
+crbug.com/982194 fast/dom/Range/getClientRects-leading-trailing-whitespaces.html [ Failure Pass ]
+crbug.com/982194 fast/dom/Range/getClientRects.html [ Failure Pass ]
 crbug.com/982194 fast/dom/Window/window-postmessage-clone-frames.html [ Failure ]
 crbug.com/982194 fast/dom/Window/window-postmessage-clone.html [ Timeout ]
 crbug.com/982194 fast/dom/elementFromPoint-relative-to-viewport.html [ Failure ]
@@ -906,7 +906,7 @@
 crbug.com/982194 fast/events/inputevents/beforeinput-remove-iframe-crash.html [ Failure ]
 crbug.com/982194 fast/events/keydown-1.html [ Failure ]
 crbug.com/982194 fast/events/max-tabindex-focus.html [ Pass Timeout ]
-crbug.com/982194 fast/events/menu-key-context-menu-document-pinch-zoom.html [ Failure ]
+crbug.com/982194 fast/events/menu-key-context-menu-document-pinch-zoom.html [ Failure Pass ]
 crbug.com/982194 fast/events/menu-key-context-menu-position.html [ Failure ]
 crbug.com/982194 fast/events/menu-key-context-menu.html [ Failure ]
 crbug.com/982194 fast/events/middleClickAutoscroll-click-hyperlink.html [ Failure ]
@@ -977,7 +977,7 @@
 crbug.com/982194 fast/events/selectstart-by-double-triple-clicks.html [ Failure ]
 crbug.com/982194 fast/events/selectstart-by-single-click-with-shift.html [ Failure ]
 crbug.com/982194 fast/events/sequential-focus-navigation-starting-point.html [ Failure ]
-crbug.com/982194 fast/events/shift-drag-selection-on-image-triggers-drag-n-drop.html [ Failure ]
+crbug.com/982194 fast/events/shift-drag-selection-on-image-triggers-drag-n-drop.html [ Crash Failure ]
 crbug.com/982194 fast/events/shift-drag-selection-on-link-triggers-drag-n-drop.html [ Failure ]
 crbug.com/982194 fast/events/simulated-click-coords.html [ Failure ]
 crbug.com/982194 fast/events/standalone-image-drag-to-editable.html [ Timeout ]
@@ -1291,8 +1291,8 @@
 crbug.com/982194 fast/scrolling/scrollable-area-frame-scrolling-yes.html [ Failure ]
 crbug.com/982194 fast/scrolling/scrollable-area-frame-visibility-hidden-child.html [ Failure ]
 crbug.com/982194 fast/scrolling/scrollable-area-frame.html [ Failure ]
-crbug.com/982194 fast/scrolling/scrollbar-tickmarks-styled-after-onload.html [ Failure ]
-crbug.com/982194 fast/scrolling/scrollbar-tickmarks-styled.html [ Failure ]
+crbug.com/982194 fast/scrolling/scrollbar-tickmarks-styled-after-onload.html [ Failure Pass ]
+crbug.com/982194 fast/scrolling/scrollbar-tickmarks-styled.html [ Failure Pass ]
 crbug.com/889952 fast/selectors/selection-window-inactive.html [ Failure ]
 crbug.com/591099 fast/selectors/shadow-host-div-with-span.html [ Failure ]
 crbug.com/591099 fast/selectors/shadow-host-div-with-text.html [ Failure ]
@@ -1374,7 +1374,7 @@
 crbug.com/982194 fast/writing-mode/border-styles-vertical-lr.html [ Failure ]
 crbug.com/982194 fast/writing-mode/english-lr-text.html [ Failure ]
 crbug.com/982194 fast/writing-mode/flipped-blocks-hit-test-line-edges.html [ Failure ]
-crbug.com/982194 fast/writing-mode/flipped-blocks-text-map-local-to-container.html [ Failure ]
+crbug.com/982194 fast/writing-mode/flipped-blocks-text-map-local-to-container.html [ Failure Pass ]
 crbug.com/982194 fast/writing-mode/japanese-lr-text.html [ Failure ]
 crbug.com/982194 fast/writing-mode/vertical-align-table-baseline.html [ Failure ]
 crbug.com/982194 fast/writing-mode/vertical-inline-block-hittest.html [ Failure ]
@@ -1417,12 +1417,12 @@
 crbug.com/982194 virtual/text-antialias/complex-text-opacity.html [ Failure ]
 crbug.com/982194 virtual/text-antialias/descent-clip-in-scaled-page.html [ Failure ]
 crbug.com/982194 virtual/text-antialias/emoji-vertical-origin-visual.html [ Failure ]
-crbug.com/982194 virtual/text-antialias/get-client-rects-grapheme.html [ Failure ]
-crbug.com/982194 virtual/text-antialias/glyph-reordering.html [ Failure ]
-crbug.com/982194 virtual/text-antialias/international/iso-8859-8.html [ Failure ]
-crbug.com/982194 virtual/text-antialias/international/rtl-selection-rect-with-fallback.html [ Failure ]
-crbug.com/982194 virtual/text-antialias/line-break-after-inline-latin1.html [ Failure ]
-crbug.com/982194 virtual/text-antialias/multiglyph-characters.html [ Failure ]
+crbug.com/982194 virtual/text-antialias/get-client-rects-grapheme.html [ Failure Pass ]
+crbug.com/982194 virtual/text-antialias/glyph-reordering.html [ Failure Pass ]
+crbug.com/982194 virtual/text-antialias/international/iso-8859-8.html [ Failure Pass ]
+crbug.com/982194 virtual/text-antialias/international/rtl-selection-rect-with-fallback.html [ Failure Pass ]
+crbug.com/982194 virtual/text-antialias/line-break-after-inline-latin1.html [ Failure Pass ]
+crbug.com/982194 virtual/text-antialias/multiglyph-characters.html [ Failure Pass ]
 crbug.com/982194 virtual/text-antialias/orientation-sideways.html [ Failure ]
 crbug.com/982194 virtual/text-antialias/recalc-position-of-linebox-after-deleting-ellipsis.html [ Failure ]
 crbug.com/982194 virtual/text-antialias/remove-zero-length-run.html [ Failure ]
@@ -1439,14 +1439,14 @@
 crbug.com/591099 virtual/text-antialias/selection/selection-rect-line-height-too-small.html [ Failure ]
 crbug.com/982194 virtual/text-antialias/selection/selection-with-inline-padding.html [ Crash ]
 crbug.com/982194 virtual/text-antialias/selection/thai-offsetForPosition-inside-character.html [ Failure ]
-crbug.com/982194 virtual/text-antialias/text-range-bounds.html [ Failure ]
+crbug.com/982194 virtual/text-antialias/text-range-bounds.html [ Failure Pass ]
 crbug.com/982194 virtual/text-antialias/whitespace/027.html [ Failure ]
 crbug.com/869364 crbug.com/874695 http/tests/devtools/console/console-correct-suggestions.js [ Crash Pass Timeout ]
 crbug.com/451577 crbug.com/924308 http/tests/devtools/console/console-dir-es6.js [ Crash Failure Pass Timeout ]
 crbug.com/451577 http/tests/devtools/console/console-error-on-call-frame.js [ Crash ]
 crbug.com/451577 crbug.com/924308 http/tests/devtools/console/console-format-es6-2.js [ Crash Failure Pass Timeout ]
 crbug.com/451577 http/tests/devtools/console/console-format-style.js [ Crash ]
-crbug.com/451577 http/tests/devtools/console/console-prompt-keyboard.js [ Failure ]
+crbug.com/451577 http/tests/devtools/console/console-prompt-keyboard.js [ Failure Pass ]
 crbug.com/451577 crbug.com/916975 http/tests/devtools/console/console-repeat-count.js [ Crash Failure Pass Timeout ]
 crbug.com/451577 http/tests/devtools/console/console-search.js [ Pass ]
 crbug.com/451577 http/tests/devtools/console/console-truncate-long-messages.js [ Crash ]
@@ -1454,7 +1454,7 @@
 crbug.com/982194 http/tests/devtools/coverage/decorations-after-script-formatter.js [ Pass Timeout ]
 crbug.com/846471 http/tests/devtools/coverage/reveal-autoformat.js [ Pass Timeout ]
 crbug.com/678482 http/tests/devtools/debugger/fetch-breakpoints.js [ Crash ]
-crbug.com/982194 http/tests/devtools/editor/text-editor-accessibility.js [ Failure ]
+crbug.com/982194 http/tests/devtools/editor/text-editor-accessibility.js [ Failure Pass ]
 crbug.com/982194 http/tests/devtools/editor/text-editor-block-indent.js [ Pass Timeout ]
 crbug.com/846997 http/tests/devtools/editor/text-editor-ctrl-d-1.js [ Timeout ]
 crbug.com/982194 http/tests/devtools/editor/text-editor-ctrl-d-2.js [ Pass Timeout ]
@@ -1503,7 +1503,7 @@
 crbug.com/451577 http/tests/devtools/elements/user-properties.js [ Pass ]
 crbug.com/941860 http/tests/devtools/extensions/extensions-events.js [ Crash ]
 crbug.com/246190 crbug.com/989860 http/tests/devtools/indexeddb/live-update-indexeddb-list.js [ Crash Failure Pass Timeout ]
-crbug.com/336481 http/tests/devtools/jump-to-previous-editing-location.js [ Failure ]
+crbug.com/336481 http/tests/devtools/jump-to-previous-editing-location.js [ Failure Pass ]
 crbug.com/327078 http/tests/devtools/network/long-script-content.js [ Pass ]
 crbug.com/938200 http/tests/devtools/network/network-blocked-reason.js [ Pass Timeout ]
 crbug.com/982194 http/tests/devtools/network/network-close-request-view.js [ Failure ]
@@ -1571,10 +1571,10 @@
 crbug.com/450493 http/tests/devtools/sources/debugger-frameworks/frameworks-sourcemap.js [ Crash Pass ]
 crbug.com/450493 http/tests/devtools/sources/debugger-frameworks/frameworks-step-into-skips-setTimeout.js [ Crash Pass ]
 crbug.com/450493 http/tests/devtools/sources/debugger-frameworks/frameworks-steppings.js [ Crash Pass ]
-crbug.com/450493 http/tests/devtools/sources/debugger-pause/debugger-change-variable.js [ Crash ]
+crbug.com/450493 http/tests/devtools/sources/debugger-pause/debugger-change-variable.js [ Crash Pass ]
 crbug.com/450493 http/tests/devtools/sources/debugger-pause/debugger-eval-on-call-frame-inside-iframe.js [ Crash ]
 crbug.com/450493 http/tests/devtools/sources/debugger-pause/debugger-eval-while-paused-throws.js [ Crash ]
-crbug.com/450493 http/tests/devtools/sources/debugger-pause/debugger-eval-while-paused.js [ Crash ]
+crbug.com/450493 http/tests/devtools/sources/debugger-pause/debugger-eval-while-paused.js [ Crash Pass ]
 crbug.com/450493 http/tests/devtools/sources/debugger-pause/debugger-mute-exception.js [ Crash ]
 crbug.com/450493 http/tests/devtools/sources/debugger-pause/debugger-pause-in-internal.js [ Crash ]
 crbug.com/450493 http/tests/devtools/sources/debugger-pause/debugger-pause-infinite-loop.js [ Crash ]
@@ -1603,7 +1603,7 @@
 crbug.com/450493 http/tests/devtools/sources/debugger-ui/custom-element-lifecycle-events.js [ Crash Pass ]
 crbug.com/450493 http/tests/devtools/sources/debugger-ui/debugger-inline-values-frames.js [ Crash ]
 crbug.com/450493 http/tests/devtools/sources/debugger-ui/debugger-inline-values.js [ Crash ]
-crbug.com/450493 http/tests/devtools/sources/debugger-ui/popover-for-spread-operator.js [ Failure ]
+crbug.com/450493 http/tests/devtools/sources/debugger-ui/popover-for-spread-operator.js [ Failure Pass ]
 crbug.com/450493 http/tests/devtools/sources/debugger-ui/reveal-not-skipped.js [ Crash ]
 crbug.com/450493 http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-2.js [ Crash ]
 crbug.com/450493 http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-3.js [ Crash ]
@@ -1624,10 +1624,10 @@
 crbug.com/450493 http/tests/devtools/sources/debugger/mutation-observer-suspend-while-paused.js [ Crash Pass ]
 crbug.com/450493 http/tests/devtools/sources/debugger/rethrow-error-from-bindings-crash.js [ Crash Pass ]
 crbug.com/450493 http/tests/devtools/sources/debugger/source-frame-breakpoint-decorations.js [ Crash ]
-crbug.com/450493 http/tests/devtools/sources/debugger/source-frame-inline-breakpoint-decorations.js [ Crash ]
+crbug.com/450493 http/tests/devtools/sources/debugger/source-frame-inline-breakpoint-decorations.js [ Crash Pass ]
 crbug.com/450493 http/tests/devtools/sources/inline-script-with-source-map.js [ Crash ]
 crbug.com/450493 http/tests/devtools/sources/source-frame-toolbar-items.js [ Crash Pass ]
-crbug.com/450493 http/tests/devtools/sources/sources-pretty-print.js [ Crash ]
+crbug.com/450493 http/tests/devtools/sources/sources-pretty-print.js [ Crash Pass ]
 crbug.com/450493 http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-1.js [ Crash ]
 crbug.com/450493 http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-4.js [ Crash ]
 crbug.com/450493 http/tests/devtools/startup/sources/debugger/script-formatter-console.js [ Crash ]
@@ -1637,7 +1637,7 @@
 crbug.com/851363 http/tests/devtools/sxg/sxg-prefetch.js [ Pass ]
 crbug.com/420008 http/tests/devtools/tracing/hit-test.js [ Failure ]
 crbug.com/420008 crbug.com/916975 http/tests/devtools/tracing/timeline-misc/timeline-event-causes.js [ Crash Failure Pass Timeout ]
-crbug.com/982194 http/tests/devtools/unit/source-frame-pretty-print.js [ Crash ]
+crbug.com/982194 http/tests/devtools/unit/source-frame-pretty-print.js [ Crash Pass ]
 crbug.com/982194 http/tests/download/default-encoding.html [ Timeout ]
 crbug.com/982194 http/tests/download/inherited-encoding.html [ Timeout ]
 crbug.com/982194 http/tests/filesystem/input-display.html [ Timeout ]
@@ -2278,7 +2278,8 @@
 crbug.com/982194 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-014.html [ Pass ]
 crbug.com/982194 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-015.html [ Pass ]
 crbug.com/982194 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/selectors4/class-id-attr-selector-invalidation-01.html [ Failure Pass ]
-crbug.com/982194 external/wpt/geolocation-API/PositionOptions.https.html [ Failure ]
+crbug.com/982194 external/wpt/forced-colors-mode/forced-colors-mode-19.html [ Pass ]
+crbug.com/982194 external/wpt/geolocation-API/PositionOptions.https.html [ Failure Pass ]
 crbug.com/982194 external/wpt/html/rendering/non-replaced-elements/tables/table-border-3q.html [ Pass ]
 crbug.com/982194 external/wpt/html/rendering/non-replaced-elements/tables/table-border-3s.html [ Pass ]
 crbug.com/982194 external/wpt/html/semantics/forms/form-submission-0/implicit-submission.optional.html [ Failure ]
@@ -2286,6 +2287,7 @@
 crbug.com/982194 external/wpt/largest-contentful-paint/element-only-when-fully-active.html [ Timeout ]
 crbug.com/982194 external/wpt/largest-contentful-paint/first-paint-equals-lcp-text.html [ Timeout ]
 crbug.com/982194 external/wpt/largest-contentful-paint/toJSON.html [ Timeout ]
+crbug.com/982194 external/wpt/mathml/relations/html5-tree/href-click-3.html [ Pass ]
 crbug.com/982194 external/wpt/native-file-system/sandboxed_FileSystemBaseHandle-postMessage-BroadcastChannel.tentative.https.window.html [ Crash ]
 crbug.com/982194 external/wpt/native-file-system/sandboxed_FileSystemBaseHandle-postMessage-MessagePort.tentative.https.window.html [ Crash ]
 crbug.com/982194 external/wpt/native-file-system/sandboxed_FileSystemBaseHandle-postMessage.tentative.https.window.html [ Crash ]
@@ -2298,6 +2300,7 @@
 crbug.com/982194 external/wpt/native-file-system/sandboxed_FileSystemWriter.tentative.https.any.html [ Crash Pass ]
 crbug.com/982194 external/wpt/native-file-system/sandboxed_FileSystemWriter.tentative.https.any.worker.html [ Crash ]
 crbug.com/982194 external/wpt/speech-api/SpeechSynthesis-speak-events.html [ Failure ]
+crbug.com/982194 external/wpt/uievents/click/click_events_on_input.html [ Timeout ]
 crbug.com/982194 fast/block/basic/020.html [ Failure ]
 crbug.com/982194 fast/block/basic/adding-near-anonymous-block.html [ Failure ]
 crbug.com/982194 fast/block/float/001.html [ Failure ]
@@ -2432,6 +2435,7 @@
 crbug.com/982194 http/tests/devtools/service-workers/user-agent-override.js [ Pass ]
 crbug.com/982194 http/tests/devtools/sources/debugger-async/async-await/async-callstack-async-await1.js [ Crash Pass ]
 crbug.com/982194 http/tests/devtools/sources/debugger-async/async-callstack-events.js [ Crash Pass ]
+crbug.com/982194 http/tests/devtools/sources/debugger-breakpoints/event-listener-breakpoints-webaudio.js [ Crash Pass ]
 crbug.com/982194 http/tests/devtools/sources/debugger-step/debugger-step-in.js [ Crash Pass ]
 crbug.com/982194 http/tests/devtools/tracing/timeline-paint/paint-profiler-update.js [ Pass ]
 crbug.com/982194 http/tests/devtools/tracing/timeline-style/timeline-style-recalc-with-invalidator-invalidations.js [ Pass Timeout ]
@@ -2558,7 +2562,7 @@
 crbug.com/982194 virtual/controls-refresh/color-scheme/time/time-appearance-basic.html [ Failure ]
 crbug.com/982194 virtual/controls-refresh/color-scheme/week/week-appearance-basic.html [ Failure ]
 crbug.com/982194 virtual/controls-refresh/datetimelocal-picker/datetimelocal-select-value.html [ Failure ]
-crbug.com/982194 virtual/disable-deferred-rendering/fast/canvas/OffscreenCanvas-copyImage.html [ Pass ]
+crbug.com/982194 virtual/disable-deferred-rendering/fast/canvas/OffscreenCanvas-copyImage.html [ Failure Pass ]
 crbug.com/982194 virtual/disable-deferred-rendering/fast/canvas/webgl/draw-webgl-to-canvas-2d-after-to-data-url-without-context.html [ Crash ]
 crbug.com/982194 virtual/disable-deferred-rendering/fast/canvas/webgl/draw-webgl-to-canvas-2d.html [ Crash ]
 crbug.com/982194 virtual/exotic-color-space/images/55.html [ Failure ]
@@ -2882,7 +2886,7 @@
 crbug.com/982194 virtual/mouseevent_fractional/fast/events/drag-remove-iframe-crash.html [ Timeout ]
 crbug.com/982194 virtual/mouseevent_fractional/fast/events/drag-svg-image-crash.html [ Crash ]
 crbug.com/982194 virtual/mouseevent_fractional/fast/events/drag_and_drop_into_removed_on_focus.html [ Crash ]
-crbug.com/982194 virtual/mouseevent_fractional/fast/events/event-hit-testing-fallback-to-iframe.html [ Failure ]
+crbug.com/982194 virtual/mouseevent_fractional/fast/events/event-hit-testing-fallback-to-iframe.html [ Crash Failure ]
 crbug.com/982194 virtual/mouseevent_fractional/fast/events/event-on-culled-inline-with-pseudo.html [ Failure ]
 crbug.com/982194 virtual/mouseevent_fractional/fast/events/event-on-culled_inline.html [ Failure ]
 crbug.com/982194 virtual/mouseevent_fractional/fast/events/event-trusted.html [ Failure ]
@@ -2896,7 +2900,7 @@
 crbug.com/982194 virtual/mouseevent_fractional/fast/events/input-image-scrolled-x-y.html [ Timeout ]
 crbug.com/982194 virtual/mouseevent_fractional/fast/events/inputevents/beforeinput-remove-iframe-crash.html [ Failure ]
 crbug.com/982194 virtual/mouseevent_fractional/fast/events/keydown-1.html [ Failure ]
-crbug.com/982194 virtual/mouseevent_fractional/fast/events/menu-key-context-menu-document-pinch-zoom.html [ Failure ]
+crbug.com/982194 virtual/mouseevent_fractional/fast/events/menu-key-context-menu-document-pinch-zoom.html [ Failure Pass ]
 crbug.com/982194 virtual/mouseevent_fractional/fast/events/menu-key-context-menu-position.html [ Failure ]
 crbug.com/982194 virtual/mouseevent_fractional/fast/events/menu-key-context-menu.html [ Crash Failure ]
 crbug.com/982194 virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-drag-scrollable-iframe-div.html [ Crash ]
@@ -3087,8 +3091,8 @@
 crbug.com/982194 virtual/scroll_customization/fast/scrolling/scrollable-area-frame-scrolling-yes.html [ Failure ]
 crbug.com/982194 virtual/scroll_customization/fast/scrolling/scrollable-area-frame-visibility-hidden-child.html [ Failure ]
 crbug.com/982194 virtual/scroll_customization/fast/scrolling/scrollable-area-frame.html [ Failure ]
-crbug.com/982194 virtual/scroll_customization/fast/scrolling/scrollbar-tickmarks-styled-after-onload.html [ Failure ]
-crbug.com/982194 virtual/scroll_customization/fast/scrolling/scrollbar-tickmarks-styled.html [ Failure ]
+crbug.com/982194 virtual/scroll_customization/fast/scrolling/scrollbar-tickmarks-styled-after-onload.html [ Failure Pass ]
+crbug.com/982194 virtual/scroll_customization/fast/scrolling/scrollbar-tickmarks-styled.html [ Failure Pass ]
 crbug.com/982194 virtual/speech-with-unified-autoplay/external/wpt/speech-api/SpeechSynthesis-speak-events.html [ Failure ]
 crbug.com/982194 virtual/speech-with-unified-autoplay/external/wpt/speech-api/SpeechSynthesis-speak-ownership.html [ Timeout ]
 crbug.com/982194 virtual/speech-with-unified-autoplay/external/wpt/speech-api/SpeechSynthesis-speak-without-activation-fails.tentative.html [ Pass ]
@@ -3188,8 +3192,9 @@
 crbug.com/982194 virtual/threaded-prefer-compositing/fast/scrolling/scrollable-area-frame-scrolling-yes.html [ Failure ]
 crbug.com/982194 virtual/threaded-prefer-compositing/fast/scrolling/scrollable-area-frame-visibility-hidden-child.html [ Failure ]
 crbug.com/982194 virtual/threaded-prefer-compositing/fast/scrolling/scrollable-area-frame.html [ Failure ]
-crbug.com/982194 virtual/threaded-prefer-compositing/fast/scrolling/scrollbar-tickmarks-styled-after-onload.html [ Failure ]
-crbug.com/982194 virtual/threaded-prefer-compositing/fast/scrolling/scrollbar-tickmarks-styled.html [ Failure ]
+crbug.com/982194 virtual/threaded-prefer-compositing/fast/scrolling/scrollbar-tickmarks-styled-after-onload.html [ Failure Pass ]
+crbug.com/982194 virtual/threaded-prefer-compositing/fast/scrolling/scrollbar-tickmarks-styled.html [ Failure Pass ]
+crbug.com/982194 virtual/threaded/external/wpt/css/css-animations/CSSAnimation-canceling.tentative.html [ Failure Pass ]
 crbug.com/982194 virtual/threaded/external/wpt/feature-policy/experimental-features/lazyload/lazyload-enabled-tentative.sub.html [ Pass ]
 crbug.com/982194 virtual/threaded/external/wpt/feature-policy/experimental-features/lazyload/loading-frame-default-eager-disabled-tentative.sub.html [ Pass ]
 crbug.com/982194 virtual/threaded/external/wpt/feature-policy/experimental-features/vertical-scroll-disabled-frame-no-scroll-manual.tentative.html [ Failure ]
diff --git a/third_party/blink/web_tests/LeakExpectations b/third_party/blink/web_tests/LeakExpectations
index 27a7b4ec..7900c0d6 100644
--- a/third_party/blink/web_tests/LeakExpectations
+++ b/third_party/blink/web_tests/LeakExpectations
@@ -105,8 +105,6 @@
 # Only times out on the leak bots.
 crbug.com/998399 [ Linux ] virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/worker-interception.https.html [ Pass Timeout ]
 
-crbug.com/1023242 [ Linux ] editing/input/edit-context.html [ Pass Failure ]
-
 ###########################################################################
 # WARNING: Memory leaks must be fixed asap. Sheriff is expected to revert #
 # culprit CLs instead of suppressing the leaks. If you have any question, #
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 7c52b4a8..1dda27d 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -69,7 +69,6 @@
 crbug.com/678491 http/tests/misc/webtiming-no-origin.html [ Crash Pass ]
 crbug.com/765779 http/tests/loading/bad-server-subframe.html [ Failure ]
 crbug.com/801992 http/tests/misc/iframe-script-modify-attr.html [ Pass Crash ]
-crbug.com/819800 external/wpt/payment-request/allowpaymentrequest/setting-allowpaymentrequest-timing.https.sub.html [ Failure ]
 crbug.com/872952 http/tests/media/autoplay/document-user-activation-feature-policy-iframe-no-gesture.html [ Failure Pass Timeout ]
 crbug.com/872952 virtual/audio-service/http/tests/media/autoplay/document-user-activation-feature-policy-iframe-no-gesture.html [ Failure Pass Timeout ]
 crbug.com/895001 external/wpt/html/user-activation/message-event-activation-api-iframe-cross-origin.sub.tentative.html [ Timeout Pass ]
@@ -1480,12 +1479,7 @@
 # ====== MathMLCore-only tests from here ======
 
 crbug.com/6606 external/wpt/mathml/presentation-markup/direction/direction-006.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/presentation-markup/direction/direction-007.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/presentation-markup/direction/direction-008.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/presentation-markup/direction/direction-009.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/presentation-markup/direction/direction-010.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/direction/direction-overall.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/presentation-markup/direction/direction-token.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/direction/direction.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-1.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-bar-001.html [ Failure ]
@@ -1566,7 +1560,6 @@
 crbug.com/6606 external/wpt/mathml/presentation-markup/tables/table-001.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/tables/table-002.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/tables/table-axis-height.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/relations/css-styling/attribute-mapping-001.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/css-styling/attribute-mapping-002.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/css-styling/color-002.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/css-styling/color-003.html [ Failure ]
@@ -1579,21 +1572,11 @@
 crbug.com/6606 external/wpt/mathml/relations/css-styling/displaystyle-015.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/css-styling/displaystyle-1.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/css-styling/displaystyle-2.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/relations/css-styling/dynamic-dir-1.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/css-styling/ignored-properties-001.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/css-styling/lengths-1.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/css-styling/lengths-2.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/relations/css-styling/mathbackground-001.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/relations/css-styling/mathbackground-002.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/relations/css-styling/mathbackground-003.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/css-styling/mathbackground-004.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/relations/css-styling/mathcolor-001.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/relations/css-styling/mathcolor-002.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/relations/css-styling/mathcolor-003.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/css-styling/mathcolor-004.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/relations/css-styling/mathsize-attribute-css-keywords.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/relations/css-styling/mathsize-attribute-legacy-values.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/relations/css-styling/mathsize-attribute.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-auto.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-bold-fraktur.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-bold-italic.html [ Failure ]
@@ -1627,7 +1610,6 @@
 crbug.com/6606 external/wpt/mathml/relations/css-styling/width-height-001.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/css-styling/writing-mode/writing-mode-001.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/css-styling/writing-mode/writing-mode-002.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/relations/html5-tree/color-attributes-1.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/html5-tree/display-1.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/html5-tree/display-2.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/html5-tree/href-click-1.html [ Failure ]
diff --git a/third_party/blink/web_tests/editing/input/edit-context.html b/third_party/blink/web_tests/editing/input/edit-context.html
index a7747dab..511eeef 100644
--- a/third_party/blink/web_tests/editing/input/edit-context.html
+++ b/third_party/blink/web_tests/editing/input/edit-context.html
@@ -457,13 +457,12 @@
   const textarea = childDocument.createElement('textarea');
   childDocument.body.appendChild(textarea);
   textarea.addEventListener("focusin", e => {
-    childDocument.childEditContext = new EditContext()
-    childDocument.childEditContext.focus();
-    childDocument.childEditContext.addEventListener("textupdate", e => {
-      console.log("iframe textupdate event fired");
+    const childEditContext = new EditContext()
+    childEditContext.focus();
+    childEditContext.addEventListener("textupdate", e => {
       child.remove();
     });
-    childDocument.childEditContext.addEventListener("textformatupdate", e => {
+    childEditContext.addEventListener("textformatupdate", e => {
     });
   });
   textarea.focus();
@@ -472,26 +471,14 @@
 }, 'Testing EditContext Iframe Document Delete');
 
 test(function() {
-  // SetComposition should not crash when event handler removes document
-  const child = document.createElement("iframe");
-  document.body.appendChild(child);
-  const childDocument = child.contentDocument;
-  const textarea = childDocument.createElement('textarea');
-  childDocument.body.appendChild(textarea);
-  textarea.addEventListener("focusin", e => {
-    childDocument.childEditContext = new EditContext()
-    childDocument.childEditContext.focus()
-    childDocument.childEditContext.addEventListener("textupdate", e => {
-      console.log("textupdate event fired");
-      textarea.remove();
-    });
-    childDocument.childEditContext.addEventListener("textformatupdate", e => {
-    });
+  const editContext1 = new EditContext();
+  editContext1.addEventListener("textupdate", e => {
   });
-  child.contentWindow.focus();
-  textarea.focus();
+  editContext1.focus();
+  gc()
   textInputController.setComposition("bar");
-}, 'Testing EditContext Iframe Element Delete');
+
+}, 'Testing EditContext GC');
 
 </script>
 </body>
diff --git a/third_party/blink/web_tests/editing/selection/move-selection-detached-frame-crash-expected.txt b/third_party/blink/web_tests/editing/selection/move-selection-detached-frame-crash-expected.txt
new file mode 100644
index 0000000..880026b
--- /dev/null
+++ b/third_party/blink/web_tests/editing/selection/move-selection-detached-frame-crash-expected.txt
@@ -0,0 +1,9 @@
+Should not crash if iframe's document element's focusout event handler removes the iframe in the parent document and the selection is moved.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS Did not crash.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/third_party/blink/web_tests/editing/selection/move-selection-detached-frame-crash.html b/third_party/blink/web_tests/editing/selection/move-selection-detached-frame-crash.html
new file mode 100644
index 0000000..238adc5
--- /dev/null
+++ b/third_party/blink/web_tests/editing/selection/move-selection-detached-frame-crash.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src="../../resources/js-test.js"></script>
+</head>
+<body>
+    <iframe></iframe>
+    <script>
+        description('Should not crash if iframe\'s document element\'s focusout event handler removes the iframe in the parent document and the selection is moved.');
+
+        function run() {
+            var iframe = document.getElementsByTagName('iframe')[0];
+            iframe.contentDocument.documentElement.contentEditable = true;
+            iframe.contentDocument.documentElement.addEventListener('focusout', function () {
+                iframe.parentNode.removeChild(iframe);
+            }, false);
+            iframe.contentDocument.documentElement.focus();
+
+            if (window.eventSender)
+                eventSender.keyDown('ArrowDown');
+
+            testPassed('Did not crash.');
+            window.finishJSTest();
+        }
+
+        document.addEventListener('DOMContentLoaded', run);
+    </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
index d9623a8..2e84db1 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -61799,6 +61799,18 @@
      {}
     ]
    ],
+   "css/css-position/position-absolute-dynamic-relayout-002.html": [
+    [
+     "css/css-position/position-absolute-dynamic-relayout-002.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-position/position-absolute-dynamic-static-position-floats-001.html": [
     [
      "css/css-position/position-absolute-dynamic-static-position-floats-001.html",
@@ -165379,6 +165391,9 @@
    "mathml/presentation-markup/spaces/space-2-ref.html": [
     []
    ],
+   "mathml/relations/css-styling/attribute-mapping-001-expected.txt": [
+    []
+   ],
    "mathml/relations/css-styling/color-001-ref.html": [
     []
    ],
@@ -167239,9 +167254,6 @@
    "payment-request/allowpaymentrequest/echo-PaymentRequest.html": [
     []
    ],
-   "payment-request/allowpaymentrequest/setting-allowpaymentrequest-timing.https.sub-expected.txt": [
-    []
-   ],
    "payment-request/blank.html": [
     []
    ],
@@ -172129,9 +172141,6 @@
    "service-workers/service-worker/resources/xslt-pass.xsl": [
     []
    ],
-   "service-workers/service-worker/sandboxed-iframe-navigator-serviceworker.https-expected.txt": [
-    []
-   ],
    "service-workers/service-worker/unregister-then-register-new-script.https-expected.txt": [
     []
    ],
@@ -173608,6 +173617,9 @@
    "tools/manifest/sourcefile.py": [
     []
    ],
+   "tools/manifest/testpaths.py": [
+    []
+   ],
    "tools/manifest/update.py": [
     []
    ],
@@ -179221,9 +179233,6 @@
    "webrtc/RTCPeerConnection-helper.js": [
     []
    ],
-   "webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt": [
-    []
-   ],
    "webrtc/RTCPeerConnection-iceGatheringState-expected.txt": [
     []
    ],
@@ -213634,6 +213643,12 @@
      {}
     ]
    ],
+   "css/css-position/position-absolute-crash-chrome-013.html": [
+    [
+     "css/css-position/position-absolute-crash-chrome-013.html",
+     {}
+    ]
+   ],
    "css/css-position/position-absolute-dynamic-containing-block.html": [
     [
      "css/css-position/position-absolute-dynamic-containing-block.html",
@@ -340351,7 +340366,7 @@
    "support"
   ],
   "README.md": [
-   "e3ee8551ff48a77652e2a4dc4fbc677e0812c5c5",
+   "8f755f2ac2fc88e012b32803fc65a68baa538596",
    "support"
   ],
   "WebCryptoAPI/META.yml": [
@@ -393670,6 +393685,10 @@
    "f012ff572691d9c42fb8f642a2fa47e524a4cacf",
    "testharness"
   ],
+  "css/css-position/position-absolute-crash-chrome-013.html": [
+   "8f0daf4bfe4aef4916e3cfb037181cf8124fb938",
+   "testharness"
+  ],
   "css/css-position/position-absolute-dynamic-containing-block.html": [
    "3968f685849663574ca213fcb90dc5fb3eaffaa3",
    "testharness"
@@ -393690,6 +393709,10 @@
    "1bde15551e7952cce210463d156217a51d3f30f3",
    "reftest"
   ],
+  "css/css-position/position-absolute-dynamic-relayout-002.html": [
+   "4dc2dc0f803dd657c90e62529f7f37f6a1efc108",
+   "reftest"
+  ],
   "css/css-position/position-absolute-dynamic-static-position-floats-001.html": [
    "a63df41089e7e75d33ee1f46d458e97c8ebf0fb0",
    "reftest"
@@ -406831,7 +406854,7 @@
    "support"
   ],
   "css/css-text/word-break/reference/word-break-break-all-inline-006-ref.html": [
-   "0232ecb6f011358493059518be22e98697d45d76",
+   "268536c98e066b3b6cf9db6fbf67e944c86dbe44",
    "support"
   ],
   "css/css-text/word-break/reference/word-break-break-all-inline-007-ref.html": [
@@ -407099,7 +407122,7 @@
    "reftest"
   ],
   "css/css-text/word-break/word-break-break-all-inline-006.html": [
-   "af2f6fb90b23db36f7e0db531f89475dfa632cd3",
+   "9f9a618ebfed4e1b747199d36741f5e567b3ceb0",
    "reftest"
   ],
   "css/css-text/word-break/word-break-break-all-inline-007.html": [
@@ -469526,8 +469549,12 @@
    "f9354266a7c6c42a75519e9771eb2c61536ad8e5",
    "testharness"
   ],
+  "mathml/relations/css-styling/attribute-mapping-001-expected.txt": [
+   "5c64785af046109797f9980ec7da6db6f4b3acb1",
+   "support"
+  ],
   "mathml/relations/css-styling/attribute-mapping-001.html": [
-   "e7c6391ebad2aad8b8d31db056a209ad1d7ebba5",
+   "3424e8c1a8cbb775a91839db6b3951dfe869af95",
    "testharness"
   ],
   "mathml/relations/css-styling/attribute-mapping-002.html": [
@@ -482014,10 +482041,6 @@
    "b0e0cbacbf2e611139cd30747acaaf1186ae21f6",
    "testharness"
   ],
-  "payment-request/allowpaymentrequest/setting-allowpaymentrequest-timing.https.sub-expected.txt": [
-   "2103791bb0a8ef4c9d6314c4e217a2121955922a",
-   "support"
-  ],
   "payment-request/allowpaymentrequest/setting-allowpaymentrequest-timing.https.sub.html": [
    "45661de7e0dedd19cda4a7a2f0a76aaa72e91ccc",
    "testharness"
@@ -497119,7 +497142,7 @@
    "support"
   ],
   "resources/testdriver-actions.js": [
-   "292fe8889f5e21bd011ade89edf540cc615e35b2",
+   "d3dc00b4d0f002670258e56d59d33061f4fc5775",
    "support"
   ],
   "resources/testdriver-vendor.js.headers": [
@@ -497351,11 +497374,11 @@
    "testharness"
   ],
   "scroll-to-text-fragment/scroll-to-text-fragment-target.html": [
-   "1ef9dbba519697c61529fba0e81dc64979257e70",
+   "1595d8bff9ebeefe0d22052cfe4dea59a8c5b750",
    "support"
   ],
   "scroll-to-text-fragment/scroll-to-text-fragment.html": [
-   "b3a35f489aca424b24c57b40796a179c5ee5b4c7",
+   "c0018eda03fc40f99f61468fea01945b73979f2f",
    "testharness"
   ],
   "secure-contexts/META.yml": [
@@ -500970,12 +500993,8 @@
    "e08b71645325cd8fe94f1001bc778ed325571a98",
    "testharness"
   ],
-  "service-workers/service-worker/sandboxed-iframe-navigator-serviceworker.https-expected.txt": [
-   "01dcfe66bfabba0b491786e3b1c3b753feaeaa4f",
-   "support"
-  ],
   "service-workers/service-worker/sandboxed-iframe-navigator-serviceworker.https.html": [
-   "a58525f422203b087a2c88ef24cfadfa567fade2",
+   "70be6ef9b0a956707384030b1be17b702663976f",
    "testharness"
   ],
   "service-workers/service-worker/service-worker-csp-connect.https.html": [
@@ -506395,7 +506414,7 @@
    "support"
   ],
   "tools/manifest/commands.json": [
-   "074d248bf27a417933195ad4f8d232468bb1b6dd",
+   "769675e0ee42c6efc36fc1150778682f933a6465",
    "support"
   ],
   "tools/manifest/download.py": [
@@ -506403,7 +506422,7 @@
    "support"
   ],
   "tools/manifest/item.py": [
-   "a38709193a9bed82e6bff5849c07679abb6abafd",
+   "217f6cac262fba020c044d963d42ca55a2361e53",
    "support"
   ],
   "tools/manifest/log.py": [
@@ -506411,13 +506430,17 @@
    "support"
   ],
   "tools/manifest/manifest.py": [
-   "8aace771cba32acca56c9c37887d4a900cafbeb0",
+   "6fb591b9c314b0dd01d93ff12f3820bf26d4fe79",
    "support"
   ],
   "tools/manifest/sourcefile.py": [
    "4788fc9bdb640447eb9997808c5b7110661a3960",
    "support"
   ],
+  "tools/manifest/testpaths.py": [
+   "3c1f09d437304a0ce7bbcbc542dede672f01a17e",
+   "support"
+  ],
   "tools/manifest/update.py": [
    "bfbaaf897126d8eeca69dcef94bb31308897fde2",
    "support"
@@ -520174,12 +520197,8 @@
    "dbfc41e0c375dea0711a8cf312846cbdb27c570b",
    "testharness"
   ],
-  "webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt": [
-   "1b3bb5465db7e3f971107832f97d33b4f93e4130",
-   "support"
-  ],
   "webrtc/RTCPeerConnection-iceConnectionState.https.html": [
-   "32f2eb9b4607121ad9aa0db99a05e3ab78750f7e",
+   "a2b2827b8494af7e33e5c81b765b92041064fe6f",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-iceGatheringState-expected.txt": [
diff --git a/third_party/blink/web_tests/external/wpt/README.md b/third_party/blink/web_tests/external/wpt/README.md
index e3ee855..8f755f2a 100644
--- a/third_party/blink/web_tests/external/wpt/README.md
+++ b/third_party/blink/web_tests/external/wpt/README.md
@@ -48,128 +48,9 @@
 Running the Tests
 =================
 
-The tests are designed to be run from your local computer. The test
-environment requires [Python 2.7+](http://www.python.org/downloads) (but not Python 3.x).
-
-On Windows, be sure to add the Python directory (`c:\python2x`, by default) to
-your `%Path%` [Environment Variable](http://www.computerhope.com/issues/ch000549.htm),
-and read the [Windows Notes](#windows-notes) section below.
-
-To get the tests running, you need to set up the test domains in your
-[`hosts` file](http://en.wikipedia.org/wiki/Hosts_%28file%29%23Location_in_the_file_system).
-
-The necessary content can be generated with `./wpt make-hosts-file`; on
-Windows, you will need to precede the prior command with `python` or
-the path to the Python binary (`python wpt make-hosts-file`).
-
-For example, on most UNIX-like systems, you can setup the hosts file with:
-
-```bash
-./wpt make-hosts-file | sudo tee -a /etc/hosts
-```
-
-And on Windows (this must be run in a PowerShell session with Administrator privileges):
-
-```powershell
-python wpt make-hosts-file | Out-File $env:systemroot\System32\drivers\etc\hosts -Encoding ascii -Append
-```
-
-If you are behind a proxy, you also need to make sure the domains above are
-excluded from your proxy lookups.
-
-
-Running Tests Manually
-======================
-
-The test server can be started using
-```
-./wpt serve
-```
-
-**On Windows**: You will need to precede the prior command with
-`python` or the path to the python binary.
-```bash
-python wpt serve
-```
-
-This will start HTTP servers on two ports and a websockets server on
-one port. By default the web servers start on ports 8000 and 8443 and
-the other ports are randomly-chosen free ports. Tests must be loaded
-from the *first* HTTP server in the output. To change the ports,
-create a `config.json` file in the wpt root directory, and add
-port definitions of your choice e.g.:
-
-```
-{
-  "ports": {
-    "http": [1234, "auto"],
-    "https":[5678]
-  }
-}
-```
-
-After your `hosts` file is configured, the servers will be locally accessible at:
-
-http://web-platform.test:8000/<br>
-https://web-platform.test:8443/ *
-
-To use the web-based runner point your browser to:
-
-http://web-platform.test:8000/tools/runner/index.html <br>
-https://web-platform.test:8443/tools/runner/index.html *
-
-\**See [Trusting Root CA](./tools/certs/README.md)*
-
-Running Tests Automatically
----------------------------
-
-Tests can be run automatically in a browser using the `run` command of
-the `wpt` script in the root of the checkout. This requires the hosts
-file setup documented above, but you must *not* have the
-test server already running when calling `wpt run`. The basic command
-line syntax is:
-
-```bash
-./wpt run product [tests]
-```
-
-**On Windows**: You will need to precede the prior command with
-`python` or the path to the python binary.
-```bash
-python wpt run product [tests]
-```
-
-where `product` is currently `firefox` or `chrome` and `[tests]` is a
-list of paths to tests. This will attempt to automatically locate a
-browser instance and install required dependencies. The command is
-very configurable; for example to specify a particular binary use
-`wpt run --binary=path product`. The full range of options can be see
-with `wpt run --help` and `wpt run --wptrunner-help`.
-
-Not all dependencies can be automatically installed; in particular the
-`certutil` tool required to run https tests with Firefox must be
-installed using a system package manager or similar.
-
-On Debian/Ubuntu certutil may be installed using:
-
-```
-sudo apt install libnss3-tools
-```
-
-And on macOS with homebrew using:
-
-```
-brew install nss
-```
-
-On other platforms, download the firefox archive and common.tests.tar.gz
-archive for your platform from
-[Mozilla CI](https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/).
-
-Then extract `certutil[.exe]` from the tests.tar.gz package and
-`libnss3[.so|.dll|.dynlib]` and put the former on your path and the latter on
-your library path.
-
+See the [documentation website](https://web-platform-tests.org/running-tests/)
+and in particular the
+[system setup for running tests locally](https://web-platform-tests.org/running-tests/from-local-system.html#system-setup).
 
 Command Line Tools
 ==================
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/CSSPseudoElement-getAnimations.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-animations/CSSPseudoElement-getAnimations.tentative-expected.txt
deleted file mode 100644
index 08e57b14..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-animations/CSSPseudoElement-getAnimations.tentative-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-PASS getAnimations returns CSSAnimation objects
-FAIL getAnimations returns CSS transitions/animations, and script-generated animations in the expected order assert_equals: 1st animation is the 1st transition sorted by name expected (string) "height" but got (undefined) undefined
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/CSSPseudoElement-getAnimations.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-animations/CSSPseudoElement-getAnimations.tentative.html
deleted file mode 100644
index 8fcdf38..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-animations/CSSPseudoElement-getAnimations.tentative.html
+++ /dev/null
@@ -1,75 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<title>CSSPseudoElement.getAnimations() for CSS animations</title>
-<!-- TODO: Add a more specific link for this once it is specified. -->
-<link rel="help" href="https://drafts.csswg.org/css-animations-2/">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="support/testcommon.js"></script>
-<style>
-@keyframes anim1 { }
-@keyframes anim2 { }
-.before::before {
-  animation: anim1 10s;
-  content: '';
-}
-.after-with-mix-anims-trans::after {
-  content: '';
-  animation: anim1 10s, anim2 10s;
-  width: 0px;
-  height: 0px;
-  transition: all 100s;
-}
-.after-change::after {
-  width: 100px;
-  height: 100px;
-  content: '';
-}
-</style>
-<div id="log"></div>
-<script>
-'use strict';
-
-test(t => {
-  const div = addDiv(t, { class: 'before' });
-  const pseudoTarget = document.getAnimations()[0].effect.target;
-  assert_equals(pseudoTarget.getAnimations().length, 1,
-                'Expected number of animations are returned');
-  assert_equals(pseudoTarget.getAnimations()[0].animationName, 'anim1',
-                'CSS animation name matches');
-}, 'getAnimations returns CSSAnimation objects');
-
-test(t => {
-  const div = addDiv(t, { class: 'after-with-mix-anims-trans' });
-  // Trigger transitions
-  flushComputedStyle(div);
-  div.classList.add('after-change');
-
-  // Create additional animation on the pseudo-element from script
-  const pseudoTarget = document.getAnimations()[0].effect.target;
-  const effect = new KeyframeEffect(pseudoTarget,
-                                    { background: ["blue", "red"] },
-                                    3 * MS_PER_SEC);
-  const newAnimation = new Animation(effect, document.timeline);
-  newAnimation.id = 'scripted-anim';
-  newAnimation.play();
-
-  // Check order - the script-generated animation should appear later
-  const anims = pseudoTarget.getAnimations();
-  assert_equals(anims.length, 5,
-                'Got expected number of animations/trnasitions running on ' +
-                '::after pseudo element');
-  assert_equals(anims[0].transitionProperty, 'height',
-                '1st animation is the 1st transition sorted by name');
-  assert_equals(anims[1].transitionProperty, 'width',
-                '2nd animation is the 2nd transition sorted by name ');
-  assert_equals(anims[2].animationName, 'anim1',
-                '3rd animation is the 1st animation in animation-name list');
-  assert_equals(anims[3].animationName, 'anim2',
-                '4rd animation is the 2nd animation in animation-name list');
-  assert_equals(anims[4].id, 'scripted-anim',
-                'Animation added by script appears last');
-}, 'getAnimations returns CSS transitions/animations, and script-generated ' +
-   'animations in the expected order');
-
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/Document-getAnimations.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-animations/Document-getAnimations.tentative-expected.txt
index 7bd0e24..e65479c4 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-animations/Document-getAnimations.tentative-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-animations/Document-getAnimations.tentative-expected.txt
@@ -12,6 +12,6 @@
 PASS Yet-to-start CSS Animations are returned
 PASS CSS Animations canceled via the API are not returned
 PASS CSS Animations canceled and restarted via the API are returned
-FAIL CSS Animations targetting (pseudo-)elements should have correct order after sorting assert_equals: Animation #2 has expected target expected (object) Element node <div id="parent" style="animation: animBottom 100s"><div ... but got (undefined) undefined
+FAIL CSS Animations targetting (pseudo-)elements should have correct order after sorting assert_equals: Animation #1 has null pseudo type expected (object) null but got (undefined) undefined
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/Document-getAnimations.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-animations/Document-getAnimations.tentative.html
index 175acf84..ba8c6d91 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-animations/Document-getAnimations.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-animations/Document-getAnimations.tentative.html
@@ -298,12 +298,12 @@
 
     if (pseudo) {
       assert_equals(
-        actual.effect.target.element,
+        actual.effect.target,
         element,
         `Animation #${index + 1} has expected target`
       );
       assert_equals(
-        actual.effect.target.type,
+        actual.effect.pseudoElement,
         pseudo,
         `Animation #${index + 1} has expected pseudo type`
       );
@@ -313,6 +313,11 @@
         element,
         `Animation #${index + 1} has expected target`
       );
+      assert_equals(
+        actual.effect.pseudoElement,
+        null,
+        `Animation #${index + 1} has null pseudo type`
+      );
     }
   }
 }, 'CSS Animations targetting (pseudo-)elements should have correct order '
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/Element-getAnimations.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-animations/Element-getAnimations.tentative-expected.txt
index f8481b4..544ec617 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-animations/Element-getAnimations.tentative-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-animations/Element-getAnimations.tentative-expected.txt
@@ -17,9 +17,9 @@
 PASS getAnimations for CSS Animations that are canceled
 FAIL getAnimations for CSS Animations follows animation-name order assert_equals: animation order after prepending to list expected "anim1" but got "anim2"
 PASS { subtree: false } on a leaf element returns the element's animations and ignore pseudo-elements
-FAIL { subtree: true } on a leaf element returns the element's animations and its pseudo-elements' animations assert_equals: The animation targeting the ::before pseudo-element should be returned second expected (string) "::before" but got (undefined) undefined
+FAIL { subtree: true } on a leaf element returns the element's animations and its pseudo-elements' animations assert_equals: The animation targeting the parent element should be returned first expected (object) null but got (undefined) undefined
 PASS { subtree: false } on an element with a child returns only the element's animations
-FAIL { subtree: true } on an element with a child returns animations from the element, its pseudo-elements, its child and its child pseudo-elements assert_equals: The animation targeting the ::before pseudo-element should be returned second expected (string) "::before" but got (undefined) undefined
+FAIL { subtree: true } on an element with a child returns animations from the element, its pseudo-elements, its child and its child pseudo-elements assert_equals: The animation targeting the parent element should be returned first expected (object) null but got (undefined) undefined
 PASS { subtree: true } on an element with many descendants returns animations from all the descendants
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/Element-getAnimations.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-animations/Element-getAnimations.tentative.html
index 5690a7d..5419e11e 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-animations/Element-getAnimations.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-animations/Element-getAnimations.tentative.html
@@ -325,10 +325,13 @@
   assert_equals(animations[0].effect.target, target,
                 'The animation targeting the parent element ' +
                 'should be returned first');
-  assert_equals(animations[1].effect.target.type, '::before',
+  assert_equals(animations[0].effect.pseudoElement, null,
+                'The animation targeting the parent element ' +
+                'should be returned first')
+  assert_equals(animations[1].effect.pseudoElement, '::before',
                 'The animation targeting the ::before pseudo-element ' +
                 'should be returned second');
-  assert_equals(animations[2].effect.target.type, '::after',
+  assert_equals(animations[2].effect.pseudoElement, '::after',
                 'The animation targeting the ::after pesudo-element ' +
                 'should be returned last');
 }, '{ subtree: true } on a leaf element returns the element\'s animations'
@@ -366,34 +369,37 @@
 
   const animations = parent.getAnimations({ subtree: true });
   assert_equals(animations.length, 6,
-                'Should find all elements, pesudo-elements that parent has');
+                'Should find all elements, pseudo-elements that parent has');
 
   assert_equals(animations[0].effect.target, parent,
                 'The animation targeting the parent element ' +
                 'should be returned first');
-  assert_equals(animations[1].effect.target.type, '::before',
+  assert_equals(animations[0].effect.pseudoElement, null,
+                'The animation targeting the parent element ' +
+                'should be returned first');
+  assert_equals(animations[1].effect.pseudoElement, '::before',
                 'The animation targeting the ::before pseudo-element ' +
                 'should be returned second');
-  assert_equals(animations[1].effect.target.element, parent,
+  assert_equals(animations[1].effect.target, parent,
                 'This ::before element should be child of parent element');
-  assert_equals(animations[2].effect.target.type, '::after',
+  assert_equals(animations[2].effect.pseudoElement, '::after',
                 'The animation targeting the ::after pesudo-element ' +
                 'should be returned third');
-  assert_equals(animations[2].effect.target.element, parent,
+  assert_equals(animations[2].effect.target, parent,
                 'This ::after element should be child of parent element');
 
   assert_equals(animations[3].effect.target, child,
                 'The animation targeting the child element ' +
                 'should be returned fourth');
-  assert_equals(animations[4].effect.target.type, '::before',
+  assert_equals(animations[4].effect.pseudoElement, '::before',
                 'The animation targeting the ::before pseudo-element ' +
                 'should be returned fifth');
-  assert_equals(animations[4].effect.target.element, child,
+  assert_equals(animations[4].effect.target, child,
                 'This ::before element should be child of child element');
-  assert_equals(animations[5].effect.target.type, '::after',
+  assert_equals(animations[5].effect.pseudoElement, '::after',
                 'The animation targeting the ::after pesudo-element ' +
                 'should be returned last');
-  assert_equals(animations[5].effect.target.element, child,
+  assert_equals(animations[5].effect.target, child,
                 'This ::after element should be child of child element');
 }, '{ subtree: true } on an element with a child returns animations from the'
    + ' element, its pseudo-elements, its child and its child pseudo-elements');
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/widows-orphans-005.html b/third_party/blink/web_tests/external/wpt/css/css-break/widows-orphans-005.html
new file mode 100644
index 0000000..7138497
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/widows-orphans-005.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-break/#widows-orphans">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1022348">
+<meta name="assert" content="Here's a multicol container with four lines, and a widows requirement of 2. We cannot honor that requirement, because the third line alone is too tall to fit in a column.">
+<style>
+  .multicol {
+      position: relative;
+      columns: 3;
+      column-fill: auto;
+      column-gap: 10px;
+      width: 320px;
+      height: 200px;
+      orphans: 1;
+      widows: 2;
+      column-rule: 1px dotted;
+      line-height: 20px;
+  }
+  .ibk {
+      display: inline-block;
+      width: 70px;
+  }
+</style>
+<p>
+  There should be three columns below. In the first column there should be a
+  black rectangle and a yellow rectangle. In the second column there should be a
+  cyan rectangle. In the third column there should be a hotpink rectangle.</p>
+<div class="multicol">
+  <div class="ibk" style="height:50px; background:black;" data-offset-x="0"></div><br>
+  <div class="ibk" style="height:50px; background:yellow;" data-offset-x="0"></div><br>
+  <div class="ibk" style="height:285px;" data-offset-x="110">
+    <!-- The implementations differ here. Gecko lets the inline-block overflow
+         the column, while Blink slices the inline-block and puts what doesn't
+         fit in the second column into the third. Blink has a bug, but that's
+         not the bug we want to test here. -->
+    <div style="height:100px; background:cyan;"></div>
+  </div><br>
+  <div class="ibk" style="height:10px; background:hotpink;" data-offset-x="220"></div><br>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+<script>
+  checkLayout("[data-offset-x]");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-crash-chrome-013.html b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-crash-chrome-013.html
new file mode 100644
index 0000000..8f0daf4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-crash-chrome-013.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://crbug.com/1021676">
+<div id="tfoot" style="display: table-footer-group; transform: scale(2);">
+  <div id="oof" style="position: absolute;">text</div>
+</div>
+<script>
+test(() => {
+  document.body.offsetTop;
+
+  // Make the ICB the containing-block.
+  document.getElementById('tfoot').style.transform = '';
+  document.body.offsetTop;
+
+  document.getElementById('oof').innerText = '';
+  document.body.offsetTop;
+}, 'test passes if it does not crash');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/CSSPseudoElement-getAnimations.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-transitions/CSSPseudoElement-getAnimations.tentative-expected.txt
deleted file mode 100644
index 63466c3f..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/CSSPseudoElement-getAnimations.tentative-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL getAnimations sorts simultaneous transitions by name assert_class_string: Got pseudo-element target expected "[object CSSPseudoElement]" but got "[object Element]"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/CSSPseudoElement-getAnimations.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/CSSPseudoElement-getAnimations.tentative.html
deleted file mode 100644
index 5229881e..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/CSSPseudoElement-getAnimations.tentative.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<title>CSSPseudoElement.getAnimations() for CSS transitions</title>
-<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#animation-composite-order">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="support/helper.js"></script>
-<style>
-.init::before {
-  content: '';
-  height: 0px;
-  width: 0px;
-  opacity: 0;
-  transition: all 100s;
-}
-.change::before {
-  height: 100px;
-  width: 100px;
-  opacity: 1;
-}
-</style>
-<div id="log"></div>
-<script>
-'use strict';
-
-test(t => {
-  const div = addDiv(t, { class: 'init' });
-  getComputedStyle(div).width;
-  div.classList.add('change');
-
-  // Sanity checks
-  assert_equals(document.getAnimations().length, 3,
-                'Got expected number of animations on document');
-  const pseudoTarget = document.getAnimations()[0].effect.target;
-  assert_class_string(pseudoTarget, 'CSSPseudoElement',
-                      'Got pseudo-element target');
-
-  // Check animations returned from the pseudo element are in correct order
-  const anims = pseudoTarget.getAnimations();
-  assert_equals(anims.length, 3,
-                'Got expected number of animations on pseudo-element');
-  assert_equals(anims[0].transitionProperty, 'height');
-  assert_equals(anims[1].transitionProperty, 'opacity');
-  assert_equals(anims[2].transitionProperty, 'width');
-}, 'getAnimations sorts simultaneous transitions by name');
-
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/Document-getAnimations.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-transitions/Document-getAnimations.tentative-expected.txt
index d859a1c..477dcaf 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/Document-getAnimations.tentative-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/Document-getAnimations.tentative-expected.txt
@@ -1,7 +1,7 @@
 This is a testharness.js-based test.
 PASS getAnimations for non-animated content
 PASS getAnimations for CSS Transitions
-FAIL CSS Transitions targetting (pseudo-)elements should have correct order after sorting assert_equals: Transition #2 has expected target expected (object) Element node <div style="display: list-item; left: 100px; transition: ... but got (undefined) undefined
+FAIL CSS Transitions targetting (pseudo-)elements should have correct order after sorting assert_equals: Transition #1 has null pseudo type expected (object) null but got (undefined) undefined
 PASS Transitions are not returned after they have finished
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/Document-getAnimations.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/Document-getAnimations.tentative.html
index 98b91e0..cecddfaa 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/Document-getAnimations.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/Document-getAnimations.tentative.html
@@ -99,12 +99,12 @@
 
     if (pseudo) {
       assert_equals(
-        actual.effect.target.element,
+        actual.effect.target,
         element,
         `Transition #${index + 1} has expected target`
       );
       assert_equals(
-        actual.effect.target.type,
+        actual.effect.pseudoElement,
         pseudo,
         `Transition #${index + 1} has expected pseudo type`
       );
@@ -114,6 +114,11 @@
         element,
         `Transition #${index + 1} has expected target`
       );
+      assert_equals(
+        actual.effect.pseudoElement,
+        null,
+        `Transition #${index + 1} has null pseudo type`
+      );
     }
   }
 }, 'CSS Transitions targetting (pseudo-)elements should have correct order '
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/attribute-mapping-001-expected.txt b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/attribute-mapping-001-expected.txt
new file mode 100644
index 0000000..5c64785a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/attribute-mapping-001-expected.txt
@@ -0,0 +1,164 @@
+This is a testharness.js-based test.
+Found 160 tests; 158 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS dir on the math element is mapped to CSS direction
+PASS mathcolor on the math element is mapped to CSS color
+PASS mathbackground on the math element is mapped to CSS background-color
+PASS mathsize on the math element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the math element are not mapped to CSS
+PASS dir on the annotation element is mapped to CSS direction
+PASS mathcolor on the annotation element is mapped to CSS color
+PASS mathbackground on the annotation element is mapped to CSS background-color
+PASS mathsize on the annotation element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the annotation element are not mapped to CSS
+PASS dir on the annotation-xml element is mapped to CSS direction
+PASS mathcolor on the annotation-xml element is mapped to CSS color
+PASS mathbackground on the annotation-xml element is mapped to CSS background-color
+PASS mathsize on the annotation-xml element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the annotation-xml element are not mapped to CSS
+PASS dir on the maction element is mapped to CSS direction
+PASS mathcolor on the maction element is mapped to CSS color
+PASS mathbackground on the maction element is mapped to CSS background-color
+PASS mathsize on the maction element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the maction element are not mapped to CSS
+PASS dir on the menclose element is mapped to CSS direction
+PASS mathcolor on the menclose element is mapped to CSS color
+PASS mathbackground on the menclose element is mapped to CSS background-color
+PASS mathsize on the menclose element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the menclose element are not mapped to CSS
+PASS dir on the merror element is mapped to CSS direction
+FAIL mathcolor on the merror element is mapped to CSS color assert_equals: no attribute expected "rgb(255, 0, 0)" but got "rgb(0, 0, 255)"
+FAIL mathbackground on the merror element is mapped to CSS background-color assert_equals: no attribute expected "rgb(255, 255, 224)" but got "rgba(0, 0, 0, 0)"
+PASS mathsize on the merror element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the merror element are not mapped to CSS
+PASS dir on the mfrac element is mapped to CSS direction
+PASS mathcolor on the mfrac element is mapped to CSS color
+PASS mathbackground on the mfrac element is mapped to CSS background-color
+PASS mathsize on the mfrac element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the mfrac element are not mapped to CSS
+PASS dir on the mi element is mapped to CSS direction
+PASS mathcolor on the mi element is mapped to CSS color
+PASS mathbackground on the mi element is mapped to CSS background-color
+PASS mathsize on the mi element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the mi element are not mapped to CSS
+PASS dir on the mmultiscripts element is mapped to CSS direction
+PASS mathcolor on the mmultiscripts element is mapped to CSS color
+PASS mathbackground on the mmultiscripts element is mapped to CSS background-color
+PASS mathsize on the mmultiscripts element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the mmultiscripts element are not mapped to CSS
+PASS dir on the mn element is mapped to CSS direction
+PASS mathcolor on the mn element is mapped to CSS color
+PASS mathbackground on the mn element is mapped to CSS background-color
+PASS mathsize on the mn element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the mn element are not mapped to CSS
+PASS dir on the mo element is mapped to CSS direction
+PASS mathcolor on the mo element is mapped to CSS color
+PASS mathbackground on the mo element is mapped to CSS background-color
+PASS mathsize on the mo element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the mo element are not mapped to CSS
+PASS dir on the mover element is mapped to CSS direction
+PASS mathcolor on the mover element is mapped to CSS color
+PASS mathbackground on the mover element is mapped to CSS background-color
+PASS mathsize on the mover element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the mover element are not mapped to CSS
+PASS dir on the mpadded element is mapped to CSS direction
+PASS mathcolor on the mpadded element is mapped to CSS color
+PASS mathbackground on the mpadded element is mapped to CSS background-color
+PASS mathsize on the mpadded element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the mpadded element are not mapped to CSS
+PASS dir on the mphantom element is mapped to CSS direction
+PASS mathcolor on the mphantom element is mapped to CSS color
+PASS mathbackground on the mphantom element is mapped to CSS background-color
+PASS mathsize on the mphantom element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the mphantom element are not mapped to CSS
+PASS dir on the mprescripts element is mapped to CSS direction
+PASS mathcolor on the mprescripts element is mapped to CSS color
+PASS mathbackground on the mprescripts element is mapped to CSS background-color
+PASS mathsize on the mprescripts element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the mprescripts element are not mapped to CSS
+PASS dir on the mroot element is mapped to CSS direction
+PASS mathcolor on the mroot element is mapped to CSS color
+PASS mathbackground on the mroot element is mapped to CSS background-color
+PASS mathsize on the mroot element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the mroot element are not mapped to CSS
+PASS dir on the mrow element is mapped to CSS direction
+PASS mathcolor on the mrow element is mapped to CSS color
+PASS mathbackground on the mrow element is mapped to CSS background-color
+PASS mathsize on the mrow element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the mrow element are not mapped to CSS
+PASS dir on the ms element is mapped to CSS direction
+PASS mathcolor on the ms element is mapped to CSS color
+PASS mathbackground on the ms element is mapped to CSS background-color
+PASS mathsize on the ms element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the ms element are not mapped to CSS
+PASS dir on the mspace element is mapped to CSS direction
+PASS mathcolor on the mspace element is mapped to CSS color
+PASS mathbackground on the mspace element is mapped to CSS background-color
+PASS mathsize on the mspace element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the mspace element are not mapped to CSS
+PASS dir on the msqrt element is mapped to CSS direction
+PASS mathcolor on the msqrt element is mapped to CSS color
+PASS mathbackground on the msqrt element is mapped to CSS background-color
+PASS mathsize on the msqrt element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the msqrt element are not mapped to CSS
+PASS dir on the mstyle element is mapped to CSS direction
+PASS mathcolor on the mstyle element is mapped to CSS color
+PASS mathbackground on the mstyle element is mapped to CSS background-color
+PASS mathsize on the mstyle element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the mstyle element are not mapped to CSS
+PASS dir on the msub element is mapped to CSS direction
+PASS mathcolor on the msub element is mapped to CSS color
+PASS mathbackground on the msub element is mapped to CSS background-color
+PASS mathsize on the msub element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the msub element are not mapped to CSS
+PASS dir on the msubsup element is mapped to CSS direction
+PASS mathcolor on the msubsup element is mapped to CSS color
+PASS mathbackground on the msubsup element is mapped to CSS background-color
+PASS mathsize on the msubsup element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the msubsup element are not mapped to CSS
+PASS dir on the msup element is mapped to CSS direction
+PASS mathcolor on the msup element is mapped to CSS color
+PASS mathbackground on the msup element is mapped to CSS background-color
+PASS mathsize on the msup element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the msup element are not mapped to CSS
+PASS dir on the mtable element is mapped to CSS direction
+PASS mathcolor on the mtable element is mapped to CSS color
+PASS mathbackground on the mtable element is mapped to CSS background-color
+PASS mathsize on the mtable element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the mtable element are not mapped to CSS
+PASS dir on the mtd element is mapped to CSS direction
+PASS mathcolor on the mtd element is mapped to CSS color
+PASS mathbackground on the mtd element is mapped to CSS background-color
+PASS mathsize on the mtd element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the mtd element are not mapped to CSS
+PASS dir on the mtext element is mapped to CSS direction
+PASS mathcolor on the mtext element is mapped to CSS color
+PASS mathbackground on the mtext element is mapped to CSS background-color
+PASS mathsize on the mtext element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the mtext element are not mapped to CSS
+PASS dir on the mtr element is mapped to CSS direction
+PASS mathcolor on the mtr element is mapped to CSS color
+PASS mathbackground on the mtr element is mapped to CSS background-color
+PASS mathsize on the mtr element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the mtr element are not mapped to CSS
+PASS dir on the munder element is mapped to CSS direction
+PASS mathcolor on the munder element is mapped to CSS color
+PASS mathbackground on the munder element is mapped to CSS background-color
+PASS mathsize on the munder element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the munder element are not mapped to CSS
+PASS dir on the munderover element is mapped to CSS direction
+PASS mathcolor on the munderover element is mapped to CSS color
+PASS mathbackground on the munderover element is mapped to CSS background-color
+PASS mathsize on the munderover element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the munderover element are not mapped to CSS
+PASS dir on the none element is mapped to CSS direction
+PASS mathcolor on the none element is mapped to CSS color
+PASS mathbackground on the none element is mapped to CSS background-color
+PASS mathsize on the none element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the none element are not mapped to CSS
+PASS dir on the semantics element is mapped to CSS direction
+PASS mathcolor on the semantics element is mapped to CSS color
+PASS mathbackground on the semantics element is mapped to CSS background-color
+PASS mathsize on the semantics element is mapped to CSS font-size
+PASS deprecated MathML3 attributes on the semantics element are not mapped to CSS
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/attribute-mapping-001.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/attribute-mapping-001.html
index e7c6391e..3424e8c 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/attribute-mapping-001.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/attribute-mapping-001.html
@@ -34,6 +34,10 @@
               assert_equals(style.getPropertyValue("direction"), "rtl", "attribute specified");
               element.setAttribute("dir", "RtL");
               assert_equals(style.getPropertyValue("direction"), "rtl", "case insensitive");
+              element.setAttribute("dir", "auto");
+              assert_equals(style.getPropertyValue("direction"), "ltr", "auto");
+              element.setAttribute("dir", "foo");
+              assert_equals(style.getPropertyValue("direction"), "ltr", "random value");
           }, `dir on the ${tag} element is mapped to CSS direction`)
 
           test(function() {
diff --git a/third_party/blink/web_tests/external/wpt/payment-request/allowpaymentrequest/setting-allowpaymentrequest-timing.https.sub-expected.txt b/third_party/blink/web_tests/external/wpt/payment-request/allowpaymentrequest/setting-allowpaymentrequest-timing.https.sub-expected.txt
deleted file mode 100644
index 2103791b..0000000
--- a/third_party/blink/web_tests/external/wpt/payment-request/allowpaymentrequest/setting-allowpaymentrequest-timing.https.sub-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL PaymentRequest setting allowpaymentrequest after document creation, before response assert_equals: expected "Exception" but got "Success"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/sandboxed-iframe-navigator-serviceworker.https-expected.txt b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/sandboxed-iframe-navigator-serviceworker.https-expected.txt
deleted file mode 100644
index 01dcfe66..0000000
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/sandboxed-iframe-navigator-serviceworker.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-PASS Accessing navigator.serviceWorker in normal iframe should not throw.
-PASS Accessing navigator.serviceWorker in sandboxed iframe should throw.
-PASS Accessing navigator.serviceWorker in sandboxed iframe with allow-same-origin flag should not throw.
-FAIL Switching iframe sandbox attribute while loading the iframe assert_equals: expected "navigator.serviceWorker failed: SecurityError" but got "getRegistration() failed: InvalidStateError"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/sandboxed-iframe-navigator-serviceworker.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/sandboxed-iframe-navigator-serviceworker.https.html
index a58525f4..70be6ef9 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/sandboxed-iframe-navigator-serviceworker.https.html
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/sandboxed-iframe-navigator-serviceworker.https.html
@@ -8,10 +8,12 @@
 var lastCallbackId = 0;
 var callbacks = {};
 function postMessageAndWaitResult(frame) {
-  return new Promise(function(resolve) {
+  return new Promise(function(resolve, reject) {
     var id = ++lastCallbackId;
     callbacks[id] = resolve;
     frame.contentWindow.postMessage({id:id}, '*');
+    const timeout = 1000;
+    step_timeout(() => reject("no msg back after " + timeout + "ms"), timeout);
   });
 }
 
@@ -105,10 +107,13 @@
           // It's not clear whether navigation subsequently creates a new
           // Document, but I'm assuming it wouldn't.
           // https://html.spec.whatwg.org/multipage/embedded-content.html#attr-iframe-sandbox
-          assert_equals(
-              result,
-              'navigator.serviceWorker failed: SecurityError');
-        });
+          assert_true(
+              false,
+              'should NOT get message back from a sandboxed frame where scripts are not allowed to execute');
+        })
+      .catch(msg => {
+        assert_true(msg.startsWith('no msg back'), 'expecting error message "no msg back"');
+      });
   }, 'Switching iframe sandbox attribute while loading the iframe');
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/tools/manifest/commands.json b/third_party/blink/web_tests/external/wpt/tools/manifest/commands.json
index 074d248..769675e 100644
--- a/third_party/blink/web_tests/external/wpt/tools/manifest/commands.json
+++ b/third_party/blink/web_tests/external/wpt/tools/manifest/commands.json
@@ -1,5 +1,23 @@
-{"manifest":
- {"path": "update.py", "script": "run", "parser": "create_parser", "help": "Update the MANIFEST.json file",
-  "virtualenv": false},
- "manifest-download":
- {"path": "download.py", "script": "run", "parser": "create_parser", "help": "Download recent pregenerated MANIFEST.json file", "virtualenv": false}}
+{
+  "manifest": {
+    "path": "update.py",
+    "script": "run",
+    "parser": "create_parser",
+    "help": "Update the MANIFEST.json file",
+    "virtualenv": false
+  },
+  "manifest-download": {
+    "path": "download.py",
+    "script": "run",
+    "parser": "create_parser",
+    "help": "Download recent pregenerated MANIFEST.json file",
+    "virtualenv": false
+  },
+  "test-paths": {
+    "path": "testpaths.py",
+    "script": "run",
+    "parser": "create_parser",
+    "help": "Print test paths given a set of test ids",
+    "virtualenv": false
+  }
+}
diff --git a/third_party/blink/web_tests/external/wpt/tools/manifest/item.py b/third_party/blink/web_tests/external/wpt/tools/manifest/item.py
index a387091..217f6cac2 100644
--- a/third_party/blink/web_tests/external/wpt/tools/manifest/item.py
+++ b/third_party/blink/web_tests/external/wpt/tools/manifest/item.py
@@ -51,7 +51,7 @@
 
     @abstractproperty
     def id(self):
-        # type: () -> Hashable
+        # type: () -> Text
         """The test's id (usually its url)"""
         pass
 
diff --git a/third_party/blink/web_tests/external/wpt/tools/manifest/manifest.py b/third_party/blink/web_tests/external/wpt/tools/manifest/manifest.py
index 8aace771..6fb591b 100644
--- a/third_party/blink/web_tests/external/wpt/tools/manifest/manifest.py
+++ b/third_party/blink/web_tests/external/wpt/tools/manifest/manifest.py
@@ -251,18 +251,18 @@
         self.url_base = url_base  # type: Text
 
     def __iter__(self):
-        # type: () -> Iterable[Tuple[str, Text, Set[ManifestItem]]]
+        # type: () -> Iterator[Tuple[str, Text, Set[ManifestItem]]]
         return self.itertypes()
 
     def itertypes(self, *types):
-        # type: (*str) -> Iterable[Tuple[str, Text, Set[ManifestItem]]]
+        # type: (*str) -> Iterator[Tuple[str, Text, Set[ManifestItem]]]
         for item_type in (types or sorted(self._data.keys())):
             for path in sorted(self._data[item_type]):
                 tests = self._data[item_type][path]
                 yield item_type, path, tests
 
     def iterpath(self, path):
-        # type: (Text) -> Iterable[ManifestItem]
+        # type: (Text) -> Iterator[ManifestItem]
         for type_tests in self._data.values():
             i = type_tests.get(path, set())
             assert i is not None
@@ -270,7 +270,7 @@
                 yield test
 
     def iterdir(self, dir_name):
-        # type: (Text) -> Iterable[ManifestItem]
+        # type: (Text) -> Iterator[ManifestItem]
         if not dir_name.endswith(os.path.sep):
             dir_name = dir_name + os.path.sep
         for type_tests in self._data.values():
diff --git a/third_party/blink/web_tests/external/wpt/tools/manifest/testpaths.py b/third_party/blink/web_tests/external/wpt/tools/manifest/testpaths.py
new file mode 100644
index 0000000..3c1f09d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/tools/manifest/testpaths.py
@@ -0,0 +1,92 @@
+import argparse
+import json
+import os
+from collections import defaultdict
+
+from six import iteritems
+
+from .manifest import load_and_update, Manifest
+from .log import get_logger
+
+MYPY = False
+if MYPY:
+    # MYPY is set to True when run under Mypy.
+    from typing import Any
+    from typing import Dict
+    from typing import Iterable
+    from typing import List
+    from typing import Text
+
+wpt_root = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
+
+logger = get_logger()
+
+
+def abs_path(path):
+    # type: (str) -> str
+    return os.path.abspath(os.path.expanduser(path))
+
+
+def create_parser():
+    # type: () -> argparse.ArgumentParser
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "-p", "--path", type=abs_path, help="Path to manifest file.")
+    parser.add_argument(
+        "--tests-root", type=abs_path, default=wpt_root, help="Path to root of tests.")
+    parser.add_argument(
+        "--no-update", dest="update", action="store_false", default=True,
+        help="Don't update manifest before continuing")
+    parser.add_argument(
+        "-r", "--rebuild", action="store_true", default=False,
+        help="Force a full rebuild of the manifest.")
+    parser.add_argument(
+        "--url-base", action="store", default="/",
+        help="Base url to use as the mount point for tests in this manifest.")
+    parser.add_argument(
+        "--cache-root", action="store", default=os.path.join(wpt_root, ".wptcache"),
+        help="Path in which to store any caches (default <tests_root>/.wptcache/)")
+    parser.add_argument(
+        "--json", action="store_true", default=False,
+        help="Output as JSON")
+    parser.add_argument(
+        "test_ids", action="store", nargs="+",
+        help="Test ids for which to get paths")
+    return parser
+
+
+def get_path_id_map(manifest_file, test_ids):
+    # type: (Manifest, Iterable[Text]) -> Dict[Text, List[Text]]
+    test_ids = set(test_ids)
+    path_id_map = defaultdict(list)  # type: Dict[Text, List[Text]]
+
+    for item_type, path, tests in manifest_file:
+        for test in tests:
+            if test.id in test_ids:
+                path_id_map[path].append(test.id)
+    return path_id_map
+
+
+def run(**kwargs):
+    # type: (**Any) -> None
+    tests_root = kwargs["tests_root"]
+    assert tests_root is not None
+    path = kwargs["path"]
+    if path is None:
+        path = os.path.join(kwargs["tests_root"], "MANIFEST.json")
+
+    manifest_file = load_and_update(tests_root,
+                                    path,
+                                    kwargs["url_base"],
+                                    update=kwargs["update"],
+                                    rebuild=kwargs["rebuild"],
+                                    cache_root=kwargs["cache_root"])
+
+    path_id_map = get_path_id_map(manifest_file, kwargs["test_ids"])
+    if kwargs["json"]:
+        print(json.dumps(path_id_map))
+    else:
+        for path, test_ids in sorted(iteritems(path_id_map)):
+            print(path)
+            for test_id in sorted(test_ids):
+                print("  " + test_id)
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt
index f12ce9d..b079e4e 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 563 tests; 544 PASS, 19 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 560 tests; 541 PASS, 19 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Setup
 PASS align-content (type: discrete) has testAccumulation function
 PASS align-content: "flex-end" onto "flex-start"
@@ -176,9 +176,6 @@
 PASS column-width (type: discrete) has testAccumulation function
 PASS column-width: "1px" onto "auto"
 PASS column-width: "auto" onto "1px"
-PASS content (type: discrete) has testAccumulation function
-PASS content: ""b"" onto ""a""
-PASS content: ""a"" onto ""b""
 PASS counter-increment (type: discrete) has testAccumulation function
 PASS counter-increment: "ident-2 2" onto "ident-1 1"
 PASS counter-increment: "ident-1 1" onto "ident-2 2"
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-expected.txt
index 5cd14a5..21a7dd2f 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 559 tests; 541 PASS, 18 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 556 tests; 538 PASS, 18 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Setup
 PASS align-content (type: discrete) has testAddition function
 PASS align-content: "flex-end" onto "flex-start"
@@ -176,9 +176,6 @@
 PASS column-width (type: discrete) has testAddition function
 PASS column-width: "1px" onto "auto"
 PASS column-width: "auto" onto "1px"
-PASS content (type: discrete) has testAddition function
-PASS content: ""b"" onto ""a""
-PASS content: ""a"" onto ""b""
 PASS counter-increment (type: discrete) has testAddition function
 PASS counter-increment: "ident-2 2" onto "ident-1 1"
 PASS counter-increment: "ident-1 1" onto "ident-2 2"
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-expected.txt
index 48cfbb4..93ad9fe 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 690 tests; 657 PASS, 33 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 686 tests; 653 PASS, 33 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Setup
 PASS align-content (type: discrete) has testInterpolation function
 PASS align-content uses discrete animation when animating between "flex-start" and "flex-end" with linear easing
@@ -212,10 +212,6 @@
 PASS column-width uses discrete animation when animating between "auto" and "1px" with linear easing
 PASS column-width uses discrete animation when animating between "auto" and "1px" with effect easing
 PASS column-width uses discrete animation when animating between "auto" and "1px" with keyframe easing
-PASS content (type: discrete) has testInterpolation function
-PASS content uses discrete animation when animating between ""a"" and ""b"" with linear easing
-PASS content uses discrete animation when animating between ""a"" and ""b"" with effect easing
-PASS content uses discrete animation when animating between ""a"" and ""b"" with keyframe easing
 PASS counter-increment (type: discrete) has testInterpolation function
 PASS counter-increment uses discrete animation when animating between "ident-1 1" and "ident-2 2" with linear easing
 PASS counter-increment uses discrete animation when animating between "ident-1 1" and "ident-2 2" with effect easing
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-list.js b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-list.js
index 11bbf1a..2f91ebc 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-list.js
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-list.js
@@ -410,15 +410,6 @@
       { type: 'discrete', options: [ [ 'auto', '1px' ] ] }
     ]
   },
-  'content': {
-    // https://drafts.csswg.org/css-content-3/#propdef-content
-    types: [
-      { type: 'discrete', options: [ [ '"a"', '"b"' ] ] }
-    ],
-    setup: t => {
-      return getPseudoElement(t, 'before');
-    }
-  },
   'counter-increment': {
     // https://drafts.csswg.org/css-lists-3/#propdef-counter-increment
     types: [
@@ -1432,13 +1423,11 @@
 };
 
 function testAnimationSamples(animation, idlName, testSamples) {
-  const type = animation.effect.target.type;
-  const target = animation.effect.target.constructor.name === 'CSSPseudoElement'
-                 ? animation.effect.target.element
-                 : animation.effect.target;
+  const pseudoType = animation.effect.pseudoElement;
+  const target = animation.effect.target;
   for (const testSample of testSamples) {
     animation.currentTime = testSample.time;
-    assert_equals(getComputedStyle(target, type)[idlName],
+    assert_equals(getComputedStyle(target, pseudoType)[idlName],
                   testSample.expected,
                   `The value should be ${testSample.expected}` +
                   ` at ${testSample.time}ms`);
@@ -1453,10 +1442,8 @@
 // don't specify an order for serializing computed values.
 // This test is for such the property.
 function testAnimationSamplesWithAnyOrder(animation, idlName, testSamples) {
-  const type = animation.effect.target.type;
-  const target = animation.effect.target.constructor.name === 'CSSPseudoElement'
-                 ? animation.effect.target.element
-                 : animation.effect.target;
+  const type = animation.effect.pseudoElement;
+  const target = animation.effect.target;
   for (const testSample of testSamples) {
     animation.currentTime = testSample.time;
 
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animatable/animate-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animatable/animate-expected.txt
index a694484..081ee31 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animatable/animate-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animatable/animate-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 138 tests; 136 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 140 tests; 137 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Element.animate() creates an Animation object
 PASS Element.animate() creates an Animation object in the relevant realm of the target element
 PASS Element.animate() creates an Animation object with a KeyframeEffect
@@ -134,9 +134,11 @@
 PASS Element.animate() correctly sets the Animation's timeline when triggered on an element in a different document
 PASS Element.animate() calls play on the Animation
 PASS Element.animate() does NOT trigger a style change event
-PASS CSSPseudoElement.animate() creates an Animation object
-FAIL CSSPseudoElement.animate() creates an Animation object for ::marker assert_true: expected true got false
-PASS CSSPseudoElement.animate() creates an Animation object targeting to the correct CSSPseudoElement object
-FAIL CSSPseudoElement.animate() creates an Animation object targeting to the correct CSSPseudoElement object for ::marker assert_true: expected true got false
+PASS animate() with pseudoElement parameter creates an Animation object
+PASS animate() with pseudoElement parameter  creates an Animation object for ::marker
+PASS animate() with pseudoElement parameter  creates an Animation object for ::first-line
+FAIL animate() with pseudoElement an Animation object targeting to the correct pseudo-element assert_equals: The returned Animation targets to the correct selector expected (string) "::before" but got (undefined) undefined
+FAIL animate() with pseudoElement an Animation object targeting to the correct pseudo-element for ::marker assert_equals: The returned Animation targets to the correct selector expected (string) "::marker" but got (undefined) undefined
+FAIL animate() with pseudoElement an Animation object targeting to the correct pseudo-element for ::first-line assert_equals: The returned Animation targets to the correct selector expected (string) "::first-line" but got (undefined) undefined
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animatable/animate.html b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animatable/animate.html
index fcf753b..2d09cdf 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animatable/animate.html
+++ b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animatable/animate.html
@@ -10,6 +10,11 @@
 <script src="../../resources/keyframe-tests.js"></script>
 <script src="../../resources/timing-utils.js"></script>
 <script src="../../resources/timing-tests.js"></script>
+<style>
+.pseudo::before {content: '';}
+.pseudo::after {content: '';}
+.pseudo::marker {content: '';}
+</style>
 <body>
 <div id="log"></div>
 <iframe width="10" height="10" id="iframe"></iframe>
@@ -235,34 +240,59 @@
   assert_false(gotTransition, 'A transition should NOT have been triggered');
 }, 'Element.animate() does NOT trigger a style change event');
 
-// Tests on CSSPseudoElement
+// Tests on pseudo-elements
 
 test(t => {
-  const pseudoTarget = getPseudoElement(t, 'before');
-  const anim = pseudoTarget.animate(null);
+  const div = createDiv(t);
+  div.classList.add('pseudo');
+  const anim = div.animate(null, {pseudoElement: '::before'});
   assert_class_string(anim, 'Animation', 'The returned object is an Animation');
-}, 'CSSPseudoElement.animate() creates an Animation object');
+}, 'animate() with pseudoElement parameter creates an Animation object');
 
 test(t => {
-  const pseudoTarget = getPseudoElement(t, 'marker');
-  const anim = pseudoTarget.animate(null);
+  const div = createDiv(t);
+  div.classList.add('pseudo');
+  div.style.display = 'list-item';
+  const anim = div.animate(null, {pseudoElement: '::marker'});
   assert_class_string(anim, 'Animation', 'The returned object is an Animation for ::marker');
-}, 'CSSPseudoElement.animate() creates an Animation object for ::marker');
+}, 'animate() with pseudoElement parameter  creates an Animation object for ::marker');
 
 test(t => {
-  const pseudoTarget = getPseudoElement(t, 'before');
-  const anim = pseudoTarget.animate(null);
-  assert_equals(anim.effect.target, pseudoTarget,
-                'The returned Animation targets to the correct object');
-}, 'CSSPseudoElement.animate() creates an Animation object targeting ' +
-   'to the correct CSSPseudoElement object');
+  const div = createDiv(t);
+  div.classList.add('pseudo');
+  div.textContent = 'foo';
+  const anim = div.animate(null, {pseudoElement: '::first-line'});
+  assert_class_string(anim, 'Animation', 'The returned object is an Animation for ::first-line');
+}, 'animate() with pseudoElement parameter  creates an Animation object for ::first-line');
 
 test(t => {
-  const pseudoTarget = getPseudoElement(t, 'marker');
-  const anim = pseudoTarget.animate(null);
-  assert_equals(anim.effect.target, pseudoTarget,
-                'The returned Animation targets to the correct object for ::marker');
-}, 'CSSPseudoElement.animate() creates an Animation object targeting ' +
-   'to the correct CSSPseudoElement object for ::marker');
+  const div = createDiv(t);
+  div.classList.add('pseudo');
+  const anim = div.animate(null, {pseudoElement: '::before'});
+  assert_equals(anim.effect.pseudoElement, '::before',
+                'The returned Animation targets to the correct selector');
+}, 'animate() with pseudoElement an Animation object targeting ' +
+   'to the correct pseudo-element');
+
+test(t => {
+  const div = createDiv(t);
+  div.classList.add('pseudo');
+  div.style.display = 'list-item';
+  const anim = div.animate(null, {pseudoElement: '::marker'});
+  assert_equals(anim.effect.pseudoElement, '::marker',
+                'The returned Animation targets to the correct selector');
+}, 'animate() with pseudoElement an Animation object targeting ' +
+   'to the correct pseudo-element for ::marker');
+
+test(t => {
+  const div = createDiv(t);
+  div.classList.add('pseudo');
+  div.textContent = 'foo';
+  const anim = div.animate(null, {pseudoElement: '::first-line'});
+  assert_equals(anim.effect.pseudoElement, '::first-line',
+                'The returned Animation targets to the correct selector');
+}, 'animate() with pseudoElement an Animation object targeting ' +
+   'to the correct pseudo-element for ::first-line');
+
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/commitStyles-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/commitStyles-expected.txt
index 4daa0bc..1281fd0 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/commitStyles-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/commitStyles-expected.txt
@@ -28,6 +28,8 @@
 FAIL Throws if the target effect is disconnected assert_throws: function "() => {
     animation.commitStyles();
   }" threw object "TypeError: animation.commitStyles is not a function" that is not a DOMException InvalidStateError: property "code" is equal to undefined, expected 11
-FAIL Checks the pseudo element condition before the not rendered condition Cannot read property 'remove' of undefined
+FAIL Checks the pseudo element condition before the not rendered condition assert_throws: function "() => {
+    animation.commitStyles();
+  }" threw object "TypeError: animation.commitStyles is not a function" that is not a DOMException NoModificationAllowedError: property "code" is equal to undefined, expected 7
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/commitStyles.html b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/commitStyles.html
index 9005db9e..d3a95e0 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/commitStyles.html
+++ b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/commitStyles.html
@@ -5,6 +5,11 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="../../testcommon.js"></script>
+<style>
+.pseudo::before {content: '';}
+.pseudo::after {content: '';}
+.pseudo::marker {content: '';}
+</style>
 <body>
 <div id="log"></div>
 <script>
@@ -263,10 +268,12 @@
 }, 'Does NOT trigger mutation observers when the change to style is redundant');
 
 test(t => {
-  const pseudo = getPseudoElement(t, 'before');
-  const animation = pseudo.animate(
+
+  const div = createDiv(t);
+  div.classList.add('pseudo');
+  const animation = div.animate(
     { opacity: 0 },
-    { duration: 1, fill: 'forwards' }
+    { duration: 1, fill: 'forwards', pseudoElement: '::before' }
   );
 
   assert_throws('NoModificationAllowedError', () => {
@@ -372,13 +379,14 @@
 }, 'Throws if the target effect is disconnected');
 
 test(t => {
-  const pseudo = getPseudoElement(t, 'before');
-  const animation = pseudo.animate(
+  const div = createDiv(t);
+  div.classList.add('pseudo');
+  const animation = div.animate(
     { opacity: 0 },
-    { duration: 1, fill: 'forwards' }
+    { duration: 1, fill: 'forwards', pseudoElement: '::before' }
   );
 
-  pseudo.element.remove();
+  div.remove();
 
   assert_throws('NoModificationAllowedError', () => {
     animation.commitStyles();
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/testcommon.js b/third_party/blink/web_tests/external/wpt/web-animations/testcommon.js
index 82b7053..f89cbbaf 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/testcommon.js
+++ b/third_party/blink/web_tests/external/wpt/web-animations/testcommon.js
@@ -87,23 +87,6 @@
   });
 }
 
-// Create a pseudo element
-function getPseudoElement(test, type) {
-  createStyle(test, { '@keyframes anim': '',
-                      [`.pseudo::${type}`]: 'animation: anim 10s; ' +
-                                            'content: \'\';'  });
-  const div = createDiv(test);
-  if (type == 'marker') {
-    div.style.display = 'list-item';
-  }
-  div.classList.add('pseudo');
-  const anims = document.getAnimations();
-  assert_true(anims.length >= 1);
-  const anim = anims[anims.length - 1];
-  anim.cancel();
-  return anim.effect.target;
-}
-
 // Cubic bezier with control points (0, 0), (x1, y1), (x2, y2), and (1, 1).
 function cubicBezier(x1, y1, x2, y2) {
   const xForT = t => {
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https.html
index 32f2eb9..a2b2827 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https.html
@@ -393,8 +393,8 @@
 
   pc2.oniceconnectionstatechange = t.unreached_func();
   pc2.close();
-  await Promise.resolve();
   assert_true(pc2.iceConnectionState === 'closed');
+  await new Promise(r => t.step_timeout(r, 100));
 }, 'Closing a PeerConnection should not fire iceconnectionstatechange event');
 
 </script>
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-mouse-operations-expected.txt b/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-mouse-operations-expected.txt
index 24e15349..2571542 100644
--- a/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-mouse-operations-expected.txt
+++ b/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-mouse-operations-expected.txt
@@ -39,7 +39,7 @@
 PASS checkYearListViewScrollOffset() < 0 is true
 Check that clicking an year list cell opens it.
 PASS checkYearListViewScrollOffset() > 0 is true
-PASS highlightedMonthButton() is "1999-10"
+PASS highlightedMonthButton() is "1999-01"
 Check that clicking the month popup sets the month.
 PASS popupWindow.global.picker.monthPopupView.isVisible is false
 PASS currentMonth() is "1999-06"
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-mouse-operations.html b/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-mouse-operations.html
index 83e25498..54b21a8 100644
--- a/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-mouse-operations.html
+++ b/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-mouse-operations.html
@@ -83,7 +83,7 @@
     eventSender.mouseScrollBy(0, 10);
     shouldBeTrue('checkYearListViewScrollOffset() > 0');
     clickYearListCell(1999);
-    shouldBeEqualToString('highlightedMonthButton()', '1999-10');
+    shouldBeEqualToString('highlightedMonthButton()', '1999-01');
 
     debug('Check that clicking the month popup sets the month.');
 
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-mouse-operations-expected.txt b/third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-mouse-operations-expected.txt
index 74295eb..34c84d4b 100644
--- a/third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-mouse-operations-expected.txt
+++ b/third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-mouse-operations-expected.txt
@@ -55,7 +55,7 @@
 PASS checkYearListViewScrollOffset() < 0 is true
 Check that clicking an year list cell opens it.
 PASS checkYearListViewScrollOffset() > 0 is true
-PASS highlightedMonthButton() is "1999-10"
+PASS highlightedMonthButton() is "1999-01"
 Check that clicking the month popup sets the month.
 PASS popupWindow.global.picker.monthPopupView.isVisible is false
 PASS currentMonth() is "1999-06"
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-mouse-operations.html b/third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-mouse-operations.html
index f482a33..410b117 100644
--- a/third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-mouse-operations.html
+++ b/third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-mouse-operations.html
@@ -107,7 +107,7 @@
     eventSender.mouseScrollBy(0, 10);
     shouldBeTrue('checkYearListViewScrollOffset() > 0');
     clickYearListCell(1999);
-    shouldBeEqualToString('highlightedMonthButton()', '1999-10');
+    shouldBeEqualToString('highlightedMonthButton()', '1999-01');
 
     debug('Check that clicking the month popup sets the month.');
 
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/week-picker-mouse-operations-expected.txt b/third_party/blink/web_tests/fast/forms/calendar-picker/week-picker-mouse-operations-expected.txt
index 5b6d0e7a..e4aa4b39 100644
--- a/third_party/blink/web_tests/fast/forms/calendar-picker/week-picker-mouse-operations-expected.txt
+++ b/third_party/blink/web_tests/fast/forms/calendar-picker/week-picker-mouse-operations-expected.txt
@@ -60,7 +60,7 @@
 PASS checkYearListViewScrollOffset() < 0 is true
 Check that clicking an year list cell opens it.
 PASS checkYearListViewScrollOffset() > 0 is true
-PASS highlightedMonthButton() is "1999-10"
+PASS highlightedMonthButton() is "1999-01"
 Check that clicking the month popup sets the month.
 PASS popupWindow.global.picker.monthPopupView.isVisible is false
 PASS currentMonth() is "1999-06"
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/week-picker-mouse-operations.html b/third_party/blink/web_tests/fast/forms/calendar-picker/week-picker-mouse-operations.html
index 4328b1d..464b133 100644
--- a/third_party/blink/web_tests/fast/forms/calendar-picker/week-picker-mouse-operations.html
+++ b/third_party/blink/web_tests/fast/forms/calendar-picker/week-picker-mouse-operations.html
@@ -115,7 +115,7 @@
     eventSender.mouseScrollBy(0, 10);
     shouldBeTrue('checkYearListViewScrollOffset() > 0');
     clickYearListCell(1999);
-    shouldBeEqualToString('highlightedMonthButton()', '1999-10');
+    shouldBeEqualToString('highlightedMonthButton()', '1999-01');
 
     debug('Check that clicking the month popup sets the month.');
 
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/fetch/metadata/sec-fetch-dest/redirect/redirect-https-downgrade.tentative.sub-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/fetch/metadata/sec-fetch-dest/redirect/redirect-https-downgrade.tentative.sub-expected.txt
deleted file mode 100644
index 42e1de3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/fetch/metadata/sec-fetch-dest/redirect/redirect-https-downgrade.tentative.sub-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = done() was called without first defining any tests
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/graphics-aam/graphics-document_on_html_element-manual-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/graphics-aam/graphics-document_on_html_element-manual-expected.txt
deleted file mode 100644
index 42e1de3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/graphics-aam/graphics-document_on_html_element-manual-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = done() was called without first defining any tests
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/graphics-aam/graphics-object_on_svg_element-manual-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/graphics-aam/graphics-object_on_svg_element-manual-expected.txt
deleted file mode 100644
index 42e1de3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/graphics-aam/graphics-object_on_svg_element-manual-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = done() was called without first defining any tests
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/graphics-aam/graphics-symbol_on_html_element-manual-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/graphics-aam/graphics-symbol_on_html_element-manual-expected.txt
deleted file mode 100644
index 42e1de3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/graphics-aam/graphics-symbol_on_html_element-manual-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = done() was called without first defining any tests
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/graphics-aam/graphics-symbol_on_svg_element-manual-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/graphics-aam/graphics-symbol_on_svg_element-manual-expected.txt
deleted file mode 100644
index 42e1de3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/graphics-aam/graphics-symbol_on_svg_element-manual-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = done() was called without first defining any tests
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window-expected.txt
deleted file mode 100644
index c960d0d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window-expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-This is a testharness.js-based test.
-PASS page-created Error (worker)
-PASS page-created Error (cross-site iframe)
-PASS page-created DOMException (worker)
-PASS page-created DOMException (cross-site iframe)
-PASS JS-engine-created TypeError (worker)
-PASS JS-engine-created TypeError (cross-site iframe)
-PASS web API-created TypeError (worker)
-PASS web API-created TypeError (cross-site iframe)
-FAIL web API-created DOMException (worker) assert_equals: expected (string) "Error: Failed to execute 'createElement' on 'Document': The tag name provided ('') is not a valid name.\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:33:14\n    at Test.<anonymous> (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:41:19)\n    at Test.step (http://web-platform.test:8001/resources/testharness.js:1917:25)\n    at async_test (http://web-platform.test:8001/resources/testharness.js:576:22)\n    at stackTests (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:40:3)\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:31:1" but got (undefined) undefined
-FAIL web API-created DOMException (cross-site iframe) assert_equals: expected (string) "Error: Failed to execute 'createElement' on 'Document': The tag name provided ('') is not a valid name.\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:33:14\n    at Test.<anonymous> (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:60:19)\n    at Test.step (http://web-platform.test:8001/resources/testharness.js:1917:25)\n    at async_test (http://web-platform.test:8001/resources/testharness.js:576:22)\n    at stackTests (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:57:3)\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:31:1" but got (undefined) undefined
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/infrastructure/expected-fail/uncaught-exception-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/infrastructure/expected-fail/uncaught-exception-expected.txt
deleted file mode 100644
index 42e1de3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/infrastructure/expected-fail/uncaught-exception-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = done() was called without first defining any tests
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/infrastructure/expected-fail/unhandled-rejection-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/infrastructure/expected-fail/unhandled-rejection-expected.txt
deleted file mode 100644
index 42e1de3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/infrastructure/expected-fail/unhandled-rejection-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = done() was called without first defining any tests
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/payment-handler/change-shipping-option-manual.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/payment-handler/change-shipping-option-manual.https-expected.txt
deleted file mode 100644
index 42e1de3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/payment-handler/change-shipping-option-manual.https-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = done() was called without first defining any tests
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/payment-request/payment-is-showing.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/payment-request/payment-is-showing.https-expected.txt
deleted file mode 100644
index 42e1de3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/payment-request/payment-is-showing.https-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = done() was called without first defining any tests
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/payment-request/payment-request-canmakepayment-method-protection.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/payment-request/payment-request-canmakepayment-method-protection.https-expected.txt
deleted file mode 100644
index 42e1de3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/payment-request/payment-request-canmakepayment-method-protection.https-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = done() was called without first defining any tests
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/speech-api/historical-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/speech-api/historical-expected.txt
deleted file mode 100644
index 07d8219..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/speech-api/historical-expected.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-This is a testharness.js-based test.
-PASS SpeechRecognitionError interface should not exist
-FAIL webkitSpeechGrammar interface should not exist assert_false: expected false got true
-FAIL webkitSpeechGrammarList interface should not exist assert_false: expected false got true
-FAIL webkitSpeechRecognition interface should not exist assert_false: expected false got true
-FAIL webkitSpeechRecognitionError interface should not exist assert_false: expected false got true
-FAIL webkitSpeechRecognitionEvent interface should not exist assert_false: expected false got true
-PASS SpeechRecognition's serviceURI attribute should not exist
-PASS SpeechRecognitionEvent's interpretation attribute should not exist
-PASS SpeechRecognitionEvent's emma attribute should not exist
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/calendar-picker/date-picker-highlighted-month-after-year-change.html b/third_party/blink/web_tests/virtual/controls-refresh/calendar-picker/date-picker-highlighted-month-after-year-change.html
new file mode 100644
index 0000000..07a9db2
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/controls-refresh/calendar-picker/date-picker-highlighted-month-after-year-change.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<script>
+testRunner.waitUntilDone();
+</script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../fast/forms/resources/common.js"></script>
+<script src="../../../fast/forms/resources/picker-common.js"></script>
+<script src="../../../fast/forms/calendar-picker/resources/calendar-picker-common.js"></script>
+<input type=date id=date value="2017-07-31">
+<script>
+
+let t = async_test('Test highlighted month in year view.');
+
+function test1() {
+  clickMonthPopupButton();
+  clickYearListCell(2018);
+  assert_equals(highlightedMonthButton(), "2018-01");
+}
+
+t.step(() => {
+  openPicker(document.getElementById('date'), t.step_func_done(test1));
+});
+</script>
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index e7483eaf..bb3dca5 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -11074,19 +11074,12 @@
     attribute @@toStringTag
     getter hitMatrix
     method constructor
-interface XRHitTestOptions
-    attribute @@toStringTag
-    getter offsetRay
-    getter space
-    method constructor
 interface XRHitTestResult
     attribute @@toStringTag
-    getter hitTestOptions
     method constructor
     method getPose
 interface XRHitTestSource
     attribute @@toStringTag
-    getter hitTestOptions
     method constructor
 interface XRInputSource
     attribute @@toStringTag
diff --git a/third_party/blink/web_tests/wpt_internal/webgpu/cts.html b/third_party/blink/web_tests/wpt_internal/webgpu/cts.html
index c67c0c8..9c78e72a 100644
--- a/third_party/blink/web_tests/wpt_internal/webgpu/cts.html
+++ b/third_party/blink/web_tests/wpt_internal/webgpu/cts.html
@@ -19,6 +19,17 @@
     width: 100%;
     height: 15em;
 }
+
+/* Test Name column */
+#results > tbody > tr > td:nth-child(2) {
+        word-break: break-word;
+}
+
+/* Message column */
+#results > tbody > tr > td:nth-child(3) {
+        white-space: pre-wrap;
+                word-break: break-word;
+}
 </style>
 
 <textarea id=results></textarea>
@@ -93,6 +104,8 @@
 
 <meta name=variant content='?q=cts:validation/createPipelineLayout:'>
 
+<meta name=variant content='?q=cts:validation/createRenderPipeline:'>
+
 <meta name=variant content='?q=cts:validation/createTexture:validation_of_sampleCount={"sampleCount":0}'>
 <meta name=variant content='?q=cts:validation/createTexture:validation_of_sampleCount={"sampleCount":1}'>
 <meta name=variant content='?q=cts:validation/createTexture:validation_of_sampleCount={"sampleCount":2}'>
@@ -215,7 +228,9 @@
 <meta name=variant content='?q=cts:validation/setBlendColor:'>
 <meta name=variant content='?q=cts:validation/setScissorRect:'>
 <meta name=variant content='?q=cts:validation/setStencilReference:'>
+<meta name=variant content='?q=cts:validation/setVertexBuffer:'>
 <meta name=variant content='?q=cts:validation/setViewport:'>
+<meta name=variant content='?q=cts:validation/vertex_state:'>
 
 <!-- Workers -->
 
@@ -288,6 +303,8 @@
 
 <meta name=variant content='?worker=1&q=cts:validation/createPipelineLayout:'>
 
+<meta name=variant content='?worker=1&q=cts:validation/createRenderPipeline:'>
+
 <meta name=variant content='?worker=1&q=cts:validation/createTexture:validation_of_sampleCount={"sampleCount":0}'>
 <meta name=variant content='?worker=1&q=cts:validation/createTexture:validation_of_sampleCount={"sampleCount":1}'>
 <meta name=variant content='?worker=1&q=cts:validation/createTexture:validation_of_sampleCount={"sampleCount":2}'>
@@ -410,4 +427,6 @@
 <meta name=variant content='?worker=1&q=cts:validation/setBlendColor:'>
 <meta name=variant content='?worker=1&q=cts:validation/setScissorRect:'>
 <meta name=variant content='?worker=1&q=cts:validation/setStencilReference:'>
+<meta name=variant content='?worker=1&q=cts:validation/setVertexBuffer:'>
 <meta name=variant content='?worker=1&q=cts:validation/setViewport:'>
+<meta name=variant content='?worker=1&q=cts:validation/vertex_state:'>
diff --git a/tools/android/roll/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy b/tools/android/roll/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
index 8ae5c28..e92c4e6 100644
--- a/tools/android/roll/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
+++ b/tools/android/roll/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
@@ -121,7 +121,12 @@
                     new File("${absoluteDepDir}/LICENSE").write(
                             new File("${normalisedRepoPath}/${dependency.licensePath}").text)
                 } else if (!dependency.licenseUrl?.trim()?.isEmpty()) {
-                    downloadFile(dependency.licenseUrl, new File("${absoluteDepDir}/LICENSE"))
+                    File destFile = new File("${absoluteDepDir}/LICENSE")
+                    downloadFile(dependency.id, dependency.licenseUrl, destFile)
+                    if (destFile.text.contains("<html")) {
+                        throw new RuntimeException("Found HTML in LICENSE file. Please add an "
+                                + "override to ChromiumDepGraph.groovy for ${dependency.name}.")
+                    }
                 }
             }
         }
@@ -478,9 +483,37 @@
         getLogger().warn(jsonDump(obj))
     }
 
-    static void downloadFile(String sourceUrl, File destinationFile) {
+    static HttpURLConnection connectAndFollowRedirects(String id, String sourceUrl) {
+        URL urlObj = new URL(sourceUrl)
+        HttpURLConnection connection
+        for (int i = 0; i < 10; ++i) {
+            // Several deps use this URL for their license, but it just points to license
+            // *template*. Generally the actual license can be found in the source code.
+            if (sourceUrl.contains("://opensource.org/licenses")) {
+                throw new RuntimeException("Found templated license URL for dependency "
+                    + id + ": " + sourceUrl
+                    + ". You will need to edit FALLBACK_PROPERTIES for this dep.")
+            }
+            connection = urlObj.openConnection()
+            switch (connection.getResponseCode()) {
+                case HttpURLConnection.HTTP_MOVED_PERM:
+                case HttpURLConnection.HTTP_MOVED_TEMP:
+                    String location = connection.getHeaderField("Location");
+                    urlObj = new URL(urlObj, location);
+                    continue
+                case HttpURLConnection.HTTP_OK:
+                    return connection
+                default:
+                    throw new RuntimeException(
+                        "Url had statusCode=" + connection.getResponseCode() + ": " + sourceUrl)
+            }
+        }
+        throw new RuntimeException("Url in redirect loop: " + sourceUrl)
+    }
+
+    static void downloadFile(String id, String sourceUrl, File destinationFile) {
         destinationFile.withOutputStream { out ->
-            out << new URL(sourceUrl).openStream()
+            out << connectAndFollowRedirects(id, sourceUrl).getInputStream()
         }
     }
 
diff --git a/tools/binary_size/libsupersize/caspian/BUILD.gn b/tools/binary_size/libsupersize/caspian/BUILD.gn
index e31449f8..f889004b 100644
--- a/tools/binary_size/libsupersize/caspian/BUILD.gn
+++ b/tools/binary_size/libsupersize/caspian/BUILD.gn
@@ -72,7 +72,7 @@
       "-s",
       "ALLOW_MEMORY_GROWTH=1",
       "-s",
-      "EXPORTED_FUNCTIONS=['_LoadSizeFile','_BuildTree','_Open','_malloc','_free']",
+      "EXPORTED_FUNCTIONS=['_LoadSizeFile','_LoadBeforeSizeFile','_BuildTree','_Open','_malloc','_free']",
       "-s",
       "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall','cwrap','UTF8ToString']",
     ]
diff --git a/tools/binary_size/libsupersize/caspian/caspian_web.cc b/tools/binary_size/libsupersize/caspian/caspian_web.cc
index fef68f5f..f7c570c 100644
--- a/tools/binary_size/libsupersize/caspian/caspian_web.cc
+++ b/tools/binary_size/libsupersize/caspian/caspian_web.cc
@@ -21,7 +21,9 @@
 
 namespace caspian {
 namespace {
-SizeInfo info;
+std::unique_ptr<SizeInfo> info;
+std::unique_ptr<SizeInfo> before_info;
+std::unique_ptr<DiffSizeInfo> diff_info;
 std::unique_ptr<TreeBuilder> builder;
 
 std::unique_ptr<Json::StreamWriter> writer;
@@ -38,7 +40,15 @@
 
 extern "C" {
 void LoadSizeFile(const char* compressed, size_t size) {
-  ParseSizeInfo(compressed, size, &info);
+  diff_info.reset(nullptr);
+  info = std::make_unique<SizeInfo>();
+  ParseSizeInfo(compressed, size, info.get());
+}
+
+void LoadBeforeSizeFile(const char* compressed, size_t size) {
+  diff_info.reset(nullptr);
+  before_info = std::make_unique<SizeInfo>();
+  ParseSizeInfo(compressed, size, before_info.get());
 }
 
 void BuildTree(bool group_by_component,
@@ -49,14 +59,22 @@
                int match_flag) {
   std::vector<std::function<bool(const Symbol&)>> filters;
 
+  const bool diff_mode = info && before_info;
+
   if (minimum_size_bytes > 0) {
-    filters.push_back([minimum_size_bytes](const Symbol& sym) -> bool {
-      return sym.pss() >= minimum_size_bytes;
-    });
+    if (!diff_mode) {
+      filters.push_back([minimum_size_bytes](const Symbol& sym) -> bool {
+        return sym.pss >= minimum_size_bytes;
+      });
+    } else {
+      filters.push_back([minimum_size_bytes](const Symbol& sym) -> bool {
+        return abs(sym.pss) >= minimum_size_bytes;
+      });
+    }
   }
 
-  // It's currently not useful to filter on more than one flag, so |match_flag|
-  // can be assumed to be a power of two.
+  // It's currently not useful to filter on more than one flag, so
+  // |match_flag| can be assumed to be a power of two.
   if (match_flag) {
     std::cout << "Filtering on flag matching " << match_flag << std::endl;
     filters.push_back([match_flag](const Symbol& sym) -> bool {
@@ -71,8 +89,7 @@
       include_sections_map[static_cast<uint8_t>(*c)] = true;
     }
     filters.push_back([&include_sections_map](const Symbol& sym) -> bool {
-      return include_sections_map[static_cast<uint8_t>(
-          info.ShortSectionName(sym.section_name))];
+      return include_sections_map[static_cast<uint8_t>(sym.sectionId)];
     });
   }
 
@@ -98,7 +115,18 @@
     }
   }
 
-  builder.reset(new TreeBuilder(&info, group_by_component, filters));
+  // BuildTree() is called every time a new filter is applied in the HTML
+  // viewer, but if we already have a DiffSizeInfo we can skip regenerating it
+  // and let the TreeBuilder filter the symbols we care about.
+  if (diff_mode && !diff_info) {
+    diff_info.reset(new DiffSizeInfo(before_info.get(), info.get()));
+  }
+
+  BaseSizeInfo* rendered_info = info.get();
+  if (diff_mode) {
+    rendered_info = diff_info.get();
+  }
+  builder.reset(new TreeBuilder(rendered_info, group_by_component, filters));
   builder->Build();
 }
 
diff --git a/tools/binary_size/libsupersize/caspian/cli.cc b/tools/binary_size/libsupersize/caspian/cli.cc
index b07f9cd..b9c9761 100644
--- a/tools/binary_size/libsupersize/caspian/cli.cc
+++ b/tools/binary_size/libsupersize/caspian/cli.cc
@@ -14,20 +14,30 @@
 #include "tools/binary_size/libsupersize/caspian/file_format.h"
 #include "tools/binary_size/libsupersize/caspian/model.h"
 
-int main(int argc, char* argv[]) {
-  if (argc < 2) {
-    std::cerr << "Usage: cli (path to .size file)" << std::endl;
-    exit(1);
-  }
-  std::ifstream ifs(argv[1], std::ifstream::in);
+void ParseSizeInfoFromFile(const char* filename, caspian::SizeInfo* info) {
+  std::ifstream ifs(filename, std::ifstream::in);
   if (!ifs.good()) {
-    std::cerr << "Unable to open file: " << argv[1] << std::endl;
+    std::cerr << "Unable to open file: " << filename << std::endl;
     exit(1);
   }
   std::string compressed((std::istreambuf_iterator<char>(ifs)),
                          std::istreambuf_iterator<char>());
+  caspian::ParseSizeInfo(compressed.data(), compressed.size(), info);
+}
+
+void Diff(const char* before_filename, const char* after_filename) {
+  caspian::SizeInfo before;
+  ParseSizeInfoFromFile(before_filename, &before);
+
+  caspian::SizeInfo after;
+  ParseSizeInfoFromFile(after_filename, &after);
+
+  caspian::DiffSizeInfo diff(&before, &after);
+}
+
+void Validate(const char* filename) {
   caspian::SizeInfo info;
-  caspian::ParseSizeInfo(compressed.data(), compressed.size(), &info);
+  ParseSizeInfoFromFile(filename, &info);
 
   size_t max_aliases = 0;
   for (auto& s : info.raw_symbols) {
@@ -44,5 +54,23 @@
     }
   }
   std::cout << "Largest number of aliases: " << max_aliases << std::endl;
+}
+
+int main(int argc, char* argv[]) {
+  if (argc < 2) {
+    std::cerr << "Must have exactly one of:" << std::endl;
+    std::cerr << "  validate, diff" << std::endl;
+    std::cerr << "Usage: " << std::endl;
+    std::cerr << "  cli validate <size file>" << std::endl;
+    std::cerr << "  cli diff <before_file> <after_file>" << std::endl;
+    exit(1);
+  }
+  if (std::string_view(argv[1]) == "diff") {
+    Diff(argv[2], argv[3]);
+  } else if (std::string_view(argv[1]) == "validate") {
+    Validate(argv[1]);
+  } else {
+    exit(1);
+  }
   return 0;
 }
diff --git a/tools/binary_size/libsupersize/caspian/file_format.cc b/tools/binary_size/libsupersize/caspian/file_format.cc
index d11a192..40e7669 100644
--- a/tools/binary_size/libsupersize/caspian/file_format.cc
+++ b/tools/binary_size/libsupersize/caspian/file_format.cc
@@ -15,6 +15,7 @@
 #include <iostream>
 #include <memory>
 #include <numeric>
+#include <set>
 #include <sstream>
 #include <string>
 #include <vector>
@@ -155,6 +156,56 @@
 
 namespace caspian {
 
+void CalculatePadding(std::vector<Symbol>* raw_symbols) {
+  std::set<const char*> seen_sections;
+  for (size_t i = 1; i < raw_symbols->size(); i++) {
+    const Symbol& prev_symbol = (*raw_symbols)[i - 1];
+    Symbol& symbol = (*raw_symbols)[i];
+
+    if (symbol.IsOverhead()) {
+      symbol.padding = symbol.size;
+    }
+    if (prev_symbol.section_name != symbol.section_name) {
+      if (seen_sections.count(symbol.section_name)) {
+        std::cerr << "Input symbols must be sorted by section, then address: "
+                  << prev_symbol << ", " << symbol << std::endl;
+        exit(1);
+      }
+      seen_sections.insert(symbol.section_name);
+      continue;
+    }
+
+    if (symbol.address <= 0 || prev_symbol.address <= 0 || !symbol.IsNative() ||
+        !prev_symbol.IsNative()) {
+      continue;
+    }
+
+    if (symbol.address == prev_symbol.address) {
+      if (symbol.aliases && symbol.aliases == prev_symbol.aliases) {
+        symbol.padding = prev_symbol.padding;
+        symbol.size = prev_symbol.size;
+        continue;
+      }
+      if (prev_symbol.SizeWithoutPadding() != 0) {
+        // Padding-only symbols happen for ** symbol gaps.
+        std::cerr << "Found duplicate symbols: " << prev_symbol << ", "
+                  << symbol << std::endl;
+        exit(1);
+      }
+    }
+
+    int32_t padding = symbol.address - prev_symbol.EndAddress();
+    symbol.padding = padding;
+    symbol.size += padding;
+    if (symbol.size < 0) {
+      std::cerr << "Symbol has negative size (likely not sorted properly):"
+                << symbol << std::endl;
+      std::cerr << "prev symbol: " << prev_symbol << std::endl;
+      exit(1);
+    }
+  }
+}
+
 void ParseSizeInfo(const char* gzipped,
                    unsigned long len,
                    ::caspian::SizeInfo* info) {
@@ -257,6 +308,8 @@
   // Construct raw symbols
   for (int section_idx = 0; section_idx < n_sections; section_idx++) {
     const char* cur_section_name = info->section_names[section_idx];
+    caspian::SectionId cur_section_id =
+        info->ShortSectionName(cur_section_name);
     const int cur_section_count = section_counts[section_idx];
     const std::vector<int64_t>& cur_addresses = addresses[section_idx];
     const std::vector<int32_t>& cur_sizes = sizes[section_idx];
@@ -295,9 +348,10 @@
           }
         }
       }
-      new_sym.section_name = cur_section_name;
+      new_sym.sectionId = cur_section_id;
       new_sym.address = cur_addresses[i];
       new_sym.size = cur_sizes[i];
+      new_sym.section_name = cur_section_name;
       new_sym.object_path = info->object_paths[cur_path_indices[i]];
       new_sym.source_path = info->source_paths[cur_path_indices[i]];
       if (has_components) {
@@ -320,6 +374,13 @@
     }
   }
 
+  CalculatePadding(&info->raw_symbols);
+
+  for (caspian::Symbol& sym : info->raw_symbols) {
+    size_t alias_count = sym.aliases ? sym.aliases->size() : 1;
+    sym.pss = static_cast<float>(sym.size) / alias_count;
+  }
+
   // If there are unparsed non-empty lines, something's gone wrong.
   CheckNoNonEmptyLinesRemain(rest);
 
diff --git a/tools/binary_size/libsupersize/caspian/model.cc b/tools/binary_size/libsupersize/caspian/model.cc
index 19f390c..fe654c7 100644
--- a/tools/binary_size/libsupersize/caspian/model.cc
+++ b/tools/binary_size/libsupersize/caspian/model.cc
@@ -5,15 +5,82 @@
 #include "tools/binary_size/libsupersize/caspian/model.h"
 
 #include <algorithm>
+#include <functional>
 #include <iostream>
+#include <list>
 #include <string>
+#include <unordered_map>
 
 #include "tools/binary_size/libsupersize/caspian/file_format.h"
 
+namespace {
+struct SymbolMatchIndex {
+  SymbolMatchIndex() {}
+  SymbolMatchIndex(caspian::SectionId id,
+                   std::string_view name,
+                   std::string_view path,
+                   int size_without_padding)
+      : nonempty(true),
+        id(id),
+        name(name),
+        path(path),
+        size_without_padding(size_without_padding) {
+    this->name = name;
+  }
+
+  operator bool() const { return nonempty; }
+
+  bool operator==(const SymbolMatchIndex& other) const {
+    return id == other.id && name == other.name && path == other.path &&
+           size_without_padding == other.size_without_padding;
+  }
+
+  bool nonempty = false;
+  caspian::SectionId id;
+  std::string_view name;
+  std::string_view path;
+  int size_without_padding;
+};
+}  // namespace
+
+namespace std {
+template <>
+struct hash<SymbolMatchIndex> {
+  static constexpr size_t kPrime1 = 105929;
+  static constexpr size_t kPrime2 = 8543;
+  size_t operator()(const SymbolMatchIndex& k) const {
+    return ((kPrime1 * static_cast<size_t>(k.id)) ^
+            hash<string_view>()(k.name) ^ hash<string_view>()(k.path) ^
+            (kPrime2 * k.size_without_padding));
+  }
+};
+}  // namespace std
+
 namespace caspian {
 
 Symbol::Symbol() = default;
 Symbol::Symbol(const Symbol& other) = default;
+Symbol& Symbol::operator=(const Symbol& other) = default;
+
+Symbol Symbol::DiffSymbolFrom(const Symbol* before_sym,
+                              const Symbol* after_sym) {
+  Symbol ret;
+  if (after_sym) {
+    ret = *after_sym;
+  } else if (before_sym) {
+    ret = *before_sym;
+  }
+
+  if (before_sym && after_sym) {
+    ret.size = after_sym->size - before_sym->size;
+    ret.pss = after_sym->pss - before_sym->pss;
+  } else if (before_sym) {
+    ret.size = -before_sym->size;
+    ret.pss = -before_sym->pss;
+  }
+
+  return ret;
+}
 
 TreeNode::TreeNode() = default;
 TreeNode::~TreeNode() {
@@ -23,6 +90,16 @@
   }
 }
 
+std::ostream& operator<<(std::ostream& os, const Symbol& sym) {
+  return os << "Symbol(full_name=" << sym.full_name
+            << ", section=" << static_cast<char>(sym.sectionId)
+            << ", address=" << sym.address << ", size=" << sym.size
+            << ", flags=" << sym.flags << ", padding=" << sym.padding << ")";
+}
+
+BaseSizeInfo::BaseSizeInfo() = default;
+BaseSizeInfo::~BaseSizeInfo() = default;
+
 SizeInfo::SizeInfo() = default;
 SizeInfo::~SizeInfo() = default;
 
@@ -61,10 +138,100 @@
   return ret;
 }
 
+namespace {
+// Copied from /base/stl_util.h
+template <class T, class Allocator, class Value>
+void Erase(std::vector<T, Allocator>& container, const Value& value) {
+  container.erase(std::remove(container.begin(), container.end(), value),
+                  container.end());
+}
+
+std::string_view GetIdPath(const Symbol& sym) {
+  return (sym.source_path && *sym.source_path) ? sym.source_path
+                                               : sym.object_path;
+}
+
+SymbolMatchIndex PerfectMatch(const Symbol& sym) {
+  return SymbolMatchIndex(sym.sectionId, sym.full_name, GetIdPath(sym),
+                          sym.pss);
+}
+
+SymbolMatchIndex PerfectMatchExceptSize(const Symbol& sym) {
+  return SymbolMatchIndex(sym.sectionId, sym.full_name, GetIdPath(sym), 0.0f);
+}
+
+void MatchSymbols(std::function<SymbolMatchIndex(const Symbol&)> key_func,
+                  std::vector<caspian::Symbol>* delta_symbols,
+                  std::vector<const caspian::Symbol*>* unmatched_before,
+                  std::vector<const caspian::Symbol*>* unmatched_after) {
+  // TODO(jaspercb): Accumulate added/dropped padding by section name.
+  std::unordered_map<SymbolMatchIndex,
+                     std::list<std::reference_wrapper<const caspian::Symbol*>>>
+      before_symbols_by_key;
+  for (const caspian::Symbol*& before_sym : *unmatched_before) {
+    SymbolMatchIndex key = key_func(*before_sym);
+    if (key) {
+      before_symbols_by_key[key].emplace_back(before_sym);
+    }
+  }
+
+  for (const caspian::Symbol*& after_sym : *unmatched_after) {
+    SymbolMatchIndex key = key_func(*after_sym);
+    if (key) {
+      const auto& found = before_symbols_by_key.find(key);
+      if (found != before_symbols_by_key.end() && found->second.size()) {
+        const caspian::Symbol*& before_sym = found->second.front().get();
+        found->second.pop_front();
+        delta_symbols->push_back(Symbol::DiffSymbolFrom(before_sym, after_sym));
+        before_sym = nullptr;
+        after_sym = nullptr;
+      }
+    }
+  }
+
+  Erase(*unmatched_before, nullptr);
+  Erase(*unmatched_after, nullptr);
+}
+}  // namespace
+
+DiffSizeInfo::DiffSizeInfo(SizeInfo* before, SizeInfo* after)
+    : before(before), after(after) {
+  // TODO(jaspercb): It's okay to do a first pass where we match on the raw
+  // name (from the .size file), but anything left over after that first step
+  // (hopefully <2k objects) needs to consider the derived full_name. Should
+  // do this lazily for efficiency - name derivation is costly.
+
+  std::vector<const caspian::Symbol*> unmatched_before;
+  for (const caspian::Symbol& sym : before->raw_symbols) {
+    unmatched_before.push_back(&sym);
+  }
+
+  std::vector<const caspian::Symbol*> unmatched_after;
+  for (const caspian::Symbol& sym : after->raw_symbols) {
+    unmatched_after.push_back(&sym);
+  }
+
+  // Attempt several rounds to use increasingly loose matching on unmatched
+  // symbols.  Any symbols still unmatched are tried in the next round.
+  for (const auto& key_function : {PerfectMatch, PerfectMatchExceptSize}) {
+    MatchSymbols(key_function, &raw_symbols, &unmatched_before,
+                 &unmatched_after);
+  }
+
+  // Add removals or deletions for any unmatched symbols.
+  for (const caspian::Symbol* after_sym : unmatched_after) {
+    raw_symbols.push_back(Symbol::DiffSymbolFrom(nullptr, after_sym));
+  }
+  for (const caspian::Symbol* before_sym : unmatched_before) {
+    raw_symbols.push_back(Symbol::DiffSymbolFrom(before_sym, nullptr));
+  }
+}
+
+DiffSizeInfo::~DiffSizeInfo() {}
+
 void TreeNode::WriteIntoJson(Json::Value* out, int depth) {
   (*out)["idPath"] = std::string(this->id_path);
   (*out)["shortNameIndex"] = this->short_name_index;
-  // TODO: Put correct information.
   std::string type;
   if (container_type != ContainerType::kSymbol) {
     type += static_cast<char>(container_type);
@@ -84,7 +251,7 @@
     // TODO: Support additional compare functions.
     auto compare_func = [](const TreeNode* const& l,
                            const TreeNode* const& r) -> bool {
-      return l->size > r->size;
+      return abs(l->size) > abs(r->size);
     };
     std::sort(this->children.begin(), this->children.end(), compare_func);
     for (unsigned int i = 0; i < this->children.size(); i++) {
@@ -124,9 +291,9 @@
   SectionId ret = SectionId::kNone;
   float max = 0.0f;
   for (auto& pair : child_stats) {
-    if (pair.second.size > max) {
+    if (abs(pair.second.size) > max) {
       ret = pair.first;
-      max = pair.second.size;
+      max = abs(pair.second.size);
     }
   }
   return ret;
diff --git a/tools/binary_size/libsupersize/caspian/model.h b/tools/binary_size/libsupersize/caspian/model.h
index f5dc71a9..a43b0b5e4 100644
--- a/tools/binary_size/libsupersize/caspian/model.h
+++ b/tools/binary_size/libsupersize/caspian/model.h
@@ -44,35 +44,65 @@
 struct Symbol {
   Symbol();
   Symbol(const Symbol& other);
+  Symbol& operator=(const Symbol& other);
+  static Symbol DiffSymbolFrom(const Symbol* before, const Symbol* after);
 
-  float pss() const {
-    int alias_count = aliases ? aliases->size() : 1;
-    return static_cast<float>(size) / alias_count;
+  bool IsOverhead() const { return full_name.substr(0, 10) == "Overhead: "; }
+
+  bool IsBss() const { return sectionId == SectionId::kBss; }
+
+  bool IsDex() const {
+    return sectionId == SectionId::kDex || sectionId == SectionId::kDexMethod;
   }
 
+  bool IsOther() const { return sectionId == SectionId::kOther; }
+
+  bool IsPak() const {
+    return sectionId == SectionId::kPakNontranslated ||
+           sectionId == SectionId::kPakTranslations;
+  }
+
+  bool IsNative() const {
+    return (sectionId == SectionId::kBss || sectionId == SectionId::kData ||
+            sectionId == SectionId::kDataRelRo ||
+            sectionId == SectionId::kText || sectionId == SectionId::kRoData);
+  }
+
+  int32_t SizeWithoutPadding() const { return size - padding; }
+
+  int32_t EndAddress() const { return address + SizeWithoutPadding(); }
+
   int32_t address = 0;
   int32_t size = 0;
   int32_t flags = 0;
   int32_t padding = 0;
+  float pss = 0.0f;
+  SectionId sectionId = SectionId::kNone;
+  std::string_view full_name;
   // Pointers into SizeInfo->raw_decompressed;
   const char* section_name = nullptr;
-  std::string_view full_name;
   const char* object_path = nullptr;
   const char* source_path = nullptr;
   const char* component = nullptr;
   std::vector<Symbol*>* aliases = nullptr;
 };
 
-struct SizeInfo {
+std::ostream& operator<<(std::ostream& os, const Symbol& sym);
+
+struct BaseSizeInfo {
+  BaseSizeInfo();
+  ~BaseSizeInfo();
+  std::vector<caspian::Symbol> raw_symbols;
+  Json::Value metadata;
+};
+
+struct SizeInfo : BaseSizeInfo {
   SizeInfo();
   ~SizeInfo();
   SizeInfo(const SizeInfo& other) = delete;
   SizeInfo& operator=(const SizeInfo& other) = delete;
   SectionId ShortSectionName(const char* section_name);
 
-  std::vector<caspian::Symbol> raw_symbols;
-  Json::Value metadata;
-
   // Entries in |raw_symbols| hold pointers to this data.
   std::vector<const char*> object_paths;
   std::vector<const char*> source_paths;
@@ -84,6 +114,16 @@
   std::deque<std::vector<Symbol*>> alias_groups;
 };
 
+struct DiffSizeInfo : BaseSizeInfo {
+  DiffSizeInfo(SizeInfo* before, SizeInfo* after);
+  ~DiffSizeInfo();
+  DiffSizeInfo(const DiffSizeInfo&) = delete;
+  DiffSizeInfo& operator=(const DiffSizeInfo&) = delete;
+
+  SizeInfo* before = nullptr;
+  SizeInfo* after = nullptr;
+};
+
 struct Stat {
   int32_t count = 0;
   float size = 0.0f;
diff --git a/tools/binary_size/libsupersize/caspian/tree_builder.cc b/tools/binary_size/libsupersize/caspian/tree_builder.cc
index a2613fb6..a7e7430a 100644
--- a/tools/binary_size/libsupersize/caspian/tree_builder.cc
+++ b/tools/binary_size/libsupersize/caspian/tree_builder.cc
@@ -41,7 +41,7 @@
 }  // namespace
 
 TreeBuilder::TreeBuilder(
-    SizeInfo* size_info,
+    BaseSizeInfo* size_info,
     bool group_by_component,
     std::vector<std::function<bool(const Symbol&)>> filters)
     : size_info_(size_info),
@@ -123,9 +123,8 @@
     TreeNode* symbol_node = new TreeNode();
     symbol_node->container_type = ContainerType::kSymbol;
     symbol_node->id_path = sym->full_name;
-    symbol_node->size = sym->pss();
-    symbol_node->node_stats = NodeStats(
-        size_info_->ShortSectionName(sym->section_name), 1, symbol_node->size);
+    symbol_node->size = sym->pss;
+    symbol_node->node_stats = NodeStats(sym->sectionId, 1, symbol_node->size);
     AttachToParent(symbol_node, file_node);
   }
 
diff --git a/tools/binary_size/libsupersize/caspian/tree_builder.h b/tools/binary_size/libsupersize/caspian/tree_builder.h
index a69fc12a..afd4270 100644
--- a/tools/binary_size/libsupersize/caspian/tree_builder.h
+++ b/tools/binary_size/libsupersize/caspian/tree_builder.h
@@ -17,7 +17,7 @@
 namespace caspian {
 class TreeBuilder {
  public:
-  TreeBuilder(SizeInfo* size_info,
+  TreeBuilder(BaseSizeInfo* size_info,
               bool group_by_component,
               std::vector<std::function<bool(const Symbol&)>> filters);
   ~TreeBuilder();
@@ -45,7 +45,7 @@
   // node.
   std::unordered_map<std::string_view, TreeNode*> _parents;
 
-  SizeInfo* size_info_ = nullptr;
+  BaseSizeInfo* size_info_ = nullptr;
   // Contained TreeNode hold lightweight string_views to fields in SizeInfo.
   // If grouping by component, this isn't possible: TreeNode id_paths are not
   // substrings of SizeInfo-owned strings. In that case, the strings are stored
diff --git a/tools/binary_size/libsupersize/start_server.py b/tools/binary_size/libsupersize/start_server.py
index 88ffefa9..82a66b7 100644
--- a/tools/binary_size/libsupersize/start_server.py
+++ b/tools/binary_size/libsupersize/start_server.py
@@ -16,6 +16,7 @@
   serve_from = None
   # Path to data file
   data_file_path = None
+  before_file_path = None
 
   #override
   def translate_path(self, path):
@@ -23,6 +24,8 @@
     relative_path = os.path.relpath(f, os.getcwd())
     if relative_path == 'data.ndjson':
       return SupersizeHTTPRequestHandler.data_file_path
+    if relative_path == 'before.size':
+      return SupersizeHTTPRequestHandler.before_file_path
     else:
       return os.path.join(SupersizeHTTPRequestHandler.serve_from, relative_path)
 
@@ -30,6 +33,13 @@
 def AddArguments(parser):
   parser.add_argument('report_file',
                       help='Path to a custom html_report data file to load.')
+  parser.add_argument(
+      '-b',
+      '--before_file',
+      type=str,
+      default='',
+      help=('Path to a "before" .size file to diff against. If present, '
+            'report_file should also be a .size file.'))
   parser.add_argument('-p', '--port', type=int, default=8000,
                       help='Port for the HTTP server')
   parser.add_argument('-a', '--address', default='localhost',
@@ -44,11 +54,13 @@
 
   SupersizeHTTPRequestHandler.serve_from = static_files
   SupersizeHTTPRequestHandler.data_file_path = args.report_file
+  SupersizeHTTPRequestHandler.before_file_path = args.before_file
   SupersizeHTTPRequestHandler.extensions_map['.wasm'] = 'application/wasm'
   httpd = BaseHTTPServer.HTTPServer(server_addr, SupersizeHTTPRequestHandler)
 
   sa = httpd.socket.getsockname()
+  before_suffix = '&before_url=before.size&wasm=1' if args.before_file else ''
   logging.warning(
-      'Server ready at http://%s:%d/viewer.html?load_url=data.ndjson',
-      sa[0], sa[1])
+      'Server ready at http://%s:%d/viewer.html?load_url=data.ndjson' +
+      before_suffix, sa[0], sa[1])
   httpd.serve_forever()
diff --git a/tools/binary_size/libsupersize/static/tree-worker-wasm.js b/tools/binary_size/libsupersize/static/tree-worker-wasm.js
index 8458b1b..f4cfff3 100644
--- a/tools/binary_size/libsupersize/static/tree-worker-wasm.js
+++ b/tools/binary_size/libsupersize/static/tree-worker-wasm.js
@@ -7,6 +7,13 @@
 importScripts('./shared.js');
 importScripts('./caspian_web.js');
 
+const LoadWasm = new Promise(function(resolve, reject) {
+  Module['onRuntimeInitialized'] = function() {
+    console.log('Loaded WebAssembly runtime');
+    resolve();
+  }
+});
+
 const _PATH_SEP = '/';
 const _NAMES_TO_FLAGS = Object.freeze({
   hot: _FLAGS.HOT,
@@ -127,84 +134,66 @@
   return dataHeap;
 }
 
-var _Open = null;
-function Open(name) {
-  if (_Open === null) {
+async function Open(name) {
+  return LoadWasm.then(() => {
     _Open = Module.cwrap('Open', 'number', ['string']);
-  }
-  let stringPtr = _Open(name);
-  // Something has gone wrong if we get back a string longer than 67MB.
-  let ret = JSON.parse(Module.UTF8ToString(stringPtr, 2 ** 26));
-  console.log(ret);
-  return ret;
+    const stringPtr = _Open(name);
+    // Something has gone wrong if we get back a string longer than 67MB.
+    const ret = JSON.parse(Module.UTF8ToString(stringPtr, 2 ** 26));
+    return ret;
+  });
 }
 
 // Placeholder input name until supplied via setInput()
-const fetcher = new DataFetcher('data.size');
+const fetcher = new DataFetcher('data.ndjson');
+let beforeFetcher = null;
 let sizeFileLoaded = false;
 
+async function loadSizeFile(isBefore, fetcher) {
+  const sizeBuffer = await fetcher.loadSizeBuffer();
+  const heapBuffer = mallocBuffer(sizeBuffer);
+  const LoadSizeFile = Module.cwrap(
+      isBefore ? 'LoadBeforeSizeFile' : 'LoadSizeFile', 'bool',
+      ['number', 'number']);
+  const start_time = Date.now();
+  LoadSizeFile(heapBuffer.byteOffset, sizeBuffer.byteLength);
+  console.log(
+      'Loaded size file in ' + (Date.now() - start_time) / 1000.0 + ' seconds');
+  Module._free(heapBuffer.byteOffset);
+}
+
 async function buildTree(
     groupBy, includeRegex, excludeRegex, includeSections, minSymbolSize,
     flagToFilter, methodCountMode, onProgress) {
-  if (!sizeFileLoaded) {
-    let sizeBuffer = await fetcher.loadSizeBuffer();
-    let heapBuffer = mallocBuffer(sizeBuffer);
-    console.log('Passing ' + sizeBuffer.byteLength + ' bytes to WebAssembly');
-    let LoadSizeFile =
-        Module.cwrap('LoadSizeFile', 'bool', ['number', 'number']);
-    let start_time = Date.now();
-    LoadSizeFile(heapBuffer.byteOffset, sizeBuffer.byteLength);
+  return await LoadWasm.then(async () => {
+    if (!sizeFileLoaded) {
+      const current = loadSizeFile(false, fetcher);
+      const before =
+          beforeFetcher !== null ? loadSizeFile(true, beforeFetcher) : null;
+      await current;
+      await before;
+      sizeFileLoaded = true;
+    }
+
+    const BuildTree = Module.cwrap(
+        'BuildTree', 'void',
+        ['bool', 'string', 'string', 'string', 'number', 'number']);
+    const start_time = Date.now();
+    const groupByComponent = groupBy === 'component';
+    BuildTree(
+        groupByComponent, includeRegex, excludeRegex, includeSections,
+        minSymbolSize, flagToFilter);
     console.log(
-        'Loaded size file in ' + (Date.now() - start_time) / 1000.0 +
+        'Constructed tree in ' + (Date.now() - start_time) / 1000.0 +
         ' seconds');
-    Module._free(heapBuffer.byteOffset);
 
-    sizeFileLoaded = true;
-  }
-
-  /**
-   * Creates data to post to the UI thread. Defaults will be used for the root
-   * and percent values if not specified.
-   * @param {{root?:TreeNode,percent?:number,error?:Error}} data Default data
-   * values to post.
-   */
-  function createProgressMessage(data = {}) {
-    let {percent} = data;
-    if (percent == null) {
-      if (meta == null) {
-        percent = 0;
-      } else {
-        percent = Math.max(builder.rootNode.size / meta.total, 0.1);
-      }
-    }
-
-    const message = {
-      root: builder.formatNode(data.root || builder.rootNode),
-      percent,
-      diffMode: meta && meta.diff_mode,
+    const root = await Open('');
+    return {
+      root: root,
+      percent: 1.0,
+      diffMode: beforeFetcher !== null,  // diff mode
     };
-    if (data.error) {
-      message.error = data.error.message;
-    }
-    return message;
-  }
-
-  let BuildTree = Module.cwrap(
-      'BuildTree', 'void',
-      ['bool', 'string', 'string', 'string', 'number', 'number']);
-  let start_time = Date.now();
-  const groupByComponent = groupBy === 'component';
-  BuildTree(
-      groupByComponent, includeRegex, excludeRegex, includeSections,
-      minSymbolSize, flagToFilter);
-  console.log('Constructed tree in ' +
-    (Date.now() - start_time)/1000.0 + ' seconds');
-
-  return {
-    root: Open(''),
-    percent: 1.0,
-    diffMode: 0, // diff mode
-  };
+  });
 }
 
 /**
@@ -215,15 +204,8 @@
 function parseOptions(options) {
   const params = new URLSearchParams(options);
 
-  const url = params.get('load_url');
   const groupBy = params.get('group_by') || 'source_path';
   const methodCountMode = params.has('method_count');
-  const flagToFilter = _NAMES_TO_FLAGS[params.get('flag_filter')] || 0;
-
-  let minSymbolSize = Number(params.get('min_size'));
-  if (Number.isNaN(minSymbolSize)) {
-    minSymbolSize = 0;
-  }
 
   const includeRegex = params.get('include');
   const excludeRegex = params.get('exclude');
@@ -238,6 +220,15 @@
     includeSections = Array.from(includeSectionsSet.values()).join('');
   }
 
+  const minSymbolSize = Number(params.get('min_size'));
+  if (Number.isNaN(minSymbolSize)) {
+    minSymbolSize = 0;
+  }
+
+  const flagToFilter = _NAMES_TO_FLAGS[params.get('flag_filter')] || 0;
+  const url = params.get('load_url');
+  const beforeUrl = params.get('before_url');
+
   return {
     groupBy,
     includeRegex,
@@ -246,6 +237,7 @@
     minSymbolSize,
     flagToFilter,
     url,
+    beforeUrl,
   };
 }
 
@@ -260,6 +252,7 @@
       minSymbolSize,
       flagToFilter,
       url,
+      beforeUrl,
     } = parseOptions(options);
     if (input === 'from-url://' && url) {
       // Display the data from the `load_url` query parameter
@@ -270,6 +263,10 @@
       fetcher.setInput(input);
     }
 
+    if (beforeUrl) {
+      beforeFetcher = new DataFetcher(beforeUrl);
+    }
+
     return buildTree(
         groupBy, includeRegex, excludeRegex, includeSections, minSymbolSize,
         flagToFilter, progress => {
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 040c482..c2eccf54 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -749,6 +749,14 @@
   </description>
 </action>
 
+<action name="Accel_Minimize_Top_Window_On_Back">
+  <owner>minch@chromium.org</owner>
+  <description>
+    Metric recorded when the user press back key in the keyboard to minimize the
+    top window at bottom page while in tablet mode.
+  </description>
+</action>
+
 <action name="Accel_Move_Active_Window_Between_Displays">
   <owner>warx@chromium.org</owner>
   <description>
@@ -15142,12 +15150,12 @@
 
 <action name="Options_AccessibilitySwitchAccess_Disable">
   <owner>dmazzoni@chromium.org</owner>
-  <description>Settings: Accessibility: Disable switch access</description>
+  <description>Settings: Accessibility: Disable Switch Access</description>
 </action>
 
 <action name="Options_AccessibilitySwitchAccess_Enable">
   <owner>dmazzoni@chromium.org</owner>
-  <description>Settings: Accessibility: Enable switch access</description>
+  <description>Settings: Accessibility: Enable Switch Access</description>
 </action>
 
 <action name="Options_AccessibilitySystemMenu_Disable">
@@ -21377,7 +21385,7 @@
   <owner>anastasi@google.com</owner>
   <owner>dtseng@chromium.org</owner>
   <description>
-    Ash system menu: Accessibility: Disable switch access
+    Ash system menu: Accessibility: Disable Switch Access
   </description>
 </action>
 
@@ -21385,7 +21393,7 @@
   <owner>anastasi@google.com</owner>
   <owner>dtseng@chromium.org</owner>
   <description>
-    Ash system menu: Accessibility: Enable switch access
+    Ash system menu: Accessibility: Enable Switch Access
   </description>
 </action>
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index f71ec25..8513098d 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -4858,9 +4858,12 @@
   <int value="-684843031" label="SafeBrowsingTriggeredPopupBlocker"/>
   <int value="-438850149" label="PermissionRequestManager"/>
   <int value="72716222" label="WebBluetooth"/>
+  <int value="88001126" label="MixedContentSettingsTabHelper"/>
   <int value="671363579" label="banners::AppBannerManager"/>
   <int value="990438262" label="MediaWebContentsObserver::OnMediaPlaying"/>
   <int value="1683256665" label="NativeFileSystem"/>
+  <int value="1774599341" label="WebAuthenticationAPI"/>
+  <int value="1985234179" label="PermissionServiceContext"/>
   <int value="2013040510" label="RenderFrameDevToolsAgentHost"/>
   <int value="2024383223"
       label="ChromePasswordManagerClient::BindCredentialManager"/>
@@ -38223,6 +38226,7 @@
   <int value="1258043820" label="AudioFocusEnforcement:enabled"/>
   <int value="1258747457"
       label="SyncPseudoUSSHistoryDeleteDirectives:disabled"/>
+  <int value="1259798038" label="AllowAmbientEQ:disabled"/>
   <int value="1260186484" label="spurious-power-button-screen-accel"/>
   <int value="1261379424" label="DecodeLossyWebPImagesToYUV:disabled"/>
   <int value="1261713150" label="ChromeHomeOptOutSnackbar:disabled"/>
@@ -38531,6 +38535,7 @@
   <int value="1658644418" label="disable-app-list-voice-search"/>
   <int value="1659082220" label="EnableManualSaving:disabled"/>
   <int value="1659372520" label="WebXrRenderPath:disabled"/>
+  <int value="1660491118" label="AllowAmbientEQ:enabled"/>
   <int value="1661925474" label="silent-debugger-extension-api"/>
   <int value="1664401033" label="ColorCorrectRendering:enabled"/>
   <int value="1665349789" label="spurious-power-button-window"/>
@@ -40878,6 +40883,7 @@
   <int value="1" label="Overflow Menu"/>
   <int value="2" label="Contextual Menu"/>
   <int value="3" label="Page"/>
+  <int value="4" label="App Menu"/>
 </enum>
 
 <enum name="MediaRouterDialParseMessageResult">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index abf6645..46683336 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -14486,6 +14486,11 @@
 
 <histogram name="Blink.Animate.UpdateTime" units="microseconds"
     expires_after="2020-05-03">
+<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
+
+<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
+
+  <owner>schenney@chromium.org</owner>
   <owner>paint-dev@chromium.org</owner>
   <summary>
     Time spent processing main frame animations during a main frame update.
@@ -16088,6 +16093,10 @@
 
 <histogram name="Blink.MainFrame.UpdateTime" units="microseconds"
     expires_after="2020-05-03">
+<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
+
+<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
+
   <owner>schenney@chromium.org</owner>
   <owner>paint-dev@chromium.org</owner>
   <summary>
@@ -16275,6 +16284,11 @@
 
 <histogram name="Blink.Paint.UpdateTime" units="microseconds"
     expires_after="2020-05-03">
+<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
+
+<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
+
+  <owner>schenney@chromium.org</owner>
   <owner>paint-dev@chromium.org</owner>
   <summary>
     Time spent updating paint in the Blink document lifecycle.
@@ -16299,6 +16313,11 @@
 
 <histogram name="Blink.PrePaint.UpdateTime" units="microseconds"
     expires_after="2020-05-03">
+<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
+
+<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
+
+  <owner>schenney@chromium.org</owner>
   <owner>paint-dev@chromium.org</owner>
   <summary>
     Time spent updating paint properties and paint invalidation in the Blink
@@ -16312,6 +16331,11 @@
 
 <histogram name="Blink.ProxyCommit.UpdateTime" units="microseconds"
     expires_after="2020-05-03">
+<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
+
+<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
+
+  <owner>schenney@chromium.org</owner>
   <owner>paint-dev@chromium.org</owner>
   <summary>
     Time spent commiting the layer tree to the impl thread in a main frame
@@ -16603,7 +16627,12 @@
 
 <histogram name="Blink.ScrollingCoordinator.UpdateTime" units="microseconds"
     expires_after="2020-05-03">
+<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
+
+<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
+
   <owner>pdr@chromium.org</owner>
+  <owner>paint-dev@chromium.org</owner>
   <summary>
     The time it took to update scrolling coordinator data (scroll gesture
     regions, touch event rects, and main thread scrolling reasons). These values
@@ -16769,6 +16798,10 @@
 
 <histogram name="Blink.Style.UpdateTime" units="microseconds"
     expires_after="2020-05-03">
+<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
+
+<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
+
   <owner>schenney@chromium.org</owner>
   <owner>layout-dev@chromium.org</owner>
   <summary>
@@ -16781,6 +16814,11 @@
 
 <histogram name="Blink.StyleAndLayout.UpdateTime" units="microseconds"
     expires_after="2020-04-19">
+<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
+
+<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
+
+  <owner>schenney@chromium.org</owner>
   <owner>layout-dev@chromium.org</owner>
   <summary>
     Time spent updating style and layout in the Blink document lifecycle.
@@ -44639,8 +44677,7 @@
   </summary>
 </histogram>
 
-<histogram name="ExploreSites.NavBackTime" units="ms" expires_after="M80">
-  <owner>angelii@google.com</owner>
+<histogram name="ExploreSites.NavBackTime" units="ms" expires_after="M85">
   <owner>chili@chromium.org</owner>
   <owner>dewittj@chromium.org</owner>
   <summary>
@@ -52509,6 +52546,18 @@
   </summary>
 </histogram>
 
+<histogram name="GCM.SendWebPushMessageStatusCode"
+    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2020-04-05">
+  <owner>alexchau@chromium.org</owner>
+  <owner>peter@chromium.org</owner>
+  <summary>
+    Result code from sending web push messages. Logs net::Error if it's not
+    net::OK. Logs net::OK if response header is not present. Otherwise, logs
+    HTTP status code returned. Recorded when received response after message has
+    been sent.
+  </summary>
+</histogram>
+
 <histogram name="GCM.StoreDestroySucceeded" enum="BooleanSuccess">
   <owner>zea@chromium.org</owner>
   <summary>
@@ -99509,7 +99558,22 @@
   <summary>
     Records when hints fetching is enabled whether the HTTPS host being
     navigated to was included in a hints fetch request and any hints returned
-    have not since expired.
+    have not since expired. Captured at navigation start so it will not include
+    hints fetched based on the current navigation.
+  </summary>
+</histogram>
+
+<histogram
+    name="OptimizationGuide.HintsFetcher.NavigationHostCoveredByFetch.AtCommit"
+    enum="BooleanCovered" expires_after="M85">
+  <owner>mcrouse@chromium.org</owner>
+  <owner>dougarnett@chromium.org</owner>
+  <summary>
+    Records when hints fetching is enabled whether the HTTPS host being
+    navigated to was included in a hints fetch request and any hints returned
+    have not since expired. Captured after a navigation navigation is committed
+    in order to determine if the fetch attempt made at navigation start
+    succeeded or not.
   </summary>
 </histogram>
 
@@ -170119,6 +170183,40 @@
   <affected-histogram name="Blink.MainFrame.UpdateLayersRatio"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" separator=".">
+  <suffix name="PostFCP" label="Update occurred after First Contentful Paint."/>
+  <affected-histogram name="Blink.Animate.UpdateTime"/>
+  <affected-histogram name="Blink.Compositing.UpdateTime"/>
+  <affected-histogram name="Blink.CompositingCommit.UpdateTime"/>
+  <affected-histogram name="Blink.ForcedStyleAndLayout.UpdateTime"/>
+  <affected-histogram name="Blink.HandleInputEvents.UpdateTime"/>
+  <affected-histogram name="Blink.IntersectionObservation.UpdateTime"/>
+  <affected-histogram name="Blink.Layout.UpdateTime"/>
+  <affected-histogram name="Blink.Paint.UpdateTime"/>
+  <affected-histogram name="Blink.PrePaint.UpdateTime"/>
+  <affected-histogram name="Blink.ProxyCommit.UpdateTime"/>
+  <affected-histogram name="Blink.ScrollingCoordinator.UpdateTime"/>
+  <affected-histogram name="Blink.Style.UpdateTime"/>
+  <affected-histogram name="Blink.StyleAndLayout.UpdateTime"/>
+</histogram_suffixes>
+
+<histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" separator=".">
+  <suffix name="PreFCP" label="Update occurred before First Contentful Paint."/>
+  <affected-histogram name="Blink.Animate.UpdateTime"/>
+  <affected-histogram name="Blink.Compositing.UpdateTime"/>
+  <affected-histogram name="Blink.CompositingCommit.UpdateTime"/>
+  <affected-histogram name="Blink.ForcedStyleAndLayout.UpdateTime"/>
+  <affected-histogram name="Blink.HandleInputEvents.UpdateTime"/>
+  <affected-histogram name="Blink.IntersectionObservation.UpdateTime"/>
+  <affected-histogram name="Blink.Layout.UpdateTime"/>
+  <affected-histogram name="Blink.Paint.UpdateTime"/>
+  <affected-histogram name="Blink.PrePaint.UpdateTime"/>
+  <affected-histogram name="Blink.ProxyCommit.UpdateTime"/>
+  <affected-histogram name="Blink.ScrollingCoordinator.UpdateTime"/>
+  <affected-histogram name="Blink.Style.UpdateTime"/>
+  <affected-histogram name="Blink.StyleAndLayout.UpdateTime"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="BlinkVisibleLoadTimeSuffixes" separator=".">
   <suffix name="2G" label="2G effective connection type"/>
   <suffix name="3G" label="3G effective connection type"/>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 6210309..12e41f2 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -5332,6 +5332,19 @@
       applied on the page load.
     </summary>
   </metric>
+  <metric name="PainfulPageLoadModelPredictionScore">
+    <summary>
+      The score output after evaluating the painful page load model. If
+      populated, this is 100x the fractional value output by the model
+      evaluation. This will be a value between 0 and 100.
+    </summary>
+  </metric>
+  <metric name="PainfulPageLoadModelVersion">
+    <summary>
+      The server-generated version of the painful page load model that was
+      evaluated for the page load.
+    </summary>
+  </metric>
 </event>
 
 <event name="PageDomainInfo">
@@ -6041,8 +6054,6 @@
     <aggregation>
       <history>
         <index fields="profile.country"/>
-        <index fields="profile.is_dominant_version"/>
-        <index fields="profile.is_latest_version"/>
         <statistics>
           <quantiles type="std-percentiles"/>
         </statistics>
@@ -7250,7 +7261,7 @@
   </metric>
   <metric name="IsSwitchAccessEnabled">
     <summary>
-      Boolean value to represent whether switch access is currently enabled.
+      Boolean value to represent whether Switch Access is currently enabled.
     </summary>
   </metric>
   <metric name="IsVideoPlaying">
diff --git a/tools/perf/core/results_processor/README.md b/tools/perf/core/results_processor/README.md
new file mode 100644
index 0000000..5fac610a
--- /dev/null
+++ b/tools/perf/core/results_processor/README.md
@@ -0,0 +1,112 @@
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file.
+-->
+
+# Results Processor
+
+Results Processor is a tool to process intermediate results produced by
+Telemetry or other test-running frameworks and extract performance measurements
+from them. It expects its input in the form of a directory with the following
+contents:
+- A `_test_results.jsonl` file.
+- Subdirectories with test artifacts (including traces) - one per test result.
+
+## Test results file
+
+The `_test_results.jsonl` file tries to follow the
+[LUCI test results format](https://chromium.googlesource.com/infra/luci/luci-go/+/HEAD/resultdb/proto/sink/v1/test_result.proto).
+Its every line is a json message of the `testResult` type. There are following
+additional conventions:
+
+- The following keys are mandatory:
+  - status
+  - testPath
+- For [json3 output](https://chromium.googlesource.com/chromium/src/+/master/docs/testing/json_test_results_format.md), the following are also mandatory:
+  - expected
+  - runDuration
+- testPath is either in the form `{benchmark_name}/{story_name}` or in the form
+`{test_suite_name}.{test_case_name}`. The format to use is specified by the
+command-line flag `--test-path-format`.
+- TBMv2 metrics, if necessary, are listed as tags with key `tbmv2`.
+- Traces, if present, are listed as output artifacts with their names starting
+with `trace/`. For details, see the [Traces subsection](#traces) below.
+- Device and benchmark diagnostics (also optional) are stored in a special
+artifact with the name `diagnostics.json` in the form of a json dict.
+Diagnostic names must be listed in the
+[reserved infos](https://cs.chromium.org/chromium/src/third_party/catapult/tracing/tracing/value/diagnostics/reserved_infos.py)
+module.
+- All artifacts files for a single test result are in a separate subdirectory.
+
+### Traces
+
+Traces should be output artifacts with their names starting with `trace/`. The
+names should also have an extension. The extension of a trace name corresponds
+to its format as follows:
+
+|extension | format
+|--- | ---
+|.json | Json trace
+|.json.gz | Gzipped json trace
+|.pb | Proto trace
+|.pb.gz | Gzipped proto trace
+
+Note that this convention relates to the artifact name only, the actual file
+stored in the host does not have to follow it.
+
+### Example
+
+Example of a single testResult message (formatted across multiple lines for ease
+of reading):
+
+    {
+        "testResult": {
+            "testPath": "benchmark/story1",
+            "status": "PASS",
+            "expected": true,
+            "startTime": "2019-10-23T09:43:32.046792Z",
+            "runDuration": "15.06s",
+            "outputArtifacts": {
+                "diagnostics.json": {
+                    "contentType": "application/json",
+                    "filePath": "/output/run_1/story1/diagnostics.json"
+                },
+                "trace/cpu/1.json": {
+                    "contentType": "application/json",
+                    "filePath": "/output/run_1/story1/trace/cpu/1.json"
+                },
+                "trace/chrome/1.json.gz": {
+                    "contentType": "application/gzip",
+                    "filePath": "/output/run_1/story1/trace/chrome/1.json.gz"
+                }
+            },
+            "tags": [
+                {
+                    "key": "tbmv2",
+                    "value": "consoleErrorMetric"
+                },
+                {
+                    "key": "tbmv2",
+                    "value": "cpuTimeMetric"
+                }
+            ]
+        }
+    }
+
+## Two modes of operation
+
+Results Processor can be invoked as a standalone script
+[tools/perf/results_processor](https://cs.chromium.org/chromium/src/tools/perf/results_processor?q=tools/perf/results_processor)
+or as a python module (see e.g.
+[benchmark_runner.py](https://cs.chromium.org/chromium/src/tools/perf/core/benchmark_runner.py)
+).
+
+The standalone script has a mandatory argument `--intermediate-dir` that should
+point to a directory with intermediate results as described above. If invoked
+from another script, the `results_processor.ProcessOptions()` method provides
+a reasonable default for the `--intermediate-dir` argument.
+
+In both modes final results are placed inside the output directory provided
+by the `--output-dir` argument. By default it’s the base directory of the
+running script.
+
diff --git a/tools/perf/core/results_processor/command_line.py b/tools/perf/core/results_processor/command_line.py
index 5ea5e73..97515ce7d 100644
--- a/tools/perf/core/results_processor/command_line.py
+++ b/tools/perf/core/results_processor/command_line.py
@@ -139,7 +139,9 @@
 
   if not options.output_formats:
     options.output_formats = ['html']
-  elif 'none' in options.output_formats:
+  else:
+    options.output_formats = sorted(set(options.output_formats))
+  if 'none' in options.output_formats:
     options.output_formats.remove('none')
 
 
diff --git a/tools/perf/core/results_processor/command_line_unittest.py b/tools/perf/core/results_processor/command_line_unittest.py
index 147bc2a3..c929abb 100644
--- a/tools/perf/core/results_processor/command_line_unittest.py
+++ b/tools/perf/core/results_processor/command_line_unittest.py
@@ -129,6 +129,12 @@
     with self.assertRaises(SystemExit):
       self.ParseArgs(['--output-format', 'unknown'])
 
+  def testNoDuplicateOutputFormats(self):
+    options = self.ParseArgs(
+        ['--output-format', 'html', '--output-format', 'csv',
+         '--output-format', 'html', '--output-format', 'csv'])
+    self.assertEqual(options.output_formats, ['csv', 'html'])
+
 
 class StandaloneTestProcessOptions(ProcessOptionsTestCase):
   def setUp(self):
diff --git a/tools/perf/core/results_processor/processor.py b/tools/perf/core/results_processor/processor.py
index 853e55d..586b5d6 100644
--- a/tools/perf/core/results_processor/processor.py
+++ b/tools/perf/core/results_processor/processor.py
@@ -205,9 +205,7 @@
   """
   artifacts = test_result.get('outputArtifacts', {})
   json_traces = [name for name in artifacts if _IsJsonTrace(name)]
-  # TODO(crbug.com/981349): Stop checking for HTML_TRACE_NAME after
-  # Telemetry does not aggregate traces anymore.
-  if json_traces and compute_metrics.HTML_TRACE_NAME not in artifacts:
+  if json_traces:
     trace_files = [artifacts[name]['filePath'] for name in json_traces]
     html_path = _BuildOutputPath(trace_files, compute_metrics.HTML_TRACE_NAME)
     logging.info('%s: Aggregating json traces %s.',
@@ -246,7 +244,7 @@
       continue
     # TODO(crbug.com/981349): Think of a more general way to
     # specify which artifacts deserve uploading.
-    if name == MEASUREMENTS_NAME:
+    if name in [DIAGNOSTICS_NAME, MEASUREMENTS_NAME]:
       continue
     remote_name = '/'.join([run_identifier, test_result['testPath'], name])
     urlsafe_remote_name = re.sub(r'[^A-Za-z0-9/.-]+', '_', remote_name)
diff --git a/tools/perf/core/results_processor/processor_test.py b/tools/perf/core/results_processor/processor_test.py
index d3962a85..0a161fd 100644
--- a/tools/perf/core/results_processor/processor_test.py
+++ b/tools/perf/core/results_processor/processor_test.py
@@ -123,9 +123,8 @@
             'benchmark/story',
             output_artifacts={
                 'logs': testing.Artifact('/logs.txt', 'gs://logs.txt'),
-                'trace/telemetry.json': testing.Artifact('/telemetry.json'),
-                'trace.html':
-                    testing.Artifact('/trace.html', 'gs://trace.html'),
+                'screenshot': testing.Artifact(
+                    os.path.join(self.output_dir, 'screenshot.png')),
             }
         ),
     )
@@ -146,7 +145,7 @@
 
     self.assertEqual(len(artifacts), 2)
     self.assertEqual(artifacts['logs'], ['gs://logs.txt'])
-    self.assertEqual(artifacts['trace.html'], ['gs://trace.html'])
+    self.assertEqual(artifacts['screenshot'], ['screenshot.png'])
 
   def testMaxValuesPerTestCase(self):
     def SomeMeasurements(num):
diff --git a/tools/resources/svgo_presubmit.py b/tools/resources/svgo_presubmit.py
index a32dfb33..4789106 100644
--- a/tools/resources/svgo_presubmit.py
+++ b/tools/resources/svgo_presubmit.py
@@ -3,8 +3,15 @@
 # found in the LICENSE file.
 
 
+# Ignore the following files from SVG optimization checks.
+BLOCKLIST = [
+  # Ignore since it holds documentation comments.
+  "components/dom_distiller/core/images/dom_distiller_material_spinner.svg",
+]
+
 def CheckOptimized(input_api, output_api):
-  file_filter = lambda f: f.LocalPath().endswith('.svg')
+  file_filter = lambda f: f.LocalPath().endswith('.svg') and \
+      f.LocalPath() not in BLOCKLIST
   svgs = input_api.AffectedFiles(file_filter=file_filter, include_deletes=False)
 
   if not svgs:
diff --git a/tools/translation/README.md b/tools/translation/README.md
index 7d3cebf..54291b95 100644
--- a/tools/translation/README.md
+++ b/tools/translation/README.md
@@ -24,3 +24,19 @@
 
 For more information, see [https://g.co/chrome/translation](https://g.co/chrome/translation).
 
+# Run the tests
+
+The presubmit automatically runs all files named `*_unittest.py `:
+```
+git cl presubmit --force
+```
+
+python
+
+# Run sanity checks
+
+This will attempt to load all .grd and .grdp files in the Chrome repo.
+Run once before uploading the CL to ensure everything works.
+```
+python helper/sanity_check.py
+```
diff --git a/tools/translation/helper/grd_helper.py b/tools/translation/helper/grd_helper.py
new file mode 100644
index 0000000..b36aee03
--- /dev/null
+++ b/tools/translation/helper/grd_helper.py
@@ -0,0 +1,75 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import sys
+import tempfile
+
+here = os.path.dirname(os.path.realpath(__file__))
+repo_root = os.path.normpath(os.path.join(here, '..', '..', '..'))
+
+try:
+  old_sys_path = sys.path
+  sys.path = sys.path + [os.path.join(repo_root, 'tools', 'grit')]
+  import grit.grd_reader
+  import grit.node.message
+  import grit.util
+finally:
+  sys.path = old_sys_path
+
+TAGS_TO_IGNORE = (
+    # <include> tags mostly point to resource files that don't contain UI
+    # strings.
+    'include',
+    # <structure> tags point to image files.
+    'structure')
+
+
+def GetGrdMessages(grd_path_or_string, dir_path):
+  """Load the grd file and return a dict of message ids to messages."""
+  doc = grit.grd_reader.Parse(
+      grd_path_or_string,
+      dir_path,
+      stop_after=None,
+      first_ids_file=None,
+      debug=False,
+      defines={'_chromium': 1},
+      tags_to_ignore=set(TAGS_TO_IGNORE))
+  return {
+      msg.attrs['name']: msg
+      for msg in doc.GetChildrenOfType(grit.node.message.MessageNode)
+  }
+
+
+def GetGrdpMessagesFromString(grdp_string):
+  """Parses the contents of the grdp file given in grdp_string.
+
+    grd_reader can't parse grdp files directly. Instead, this replaces grd
+    specific tags in the input string with grdp specific tags, writes the output
+    string in a temporary file and loads the grd from the temporary file.
+
+    This code previously created a temporary directory (using grit.util), wrote
+    a temporary grd file pointing to another temporary grdp file that contained
+    the input string. This was not testable since os.path.exists is overridden
+    in tests and grit.util uses mkdirs which in turn calls os.path.exists. This
+    new code works because it doesn't need to check the existence of a path.
+
+    It's expected that a grdp file will not refer to another grdp file via a
+    <part> tag. Currently, none of the grdp files in the repository does that.
+     """
+  replaced_string = grdp_string.replace(
+      '<grit-part>',
+      """<grit base_dir="." latest_public_release="1" current_release="1">
+            <release seq="1" allow_pseudo="false">
+              <messages fallback_to_english="true">
+        """)
+  replaced_string = replaced_string.replace(
+      '</grit-part>', """</messages>
+          </release>
+        </grit>""")
+  with tempfile.NamedTemporaryFile() as f:
+    f.write(replaced_string.encode('utf-8'))
+    f.flush()
+    f.seek(0)
+    return GetGrdMessages(f.name, os.path.dirname(f.name))
diff --git a/tools/translation/helper/grd_helper_unittest.py b/tools/translation/helper/grd_helper_unittest.py
new file mode 100644
index 0000000..cfa181d
--- /dev/null
+++ b/tools/translation/helper/grd_helper_unittest.py
@@ -0,0 +1,43 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Unit tests for grd_helper.py."""
+
+import io
+import os
+import unittest
+from .helper import grd_helper
+
+here = os.path.dirname(os.path.realpath(__file__))
+repo_root = os.path.normpath(os.path.join(here, '..', '..', '..'))
+
+
+def read_file_as_text(path):
+  with io.open(path, mode='r', encoding='utf-8') as f:
+    return f.read()
+
+
+class GrdHelperTest(unittest.TestCase):
+
+  def testReadGrdMessages(self):
+    grd_dir = os.path.join(repo_root, 'tools', 'translation', 'testdata')
+    messages = grd_helper.GetGrdMessages(
+        os.path.join(grd_dir, 'test.grd'), grd_dir)
+    # Result should contain all messages from test.grd and part.grdp.
+    self.assertTrue('IDS_TEST_STRING1' in messages)
+    self.assertTrue('IDS_TEST_STRING2' in messages)
+    self.assertTrue('IDS_TEST_STRING_NON_TRANSLATEABLE' in messages)
+    self.assertTrue('IDS_PART_STRING_NON_TRANSLATEABLE' in messages)
+
+  def testReadGrdpMessages(self):
+    messages = grd_helper.GetGrdpMessagesFromString(
+        read_file_as_text(
+            os.path.join(repo_root, 'tools', 'translation', 'testdata',
+                         'part.grdp')))
+    self.assertTrue('IDS_PART_STRING1' in messages)
+    self.assertTrue('IDS_PART_STRING2' in messages)
+    print('DONE')
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/tools/translation/helper/sanity_check.py b/tools/translation/helper/sanity_check.py
new file mode 100644
index 0000000..cf2365de
--- /dev/null
+++ b/tools/translation/helper/sanity_check.py
@@ -0,0 +1,82 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Sanity checking for grd_helper.py. Run manually before uploading a CL."""
+
+from .helper import grd_helper
+from .helper import translation_helper
+import io
+import os
+import subprocess
+import sys
+
+if sys.platform.startswith('win'):
+  # Use the |git.bat| in the depot_tools/ on Windows.
+  GIT = 'git.bat'
+else:
+  GIT = 'git'
+
+here = os.path.dirname(os.path.realpath(__file__))
+repo_root = os.path.normpath(os.path.join(here, '..', '..', '..'))
+
+
+def list_files_in_repository(repo_path, pattern):
+  """Lists all files matching given pattern in the given git repository"""
+  # This works because git does its own glob expansion even though there is no
+  # shell to do it.
+  output = subprocess.check_output([GIT, 'ls-files', '--', pattern],
+                                   cwd=repo_path)
+  return output.strip().splitlines()
+
+
+def read_file_as_text(path):
+  with io.open(path, mode='r', encoding='utf-8') as f:
+    return f.read()
+
+
+# Sanity checks to ensure that we can parse all grd and grdp files in the repo.
+# Must not fail.
+def Run():
+  grds = list_files_in_repository(repo_root, '*.grd')
+  grdps = list_files_in_repository(repo_root, '*.grdp')
+
+  print('Found %d grds, %d grdps in the repo.' % (len(grds), len(grdps)))
+  # Make sure we can parse all .grd files in the source tree. Grd files are
+  # parsed via the file path.
+  for grd in grds:
+    # This file is intentionally missing an include, skip it.
+    if grd == os.path.join('tools', 'translation', 'testdata', 'internal.grd'):
+      continue
+    path = os.path.join(repo_root, grd)
+    grd_helper.GetGrdMessages(path, os.path.dirname(path))
+
+  # Make sure we can parse all .grdp files in the source tree.
+  # Grdp files are parsed using file contents instead of path.
+  for grdp in grdps:
+    path = os.path.join(repo_root, grdp)
+    # Parse grdp files using file contents.
+    contents = read_file_as_text(path)
+    grd_helper.GetGrdpMessagesFromString(contents)
+
+  print('Successfully parsed all .grd and .grdp files in the repo.')
+
+  # Additional check for translateable grds. Translateable grds are a subset
+  # of all grds so this checks some files twice, but it exercises the
+  # get_translatable_grds() path and also doesn't need to skip internal.grd.
+  TRANSLATION_EXPECTATIONS_PATH = os.path.join(repo_root, 'tools',
+                                               'gritsettings',
+                                               'translation_expectations.pyl')
+  translateable_grds = translation_helper.get_translatable_grds(
+      repo_root, grds, TRANSLATION_EXPECTATIONS_PATH)
+  print('Found %d translateable .grd files in translation expectations.' %
+        len(translateable_grds))
+  for grd in translateable_grds:
+    path = os.path.join(repo_root, grd.path)
+    grd_helper.GetGrdMessages(path, os.path.dirname(path))
+  print('Successfully parsed all translateable_grds .grd files in translation '
+        'expectations.')
+  print('DONE')
+
+
+if __name__ == '__main__':
+  Run()
diff --git a/ui/accessibility/accessibility_switches.h b/ui/accessibility/accessibility_switches.h
index e34013a..aacf314 100644
--- a/ui/accessibility/accessibility_switches.h
+++ b/ui/accessibility/accessibility_switches.h
@@ -25,7 +25,7 @@
 // Returns true if experimental accessibility language detection is enabled.
 AX_EXPORT bool IsExperimentalAccessibilityLanguageDetectionEnabled();
 
-// Returns true if experimental accessibility switch access text is enabled.
+// Returns true if experimental accessibility Switch Access text is enabled.
 AX_EXPORT bool IsExperimentalAccessibilitySwitchAccessTextEnabled();
 
 #if defined(OS_WIN)
diff --git a/ui/base/test/scoped_fake_nswindow_focus.mm b/ui/base/test/scoped_fake_nswindow_focus.mm
index 91bd4b55..00b340af 100644
--- a/ui/base/test/scoped_fake_nswindow_focus.mm
+++ b/ui/base/test/scoped_fake_nswindow_focus.mm
@@ -14,7 +14,7 @@
 namespace {
 
 NSWindow* g_fake_focused_window = nil;
-IMP g_order_out_impl_ = nullptr;
+base::mac::ScopedObjCClassSwizzler* g_order_out_swizzler = nullptr;
 
 void SetFocus(NSWindow* window) {
   g_fake_focused_window = window;
@@ -74,7 +74,7 @@
   NSWindow* selfAsWindow = base::mac::ObjCCastStrict<NSWindow>(self);
   if (selfAsWindow == g_fake_focused_window)
     ClearFocus();
-  g_order_out_impl_(self, _cmd, sender);
+  g_order_out_swizzler->InvokeOriginal<void, id>(self, _cmd, sender);
 }
 
 - (void)resignKeyWindow {
@@ -117,10 +117,11 @@
           new ScopedObjCClassSwizzler([NSWindow class],
                                       [FakeNSWindowFocusDonor class],
                                       @selector(orderOut:))) {
-  g_order_out_impl_ = order_out_swizzler_->GetOriginalImplementation();
+  g_order_out_swizzler = order_out_swizzler_.get();
 }
 
 ScopedFakeNSWindowFocus::~ScopedFakeNSWindowFocus() {
+  g_order_out_swizzler = nullptr;
   ClearFocus();
 }
 
diff --git a/ui/base/test/scoped_fake_nswindow_fullscreen.mm b/ui/base/test/scoped_fake_nswindow_fullscreen.mm
index 3ad4b694..f09aa141 100644
--- a/ui/base/test/scoped_fake_nswindow_fullscreen.mm
+++ b/ui/base/test/scoped_fake_nswindow_fullscreen.mm
@@ -61,14 +61,14 @@
     }
   }
 
-  IMP SetStyleMaskImplementation() {
-    return set_style_mask_swizzler_.GetOriginalImplementation();
+  void OriginalSetStyleMask(id receiver, SEL selector, NSUInteger mask) {
+    return set_style_mask_swizzler_.InvokeOriginal<void, NSUInteger>(
+        receiver, selector, mask);
   }
 
   NSUInteger StyleMaskForWindow(NSWindow* window) {
-    NSUInteger actual_style_mask = reinterpret_cast<NSUInteger>(
-        style_mask_swizzler_.GetOriginalImplementation()(window,
-                                                         @selector(styleMask)));
+    auto actual_style_mask = style_mask_swizzler_.InvokeOriginal<NSUInteger>(
+        window, @selector(styleMask));
     if (window_ != window || !style_as_fullscreen_)
       return actual_style_mask;
 
@@ -233,7 +233,7 @@
     NOTREACHED() << "Can't set NSFullScreenWindowMask while faking fullscreen.";
   }
   newMask &= ~NSFullScreenWindowMask;
-  g_fake_fullscreen_impl->SetStyleMaskImplementation()(self, _cmd, newMask);
+  g_fake_fullscreen_impl->OriginalSetStyleMask(self, _cmd, newMask);
 }
 
 @end
diff --git a/ui/color/color_mixer.cc b/ui/color/color_mixer.cc
index f0d038bd..2069d5b 100644
--- a/ui/color/color_mixer.cc
+++ b/ui/color/color_mixer.cc
@@ -4,8 +4,6 @@
 
 #include "ui/color/color_mixer.h"
 
-#include "base/containers/adapters.h"
-#include "base/stl_util.h"
 #include "ui/color/color_recipe.h"
 #include "ui/gfx/color_palette.h"
 
@@ -20,17 +18,16 @@
 
 ColorMixer::~ColorMixer() = default;
 
+ColorRecipe& ColorMixer::operator[](ColorId id) {
+  DCHECK_COLOR_ID_VALID(id);
+  return recipes_[id];
+}
+
 void ColorMixer::AddSet(ColorSet&& set) {
   DCHECK(FindSetWithId(set.id) == sets_.cend());
   sets_.push_front(std::move(set));
 }
 
-ColorRecipe& ColorMixer::AddRecipe(ColorId id) {
-  DCHECK_COLOR_ID_VALID(id);
-  DCHECK(!base::Contains(recipes_, id));
-  return recipes_[id];
-}
-
 SkColor ColorMixer::GetInputColor(ColorId id) const {
   DCHECK_COLOR_ID_VALID(id);
   for (const auto& set : sets_) {
diff --git a/ui/color/color_mixer.h b/ui/color/color_mixer.h
index d5879487..899f894f5 100644
--- a/ui/color/color_mixer.h
+++ b/ui/color/color_mixer.h
@@ -39,16 +39,14 @@
   ColorMixer& operator=(ColorMixer&&) noexcept;
   ~ColorMixer();
 
+  // Adds a recipe for |id| if it does not exist.
+  ColorRecipe& operator[](ColorId id);
+
   // Adds |set| to |sets_|.  |set| must not have the same ID as any previously
   // added sets, though it may contain colors with the same IDs as colors in
   // those sets; in such cases, the last-added set takes priority.
   void AddSet(ColorSet&& set);
 
-  // Adds a recipe for |id|, which must not already have an existing recipe.
-  // Returns a non-const ref to allow chaining calls to
-  // ColorRecipe::AddTransform().
-  ColorRecipe& AddRecipe(ColorId id);
-
   // Returns the input color for |id|.  First searches all |sets_| in reverse
   // order; if not found, asks the previous mixer for the result color.  If
   // there is no previous mixer, returns gfx::kPlaceholderColor.
diff --git a/ui/color/color_mixer_unittest.cc b/ui/color/color_mixer_unittest.cc
index f2ad20f1..4babf86 100644
--- a/ui/color/color_mixer_unittest.cc
+++ b/ui/color/color_mixer_unittest.cc
@@ -27,10 +27,10 @@
   EXPECT_EQ(SK_ColorGREEN, mixer.GetResultColor(kColorTest0));
 }
 
-// Tests that the recipe returned by AddRecipe() is respected by the mixer.
-TEST(ColorMixerTest, AddRecipe) {
+// Tests that the recipe returned by operator[] is respected by the mixer.
+TEST(ColorMixerTest, AccessOperator) {
   ColorMixer mixer;
-  mixer.AddRecipe(kColorTest0).AddTransform(FromColor(SK_ColorGREEN));
+  mixer[kColorTest0].AddTransform(FromColor(SK_ColorGREEN));
   EXPECT_EQ(SK_ColorGREEN, mixer.GetResultColor(kColorTest0));
 }
 
@@ -86,8 +86,8 @@
 TEST(ColorMixerTest, GetInputColorIgnoresRecipe) {
   ColorMixer mixer;
   mixer.AddSet({kColorSetTest0, {{kColorTest0, SK_ColorGREEN}}});
-  mixer.AddRecipe(kColorTest0)
-      .AddTransform(GetColorWithMaxContrast(FromTransformInput()));
+  mixer[kColorTest0].AddTransform(
+      GetColorWithMaxContrast(FromTransformInput()));
   EXPECT_EQ(SK_ColorGREEN, mixer.GetInputColor(kColorTest0));
 }
 
@@ -97,8 +97,8 @@
 TEST(ColorMixerTest, GetInputColorRespectsRecipePreviousMixer) {
   ColorMixer mixer0;
   mixer0.AddSet({kColorSetTest0, {{kColorTest0, SK_ColorGREEN}}});
-  mixer0.AddRecipe(kColorTest0)
-      .AddTransform(GetColorWithMaxContrast(FromTransformInput()));
+  mixer0[kColorTest0].AddTransform(
+      GetColorWithMaxContrast(FromTransformInput()));
   ColorMixer mixer1(&mixer0);
   mixer1.AddSet({kColorSetTest1, {{kColorTest1, SK_ColorRED}}});
   EXPECT_EQ(color_utils::GetColorWithMaxContrast(SK_ColorGREEN),
@@ -169,8 +169,8 @@
 TEST(ColorMixerTest, GetOriginalColorFromSetIgnoresRecipe) {
   ColorMixer mixer;
   mixer.AddSet({kColorSetTest0, {{kColorTest0, SK_ColorGREEN}}});
-  mixer.AddRecipe(kColorTest0)
-      .AddTransform(GetColorWithMaxContrast(FromTransformInput()));
+  mixer[kColorTest0].AddTransform(
+      GetColorWithMaxContrast(FromTransformInput()));
   EXPECT_EQ(SK_ColorGREEN,
             mixer.GetOriginalColorFromSet(kColorTest0, kColorSetTest0));
 }
@@ -181,8 +181,8 @@
 TEST(ColorMixerTest, GetOriginalColorFromSetIgnoresRecipePreviousMixer) {
   ColorMixer mixer0;
   mixer0.AddSet({kColorSetTest0, {{kColorTest0, SK_ColorGREEN}}});
-  mixer0.AddRecipe(kColorTest0)
-      .AddTransform(GetColorWithMaxContrast(FromTransformInput()));
+  mixer0[kColorTest0].AddTransform(
+      GetColorWithMaxContrast(FromTransformInput()));
   ColorMixer mixer1(&mixer0);
   mixer1.AddSet({kColorSetTest1, {{kColorTest1, SK_ColorRED}}});
   EXPECT_EQ(SK_ColorGREEN,
@@ -207,9 +207,9 @@
 // initial value for its requested color.
 TEST(ColorMixerTest, GetResultColorNoSet) {
   ColorMixer mixer;
-  mixer.AddRecipe(kColorTest0).AddTransform(FromColor(SK_ColorGREEN));
-  mixer.AddRecipe(kColorTest1)
-      .AddTransform(GetColorWithMaxContrast(FromTransformInput()));
+  mixer[kColorTest0].AddTransform(FromColor(SK_ColorGREEN));
+  mixer[kColorTest1].AddTransform(
+      GetColorWithMaxContrast(FromTransformInput()));
   EXPECT_EQ(SK_ColorGREEN, mixer.GetResultColor(kColorTest0));
   EXPECT_NE(gfx::kPlaceholderColor, mixer.GetResultColor(kColorTest1));
 }
@@ -220,8 +220,8 @@
   ColorMixer mixer;
   mixer.AddSet({kColorSetTest0,
                 {{kColorTest0, SK_ColorWHITE}, {kColorTest1, SK_ColorBLACK}}});
-  mixer.AddRecipe(kColorTest0).AddTransform(FromColor(SK_ColorGREEN));
-  mixer.AddRecipe(kColorTest1).AddTransform(FromColor(SK_ColorGREEN));
+  mixer[kColorTest0].AddTransform(FromColor(SK_ColorGREEN));
+  mixer[kColorTest1].AddTransform(FromColor(SK_ColorGREEN));
   EXPECT_EQ(SK_ColorGREEN, mixer.GetResultColor(kColorTest0));
   EXPECT_EQ(SK_ColorGREEN, mixer.GetResultColor(kColorTest1));
 }
@@ -231,14 +231,12 @@
 TEST(ColorMixerTest, GetResultColorChained) {
   ColorMixer mixer;
   mixer.AddSet({kColorSetTest0, {{kColorTest1, SK_ColorWHITE}}});
-  mixer.AddRecipe(kColorTest0).AddTransform(FromColor(gfx::kGoogleBlue050));
-  mixer.AddRecipe(kColorTest1)
-      .AddTransform(BlendTowardMaxContrast(
-          GetColorWithMaxContrast(FromTransformInput()), 0x29));
-  mixer.AddRecipe(kColorTest2)
-      .AddTransform(BlendForMinContrast(FromColor(gfx::kGoogleBlue500),
-                                        FromResultColor(kColorTest1),
-                                        FromResultColor(kColorTest0)));
+  mixer[kColorTest0].AddTransform(FromColor(gfx::kGoogleBlue050));
+  mixer[kColorTest1].AddTransform(BlendTowardMaxContrast(
+      GetColorWithMaxContrast(FromTransformInput()), 0x29));
+  mixer[kColorTest2].AddTransform(BlendForMinContrast(
+      FromColor(gfx::kGoogleBlue500), FromResultColor(kColorTest1),
+      FromResultColor(kColorTest0)));
   EXPECT_EQ(SkColorSetRGB(0x89, 0xB3, 0xF8), mixer.GetResultColor(kColorTest2));
 }
 
diff --git a/ui/color/color_provider.cc b/ui/color/color_provider.cc
index d1d8d34..e9b34d4 100644
--- a/ui/color/color_provider.cc
+++ b/ui/color/color_provider.cc
@@ -11,7 +11,7 @@
 
 namespace ui {
 
-ColorMixer* ColorProvider::AddMixer() {
+ColorMixer& ColorProvider::AddMixer() {
   // Adding a mixer could change any of the result colors.
   cache_.clear();
 
@@ -20,7 +20,7 @@
   // mixer for its result, and trust mixers to query each other back up the
   // chain as needed.
   mixers_.emplace_front(mixers_.empty() ? nullptr : &mixers_.front());
-  return &mixers_.front();
+  return mixers_.front();
 }
 
 SkColor ColorProvider::GetColor(ColorId id, ColorVariant variant) const {
diff --git a/ui/color/color_provider.h b/ui/color/color_provider.h
index b8a6a56..62347b8 100644
--- a/ui/color/color_provider.h
+++ b/ui/color/color_provider.h
@@ -30,9 +30,9 @@
   ColorProvider& operator=(const ColorProvider&) = delete;
   ~ColorProvider();
 
-  // Adds a mixer to the end of the current color pipeline.  Returns a pointer
+  // Adds a mixer to the end of the current color pipeline.  Returns a reference
   // to the added mixer so callers can subsequently add sets and/or recipes.
-  ColorMixer* AddMixer();
+  ColorMixer& AddMixer();
 
   // Returns the result color for |id| by applying the effects of each mixer in
   // order.  Returns gfx::kPlaceholderColor if no mixer knows how to construct
diff --git a/ui/color/color_provider_unittest.cc b/ui/color/color_provider_unittest.cc
index c094c03..381faa3 100644
--- a/ui/color/color_provider_unittest.cc
+++ b/ui/color/color_provider_unittest.cc
@@ -11,14 +11,6 @@
 namespace ui {
 namespace {
 
-// Tests that AddMixer() returns non-null pointers, implying addition was
-// successful.  (Other tests below will verify the mixers have an effect.)
-TEST(ColorProviderTest, AddMixer) {
-  ColorProvider provider;
-  EXPECT_NE(nullptr, provider.AddMixer());
-  EXPECT_NE(nullptr, provider.AddMixer());
-}
-
 // Tests that when there are no mixers, GetColor() returns a placeholder value.
 TEST(ColorProviderTest, GetColorNoMixers) {
   EXPECT_EQ(gfx::kPlaceholderColor, ColorProvider().GetColor(kColorTest0));
@@ -28,7 +20,7 @@
 // possible.
 TEST(ColorProviderTest, SingleMixer) {
   ColorProvider provider;
-  provider.AddMixer()->AddSet({kColorSetTest0, {{kColorTest0, SK_ColorGREEN}}});
+  provider.AddMixer().AddSet({kColorSetTest0, {{kColorTest0, SK_ColorGREEN}}});
   EXPECT_EQ(SK_ColorGREEN, provider.GetColor(kColorTest0));
   EXPECT_EQ(gfx::kPlaceholderColor, provider.GetColor(kColorTest1));
 }
@@ -37,8 +29,8 @@
 // use of both.
 TEST(ColorProviderTest, NonOverlappingMixers) {
   ColorProvider provider;
-  provider.AddMixer()->AddSet({kColorSetTest0, {{kColorTest0, SK_ColorGREEN}}});
-  provider.AddMixer()->AddSet({kColorSetTest1, {{kColorTest1, SK_ColorRED}}});
+  provider.AddMixer().AddSet({kColorSetTest0, {{kColorTest0, SK_ColorGREEN}}});
+  provider.AddMixer().AddSet({kColorSetTest1, {{kColorTest1, SK_ColorRED}}});
   EXPECT_EQ(SK_ColorGREEN, provider.GetColor(kColorTest0));
   EXPECT_EQ(SK_ColorRED, provider.GetColor(kColorTest1));
 }
@@ -47,8 +39,8 @@
 // added takes priority.
 TEST(ColorProviderTest, OverlappingMixers) {
   ColorProvider provider;
-  provider.AddMixer()->AddSet({kColorSetTest0, {{kColorTest0, SK_ColorGREEN}}});
-  provider.AddMixer()->AddSet({kColorSetTest0, {{kColorTest0, SK_ColorRED}}});
+  provider.AddMixer().AddSet({kColorSetTest0, {{kColorTest0, SK_ColorGREEN}}});
+  provider.AddMixer().AddSet({kColorSetTest0, {{kColorTest0, SK_ColorRED}}});
   EXPECT_EQ(SK_ColorRED, provider.GetColor(kColorTest0));
 }
 
@@ -56,10 +48,10 @@
 // This attempts to verify that nothing is badly wrong with color caching.
 TEST(ColorProviderTest, Caching) {
   ColorProvider provider;
-  provider.AddMixer()->AddSet({kColorSetTest0, {{kColorTest0, SK_ColorGREEN}}});
+  provider.AddMixer().AddSet({kColorSetTest0, {{kColorTest0, SK_ColorGREEN}}});
   EXPECT_EQ(SK_ColorGREEN, provider.GetColor(kColorTest0));
   EXPECT_EQ(SK_ColorGREEN, provider.GetColor(kColorTest0));
-  provider.AddMixer()->AddSet({kColorSetTest0, {{kColorTest0, SK_ColorRED}}});
+  provider.AddMixer().AddSet({kColorSetTest0, {{kColorTest0, SK_ColorRED}}});
   EXPECT_EQ(SK_ColorRED, provider.GetColor(kColorTest0));
 }
 
diff --git a/ui/color/color_transform_unittest.cc b/ui/color/color_transform_unittest.cc
index f8cc4a3..4a0b1db 100644
--- a/ui/color/color_transform_unittest.cc
+++ b/ui/color/color_transform_unittest.cc
@@ -156,7 +156,7 @@
   ColorMixer mixer;
   mixer.AddSet({kColorSetTest0,
                 {{kColorTest0, SK_ColorGREEN}, {kColorTest1, kTest1Color}}});
-  mixer.AddRecipe(kColorTest0).AddTransform(FromInputColor(kColorTest1));
+  mixer[kColorTest0].AddTransform(FromInputColor(kColorTest1));
   const auto verify_color = [&](SkColor input) {
     EXPECT_EQ(kTest1Color, transform.Run(input, mixer));
   };
diff --git a/ui/color/win/native_color_mixer.cc b/ui/color/win/native_color_mixer.cc
index 798c087..0c78d323 100644
--- a/ui/color/win/native_color_mixer.cc
+++ b/ui/color/win/native_color_mixer.cc
@@ -21,7 +21,7 @@
   // reverse-engineering current Windows behavior.  Or maybe the union of all
   // these.
 #define MAP(chrome, native) {chrome, color_utils::GetSysSkColor(native)}
-  provider->AddMixer()->AddSet(
+  provider->AddMixer().AddSet(
       {kColorSetNative,
        {
            MAP(kColorNative3dDkShadow, COLOR_3DDKSHADOW),
diff --git a/ui/views/controls/label.cc b/ui/views/controls/label.cc
index 4c8d4ee1..335aa2b 100644
--- a/ui/views/controls/label.cc
+++ b/ui/views/controls/label.cc
@@ -460,7 +460,8 @@
   if (!GetVisible() && collapse_when_hidden_)
     return gfx::Size();
 
-  gfx::Size size(0, font_list().GetHeight());
+  // Always reserve vertical space for at least one line.
+  gfx::Size size(0, std::max(font_list().GetHeight(), GetLineHeight()));
   if (elide_behavior_ == gfx::ELIDE_HEAD ||
       elide_behavior_ == gfx::ELIDE_MIDDLE ||
       elide_behavior_ == gfx::ELIDE_TAIL ||
@@ -479,6 +480,7 @@
       size.SetToMin(GetTextSize());
     }
   }
+
   size.Enlarge(GetInsets().width(), GetInsets().height());
   return size;
 }
diff --git a/ui/views/controls/label_unittest.cc b/ui/views/controls/label_unittest.cc
index ff8e63d..518180f9 100644
--- a/ui/views/controls/label_unittest.cc
+++ b/ui/views/controls/label_unittest.cc
@@ -326,6 +326,54 @@
   EXPECT_EQ(was_rtl, base::i18n::IsRTL());
 }
 
+TEST_F(LabelTest, MinimumSizeRespectsLineHeight) {
+  base::string16 text(ASCIIToUTF16("This is example text."));
+  label()->SetText(text);
+
+  const gfx::Size minimum_size = label()->GetMinimumSize();
+  const int expected_height = minimum_size.height() + 10;
+  label()->SetLineHeight(expected_height);
+  EXPECT_EQ(expected_height, label()->GetMinimumSize().height());
+}
+
+TEST_F(LabelTest, MinimumSizeRespectsLineHeightMultiline) {
+  base::string16 text(ASCIIToUTF16("This is example text."));
+  label()->SetText(text);
+  label()->SetMultiLine(true);
+
+  const gfx::Size minimum_size = label()->GetMinimumSize();
+  const int expected_height = minimum_size.height() + 10;
+  label()->SetLineHeight(expected_height);
+  EXPECT_EQ(expected_height, label()->GetMinimumSize().height());
+}
+
+TEST_F(LabelTest, MinimumSizeRespectsLineHeightWithInsets) {
+  base::string16 text(ASCIIToUTF16("This is example text."));
+  label()->SetText(text);
+
+  const gfx::Size minimum_size = label()->GetMinimumSize();
+  int expected_height = minimum_size.height() + 10;
+  label()->SetLineHeight(expected_height);
+  constexpr gfx::Insets kInsets{2, 3, 4, 5};
+  expected_height += kInsets.height();
+  label()->SetBorder(CreateEmptyBorder(kInsets));
+  EXPECT_EQ(expected_height, label()->GetMinimumSize().height());
+}
+
+TEST_F(LabelTest, MinimumSizeRespectsLineHeightMultilineWithInsets) {
+  base::string16 text(ASCIIToUTF16("This is example text."));
+  label()->SetText(text);
+  label()->SetMultiLine(true);
+
+  const gfx::Size minimum_size = label()->GetMinimumSize();
+  int expected_height = minimum_size.height() + 10;
+  label()->SetLineHeight(expected_height);
+  constexpr gfx::Insets kInsets{2, 3, 4, 5};
+  expected_height += kInsets.height();
+  label()->SetBorder(CreateEmptyBorder(kInsets));
+  EXPECT_EQ(expected_height, label()->GetMinimumSize().height());
+}
+
 TEST_F(LabelTest, ElideBehavior) {
   base::string16 text(ASCIIToUTF16("This is example text."));
   label()->SetText(text);
diff --git a/ui/views/test/event_generator_delegate_mac.mm b/ui/views/test/event_generator_delegate_mac.mm
index dec0b10..5788e69 100644
--- a/ui/views/test/event_generator_delegate_mac.mm
+++ b/ui/views/test/event_generator_delegate_mac.mm
@@ -252,8 +252,8 @@
 
   static EventGeneratorDelegateMac* instance() { return instance_; }
 
-  IMP CurrentEventMethod() {
-    return swizzle_current_event_->GetOriginalImplementation();
+  NSEvent* OriginalCurrentEvent(id receiver, SEL selector) {
+    return swizzle_current_event_->InvokeOriginal<NSEvent*>(receiver, selector);
   }
 
   NSWindow* window() { return window_.get(); }
@@ -634,8 +634,8 @@
     return g_current_event;
 
   // Find the original implementation and invoke it.
-  IMP original = EventGeneratorDelegateMac::instance()->CurrentEventMethod();
-  return original(self, _cmd);
+  return EventGeneratorDelegateMac::instance()->OriginalCurrentEvent(self,
+                                                                     _cmd);
 }
 
 @end
diff --git a/ui/views/widget/native_widget_mac_unittest.mm b/ui/views/widget/native_widget_mac_unittest.mm
index 82df06d7..58e85500 100644
--- a/ui/views/widget/native_widget_mac_unittest.mm
+++ b/ui/views/widget/native_widget_mac_unittest.mm
@@ -1139,8 +1139,8 @@
 
   ~ScopedSwizzleWaiter() { instance_ = nullptr; }
 
-  static IMP GetMethodAndMarkCalled() {
-    return instance_->GetMethodInternal();
+  static void OriginalSetWindowStateForEnd(id receiver, SEL method) {
+    return instance_->CallMethodInternal(receiver, method);
   }
 
   void WaitForMethod() {
@@ -1158,12 +1158,12 @@
   bool method_called() const { return method_called_; }
 
  private:
-  IMP GetMethodInternal() {
+  void CallMethodInternal(id receiver, SEL selector) {
     DCHECK(!method_called_);
     method_called_ = true;
     if (run_loop_)
       run_loop_->Quit();
-    return swizzler_.GetOriginalImplementation();
+    swizzler_.InvokeOriginal<void>(receiver, selector);
   }
 
   static ScopedSwizzleWaiter* instance_;
@@ -2475,7 +2475,7 @@
 
 @implementation TestStopAnimationWaiter
 - (void)setWindowStateForEnd {
-  views::test::ScopedSwizzleWaiter::GetMethodAndMarkCalled()(self, _cmd);
+  views::test::ScopedSwizzleWaiter::OriginalSetWindowStateForEnd(self, _cmd);
 }
 @end
 
diff --git a/ui/webui/resources/css/tree.css b/ui/webui/resources/css/tree.css
index 6454b96..d0f7939c 100644
--- a/ui/webui/resources/css/tree.css
+++ b/ui/webui/resources/css/tree.css
@@ -171,3 +171,15 @@
   margin: -2px -3px -2px -8px;
   padding: 1px 1px 1px 7px;
 }
+
+@media(forced-colors) {

+  .tree-row[selected],

+  .tree-row:hover,

+  .tree-row[selected]:hover,

+  tree:focus .tree-row[selected] {

+    background-color: Highlight;

+    background-image: none;

+    color: HighlightText;

+    forced-color-adjust: none;

+  }

+}
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/TabCallbackProxy.java b/weblayer/browser/java/org/chromium/weblayer_private/TabCallbackProxy.java
index 1ece19a..238cf84 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/TabCallbackProxy.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/TabCallbackProxy.java
@@ -36,6 +36,11 @@
         mClient.visibleUrlChanged(string);
     }
 
+    @CalledByNative
+    private void onRenderProcessGone() throws RemoteException {
+        mClient.onRenderProcessGone();
+    }
+
     @NativeMethods
     interface Natives {
         long createTabCallbackProxy(TabCallbackProxy proxy, long tab);
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl
index 793f48cd..70812c9b 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl
@@ -12,4 +12,6 @@
   void visibleUrlChanged(in String url) = 0;
 
   void onNewTab(in int tabId, in int mode) = 1;
+
+  void onRenderProcessGone() = 2;
 }
diff --git a/weblayer/browser/ssl_browsertest.cc b/weblayer/browser/ssl_browsertest.cc
index 1550290..7627829 100644
--- a/weblayer/browser/ssl_browsertest.cc
+++ b/weblayer/browser/ssl_browsertest.cc
@@ -51,13 +51,9 @@
     EXPECT_FALSE(IsShowingSecurityInterstitial(shell()->tab()));
   }
 
-  void NavigateToPageWithSslError() {
-    // Now do a navigation that should result in an SSL error.
-    GURL url_with_mismatched_cert =
-        https_server_mismatched_->GetURL("/simple_page.html");
-
-    NavigateAndWaitForFailure(url_with_mismatched_cert, shell());
-
+  void NavigateToPageWithSslErrorExpectBlocked() {
+    // Do a navigation that should result in an SSL error.
+    NavigateAndWaitForFailure(bad_ssl_url(), shell());
     // First check that there *is* an interstitial.
     ASSERT_TRUE(IsShowingSecurityInterstitial(shell()->tab()));
 
@@ -69,7 +65,37 @@
     // ssl_browsertest.cc's CheckAuthenticationBrokenState() function.
   }
 
+  void NavigateToPageWithSslErrorExpectNotBlocked() {
+    NavigateAndWaitForCompletion(bad_ssl_url(), shell());
+    EXPECT_FALSE(IsShowingSecurityInterstitial(shell()->tab()));
+
+    // TODO(blundell): Check the security state once security state is available
+    // via the public WebLayer API, following the example of //chrome's
+    // ssl_browsertest.cc's CheckAuthenticationBrokenState() function.
+  }
+
+  void InteractWithBlockingPage(bool proceed) {
+    TestNavigationObserver navigation_observer(
+        proceed ? bad_ssl_url() : ok_url(),
+        TestNavigationObserver::NavigationEvent::Completion, shell());
+    ExecuteScript(shell(),
+                  "window.certificateErrorPageController." +
+                      std::string(proceed ? "proceed" : "dontProceed") + "();",
+                  false /*use_separate_isolate*/);
+    navigation_observer.Wait();
+    EXPECT_FALSE(IsShowingSSLInterstitial(shell()->tab()));
+  }
+
+  void NavigateToOtherOkPage() {
+    NavigateAndWaitForCompletion(https_server_->GetURL("/simple_page2.html"),
+                                 shell());
+    EXPECT_FALSE(IsShowingSecurityInterstitial(shell()->tab()));
+  }
+
   GURL ok_url() { return https_server_->GetURL("/simple_page.html"); }
+  GURL bad_ssl_url() {
+    return https_server_mismatched_->GetURL("/simple_page.html");
+  }
 
  protected:
   std::unique_ptr<net::EmbeddedTestServer> https_server_;
@@ -82,30 +108,45 @@
 // Tests clicking "take me back" on the interstitial page.
 IN_PROC_BROWSER_TEST_F(SSLBrowserTest, TakeMeBack) {
   NavigateToOkPage();
-  NavigateToPageWithSslError();
+  NavigateToPageWithSslErrorExpectBlocked();
 
   // Click "Take me back".
-  TestNavigationObserver navigation_observer(
-      ok_url(), TestNavigationObserver::NavigationEvent::Completion, shell());
-  ExecuteScript(shell(), "window.certificateErrorPageController.dontProceed();",
-                false /*use_separate_isolate*/);
-  navigation_observer.Wait();
-  EXPECT_FALSE(IsShowingSSLInterstitial(shell()->tab()));
+  InteractWithBlockingPage(false /*proceed*/);
 
   // Check that it's possible to navigate to a new page.
-  NavigateAndWaitForCompletion(https_server_->GetURL("/simple_page2.html"),
-                               shell());
-  EXPECT_FALSE(IsShowingSecurityInterstitial(shell()->tab()));
+  NavigateToOtherOkPage();
+
+  // Navigate to the bad SSL page again, an interstitial shows again (in
+  // contrast to what would happen had the user chosen to proceed).
+  NavigateToPageWithSslErrorExpectBlocked();
+}
+
+// Tests clicking proceed link on the interstitial page. This is a PRE_ test
+// because it also acts as setup for the test below which verifies the behavior
+// across restarts.
+IN_PROC_BROWSER_TEST_F(SSLBrowserTest, PRE_Proceed) {
+  NavigateToOkPage();
+  NavigateToPageWithSslErrorExpectBlocked();
+  InteractWithBlockingPage(true /*proceed*/);
+
+  // Go back to an OK page, then try to navigate again. The "Proceed" decision
+  // should be saved, so no interstitial is shown this time.
+  NavigateToOkPage();
+  NavigateToPageWithSslErrorExpectNotBlocked();
+}
+
+// The proceed decision is not perpetuated across WebLayer sessions, i.e.
+// WebLayer will block again when navigating to the same bad page that was
+// previously proceeded through.
+IN_PROC_BROWSER_TEST_F(SSLBrowserTest, Proceed) {
+  NavigateToPageWithSslErrorExpectBlocked();
 }
 
 // Tests navigating away from the interstitial page.
 IN_PROC_BROWSER_TEST_F(SSLBrowserTest, NavigateAway) {
   NavigateToOkPage();
-  NavigateToPageWithSslError();
-
-  NavigateAndWaitForCompletion(https_server_->GetURL("/simple_page2.html"),
-                               shell());
-  EXPECT_FALSE(IsShowingSecurityInterstitial(shell()->tab()));
+  NavigateToPageWithSslErrorExpectBlocked();
+  NavigateToOtherOkPage();
 }
 
 }  // namespace weblayer
diff --git a/weblayer/browser/tab_callback_proxy.cc b/weblayer/browser/tab_callback_proxy.cc
index a9ce68e..e83eed5 100644
--- a/weblayer/browser/tab_callback_proxy.cc
+++ b/weblayer/browser/tab_callback_proxy.cc
@@ -31,6 +31,11 @@
   Java_TabCallbackProxy_visibleUrlChanged(env, java_observer_, jstring_url);
 }
 
+void TabCallbackProxy::OnRenderProcessGone() {
+  Java_TabCallbackProxy_onRenderProcessGone(AttachCurrentThread(),
+                                            java_observer_);
+}
+
 static jlong JNI_TabCallbackProxy_CreateTabCallbackProxy(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& proxy,
diff --git a/weblayer/browser/tab_callback_proxy.h b/weblayer/browser/tab_callback_proxy.h
index b4a08e2..89bda15 100644
--- a/weblayer/browser/tab_callback_proxy.h
+++ b/weblayer/browser/tab_callback_proxy.h
@@ -25,6 +25,8 @@
   // BrowserObserver:
   void DisplayedUrlChanged(const GURL& url) override;
 
+  void OnRenderProcessGone() override;
+
  private:
   Tab* tab_;
   base::android::ScopedJavaGlobalRef<jobject> java_observer_;
diff --git a/weblayer/browser/tab_impl.cc b/weblayer/browser/tab_impl.cc
index b43c9b5..107dfe2 100644
--- a/weblayer/browser/tab_impl.cc
+++ b/weblayer/browser/tab_impl.cc
@@ -362,6 +362,12 @@
 #endif
 }
 
+void TabImpl::RenderProcessGone(base::TerminationStatus status) {
+  for (auto& observer : observers_) {
+    observer.OnRenderProcessGone();
+  }
+}
+
 void TabImpl::OnExitFullscreen() {
   // If |processing_enter_fullscreen_| is true, it means the callback is being
   // called while processing EnterFullscreenModeForTab(). WebContents doesn't
diff --git a/weblayer/browser/tab_impl.h b/weblayer/browser/tab_impl.h
index ce7c4ae..bbbf74e 100644
--- a/weblayer/browser/tab_impl.h
+++ b/weblayer/browser/tab_impl.h
@@ -125,6 +125,7 @@
   // content::WebContentsObserver:
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
+  void RenderProcessGone(base::TerminationStatus status) override;
 
   // Called from closure supplied to delegate to exit fullscreen.
   void OnExitFullscreen();
diff --git a/weblayer/public/java/org/chromium/weblayer/Tab.java b/weblayer/public/java/org/chromium/weblayer/Tab.java
index f9f5a0c..0d4c4ef5 100644
--- a/weblayer/public/java/org/chromium/weblayer/Tab.java
+++ b/weblayer/public/java/org/chromium/weblayer/Tab.java
@@ -224,6 +224,13 @@
             assert tab.getBrowser() == getBrowser();
             mNewTabCallback.onNewTab(tab, mode);
         }
+
+        @Override
+        public void onRenderProcessGone() {
+            for (TabCallback callback : mCallbacks) {
+                callback.onRenderProcessGone();
+            }
+        }
     }
 
     private static final class DownloadCallbackClientImpl extends IDownloadCallbackClient.Stub {
diff --git a/weblayer/public/java/org/chromium/weblayer/TabCallback.java b/weblayer/public/java/org/chromium/weblayer/TabCallback.java
index 045b9e3..c48ba288 100644
--- a/weblayer/public/java/org/chromium/weblayer/TabCallback.java
+++ b/weblayer/public/java/org/chromium/weblayer/TabCallback.java
@@ -18,4 +18,10 @@
      * @param url The new user-visible url.
      */
     public void onVisibleUrlChanged(@NonNull Uri url) {}
+
+    /**
+     * Triggered when the render process dies, either due to crash or killed by the system to
+     * reclaim memory.
+     */
+    public void onRenderProcessGone() {}
 }
diff --git a/weblayer/public/tab_observer.h b/weblayer/public/tab_observer.h
index 2d0cd0b5..a9f5c4a 100644
--- a/weblayer/public/tab_observer.h
+++ b/weblayer/public/tab_observer.h
@@ -14,6 +14,10 @@
   // The URL bar should be updated to |url|.
   virtual void DisplayedUrlChanged(const GURL& url) {}
 
+  // Triggered when the render process dies, either due to crash or killed by the system to
+  // reclaim memory.
+  virtual void OnRenderProcessGone() {}
+
  protected:
   virtual ~TabObserver() {}
 };
diff --git a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/TabCallbackTest.java b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/TabCallbackTest.java
index 411e266d..0d9909b 100644
--- a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/TabCallbackTest.java
+++ b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/TabCallbackTest.java
@@ -12,15 +12,18 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.weblayer.Tab;
 import org.chromium.weblayer.TabCallback;
 import org.chromium.weblayer.shell.InstrumentationActivity;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.TimeoutException;
 
 /**
  * Tests that TabCallback methods are invoked as expected.
@@ -71,14 +74,33 @@
         String startupUrl = "about:blank";
         InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(startupUrl);
 
-        Callback calllback = new Callback();
+        Callback callback = new Callback();
         TestThreadUtils.runOnUiThreadBlocking(
-                () -> { activity.getTab().registerTabCallback(calllback); });
+                () -> { activity.getTab().registerTabCallback(callback); });
 
         String url = "data:text,foo";
         mActivityTestRule.navigateAndWait(url);
 
         /* Verify that the visible URL changes to the target. */
-        calllback.visibleUrlChangedCallback.waitUntilValueObserved(url);
+        callback.visibleUrlChangedCallback.waitUntilValueObserved(url);
+    }
+
+    @Test
+    @SmallTest
+    public void testOnRenderProcessGone() throws TimeoutException {
+        InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank");
+        CallbackHelper callbackHelper = new CallbackHelper();
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Tab tab = activity.getTab();
+            TabCallback callback = new TabCallback() {
+                @Override
+                public void onRenderProcessGone() {
+                    callbackHelper.notifyCalled();
+                }
+            };
+            tab.registerTabCallback(callback);
+            tab.getNavigationController().navigate(Uri.parse("chrome://crash"));
+        });
+        callbackHelper.waitForFirst();
     }
 }