diff --git a/AUTHORS b/AUTHORS
index 30c9988..376a9b3 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -637,6 +637,7 @@
 Matthew Demarest <demarem@amazon.com>
 Matthew Robertson <matthewrobertson03@gmail.com>
 Matthew Turk <matthewturk@gmail.com>
+Matthew Webb <mwebbmwebb@gmail.com>
 Matthew Willis <appamatto@gmail.com>
 Matthias Reitinger <reimarvin@gmail.com>
 Matthieu Rigolot <matthieu.rigolot@gmail.com>
diff --git a/DEPS b/DEPS
index d56c2f3..0a6b4f0 100644
--- a/DEPS
+++ b/DEPS
@@ -175,11 +175,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': '4045cd82e75548918692b72f97eb89b125478a50',
+  'skia_revision': '6a5187a8895807f2412f6a4a1a209a859d4e93fb',
   # 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': '703b9d4881faf3dd412fafbe339a27fcccaacdcc',
+  'v8_revision': '3d1d9352dd510535d6a0feff640d8d2147658823',
   # 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.
@@ -302,7 +302,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '9d2de1d6d45f4dfd006a8d6d70ffdf5fb44aed84',
+  'dawn_revision': '7119a0278da3ae1044a0dcaaf979a2b5294a10ee',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -525,7 +525,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'c7ff69c0bd9b7d62491066f56b9c3d953a4a7f4b',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '4454d0be942d588fcea5a390fb78711c07513a6c',
       'condition': 'checkout_ios',
   },
 
@@ -856,7 +856,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'ca6b5b7a178116b8f514462b7b8c27bdd7cc4e4c',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'eb17dbb23599aadf015f4b5cee7d592a0910e6eb',
       'condition': 'checkout_linux',
   },
 
@@ -1218,7 +1218,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '6f26bce0b1c4e8ce0e13332f7c0083788def5fdf',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + '94b61ee5c25c872920f45e5581442e4bf1a5a7ea',
+    Var('chromium_git') + '/openscreen' + '@' + 'e998a6b46dddfecde30aa25642bad41a7e5d9780',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + '9e97b73e7dd2bfc07745489d728f6a36665c648f',
@@ -1465,7 +1465,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'dfae553e3a6928c9cf71121650bd4b605c359587',
+    Var('webrtc_git') + '/src.git' + '@' + 'f7065f4414856c15038f8bdbe4ef84e54056f9d6',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1540,7 +1540,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@08494419674464d45d9d0f6d8ebfddc15286d992',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@6e3984a4c68ea883e183a3e8118f14b6a315ef2f',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/base/check_unittest.cc b/base/check_unittest.cc
index 8f7095f..6cdc372b 100644
--- a/base/check_unittest.cc
+++ b/base/check_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/logging.h"
 #include "base/strings/string_piece.h"
 #include "base/test/gtest_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -181,14 +182,15 @@
 #if defined(DCHECK_IS_CONFIGURABLE)
 class ScopedDcheckSeverity {
  public:
-  ScopedDcheckSeverity(LogSeverity new_severity) : old_severity_(LOG_DCHECK) {
-    LOG_DCHECK = new_severity;
+  ScopedDcheckSeverity(logging::LogSeverity new_severity)
+      : old_severity_(logging::LOG_DCHECK) {
+    logging::LOG_DCHECK = new_severity;
   }
 
-  ~ScopedDcheckSeverity() { LOG_DCHECK = old_severity_; }
+  ~ScopedDcheckSeverity() { logging::LOG_DCHECK = old_severity_; }
 
  private:
-  LogSeverity old_severity_;
+  logging::LogSeverity old_severity_;
 };
 #endif  // defined(DCHECK_IS_CONFIGURABLE)
 
@@ -203,7 +205,7 @@
   // DCHECKs are enabled, and LOG_DCHECK is mutable, but defaults to non-fatal.
   // Set it to LOG_FATAL to get the expected behavior from the rest of this
   // test.
-  ScopedDcheckSeverity dcheck_severity(LOG_FATAL);
+  ScopedDcheckSeverity dcheck_severity(logging::LOG_FATAL);
 #endif  // defined(DCHECK_IS_CONFIGURABLE)
 
 #if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
@@ -305,16 +307,16 @@
   // Verify that DCHECKs default to non-fatal in configurable-DCHECK builds.
   // Note that we require only that DCHECK is non-fatal by default, rather
   // than requiring that it be exactly INFO, ERROR, etc level.
-  EXPECT_LT(LOG_DCHECK, LOG_FATAL);
+  EXPECT_LT(logging::LOG_DCHECK, logging::LOG_FATAL);
   DCHECK(false);
 
   // Verify that DCHECK* aren't hard-wired to crash on failure.
-  LOG_DCHECK = LOG_INFO;
+  logging::LOG_DCHECK = logging::LOG_INFO;
   DCHECK(false);
   DCHECK_EQ(1, 2);
 
   // Verify that DCHECK does crash if LOG_DCHECK is set to LOG_FATAL.
-  LOG_DCHECK = LOG_FATAL;
+  logging::LOG_DCHECK = logging::LOG_FATAL;
   EXPECT_CHECK("Check failed: false. ", DCHECK(false));
   EXPECT_CHECK("Check failed: 1 == 2 (1 vs. 2)", DCHECK_EQ(1, 2));
 }
@@ -327,20 +329,20 @@
   {
     base::test::ScopedFeatureList feature_list;
     feature_list.InitFromCommandLine("DcheckIsFatal", "");
-    EXPECT_EQ(LOG_DCHECK, LOG_FATAL);
+    EXPECT_EQ(logging::LOG_DCHECK, logging::LOG_FATAL);
   }
 
   {
     base::test::ScopedFeatureList feature_list;
     feature_list.InitFromCommandLine("", "DcheckIsFatal");
-    EXPECT_LT(LOG_DCHECK, LOG_FATAL);
+    EXPECT_LT(logging::LOG_DCHECK, logging::LOG_FATAL);
   }
 
   // The default case is last, so we leave LOG_DCHECK in the default state.
   {
     base::test::ScopedFeatureList feature_list;
     feature_list.InitFromCommandLine("", "");
-    EXPECT_LT(LOG_DCHECK, LOG_FATAL);
+    EXPECT_LT(logging::LOG_DCHECK, logging::LOG_FATAL);
   }
 }
 #endif  // defined(DCHECK_IS_CONFIGURABLE)
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index f4af27f..2dd2937 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -875,12 +875,12 @@
   # own dependencies.
   #
   # Variables
+  #   sources: List of resource files for this target.
   #   deps: Specifies the dependencies of this target. Any Android resources
   #     listed in deps will be included by libraries/apks that depend on this
   #     target.
   #   alternative_android_sdk_dep: Optional. Alternative Android system
   #     android java target to use.
-  #   resource_dirs: List of directories containing resources for this target.
   #   android_manifest: AndroidManifest.xml for this target (optional). Will be
   #     merged into apks that directly or indirectly depend on this target.
   #   android_manifest_dep: Target that generates AndroidManifest (if applicable)
@@ -918,7 +918,10 @@
       not_needed(invoker, [ "v14_skip" ])
     }
 
-    _pass_resource_files = defined(invoker.sources)
+    assert(!defined(invoker.resource_dirs) || defined(invoker.sources),
+           "resource_dirs in android_resources is deprecated. Please use " +
+               "sources=[] and list resource files instead. Details: " +
+               "https://crbug.com/1026378")
     _res_sources_path = "$target_gen_dir/${invoker.target_name}.res.sources"
 
     _resources_zip = "$target_out_dir/$target_name.resources.zip"
@@ -941,19 +944,11 @@
       _deps += [ "//third_party/android_sdk:android_sdk_java" ]
     }
 
-    if (_pass_resource_files) {
-      _resource_files = invoker.sources
-    } else {
-      _sources_build_rel = []
-
-      if (invoker.resource_dirs != []) {
-        _sources_build_rel +=
-            exec_script("//build/android/gyp/find.py",
-                        rebase_path(invoker.resource_dirs, root_build_dir),
-                        "list lines")
-      }
-      _resource_files = rebase_path(_sources_build_rel, ".", root_build_dir)
+    _resource_files = []
+    if (defined(invoker.sources)) {
+      _resource_files += invoker.sources
     }
+
     _rebased_resource_files = rebase_path(_resource_files, root_build_dir)
     write_file(_res_sources_path, _rebased_resource_files)
 
diff --git a/build/config/fuchsia/build_cmx_from_fragment.py b/build/config/fuchsia/build_cmx_from_fragment.py
new file mode 100644
index 0000000..ac7e349
--- /dev/null
+++ b/build/config/fuchsia/build_cmx_from_fragment.py
@@ -0,0 +1,49 @@
+# Copyright 2020 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.
+"""Creates a complete CMX (v1) component manifest, from a program name and
+   manifest fragment file."""
+
+import argparse
+import json
+import sys
+
+
+def BuildCmxFromFragment(output_file, fragment_file, program_binary):
+  """Reads a CMX fragment specifying e.g. features & sandbox, and a program
+     binary's filename, and writes out the full CMX.
+
+     output_file: Build-relative filename at which to write the full CMX.
+     fragment_file: Build-relative filename of the CMX fragment to read from.
+     program_binary: Package-relative filename of the program binary.
+  """
+
+  with open(output_file, 'w') as component_manifest_file:
+    component_manifest = json.load(open(fragment_file, 'r'))
+    component_manifest.update({
+        'program': {
+            'binary': program_binary
+        },
+    })
+    json.dump(component_manifest, component_manifest_file)
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+      '--cmx-fragment',
+      required=True,
+      help='Path to the CMX fragment to read from')
+  parser.add_argument(
+      '--cmx', required=True, help='Path to write the complete CMX file to')
+  parser.add_argument(
+      '--program',
+      required=True,
+      help='Package-relative path to the program binary')
+  args = parser.parse_args()
+
+  return BuildCmxFromFragment(args.cmx, args.cmx_fragment, args.program)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/build/config/fuchsia/elfinfo.py b/build/config/fuchsia/elfinfo.py
deleted file mode 100644
index ec3a57a..0000000
--- a/build/config/fuchsia/elfinfo.py
+++ /dev/null
@@ -1,423 +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.
-"""Parses ELF information without relying external tools.
-
-This file was originally copied and adapted from:
-https://fuchsia.googlesource.com/fuchsia/+/827f9fe/build/images/elfinfo.py
-"""
-
-from contextlib import contextmanager
-from collections import namedtuple
-import mmap
-import os
-import struct
-import uuid
-
-# Standard ELF constants.
-ELFMAG = '\x7fELF'
-EI_CLASS = 4
-ELFCLASS32 = 1
-ELFCLASS64 = 2
-EI_DATA = 5
-ELFDATA2LSB = 1
-ELFDATA2MSB = 2
-EM_386 = 3
-EM_ARM = 40
-EM_X86_64 = 62
-EM_AARCH64 = 183
-PT_LOAD = 1
-PT_DYNAMIC = 2
-PT_INTERP = 3
-PT_NOTE = 4
-DT_NEEDED = 1
-DT_STRTAB = 5
-DT_SONAME = 14
-NT_GNU_BUILD_ID = 3
-SHT_SYMTAB = 2
-
-
-class elf_note(namedtuple('elf_note', [
-    'name',
-    'type',
-    'desc',
-])):
-
-  # An ELF note is identified by (name_string, type_integer).
-  def ident(self):
-    return (self.name, self.type)
-
-  def is_build_id(self):
-    return self.ident() == ('GNU\0', NT_GNU_BUILD_ID)
-
-  def build_id_hex(self):
-    if self.is_build_id():
-      return ''.join(('%02x' % ord(byte)) for byte in self.desc)
-    return None
-
-  def __repr__(self):
-    return ('elf_note(%r, %#x, <%d bytes>)' % (self.name, self.type,
-                                               len(self.desc)))
-
-
-def gen_elf():
-  # { 'Struct1': (ELFCLASS32 fields, ELFCLASS64 fields),
-  #   'Struct2': fields_same_for_both, ... }
-  elf_types = {
-      'Ehdr': ([
-          ('e_ident', '16s'),
-          ('e_type', 'H'),
-          ('e_machine', 'H'),
-          ('e_version', 'I'),
-          ('e_entry', 'I'),
-          ('e_phoff', 'I'),
-          ('e_shoff', 'I'),
-          ('e_flags', 'I'),
-          ('e_ehsize', 'H'),
-          ('e_phentsize', 'H'),
-          ('e_phnum', 'H'),
-          ('e_shentsize', 'H'),
-          ('e_shnum', 'H'),
-          ('e_shstrndx', 'H'),
-      ], [
-          ('e_ident', '16s'),
-          ('e_type', 'H'),
-          ('e_machine', 'H'),
-          ('e_version', 'I'),
-          ('e_entry', 'Q'),
-          ('e_phoff', 'Q'),
-          ('e_shoff', 'Q'),
-          ('e_flags', 'I'),
-          ('e_ehsize', 'H'),
-          ('e_phentsize', 'H'),
-          ('e_phnum', 'H'),
-          ('e_shentsize', 'H'),
-          ('e_shnum', 'H'),
-          ('e_shstrndx', 'H'),
-      ]),
-      'Phdr': ([
-          ('p_type', 'I'),
-          ('p_offset', 'I'),
-          ('p_vaddr', 'I'),
-          ('p_paddr', 'I'),
-          ('p_filesz', 'I'),
-          ('p_memsz', 'I'),
-          ('p_flags', 'I'),
-          ('p_align', 'I'),
-      ], [
-          ('p_type', 'I'),
-          ('p_flags', 'I'),
-          ('p_offset', 'Q'),
-          ('p_vaddr', 'Q'),
-          ('p_paddr', 'Q'),
-          ('p_filesz', 'Q'),
-          ('p_memsz', 'Q'),
-          ('p_align', 'Q'),
-      ]),
-      'Shdr': ([
-          ('sh_name', 'L'),
-          ('sh_type', 'L'),
-          ('sh_flags', 'L'),
-          ('sh_addr', 'L'),
-          ('sh_offset', 'L'),
-          ('sh_size', 'L'),
-          ('sh_link', 'L'),
-          ('sh_info', 'L'),
-          ('sh_addralign', 'L'),
-          ('sh_entsize', 'L'),
-      ], [
-          ('sh_name', 'L'),
-          ('sh_type', 'L'),
-          ('sh_flags', 'Q'),
-          ('sh_addr', 'Q'),
-          ('sh_offset', 'Q'),
-          ('sh_size', 'Q'),
-          ('sh_link', 'L'),
-          ('sh_info', 'L'),
-          ('sh_addralign', 'Q'),
-          ('sh_entsize', 'Q'),
-      ]),
-      'Dyn': ([
-          ('d_tag', 'i'),
-          ('d_val', 'I'),
-      ], [
-          ('d_tag', 'q'),
-          ('d_val', 'Q'),
-      ]),
-      'Nhdr': [
-          ('n_namesz', 'I'),
-          ('n_descsz', 'I'),
-          ('n_type', 'I'),
-      ],
-      'dwarf2_line_header': [
-          ('unit_length', 'L'),
-          ('version', 'H'),
-          ('header_length', 'L'),
-          ('minimum_instruction_length', 'B'),
-          ('default_is_stmt', 'B'),
-          ('line_base', 'b'),
-          ('line_range', 'B'),
-          ('opcode_base', 'B'),
-      ],
-      'dwarf4_line_header': [
-          ('unit_length', 'L'),
-          ('version', 'H'),
-          ('header_length', 'L'),
-          ('minimum_instruction_length', 'B'),
-          ('maximum_operations_per_instruction', 'B'),
-          ('default_is_stmt', 'B'),
-          ('line_base', 'b'),
-          ('line_range', 'b'),
-          ('opcode_base', 'B'),
-      ],
-  }
-
-  # There is an accessor for each struct, e.g. Ehdr.
-  # Ehdr.read is a function like Struct.unpack_from.
-  # Ehdr.size is the size of the struct.
-  elf_accessor = namedtuple('elf_accessor', ['size', 'read', 'write', 'pack'])
-
-  # All the accessors for a format (class, byte-order) form one elf,
-  # e.g. use elf.Ehdr and elf.Phdr.
-  elf = namedtuple('elf', elf_types.keys())
-
-  def gen_accessors(is64, struct_byte_order):
-
-    def make_accessor(type, decoder):
-      return elf_accessor(
-          size=decoder.size,
-          read=lambda buffer, offset=0: type._make(
-              decoder.unpack_from(buffer, offset)),
-          write=lambda buffer, offset, x: decoder.pack_into(
-              buffer, offset, *x),
-          pack=lambda x: decoder.pack(*x))
-
-    for name, fields in elf_types.iteritems():
-      if isinstance(fields, tuple):
-        fields = fields[1 if is64 else 0]
-      type = namedtuple(name, [field_name for field_name, fmt in fields])
-      decoder = struct.Struct(struct_byte_order + ''.join(
-          fmt for field_name, fmt in fields))
-      yield make_accessor(type, decoder)
-
-  for elfclass, is64 in [(ELFCLASS32, False), (ELFCLASS64, True)]:
-    for elf_bo, struct_bo in [(ELFDATA2LSB, '<'), (ELFDATA2MSB, '>')]:
-      yield ((chr(elfclass), chr(elf_bo)), elf(*gen_accessors(is64, struct_bo)))
-
-
-# e.g. ELF[file[EI_CLASS], file[EI_DATA]].Ehdr.read(file).e_phnum
-ELF = dict(gen_elf())
-
-
-def get_elf_accessor(file):
-  # If it looks like an ELF file, whip out the decoder ring.
-  if file[:len(ELFMAG)] == ELFMAG:
-    return ELF[file[EI_CLASS], file[EI_DATA]]
-  return None
-
-
-def gen_phdrs(file, elf, ehdr):
-  for pos in xrange(0, ehdr.e_phnum * elf.Phdr.size, elf.Phdr.size):
-    yield elf.Phdr.read(file, ehdr.e_phoff + pos)
-
-
-def gen_shdrs(file, elf, ehdr):
-  for pos in xrange(0, ehdr.e_shnum * elf.Shdr.size, elf.Shdr.size):
-    yield elf.Shdr.read(file, ehdr.e_shoff + pos)
-
-
-cpu = namedtuple(
-    'cpu',
-    [
-        'e_machine',  # ELF e_machine int
-        'llvm',  # LLVM triple CPU component
-        'gn',  # GN target_cpu
-    ])
-
-ELF_MACHINE_TO_CPU = {
-    elf: cpu(elf, llvm, gn) for elf, llvm, gn in [
-        (EM_386, 'i386', 'x86'),
-        (EM_ARM, 'arm', 'arm'),
-        (EM_X86_64, 'x86_64', 'x64'),
-        (EM_AARCH64, 'aarch64', 'arm64'),
-    ]
-}
-
-
-@contextmanager
-def mmapper(filename):
-  """A context manager that yields (fd, file_contents) given a file name.
-This ensures that the mmap and file objects are closed at the end of the
-'with' statement."""
-  fileobj = open(filename, 'rb')
-  fd = fileobj.fileno()
-  if os.fstat(fd).st_size == 0:
-    # mmap can't handle empty files.
-    try:
-      yield fd, ''
-    finally:
-      fileobj.close()
-  else:
-    mmapobj = mmap.mmap(fd, 0, access=mmap.ACCESS_READ)
-    try:
-      yield fd, mmapobj
-    finally:
-      mmapobj.close()
-      fileobj.close()
-
-
-elf_info = namedtuple(
-    'elf_info',
-    [
-        'filename',
-        'cpu',  # cpu tuple
-        'notes',  # list of (ident, desc): selected notes
-        'build_id',  # string: lowercase hex
-        'stripped',  # bool: Has no symbols or .debug_* sections
-        'interp',  # string or None: PT_INTERP (without \0)
-        'soname',  # string or None: DT_SONAME
-        'needed',  # list of strings: DT_NEEDED
-    ])
-
-
-def get_elf_info(filename, match_notes=False):
-  file = None
-  elf = None
-  ehdr = None
-  phdrs = None
-
-  # Yields an elf_note for each note in any PT_NOTE segment.
-  def gen_notes():
-
-    def round_up_to(size):
-      return ((size + 3) / 4) * 4
-
-    for phdr in phdrs:
-      if phdr.p_type == PT_NOTE:
-        pos = phdr.p_offset
-        while pos < phdr.p_offset + phdr.p_filesz:
-          nhdr = elf.Nhdr.read(file, pos)
-          pos += elf.Nhdr.size
-          name = file[pos:pos + nhdr.n_namesz]
-          pos += round_up_to(nhdr.n_namesz)
-          desc = file[pos:pos + nhdr.n_descsz]
-          pos += round_up_to(nhdr.n_descsz)
-          yield elf_note(name, nhdr.n_type, desc)
-
-  def gen_sections():
-    shdrs = list(gen_shdrs(file, elf, ehdr))
-    if not shdrs:
-      return
-    strtab_shdr = shdrs[ehdr.e_shstrndx]
-    for shdr, i in zip(shdrs, xrange(len(shdrs))):
-      if i == 0:
-        continue
-      assert shdr.sh_name < strtab_shdr.sh_size, (
-          "%s: invalid sh_name" % filename)
-      yield (shdr, extract_C_string(strtab_shdr.sh_offset + shdr.sh_name))
-
-  # Generates '\0'-terminated strings starting at the given offset,
-  # until an empty string.
-  def gen_strings(start):
-    while True:
-      end = file.find('\0', start)
-      assert end >= start, (
-          "%s: Unterminated string at %#x" % (filename, start))
-      if start == end:
-        break
-      yield file[start:end]
-      start = end + 1
-
-  def extract_C_string(start):
-    for string in gen_strings(start):
-      return string
-    return ''
-
-  # Returns a string of hex digits (or None).
-  def get_build_id():
-    build_id = None
-    for note in gen_notes():
-      # Note that the last build_id note needs to be used due to TO-442.
-      possible_build_id = note.build_id_hex()
-      if possible_build_id:
-        build_id = possible_build_id
-    return build_id
-
-  # Returns a list of elf_note objects.
-  def get_matching_notes():
-    if isinstance(match_notes, bool):
-      if match_notes:
-        return list(gen_notes())
-      else:
-        return []
-    # If not a bool, it's an iterable of ident pairs.
-    return [note for note in gen_notes() if note.ident() in match_notes]
-
-  # Returns a string (without trailing '\0'), or None.
-  def get_interp():
-    # PT_INTERP points directly to a string in the file.
-    for interp in (phdr for phdr in phdrs if phdr.p_type == PT_INTERP):
-      interp = file[interp.p_offset:interp.p_offset + interp.p_filesz]
-      if interp[-1:] == '\0':
-        interp = interp[:-1]
-      return interp
-    return None
-
-  # Returns a set of strings.
-  def get_soname_and_needed():
-    # Each DT_NEEDED or DT_SONAME points to a string in the .dynstr table.
-    def GenDTStrings(tag):
-      return (extract_C_string(strtab_offset + dt.d_val)
-              for dt in dyn
-              if dt.d_tag == tag)
-
-    # PT_DYNAMIC points to the list of ElfNN_Dyn tags.
-    for dynamic in (phdr for phdr in phdrs if phdr.p_type == PT_DYNAMIC):
-      dyn = [
-          elf.Dyn.read(file, dynamic.p_offset + dyn_offset)
-          for dyn_offset in xrange(0, dynamic.p_filesz, elf.Dyn.size)
-      ]
-
-      # DT_STRTAB points to the string table's vaddr (.dynstr).
-      [strtab_vaddr] = [dt.d_val for dt in dyn if dt.d_tag == DT_STRTAB]
-
-      # Find the PT_LOAD containing the vaddr to compute the file offset.
-      [strtab_offset] = [
-          strtab_vaddr - phdr.p_vaddr + phdr.p_offset
-          for phdr in phdrs
-          if (phdr.p_type == PT_LOAD and phdr.p_vaddr <= strtab_vaddr and
-              strtab_vaddr - phdr.p_vaddr < phdr.p_filesz)
-      ]
-
-      soname = None
-      for soname in GenDTStrings(DT_SONAME):
-        break
-
-      return soname, set(GenDTStrings(DT_NEEDED))
-    return None, set()
-
-  def get_stripped():
-    return all(shdr.sh_type != SHT_SYMTAB and not name.startswith('.debug_')
-               for shdr, name in gen_sections())
-
-  def get_cpu():
-    return ELF_MACHINE_TO_CPU.get(ehdr.e_machine)
-
-  # Map in the whole file's contents and use it as a string.
-  with mmapper(filename) as mapped:
-    fd, file = mapped
-    elf = get_elf_accessor(file)
-    if elf is not None:
-      # ELF header leads to program headers.
-      ehdr = elf.Ehdr.read(file)
-      assert ehdr.e_phentsize == elf.Phdr.size, (
-          "%s: invalid e_phentsize" % filename)
-      phdrs = list(gen_phdrs(file, elf, ehdr))
-      return elf_info(filename, get_cpu(), get_matching_notes(), get_build_id(),
-                      get_stripped(), get_interp(), *get_soname_and_needed())
-
-  return None
-
-
-# Module public API.
-__all__ = ['cpu', 'elf_info', 'elf_note', 'get_elf_accessor', 'get_elf_info']
diff --git a/build/config/fuchsia/generate_runner_scripts.gni b/build/config/fuchsia/generate_runner_scripts.gni
index 3d45f12..1afbfa4 100644
--- a/build/config/fuchsia/generate_runner_scripts.gni
+++ b/build/config/fuchsia/generate_runner_scripts.gni
@@ -81,12 +81,17 @@
     }
     wrapper_script = generated_run_pkg_script_path
 
-    deps = [ invoker.package ]
-    if (defined(invoker.deps)) {
-      deps += invoker.deps
-    }
+    data_deps = [
+      invoker.package,
 
-    data_deps = [ invoker.package ]
+      # Runner scripts require access to "ids.txt" for symbolization, and to
+      # the "package" from which to get the name & version to deploy, which
+      # are outputs of the archive manifest generation action.
+      invoker.package + "__archive-manifest",
+    ]
+    if (defined(invoker.data_deps)) {
+      data_deps += invoker.data_deps
+    }
 
     # Declares the files that are needed for test execution on the
     # swarming test client.
@@ -133,7 +138,10 @@
         package_dep_target = package_dep[0]
         package_dep_name = package_dep[1]
 
-        deps += [ package_dep_target ]
+        data_deps += [
+          package_dep_target,
+          package_dep_target + "__archive-manifest",
+        ]
         package_dep_path = rebase_path(
                 get_label_info(package_dep_target, "target_gen_dir") + "/" +
                     package_dep_name + "/" + package_dep_name + ".far",
@@ -178,9 +186,9 @@
     executable = rebase_path("//build/fuchsia/deploy_to_amber_repo.py")
     wrapper_script = generated_install_pkg_script_path
 
-    deps = [ invoker.package ]
-    if (defined(invoker.deps)) {
-      deps += invoker.deps
+    data_deps = [ invoker.package ]
+    if (defined(invoker.data_deps)) {
+      data_deps += invoker.data_deps
     }
 
     # Build a list of all packages to install, and pass the list to the runner
@@ -191,7 +199,7 @@
         package_dep_target = package_dep[0]
         package_dep_name = package_dep[1]
 
-        deps += [ package_dep_target ]
+        data_deps += [ package_dep_target ]
         package_dep_path = rebase_path(
                 get_label_info(package_dep_target, "target_gen_dir") + "/" +
                     package_dep_name + "/" + package_dep_name + ".far",
diff --git a/build/config/fuchsia/package.gni b/build/config/fuchsia/package.gni
index 8fe34fe0..e7bca25 100644
--- a/build/config/fuchsia/package.gni
+++ b/build/config/fuchsia/package.gni
@@ -2,8 +2,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/sysroot.gni")
+import("//third_party/fuchsia-sdk/sdk/build/component.gni")
+import("//third_party/fuchsia-sdk/sdk/build/package.gni")
 
+# DEPRECATED: Use the Fuchsia SDK's fuchsia_component() and fuchsia_package()
+# templates directly, in new code.
+#
 # Creates a Fuchsia .far package file containing a Fuchsia component.
 #
 # Parameters are:
@@ -28,210 +32,107 @@
 #
 # TODO(https://crbug.com/1050703): Migrate consumers to GN SDK equivalents.
 template("cr_fuchsia_package") {
-  pkg = {
-    forward_variables_from(invoker, "*")
+  assert(defined(invoker.binary))
 
-    if (defined(package_name_override)) {
-      package_name = package_name_override
-    } else {
-      package_name = invoker.target_name
-    }
+  if (defined(invoker.package_name_override)) {
+    _package_name = invoker.package_name_override
+  } else {
+    _package_name = invoker.target_name
+  }
 
-    if (defined(archive_name_override)) {
-      archive_filename = archive_name_override
-    } else {
-      archive_filename = package_name
-    }
+  _package_contents = [ invoker.binary ]
+  if (defined(invoker.deps)) {
+    _package_contents += invoker.deps
+  }
 
-    if (!defined(manifest)) {
+  _component_cmx_target = target_name + "__cr-component-cmx"
+  _component_target = target_name + "__cr-component"
+  _package_components = [ ":${_component_target}" ]
+  _component_manifest = "${target_gen_dir}/${target_name}.cmx"
+
+  # Process the CMX fragment in |manifest| to get a full manifest.
+  action(_component_cmx_target) {
+    forward_variables_from(invoker, [ "testonly" ])
+
+    if (!defined(invoker.manifest)) {
       assert(testonly == true)
 
       # TODO(1019938): switch the default to tests.cmx which doesn't request
       # the deprecated-ambient-replace-as-executable feature.
-      manifest = "//build/config/fuchsia/tests-with-exec.cmx"
+      _manifest_fragment = "//build/config/fuchsia/tests-with-exec.cmx"
+    } else {
+      _manifest_fragment = invoker.manifest
+    }
+
+    script = "//build/config/fuchsia/build_cmx_from_fragment.py"
+    inputs = [ _manifest_fragment ]
+    outputs = [ _component_manifest ]
+
+    args = [
+      "--cmx-fragment",
+      rebase_path(_manifest_fragment),
+      "--cmx",
+      rebase_path(_component_manifest),
+      "--program",
+      get_label_info(invoker.binary, "name"),
+    ]
+  }
+
+  # Declare the primary component for this package.
+  fuchsia_component(_component_target) {
+    forward_variables_from(invoker, [ "testonly" ])
+
+    deps = [ ":${_component_cmx_target}" ]
+    manifest = _component_manifest
+
+    if (defined(invoker.component_name_override)) {
+      manifest_output_name = "${invoker.component_name_override}"
+    } else {
+      manifest_output_name = "${_package_name}"
+    }
+
+    data_deps = _package_contents
+  }
+
+  # Bundle manifests providing additional entrypoints into the package.
+  if (defined(invoker.additional_manifests)) {
+    foreach(filename, invoker.additional_manifests) {
+      _additional_component_target = target_name + "_" + filename
+      _package_components += [ ":${_additional_component_target}" ]
+      fuchsia_component(_additional_component_target) {
+        forward_variables_from(invoker, [ "testonly" ])
+        data_deps = _package_contents
+        manifest = filename
+      }
     }
   }
-  assert(defined(pkg.binary))
 
-  _pm_tool_path = "//third_party/fuchsia-sdk/sdk/tools/pm"
-
-  _pkg_out_dir = "${target_gen_dir}/${pkg.archive_filename}"
-  _runtime_deps_file = "$_pkg_out_dir/${pkg.archive_filename}.runtime_deps"
-  _archive_manifest = "$_pkg_out_dir/${pkg.archive_filename}.archive_manifest"
-  _build_ids_file = "$_pkg_out_dir/ids.txt"
-  _meta_far_file = "$_pkg_out_dir/meta.far"
-  _combined_far_file = "$_pkg_out_dir/${pkg.package_name}-0.far"
-  _final_far_file = "$_pkg_out_dir/${pkg.archive_filename}.far"
-  _package_info_path = "$_pkg_out_dir/package"
-
-  if (defined(pkg.component_name_override)) {
-    _generated_cmx = "$_pkg_out_dir/${pkg.component_name_override}.cmx"
+  # Generate a Fuchsia ARchive (FAR) of the requested name.
+  if (defined(invoker.archive_name_override)) {
+    _archive_name = invoker.archive_name_override
   } else {
-    _generated_cmx = "$_pkg_out_dir/${pkg.package_name}.cmx"
+    _archive_name = _package_name
   }
 
-  _write_manifest_target = "${pkg.package_name}__write_manifest"
-  _package_target = "${pkg.package_name}__pkg"
-  _bundle_target = "${pkg.package_name}__bundle"
+  if (_archive_name != _package_name) {
+    _archive_target = target_name + "__cr-archive"
 
-  # Generates a manifest file based on the GN runtime deps
-  # suitable for "pm" tool consumption.
-  action(_write_manifest_target) {
-    _depfile = "${target_gen_dir}/${target_name}_stamp.d"
-
-    forward_variables_from(invoker,
-                           [
-                             "data",
-                             "deps",
-                             "testonly",
-                           ])
-
-    script = "//build/config/fuchsia/prepare_package_inputs.py"
-
-    inputs = [
-      _runtime_deps_file,
-      pkg.manifest,
-    ]
-
-    outputs = [
-      _archive_manifest,
-      _build_ids_file,
-      _generated_cmx,
-    ]
-
-    if (!defined(deps)) {
-      deps = []
+    copy(target_name) {
+      deps = [ ":${_archive_target}" ]
+      _pkg_out_dir = "${target_gen_dir}/${_package_name}"
+      sources = [ "${_pkg_out_dir}/${_package_name}.far" ]
+      outputs = [ "${_pkg_out_dir}/${_archive_name}.far" ]
     }
-    deps += [ pkg.binary ]
-    data_deps = deps
-
-    # Use a depfile to trigger package rebuilds if any of the files (static
-    # assets, shared libraries, etc.) included by the package have changed.
-    depfile = _depfile
-
-    args = [
-      "--root-dir",
-      rebase_path("//", root_build_dir),
-      "--out-dir",
-      rebase_path(root_out_dir, root_build_dir),
-      "--app-name",
-      pkg.package_name,
-      "--app-filename",
-      get_label_info(pkg.binary, "name"),
-      "--manifest-input-path",
-      rebase_path(pkg.manifest, root_build_dir),
-      "--runtime-deps-file",
-      rebase_path(_runtime_deps_file, root_build_dir),
-      "--depfile-path",
-      rebase_path(_depfile, root_build_dir),
-      "--package-manifest-path",
-      rebase_path(_archive_manifest, root_build_dir),
-      "--component-manifest-path",
-      rebase_path(_generated_cmx, root_build_dir),
-      "--build-ids-file",
-      rebase_path(_build_ids_file, root_build_dir),
-    ]
-
-    if (defined(pkg.excluded_files)) {
-      foreach(filename, pkg.excluded_files) {
-        args += [
-          "--exclude-file",
-          filename,
-        ]
-      }
-    }
-
-    if (defined(pkg.additional_manifests)) {
-      foreach(filename, pkg.additional_manifests) {
-        args += [
-          "--additional-manifest",
-          rebase_path(filename),
-        ]
-      }
-    }
-
-    write_runtime_deps = _runtime_deps_file
+  } else {
+    _archive_target = target_name
   }
 
-  # Creates a signed Fuchsia metadata package.
-  action(_package_target) {
+  fuchsia_package(_archive_target) {
     forward_variables_from(invoker, [ "testonly" ])
-
-    script = "//build/gn_run_binary.py"
-
-    deps = [ ":$_write_manifest_target" ]
-
-    inputs = [
-      # Depend on the SDK hash, to ensure rebuild if the SDK tools change.
-      "//third_party/fuchsia-sdk/sdk/.hash",
-    ]
-
-    if (defined(pkg.additional_manifests)) {
-      inputs += pkg.additional_manifests
+    package_name = _package_name
+    if (defined(invoker.excluded_files)) {
+      excluded_files = invoker.excluded_files
     }
-
-    outputs = [ _meta_far_file ]
-
-    args = [
-      rebase_path(_pm_tool_path, root_build_dir),
-      "-o",
-      rebase_path(_pkg_out_dir, root_build_dir),
-      "-m",
-      rebase_path(_archive_manifest, root_build_dir),
-      "build",
-    ]
-  }
-
-  # Creates a package containing the metadata archive and blob data.
-  action(_bundle_target) {
-    forward_variables_from(invoker, [ "testonly" ])
-
-    script = "//build/gn_run_binary.py"
-
-    deps = [
-      ":$_package_target",
-      ":$_write_manifest_target",
-    ]
-
-    inputs = [
-      # Depend on the SDK hash, to ensure rebuild if the SDK tools change.
-      "//third_party/fuchsia-sdk/sdk/.hash",
-      _meta_far_file,
-      _archive_manifest,
-    ]
-
-    outputs = [ _combined_far_file ]
-
-    args = [
-      rebase_path(_pm_tool_path, root_build_dir),
-      "-o",
-      rebase_path(_pkg_out_dir, root_build_dir),
-      "-m",
-      rebase_path(_archive_manifest, root_build_dir),
-      "archive",
-    ]
-  }
-
-  # Copies the archive to a well-known path.
-  # TODO(kmarshall): Use a 'pm' output flag to write directly to the desired
-  # file path instead.
-  copy(target_name) {
-    forward_variables_from(invoker, [ "testonly" ])
-
-    # Allows dependent targets to make use of "ids.txt".
-    public_deps = [ ":$_write_manifest_target" ]
-
-    deps = [ ":$_bundle_target" ]
-
-    data = [
-      _final_far_file,
-
-      # Files specified here so that they can be read by isolated testbots.
-      _package_info_path,
-      _build_ids_file,
-    ]
-
-    sources = [ _combined_far_file ]
-    outputs = [ _final_far_file ]
+    deps = _package_components
   }
 }
diff --git a/build/config/fuchsia/prepare_package_inputs.py b/build/config/fuchsia/prepare_package_inputs.py
deleted file mode 100644
index 741dd3e3..0000000
--- a/build/config/fuchsia/prepare_package_inputs.py
+++ /dev/null
@@ -1,201 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Creates a archive manifest used for Fuchsia package generation."""
-
-import argparse
-import elfinfo
-import json
-import os
-import re
-import subprocess
-import sys
-import tempfile
-
-
-def MakePackagePath(file_path, roots):
-  """Computes a path for |file_path| that is relative to one of the directory
-  paths in |roots|.
-
-  file_path: The file path to relativize.
-  roots: A list of directory paths which may serve as a relative root
-         for |file_path|.
-
-  Examples:
-
-  >>> MakePackagePath('/foo/bar.txt', ['/foo/'])
-  'bar.txt'
-
-  >>> MakePackagePath('/foo/dir/bar.txt', ['/foo/'])
-  'dir/bar.txt'
-
-  >>> MakePackagePath('/foo/out/Debug/bar.exe', ['/foo/', '/foo/out/Debug/'])
-  'bar.exe'
-  """
-
-  # Prevents greedily matching against a shallow path when a deeper, better
-  # matching path exists.
-  roots.sort(key=len, reverse=True)
-
-  for next_root in roots:
-    if not next_root.endswith(os.sep):
-      next_root += os.sep
-
-    if file_path.startswith(next_root):
-      relative_path = file_path[len(next_root):]
-
-      return relative_path
-
-  return file_path
-
-
-def _GetStrippedPath(bin_path):
-  """Finds the stripped version of the binary |bin_path| in the build
-  output directory."""
-
-  return bin_path.replace('lib.unstripped/', 'lib/').replace(
-      'exe.unstripped/', '')
-
-
-def _IsBinary(path):
-  """Checks if the file at |path| is an ELF executable by inspecting its FourCC
-  header."""
-
-  with open(path, 'rb') as f:
-    file_tag = f.read(4)
-  return file_tag == '\x7fELF'
-
-
-def _WriteBuildIdsTxt(binary_paths, ids_txt_path):
-  """Writes an index text file that maps build IDs to the paths of unstripped
-  binaries."""
-
-  with open(ids_txt_path, 'w') as ids_file:
-    for binary_path in binary_paths:
-      # Paths to the unstripped executables listed in "ids.txt" are specified
-      # as relative paths to that file.
-      relative_path = os.path.relpath(
-          os.path.abspath(binary_path),
-          os.path.dirname(os.path.abspath(ids_txt_path)))
-
-      info = elfinfo.get_elf_info(_GetStrippedPath(binary_path))
-      ids_file.write(info.build_id + ' ' + relative_path + '\n')
-
-
-def BuildManifest(args):
-  binaries = []
-  with open(args.package_manifest_path, 'w') as package_manifest, \
-       open(args.depfile_path, 'w') as depfile:
-    # Process the runtime deps file for file paths, recursively walking
-    # directories as needed.
-    # MakePackagePath() may relativize to either the source root or output
-    # directory.
-    # runtime_deps may contain duplicate paths, so use a set for
-    # de-duplication.
-    expanded_files = set()
-    for next_path in open(args.runtime_deps_file, 'r'):
-      next_path = next_path.strip()
-      if os.path.isdir(next_path):
-        for root, _, files in os.walk(next_path):
-          for current_file in files:
-            if current_file.startswith('.'):
-              continue
-            expanded_files.add(
-                os.path.join(root, current_file))
-      else:
-        expanded_files.add(next_path)
-
-    # Format and write out the manifest contents.
-    gen_dir = os.path.normpath(os.path.join(args.out_dir, "gen"))
-    app_found = False
-    excluded_files_set = set(args.exclude_file)
-    for current_file in expanded_files:
-      if _IsBinary(current_file):
-        binaries.append(current_file)
-        current_file = _GetStrippedPath(current_file)
-
-      in_package_path = MakePackagePath(current_file,
-                                        [gen_dir, args.root_dir, args.out_dir])
-      if in_package_path == args.app_filename:
-        app_found = True
-
-      if in_package_path in excluded_files_set:
-        excluded_files_set.remove(in_package_path)
-        continue
-
-      package_manifest.write('%s=%s\n' % (in_package_path, current_file))
-
-    if len(excluded_files_set) > 0:
-      raise Exception('Some files were excluded with --exclude-file, but '
-                      'not found in the deps list: %s' %
-                          ', '.join(excluded_files_set));
-
-    if not app_found:
-      raise Exception('Could not locate executable inside runtime_deps.')
-
-    # Write meta/package manifest file.
-    with open(os.path.join(os.path.dirname(args.package_manifest_path),
-                           'package'), 'w') as package_json:
-      json.dump({'version': '0', 'name': args.app_name}, package_json)
-      package_manifest.write('meta/package=%s\n' % os.path.relpath(
-          package_json.name, args.out_dir))
-
-    # Write component manifest file.
-    with open(args.component_manifest_path, 'w') as component_manifest_file:
-      component_manifest = json.load(open(args.manifest_input_path, 'r'))
-      component_manifest.update({
-          'program': { 'binary': args.app_filename },
-      })
-      json.dump(component_manifest, component_manifest_file)
-
-      package_manifest.write(
-          'meta/%s=%s\n' % (os.path.basename(component_manifest_file.name),
-                            os.path.relpath(args.component_manifest_path,
-                                            args.out_dir)))
-
-    for component_manifest in args.additional_manifest:
-      package_manifest.write(
-          'meta/%s=%s\n' % (os.path.basename(component_manifest),
-                            os.path.relpath(component_manifest, args.out_dir)))
-
-    depfile.write(
-        "%s: %s" % (os.path.relpath(args.package_manifest_path, args.out_dir),
-                    " ".join([os.path.relpath(f, args.out_dir)
-                              for f in expanded_files])))
-
-    _WriteBuildIdsTxt(binaries, args.build_ids_file)
-
-  return 0
-
-
-def main():
-  parser = argparse.ArgumentParser()
-  parser.add_argument('--root-dir', required=True, help='Build root directory')
-  parser.add_argument('--out-dir', required=True, help='Build output directory')
-  parser.add_argument('--app-name', required=True, help='Package name')
-  parser.add_argument('--app-filename', required=True,
-      help='Path to the main application binary relative to the output dir.')
-  parser.add_argument('--manifest-input-path', required=True,
-      help='Path to the manifest file relative to the output dir.')
-  parser.add_argument('--runtime-deps-file', required=True,
-      help='File with the list of runtime dependencies.')
-  parser.add_argument('--depfile-path', required=True,
-      help='Path to write GN deps file.')
-  parser.add_argument('--exclude-file', action='append', default=[],
-      help='Package-relative file path to exclude from the package.')
-  parser.add_argument('--additional-manifest', action='append', default=[],
-      help='Additional component manifest file to include in the package.')
-  parser.add_argument('--package-manifest-path', required=True,
-                      help='Package manifest (file listing) output path.')
-  parser.add_argument('--component-manifest-path', required=True,
-                      help='Component manifest (.cmx) output path.')
-  parser.add_argument('--build-ids-file', required=True,
-                      help='Debug symbol index path.')
-
-  args = parser.parse_args()
-
-  return BuildManifest(args)
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/build/config/fuchsia/symbol_archive.gni b/build/config/fuchsia/symbol_archive.gni
index 28fa248..9dcb53c 100644
--- a/build/config/fuchsia/symbol_archive.gni
+++ b/build/config/fuchsia/symbol_archive.gni
@@ -8,6 +8,7 @@
 # ".build_ids" convention used by the symbolizer and GNU GDB.
 #
 # Parameters:
+#   deps: Must all be cr_fuchsia_package() or fuchsia_package() targets.
 #   ids_txt: The "ids.txt" file which lists the relative paths to unstripped
 #            executables and libraries, along with their build IDs.
 #   archive_name: The path to the compressed tarball that will be generated.
@@ -24,7 +25,16 @@
 
     outputs = [ _build_ids ]
 
-    deps = invoker.deps
+    # For each package in |deps| it is necessary to additionally depend upon
+    # the corresponding archive-manifest target, which is what creates the
+    # ids.txt file.
+    deps = []
+    foreach(package, invoker.deps) {
+      deps += [
+        package,
+        package + "__archive-manifest",
+      ]
+    }
 
     args = [
       rebase_path(_ids_txt),
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 16bcf906..6c224f8 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1628,7 +1628,7 @@
     "//components/minidump_uploader:minidump_uploader_java",
     "//components/paint_preview/player/android:player_java_test_support",
     "//content/public/test/android:content_java_test_support",
-    "//ui/android:ui_javatests",
+    "//ui/android:clipboard_java_test_support",
   ]
 }
 
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index c454ecd..0096116 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -424,6 +424,7 @@
   "java/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationEventObserver.java",
   "java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java",
   "java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java",
+  "java/src/org/chromium/chrome/browser/customtabs/CustomTabOrientationController.java",
   "java/src/org/chromium/chrome/browser/customtabs/CustomTabSessionHandler.java",
   "java/src/org/chromium/chrome/browser/customtabs/CustomTabStatusBarColorProvider.java",
   "java/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicy.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index 6ad5bbb..a20cf34 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -18,7 +18,6 @@
   "javatests/src/org/chromium/chrome/browser/FocusedEditableTextFieldZoomTest.java",
   "javatests/src/org/chromium/chrome/browser/HTTPSTabsOpenedFromExternalAppTest.java",
   "javatests/src/org/chromium/chrome/browser/InstalledAppTest.java",
-  "javatests/src/org/chromium/chrome/browser/InstantStartTest.java",
   "javatests/src/org/chromium/chrome/browser/IntentHandlerTest.java",
   "javatests/src/org/chromium/chrome/browser/JavaScriptEvalChromeTest.java",
   "javatests/src/org/chromium/chrome/browser/LauncherShortcutTest.java",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataNativeDelegate.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataNativeDelegate.java
index 0ff520f..2e7b3ed 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataNativeDelegate.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataNativeDelegate.java
@@ -33,19 +33,10 @@
     @Override
     public void onContactInfoChanged(@Nullable AutofillContact contact) {
         if (mNativeAssistantCollectUserDataDelegate != 0) {
-            String name = null;
-            String phone = null;
-            String email = null;
-
-            if (contact != null) {
-                name = contact.getPayerName();
-                phone = contact.getPayerPhone();
-                email = contact.getPayerEmail();
-            }
-
             AssistantCollectUserDataNativeDelegateJni.get().onContactInfoChanged(
                     mNativeAssistantCollectUserDataDelegate,
-                    AssistantCollectUserDataNativeDelegate.this, name, phone, email);
+                    AssistantCollectUserDataNativeDelegate.this,
+                    contact != null ? contact.getProfile() : null);
         }
     }
 
@@ -186,8 +177,8 @@
     @NativeMethods
     interface Natives {
         void onContactInfoChanged(long nativeAssistantCollectUserDataDelegate,
-                AssistantCollectUserDataNativeDelegate caller, @Nullable String payerName,
-                @Nullable String payerPhone, @Nullable String payerEmail);
+                AssistantCollectUserDataNativeDelegate caller,
+                @Nullable PersonalDataManager.AutofillProfile contactProfile);
         void onShippingAddressChanged(long nativeAssistantCollectUserDataDelegate,
                 AssistantCollectUserDataNativeDelegate caller,
                 @Nullable PersonalDataManager.AutofillProfile address);
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPersonalDataManagerTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPersonalDataManagerTest.java
index fde7a1e5..e73dc3f 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPersonalDataManagerTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPersonalDataManagerTest.java
@@ -10,6 +10,7 @@
 import static android.support.test.espresso.action.ViewActions.click;
 import static android.support.test.espresso.action.ViewActions.scrollTo;
 import static android.support.test.espresso.action.ViewActions.typeText;
+import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
 import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
 import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
@@ -170,6 +171,68 @@
     }
 
     /**
+     * Add a contact with Autofill Assistant UI, then edit the profile multiple times (see
+     * b/153139772).
+     */
+    @Test
+    @MediumTest
+    public void testCreateAndEditProfileMultipleTimes() throws Exception {
+        ArrayList<ActionProto> list = new ArrayList<>();
+        list.add(
+                (ActionProto) ActionProto.newBuilder()
+                        .setCollectUserData(
+                                CollectUserDataProto.newBuilder()
+                                        .setContactDetails(ContactDetailsProto.newBuilder()
+                                                                   .setContactDetailsName("contact")
+                                                                   .setRequestPayerName(true)
+                                                                   .setRequestPayerEmail(true)
+                                                                   .setRequestPayerPhone(false))
+                                        .setRequestTermsAndConditions(false))
+                        .build());
+        AutofillAssistantTestScript script = new AutofillAssistantTestScript(
+                (SupportedScriptProto) SupportedScriptProto.newBuilder()
+                        .setPath("form_target_website.html")
+                        .setPresentation(PresentationProto.newBuilder().setAutostart(true).setChip(
+                                ChipProto.newBuilder().setText("Address")))
+                        .build(),
+                list);
+        runScript(script);
+
+        waitUntilViewMatchesCondition(
+                allOf(withId(R.id.section_title_add_button_label), withText("Add contact info")),
+                isCompletelyDisplayed());
+        onView(allOf(withId(R.id.section_title_add_button_label), withText("Add contact info")))
+                .perform(click());
+        waitUntilViewMatchesCondition(
+                withContentDescription("Name*"), allOf(isDisplayed(), isEnabled()));
+        onView(withContentDescription("Name*")).perform(typeText("John Doe"));
+        waitUntilViewMatchesCondition(
+                withContentDescription("Email*"), allOf(isDisplayed(), isEnabled()));
+        onView(withContentDescription("Email*")).perform(typeText("doe@google.com"));
+        onView(withId(org.chromium.chrome.R.id.editor_dialog_done_button)).perform(click());
+        waitUntilViewMatchesCondition(withText("Continue"), isEnabled());
+
+        // First edit: no changes.
+        onView(withText("Contact info")).perform(click());
+        onView(withContentDescription("Edit contact info")).perform(click());
+        waitUntilViewMatchesCondition(
+                withContentDescription("Name*"), allOf(isDisplayed(), isEnabled()));
+        onView(withId(org.chromium.chrome.R.id.editor_dialog_done_button)).perform(click());
+
+        // Second edit: change name from John Doe to Jane Doe.
+        onView(withContentDescription("Edit contact info")).perform(click());
+        waitUntilViewMatchesCondition(
+                withContentDescription("Name*"), allOf(isDisplayed(), isEnabled()));
+        onView(withContentDescription("Name*")).perform(clearText(), typeText("Jane Doe"));
+        onView(withId(org.chromium.chrome.R.id.editor_dialog_done_button)).perform(click());
+
+        // There used to be a bug where consecutive edits of the same profile would create a
+        // duplicate profile, which would break the following checks.
+        onView(withText(containsString("John Doe"))).check(doesNotExist());
+        onView(withText(containsString("Jane Doe"))).check(matches(isDisplayed()));
+    }
+
+    /**
      * Catch the insert of a profile added outside of the Autofill Assistant, e.g. with the Chrome
      * settings UI, and fill it into the form.
      */
diff --git a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceLayout.java b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceLayout.java
index 5f90465..cc21289 100644
--- a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceLayout.java
+++ b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceLayout.java
@@ -422,7 +422,7 @@
     private void reportAnimationPerf(boolean isShrinking) {
         int frameRendered = mFrameCount - mStartFrame;
         long elapsedMs = SystemClock.elapsedRealtime() - mStartTime;
-        long lastDirty = mTabListDelegate.getLastDirtyTimeForTesting();
+        long lastDirty = mTabListDelegate.getLastDirtyTime();
         int dirtySpan = (int) (lastDirty - mStartTime);
         float fps = 1000.f * frameRendered / elapsedMs;
         String message = String.format(Locale.US,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/InstantStartTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
similarity index 96%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/InstantStartTest.java
rename to chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
index e1b9524..bb335f8f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/InstantStartTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser;
+package org.chromium.chrome.features.start_surface;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -40,8 +40,6 @@
 import org.chromium.chrome.browser.tasks.pseudotab.TabAttributeCache;
 import org.chromium.chrome.browser.tasks.tab_management.TabManagementDelegate;
 import org.chromium.chrome.browser.tasks.tab_management.TabManagementModuleProvider;
-import org.chromium.chrome.features.start_surface.StartSurface;
-import org.chromium.chrome.features.start_surface.StartSurfaceLayout;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
@@ -153,8 +151,15 @@
      */
     @Test
     @SmallTest
-    @CommandLineFlags.Add(ChromeSwitches.DISABLE_NATIVE_INITIALIZATION)
+    // clang-format off
+    @Features.EnableFeatures({ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID + "<Study"})
+    @CommandLineFlags.Add({ChromeSwitches.DISABLE_NATIVE_INITIALIZATION,
+            "force-fieldtrials=Study/Group",
+            "force-fieldtrial-params=Study.Group:allow_to_refetch/true/thumbnail_aspect_ratio/2.0"})
     public void fetchThumbnailsPreNativeTest() {
+        // clang-format on
+        Assert.assertTrue(TabContentManager.ALLOW_TO_REFETCH_TAB_THUMBNAIL_VARIATION.getValue());
+
         int tabId = 0;
         mThumbnailFetchCount = 0;
         Callback<Bitmap> thumbnailFetchListener = (bitmap) -> {
diff --git a/chrome/android/features/start_surface/internal/javatests/start_surface_test_java_sources.gni b/chrome/android/features/start_surface/internal/javatests/start_surface_test_java_sources.gni
index df6c744..92bb05ab 100644
--- a/chrome/android/features/start_surface/internal/javatests/start_surface_test_java_sources.gni
+++ b/chrome/android/features/start_surface/internal/javatests/start_surface_test_java_sources.gni
@@ -5,6 +5,7 @@
 start_surface_test_java_sources = [
   "//chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/BottomBarViewBinderTest.java",
   "//chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/ExploreSurfaceViewBinderTest.java",
+  "//chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java",
   "//chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/SecondaryTasksSurfaceViewBinderTest.java",
   "//chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutPerfTest.java",
   "//chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/SingleTabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/SingleTabSwitcherCoordinator.java
index e950ddf..09b44cd 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/SingleTabSwitcherCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/SingleTabSwitcherCoordinator.java
@@ -51,7 +51,7 @@
             }
 
             @Override
-            public long getLastDirtyTimeForTesting() {
+            public long getLastDirtyTime() {
                 assert false : "should not reach here";
                 return 0;
             }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
index 1fefcb7..f7ad37b 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
@@ -388,8 +388,8 @@
         return mRecyclerView.getResourceId();
     }
 
-    long getLastDirtyTimeForTesting() {
-        return mRecyclerView.getLastDirtyTimeForTesting();
+    long getLastDirtyTime() {
+        return mRecyclerView.getLastDirtyTime();
     }
 
     /**
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
index 4517d87..890e283 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
@@ -266,7 +266,7 @@
         return mResourceId;
     }
 
-    long getLastDirtyTimeForTesting() {
+    long getLastDirtyTime() {
         return mLastDirtyTime;
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcher.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcher.java
index a0967892..f51f27ff 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcher.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcher.java
@@ -142,7 +142,7 @@
          * @return The timestamp of last dirty event of {@link ViewResourceAdapter} of
          * {@link TabListRecyclerView}.
          */
-        long getLastDirtyTimeForTesting();
+        long getLastDirtyTime();
 
         /**
          * Before calling {@link Controller#showOverview} to start showing the
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
index 0bad646..13654c9 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
@@ -308,8 +308,8 @@
     }
 
     @Override
-    public long getLastDirtyTimeForTesting() {
-        return mTabListCoordinator.getLastDirtyTimeForTesting();
+    public long getLastDirtyTime() {
+        return mTabListCoordinator.getLastDirtyTime();
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/TwaSplashController.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/TwaSplashController.java
index 449d7f0..63238be 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/TwaSplashController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/TwaSplashController.java
@@ -13,7 +13,6 @@
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.Matrix;
-import android.os.Build;
 import android.os.Bundle;
 import android.view.View;
 import android.view.ViewGroup;
@@ -25,14 +24,14 @@
 
 import org.chromium.base.IntentUtils;
 import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider;
+import org.chromium.chrome.browser.customtabs.BaseCustomTabActivity;
+import org.chromium.chrome.browser.customtabs.CustomTabOrientationController;
 import org.chromium.chrome.browser.customtabs.TranslucentCustomTabActivity;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.InflationObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.webapps.SplashController;
 import org.chromium.chrome.browser.webapps.SplashDelegate;
-import org.chromium.chrome.browser.webapps.SplashscreenObserver;
-import org.chromium.content_public.browser.ScreenOrientationProvider;
 import org.chromium.ui.base.ActivityWindowAndroid;
 import org.chromium.ui.util.ColorUtils;
 
@@ -64,9 +63,7 @@
  * gc-ed when it finishes its job (to that end, it removes all observers it has set).
  * If these lifecycle assumptions change, consider whether @ActivityScope needs to be added.
  */
-public class TwaSplashController
-        implements InflationObserver, SplashDelegate, SplashscreenObserver {
-
+public class TwaSplashController implements InflationObserver, SplashDelegate {
     // TODO(pshmakov): move this to AndroidX.
     private static final String KEY_SHOWN_IN_CLIENT =
             "androidx.browser.trusted.KEY_SPLASH_SCREEN_SHOWN_IN_CLIENT";
@@ -75,7 +72,6 @@
     private final Activity mActivity;
     private final ActivityWindowAndroid mActivityWindowAndroid;
     private final ActivityLifecycleDispatcher mLifecycleDispatcher;
-    private final ScreenOrientationProvider mScreenOrientationProvider;
     private final SplashImageHolder mSplashImageCache;
     private final BrowserServicesIntentDataProvider mIntentDataProvider;
 
@@ -83,31 +79,27 @@
     public TwaSplashController(SplashController splashController, Activity activity,
             ActivityWindowAndroid activityWindowAndroid,
             ActivityLifecycleDispatcher lifecycleDispatcher,
-            ScreenOrientationProvider screenOrientationProvider, SplashImageHolder splashImageCache,
+            CustomTabOrientationController orientationController,
+            SplashImageHolder splashImageCache,
             BrowserServicesIntentDataProvider intentDataProvider) {
         mSplashController = splashController;
         mActivity = activity;
         mActivityWindowAndroid = activityWindowAndroid;
         mLifecycleDispatcher = lifecycleDispatcher;
-        mScreenOrientationProvider = screenOrientationProvider;
         mSplashImageCache = splashImageCache;
         mIntentDataProvider = intentDataProvider;
 
         long splashHideAnimationDurationMs = IntentUtils.safeGetInt(
                 getSplashScreenParamsFromIntent(), SplashScreenParamKey.KEY_FADE_OUT_DURATION_MS,
                 0);
-        boolean isWindowInitiallyTranslucent = mActivity instanceof TranslucentCustomTabActivity;
+        boolean isWindowInitiallyTranslucent =
+                BaseCustomTabActivity.isWindowInitiallyTranslucent(activity);
         mSplashController.setConfig(
                 this, isWindowInitiallyTranslucent, splashHideAnimationDurationMs);
+        orientationController.delayOrientationRequestsIfNeeded(
+                mSplashController, isWindowInitiallyTranslucent);
 
-        mSplashController.addObserver(this);
         lifecycleDispatcher.register(this);
-
-        // Setting the screen orientation while the activity is translucent throws an exception on
-        // O (but not on O MR1). Delay setting it.
-        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O) {
-            mScreenOrientationProvider.delayOrientationRequests(mActivityWindowAndroid);
-        }
     }
 
     @Override
@@ -142,14 +134,6 @@
         mSplashController.bringSplashBackToFront();
     }
 
-    @Override
-    public void onTranslucencyRemoved() {
-        mScreenOrientationProvider.runDelayedOrientationRequests(mActivityWindowAndroid);
-    }
-
-    @Override
-    public void onSplashscreenHidden(long startTimestamp, long endTimestamp) {}
-
     private void applyCustomizationsToSplashScreenView(ImageView imageView) {
         Bundle params = getSplashScreenParamsFromIntent();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java
index 45477d8d..d878740 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java
@@ -79,7 +79,7 @@
     @VisibleForTesting
     public static final String UMA_THUMBNAIL_FETCHING_RESULT =
             "GridTabSwitcher.ThumbnailFetchingResult";
-    private Set<Integer> mRefectchedTabIds;
+    private final Set<Integer> mRefectchedTabIds = new HashSet<>();
 
     private float mThumbnailScale;
     private int mFullResThumbnailsMaxSize;
@@ -160,18 +160,43 @@
         mContentOffsetProvider = contentOffsetProvider;
         mTabFinder = tabFinder;
         mSnapshotsEnabled = snapshotsEnabled;
-    }
 
-    /**
-     * Called after native library is loaded.
-     */
-    public void initWithNative() {
         // Override the cache size on the command line with --thumbnails=100
         int defaultCacheSize = getIntegerResourceWithOverride(
                 mContext, R.integer.default_thumbnail_cache_size, ChromeSwitches.THUMBNAILS);
 
         mFullResThumbnailsMaxSize = defaultCacheSize;
 
+        float thumbnailScale = 1.f;
+        DisplayAndroid display = DisplayAndroid.getNonMultiDisplay(mContext);
+        float deviceDensity = display.getDipScale();
+        if (DeviceFormFactor.isNonMultiDisplayContextOnTablet(mContext)) {
+            // Scale all tablets to MDPI.
+            thumbnailScale = 1.f / deviceDensity;
+        } else {
+            // For phones, reduce the amount of memory usage by capturing a lower-res thumbnail for
+            // devices with resolution higher than HDPI (crbug.com/357740).
+            if (deviceDensity > 1.5f) {
+                thumbnailScale = 1.5f / deviceDensity;
+            }
+        }
+        mThumbnailScale = thumbnailScale;
+
+        mPriorityTabIds = new int[mFullResThumbnailsMaxSize];
+
+        if (TabUiFeatureUtilities.isTabThumbnailAspectRatioNotOne()
+                || ALLOW_TO_REFETCH_TAB_THUMBNAIL_VARIATION.getValue()) {
+            mExpectedThumbnailAspectRatio =
+                    (float) TabUiFeatureUtilities.THUMBNAIL_ASPECT_RATIO.getValue();
+            mExpectedThumbnailAspectRatio =
+                    MathUtils.clamp(mExpectedThumbnailAspectRatio, 0.5f, 2.0f);
+        }
+    }
+
+    /**
+     * Called after native library is loaded.
+     */
+    public void initWithNative() {
         int compressionQueueMaxSize =
                 mContext.getResources().getInteger(R.integer.default_compression_queue_size);
         int writeQueueMaxSize =
@@ -183,40 +208,12 @@
                 R.integer.default_approximation_thumbnail_cache_size,
                 ChromeSwitches.APPROXIMATION_THUMBNAILS);
 
-        float thumbnailScale = 1.f;
-        boolean useApproximationThumbnails;
+        boolean useApproximationThumbnails =
+                !DeviceFormFactor.isNonMultiDisplayContextOnTablet(mContext);
         boolean saveJpegThumbnails = TabUiFeatureUtilities.isGridTabSwitcherEnabled();
-        DisplayAndroid display = DisplayAndroid.getNonMultiDisplay(mContext);
-        float deviceDensity = display.getDipScale();
-        if (DeviceFormFactor.isNonMultiDisplayContextOnTablet(mContext)) {
-            // Scale all tablets to MDPI.
-            thumbnailScale = 1.f / deviceDensity;
-            useApproximationThumbnails = false;
-        } else {
-            // For phones, reduce the amount of memory usage by capturing a lower-res thumbnail for
-            // devices with resolution higher than HDPI (crbug.com/357740).
-            if (deviceDensity > 1.5f) {
-                thumbnailScale = 1.5f / deviceDensity;
-            }
-            useApproximationThumbnails = true;
-        }
-        mThumbnailScale = thumbnailScale;
-
-        mPriorityTabIds = new int[mFullResThumbnailsMaxSize];
-
-        if (TabUiFeatureUtilities.isTabThumbnailAspectRatioNotOne()
-                || ALLOW_TO_REFETCH_TAB_THUMBNAIL_VARIATION.getValue()) {
-            mRefectchedTabIds = new HashSet<>();
-            mExpectedThumbnailAspectRatio =
-                    (float) ChromeFeatureList.getFieldTrialParamByFeatureAsDouble(
-                            ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, "thumbnail_aspect_ratio",
-                            1.0);
-            mExpectedThumbnailAspectRatio =
-                    MathUtils.clamp(mExpectedThumbnailAspectRatio, 0.5f, 2.0f);
-        }
 
         mNativeTabContentManager = TabContentManagerJni.get().init(TabContentManager.this,
-                defaultCacheSize, approximationCacheSize, compressionQueueMaxSize,
+                mFullResThumbnailsMaxSize, approximationCacheSize, compressionQueueMaxSize,
                 writeQueueMaxSize, useApproximationThumbnails, saveJpegThumbnails);
     }
 
@@ -467,8 +464,14 @@
                                         >= ASPECT_RATIO_PRECISION) {
                             recordThumbnailFetchingResult(
                                     ThumbnailFetchingResult.GOT_DIFFERENT_ASPECT_RATIO_JPEG);
+
+                            if (mNativeTabContentManager == 0) {
+                                callback.onResult(jpeg);
+                                return;
+                            }
+                            if (!mSnapshotsEnabled) return;
+
                             mRefectchedTabIds.add(tabId);
-                            if (mNativeTabContentManager == 0 || !mSnapshotsEnabled) return;
                             TabContentManagerJni.get().getEtc1TabThumbnail(mNativeTabContentManager,
                                     TabContentManager.this, tabId,
                                     (etc1) -> callback.onResult(etc1));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
index f81fbb7..fe1268e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.customtabs;
 
+import android.app.Activity;
 import android.content.Intent;
 import android.util.Pair;
 import android.view.KeyEvent;
@@ -32,6 +33,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
 import org.chromium.chrome.browser.ui.RootUiCoordinator;
+import org.chromium.chrome.browser.webapps.SameTaskWebApkActivity;
 import org.chromium.chrome.browser.webapps.WebappActivityCoordinator;
 
 /**
@@ -63,6 +65,14 @@
     @VisibleForTesting
     public abstract BrowserServicesIntentDataProvider getIntentDataProvider();
 
+    /**
+     * @return Whether the activity window is initially translucent.
+     */
+    public static boolean isWindowInitiallyTranslucent(Activity activity) {
+        return activity instanceof TranslucentCustomTabActivity
+                || activity instanceof SameTaskWebApkActivity;
+    }
+
     @Override
     protected RootUiCoordinator createRootUiCoordinator() {
         return new BaseCustomTabRootUiCoordinator(this, getShareDelegateSupplier(),
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabOrientationController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabOrientationController.java
new file mode 100644
index 0000000..906880e
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabOrientationController.java
@@ -0,0 +1,86 @@
+// Copyright 2020 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.customtabs;
+
+import android.os.Build;
+
+import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider;
+import org.chromium.chrome.browser.dependency_injection.ActivityScope;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.InflationObserver;
+import org.chromium.chrome.browser.webapps.SplashController;
+import org.chromium.chrome.browser.webapps.SplashscreenObserver;
+import org.chromium.chrome.browser.webapps.WebappExtras;
+import org.chromium.content_public.browser.ScreenOrientationProvider;
+import org.chromium.content_public.common.ScreenOrientationValues;
+import org.chromium.ui.base.ActivityWindowAndroid;
+
+import javax.inject.Inject;
+
+/**
+ * Manages setting the initial screen orientation for the custom tab.
+ * Delays all screen orientation requests till the activity translucency is removed.
+ */
+@ActivityScope
+public class CustomTabOrientationController implements InflationObserver {
+    private final ActivityWindowAndroid mActivityWindowAndroid;
+    private final ActivityLifecycleDispatcher mLifecycleDispatcher;
+
+    private @ScreenOrientationValues int mLockScreenOrientation = ScreenOrientationValues.DEFAULT;
+
+    @Inject
+    public CustomTabOrientationController(ActivityWindowAndroid activityWindowAndroid,
+            BrowserServicesIntentDataProvider intentDataProvider,
+            ActivityLifecycleDispatcher lifecycleDispatcher) {
+        mActivityWindowAndroid = activityWindowAndroid;
+        mLifecycleDispatcher = lifecycleDispatcher;
+
+        WebappExtras webappExtras = intentDataProvider.getWebappExtras();
+        if (webappExtras != null) {
+            mLockScreenOrientation = webappExtras.orientation;
+            mLifecycleDispatcher.register(this);
+        }
+    }
+
+    /**
+     * Delays screen orientation requests if the activity window's initial translucency and the
+     * Android OS version requires it.
+     * Should be called:
+     * - Prior to pre inflation startup occurring.
+     * - Only if the splash screen is shown for the activity.
+     */
+    public void delayOrientationRequestsIfNeeded(
+            SplashController splashController, boolean isWindowInitiallyTranslucent) {
+        // Setting the screen orientation while the activity is translucent throws an exception on
+        // O (but not on O MR1).
+        if (!isWindowInitiallyTranslucent || Build.VERSION.SDK_INT != Build.VERSION_CODES.O) return;
+
+        ScreenOrientationProvider.getInstance().delayOrientationRequests(mActivityWindowAndroid);
+
+        splashController.addObserver(new SplashscreenObserver() {
+            @Override
+            public void onTranslucencyRemoved() {
+                ScreenOrientationProvider.getInstance().runDelayedOrientationRequests(
+                        mActivityWindowAndroid);
+            }
+
+            @Override
+            public void onSplashscreenHidden(long startTimestamp, long endTimestamp) {}
+        });
+    }
+
+    @Override
+    public void onPreInflationStartup() {
+        mLifecycleDispatcher.unregister(this);
+
+        // Queue up default screen orientation request now because the web page might change it via
+        // JavaScript.
+        ScreenOrientationProvider.getInstance().lockOrientation(
+                mActivityWindowAndroid, (byte) mLockScreenOrientation);
+    }
+
+    @Override
+    public void onPostInflationStartup() {}
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java
index 14c08a5..ac9a0d0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java
@@ -300,8 +300,11 @@
         CookieControlsView.CookieControlsParams cookieControlsParams =
                 new CookieControlsView.CookieControlsParams();
         cookieControlsParams.onUiClosingCallback = mBridge::onUiClosing;
-        cookieControlsParams.onCheckedChangedCallback =
-                mBridge::setThirdPartyCookieBlockingEnabledForSite;
+        cookieControlsParams.onCheckedChangedCallback = (Boolean blockCookies) -> {
+            recordAction(blockCookies ? PageInfoAction.PAGE_INFO_COOKIE_BLOCKED_FOR_SITE
+                                      : PageInfoAction.PAGE_INFO_COOKIE_ALLOWED_FOR_SITE);
+            mBridge.setThirdPartyCookieBlockingEnabledForSite(blockCookies);
+        };
         mView.getCookieControlsView().setParams(cookieControlsParams);
 
         mWebContentsObserver = new WebContentsObserver(webContents) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkExtras.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkExtras.java
index e577f794..06d141f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkExtras.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkExtras.java
@@ -4,9 +4,12 @@
 
 package org.chromium.chrome.browser.webapps;
 
+import androidx.annotation.NonNull;
+
 import org.chromium.chrome.browser.webapps.WebApkInfo.ShareTarget;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -22,6 +25,7 @@
     /**
      * Icon to use for the splash screen.
      */
+    @NonNull
     public final WebappIcon splashIcon;
 
     /**
@@ -50,6 +54,7 @@
     /**
      * Map of the WebAPK's icon URLs to Murmur2 hashes of the icon untransformed bytes.
      */
+    @NonNull
     public final Map<String, String> iconUrlToMurmur2HashMap;
 
     /**
@@ -57,6 +62,7 @@
      * TODO(pkotwicz): Remove this property in favor of
      * {@link BrowserServicesIntentDataProvider#shareTarget()}
      */
+    @NonNull
     public final ShareTarget shareTarget;
 
     /**
@@ -70,6 +76,7 @@
     /**
      * The list of the WebAPK's shortcuts.
      */
+    @NonNull
     public final List<ShortcutItem> shortcutItems;
 
     /**
@@ -99,16 +106,16 @@
         return new WebApkExtras(null /* webApkPackageName */, new WebappIcon(),
                 false /* isSplashIconMaskable */, 0 /* shellApkVersion */, null /* manifestUrl */,
                 null /* manifestStartUrl */, WebApkDistributor.OTHER,
-                null /* iconUrlToMurmur2HashMap */, new ShareTarget(),
+                new HashMap<String, String>() /* iconUrlToMurmur2HashMap */, new ShareTarget(),
                 false /* isSplashProvidedByWebApk */, new ArrayList<>() /* shortcutItems */,
                 0 /* webApkVersionCode */);
     }
 
-    public WebApkExtras(String webApkPackageName, WebappIcon splashIcon,
+    public WebApkExtras(String webApkPackageName, @NonNull WebappIcon splashIcon,
             boolean isSplashIconMaskable, int shellApkVersion, String manifestUrl,
             String manifestStartUrl, @WebApkDistributor int distributor,
-            Map<String, String> iconUrlToMurmur2HashMap, ShareTarget shareTarget,
-            boolean isSplashProvidedByWebApk, List<ShortcutItem> shortcutItems,
+            @NonNull Map<String, String> iconUrlToMurmur2HashMap, @NonNull ShareTarget shareTarget,
+            boolean isSplashProvidedByWebApk, @NonNull List<ShortcutItem> shortcutItems,
             int webApkVersionCode) {
         this.webApkPackageName = webApkPackageName;
         this.splashIcon = splashIcon;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java
index bf8a091..5aabe5ea 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java
@@ -10,10 +10,8 @@
 import androidx.annotation.Nullable;
 import androidx.browser.trusted.sharing.ShareData;
 
-import org.chromium.chrome.browser.ShortcutHelper;
 import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.webapps.WebApkExtras.ShortcutItem;
-import org.chromium.webapk.lib.common.WebApkConstants;
 
 import java.util.Arrays;
 import java.util.List;
@@ -180,96 +178,12 @@
         return (provider != null) ? new WebApkInfo(provider) : null;
     }
 
-    public WebApkInfo(@NonNull BrowserServicesIntentDataProvider provider) {
-        super(provider);
-    }
-
-    /**
-     * Returns the splash icon in Bitmap form.
-     */
-    public WebappIcon splashIcon() {
-        return getWebApkExtras().splashIcon;
-    }
-
-    public boolean isSplashIconMaskable() {
-        return getWebApkExtras().isSplashIconMaskable;
-    }
-
-    /** Returns data about the WebAPK's share intent handlers. */
-    public ShareTarget shareTarget() {
-        return getWebApkExtras().shareTarget;
-    }
-
-    /**
-     * Returns the WebAPK's version code.
-     */
-    public int webApkVersionCode() {
-        return getWebApkExtras().webApkVersionCode;
-    }
-
-    @Override
-    public boolean isForWebApk() {
-        return true;
-    }
-
-    @Override
-    public String webApkPackageName() {
-        return getWebApkExtras().webApkPackageName;
-    }
-
-    @Override
-    public boolean isSplashProvidedByWebApk() {
-        return getWebApkExtras().isSplashProvidedByWebApk;
-    }
-
-    public int shellApkVersion() {
-        return getWebApkExtras().shellApkVersion;
-    }
-
-    public String manifestUrl() {
-        return getWebApkExtras().manifestUrl;
-    }
-
-    public String manifestStartUrl() {
-        return getWebApkExtras().manifestStartUrl;
-    }
-
-    public @WebApkDistributor int distributor() {
-        return getWebApkExtras().distributor;
-    }
-
-    public Map<String, String> iconUrlToMurmur2HashMap() {
-        return getWebApkExtras().iconUrlToMurmur2HashMap;
-    }
-
-    public ShareData shareData() {
-        return mProvider.getShareData();
-    }
-
-    public List<ShortcutItem> shortcutItems() {
-        return getWebApkExtras().shortcutItems;
-    }
-
-    private WebApkExtras getWebApkExtras() {
-        WebApkExtras extras = mProvider.getWebApkExtras();
-        assert extras != null;
-        return extras;
-    }
-
-    @Override
-    public void setWebappIntentExtras(Intent intent) {
-        // For launching a WebAPK Activity.
-        intent.putExtra(ShortcutHelper.EXTRA_ID, id());
-        intent.putExtra(ShortcutHelper.EXTRA_URL, url());
-        intent.putExtra(ShortcutHelper.EXTRA_SOURCE, source());
-        intent.putExtra(ShortcutHelper.EXTRA_FORCE_NAVIGATION, shouldForceNavigation());
-        intent.putExtra(WebApkConstants.EXTRA_WEBAPK_PACKAGE_NAME, webApkPackageName());
-        intent.putExtra(
-                WebApkConstants.EXTRA_SPLASH_PROVIDED_BY_WEBAPK, isSplashProvidedByWebApk());
-    }
-
     /** Returns the value if it is non-null. Returns an empty string otherwise. */
     private static String replaceNullWithEmpty(String value) {
         return (value == null) ? "" : value;
     }
+
+    public WebApkInfo(@NonNull BrowserServicesIntentDataProvider provider) {
+        super(provider);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
index afe05fb0..336b1854 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -11,7 +11,6 @@
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
-import android.os.Build;
 import android.os.StrictMode;
 import android.text.TextUtils;
 import android.view.ViewGroup;
@@ -51,7 +50,6 @@
 import org.chromium.chrome.browser.webapps.dependency_injection.WebappActivityModule;
 import org.chromium.components.embedder_support.delegate.WebContentsDelegateAndroid;
 import org.chromium.content_public.browser.NavigationHandle;
-import org.chromium.content_public.browser.ScreenOrientationProvider;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.webapk.lib.common.WebApkConstants;
 
@@ -294,8 +292,6 @@
 
         super.performPreInflationStartup();
 
-        applyScreenOrientation();
-
         if (mWebappInfo.displayMode() == WebDisplayMode.FULLSCREEN) {
             new ImmersiveModeController(getLifecycleDispatcher(), this).enterImmersiveMode(
                     LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT, false /*sticky*/);
@@ -517,39 +513,13 @@
     /** Inits the splash screen */
     private void initSplash() {
         // Splash screen is shown after preInflationStartup() is run and the delegate is set.
-        boolean isWindowInitiallyTranslucent = mWebappInfo.isSplashProvidedByWebApk();
+        boolean isWindowInitiallyTranslucent =
+                BaseCustomTabActivity.isWindowInitiallyTranslucent(this);
         mSplashController.setConfig(
                 new WebappSplashDelegate(this, mTabObserverRegistrar, mWebappInfo),
                 isWindowInitiallyTranslucent, WebappSplashDelegate.HIDE_ANIMATION_DURATION_MS);
     }
 
-    /** Sets the screen orientation. */
-    private void applyScreenOrientation() {
-        if (mWebappInfo.isSplashProvidedByWebApk()
-                && Build.VERSION.SDK_INT == Build.VERSION_CODES.O) {
-            // When the splash screen is provided by the WebAPK, the activity is initially
-            // translucent. Setting the screen orientation while the activity is translucent
-            // throws an exception on O (but not O MR1). Delay setting it.
-            ScreenOrientationProvider.getInstance().delayOrientationRequests(getWindowAndroid());
-
-            mSplashController.addObserver(new SplashscreenObserver() {
-                @Override
-                public void onTranslucencyRemoved() {
-                    ScreenOrientationProvider.getInstance().runDelayedOrientationRequests(
-                            getWindowAndroid());
-                }
-
-                @Override
-                public void onSplashscreenHidden(long startTimestamp, long endTimestamp) {}
-            });
-
-            // Fall through and queue up request for the default screen orientation because the web
-            // page might change it via JavaScript.
-        }
-        ScreenOrientationProvider.getInstance().lockOrientation(
-                getWindowAndroid(), (byte) mWebappInfo.orientation());
-    }
-
     @Override
     public void onUpdateStateChanged() {}
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivityCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivityCoordinator.java
index 3541c82..94625a1e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivityCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivityCoordinator.java
@@ -12,6 +12,8 @@
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.CurrentPageVerifier;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityBrowserControlsVisibilityManager;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.Verifier;
+import org.chromium.chrome.browser.customtabs.BaseCustomTabActivity;
+import org.chromium.chrome.browser.customtabs.CustomTabOrientationController;
 import org.chromium.chrome.browser.customtabs.ExternalIntentsPolicyProvider;
 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityNavigationController;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
@@ -41,6 +43,7 @@
             ActivityTabProvider activityTabProvider, CurrentPageVerifier currentPageVerifier,
             Verifier verifier, CustomTabActivityNavigationController navigationController,
             ExternalIntentsPolicyProvider externalIntentsPolicyProvider,
+            CustomTabOrientationController orientationController, SplashController splashController,
             WebappDeferredStartupWithStorageHandler deferredStartupWithStorageHandler,
             WebappActionsNotificationManager actionsNotificationManager,
             ActivityLifecycleDispatcher lifecycleDispatcher,
@@ -70,6 +73,9 @@
             }
         });
 
+        orientationController.delayOrientationRequestsIfNeeded(
+                splashController, BaseCustomTabActivity.isWindowInitiallyTranslucent(activity));
+
         currentPageVerifier.addVerificationObserver(this::onVerificationUpdate);
         lifecycleDispatcher.register(this);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java
index 33fa8a0..5537fa2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java
@@ -263,12 +263,11 @@
             editor.putInt(KEY_VERSION, ShortcutHelper.WEBAPP_SHORTCUT_VERSION);
 
             if (info.isForWebApk()) {
-                WebApkInfo webApkInfo = (WebApkInfo) info;
-                editor.putString(KEY_WEBAPK_PACKAGE_NAME, webApkInfo.webApkPackageName());
-                editor.putString(KEY_WEBAPK_MANIFEST_URL, webApkInfo.manifestUrl());
-                editor.putInt(KEY_WEBAPK_VERSION_CODE, webApkInfo.webApkVersionCode());
+                editor.putString(KEY_WEBAPK_PACKAGE_NAME, info.webApkPackageName());
+                editor.putString(KEY_WEBAPK_MANIFEST_URL, info.manifestUrl());
+                editor.putInt(KEY_WEBAPK_VERSION_CODE, info.webApkVersionCode());
                 editor.putLong(KEY_WEBAPK_INSTALL_TIMESTAMP,
-                        fetchWebApkInstallTimestamp(webApkInfo.webApkPackageName()));
+                        fetchWebApkInstallTimestamp(info.webApkPackageName()));
             } else {
                 editor.putString(KEY_NAME, info.name());
                 editor.putString(KEY_SHORT_NAME, info.shortName());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappExtras.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappExtras.java
index 401f48a8..00c802ff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappExtras.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappExtras.java
@@ -6,6 +6,8 @@
 
 import android.graphics.Color;
 
+import androidx.annotation.NonNull;
+
 import org.chromium.chrome.browser.ShortcutSource;
 import org.chromium.content_public.common.ScreenOrientationValues;
 
@@ -28,6 +30,7 @@
     /**
      * The webapp's launcher icon.
      */
+    @NonNull
     public final WebappIcon icon;
 
     /**
@@ -87,10 +90,10 @@
                 false /* isIconAdaptive */, false /* shouldForceNavigation */);
     }
 
-    public WebappExtras(String id, String url, String scopeUrl, WebappIcon icon, String name,
-            String shortName, @WebDisplayMode int displayMode, int orientation, int source,
-            Integer backgroundColor, int defaultBackgroundColor, boolean isIconGenerated,
-            boolean isIconAdaptive, boolean shouldForceNavigation) {
+    public WebappExtras(String id, String url, String scopeUrl, @NonNull WebappIcon icon,
+            String name, String shortName, @WebDisplayMode int displayMode, int orientation,
+            int source, Integer backgroundColor, int defaultBackgroundColor,
+            boolean isIconGenerated, boolean isIconAdaptive, boolean shouldForceNavigation) {
         this.id = id;
         this.url = url;
         this.scopeUrl = scopeUrl;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java
index 5b470ce..c6d60ed 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java
@@ -5,18 +5,27 @@
 package org.chromium.chrome.browser.webapps;
 
 import android.content.Intent;
+import android.text.TextUtils;
 
 import androidx.annotation.NonNull;
+import androidx.browser.trusted.sharing.ShareData;
 
 import org.chromium.chrome.browser.ShortcutHelper;
 import org.chromium.chrome.browser.ShortcutSource;
 import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider;
+import org.chromium.chrome.browser.webapps.WebApkExtras.ShortcutItem;
+import org.chromium.chrome.browser.webapps.WebApkInfo.ShareTarget;
+import org.chromium.webapk.lib.common.WebApkConstants;
+
+import java.util.List;
+import java.util.Map;
 
 /**
  * Stores info about a web app.
  */
 public class WebappInfo {
-    protected final BrowserServicesIntentDataProvider mProvider;
+    private final @NonNull BrowserServicesIntentDataProvider mProvider;
+    private final @NonNull WebApkExtras mWebApkExtras;
 
     public static WebappInfo createEmpty() {
         return new WebappInfo(
@@ -36,6 +45,8 @@
 
     protected WebappInfo(@NonNull BrowserServicesIntentDataProvider provider) {
         mProvider = provider;
+        WebApkExtras webApkExtras = provider.getWebApkExtras();
+        mWebApkExtras = (webApkExtras != null) ? webApkExtras : WebApkExtras.createEmpty();
     }
 
     @NonNull
@@ -76,11 +87,11 @@
     }
 
     public boolean isForWebApk() {
-        return false;
+        return !TextUtils.isEmpty(webApkPackageName());
     }
 
     public String webApkPackageName() {
-        return null;
+        return mWebApkExtras.webApkPackageName;
     }
 
     public int orientation() {
@@ -136,6 +147,7 @@
     /**
      * Returns the icon.
      */
+    @NonNull
     public WebappIcon icon() {
         return getWebappExtras().icon;
     }
@@ -159,7 +171,62 @@
      * and (2) has a content provider which provides a screenshot of the splash screen.
      */
     public boolean isSplashProvidedByWebApk() {
-        return false;
+        return mWebApkExtras.isSplashProvidedByWebApk;
+    }
+
+    /**
+     * Returns the WebAPK's splash icon.
+     */
+    @NonNull
+    public WebappIcon splashIcon() {
+        return mWebApkExtras.splashIcon;
+    }
+
+    public boolean isSplashIconMaskable() {
+        return mWebApkExtras.isSplashIconMaskable;
+    }
+
+    /** Returns data about the WebAPK's share intent handlers. */
+    @NonNull
+    public ShareTarget shareTarget() {
+        return mWebApkExtras.shareTarget;
+    }
+
+    /**
+     * Returns the WebAPK's version code.
+     */
+    public int webApkVersionCode() {
+        return mWebApkExtras.webApkVersionCode;
+    }
+
+    public int shellApkVersion() {
+        return mWebApkExtras.shellApkVersion;
+    }
+
+    public String manifestUrl() {
+        return mWebApkExtras.manifestUrl;
+    }
+
+    public String manifestStartUrl() {
+        return mWebApkExtras.manifestStartUrl;
+    }
+
+    public @WebApkDistributor int distributor() {
+        return mWebApkExtras.distributor;
+    }
+
+    @NonNull
+    public Map<String, String> iconUrlToMurmur2HashMap() {
+        return mWebApkExtras.iconUrlToMurmur2HashMap;
+    }
+
+    public ShareData shareData() {
+        return mProvider.getShareData();
+    }
+
+    @NonNull
+    public List<ShortcutItem> shortcutItems() {
+        return mWebApkExtras.shortcutItems;
     }
 
     /**
@@ -170,18 +237,24 @@
         intent.putExtra(ShortcutHelper.EXTRA_ID, id());
         intent.putExtra(ShortcutHelper.EXTRA_URL, url());
         intent.putExtra(ShortcutHelper.EXTRA_FORCE_NAVIGATION, shouldForceNavigation());
-        intent.putExtra(ShortcutHelper.EXTRA_SCOPE, scopeUrl());
-        intent.putExtra(ShortcutHelper.EXTRA_ICON, icon().encoded());
-        intent.putExtra(ShortcutHelper.EXTRA_VERSION, ShortcutHelper.WEBAPP_SHORTCUT_VERSION);
-        intent.putExtra(ShortcutHelper.EXTRA_NAME, name());
-        intent.putExtra(ShortcutHelper.EXTRA_SHORT_NAME, shortName());
-        intent.putExtra(ShortcutHelper.EXTRA_DISPLAY_MODE, displayMode());
-        intent.putExtra(ShortcutHelper.EXTRA_ORIENTATION, orientation());
         intent.putExtra(ShortcutHelper.EXTRA_SOURCE, source());
-        intent.putExtra(ShortcutHelper.EXTRA_THEME_COLOR, toolbarColor());
-        intent.putExtra(ShortcutHelper.EXTRA_BACKGROUND_COLOR, backgroundColor());
-        intent.putExtra(ShortcutHelper.EXTRA_IS_ICON_GENERATED, isIconGenerated());
-        intent.putExtra(ShortcutHelper.EXTRA_IS_ICON_ADAPTIVE, isIconAdaptive());
+        if (isForWebApk()) {
+            intent.putExtra(WebApkConstants.EXTRA_WEBAPK_PACKAGE_NAME, webApkPackageName());
+            intent.putExtra(
+                    WebApkConstants.EXTRA_SPLASH_PROVIDED_BY_WEBAPK, isSplashProvidedByWebApk());
+        } else {
+            intent.putExtra(ShortcutHelper.EXTRA_SCOPE, scopeUrl());
+            intent.putExtra(ShortcutHelper.EXTRA_ICON, icon().encoded());
+            intent.putExtra(ShortcutHelper.EXTRA_VERSION, ShortcutHelper.WEBAPP_SHORTCUT_VERSION);
+            intent.putExtra(ShortcutHelper.EXTRA_NAME, name());
+            intent.putExtra(ShortcutHelper.EXTRA_SHORT_NAME, shortName());
+            intent.putExtra(ShortcutHelper.EXTRA_DISPLAY_MODE, displayMode());
+            intent.putExtra(ShortcutHelper.EXTRA_ORIENTATION, orientation());
+            intent.putExtra(ShortcutHelper.EXTRA_THEME_COLOR, toolbarColor());
+            intent.putExtra(ShortcutHelper.EXTRA_BACKGROUND_COLOR, backgroundColor());
+            intent.putExtra(ShortcutHelper.EXTRA_IS_ICON_GENERATED, isIconGenerated());
+            intent.putExtra(ShortcutHelper.EXTRA_IS_ICON_ADAPTIVE, isIconAdaptive());
+        }
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappLauncherActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappLauncherActivity.java
index 75df241..1498880 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappLauncherActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappLauncherActivity.java
@@ -65,7 +65,7 @@
     private static final String TAG = "webapps";
 
     /** Creates intent to relaunch WebAPK. */
-    public static Intent createRelaunchWebApkIntent(Intent sourceIntent, WebApkInfo webApkInfo) {
+    public static Intent createRelaunchWebApkIntent(Intent sourceIntent, WebappInfo webApkInfo) {
         assert webApkInfo != null;
 
         Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(webApkInfo.url()));
@@ -215,7 +215,7 @@
     /** Relaunches WebAPK. */
     private static void relaunchWebApk(
             Activity launchingActivity, Intent sourceIntent, @NonNull WebappInfo info) {
-        Intent launchIntent = createRelaunchWebApkIntent(sourceIntent, (WebApkInfo) info);
+        Intent launchIntent = createRelaunchWebApkIntent(sourceIntent, info);
         launchAfterDelay(
                 launchingActivity.getApplicationContext(), launchIntent, WEBAPK_LAUNCH_DELAY_MS);
         ApiCompatibilityUtils.finishAndRemoveTask(launchingActivity);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappSplashDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappSplashDelegate.java
index 9f253b190..e17a3f0d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappSplashDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappSplashDelegate.java
@@ -77,9 +77,8 @@
         splashScreen.setBackgroundColor(backgroundColor);
 
         if (mWebappInfo.isForWebApk()) {
-            WebApkInfo webApkInfo = (WebApkInfo) mWebappInfo;
             initializeWebApkInfoSplashLayout(splashScreen, backgroundColor,
-                    webApkInfo.splashIcon().bitmap(), webApkInfo.isSplashIconMaskable());
+                    mWebappInfo.splashIcon().bitmap(), mWebappInfo.isSplashIconMaskable());
             return splashScreen;
         }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/CookieControlsViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/CookieControlsViewTest.java
index 036c3f8..159e8d7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/CookieControlsViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/CookieControlsViewTest.java
@@ -17,12 +17,14 @@
 import android.support.test.filters.MediumTest;
 
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
@@ -68,6 +70,14 @@
         });
     }
 
+    private int getTotalPageActionHistogramCount() {
+        return RecordHistogram.getHistogramTotalCountForTesting("WebsiteSettings.Action");
+    }
+
+    private int getPageActionHistogramCount(@PageInfoAction int action) {
+        return RecordHistogram.getHistogramValueCountForTesting("WebsiteSettings.Action", action);
+    }
+
     @Before
     public void setUp() throws InterruptedException {
         mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
@@ -121,17 +131,33 @@
     @Test
     @MediumTest
     public void testUpdate() {
+        Assert.assertEquals(0, getTotalPageActionHistogramCount());
+
         setThirdPartyCookieBlocking(true);
         loadUrlAndOpenPageInfo(mTestServer.getURLWithHostName("foo.com", mPath));
+        Assert.assertEquals(1, getTotalPageActionHistogramCount());
+        Assert.assertEquals(1, getPageActionHistogramCount(PageInfoAction.PAGE_INFO_OPENED));
         int switch_id = R.id.cookie_controls_block_cookies_switch;
         onView(withId(switch_id)).check(matches(isChecked()));
         onView(withId(switch_id)).perform(click());
         onView(withId(switch_id)).check(matches(isNotChecked()));
+        Assert.assertEquals(2, getTotalPageActionHistogramCount());
+        Assert.assertEquals(
+                1, getPageActionHistogramCount(PageInfoAction.PAGE_INFO_COOKIE_ALLOWED_FOR_SITE));
+
         // Load a different page.
         loadUrlAndOpenPageInfo(mTestServer.getURLWithHostName("bar.com", mPath));
         onView(withId(switch_id)).check(matches(isChecked()));
+
         // Go back to foo.com.
         loadUrlAndOpenPageInfo(mTestServer.getURLWithHostName("foo.com", mPath));
         onView(withId(switch_id)).check(matches(isNotChecked()));
+        onView(withId(switch_id)).perform(click());
+        onView(withId(switch_id)).check(matches(isChecked()));
+        Assert.assertEquals(5, getTotalPageActionHistogramCount());
+
+        Assert.assertEquals(
+                1, getPageActionHistogramCount(PageInfoAction.PAGE_INFO_COOKIE_BLOCKED_FOR_SITE));
+        Assert.assertEquals(3, getPageActionHistogramCount(PageInfoAction.PAGE_INFO_OPENED));
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderTest.java
index 80a2fdd..5356be0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderTest.java
@@ -39,6 +39,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
@@ -1290,9 +1291,7 @@
         mockTwaAndItsInstaller("com.merchant.twa", "com.android.vending");
         findApps(methods);
 
-        Assert.assertEquals("2 app should still match the query", 2, mPaymentApps.size());
-        Assert.assertEquals("com.merchant.twa", mPaymentApps.get(0).getIdentifier());
-        Assert.assertEquals("com.bobpay", mPaymentApps.get(1).getIdentifier());
+        assertPaymentAppsHaveIdentifiers("com.merchant.twa", "com.bobpay");
     }
 
     /**
@@ -1507,4 +1506,23 @@
         mPackageManager.setMockTrustedWebActivity(twaPackageName);
         mPackageManager.mockInstallerForPackage(twaPackageName, installerPackageName);
     }
+
+    private void assertPaymentAppsHaveIdentifiers(String... expectedIds) {
+        Set<String> ids = new HashSet<>();
+        for (PaymentApp app : mPaymentApps) {
+            ids.add(app.getIdentifier());
+        }
+        Assert.assertEquals(
+                String.format(Locale.getDefault(), "Expected %d apps, but got %d apps instead.",
+                        expectedIds.length, ids.size()),
+                expectedIds.length, ids.size());
+        for (String expectedId : expectedIds) {
+            Assert.assertTrue(String.format(Locale.getDefault(),
+                                      "Expected id %s is not found. "
+                                              + "Expected identifiers: %s. "
+                                              + "Actual identifiers: %s",
+                                      expectedId, expectedIds.toString(), ids.toString()),
+                    ids.contains(expectedId));
+        }
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/WebPaymentIntentHelperTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/WebPaymentIntentHelperTest.java
index d48b549..44296229 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/WebPaymentIntentHelperTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/WebPaymentIntentHelperTest.java
@@ -59,14 +59,14 @@
         extras.putString(WebPaymentIntentHelper.EXTRA_RESPONSE_DETAILS, "\"key\":\"value\"}");
         extras.putString(WebPaymentIntentHelper.EXTRA_RESPONSE_METHOD_NAME, "maxPay");
         Bundle addressBundle = new Bundle();
-        addressBundle.putString(WebPaymentIntentHelper.EXTRA_ADDRESS_COUNTRY, "Canada");
+        addressBundle.putString(Address.EXTRA_ADDRESS_COUNTRY, "Canada");
         String[] addressLine = {"111 Richmond Street West"};
-        addressBundle.putStringArray(WebPaymentIntentHelper.EXTRA_ADDRESS_LINES, addressLine);
-        addressBundle.putString(WebPaymentIntentHelper.EXTRA_ADDRESS_REGION, "Ontario");
-        addressBundle.putString(WebPaymentIntentHelper.EXTRA_ADDRESS_CITY, "Toronto");
-        addressBundle.putString(WebPaymentIntentHelper.EXTRA_ADDRESS_POSTAL_CODE, "M5H2G4");
-        addressBundle.putString(WebPaymentIntentHelper.EXTRA_ADDRESS_RECIPIENT, "John Smith");
-        addressBundle.putString(WebPaymentIntentHelper.EXTRA_ADDRESS_PHONE, "4169158200");
+        addressBundle.putStringArray(Address.EXTRA_ADDRESS_LINES, addressLine);
+        addressBundle.putString(Address.EXTRA_ADDRESS_REGION, "Ontario");
+        addressBundle.putString(Address.EXTRA_ADDRESS_CITY, "Toronto");
+        addressBundle.putString(Address.EXTRA_ADDRESS_POSTAL_CODE, "M5H2G4");
+        addressBundle.putString(Address.EXTRA_ADDRESS_RECIPIENT, "John Smith");
+        addressBundle.putString(Address.EXTRA_ADDRESS_PHONE, "4169158200");
         extras.putBundle(WebPaymentIntentHelper.EXTRA_SHIPPING_ADDRESS, addressBundle);
         extras.putString(WebPaymentIntentHelper.EXTRA_RESPONSE_PAYER_NAME, "John Smith");
         extras.putString(WebPaymentIntentHelper.EXTRA_RESPONSE_PAYER_PHONE, "4169158200");
@@ -743,14 +743,14 @@
         extras.putString(WebPaymentIntentHelper.EXTRA_RESPONSE_DETAILS, "\"key\":\"value\"}");
         extras.putString(WebPaymentIntentHelper.EXTRA_RESPONSE_METHOD_NAME, "maxPay");
         Bundle addressBundle = new Bundle();
-        addressBundle.putString(WebPaymentIntentHelper.EXTRA_ADDRESS_COUNTRY, "Canada");
+        addressBundle.putString(Address.EXTRA_ADDRESS_COUNTRY, "Canada");
         String[] addressLine = {"111 Richmond Street West"};
-        addressBundle.putStringArray(WebPaymentIntentHelper.EXTRA_ADDRESS_LINES, addressLine);
-        addressBundle.putString(WebPaymentIntentHelper.EXTRA_ADDRESS_REGION, "Ontario");
-        addressBundle.putString(WebPaymentIntentHelper.EXTRA_ADDRESS_CITY, "Toronto");
-        addressBundle.putString(WebPaymentIntentHelper.EXTRA_ADDRESS_POSTAL_CODE, "M5H2G4");
-        addressBundle.putString(WebPaymentIntentHelper.EXTRA_ADDRESS_RECIPIENT, "John Smith");
-        addressBundle.putString(WebPaymentIntentHelper.EXTRA_ADDRESS_PHONE, "4169158200");
+        addressBundle.putStringArray(Address.EXTRA_ADDRESS_LINES, addressLine);
+        addressBundle.putString(Address.EXTRA_ADDRESS_REGION, "Ontario");
+        addressBundle.putString(Address.EXTRA_ADDRESS_CITY, "Toronto");
+        addressBundle.putString(Address.EXTRA_ADDRESS_POSTAL_CODE, "M5H2G4");
+        addressBundle.putString(Address.EXTRA_ADDRESS_RECIPIENT, "John Smith");
+        addressBundle.putString(Address.EXTRA_ADDRESS_PHONE, "4169158200");
         extras.putBundle(WebPaymentIntentHelper.EXTRA_SHIPPING_ADDRESS, addressBundle);
         extras.putString(WebPaymentIntentHelper.EXTRA_RESPONSE_PAYER_NAME, "John Smith");
         extras.putString(WebPaymentIntentHelper.EXTRA_RESPONSE_PAYER_PHONE, "4169158200");
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkInitializationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkInitializationTest.java
index 84eca9e..f61455b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkInitializationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkInitializationTest.java
@@ -17,6 +17,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.customtabs.CustomTabOrientationController;
 import org.chromium.chrome.browser.dependency_injection.ChromeActivityCommonsModule;
 import org.chromium.chrome.browser.dependency_injection.ModuleOverridesRule;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
@@ -97,8 +98,9 @@
 
     /**
      * Test that {@link WebappActionsNotificationManager},
-     * {@link WebappDisclosureSnackbarController} and {@link WebApkActivityLifecycleUmaTracker} are
-     * constructed when a WebAPK Activity is launched.
+     * {@link WebappDisclosureSnackbarController}, {@link WebApkActivityLifecycleUmaTracker} and
+     * {@link CustomTabOrientationController} are constructed when a {@link WebApkActivity} is
+     * launched.
      */
     @Test
     @LargeTest
@@ -119,6 +121,8 @@
                 WebappDisclosureSnackbarController.class.getName()));
         assertTrue(registeredObserverClassNames.contains(
                 WebApkActivityLifecycleUmaTracker.class.getName()));
+        assertTrue(registeredObserverClassNames.contains(
+                CustomTabOrientationController.class.getName()));
 
         // Test that WebappActiveTabUmaTracker is hooked up.
         assertTrue(0 < RecordHistogram.getHistogramTotalCountForTesting(
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 45fe507..32541cb 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -864,15 +864,9 @@
   <message name="IDS_LOGIN_RECOMMEND_APPS_INSTALL" desc="The label on the button that installs the user selected apps">
     Install &#38; continue
   </message>
-  <message name="IDS_LOGIN_RECOMMEND_APPS_RETRY" desc="The label on the button that tries to load the app list again">
-    Retry
-  </message>
   <message name="IDS_LOGIN_RECOMMEND_APPS_SCREEN_LOADING" desc="Message shown while the recommended app list is being downloaded.">
     Please wait...
   </message>
-  <message name="IDS_LOGIN_RECOMMEND_APPS_SCREEN_ERROR" desc="Load error message of the Recommend Apps OOBE dialog.">
-    The list of apps cannot be loaded. Please retry.
-  </message>
   <message name="IDS_LOGIN_APP_DOWNLOADING_SCREEN_TITLE_SINGULAR" desc="The title of the dialog that tells the user the selected Android App is downloading.">
     We'll install that app for you
   </message>
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 2b49f07..0a825de 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -2506,20 +2506,6 @@
         </message>
       </if>
 
-      <!-- Web Share Target picker dialog -->
-      <message name="IDS_WEBSHARE_TARGET_PICKER_TITLE" desc="Title of the dialog to choose a target for sharing.">
-        Share via
-      </message>
-      <message name="IDS_WEBSHARE_TARGET_PICKER_LABEL" desc="Text of the main label in the dialog to choose a target for sharing.">
-        Choose an app to share to:
-      </message>
-      <message name="IDS_WEBSHARE_TARGET_PICKER_COMMIT" desc="Title of the button to share content to the chosen application.">
-        Share
-      </message>
-      <message name="IDS_WEBSHARE_TARGET_DIALOG_ITEM_TEXT" desc="Text for a Web Share Target item in the Web Share Target dialog.">
-        <ph name="APP_NAME">$1<ex>Google Maps</ex></ph> (<ph name="APP_URL">$2<ex>https://google.com/maps</ex></ph>)
-      </message>
-
       <!-- Content blocking strings -->
       <message name="IDS_MANAGE" desc="Text for a button on permission bubbles, which opens a more detailed content settings page where users can manage that particular setting.">
         Manage
@@ -10028,12 +10014,6 @@
       <message name="IDS_WEBAUTHN_GENERIC_TITLE" desc="Title of most dialogs shown while the user is authenticating on a web site using a security key.">
         Use your security key with <ph name="APP_NAME">$1<ex>google.com</ex></ph>
       </message>
-      <message name="IDS_WEBAUTHN_WELCOME_SCREEN_TITLE" desc="Title of the dialog shown the very first time the user wants to authenticate on a web site using a security key.">
-        <ph name="APP_NAME">$1<ex>google.com</ex></ph> wants to verify your identity
-      </message>
-      <message name="IDS_WEBAUTHN_WELCOME_SCREEN_DESCRIPTION" desc="Description in the dialog shown the very first time the user wants to authenticate on a web site using a security key.">
-        Verifying your identity helps protect your personal information
-      </message>
       <message name="IDS_WEBAUTHN_WELCOME_SCREEN_NEXT" desc="Button text to continue using a security key for authentication on the web for the very first time.">
         Continue
       </message>
@@ -10266,12 +10246,6 @@
       </message>
 
       <!-- WebAuthn account selection for resident keys -->
-      <message name="IDS_WEBAUTHN_ACCOUNT_COLUMN" desc="The title of a column in a table under which will be a list of usernames or email addresses, e.g. joe@gmail.com.">
-        Account
-      </message>
-      <message name="IDS_WEBAUTHN_NAME_COLUMN" desc="The title of a column in a table under which will be a list of names on accounts, i.e. names of people. For example, Joe Smith.">
-        Name
-      </message>
       <message name="IDS_WEBAUTHN_SELECT_ACCOUNT" desc="The title on a dialog where the user is expected to select an account from a list. For example, the list may include several identities, e.g. joe@gmail.com, mary@gmail.com, and the user has to select one to login as.">
         Select an account to sign in
       </message>
diff --git a/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.cc b/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.cc
index 946f8c3..a4d6b29 100644
--- a/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.cc
+++ b/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.cc
@@ -38,39 +38,16 @@
 void AssistantCollectUserDataDelegate::OnContactInfoChanged(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& jcaller,
-    const base::android::JavaParamRef<jstring>& jpayer_name,
-    const base::android::JavaParamRef<jstring>& jpayer_phone,
-    const base::android::JavaParamRef<jstring>& jpayer_email) {
-  if (!jpayer_name && !jpayer_phone && !jpayer_email) {
+    const base::android::JavaParamRef<jobject>& jcontact_profile) {
+  if (!jcontact_profile) {
     ui_controller_->OnContactInfoChanged(nullptr);
     return;
   }
 
-  std::string name = ui_controller_android_utils::SafeConvertJavaStringToNative(
-      env, jpayer_name);
-  std::string phone =
-      ui_controller_android_utils::SafeConvertJavaStringToNative(env,
-                                                                 jpayer_phone);
-  std::string email =
-      ui_controller_android_utils::SafeConvertJavaStringToNative(env,
-                                                                 jpayer_email);
-
   auto contact_profile = std::make_unique<autofill::AutofillProfile>();
-  contact_profile->SetRawInfo(autofill::ServerFieldType::NAME_FULL,
-                              base::UTF8ToUTF16(name));
-  autofill::data_util::NameParts parts =
-      autofill::data_util::SplitName(base::UTF8ToUTF16(name));
-  contact_profile->SetRawInfo(autofill::ServerFieldType::NAME_FIRST,
-                              parts.given);
-  contact_profile->SetRawInfo(autofill::ServerFieldType::NAME_MIDDLE,
-                              parts.middle);
-  contact_profile->SetRawInfo(autofill::ServerFieldType::NAME_LAST,
-                              parts.family);
-  contact_profile->SetRawInfo(autofill::ServerFieldType::EMAIL_ADDRESS,
-                              base::UTF8ToUTF16(email));
-  contact_profile->SetRawInfo(
-      autofill::ServerFieldType::PHONE_HOME_WHOLE_NUMBER,
-      base::UTF8ToUTF16(phone));
+  autofill::PersonalDataManagerAndroid::PopulateNativeProfileFromJava(
+      jcontact_profile, env, contact_profile.get());
+
   ui_controller_->OnContactInfoChanged(std::move(contact_profile));
 }
 
diff --git a/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.h b/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.h
index 4f682ef..8dc3abe94 100644
--- a/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.h
+++ b/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.h
@@ -18,9 +18,7 @@
   void OnContactInfoChanged(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& jcaller,
-      const base::android::JavaParamRef<jstring>& jpayer_name,
-      const base::android::JavaParamRef<jstring>& jpayer_phone,
-      const base::android::JavaParamRef<jstring>& jpayer_email);
+      const base::android::JavaParamRef<jobject>& jcontact_profile);
 
   void OnShippingAddressChanged(
       JNIEnv* env,
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 1d84022..6c39819c 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -311,6 +311,7 @@
   KeepAliveRegistry::GetInstance()->AddObserver(this);
 #endif  // !defined(OS_ANDROID)
 
+  MigrateObsoleteLocalStatePrefs(local_state());
   pref_change_registrar_.Init(local_state());
 
   // Initialize the notification for the default browser setting policy.
diff --git a/chrome/browser/chrome_back_forward_cache_browsertest.cc b/chrome/browser/chrome_back_forward_cache_browsertest.cc
index aa23d155..73dbf1c8 100644
--- a/chrome/browser/chrome_back_forward_cache_browsertest.cc
+++ b/chrome/browser/chrome_back_forward_cache_browsertest.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/auto_reset.h"
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/test/mock_callback.h"
@@ -10,18 +9,15 @@
 #include "build/build_config.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/permissions/permission_manager_factory.h"
-#include "chrome/browser/printing/print_view_manager_common.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/network_session_configurator/common/network_switches.h"
 #include "components/permissions/permission_manager.h"
-#include "components/printing/common/print.mojom.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
@@ -32,7 +28,6 @@
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
-#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/mojom/webshare/webshare.mojom.h"
 
 class ChromeBackForwardCacheBrowserTest : public InProcessBrowserTest {
@@ -247,120 +242,6 @@
   delete_observer_rfh_a.WaitUntilDeleted();
 }
 
-class PrintPreviewObserver : printing::PrintPreviewUI::TestDelegate {
- public:
-  explicit PrintPreviewObserver(bool wait_for_loaded) {
-    if (wait_for_loaded)
-      queue_.emplace();  // DOMMessageQueue doesn't allow assignment
-    printing::PrintPreviewUI::SetDelegateForTesting(this);
-  }
-
-  ~PrintPreviewObserver() override {
-    printing::PrintPreviewUI::SetDelegateForTesting(nullptr);
-  }
-
-  void WaitUntilPreviewIsReady() {
-    if (rendered_page_count_ >= total_page_count_)
-      return;
-
-    base::RunLoop run_loop;
-    base::AutoReset<base::RunLoop*> auto_reset(&run_loop_, &run_loop);
-    run_loop.Run();
-
-    if (queue_.has_value()) {
-      std::string message;
-      EXPECT_TRUE(queue_->WaitForMessage(&message));
-      EXPECT_EQ("\"success\"", message);
-    }
-  }
-
- private:
-  // PrintPreviewUI::TestDelegate:
-  void DidGetPreviewPageCount(int page_count) override {
-    total_page_count_ = page_count;
-  }
-
-  // PrintPreviewUI::TestDelegate:
-  void DidRenderPreviewPage(content::WebContents* preview_dialog) override {
-    ++rendered_page_count_;
-    CHECK(rendered_page_count_ <= total_page_count_);
-    if (rendered_page_count_ == total_page_count_ && run_loop_) {
-      run_loop_->Quit();
-
-      if (queue_.has_value()) {
-        content::ExecuteScriptAsync(
-            preview_dialog,
-            "window.addEventListener('message', event => {"
-            "  if (event.data.type === 'documentLoaded') {"
-            "    domAutomationController.send(event.data.load_state);"
-            "  }"
-            "});");
-      }
-    }
-  }
-
-  base::Optional<content::DOMMessageQueue> queue_;
-  int total_page_count_ = 1;
-  int rendered_page_count_ = 0;
-  base::RunLoop* run_loop_ = nullptr;
-
-  DISALLOW_COPY_AND_ASSIGN(PrintPreviewObserver);
-};
-
-class ChromeBackForwardCacheBrowserTestWithPrinting
-    : public ChromeBackForwardCacheBrowserTest {
- public:
-  ChromeBackForwardCacheBrowserTestWithPrinting() = default;
-  ~ChromeBackForwardCacheBrowserTestWithPrinting() override = default;
-
-  void PrintAndWaitUntilPreviewIsReady(bool print_only_selection) {
-    PrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/true);
-    printing::StartPrint(browser()->tab_strip_model()->GetActiveWebContents(),
-                         /*print_renderer=*/mojo::NullAssociatedRemote(),
-                         /*print_preview_disabled=*/false,
-                         print_only_selection);
-    print_preview_observer.WaitUntilPreviewIsReady();
-  }
-
-  void WaitUntilMessagesReceived() {
-    run_loop_ = std::make_unique<base::RunLoop>();
-    run_loop_->Run();
-  }
-
-  static mojo::AssociatedRemote<printing::mojom::PrintRenderFrame>
-  GetPrintRenderFrame(content::RenderFrameHost* rfh) {
-    mojo::AssociatedRemote<printing::mojom::PrintRenderFrame> remote;
-    rfh->GetRemoteAssociatedInterfaces()->GetInterface(&remote);
-    return remote;
-  }
-
- private:
-  std::unique_ptr<base::RunLoop> run_loop_;
-};
-
-IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTestWithPrinting,
-                       DoesNotCacheIfPrinting) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  // 1) Navigate to A.
-  EXPECT_TRUE(content::NavigateToURL(
-      web_contents(), embedded_test_server()->GetURL("/printing/test1.html")));
-  content::RenderFrameHost* rfh_a = current_frame_host();
-  content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
-  PrintAndWaitUntilPreviewIsReady(/*print_only_selection=*/false);
-
-  // 2) Navigate to B.
-  EXPECT_TRUE(content::NavigateToURL(web_contents(), GetURL("b.com")));
-  content::RenderFrameHost* rfh_b = current_frame_host();
-  content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
-  // A is not cached because of printing.
-  EXPECT_TRUE(delete_observer_rfh_a.deleted());
-
-  // 3) Navigate back.
-  web_contents()->GetController().GoBack();
-  EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
-}
-
 #if defined(OS_ANDROID)
 IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest,
                        DoesNotCacheIfWebShare) {
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 2cdc9273..16a3e9fe 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -2207,8 +2207,6 @@
     "release_notes/release_notes_storage.cc",
     "release_notes/release_notes_storage.h",
     "reset/metrics.h",
-    "resource_reporter/resource_reporter.cc",
-    "resource_reporter/resource_reporter.h",
     "scheduler_configuration_manager.cc",
     "scheduler_configuration_manager.h",
     "secure_channel/secure_channel_client_provider.cc",
@@ -3132,7 +3130,6 @@
     "proxy_config_service_impl_unittest.cc",
     "release_notes/release_notes_notification_unittest.cc",
     "release_notes/release_notes_storage_unittest.cc",
-    "resource_reporter/resource_reporter_unittest.cc",
     "scheduler_configuration_manager_unittest.cc",
     "session_length_limiter_unittest.cc",
     "settings/cros_settings_unittest.cc",
diff --git a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
index 8238225..621c59e 100644
--- a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
+++ b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
@@ -529,6 +529,12 @@
   // Press Search+/ to enter ChromeVox's "find in page".
   sm_.Call([this]() { SendKeyPressWithSearch(ui::VKEY_OEM_2); });
   sm_.ExpectSpeech("Find in page");
+  sm_.ExpectSpeech("Search");
+  sm_.ExpectSpeech(
+      "Type to search the page. Press enter to jump to the result, up or down "
+      "arrows to browse results, keep typing to change your search, or escape "
+      "to cancel.");
+
   sm_.Replay();
 }
 
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index 0e5f6f7..895aa01f 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -107,7 +107,6 @@
 #include "chrome/browser/chromeos/power/smart_charging/smart_charging_manager.h"
 #include "chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "chrome/browser/chromeos/resource_reporter/resource_reporter.h"
 #include "chrome/browser/chromeos/scheduler_configuration_manager.h"
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
 #include "chrome/browser/chromeos/settings/shutdown_policy_forwarder.h"
@@ -578,9 +577,6 @@
   network_throttling_observer_.reset(
       new NetworkThrottlingObserver(g_browser_process->local_state()));
 
-  ResourceReporter::GetInstance()->StartMonitoring(
-      task_manager::TaskManagerInterface::GetTaskManager());
-
   discover_manager_ = std::make_unique<DiscoverManager>();
 
   g_browser_process->platform_part()->InitializeSchedulerConfigurationManager();
@@ -1005,8 +1001,6 @@
 void ChromeBrowserMainPartsChromeos::PostMainMessageLoopRun() {
   crostini_unsupported_action_notifier_.reset();
 
-  ResourceReporter::GetInstance()->StopMonitoring();
-
   BootTimesRecorder::Get()->AddLogoutTimeMarker("UIMessageLoopEnded", true);
 
   if (lock_screen_apps_state_controller_)
diff --git a/chrome/browser/chromeos/login/eula_browsertest.cc b/chrome/browser/chromeos/login/eula_browsertest.cc
index 923ecc05..9d6ef2f 100644
--- a/chrome/browser/chromeos/login/eula_browsertest.cc
+++ b/chrome/browser/chromeos/login/eula_browsertest.cc
@@ -15,6 +15,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/task/post_task.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "build/branding_buildflags.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/login/test/dialog_window_waiter.h"
@@ -43,12 +44,14 @@
 #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"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 
 using net::test_server::BasicHttpResponse;
 using net::test_server::HttpRequest;
 using net::test_server::HttpResponse;
+using ::testing::ElementsAre;
 
 namespace chromeos {
 namespace {
@@ -302,6 +305,7 @@
 // Tests that clicking on "System security settings" button opens a dialog
 // showing the TPM password.
 IN_PROC_BROWSER_TEST_F(EulaTest, DisplaysTpmPassword) {
+  base::HistogramTester histogram_tester;
   ShowEulaScreen();
 
   NonPolymerOobeJS().TapOnPath({"oobe-eula-md", "installationSettings"});
@@ -315,12 +319,16 @@
   test::OobeJS().ExpectEQ(
       "$('oobe-eula-md').$$('#eula-password').textContent.trim()",
       std::string(FakeCryptohomeClient::kStubTpmPassword));
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples("OOBE.EulaScreen.UserActions"),
+      ElementsAre(base::Bucket(
+          static_cast<int>(EulaScreen::UserAction::kShowSecuritySettings), 1)));
 }
 
 // Verifies statistic collection accepted flow.
 // Advaces to the next screen and verifies stats collection is enabled.
-// Flaky on LSAN/ASAN: crbug.com/952482.
 IN_PROC_BROWSER_TEST_F(EulaTest, EnableUsageStats) {
+  base::HistogramTester histogram_tester;
   ShowEulaScreen();
 
   // Verify that toggle is enabled by default.
@@ -340,6 +348,9 @@
   auto subscription =
       StatsReportingController::Get()->AddObserver(runloop.QuitClosure());
 
+  // Enable and disable usageStats that to see that metrics are recorded.
+  NonPolymerOobeJS().TapOnPath({"oobe-eula-md", "usageStats"});
+  NonPolymerOobeJS().TapOnPath({"oobe-eula-md", "usageStats"});
   // Advance to the next screen for changes to take effect.
   test::OobeJS().TapOnPath({"oobe-eula-md", "acceptButton"});
 
@@ -351,11 +362,22 @@
   EXPECT_TRUE(g_browser_process->local_state()->GetBoolean(
       metrics::prefs::kMetricsReportingEnabled));
   EXPECT_TRUE(GetGoogleCollectStatsConsent());
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples("OOBE.EulaScreen.UserActions"),
+      ElementsAre(
+          base::Bucket(
+              static_cast<int>(EulaScreen::UserAction::kAcceptButtonClicked),
+              1),
+          base::Bucket(
+              static_cast<int>(EulaScreen::UserAction::kUnselectStatsUsage), 1),
+          base::Bucket(
+              static_cast<int>(EulaScreen::UserAction::kSelectStatsUsage), 1)));
 }
 
 // Verify statistic collection denied flow. Clicks on usage stats toggle,
 // advaces to the next screen and verifies stats collection is disabled.
 IN_PROC_BROWSER_TEST_F(EulaTest, DisableUsageStats) {
+  base::HistogramTester histogram_tester;
   ShowEulaScreen();
 
   // Verify that toggle is enabled by default.
@@ -388,10 +410,20 @@
   EXPECT_FALSE(g_browser_process->local_state()->GetBoolean(
       metrics::prefs::kMetricsReportingEnabled));
   EXPECT_FALSE(GetGoogleCollectStatsConsent());
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples("OOBE.EulaScreen.UserActions"),
+      ElementsAre(
+          base::Bucket(
+              static_cast<int>(EulaScreen::UserAction::kAcceptButtonClicked),
+              1),
+          base::Bucket(
+              static_cast<int>(EulaScreen::UserAction::kUnselectStatsUsage),
+              1)));
 }
 
 // Tests that clicking on "Learn more" button opens a help dialog.
 IN_PROC_BROWSER_TEST_F(EulaTest, LearnMore) {
+  base::HistogramTester histogram_tester;
   ShowEulaScreen();
 
   // Load HelperApp extension.
@@ -405,6 +437,38 @@
 
   // Wait until help dialog is displayed.
   waiter.Wait();
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples("OOBE.EulaScreen.UserActions"),
+      ElementsAre(base::Bucket(
+          static_cast<int>(EulaScreen::UserAction::kShowStatsUsageLearnMore),
+          1)));
+}
+
+// Tests that "Additional ToS" dialog could be opened and closed.
+IN_PROC_BROWSER_TEST_F(EulaTest, AdditionalToS) {
+  base::HistogramTester histogram_tester;
+  ShowEulaScreen();
+
+  NonPolymerOobeJS().TapOnPath({"oobe-eula-md", "additionalTerms"});
+
+  test::OobeJS()
+      .CreateWaiter(
+          test::GetOobeElementPath({"oobe-eula-md", "additional-tos"}) +
+          ".open")
+      ->Wait();
+
+  NonPolymerOobeJS().TapOnPath({"oobe-eula-md", "close-additional-tos"});
+
+  test::OobeJS()
+      .CreateWaiter(
+          test::GetOobeElementPath({"oobe-eula-md", "additional-tos"}) +
+          ".open === false")
+      ->Wait();
+
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples("OOBE.EulaScreen.UserActions"),
+      ElementsAre(base::Bucket(
+          static_cast<int>(EulaScreen::UserAction::kShowAdditionalTos), 1)));
 }
 
 }  // namespace
diff --git a/chrome/browser/chromeos/login/oobe_browsertest.cc b/chrome/browser/chromeos/login/oobe_browsertest.cc
index 2d2b6a919..1ce1cbcd 100644
--- a/chrome/browser/chromeos/login/oobe_browsertest.cc
+++ b/chrome/browser/chromeos/login/oobe_browsertest.cc
@@ -20,7 +20,6 @@
 #include "chrome/browser/ui/webui/signin/signin_utils.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
-#include "chromeos/constants/chromeos_features.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/dbus/cryptohome/fake_cryptohome_client.h"
 #include "chromeos/dbus/cryptohome/key.pb.h"
@@ -126,141 +125,4 @@
   OobeScreenWaiter(EnrollmentScreenView::kScreenId).Wait();
 }
 
-class GaiaActionButtonsTest : public OobeBaseTest {
- public:
-  GaiaActionButtonsTest() = default;
-  ~GaiaActionButtonsTest() override = default;
-
-  void SetUp() override {
-    scoped_feature_list_.InitAndEnableFeature(features::kGaiaActionButtons);
-    OobeBaseTest::SetUp();
-  }
-
-  void WaitForPageLoad() {
-    WaitForGaiaPageLoad();
-    // Also wait for the javascript page to load
-    GaiaJSChecker().CreateWaiter("gaia.chromeOSLogin.initialized_")->Wait();
-  }
-
-  // A JSChecker for the GAIA Webview
-  test::JSChecker GaiaJSChecker() { return SigninFrameJS(); }
-
-  // A JSChecker for the Oobe screen
-  test::JSChecker OobeJSChecker() const { return test::OobeJS(); }
-
-  void ExpectHidden(const std::string& button) const {
-    OobeJSChecker()
-        .CreateVisibilityWaiter(false, {module_name, button})
-        ->Wait();
-  }
-
-  void ExpectVisible(const std::string& button) const {
-    OobeJSChecker().CreateVisibilityWaiter(true, {module_name, button})->Wait();
-  }
-
-  void ExpectEnabled(const std::string& button) const {
-    OobeJSChecker().CreateEnabledWaiter(true, {module_name, button})->Wait();
-  }
-
-  void ExpectDisabled(const std::string& button) const {
-    OobeJSChecker().CreateEnabledWaiter(false, {module_name, button})->Wait();
-  }
-
-  void ExpectLabel(const std::string& button, const std::string& label) const {
-    ASSERT_EQ(GetLabel(button), label);
-  }
-
-  std::string GetLabel(const std::string& button) const {
-    const std::string js_path =
-        chromeos::test::GetOobeElementPath({module_name, button});
-    return test::OobeJS().GetString(js_path + ".innerText");
-  }
-
-  void EnablePrimaryButton() {
-    GaiaJSChecker().Evaluate("sendSetPrimaryActionLabel('label')");
-    GaiaJSChecker().Evaluate("sendSetPrimaryActionEnabled(true)");
-  }
-
- private:
-  FakeGaiaMixin fake_gaia_{&mixin_host_, embedded_test_server()};
-  base::test::ScopedFeatureList scoped_feature_list_{};
-  const char* module_name = "gaia-signin";
-
-  DISALLOW_COPY_AND_ASSIGN(GaiaActionButtonsTest);
-};
-
-IN_PROC_BROWSER_TEST_F(GaiaActionButtonsTest, PrimaryActionButtonLabel) {
-  WaitForPageLoad();
-
-  // Initially the button is hidden
-  ExpectHidden("primary-action-button");
-
-  // It is shown when the label is set
-  GaiaJSChecker().Evaluate("sendSetPrimaryActionLabel('the-label')");
-  ExpectVisible("primary-action-button");
-  ExpectLabel("primary-action-button", "the-label");
-
-  // It is hidden when the label is set to nill
-  GaiaJSChecker().Evaluate("sendSetPrimaryActionLabel(null)");
-  ExpectHidden("primary-action-button");
-}
-
-IN_PROC_BROWSER_TEST_F(GaiaActionButtonsTest, PrimaryActionButtonEnabled) {
-  WaitForPageLoad();
-
-  // Initially the button is enabled
-  ExpectEnabled("primary-action-button");
-
-  // It can be disabled
-  GaiaJSChecker().Evaluate("sendSetPrimaryActionEnabled(false)");
-  ExpectDisabled("primary-action-button");
-
-  // It can be enabled
-  GaiaJSChecker().Evaluate("sendSetPrimaryActionEnabled(true)");
-  ExpectEnabled("primary-action-button");
-}
-
-IN_PROC_BROWSER_TEST_F(GaiaActionButtonsTest, SecondaryActionButtonLabel) {
-  WaitForPageLoad();
-
-  // Initially the button is hidden
-  ExpectHidden("secondary-action-button");
-
-  // It is shown when the label is set
-  GaiaJSChecker().Evaluate("sendSetSecondaryActionLabel('the-label')");
-  ExpectVisible("secondary-action-button");
-  ExpectLabel("secondary-action-button", "the-label");
-
-  // It is hidden when the label is set to nill
-  GaiaJSChecker().Evaluate("sendSetSecondaryActionLabel(null)");
-  ExpectHidden("secondary-action-button");
-}
-
-IN_PROC_BROWSER_TEST_F(GaiaActionButtonsTest, SecondaryActionButtonEnabled) {
-  WaitForPageLoad();
-
-  // Initially the button is enabled
-  ExpectEnabled("secondary-action-button");
-
-  // It can be disabled
-  GaiaJSChecker().Evaluate("sendSetSecondaryActionEnabled(false)");
-  ExpectDisabled("secondary-action-button");
-
-  // It can be enabled
-  GaiaJSChecker().Evaluate("sendSetSecondaryActionEnabled(true)");
-  ExpectEnabled("secondary-action-button");
-}
-
-IN_PROC_BROWSER_TEST_F(GaiaActionButtonsTest, SetAllActionsEnabled) {
-  WaitForPageLoad();
-
-  GaiaJSChecker().Evaluate("sendSetAllActionsEnabled(false)");
-  ExpectDisabled("primary-action-button");
-  ExpectDisabled("secondary-action-button");
-
-  GaiaJSChecker().Evaluate("sendSetAllActionsEnabled(true)");
-  ExpectEnabled("primary-action-button");
-  ExpectEnabled("secondary-action-button");
-}
-
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/screens/eula_screen.cc b/chrome/browser/chromeos/login/screens/eula_screen.cc
index ed4c199..cdf298b 100644
--- a/chrome/browser/chromeos/login/screens/eula_screen.cc
+++ b/chrome/browser/chromeos/login/screens/eula_screen.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/customization/customization_document.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
@@ -20,6 +21,54 @@
 
 constexpr const char kUserActionAcceptButtonClicked[] = "accept-button";
 constexpr const char kUserActionBackButtonClicked[] = "back-button";
+constexpr const char kUserActionShowAdditionalTos[] = "show-additional-tos";
+constexpr const char kUserActionShowSecuritySettings[] =
+    "show-security-settings";
+constexpr const char kUserActionShowStatsUsageLearnMore[] =
+    "show-stats-usage-learn-more";
+constexpr const char kUserActionUnselectStatsUsage[] = "unselect-stats-usage";
+constexpr const char kUserActionSelectStatsUsage[] = "select-stats-usage";
+
+struct EulaUserAction {
+  const char* name_;
+  EulaScreen::UserAction uma_name_;
+};
+
+const EulaUserAction actions[] = {
+    {kUserActionAcceptButtonClicked,
+     EulaScreen::UserAction::kAcceptButtonClicked},
+    {kUserActionBackButtonClicked, EulaScreen::UserAction::kBackButtonClicked},
+    {kUserActionShowAdditionalTos, EulaScreen::UserAction::kShowAdditionalTos},
+    {kUserActionShowSecuritySettings,
+     EulaScreen::UserAction::kShowSecuritySettings},
+    {kUserActionShowStatsUsageLearnMore,
+     EulaScreen::UserAction::kShowStatsUsageLearnMore},
+    {kUserActionUnselectStatsUsage,
+     EulaScreen::UserAction::kUnselectStatsUsage},
+    {kUserActionSelectStatsUsage, EulaScreen::UserAction::kSelectStatsUsage},
+};
+
+void RecordEulaScreenAction(EulaScreen::UserAction value) {
+  base::UmaHistogramEnumeration("OOBE.EulaScreen.UserActions", value);
+}
+
+bool IsEulaUserAction(const std::string& action_id) {
+  for (const auto& el : actions) {
+    if (action_id == el.name_)
+      return true;
+  }
+  return false;
+}
+
+void RecordUserAction(const std::string& action_id) {
+  for (const auto& el : actions) {
+    if (action_id == el.name_) {
+      RecordEulaScreenAction(el.uma_name_);
+      return;
+    }
+  }
+  NOTREACHED() << "Unexpected action id: " << action_id;
+}
 
 // Reflects the value of usage statistics reporting checkbox shown in eula
 // UI. The value is expected to survive EulaScreen res-hows within a single
@@ -93,14 +142,17 @@
 }
 
 void EulaScreen::OnUserAction(const std::string& action_id) {
+  if (!IsEulaUserAction(action_id)) {
+    BaseScreen::OnUserAction(action_id);
+    return;
+  }
+  RecordUserAction(action_id);
   if (action_id == kUserActionAcceptButtonClicked) {
     exit_callback_.Run(g_usage_statistics_reporting_enabled
                            ? Result::ACCEPTED_WITH_USAGE_STATS_REPORTING
                            : Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
   } else if (action_id == kUserActionBackButtonClicked) {
     exit_callback_.Run(Result::BACK);
-  } else {
-    BaseScreen::OnUserAction(action_id);
   }
 }
 
diff --git a/chrome/browser/chromeos/login/screens/eula_screen.h b/chrome/browser/chromeos/login/screens/eula_screen.h
index c518dc3..6af145a5 100644
--- a/chrome/browser/chromeos/login/screens/eula_screen.h
+++ b/chrome/browser/chromeos/login/screens/eula_screen.h
@@ -31,6 +31,21 @@
     BACK
   };
 
+  // This enum is tied directly to a UMA enum defined in
+  // //tools/metrics/histograms/enums.xml, and should always reflect it (do not
+  // change one without changing the other).  Entries should be never modified
+  // or deleted.  Only additions possible.
+  enum class UserAction {
+    kAcceptButtonClicked = 0,
+    kBackButtonClicked = 1,
+    kShowAdditionalTos = 2,
+    kShowSecuritySettings = 3,
+    kShowStatsUsageLearnMore = 4,
+    kUnselectStatsUsage = 5,
+    kSelectStatsUsage = 6,
+    kMaxValue = kSelectStatsUsage
+  };
+
   static std::string GetResultString(Result result);
 
   using ScreenExitCallback = base::RepeatingCallback<void(Result result)>;
diff --git a/chrome/browser/chromeos/login/screens/recommend_apps_screen.cc b/chrome/browser/chromeos/login/screens/recommend_apps_screen.cc
index 11369db..90d69211 100644
--- a/chrome/browser/chromeos/login/screens/recommend_apps_screen.cc
+++ b/chrome/browser/chromeos/login/screens/recommend_apps_screen.cc
@@ -19,6 +19,8 @@
       return "Selected";
     case Result::SKIPPED:
       return "Skipped";
+    case Result::LOAD_ERROR:
+      return "LoadError";
     case Result::NOT_APPLICABLE:
       return BaseScreen::kNotApplicable;
   }
@@ -45,10 +47,6 @@
   exit_callback_.Run(Result::SKIPPED);
 }
 
-void RecommendAppsScreen::OnRetry() {
-  recommend_apps_fetcher_->Retry();
-}
-
 void RecommendAppsScreen::OnInstall() {
   exit_callback_.Run(Result::SELECTED);
 }
@@ -90,8 +88,7 @@
 }
 
 void RecommendAppsScreen::OnLoadError() {
-  if (view_)
-    view_->OnLoadError();
+  exit_callback_.Run(Result::LOAD_ERROR);
 }
 
 void RecommendAppsScreen::OnParseResponseError() {
diff --git a/chrome/browser/chromeos/login/screens/recommend_apps_screen.h b/chrome/browser/chromeos/login/screens/recommend_apps_screen.h
index 759c37a..b18bf72 100644
--- a/chrome/browser/chromeos/login/screens/recommend_apps_screen.h
+++ b/chrome/browser/chromeos/login/screens/recommend_apps_screen.h
@@ -27,7 +27,7 @@
 class RecommendAppsScreen : public BaseScreen,
                             public RecommendAppsFetcherDelegate {
  public:
-  enum class Result { SELECTED, SKIPPED, NOT_APPLICABLE };
+  enum class Result { SELECTED, SKIPPED, NOT_APPLICABLE, LOAD_ERROR };
 
   static std::string GetResultString(Result result);
 
diff --git a/chrome/browser/chromeos/login/screens/recommend_apps_screen_browsertest.cc b/chrome/browser/chromeos/login/screens/recommend_apps_screen_browsertest.cc
index 187ab9b4..3423df0 100644
--- a/chrome/browser/chromeos/login/screens/recommend_apps_screen_browsertest.cc
+++ b/chrome/browser/chromeos/login/screens/recommend_apps_screen_browsertest.cc
@@ -271,9 +271,6 @@
       "recommend-apps-screen", "recommend-apps-install-button"};
   const std::initializer_list<base::StringPiece> skip_button = {
       "recommend-apps-screen", "recommend-apps-skip-button"};
-  const std::initializer_list<base::StringPiece> retry_button = {
-      "recommend-apps-screen", "recommend-apps-retry-button"};
-
   test::OobeJS().ExpectDisabledPath(install_button);
 
   test::OobeJS()
@@ -285,14 +282,12 @@
   test::OobeJS().ExpectDisabledPath(install_button);
   test::OobeJS().ExpectPathDisplayed(true, skip_button);
   test::OobeJS().ExpectEnabledPath(skip_button);
-  test::OobeJS().ExpectPathDisplayed(false, retry_button);
 
   ASSERT_TRUE(ToggleAppsSelection(
       webview_path, "['test.app.foo.app1', 'test.app.foo.app2']"));
 
   test::OobeJS().CreateEnabledWaiter(true, install_button)->Wait();
   test::OobeJS().ExpectEnabledPath(skip_button);
-  test::OobeJS().ExpectPathDisplayed(false, retry_button);
 
   test::OobeJS().TapOnPath(install_button);
 
@@ -342,8 +337,6 @@
       "recommend-apps-screen", "recommend-apps-install-button"};
   const std::initializer_list<base::StringPiece> skip_button = {
       "recommend-apps-screen", "recommend-apps-skip-button"};
-  const std::initializer_list<base::StringPiece> retry_button = {
-      "recommend-apps-screen", "recommend-apps-retry-button"};
 
   test::OobeJS().ExpectDisabledPath(install_button);
 
@@ -356,14 +349,12 @@
   test::OobeJS().ExpectDisabledPath(install_button);
   test::OobeJS().ExpectPathDisplayed(true, skip_button);
   test::OobeJS().ExpectEnabledPath(skip_button);
-  test::OobeJS().ExpectPathDisplayed(false, retry_button);
 
   ASSERT_TRUE(ToggleAppsSelection(
       webview_path, "['test.app.foo.app1', 'test.app.foo.app2']"));
 
   test::OobeJS().CreateEnabledWaiter(true, install_button)->Wait();
   test::OobeJS().ExpectEnabledPath(skip_button);
-  test::OobeJS().ExpectPathDisplayed(false, retry_button);
 
   ASSERT_TRUE(ToggleAppsSelection(webview_path, "['test.app.foo.app1']"));
 
@@ -414,8 +405,6 @@
       "recommend-apps-screen", "recommend-apps-install-button"};
   const std::initializer_list<base::StringPiece> skip_button = {
       "recommend-apps-screen", "recommend-apps-skip-button"};
-  const std::initializer_list<base::StringPiece> retry_button = {
-      "recommend-apps-screen", "recommend-apps-retry-button"};
 
   test::OobeJS().ExpectDisabledPath(install_button);
 
@@ -428,13 +417,11 @@
   test::OobeJS().ExpectDisabledPath(install_button);
   test::OobeJS().ExpectPathDisplayed(true, skip_button);
   test::OobeJS().ExpectEnabledPath(skip_button);
-  test::OobeJS().ExpectPathDisplayed(false, retry_button);
 
   ASSERT_TRUE(ToggleAppsSelection(webview_path, "['test.app.foo.app2']"));
 
   test::OobeJS().CreateEnabledWaiter(true, install_button)->Wait();
   test::OobeJS().ExpectEnabledPath(skip_button);
-  test::OobeJS().ExpectPathDisplayed(false, retry_button);
 
   test::OobeJS().TapOnPath(skip_button);
 
@@ -480,8 +467,6 @@
       "recommend-apps-screen", "recommend-apps-install-button"};
   const std::initializer_list<base::StringPiece> skip_button = {
       "recommend-apps-screen", "recommend-apps-skip-button"};
-  const std::initializer_list<base::StringPiece> retry_button = {
-      "recommend-apps-screen", "recommend-apps-retry-button"};
 
   test::OobeJS().ExpectDisabledPath(install_button);
 
@@ -494,19 +479,16 @@
   test::OobeJS().ExpectDisabledPath(install_button);
   test::OobeJS().ExpectPathDisplayed(true, skip_button);
   test::OobeJS().ExpectEnabledPath(skip_button);
-  test::OobeJS().ExpectPathDisplayed(false, retry_button);
 
   ASSERT_TRUE(ToggleAppsSelection(webview_path, "['test.app.foo.app2']"));
 
   test::OobeJS().CreateEnabledWaiter(true, install_button)->Wait();
   test::OobeJS().ExpectEnabledPath(skip_button);
-  test::OobeJS().ExpectPathDisplayed(false, retry_button);
 
   ASSERT_TRUE(ToggleAppsSelection(webview_path, "['test.app.foo.app2']"));
 
   test::OobeJS().CreateEnabledWaiter(false, install_button)->Wait();
   test::OobeJS().ExpectEnabledPath(skip_button);
-  test::OobeJS().ExpectPathDisplayed(false, retry_button);
 
   test::OobeJS().TapOnPath(skip_button);
 
@@ -592,13 +574,10 @@
       "recommend-apps-screen", "recommend-apps-install-button"};
   const std::initializer_list<base::StringPiece> skip_button = {
       "recommend-apps-screen", "recommend-apps-skip-button"};
-  const std::initializer_list<base::StringPiece> retry_button = {
-      "recommend-apps-screen", "recommend-apps-retry-button"};
 
   test::OobeJS().CreateDisplayedWaiter(true, skip_button)->Wait();
   test::OobeJS().ExpectEnabledPath(skip_button);
   test::OobeJS().ExpectDisabledPath(install_button);
-  test::OobeJS().ExpectPathDisplayed(false, retry_button);
 
   test::OobeJS().TapOnPath(skip_button);
 
@@ -634,125 +613,6 @@
   EXPECT_EQ(0, recommend_apps_fetcher_->retries());
 }
 
-IN_PROC_BROWSER_TEST_F(RecommendAppsScreenTest, SkipOnLoadError) {
-  recommend_apps_screen_->Show();
-
-  OobeScreenWaiter screen_waiter(RecommendAppsScreenView::kScreenId);
-  screen_waiter.set_assert_next_screen();
-  screen_waiter.Wait();
-
-  // Wait for loading screen.
-  test::OobeJS()
-      .CreateVisibilityWaiter(true, {"recommend-apps-loading"})
-      ->Wait();
-  test::OobeJS().ExpectHidden("recommend-apps-screen");
-
-  recommend_apps_fetcher_->SimulateLoadError();
-
-  test::OobeJS()
-      .CreateVisibilityWaiter(true, {"recommend-apps-screen"})
-      ->Wait();
-  test::OobeJS().ExpectHidden("recommend-apps-loading");
-
-  const std::initializer_list<base::StringPiece> install_button = {
-      "recommend-apps-screen", "recommend-apps-install-button"};
-  const std::initializer_list<base::StringPiece> skip_button = {
-      "recommend-apps-screen", "recommend-apps-skip-button"};
-  const std::initializer_list<base::StringPiece> retry_button = {
-      "recommend-apps-screen", "recommend-apps-retry-button"};
-
-  test::OobeJS().CreateDisplayedWaiter(true, skip_button)->Wait();
-  test::OobeJS().ExpectEnabledPath(skip_button);
-  test::OobeJS().CreateDisplayedWaiter(true, retry_button)->Wait();
-  test::OobeJS().ExpectEnabledPath(retry_button);
-  test::OobeJS().ExpectPathDisplayed(false, install_button);
-
-  test::OobeJS().TapOnPath(skip_button);
-
-  WaitForScreenExit();
-  EXPECT_EQ(RecommendAppsScreen::Result::SKIPPED, screen_result_.value());
-  EXPECT_EQ(0, recommend_apps_fetcher_->retries());
-
-  const base::Value* fast_reinstall_packages =
-      ProfileManager::GetActiveUserProfile()->GetPrefs()->Get(
-          arc::prefs::kArcFastAppReinstallPackages);
-  ASSERT_TRUE(fast_reinstall_packages);
-  EXPECT_EQ(base::Value(base::Value::Type::LIST), *fast_reinstall_packages);
-}
-
-IN_PROC_BROWSER_TEST_F(RecommendAppsScreenTest, RetryOnLoadError) {
-  recommend_apps_screen_->Show();
-
-  OobeScreenWaiter screen_waiter(RecommendAppsScreenView::kScreenId);
-  screen_waiter.set_assert_next_screen();
-  screen_waiter.Wait();
-
-  // Wait for loading screen.
-  test::OobeJS()
-      .CreateVisibilityWaiter(true, {"recommend-apps-loading"})
-      ->Wait();
-  test::OobeJS().ExpectHidden("recommend-apps-screen");
-
-  recommend_apps_fetcher_->SimulateLoadError();
-
-  test::OobeJS()
-      .CreateVisibilityWaiter(true, {"recommend-apps-screen"})
-      ->Wait();
-  test::OobeJS().ExpectHidden("recommend-apps-loading");
-
-  const std::initializer_list<base::StringPiece> install_button = {
-      "recommend-apps-screen", "recommend-apps-install-button"};
-  const std::initializer_list<base::StringPiece> skip_button = {
-      "recommend-apps-screen", "recommend-apps-skip-button"};
-  const std::initializer_list<base::StringPiece> retry_button = {
-      "recommend-apps-screen", "recommend-apps-retry-button"};
-
-  test::OobeJS().CreateDisplayedWaiter(true, skip_button)->Wait();
-  test::OobeJS().ExpectEnabledPath(skip_button);
-  test::OobeJS().CreateDisplayedWaiter(true, retry_button)->Wait();
-  test::OobeJS().ExpectEnabledPath(retry_button);
-  test::OobeJS().ExpectPathDisplayed(false, install_button);
-
-  test::OobeJS().TapOnPath(retry_button);
-
-  EXPECT_EQ(1, recommend_apps_fetcher_->retries());
-
-  test::OobeJS()
-      .CreateVisibilityWaiter(false, {"recommend-apps-screen"})
-      ->Wait();
-  test::OobeJS().ExpectVisible("recommend-apps-loading");
-
-  std::vector<FakeAppInfo> test_apps = {
-      FakeAppInfo("test.app.foo.app1", "Test app 1")};
-  recommend_apps_fetcher_->SimulateSuccess(test_apps);
-
-  test::OobeJS()
-      .CreateVisibilityWaiter(true, {"recommend-apps-screen"})
-      ->Wait();
-  test::OobeJS().ExpectHidden("recommend-apps-loading");
-
-  const std::string webview_path =
-      test::GetOobeElementPath({"recommend-apps-screen", "app-list-view"});
-  test::OobeJS()
-      .CreateDisplayedWaiter(true, {"recommend-apps-screen", "app-list-view"})
-      ->Wait();
-  ASSERT_TRUE(WaitForAppListSize(webview_path, test_apps.size()));
-
-  test::OobeJS().ExpectPathDisplayed(true, install_button);
-  test::OobeJS().ExpectDisabledPath(install_button);
-  test::OobeJS().ExpectPathDisplayed(true, skip_button);
-  test::OobeJS().ExpectEnabledPath(skip_button);
-  test::OobeJS().ExpectPathDisplayed(false, retry_button);
-
-  EXPECT_FALSE(screen_result_.has_value());
-  EXPECT_EQ(1, recommend_apps_fetcher_->retries());
-
-  const base::Value* fast_reinstall_packages =
-      ProfileManager::GetActiveUserProfile()->GetPrefs()->Get(
-          arc::prefs::kArcFastAppReinstallPackages);
-  ASSERT_TRUE(fast_reinstall_packages);
-  EXPECT_EQ(base::Value(base::Value::Type::LIST), *fast_reinstall_packages);
-}
 
 IN_PROC_BROWSER_TEST_F(RecommendAppsScreenTest, SkipDueToManagedUser) {
   GetProfilePolicyConnector()->OverrideIsManagedForTesting(true);
diff --git a/chrome/browser/chromeos/login/webview_login_browsertest.cc b/chrome/browser/chromeos/login/webview_login_browsertest.cc
index 89f473c..b605364 100644
--- a/chrome/browser/chromeos/login/webview_login_browsertest.cc
+++ b/chrome/browser/chromeos/login/webview_login_browsertest.cc
@@ -45,6 +45,7 @@
 #include "chrome/browser/ui/webui/chromeos/login/eula_screen_handler.h"
 #include "chrome/browser/ui/webui/signin/signin_utils.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/dbus/cryptohome/fake_cryptohome_client.h"
 #include "chromeos/dbus/session_manager/fake_session_manager_client.h"
@@ -89,6 +90,11 @@
 constexpr char kTestCookieValue[] = "present";
 constexpr char kTestCookieHost[] = "host1.com";
 
+constexpr std::initializer_list<base::StringPiece> kPrimaryButton = {
+    "gaia-signin", "primary-action-button"};
+constexpr std::initializer_list<base::StringPiece> kSecondaryButton = {
+    "gaia-signin", "secondary-action-button"};
+
 void InjectCookieDoneCallback(
     base::OnceClosure done_closure,
     net::CanonicalCookie::CookieInclusionStatus status) {
@@ -183,7 +189,9 @@
 
 class WebviewLoginTest : public OobeBaseTest {
  public:
-  WebviewLoginTest() = default;
+  WebviewLoginTest() {
+    scoped_feature_list_.InitWithFeatures({features::kGaiaActionButtons}, {});
+  }
   ~WebviewLoginTest() override = default;
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
@@ -247,10 +255,59 @@
   FakeGaiaMixin fake_gaia_{&mixin_host_, embedded_test_server()};
 
  private:
+  base::test::ScopedFeatureList scoped_feature_list_;
   DISALLOW_COPY_AND_ASSIGN(WebviewLoginTest);
 };
 
 // Basic signin with username and password.
+IN_PROC_BROWSER_TEST_F(WebviewLoginTest, NativeTest) {
+  WaitForGaiaPageLoadAndPropertyUpdate();
+  ExpectIdentifierPage();
+  SigninFrameJS().TypeIntoPath(FakeGaiaMixin::kFakeUserEmail, {"identifier"});
+  test::OobeJS().ClickOnPath(kPrimaryButton);
+  WaitForGaiaPageBackButtonUpdate();
+  ExpectPasswordPage();
+
+  test::OobeJS().ExpectVisiblePath(kSecondaryButton);
+  test::OobeJS().ExpectEnabledPath(kSecondaryButton);
+
+  // Check events propagation.
+  SigninFrameJS().ExecuteAsync("sendSetAllActionsEnabled(false)");
+  test::OobeJS().CreateEnabledWaiter(false, kPrimaryButton)->Wait();
+  test::OobeJS().CreateEnabledWaiter(false, kSecondaryButton)->Wait();
+  test::OobeJS().ExpectVisiblePath(kPrimaryButton);
+  test::OobeJS().ExpectVisiblePath(kSecondaryButton);
+
+  SigninFrameJS().ExecuteAsync("sendSetSecondaryActionEnabled(true)");
+  test::OobeJS().CreateEnabledWaiter(true, kSecondaryButton)->Wait();
+  test::OobeJS().ExpectVisiblePath(kSecondaryButton);
+
+  // Click on the secondary button disables it.
+  test::OobeJS().ClickOnPath(kSecondaryButton);
+  test::OobeJS().CreateEnabledWaiter(false, kSecondaryButton)->Wait();
+
+  SigninFrameJS().ExecuteAsync("sendSetPrimaryActionEnabled(true)");
+  test::OobeJS().CreateEnabledWaiter(true, kPrimaryButton)->Wait();
+  test::OobeJS().ExpectVisiblePath(kPrimaryButton);
+
+  SigninFrameJS().ExecuteAsync("sendSetPrimaryActionLabel(null)");
+  test::OobeJS().CreateVisibilityWaiter(false, kPrimaryButton)->Wait();
+
+  SigninFrameJS().ExecuteAsync("sendSetSecondaryActionLabel(null)");
+  test::OobeJS().CreateVisibilityWaiter(false, kSecondaryButton)->Wait();
+
+  SigninFrameJS().ExecuteAsync("sendSetPrimaryActionLabel('Submit')");
+  test::OobeJS().CreateVisibilityWaiter(true, kPrimaryButton)->Wait();
+  test::OobeJS().ExpectElementText("Submit", kPrimaryButton);
+
+  SigninFrameJS().TypeIntoPath("[]", {"services"});
+  SigninFrameJS().TypeIntoPath(FakeGaiaMixin::kFakeUserPassword, {"password"});
+  test::OobeJS().ClickOnPath(kPrimaryButton);
+
+  test::WaitForPrimaryUserSessionStart();
+}
+
+// Basic signin with username and password.
 IN_PROC_BROWSER_TEST_F(WebviewLoginTest, Basic) {
   base::HistogramTester histogram_tester;
   WaitForGaiaPageLoadAndPropertyUpdate();
@@ -291,7 +348,7 @@
 
   // Move to password page.
   SigninFrameJS().TypeIntoPath(FakeGaiaMixin::kFakeUserEmail, {"identifier"});
-  SigninFrameJS().TapOn("nextButton");
+  test::OobeJS().ClickOnPath(kPrimaryButton);
   WaitForGaiaPageBackButtonUpdate();
   ExpectPasswordPage();
 
@@ -300,14 +357,14 @@
   WaitForGaiaPageBackButtonUpdate();
   ExpectIdentifierPage();
   // Click next to password page, user id is remembered.
-  SigninFrameJS().TapOn("nextButton");
+  test::OobeJS().ClickOnPath(kPrimaryButton);
   WaitForGaiaPageBackButtonUpdate();
   ExpectPasswordPage();
 
   // Finish sign-up.
   SigninFrameJS().TypeIntoPath("[]", {"services"});
   SigninFrameJS().TypeIntoPath(FakeGaiaMixin::kFakeUserPassword, {"password"});
-  SigninFrameJS().TapOn("nextButton");
+  test::OobeJS().ClickOnPath(kPrimaryButton);
 
   test::WaitForPrimaryUserSessionStart();
 }
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index c75cc91..dba56768 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -1176,6 +1176,7 @@
       break;
     case RecommendAppsScreen::Result::SKIPPED:
     case RecommendAppsScreen::Result::NOT_APPLICABLE:
+    case RecommendAppsScreen::Result::LOAD_ERROR:
       ShowAssistantOptInFlowScreen();
       break;
   }
diff --git a/chrome/browser/chromeos/resource_reporter/OWNERS b/chrome/browser/chromeos/resource_reporter/OWNERS
deleted file mode 100644
index e3e4f11..0000000
--- a/chrome/browser/chromeos/resource_reporter/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-afakhry@chromium.org
-nick@chromium.org
\ No newline at end of file
diff --git a/chrome/browser/chromeos/resource_reporter/resource_reporter.cc b/chrome/browser/chromeos/resource_reporter/resource_reporter.cc
deleted file mode 100644
index cb5c0f0..0000000
--- a/chrome/browser/chromeos/resource_reporter/resource_reporter.cc
+++ /dev/null
@@ -1,433 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/resource_reporter/resource_reporter.h"
-
-#include <cstdint>
-#include <queue>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/memory/memory_pressure_monitor.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/rand_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/system/sys_info.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/task_manager/task_manager_interface.h"
-#include "components/rappor/rappor_service_impl.h"
-#include "content/public/browser/browser_thread.h"
-
-namespace chromeos {
-
-namespace {
-
-#define GET_ENUM_VAL(enum_entry) static_cast<int>(enum_entry)
-
-// At a critical memory pressure event, we only care about a single complete
-// refresh from the task manager (with background calculations). So we request
-// the minimum refresh rate (once per second).
-constexpr int64_t kRefreshIntervalSeconds = 1;
-
-// Various memory usage sizes in bytes.
-constexpr int64_t kMemory1GB = 1024 * 1024 * 1024;
-constexpr int64_t kMemory800MB = 800 * 1024 * 1024;
-constexpr int64_t kMemory600MB = 600 * 1024 * 1024;
-constexpr int64_t kMemory400MB = 400 * 1024 * 1024;
-constexpr int64_t kMemory200MB = 200 * 1024 * 1024;
-
-// The name of the Rappor metric to report the CPU usage.
-constexpr char kCpuRapporMetric[] = "ResourceReporter.Cpu";
-
-// The name of the Rappor metric to report the memory usage.
-constexpr char kMemoryRapporMetric[] = "ResourceReporter.Memory";
-
-// The name of the string field of the Rappor metrics in which we'll record the
-// task's Rappor sample name.
-constexpr char kRapporTaskStringField[] = "task";
-
-// The name of the flags field of the Rappor metrics in which we'll store the
-// priority of the process on which the task is running.
-constexpr char kRapporPriorityFlagsField[] = "priority";
-
-// The name of the flags field of the CPU usage Rappor metrics in which we'll
-// record the number of cores in the current system.
-constexpr char kRapporNumCoresRangeFlagsField[] = "num_cores_range";
-
-// The name of the flags field of the Rappor metrics in which we'll store the
-// CPU / memory usage ranges.
-constexpr char kRapporUsageRangeFlagsField[] = "usage_range";
-
-// Key used to store the last time a Rappor report was recorded in local_state.
-constexpr char kLastRapporReportTimeKey[] =
-    "resource_reporter.last_report_time";
-
-// To keep privacy guarantees of Rappor, we limit the reports to at most once
-// per day.
-constexpr base::TimeDelta kMinimumTimeBetweenReports =
-    base::TimeDelta::FromDays(1);
-
-constexpr double kTaskCpuThresholdForReporting = 70.0;
-
-}  // namespace
-
-ResourceReporter::TaskRecord::TaskRecord(task_manager::TaskId task_id)
-    : id(task_id), cpu_percent(0.0), memory_bytes(0), is_background(false) {}
-
-ResourceReporter::TaskRecord::TaskRecord(task_manager::TaskId the_id,
-                                         const std::string& task_name,
-                                         double cpu_percent,
-                                         int64_t memory_bytes,
-                                         bool background)
-    : id(the_id),
-      task_name_for_rappor(task_name),
-      cpu_percent(cpu_percent),
-      memory_bytes(memory_bytes),
-      is_background(background) {}
-
-ResourceReporter::~ResourceReporter() {
-}
-
-// static
-ResourceReporter* ResourceReporter::GetInstance() {
-  return base::Singleton<ResourceReporter>::get();
-}
-
-// static
-void ResourceReporter::RegisterPrefs(PrefRegistrySimple* registry) {
-  registry->RegisterDoublePref(kLastRapporReportTimeKey, 0.0);
-}
-
-void ResourceReporter::StartMonitoring(
-    task_manager::TaskManagerInterface* task_manager_to_observe) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  if (is_monitoring_)
-    return;
-
-  task_manager_to_observe_ = task_manager_to_observe;
-  DCHECK(task_manager_to_observe_);
-  is_monitoring_ = true;
-  memory_pressure_listener_.reset(new base::MemoryPressureListener(
-      base::Bind(&ResourceReporter::OnMemoryPressure, base::Unretained(this))));
-}
-
-void ResourceReporter::StopMonitoring() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  if (!is_monitoring_)
-    return;
-
-  // We might be shutting down right after a critical memory pressure event, and
-  // before we get an update from the task manager with all background
-  // calculations refreshed. In this case we must unregister from the task
-  // manager here.
-  StopRecordingCurrentState();
-
-  is_monitoring_ = false;
-  memory_pressure_listener_.reset();
-}
-
-void ResourceReporter::OnTasksRefreshedWithBackgroundCalculations(
-    const task_manager::TaskIdList& task_ids) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  task_records_.clear();
-  task_records_.reserve(task_ids.size());
-
-  for (const auto& id : task_ids) {
-    const double cpu_usage =
-        (observed_task_manager()->GetPlatformIndependentCPUUsage(id) /
-         base::SysInfo::NumberOfProcessors());
-    const int64_t memory_usage =
-        observed_task_manager()->GetMemoryFootprintUsage(id);
-
-    // Browser and GPU processes are reported later using UMA histograms as they
-    // don't have any privacy issues.
-    const auto task_type = observed_task_manager()->GetType(id);
-    switch (task_type) {
-      case task_manager::Task::UNKNOWN:
-      case task_manager::Task::ZYGOTE:
-        break;
-
-      case task_manager::Task::BROWSER:
-        last_browser_process_cpu_ = cpu_usage;
-        last_browser_process_memory_ = memory_usage >= 0 ? memory_usage : 0;
-        break;
-
-      case task_manager::Task::GPU:
-        last_gpu_process_cpu_ = cpu_usage;
-        last_gpu_process_memory_ = memory_usage >= 0 ? memory_usage : 0;
-        break;
-
-      default:
-        // Other tasks types will be reported using Rappor.
-        if (memory_usage < GetTaskMemoryThresholdForReporting() &&
-            cpu_usage < GetTaskCpuThresholdForReporting()) {
-          // We only care about CPU and memory intensive tasks.
-          break;
-        }
-
-        task_records_.emplace_back(
-            id, observed_task_manager()->GetTaskNameForRappor(id), cpu_usage,
-            memory_usage,
-            observed_task_manager()->IsTaskOnBackgroundedProcess(id));
-    }
-  }
-
-  // Now that we got the data, we don't need the task manager anymore.
-  if (base::MemoryPressureMonitor::Get()->GetCurrentPressureLevel() !=
-          MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_CRITICAL ||
-      !task_records_.empty()) {
-    // The memory pressure events are emitted once per second. In order to avoid
-    // unsubscribing and then resubscribing to the task manager again on the
-    // next event, we keep listening to the task manager as long as the memory
-    // pressure level is critical AND we couldn't find any violators yet.
-    StopRecordingCurrentState();
-  }
-
-  // Schedule reporting the samples.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&ResourceReporter::ReportSamples, base::Unretained(this)));
-}
-
-ResourceReporter::ResourceReporter()
-    : TaskManagerObserver(base::TimeDelta::FromSeconds(kRefreshIntervalSeconds),
-                          task_manager::REFRESH_TYPE_CPU |
-                              task_manager::REFRESH_TYPE_MEMORY_FOOTPRINT |
-                              task_manager::REFRESH_TYPE_PRIORITY),
-      task_manager_to_observe_(nullptr),
-      system_cpu_cores_range_(GetCurrentSystemCpuCoresRange()),
-      last_browser_process_cpu_(0.0),
-      last_gpu_process_cpu_(0.0),
-      last_browser_process_memory_(0),
-      last_gpu_process_memory_(0),
-      is_monitoring_(false) {}
-
-// static
-double ResourceReporter::GetTaskCpuThresholdForReporting() {
-  return kTaskCpuThresholdForReporting;
-}
-
-// static
-int64_t ResourceReporter::GetTaskMemoryThresholdForReporting() {
-  static const int64_t threshold = 0.6 *
-                                   base::SysInfo::AmountOfPhysicalMemory() /
-                                   base::SysInfo::NumberOfProcessors();
-  return threshold;
-}
-
-// static
-std::unique_ptr<rappor::Sample> ResourceReporter::CreateRapporSample(
-    rappor::RapporServiceImpl* rappor_service,
-    const ResourceReporter::TaskRecord& task_record) {
-  std::unique_ptr<rappor::Sample> sample(
-      rappor_service->CreateSample(rappor::UMA_RAPPOR_TYPE));
-  sample->SetStringField(kRapporTaskStringField,
-                         task_record.task_name_for_rappor);
-  sample->SetFlagsField(kRapporPriorityFlagsField,
-                        task_record.is_background ?
-                            GET_ENUM_VAL(TaskProcessPriority::BACKGROUND) :
-                            GET_ENUM_VAL(TaskProcessPriority::FOREGROUND),
-                        GET_ENUM_VAL(TaskProcessPriority::NUM_PRIORITIES));
-  return sample;
-}
-
-// static
-ResourceReporter::CpuUsageRange
-ResourceReporter::GetCpuUsageRange(double cpu) {
-  if (cpu > 60.0)
-    return CpuUsageRange::RANGE_ABOVE_60_PERCENT;
-  if (cpu > 30.0)
-    return CpuUsageRange::RANGE_30_TO_60_PERCENT;
-  if (cpu > 10.0)
-    return CpuUsageRange::RANGE_10_TO_30_PERCENT;
-
-  return CpuUsageRange::RANGE_0_TO_10_PERCENT;
-}
-
-// static
-ResourceReporter::MemoryUsageRange
-ResourceReporter::GetMemoryUsageRange(int64_t memory_in_bytes) {
-  if (memory_in_bytes > kMemory1GB)
-    return MemoryUsageRange::RANGE_ABOVE_1_GB;
-  if (memory_in_bytes > kMemory800MB)
-    return MemoryUsageRange::RANGE_800_TO_1_GB;
-  if (memory_in_bytes > kMemory600MB)
-    return MemoryUsageRange::RANGE_600_TO_800_MB;
-  if (memory_in_bytes > kMemory400MB)
-      return MemoryUsageRange::RANGE_400_TO_600_MB;
-  if (memory_in_bytes > kMemory200MB)
-      return MemoryUsageRange::RANGE_200_TO_400_MB;
-
-  return MemoryUsageRange::RANGE_0_TO_200_MB;
-}
-
-// static
-ResourceReporter::CpuCoresNumberRange
-ResourceReporter::GetCurrentSystemCpuCoresRange() {
-  const int cpus = base::SysInfo::NumberOfProcessors();
-
-  if (cpus > 16)
-    return CpuCoresNumberRange::RANGE_ABOVE_16_CORES;
-  if (cpus > 8)
-    return CpuCoresNumberRange::RANGE_9_TO_16_CORES;
-  if (cpus > 4)
-    return CpuCoresNumberRange::RANGE_5_TO_8_CORES;
-  if (cpus > 2)
-    return CpuCoresNumberRange::RANGE_3_TO_4_CORES;
-  if (cpus == 2)
-    return CpuCoresNumberRange::RANGE_2_CORES;
-  if (cpus == 1)
-    return CpuCoresNumberRange::RANGE_1_CORE;
-
-  NOTREACHED();
-  return CpuCoresNumberRange::RANGE_NA;
-}
-
-const ResourceReporter::TaskRecord* ResourceReporter::SampleTaskByCpu() const {
-  // Perform a weighted random sampling taking the tasks' CPU usage as their
-  // weights to randomly select one of them to be reported by Rappor. The higher
-  // the CPU usage, the higher the chance that the task will be selected.
-  // See https://en.wikipedia.org/wiki/Reservoir_sampling.
-  const TaskRecord* sampled_task = nullptr;
-  double cpu_weights_sum = 0;
-  for (const auto& task_data : task_records_) {
-    if ((base::RandDouble() * (cpu_weights_sum + task_data.cpu_percent)) >=
-        cpu_weights_sum) {
-      sampled_task = &task_data;
-    }
-    cpu_weights_sum += task_data.cpu_percent;
-  }
-
-  return sampled_task;
-}
-
-const ResourceReporter::TaskRecord*
-ResourceReporter::SampleTaskByMemory() const {
-  // Perform a weighted random sampling taking the tasks' memory usage as their
-  // weights to randomly select one of them to be reported by Rappor. The higher
-  // the memory usage, the higher the chance that the task will be selected.
-  // See https://en.wikipedia.org/wiki/Reservoir_sampling.
-  const TaskRecord* sampled_task = nullptr;
-  int64_t memory_weights_sum = 0;
-  for (const auto& task_data : task_records_) {
-    if ((base::RandDouble() * (memory_weights_sum + task_data.memory_bytes)) >=
-        memory_weights_sum) {
-      sampled_task = &task_data;
-    }
-    memory_weights_sum += task_data.memory_bytes;
-  }
-
-  return sampled_task;
-}
-
-void ResourceReporter::ReportSamples() {
-  // Report browser and GPU processes usage using UMA histograms.
-  UMA_HISTOGRAM_ENUMERATION(
-      "ResourceReporter.BrowserProcess.CpuUsage",
-      GET_ENUM_VAL(GetCpuUsageRange(last_browser_process_cpu_)),
-      GET_ENUM_VAL(CpuUsageRange::NUM_RANGES));
-  UMA_HISTOGRAM_ENUMERATION(
-      "ResourceReporter.BrowserProcess.MemoryUsage",
-      GET_ENUM_VAL(GetMemoryUsageRange(last_browser_process_memory_)),
-      GET_ENUM_VAL(MemoryUsageRange::NUM_RANGES));
-  UMA_HISTOGRAM_ENUMERATION(
-      "ResourceReporter.GpuProcess.CpuUsage",
-      GET_ENUM_VAL(GetCpuUsageRange(last_gpu_process_cpu_)),
-      GET_ENUM_VAL(CpuUsageRange::NUM_RANGES));
-  UMA_HISTOGRAM_ENUMERATION(
-      "ResourceReporter.GpuProcess.MemoryUsage",
-      GET_ENUM_VAL(GetMemoryUsageRange(last_gpu_process_memory_)),
-      GET_ENUM_VAL(MemoryUsageRange::NUM_RANGES));
-
-  // For the rest of tasks, report them using Rappor.
-  auto* rappor_service = g_browser_process->rappor_service();
-  if (!rappor_service || task_records_.empty())
-    return;
-
-  // We have samples to report via Rappor. Store 'now' as the time of the last
-  // report.
-  if (g_browser_process->local_state()) {
-    g_browser_process->local_state()->SetDouble(
-        kLastRapporReportTimeKey, base::Time::NowFromSystemTime().ToDoubleT());
-  }
-
-  // Use weighted random sampling to select a task to report in the CPU
-  // metric.
-  const TaskRecord* sampled_cpu_task = SampleTaskByCpu();
-  if (sampled_cpu_task) {
-    std::unique_ptr<rappor::Sample> cpu_sample(
-        CreateRapporSample(rappor_service, *sampled_cpu_task));
-    cpu_sample->SetFlagsField(kRapporNumCoresRangeFlagsField,
-                              GET_ENUM_VAL(system_cpu_cores_range_),
-                              GET_ENUM_VAL(CpuCoresNumberRange::NUM_RANGES));
-    cpu_sample->SetFlagsField(
-        kRapporUsageRangeFlagsField,
-        GET_ENUM_VAL(GetCpuUsageRange(sampled_cpu_task->cpu_percent)),
-        GET_ENUM_VAL(CpuUsageRange::NUM_RANGES));
-    rappor_service->RecordSample(kCpuRapporMetric, std::move(cpu_sample));
-  }
-
-  // Use weighted random sampling to select a task to report in the memory
-  // metric.
-  const TaskRecord* sampled_memory_task = SampleTaskByMemory();
-  if (sampled_memory_task) {
-    std::unique_ptr<rappor::Sample> memory_sample(
-        CreateRapporSample(rappor_service, *sampled_memory_task));
-    memory_sample->SetFlagsField(
-        kRapporUsageRangeFlagsField,
-        GET_ENUM_VAL(GetMemoryUsageRange(sampled_memory_task->memory_bytes)),
-        GET_ENUM_VAL(MemoryUsageRange::NUM_RANGES));
-    rappor_service->RecordSample(kMemoryRapporMetric, std::move(memory_sample));
-  }
-}
-
-void ResourceReporter::OnMemoryPressure(
-    MemoryPressureLevel memory_pressure_level) {
-  if (memory_pressure_level ==
-      MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_CRITICAL) {
-    StartRecordingCurrentState();
-  } else {
-    StopRecordingCurrentState();
-  }
-}
-
-void ResourceReporter::StartRecordingCurrentState() {
-  // If we are already listening to the task manager, then we're waiting for
-  // a refresh event.
-  if (observed_task_manager())
-    return;
-
-  // We only record Rappor samples only if it's the first ever critical memory
-  // pressure event we receive, or it has been more than
-  // |kMinimumTimeBetweenReportsInMs| since the last time we recorded samples.
-  if (g_browser_process->local_state()) {
-    const base::Time now = base::Time::NowFromSystemTime();
-    const base::Time last_rappor_report_time = base::Time::FromDoubleT(
-        g_browser_process->local_state()->GetDouble(kLastRapporReportTimeKey));
-    const base::TimeDelta delta_since_last_report =
-        now >= last_rappor_report_time ? now - last_rappor_report_time
-                                       : base::TimeDelta::Max();
-
-    if (delta_since_last_report < kMinimumTimeBetweenReports)
-      return;
-  }
-
-  // Start listening to the task manager and wait for the first refresh event
-  // with background calculations completion.
-  task_manager_to_observe_->AddObserver(this);
-}
-
-void ResourceReporter::StopRecordingCurrentState() {
-  // If we are still listening to the task manager from an earlier critical
-  // memory pressure level, we need to stop listening to it.
-  if (observed_task_manager())
-    observed_task_manager()->RemoveObserver(this);
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/resource_reporter/resource_reporter.h b/chrome/browser/chromeos/resource_reporter/resource_reporter.h
deleted file mode 100644
index ef5bec3..0000000
--- a/chrome/browser/chromeos/resource_reporter/resource_reporter.h
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_RESOURCE_REPORTER_RESOURCE_REPORTER_H_
-#define CHROME_BROWSER_CHROMEOS_RESOURCE_REPORTER_RESOURCE_REPORTER_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/memory_pressure_listener.h"
-#include "base/memory/singleton.h"
-#include "chrome/browser/task_manager/task_manager_observer.h"
-#include "components/metrics/metrics_service.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/rappor/public/sample.h"
-
-namespace chromeos {
-
-// A system that tracks the top |kTopConsumersCount| CPU and memory consumer
-// Chrome tasks and reports a weighted random sample of them via Rappor whenever
-// memory pressure is critical. The reporting is limited to once per
-// |kMinimumTimeBetweenReportsInMS|.
-class ResourceReporter : public task_manager::TaskManagerObserver {
- public:
-  // A collection of the data of a task manager's task that the ResourceReporter
-  // is interested in.
-  struct TaskRecord {
-    explicit TaskRecord(task_manager::TaskId task_id);
-
-    TaskRecord(task_manager::TaskId task_id,
-               const std::string& task_name,
-               double cpu_percent,
-               int64_t memory_bytes,
-               bool background);
-
-    // The ID of the task.
-    task_manager::TaskId id;
-
-    // The canonicalized task name to be used to represent the task in a Rappor
-    // sample.
-    std::string task_name_for_rappor;
-
-    // The CPU usage of the task from the most recent task manager refresh in
-    // percentage [0.0, 100.0].
-    double cpu_percent;
-
-    // The memory footprint of the task from the most recent task manager
-    // refresh in bytes. It doesn't include shared memory. A value of -1 is
-    // invalid and means that the memory usage measurement for this task is not
-    // ready yet. See TaskManagerInterface::GetMemoryFootprintUsage().
-    int64_t memory_bytes;
-
-    // True if the task is running on a process at background priority.
-    bool is_background;
-  };
-
-  ~ResourceReporter() override;
-
-  // The singleton instance.
-  static ResourceReporter* GetInstance();
-
-  static void RegisterPrefs(PrefRegistrySimple* registry);
-
-  // Start / stop observing the task manager and the memory pressure events.
-  void StartMonitoring(
-      task_manager::TaskManagerInterface* task_manager_to_observe);
-  void StopMonitoring();
-
-  // task_manager::TaskManagerObserver:
-  void OnTasksRefreshedWithBackgroundCalculations(
-      const task_manager::TaskIdList& task_ids) override;
-
- private:
-  friend struct base::DefaultSingletonTraits<ResourceReporter>;
-  friend class ResourceReporterTest;
-  FRIEND_TEST_ALL_PREFIXES(ResourceReporterTest, TestGetCpuRapporMetricName);
-  FRIEND_TEST_ALL_PREFIXES(ResourceReporterTest, TestGetMemoryRapporMetricName);
-  FRIEND_TEST_ALL_PREFIXES(ResourceReporterTest, TestAll);
-
-  // WARNING: The below enum MUST never be renamed, modified or reordered, as
-  // they're written to logs. You can only insert a new element immediately
-  // before the last, and update the value of the last element.
-  enum class TaskProcessPriority {
-    FOREGROUND      = 0,
-    BACKGROUND      = 1 << 0,
-    NUM_PRIORITIES  = 2,
-  };
-
-  // WARNING: The below enum MUST never be renamed, modified or reordered, as
-  // they're written to logs. You can only insert a new element immediately
-  // before the last, and update the value of the last element.
-  enum class CpuUsageRange {
-    RANGE_0_TO_10_PERCENT   = 0,
-    RANGE_10_TO_30_PERCENT  = 1 << 0,
-    RANGE_30_TO_60_PERCENT  = 1 << 1,
-    RANGE_ABOVE_60_PERCENT  = 1 << 2,
-    NUM_RANGES              = 4,
-  };
-
-  // WARNING: The below enum MUST never be renamed, modified or reordered, as
-  // they're written to logs. You can only insert a new element immediately
-  // before the last, and update the value of the last element.
-  enum class MemoryUsageRange {
-    RANGE_0_TO_200_MB    = 0,
-    RANGE_200_TO_400_MB  = 1 << 0,
-    RANGE_400_TO_600_MB  = 1 << 1,
-    RANGE_600_TO_800_MB  = 1 << 2,
-    RANGE_800_TO_1_GB    = 1 << 3,
-    RANGE_ABOVE_1_GB     = 1 << 4,
-    NUM_RANGES           = 6,
-  };
-
-  // WARNING: The below enum MUST never be renamed, modified or reordered, as
-  // they're written to logs. You can only insert a new element immediately
-  // before the last, and update the value of the last element.
-  enum class CpuCoresNumberRange {
-    RANGE_NA              = 0,
-    RANGE_1_CORE          = 1 << 0,
-    RANGE_2_CORES         = 1 << 1,
-    RANGE_3_TO_4_CORES    = 1 << 2,
-    RANGE_5_TO_8_CORES    = 1 << 3,
-    RANGE_9_TO_16_CORES   = 1 << 4,
-    RANGE_ABOVE_16_CORES  = 1 << 5,
-    NUM_RANGES            = 7,
-  };
-
-  ResourceReporter();
-
-  // The CPU and memory thresholds beyond which the tasks will be reported.
-  static double GetTaskCpuThresholdForReporting();
-  static int64_t GetTaskMemoryThresholdForReporting();
-
-  // Creates a Rappor sample for the given |task_record|.
-  static std::unique_ptr<rappor::Sample> CreateRapporSample(
-      rappor::RapporServiceImpl* rappor_service,
-      const TaskRecord& task_record);
-
-  // Gets the CPU/memory usage ranges given the |cpu| / |memory_in_bytes|
-  // values.
-  static CpuUsageRange GetCpuUsageRange(double cpu);
-  static MemoryUsageRange GetMemoryUsageRange(int64_t memory_in_bytes);
-
-  // Gets the bucket in which the current system's number of CPU cores fall.
-  static CpuCoresNumberRange GetCurrentSystemCpuCoresRange();
-
-  // Perform a weighted random sampling to select a task by its CPU or memory
-  // usage weights so that we can record samples for them via Rappor.
-  // They return nullptr if no TaskRecord has been selected.
-  const TaskRecord* SampleTaskByCpu() const;
-  const TaskRecord* SampleTaskByMemory() const;
-
-  // Does the actual recording of Rappor and UMA samples.
-  void ReportSamples();
-
-  // The callback function that will be invoked on memory pressure events.
-  using MemoryPressureLevel = base::MemoryPressureListener::MemoryPressureLevel;
-  void OnMemoryPressure(MemoryPressureLevel memory_pressure_level);
-
-  void StartRecordingCurrentState();
-  void StopRecordingCurrentState();
-
-  // Monitor memory pressure events.
-  std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
-
-  // Contains the data about the most CPU and memory intensive tasks collected
-  // at a critical memory pressure event.
-  std::vector<TaskRecord> task_records_;
-
-  // Either the real task manager implementation, or a test implementation in
-  // unit tests.
-  task_manager::TaskManagerInterface* task_manager_to_observe_;
-
-  // The range that includes the number of CPU cores in the current system.
-  const CpuCoresNumberRange system_cpu_cores_range_;
-
-  // The most recent reading for the browser and GPU processes to be reported as
-  // UMA histograms when the system is under critical memory pressure.
-  double last_browser_process_cpu_;
-  double last_gpu_process_cpu_;
-  int64_t last_browser_process_memory_;
-  int64_t last_gpu_process_memory_;
-
-  // Tracks whether monitoring started or not.
-  bool is_monitoring_;
-
-  DISALLOW_COPY_AND_ASSIGN(ResourceReporter);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_RESOURCE_REPORTER_RESOURCE_REPORTER_H_
diff --git a/chrome/browser/chromeos/resource_reporter/resource_reporter_unittest.cc b/chrome/browser/chromeos/resource_reporter/resource_reporter_unittest.cc
deleted file mode 100644
index 45b8231..0000000
--- a/chrome/browser/chromeos/resource_reporter/resource_reporter_unittest.cc
+++ /dev/null
@@ -1,279 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <limits>
-#include <map>
-#include <string>
-#include <vector>
-
-#include "base/run_loop.h"
-#include "base/stl_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/system/sys_info.h"
-#include "base/timer/mock_timer.h"
-#include "base/util/memory_pressure/fake_memory_pressure_monitor.h"
-#include "chrome/browser/chromeos/resource_reporter/resource_reporter.h"
-#include "chrome/browser/task_manager/test_task_manager.h"
-#include "content/public/test/browser_task_environment.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using task_manager::TaskId;
-
-namespace chromeos {
-
-namespace {
-
-constexpr int64_t k1KB = 1024;
-constexpr int64_t k1MB = 1024 * 1024;
-constexpr int64_t k1GB = 1024 * 1024 * 1024;
-
-constexpr double kBrowserProcessCpu = 21.0;
-constexpr int64_t kBrowserProcessMemory = 300 * k1MB;
-constexpr double kGpuProcessCpu = 60.0;
-constexpr int64_t kGpuProcessMemory = 900 * k1MB;
-
-// A list of task records that we'll use to fill the task manager.
-const ResourceReporter::TaskRecord kTestTasks[] = {
-    {0, "0", 30.0, 43 * k1KB, false},
-    {1, "1", 9.0, 20 * k1MB, false},
-    {2, "2", 35.0, 3 * k1GB, false},
-    // Browser task.
-    {3, "3", kBrowserProcessCpu, kBrowserProcessMemory, false},
-    {4, "4", 85.0, 400 * k1KB, false},
-    {5, "5", 30.1, 500 * k1MB, false},
-    // GPU task.
-    {6, "6", kGpuProcessCpu, kGpuProcessMemory, false},
-    {7, "7", 4.0, 1 * k1GB, false},
-    {8, "8", 40.0, 64 * k1KB, false},
-    {9, "9", 93.0, 64 * k1MB, false},
-    {10, "10", 2.23, 2 * k1KB, false},
-    {11, "11", 55.0, 40 * k1MB, false},
-    {12, "12", 87.0, 30 * k1KB, false},
-};
-
-constexpr size_t kTasksSize = base::size(kTestTasks);
-
-// A test implementation of the task manager that can be used to collect CPU and
-// memory usage so that they can be tested with the resource reporter.
-class DummyTaskManager : public task_manager::TestTaskManager {
- public:
-  DummyTaskManager() {
-    set_timer_for_testing(std::make_unique<base::MockRepeatingTimer>());
-  }
-  ~DummyTaskManager() override {}
-
-  // task_manager::TestTaskManager:
-  double GetPlatformIndependentCPUUsage(TaskId task_id) const override {
-    // |cpu_percent| expresses the expected value that the metrics reporter
-    // should give for this Task's group, which is a percentage-of-total,
-    // so we need to multiply up by the number of cores, to have TaskManager
-    // return the correct percentage-of-core CPU usage.
-    return tasks_.at(task_id)->cpu_percent *
-           base::SysInfo::NumberOfProcessors();
-  }
-  int64_t GetMemoryFootprintUsage(TaskId task_id) const override {
-    return tasks_.at(task_id)->memory_bytes;
-  }
-  const std::string& GetTaskNameForRappor(TaskId task_id) const override {
-    return tasks_.at(task_id)->task_name_for_rappor;
-  }
-  task_manager::Task::Type GetType(TaskId task_id) const override {
-    switch (task_id) {
-      case 3:
-        return task_manager::Task::BROWSER;
-
-      case 6:
-        return task_manager::Task::GPU;
-
-      default:
-        return task_manager::Task::RENDERER;
-    }
-  }
-
-  void AddTaskFromIndex(size_t index) {
-    tasks_[kTestTasks[index].id] = &kTestTasks[index];
-  }
-
-  void ManualRefresh() {
-    ids_.clear();
-    for (const auto& pair : tasks_)
-      ids_.push_back(pair.first);
-
-    NotifyObserversOnRefreshWithBackgroundCalculations(ids_);
-  }
-
- private:
-  std::map<TaskId, const ResourceReporter::TaskRecord*> tasks_;
-
-  DISALLOW_COPY_AND_ASSIGN(DummyTaskManager);
-};
-
-}  // namespace
-
-class ResourceReporterTest : public testing::Test {
- public:
-  ResourceReporterTest() {}
-  ~ResourceReporterTest() override {}
-
-  void SetUp() override {
-    resource_reporter()->StartMonitoring(&task_manager_);
-  }
-
-  void TearDown() override { resource_reporter()->StopMonitoring(); }
-
-  // Adds a number of tasks less than |kTopConsumersCount| to the task manager.
-  void AddTasks() {
-    for (size_t i = 0; i < kTasksSize; ++i)
-      task_manager_.AddTaskFromIndex(i);
-  }
-
-  // Manually refresh the task manager.
-  void RefreshTaskManager() {
-    task_manager_.ManualRefresh();
-  }
-
-  ResourceReporter* resource_reporter() const {
-    return ResourceReporter::GetInstance();
-  }
-
-  util::test::FakeMemoryPressureMonitor* monitor() { return &monitor_; }
-
- private:
-  content::BrowserTaskEnvironment task_environment_;
-
-  util::test::FakeMemoryPressureMonitor monitor_;
-
-  DummyTaskManager task_manager_;
-
-  DISALLOW_COPY_AND_ASSIGN(ResourceReporterTest);
-};
-
-// Tests that ResourceReporter::GetCpuRapporMetricName() returns the correct
-// metric name that corresponds to the given CPU usage.
-TEST_F(ResourceReporterTest, TestGetCpuRapporMetricName) {
-  EXPECT_EQ(ResourceReporter::CpuUsageRange::RANGE_0_TO_10_PERCENT,
-            ResourceReporter::GetCpuUsageRange(0.3));
-  EXPECT_EQ(ResourceReporter::CpuUsageRange::RANGE_0_TO_10_PERCENT,
-            ResourceReporter::GetCpuUsageRange(5.7));
-  EXPECT_EQ(ResourceReporter::CpuUsageRange::RANGE_0_TO_10_PERCENT,
-            ResourceReporter::GetCpuUsageRange(9.99));
-  EXPECT_EQ(ResourceReporter::CpuUsageRange::RANGE_0_TO_10_PERCENT,
-            ResourceReporter::GetCpuUsageRange(10.0));
-
-  EXPECT_EQ(ResourceReporter::CpuUsageRange::RANGE_10_TO_30_PERCENT,
-            ResourceReporter::GetCpuUsageRange(10.1));
-  EXPECT_EQ(ResourceReporter::CpuUsageRange::RANGE_10_TO_30_PERCENT,
-            ResourceReporter::GetCpuUsageRange(29.99));
-  EXPECT_EQ(ResourceReporter::CpuUsageRange::RANGE_10_TO_30_PERCENT,
-            ResourceReporter::GetCpuUsageRange(30.0));
-
-  EXPECT_EQ(ResourceReporter::CpuUsageRange::RANGE_30_TO_60_PERCENT,
-            ResourceReporter::GetCpuUsageRange(30.1));
-  EXPECT_EQ(ResourceReporter::CpuUsageRange::RANGE_30_TO_60_PERCENT,
-            ResourceReporter::GetCpuUsageRange(59.99));
-  EXPECT_EQ(ResourceReporter::CpuUsageRange::RANGE_30_TO_60_PERCENT,
-            ResourceReporter::GetCpuUsageRange(60.0));
-
-  EXPECT_EQ(ResourceReporter::CpuUsageRange::RANGE_ABOVE_60_PERCENT,
-            ResourceReporter::GetCpuUsageRange(60.1));
-  EXPECT_EQ(ResourceReporter::CpuUsageRange::RANGE_ABOVE_60_PERCENT,
-            ResourceReporter::GetCpuUsageRange(100.0));
-}
-
-// Tests that ResourceReporter::GetMemoryRapporMetricName() returns the correct
-// metric names for the given memory usage.
-TEST_F(ResourceReporterTest, TestGetMemoryRapporMetricName) {
-  EXPECT_EQ(ResourceReporter::MemoryUsageRange::RANGE_0_TO_200_MB,
-            ResourceReporter::GetMemoryUsageRange(2 * k1KB));
-  EXPECT_EQ(ResourceReporter::MemoryUsageRange::RANGE_0_TO_200_MB,
-            ResourceReporter::GetMemoryUsageRange(20 * k1MB));
-  EXPECT_EQ(ResourceReporter::MemoryUsageRange::RANGE_0_TO_200_MB,
-            ResourceReporter::GetMemoryUsageRange(200 * k1MB));
-
-  EXPECT_EQ(ResourceReporter::MemoryUsageRange::RANGE_200_TO_400_MB,
-            ResourceReporter::GetMemoryUsageRange(201 * k1MB));
-  EXPECT_EQ(ResourceReporter::MemoryUsageRange::RANGE_200_TO_400_MB,
-            ResourceReporter::GetMemoryUsageRange(400 * k1MB));
-
-  EXPECT_EQ(ResourceReporter::MemoryUsageRange::RANGE_400_TO_600_MB,
-            ResourceReporter::GetMemoryUsageRange(401 * k1MB));
-  EXPECT_EQ(ResourceReporter::MemoryUsageRange::RANGE_400_TO_600_MB,
-            ResourceReporter::GetMemoryUsageRange(600 * k1MB));
-
-  EXPECT_EQ(ResourceReporter::MemoryUsageRange::RANGE_600_TO_800_MB,
-            ResourceReporter::GetMemoryUsageRange(601 * k1MB));
-  EXPECT_EQ(ResourceReporter::MemoryUsageRange::RANGE_600_TO_800_MB,
-            ResourceReporter::GetMemoryUsageRange(800 * k1MB));
-
-  EXPECT_EQ(ResourceReporter::MemoryUsageRange::RANGE_800_TO_1_GB,
-            ResourceReporter::GetMemoryUsageRange(801 * k1MB));
-  EXPECT_EQ(ResourceReporter::MemoryUsageRange::RANGE_800_TO_1_GB,
-            ResourceReporter::GetMemoryUsageRange(1 * k1GB));
-
-  EXPECT_EQ(ResourceReporter::MemoryUsageRange::RANGE_ABOVE_1_GB,
-            ResourceReporter::GetMemoryUsageRange(1 * k1GB + 1 * k1KB));
-}
-
-// Tests all the interactions between the resource reporter and the task
-// manager.
-TEST_F(ResourceReporterTest, TestAll) {
-  using MemoryPressureLevel = base::MemoryPressureListener::MemoryPressureLevel;
-
-  // Moderate memory pressure events should not trigger any sampling.
-  monitor()->SetAndNotifyMemoryPressure(
-      MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(resource_reporter()->observed_task_manager());
-
-  // A critical memory pressure event, but the task manager is not tracking any
-  // resource intensive tasks yet.
-  monitor()->SetAndNotifyMemoryPressure(
-      MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_CRITICAL);
-  base::RunLoop().RunUntilIdle();
-  // We should keep listening to the task manager, even after a refresh.
-  RefreshTaskManager();
-  EXPECT_TRUE(resource_reporter()->observed_task_manager());
-
-  // Memory pressure reduces to moderate again, we should stop watching the task
-  // manager.
-  monitor()->SetAndNotifyMemoryPressure(
-      MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(resource_reporter()->observed_task_manager());
-
-  // Memory pressure becomes critical and we have violating tasks.
-  AddTasks();
-  monitor()->SetAndNotifyMemoryPressure(
-      MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_CRITICAL);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(resource_reporter()->observed_task_manager());
-  RefreshTaskManager();
-
-  // Make sure that the ResourceReporter is no longer listening to the task
-  // manager right after the refresh.
-  EXPECT_FALSE(resource_reporter()->observed_task_manager());
-
-  // Make sure the ResourceReporter is not tracking any but the tasks exceeding
-  // the defined resource use thresholds.
-  ASSERT_FALSE(resource_reporter()->task_records_.empty());
-  for (const auto& task_record : resource_reporter()->task_records_) {
-    EXPECT_TRUE(task_record.cpu_percent >=
-                    ResourceReporter::GetTaskCpuThresholdForReporting() ||
-                task_record.memory_bytes >=
-                    ResourceReporter::GetTaskMemoryThresholdForReporting());
-  }
-
-  // Make sure you have the right info about the Browser and GPU process.
-  EXPECT_DOUBLE_EQ(resource_reporter()->last_browser_process_cpu_,
-                   kBrowserProcessCpu);
-  EXPECT_EQ(resource_reporter()->last_browser_process_memory_,
-            kBrowserProcessMemory);
-  EXPECT_DOUBLE_EQ(resource_reporter()->last_gpu_process_cpu_, kGpuProcessCpu);
-  EXPECT_EQ(resource_reporter()->last_gpu_process_memory_, kGpuProcessMemory);
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/enterprise/connectors/connectors_manager.h b/chrome/browser/enterprise/connectors/connectors_manager.h
index ee06a40..97ebebb 100644
--- a/chrome/browser/enterprise/connectors/connectors_manager.h
+++ b/chrome/browser/enterprise/connectors/connectors_manager.h
@@ -75,8 +75,17 @@
                            AnalysisConnector connector,
                            AnalysisSettingsCallback callback);
 
+  // Public legacy functions.
+  // These functions are used to interact with legacy policies and should only
+  // be called while the connectors equivalent isn't available. They should be
+  // removed once legacy policies are deprecated.
+
+  // Check a url against the corresponding URL patterns policies.
+  bool MatchURLAgainstLegacyDlpPolicies(const GURL& url, bool upload) const;
+  bool MatchURLAgainstLegacyMalwarePolicies(const GURL& url, bool upload) const;
+
  private:
-  // Legacy functions.
+  // Private legacy functions.
   // These functions are used to interact with legacy policies and should stay
   // private. They should be removed once legacy policies are deprecated.
 
@@ -90,8 +99,6 @@
   bool LegacyBlockLargeFiles(bool upload) const;
   bool LegacyBlockUnsupportedFileTypes(bool upload) const;
 
-  bool MatchURLAgainstLegacyDlpPolicies(const GURL& url, bool upload) const;
-  bool MatchURLAgainstLegacyMalwarePolicies(const GURL& url, bool upload) const;
   std::set<std::string> MatchURLAgainstLegacyPolicies(const GURL& url,
                                                       bool upload) const;
 };
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 4ee3fc7..1fc7936 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -114,7 +114,7 @@
   },
   {
     "name": "app-management",
-    "owners": [ "//chrome/browser/resources/app_management/OWNERS" ],
+    "owners": [ "//chrome/browser/ui/webui/app_management/OWNERS" ],
     "expiry_milestone": 81
   },
   {
diff --git a/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc b/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
index 8761339..0788204 100644
--- a/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
+++ b/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
@@ -172,7 +172,18 @@
     return prompt_factory_.get();
   }
 
- private:
+  void VerifyResultState(blink::mojom::MediaStreamRequestResult result,
+                         bool has_audio,
+                         bool has_video) {
+    EXPECT_EQ(result, media_stream_result());
+    EXPECT_EQ(has_audio,
+              CheckDevicesListContains(
+                  blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
+    EXPECT_EQ(has_video,
+              CheckDevicesListContains(
+                  blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
+  }
+
   void SetUpOnMainThread() override {
     WebRtcTestBase::SetUpOnMainThread();
 
@@ -702,13 +713,9 @@
 
     // Check the media stream result is expected and the devices returned are
     // expected;
-    ASSERT_EQ(test.ExpectedMediaStreamResult(), media_stream_result());
-    ASSERT_EQ(CheckDevicesListContains(
-                  blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE),
-              test.ExpectMicAllowed() && test.ExpectCamAllowed());
-    ASSERT_EQ(CheckDevicesListContains(
-                  blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE),
-              test.ExpectMicAllowed() && test.ExpectCamAllowed());
+    VerifyResultState(test.ExpectedMediaStreamResult(),
+                      test.ExpectMicAllowed() && test.ExpectCamAllowed(),
+                      test.ExpectMicAllowed() && test.ExpectCamAllowed());
   }
 }
 
@@ -721,11 +728,7 @@
 
   ASSERT_EQ(0, prompt_factory()->TotalRequestCount());
 
-  ASSERT_EQ(blink::mojom::MediaStreamRequestResult::OK, media_stream_result());
-  ASSERT_FALSE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
-  ASSERT_TRUE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
+  VerifyResultState(blink::mojom::MediaStreamRequestResult::OK, false, true);
 }
 
 IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
@@ -745,12 +748,7 @@
   ASSERT_TRUE(prompt_factory()->RequestTypeSeen(
       permissions::PermissionRequestType::PERMISSION_MEDIASTREAM_MIC));
 
-  // Accept the prompt.
-  ASSERT_EQ(blink::mojom::MediaStreamRequestResult::OK, media_stream_result());
-  ASSERT_TRUE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
-  ASSERT_TRUE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
+  VerifyResultState(blink::mojom::MediaStreamRequestResult::OK, true, true);
 
   // Check that re-requesting allows without prompting.
   prompt_factory()->ResetCounts();
@@ -758,11 +756,7 @@
                      CreateRequest(example_audio_id(), example_video_id()));
   ASSERT_EQ(0, prompt_factory()->TotalRequestCount());
 
-  ASSERT_EQ(blink::mojom::MediaStreamRequestResult::OK, media_stream_result());
-  ASSERT_TRUE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
-  ASSERT_TRUE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
+  VerifyResultState(blink::mojom::MediaStreamRequestResult::OK, true, true);
 }
 
 IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
@@ -778,16 +772,12 @@
                             blink::MEDIA_OPEN_DEVICE_PEPPER_ONLY));
   ASSERT_EQ(0, prompt_factory()->TotalRequestCount());
 
-  ASSERT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
-            media_stream_result());
-  ASSERT_FALSE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
-  ASSERT_FALSE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
+  VerifyResultState(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
+                    false, false);
 }
 
 IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest, WebContentsDestroyed) {
-  InitWithUrl(GURL("http://www.example.com"));
+  InitWithUrl(embedded_test_server()->GetURL("/simple.html"));
 
   prompt_factory()->set_response_type(
       permissions::PermissionRequestManager::ACCEPT_ALL);
@@ -801,12 +791,46 @@
   RequestPermissions(nullptr, request);
   ASSERT_EQ(0, prompt_factory()->TotalRequestCount());
 
-  ASSERT_EQ(blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN,
-            media_stream_result());
-  ASSERT_FALSE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
-  ASSERT_FALSE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
+  VerifyResultState(
+      blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN, false,
+      false);
+}
+
+IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
+                       WebContentsDestroyedDuringRequest) {
+  InitWithUrl(embedded_test_server()->GetURL("/simple.html"));
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GURL("about:blank"), WindowOpenDisposition::NEW_BACKGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
+
+  prompt_factory()->set_response_type(
+      permissions::PermissionRequestManager::ACCEPT_ALL);
+
+  content::WebContents* prompt_contents = GetWebContents();
+  const int prompt_contents_index =
+      browser()->tab_strip_model()->GetIndexOfWebContents(prompt_contents);
+
+  // Now request permissions, but before the callback is asynchronously called,
+  // destroy the tab.
+  permission_bubble_media_access_handler_->HandleRequest(
+      prompt_contents, CreateRequest(example_audio_id(), example_video_id()),
+      base::BindOnce([](const blink::MediaStreamDevices& devices,
+                        blink::mojom::MediaStreamRequestResult result,
+                        std::unique_ptr<content::MediaStreamUI> ui) {
+        ADD_FAILURE() << " this callback shouldn't be reached";
+      }),
+      nullptr);
+  // Since the mock prompt factory holds a reference to the
+  // PermissionRequestManager for the WebContents and uses that reference in its
+  // destructor, it has to be destroyed before the tab.
+  prompt_factory_.reset();
+  ASSERT_TRUE(browser()->tab_strip_model()->CloseWebContentsAt(
+      prompt_contents_index, TabStripModel::CloseTypes::CLOSE_USER_GESTURE));
+  base::RunLoop().RunUntilIdle();
+
+  VerifyResultState(
+      blink::mojom::MediaStreamRequestResult::NUM_MEDIA_REQUEST_RESULTS, false,
+      false);
 }
 
 // Request and block microphone and camera access with kill switch.
@@ -832,13 +856,8 @@
                      CreateRequest(example_audio_id(), example_video_id()));
 
   ASSERT_EQ(0, prompt_factory()->TotalRequestCount());
-
-  ASSERT_EQ(blink::mojom::MediaStreamRequestResult::KILL_SWITCH_ON,
-            media_stream_result());
-  ASSERT_FALSE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
-  ASSERT_FALSE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
+  VerifyResultState(blink::mojom::MediaStreamRequestResult::KILL_SWITCH_ON,
+                    false, false);
 }
 
 IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
@@ -865,12 +884,8 @@
 
   ASSERT_EQ(0, prompt_factory()->TotalRequestCount());
 
-  ASSERT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
-            media_stream_result());
-  ASSERT_FALSE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
-  ASSERT_FALSE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
+  VerifyResultState(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
+                    false, false);
   EXPECT_EQ(TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED,
             GetContentSettings()->GetMicrophoneCameraState());
 }
@@ -899,12 +914,8 @@
 
   ASSERT_EQ(0, prompt_factory()->TotalRequestCount());
 
-  ASSERT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
-            media_stream_result());
-  ASSERT_FALSE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
-  ASSERT_FALSE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
+  VerifyResultState(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
+                    false, false);
   EXPECT_EQ(TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED,
             GetContentSettings()->GetMicrophoneCameraState());
 }
@@ -917,12 +928,7 @@
       GetWebContents(),
       CreateRequestWithType(example_audio_id(), std::string(),
                             blink::MEDIA_OPEN_DEVICE_PEPPER_ONLY));
-
-  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, media_stream_result());
-  EXPECT_TRUE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
-  EXPECT_FALSE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
+  VerifyResultState(blink::mojom::MediaStreamRequestResult::OK, true, false);
 }
 
 IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
@@ -933,10 +939,5 @@
       GetWebContents(),
       CreateRequestWithType(std::string(), example_video_id(),
                             blink::MEDIA_OPEN_DEVICE_PEPPER_ONLY));
-
-  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, media_stream_result());
-  EXPECT_FALSE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
-  EXPECT_TRUE(CheckDevicesListContains(
-      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
+  VerifyResultState(blink::mojom::MediaStreamRequestResult::OK, false, true);
 }
diff --git a/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc b/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
index d53732e2..03632378 100644
--- a/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
+++ b/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
@@ -294,6 +294,11 @@
     bool blocked_by_feature_policy,
     ContentSetting audio_setting,
     ContentSetting video_setting) {
+  if (pending_requests_.find(web_contents) == pending_requests_.end()) {
+    // WebContents has been destroyed. Don't need to do anything.
+    return;
+  }
+
   // If the kill switch is, or the request was blocked because of feature
   // policy we don't update the tab context.
   if (result != blink::mojom::MediaStreamRequestResult::KILL_SWITCH_ON &&
diff --git a/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc b/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc
index f72bf543..e57fad7d 100644
--- a/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc
+++ b/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc
@@ -15,6 +15,8 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/metrics/user_action_tester.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/scoped_run_loop_timeout.h"
+#include "base/test/test_timeouts.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/engagement/site_engagement_score.h"
@@ -462,6 +464,10 @@
   ASSERT_EQ(1u, notifications.size());
 
   web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+  // We see some timeouts in dbg tests, so increase the wait timeout to the
+  // test launcher's timeout.
+  const base::test::ScopedRunLoopTimeout specific_timeout(
+          FROM_HERE, TestTimeouts::test_launcher_timeout());
   ASSERT_TRUE(content::WaitForLoadStop(web_contents));
 
   // No engagement should be granted for clicking on the settings link.
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index ac6d48a..ed8ff10 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -314,7 +314,6 @@
 #include "chrome/browser/chromeos/printing/enterprise_printers_provider.h"
 #include "chrome/browser/chromeos/printing/history/print_job_history_service.h"
 #include "chrome/browser/chromeos/release_notes/release_notes_storage.h"
-#include "chrome/browser/chromeos/resource_reporter/resource_reporter.h"
 #include "chrome/browser/chromeos/settings/device_settings_cache.h"
 #include "chrome/browser/chromeos/system/automatic_reboot_manager.h"
 #include "chrome/browser/chromeos/system/input_device_settings.h"
@@ -520,6 +519,21 @@
 const char kAmbientModeTopicSource[] = "settings.ambient_mode.topic_source";
 #endif  // defined(OS_CHROMEOS)
 
+// Register local state used only for migration (clearing or moving to a new
+// key).
+void RegisterLocalStatePrefsForMigration(PrefRegistrySimple* registry) {
+  registry->RegisterBooleanPref(kGCMChannelStatus, true);
+  registry->RegisterIntegerPref(kGCMChannelPollIntervalSeconds, 0);
+  registry->RegisterInt64Pref(kGCMChannelLastCheckTime, 0);
+  registry->RegisterListPref(kInvalidatorSavedInvalidations);
+  registry->RegisterStringPref(kInvalidatorInvalidationState, std::string());
+  registry->RegisterStringPref(kInvalidatorClientId, std::string());
+
+#if defined(OS_WIN)
+  registry->RegisterBooleanPref(kHasSeenWin10PromoPage, false);
+#endif
+}
+
 // Register prefs used only for migration (clearing or moving to a new key).
 void RegisterProfilePrefsForMigration(
     user_prefs::PrefRegistrySyncable* registry) {
@@ -690,7 +704,6 @@
       RegisterLocalStatePrefs(registry);
   chromeos::Preferences::RegisterPrefs(registry);
   chromeos::ResetScreen::RegisterPrefs(registry);
-  chromeos::ResourceReporter::RegisterPrefs(registry);
   chromeos::SchedulerConfigurationManager::RegisterLocalStatePrefs(registry);
   chromeos::ServicesCustomizationDocument::RegisterPrefs(registry);
   chromeos::SigninScreenHandler::RegisterPrefs(registry);
@@ -749,8 +762,6 @@
   ModuleDatabase::RegisterLocalStatePrefs(registry);
   ThirdPartyConflictsManager::RegisterLocalStatePrefs(registry);
 #endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
-
-  registry->RegisterBooleanPref(kHasSeenWin10PromoPage, false);  // DEPRECATED
 #endif  // defined(OS_WIN)
 
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
@@ -770,15 +781,7 @@
   RegisterBrowserViewLocalPrefs(registry);
 #endif
 
-  // Obsolete. See MigrateObsoleteBrowserPrefs().
-  registry->RegisterBooleanPref(kGCMChannelStatus, true);
-  registry->RegisterIntegerPref(kGCMChannelPollIntervalSeconds, 0);
-  registry->RegisterInt64Pref(kGCMChannelLastCheckTime, 0);
-
-  // Obsolete. See MigrateObsoleteBrowserPrefs().
-  registry->RegisterListPref(kInvalidatorSavedInvalidations);
-  registry->RegisterStringPref(kInvalidatorInvalidationState, std::string());
-  registry->RegisterStringPref(kInvalidatorClientId, std::string());
+  RegisterLocalStatePrefsForMigration(registry);
 }
 
 // Register prefs applicable to all profiles.
@@ -1067,7 +1070,7 @@
 #endif
 
 // This method should be periodically pruned of year+ old migrations.
-void MigrateObsoleteBrowserPrefs(Profile* profile, PrefService* local_state) {
+void MigrateObsoleteLocalStatePrefs(PrefService* local_state) {
 #if defined(OS_WIN)
   // Added 6/2019.
   local_state->ClearPref(kHasSeenWin10PromoPage);
diff --git a/chrome/browser/prefs/browser_prefs.h b/chrome/browser/prefs/browser_prefs.h
index 05f6c20..bff02b1 100644
--- a/chrome/browser/prefs/browser_prefs.h
+++ b/chrome/browser/prefs/browser_prefs.h
@@ -5,10 +5,9 @@
 #ifndef CHROME_BROWSER_PREFS_BROWSER_PREFS_H_
 #define CHROME_BROWSER_PREFS_BROWSER_PREFS_H_
 
-#include <set>
+#include <string>
 
 #include "build/build_config.h"
-#include "components/prefs/pref_value_store.h"
 
 class PrefRegistrySimple;
 class PrefService;
@@ -41,8 +40,8 @@
 // Migrate/cleanup deprecated prefs in |local_state|. Over time, long deprecated
 // prefs should be removed as new ones are added, but this call should never go
 // away (even if it becomes an empty call for some time) as it should remain
-// *the* place to drop deprecated browser prefs at.
-void MigrateObsoleteBrowserPrefs(Profile* profile, PrefService* local_state);
+// *the* place to drop deprecated browser-level (Local State) prefs at.
+void MigrateObsoleteLocalStatePrefs(PrefService* local_state);
 
 // Migrate/cleanup deprecated prefs in |profile|'s pref store. Over time, long
 // deprecated prefs should be removed as new ones are added, but this call
diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc
index aaf639ac..ba0a4ad 100644
--- a/chrome/browser/profiles/profile.cc
+++ b/chrome/browser/profiles/profile.cc
@@ -423,6 +423,14 @@
     observer.OnOffTheRecordProfileCreated(off_the_record);
 }
 
+Profile* Profile::GetPrimaryOTRProfile() {
+  return GetOffTheRecordProfile(OTRProfileID::PrimaryID());
+}
+
+bool Profile::HasPrimaryOTRProfile() {
+  return HasOffTheRecordProfile(OTRProfileID::PrimaryID());
+}
+
 variations::VariationsClient* Profile::GetVariationsClient() {
   if (!chrome_variations_client_)
     chrome_variations_client_ = std::make_unique<ChromeVariationsClient>(this);
diff --git a/chrome/browser/profiles/profile.h b/chrome/browser/profiles/profile.h
index 4b47cd5..fd9b2978 100644
--- a/chrome/browser/profiles/profile.h
+++ b/chrome/browser/profiles/profile.h
@@ -127,12 +127,16 @@
       return profile_id_ < other.profile_id_;
     }
 
+   private:
+    friend std::ostream& operator<<(std::ostream& out,
+                                    const Profile::OTRProfileID& profile_id);
+
+    OTRProfileID() = default;
+
     // Returns this OTRProfileID in a string format that can be used for debug
     // message.
     const std::string& ToString() const;
 
-   private:
-    OTRProfileID() = default;
     const std::string profile_id_;
   };
 
@@ -223,6 +227,10 @@
   // Returns all OffTheRecord profiles.
   virtual std::vector<Profile*> GetAllOffTheRecordProfiles() = 0;
 
+  // Returns the primary OffTheRecord profile. Creates the profile if it doesn't
+  // exist.
+  Profile* GetPrimaryOTRProfile();
+
   // Destroys the OffTheRecord profile.
   virtual void DestroyOffTheRecordProfile(Profile* otr_profile) = 0;
 
@@ -239,6 +247,9 @@
   // Returns true if the profile has any OffTheRecord profiles.
   virtual bool HasAnyOffTheRecordProfile() = 0;
 
+  // True if the primary OffTheRecord profile exists.
+  bool HasPrimaryOTRProfile();
+
   // Return the original "recording" profile. This method returns this if the
   // profile is not OffTheRecord.
   virtual Profile* GetOriginalProfile() = 0;
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 0a75e9d..5125dda 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -981,9 +981,8 @@
 
 void ProfileImpl::OnLocaleReady() {
   TRACE_EVENT0("browser", "ProfileImpl::OnLocaleReady");
+
   // Migrate obsolete prefs.
-  if (g_browser_process->local_state())
-    MigrateObsoleteBrowserPrefs(this, g_browser_process->local_state());
   MigrateObsoleteProfilePrefs(this);
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   // Note: Extension preferences can be keyed off the extension ID, so need to
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index 8aa05da..6377d719 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -400,6 +400,7 @@
       "background/logging/log_store_test.js",
       "background/output_test.js",
       "background/recovery_strategy_test.js",
+      "background/smart_sticky_mode_test.js",
       "braille/braille_table_test.js",
       "braille/braille_translator_manager_test.js",
       "braille/liblouis_test.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js
index ac6624ba..608df14 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js
@@ -150,3 +150,13 @@
 ChromeVoxState.addObserver = function(observer) {
   ChromeVoxState.observers.push(observer);
 };
+
+/**
+ * @param {ChromeVoxStateObserver} observer
+ */
+ChromeVoxState.removeObserver = function(observer) {
+  const index = ChromeVoxState.observers.indexOf(observer);
+  if (index > -1) {
+    ChromeVoxState.observers.splice(index, 1);
+  }
+};
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode.js
index 148075033..0e3c7ead 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode.js
@@ -35,16 +35,42 @@
       return;
     }
 
-    const isRangeEditable =
-        newRange.start.node.state[chrome.automation.StateType.EDITABLE];
+    // Several cases arise which may lead to a sticky mode toggle:
+    // The node is either editable itself or a descendant of an editable.
+    // The node is a relation target of an editable.
+    const node = newRange.start.node;
+    let shouldTurnOffStickyMode = false;
+    if (node.state[chrome.automation.StateType.EDITABLE] ||
+        (node.parent &&
+         node.parent.state[chrome.automation.StateType.EDITABLE])) {
+      // This covers both editable nodes, and inline text boxes (which are not
+      // editable themselves, but may have an editable parent).
+      shouldTurnOffStickyMode = true;
+    } else {
+      let focus = node;
+      while (!shouldTurnOffStickyMode && focus) {
+        if (focus.activeDescendantFor && focus.activeDescendantFor.length) {
+          shouldTurnOffStickyMode |= focus.activeDescendantFor.some(
+              (n) => n.state[chrome.automation.StateType.EDITABLE]);
+        }
 
-    // This toggler should not make any changes when the range isn't editable
-    // and we haven't previously tracked any sticky mode state from the user.
-    if (!isRangeEditable && !this.didTurnOffStickyMode_) {
+        if (focus.controlledBy && focus.controlledBy.length) {
+          shouldTurnOffStickyMode |= focus.controlledBy.some(
+              (n) => n.state[chrome.automation.StateType.EDITABLE]);
+        }
+
+        focus = focus.parent;
+      }
+    }
+
+    // This toggler should not make any changes when the range isn't what we're
+    // lloking for and we haven't previously tracked any sticky mode state from
+    // the user.
+    if (!shouldTurnOffStickyMode && !this.didTurnOffStickyMode_) {
       return;
     }
 
-    if (isRangeEditable) {
+    if (shouldTurnOffStickyMode) {
       if (!ChromeVox.isStickyPrefOn) {
         // Sticky mode was already off; do not track the current sticky state
         // since we may have set it ourselves.
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode_test.js
new file mode 100644
index 0000000..281eb5c
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode_test.js
@@ -0,0 +1,63 @@
+// Copyright 2020 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 test fixture.
+GEN_INCLUDE([
+  '../testing/chromevox_next_e2e_test_base.js', '../testing/assert_additions.js'
+]);
+
+/**
+ * Test fixture for SmartStickyMode.
+ */
+ChromeVoxSmartStickyModeTest = class extends ChromeVoxNextE2ETest {};
+
+TEST_F('ChromeVoxSmartStickyModeTest', 'PossibleRangeTypes', function() {
+  this.runWithLoadedTree(
+      `
+    <p>start</p>
+    <input aria-controls="controls-target" type="text"></input>
+    <textarea aria-activedescendant="active-descendant-target"></textarea>
+    <div contenteditable><h3>hello</h3></div>
+    <ul id="controls-target"><li>end</ul>
+    <ul id="active-descendant-target"><li>end</ul>
+  `,
+      function(root) {
+        const ssm = new SmartStickyMode();
+
+        // Deregister from actual range changes.
+        ChromeVoxState.removeObserver(ssm);
+        assertFalse(ssm.didTurnOffStickyMode_);
+        const [p, input, textarea, contenteditable, ul1, ul2] = root.children;
+
+        function assertDidTurnOffForNode(node) {
+          ssm.onCurrentRangeChanged(cursors.Range.fromNode(node));
+          assertTrue(ssm.didTurnOffStickyMode_);
+        }
+
+        function assertDidNotTurnOffForNode(node) {
+          ssm.onCurrentRangeChanged(cursors.Range.fromNode(node));
+          assertFalse(ssm.didTurnOffStickyMode_);
+        }
+
+        // First, turn on sticky mode and try changing range to various parts of
+        // the document.
+        ChromeVoxBackground.setPref(
+            'sticky', true /* value */, true /* announce */);
+
+        assertDidTurnOffForNode(input);
+        assertDidTurnOffForNode(textarea);
+        assertDidNotTurnOffForNode(p);
+
+        assertDidTurnOffForNode(contenteditable);
+        assertDidTurnOffForNode(ul1);
+        assertDidNotTurnOffForNode(p);
+        assertDidTurnOffForNode(ul2);
+        assertDidTurnOffForNode(ul1.firstChild);
+        assertDidNotTurnOffForNode(p);
+        assertDidTurnOffForNode(ul2.firstChild);
+        assertDidNotTurnOffForNode(p);
+        assertDidTurnOffForNode(contenteditable.find({role: 'heading'}));
+        assertDidTurnOffForNode(contenteditable.find({role: 'inlineTextBox'}));
+      });
+});
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.html b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.html
index a385ba0f..61fdaea 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.html
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.html
@@ -41,7 +41,10 @@
       </div>
     </div>
     <div id="search-container">
-      <input class="i18n" msgid="search_widget_intro" id="search" type="search">
+      <input class="i18n" msgid="search_widget_intro" id="search" type="search"
+             aria-describedby="searchDescription">
+      <div class="i18n" id="searchDescription"
+           msgid="search_widget_description"></div>
     </div>
     <div id="annotations-container" hidden>
       <input class="i18n" msgid="annotations_widget_intro" id="annotations"
diff --git a/chrome/browser/resources/chromeos/accessibility/strings/chromevox_strings.grdp b/chrome/browser/resources/chromeos/accessibility/strings/chromevox_strings.grdp
index 90c6cdf..cba15f7 100644
--- a/chrome/browser/resources/chromeos/accessibility/strings/chromevox_strings.grdp
+++ b/chrome/browser/resources/chromeos/accessibility/strings/chromevox_strings.grdp
@@ -1504,6 +1504,9 @@
   <message desc="Spoken when the search widget first shows." name="IDS_CHROMEVOX_SEARCH_WIDGET_INTRO">
     Find in page
   </message>
+  <message desc="Instructions on how to use the ChromeVox find in page text input." name="IDS_CHROMEVOX_SEARCH_WIDGET_DESCRIPTION">
+    Type to search the page. Press enter to jump to the result, up or down arrows to browse results, keep typing to change your search, or escape to cancel.
+  </message>
   <message desc="Category displayed in the options page under keyboard commands." name="IDS_CHROMEVOX_MODIFIER_KEYS">
     Modifier Keys
   </message>
diff --git a/chrome/browser/resources/chromeos/login/images/1x/download_fail_illustration.svg b/chrome/browser/resources/chromeos/login/images/1x/download_fail_illustration.svg
deleted file mode 100644
index 8dfbac96..0000000
--- a/chrome/browser/resources/chromeos/login/images/1x/download_fail_illustration.svg
+++ /dev/null
@@ -1,16 +0,0 @@
-<svg version="1.1" viewBox="0 0 264 264" xmlns="http://www.w3.org/2000/svg">
-  <g fill="none" fill-rule="evenodd">
-    <path d="m171.6 82.336-6.413-6.413c-0.885-0.885-0.885-2.321 0-3.206l6.413-6.413c0.885-0.886 2.321-0.886 3.207 0l6.412 6.413c0.886 0.885 0.886 2.321 0 3.206l-6.412 6.413c-0.886 0.885-2.322 0.885-3.207 0" fill="#EA4335"/>
-    <path d="m105.87 48.101-6.413-6.413c-0.885-0.885-0.885-2.321 0-3.206l6.413-6.413c0.886-0.885 2.321-0.885 3.207 0l6.413 6.413c0.885 0.885 0.885 2.321 0 3.206l-6.413 6.413c-0.886 0.885-2.321 0.885-3.207 0" fill="#FBBC04"/>
-    <path d="m96.554 123.42 6.413-6.413c0.885-0.885 2.321-0.885 3.207 0l6.412 6.413c0.886 0.885 0.886 2.321 0 3.207l-6.412 6.412c-0.886 0.886-2.322 0.886-3.207 0l-6.413-6.412c-0.885-0.886-0.885-2.322 0-3.207" fill="#F1F3F4"/>
-    <path d="m129.01 65.679c-2.214-2.214-2.214-5.802 0-8.016l4.81-4.81c2.213-2.213 5.802-2.213 8.016 0 2.213 2.214 2.213 5.803 0 8.016l-4.81 4.81c-2.214 2.213-5.803 2.213-8.016 0" fill="#4285F4"/>
-    <path d="m133.77 102.36c-2.214-2.214-2.214-5.802 0-8.016l4.81-4.81c2.213-2.214 5.802-2.214 8.016 0 2.213 2.214 2.213 5.803 0 8.016l-4.81 4.81c-2.214 2.213-5.803 2.213-8.016 0" fill="#F1F3F4"/>
-    <path d="m146.85 97.289 0.741-0.741c2.214-2.214 2.214-5.802 0-8.016-2.213-2.213-5.803-2.213-8.016 0l-4.809 4.809c-0.745 0.745-1.217 1.651-1.461 2.602 4.621 0.068 9.146 0.527 13.545 1.346" fill="#FBBC04"/>
-    <path d="m87.07 75.282 11.947 3.201c1.686 0.452 2.25 2.559 1.016 3.793l-8.745 8.746c-1.235 1.234-3.342 0.67-3.794-1.016l-3.201-11.947c-0.452-1.686 1.091-3.229 2.777-2.777" fill="#34A853"/>
-    <path d="m151.38 118.92 11.947 3.201c1.686 0.452 2.25 2.559 1.016 3.793l-8.745 8.746c-1.235 1.234-3.342 0.67-3.794-1.016l-3.201-11.947c-0.452-1.686 1.091-3.229 2.777-2.777" fill="#F1F3F4"/>
-    <path d="m159 17c0 3.866-3.134 7-7 7s-7-3.134-7-7 3.134-7 7-7 7 3.134 7 7" fill="#34A853"/>
-    <path d="m212 175c0 44.183-35.817 80-80 80s-80-35.817-80-80 35.817-80 80-80 80 35.817 80 80z" stroke="#DADCE0" stroke-width="4"/>
-    <path d="m137 183.81 11.191-11.166 7.063 7.0791-23.394 23.341-23.393-23.341 7.0632-7.079 11.468 11.443v-35.086h10v34.809z" fill="#DADCE0" fill-rule="nonzero"/>
-    <path d="m101 151 62 62" stroke="#DADCE0" stroke-width="4"/>
-  </g>
-</svg>
diff --git a/chrome/browser/resources/chromeos/login/images/2x/download_fail_illustration.svg b/chrome/browser/resources/chromeos/login/images/2x/download_fail_illustration.svg
deleted file mode 100644
index 288fc539..0000000
--- a/chrome/browser/resources/chromeos/login/images/2x/download_fail_illustration.svg
+++ /dev/null
@@ -1,16 +0,0 @@
-<svg version="1.1" viewBox="0 0 528 528" xmlns="http://www.w3.org/2000/svg">
-  <g fill="none" fill-rule="evenodd">
-    <path d="m343.21 164.67-12.826-12.826c-1.77-1.77-1.77-4.642 0-6.412l12.826-12.826c1.77-1.772 4.642-1.772 6.414 0l12.824 12.826c1.772 1.77 1.772 4.642 0 6.412l-12.824 12.826c-1.772 1.77-4.644 1.77-6.414 0" fill="#EA4335"/>
-    <path d="m211.74 96.202-12.826-12.826c-1.77-1.77-1.77-4.642 0-6.412l12.826-12.826c1.772-1.77 4.642-1.77 6.414 0l12.826 12.826c1.77 1.77 1.77 4.642 0 6.412l-12.826 12.826c-1.772 1.77-4.642 1.77-6.414 0" fill="#FBBC04"/>
-    <path d="m193.11 246.83 12.826-12.826c1.77-1.77 4.642-1.77 6.414 0l12.824 12.826c1.772 1.77 1.772 4.642 0 6.414l-12.824 12.824c-1.772 1.772-4.644 1.772-6.414 0l-12.826-12.824c-1.77-1.772-1.77-4.644 0-6.414" fill="#F1F3F4"/>
-    <path d="m258.02 131.36c-4.428-4.428-4.428-11.604 0-16.032l9.62-9.62c4.426-4.426 11.604-4.426 16.032 0 4.426 4.428 4.426 11.606 0 16.032l-9.62 9.62c-4.428 4.426-11.606 4.426-16.032 0" fill="#4285F4"/>
-    <path d="m267.53 204.72c-4.428-4.428-4.428-11.604 0-16.032l9.62-9.62c4.426-4.428 11.604-4.428 16.032 0 4.426 4.428 4.426 11.606 0 16.032l-9.62 9.62c-4.428 4.426-11.606 4.426-16.032 0" fill="#F1F3F4"/>
-    <path d="m293.7 194.58 1.482-1.482c4.428-4.428 4.428-11.604 0-16.032-4.426-4.426-11.606-4.426-16.032 0l-9.618 9.618c-1.49 1.49-2.434 3.302-2.922 5.204 9.242 0.136 18.292 1.054 27.09 2.692" fill="#FBBC04"/>
-    <path d="m174.14 150.56 23.894 6.402c3.372 0.904 4.5 5.118 2.032 7.586l-17.49 17.492c-2.47 2.468-6.684 1.34-7.588-2.032l-6.402-23.894c-0.904-3.372 2.182-6.458 5.554-5.554" fill="#34A853"/>
-    <path d="m302.77 237.85 23.894 6.402c3.372 0.904 4.5 5.118 2.032 7.586l-17.49 17.492c-2.47 2.468-6.684 1.34-7.588-2.032l-6.402-23.894c-0.904-3.372 2.182-6.458 5.554-5.554" fill="#F1F3F4"/>
-    <path d="m318 34c0 7.732-6.268 14-14 14s-14-6.268-14-14 6.268-14 14-14 14 6.268 14 14" fill="#34A853"/>
-    <path d="m424 350c0 88.366-71.634 160-160 160s-160-71.634-160-160 71.634-160 160-160 160 71.634 160 160z" stroke="#DADCE0" stroke-width="8"/>
-    <path d="m274 367.62 22.383-22.332 14.126 14.158-46.787 46.681-46.785-46.681 14.126-14.158 22.937 22.886v-70.172h20v69.618z" fill="#DADCE0" fill-rule="nonzero"/>
-    <path d="m202 302 124 124" stroke="#DADCE0" stroke-width="8"/>
-  </g>
-</svg>
diff --git a/chrome/browser/resources/chromeos/login/oobe_eula.html b/chrome/browser/resources/chromeos/login/oobe_eula.html
index c91edbb3c..1ff7069 100644
--- a/chrome/browser/resources/chromeos/login/oobe_eula.html
+++ b/chrome/browser/resources/chromeos/login/oobe_eula.html
@@ -123,8 +123,8 @@
       <webview slot="body" role="document" class="flex oobe-tos-webview"
           id="additionalChromeToSFrame">
       </webview>
-      <oobe-text-button slot="button-container" inverse
-          on-click="hideToSDialog_" text-key="oobeModalDialogClose">
+      <oobe-text-button id="close-additional-tos" slot="button-container"
+          on-click="hideToSDialog_" text-key="oobeModalDialogClose" inverse>
       </oobe-text-button>
     </cr-dialog>
   </template>
diff --git a/chrome/browser/resources/chromeos/login/oobe_eula.js b/chrome/browser/resources/chromeos/login/oobe_eula.js
index d6b3cb67..3027716 100644
--- a/chrome/browser/resources/chromeos/login/oobe_eula.js
+++ b/chrome/browser/resources/chromeos/login/oobe_eula.js
@@ -150,6 +150,11 @@
    * @private
    */
   onUsageChanged_() {
+    if (this.$.usageStats.checked) {
+      chrome.send('login.EulaScreen.userActed', ['select-stats-usage']);
+    } else {
+      chrome.send('login.EulaScreen.userActed', ['unselect-stats-usage']);
+    }
     this.screen.onUsageStatsClicked_(this.$.usageStats.checked);
   },
 
@@ -157,6 +162,7 @@
    * @private
    */
   onAdditionalTermsClicked_() {
+    chrome.send('login.EulaScreen.userActed', ['show-additional-tos']);
     this.$['additional-tos'].showModal();
   },
 
@@ -182,6 +188,7 @@
    * @private
    */
   onInstallationSettingsClicked_() {
+    chrome.send('login.EulaScreen.userActed', ['show-security-settings']);
     chrome.send('eulaOnInstallationSettingsPopupOpened');
     this.$.eulaDialog.hidden = true;
     this.$.installationSettingsDialog.hidden = false;
@@ -205,6 +212,7 @@
    * @private
    */
   onUsageStatsHelpLinkClicked_(e) {
+    chrome.send('login.EulaScreen.userActed', ['show-stats-usage-learn-more']);
     this.$['learn-more'].focus();
     chrome.send('eulaOnLearnMore');
     e.stopPropagation();
diff --git a/chrome/browser/resources/chromeos/login/recommend_apps.css b/chrome/browser/resources/chromeos/login/recommend_apps.css
index ba87a160..df985f4 100644
--- a/chrome/browser/resources/chromeos/login/recommend_apps.css
+++ b/chrome/browser/resources/chromeos/login/recommend_apps.css
@@ -15,11 +15,7 @@
 .recommend-apps-loading #app-list-error,
 .recommend-apps-loaded #recommend-apps-retry-button,
 .recommend-apps-loading #recommend-apps-install-button,
-.recommend-apps-loading #recommend-apps-retry-button,
-.error #subtitle,
-.error #app-list-view-container,
-.error .app-list-loading,
-.error #recommend-apps-install-button {
+.recommend-apps-loading #recommend-apps-retry-button {
   display: none;
 }
 
diff --git a/chrome/browser/resources/chromeos/login/recommend_apps.html b/chrome/browser/resources/chromeos/login/recommend_apps.html
index 98ed6fa..4067e976 100644
--- a/chrome/browser/resources/chromeos/login/recommend_apps.html
+++ b/chrome/browser/resources/chromeos/login/recommend_apps.html
@@ -18,16 +18,6 @@
       <iron-icon src="chrome://oobe/playstore.svg" slot="oobe-icon">
       </iron-icon>
       <div id="subtitle" slot="subtitle"></div>
-      <div id="app-list-error" slot="subtitle">
-        [[i18nDynamic(locale, 'recommendAppsError')]]
-      </div>
-      <div slot="footer" class="flex layout vertical center center-justified"
-          id="download-fail-illustration">
-        <img id="illustration"
-            srcset="images/1x/download_fail_illustration.svg 1x,
-            images/2x/download_fail_illustration.svg 2x"
-            class="oobe-illustration">
-      </div>
       <div id="app-list-view-container" slot="footer">
         <webview id="app-list-view"></webview>
       </div>
@@ -41,9 +31,6 @@
         <oobe-next-button id="recommend-apps-install-button"
             text-key="recommendAppsInstall" on-tap="onInstall_" inverse>
         </oobe-next-button>
-        <oobe-text-button id="recommend-apps-retry-button"
-            text-key="recommendAppsRetry" on-tap="onRetry_" border inverse>
-        </oobe-text-button>
       </div>
     </oobe-dialog>
   </template>
diff --git a/chrome/browser/resources/chromeos/login/recommend_apps.js b/chrome/browser/resources/chromeos/login/recommend_apps.js
index 68fbea8..3943c0f 100644
--- a/chrome/browser/resources/chromeos/login/recommend_apps.js
+++ b/chrome/browser/resources/chromeos/login/recommend_apps.js
@@ -40,11 +40,6 @@
     this.screen.onInstall();
   },
 
-  /** @private */
-  onRetry_() {
-    this.screen.onRetry();
-  },
-
   /**
    * Returns element by its id.
    * @param id String The ID of the element.
diff --git a/chrome/browser/resources/chromeos/login/screen_recommend_apps.js b/chrome/browser/resources/chromeos/login/screen_recommend_apps.js
index a73475c..14ebd0b9 100644
--- a/chrome/browser/resources/chromeos/login/screen_recommend_apps.js
+++ b/chrome/browser/resources/chromeos/login/screen_recommend_apps.js
@@ -8,8 +8,7 @@
 
 login.createScreen('RecommendAppsScreen', 'recommend-apps', function() {
   return {
-    EXTERNAL_API:
-        ['loadAppList', 'setThrobberVisible', 'setWebview', 'showError'],
+    EXTERNAL_API: ['loadAppList', 'setThrobberVisible', 'setWebview'],
 
     /** Initial UI State for screen */
     getOobeUIInitialState() {
@@ -83,21 +82,6 @@
       window.addEventListener('message', this.onMessage);
     },
 
-    /**
-     * Shows error UI when it fails to load the recommended app list.
-     */
-    showError() {
-      this.ensureInitialized_();
-
-      // Hide the loading throbber and show the error message.
-      this.setThrobberVisible(false);
-      this.removeClass_('recommend-apps-loading');
-      this.removeClass_('recommend-apps-loaded');
-      this.addClass_('error');
-
-      this.getElement_('recommend-apps-retry-button').focus();
-    },
-
     setWebview(contents) {
       const appListView = this.getElement_('app-list-view');
       appListView.src =
@@ -149,7 +133,6 @@
      */
     onGenerateContents() {
       this.removeClass_('recommend-apps-loading');
-      this.removeClass_('error');
       this.addClass_('recommend-apps-loaded');
       this.getElement_('recommend-apps-install-button').focus();
     },
@@ -177,18 +160,6 @@
     },
 
     /**
-     * Handles Retry button click.
-     */
-    onRetry() {
-      this.setThrobberVisible(true);
-      this.removeClass_('recommend-apps-loaded');
-      this.removeClass_('error');
-      this.addClass_('recommend-apps-loading');
-
-      chrome.send('recommendAppsRetry');
-    },
-
-    /**
      * Handles the message sent from the WebView.
      */
     onMessage(event) {
diff --git a/chrome/browser/resources/settings/chromeos/os_search_page/os_search_page.html b/chrome/browser/resources/settings/chromeos/os_search_page/os_search_page.html
index a440888..95141408 100644
--- a/chrome/browser/resources/settings/chromeos/os_search_page/os_search_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_search_page/os_search_page.html
@@ -45,7 +45,7 @@
         padding-inline-end: 8px;
       }
     </style>
-    <settings-animated-pages id="pages" section="search"
+    <settings-animated-pages id="pages" section="osSearch"
         focus-config="[[focusConfig_]]">
       <div route-path="default">
         <!-- Omnibox and launcher search engine. This shares the same pref with
diff --git a/chrome/browser/resources/settings/privacy_page/security_page.js b/chrome/browser/resources/settings/privacy_page/security_page.js
index 01a5a9fc..0e55018 100644
--- a/chrome/browser/resources/settings/privacy_page/security_page.js
+++ b/chrome/browser/resources/settings/privacy_page/security_page.js
@@ -56,7 +56,7 @@
     /** @private */
     selectSafeBrowsingRadio_: {
       type: String,
-      computed: 'computeSelectSafeBrowsingRadio_(prefs.safeBrowsing.*)',
+      computed: 'computeSelectSafeBrowsingRadio_(prefs.safebrowsing.*)',
     },
 
     /** @private {!settings.SafeBrowsingRadioManagedState} */
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 aad902f..12637d9 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
@@ -25,6 +25,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/enterprise/connectors/connectors_manager.h"
 #include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h"
 #include "chrome/browser/file_util_service.h"
 #include "chrome/browser/profiles/profile.h"
@@ -64,14 +65,6 @@
   return factory.get();
 }
 
-// Determines if the completion callback should be called only after all the
-// scan requests have finished and the verdicts known.
-bool WaitForVerdict() {
-  int state = g_browser_process->local_state()->GetInteger(
-      prefs::kDelayDeliveryUntilVerdict);
-  return state == DELAY_UPLOADS || state == DELAY_UPLOADS_AND_DOWNLOADS;
-}
-
 // A BinaryUploadService::Request implementation that gets the data to scan
 // from a string.
 class StringSourceRequest : public BinaryUploadService::Request {
@@ -149,26 +142,6 @@
   return mime_type;
 }
 
-bool AllowLargeFile() {
-  int state = g_browser_process->local_state()->GetInteger(
-      prefs::kBlockLargeFileTransfer);
-  return state != BLOCK_LARGE_UPLOADS &&
-         state != BLOCK_LARGE_UPLOADS_AND_DOWNLOADS;
-}
-
-bool AllowEncryptedFiles() {
-  int state = g_browser_process->local_state()->GetInteger(
-      prefs::kAllowPasswordProtectedFiles);
-  return state == ALLOW_UPLOADS || state == ALLOW_UPLOADS_AND_DOWNLOADS;
-}
-
-bool AllowUnsupportedFileTypes() {
-  int state = g_browser_process->local_state()->GetInteger(
-      prefs::kBlockUnsupportedFiletypes);
-  return state != BLOCK_UNSUPPORTED_FILETYPES_UPLOADS &&
-         state != BLOCK_UNSUPPORTED_FILETYPES_UPLOADS_AND_DOWNLOADS;
-}
-
 bool* UIEnabledStorage() {
   static bool enabled = true;
   return &enabled;
@@ -252,8 +225,11 @@
   RunCallback();
 }
 
+// static
 bool DeepScanningDialogDelegate::ResultShouldAllowDataUse(
-    BinaryUploadService::Result result) {
+    BinaryUploadService::Result result,
+    const enterprise_connectors::ConnectorsManager::AnalysisSettings&
+        settings) {
   // Keep this implemented as a switch instead of a simpler if statement so that
   // new values added to BinaryUploadService::Result cause a compiler error.
   switch (result) {
@@ -270,13 +246,13 @@
       return true;
 
     case BinaryUploadService::Result::FILE_TOO_LARGE:
-      return AllowLargeFile();
+      return !settings.block_large_files;
 
     case BinaryUploadService::Result::FILE_ENCRYPTED:
-      return AllowEncryptedFiles();
+      return !settings.block_password_protected_files;
 
     case BinaryUploadService::Result::UNSUPPORTED_FILE_TYPE:
-      return AllowUnsupportedFileTypes();
+      return !settings.block_unsupported_file_types;
   }
 }
 
@@ -300,15 +276,14 @@
       (state == CHECK_UPLOADS || state == CHECK_UPLOADS_AND_DOWNLOADS);
 
   if (url.is_valid())
-    data->url = url.spec();
-  if (data->do_dlp_scan &&
-      g_browser_process->local_state()->HasPrefPath(
-          prefs::kURLsToNotCheckComplianceOfUploadedContent)) {
-    const base::ListValue* filters = g_browser_process->local_state()->GetList(
-        prefs::kURLsToNotCheckComplianceOfUploadedContent);
-    url_matcher::URLMatcher matcher;
-    policy::url_util::AddAllowFilters(&matcher, filters);
-    data->do_dlp_scan = matcher.MatchURL(url).empty();
+    data->url = url;
+  if (data->do_dlp_scan) {
+    // TODO(domfc): Instantiate the manager somewhere that can be accessed by
+    // both the upload and download paths instead. This is fine for now since
+    // the manager isn't caching anything.
+    data->do_dlp_scan =
+        enterprise_connectors::ConnectorsManager()
+            .MatchURLAgainstLegacyDlpPolicies(url, /*upload*/ true);
   }
 
   // See if malware checks are needed.
@@ -320,17 +295,9 @@
       (state == SEND_UPLOADS || state == SEND_UPLOADS_AND_DOWNLOADS);
 
   if (data->do_malware_scan) {
-    if (g_browser_process->local_state()->HasPrefPath(
-            prefs::kURLsToCheckForMalwareOfUploadedContent)) {
-      const base::ListValue* filters =
-          g_browser_process->local_state()->GetList(
-              prefs::kURLsToCheckForMalwareOfUploadedContent);
-      url_matcher::URLMatcher matcher;
-      policy::url_util::AddAllowFilters(&matcher, filters);
-      data->do_malware_scan = !matcher.MatchURL(url).empty();
-    } else {
-      data->do_malware_scan = false;
-    }
+    data->do_malware_scan =
+        enterprise_connectors::ConnectorsManager()
+            .MatchURLAgainstLegacyMalwarePolicies(url, /*upload*/ true);
   }
 
   return data->do_dlp_scan || data->do_malware_scan;
@@ -342,8 +309,36 @@
     Data data,
     CompletionCallback callback,
     DeepScanAccessPoint access_point) {
+  enterprise_connectors::ConnectorsManager().GetAnalysisSettings(
+      data.url, enterprise_connectors::AnalysisConnector::FILE_ATTACHED,
+      base::BindOnce(&DeepScanningDialogDelegate::OnGotAnalysisSettings,
+                     web_contents, std::move(data), std::move(callback),
+                     access_point));
+}
+
+// static
+void DeepScanningDialogDelegate::OnGotAnalysisSettings(
+    content::WebContents* web_contents,
+    Data data,
+    CompletionCallback callback,
+    DeepScanAccessPoint access_point,
+    base::Optional<enterprise_connectors::ConnectorsManager::AnalysisSettings>
+        settings) {
+  // Proceed with a scan after obtaining settings, otherwise complete with a
+  // positive result.
+  if (!settings.has_value()) {
+    Result result;
+    result.text_results = std::vector<bool>(data.text.size(), true);
+    result.paths_results = std::vector<bool>(data.paths.size(), true);
+    std::move(callback).Run(std::move(data), std::move(result));
+    return;
+  }
+
+  data.settings = std::move(settings.value());
+
   Factory* testing_factory = GetFactoryStorage();
-  bool wait_for_verdict = WaitForVerdict();
+  bool wait_for_verdict = data.settings.block_until_verdict ==
+                          enterprise_connectors::BlockUntilVerdict::BLOCK;
 
   // Using new instead of std::make_unique<> to access non public constructor.
   auto delegate = testing_factory->is_null()
@@ -438,7 +433,7 @@
       content_size, result, response);
 
   text_request_complete_ = true;
-  bool text_complies = ResultShouldAllowDataUse(result) &&
+  bool text_complies = ResultShouldAllowDataUse(result, data_.settings) &&
                        DlpTriggeredRulesOK(response.dlp_scan_verdict());
   std::fill(result_.text_results.begin(), result_.text_results.end(),
             text_complies);
@@ -476,7 +471,8 @@
                  MalwareDeepScanningVerdict::CLEAN;
   }
 
-  bool file_complies = ResultShouldAllowDataUse(result) && dlp_ok && malware_ok;
+  bool file_complies =
+      ResultShouldAllowDataUse(result, data_.settings) && dlp_ok && malware_ok;
   result_.paths_results[index] = file_complies;
 
   ++file_result_count_;
@@ -610,7 +606,7 @@
   if (data_.do_dlp_scan) {
     DlpDeepScanningClientRequest dlp_request;
     dlp_request.set_content_source(trigger);
-    dlp_request.set_url(data_.url);
+    dlp_request.set_url(data_.url.spec());
     request->set_request_dlp_scan(std::move(dlp_request));
   }
 
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 a2abc73..af57348f 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
@@ -16,6 +16,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
+#include "chrome/browser/enterprise/connectors/connectors_manager.h"
 #include "chrome/browser/enterprise/connectors/enterprise_connectors_policy_handler.h"
 #include "chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h"
 #include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h"
@@ -77,13 +78,16 @@
     bool do_malware_scan = false;
 
     // URL of the page that is to receive sensitive data.
-    std::string url;
+    GURL url;
 
     // Text data to scan, such as plain text, URLs, HTML content, etc.
     std::vector<base::string16> text;
 
     // List of files to scan.
     std::vector<base::FilePath> paths;
+
+    // The settings to use for the analysis of the data in this struct.
+    enterprise_connectors::ConnectorsManager::AnalysisSettings settings;
   };
 
   // Result of deep scanning.  Each Result contains the verdicts of deep scans
@@ -221,7 +225,10 @@
 
   // Determines if a request result should be used to allow a data use or to
   // block it.
-  static bool ResultShouldAllowDataUse(BinaryUploadService::Result result);
+  static bool ResultShouldAllowDataUse(
+      BinaryUploadService::Result result,
+      const enterprise_connectors::ConnectorsManager::AnalysisSettings&
+          settings);
 
  protected:
   DeepScanningDialogDelegate(content::WebContents* web_contents,
@@ -242,6 +249,15 @@
   }
 
  private:
+  // Callback used to obtain analysis settings from the Connectors manager.
+  static void OnGotAnalysisSettings(
+      content::WebContents* web_contents,
+      Data data,
+      CompletionCallback callback,
+      DeepScanAccessPoint access_point,
+      base::Optional<enterprise_connectors::ConnectorsManager::AnalysisSettings>
+          settings);
+
   // Uploads data for deep scanning.  Returns true if uploading is occurring in
   // the background and false if there is nothing to do.
   bool UploadData();
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_browsertest.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_browsertest.cc
index 5ab3c9c..f1c7e14 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_browsertest.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_browsertest.cc
@@ -8,6 +8,7 @@
 #include "base/path_service.h"
 #include "base/stl_util.h"
 #include "base/test/bind_test_util.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h"
 #include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h"
 #include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_browsertest_base.h"
@@ -19,6 +20,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
 #include "components/policy/core/common/cloud/realtime_reporting_job_configuration.h"
+#include "components/prefs/scoped_user_pref_update.h"
 
 using extensions::SafeBrowsingPrivateEventRouter;
 using ::testing::_;
@@ -162,6 +164,8 @@
 
 constexpr char kDmToken[] = "dm_token";
 
+constexpr char kTestUrl[] = "https://google.com";
+
 }  // namespace
 
 // Tests the behavior of the dialog delegate with minimal overriding of methods.
@@ -182,6 +186,12 @@
     SetWaitPolicy(DELAY_UPLOADS);
     SetUnsafeEventsReportingPolicy(true);
 
+    // Add the wildcard pattern to this policy since malware responses are
+    // verified for most of these tests.
+    ListPrefUpdate(g_browser_process->local_state(),
+                   prefs::kURLsToCheckForMalwareOfUploadedContent)
+        ->Append("*");
+
     client_ = std::make_unique<policy::MockCloudPolicyClient>();
     extensions::SafeBrowsingPrivateEventRouterFactory::GetForProfile(
         browser()->profile())
@@ -224,6 +234,8 @@
   data.do_malware_scan = true;
   data.text.emplace_back(base::UTF8ToUTF16("foo"));
   data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.doc"));
+  ASSERT_TRUE(DeepScanningDialogDelegate::IsEnabled(browser()->profile(),
+                                                    GURL(kTestUrl), &data));
 
   // Nothing should be reported for unauthorized users.
   ExpectNoReport();
@@ -255,16 +267,6 @@
 IN_PROC_BROWSER_TEST_F(DeepScanningDialogDelegateBrowserTest, Files) {
   base::ScopedAllowBlockingForTesting allow_blocking;
 
-  // Create the files to be opened and scanned.
-  DeepScanningDialogDelegate::Data data;
-  data.do_dlp_scan = true;
-  data.do_malware_scan = true;
-  CreateFilesForTest({"ok.doc", "bad.exe"},
-                     {"ok file content", "bad file content"}, &data);
-
-  FakeBinaryUploadServiceStorage()->SetAuthorized(true);
-  FakeBinaryUploadServiceStorage()->SetShouldAutomaticallyAuthorize(true);
-
   // Set up delegate and upload service.
   EnableUploadsScanningAndReporting();
 
@@ -283,6 +285,18 @@
   bad_response.mutable_malware_scan_verdict()->set_verdict(
       MalwareDeepScanningVerdict::MALWARE);
 
+  FakeBinaryUploadServiceStorage()->SetAuthorized(true);
+  FakeBinaryUploadServiceStorage()->SetShouldAutomaticallyAuthorize(true);
+
+  // Create the files to be opened and scanned.
+  DeepScanningDialogDelegate::Data data;
+  data.do_dlp_scan = true;
+  data.do_malware_scan = true;
+  CreateFilesForTest({"ok.doc", "bad.exe"},
+                     {"ok file content", "bad file content"}, &data);
+  ASSERT_TRUE(DeepScanningDialogDelegate::IsEnabled(browser()->profile(),
+                                                    GURL(kTestUrl), &data));
+
   // The malware verdict means an event should be reported.
   EventReportValidator validator(client());
   validator.ExpectDangerousDeepScanningResult(
@@ -379,6 +393,8 @@
   data.do_dlp_scan = true;
   data.do_malware_scan = true;
   data.paths.emplace_back(test_zip);
+  ASSERT_TRUE(DeepScanningDialogDelegate::IsEnabled(browser()->profile(),
+                                                    GURL(kTestUrl), &data));
 
   // The file should be reported as unscanned.
   EventReportValidator validator(client());
@@ -450,15 +466,12 @@
     Test) {
   base::ScopedAllowBlockingForTesting allow_blocking;
 
-  // Create the files with unsupported types.
-  DeepScanningDialogDelegate::Data data;
-  data.do_dlp_scan = true;
-  data.do_malware_scan = false;
-  CreateFilesForTest({"a.sh"}, {"file content"}, &data);
-
   // Set up delegate and upload service.
   EnableUploadsScanningAndReporting();
   SetBlockUnsupportedFileTypesPolicy(block_unsupported_file_types());
+  ListPrefUpdate(g_browser_process->local_state(),
+                 prefs::kURLsToCheckForMalwareOfUploadedContent)
+      ->Clear();
 
   DeepScanningDialogDelegate::SetFactoryForTesting(
       base::BindRepeating(&MinimalFakeDeepScanningDialogDelegate::Create));
@@ -466,6 +479,14 @@
   FakeBinaryUploadServiceStorage()->SetAuthorized(true);
   FakeBinaryUploadServiceStorage()->SetShouldAutomaticallyAuthorize(true);
 
+  // Create the files with unsupported types.
+  DeepScanningDialogDelegate::Data data;
+  data.do_dlp_scan = true;
+  data.do_malware_scan = false;
+  CreateFilesForTest({"a.sh"}, {"file content"}, &data);
+  ASSERT_TRUE(DeepScanningDialogDelegate::IsEnabled(browser()->profile(),
+                                                    GURL(kTestUrl), &data));
+
   // The file should be reported as unscanned.
   EventReportValidator validator(client());
   validator.ExpectUnscannedFileEvent(
@@ -541,14 +562,6 @@
     Test) {
   base::ScopedAllowBlockingForTesting allow_blocking;
 
-  // Create the large file.
-  DeepScanningDialogDelegate::Data data;
-  data.do_dlp_scan = true;
-  data.do_malware_scan = true;
-  CreateFilesForTest(
-      {"large.doc"},
-      {std::string(BinaryUploadService::kMaxUploadSizeBytes + 1, 'a')}, &data);
-
   // Set up delegate and upload service.
   EnableUploadsScanningAndReporting();
   SetBlockLargeFileTransferPolicy(block_large_file_transfer());
@@ -559,6 +572,17 @@
   FakeBinaryUploadServiceStorage()->SetAuthorized(true);
   FakeBinaryUploadServiceStorage()->SetShouldAutomaticallyAuthorize(true);
 
+  // Create the large file.
+  DeepScanningDialogDelegate::Data data;
+  data.do_dlp_scan = true;
+  data.do_malware_scan = true;
+
+  CreateFilesForTest(
+      {"large.doc"},
+      {std::string(BinaryUploadService::kMaxUploadSizeBytes + 1, 'a')}, &data);
+  ASSERT_TRUE(DeepScanningDialogDelegate::IsEnabled(browser()->profile(),
+                                                    GURL(kTestUrl), &data));
+
   // The file should be reported as unscanned.
   EventReportValidator validator(client());
   validator.ExpectUnscannedFileEvent(
@@ -675,6 +699,8 @@
   data.do_malware_scan = true;
   data.text.emplace_back(base::UTF8ToUTF16("text1"));
   data.text.emplace_back(base::UTF8ToUTF16("text2"));
+  ASSERT_TRUE(DeepScanningDialogDelegate::IsEnabled(browser()->profile(),
+                                                    GURL(kTestUrl), &data));
 
   // Start test.
   DeepScanningDialogDelegate::ShowForWebContents(
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 18983de..0a0fc59 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
@@ -1232,9 +1232,11 @@
   data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.7z"));
   data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.bzip"));
   data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.cab"));
+  data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.csv"));
   data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.doc"));
   data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.docx"));
   data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.eps"));
+  data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.tar.gz"));
   data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.gzip"));
   data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.odt"));
   data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.pdf"));
@@ -1262,8 +1264,8 @@
              base::BindOnce(
                  [](bool* called, const DeepScanningDialogDelegate::Data& data,
                     const DeepScanningDialogDelegate::Result& result) {
-                   EXPECT_EQ(21u, data.paths.size());
-                   EXPECT_EQ(21u, result.paths_results.size());
+                   EXPECT_EQ(23u, data.paths.size());
+                   EXPECT_EQ(23u, result.paths_results.size());
 
                    // The supported types should be marked as false.
                    for (const auto& result : result.paths_results)
@@ -1466,7 +1468,7 @@
 
             bool expected =
                 DeepScanningDialogDelegate::ResultShouldAllowDataUse(
-                    GetParam());
+                    GetParam(), data.settings);
             EXPECT_EQ(expected, result.paths_results[0]);
             *called = true;
           },
@@ -1487,7 +1489,25 @@
                     BinaryUploadService::Result::UNAUTHORIZED,
                     BinaryUploadService::Result::FILE_ENCRYPTED));
 
-using DeepScanningDialogDelegatePolicyResultsTest = BaseTest;
+class DeepScanningDialogDelegatePolicyResultsTest : public BaseTest {
+ public:
+  enterprise_connectors::ConnectorsManager::AnalysisSettings settings() {
+    base::Optional<enterprise_connectors::ConnectorsManager::AnalysisSettings>
+        settings;
+    manager_.GetAnalysisSettings(
+        GURL(kTestUrl), enterprise_connectors::AnalysisConnector::FILE_ATTACHED,
+        base::BindLambdaForTesting(
+            [&settings](
+                base::Optional<
+                    enterprise_connectors::ConnectorsManager::AnalysisSettings>
+                    tmp_settings) { settings = std::move(tmp_settings); }));
+    EXPECT_TRUE(settings.has_value());
+    return std::move(settings.value());
+  }
+
+ private:
+  enterprise_connectors::ConnectorsManager manager_;
+};
 
 TEST_F(DeepScanningDialogDelegatePolicyResultsTest, BlockLargeFile) {
   // The value returned by ResultShouldAllowDataUse for FILE_TOO_LARGE should
@@ -1495,19 +1515,19 @@
   SetBlockLargeFilePolicy(
       BlockLargeFileTransferValues::BLOCK_LARGE_UPLOADS_AND_DOWNLOADS);
   EXPECT_FALSE(DeepScanningDialogDelegate::ResultShouldAllowDataUse(
-      BinaryUploadService::Result::FILE_TOO_LARGE));
+      BinaryUploadService::Result::FILE_TOO_LARGE, settings()));
 
   SetBlockLargeFilePolicy(BlockLargeFileTransferValues::BLOCK_LARGE_DOWNLOADS);
   EXPECT_TRUE(DeepScanningDialogDelegate::ResultShouldAllowDataUse(
-      BinaryUploadService::Result::FILE_TOO_LARGE));
+      BinaryUploadService::Result::FILE_TOO_LARGE, settings()));
 
   SetBlockLargeFilePolicy(BlockLargeFileTransferValues::BLOCK_LARGE_UPLOADS);
   EXPECT_FALSE(DeepScanningDialogDelegate::ResultShouldAllowDataUse(
-      BinaryUploadService::Result::FILE_TOO_LARGE));
+      BinaryUploadService::Result::FILE_TOO_LARGE, settings()));
 
   SetBlockLargeFilePolicy(BlockLargeFileTransferValues::BLOCK_NONE);
   EXPECT_TRUE(DeepScanningDialogDelegate::ResultShouldAllowDataUse(
-      BinaryUploadService::Result::FILE_TOO_LARGE));
+      BinaryUploadService::Result::FILE_TOO_LARGE, settings()));
 }
 
 TEST_F(DeepScanningDialogDelegatePolicyResultsTest,
@@ -1517,19 +1537,19 @@
   SetAllowPasswordPolicy(
       AllowPasswordProtectedFilesValues::ALLOW_UPLOADS_AND_DOWNLOADS);
   EXPECT_TRUE(DeepScanningDialogDelegate::ResultShouldAllowDataUse(
-      BinaryUploadService::Result::FILE_ENCRYPTED));
+      BinaryUploadService::Result::FILE_ENCRYPTED, settings()));
 
   SetAllowPasswordPolicy(AllowPasswordProtectedFilesValues::ALLOW_DOWNLOADS);
   EXPECT_FALSE(DeepScanningDialogDelegate::ResultShouldAllowDataUse(
-      BinaryUploadService::Result::FILE_ENCRYPTED));
+      BinaryUploadService::Result::FILE_ENCRYPTED, settings()));
 
   SetAllowPasswordPolicy(AllowPasswordProtectedFilesValues::ALLOW_UPLOADS);
   EXPECT_TRUE(DeepScanningDialogDelegate::ResultShouldAllowDataUse(
-      BinaryUploadService::Result::FILE_ENCRYPTED));
+      BinaryUploadService::Result::FILE_ENCRYPTED, settings()));
 
   SetAllowPasswordPolicy(AllowPasswordProtectedFilesValues::ALLOW_NONE);
   EXPECT_FALSE(DeepScanningDialogDelegate::ResultShouldAllowDataUse(
-      BinaryUploadService::Result::FILE_ENCRYPTED));
+      BinaryUploadService::Result::FILE_ENCRYPTED, settings()));
 }
 
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views_browsertest.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views_browsertest.cc
index 4a722c9..a95c33dd 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views_browsertest.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views_browsertest.cc
@@ -5,12 +5,14 @@
 #include "base/test/bind_test_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_browsertest_base.h"
 #include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.h"
 #include "chrome/browser/safe_browsing/cloud_content_scanning/fake_deep_scanning_dialog_delegate.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
+#include "components/prefs/scoped_user_pref_update.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/views/controls/image_view.h"
@@ -427,6 +429,8 @@
   DeepScanAccessPoint access_point() const { return std::get<2>(GetParam()); }
 };
 
+constexpr char kTestUrl[] = "https://google.com";
+
 }  // namespace
 
 IN_PROC_BROWSER_TEST_P(DeepScanningDialogViewsBehaviorBrowserTest, Test) {
@@ -448,6 +452,9 @@
   if (malware_enabled()) {
     malware = malware_success();
     SetMalwarePolicy(SEND_UPLOADS_AND_DOWNLOADS);
+    ListPrefUpdate(g_browser_process->local_state(),
+                   prefs::kURLsToCheckForMalwareOfUploadedContent)
+        ->Append("*");
   }
   SetStatusCallbackResponse(
       SimpleDeepScanningClientResponseForTesting(dlp, malware));
@@ -467,6 +474,8 @@
   data.do_dlp_scan = dlp_enabled();
   data.do_malware_scan = malware_enabled();
   CreateFilesForTest({"foo.doc"}, {"content"}, &data);
+  ASSERT_TRUE(DeepScanningDialogDelegate::IsEnabled(browser()->profile(),
+                                                    GURL(kTestUrl), &data));
 
   DeepScanningDialogDelegate::ShowForWebContents(
       browser()->tab_strip_model()->GetActiveWebContents(), std::move(data),
@@ -536,6 +545,8 @@
   data.do_malware_scan = false;
   CreateFilesForTest({"foo.doc", "bar.doc", "baz.doc"},
                      {"random", "file", "contents"}, &data);
+  ASSERT_TRUE(DeepScanningDialogDelegate::IsEnabled(browser()->profile(),
+                                                    GURL(kTestUrl), &data));
 
   DeepScanningDialogDelegate::ShowForWebContents(
       browser()->tab_strip_model()->GetActiveWebContents(), std::move(data),
@@ -584,6 +595,8 @@
   data.text.emplace_back(base::UTF8ToUTF16("foo"));
   data.text.emplace_back(base::UTF8ToUTF16("bar"));
   CreateFilesForTest({"foo.doc", "bar.doc"}, {"file", "content"}, &data);
+  ASSERT_TRUE(DeepScanningDialogDelegate::IsEnabled(browser()->profile(),
+                                                    GURL(kTestUrl), &data));
 
   DeepScanningDialogDelegate::ShowForWebContents(
       browser()->tab_strip_model()->GetActiveWebContents(), std::move(data),
@@ -642,6 +655,8 @@
     CreateFilesForTest({"foo.doc"}, {"content"}, &data);
   else
     data.text.emplace_back(base::UTF8ToUTF16("foo"));
+  ASSERT_TRUE(DeepScanningDialogDelegate::IsEnabled(browser()->profile(),
+                                                    GURL(kTestUrl), &data));
 
   DeepScanningDialogDelegate::ShowForWebContents(
       browser()->tab_strip_model()->GetActiveWebContents(), std::move(data),
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc
index 981d34e..6efdeaf 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc
@@ -230,13 +230,14 @@
       50);
 }
 
-std::array<const base::FilePath::CharType*, 21> SupportedDlpFileTypes() {
+std::array<const base::FilePath::CharType*, 23> SupportedDlpFileTypes() {
   // Keep sorted for efficient access.
-  static constexpr const std::array<const base::FilePath::CharType*, 21>
+  static constexpr const std::array<const base::FilePath::CharType*, 23>
       kSupportedDLPFileTypes = {
           FILE_PATH_LITERAL(".7z"),   FILE_PATH_LITERAL(".bzip"),
-          FILE_PATH_LITERAL(".cab"),  FILE_PATH_LITERAL(".doc"),
-          FILE_PATH_LITERAL(".docx"), FILE_PATH_LITERAL(".eps"),
+          FILE_PATH_LITERAL(".cab"),  FILE_PATH_LITERAL(".csv"),
+          FILE_PATH_LITERAL(".doc"),  FILE_PATH_LITERAL(".docx"),
+          FILE_PATH_LITERAL(".eps"),  FILE_PATH_LITERAL(".gz"),
           FILE_PATH_LITERAL(".gzip"), FILE_PATH_LITERAL(".odt"),
           FILE_PATH_LITERAL(".pdf"),  FILE_PATH_LITERAL(".ppt"),
           FILE_PATH_LITERAL(".pptx"), FILE_PATH_LITERAL(".ps"),
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h
index dafcbd0..776f0c63 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h
@@ -77,7 +77,7 @@
                            bool success);
 
 // Returns an array of the file types supported for DLP scanning.
-std::array<const base::FilePath::CharType*, 21> SupportedDlpFileTypes();
+std::array<const base::FilePath::CharType*, 23> SupportedDlpFileTypes();
 
 // Returns true if the given file type is supported for DLP scanning.
 bool FileTypeSupported(bool for_malware_scan,
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils_unittest.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils_unittest.cc
index ff87d4d..25b80d6f 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils_unittest.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils_unittest.cc
@@ -73,8 +73,32 @@
                      testing::ValuesIn(kAllBinaryUploadServiceResults)));
 
 TEST_P(DeepScanningUtilsUMATest, SuccessfulScanVerdicts) {
+  // Record metrics for the 5 successful scan possibilities:
+  // - A default response
+  // - A DLP response with SUCCESS
+  // - A malware respopnse with MALWARE, UWS, CLEAN
   RecordDeepScanMetrics(access_point(), kDuration, kTotalBytes, result(),
                         DeepScanningClientResponse());
+  {
+    DlpDeepScanningVerdict dlp_verdict;
+    dlp_verdict.set_status(DlpDeepScanningVerdict::SUCCESS);
+    DeepScanningClientResponse response;
+    *response.mutable_dlp_scan_verdict() = dlp_verdict;
+
+    RecordDeepScanMetrics(access_point(), kDuration, kTotalBytes, result(),
+                          response);
+  }
+  for (const auto verdict :
+       {MalwareDeepScanningVerdict::MALWARE, MalwareDeepScanningVerdict::UWS,
+        MalwareDeepScanningVerdict::CLEAN}) {
+    MalwareDeepScanningVerdict malware_verdict;
+    malware_verdict.set_verdict(verdict);
+    DeepScanningClientResponse response;
+    *response.mutable_malware_scan_verdict() = malware_verdict;
+
+    RecordDeepScanMetrics(access_point(), kDuration, kTotalBytes, result(),
+                          response);
+  }
 
   if (result() == BinaryUploadService::Result::UNAUTHORIZED) {
     EXPECT_EQ(
@@ -83,77 +107,83 @@
   } else {
     // We expect at least 2 histograms (<access-point>.Duration and
     // <access-point>.<result>.Duration), but only expect a third histogram in
-    // the success case (bytes/seconds).
+    // the success case (bytes/seconds). Each of these should have 5 records to
+    // match each of the RecordDeepScanMetrics calls.
     uint64_t expected_histograms = success() ? 3u : 2u;
-    EXPECT_EQ(
-        expected_histograms,
-        histograms().GetTotalCountsForPrefix("SafeBrowsing.DeepScan.").size());
+    auto recorded =
+        histograms().GetTotalCountsForPrefix("SafeBrowsing.DeepScan.");
+    EXPECT_EQ(expected_histograms, recorded.size());
     if (success()) {
-      histograms().ExpectUniqueSample(
-          "SafeBrowsing.DeepScan." + access_point_string() + ".BytesPerSeconds",
-          kTotalBytes / kDuration.InSeconds(), 1);
+      EXPECT_EQ(recorded["SafeBrowsing.DeepScan." + access_point_string() +
+                         ".BytesPerSeconds"],
+                5);
     }
-    histograms().ExpectTimeBucketCount(
-        "SafeBrowsing.DeepScan." + access_point_string() + ".Duration",
-        kDuration, 1);
-    histograms().ExpectTimeBucketCount("SafeBrowsing.DeepScan." +
-                                           access_point_string() + "." +
-                                           result_value(true) + ".Duration",
-                                       kDuration, 1);
+    EXPECT_EQ(recorded["SafeBrowsing.DeepScan." + access_point_string() +
+                       ".Duration"],
+              5);
+    EXPECT_EQ(recorded["SafeBrowsing.DeepScan." + access_point_string() + "." +
+                       result_value(true) + ".Duration"],
+              5);
   }
 }
 
-TEST_P(DeepScanningUtilsUMATest, UnsuccessfulDlpScanVerdict) {
-  DlpDeepScanningVerdict dlp_verdict;
-  dlp_verdict.set_status(DlpDeepScanningVerdict::FAILURE);
-  DeepScanningClientResponse response;
-  *response.mutable_dlp_scan_verdict() = dlp_verdict;
+TEST_P(DeepScanningUtilsUMATest, UnsuccessfulDlpScanVerdicts) {
+  // Record metrics for the 2 unsuccessful DLP scan possibilities.
+  for (const auto verdict : {DlpDeepScanningVerdict::FAILURE,
+                             DlpDeepScanningVerdict::STATUS_UNKNOWN}) {
+    DlpDeepScanningVerdict dlp_verdict;
+    dlp_verdict.set_status(verdict);
+    DeepScanningClientResponse response;
+    *response.mutable_dlp_scan_verdict() = dlp_verdict;
 
-  RecordDeepScanMetrics(access_point(), kDuration, kTotalBytes, result(),
-                        response);
+    RecordDeepScanMetrics(access_point(), kDuration, kTotalBytes, result(),
+                          response);
+  }
 
   if (result() == BinaryUploadService::Result::UNAUTHORIZED) {
     EXPECT_EQ(
         0u,
         histograms().GetTotalCountsForPrefix("SafeBrowsing.DeepScan.").size());
   } else {
-    EXPECT_EQ(
-        2u,
-        histograms().GetTotalCountsForPrefix("SafeBrowsing.DeepScan.").size());
-    histograms().ExpectTimeBucketCount(
-        "SafeBrowsing.DeepScan." + access_point_string() + ".Duration",
-        kDuration, 1);
-    histograms().ExpectTimeBucketCount("SafeBrowsing.DeepScan." +
-                                           access_point_string() + "." +
-                                           result_value(false) + ".Duration",
-                                       kDuration, 1);
+    auto recorded =
+        histograms().GetTotalCountsForPrefix("SafeBrowsing.DeepScan.");
+    EXPECT_EQ(2u, recorded.size());
+    EXPECT_EQ(recorded["SafeBrowsing.DeepScan." + access_point_string() +
+                       ".Duration"],
+              2);
+    EXPECT_EQ(recorded["SafeBrowsing.DeepScan." + access_point_string() + "." +
+                       result_value(false) + ".Duration"],
+              2);
   }
 }
 
 TEST_P(DeepScanningUtilsUMATest, UnsuccessfulMalwareScanVerdict) {
-  MalwareDeepScanningVerdict malware_verdict;
-  malware_verdict.set_verdict(MalwareDeepScanningVerdict::VERDICT_UNSPECIFIED);
-  DeepScanningClientResponse response;
-  *response.mutable_malware_scan_verdict() = malware_verdict;
+  // Record metrics for the 2 unsuccessful malware scan possibilities.
+  for (const auto verdict : {MalwareDeepScanningVerdict::VERDICT_UNSPECIFIED,
+                             MalwareDeepScanningVerdict::SCAN_FAILURE}) {
+    MalwareDeepScanningVerdict malware_verdict;
+    malware_verdict.set_verdict(verdict);
+    DeepScanningClientResponse response;
+    *response.mutable_malware_scan_verdict() = malware_verdict;
 
-  RecordDeepScanMetrics(access_point(), kDuration, kTotalBytes, result(),
-                        response);
+    RecordDeepScanMetrics(access_point(), kDuration, kTotalBytes, result(),
+                          response);
+  }
 
   if (result() == BinaryUploadService::Result::UNAUTHORIZED) {
     EXPECT_EQ(
         0u,
         histograms().GetTotalCountsForPrefix("SafeBrowsing.DeepScan.").size());
   } else {
-    EXPECT_EQ(
-        2u,
-        histograms().GetTotalCountsForPrefix("SafeBrowsing.DeepScan.").size());
-    histograms().ExpectTimeBucketCount(
-        "SafeBrowsing.DeepScan." + access_point_string() + ".Duration",
-        kDuration, 1);
-    histograms().ExpectTimeBucketCount("SafeBrowsing.DeepScan." +
-                                           access_point_string() + "." +
-                                           result_value(false) + ".Duration",
-                                       kDuration, 1);
+    auto recorded =
+        histograms().GetTotalCountsForPrefix("SafeBrowsing.DeepScan.");
+    EXPECT_EQ(2u, recorded.size());
+    EXPECT_EQ(recorded["SafeBrowsing.DeepScan." + access_point_string() +
+                       ".Duration"],
+              2);
+    EXPECT_EQ(recorded["SafeBrowsing.DeepScan." + access_point_string() + "." +
+                       result_value(false) + ".Duration"],
+              2);
   }
 }
 
diff --git a/chrome/browser/service_process/service_process_control_browsertest.cc b/chrome/browser/service_process/service_process_control_browsertest.cc
index b4a80e3..8fb8eff 100644
--- a/chrome/browser/service_process/service_process_control_browsertest.cc
+++ b/chrome/browser/service_process/service_process_control_browsertest.cc
@@ -98,10 +98,14 @@
   void SetUp() override {
     InProcessBrowserTest::SetUp();
 
+#if defined(OS_MACOSX) || defined(OS_LINUX)
     // This should not be needed because TearDown() ends with a closed
     // service_process_, but HistogramsTimeout and Histograms fail without this
-    // on Mac.
+    // on Mac, and on Linux asan builds (https://crbug.com/1059446).
+    // Note that closing the process handle means that the exit-code check in
+    // TearDown will be skipped.
     service_process_.Close();
+#endif
   }
 
   void TearDown() override {
diff --git a/chrome/browser/spellchecker/spellcheck_language_blacklist_policy_handler.cc b/chrome/browser/spellchecker/spellcheck_language_blacklist_policy_handler.cc
index 77fd7c24..c6e9b80 100644
--- a/chrome/browser/spellchecker/spellcheck_language_blacklist_policy_handler.cc
+++ b/chrome/browser/spellchecker/spellcheck_language_blacklist_policy_handler.cc
@@ -12,12 +12,13 @@
 #include "base/syslog_logging.h"
 #include "base/values.h"
 #include "build/build_config.h"
+#include "chrome/browser/spellchecker/spellcheck_service.h"
 #include "chrome/common/pref_names.h"
 #include "components/policy/core/browser/policy_error_map.h"
 #include "components/policy/policy_constants.h"
 #include "components/prefs/pref_value_map.h"
 #include "components/spellcheck/browser/pref_names.h"
-#include "components/spellcheck/common/spellcheck_common.h"
+#include "components/spellcheck/common/spellcheck_features.h"
 #include "components/strings/grit/components_strings.h"
 
 SpellcheckLanguageBlacklistPolicyHandler::
@@ -113,9 +114,11 @@
   // Separate the valid languages from the unknown / unsupported languages and
   // the languages that also appear in the SpellcheckLanguage policy.
   for (const base::Value& language : value->GetList()) {
+    std::string candidate_language =
+        base::TrimWhitespaceASCII(language.GetString(), base::TRIM_ALL)
+            .as_string();
     std::string current_language =
-        spellcheck::GetCorrespondingSpellCheckLanguage(
-            base::TrimWhitespaceASCII(language.GetString(), base::TRIM_ALL));
+        SpellcheckService::GetSupportedAcceptLanguageCode(candidate_language);
 
     if (current_language.empty()) {
       unknown->emplace_back(language.GetString());
diff --git a/chrome/browser/spellchecker/spellcheck_language_policy_handler.cc b/chrome/browser/spellchecker/spellcheck_language_policy_handler.cc
index b5407f5..7724da7d 100644
--- a/chrome/browser/spellchecker/spellcheck_language_policy_handler.cc
+++ b/chrome/browser/spellchecker/spellcheck_language_policy_handler.cc
@@ -11,12 +11,13 @@
 #include "base/syslog_logging.h"
 #include "base/values.h"
 #include "build/build_config.h"
+#include "chrome/browser/spellchecker/spellcheck_service.h"
 #include "chrome/common/pref_names.h"
 #include "components/policy/core/browser/policy_error_map.h"
 #include "components/policy/policy_constants.h"
 #include "components/prefs/pref_value_map.h"
 #include "components/spellcheck/browser/pref_names.h"
-#include "components/spellcheck/common/spellcheck_common.h"
+#include "components/spellcheck/common/spellcheck_features.h"
 #include "components/strings/grit/components_strings.h"
 
 SpellcheckLanguagePolicyHandler::SpellcheckLanguagePolicyHandler()
@@ -86,9 +87,11 @@
 
   // Separate the valid languages from the unknown / unsupported languages.
   for (const base::Value& language : value->GetList()) {
+    std::string candidate_language =
+        base::TrimWhitespaceASCII(language.GetString(), base::TRIM_ALL)
+            .as_string();
     std::string current_language =
-        spellcheck::GetCorrespondingSpellCheckLanguage(
-            base::TrimWhitespaceASCII(language.GetString(), base::TRIM_ALL));
+        SpellcheckService::GetSupportedAcceptLanguageCode(candidate_language);
 
     if (current_language.empty()) {
       unknown->emplace_back(language.GetString());
diff --git a/chrome/browser/spellchecker/spellcheck_language_policy_handlers_unittest.cc b/chrome/browser/spellchecker/spellcheck_language_policy_handlers_unittest.cc
new file mode 100644
index 0000000..fd5ff6c2
--- /dev/null
+++ b/chrome/browser/spellchecker/spellcheck_language_policy_handlers_unittest.cc
@@ -0,0 +1,207 @@
+// Copyright 2020 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.
+
+// Chromium style is to have one unit test per one header file. However, the
+// applied blocked spellcheck language policy depends on the applied forced
+// language policy. If a language is both blocked and forced, forced wins. It is
+// only practical to test this interaction in a single unit test covering both
+// header files.
+#include "chrome/browser/spellchecker/spellcheck_language_blacklist_policy_handler.h"
+#include "chrome/browser/spellchecker/spellcheck_language_policy_handler.h"
+
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "base/strings/string_util.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/values.h"
+#include "chrome/common/pref_names.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/pref_value_map.h"
+#include "components/spellcheck/browser/pref_names.h"
+#include "components/spellcheck/common/spellcheck_features.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+struct TestCase {
+  TestCase(const std::vector<std::string>& blocked_languages,
+           const std::vector<std::string>& forced_languages,
+           const std::vector<std::string>& expected_blocked_languages,
+           const std::vector<std::string>& expected_forced_languages,
+           bool spellcheck_enabled,
+           bool windows_spellchecker_enabled)
+      : blocked_languages(blocked_languages),
+        forced_languages(forced_languages),
+        expected_blocked_languages(expected_blocked_languages),
+        expected_forced_languages(expected_forced_languages),
+        spellcheck_enabled(spellcheck_enabled),
+        windows_spellchecker_enabled(windows_spellchecker_enabled) {}
+
+  std::vector<std::string> blocked_languages;
+  std::vector<std::string> forced_languages;
+  std::vector<std::string> expected_blocked_languages;
+  std::vector<std::string> expected_forced_languages;
+  bool spellcheck_enabled;
+  bool windows_spellchecker_enabled;
+};
+
+std::ostream& operator<<(std::ostream& out, const TestCase& test_case) {
+  out << "blocked_languages=["
+      << base::JoinString(test_case.blocked_languages, ",")
+      << "], forced_languages=["
+      << base::JoinString(test_case.forced_languages, ",")
+      << "], expected_blocked_languages=["
+      << base::JoinString(test_case.expected_blocked_languages, ",")
+      << "], expected_forced_languages=["
+      << base::JoinString(test_case.expected_forced_languages, ",")
+      << "], spellcheck_enabled=" << test_case.spellcheck_enabled
+      << "], windows_spellchecker_enabled="
+      << test_case.windows_spellchecker_enabled;
+  return out;
+}
+
+class SpellcheckLanguagePolicyHandlersTest
+    : public testing::TestWithParam<TestCase> {
+ public:
+  SpellcheckLanguagePolicyHandlersTest() = default;
+  ~SpellcheckLanguagePolicyHandlersTest() override = default;
+
+  void CheckPrefs(const PrefValueMap& prefs,
+                  const std::string& key,
+                  const std::vector<std::string>& expected) {
+    // Retrieve the spellcheck enabled pref (if it exists).
+    const base::Value* spellcheck_enabled_pref = nullptr;
+    const bool is_spellcheck_enabled_pref_set = prefs.GetValue(
+        spellcheck::prefs::kSpellCheckEnable, &spellcheck_enabled_pref);
+
+    const base::Value* languages_list = nullptr;
+    if (GetParam().spellcheck_enabled) {
+      EXPECT_TRUE(is_spellcheck_enabled_pref_set);
+      EXPECT_TRUE(spellcheck_enabled_pref->is_bool());
+      EXPECT_TRUE(spellcheck_enabled_pref->GetBool());
+
+      EXPECT_TRUE(prefs.GetValue(key, &languages_list));
+      EXPECT_TRUE(languages_list->is_list());
+      EXPECT_EQ(expected.size(), languages_list->GetList().size());
+
+      for (const auto& language : languages_list->GetList()) {
+        EXPECT_TRUE(language.is_string());
+        EXPECT_TRUE(std::find(expected.begin(), expected.end(),
+                              language.GetString()) != expected.end());
+      }
+    } else {
+      EXPECT_FALSE(is_spellcheck_enabled_pref_set);
+      // No language list should be added to prefs if spellchecking disabled.
+      EXPECT_FALSE(prefs.GetValue(key, &languages_list));
+    }
+  }
+};
+
+TEST_P(SpellcheckLanguagePolicyHandlersTest, ApplyPolicySettings) {
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+  base::test::ScopedFeatureList feature_list;
+  if (GetParam().windows_spellchecker_enabled) {
+    if (!spellcheck::WindowsVersionSupportsSpellchecker())
+      return;
+
+    // Force hybrid spellchecking to be enabled.
+    feature_list.InitWithFeatures(
+        /*enabled_features=*/{spellcheck::kWinUseBrowserSpellChecker,
+                              spellcheck::kWinUseHybridSpellChecker},
+        /*disabled_features=*/{});
+  } else {
+    // Hunspell-only spellcheck languages will be used.
+    feature_list.InitAndDisableFeature(spellcheck::kWinUseBrowserSpellChecker);
+  }
+#endif  // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+
+  PrefValueMap prefs;
+  policy::PolicyMap policy;
+
+  base::Value blocked_languages_list(base::Value::Type::LIST);
+  for (const auto& blocked_language : GetParam().blocked_languages) {
+    blocked_languages_list.Append(std::move(blocked_language));
+  }
+
+  base::Value forced_languages_list(base::Value::Type::LIST);
+  for (const auto& forced_language : GetParam().forced_languages) {
+    forced_languages_list.Append(std::move(forced_language));
+  }
+
+  policy.Set(policy::key::kSpellcheckLanguageBlacklist,
+             policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
+             policy::POLICY_SOURCE_ENTERPRISE_DEFAULT,
+             base::Value::ToUniquePtrValue(std::move(blocked_languages_list)),
+             nullptr);
+
+  policy.Set(
+      policy::key::kSpellcheckLanguage, policy::POLICY_LEVEL_MANDATORY,
+      policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_ENTERPRISE_DEFAULT,
+      base::Value::ToUniquePtrValue(std::move(forced_languages_list)), nullptr);
+
+  policy.Set(
+      policy::key::kSpellcheckEnabled, policy::POLICY_LEVEL_MANDATORY,
+      policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_ENTERPRISE_DEFAULT,
+      std::make_unique<base::Value>(GetParam().spellcheck_enabled), nullptr);
+
+  // Apply policy to the forced languages handler.
+  SpellcheckLanguagePolicyHandler forced_languages_handler;
+  forced_languages_handler.ApplyPolicySettings(policy, &prefs);
+
+  // Apply policy to the blocked languages handler.
+  SpellcheckLanguageBlacklistPolicyHandler blocked_languages_handler;
+  blocked_languages_handler.ApplyPolicySettings(policy, &prefs);
+
+  // Check if forced languages preferences are as expected.
+  CheckPrefs(prefs, spellcheck::prefs::kSpellCheckForcedDictionaries,
+             GetParam().expected_forced_languages);
+
+  // Check if blocked languages preferences are as expected.
+  CheckPrefs(prefs, spellcheck::prefs::kSpellCheckBlacklistedDictionaries,
+             GetParam().expected_blocked_languages);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    TestCases,
+    SpellcheckLanguagePolicyHandlersTest,
+    testing::Values(
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+        // Test cases for Windows spellchecker (policy languages not restricted
+        // to Hunspell).
+        TestCase({"ar-SA", "es-MX", "fi", "fr",
+                  "not-a-language"} /* blocked languages */,
+                 {"fi", "fr", "not-another-language"} /* forced languages */,
+                 {"ar", "es-MX"} /* expected blocked languages */,
+                 {"fi", "fr"} /* expected forced languages */,
+                 true /* spellcheck enabled */,
+                 true /* windows spellchecker enabled */),
+        TestCase({"ar-SA", "es-MX", "fi", "fr "} /* blocked languages */,
+                 {"fi", "fr"} /* forced languages */,
+                 {""} /* expected blocked languages */,
+                 {""} /* expected forced languages */,
+                 false /* spellcheck enabled */,
+                 true /* windows spellchecker enabled */),
+#endif  // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+        // Test cases for Hunspell only spellchecker. ar-SA and fi are
+        // non-Hunspell languages so are ignored for policy enforcement. If they
+        // ever obtain Hunspell support, the first test case below will fail.
+        TestCase({"ar-SA", "es-MX", "fi", "fr",
+                  "not-a-language"} /* blocked languages */,
+                 {"fi", "fr", "not-another-language"} /* forced languages */,
+                 {"es-MX"} /* expected blocked languages */,
+                 {"fr"} /* expected forced languages */,
+                 true /* spellcheck enabled */,
+                 false /* windows spellchecker enabled */),
+        TestCase({"ar-SA", "es-MX", "fi", "fr",
+                  "not-a-language"} /* blocked languages */,
+                 {"fi", "fr", "not-another-language"} /* forced languages */,
+                 {""} /* expected blocked languages */,
+                 {""} /* expected forced languages */,
+                 false /* spellcheck enabled */,
+                 false /* windows spellchecker enabled */)));
+
+}  // namespace policy
diff --git a/chrome/browser/spellchecker/spellcheck_service.cc b/chrome/browser/spellchecker/spellcheck_service.cc
index 7d3ddb7..4aea771d 100644
--- a/chrome/browser/spellchecker/spellcheck_service.cc
+++ b/chrome/browser/spellchecker/spellcheck_service.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/spellchecker/spellcheck_service.h"
 
 #include <algorithm>
+#include <iterator>
 #include <set>
 #include <utility>
 
@@ -38,6 +39,7 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/storage_partition.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "ui/base/l10n/l10n_util.h"
 
 #if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
 #include "base/task/post_task.h"
@@ -200,6 +202,84 @@
   return true;
 }
 
+// static
+std::string SpellcheckService::GetSupportedAcceptLanguageCode(
+    const std::string& supported_language_full_tag) {
+  // Default to accept language in hardcoded list of Hunspell dictionaries
+  // (kSupportedSpellCheckerLanguages).
+  std::string supported_accept_language =
+      spellcheck::GetCorrespondingSpellCheckLanguage(
+          supported_language_full_tag);
+
+#if defined(OS_WIN)
+  if (!spellcheck::UseWinHybridSpellChecker())
+    return supported_accept_language;
+
+  // Collect the hardcoded list of accept-languages supported by the browser,
+  // that is, languages that can be added as preferred languages in the
+  // languages settings page.
+  std::vector<std::string> accept_languages;
+  l10n_util::GetAcceptLanguages(&accept_languages);
+
+  // First try exact match. Per BCP47, tags are in ASCII and should be treated
+  // as case-insensitive (although there are conventions for the capitalization
+  // of subtags).
+  auto iter =
+      std::find_if(accept_languages.begin(), accept_languages.end(),
+                   [supported_language_full_tag](const auto& accept_language) {
+                     return base::EqualsCaseInsensitiveASCII(
+                         supported_language_full_tag, accept_language);
+                   });
+  if (iter != accept_languages.end())
+    return *iter;
+
+  // Then try matching just the language and (optional) script subtags, but
+  // not the region subtag. For example, Edge supports sr-Cyrl-RS as an accept
+  // language, but not sr-Cyrl-CS. Matching language + script subtags assures
+  // we get the correct script for spellchecking, and not use sr-Latn-RS if
+  // language packs for both scripts are installed on the system.
+  if (!base::Contains(supported_language_full_tag, "-"))
+    return "";
+
+  iter =
+      std::find_if(accept_languages.begin(), accept_languages.end(),
+                   [supported_language_full_tag](const auto& accept_language) {
+                     return base::EqualsCaseInsensitiveASCII(
+                         SpellcheckService::GetLanguageAndScriptTag(
+                             supported_language_full_tag,
+                             /* include_script_tag */ true),
+                         SpellcheckService::GetLanguageAndScriptTag(
+                             accept_language,
+                             /* include_script_tag */ true));
+                   });
+
+  if (iter != accept_languages.end())
+    return *iter;
+
+  // Then try just matching the leading language subtag. E.g. Edge supports
+  // kok as an accept language, but if the Konkani language pack is
+  // installed the Windows spellcheck API reports kok-Deva-IN for the
+  // dictionary name.
+  iter =
+      std::find_if(accept_languages.begin(), accept_languages.end(),
+                   [supported_language_full_tag](const auto& accept_language) {
+                     return base::EqualsCaseInsensitiveASCII(
+                         SpellcheckService::GetLanguageAndScriptTag(
+                             supported_language_full_tag,
+                             /* include_script_tag */ false),
+                         SpellcheckService::GetLanguageAndScriptTag(
+                             accept_language,
+                             /* include_script_tag */ false));
+                   });
+
+  if (iter != accept_languages.end())
+    return *iter;
+
+#endif  // defined(OS_WIN)
+
+  return supported_accept_language;
+}
+
 void SpellcheckService::StartRecordingMetrics(bool spellcheck_enabled) {
   metrics_ = std::make_unique<SpellCheckHostMetrics>();
   metrics_->RecordEnabledStats(spellcheck_enabled);
@@ -361,6 +441,33 @@
 }
 
 // static
+std::string SpellcheckService::GetLanguageAndScriptTag(
+    const std::string& full_tag,
+    bool include_script_tag) {
+  std::string language_and_script_tag;
+
+  std::vector<std::string> subtags = base::SplitString(
+      full_tag, "-", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  // Language subtag is required, all others optional.
+  DCHECK_GE(subtags.size(), 1ULL);
+  std::vector<std::string> subtag_tokens_to_pass;
+  subtag_tokens_to_pass.push_back(subtags.front());
+  subtags.erase(subtags.begin());
+
+  // The optional script subtag always follows the language subtag, and is 4
+  // characters in length.
+  if (include_script_tag) {
+    if (!subtags.empty() && subtags.front().length() == 4) {
+      subtag_tokens_to_pass.push_back(subtags.front());
+    }
+  }
+
+  language_and_script_tag = base::JoinString(subtag_tokens_to_pass, "-");
+
+  return language_and_script_tag;
+}
+
+// static
 void SpellcheckService::AttachStatusEvent(base::WaitableEvent* status_event) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
diff --git a/chrome/browser/spellchecker/spellcheck_service.h b/chrome/browser/spellchecker/spellcheck_service.h
index 803c7da..97df1313 100644
--- a/chrome/browser/spellchecker/spellcheck_service.h
+++ b/chrome/browser/spellchecker/spellcheck_service.h
@@ -94,6 +94,13 @@
   // when we do not set an event to |status_event_|.
   static bool SignalStatusEvent(EventType type);
 
+  // Get the best match of a supported accept language code for the provided
+  // language tag. Returns an empty string if no match is found. Method cannot
+  // be defined in spellcheck_common.h as it depends on l10n_util, and code
+  // under components cannot depend on ui/base.
+  static std::string GetSupportedAcceptLanguageCode(
+      const std::string& supported_language_full_tag);
+
   // Instantiates SpellCheckHostMetrics object and makes it ready for recording
   // metrics. This should be called only if the metrics recording is active.
   void StartRecordingMetrics(bool spellcheck_enabled);
@@ -161,6 +168,11 @@
  private:
   FRIEND_TEST_ALL_PREFIXES(SpellcheckServiceBrowserTest, DeleteCorruptedBDICT);
 
+  // Parses a full BCP47 language tag to return just the language subtag,
+  // optionally with a hyphen and script subtag appended.
+  static std::string GetLanguageAndScriptTag(const std::string& full_tag,
+                                             bool include_script_tag);
+
   // Attaches an event so browser tests can listen the status events.
   static void AttachStatusEvent(base::WaitableEvent* status_event);
 
diff --git a/chrome/browser/ssl/tls_deprecation_config.cc b/chrome/browser/ssl/tls_deprecation_config.cc
index 05e247b..335c015 100644
--- a/chrome/browser/ssl/tls_deprecation_config.cc
+++ b/chrome/browser/ssl/tls_deprecation_config.cc
@@ -33,6 +33,8 @@
     return proto_.get();
   }
 
+  void Reset() { proto_.reset(); }
+
   static TLSDeprecationConfigSingleton& GetInstance() {
     static base::NoDestructor<TLSDeprecationConfigSingleton> instance;
     return *instance;
@@ -77,3 +79,7 @@
 
   return lower != control_site_hashes.end() && *lower == host_hash;
 }
+
+void ResetTLSDeprecationConfigForTesting() {
+  TLSDeprecationConfigSingleton::GetInstance().Reset();
+}
diff --git a/chrome/browser/ssl/tls_deprecation_config.h b/chrome/browser/ssl/tls_deprecation_config.h
index edee244..613a1c7 100644
--- a/chrome/browser/ssl/tls_deprecation_config.h
+++ b/chrome/browser/ssl/tls_deprecation_config.h
@@ -13,4 +13,6 @@
 
 bool ShouldSuppressLegacyTLSWarning(const GURL& url);
 
+void ResetTLSDeprecationConfigForTesting();
+
 #endif  // CHROME_BROWSER_SSL_TLS_DEPRECATION_CONFIG_H_
diff --git a/chrome/browser/ssl/tls_deprecation_config_unittest.cc b/chrome/browser/ssl/tls_deprecation_config_unittest.cc
index a0ee9030..239ed8b 100644
--- a/chrome/browser/ssl/tls_deprecation_config_unittest.cc
+++ b/chrome/browser/ssl/tls_deprecation_config_unittest.cc
@@ -14,14 +14,19 @@
 
 using chrome_browser_ssl::LegacyTLSExperimentConfig;
 
+class TLSDeprecationConfigTest : public testing::Test {
+ protected:
+  void TearDown() override { ResetTLSDeprecationConfigForTesting(); }
+};
+
 // Tests the case where no proto has been set by the component installer.
-TEST(TLSDeprecationConfigTest, NoProto) {
+TEST_F(TLSDeprecationConfigTest, NoProto) {
   EXPECT_TRUE(ShouldSuppressLegacyTLSWarning(GURL("https://example.test")));
 }
 
 // This tests that when no sites are in the control set,
 // IsTLSDeprecationControlSite() returns false.
-TEST(TLSDeprecationConfigTest, NoControlSites) {
+TEST_F(TLSDeprecationConfigTest, NoControlSites) {
   GURL control_site("https://control.test");
   std::string control_site_hex =
       "f12b47771bb3c2bcc85a5347d195523013ec5a23b4c761b5d6aacf04bafc5e23";
@@ -38,7 +43,7 @@
 // This tests that when only a single control site is in the control set,
 // IsTLSDeprecationControlSite() works correctly for the site in the control and
 // for sites not in the control.
-TEST(TLSDeprecationConfigTest, SingleControlSite) {
+TEST_F(TLSDeprecationConfigTest, SingleControlSite) {
   GURL control_site("https://control.test");
   std::string control_site_hex =
       "f12b47771bb3c2bcc85a5347d195523013ec5a23b4c761b5d6aacf04bafc5e23";
@@ -65,7 +70,7 @@
 
 // This tests that the binary search in IsTLSDeprecationControlSite() works for
 // both a site that is in the control set and a site that is not.
-TEST(TLSDeprecationConfigTest, ManyControlSites) {
+TEST_F(TLSDeprecationConfigTest, ManyControlSites) {
   const struct {
     GURL url;
     std::string hash;
diff --git a/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc b/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc
index bdefbf76..062d5c47 100644
--- a/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc
@@ -89,13 +89,7 @@
   EXPECT_TRUE(ExpectUserEvents({specifics1, specifics1, specifics2}));
 }
 
-// Flaky (mostly) on ASan/TSan. http://crbug.com/998130
-#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER)
-#define MAYBE_RetryParallel DISABLED_RetryParallel
-#else
-#define MAYBE_RetryParallel RetryParallel
-#endif
-IN_PROC_BROWSER_TEST_F(SingleClientUserEventsSyncTest, MAYBE_RetryParallel) {
+IN_PROC_BROWSER_TEST_F(SingleClientUserEventsSyncTest, RetryParallel) {
   ASSERT_TRUE(SetupSync());
 
   const UserEventSpecifics specifics1 =
@@ -115,7 +109,7 @@
   UserEventSpecifics retry_specifics;
   GetFakeServer()->OverrideResponseType(base::BindLambdaForTesting(
       [&](const syncer::LoopbackServerEntity& entity) {
-        if (first) {
+        if (first && entity.GetModelType() == syncer::USER_EVENTS) {
           first = false;
           SyncEntity sync_entity;
           entity.SerializeAsProto(&sync_entity);
diff --git a/chrome/browser/task_manager/mock_web_contents_task_manager.cc b/chrome/browser/task_manager/mock_web_contents_task_manager.cc
index c11f0d0..5f94aef 100644
--- a/chrome/browser/task_manager/mock_web_contents_task_manager.cc
+++ b/chrome/browser/task_manager/mock_web_contents_task_manager.cc
@@ -7,15 +7,11 @@
 #include "base/stl_util.h"
 #include "build/build_config.h"
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/resource_reporter/resource_reporter.h"
-#endif  // defined(OS_CHROMEOS)
-
 namespace task_manager {
 
-MockWebContentsTaskManager::MockWebContentsTaskManager() {}
+MockWebContentsTaskManager::MockWebContentsTaskManager() = default;
 
-MockWebContentsTaskManager::~MockWebContentsTaskManager() {}
+MockWebContentsTaskManager::~MockWebContentsTaskManager() = default;
 
 void MockWebContentsTaskManager::TaskAdded(Task* task) {
   DCHECK(task);
@@ -30,12 +26,6 @@
 }
 
 void MockWebContentsTaskManager::StartObserving() {
-#if defined(OS_CHROMEOS)
-  // On ChromeOS, the ResourceReporter needs to be turned off so as not to
-  // interfere with the tests.
-  chromeos::ResourceReporter::GetInstance()->StopMonitoring();
-#endif  // defined(OS_CHROMEOS)
-
   provider_.SetObserver(this);
 }
 
diff --git a/chrome/browser/task_manager/providers/arc/arc_process_task.cc b/chrome/browser/task_manager/providers/arc/arc_process_task.cc
index 0d56a32..1db9d91 100644
--- a/chrome/browser/task_manager/providers/arc/arc_process_task.cc
+++ b/chrome/browser/task_manager/providers/arc/arc_process_task.cc
@@ -74,7 +74,6 @@
 
 ArcProcessTask::ArcProcessTask(arc::ArcProcess arc_process)
     : Task(MakeTitle(arc_process),
-           arc_process.process_name(),
            nullptr /* icon */,
            arc_process.pid()),
       arc_process_(std::move(arc_process)) {
diff --git a/chrome/browser/task_manager/providers/browser_process_task.cc b/chrome/browser/task_manager/providers/browser_process_task.cc
index bb3cc21..b4d1229 100644
--- a/chrome/browser/task_manager/providers/browser_process_task.cc
+++ b/chrome/browser/task_manager/providers/browser_process_task.cc
@@ -16,7 +16,6 @@
 
 BrowserProcessTask::BrowserProcessTask()
     : Task(l10n_util::GetStringUTF16(IDS_TASK_MANAGER_WEB_BROWSER_CELL_TEXT),
-           "Browser Process",
            FetchIcon(IDR_PRODUCT_LOGO_16, &s_icon_),
            base::GetCurrentProcessHandle()),
       used_sqlite_memory_(-1) {}
diff --git a/chrome/browser/task_manager/providers/child_process_task.cc b/chrome/browser/task_manager/providers/child_process_task.cc
index 73f9204..4815516d1 100644
--- a/chrome/browser/task_manager/providers/child_process_task.cc
+++ b/chrome/browser/task_manager/providers/child_process_task.cc
@@ -162,7 +162,6 @@
 
 ChildProcessTask::ChildProcessTask(const content::ChildProcessData& data)
     : Task(GetLocalizedTitle(data.name, data.process_type),
-           base::UTF16ToUTF8(data.name),
            FetchIcon(IDR_PLUGINS_FAVICON, &s_icon_),
            data.GetProcess().Handle()),
       process_resources_sampler_(CreateProcessResourcesSampler(data.id)),
diff --git a/chrome/browser/task_manager/providers/fallback_task_provider_unittest.cc b/chrome/browser/task_manager/providers/fallback_task_provider_unittest.cc
index 579bb30..3ff32615 100644
--- a/chrome/browser/task_manager/providers/fallback_task_provider_unittest.cc
+++ b/chrome/browser/task_manager/providers/fallback_task_provider_unittest.cc
@@ -21,7 +21,6 @@
  public:
   FakeTask(base::ProcessId process_id, Type type, const std::string& title)
       : Task(base::ASCIIToUTF16(title),
-             "FakeTask",
              nullptr,
              base::kNullProcessHandle,
              process_id),
diff --git a/chrome/browser/task_manager/providers/task.cc b/chrome/browser/task_manager/providers/task.cc
index 1c96046..bb974882 100644
--- a/chrome/browser/task_manager/providers/task.cc
+++ b/chrome/browser/task_manager/providers/task.cc
@@ -34,7 +34,6 @@
 }  // namespace
 
 Task::Task(const base::string16& title,
-           const std::string& rappor_sample,
            const gfx::ImageSkia* icon,
            base::ProcessHandle handle,
            base::ProcessId process_id)
@@ -46,12 +45,11 @@
       network_sent_rate_(0),
       network_read_rate_(0),
       title_(title),
-      rappor_sample_name_(rappor_sample),
       icon_(icon ? *icon : gfx::ImageSkia()),
       process_handle_(handle),
       process_id_(DetermineProcessId(handle, process_id)) {}
 
-Task::~Task() {}
+Task::~Task() = default;
 
 // static
 base::string16 Task::GetProfileNameFromProfile(Profile* profile) {
diff --git a/chrome/browser/task_manager/providers/task.h b/chrome/browser/task_manager/providers/task.h
index 61d32b0..f503e46e 100644
--- a/chrome/browser/task_manager/providers/task.h
+++ b/chrome/browser/task_manager/providers/task.h
@@ -60,11 +60,9 @@
   };
 
   // Create a task with the given |title| and the given favicon |icon|. This
-  // task runs on a process whose handle is |handle|. |rappor_sample| is the
-  // name of the sample to be recorded if this task needs to be reported by
-  // Rappor. If |process_id| is not supplied, it will be determined by |handle|.
+  // task runs on a process whose handle is |handle|.
+  // If |process_id| is not supplied, it will be determined by |handle|.
   Task(const base::string16& title,
-       const std::string& rappor_sample,
        const gfx::ImageSkia* icon,
        base::ProcessHandle handle,
        base::ProcessId process_id = base::kNullProcessId);
@@ -177,7 +175,6 @@
   }
 
   const base::string16& title() const { return title_; }
-  const std::string& rappor_sample_name() const { return rappor_sample_name_; }
   const gfx::ImageSkia& icon() const { return icon_; }
   const base::ProcessHandle& process_handle() const { return process_handle_; }
   const base::ProcessId& process_id() const { return process_id_; }
@@ -188,9 +185,6 @@
   // Returns |*result_image|.
   static gfx::ImageSkia* FetchIcon(int id, gfx::ImageSkia** result_image);
   void set_title(const base::string16& new_title) { title_ = new_title; }
-  void set_rappor_sample_name(const std::string& sample) {
-    rappor_sample_name_ = sample;
-  }
   void set_icon(const gfx::ImageSkia& new_icon) { icon_ = new_icon; }
 
  private:
@@ -226,10 +220,6 @@
   // The title of the task.
   base::string16 title_;
 
-  // The name of the sample representing this task when a Rappor sample needs to
-  // be recorded for it.
-  std::string rappor_sample_name_;
-
   // The favicon.
   gfx::ImageSkia icon_;
 
diff --git a/chrome/browser/task_manager/providers/vm/vm_process_task.cc b/chrome/browser/task_manager/providers/vm/vm_process_task.cc
index 5740e13d7..5f8da7b 100644
--- a/chrome/browser/task_manager/providers/vm/vm_process_task.cc
+++ b/chrome/browser/task_manager/providers/vm/vm_process_task.cc
@@ -33,7 +33,7 @@
                              base::ProcessId pid,
                              const std::string& owner_id,
                              const std::string& vm_name)
-    : Task(MakeTitle(ids_vm_prefix, vm_name), vm_name, icon, pid),
+    : Task(MakeTitle(ids_vm_prefix, vm_name), icon, pid),
       owner_id_(owner_id),
       vm_name_(vm_name) {}
 
diff --git a/chrome/browser/task_manager/providers/web_contents/renderer_task.cc b/chrome/browser/task_manager/providers/web_contents/renderer_task.cc
index 37145d60..1fb42a1c 100644
--- a/chrome/browser/task_manager/providers/web_contents/renderer_task.cc
+++ b/chrome/browser/task_manager/providers/web_contents/renderer_task.cc
@@ -51,10 +51,6 @@
   return (flags & (REFRESH_TYPE_V8_MEMORY | REFRESH_TYPE_WEBCACHE_STATS)) == 0;
 }
 
-std::string GetRapporSampleName(content::WebContents* web_contents) {
-  return web_contents->GetVisibleURL().GetOrigin().spec();
-}
-
 }  // namespace
 
 RendererTask::RendererTask(const base::string16& title,
@@ -78,7 +74,6 @@
                            content::WebContents* web_contents,
                            content::RenderProcessHost* render_process_host)
     : Task(title,
-           GetRapporSampleName(web_contents),
            icon,
            render_process_host->GetProcess().Handle()),
       web_contents_(web_contents),
@@ -106,10 +101,6 @@
       RemoveObserver(this);
 }
 
-void RendererTask::UpdateRapporSampleName() {
-  set_rappor_sample_name(GetRapporSampleName(web_contents()));
-}
-
 void RendererTask::Activate() {
   if (!web_contents_->GetDelegate())
     return;
diff --git a/chrome/browser/task_manager/providers/web_contents/renderer_task.h b/chrome/browser/task_manager/providers/web_contents/renderer_task.h
index a4dd3cc..26d0db8 100644
--- a/chrome/browser/task_manager/providers/web_contents/renderer_task.h
+++ b/chrome/browser/task_manager/providers/web_contents/renderer_task.h
@@ -48,11 +48,6 @@
   // can update their favicons.
   virtual void UpdateFavicon() = 0;
 
-  // An overridable method that will be called when the event
-  // WebContentsObserver::DidNavigateMainFrame() occurs, so that we can update
-  // their Rappor sample name when a navigation takes place.
-  virtual void UpdateRapporSampleName();
-
   // task_manager::Task:
   void Activate() override;
   void Refresh(const base::TimeDelta& update_interval,
diff --git a/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc b/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc
index d0ebc46..0d548b1 100644
--- a/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc
+++ b/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc
@@ -240,8 +240,6 @@
   if (!main_frame_task)
     return;
 
-  main_frame_task->UpdateRapporSampleName();
-
   ForEachTask(base::Bind([](RendererTask* task) {
     // Listening to WebContentsObserver::TitleWasSet() only is not enough in
     // some cases when the the web page doesn't have a title. That's why we
diff --git a/chrome/browser/task_manager/providers/worker_task.cc b/chrome/browser/task_manager/providers/worker_task.cc
index 82f2fde..7c3216a5a 100644
--- a/chrome/browser/task_manager/providers/worker_task.cc
+++ b/chrome/browser/task_manager/providers/worker_task.cc
@@ -40,7 +40,6 @@
                        Task::Type task_type,
                        int render_process_id)
     : Task(GetTaskTitle(/*script_url=*/GURL(), task_type),
-           /*rappor_sample=*/std::string(),
            /*icon=*/nullptr,
            handle),
       task_type_(task_type),
@@ -58,7 +57,6 @@
 
 void WorkerTask::SetScriptUrl(const GURL& script_url) {
   set_title(GetTaskTitle(script_url, task_type_));
-  set_rappor_sample_name(script_url.spec());
 }
 
 }  // namespace task_manager
diff --git a/chrome/browser/task_manager/sampling/task_group_unittest.cc b/chrome/browser/task_manager/sampling/task_group_unittest.cc
index 8169f3b..c74b2245 100644
--- a/chrome/browser/task_manager/sampling/task_group_unittest.cc
+++ b/chrome/browser/task_manager/sampling/task_group_unittest.cc
@@ -30,7 +30,6 @@
  public:
   FakeTask(base::ProcessId process_id, Type type, bool is_running_in_vm)
       : Task(base::string16(),
-             "FakeTask",
              nullptr,
              base::kNullProcessHandle,
              process_id),
diff --git a/chrome/browser/task_manager/sampling/task_manager_impl.cc b/chrome/browser/task_manager/sampling/task_manager_impl.cc
index 36a81b3..b722397 100644
--- a/chrome/browser/task_manager/sampling/task_manager_impl.cc
+++ b/chrome/browser/task_manager/sampling/task_manager_impl.cc
@@ -234,10 +234,6 @@
   return GetTaskByTaskId(task_id)->title();
 }
 
-const std::string& TaskManagerImpl::GetTaskNameForRappor(TaskId task_id) const {
-  return GetTaskByTaskId(task_id)->rappor_sample_name();
-}
-
 base::string16 TaskManagerImpl::GetProfileName(TaskId task_id) const {
   return GetTaskByTaskId(task_id)->GetProfileName();
 }
diff --git a/chrome/browser/task_manager/sampling/task_manager_impl.h b/chrome/browser/task_manager/sampling/task_manager_impl.h
index 7a2c7d5..1b314e2 100644
--- a/chrome/browser/task_manager/sampling/task_manager_impl.h
+++ b/chrome/browser/task_manager/sampling/task_manager_impl.h
@@ -97,7 +97,6 @@
   int GetOpenFdCount(TaskId task_id) const override;
   bool IsTaskOnBackgroundedProcess(TaskId task_id) const override;
   const base::string16& GetTitle(TaskId task_id) const override;
-  const std::string& GetTaskNameForRappor(TaskId task_id) const override;
   base::string16 GetProfileName(TaskId task_id) const override;
   const gfx::ImageSkia& GetIcon(TaskId task_id) const override;
   const base::ProcessHandle& GetProcessHandle(TaskId task_id) const override;
diff --git a/chrome/browser/task_manager/sampling/task_manager_impl_unittest.cc b/chrome/browser/task_manager/sampling/task_manager_impl_unittest.cc
index 6d0aaaf..d6d1338 100644
--- a/chrome/browser/task_manager/sampling/task_manager_impl_unittest.cc
+++ b/chrome/browser/task_manager/sampling/task_manager_impl_unittest.cc
@@ -27,7 +27,6 @@
            const std::string& title,
            SessionID tab_id)
       : Task(base::ASCIIToUTF16(title),
-             "FakeTask",
              nullptr,
              base::kNullProcessHandle,
              process_id),
diff --git a/chrome/browser/task_manager/task_manager_interface.h b/chrome/browser/task_manager/task_manager_interface.h
index 89bc94f0..8915b3a 100644
--- a/chrome/browser/task_manager/task_manager_interface.h
+++ b/chrome/browser/task_manager/task_manager_interface.h
@@ -119,10 +119,6 @@
   // Returns the title of the task with |task_id|.
   virtual const base::string16& GetTitle(TaskId task_id) const = 0;
 
-  // Returns the canonicalized name of the task with |task_id| that can be used
-  // to represent this task in a Rappor sample via RapporServiceImpl.
-  virtual const std::string& GetTaskNameForRappor(TaskId task_id) const = 0;
-
   // Returns the name of the profile associated with the browser context of the
   // render view host that the task with |task_id| represents (if that task
   // represents a renderer).
diff --git a/chrome/browser/task_manager/test_task_manager.cc b/chrome/browser/task_manager/test_task_manager.cc
index 0b45d27..604c6996 100644
--- a/chrome/browser/task_manager/test_task_manager.cc
+++ b/chrome/browser/task_manager/test_task_manager.cc
@@ -82,10 +82,6 @@
   return title_;
 }
 
-const std::string& TestTaskManager::GetTaskNameForRappor(TaskId task_id) const {
-  return rappor_sample_;
-}
-
 base::string16 TestTaskManager::GetProfileName(TaskId task_id) const {
   return base::string16();
 }
diff --git a/chrome/browser/task_manager/test_task_manager.h b/chrome/browser/task_manager/test_task_manager.h
index 7ad12ae..c27d3a8a 100644
--- a/chrome/browser/task_manager/test_task_manager.h
+++ b/chrome/browser/task_manager/test_task_manager.h
@@ -45,7 +45,6 @@
   int GetOpenFdCount(TaskId task_id) const override;
   bool IsTaskOnBackgroundedProcess(TaskId task_id) const override;
   const base::string16& GetTitle(TaskId task_id) const override;
-  const std::string& GetTaskNameForRappor(TaskId task_id) const override;
   base::string16 GetProfileName(TaskId task_id) const override;
   const gfx::ImageSkia& GetIcon(TaskId task_id) const override;
   const base::ProcessHandle& GetProcessHandle(TaskId task_id) const override;
@@ -86,7 +85,6 @@
   base::ProcessHandle handle_;
   base::ProcessId pid_;
   base::string16 title_;
-  std::string rappor_sample_;
   gfx::ImageSkia icon_;
   TaskIdList ids_;
 
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
index d9e34cf..285605d1 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
@@ -205,6 +205,10 @@
       withhold_permissions_checkbox_(nullptr) {
   DCHECK(prompt_->extension());
 
+  extensions::ExtensionRegistry* extension_registry =
+      extensions::ExtensionRegistry::Get(profile_);
+  extension_registry_observer_.Add(extension_registry);
+
   int buttons = prompt_->GetDialogButtons();
   DCHECK(buttons & ui::DIALOG_BUTTON_CANCEL);
 
@@ -399,6 +403,29 @@
   return true;
 }
 
+void ExtensionInstallDialogView::CloseDialog() {
+  GetWidget()->Close();
+}
+
+void ExtensionInstallDialogView::OnExtensionUninstalled(
+    content::BrowserContext* browser_context,
+    const extensions::Extension* extension,
+    extensions::UninstallReason reason) {
+  // Close the dialog if the extension is uninstalled.
+  if (extension->id() != prompt_->extension()->id())
+    return;
+  CloseDialog();
+}
+
+void ExtensionInstallDialogView::OnShutdown(
+    extensions::ExtensionRegistry* registry) {
+  extensions::ExtensionRegistry* extension_registry =
+      extensions::ExtensionRegistry::Get(profile_);
+  DCHECK_EQ(extension_registry, registry);
+  extension_registry_observer_.Remove(extension_registry);
+  CloseDialog();
+}
+
 ax::mojom::Role ExtensionInstallDialogView::GetAccessibleWindowRole() {
   return ax::mojom::Role::kAlertDialog;
 }
@@ -424,7 +451,7 @@
     chrome::ScopedTabbedBrowserDisplayer displayer(profile_);
     displayer.browser()->OpenURL(params);
   }
-  GetWidget()->Close();
+  CloseDialog();
 }
 
 void ExtensionInstallDialogView::CreateContents() {
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.h b/chrome/browser/ui/views/extensions/extension_install_dialog_view.h
index 4cb47f69..b4e1f500 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.h
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.h
@@ -9,9 +9,13 @@
 
 #include "base/macros.h"
 #include "base/optional.h"
+#include "base/scoped_observer.h"
 #include "base/timer/elapsed_timer.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/extensions/extension_install_prompt.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_registry_observer.h"
+#include "extensions/browser/uninstall_reason.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/checkbox.h"
@@ -26,7 +30,9 @@
 // Modal dialog that shows when the user attempts to install an extension. Also
 // shown if the extension is already installed but needs additional permissions.
 // Not a normal "bubble" despite being a subclass of BubbleDialogDelegateView.
-class ExtensionInstallDialogView : public views::BubbleDialogDelegateView {
+class ExtensionInstallDialogView
+    : public views::BubbleDialogDelegateView,
+      public extensions::ExtensionRegistryObserver {
  public:
   // The views::View::id of the ratings section in the dialog.
   static const int kRatingsViewId = 1;
@@ -57,6 +63,14 @@
   bool IsDialogButtonEnabled(ui::DialogButton button) const override;
   bool ShouldShowCloseButton() const override;
 
+  void CloseDialog();
+
+  // extensions::ExtensionRegistryObserver:
+  void OnExtensionUninstalled(content::BrowserContext* browser_context,
+                              const extensions::Extension* extension,
+                              extensions::UninstallReason reason) override;
+  void OnShutdown(extensions::ExtensionRegistry* registry) override;
+
   // views::WidgetDelegate:
   ax::mojom::Role GetAccessibleWindowRole() override;
   base::string16 GetAccessibleWindowTitle() const override;
@@ -83,6 +97,9 @@
   ExtensionInstallPrompt::DoneCallback done_callback_;
   std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt_;
   base::string16 title_;
+  ScopedObserver<extensions::ExtensionRegistry,
+                 extensions::ExtensionRegistryObserver>
+      extension_registry_observer_{this};
 
   // The scroll view containing all the details for the dialog (including all
   // collapsible/expandable sections).
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
index 5d24528..c523300 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/extensions/extension_icon_manager.h"
 #include "chrome/browser/extensions/extension_install_prompt.h"
 #include "chrome/browser/extensions/extension_install_prompt_test_helper.h"
+#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/platform_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
@@ -31,6 +32,9 @@
 #include "components/constrained_window/constrained_window_views.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_utils.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/test_extension_registry_observer.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_features.h"
 #include "extensions/common/permissions/permission_message_provider.h"
@@ -68,6 +72,9 @@
   // Creates and returns an install prompt of |prompt_type|.
   std::unique_ptr<ExtensionInstallPrompt::Prompt> CreatePrompt(
       ExtensionInstallPrompt::PromptType prompt_type);
+  std::unique_ptr<ExtensionInstallPrompt::Prompt> CreatePrompt(
+      ExtensionInstallPrompt::PromptType prompt_type,
+      const extensions::Extension* extension);
 
   content::WebContents* web_contents() { return web_contents_; }
 
@@ -93,13 +100,20 @@
 std::unique_ptr<ExtensionInstallPrompt::Prompt>
 ExtensionInstallDialogViewTestBase::CreatePrompt(
     ExtensionInstallPrompt::PromptType prompt_type) {
+  return CreatePrompt(prompt_type, extension_);
+}
+
+std::unique_ptr<ExtensionInstallPrompt::Prompt>
+ExtensionInstallDialogViewTestBase::CreatePrompt(
+    ExtensionInstallPrompt::PromptType prompt_type,
+    const extensions::Extension* extension) {
   std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt(
       new ExtensionInstallPrompt::Prompt(prompt_type));
-  prompt->set_extension(extension_);
+  prompt->set_extension(extension);
 
   std::unique_ptr<ExtensionIconManager> icon_manager(
       new ExtensionIconManager());
-  prompt->set_icon(icon_manager->GetIcon(extension_->id()));
+  prompt->set_icon(icon_manager->GetIcon(extension->id()));
 
   return prompt;
 }
@@ -436,6 +450,82 @@
   ShowAndVerifyUi();
 }
 
+class ExtensionInstallDialogViewOnUninstallationTest
+    : public ExtensionInstallDialogViewTest {
+ public:
+  ExtensionInstallDialogViewOnUninstallationTest() = default;
+  ExtensionInstallDialogViewOnUninstallationTest(
+      const ExtensionInstallDialogViewOnUninstallationTest&) = delete;
+  ExtensionInstallDialogViewOnUninstallationTest& operator=(
+      const ExtensionInstallDialogViewOnUninstallationTest&) = delete;
+
+ protected:
+  void UninstallExtension(const std::string& extension_id);
+};
+
+void ExtensionInstallDialogViewOnUninstallationTest::UninstallExtension(
+    const std::string& extension_id) {
+  extensions::TestExtensionRegistryObserver observer(
+      extensions::ExtensionRegistry::Get(browser()->profile()), extension_id);
+  extensions::ExtensionSystem::Get(browser()->profile())
+      ->extension_service()
+      ->UninstallExtension(
+          extension_id,
+          extensions::UninstallReason::UNINSTALL_REASON_FOR_TESTING, nullptr);
+  observer.WaitForExtensionUninstalled();
+}
+
+IN_PROC_BROWSER_TEST_F(ExtensionInstallDialogViewOnUninstallationTest,
+                       UninstallingExtensionClosesDialog) {
+  const extensions::Extension* extension =
+      LoadExtension(test_data_dir_.AppendASCII(
+          "install_prompt/dialog_on_uninstall/same_extension"));
+  ASSERT_TRUE(extension);
+  std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt =
+      CreatePrompt(ExtensionInstallPrompt::REPAIR_PROMPT, extension);
+  ExtensionInstallDialogView* dialog = new ExtensionInstallDialogView(
+      profile(), web_contents(), base::DoNothing(), std::move(prompt));
+
+  views::Widget* modal_dialog = views::DialogDelegate::CreateDialogWidget(
+      dialog, nullptr,
+      platform_util::GetViewForWindow(browser()->window()->GetNativeWindow()));
+  ASSERT_TRUE(modal_dialog);
+  views::test::WidgetClosingObserver dialog_observer(modal_dialog);
+  modal_dialog->Show();
+  EXPECT_FALSE(modal_dialog->IsClosed());
+  UninstallExtension(extension->id());
+  dialog_observer.Wait();
+  EXPECT_TRUE(dialog_observer.widget_closed());
+}
+
+IN_PROC_BROWSER_TEST_F(ExtensionInstallDialogViewOnUninstallationTest,
+                       UninstallingOtherExtensionDoesNotCloseDialog) {
+  const extensions::Extension* extension =
+      LoadExtension(test_data_dir_.AppendASCII(
+          "install_prompt/dialog_on_uninstall/same_extension"));
+  const extensions::Extension* other_extension =
+      LoadExtension(test_data_dir_.AppendASCII(
+          "install_prompt/dialog_on_uninstall/other_extension"));
+  ASSERT_TRUE(extension);
+  ASSERT_TRUE(other_extension);
+  std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt =
+      CreatePrompt(ExtensionInstallPrompt::REPAIR_PROMPT, extension);
+  ExtensionInstallDialogView* dialog = new ExtensionInstallDialogView(
+      profile(), web_contents(), base::DoNothing(), std::move(prompt));
+
+  views::Widget* modal_dialog = views::DialogDelegate::CreateDialogWidget(
+      dialog, nullptr,
+      platform_util::GetViewForWindow(browser()->window()->GetNativeWindow()));
+  ASSERT_TRUE(modal_dialog);
+  views::test::WidgetClosingObserver dialog_observer(modal_dialog);
+  modal_dialog->Show();
+  EXPECT_FALSE(modal_dialog->IsClosed());
+  UninstallExtension(other_extension->id());
+  ASSERT_TRUE(modal_dialog);
+  modal_dialog->Close();
+  dialog_observer.Wait();
+}
+
 class ExtensionInstallDialogRatingsSectionTest
     : public ExtensionInstallDialogViewTest {
  public:
diff --git a/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller.cc b/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller.cc
index 87ec5f8d..acf6fe7 100644
--- a/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller.cc
+++ b/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller.cc
@@ -31,6 +31,9 @@
   DCHECK(profile_);
 }
 
+GlobalMediaControlsPromoController::~GlobalMediaControlsPromoController() =
+    default;
+
 void GlobalMediaControlsPromoController::ShowPromo() {
   // This shouldn't be called more than once. Check that state is fresh.
   DCHECK(!show_promo_called_);
@@ -59,7 +62,7 @@
       string_specifier, base::nullopt, base::nullopt, base::nullopt,
       std::move(feature_promo_bubble_timeout));
   promo_bubble_->set_close_on_deactivate(false);
-  promo_bubble_->GetWidget()->AddObserver(this);
+  observer_.Add(promo_bubble_->GetWidget());
 }
 
 void GlobalMediaControlsPromoController::OnMediaDialogOpened() {
@@ -79,6 +82,8 @@
   DCHECK(promo_bubble_);
   promo_bubble_ = nullptr;
 
+  observer_.Remove(widget);
+
   FinishPromo();
 }
 
diff --git a/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller.h b/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller.h
index b3c18dd4..ed164eb 100644
--- a/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller.h
+++ b/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller.h
@@ -5,8 +5,10 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_FEATURE_PROMOS_GLOBAL_MEDIA_CONTROLS_PROMO_CONTROLLER_H_
 #define CHROME_BROWSER_UI_VIEWS_FEATURE_PROMOS_GLOBAL_MEDIA_CONTROLS_PROMO_CONTROLLER_H_
 
+#include "base/scoped_observer.h"
 #include "chrome/browser/ui/global_media_controls/media_toolbar_button_observer.h"
 #include "chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.h"
+#include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
 
 class MediaToolbarButtonView;
@@ -21,7 +23,7 @@
  public:
   GlobalMediaControlsPromoController(MediaToolbarButtonView* owner,
                                      Profile* profile);
-  ~GlobalMediaControlsPromoController() override = default;
+  ~GlobalMediaControlsPromoController() override;
 
   // Shows the IPH promo. Should only be called once.
   void ShowPromo();
@@ -51,6 +53,8 @@
   Profile* const profile_;
   FeaturePromoBubbleView* promo_bubble_ = nullptr;
 
+  ScopedObserver<views::Widget, views::WidgetObserver> observer_{this};
+
   // Whether we are showing the promo.
   bool is_showing_ = false;
 
diff --git a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc
index ee1f66e..0006756 100644
--- a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc
+++ b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc
@@ -37,6 +37,8 @@
       browser_view_(browser_view) {
 }
 
+ReopenTabPromoController::~ReopenTabPromoController() = default;
+
 void ReopenTabPromoController::ShowPromo() {
   // This shouldn't be called more than once. Check that state is fresh.
   DCHECK(!show_promo_called_);
@@ -74,7 +76,7 @@
                 IDS_REOPEN_TAB_PROMO, base::nullopt,
                 IDS_REOPEN_TAB_PROMO_SCREENREADER, accelerator);
   promo_bubble_->set_close_on_deactivate(false);
-  promo_bubble_->GetWidget()->AddObserver(this);
+  observer_.Add(promo_bubble_->GetWidget());
 }
 
 void ReopenTabPromoController::OnTabReopened(int command_id) {
@@ -104,6 +106,8 @@
   // timed out without the user following our IPH. End it.
   if (promo_step_ == StepAtDismissal::kBubbleShown)
     PromoEnded();
+
+  observer_.Remove(widget);
 }
 
 void ReopenTabPromoController::AppMenuShown() {
diff --git a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h
index 5c85e7bd..beb2bea 100644
--- a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h
+++ b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h
@@ -5,7 +5,9 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_FEATURE_PROMOS_REOPEN_TAB_PROMO_CONTROLLER_H_
 #define CHROME_BROWSER_UI_VIEWS_FEATURE_PROMOS_REOPEN_TAB_PROMO_CONTROLLER_H_
 
+#include "base/scoped_observer.h"
 #include "chrome/browser/ui/views/frame/app_menu_button_observer.h"
+#include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
 
 class BrowserView;
@@ -19,7 +21,7 @@
                                  public views::WidgetObserver {
  public:
   explicit ReopenTabPromoController(BrowserView* browser_view);
-  ~ReopenTabPromoController() override = default;
+  ~ReopenTabPromoController() override;
 
   // Shows the IPH promo. Should only be called once.
   void ShowPromo();
@@ -67,6 +69,8 @@
   BrowserView* const browser_view_;
   FeaturePromoBubbleView* promo_bubble_ = nullptr;
 
+  ScopedObserver<views::Widget, views::WidgetObserver> observer_{this};
+
   // The promo stage that has been reached, logged to a histogram when the promo
   // flow ends.
   StepAtDismissal promo_step_ = StepAtDismissal::kBubbleShown;
diff --git a/chrome/browser/ui/views/global_error_bubble_view.cc b/chrome/browser/ui/views/global_error_bubble_view.cc
index 5279cd2..99f0e2cb 100644
--- a/chrome/browser/ui/views/global_error_bubble_view.cc
+++ b/chrome/browser/ui/views/global_error_bubble_view.cc
@@ -34,33 +34,17 @@
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/grid_layout.h"
 
-namespace {
-
-const int kMaxBubbleViewWidth = 362;
-
-views::View* GetGlobalErrorBubbleAnchorView(Browser* browser) {
-  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
-  return browser_view->toolbar_button_provider()->GetAppMenuButton();
-}
-
-gfx::Rect GetGlobalErrorBubbleAnchorRect(Browser* browser) {
-  return gfx::Rect();
-}
-
-}  // namespace
-
 // GlobalErrorBubbleViewBase ---------------------------------------------------
 
 // static
 GlobalErrorBubbleViewBase* GlobalErrorBubbleViewBase::ShowStandardBubbleView(
     Browser* browser,
     const base::WeakPtr<GlobalErrorWithStandardBubble>& error) {
-  views::View* anchor_view = GetGlobalErrorBubbleAnchorView(browser);
-  gfx::Rect anchor_rect;
-  if (!anchor_view)
-    anchor_rect = GetGlobalErrorBubbleAnchorRect(browser);
+  views::View* anchor_view = BrowserView::GetBrowserViewForBrowser(browser)
+                                 ->toolbar_button_provider()
+                                 ->GetAppMenuButton();
   GlobalErrorBubbleView* bubble_view = new GlobalErrorBubbleView(
-      anchor_view, anchor_rect, views::BubbleBorder::TOP_RIGHT, browser, error);
+      anchor_view, views::BubbleBorder::TOP_RIGHT, browser, error);
   views::BubbleDialogDelegateView::CreateBubble(bubble_view);
   bubble_view->GetWidget()->Show();
   return bubble_view;
@@ -70,7 +54,6 @@
 
 GlobalErrorBubbleView::GlobalErrorBubbleView(
     views::View* anchor_view,
-    const gfx::Rect& anchor_rect,
     views::BubbleBorder::Arrow arrow,
     Browser* browser,
     const base::WeakPtr<GlobalErrorWithStandardBubble>& error)
@@ -104,11 +87,6 @@
         this, error_->GetBubbleViewDetailsButtonLabel()));
   }
 
-  if (!anchor_view) {
-    SetAnchorRect(anchor_rect);
-    set_parent_window(
-        platform_util::GetViewForWindow(browser->window()->GetNativeWindow()));
-  }
   chrome::RecordDialogCreation(chrome::DialogIdentifier::GLOBAL_ERROR);
 }
 
@@ -126,6 +104,7 @@
 }
 
 void GlobalErrorBubbleView::Init() {
+  const int kMaxBubbleViewWidth = 362;
   // |error_| is assumed to be valid, and stay valid, at least until Init()
   // returns.
 
diff --git a/chrome/browser/ui/views/global_error_bubble_view.h b/chrome/browser/ui/views/global_error_bubble_view.h
index 442167a3..5417237f 100644
--- a/chrome/browser/ui/views/global_error_bubble_view.h
+++ b/chrome/browser/ui/views/global_error_bubble_view.h
@@ -21,7 +21,6 @@
  public:
   GlobalErrorBubbleView(
       views::View* anchor_view,
-      const gfx::Rect& anchor_rect,
       views::BubbleBorder::Arrow arrow,
       Browser* browser,
       const base::WeakPtr<GlobalErrorWithStandardBubble>& error);
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
index 1f0ba0e..a9b83963 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
@@ -151,9 +151,10 @@
 }
 
 OmniboxPopupContentsView::~OmniboxPopupContentsView() {
-  // We don't need to do anything with |popup_| here.  The OS either has already
-  // closed the window, in which case it's been deleted, or it will soon, in
-  // which case there's nothing we need to do.
+  // We don't need to close or delete |popup_| here. The OS either has already
+  // closed the window, in which case it's been deleted, or it will soon.
+  if (popup_)
+    popup_->RemoveObserver(this);
 }
 
 void OmniboxPopupContentsView::OpenMatch(
diff --git a/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.cc
index c88297b..27dabbd 100644
--- a/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.cc
@@ -19,7 +19,6 @@
 namespace {
 
 constexpr const char kUserActionSkip[] = "recommendAppsSkip";
-constexpr const char kUserActionRetry[] = "recommendAppsRetry";
 constexpr const char kUserActionInstall[] = "recommendAppsInstall";
 
 constexpr const int kMaxAppCount = 21;
@@ -91,15 +90,12 @@
                IDS_LOGIN_RECOMMEND_APPS_SCREEN_DESCRIPTION);
   builder->Add("recommendAppsSkip", IDS_LOGIN_RECOMMEND_APPS_SKIP);
   builder->Add("recommendAppsInstall", IDS_LOGIN_RECOMMEND_APPS_INSTALL);
-  builder->Add("recommendAppsRetry", IDS_LOGIN_RECOMMEND_APPS_RETRY);
   builder->Add("recommendAppsLoading", IDS_LOGIN_RECOMMEND_APPS_SCREEN_LOADING);
-  builder->Add("recommendAppsError", IDS_LOGIN_RECOMMEND_APPS_SCREEN_ERROR);
 }
 
 void RecommendAppsScreenHandler::RegisterMessages() {
   BaseScreenHandler::RegisterMessages();
   AddCallback(kUserActionSkip, &RecommendAppsScreenHandler::OnUserSkip);
-  AddCallback(kUserActionRetry, &RecommendAppsScreenHandler::HandleRetry);
   AddRawCallback(kUserActionInstall,
                  &RecommendAppsScreenHandler::HandleInstall);
 }
@@ -110,6 +106,10 @@
 }
 
 void RecommendAppsScreenHandler::Show() {
+  if (!page_is_ready()) {
+    show_on_init_ = true;
+    return;
+  }
   ShowScreen(kScreenId);
 
   Profile* profile = ProfileManager::GetActiveUserProfile();
@@ -118,11 +118,6 @@
 
 void RecommendAppsScreenHandler::Hide() {}
 
-void RecommendAppsScreenHandler::OnLoadError() {
-  RecordUmaScreenState(RecommendAppsScreenState::ERROR);
-  CallJS("login.RecommendAppsScreen.showError");
-}
-
 void RecommendAppsScreenHandler::OnLoadSuccess(const base::Value& app_list) {
   recommended_app_count_ = static_cast<int>(app_list.GetList().size());
   LoadAppListInUI(app_list);
@@ -133,15 +128,14 @@
   HandleSkip();
 }
 
-void RecommendAppsScreenHandler::Initialize() {}
+void RecommendAppsScreenHandler::Initialize() {
+  if (show_on_init_) {
+    Show();
+    show_on_init_ = false;
+  }
+}
 
 void RecommendAppsScreenHandler::LoadAppListInUI(const base::Value& app_list) {
-  if (!page_is_ready()) {
-    RecordUmaScreenState(RecommendAppsScreenState::ERROR);
-    CallJS("login.RecommendAppsScreen.showError");
-    return;
-  }
-
   RecordUmaScreenState(RecommendAppsScreenState::SHOW);
   const ui::ResourceBundle& resource_bundle =
       ui::ResourceBundle::GetSharedInstance();
@@ -166,12 +160,6 @@
     screen_->OnSkip();
 }
 
-void RecommendAppsScreenHandler::HandleRetry() {
-  RecordUmaScreenAction(RecommendAppsScreenAction::RETRIED);
-  if (screen_)
-    screen_->OnRetry();
-}
-
 void RecommendAppsScreenHandler::HandleInstall(const base::ListValue* args) {
   if (recommended_app_count_ != 0) {
     int selected_app_count = static_cast<int>(args->GetSize());
diff --git a/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.h
index 268ad8c..de0fd1ed 100644
--- a/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.h
@@ -31,10 +31,6 @@
   // Hides the contents of the screen.
   virtual void Hide() = 0;
 
-  // Called when the download of the recommend app list fails. Show an error
-  // message to the user.
-  virtual void OnLoadError() = 0;
-
   // Called when the download of the recommend app list is successful. Shows the
   // downloaded |app_list| to the user.
   virtual void OnLoadSuccess(const base::Value& app_list) = 0;
@@ -62,7 +58,6 @@
   void Bind(RecommendAppsScreen* screen) override;
   void Show() override;
   void Hide() override;
-  void OnLoadError() override;
   void OnLoadSuccess(const base::Value& app_list) override;
   void OnParseResponseError() override;
 
@@ -85,6 +80,9 @@
 
   int recommended_app_count_ = 0;
 
+  // If true, Initialize() will call Show().
+  bool show_on_init_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(RecommendAppsScreenHandler);
 };
 
diff --git a/chrome/browser/web_applications/OWNERS b/chrome/browser/web_applications/OWNERS
index 7217a06..1d217986 100644
--- a/chrome/browser/web_applications/OWNERS
+++ b/chrome/browser/web_applications/OWNERS
@@ -10,4 +10,7 @@
 
 # OS shortcuts
 tapted@chromium.org
-# COMPONENT: UI>Browser>WebAppinstalls
+# COMPONENT: UI>Browser>WebAppInstalls
+
+per-file web_app_file_handler*=davidbienvenu@chromium.org
+per-file web_app_file_handler*=jessemckenna@google.com
diff --git a/chrome/browser/web_applications/chrome_pwa_launcher/OWNERS b/chrome/browser/web_applications/chrome_pwa_launcher/OWNERS
index b7c2ade4..53fd8cf 100644
--- a/chrome/browser/web_applications/chrome_pwa_launcher/OWNERS
+++ b/chrome/browser/web_applications/chrome_pwa_launcher/OWNERS
@@ -1,5 +1,5 @@
 davidbienvenu@chromium.org
 jessemckenna@google.com
 
-# COMPONENT: UI>Browser>WebAppinstalls
+# COMPONENT: UI>Browser>WebAppInstalls
 # OS: Windows
diff --git a/chrome/browser/web_applications/chrome_pwa_launcher/chrome_pwa_launcher_util.cc b/chrome/browser/web_applications/chrome_pwa_launcher/chrome_pwa_launcher_util.cc
index cf90617..e40f660 100644
--- a/chrome/browser/web_applications/chrome_pwa_launcher/chrome_pwa_launcher_util.cc
+++ b/chrome/browser/web_applications/chrome_pwa_launcher/chrome_pwa_launcher_util.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/web_applications/chrome_pwa_launcher/chrome_pwa_launcher_util.h"
 
+#include "base/files/file_util.h"
 #include "base/path_service.h"
 #include "chrome/common/chrome_constants.h"
 
@@ -20,8 +21,12 @@
   base::FilePath chrome_dir;
   if (!base::PathService::Get(base::DIR_EXE, &chrome_dir))
     return base::FilePath();
-  return chrome_dir.AppendASCII(chrome::kChromeVersion)
-      .Append(kChromePwaLauncherExecutable);
+  base::FilePath launcher_path = chrome_dir.AppendASCII(chrome::kChromeVersion)
+                                     .Append(kChromePwaLauncherExecutable);
+  if (base::PathExists(launcher_path))
+    return launcher_path;
+  // In dev builds, the launcher will be in the executable directory.
+  return chrome_dir.Append(kChromePwaLauncherExecutable);
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/components/OWNERS b/chrome/browser/web_applications/components/OWNERS
new file mode 100644
index 0000000..14435acb
--- /dev/null
+++ b/chrome/browser/web_applications/components/OWNERS
@@ -0,0 +1,2 @@
+per-file web_app_file_handler_registration*=davidbienvenu@chromium.org
+per-file web_app_file_handler_registration*=jessemckenna@google.com
diff --git a/chrome/browser/web_applications/components/web_app_file_handler_registration_win_unittest.cc b/chrome/browser/web_applications/components/web_app_file_handler_registration_win_unittest.cc
index 367c0f1e..6eaf2a00 100644
--- a/chrome/browser/web_applications/components/web_app_file_handler_registration_win_unittest.cc
+++ b/chrome/browser/web_applications/components/web_app_file_handler_registration_win_unittest.cc
@@ -61,14 +61,6 @@
         registry_override_.OverrideRegistry(HKEY_LOCAL_MACHINE));
     ASSERT_NO_FATAL_FAILURE(
         registry_override_.OverrideRegistry(HKEY_CURRENT_USER));
-
-    // Create a mock chrome_pwa_launcher.exe in a mock Chrome version directory,
-    // where the file-registration code expects it to be.
-    const base::FilePath pwa_launcher_path = GetChromePwaLauncherPath();
-    ASSERT_TRUE(temp_version_dir_.Set(pwa_launcher_path.DirName()));
-    ASSERT_TRUE(
-        base::File(pwa_launcher_path, base::File::FLAG_CREATE).IsValid());
-
     testing_profile_manager_ = std::make_unique<TestingProfileManager>(
         TestingBrowserProcess::GetGlobal());
     ASSERT_TRUE(testing_profile_manager_->SetUp());
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index b0f3565..312a8c8 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -4475,6 +4475,7 @@
     sources += [
       "../browser/spellchecker/spell_check_host_chrome_impl_mac_unittest.cc",
       "../browser/spellchecker/spellcheck_custom_dictionary_unittest.cc",
+      "../browser/spellchecker/spellcheck_language_policy_handlers_unittest.cc",
       "../browser/spellchecker/spellcheck_service_unittest.cc",
       "../tools/convert_dict/convert_dict_unittest.cc",
     ]
diff --git a/chrome/test/chromedriver/docs/chrome_connection.md b/chrome/test/chromedriver/docs/chrome_connection.md
new file mode 100644
index 0000000..07a5b22
--- /dev/null
+++ b/chrome/test/chromedriver/docs/chrome_connection.md
@@ -0,0 +1,96 @@
+# Chrome Connection
+
+## Overview
+
+ChromeDriver uses several classes to connect to Chrome and control it.
+* `Chrome` class represents the Chrome browser controlled by ChromeDriver.
+* `DevToolsClient` class represents a connection to Chrome.
+* `WebView` class wraps around a `DevToolsClient` to provide some higher level
+  methods.
+
+## `Chrome` class
+
+ChromeDriver uses abstract class `Chrome` to represent the Chrome browser that
+it controls. Other classes derived from it provide the actual implementations.
+Here is its inheritance hierarchy:
+
+* [`Chrome`](../chrome/chrome.h): Abstract base class, defining the API.
+  * [`ChromeImpl`](../chrome/chrome_impl.h): Still an abstract class,
+    it contains implementation code that is shared by concrete classes.
+    * [`ChromeDesktopImpl`](../chrome/chrome_desktop_impl.h):
+      Represents a local desktop Chrome browser.
+    * [`ChromeAndroidImpl`](../chrome/chrome_android_impl.h):
+      Represents an adb connection to a Chrome running on an Android device.
+    * [`ChromeRemoteImpl`](../chrome/chrome_remote_impl.h):
+      Represents a Chrome browser that is started
+      independently from ChromeDriver, and is connected through a TCP port.
+      The browser can be running locally or remotely.
+  * [`StubChrome`](../chrome/stub_chrome.h): An implementation with all methods
+    stubbed out, used for unit tests only.
+    * Various test classes.
+
+There is a separate instance of `ChromeImpl` for each ChromeDriver session.
+It has several fields related to the connection between
+ChromeDriver and the browser:
+
+* `ChromeImpl::devtools_http_client_` is a `DevToolsHttpClient` object.
+  It contains the URL necessary to connect to browser DevTools (e.g.,
+  http://localhost:12345), but doesn't contain any actual connections.
+
+* `ChromeImpl::devtools_websocket_client_` is a `DevToolsClient` object.
+  It encapsulates a browser-wide DevTools connection, used for sending commands
+  that apply to the whole browser.
+  This instance of `DevToolsClient` is sometimes referred to as the
+  "browser-wide `DevToolsClient`".
+
+* `ChromeImpl::web_views_` is a list of `WebView` objects,
+  one for each tab active in the browser.
+  These objects are used for sending commands that apply to a specific tab.
+  `Chrome::GetWebViewById` allows retrieving a `WebView` by its ID.
+  (There are additional `WebView` instances that represent frames,
+  but `ChromeImpl` is not aware of them.)
+
+## `DevToolsClient` and `WebView`
+
+Abstract class [`DevToolsClient`](../chrome/devtools_client.h) and its
+implementation class [`DevToolsClientImpl`](../chrome/devtools_client_impl.h)
+handle communication between ChromeDriver and DevTools.
+`DevToolsClient` contains methods to connect to Chrome,
+send commands to Chrome, and receive responses and events from Chrome.
+Changes are rarely needed at this level to implement new features.
+
+The abstract class [`WebView`](../chrome/web_view.h) and its implementation
+class [`WebViewImpl`](../chrome/web_view_impl.h) contain higher-level
+methods on top of `DevToolsClient`. In addition to wrappers to methods
+provided by `DevToolsClient`, it also has higher level methods for
+synthetic event dispatching, cookie handling, etc.
+The rest of ChromeDriver usually interacts with Chrome through `WebView` class.
+
+There are several types of `DevToolsClient` and `WebView` instances:
+
+* Browser-wide `DevToolsClient`, with no matching `WebView`.
+  It has id `"browser"`.
+
+* `DevToolsClient` and `WebView` representing a tab or window.
+  Both should have the same id, a 32-digit uppercase hexadecimal number.
+
+* `DevToolsClient` and `WebView` representing an OOPIF (out-of-process iframe,
+  i.e., a frame connected to a different renderer process than its parent
+  frame). They are created in response to
+  [`Target.attachedToTarget` event](https://chromedevtools.github.io/devtools-protocol/tot/Target#event-attachedToTarget) from DevTools.
+  Each OOPIF has a `targetId` and a `sessionId` (not to be confused
+  with ChromeDriver's session ID), both 32-bit hexadecimal numbers.
+  The `DevToolsClient` uses the `sessionId` as its ID, and has a parent
+  `DevToolsClient`. The `WebView` uses the `targetId` (same as `frameId`)
+  as its ID, and has a parent `WebView`.
+  No real connections are made. All communications are forwarded by the parent
+  `DevToolsClient` and `WebView`.
+
+Note that in most case, each instance of `DevToolsClient` is wrapped by a
+`WebView`. The browser-wide `DevToolsClient` is the only instance not wrapped.
+
+From the point of view of the client application, each browser tab or window is
+represented by a window handle, a string formed by concatenation `"CDwindow-"`
+with the ID of the `DevToolsClient` and `WebView` representing the tab/window.
+Each session has a tab that is currently active. The `WebView` connected to that
+tab can be retrieved with `Session::GetTargetWindow`.
diff --git a/chrome/test/data/extensions/install_prompt/dialog_on_uninstall/other_extension/manifest.json b/chrome/test/data/extensions/install_prompt/dialog_on_uninstall/other_extension/manifest.json
new file mode 100644
index 0000000..ab7d4433
--- /dev/null
+++ b/chrome/test/data/extensions/install_prompt/dialog_on_uninstall/other_extension/manifest.json
@@ -0,0 +1,6 @@
+{
+  "name": "chrome_extension_name__other",
+  "description": "chrome_extension_desc__other",
+  "version": "1",
+  "manifest_version": 2
+}
diff --git a/chrome/test/data/extensions/install_prompt/dialog_on_uninstall/same_extension/manifest.json b/chrome/test/data/extensions/install_prompt/dialog_on_uninstall/same_extension/manifest.json
new file mode 100644
index 0000000..56084fe
--- /dev/null
+++ b/chrome/test/data/extensions/install_prompt/dialog_on_uninstall/same_extension/manifest.json
@@ -0,0 +1,6 @@
+{
+  "name": "chrome_extension_name__",
+  "description": "chrome_extension_desc__",
+  "version": "1",
+  "manifest_version": 2
+}
diff --git a/chrome/test/data/webui/settings/security_page_test.js b/chrome/test/data/webui/settings/security_page_test.js
index f1a934f..a0b9acf 100644
--- a/chrome/test/data/webui/settings/security_page_test.js
+++ b/chrome/test/data/webui/settings/security_page_test.js
@@ -176,6 +176,37 @@
         page.prefs.profile.password_manager_leak_detection.value == previous);
   });
 
+  test('SafeBrowsingRadio_PreferenceUpdate', function() {
+    const enhancedRadio = page.$$('#safeBrowsingEnhanced');
+    const standardRadio = page.$$('#safeBrowsingStandard');
+    const disabledRadio = page.$$('#safeBrowsingDisabled');
+
+    // Set an enhanced protection preference state and ensure the radio buttons
+    // correctly reflect this.
+    page.set('prefs.safebrowsing.enabled.value', true);
+    page.set('prefs.safebrowsing.enhanced.value', true);
+    Polymer.dom.flush();
+    assertTrue(enhancedRadio.checked);
+    assertFalse(standardRadio.checked);
+    assertFalse(disabledRadio.checked);
+
+    // As above but for an enabled protection state.
+    page.set('prefs.safebrowsing.enabled.value', true);
+    page.set('prefs.safebrowsing.enhanced.value', false);
+    Polymer.dom.flush();
+    assertFalse(enhancedRadio.checked);
+    assertTrue(standardRadio.checked);
+    assertFalse(disabledRadio.checked);
+
+    // As above but for a safebrowsing disabled state.
+    page.set('prefs.safebrowsing.enabled.value', false);
+    page.set('prefs.safebrowsing.enhanced.value', false);
+    Polymer.dom.flush();
+    assertFalse(enhancedRadio.checked);
+    assertFalse(standardRadio.checked);
+    assertTrue(disabledRadio.checked);
+  });
+
   test('SafeBrowsingRadio_ManagedState', async function() {
     const enhancedRadio = page.$$('#safeBrowsingEnhanced');
     const standardRadio = page.$$('#safeBrowsingStandard');
diff --git a/components/download/internal/common/download_stats.cc b/components/download/internal/common/download_stats.cc
index ca82992..fee6727 100644
--- a/components/download/internal/common/download_stats.cc
+++ b/components/download/internal/common/download_stats.cc
@@ -734,32 +734,6 @@
       DownloadContent::MAX);
 }
 
-void RecordDownloadContentTypeSecurity(
-    const GURL& download_url,
-    const std::vector<GURL>& url_chain,
-    const std::string& mime_type,
-    const base::RepeatingCallback<bool(const GURL&)>&
-        is_origin_secure_callback) {
-  bool is_final_download_secure = is_origin_secure_callback.Run(download_url);
-  bool is_redirect_chain_secure = true;
-  for (const auto& url : url_chain) {
-    if (!is_origin_secure_callback.Run(url)) {
-      is_redirect_chain_secure = false;
-      break;
-    }
-  }
-
-  DownloadContent download_content =
-      download::DownloadContentFromMimeType(mime_type, false);
-  if (is_final_download_secure && is_redirect_chain_secure) {
-    UMA_HISTOGRAM_ENUMERATION("Download.Start.ContentType.SecureChain",
-                              download_content, DownloadContent::MAX);
-  } else {
-    UMA_HISTOGRAM_ENUMERATION("Download.Start.ContentType.InsecureChain",
-                              download_content, DownloadContent::MAX);
-  }
-}
-
 void RecordDownloadSourcePageTransitionType(
     const base::Optional<ui::PageTransition>& page_transition) {
   if (!page_transition)
diff --git a/components/download/internal/common/in_progress_download_manager.cc b/components/download/internal/common/in_progress_download_manager.cc
index 6e84587..5d3a187 100644
--- a/components/download/internal/common/in_progress_download_manager.cc
+++ b/components/download/internal/common/in_progress_download_manager.cc
@@ -487,11 +487,6 @@
   std::vector<GURL> url_chain = info->url_chain;
   std::string mime_type = info->mime_type;
 
-  if (info->is_new_download) {
-    RecordDownloadContentTypeSecurity(info->url(), info->url_chain,
-                                      info->mime_type, is_origin_secure_cb_);
-  }
-
   // If the download cannot be found locally, ask |delegate_| to provide the
   // DownloadItem.
   if (delegate_ && !GetDownloadByGuid(info->guid)) {
diff --git a/components/download/public/common/download_stats.h b/components/download/public/common/download_stats.h
index afd011d..ed5a4c7b 100644
--- a/components/download/public/common/download_stats.h
+++ b/components/download/public/common/download_stats.h
@@ -420,13 +420,6 @@
     const GURL& download_url,
     const std::vector<GURL>& url_chain);
 
-COMPONENTS_DOWNLOAD_EXPORT void RecordDownloadContentTypeSecurity(
-    const GURL& download_url,
-    const std::vector<GURL>& url_chain,
-    const std::string& mime_type,
-    const base::RepeatingCallback<bool(const GURL&)>&
-        is_origin_secure_callback);
-
 COMPONENTS_DOWNLOAD_EXPORT void RecordDownloadSourcePageTransitionType(
     const base::Optional<ui::PageTransition>& transition);
 
diff --git a/components/exo/shell_surface_base.cc b/components/exo/shell_surface_base.cc
index 17449b5..32b654a3 100644
--- a/components/exo/shell_surface_base.cc
+++ b/components/exo/shell_surface_base.cc
@@ -694,6 +694,8 @@
 
 void ShellSurfaceBase::WindowClosing() {
   SetEnabled(false);
+  if (widget_)
+    widget_->RemoveObserver(this);
   widget_ = nullptr;
 }
 
diff --git a/components/open_from_clipboard/BUILD.gn b/components/open_from_clipboard/BUILD.gn
index a7099b0..411035c 100644
--- a/components/open_from_clipboard/BUILD.gn
+++ b/components/open_from_clipboard/BUILD.gn
@@ -93,7 +93,11 @@
 
   if (!is_ios) {
     sources += [ "clipboard_recent_content_generic_unittest.cc" ]
-    deps += [ "//ui/base/clipboard:clipboard_test_support" ]
+    deps += [
+      ":feature_flags",
+      "//base/test:test_support",
+      "//ui/base/clipboard:clipboard_test_support",
+    ]
   } else {
     sources += [ "clipboard_recent_content_ios_unittest.mm" ]
     configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/components/open_from_clipboard/clipboard_recent_content_generic_unittest.cc b/components/open_from_clipboard/clipboard_recent_content_generic_unittest.cc
index 4edc278..bc9cffaa0 100644
--- a/components/open_from_clipboard/clipboard_recent_content_generic_unittest.cc
+++ b/components/open_from_clipboard/clipboard_recent_content_generic_unittest.cc
@@ -11,7 +11,9 @@
 #include "base/stl_util.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
+#include "components/open_from_clipboard/clipboard_recent_content_features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/clipboard/test/test_clipboard.h"
 #include "url/gurl.h"
@@ -75,14 +77,18 @@
 }
 
 TEST_F(ClipboardRecentContentGenericTest, OlderURLsNotSuggested) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      kClipboardMaximumAge, {{kClipboardMaximumAgeParam, "600"}});
   ClipboardRecentContentGeneric recent_content;
   base::Time now = base::Time::Now();
   std::string text = "http://example.com/";
   test_clipboard_->WriteText(text.data(), text.length());
-  test_clipboard_->SetLastModifiedTime(now - base::TimeDelta::FromSeconds(10));
+  test_clipboard_->SetLastModifiedTime(now - base::TimeDelta::FromMinutes(9));
   EXPECT_TRUE(recent_content.GetRecentURLFromClipboard().has_value());
-  // If the last modified time is days ago, the URL shouldn't be suggested.
-  test_clipboard_->SetLastModifiedTime(now - base::TimeDelta::FromDays(2));
+  // If the last modified time is 10 minutes ago, the URL shouldn't be
+  // suggested.
+  test_clipboard_->SetLastModifiedTime(now - base::TimeDelta::FromMinutes(11));
   EXPECT_FALSE(recent_content.GetRecentURLFromClipboard().has_value());
 }
 
diff --git a/components/page_info/page_info.h b/components/page_info/page_info.h
index 4ad9524..daa699a 100644
--- a/components/page_info/page_info.h
+++ b/components/page_info/page_info.h
@@ -129,6 +129,8 @@
     PAGE_INFO_CONNECTION_HELP_OPENED = 8,
     PAGE_INFO_SITE_SETTINGS_OPENED = 9,
     PAGE_INFO_SECURITY_DETAILS_OPENED = 10,
+    PAGE_INFO_COOKIE_ALLOWED_FOR_SITE = 11,
+    PAGE_INFO_COOKIE_BLOCKED_FOR_SITE = 12,
     PAGE_INFO_COUNT
   };
 
diff --git a/components/password_manager/core/browser/fake_form_fetcher.cc b/components/password_manager/core/browser/fake_form_fetcher.cc
index 3c8789f..d8e8f40 100644
--- a/components/password_manager/core/browser/fake_form_fetcher.cc
+++ b/components/password_manager/core/browser/fake_form_fetcher.cc
@@ -26,6 +26,10 @@
   consumers_.RemoveObserver(consumer);
 }
 
+void FakeFormFetcher::Fetch() {
+  state_ = State::WAITING;
+}
+
 FormFetcher::State FakeFormFetcher::GetState() const {
   return state_;
 }
@@ -80,10 +84,6 @@
     consumer.OnFetchCompleted();
 }
 
-void FakeFormFetcher::Fetch() {
-  state_ = State::WAITING;
-}
-
 std::unique_ptr<FormFetcher> FakeFormFetcher::Clone() {
   return std::make_unique<FakeFormFetcher>();
 }
diff --git a/components/password_manager/core/browser/fake_form_fetcher.h b/components/password_manager/core/browser/fake_form_fetcher.h
index bae0e3ea..805f30e 100644
--- a/components/password_manager/core/browser/fake_form_fetcher.h
+++ b/components/password_manager/core/browser/fake_form_fetcher.h
@@ -37,6 +37,9 @@
 
   void RemoveConsumer(Consumer* consumer) override;
 
+  // Only sets the internal state to WAITING, no call to PasswordStore.
+  void Fetch() override;
+
   // Returns State::WAITING if Fetch() was called after any Set* calls, and
   // State::NOT_WAITING otherwise.
   State GetState() const override;
@@ -80,9 +83,6 @@
 
   void NotifyFetchCompleted();
 
-  // Only sets the internal state to WAITING, no call to PasswordStore.
-  void Fetch() override;
-
   // Returns a new FakeFormFetcher.
   std::unique_ptr<FormFetcher> Clone() override;
 
diff --git a/components/password_manager/core/browser/form_fetcher.h b/components/password_manager/core/browser/form_fetcher.h
index 682ab53..4d0949d 100644
--- a/components/password_manager/core/browser/form_fetcher.h
+++ b/components/password_manager/core/browser/form_fetcher.h
@@ -25,8 +25,6 @@
 // indirection allows caching of identical requests from PFM on the same origin,
 // as well as easier testing (no need to mock the whole PasswordStore when
 // testing a PFM).
-// TODO(crbug.com/621355): Actually modify the API to support fetching in the
-// FormFetcher instance.
 class FormFetcher {
  public:
   // State of waiting for a response from a PasswordStore. There might be
@@ -54,6 +52,12 @@
   // Call this to stop |consumer| from receiving updates from |this|.
   virtual void RemoveConsumer(Consumer* consumer) = 0;
 
+  // Fetches stored matching logins. In addition the statistics is fetched on
+  // platforms with the password bubble. This is called automatically during
+  // construction and can be called manually later as well to cause an update
+  // of the cached credentials.
+  virtual void Fetch() = 0;
+
   // Returns the current state of the FormFetcher
   virtual State GetState() const = 0;
 
@@ -87,12 +91,6 @@
   // Pointer to a preferred entry in the vector returned by GetBestMatches().
   virtual const autofill::PasswordForm* GetPreferredMatch() const = 0;
 
-  // Fetches stored matching logins. In addition the statistics is fetched on
-  // platforms with the password bubble. This is called automatically during
-  // construction and can be called manually later as well to cause an update
-  // of the cached credentials.
-  virtual void Fetch() = 0;
-
   // Creates a copy of |*this| with contains the same credentials without the
   // need for calling Fetch().
   virtual std::unique_ptr<FormFetcher> Clone() = 0;
diff --git a/components/password_manager/core/browser/form_fetcher_impl.cc b/components/password_manager/core/browser/form_fetcher_impl.cc
index 43aa314..4d804853 100644
--- a/components/password_manager/core/browser/form_fetcher_impl.cc
+++ b/components/password_manager/core/browser/form_fetcher_impl.cc
@@ -89,6 +89,41 @@
   consumers_.RemoveObserver(consumer);
 }
 
+void FormFetcherImpl::Fetch() {
+  std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
+  if (password_manager_util::IsLoggingActive(client_)) {
+    logger.reset(
+        new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
+    logger->LogMessage(Logger::STRING_FETCH_METHOD);
+    logger->LogNumber(Logger::STRING_FORM_FETCHER_STATE,
+                      static_cast<int>(state_));
+  }
+
+  if (state_ == State::WAITING) {
+    // There is currently a password store query in progress, need to re-fetch
+    // store results later.
+    need_to_refetch_ = true;
+    return;
+  }
+
+  PasswordStore* password_store = client_->GetProfilePasswordStore();
+  if (!password_store) {
+    if (logger)
+      logger->LogMessage(Logger::STRING_NO_STORE);
+    NOTREACHED();
+    return;
+  }
+  state_ = State::WAITING;
+  password_store->GetLogins(form_digest_, this);
+
+// The statistics isn't needed on mobile, only on desktop. Let's save some
+// processor cycles.
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
+  // The statistics is needed for the "Save password?" bubble.
+  password_store->GetSiteStats(form_digest_.origin.GetOrigin(), this);
+#endif
+}
+
 FormFetcherImpl::State FormFetcherImpl::GetState() const {
   return state_;
 }
@@ -167,41 +202,6 @@
   ProcessPasswordStoreResults(std::move(forms));
 }
 
-void FormFetcherImpl::Fetch() {
-  std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
-  if (password_manager_util::IsLoggingActive(client_)) {
-    logger.reset(
-        new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
-    logger->LogMessage(Logger::STRING_FETCH_METHOD);
-    logger->LogNumber(Logger::STRING_FORM_FETCHER_STATE,
-                      static_cast<int>(state_));
-  }
-
-  if (state_ == State::WAITING) {
-    // There is currently a password store query in progress, need to re-fetch
-    // store results later.
-    need_to_refetch_ = true;
-    return;
-  }
-
-  PasswordStore* password_store = client_->GetProfilePasswordStore();
-  if (!password_store) {
-    if (logger)
-      logger->LogMessage(Logger::STRING_NO_STORE);
-    NOTREACHED();
-    return;
-  }
-  state_ = State::WAITING;
-  password_store->GetLogins(form_digest_, this);
-
-// The statistics isn't needed on mobile, only on desktop. Let's save some
-// processor cycles.
-#if !defined(OS_IOS) && !defined(OS_ANDROID)
-  // The statistics is needed for the "Save password?" bubble.
-  password_store->GetSiteStats(form_digest_.origin.GetOrigin(), this);
-#endif
-}
-
 std::unique_ptr<FormFetcher> FormFetcherImpl::Clone() {
   // Create the copy without the "HTTPS migration" activated. If it was needed,
   // then it was done by |this| already.
diff --git a/components/password_manager/core/browser/form_fetcher_impl.h b/components/password_manager/core/browser/form_fetcher_impl.h
index 42a5ab30e..4a8a00b 100644
--- a/components/password_manager/core/browser/form_fetcher_impl.h
+++ b/components/password_manager/core/browser/form_fetcher_impl.h
@@ -45,25 +45,19 @@
   // FormFetcher:
   void AddConsumer(FormFetcher::Consumer* consumer) override;
   void RemoveConsumer(FormFetcher::Consumer* consumer) override;
+  void Fetch() override;
   State GetState() const override;
   const std::vector<InteractionsStats>& GetInteractionsStats() const override;
-
   std::vector<const autofill::PasswordForm*> GetNonFederatedMatches()
       const override;
   std::vector<const autofill::PasswordForm*> GetFederatedMatches()
       const override;
-
   bool IsBlacklisted() const override;
-
   const std::vector<const autofill::PasswordForm*>& GetAllRelevantMatches()
       const override;
-
   const std::vector<const autofill::PasswordForm*>& GetBestMatches()
       const override;
-
   const autofill::PasswordForm* GetPreferredMatch() const override;
-
-  void Fetch() override;
   std::unique_ptr<FormFetcher> Clone() override;
 
   // PasswordStoreConsumer:
diff --git a/components/password_manager/core/browser/multi_store_form_fetcher.cc b/components/password_manager/core/browser/multi_store_form_fetcher.cc
index 8ec7a426..823b8c0b 100644
--- a/components/password_manager/core/browser/multi_store_form_fetcher.cc
+++ b/components/password_manager/core/browser/multi_store_form_fetcher.cc
@@ -25,14 +25,6 @@
 
 MultiStoreFormFetcher::~MultiStoreFormFetcher() = default;
 
-bool MultiStoreFormFetcher::IsBlacklisted() const {
-  if (client_->GetPasswordFeatureManager()->GetDefaultPasswordStore() ==
-      PasswordForm::Store::kAccountStore) {
-    return is_blacklisted_in_account_store_;
-  }
-  return is_blacklisted_in_profile_store_;
-}
-
 void MultiStoreFormFetcher::Fetch() {
   if (password_manager_util::IsLoggingActive(client_)) {
     BrowserSavePasswordProgressLogger logger(client_->GetLogManager());
@@ -63,6 +55,14 @@
   }
 }
 
+bool MultiStoreFormFetcher::IsBlacklisted() const {
+  if (client_->GetPasswordFeatureManager()->GetDefaultPasswordStore() ==
+      PasswordForm::Store::kAccountStore) {
+    return is_blacklisted_in_account_store_;
+  }
+  return is_blacklisted_in_profile_store_;
+}
+
 void MultiStoreFormFetcher::OnGetPasswordStoreResults(
     std::vector<std::unique_ptr<PasswordForm>> results) {
   DCHECK_EQ(State::WAITING, state_);
diff --git a/components/password_manager/core/browser/multi_store_form_fetcher.h b/components/password_manager/core/browser/multi_store_form_fetcher.h
index fb25367a..3954b10 100644
--- a/components/password_manager/core/browser/multi_store_form_fetcher.h
+++ b/components/password_manager/core/browser/multi_store_form_fetcher.h
@@ -21,9 +21,10 @@
                         bool should_migrate_http_passwords);
   ~MultiStoreFormFetcher() override;
 
+  // FormFetcher overrides.
+  void Fetch() override;
   bool IsBlacklisted() const override;
 
-  void Fetch() override;
   void OnGetPasswordStoreResults(
       std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
 
diff --git a/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc b/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
index 565bbb77..7e5d29e6 100644
--- a/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
+++ b/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
@@ -902,9 +902,6 @@
   ON_CALL(*mock_password_store_sync(), IsAccountStore())
       .WillByDefault(Return(true));
 
-  const int kPrimaryKeyUnsyncedCredential = 1000;
-  const int kPrimaryKeySyncedCredential = 1001;
-  const int kPrimaryKeyUnsyncedDeletion = 1002;
   const std::string kPrimaryKeyUnsyncedCredentialStr = "1000";
   const std::string kPrimaryKeySyncedCredentialStr = "1001";
   const std::string kPrimaryKeyUnsyncedDeletionStr = "1002";
@@ -934,15 +931,16 @@
         return batch;
       });
 
+  // No form is added to the database for the unsynced deletion primary key,
+  // because the deletion is supposed to have already removed such form.
+  const int kPrimaryKeyUnsyncedCredential = 1000;
+  const int kPrimaryKeySyncedCredential = 1001;
   autofill::PasswordForm unsynced_credential = MakePasswordForm(kSignonRealm1);
   autofill::PasswordForm synced_credential = MakePasswordForm(kSignonRealm2);
-  autofill::PasswordForm unsynced_deletion = MakePasswordForm(kSignonRealm3);
   fake_db()->AddLoginForPrimaryKey(kPrimaryKeyUnsyncedCredential,
                                    unsynced_credential);
   fake_db()->AddLoginForPrimaryKey(kPrimaryKeySyncedCredential,
                                    synced_credential);
-  fake_db()->AddLoginForPrimaryKey(kPrimaryKeyUnsyncedDeletion,
-                                   unsynced_deletion);
 
   // The notification should only contain new credentials that are unsynced,
   // ignoring both synced ones and deletion entries.
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/Address.java b/components/payments/content/android/java/src/org/chromium/components/payments/Address.java
index 6a87e1c..0d106e4 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/Address.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/Address.java
@@ -4,6 +4,10 @@
 
 package org.chromium.components.payments;
 
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+
 /**
  * An immutable class that mirrors org.chromium.payments.mojom.PaymentAddress.
  * https://w3c.github.io/payment-request/#paymentaddress-interface
@@ -63,4 +67,39 @@
         this.recipient = recipient;
         this.phone = phone;
     }
+
+    // Keys in shipping address bundle.
+    public static final String EXTRA_ADDRESS_COUNTRY = "country";
+    public static final String EXTRA_ADDRESS_LINES = "addressLines";
+    public static final String EXTRA_ADDRESS_REGION = "region";
+    public static final String EXTRA_ADDRESS_CITY = "city";
+    public static final String EXTRA_ADDRESS_DEPENDENT_LOCALITY = "dependentLocality";
+    public static final String EXTRA_ADDRESS_POSTAL_CODE = "postalCode";
+    public static final String EXTRA_ADDRESS_SORTING_CODE = "sortingCode";
+    public static final String EXTRA_ADDRESS_ORGANIZATION = "organization";
+    public static final String EXTRA_ADDRESS_RECIPIENT = "recipient";
+    public static final String EXTRA_ADDRESS_PHONE = "phone";
+
+    /**
+     * @param address Bundle to be parsed.
+     * @return converted Address or null.
+     */
+    @Nullable
+    public static Address createFromBundle(@Nullable Bundle address) {
+        if (address == null) return null;
+        return new Address(getStringOrEmpty(address, EXTRA_ADDRESS_COUNTRY),
+                address.getStringArray(EXTRA_ADDRESS_LINES),
+                getStringOrEmpty(address, EXTRA_ADDRESS_REGION),
+                getStringOrEmpty(address, EXTRA_ADDRESS_CITY),
+                getStringOrEmpty(address, EXTRA_ADDRESS_DEPENDENT_LOCALITY),
+                getStringOrEmpty(address, EXTRA_ADDRESS_POSTAL_CODE),
+                getStringOrEmpty(address, EXTRA_ADDRESS_SORTING_CODE),
+                getStringOrEmpty(address, EXTRA_ADDRESS_ORGANIZATION),
+                getStringOrEmpty(address, EXTRA_ADDRESS_RECIPIENT),
+                getStringOrEmpty(address, EXTRA_ADDRESS_PHONE));
+    }
+
+    private static String getStringOrEmpty(Bundle bundle, String key) {
+        return bundle.getString(key, /*defaultValue =*/"");
+    }
 }
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/intent/WebPaymentIntentHelper.java b/components/payments/content/android/java/src/org/chromium/components/payments/intent/WebPaymentIntentHelper.java
index 57ef7fc9..b13bd01 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/intent/WebPaymentIntentHelper.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/intent/WebPaymentIntentHelper.java
@@ -75,18 +75,8 @@
     public static final String EXTRA_RESPONSE_PAYER_EMAIL = "payerEmail";
     public static final String EXTRA_RESPONSE_PAYER_PHONE = "payerPhone";
 
-    // Shipping address parsable and its fields, used in payment response and shippingAddressChange.
+    // Shipping address bundle used in payment response and shippingAddressChange.
     public static final String EXTRA_SHIPPING_ADDRESS = "shippingAddress";
-    public static final String EXTRA_ADDRESS_COUNTRY = "country";
-    public static final String EXTRA_ADDRESS_LINES = "addressLines";
-    public static final String EXTRA_ADDRESS_REGION = "region";
-    public static final String EXTRA_ADDRESS_CITY = "city";
-    public static final String EXTRA_ADDRESS_DEPENDENT_LOCALITY = "dependentLocality";
-    public static final String EXTRA_ADDRESS_POSTAL_CODE = "postalCode";
-    public static final String EXTRA_ADDRESS_SORTING_CODE = "sortingCode";
-    public static final String EXTRA_ADDRESS_ORGANIZATION = "organization";
-    public static final String EXTRA_ADDRESS_RECIPIENT = "recipient";
-    public static final String EXTRA_ADDRESS_PHONE = "phone";
 
     private static final String EMPTY_JSON_DATA = "{}";
 
@@ -165,16 +155,7 @@
                 errorCallback.onPaymentError(ErrorStrings.SHIPPING_ADDRESS_INVALID);
                 return;
             }
-            shippingAddress = new Address(getStringOrEmpty(addressBundle, EXTRA_ADDRESS_COUNTRY),
-                    addressBundle.getStringArray(EXTRA_ADDRESS_LINES),
-                    getStringOrEmpty(addressBundle, EXTRA_ADDRESS_REGION),
-                    getStringOrEmpty(addressBundle, EXTRA_ADDRESS_CITY),
-                    getStringOrEmpty(addressBundle, EXTRA_ADDRESS_DEPENDENT_LOCALITY),
-                    getStringOrEmpty(addressBundle, EXTRA_ADDRESS_POSTAL_CODE),
-                    getStringOrEmpty(addressBundle, EXTRA_ADDRESS_SORTING_CODE),
-                    getStringOrEmpty(addressBundle, EXTRA_ADDRESS_ORGANIZATION),
-                    getStringOrEmpty(addressBundle, EXTRA_ADDRESS_RECIPIENT),
-                    getStringOrEmpty(addressBundle, EXTRA_ADDRESS_PHONE));
+            shippingAddress = Address.createFromBundle(addressBundle);
         } else { // !requestedPaymentOptions.requestShipping
             shippingAddress = new Address();
         }
@@ -585,10 +566,6 @@
     }
 
     private static String getStringOrEmpty(Intent data, String key) {
-        return getStringOrEmpty(data.getExtras(), key);
-    }
-
-    private static String getStringOrEmpty(Bundle bundle, String key) {
-        return bundle.getString(key, /*defaultValue =*/"");
+        return data.getExtras().getString(key, /*defaultValue =*/"");
     }
 }
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor.cc b/components/sync/model_impl/client_tag_based_model_type_processor.cc
index 2b199d52..aa935f0 100644
--- a/components/sync/model_impl/client_tag_based_model_type_processor.cc
+++ b/components/sync/model_impl/client_tag_based_model_type_processor.cc
@@ -42,9 +42,6 @@
       dump_stack_(dump_stack),
       commit_only_(commit_only) {
   ResetState(CLEAR_METADATA);
-  // TODO(crbug.com/947044): create entity tracker after initial merge. It will
-  // be changed in a follow-up patch.
-  DCHECK(entity_tracker_);
 }
 
 ClientTagBasedModelTypeProcessor::~ClientTagBasedModelTypeProcessor() {
@@ -80,7 +77,7 @@
 void ClientTagBasedModelTypeProcessor::ModelReadyToSync(
     std::unique_ptr<MetadataBatch> batch) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!entity_tracker_->size());
+  DCHECK(!entity_tracker_);
   DCHECK(!model_ready_to_sync_);
 
   model_ready_to_sync_ = true;
@@ -92,7 +89,7 @@
   if (batch->GetModelTypeState().initial_sync_done()) {
     EntityMetadataMap metadata_map(batch->TakeAllMetadata());
     entity_tracker_ = std::make_unique<ProcessorEntityTracker>(
-        std::move(metadata_map), batch->GetModelTypeState());
+        batch->GetModelTypeState(), std::move(metadata_map));
   } else {
     // In older versions of the binary, commit-only types did not persist
     // initial_sync_done(). So this branch can be exercised for commit-only
@@ -101,7 +98,6 @@
     // persisted Sync metadata is in an inconsistent state.
     DCHECK(commit_only_ || batch->TakeAllMetadata().empty())
         << ModelTypeToString(type_);
-    entity_tracker_->InitializeMetadata(type_);
   }
 
   ConnectIfReady();
@@ -126,61 +122,34 @@
     return;
   }
 
-  sync_pb::ModelTypeState model_type_state =
-      entity_tracker_->model_type_state();
-
-  if (!model_type_state.has_cache_guid()) {
-    // Initialize the cache_guid for old clients that didn't persist it.
-    model_type_state.set_cache_guid(activation_request_.cache_guid);
-  }
-  // Check for invalid persisted metadata.
-  if (model_type_state.cache_guid() != activation_request_.cache_guid ||
-      model_type_state.progress_marker().data_type_id() !=
-          GetSpecificsFieldNumberFromModelType(type_)) {
-    // There is a mismatch between the cache guid or the data type id stored in
-    // |model_type_state_| and the one received from sync. This indicates that
-    // the stored metadata are invalid (e.g. has been manipulated) and don't
-    // belong to the current syncing client.
-    if (model_type_state.progress_marker().data_type_id() !=
-        GetSpecificsFieldNumberFromModelType(type_)) {
-      UMA_HISTOGRAM_ENUMERATION("Sync.PersistedModelTypeIdMismatch",
-                                ModelTypeHistogramValue(type_));
-    }
-    ClearMetadataAndResetState();
-    model_type_state = entity_tracker_->model_type_state();
-    model_type_state.set_cache_guid(activation_request_.cache_guid);
-
-    // The model is still ready to sync (with the same |bridge_|) - replay
-    // the initialization.
-    model_ready_to_sync_ = true;
-    // Notify the bridge sync is starting to simulate an enable event.
-    bridge_->OnSyncStarting(activation_request_);
-
-    DCHECK(!model_type_state.initial_sync_done());
-  }
-
-  // Cache GUID verification earlier above guarantees the user is the same.
-  model_type_state.set_authenticated_account_id(
-      activation_request_.authenticated_account_id.ToString());
-
-  entity_tracker_->set_model_type_state(model_type_state);
-
-  // For commit-only types, no updates are expected and hence we can consider
-  // initial_sync_done(), reflecting that sync is enabled.
-  if (commit_only_ &&
-      !entity_tracker_->model_type_state().initial_sync_done()) {
-    sync_pb::ModelTypeState model_type_state_sync_done =
-        entity_tracker_->model_type_state();
-    model_type_state_sync_done.set_initial_sync_done(true);
-    OnFullUpdateReceived(model_type_state_sync_done, UpdateResponseDataList());
-    DCHECK(IsTrackingMetadata());
-  }
-
-  DCHECK_EQ(entity_tracker_->model_type_state().cache_guid(),
-            activation_request_.cache_guid);
+  CheckForInvalidPersistedMetadata();
 
   auto activation_response = std::make_unique<DataTypeActivationResponse>();
-  activation_response->model_type_state = entity_tracker_->model_type_state();
+  if (!entity_tracker_) {
+    sync_pb::ModelTypeState model_type_state;
+    model_type_state.mutable_progress_marker()->set_data_type_id(
+        GetSpecificsFieldNumberFromModelType(type_));
+    model_type_state.set_cache_guid(activation_request_.cache_guid);
+    model_type_state.set_authenticated_account_id(
+        activation_request_.authenticated_account_id.ToString());
+    if (commit_only_) {
+      // For commit-only types, no updates are expected and hence we can
+      // consider initial_sync_done(), reflecting that sync is enabled.
+      model_type_state.set_initial_sync_done(true);
+      OnFullUpdateReceived(model_type_state, UpdateResponseDataList());
+      DCHECK(entity_tracker_);
+    } else {
+      activation_response->model_type_state = model_type_state;
+    }
+  }
+
+  if (entity_tracker_) {
+    activation_response->model_type_state = entity_tracker_->model_type_state();
+  }
+
+  DCHECK_EQ(activation_response->model_type_state.cache_guid(),
+            activation_request_.cache_guid);
+
   activation_response->type_processor =
       std::make_unique<ModelTypeProcessorProxy>(
           weak_ptr_factory_for_worker_.GetWeakPtr(),
@@ -235,8 +204,12 @@
 void ClientTagBasedModelTypeProcessor::ClearMetadataAndResetState() {
   std::unique_ptr<MetadataChangeList> change_list;
 
+  // All changes before the initial sync is done are ignored and in fact they
+  // were never persisted by the bridge (prior to MergeSyncData), so no
+  // entities should be tracking.
+  //
   // Clear metadata if MergeSyncData() was called before.
-  if (entity_tracker_->model_type_state().initial_sync_done()) {
+  if (entity_tracker_) {
     change_list = bridge_->CreateMetadataChangeList();
     std::vector<const ProcessorEntity*> entities =
         entity_tracker_->GetAllEntitiesIncludingTombstones();
@@ -244,11 +217,6 @@
       change_list->ClearMetadata(entity->storage_key());
     }
     change_list->ClearModelTypeState();
-  } else {
-    // All changes before the initial sync is done are ignored and in fact they
-    // were never persisted by the bridge (prior to MergeSyncData), so we should
-    // be tracking no entities.
-    DCHECK(!entity_tracker_->size());
   }
 
   bridge_->ApplyStopSyncChanges(std::move(change_list));
@@ -258,7 +226,7 @@
 }
 
 bool ClientTagBasedModelTypeProcessor::IsTrackingMetadata() {
-  return entity_tracker_->model_type_state().initial_sync_done();
+  return entity_tracker_ != nullptr;
 }
 
 std::string ClientTagBasedModelTypeProcessor::TrackedAccountId() {
@@ -340,7 +308,9 @@
   weak_ptr_factory_for_worker_.InvalidateWeakPtrs();
   worker_.reset();
 
-  entity_tracker_->ClearTransientSyncState();
+  if (entity_tracker_) {
+    entity_tracker_->ClearTransientSyncState();
+  }
 }
 
 void ClientTagBasedModelTypeProcessor::Put(
@@ -356,7 +326,7 @@
   DCHECK(!storage_key.empty());
   DCHECK_EQ(type_, GetModelTypeFromSpecifics(data->specifics));
 
-  if (!entity_tracker_->model_type_state().initial_sync_done()) {
+  if (!entity_tracker_) {
     // Ignore changes before the initial sync is done.
     return;
   }
@@ -416,7 +386,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(IsAllowingChanges());
 
-  if (!entity_tracker_->model_type_state().initial_sync_done()) {
+  if (!entity_tracker_) {
     // Ignore changes before the initial sync is done.
     return;
   }
@@ -444,7 +414,7 @@
   DCHECK(!client_tag_hash.value().empty());
   DCHECK(!storage_key.empty());
   DCHECK(!bridge_->SupportsGetStorageKey());
-  DCHECK(entity_tracker_->model_type_state().initial_sync_done());
+  DCHECK(entity_tracker_);
 
   ProcessorEntity* entity =
       entity_tracker_->GetEntityForTagHash(client_tag_hash);
@@ -458,17 +428,29 @@
 
 void ClientTagBasedModelTypeProcessor::UntrackEntityForStorageKey(
     const std::string& storage_key) {
+  if (!entity_tracker_) {
+    // Ignore changes before the initial sync is done.
+    return;
+  }
   entity_tracker_->RemoveEntityForStorageKey(storage_key);
 }
 
 void ClientTagBasedModelTypeProcessor::UntrackEntityForClientTagHash(
     const ClientTagHash& client_tag_hash) {
   DCHECK(!client_tag_hash.value().empty());
+  if (!entity_tracker_) {
+    // Ignore changes before the initial sync is done.
+    return;
+  }
   entity_tracker_->RemoveEntityForClientTagHash(client_tag_hash);
 }
 
 bool ClientTagBasedModelTypeProcessor::IsEntityUnsynced(
     const std::string& storage_key) {
+  if (!entity_tracker_) {
+    return false;
+  }
+
   const ProcessorEntity* entity =
       entity_tracker_->GetEntityForStorageKey(storage_key);
   if (entity == nullptr) {
@@ -480,6 +462,10 @@
 
 base::Time ClientTagBasedModelTypeProcessor::GetEntityCreationTime(
     const std::string& storage_key) const {
+  if (!entity_tracker_) {
+    return base::Time();
+  }
+
   const ProcessorEntity* entity =
       entity_tracker_->GetEntityForStorageKey(storage_key);
   if (entity == nullptr) {
@@ -490,6 +476,10 @@
 
 base::Time ClientTagBasedModelTypeProcessor::GetEntityModificationTime(
     const std::string& storage_key) const {
+  if (!entity_tracker_) {
+    return base::Time();
+  }
+
   const ProcessorEntity* entity =
       entity_tracker_->GetEntityForStorageKey(storage_key);
   if (entity == nullptr) {
@@ -504,7 +494,7 @@
     return;
 
   // Don't send anything if the type is not ready to handle commits.
-  if (!entity_tracker_->model_type_state().initial_sync_done())
+  if (!entity_tracker_)
     return;
 
   // Nudge worker if there are any entities with local changes.0
@@ -517,10 +507,13 @@
     GetLocalChangesCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_GT(max_entries, 0U);
+
   // If there is a model error, it must have been reported already but hasn't
   // reached the sync engine yet. In this case return directly to avoid
   // interactions with the bridge.
-  if (model_error_) {
+  // In some cases local changes may be requested before entity tracker is
+  // loaded. Just invoke the callback with empty list.
+  if (model_error_ || !entity_tracker_) {
     std::move(callback).Run(CommitRequestDataList());
     return;
   }
@@ -556,6 +549,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!model_error_);
 
+  DCHECK(entity_tracker_)
+      << "Received commit response when entity tracker is null. Type: "
+      << ModelTypeToString(type_);
+
   // |error_response_list| is ignored, because all errors are treated as
   // transientand the processor with eventually retry.
 
@@ -654,8 +651,7 @@
   // always clear all data. We do this to allow the server to replace all data
   // on the client, without having to know exactly which entities the client
   // has.
-  const bool is_initial_sync =
-      !entity_tracker_->model_type_state().initial_sync_done();
+  const bool is_initial_sync = !IsTrackingMetadata();
   if (is_initial_sync || HasClearAllDirective(model_type_state)) {
     error = OnFullUpdateReceived(model_type_state, std::move(updates));
   } else {
@@ -683,6 +679,7 @@
         /*buckets=*/50);
   }
 
+  DCHECK(entity_tracker_);
   // If there were entities with empty storage keys, they should have been
   // updated by bridge as part of ApplySyncChanges.
   DCHECK(entity_tracker_->AllStorageKeysPopulated());
@@ -693,7 +690,7 @@
 bool ClientTagBasedModelTypeProcessor::ValidateUpdate(
     const sync_pb::ModelTypeState& model_type_state,
     const UpdateResponseDataList& updates) {
-  if (!entity_tracker_->model_type_state().initial_sync_done()) {
+  if (!entity_tracker_) {
     // Due to uss_migrator, initial sync (when migrating from non-USS) does not
     // contain any gc directives. Thus, we cannot expect the conditions below to
     // be satisfied. It is okay to skip the check as for an initial sync, the gc
@@ -733,30 +730,33 @@
       bridge_->CreateMetadataChangeList();
   DCHECK(model_ready_to_sync_);
 
-  // Check that the worker correctly marked initial sync as done
-  // for this update.
+  // Check that the worker correctly marked initial sync as done for this
+  // update.
   DCHECK(model_type_state.initial_sync_done());
 
-  if (HasClearAllDirective(model_type_state)) {
+  // Ensure that this is the initial sync, and it was not already marked done.
+  DCHECK(HasClearAllDirective(model_type_state) || !entity_tracker_);
+
+  if (entity_tracker_ && HasClearAllDirective(model_type_state)) {
     ExpireAllEntries(metadata_changes.get());
-  } else {
-    // Ensure that this is the initial sync, and it was not already marked done.
-    DCHECK(!entity_tracker_->model_type_state().initial_sync_done());
+    entity_tracker_->set_model_type_state(model_type_state);
+  }
+
+  if (!entity_tracker_) {
+    entity_tracker_ = std::make_unique<ProcessorEntityTracker>(
+        model_type_state, EntityMetadataMap());
   }
 
   // TODO(crbug.com/1041888): the comment below may be wrong in case where a
-  // datatype supports non-incremental updates and local updates are acceptable.
-
+  // datatype supports non-incremental updates and local updates are
+  // acceptable.
   // Given that we either just removed all existing sync entities (in the full
-  // update case), or we just started sync for the first time, we should not
-  // have any entities here.
+  // update case).
   DCHECK(!entity_tracker_->size());
 
-  EntityChangeList entity_data;
-
-  entity_tracker_->set_model_type_state(model_type_state);
   metadata_changes->UpdateModelTypeState(entity_tracker_->model_type_state());
 
+  EntityChangeList entity_data;
   for (syncer::UpdateResponseData& update : updates) {
     const ClientTagHash& client_tag_hash = update.entity.client_tag_hash;
     if (client_tag_hash.value().empty()) {
@@ -812,6 +812,7 @@
     UpdateResponseDataList updates) {
   DCHECK(model_ready_to_sync_);
   DCHECK(model_type_state.initial_sync_done());
+  DCHECK(entity_tracker_);
 
   ClientTagBasedRemoteUpdateHandler updates_handler(type_, bridge_,
                                                     entity_tracker_.get());
@@ -832,6 +833,8 @@
 
   ConsumeDataBatch(std::move(storage_keys_to_load), std::move(data_batch));
 
+  // TODO(rushans): looks like some old logic, remove ConnectIfReady() in a
+  // separate patch.
   ConnectIfReady();
   CommitLocalChanges(max_entries, std::move(callback));
 }
@@ -839,6 +842,7 @@
 void ClientTagBasedModelTypeProcessor::ConsumeDataBatch(
     std::unordered_set<std::string> storage_keys_to_load,
     std::unique_ptr<DataBatch> data_batch) {
+  DCHECK(entity_tracker_);
   while (data_batch->HasNext()) {
     KeyAndData data = data_batch->Next();
     const std::string& storage_key = data.first;
@@ -896,6 +900,7 @@
     size_t max_entries,
     GetLocalChangesCallback callback) {
   DCHECK(!model_error_);
+  DCHECK(entity_tracker_);
   // Prepares entities commit request data for entities which are
   // out of sync with the sync thread.
   CommitRequestDataList commit_requests;
@@ -918,6 +923,7 @@
 ClientTagHash ClientTagBasedModelTypeProcessor::GetClientTagHash(
     const std::string& storage_key,
     const EntityData& data) const {
+  DCHECK(entity_tracker_);
   const base::Optional<ClientTagHash> client_tag_hash =
       entity_tracker_->GetClientTagHash(storage_key);
   DCHECK(bridge_->SupportsGetClientTag());
@@ -930,6 +936,7 @@
     const std::string& storage_key,
     const EntityData& data) {
   DCHECK(!bridge_->SupportsGetStorageKey() || !storage_key.empty());
+  DCHECK(entity_tracker_);
   ProcessorEntity* entity_ptr = entity_tracker_->Add(storage_key, data);
   return entity_ptr;
 }
@@ -955,7 +962,9 @@
 size_t ClientTagBasedModelTypeProcessor::EstimateMemoryUsage() const {
   using base::trace_event::EstimateMemoryUsage;
   size_t memory_usage = 0;
-  memory_usage += entity_tracker_->EstimateMemoryUsage();
+  if (entity_tracker_) {
+    memory_usage += entity_tracker_->EstimateMemoryUsage();
+  }
   if (bridge_) {
     memory_usage += bridge_->EstimateSyncOverheadMemoryUsage();
   }
@@ -963,12 +972,13 @@
 }
 
 bool ClientTagBasedModelTypeProcessor::HasLocalChangesForTest() const {
-  return entity_tracker_->HasLocalChanges();
+  return entity_tracker_ && entity_tracker_->HasLocalChanges();
 }
 
 bool ClientTagBasedModelTypeProcessor::IsTrackingEntityForTest(
     const std::string& storage_key) const {
-  return entity_tracker_->GetEntityForStorageKey(storage_key) != nullptr;
+  return entity_tracker_ &&
+         entity_tracker_->GetEntityForStorageKey(storage_key) != nullptr;
 }
 
 bool ClientTagBasedModelTypeProcessor::IsModelReadyToSyncForTest() const {
@@ -978,6 +988,7 @@
 void ClientTagBasedModelTypeProcessor::ExpireAllEntries(
     MetadataChangeList* metadata_changes) {
   DCHECK(metadata_changes);
+  DCHECK(entity_tracker_);
 
   std::vector<std::string> storage_key_to_be_deleted;
   for (const ProcessorEntity* entity :
@@ -996,6 +1007,7 @@
     const std::string& storage_key,
     MetadataChangeList* metadata_change_list) {
   DCHECK(!storage_key.empty());
+  DCHECK(entity_tracker_);
   DCHECK(entity_tracker_->GetEntityForStorageKey(storage_key));
   metadata_change_list->ClearMetadata(storage_key);
   entity_tracker_->RemoveEntityForStorageKey(storage_key);
@@ -1011,7 +1023,7 @@
       break;
     case CLEAR_METADATA:
       model_ready_to_sync_ = false;
-      entity_tracker_ = std::make_unique<ProcessorEntityTracker>(type_);
+      entity_tracker_.reset();
       break;
   }
 
@@ -1083,18 +1095,77 @@
   std::move(callback).Run(type_, std::move(all_nodes));
 }
 
+void ClientTagBasedModelTypeProcessor::InitializeCacheGuidForOldClients() {
+  DCHECK(entity_tracker_);
+
+  // TODO(rushans): remove this logic because cache GUID must have been
+  // initialized for most of clients. Clients that have not initialized yet will
+  // behave as in cache GUID mismatch situation.
+  if (!entity_tracker_->model_type_state().has_cache_guid()) {
+    sync_pb::ModelTypeState model_type_state =
+        entity_tracker_->model_type_state();
+    model_type_state.set_cache_guid(activation_request_.cache_guid);
+    entity_tracker_->set_model_type_state(model_type_state);
+  }
+}
+
+void ClientTagBasedModelTypeProcessor::CheckForInvalidPersistedMetadata() {
+  if (!entity_tracker_) {
+    return;
+  }
+
+  InitializeCacheGuidForOldClients();
+
+  const sync_pb::ModelTypeState& model_type_state =
+      entity_tracker_->model_type_state();
+  const bool invalid_cache_guid =
+      model_type_state.cache_guid() != activation_request_.cache_guid;
+  const bool invalid_data_type_id =
+      model_type_state.progress_marker().data_type_id() !=
+      GetSpecificsFieldNumberFromModelType(type_);
+  const bool invalid_authenticated_account_id =
+      model_type_state.authenticated_account_id() !=
+      activation_request_.authenticated_account_id.ToString();
+  // Check for invalid persisted metadata.
+  // TODO(rushans): add UMA for each case of inconsistent data.
+  if (!invalid_cache_guid && !invalid_data_type_id &&
+      !invalid_authenticated_account_id) {
+    return;
+  }
+  // There is a mismatch between the cache guid or the data type id stored
+  // in |model_type_state_| and the one received from sync. This indicates
+  // that the stored metadata are invalid (e.g. has been manipulated) and
+  // don't belong to the current syncing client.
+  if (model_type_state.progress_marker().data_type_id() !=
+      GetSpecificsFieldNumberFromModelType(type_)) {
+    UMA_HISTOGRAM_ENUMERATION("Sync.PersistedModelTypeIdMismatch",
+                              ModelTypeHistogramValue(type_));
+  }
+  ClearMetadataAndResetState();
+  // The model is still ready to sync (with the same |bridge_|) - replay
+  // the initialization.
+  model_ready_to_sync_ = true;
+  // Notify the bridge sync is starting to simulate an enable event.
+  bridge_->OnSyncStarting(activation_request_);
+  DCHECK(!entity_tracker_);
+}
+
 void ClientTagBasedModelTypeProcessor::GetStatusCountersForDebugging(
     StatusCountersCallback callback) {
   StatusCounters counters;
-  counters.num_entries_and_tombstones = entity_tracker_->size();
-  counters.num_entries = entity_tracker_->CountNonTombstoneEntries();
+  if (entity_tracker_) {
+    counters.num_entries_and_tombstones = entity_tracker_->size();
+    counters.num_entries = entity_tracker_->CountNonTombstoneEntries();
+  }
   std::move(callback).Run(type_, counters);
 }
 
 void ClientTagBasedModelTypeProcessor::RecordMemoryUsageAndCountsHistograms() {
   SyncRecordModelTypeMemoryHistogram(type_, EstimateMemoryUsage());
-  SyncRecordModelTypeCountHistogram(
-      type_, entity_tracker_->CountNonTombstoneEntries());
+  const size_t non_tombstone_entries_count =
+      entity_tracker_ == nullptr ? 0
+                                 : entity_tracker_->CountNonTombstoneEntries();
+  SyncRecordModelTypeCountHistogram(type_, non_tombstone_entries_count);
 }
 
 }  // namespace syncer
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor.h b/components/sync/model_impl/client_tag_based_model_type_processor.h
index 99a423a..7b9d788a 100644
--- a/components/sync/model_impl/client_tag_based_model_type_processor.h
+++ b/components/sync/model_impl/client_tag_based_model_type_processor.h
@@ -210,6 +210,13 @@
   void MergeDataWithMetadataForDebugging(AllNodesCallback callback,
                                          std::unique_ptr<DataBatch> batch);
 
+  // Initialize the cache_guid for old clients that didn't persist it.
+  void InitializeCacheGuidForOldClients();
+
+  // Checks for valid cache GUID and data type id. Resets state if metadata is
+  // invalid.
+  void CheckForInvalidPersistedMetadata();
+
   /////////////////////
   // Processor state //
   /////////////////////
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc b/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
index b5cb240..a1c7404 100644
--- a/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
+++ b/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
@@ -38,6 +38,8 @@
 
 namespace {
 
+const char kDefaultAuthenticatedAccountId[] = "DefaultAccountId";
+
 const char kKey1[] = "key1";
 const char kKey2[] = "key2";
 const char kKey3[] = "key3";
@@ -133,6 +135,8 @@
     model_type_state.set_initial_sync_done(is_done);
     model_type_state.mutable_progress_marker()->set_data_type_id(
         GetSpecificsFieldNumberFromModelType(model_type_));
+    model_type_state.set_authenticated_account_id(
+        kDefaultAuthenticatedAccountId);
     db_->set_model_type_state(model_type_state);
   }
 
@@ -272,10 +276,10 @@
 
   void OnCommitDataLoaded() { bridge()->OnCommitDataLoaded(); }
 
-  void OnSyncStarting(
-      const std::string& authenticated_account_id = "SomeAccountId",
-      const std::string& cache_guid = "TestCacheGuid",
-      SyncMode sync_mode = SyncMode::kFull) {
+  void OnSyncStarting(const std::string& authenticated_account_id =
+                          kDefaultAuthenticatedAccountId,
+                      const std::string& cache_guid = "TestCacheGuid",
+                      SyncMode sync_mode = SyncMode::kFull) {
     DataTypeActivationRequest request;
     request.error_handler = base::BindRepeating(
         &ClientTagBasedModelTypeProcessorTest::ErrorReceived,
@@ -374,7 +378,9 @@
 
   // Return the number of entities the processor has metadata for.
   size_t ProcessorEntityCount() const {
-    return type_processor()->entity_tracker_->size();
+    if (type_processor()->entity_tracker_)
+      return type_processor()->entity_tracker_->size();
+    return 0;
   }
 
   // Expect to receive an error from the processor.
@@ -454,9 +460,10 @@
        ShouldExposeNewlyTrackedAccountId) {
   ModelReadyToSync();
   ASSERT_EQ("", type_processor()->TrackedAccountId());
-  OnSyncStarting("SomeAccountId");
+  OnSyncStarting();
   worker()->UpdateFromServer();
-  EXPECT_EQ("SomeAccountId", type_processor()->TrackedAccountId());
+  EXPECT_EQ(kDefaultAuthenticatedAccountId,
+            type_processor()->TrackedAccountId());
 }
 
 TEST_F(ClientTagBasedModelTypeProcessorTest,
@@ -482,7 +489,7 @@
        ShouldExposeNewlyTrackedCacheGuid) {
   ModelReadyToSync();
   ASSERT_EQ("", type_processor()->TrackedCacheGuid());
-  OnSyncStarting("SomeAccountId", "TestCacheGuid");
+  OnSyncStarting(kDefaultAuthenticatedAccountId, "TestCacheGuid");
   worker()->UpdateFromServer();
   EXPECT_EQ("TestCacheGuid", type_processor()->TrackedCacheGuid());
 }
@@ -493,6 +500,7 @@
   sync_pb::ModelTypeState model_type_state(metadata_batch->GetModelTypeState());
   model_type_state.set_initial_sync_done(true);
   model_type_state.set_cache_guid("PersistedCacheGuid");
+  model_type_state.set_authenticated_account_id(kDefaultAuthenticatedAccountId);
   model_type_state.mutable_progress_marker()->set_data_type_id(
       GetSpecificsFieldNumberFromModelType(GetModelType()));
   metadata_batch->SetModelTypeState(model_type_state);
@@ -502,7 +510,7 @@
   EXPECT_EQ("PersistedCacheGuid", type_processor()->TrackedCacheGuid());
 
   // If sync gets started, the cache guid should still be set.
-  OnSyncStarting("SomeAccountId", "PersistedCacheGuid");
+  OnSyncStarting(kDefaultAuthenticatedAccountId, "PersistedCacheGuid");
   EXPECT_EQ("PersistedCacheGuid", type_processor()->TrackedCacheGuid());
 }
 
@@ -1873,7 +1881,8 @@
 TEST_F(ClientTagBasedModelTypeProcessorTest,
        ShouldReportEphemeralConfigurationTime) {
   InitializeToMetadataLoaded(/*initial_sync_done=*/false);
-  OnSyncStarting("SomeAccountId", "TestCacheGuid", SyncMode::kTransportOnly);
+  OnSyncStarting(kDefaultAuthenticatedAccountId, "TestCacheGuid",
+                 SyncMode::kTransportOnly);
 
   base::HistogramTester histogram_tester;
 
@@ -1897,7 +1906,8 @@
 TEST_F(ClientTagBasedModelTypeProcessorTest,
        ShouldReportPersistentConfigurationTime) {
   InitializeToMetadataLoaded(/*initial_sync_done=*/false);
-  OnSyncStarting("SomeAccountId", "TestCacheGuid", SyncMode::kFull);
+  OnSyncStarting(kDefaultAuthenticatedAccountId, "TestCacheGuid",
+                 SyncMode::kFull);
 
   base::HistogramTester histogram_tester;
 
@@ -1966,7 +1976,8 @@
 TEST_F(FullUpdateClientTagBasedModelTypeProcessorTest,
        ShouldReportEphemeralConfigurationTimeOnlyForFirstFullUpdate) {
   InitializeToMetadataLoaded(/*initial_sync_done=*/false);
-  OnSyncStarting("SomeAccountId", "TestCacheGuid", SyncMode::kTransportOnly);
+  OnSyncStarting(kDefaultAuthenticatedAccountId, "TestCacheGuid",
+                 SyncMode::kTransportOnly);
 
   UpdateResponseDataList updates1;
   updates1.push_back(worker()->GenerateUpdateData(
@@ -2534,6 +2545,7 @@
 
 TEST_F(ClientTagBasedModelTypeProcessorTest,
        ShouldPropagateFailedCommitItemsToBridgeWhenCommitCompleted) {
+  InitializeToReadyState();
   FailedCommitResponseData response_data;
   response_data.client_tag_hash = GetHash("dummy tag");
   response_data.response_type = sync_pb::CommitResponse::TRANSIENT_ERROR;
@@ -2576,6 +2588,7 @@
 
 TEST_F(ClientTagBasedModelTypeProcessorTest,
        ShouldNotPropagateFailedCommitAttemptToBridgeWhenNoFailedItems) {
+  InitializeToReadyState();
   auto on_commit_attempt_errors_callback = base::BindOnce(
       [](const FailedCommitResponseDataList& error_response_list) {
         ADD_FAILURE()
@@ -2609,8 +2622,9 @@
        ShouldExposeNewlyTrackedAccountId) {
   ModelReadyToSync();
   ASSERT_EQ("", type_processor()->TrackedAccountId());
-  OnSyncStarting("SomeAccountId");
-  EXPECT_EQ("SomeAccountId", type_processor()->TrackedAccountId());
+  OnSyncStarting();
+  EXPECT_EQ(kDefaultAuthenticatedAccountId,
+            type_processor()->TrackedAccountId());
 }
 
 TEST_F(CommitOnlyClientTagBasedModelTypeProcessorTest,
@@ -2705,4 +2719,50 @@
   EXPECT_EQ(0U, db()->metadata_count());
 }
 
+TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldResetOnInvalidCacheGuid) {
+  ResetStateWriteItem(kKey1, kValue1);
+  InitializeToMetadataLoaded();
+  OnSyncStarting();
+  OnCommitDataLoaded();
+  ASSERT_EQ(1U, ProcessorEntityCount());
+
+  ResetStateWriteItem(kKey1, kValue1);
+  sync_pb::ModelTypeState model_type_state = db()->model_type_state();
+  model_type_state.set_cache_guid("OtherCacheGuid");
+  db()->set_model_type_state(model_type_state);
+
+  ModelReadyToSync();
+  OnSyncStarting();
+  EXPECT_EQ(0U, ProcessorEntityCount());
+}
+
+TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldResetOnInvalidDataTypeId) {
+  ResetStateWriteItem(kKey1, kValue1);
+  ASSERT_EQ(0U, ProcessorEntityCount());
+
+  // Initialize change processor, expect to load data from the bridge.
+  InitializeToMetadataLoaded();
+  OnSyncStarting();
+  OnCommitDataLoaded();
+  ASSERT_EQ(1U, ProcessorEntityCount());
+
+  ResetStateWriteItem(kKey1, kValue1);
+
+  base::HistogramTester histogram_tester;
+  OnSyncStarting();
+  // Set different data type id.
+  sync_pb::ModelTypeState model_type_state = db()->model_type_state();
+
+  ASSERT_NE(model_type_state.progress_marker().data_type_id(),
+            GetSpecificsFieldNumberFromModelType(AUTOFILL));
+  model_type_state.mutable_progress_marker()->set_data_type_id(
+      GetSpecificsFieldNumberFromModelType(AUTOFILL));
+  db()->set_model_type_state(model_type_state);
+
+  ModelReadyToSync();
+  EXPECT_EQ(0U, ProcessorEntityCount());
+  histogram_tester.ExpectUniqueSample("Sync.PersistedModelTypeIdMismatch",
+                                      ModelTypeForHistograms::kPreferences, 1);
+}
+
 }  // namespace syncer
diff --git a/components/sync/model_impl/processor_entity_tracker.cc b/components/sync/model_impl/processor_entity_tracker.cc
index ba70792b..adab2d3 100644
--- a/components/sync/model_impl/processor_entity_tracker.cc
+++ b/components/sync/model_impl/processor_entity_tracker.cc
@@ -12,14 +12,10 @@
 
 namespace syncer {
 
-ProcessorEntityTracker::ProcessorEntityTracker(ModelType type) {
-  InitializeMetadata(type);
-}
-
 ProcessorEntityTracker::ProcessorEntityTracker(
+    const sync_pb::ModelTypeState& model_type_state,
     std::map<std::string, std::unique_ptr<sync_pb::EntityMetadata>>
-        metadata_map,
-    const sync_pb::ModelTypeState& model_type_state)
+        metadata_map)
     : model_type_state_(model_type_state) {
   DCHECK(model_type_state.initial_sync_done());
   for (auto& kv : metadata_map) {
@@ -196,11 +192,6 @@
   return entities_.size();
 }
 
-void ProcessorEntityTracker::InitializeMetadata(ModelType type) {
-  model_type_state_.mutable_progress_marker()->set_data_type_id(
-      GetSpecificsFieldNumberFromModelType(type));
-}
-
 std::vector<const ProcessorEntity*>
 ProcessorEntityTracker::IncrementSequenceNumberForAllExcept(
     const std::unordered_set<std::string>& already_updated_storage_keys) {
diff --git a/components/sync/model_impl/processor_entity_tracker.h b/components/sync/model_impl/processor_entity_tracker.h
index 7c513727..01acc3a 100644
--- a/components/sync/model_impl/processor_entity_tracker.h
+++ b/components/sync/model_impl/processor_entity_tracker.h
@@ -23,15 +23,13 @@
 // This component tracks entities for ClientTagBasedModelTypeProcessor.
 class ProcessorEntityTracker {
  public:
-  explicit ProcessorEntityTracker(ModelType type);
-
   // Creates tracker and fills entities data from batch metadata map. This
   // constructor must be used only if initial_sync_done returns true in
   // |model_type_state|.
   ProcessorEntityTracker(
+      const sync_pb::ModelTypeState& model_type_state,
       std::map<std::string, std::unique_ptr<sync_pb::EntityMetadata>>
-          metadata_map,
-      const sync_pb::ModelTypeState& model_type_state);
+          metadata_map);
 
   ~ProcessorEntityTracker();
 
@@ -91,9 +89,6 @@
     model_type_state_ = model_type_state;
   }
 
-  // Sets data type id to model type state. Used for first time syncing.
-  void InitializeMetadata(ModelType type);
-
   // Returns number of entities, including tombstones.
   size_t size() const;
 
diff --git a/components/sync_sessions/session_sync_bridge_unittest.cc b/components/sync_sessions/session_sync_bridge_unittest.cc
index c14d8998..0493c00 100644
--- a/components/sync_sessions/session_sync_bridge_unittest.cc
+++ b/components/sync_sessions/session_sync_bridge_unittest.cc
@@ -228,6 +228,7 @@
     state.set_initial_sync_done(true);
     state.mutable_progress_marker()->set_data_type_id(
         GetSpecificsFieldNumberFromModelType(syncer::SESSIONS));
+    state.set_authenticated_account_id("SomeAccountId");
     syncer::UpdateResponseDataList initial_updates;
     for (const SessionSpecifics& specifics : remote_data) {
       initial_updates.push_back(SpecificsToUpdateResponse(specifics));
diff --git a/components/viz/common/resources/resource_format_utils.cc b/components/viz/common/resources/resource_format_utils.cc
index dfe9cce4b..ddaea4c 100644
--- a/components/viz/common/resources/resource_format_utils.cc
+++ b/components/viz/common/resources/resource_format_utils.cc
@@ -200,6 +200,8 @@
     return GL_RG8_EXT;
   else if (format == ETC1)
     return GL_ETC1_RGB8_OES;
+  else if (format == RGBA_1010102 || format == BGRA_1010102)
+    return GL_RGB10_A2_EXT;
 
   return GLDataFormat(format);
 }
@@ -337,6 +339,8 @@
     case R16_EXT:
     case RGBA_4444:
     case RGBA_8888:
+    case RGBA_1010102:
+    case BGRA_1010102:
     case RGBA_F16:
       return true;
     // These formats have no BufferFormat equivalent or are only used
@@ -350,8 +354,6 @@
     case RG_88:
     case RGBX_8888:
     case BGRX_8888:
-    case RGBA_1010102:
-    case BGRA_1010102:
     case YVU_420:
     case YUV_420_BIPLANAR:
     case P010:
@@ -431,7 +433,6 @@
   switch (format) {
     case BGR_565:
     case BGRX_8888:
-    case BGRA_1010102:
     case YVU_420:
     case YUV_420_BIPLANAR:
     case P010:
diff --git a/components/viz/service/display/ca_layer_overlay.cc b/components/viz/service/display/ca_layer_overlay.cc
index 4638b06..393a085 100644
--- a/components/viz/service/display/ca_layer_overlay.cc
+++ b/components/viz/service/display/ca_layer_overlay.cc
@@ -196,7 +196,8 @@
       return CA_LAYER_FAILED_QUAD_BLEND_MODE;
 
     // Early-out for invisible quads.
-    if (quad->shared_quad_state->opacity == 0.f) {
+    if (quad->shared_quad_state->opacity == 0.f ||
+        quad->visible_rect.IsEmpty()) {
       *skip = true;
       return CA_LAYER_SUCCESS;
     }
@@ -307,8 +308,15 @@
     CALayerOverlayList* ca_layer_overlays) const {
   CALayerResult result = CA_LAYER_SUCCESS;
 
-  if (quad_list.size() < kTooManyQuads)
-    ca_layer_overlays->reserve(quad_list.size());
+  size_t num_visible_quads = quad_list.size();
+  for (const auto* quad : quad_list) {
+    if (quad->shared_quad_state->opacity == 0.f ||
+        quad->visible_rect.IsEmpty()) {
+      num_visible_quads--;
+    }
+  }
+  if (num_visible_quads < kTooManyQuads)
+    ca_layer_overlays->reserve(num_visible_quads);
   else
     result = CA_LAYER_FAILED_TOO_MANY_QUADS;
 
diff --git a/components/viz/service/display/overlay_ca_unittest.cc b/components/viz/service/display/overlay_ca_unittest.cc
index f4adf98..4be3865 100644
--- a/components/viz/service/display/overlay_ca_unittest.cc
+++ b/components/viz/service/display/overlay_ca_unittest.cc
@@ -382,6 +382,28 @@
   EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
 }
 
+TEST_F(CALayerOverlayTest, SkipNonVisible) {
+  std::unique_ptr<RenderPass> pass = CreateRenderPass();
+  CreateFullscreenCandidateQuad(
+      resource_provider_.get(), child_resource_provider_.get(),
+      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+  pass->quad_list.back()->visible_rect.set_size(gfx::Size());
+
+  gfx::Rect damage_rect;
+  CALayerOverlayList ca_layer_list;
+  OverlayProcessorInterface::FilterOperationsMap render_pass_filters;
+  OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters;
+  RenderPassList pass_list;
+  pass_list.push_back(std::move(pass));
+  overlay_processor_->ProcessForOverlays(
+      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &ca_layer_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(gfx::Rect(), damage_rect);
+  EXPECT_EQ(0U, ca_layer_list.size());
+  EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
+}
+
 class CALayerOverlayRPDQTest : public CALayerOverlayTest {
  protected:
   void SetUp() override {
@@ -501,8 +523,10 @@
 TEST_F(CALayerOverlayRPDQTest, TooManyRenderPassDrawQuads) {
   filters_.Append(cc::FilterOperation::CreateBlurFilter(0.8f));
   int count = 35;
-
-  for (int i = 0; i < count; ++i) {
+  quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
+                kOverlayRect, render_pass_id_, 2, gfx::RectF(), gfx::Size(),
+                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
+  for (int i = 1; i < count; ++i) {
     auto* quad = pass_->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
     quad->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
                  kOverlayRect, render_pass_id_, 2, gfx::RectF(), gfx::Size(),
diff --git a/content/browser/appcache/appcache.cc b/content/browser/appcache/appcache.cc
index 52f9bcd1..f823298 100644
--- a/content/browser/appcache/appcache.cc
+++ b/content/browser/appcache/appcache.cc
@@ -341,7 +341,6 @@
     info.response_size = pair.second.response_size();
     info.padding_size = pair.second.padding_size();
     info.response_id = pair.second.response_id();
-    info.token_expires = pair.second.token_expires();
   }
 }
 
diff --git a/content/browser/appcache/appcache_internals_ui.cc b/content/browser/appcache/appcache_internals_ui.cc
index 7e9f65c..aae37ef 100644
--- a/content/browser/appcache/appcache_internals_ui.cc
+++ b/content/browser/appcache/appcache_internals_ui.cc
@@ -146,7 +146,6 @@
   dict->SetBoolean("isFallback", resource_info.is_fallback);
   dict->SetBoolean("isIntercept", resource_info.is_intercept);
   dict->SetBoolean("isForeign", resource_info.is_foreign);
-  dict->SetDouble("tokenExpires", resource_info.token_expires.ToJsTime());
 
   return dict;
 }
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
index 2cc94e3..113086e 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -615,6 +615,10 @@
       base::BindRepeating(&RenderFrameHostImpl::BindRestrictedCookieManager,
                           base::Unretained(host)));
 
+  map->Add<network::mojom::HasTrustTokensAnswerer>(
+      base::BindRepeating(&RenderFrameHostImpl::BindHasTrustTokensAnswerer,
+                          base::Unretained(host)));
+
   map->Add<shape_detection::mojom::BarcodeDetectionProvider>(
       base::BindRepeating(&BindBarcodeDetectionProvider));
 
diff --git a/content/browser/display_cutout/display_cutout_host_impl.cc b/content/browser/display_cutout/display_cutout_host_impl.cc
index 7a266559..e8d64f7 100644
--- a/content/browser/display_cutout/display_cutout_host_impl.cc
+++ b/content/browser/display_cutout/display_cutout_host_impl.cc
@@ -71,9 +71,7 @@
   // If we finish a main frame navigation and the |WebDisplayMode| is
   // fullscreen then we should make the main frame the current
   // |RenderFrameHost|.
-  RenderWidgetHostImpl* rwh =
-      web_contents_impl_->GetRenderViewHost()->GetWidget();
-  blink::mojom::DisplayMode mode = web_contents_impl_->GetDisplayMode(rwh);
+  blink::mojom::DisplayMode mode = web_contents_impl_->GetDisplayMode();
   if (mode == blink::mojom::DisplayMode::kFullscreen)
     SetCurrentRenderFrameHost(web_contents_impl_->GetMainFrame());
 }
diff --git a/content/browser/frame_host/back_forward_cache_impl.cc b/content/browser/frame_host/back_forward_cache_impl.cc
index 9ce2816..ff7a2122 100644
--- a/content/browser/frame_host/back_forward_cache_impl.cc
+++ b/content/browser/frame_host/back_forward_cache_impl.cc
@@ -149,8 +149,7 @@
       FeatureToBit(WebSchedulerTrackedFeature::kWakeLock) |
       FeatureToBit(WebSchedulerTrackedFeature::kWebShare) |
       FeatureToBit(WebSchedulerTrackedFeature::kWebFileSystem) |
-      FeatureToBit(WebSchedulerTrackedFeature::kAppBanner) |
-      FeatureToBit(WebSchedulerTrackedFeature::kPrinting);
+      FeatureToBit(WebSchedulerTrackedFeature::kAppBanner);
 
   uint64_t result = kAlwaysDisallowedFeatures;
 
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 8e11789f..ed32810 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -188,6 +188,7 @@
 #include "services/device/public/mojom/wake_lock.mojom.h"
 #include "services/device/public/mojom/wake_lock_context.mojom.h"
 #include "services/network/public/cpp/features.h"
+#include "services/network/public/cpp/is_potentially_trustworthy.h"
 #include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
 #include "services/network/public/mojom/network_service.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
@@ -6978,6 +6979,44 @@
       std::move(receiver));
 }
 
+void RenderFrameHostImpl::BindHasTrustTokensAnswerer(
+    mojo::PendingReceiver<network::mojom::HasTrustTokensAnswerer> receiver) {
+  auto top_frame_origin = ComputeTopFrameOrigin(GetLastCommittedOrigin());
+
+  // A check at the callsite in the renderer ensures a correctly functioning
+  // renderer will only request this Mojo handle if the top-frame origin is
+  // potentially trustworthy and has scheme HTTP or HTTPS.
+  if ((top_frame_origin.scheme() != url::kHttpScheme &&
+       top_frame_origin.scheme() != url::kHttpsScheme) ||
+      !network::IsOriginPotentiallyTrustworthy(top_frame_origin)) {
+    mojo::ReportBadMessage(
+        "Attempted to get a HasTrustTokensAnswerer for a non-trustworthy or "
+        "non-HTTP/HTTPS top-frame origin.");
+    return;
+  }
+
+  // This is enforced in benign renderers by the [SecureContext] IDL
+  // attribute on Document::hasTrustToken.
+  if (!network::IsOriginPotentiallyTrustworthy(GetLastCommittedOrigin())) {
+    mojo::ReportBadMessage(
+        "Attempted to get a HasTrustTokensAnswerer from an insecure context.");
+    return;
+  }
+
+  // This is enforced in benign renderers by the RuntimeEnabled=TrustTokens IDL
+  // attribute (the base::Feature's value is tied to the
+  // RuntimeEnabledFeature's).
+  if (!base::FeatureList::IsEnabled(network::features::kTrustTokens)) {
+    mojo::ReportBadMessage(
+        "Attempted to get a HasTrustTokensAnswerer with Trust Tokens "
+        "disabled.");
+    return;
+  }
+
+  GetProcess()->GetStoragePartition()->CreateHasTrustTokensAnswerer(
+      std::move(receiver), ComputeTopFrameOrigin(GetLastCommittedOrigin()));
+}
+
 void RenderFrameHostImpl::GetInterface(
     const std::string& interface_name,
     mojo::ScopedMessagePipeHandle interface_pipe) {
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 78614f4..b222465 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -83,6 +83,7 @@
 #include "services/network/public/cpp/content_security_policy/csp_context.h"
 #include "services/network/public/cpp/cross_origin_embedder_policy.h"
 #include "services/network/public/mojom/network_context.mojom.h"
+#include "services/network/public/mojom/trust_tokens.mojom.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/public/mojom/interface_provider.mojom.h"
 #include "services/viz/public/mojom/hit_test/input_target_client.mojom.h"
@@ -1238,6 +1239,18 @@
   void BindRestrictedCookieManager(
       mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver);
 
+  // Requires the following preconditions, reporting a bad message otherwise.
+  //
+  // 1. This frame's top-frame origin must be potentially trustworthy and
+  // have scheme HTTP or HTTPS. (See network::SuitableTrustTokenOrigin's class
+  // comment for the rationale.)
+  //
+  // 2. Trust Tokens must be enabled (network::features::kTrustTokens).
+  //
+  // 3. This frame's origin must be potentially trustworthy.
+  void BindHasTrustTokensAnswerer(
+      mojo::PendingReceiver<network::mojom::HasTrustTokensAnswerer> receiver);
+
   // Creates connections to WebUSB interfaces bound to this frame.
   void CreateWebUsbService(
       mojo::PendingReceiver<blink::mojom::WebUsbService> receiver);
diff --git a/content/browser/renderer_host/render_widget_host_delegate.cc b/content/browser/renderer_host/render_widget_host_delegate.cc
index 7c12613..3f91d5f 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.cc
+++ b/content/browser/renderer_host/render_widget_host_delegate.cc
@@ -87,8 +87,7 @@
   return false;
 }
 
-blink::mojom::DisplayMode RenderWidgetHostDelegate::GetDisplayMode(
-    RenderWidgetHostImpl* render_widget_host) const {
+blink::mojom::DisplayMode RenderWidgetHostDelegate::GetDisplayMode() const {
   return blink::mojom::DisplayMode::kBrowser;
 }
 
diff --git a/content/browser/renderer_host/render_widget_host_delegate.h b/content/browser/renderer_host/render_widget_host_delegate.h
index ec91ee51..3874e6f 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.h
+++ b/content/browser/renderer_host/render_widget_host_delegate.h
@@ -218,9 +218,9 @@
   // solid color is displayed instead.
   virtual bool ShouldShowStaleContentOnEviction();
 
-  // Returns the display mode for the view.
-  virtual blink::mojom::DisplayMode GetDisplayMode(
-      RenderWidgetHostImpl* render_widget_host) const;
+  // Returns the display mode for all widgets in the frame tree. Only applies
+  // to frame-based widgets. Other widgets are always kBrowser.
+  virtual blink::mojom::DisplayMode GetDisplayMode() const;
 
   // Notification that the widget has lost capture.
   virtual void LostCapture(RenderWidgetHostImpl* render_widget_host) {}
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 5ad66a6a..55e1970 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -781,13 +781,23 @@
   // which are to be sent to the renderer process.
   DCHECK(view_);
 
+  // Differentiate between widgets for frames vs widgets for popups/pepper.
+  // Historically this was done by finding the RenderViewHost for the widget,
+  // but a child local root would not convert to a RenderViewHost but is for a
+  // frame.
+  const bool is_frame_widget = owner_delegate_ || owned_by_render_frame_host_;
+
   VisualProperties visual_properties;
 
   GetScreenInfo(&visual_properties.screen_info);
 
   visual_properties.is_fullscreen_granted =
       delegate_->IsFullscreenForCurrentTab();
-  visual_properties.display_mode = delegate_->GetDisplayMode(this);
+
+  if (is_frame_widget)
+    visual_properties.display_mode = delegate_->GetDisplayMode();
+  else
+    visual_properties.display_mode = blink::mojom::DisplayMode::kBrowser;
   visual_properties.zoom_level = delegate_->GetPendingPageZoomLevel();
 
   RenderViewHostDelegateView* rvh_delegate_view = delegate_->GetDelegateView();
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc b/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc
index 8ba0170..dcd2a41 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc
@@ -291,7 +291,7 @@
     EXPECT_EQ(initial_size, child_visible_viewport_size);
   }
 
-  // Same check as abvoe but for a nested WebContents.
+  // Same check as above but for a nested WebContents.
   {
     base::RunLoop loop;
 
@@ -496,4 +496,101 @@
                .size() != 1);
 }
 
+// Auto-resize is only implemented for Ash and GuestViews. So we need to inject
+// an implementation that actually resizes the top level widget.
+class DisplayModeControllingWebContentsDelegate : public WebContentsDelegate {
+ public:
+  blink::mojom::DisplayMode GetDisplayMode(
+      const WebContents* web_contents) override {
+    return mode_;
+  }
+
+  void set_display_mode(blink::mojom::DisplayMode mode) { mode_ = mode; }
+
+ private:
+  // The is the default value throughout the browser and renderer.
+  blink::mojom::DisplayMode mode_ = blink::mojom::DisplayMode::kBrowser;
+};
+
+// TODO(crbug.com/1060336): Unlike most VisualProperties, the DisplayMode does
+// not propagate down the tree of RenderWidgets, but is sent independently to
+// each RenderWidget.
+IN_PROC_BROWSER_TEST_F(RenderWidgetHostViewChildFrameBrowserTest,
+                       VisualPropertiesPropagation_DisplayMode) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(a,b)"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  auto* web_contents = static_cast<WebContentsImpl*>(shell()->web_contents());
+
+  DisplayModeControllingWebContentsDelegate display_mode_delegate;
+  shell()->web_contents()->SetDelegate(&display_mode_delegate);
+
+  // Main frame.
+  FrameTreeNode* root = web_contents->GetFrameTree()->root();
+  RenderWidgetHostImpl* root_widget =
+      root->current_frame_host()->GetRenderWidgetHost();
+  // In-process frame.
+  FrameTreeNode* ipchild = root->child_at(0);
+  RenderWidgetHostImpl* ipchild_widget =
+      ipchild->current_frame_host()->GetRenderWidgetHost();
+  // Out-of-process frame.
+  FrameTreeNode* oopchild = root->child_at(1);
+  RenderWidgetHostImpl* oopchild_widget =
+      oopchild->current_frame_host()->GetRenderWidgetHost();
+
+  // Check all frames for the initial value.
+  EXPECT_EQ(
+      true,
+      EvalJs(root, "window.matchMedia('(display-mode: browser)').matches"));
+  EXPECT_EQ(
+      true,
+      EvalJs(ipchild, "window.matchMedia('(display-mode: browser)').matches"));
+  EXPECT_EQ(
+      true,
+      EvalJs(oopchild, "window.matchMedia('(display-mode: browser)').matches"));
+
+  // The display mode changes.
+  display_mode_delegate.set_display_mode(
+      blink::mojom::DisplayMode::kStandalone);
+  // Each RenderWidgetHost would need to hear about that by having
+  // SynchronizeVisualProperties() called. It's not clear what triggers that but
+  // the place that changes the DisplayMode would be responsible.
+  root_widget->SynchronizeVisualProperties();
+  ipchild_widget->SynchronizeVisualProperties();
+  oopchild_widget->SynchronizeVisualProperties();
+
+  // Check all frames for the changed value.
+  EXPECT_EQ(
+      true,
+      EvalJsAfterLifecycleUpdate(
+          root, "", "window.matchMedia('(display-mode: standalone)').matches"));
+  EXPECT_EQ(true,
+            EvalJsAfterLifecycleUpdate(
+                ipchild, "",
+                "window.matchMedia('(display-mode: standalone)').matches"));
+  EXPECT_EQ(true,
+            EvalJsAfterLifecycleUpdate(
+                oopchild, "",
+                "window.matchMedia('(display-mode: standalone)').matches"));
+
+  // Navigate a frame to b.com, which we already have a process for.
+  GURL same_site_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
+  NavigateFrameToURL(root->child_at(0), same_site_url);
+
+  // The navigated frame sees the correct (non-default) value.
+  EXPECT_EQ(true,
+            EvalJs(root->child_at(0),
+                   "window.matchMedia('(display-mode: standalone)').matches"));
+
+  // Navigate the frame to c.com, which we don't have a process for.
+  GURL cross_site_url(embedded_test_server()->GetURL("c.com", "/title2.html"));
+  NavigateFrameToURL(root->child_at(0), cross_site_url);
+
+  // The navigated frame sees the correct (non-default) value.
+  EXPECT_EQ(true,
+            EvalJs(root->child_at(0),
+                   "window.matchMedia('(display-mode: standalone)').matches"));
+}
+
 }  // namespace content
diff --git a/content/browser/resources/appcache/appcache_internals.css b/content/browser/resources/appcache/appcache_internals.css
index eb3908a..2daa6f5 100644
--- a/content/browser/resources/appcache/appcache_internals.css
+++ b/content/browser/resources/appcache/appcache_internals.css
@@ -86,7 +86,3 @@
 .appcache-details td {
   width: 80px;
 }
-
-.appcache-details td.token-expires {
-  width: 500px;
-}
diff --git a/content/browser/resources/appcache/appcache_internals.html b/content/browser/resources/appcache/appcache_internals.html
index 951164f..f8ab596 100644
--- a/content/browser/resources/appcache/appcache_internals.html
+++ b/content/browser/resources/appcache/appcache_internals.html
@@ -104,7 +104,6 @@
             <th>Response</th>
             <th>Padding</th>
             <th>Properties</th>
-            <th>Token Expires</th>
           </tr>
         </thead>
         <tbody class="appcache-details"
@@ -119,9 +118,6 @@
             <td jscontent="responseSize"></td>
             <td jscontent="paddingSize"></td>
             <td jscontent="properties"></td>
-            <td class="token-expires" jscontent=
-              "tokenExpires ? new Date(tokenExpires) : '(unset)'">
-            </td>
           </tr>
         </tbody>
       </table>
diff --git a/content/browser/resources/appcache/appcache_internals.js b/content/browser/resources/appcache/appcache_internals.js
index 12eccae9..0003fe0 100644
--- a/content/browser/resources/appcache/appcache_internals.js
+++ b/content/browser/resources/appcache/appcache_internals.js
@@ -174,7 +174,6 @@
         properties: properties,
         fileUrl: details.url,
         responseId: details.responseId,
-        tokenExpires: details.tokenExpires,
       });
     }
     return simpleVector;
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index c8224c0..19fe65c1 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -97,6 +97,7 @@
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/mojom/cookie_manager.mojom.h"
 #include "services/network/public/mojom/network_context.mojom.h"
+#include "services/network/public/mojom/trust_tokens.mojom.h"
 #include "storage/browser/blob/blob_registry_impl.h"
 #include "storage/browser/blob/blob_storage_context.h"
 #include "storage/browser/database/database_tracker.h"
@@ -1669,6 +1670,14 @@
   }
 }
 
+void StoragePartitionImpl::CreateHasTrustTokensAnswerer(
+    mojo::PendingReceiver<network::mojom::HasTrustTokensAnswerer> receiver,
+    const url::Origin& top_frame_origin) {
+  DCHECK(initialized_);
+  GetNetworkContext()->GetHasTrustTokensAnswerer(std::move(receiver),
+                                                 top_frame_origin);
+}
+
 storage::QuotaManager* StoragePartitionImpl::GetQuotaManager() {
   DCHECK(initialized_);
   return quota_manager_.get();
diff --git a/content/browser/storage_partition_impl.h b/content/browser/storage_partition_impl.h
index eedad24..5bc9275 100644
--- a/content/browser/storage_partition_impl.h
+++ b/content/browser/storage_partition_impl.h
@@ -131,6 +131,9 @@
       int routing_id,
       mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver)
       override;
+  void CreateHasTrustTokensAnswerer(
+      mojo::PendingReceiver<network::mojom::HasTrustTokensAnswerer> receiver,
+      const url::Origin& top_frame_origin) override;
   storage::QuotaManager* GetQuotaManager() override;
   ChromeAppCacheService* GetAppCacheService() override;
   BackgroundSyncContextImpl* GetBackgroundSyncContext() override;
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 9f596277..f59b9b6 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -2687,11 +2687,7 @@
   return IsFullscreenForCurrentTab();
 }
 
-blink::mojom::DisplayMode WebContentsImpl::GetDisplayMode(
-    RenderWidgetHostImpl* render_widget_host) const {
-  if (!RenderViewHostImpl::From(render_widget_host))
-    return blink::mojom::DisplayMode::kBrowser;
-
+blink::mojom::DisplayMode WebContentsImpl::GetDisplayMode() const {
   return delegate_ ? delegate_->GetDisplayMode(this)
                    : blink::mojom::DisplayMode::kBrowser;
 }
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 05254ab..49ed5dc 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -888,8 +888,7 @@
   RenderWidgetHostImpl* GetKeyboardLockWidget() override;
   // The following function is already listed under WebContents overrides:
   // bool IsFullscreenForCurrentTab() const override;
-  blink::mojom::DisplayMode GetDisplayMode(
-      RenderWidgetHostImpl* render_widget_host) const override;
+  blink::mojom::DisplayMode GetDisplayMode() const override;
   void LostCapture(RenderWidgetHostImpl* render_widget_host) override;
   void LostMouseLock(RenderWidgetHostImpl* render_widget_host) override;
   bool HasMouseLock(RenderWidgetHostImpl* render_widget_host) override;
diff --git a/content/public/browser/storage_partition.h b/content/public/browser/storage_partition.h
index 022b6373..91d1b77 100644
--- a/content/public/browser/storage_partition.h
+++ b/content/public/browser/storage_partition.h
@@ -18,6 +18,7 @@
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/mojom/cookie_manager.mojom-forward.h"
 #include "services/network/public/mojom/restricted_cookie_manager.mojom-forward.h"
+#include "services/network/public/mojom/trust_tokens.mojom-forward.h"
 
 class GURL;
 
@@ -120,6 +121,10 @@
       mojo::PendingReceiver<network::mojom::RestrictedCookieManager>
           receiver) = 0;
 
+  virtual void CreateHasTrustTokensAnswerer(
+      mojo::PendingReceiver<network::mojom::HasTrustTokensAnswerer> receiver,
+      const url::Origin& top_frame_origin) = 0;
+
   virtual storage::QuotaManager* GetQuotaManager() = 0;
   virtual AppCacheService* GetAppCacheService() = 0;
   virtual BackgroundSyncContext* GetBackgroundSyncContext() = 0;
diff --git a/content/public/test/test_storage_partition.cc b/content/public/test/test_storage_partition.cc
index 42cc3309..f08684d 100644
--- a/content/public/test/test_storage_partition.cc
+++ b/content/public/test/test_storage_partition.cc
@@ -53,6 +53,12 @@
   NOTREACHED();
 }
 
+void TestStoragePartition::CreateHasTrustTokensAnswerer(
+    mojo::PendingReceiver<network::mojom::HasTrustTokensAnswerer> receiver,
+    const url::Origin& top_frame_origin) {
+  NOTREACHED() << "Not implemented.";
+}
+
 storage::QuotaManager* TestStoragePartition::GetQuotaManager() {
   return quota_manager_;
 }
diff --git a/content/public/test/test_storage_partition.h b/content/public/test/test_storage_partition.h
index 85c0b9a..3a04824 100644
--- a/content/public/test/test_storage_partition.h
+++ b/content/public/test/test_storage_partition.h
@@ -75,6 +75,10 @@
       mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver)
       override;
 
+  void CreateHasTrustTokensAnswerer(
+      mojo::PendingReceiver<network::mojom::HasTrustTokensAnswerer> receiver,
+      const url::Origin& top_frame_origin) override;
+
   void set_quota_manager(storage::QuotaManager* manager) {
     quota_manager_ = manager;
   }
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index f18664d..91444f942 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -1399,7 +1399,6 @@
 
   std::unique_ptr<RenderWidget> render_widget = RenderWidget::CreateForFrame(
       params->main_frame_widget_routing_id, compositor_deps,
-      params->visual_properties.display_mode,
       render_view->widgets_never_composited());
   render_widget->set_delegate(render_view);
 
@@ -1563,7 +1562,6 @@
 
     std::unique_ptr<RenderWidget> render_widget = RenderWidget::CreateForFrame(
         widget_params->routing_id, compositor_deps,
-        widget_params->visual_properties.display_mode,
         render_view->widgets_never_composited());
     render_widget->set_delegate(render_view);
 
@@ -1613,7 +1611,6 @@
     // space/context.
     std::unique_ptr<RenderWidget> render_widget = RenderWidget::CreateForFrame(
         widget_params->routing_id, compositor_deps,
-        widget_params->visual_properties.display_mode,
         render_view->widgets_never_composited());
 
     // Non-owning pointer that is self-referencing and destroyed by calling
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index f0cb483..c0f5ce15 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -454,10 +454,6 @@
 
   bool local_main_frame = params->main_frame_routing_id != MSG_ROUTING_NONE;
 
-  // TODO(danakj): Put this in with making the RenderFrame? Does order matter?
-  if (local_main_frame)
-    GetWebView()->SetDisplayMode(params->visual_properties.display_mode);
-
   ApplyWebPreferences(webkit_preferences_, GetWebView());
   ApplyCommandLineToSettings(GetWebView()->GetSettings());
 
@@ -1048,11 +1044,6 @@
   return false;
 }
 
-void RenderViewImpl::ApplyNewDisplayModeForWidget(
-    blink::mojom::DisplayMode new_display_mode) {
-  GetWebView()->SetDisplayMode(new_display_mode);
-}
-
 void RenderViewImpl::ApplyAutoResizeLimitsForWidget(const gfx::Size& min_size,
                                                     const gfx::Size& max_size) {
   GetWebView()->EnableAutoResizeMode(min_size, max_size);
@@ -1386,7 +1377,6 @@
 
   RenderWidget* popup_widget = RenderWidget::CreateForPopup(
       widget_routing_id, opener_render_widget->compositor_deps(),
-      blink::mojom::DisplayMode::kUndefined,
       /*hidden=*/false,
       /*never_composited=*/false, std::move(widget_channel_receiver));
 
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index 701aac4..75becca2 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -368,8 +368,6 @@
   void SetActiveForWidget(bool active) override;
   bool SupportsMultipleWindowsForWidget() override;
   bool ShouldAckSyntheticInputImmediately() override;
-  void ApplyNewDisplayModeForWidget(
-      blink::mojom::DisplayMode new_display_mode) override;
   void ApplyAutoResizeLimitsForWidget(const gfx::Size& min_size,
                                       const gfx::Size& max_size) override;
   void DisableAutoResizeForWidget() override;
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index f461c4e6..fb0a29d8 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -391,33 +391,30 @@
 std::unique_ptr<RenderWidget> RenderWidget::CreateForFrame(
     int32_t widget_routing_id,
     CompositorDependencies* compositor_deps,
-    blink::mojom::DisplayMode display_mode,
     bool never_composited) {
   if (g_create_render_widget_for_frame) {
     return g_create_render_widget_for_frame(widget_routing_id, compositor_deps,
-                                            display_mode, never_composited,
+                                            never_composited,
                                             mojo::NullReceiver());
   }
 
-  return std::make_unique<RenderWidget>(
-      widget_routing_id, compositor_deps, display_mode,
-      /*hidden=*/true, never_composited, mojo::NullReceiver());
+  return std::make_unique<RenderWidget>(widget_routing_id, compositor_deps,
+                                        /*hidden=*/true, never_composited,
+                                        mojo::NullReceiver());
 }
 
 RenderWidget* RenderWidget::CreateForPopup(
     int32_t widget_routing_id,
     CompositorDependencies* compositor_deps,
-    blink::mojom::DisplayMode display_mode,
     bool hidden,
     bool never_composited,
     mojo::PendingReceiver<mojom::Widget> widget_receiver) {
-  return new RenderWidget(widget_routing_id, compositor_deps, display_mode,
-                          hidden, never_composited, std::move(widget_receiver));
+  return new RenderWidget(widget_routing_id, compositor_deps, hidden,
+                          never_composited, std::move(widget_receiver));
 }
 
 RenderWidget::RenderWidget(int32_t widget_routing_id,
                            CompositorDependencies* compositor_deps,
-                           blink::mojom::DisplayMode display_mode,
                            bool hidden,
                            bool never_composited,
                            mojo::PendingReceiver<mojom::Widget> widget_receiver)
@@ -425,7 +422,6 @@
       compositor_deps_(compositor_deps),
       is_hidden_(hidden),
       never_composited_(never_composited),
-      display_mode_(display_mode),
       next_previous_flags_(kInvalidNextPreviousFlagsValue),
       frame_swap_message_queue_(new FrameSwapMessageQueue(routing_id_)),
       widget_receiver_(this, std::move(widget_receiver)) {
@@ -730,6 +726,13 @@
           visual_properties.screen_info.color_space);
   }
 
+  // TODO(danakj): In order to synchronize updates between local roots, the
+  // display mode should be propagated to RenderFrameProxies and down through
+  // their RenderWidgetHosts to child RenderWidgets via the VisualProperties
+  // waterfall, instead of coming to each RenderWidget independently.
+  // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/display-mode
+  GetWebWidget()->SetDisplayMode(visual_properties.display_mode);
+
   if (delegate()) {
     if (size_ != visual_properties.new_size) {
       // Only hide popups when the size changes. Eg https://crbug.com/761908.
@@ -737,11 +740,6 @@
       web_view->CancelPagePopup();
     }
 
-    if (display_mode_ != visual_properties.display_mode) {
-      display_mode_ = visual_properties.display_mode;
-      delegate()->ApplyNewDisplayModeForWidget(visual_properties.display_mode);
-    }
-
     SetAutoResizeMode(visual_properties.auto_resize_enabled,
                       visual_properties.min_size_for_auto_resize,
                       visual_properties.max_size_for_auto_resize,
@@ -813,7 +811,6 @@
       // TODO(danakj): Isn't the display mode check redundant with the
       // fullscreen one?
       if (visual_properties.is_fullscreen_granted != is_fullscreen_granted_ ||
-          visual_properties.display_mode != display_mode_ ||
           visual_properties.screen_info.device_scale_factor !=
               screen_info_.device_scale_factor)
         ignore_resize_ipc = false;
@@ -859,7 +856,6 @@
       visible_viewport_size_ = visual_properties.visible_viewport_size;
 
       if (!auto_resize_mode_) {
-        display_mode_ = visual_properties.display_mode;
         size_ = visual_properties.new_size;
         ResizeWebWidget();
       }
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index a17e429d..b48c2e66 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -53,7 +53,6 @@
 #include "ppapi/buildflags/buildflags.h"
 #include "services/network/public/mojom/referrer_policy.mojom.h"
 #include "third_party/blink/public/common/input/web_input_event.h"
-#include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
 #include "third_party/blink/public/platform/viewport_intersection_state.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_text_input_info.h"
@@ -153,7 +152,6 @@
  public:
   RenderWidget(int32_t widget_routing_id,
                CompositorDependencies* compositor_deps,
-               blink::mojom::DisplayMode display_mode,
                bool hidden,
                bool never_composited,
                mojo::PendingReceiver<mojom::Widget> widget_receiver);
@@ -176,7 +174,6 @@
   using CreateRenderWidgetFunction = std::unique_ptr<RenderWidget> (*)(
       int32_t,
       CompositorDependencies*,
-      blink::mojom::DisplayMode display_mode,
       bool never_composited,
       mojo::PendingReceiver<mojom::Widget> widget_receiver);
   // Overrides the implementation of CreateForFrame() function below. Used by
@@ -190,7 +187,6 @@
   static std::unique_ptr<RenderWidget> CreateForFrame(
       int32_t widget_routing_id,
       CompositorDependencies* compositor_deps,
-      blink::mojom::DisplayMode display_mode,
       bool never_composited);
 
   // Creates a RenderWidget for a popup. This is separate from CreateForFrame()
@@ -201,7 +197,6 @@
   static RenderWidget* CreateForPopup(
       int32_t widget_routing_id,
       CompositorDependencies* compositor_deps,
-      blink::mojom::DisplayMode display_mode,
       bool hidden,
       bool never_composited,
       mojo::PendingReceiver<mojom::Widget> widget_receiver);
@@ -263,7 +258,6 @@
 
   const gfx::Size& size() const { return size_; }
   bool is_fullscreen_granted() const { return is_fullscreen_granted_; }
-  blink::mojom::DisplayMode display_mode() const { return display_mode_; }
   bool is_hidden() const { return is_hidden_; }
   bool has_host_context_menu_location() const {
     return has_host_context_menu_location_;
@@ -898,9 +892,6 @@
   // Indicates whether tab-initiated fullscreen was granted.
   bool is_fullscreen_granted_ = false;
 
-  // Indicates the display mode.
-  blink::mojom::DisplayMode display_mode_;
-
   // It is possible that one ImeEventGuard is nested inside another
   // ImeEventGuard. We keep track of the outermost one, and update it as needed.
   ImeEventGuard* ime_event_guard_ = nullptr;
diff --git a/content/renderer/render_widget_delegate.h b/content/renderer/render_widget_delegate.h
index bd7d362..50bc8b8 100644
--- a/content/renderer/render_widget_delegate.h
+++ b/content/renderer/render_widget_delegate.h
@@ -38,11 +38,6 @@
   // These methods called during handling of a SynchronizeVisualProperties
   // message to handle updating state on the delegate.
   //
-  // Called during handling a SynchronizeVisualProperties message, with the new
-  // display mode that will be applied to the RenderWidget. The display mode in
-  // the RenderWidget is already changed when this method is called.
-  virtual void ApplyNewDisplayModeForWidget(
-      blink::mojom::DisplayMode new_display_mode) = 0;
   // Called during handling a SynchronizeVisualProperties message, if auto
   // resize is enabled, with the new auto size limits.
   virtual void ApplyAutoResizeLimitsForWidget(const gfx::Size& min_size,
diff --git a/content/renderer/render_widget_fullscreen_pepper.cc b/content/renderer/render_widget_fullscreen_pepper.cc
index 8622d653..6e1bb4e 100644
--- a/content/renderer/render_widget_fullscreen_pepper.cc
+++ b/content/renderer/render_widget_fullscreen_pepper.cc
@@ -199,7 +199,6 @@
     blink::WebURL main_frame_url)
     : RenderWidget(routing_id,
                    compositor_deps,
-                   /*display_mode=*/blink::mojom::DisplayMode::kUndefined,
                    /*hidden=*/false,
                    /*never_composited=*/false,
                    std::move(widget_receiver)),
diff --git a/content/renderer/render_widget_unittest.cc b/content/renderer/render_widget_unittest.cc
index f03bffc..59b4ff7 100644
--- a/content/renderer/render_widget_unittest.cc
+++ b/content/renderer/render_widget_unittest.cc
@@ -188,7 +188,6 @@
   explicit InteractiveRenderWidget(CompositorDependencies* compositor_deps)
       : RenderWidget(++next_routing_id_,
                      compositor_deps,
-                     blink::mojom::DisplayMode::kUndefined,
                      /*is_hidden=*/false,
                      /*never_composited=*/false,
                      mojo::NullReceiver()) {}
@@ -538,8 +537,6 @@
   void SetActiveForWidget(bool active) override {}
   bool SupportsMultipleWindowsForWidget() override { return true; }
   bool ShouldAckSyntheticInputImmediately() override { return true; }
-  void ApplyNewDisplayModeForWidget(
-      blink::mojom::DisplayMode new_display_mode) override {}
   void ApplyAutoResizeLimitsForWidget(const gfx::Size& min_size,
                                       const gfx::Size& max_size) override {}
   void DisableAutoResizeForWidget() override {}
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 452ab4c5..3fae69a 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -287,6 +287,9 @@
 crbug.com/1010942 [ win amd vulkan passthrough ] conformance/glsl/samplers/glsl-function-texture2dproj.html [ Failure ]
 crbug.com/angleproject/4286 [ win amd vulkan passthrough ] conformance/rendering/out-of-bounds-array-buffers.html [ Failure ]
 
+# OpenGL / Win/ Passthrough
+crbug.com/angleproject/4555 [ win opengl passthrough ] conformance/misc/type-conversion-test.html [ Skip ]
+
 
 ####################
 # Mac failures     #
diff --git a/content/test/web_test_support_renderer.cc b/content/test/web_test_support_renderer.cc
index 5344842..80040b6 100644
--- a/content/test/web_test_support_renderer.cc
+++ b/content/test/web_test_support_renderer.cc
@@ -64,12 +64,11 @@
 std::unique_ptr<RenderWidget> CreateRenderWidgetForFrame(
     int32_t routing_id,
     CompositorDependencies* compositor_deps,
-    blink::mojom::DisplayMode display_mode,
     bool never_composited,
     mojo::PendingReceiver<mojom::Widget> widget_receiver) {
-  return std::make_unique<WebWidgetTestProxy>(
-      routing_id, compositor_deps, display_mode,
-      /*hidden=*/true, never_composited, std::move(widget_receiver));
+  return std::make_unique<WebWidgetTestProxy>(routing_id, compositor_deps,
+                                              /*hidden=*/true, never_composited,
+                                              std::move(widget_receiver));
 }
 
 RenderFrameImpl* CreateWebFrameTestProxy(RenderFrameImpl::CreateParams params) {
diff --git a/docs/linux/build_instructions.md b/docs/linux/build_instructions.md
index 2381549..c99f9561 100644
--- a/docs/linux/build_instructions.md
+++ b/docs/linux/build_instructions.md
@@ -145,7 +145,7 @@
 If you are not a Googler and would like to use `Goma`
 [sign up](https://docs.google.com/forms/d/1NKHcyqYqw3c4jftrLPwvyiPlolRm4Hf6ObrB83wHXy8/viewform).
 
-Once you've allowed to use `Goma` service and installed the client,
+Once you're allowed to use `Goma` and have installed the client,
 [set the following GN args](https://www.chromium.org/developers/gn-build-configuration#TOC-Goma):
 
 ```
diff --git a/fuchsia/engine/chromium.cmx b/fuchsia/engine/chromium.cmx
deleted file mode 100644
index 8a776f33..0000000
--- a/fuchsia/engine/chromium.cmx
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "sandbox": {
-    "features": [
-      "config-data",
-      "deprecated-ambient-replace-as-executable",
-      "root-ssl-certificates",
-      "vulkan"
-    ],
-    "services": [
-      "fuchsia.logger.LogSink",
-      "fuchsia.process.Launcher"
-    ]
-  }
-}
diff --git a/fuchsia/engine/web_engine_integration_test.cc b/fuchsia/engine/web_engine_integration_test.cc
index 13e6b04..819937b 100644
--- a/fuchsia/engine/web_engine_integration_test.cc
+++ b/fuchsia/engine/web_engine_integration_test.cc
@@ -263,7 +263,8 @@
 // Check that if the CreateContextParams has |remote_debugging_port| set then:
 // - DevTools becomes available when the first debuggable Frame is created.
 // - DevTools closes when the last debuggable Frame is closed.
-TEST_F(WebEngineIntegrationTest, RemoteDebuggingPort) {
+// Test is flaky. http://crbug.com/1067727
+TEST_F(WebEngineIntegrationTest, DISABLED_RemoteDebuggingPort) {
   StartWebEngine();
 
   // Create a Context with remote debugging enabled via an ephemeral port.
@@ -520,4 +521,4 @@
       embedded_test_server_.GetURL("/mic.html?NoPermission").spec()));
 
   navigation_listener_->RunUntilTitleEquals("ended");
-}
\ No newline at end of file
+}
diff --git a/fuchsia/runners/cast/cast_runner.cmx b/fuchsia/runners/cast/cast_runner.cmx
index 987ad94..43e4234 100644
--- a/fuchsia/runners/cast/cast_runner.cmx
+++ b/fuchsia/runners/cast/cast_runner.cmx
@@ -3,7 +3,6 @@
     "features": [
       "config-data"
     ],
-    "$comment": "Not all services are passed to WebEngine, see cast_runner.cc",
     "services": [
       "chromium.cast.ApplicationConfigManager",
       "fuchsia.accessibility.semantics.SemanticsManager",
diff --git a/google_apis/test/embedded_setup_chromeos.html b/google_apis/test/embedded_setup_chromeos.html
index 951a2e9..f99d8e73 100644
--- a/google_apis/test/embedded_setup_chromeos.html
+++ b/google_apis/test/embedded_setup_chromeos.html
@@ -17,6 +17,8 @@
   document.getElementById('page2').hidden = true;
   history.replaceState({}, '', window.location.pathname + '#identifier');
   gaia.chromeOSLogin.backButton(false);
+  sendSetPrimaryActionLabel('Next');
+  sendSetPrimaryActionEnabled(true);
 }
 
 gaia.chromeOSLogin.registerHtml5Listener = function() {
@@ -28,6 +30,10 @@
         if (!gaia.chromeOSLogin.initialized_) {
           gaia.chromeOSLogin.initialized_ = true;
           goFirstPage();
+        } else if (e.data == 'primaryActionHit') {
+          goNext();
+        } else if (e.data == 'secondaryActionHit') {
+          sendSetSecondaryActionEnabled(false);
         }
     }
   };
@@ -111,6 +117,10 @@
 
     gaia.chromeOSLogin.attemptLogin(email, "");
     gaia.chromeOSLogin.backButton(true);
+    sendSetPrimaryActionLabel('Next');
+    sendSetPrimaryActionEnabled(true);
+    sendSetSecondaryActionLabel('Disable button');
+    sendSetSecondaryActionEnabled(true);
   } else if (!document.getElementById("page2").hidden) {
     var email = document.getElementById("identifier").value;
     var password = document.getElementById("password").value;
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
index eb80eea8..6f9264a 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
@@ -746,13 +746,13 @@
     FormatInfo& info = format_info_[i];
     if (!viz::GLSupportsFormat(format))
       continue;
-    GLuint image_internal_format = viz::GLInternalFormat(format);
-    GLenum gl_format = viz::GLDataFormat(format);
-    GLenum gl_type = viz::GLDataType(format);
-    bool uncompressed_format_valid =
+    const GLuint image_internal_format = viz::GLInternalFormat(format);
+    const GLenum gl_format = viz::GLDataFormat(format);
+    const GLenum gl_type = viz::GLDataType(format);
+    const bool uncompressed_format_valid =
         validators->texture_internal_format.IsValid(image_internal_format) &&
         validators->texture_format.IsValid(gl_format);
-    bool compressed_format_valid =
+    const bool compressed_format_valid =
         validators->compressed_texture_format.IsValid(image_internal_format);
     if ((uncompressed_format_valid || compressed_format_valid) &&
         validators->pixel_type.IsValid(gl_type)) {
@@ -781,21 +781,24 @@
       }
     }
     if (!info.enabled || !enable_scanout_images ||
-        !IsGpuMemoryBufferFormatSupported(format))
+        !IsGpuMemoryBufferFormatSupported(format)) {
       continue;
-    gfx::BufferFormat buffer_format = viz::BufferFormat(format);
+    }
+    const gfx::BufferFormat buffer_format = viz::BufferFormat(format);
     switch (buffer_format) {
       case gfx::BufferFormat::RGBA_8888:
       case gfx::BufferFormat::BGRA_8888:
       case gfx::BufferFormat::RGBA_F16:
       case gfx::BufferFormat::R_8:
+      case gfx::BufferFormat::BGRA_1010102:
+      case gfx::BufferFormat::RGBA_1010102:
         break;
       default:
         continue;
     }
     info.allow_scanout = true;
     info.buffer_format = buffer_format;
-    DCHECK_EQ(info.gl_format,
+    DCHECK_EQ(info.image_internal_format,
               gl::BufferFormatToGLInternalFormat(buffer_format));
     if (base::Contains(gpu_preferences.texture_target_exception_list,
                        gfx::BufferUsageAndFormat(gfx::BufferUsage::SCANOUT,
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc
index 061a6a4..5b51b2a2 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc
@@ -77,7 +77,7 @@
 }
 
 class SharedImageBackingFactoryGLTextureTestBase
-    : public testing::TestWithParam<bool> {
+    : public testing::TestWithParam<std::tuple<bool, viz::ResourceFormat>> {
  public:
   SharedImageBackingFactoryGLTextureTestBase(bool is_thread_safe)
       : shared_image_manager_(
@@ -96,6 +96,13 @@
         feature_info->validators()->compressed_texture_format.IsValid(
             GL_ETC1_RGB8_OES);
 
+    if ((get_format() == viz::ResourceFormat::BGRA_1010102 &&
+         !feature_info->feature_flags().chromium_image_ar30) ||
+        (get_format() == viz::ResourceFormat::RGBA_1010102 &&
+         !feature_info->feature_flags().chromium_image_ab30)) {
+      GTEST_SKIP();
+    }
+
     GpuPreferences preferences;
     preferences.use_passthrough_cmd_decoder = use_passthrough();
     backing_factory_ = std::make_unique<SharedImageBackingFactoryGLTexture>(
@@ -109,9 +116,12 @@
   }
 
   bool use_passthrough() {
-    return GetParam() && gles2::PassthroughCommandDecoderSupported();
+    return std::get<0>(GetParam()) &&
+           gles2::PassthroughCommandDecoderSupported();
   }
 
+  viz::ResourceFormat get_format() { return std::get<1>(GetParam()); }
+
   bool supports_etc1() { return supports_etc1_; }
 
   GrContext* gr_context() { return context_state_->gr_context(); }
@@ -197,7 +207,7 @@
 
 TEST_P(SharedImageBackingFactoryGLTextureTest, Basic) {
   auto mailbox = Mailbox::GenerateForSharedImage();
-  auto format = viz::ResourceFormat::RGBA_8888;
+  auto format = get_format();
   gfx::Size size(256, 256);
   auto color_space = gfx::ColorSpace::CreateSRGB();
   uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
@@ -264,6 +274,10 @@
     gl_representation.reset();
   }
 
+  // Skia does not support RGBA_1010102 as render target, only BGRA_1010102.
+  if (format == viz::ResourceFormat::RGBA_1010102)
+    return;
+
   // Finally, validate a SharedImageRepresentationSkia.
   auto skia_representation = shared_image_representation_factory_->ProduceSkia(
       mailbox, context_state_.get());
@@ -291,20 +305,20 @@
   EXPECT_TRUE(promise_texture);
   EXPECT_TRUE(begin_semaphores.empty());
   EXPECT_TRUE(end_semaphores.empty());
-    GrBackendTexture backend_texture = promise_texture->backendTexture();
-    EXPECT_TRUE(backend_texture.isValid());
-    EXPECT_EQ(size.width(), backend_texture.width());
-    EXPECT_EQ(size.height(), backend_texture.height());
-    scoped_read_access.reset();
-    skia_representation.reset();
+  GrBackendTexture backend_texture = promise_texture->backendTexture();
+  EXPECT_TRUE(backend_texture.isValid());
+  EXPECT_EQ(size.width(), backend_texture.width());
+  EXPECT_EQ(size.height(), backend_texture.height());
+  scoped_read_access.reset();
+  skia_representation.reset();
 
-    shared_image.reset();
-    EXPECT_FALSE(mailbox_manager_.ConsumeTexture(mailbox));
+  shared_image.reset();
+  EXPECT_FALSE(mailbox_manager_.ConsumeTexture(mailbox));
 }
 
 TEST_P(SharedImageBackingFactoryGLTextureTest, Image) {
   auto mailbox = Mailbox::GenerateForSharedImage();
-  auto format = viz::ResourceFormat::RGBA_8888;
+  auto format = get_format();
   gfx::Size size(256, 256);
   auto color_space = gfx::ColorSpace::CreateSRGB();
   uint32_t usage = SHARED_IMAGE_USAGE_SCANOUT;
@@ -378,6 +392,10 @@
     gl_representation.reset();
   }
 
+  // Skia does not support RGBA_1010102 as render target, only BGRA_1010102.
+  if (format == viz::ResourceFormat::RGBA_1010102)
+    return;
+
   // Finally, validate a SharedImageRepresentationSkia.
   auto skia_representation = shared_image_representation_factory_->ProduceSkia(
       mailbox, context_state_.get());
@@ -417,9 +435,9 @@
 
   if (!use_passthrough() &&
       context_state_->feature_info()->feature_flags().ext_texture_rg) {
-    // Create a R-8 image texture, and check that the internal_format is that of
-    // the image (GL_RGBA for TextureImageFactory). This only matters for the
-    // validating decoder.
+    // Create a R-8 image texture, and check that the internal_format is that
+    // of the image (GL_RGBA for TextureImageFactory). This only matters for
+    // the validating decoder.
     auto format = viz::ResourceFormat::RED_8;
     gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle;
     backing = backing_factory_->CreateSharedImage(
@@ -498,7 +516,7 @@
 
 TEST_P(SharedImageBackingFactoryGLTextureTest, InitialDataImage) {
   auto mailbox = Mailbox::GenerateForSharedImage();
-  auto format = viz::ResourceFormat::RGBA_8888;
+  auto format = get_format();
   gfx::Size size(256, 256);
   auto color_space = gfx::ColorSpace::CreateSRGB();
   uint32_t usage = SHARED_IMAGE_USAGE_GLES2 | SHARED_IMAGE_USAGE_SCANOUT;
@@ -537,7 +555,7 @@
 
 TEST_P(SharedImageBackingFactoryGLTextureTest, InitialDataWrongSize) {
   auto mailbox = Mailbox::GenerateForSharedImage();
-  auto format = viz::ResourceFormat::RGBA_8888;
+  auto format = get_format();
   gfx::Size size(256, 256);
   auto color_space = gfx::ColorSpace::CreateSRGB();
   uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
@@ -566,7 +584,7 @@
 
 TEST_P(SharedImageBackingFactoryGLTextureTest, InvalidSize) {
   auto mailbox = Mailbox::GenerateForSharedImage();
-  auto format = viz::ResourceFormat::RGBA_8888;
+  auto format = get_format();
   gfx::Size size(0, 0);
   auto color_space = gfx::ColorSpace::CreateSRGB();
   gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle;
@@ -585,7 +603,7 @@
 
 TEST_P(SharedImageBackingFactoryGLTextureTest, EstimatedSize) {
   auto mailbox = Mailbox::GenerateForSharedImage();
-  auto format = viz::ResourceFormat::RGBA_8888;
+  auto format = get_format();
   gfx::Size size(256, 256);
   auto color_space = gfx::ColorSpace::CreateSRGB();
   gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle;
@@ -749,7 +767,7 @@
        GpuMemoryBufferImportEmpty) {
   auto mailbox = Mailbox::GenerateForSharedImage();
   gfx::Size size(256, 256);
-  gfx::BufferFormat format = gfx::BufferFormat::RGBA_8888;
+  gfx::BufferFormat format = viz::BufferFormat(get_format());
   auto color_space = gfx::ColorSpace::CreateSRGB();
   uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
 
@@ -764,7 +782,7 @@
        GpuMemoryBufferImportNative) {
   auto mailbox = Mailbox::GenerateForSharedImage();
   gfx::Size size(256, 256);
-  gfx::BufferFormat format = gfx::BufferFormat::RGBA_8888;
+  gfx::BufferFormat format = viz::BufferFormat(get_format());
   auto color_space = gfx::ColorSpace::CreateSRGB();
   uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
 
@@ -792,7 +810,7 @@
        GpuMemoryBufferImportSharedMemory) {
   auto mailbox = Mailbox::GenerateForSharedImage();
   gfx::Size size(256, 256);
-  gfx::BufferFormat format = gfx::BufferFormat::RGBA_8888;
+  gfx::BufferFormat format = viz::BufferFormat(get_format());
   auto color_space = gfx::ColorSpace::CreateSRGB();
   uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
 
@@ -826,7 +844,7 @@
     return;
   auto mailbox = Mailbox::GenerateForSharedImage();
   gfx::Size size(256, 256);
-  gfx::BufferFormat format = gfx::BufferFormat::RGBA_8888;
+  gfx::BufferFormat format = viz::BufferFormat(get_format());
   auto color_space = gfx::ColorSpace::CreateSRGB();
   uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
 
@@ -847,7 +865,7 @@
   EXPECT_TRUE(representation);
   EXPECT_TRUE(representation->GetTexture()->service_id());
   EXPECT_EQ(size, representation->size());
-  EXPECT_EQ(viz::ResourceFormat::RGBA_8888, representation->format());
+  EXPECT_EQ(get_format(), representation->format());
   EXPECT_EQ(color_space, representation->color_space());
   EXPECT_EQ(usage, representation->usage());
 
@@ -1044,15 +1062,28 @@
   EXPECT_FALSE(mailbox_manager_->ConsumeTexture(mailbox_));
 }
 
+#if !defined(OS_ANDROID)
+const auto kResourceFormats =
+    ::testing::Values(viz::ResourceFormat::RGBA_8888,
+                      viz::ResourceFormat::BGRA_1010102,
+                      viz::ResourceFormat::RGBA_1010102);
+#else
+// High bit depth rendering is not supported on Android.
+const auto kResourceFormats = ::testing::Values(viz::ResourceFormat::RGBA_8888);
+#endif
+
 INSTANTIATE_TEST_SUITE_P(Service,
                          SharedImageBackingFactoryGLTextureTest,
-                         ::testing::Bool());
+                         ::testing::Combine(::testing::Bool(),
+                                            kResourceFormats));
 INSTANTIATE_TEST_SUITE_P(Service,
                          SharedImageBackingFactoryGLTextureThreadSafeTest,
-                         ::testing::Bool());
+                         ::testing::Combine(::testing::Bool(),
+                                            kResourceFormats));
 INSTANTIATE_TEST_SUITE_P(Service,
                          SharedImageBackingFactoryGLTextureWithGMBTest,
-                         ::testing::Bool());
+                         ::testing::Combine(::testing::Bool(),
+                                            kResourceFormats));
 
 }  // anonymous namespace
 }  // namespace gpu
diff --git a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.cc b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.cc
index 203c58c..db815ad 100644
--- a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.cc
+++ b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.cc
@@ -5,6 +5,7 @@
 #include "ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.h"
 
 #include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/sequenced_task_runner.h"
 #include "base/task/post_task.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
@@ -25,6 +26,7 @@
     : ChromeBrowserState(std::move(io_task_runner)),
       otr_state_path_(otr_path),
       original_chrome_browser_state_(original_chrome_browser_state),
+      start_time_(base::Time::Now()),
       prefs_(CreateIncognitoBrowserStatePrefs(
           static_cast<sync_preferences::PrefServiceSyncable*>(
               original_chrome_browser_state->GetPrefs()))) {
@@ -40,6 +42,11 @@
   if (pref_proxy_config_tracker_)
     pref_proxy_config_tracker_->DetachFromPrefService();
 
+  const base::TimeDelta duration = base::Time::Now() - start_time_;
+  base::UmaHistogramCustomCounts(
+      "Profile.Incognito.Lifetime", duration.InMinutes(), 1,
+      base::TimeDelta::FromDays(28).InMinutes(), 100);
+
   // Clears any data the network stack contains that may be related to the
   // OTR session.
   GetApplicationContext()->GetIOSChromeIOThread()->ChangedToOnTheRecord();
diff --git a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.h b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.h
index 7a04901..cca2aa1 100644
--- a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.h
+++ b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.h
@@ -52,6 +52,9 @@
   base::FilePath otr_state_path_;
   ChromeBrowserState* original_chrome_browser_state_;  // weak
 
+  // Creation time of the off-the-record BrowserState.
+  const base::Time start_time_;
+
   std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs_;
 
   std::unique_ptr<OffTheRecordChromeBrowserStateIOData::Handle> io_data_;
diff --git a/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.mm b/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.mm
index 5d32abf..387fc04 100644
--- a/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.mm
+++ b/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.mm
@@ -594,7 +594,7 @@
   if (identities.count != 0) {
     newIdentity = identities[0];
   }
-  if (newIdentity != _defaultIdentity) {
+  if (![_defaultIdentity isEqual:newIdentity]) {
     [self selectIdentity:newIdentity];
     [self sendConsumerNotificationWithIdentityChanged:YES];
   }
diff --git a/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_mediator.mm b/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_mediator.mm
index c89403b..c519da1 100644
--- a/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_mediator.mm
+++ b/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_mediator.mm
@@ -42,7 +42,7 @@
 }
 
 - (void)setSelectedIdentity:(ChromeIdentity*)selectedIdentity {
-  if (_selectedIdentity == selectedIdentity)
+  if ([_selectedIdentity isEqual:selectedIdentity])
     return;
   IdentityChooserItem* previousSelectedItem = [self.consumer
       identityChooserItemWithGaiaID:self.selectedIdentity.gaiaID];
diff --git a/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_mediator.mm b/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_mediator.mm
index df7f6ea..45ba2e98 100644
--- a/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_mediator.mm
+++ b/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_mediator.mm
@@ -51,7 +51,7 @@
 }
 
 - (void)setSelectedIdentity:(ChromeIdentity*)selectedIdentity {
-  if (selectedIdentity == self.selectedIdentity) {
+  if ([self.selectedIdentity isEqual:selectedIdentity]) {
     return;
   }
   // nil is allowed only if there is no other identity.
@@ -145,7 +145,7 @@
 }
 
 - (void)profileUpdate:(ChromeIdentity*)identity {
-  if (identity == self.selectedIdentity) {
+  if ([self.selectedIdentity isEqual:identity]) {
     [self updateViewController];
   }
 }
diff --git a/ios/chrome/browser/ui/browser_view/BUILD.gn b/ios/chrome/browser/ui/browser_view/BUILD.gn
index 3ac4343..27ef560 100644
--- a/ios/chrome/browser/ui/browser_view/BUILD.gn
+++ b/ios/chrome/browser/ui/browser_view/BUILD.gn
@@ -127,6 +127,7 @@
     "//ios/chrome/browser/ui/popup_menu",
     "//ios/chrome/browser/ui/presenters",
     "//ios/chrome/browser/ui/print",
+    "//ios/chrome/browser/ui/qr_generator",
     "//ios/chrome/browser/ui/qr_scanner:coordinator",
     "//ios/chrome/browser/ui/reading_list",
     "//ios/chrome/browser/ui/recent_tabs",
diff --git a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
index 2369add..545cdd7 100644
--- a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
@@ -53,6 +53,7 @@
 #import "ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.h"
 #import "ios/chrome/browser/ui/passwords/password_breach_coordinator.h"
 #import "ios/chrome/browser/ui/print/print_controller.h"
+#import "ios/chrome/browser/ui/qr_generator/qr_generator_coordinator.h"
 #import "ios/chrome/browser/ui/qr_scanner/qr_scanner_legacy_coordinator.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_coordinator.h"
 #import "ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h"
@@ -62,6 +63,7 @@
 #import "ios/chrome/browser/ui/toolbar/accessory/toolbar_accessory_coordinator_delegate.h"
 #import "ios/chrome/browser/ui/toolbar/accessory/toolbar_accessory_presenter.h"
 #import "ios/chrome/browser/ui/translate/legacy_translate_infobar_coordinator.h"
+#include "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/browser/url_loading/url_loading_browser_agent.h"
 #import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/web/features.h"
@@ -152,6 +154,9 @@
 // TODO(crbug.com/910017): Convert to coordinator.
 @property(nonatomic, strong) PrintController* printController;
 
+// Coordinator for the QR generator UI.
+@property(nonatomic, strong) QRGeneratorCoordinator* qrGeneratorCoordinator;
+
 // Coordinator for the QR scanner.
 @property(nonatomic, strong) QRScannerLegacyCoordinator* qrScannerCoordinator;
 
@@ -268,6 +273,9 @@
   [self.readingListCoordinator stop];
   self.readingListCoordinator = nil;
 
+  [self.qrGeneratorCoordinator stop];
+  self.qrGeneratorCoordinator = nil;
+
   [self.passwordBreachCoordinator stop];
 
   [self.pageInfoCoordinator stop];
@@ -374,6 +382,8 @@
                          browser:self.browser];
   [self.qrScannerCoordinator start];
 
+  /* QRGeneratorCoordinator is created and started by a command. */
+
   /* ReadingListCoordinator is created and started by a BrowserCommand */
 
   /* RecentTabsCoordinator is created and started by a BrowserCommand */
@@ -440,6 +450,9 @@
 
   self.printController = nil;
 
+  [self.qrGeneratorCoordinator stop];
+  self.qrGeneratorCoordinator = nil;
+
   [self.qrScannerCoordinator stop];
   self.qrScannerCoordinator = nil;
 
@@ -718,7 +731,18 @@
 #pragma mark - QRGenerationCommands
 
 - (void)generateQRCode:(GenerateQRCodeCommand*)command {
-  // TODO(crbug.com/1064990): Implement Coordinator and starting here.
+  DCHECK(base::FeatureList::IsEnabled(kQRCodeGeneration));
+  self.qrGeneratorCoordinator = [[QRGeneratorCoordinator alloc]
+      initWithBaseViewController:self.viewController
+                         browser:self.browser
+                           title:command.title
+                             URL:command.URL];
+  [self.qrGeneratorCoordinator start];
+}
+
+- (void)hideQRCode {
+  [self.qrGeneratorCoordinator stop];
+  self.qrGeneratorCoordinator = nil;
 }
 
 #pragma mark - FormInputAccessoryCoordinatorNavigator
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index 85f8d625..3a06d1c 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -934,7 +934,10 @@
            webStateList:self.browser->GetWebStateList()];
     StartBroadcastingMainContentUI(self, broadcaster);
 
-    self.fullscreenController->SetWebStateList(self.browser->GetWebStateList());
+    if (!fullscreen::features::ShouldScopeFullscreenControllerToBrowser()) {
+      self.fullscreenController->SetWebStateList(
+          self.browser->GetWebStateList());
+    }
 
     _fullscreenUIUpdater =
         std::make_unique<FullscreenUIUpdater>(self.fullscreenController, self);
@@ -950,7 +953,9 @@
 
     _fullscreenUIUpdater = nullptr;
 
-    self.fullscreenController->SetWebStateList(nullptr);
+    if (!fullscreen::features::ShouldScopeFullscreenControllerToBrowser()) {
+      self.fullscreenController->SetWebStateList(nullptr);
+    }
   }
 }
 
diff --git a/ios/chrome/browser/ui/commands/qr_generation_commands.h b/ios/chrome/browser/ui/commands/qr_generation_commands.h
index 5d910cf..8a4b2eb 100644
--- a/ios/chrome/browser/ui/commands/qr_generation_commands.h
+++ b/ios/chrome/browser/ui/commands/qr_generation_commands.h
@@ -14,6 +14,9 @@
 // Generates a QR code based on the |command| properties and displays it.
 - (void)generateQRCode:(GenerateQRCodeCommand*)command;
 
+// Dismisses the view showing a QR code, if present.
+- (void)hideQRCode;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_COMMANDS_QR_GENERATION_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/fullscreen/BUILD.gn b/ios/chrome/browser/ui/fullscreen/BUILD.gn
index 42e4cf73..dfd9f65 100644
--- a/ios/chrome/browser/ui/fullscreen/BUILD.gn
+++ b/ios/chrome/browser/ui/fullscreen/BUILD.gn
@@ -60,6 +60,8 @@
 
 source_set("internal") {
   sources = [
+    "fullscreen_browser_observer.h",
+    "fullscreen_browser_observer.mm",
     "fullscreen_content_adjustment_util.h",
     "fullscreen_content_adjustment_util.mm",
     "fullscreen_controller_impl.h",
@@ -126,6 +128,7 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "fullscreen_browser_observer_unittest.mm",
     "fullscreen_mediator_unittest.mm",
     "fullscreen_model_unittest.mm",
     "fullscreen_ui_updater_unittest.mm",
@@ -141,6 +144,7 @@
     ":internal",
     ":ui",
     "//base/test:test_support",
+    "//ios/chrome/browser/main:test_support",
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/broadcaster",
     "//ios/chrome/browser/ui/fullscreen/test",
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_browser_observer.h b/ios/chrome/browser/ui/fullscreen/fullscreen_browser_observer.h
new file mode 100644
index 0000000..04c18785
--- /dev/null
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_browser_observer.h
@@ -0,0 +1,31 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_FULLSCREEN_FULLSCREEN_BROWSER_OBSERVER_H_
+#define IOS_CHROME_BROWSER_UI_FULLSCREEN_FULLSCREEN_BROWSER_OBSERVER_H_
+
+#include "base/scoped_observer.h"
+#import "ios/chrome/browser/main/browser.h"
+#import "ios/chrome/browser/main/browser_observer.h"
+#import "ios/chrome/browser/ui/fullscreen/fullscreen_web_state_list_observer.h"
+
+// A BrowserObserver that observes the BrowserDestroyed callback.
+class FullscreenBrowserObserver : public BrowserObserver {
+ public:
+  FullscreenBrowserObserver(
+      FullscreenWebStateListObserver* web_state_list_observer,
+      Browser* browser);
+  ~FullscreenBrowserObserver() override;
+
+ private:
+  // BrowserObserver
+  void BrowserDestroyed(Browser* browser) override;
+
+  // The FullscreenWebStateListObserver passed on construction.
+  FullscreenWebStateListObserver* web_state_list_observer_;
+  // Scoped observer that facilitates observing an BrowserObserver.
+  ScopedObserver<Browser, BrowserObserver> scoped_observer_;
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_FULLSCREEN_FULLSCREEN_BROWSER_OBSERVER_H_
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_browser_observer.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_browser_observer.mm
new file mode 100644
index 0000000..d921e24
--- /dev/null
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_browser_observer.mm
@@ -0,0 +1,33 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/fullscreen/fullscreen_browser_observer.h"
+
+#import "ios/chrome/browser/main/browser.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+FullscreenBrowserObserver::FullscreenBrowserObserver(
+    FullscreenWebStateListObserver* web_state_list_observer,
+    Browser* browser)
+    : web_state_list_observer_(web_state_list_observer),
+      scoped_observer_(this) {
+  DCHECK(web_state_list_observer_);
+  // TODO(crbug.com/790886): DCHECK |browser| once FullscreenController is fully
+  // scoped to a Browser.
+  if (browser) {
+    web_state_list_observer_->SetWebStateList(browser->GetWebStateList());
+    scoped_observer_.Add(browser);
+  }
+}
+
+FullscreenBrowserObserver::~FullscreenBrowserObserver() = default;
+
+void FullscreenBrowserObserver::FullscreenBrowserObserver::BrowserDestroyed(
+    Browser* browser) {
+  web_state_list_observer_->SetWebStateList(nullptr);
+  scoped_observer_.Remove(browser);
+}
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_browser_observer_unittest.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_browser_observer_unittest.mm
new file mode 100644
index 0000000..c3e1918
--- /dev/null
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_browser_observer_unittest.mm
@@ -0,0 +1,50 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/fullscreen/fullscreen_browser_observer.h"
+
+#include "base/test/task_environment.h"
+#import "ios/chrome/browser/main/test_browser.h"
+#import "ios/chrome/browser/ui/fullscreen/fullscreen_mediator.h"
+#import "ios/chrome/browser/ui/fullscreen/fullscreen_model.h"
+#import "ios/chrome/browser/ui/fullscreen/fullscreen_web_state_list_observer.h"
+#import "ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Tests FullscreenBrowserObserver functionality.
+class FullscreenBrowserObserverTest : public PlatformTest {
+ public:
+  FullscreenBrowserObserverTest()
+      : browser_(std::make_unique<TestBrowser>()),
+        controller_(&model_),
+        mediator_(&controller_, &model_),
+        web_state_list_observer_(&controller_, &model_, &mediator_),
+        browser_observer_(&web_state_list_observer_, browser_.get()) {
+    web_state_list_observer_.SetWebStateList(browser_.get()->GetWebStateList());
+  }
+
+  ~FullscreenBrowserObserverTest() override { mediator_.Disconnect(); }
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+  std::unique_ptr<TestBrowser> browser_;
+  TestFullscreenController controller_;
+  FullscreenModel model_;
+  FullscreenMediator mediator_;
+  FullscreenWebStateListObserver web_state_list_observer_;
+  FullscreenBrowserObserver browser_observer_;
+};
+
+// Tests that FullscreenBrowserObserver resets the FullscreenController's
+// WebStateList.
+TEST_F(FullscreenBrowserObserverTest, BrowserDestroyed) {
+  EXPECT_TRUE(web_state_list_observer_.GetWebStateList());
+  browser_ = nullptr;
+  EXPECT_FALSE(web_state_list_observer_.GetWebStateList());
+}
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.h b/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.h
index aa371c8b..927f233 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.h
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.h
@@ -5,18 +5,20 @@
 #ifndef IOS_CHROME_BROWSER_UI_FULLSCREEN_FULLSCREEN_CONTROLLER_IMPL_H_
 #define IOS_CHROME_BROWSER_UI_FULLSCREEN_FULLSCREEN_CONTROLLER_IMPL_H_
 
+#import "ios/chrome/browser/ui/fullscreen/fullscreen_browser_observer.h"
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_controller.h"
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_mediator.h"
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_model.h"
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_web_state_list_observer.h"
 
+class Browser;
 @class ChromeBroadcastOberverBridge;
 @class FullscreenSystemNotificationObserver;
 
 // Implementation of FullscreenController.
 class FullscreenControllerImpl : public FullscreenController {
  public:
-  explicit FullscreenControllerImpl();
+  explicit FullscreenControllerImpl(Browser* browser);
 
   // Not copyable or movable
   FullscreenControllerImpl(const FullscreenControllerImpl&) = delete;
@@ -56,6 +58,9 @@
   FullscreenMediator mediator_;
   // A WebStateListObserver that updates |model_| for WebStateList changes.
   FullscreenWebStateListObserver web_state_list_observer_;
+  // A FullscreenBrowserObserver that resets |web_state_list_| when the Browser
+  // is destroyed.
+  FullscreenBrowserObserver fullscreen_browser_observer_;
   // The bridge used to forward brodcasted UI to |model_|.
   __strong ChromeBroadcastOberverBridge* bridge_ = nil;
   // A helper object that listens for system notifications.
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.mm
index d6542d9..f88f5ab8 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.mm
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.mm
@@ -33,8 +33,12 @@
 // BrowserUserData when the flag is turned on by default.
 class FullscreenControllerContainer : public base::SupportsUserData::Data {
  public:
+  FullscreenControllerContainer(Browser* browser)
+      : fullscreen_controller_(
+            std::make_unique<FullscreenControllerImpl>(browser)) {}
   FullscreenControllerContainer()
-      : fullscreen_controller_(std::make_unique<FullscreenControllerImpl>()) {}
+      : fullscreen_controller_(
+            std::make_unique<FullscreenControllerImpl>(nullptr)) {}
   ~FullscreenControllerContainer() override;
 
   FullscreenControllerContainer(const FullscreenControllerContainer&) = delete;
@@ -84,7 +88,8 @@
       static_cast<FullscreenControllerContainer*>(
           browser->GetUserData(kFullscreenControllerContainerUserDataKey));
   if (!fullscreen_controller_container) {
-    fullscreen_controller_container = new FullscreenControllerContainer;
+    fullscreen_controller_container =
+        new FullscreenControllerContainer(browser);
     browser->SetUserData(kFullscreenControllerContainerUserDataKey,
                          base::WrapUnique(fullscreen_controller_container));
   }
@@ -109,10 +114,11 @@
       ->GetFullscreenController();
 }
 
-FullscreenControllerImpl::FullscreenControllerImpl()
+FullscreenControllerImpl::FullscreenControllerImpl(Browser* browser)
     : broadcaster_([[ChromeBroadcaster alloc] init]),
       mediator_(this, &model_),
       web_state_list_observer_(this, &model_, &mediator_),
+      fullscreen_browser_observer_(&web_state_list_observer_, browser),
       bridge_([[ChromeBroadcastOberverBridge alloc] initWithObserver:&model_]),
       notification_observer_([[FullscreenSystemNotificationObserver alloc]
           initWithController:this
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index 42f98bc..36bfb06e 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -320,8 +320,6 @@
               startupInformation:self.mainController
                         appState:self.mainController.appState];
 
-  [self scheduleShowPromo];
-
   // Before bringing up the UI, make sure the launch mode is correct, and
   // check for previous crashes.
   BOOL startInIncognito =
@@ -416,6 +414,8 @@
     [self showFirstRunUI];
     // Do not ever show the 'restore' infobar during first run.
     self.mainController.restoreHelper = nil;
+  } else {
+    [self scheduleShowPromo];
   }
 }
 
@@ -453,6 +453,7 @@
 
 // Initializes the first run UI and presents it to the user.
 - (void)showFirstRunUI {
+  DCHECK(!self.signinCoordinator);
   // Register for the first run dismissal notification to reset
   // |sceneState.presentingFirstRunUI| flag;
   [[NSNotificationCenter defaultCenter]
diff --git a/ios/chrome/browser/ui/qr_generator/BUILD.gn b/ios/chrome/browser/ui/qr_generator/BUILD.gn
new file mode 100644
index 0000000..b43a487
--- /dev/null
+++ b/ios/chrome/browser/ui/qr_generator/BUILD.gn
@@ -0,0 +1,53 @@
+# Copyright 2020 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.
+
+source_set("ui") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "qr_generator_view_controller.h",
+    "qr_generator_view_controller.mm",
+  ]
+  deps = [
+    "//base",
+    "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/common/ui/colors",
+    "//ui/base",
+    "//url:url",
+  ]
+  libs = [ "UIKit.framework" ]
+}
+
+source_set("qr_generator") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "qr_generator_coordinator.h",
+    "qr_generator_coordinator.mm",
+  ]
+  deps = [
+    ":ui",
+    "//base",
+    "//ios/chrome/browser/main:public",
+    "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
+  ]
+}
+
+source_set("unit_tests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  sources = [ "qr_generator_view_controller_unittest.mm" ]
+  deps = [
+    ":ui",
+    "//base",
+    "//base/test:test_support",
+    "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/common/ui/colors",
+    "//ios/chrome/test/fakes",
+    "//ios/web",
+    "//ios/web/public/test",
+    "//testing/gtest",
+    "//third_party/ocmock",
+    "//ui/base",
+  ]
+}
diff --git a/ios/chrome/browser/ui/qr_generator/qr_generator_coordinator.h b/ios/chrome/browser/ui/qr_generator/qr_generator_coordinator.h
new file mode 100644
index 0000000..7130b58
--- /dev/null
+++ b/ios/chrome/browser/ui/qr_generator/qr_generator_coordinator.h
@@ -0,0 +1,34 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_QR_GENERATOR_QR_GENERATOR_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_UI_QR_GENERATOR_QR_GENERATOR_COORDINATOR_H_
+
+#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
+#include "url/gurl.h"
+
+// QRGeneratorCoordinator presents the public interface for the QR code
+// generation feature.
+@interface QRGeneratorCoordinator : ChromeCoordinator
+
+// Initializes an instance with a base |viewController|, the current |browser|,
+// the |title| and |URL| of a webpage to generate a QR code for.
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser
+                                     title:(NSString*)title
+                                       URL:(const GURL&)URL
+    NS_DESIGNATED_INITIALIZER;
+
+// Unavailable, use -initWithBaseViewController:browser:title:URL:.
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                              browserState:(ChromeBrowserState*)browserState
+    NS_UNAVAILABLE;
+
+// Unavailable, use -initWithBaseViewController:browser:title:URL:.
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_QR_GENERATOR_QR_GENERATOR_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/qr_generator/qr_generator_coordinator.mm b/ios/chrome/browser/ui/qr_generator/qr_generator_coordinator.mm
new file mode 100644
index 0000000..1788e47
--- /dev/null
+++ b/ios/chrome/browser/ui/qr_generator/qr_generator_coordinator.mm
@@ -0,0 +1,82 @@
+
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/qr_generator/qr_generator_coordinator.h"
+
+#include "ios/chrome/browser/main/browser.h"
+#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
+#import "ios/chrome/browser/ui/commands/qr_generation_commands.h"
+#import "ios/chrome/browser/ui/qr_generator/qr_generator_view_controller.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface QRGeneratorCoordinator () {
+  // URL of a page to generate a QR code for.
+  GURL _URL;
+}
+
+// To be used to dispatch commands to the browser.
+@property(nonatomic, strong) CommandDispatcher* dispatcher;
+
+// Main view controller which will be the parent, or ancestor, of other
+// view controllers related to this feature.
+@property(nonatomic, strong) UINavigationController* viewController;
+
+// View controller used to display the QR code and actions.
+@property(nonatomic, strong)
+    QRGeneratorViewController* qrGeneratorViewController;
+
+// Title of a page to generate a QR code for.
+@property(nonatomic, copy) NSString* title;
+
+@end
+
+@implementation QRGeneratorCoordinator
+
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser
+                                     title:(NSString*)title
+                                       URL:(const GURL&)URL {
+  if (self = [super initWithBaseViewController:viewController
+                                       browser:browser]) {
+    _title = title;
+    _URL = URL;
+  }
+  return self;
+}
+
+#pragma mark - Chrome Coordinator
+
+- (void)start {
+  self.dispatcher = self.browser->GetCommandDispatcher();
+
+  self.qrGeneratorViewController = [[QRGeneratorViewController alloc] init];
+  self.qrGeneratorViewController.handler =
+      HandlerForProtocol(self.dispatcher, QRGenerationCommands);
+
+  self.viewController = [[UINavigationController alloc]
+      initWithRootViewController:self.qrGeneratorViewController];
+  [self.viewController setModalPresentationStyle:UIModalPresentationFormSheet];
+
+  [self.baseViewController presentViewController:self.viewController
+                                        animated:YES
+                                      completion:nil];
+  [super start];
+}
+
+- (void)stop {
+  [self.viewController.presentingViewController
+      dismissViewControllerAnimated:YES
+                         completion:nil];
+
+  self.qrGeneratorViewController = nil;
+  self.viewController = nil;
+
+  [super stop];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/qr_generator/qr_generator_view_controller.h b/ios/chrome/browser/ui/qr_generator/qr_generator_view_controller.h
new file mode 100644
index 0000000..0039158
--- /dev/null
+++ b/ios/chrome/browser/ui/qr_generator/qr_generator_view_controller.h
@@ -0,0 +1,20 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_QR_GENERATOR_QR_GENERATOR_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_QR_GENERATOR_QR_GENERATOR_VIEW_CONTROLLER_H_
+
+#import "ios/chrome/browser/ui/commands/qr_generation_commands.h"
+
+#import <UIKit/UIKit.h>
+
+// View controller that displays a QR code representing a given website.
+@interface QRGeneratorViewController : UIViewController
+
+// Command handler for communicating with other components of the app.
+@property(nonatomic, weak) id<QRGenerationCommands> handler;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_QR_GENERATOR_QR_GENERATOR_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/qr_generator/qr_generator_view_controller.mm b/ios/chrome/browser/ui/qr_generator/qr_generator_view_controller.mm
new file mode 100644
index 0000000..e3a0a04
--- /dev/null
+++ b/ios/chrome/browser/ui/qr_generator/qr_generator_view_controller.mm
@@ -0,0 +1,49 @@
+
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/qr_generator/qr_generator_view_controller.h"
+
+#import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+NSString* const kQRGeneratorDoneButtonId = @"kQRGeneratorDoneButtonId";
+
+@implementation QRGeneratorViewController
+
+#pragma mark - UIViewController
+
+- (void)viewDidLoad {
+  [super viewDidLoad];
+
+  [self setUpNavigation];
+}
+
+#pragma mark - Private Methods
+
+- (void)setUpNavigation {
+  // Set shadowImage to an empty image to remove the separator between the
+  // navigation bar and the main content.
+  self.navigationController.navigationBar.shadowImage = [[UIImage alloc] init];
+
+  // Set background color.
+  [self.navigationController.navigationBar setTranslucent:NO];
+  [self.navigationController.navigationBar
+      setBarTintColor:UIColor.cr_systemBackgroundColor];
+  [self.navigationController.view
+      setBackgroundColor:UIColor.cr_systemBackgroundColor];
+
+  // Add a Done button to the navigation bar.
+  UIBarButtonItem* doneButton = [[UIBarButtonItem alloc]
+      initWithBarButtonSystemItem:UIBarButtonSystemItemDone
+                           target:self.handler
+                           action:@selector(hideQRCode)];
+  doneButton.accessibilityIdentifier = kQRGeneratorDoneButtonId;
+  self.navigationItem.rightBarButtonItem = doneButton;
+}
+
+@end
diff --git a/ios/chrome/browser/ui/qr_generator/qr_generator_view_controller_unittest.mm b/ios/chrome/browser/ui/qr_generator/qr_generator_view_controller_unittest.mm
new file mode 100644
index 0000000..7157979
--- /dev/null
+++ b/ios/chrome/browser/ui/qr_generator/qr_generator_view_controller_unittest.mm
@@ -0,0 +1,63 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/qr_generator/qr_generator_view_controller.h"
+
+#import "ios/chrome/browser/ui/commands/qr_generation_commands.h"
+#import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
+#include "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#include "url/gurl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+class QRGeneratorViewControllerTest : public PlatformTest {
+ public:
+  QRGeneratorViewControllerTest()
+      : view_controller_([[QRGeneratorViewController alloc] init]) {}
+
+ protected:
+  QRGeneratorViewController* view_controller_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(QRGeneratorViewControllerTest);
+};
+
+// Tests that a Done button gets added to the navigation bar, and its action
+// dispatches the right command.
+TEST_F(QRGeneratorViewControllerTest, DoneButton_DispatchesCommand) {
+  // Set-up mocked handler.
+  id mockedHandler = OCMStrictProtocolMock(@protocol(QRGenerationCommands));
+  [[mockedHandler expect] hideQRCode];
+  [view_controller_ setHandler:mockedHandler];
+
+  // Set-up mocked navigation item.
+  __block UIBarButtonItem* capturedButton;
+  id navItemMock = OCMStrictClassMock([UINavigationItem class]);
+  [[navItemMock expect]
+      setRightBarButtonItem:[OCMArg
+                                checkWithBlock:^BOOL(UIBarButtonItem* button) {
+                                  capturedButton = button;
+                                  return !!capturedButton;
+                                }]];
+
+  id vcPartialMock = OCMPartialMock(view_controller_);
+  OCMStub([vcPartialMock navigationItem]).andReturn(navItemMock);
+
+  [view_controller_ viewDidLoad];
+
+  // Button should've been given to the navigationItem.
+  [navItemMock verify];
+
+  // Simulate a click on the button.
+  [[UIApplication sharedApplication] sendAction:capturedButton.action
+                                             to:capturedButton.target
+                                           from:nil
+                                       forEvent:nil];
+
+  // Callback should've gotten invoked.
+  [mockedHandler verify];
+}
diff --git a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
index b39f97e..b5798f4 100644
--- a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
@@ -1168,7 +1168,7 @@
 #pragma mark ChromeIdentityServiceObserver
 
 - (void)profileUpdate:(ChromeIdentity*)identity {
-  if (identity == _identity) {
+  if ([_identity isEqual:identity]) {
     [self reloadAccountCell];
   }
 }
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/tab_grid_transition_handler.mm b/ios/chrome/browser/ui/tab_grid/transitions/tab_grid_transition_handler.mm
index df06e66e..dc2c68c 100644
--- a/ios/chrome/browser/ui/tab_grid/transitions/tab_grid_transition_handler.mm
+++ b/ios/chrome/browser/ui/tab_grid/transitions/tab_grid_transition_handler.mm
@@ -100,6 +100,8 @@
   browser.view.frame = tabGrid.view.bounds;
   [tabGrid.view addSubview:browser.view];
 
+  browser.view.accessibilityViewIsModal = YES;
+
   browser.view.alpha = 0;
 
   if (UIAccessibilityIsReduceMotionEnabled() ||
diff --git a/ios/chrome/credential_provider_extension/BUILD.gn b/ios/chrome/credential_provider_extension/BUILD.gn
index 760e0fa8..63f4986b 100644
--- a/ios/chrome/credential_provider_extension/BUILD.gn
+++ b/ios/chrome/credential_provider_extension/BUILD.gn
@@ -53,6 +53,7 @@
     "//base",
     "//ios/chrome/common/app_group",
     "//ios/chrome/common/app_group:command",
+    "//ios/chrome/common/credential_provider",
     "//ios/chrome/common/ui/colors",
     "//ios/chrome/common/ui/reauthentication",
     "//ios/chrome/credential_provider_extension/resources",
diff --git a/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm b/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm
index e2abcd2..174b7a0 100644
--- a/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm
+++ b/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm
@@ -4,6 +4,8 @@
 
 #import "ios/chrome/credential_provider_extension/credential_provider_view_controller.h"
 
+#import "ios/chrome/common/credential_provider/archivable_credential_store.h"
+#import "ios/chrome/common/credential_provider/constants.h"
 #import "ios/chrome/common/ui/reauthentication/reauthentication_module.h"
 #import "ios/chrome/credential_provider_extension/reauthentication_handler.h"
 #import "ios/chrome/credential_provider_extension/ui/credential_list_coordinator.h"
@@ -14,6 +16,9 @@
 
 @interface CredentialProviderViewController () <SuccessfulReauthTimeAccessor>
 
+// Interface for the persistent credential store.
+@property(nonatomic, strong) id<CredentialStore> credentialStore;
+
 // List coordinator that shows the list of passwords when started.
 @property(nonatomic, strong) CredentialListCoordinator* listCoordinator;
 
@@ -38,6 +43,7 @@
     if (result != ReauthenticationResult::kFailure) {
       self.listCoordinator = [[CredentialListCoordinator alloc]
           initWithBaseViewController:self
+                     credentialStore:self.credentialStore
                              context:self.extensionContext
                   serviceIdentifiers:serviceIdentifiers];
       [self.listCoordinator start];
@@ -55,6 +61,14 @@
 
 #pragma mark - Properties
 
+- (id<CredentialStore>)credentialStore {
+  if (!_credentialStore) {
+    _credentialStore = [[ArchivableCredentialStore alloc]
+        initWithFileURL:CredentialProviderSharedArchivableStoreURL()];
+  }
+  return _credentialStore;
+}
+
 - (ReauthenticationHandler*)reauthenticationHandler {
   if (!_reauthenticationHandler) {
     _reauthenticationHandler = [[ReauthenticationHandler alloc]
diff --git a/ios/chrome/credential_provider_extension/ui/credential_list_coordinator.h b/ios/chrome/credential_provider_extension/ui/credential_list_coordinator.h
index b8a9c3d..94a633fc 100644
--- a/ios/chrome/credential_provider_extension/ui/credential_list_coordinator.h
+++ b/ios/chrome/credential_provider_extension/ui/credential_list_coordinator.h
@@ -10,6 +10,7 @@
 @class ASCredentialServiceIdentifier;
 @class ASCredentialProviderExtensionContext;
 @class UIViewController;
+@protocol CredentialStore;
 
 // This feature presents a list of credentials for the user to choose.
 @interface CredentialListCoordinator : NSObject
@@ -19,6 +20,7 @@
 // can be nil.
 - (instancetype)
     initWithBaseViewController:(UIViewController*)baseViewController
+               credentialStore:(id<CredentialStore>)credentialStore
                        context:(ASCredentialProviderExtensionContext*)context
             serviceIdentifiers:
                 (NSArray<ASCredentialServiceIdentifier*>*)serviceIdentifiers
diff --git a/ios/chrome/credential_provider_extension/ui/credential_list_coordinator.mm b/ios/chrome/credential_provider_extension/ui/credential_list_coordinator.mm
index 13521c4..5466dbc 100644
--- a/ios/chrome/credential_provider_extension/ui/credential_list_coordinator.mm
+++ b/ios/chrome/credential_provider_extension/ui/credential_list_coordinator.mm
@@ -29,6 +29,9 @@
 // The mediator of this coordinator.
 @property(nonatomic, strong) CredentialListMediator* mediator;
 
+// Interface for the persistent credential store.
+@property(nonatomic, weak) id<CredentialStore> credentialStore;
+
 // The extension context in which the credential list was started.
 @property(nonatomic, weak) ASCredentialProviderExtensionContext* context;
 
@@ -42,6 +45,7 @@
 
 - (instancetype)
     initWithBaseViewController:(UIViewController*)baseViewController
+               credentialStore:(id<CredentialStore>)credentialStore
                        context:(ASCredentialProviderExtensionContext*)context
             serviceIdentifiers:
                 (NSArray<ASCredentialServiceIdentifier*>*)serviceIdentifiers {
@@ -50,6 +54,7 @@
     _baseViewController = baseViewController;
     _context = context;
     _serviceIdentifiers = serviceIdentifiers;
+    _credentialStore = credentialStore;
   }
   return self;
 }
@@ -60,6 +65,7 @@
   self.mediator = [[CredentialListMediator alloc]
         initWithConsumer:credentialListViewController
                UIHandler:self
+         credentialStore:self.credentialStore
                  context:self.context
       serviceIdentifiers:self.serviceIdentifiers];
 
diff --git a/ios/chrome/credential_provider_extension/ui/credential_list_mediator.h b/ios/chrome/credential_provider_extension/ui/credential_list_mediator.h
index e8a229b..75d2fb0 100644
--- a/ios/chrome/credential_provider_extension/ui/credential_list_mediator.h
+++ b/ios/chrome/credential_provider_extension/ui/credential_list_mediator.h
@@ -11,6 +11,7 @@
 @class ASCredentialProviderExtensionContext;
 @protocol CredentialListConsumer;
 @protocol CredentialListUIHandler;
+@protocol CredentialStore;
 
 // This mediator fetches and organizes the credentials for its consumer.
 @interface CredentialListMediator : NSObject
@@ -18,6 +19,7 @@
 // |serviceIdentifiers| will be used to prioritize data, can be nil.
 - (instancetype)initWithConsumer:(id<CredentialListConsumer>)consumer
                        UIHandler:(id<CredentialListUIHandler>)UIHandler
+                 credentialStore:(id<CredentialStore>)credentialStore
                          context:(ASCredentialProviderExtensionContext*)context
               serviceIdentifiers:
                   (NSArray<ASCredentialServiceIdentifier*>*)serviceIdentifiers
diff --git a/ios/chrome/credential_provider_extension/ui/credential_list_mediator.mm b/ios/chrome/credential_provider_extension/ui/credential_list_mediator.mm
index f51eccff..5fb7d0f7 100644
--- a/ios/chrome/credential_provider_extension/ui/credential_list_mediator.mm
+++ b/ios/chrome/credential_provider_extension/ui/credential_list_mediator.mm
@@ -6,6 +6,7 @@
 
 #import <AuthenticationServices/AuthenticationServices.h>
 
+#import "ios/chrome/common/credential_provider/credential_store.h"
 #import "ios/chrome/credential_provider_extension/ui/credential_list_consumer.h"
 #import "ios/chrome/credential_provider_extension/ui/credential_list_ui_handler.h"
 
@@ -21,6 +22,9 @@
 // The consumer for this mediator.
 @property(nonatomic, weak) id<CredentialListConsumer> consumer;
 
+// Interface for the persistent credential store.
+@property(nonatomic, weak) id<CredentialStore> credentialStore;
+
 // The service identifiers to be prioritized.
 @property(nonatomic, strong)
     NSArray<ASCredentialServiceIdentifier*>* serviceIdentifiers;
@@ -34,6 +38,7 @@
 
 - (instancetype)initWithConsumer:(id<CredentialListConsumer>)consumer
                        UIHandler:(id<CredentialListUIHandler>)UIHandler
+                 credentialStore:(id<CredentialStore>)credentialStore
                          context:(ASCredentialProviderExtensionContext*)context
               serviceIdentifiers:
                   (NSArray<ASCredentialServiceIdentifier*>*)serviceIdentifiers {
@@ -43,15 +48,20 @@
     _UIHandler = UIHandler;
     _consumer = consumer;
     _consumer.delegate = self;
+    _credentialStore = credentialStore;
     _context = context;
   }
   return self;
 }
 
 - (void)fetchCredentials {
-  // TODO(crbug.com/1045454): Implement this method. For now present the empty
-  // credentials screen all the time.
-  [self.UIHandler showEmptyCredentials];
+  // TODO(crbug.com/1045454): Implement ordering and suggestions.
+  NSArray<id<Credential>>* allCredentials = self.credentialStore.credentials;
+  if (!allCredentials.count) {
+    [self.UIHandler showEmptyCredentials];
+    return;
+  }
+  [self.consumer presentSuggestedPasswords:nil allPasswords:allCredentials];
 }
 
 #pragma mark - CredentialListConsumerDelegate
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index 073e605..8b7662f4 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -282,6 +282,7 @@
     "//ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs:unit_tests",
     "//ios/chrome/browser/ui/popup_menu:unit_tests",
     "//ios/chrome/browser/ui/presenters:unit_tests",
+    "//ios/chrome/browser/ui/qr_generator:unit_tests",
     "//ios/chrome/browser/ui/reading_list:unit_tests",
     "//ios/chrome/browser/ui/recent_tabs:unit_tests",
     "//ios/chrome/browser/ui/sad_tab:unit_tests",
diff --git a/media/gpu/ipc/service/gpu_video_decode_accelerator.cc b/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
index ce765c85..efe9f60 100644
--- a/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
+++ b/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
@@ -466,42 +466,45 @@
         return;
       }
 
-      gpu::gles2::TextureRef* texture_ref =
-          texture_manager->GetTexture(buffer_texture_ids[j]);
-      if (texture_ref) {
-        gpu::gles2::Texture* info = texture_ref->texture();
-        if (texture_target_ == GL_TEXTURE_EXTERNAL_OES ||
-            texture_target_ == GL_TEXTURE_RECTANGLE_ARB) {
-          // These textures have their dimensions defined by the underlying
-          // storage.
-          // Use |texture_dimensions_| for this size.
-          texture_manager->SetLevelInfo(texture_ref, texture_target_, 0,
-                                        GL_RGBA, texture_dimensions_.width(),
-                                        texture_dimensions_.height(), 1, 0,
-                                        GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
-        } else {
-          // For other targets, texture dimensions should already be defined.
-          GLsizei width = 0, height = 0;
-          info->GetLevelSize(texture_target_, 0, &width, &height, nullptr);
-          if (width != texture_dimensions_.width() ||
-              height != texture_dimensions_.height()) {
-            DLOG(ERROR) << "Size mismatch for texture id "
-                        << buffer_texture_ids[j];
-            NotifyError(VideoDecodeAccelerator::INVALID_ARGUMENT);
-            return;
-          }
+      if (texture_manager) {
+        gpu::gles2::TextureRef* texture_ref =
+            texture_manager->GetTexture(buffer_texture_ids[j]);
+        if (texture_ref) {
+          gpu::gles2::Texture* info = texture_ref->texture();
+          if (texture_target_ == GL_TEXTURE_EXTERNAL_OES ||
+              texture_target_ == GL_TEXTURE_RECTANGLE_ARB) {
+            // These textures have their dimensions defined by the underlying
+            // storage.
+            // Use |texture_dimensions_| for this size.
+            texture_manager->SetLevelInfo(
+                texture_ref, texture_target_, 0, GL_RGBA,
+                texture_dimensions_.width(), texture_dimensions_.height(), 1, 0,
+                GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
+          } else {
+            // For other targets, texture dimensions should already be defined.
+            GLsizei width = 0, height = 0;
+            info->GetLevelSize(texture_target_, 0, &width, &height, nullptr);
+            if (width != texture_dimensions_.width() ||
+                height != texture_dimensions_.height()) {
+              DLOG(ERROR) << "Size mismatch for texture id "
+                          << buffer_texture_ids[j];
+              NotifyError(VideoDecodeAccelerator::INVALID_ARGUMENT);
+              return;
+            }
 
-          // TODO(dshwang): after moving to D3D11, remove this.
-          // https://crbug.com/438691
-          GLenum format = video_decode_accelerator_->GetSurfaceInternalFormat();
-          if (format != GL_RGBA) {
-            DCHECK(format == GL_BGRA_EXT);
-            texture_manager->SetLevelInfo(texture_ref, texture_target_, 0,
-                                          format, width, height, 1, 0, format,
-                                          GL_UNSIGNED_BYTE, gfx::Rect());
+            // TODO(dshwang): after moving to D3D11, remove this.
+            // https://crbug.com/438691
+            GLenum format =
+                video_decode_accelerator_->GetSurfaceInternalFormat();
+            if (format != GL_RGBA) {
+              DCHECK(format == GL_BGRA_EXT);
+              texture_manager->SetLevelInfo(texture_ref, texture_target_, 0,
+                                            format, width, height, 1, 0, format,
+                                            GL_UNSIGNED_BYTE, gfx::Rect());
+            }
           }
+          current_textures.push_back(texture_ref);
         }
-        current_textures.push_back(texture_ref);
       }
       service_ids.push_back(texture_base->service_id());
     }
diff --git a/net/quic/mock_crypto_client_stream.cc b/net/quic/mock_crypto_client_stream.cc
index 2eba680..93e9706 100644
--- a/net/quic/mock_crypto_client_stream.cc
+++ b/net/quic/mock_crypto_client_stream.cc
@@ -182,7 +182,7 @@
               std::make_unique<NullDecrypter>(Perspective::IS_CLIENT));
         }
         session()->connection()->SetEncrypter(ENCRYPTION_INITIAL, nullptr);
-        session()->connection()->SetEncrypter(
+        session()->OnNewEncryptionKeyAvailable(
             ENCRYPTION_FORWARD_SECURE,
             std::make_unique<NullEncrypter>(Perspective::IS_CLIENT));
       }
@@ -264,7 +264,7 @@
             std::make_unique<NullDecrypter>(Perspective::IS_CLIENT));
       }
       session()->connection()->SetEncrypter(ENCRYPTION_INITIAL, nullptr);
-      session()->connection()->SetEncrypter(
+      session()->OnNewEncryptionKeyAvailable(
           ENCRYPTION_FORWARD_SECURE,
           std::make_unique<NullEncrypter>(Perspective::IS_CLIENT));
     }
diff --git a/net/quic/quic_chromium_client_session_test.cc b/net/quic/quic_chromium_client_session_test.cc
index 71e76fc..81bdb37 100644
--- a/net/quic/quic_chromium_client_session_test.cc
+++ b/net/quic/quic_chromium_client_session_test.cc
@@ -1900,7 +1900,8 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, DetectPathDegradingDuringHandshake) {
-  if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) {
+  if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 ||
+      GetQuicReloadableFlag(quic_use_blackhole_detector)) {
     // TODO(nharper, b/112643533): Figure out why this test fails when TLS is
     // enabled and fix it.
     return;
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index a30f23e..8ef66a8e 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -380,11 +380,11 @@
 // keys are available).
 QUIC_FLAG(bool,
           FLAGS_quic_restart_flag_quic_send_settings_on_write_key_available,
-          false)
+          true)
 
 // If true, use blackhole detector in QuicConnection to detect path degrading
 // and network blackhole.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_use_blackhole_detector, false)
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_use_blackhole_detector, true)
 
 // If true, use idle network detector to detect handshake timeout and idle
 // network timeout.
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index 75204a8c..1cb6fa2 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -3307,8 +3307,10 @@
 // Verify that if a QUIC connection RTOs, the QuicHttpStream will
 // return QUIC_PROTOCOL_ERROR.
 TEST_P(QuicNetworkTransactionTest, TooManyRtosAfterHandshakeConfirmed) {
-  if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) {
+  if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 ||
+      GetQuicReloadableFlag(quic_use_blackhole_detector)) {
     // QUIC with TLS1.3 handshake doesn't support 0-rtt.
+    // TODO(fayang): Add time driven TOO_MANY_RTOS test.
     return;
   }
 
@@ -3436,8 +3438,10 @@
 // QUIC will not be marked as broken.
 TEST_P(QuicNetworkTransactionTest,
        TooManyRtosAfterHandshakeConfirmedAndStreamReset) {
-  if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) {
+  if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 ||
+      GetQuicReloadableFlag(quic_use_blackhole_detector)) {
     // QUIC with TLS1.3 handshake doesn't support 0-rtt.
+    // TODO(fayang): Add time driven TOO_MANY_RTOS test.
     return;
   }
 
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 3422b14..65fcd744 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -97,8 +97,10 @@
 #include "services/network/throttling/network_conditions.h"
 #include "services/network/throttling/throttling_controller.h"
 #include "services/network/throttling/throttling_network_transaction_factory.h"
+#include "services/network/trust_tokens/has_trust_tokens_answerer.h"
 #include "services/network/trust_tokens/pending_trust_token_store.h"
 #include "services/network/trust_tokens/sqlite_trust_token_persister.h"
+#include "services/network/trust_tokens/suitable_trust_token_origin.h"
 #include "services/network/trust_tokens/trust_token_parameterization.h"
 #include "services/network/trust_tokens/trust_token_store.h"
 #include "services/network/url_loader.h"
@@ -572,6 +574,25 @@
       std::move(receiver));
 }
 
+void NetworkContext::GetHasTrustTokensAnswerer(
+    mojo::PendingReceiver<mojom::HasTrustTokensAnswerer> receiver,
+    const url::Origin& top_frame_origin) {
+  // Only called when Trust Tokens is enabled, i.e. trust_token_store_ is
+  // non-null.
+  DCHECK(trust_token_store_);
+
+  base::Optional<SuitableTrustTokenOrigin> suitable_top_frame_origin =
+      SuitableTrustTokenOrigin::Create(top_frame_origin);
+
+  // It's safe to dereference |suitable_top_frame_origin| here as, during the
+  // process of vending the HasTrustTokensAnswerer, the browser ensures that
+  // the requesting context's top frame origin is suitable for Trust Tokens.
+  auto answerer = std::make_unique<HasTrustTokensAnswerer>(
+      std::move(*suitable_top_frame_origin), trust_token_store_.get());
+
+  has_trust_tokens_answerers_.Add(std::move(answerer), std::move(receiver));
+}
+
 void NetworkContext::OnProxyLookupComplete(
     ProxyLookupRequest* proxy_lookup_request) {
   auto it = proxy_lookup_requests_.find(proxy_lookup_request);
diff --git a/services/network/network_context.h b/services/network/network_context.h
index b529d0d4..1c1446d 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -195,6 +195,9 @@
       bool is_service_worker,
       int32_t process_id,
       int32_t routing_id) override;
+  void GetHasTrustTokensAnswerer(
+      mojo::PendingReceiver<mojom::HasTrustTokensAnswerer> receiver,
+      const url::Origin& top_frame_origin) override;
   void ClearNetworkingHistorySince(
       base::Time time,
       base::OnceClosure completion_callback) override;
@@ -577,6 +580,12 @@
   // See the comment for |trust_token_store()|.
   std::unique_ptr<PendingTrustTokenStore> trust_token_store_;
 
+  // Ordering: this must be after |trust_token_store_| since the
+  // HasTrustTokensAnswerers are provided non-owning pointers to
+  // |trust_token_store_|.
+  mojo::UniqueReceiverSet<mojom::HasTrustTokensAnswerer>
+      has_trust_tokens_answerers_;
+
 #if !defined(OS_IOS)
   std::unique_ptr<WebSocketFactory> websocket_factory_;
 #endif  // !defined(OS_IOS)
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index 797d8c6..02675e0c 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -39,6 +39,7 @@
 import "services/network/public/mojom/site_for_cookies.mojom";
 import "services/network/public/mojom/ssl_config.mojom";
 import "services/network/public/mojom/tcp_socket.mojom";
+import "services/network/public/mojom/trust_tokens.mojom";
 import "services/network/public/mojom/udp_socket.mojom";
 import "services/network/public/mojom/url_loader.mojom";
 import "services/network/public/mojom/url_loader_factory.mojom";
@@ -864,6 +865,24 @@
       bool is_service_worker,
       int32 process_id, int32 routing_id);
 
+  // Provides a HasTrustTokensAnswerer scoped to the given top-frame origin
+  // (subsequent calls to its HasTrustTokens(issuer) method will attempt to
+  // associate |issuer| with |top_frame_origin|, in the sense of
+  // TrustTokenStore::SetAssociation, and reject if |top_frame_origin| is
+  // already at its number-of-associated-issuers limit).
+  //
+  // The caller must ensure (enforced by ReportBadMessage) that
+  // |top_frame_origin| is both
+  // (1) potentially trustworthy and
+  // (2) either HTTP or HTTPS.
+  //
+  // The first is a general security requirement; the second is in order to
+  // ensure that the origin has a unique serialization (and, consequently, is
+  // suitable for keying persistent Trust Tokens state).
+  GetHasTrustTokensAnswerer(
+      pending_receiver<HasTrustTokensAnswerer> has_trust_tokens_answerer,
+      url.mojom.Origin top_frame_origin);
+
   // Clears network objects with implicit URL history information. Data related
   // to events that happened prior to |start_time| may be retained. Only applies
   // to network objects without more specific methods (Cookies,
diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h
index 7d09d1b..51593e2 100644
--- a/services/network/test/test_network_context.h
+++ b/services/network/test/test_network_context.h
@@ -59,6 +59,9 @@
       bool is_service_worker,
       int32_t process_id,
       int32_t routing_id) override {}
+  void GetHasTrustTokensAnswerer(
+      mojo::PendingReceiver<mojom::HasTrustTokensAnswerer> receiver,
+      const url::Origin& top_frame_origin) override {}
   void ClearNetworkingHistorySince(
       base::Time start_time,
       ClearNetworkingHistorySinceCallback callback) override {}
diff --git a/testing/buildbot/filters/android.emulator.content_browsertests.filter b/testing/buildbot/filters/android.emulator.content_browsertests.filter
index c2f3b49..1f5b340 100644
--- a/testing/buildbot/filters/android.emulator.content_browsertests.filter
+++ b/testing/buildbot/filters/android.emulator.content_browsertests.filter
@@ -1,8 +1,8 @@
 # crbug.com/1054829
--All/TouchActionBrowserTest.PanXAtYAreaWithTimeout/*
--All/TouchActionBrowserTest.TouchActionNone/*
--All/TouchActionBrowserTest.TwoFingerPanXAtYAreaWithTimeout/*
--All/TouchActionBrowserTest.BlockDoubleTapDragZoom/*
+-All/TouchActionBrowserTest.PanXAtYAreaWithTimeout
+-All/TouchActionBrowserTest.TouchActionNone
+-All/TouchActionBrowserTest.TwoFingerPanXAtYAreaWithTimeout
+-All/TouchActionBrowserTest.BlockDoubleTapDragZoom
 
 # crbug.com/1056878
 -BackForwardCacheBrowserTest.VideoSuspendAndResume
diff --git a/testing/test.gni b/testing/test.gni
index eda4330..d2b54c2 100644
--- a/testing/test.gni
+++ b/testing/test.gni
@@ -217,7 +217,7 @@
       package = ":$_pkg_target"
       package_name_override = _output_name
 
-      deps = [ "//testing/buildbot/filters:fuchsia_filters" ]
+      data_deps = [ "//testing/buildbot/filters:fuchsia_filters" ]
     }
 
     cr_fuchsia_package(_pkg_target) {
diff --git a/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc b/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc
index 771b0b1..0363733f 100644
--- a/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc
+++ b/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc
@@ -91,8 +91,6 @@
       return "WebFileSystem";
     case WebSchedulerTrackedFeature::kAppBanner:
       return "AppBanner";
-    case WebSchedulerTrackedFeature::kPrinting:
-      return "Printing";
   }
 }
 
diff --git a/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h b/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h
index a71ad8cb..736fd04 100644
--- a/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h
+++ b/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h
@@ -85,10 +85,9 @@
   kOutstandingNetworkRequestXHR = 41,
 
   kAppBanner = 42,
-  kPrinting = 43,
 
   // NB: This enum is used in a bitmask, so kMaxValue must be less than 64.
-  kMaxValue = kPrinting
+  kMaxValue = kAppBanner
 };
 
 static_assert(static_cast<uint32_t>(WebSchedulerTrackedFeature::kMaxValue) < 64,
diff --git a/third_party/blink/public/mojom/appcache/appcache.mojom b/third_party/blink/public/mojom/appcache/appcache.mojom
index f3ea903..39d2707 100644
--- a/third_party/blink/public/mojom/appcache/appcache.mojom
+++ b/third_party/blink/public/mojom/appcache/appcache.mojom
@@ -49,7 +49,6 @@
   bool is_foreign;
   bool is_explicit;
   int64 response_id;
-  mojo_base.mojom.Time token_expires;
 };
 
 struct AppCacheErrorDetails {
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index a416dc5..764f8fe 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -2539,6 +2539,7 @@
   // The above items are available in M83 branch.
 
   kWrongBaselineOfButtonElement = 3201,
+  kV8Document_HasTrustToken_Method = 3202,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/web/web_view.h b/third_party/blink/public/web/web_view.h
index 406c19b..02dd65e 100644
--- a/third_party/blink/public/web/web_view.h
+++ b/third_party/blink/public/web/web_view.h
@@ -35,7 +35,6 @@
 #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
 #include "third_party/blink/public/common/page/page_visibility_state.h"
 #include "third_party/blink/public/mojom/input/focus_type.mojom-shared.h"
-#include "third_party/blink/public/mojom/manifest/display_mode.mojom-shared.h"
 #include "third_party/blink/public/platform/web_drag_operation.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -279,9 +278,6 @@
   // Indicates that view's preferred size changes will be sent to the browser.
   virtual void EnablePreferredSizeChangedMode() = 0;
 
-  // Sets the display mode of the web app.
-  virtual void SetDisplayMode(blink::mojom::DisplayMode) = 0;
-
   // Sets the ratio as computed by computePageScaleConstraints.
   // TODO(oshima): Remove this once the device scale factor implementation is
   // fully migrated to use zooming mechanism.
diff --git a/third_party/blink/public/web/web_widget.h b/third_party/blink/public/web/web_widget.h
index a8fb3381..5a5c579 100644
--- a/third_party/blink/public/web/web_widget.h
+++ b/third_party/blink/public/web/web_widget.h
@@ -39,6 +39,7 @@
 #include "cc/trees/layer_tree_host_client.h"
 #include "third_party/blink/public/common/input/web_menu_source_type.h"
 #include "third_party/blink/public/common/metrics/document_update_reason.h"
+#include "third_party/blink/public/mojom/manifest/display_mode.mojom-shared.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_input_event_result.h"
 #include "third_party/blink/public/platform/web_rect.h"
@@ -199,6 +200,10 @@
   // Called to inform the WebWidget that it has gained or lost keyboard focus.
   virtual void SetFocus(bool) {}
 
+  // Sets the display mode, which comes from the top-level browsing context and
+  // is applied to all widgets.
+  virtual void SetDisplayMode(mojom::DisplayMode) {}
+
   // Returns the anchor and focus bounds of the current selection.
   // If the selection range is empty, it returns the caret bounds.
   virtual bool SelectionBounds(WebRect& anchor, WebRect& focus) const {
diff --git a/third_party/blink/renderer/core/css/media_values.cc b/third_party/blink/renderer/core/css/media_values.cc
index 3ff366de..fda4355 100644
--- a/third_party/blink/renderer/core/css/media_values.cc
+++ b/third_party/blink/renderer/core/css/media_values.cc
@@ -21,6 +21,7 @@
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/platform/graphics/color_space_gamut.h"
+#include "third_party/blink/renderer/platform/widget/frame_widget.h"
 
 namespace blink {
 
@@ -123,18 +124,19 @@
   return frame->View()->MediaType();
 }
 
-blink::mojom::DisplayMode MediaValues::CalculateDisplayMode(LocalFrame* frame) {
+mojom::blink::DisplayMode MediaValues::CalculateDisplayMode(LocalFrame* frame) {
   DCHECK(frame);
+
   blink::mojom::DisplayMode mode =
       frame->GetPage()->GetSettings().GetDisplayModeOverride();
-
-  if (mode != blink::mojom::DisplayMode::kUndefined)
+  if (mode != mojom::blink::DisplayMode::kUndefined)
     return mode;
 
-  if (!frame->View())
-    return blink::mojom::DisplayMode::kBrowser;
+  FrameWidget* widget = frame->GetWidgetForLocalRoot();
+  if (!widget)  // Is null in non-ordinary Pages.
+    return mojom::blink::DisplayMode::kBrowser;
 
-  return frame->View()->DisplayMode();
+  return widget->DisplayMode();
 }
 
 bool MediaValues::CalculateThreeDEnabled(LocalFrame* frame) {
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 09f4c78..690f174 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -48,6 +48,7 @@
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "services/metrics/public/mojom/ukm_interface.mojom-blink.h"
 #include "services/network/public/mojom/ip_address_space.mojom-blink.h"
+#include "services/network/public/mojom/trust_tokens.mojom-blink.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/common/feature_policy/document_policy_features.h"
@@ -71,6 +72,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v0_custom_element_constructor_builder.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_element_creation_options.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_element_registration_options.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
 #include "third_party/blink/renderer/bindings/core/v8/window_proxy.h"
 #include "third_party/blink/renderer/core/accessibility/ax_context.h"
 #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
@@ -767,6 +769,7 @@
       display_lock_document_state_(
           MakeGarbageCollected<DisplayLockDocumentState>(this)),
       permission_service_(GetExecutionContext()),
+      has_trust_tokens_answerer_(GetExecutionContext()),
       font_preload_manager_(*this) {
   security_initializer.ApplyPendingDataToDocument(*this);
   GetOriginTrialContext()->BindExecutionContext(GetExecutionContext());
@@ -6221,6 +6224,110 @@
   return promise;
 }
 
+ScriptPromise Document::hasTrustToken(ScriptState* script_state,
+                                      const String& issuer,
+                                      ExceptionState& exception_state) {
+  ScriptPromiseResolver* resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+
+  ScriptPromise promise = resolver->Promise();
+
+  // Trust Tokens state is keyed by issuer and top-frame origins that
+  // are both (1) HTTP or HTTPS and (2) potentially trustworthy. Consequently,
+  // we can return early if either the issuer or the top-frame origin fails to
+  // satisfy either of these requirements.
+  KURL issuer_url = KURL(issuer);
+  auto issuer_origin = SecurityOrigin::Create(issuer_url);
+  if (!issuer_url.ProtocolIsInHTTPFamily() ||
+      !issuer_origin->IsPotentiallyTrustworthy()) {
+    exception_state.ThrowTypeError(
+        "hasTrustToken: Trust token issuer origins must be both HTTP(S) and "
+        "secure (\"potentially trustworthy\").");
+    resolver->Reject(exception_state);
+    return promise;
+  }
+
+  scoped_refptr<const SecurityOrigin> top_frame_origin = TopFrameOrigin();
+  if (!top_frame_origin) {
+    // Note: One case where there might be no top frame origin is if this
+    // document is destroyed. In this case, this function will return
+    // `undefined`. Still bother adding the exception and rejecting, just in
+    // case there are other situations in which the top frame origin might be
+    // absent.
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "hasTrustToken: Cannot execute in "
+                                      "documents lacking top-frame origins.");
+    resolver->Reject(exception_state);
+    return promise;
+  }
+
+  if (!top_frame_origin->IsPotentiallyTrustworthy() ||
+      (top_frame_origin->Protocol() != url::kHttpsScheme &&
+       top_frame_origin->Protocol() != url::kHttpScheme)) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kNotAllowedError,
+        "hasTrustToken: Cannot execute in "
+        "documents without secure, HTTP(S), top-frame origins.");
+    resolver->Reject(exception_state);
+    return promise;
+  }
+
+  if (!has_trust_tokens_answerer_.is_bound()) {
+    GetBrowserInterfaceBroker().GetInterface(
+        has_trust_tokens_answerer_.BindNewPipeAndPassReceiver(
+            GetExecutionContext()->GetTaskRunner(TaskType::kInternalDefault)));
+    has_trust_tokens_answerer_.set_disconnect_handler(
+        WTF::Bind(&Document::HasTrustTokensAnswererConnectionError,
+                  WrapWeakPersistent(this)));
+  }
+
+  pending_has_trust_tokens_resolvers_.insert(resolver);
+
+  has_trust_tokens_answerer_->HasTrustTokens(
+      issuer_origin,
+      WTF::Bind(
+          [](WeakPersistent<ScriptPromiseResolver> resolver,
+             WeakPersistent<Document> document,
+             network::mojom::blink::HasTrustTokensResultPtr result) {
+            // If there was a Mojo connection error, the promise was already
+            // resolved and deleted.
+            if (!base::Contains(document->pending_has_trust_tokens_resolvers_,
+                                resolver)) {
+              return;
+            }
+
+            if (result->status ==
+                network::mojom::blink::TrustTokenOperationStatus::kOk) {
+              resolver->Resolve(result->has_trust_tokens);
+            } else {
+              ScriptState* state = resolver->GetScriptState();
+              ScriptState::Scope scope(state);
+              resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
+                  state->GetIsolate(), DOMExceptionCode::kOperationError,
+                  "Failed to retrieve hasTrustToken response. (Would "
+                  "associating the given issuer with this top-level origin "
+                  "have exceeded its number-of-issuers limit?)"));
+            }
+
+            document->pending_has_trust_tokens_resolvers_.erase(resolver);
+          },
+          WrapWeakPersistent(resolver), WrapWeakPersistent(this)));
+
+  return promise;
+}
+
+void Document::HasTrustTokensAnswererConnectionError() {
+  has_trust_tokens_answerer_.reset();
+  for (const auto& resolver : pending_has_trust_tokens_resolvers_) {
+    ScriptState* state = resolver->GetScriptState();
+    ScriptState::Scope scope(state);
+    resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
+        state->GetIsolate(), DOMExceptionCode::kOperationError,
+        "Internal error retrieving hasTrustToken response."));
+  }
+  pending_has_trust_tokens_resolvers_.clear();
+}
+
 static bool IsValidNameNonASCII(const LChar* characters, unsigned length) {
   if (!IsValidNameStart(characters[0]))
     return false;
@@ -8228,6 +8335,8 @@
   visitor->Trace(display_lock_document_state_);
   visitor->Trace(form_to_pending_submission_);
   visitor->Trace(permission_service_);
+  visitor->Trace(has_trust_tokens_answerer_);
+  visitor->Trace(pending_has_trust_tokens_resolvers_);
   visitor->Trace(font_preload_manager_);
   Supplementable<Document>::Trace(visitor);
   TreeScope::Trace(visitor);
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 08671a4..8d3ea58 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -38,10 +38,12 @@
 #include "base/timer/elapsed_timer.h"
 #include "net/cookies/site_for_cookies.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
+#include "services/network/public/mojom/trust_tokens.mojom-blink.h"
 #include "third_party/blink/public/common/metrics/document_update_reason.h"
 #include "third_party/blink/public/mojom/input/focus_type.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/permissions/permission.mojom-blink.h"
 #include "third_party/blink/public/mojom/scroll/scrollbar_mode.mojom-blink.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/accessibility/axid.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/media_value_change.h"
@@ -1138,6 +1140,7 @@
   const KURL& CookieURL() const { return cookie_url_; }
   void SetCookieURL(const KURL& url) { cookie_url_ = url; }
 
+  // Returns null if the document is not attached to a frame.
   scoped_refptr<const SecurityOrigin> TopFrameOrigin() const;
 
   net::SiteForCookies SiteForCookies() const;
@@ -1153,6 +1156,15 @@
   ScriptPromise hasStorageAccess(ScriptState* script_state) const;
   ScriptPromise requestStorageAccess(ScriptState* script_state);
 
+  // Sends a query via Mojo to ask whether the user has any trust tokens. This
+  // can reject on permissions errors (e.g. associating |issuer| with the
+  // top-level origin would exceed the top-level origin's limit on the number of
+  // associated issuers) or on other internal errors (e.g. the network service
+  // is unavailable).
+  ScriptPromise hasTrustToken(ScriptState* script_state,
+                              const String& issuer,
+                              ExceptionState&);
+
   // The following implements the rule from HTML 4 for what valid names are.
   // To get this right for all the XML cases, we probably have to improve this
   // or move it and make it sensitive to the type of document.
@@ -1765,6 +1777,7 @@
   }
 
  private:
+  friend class DocumentTest;
   friend class IgnoreDestructiveWriteCountIncrementer;
   friend class ThrowOnDynamicMarkupInsertionCountIncrementer;
   friend class IgnoreOpensDuringUnloadCountIncrementer;
@@ -1903,6 +1916,10 @@
 
   void ExecuteFormSubmission(HTMLFormElement* form_element);
 
+  // Handles a connection error to |has_trust_tokens_answerer_| by rejecting all
+  // pending promises created by |hasTrustToken|.
+  void HasTrustTokensAnswererConnectionError();
+
   DocumentLifecycle lifecycle_;
 
   bool evaluate_media_queries_on_style_recalc_;
@@ -2318,6 +2335,22 @@
   // storage or not.
   HeapMojoRemote<mojom::blink::PermissionService> permission_service_;
 
+  // Mojo remote used to answer API calls asking whether the user has trust
+  // tokens (https://github.com/wicg/trust-token-api). The other endpoint
+  // is in the network service, which may crash and restart. To handle this:
+  //   1. |pending_has_trust_tokens_resolvers_| keeps track of promises
+  // depending on |has_trust_tokens_answerer_|'s answers;
+  //   2. |HasTrustTokensAnswererConnectionError| handles connection errors by
+  // rejecting all pending promises and clearing the pending set.
+  HeapMojoRemote<network::mojom::blink::HasTrustTokensAnswerer>
+      has_trust_tokens_answerer_;
+
+  // In order to be able to answer promises when the Mojo remote disconnects,
+  // maintain all pending promises here, deleting them on successful completion
+  // or on connection error, whichever comes first.
+  HeapHashSet<Member<ScriptPromiseResolver>>
+      pending_has_trust_tokens_resolvers_;
+
   FontPreloadManager font_preload_manager_;
 };
 
diff --git a/third_party/blink/renderer/core/dom/document.idl b/third_party/blink/renderer/core/dom/document.idl
index a8025f5..efce87b 100644
--- a/third_party/blink/renderer/core/dom/document.idl
+++ b/third_party/blink/renderer/core/dom/document.idl
@@ -200,6 +200,9 @@
     [MeasureAs=PrefixedPageVisibility, ImplementedAs=visibilityState] readonly attribute DOMString webkitVisibilityState;
     [MeasureAs=PrefixedPageVisibility, ImplementedAs=hidden] readonly attribute boolean webkitHidden;
 
+    // Trust Token API (https://github.com/wicg/trust-token-api)
+    [CallWith=ScriptState, Measure, RaisesException, NewObject, SecureContext, RuntimeEnabled=TrustTokens] Promise<boolean> hasTrustToken(USVString issuer);
+
     // Event handler attributes
     attribute EventHandler onbeforecopy;
     attribute EventHandler onbeforecut;
diff --git a/third_party/blink/renderer/core/dom/document_test.cc b/third_party/blink/renderer/core/dom/document_test.cc
index 5d3721c..923a1b5 100644
--- a/third_party/blink/renderer/core/dom/document_test.cc
+++ b/third_party/blink/renderer/core/dom/document_test.cc
@@ -38,11 +38,15 @@
 #include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/common/feature_policy/document_policy_features.h"
 #include "third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/isolated_world_csp.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_tester.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_dom_exception.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
 #include "third_party/blink/renderer/core/css/media_query_list_listener.h"
 #include "third_party/blink/renderer/core/css/media_query_matcher.h"
 #include "third_party/blink/renderer/core/dom/document_fragment.h"
@@ -82,6 +86,12 @@
 using network::mojom::ContentSecurityPolicyType;
 
 class DocumentTest : public PageTestBase {
+ public:
+  static void SimulateHasTrustTokensAnswererConnectionError(
+      Document* document) {
+    document->HasTrustTokensAnswererConnectionError();
+  }
+
  protected:
   void TearDown() override {
     ThreadState::Current()->CollectAllGarbageForTesting();
@@ -329,6 +339,19 @@
   bool notified_ = false;
 };
 
+bool IsDOMException(ScriptState* script_state,
+                    ScriptValue value,
+                    DOMExceptionCode code) {
+  auto* dom_exception = V8DOMException::ToImplWithTypeCheck(
+      script_state->GetIsolate(), value.V8Value());
+  if (!dom_exception)
+    return false;
+
+  // Unfortunately, it's not enough to check |dom_exception->code() == code|,
+  // as DOMException::code is only populated for the DOMExceptionCodes with
+  // "legacy code" numeric values.
+  return dom_exception->name() == DOMException(code).name();
+}
 }  // anonymous namespace
 
 TEST_F(DocumentTest, CreateRangeAdjustedToTreeScopeWithPositionInShadowTree) {
@@ -1219,6 +1242,177 @@
   EXPECT_EQ(DoubleSize(400, 960), page_size);
 }
 
+TEST(Document, HandlesDisconnectDuringHasTrustToken) {
+  // Check that a Mojo handle disconnecting during hasTrustToken operation
+  // execution results in the promise getting rejected with the proper
+  // exception.
+  V8TestingScope scope(KURL("https://trusttoken.example"));
+
+  Document& document = scope.GetDocument();
+
+  auto promise =
+      document.hasTrustToken(scope.GetScriptState(), "https://issuer.example",
+                             scope.GetExceptionState());
+  DocumentTest::SimulateHasTrustTokensAnswererConnectionError(&document);
+
+  ASSERT_TRUE(promise.IsAssociatedWith(scope.GetScriptState()));
+
+  ScriptPromiseTester promise_tester(scope.GetScriptState(), promise);
+  promise_tester.WaitUntilSettled();
+  EXPECT_TRUE(promise_tester.IsRejected());
+  EXPECT_TRUE(IsDOMException(scope.GetScriptState(), promise_tester.Value(),
+                             DOMExceptionCode::kOperationError));
+}
+
+TEST(Document, RejectsHasTrustTokenCallFromNonHttpNonHttpsDocument) {
+  // Check that hasTrustToken getting called from a secure, but
+  // non-http/non-https, document results in an exception being thrown.
+  V8TestingScope scope(KURL("file:///trusttoken.txt"));
+
+  Document& document = scope.GetDocument();
+  ScriptState* script_state = scope.GetScriptState();
+  ExceptionState exception_state(script_state->GetIsolate(),
+                                 ExceptionState::kExecutionContext, "Document",
+                                 "hasTrustToken");
+
+  auto promise = document.hasTrustToken(script_state, "https://issuer.example",
+                                        exception_state);
+
+  ScriptPromiseTester promise_tester(script_state, promise);
+  promise_tester.WaitUntilSettled();
+  EXPECT_TRUE(promise_tester.IsRejected());
+  EXPECT_TRUE(IsDOMException(script_state, promise_tester.Value(),
+                             DOMExceptionCode::kNotAllowedError));
+}
+
+namespace {
+class MockHasTrustTokensAnswerer
+    : public network::mojom::blink::HasTrustTokensAnswerer {
+ public:
+  enum Outcome { kError, kTrue, kFalse };
+  explicit MockHasTrustTokensAnswerer(Outcome outcome) : outcome_(outcome) {}
+
+  void HasTrustTokens(
+      const ::scoped_refptr<const ::blink::SecurityOrigin>& issuer,
+      HasTrustTokensCallback callback) override {
+    auto result = network::mojom::blink::HasTrustTokensResult::New();
+    result->status = network::mojom::blink::TrustTokenOperationStatus::kOk;
+    switch (outcome_) {
+      case kTrue: {
+        result->has_trust_tokens = true;
+        std::move(callback).Run(std::move(result));
+        return;
+      }
+      case kFalse: {
+        result->has_trust_tokens = false;
+        std::move(callback).Run(std::move(result));
+        return;
+      }
+      case kError: {
+        result->status =
+            network::mojom::blink::TrustTokenOperationStatus::kUnknownError;
+        std::move(callback).Run(std::move(result));
+      }
+    }
+  }
+
+  void Bind(mojo::ScopedMessagePipeHandle handle) {
+    receiver_.Bind(
+        mojo::PendingReceiver<network::mojom::blink::HasTrustTokensAnswerer>(
+            std::move(handle)));
+  }
+
+ private:
+  Outcome outcome_;
+  mojo::Receiver<network::mojom::blink::HasTrustTokensAnswerer> receiver_{this};
+};
+}  // namespace
+
+TEST(Document, HasTrustTokenSuccess) {
+  V8TestingScope scope(KURL("https://secure.example"));
+
+  MockHasTrustTokensAnswerer answerer(MockHasTrustTokensAnswerer::kTrue);
+
+  Document& document = scope.GetDocument();
+  document.GetFrame()->GetBrowserInterfaceBroker().SetBinderForTesting(
+      network::mojom::blink::HasTrustTokensAnswerer::Name_,
+      WTF::BindRepeating(&MockHasTrustTokensAnswerer::Bind,
+                         WTF::Unretained(&answerer)));
+
+  ScriptState* script_state = scope.GetScriptState();
+  ExceptionState exception_state(script_state->GetIsolate(),
+                                 ExceptionState::kExecutionContext, "Document",
+                                 "hasTrustToken");
+
+  auto promise = document.hasTrustToken(script_state, "https://issuer.example",
+                                        exception_state);
+
+  ScriptPromiseTester promise_tester(script_state, promise);
+  promise_tester.WaitUntilSettled();
+  EXPECT_TRUE(promise_tester.IsFulfilled());
+  EXPECT_TRUE(promise_tester.Value().V8Value()->IsTrue());
+
+  document.GetFrame()->GetBrowserInterfaceBroker().SetBinderForTesting(
+      network::mojom::blink::HasTrustTokensAnswerer::Name_, {});
+}
+
+TEST(Document, HasTrustTokenSuccessWithFalseValue) {
+  V8TestingScope scope(KURL("https://secure.example"));
+
+  MockHasTrustTokensAnswerer answerer(MockHasTrustTokensAnswerer::kFalse);
+
+  Document& document = scope.GetDocument();
+  document.GetFrame()->GetBrowserInterfaceBroker().SetBinderForTesting(
+      network::mojom::blink::HasTrustTokensAnswerer::Name_,
+      WTF::BindRepeating(&MockHasTrustTokensAnswerer::Bind,
+                         WTF::Unretained(&answerer)));
+
+  ScriptState* script_state = scope.GetScriptState();
+  ExceptionState exception_state(script_state->GetIsolate(),
+                                 ExceptionState::kExecutionContext, "Document",
+                                 "hasTrustToken");
+
+  auto promise = document.hasTrustToken(script_state, "https://issuer.example",
+                                        exception_state);
+
+  ScriptPromiseTester promise_tester(script_state, promise);
+  promise_tester.WaitUntilSettled();
+  EXPECT_TRUE(promise_tester.IsFulfilled());
+  EXPECT_TRUE(promise_tester.Value().V8Value()->IsFalse());
+
+  document.GetFrame()->GetBrowserInterfaceBroker().SetBinderForTesting(
+      network::mojom::blink::HasTrustTokensAnswerer::Name_, {});
+}
+
+TEST(Document, HasTrustTokenOperationError) {
+  V8TestingScope scope(KURL("https://secure.example"));
+
+  MockHasTrustTokensAnswerer answerer(MockHasTrustTokensAnswerer::kError);
+
+  Document& document = scope.GetDocument();
+  document.GetFrame()->GetBrowserInterfaceBroker().SetBinderForTesting(
+      network::mojom::blink::HasTrustTokensAnswerer::Name_,
+      WTF::BindRepeating(&MockHasTrustTokensAnswerer::Bind,
+                         WTF::Unretained(&answerer)));
+
+  ScriptState* script_state = scope.GetScriptState();
+  ExceptionState exception_state(script_state->GetIsolate(),
+                                 ExceptionState::kExecutionContext, "Document",
+                                 "hasTrustToken");
+
+  auto promise = document.hasTrustToken(script_state, "https://issuer.example",
+                                        exception_state);
+
+  ScriptPromiseTester promise_tester(script_state, promise);
+  promise_tester.WaitUntilSettled();
+  EXPECT_TRUE(promise_tester.IsRejected());
+  EXPECT_TRUE(IsDOMException(script_state, promise_tester.Value(),
+                             DOMExceptionCode::kOperationError));
+
+  document.GetFrame()->GetBrowserInterfaceBroker().SetBinderForTesting(
+      network::mojom::blink::HasTrustTokensAnswerer::Name_, {});
+}
+
 /**
  * Tests for viewport-fit propagation.
  */
diff --git a/third_party/blink/renderer/core/exported/web_frame_content_dumper.cc b/third_party/blink/renderer/core/exported/web_frame_content_dumper.cc
index f71a66f0..1728a59 100644
--- a/third_party/blink/renderer/core/exported/web_frame_content_dumper.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_content_dumper.cc
@@ -103,6 +103,10 @@
     return WebString();
 
   DCHECK(web_view->MainFrameWidget());
+  // Updating the document lifecycle isn't enough, the BeginFrame() step
+  // should come first which runs events such as notifying of media query
+  // changes or raf-based events.
+  web_view->MainFrameWidget()->BeginFrame(base::TimeTicks::Now());
   web_view->MainFrameWidget()->UpdateAllLifecyclePhases(
       DocumentUpdateReason::kTest);
 
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 43fc2bb..3d6eef0d 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -401,14 +401,6 @@
   }
 }
 
-void WebViewImpl::SetDisplayMode(blink::mojom::DisplayMode mode) {
-  display_mode_ = mode;
-  if (!MainFrameImpl() || !MainFrameImpl()->GetFrameView())
-    return;
-
-  MainFrameImpl()->GetFrameView()->SetDisplayMode(mode);
-}
-
 void WebViewImpl::MouseContextMenu(const WebMouseEvent& event) {
   if (!MainFrameImpl() || !MainFrameImpl()->GetFrameView())
     return;
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h
index 60cfa74..ae5fb3f 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -41,7 +41,6 @@
 #include "third_party/blink/public/common/input/web_input_event.h"
 #include "third_party/blink/public/mojom/frame/frame.mojom-blink.h"
 #include "third_party/blink/public/mojom/input/focus_type.mojom-blink-forward.h"
-#include "third_party/blink/public/mojom/manifest/display_mode.mojom-shared.h"
 #include "third_party/blink/public/mojom/page/page.mojom-blink.h"
 #include "third_party/blink/public/platform/web_input_event_result.h"
 #include "third_party/blink/public/platform/web_rect.h"
@@ -181,7 +180,6 @@
   WebSize ContentsPreferredMinimumSize() override;
   void UpdatePreferredSize() override;
   void EnablePreferredSizeChangedMode() override;
-  void SetDisplayMode(blink::mojom::DisplayMode) override;
   void SetDeviceScaleFactor(float) override;
   void SetZoomFactorForDeviceScaleFactor(float) override;
   float ZoomFactorForDeviceScaleFactor() override {
@@ -397,7 +395,6 @@
 
   WebSize Size();
   IntSize MainFrameSize();
-  blink::mojom::DisplayMode DisplayMode() const { return display_mode_; }
 
   PageScaleConstraintsSet& GetPageScaleConstraintsSet() const;
 
@@ -689,7 +686,6 @@
   bool should_dispatch_first_visually_non_empty_layout_ = false;
   bool should_dispatch_first_layout_after_finished_parsing_ = false;
   bool should_dispatch_first_layout_after_finished_loading_ = false;
-  blink::mojom::DisplayMode display_mode_ = blink::mojom::DisplayMode::kBrowser;
 
   // TODO(bokan): Temporary debugging added to diagnose
   // https://crbug.com/992315. Somehow we're synchronously calling
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc
index 59433d10..970b1a2fc 100644
--- a/third_party/blink/renderer/core/exported/web_view_test.cc
+++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -67,7 +67,6 @@
 #include "third_party/blink/public/web/web_element.h"
 #include "third_party/blink/public/web/web_frame.h"
 #include "third_party/blink/public/web/web_frame_content_dumper.h"
-#include "third_party/blink/public/web/web_frame_widget.h"
 #include "third_party/blink/public/web/web_hit_test_result.h"
 #include "third_party/blink/public/web/web_input_method_controller.h"
 #include "third_party/blink/public/web/web_local_frame.h"
@@ -80,6 +79,8 @@
 #include "third_party/blink/public/web/web_widget.h"
 #include "third_party/blink/public/web/web_widget_client.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_document.h"
+#include "third_party/blink/renderer/core/css/media_query_list_listener.h"
+#include "third_party/blink/renderer/core/css/media_query_matcher.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
@@ -95,6 +96,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
+#include "third_party/blink/renderer/core/frame/web_frame_widget_base.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
 #include "third_party/blink/renderer/core/html/forms/external_date_time_chooser.h"
@@ -3948,19 +3950,70 @@
 
 TEST_F(WebViewTest, ChangeDisplayMode) {
   RegisterMockedHttpURLLoad("display_mode.html");
-  WebView* web_view =
+  WebViewImpl* web_view =
       web_view_helper_.InitializeAndLoad(base_url_ + "display_mode.html");
 
-  std::string content =
-      WebFrameContentDumper::DumpWebViewAsText(web_view, 21).Utf8();
+  String content = WebFrameContentDumper::DumpWebViewAsText(web_view, 21);
   EXPECT_EQ("regular-ui", content);
 
-  web_view->SetDisplayMode(blink::mojom::DisplayMode::kMinimalUi);
-  content = WebFrameContentDumper::DumpWebViewAsText(web_view, 21).Utf8();
+  web_view->MainFrameImpl()->LocalRootFrameWidget()->SetDisplayMode(
+      mojom::blink::DisplayMode::kMinimalUi);
+  content = WebFrameContentDumper::DumpWebViewAsText(web_view, 21);
   EXPECT_EQ("minimal-ui", content);
   web_view_helper_.Reset();
 }
 
+TEST_F(WebViewTest, ChangeDisplayModeChildFrame) {
+  RegisterMockedHttpURLLoad("iframe-display_mode.html");
+  RegisterMockedHttpURLLoad("display_mode.html");
+  WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
+      base_url_ + "iframe-display_mode.html");
+
+  String content = WebFrameContentDumper::DumpWebViewAsText(web_view, 21);
+  // An iframe inserts whitespace into the content.
+  EXPECT_EQ("regular-ui", content.StripWhiteSpace());
+
+  web_view->MainFrameImpl()->LocalRootFrameWidget()->SetDisplayMode(
+      mojom::blink::DisplayMode::kMinimalUi);
+  content = WebFrameContentDumper::DumpWebViewAsText(web_view, 21);
+  // An iframe inserts whitespace into the content.
+  EXPECT_EQ("minimal-ui", content.StripWhiteSpace());
+  web_view_helper_.Reset();
+}
+
+TEST_F(WebViewTest, ChangeDisplayModeAlertsListener) {
+  RegisterMockedHttpURLLoad("display_mode_listener.html");
+  WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
+      base_url_ + "display_mode_listener.html");
+
+  String content = WebFrameContentDumper::DumpWebViewAsText(web_view, 21);
+  EXPECT_EQ("regular-ui", content);
+
+  web_view->MainFrameImpl()->LocalRootFrameWidget()->SetDisplayMode(
+      mojom::blink::DisplayMode::kMinimalUi);
+  content = WebFrameContentDumper::DumpWebViewAsText(web_view, 21);
+  EXPECT_EQ("minimal-ui", content);
+  web_view_helper_.Reset();
+}
+
+TEST_F(WebViewTest, ChangeDisplayModeChildFrameAlertsListener) {
+  RegisterMockedHttpURLLoad("iframe-display_mode_listener.html");
+  RegisterMockedHttpURLLoad("display_mode_listener.html");
+  WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
+      base_url_ + "iframe-display_mode_listener.html");
+
+  String content = WebFrameContentDumper::DumpWebViewAsText(web_view, 21);
+  // An iframe inserts whitespace into the content.
+  EXPECT_EQ("regular-ui", content.StripWhiteSpace());
+
+  web_view->MainFrameImpl()->LocalRootFrameWidget()->SetDisplayMode(
+      mojom::blink::DisplayMode::kMinimalUi);
+  content = WebFrameContentDumper::DumpWebViewAsText(web_view, 21);
+  // An iframe inserts whitespace into the content.
+  EXPECT_EQ("minimal-ui", content.StripWhiteSpace());
+  web_view_helper_.Reset();
+}
+
 TEST_F(WebViewTest, AddFrameInCloseUnload) {
   CreateChildCounterFrameClient frame_client;
   RegisterMockedHttpURLLoad("add_frame_in_unload.html");
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index ab0d55c3..0b3593de 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -902,6 +902,16 @@
   }
 }
 
+void LocalFrame::MediaQueryAffectingValueChangedForLocalSubtree(
+    MediaValueChange value) {
+  GetDocument()->MediaQueryAffectingValueChanged(value);
+  for (Frame* child = Tree().FirstChild(); child;
+       child = child->Tree().NextSibling()) {
+    if (auto* child_local_frame = DynamicTo<LocalFrame>(child))
+      child_local_frame->MediaQueryAffectingValueChangedForLocalSubtree(value);
+  }
+}
+
 double LocalFrame::DevicePixelRatio() const {
   if (!page_)
     return 0;
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index 30763d3..e280e66b 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -261,6 +261,10 @@
   void DeviceScaleFactorChanged();
   double DevicePixelRatio() const;
 
+  // Informs the local root's document and its local descendant subtree that a
+  // media query value changed.
+  void MediaQueryAffectingValueChangedForLocalSubtree(MediaValueChange);
+
   String SelectedText() const;
   String SelectedTextForClipboard() const;
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index b97c7484..86f072c 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -230,7 +230,6 @@
 LocalFrameView::LocalFrameView(LocalFrame& frame, IntRect frame_rect)
     : FrameView(frame_rect),
       frame_(frame),
-      display_mode_(blink::mojom::DisplayMode::kBrowser),
       can_have_scrollbars_(true),
       has_pending_layout_(false),
       layout_scheduling_enabled_(true),
@@ -1181,18 +1180,6 @@
   part_update_set_.insert(&object);
 }
 
-void LocalFrameView::SetDisplayMode(blink::mojom::DisplayMode mode) {
-  if (mode == display_mode_)
-    return;
-
-  display_mode_ = mode;
-
-  if (frame_->GetDocument()) {
-    frame_->GetDocument()->MediaQueryAffectingValueChanged(
-        MediaValueChange::kOther);
-  }
-}
-
 void LocalFrameView::SetDisplayShape(DisplayShape display_shape) {
   if (display_shape == display_shape_)
     return;
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index f9bc706..fb416c0 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -30,7 +30,6 @@
 
 #include "third_party/blink/public/common/metrics/document_update_reason.h"
 #include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink-forward.h"
-#include "third_party/blink/public/mojom/manifest/display_mode.mojom-shared.h"
 #include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink-forward.h"
 #include "third_party/blink/public/platform/shape_properties.h"
 #include "third_party/blink/renderer/core/core_export.h"
@@ -275,9 +274,6 @@
   void SetMediaType(const AtomicString&);
   void AdjustMediaTypeForPrinting(bool printing);
 
-  blink::mojom::DisplayMode DisplayMode() { return display_mode_; }
-  void SetDisplayMode(blink::mojom::DisplayMode);
-
   DisplayShape GetDisplayShape() { return display_shape_; }
   void SetDisplayShape(DisplayShape);
 
@@ -858,8 +854,6 @@
 
   Member<LocalFrame> frame_;
 
-  blink::mojom::DisplayMode display_mode_;
-
   DisplayShape display_shape_;
 
   bool can_have_scrollbars_;
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
index 6d5fb29..5a6a8ceb 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
@@ -458,6 +458,10 @@
       listener_class);
 }
 
+mojom::blink::DisplayMode WebFrameWidgetBase::DisplayMode() const {
+  return display_mode_;
+}
+
 void WebFrameWidgetBase::StartDeferringCommits(base::TimeDelta timeout) {
   if (!View()->does_composite())
     return;
@@ -631,6 +635,15 @@
   ApplyViewportChanges(args);
 }
 
+void WebFrameWidgetBase::SetDisplayMode(mojom::blink::DisplayMode mode) {
+  if (mode != display_mode_) {
+    display_mode_ = mode;
+    LocalFrame* frame = LocalRootImpl()->GetFrame();
+    frame->MediaQueryAffectingValueChangedForLocalSubtree(
+        MediaValueChange::kOther);
+  }
+}
+
 void WebFrameWidgetBase::RequestAnimationAfterDelay(
     const base::TimeDelta& delay) {
   DCHECK(request_animation_after_delay_timer_.get());
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.h b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
index 2663b0e..740df401 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
@@ -14,6 +14,7 @@
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "services/network/public/mojom/referrer_policy.mojom-blink-forward.h"
 #include "third_party/blink/public/common/input/web_gesture_device.h"
+#include "third_party/blink/public/mojom/manifest/display_mode.mojom-blink.h"
 #include "third_party/blink/public/mojom/page/widget.mojom-blink.h"
 #include "third_party/blink/public/platform/cross_variant_mojo_util.h"
 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
@@ -111,6 +112,7 @@
                                   cc::EventListenerProperties) final;
   cc::EventListenerProperties EventListenerProperties(
       cc::EventListenerClass) const final;
+  mojom::blink::DisplayMode DisplayMode() const override;
 
   // WebFrameWidget implementation.
   void Close() override;
@@ -167,6 +169,7 @@
   void SetCompositorVisible(bool visible) override;
   void UpdateVisualState() override;
   void WillBeginCompositorFrame() final;
+  void SetDisplayMode(mojom::blink::DisplayMode) override;
 
   // WidgetBaseClient methods.
   void DispatchRafAlignedInput(base::TimeTicks frame_time) override;
@@ -296,6 +299,8 @@
   void CancelDrag();
   void RequestAnimationAfterDelayTimerFired(TimerBase*);
 
+  static bool ignore_input_events_;
+
   WebWidgetClient* client_;
 
   // WebFrameWidget is associated with a subtree of the frame tree,
@@ -303,7 +308,7 @@
   // points to the root of that subtree.
   Member<WebLocalFrameImpl> local_root_;
 
-  static bool ignore_input_events_;
+  mojom::blink::DisplayMode display_mode_;
 
   // This is owned by the LayerTreeHostImpl, and should only be used on the
   // compositor thread, so we keep the TaskRunner where you post tasks to
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index e4f4ed4..f0255b4 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -254,7 +254,6 @@
 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
-#include "third_party/blink/renderer/platform/scheduler/public/scheduling_policy.h"
 #include "third_party/blink/renderer/platform/text/text_direction.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
@@ -1492,11 +1491,6 @@
   is_in_printing_ = true;
 #endif
 
-  // Disable BackForwardCache when printing API is used.
-  GetFrame()->GetFrameScheduler()->RegisterStickyFeature(
-      blink::SchedulingPolicy::Feature::kPrinting,
-      {blink::SchedulingPolicy::RecordMetricsForBackForwardCache()});
-
   GetFrame()->GetDocument()->SetPrinting(Document::kBeforePrinting);
   DispatchPrintEventRecursively(event_type_names::kBeforeprint);
 }
@@ -1949,8 +1943,6 @@
                                            web_view->MaxAutoSize());
   }
 
-  GetFrame()->View()->SetDisplayMode(web_view->DisplayMode());
-
   if (frame_widget_)
     frame_widget_->DidCreateLocalRootView();
 }
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc b/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc
index 9829728..fbe1cc5 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc
@@ -309,8 +309,9 @@
   // deadlines (6.7s or 13s) bypass the web test running deadline (6s)
   // and result in timeouts on different tests. We use
   // enforce_idle_encoding_for_test_ to test idle encoding in unit tests.
-  // We also don't use idle tasks in workers because there's no proper idle
-  // queue there and tasks can take too long without requestAnimationFrame.
+  // We also don't use idle tasks in workers because the short idle periods are
+  // not implemented, so the idle task can take a long time even when the thread
+  // is not busy.
   bool use_idle_encoding =
       WTF::IsMainThread() && (mime_type_ != kMimeTypeWebp) &&
       (enforce_idle_encoding_for_test_ ||
diff --git a/third_party/blink/renderer/core/input/event_handler_test.cc b/third_party/blink/renderer/core/input/event_handler_test.cc
index cca36d9..a18739e7 100644
--- a/third_party/blink/renderer/core/input/event_handler_test.cc
+++ b/third_party/blink/renderer/core/input/event_handler_test.cc
@@ -28,6 +28,8 @@
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
+#include "third_party/blink/renderer/core/frame/web_frame_widget_base.h"
+#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
 #include "third_party/blink/renderer/core/html/html_iframe_element.h"
@@ -2211,7 +2213,8 @@
   GetDocument().GetSettings()->SetDontSendKeyEventsToJavascript(true);
   GetDocument().GetSettings()->SetScrollAnimatorEnabled(false);
   GetDocument().GetSettings()->SetWebAppScope(GetDocument().Url());
-  GetDocument().View()->SetDisplayMode(blink::mojom::DisplayMode::kFullscreen);
+  WebView().MainFrameImpl()->LocalRootFrameWidget()->SetDisplayMode(
+      blink::mojom::DisplayMode::kFullscreen);
   request.Complete(R"HTML(
     <!DOCTYPE html>
     <style>
diff --git a/third_party/blink/renderer/core/input/keyboard_event_manager.cc b/third_party/blink/renderer/core/input/keyboard_event_manager.cc
index 8589587..124043f 100644
--- a/third_party/blink/renderer/core/input/keyboard_event_manager.cc
+++ b/third_party/blink/renderer/core/input/keyboard_event_manager.cc
@@ -30,6 +30,7 @@
 #include "third_party/blink/renderer/core/page/spatial_navigation.h"
 #include "third_party/blink/renderer/core/page/spatial_navigation_controller.h"
 #include "third_party/blink/renderer/platform/keyboard_codes.h"
+#include "third_party/blink/renderer/platform/widget/frame_widget.h"
 #include "third_party/blink/renderer/platform/windows_keyboard_codes.h"
 #include "ui/events/keycodes/dom/keycode_converter.h"
 
@@ -236,8 +237,8 @@
 
   if (!should_send_key_events_to_js &&
       frame_->GetDocument()->IsInWebAppScope()) {
-    DCHECK(frame_->View());
-    blink::mojom::DisplayMode display_mode = frame_->View()->DisplayMode();
+    mojom::blink::DisplayMode display_mode =
+        frame_->GetWidgetForLocalRoot()->DisplayMode();
     should_send_key_events_to_js =
         display_mode == blink::mojom::DisplayMode::kMinimalUi ||
         display_mode == blink::mojom::DisplayMode::kStandalone ||
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
index a7c94b07..c9f9662 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
@@ -448,9 +448,6 @@
 
   UpdateElementId();
 
-  graphics_layer_->SetHasWillChangeTransformHint(
-      style.HasWillChangeTransformHint());
-
   if (style.Preserves3D() && style.HasOpacity() &&
       owning_layer_.Has3DTransformedDescendant()) {
     UseCounter::Count(layout_object.GetDocument(),
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index 5736b22c..f764ea5b 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -366,6 +366,7 @@
 }
 
 bool PaintLayer::ScrollsWithRespectTo(const PaintLayer* other) const {
+  DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
   if (FixedToViewport() != other->FixedToViewport())
     return true;
   // If either element sticks we cannot trivially determine that the layers do
diff --git a/third_party/blink/renderer/core/testing/data/display_mode_listener.html b/third_party/blink/renderer/core/testing/data/display_mode_listener.html
new file mode 100644
index 0000000..1c744ed
--- /dev/null
+++ b/third_party/blink/renderer/core/testing/data/display_mode_listener.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+  <style>
+    #minimal { display: none; }
+</style>
+<body>
+
+<div id="minimal">minimal-ui</div>
+
+<div id="regular">regular-ui</div>
+
+<script>
+  function mediaquery() {
+      document.getElementById('minimal').style.display = "block";
+      document.getElementById('regular').style.display = "none";
+  }
+  window.matchMedia('(display-mode: minimal-ui)').addListener(mediaquery);
+</script>
+</body>
+</html>
diff --git a/third_party/blink/renderer/core/testing/data/iframe-display_mode.html b/third_party/blink/renderer/core/testing/data/iframe-display_mode.html
new file mode 100644
index 0000000..5f1b4bee
--- /dev/null
+++ b/third_party/blink/renderer/core/testing/data/iframe-display_mode.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<html>
+  <body>
+    <iframe src="display_mode.html"></iframe>
+</body>
+</html>
diff --git a/third_party/blink/renderer/core/testing/data/iframe-display_mode_listener.html b/third_party/blink/renderer/core/testing/data/iframe-display_mode_listener.html
new file mode 100644
index 0000000..735f30f
--- /dev/null
+++ b/third_party/blink/renderer/core/testing/data/iframe-display_mode_listener.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<html>
+  <body>
+    <iframe src="display_mode_listener.html"></iframe>
+</body>
+</html>
diff --git a/third_party/blink/renderer/core/testing/internals.cc b/third_party/blink/renderer/core/testing/internals.cc
index c494d2f..984b4f7 100644
--- a/third_party/blink/renderer/core/testing/internals.cc
+++ b/third_party/blink/renderer/core/testing/internals.cc
@@ -2165,6 +2165,7 @@
 bool Internals::scrollsWithRespectTo(Element* element1,
                                      Element* element2,
                                      ExceptionState& exception_state) {
+  DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
   DCHECK(element1 && element2);
   element1->GetDocument().View()->UpdateAllLifecyclePhases(
       DocumentUpdateReason::kTest);
diff --git a/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc b/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc
index 962fb260..8ec4ba1 100644
--- a/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc
+++ b/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc
@@ -24,6 +24,7 @@
 #include "third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_window.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/widget/frame_widget.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 
 namespace blink {
@@ -318,12 +319,15 @@
   // - Document runs in a Chrome Extension.
   // - Document is in fullscreen.
   // - Document is in a PWA window that runs in the scope of the PWA.
+  bool is_in_pwa_window = false;
+  if (GetSupplementable()->GetFrame()) {
+    mojom::blink::DisplayMode display_mode =
+        GetSupplementable()->GetFrame()->GetWidgetForLocalRoot()->DisplayMode();
+    is_in_pwa_window = display_mode != mojom::blink::DisplayMode::kBrowser;
+  }
   if (!(GetSupplementable()->Url().ProtocolIs("chrome-extension") ||
         Fullscreen::FullscreenElementFrom(*GetSupplementable()) ||
-        (GetSupplementable()->View() &&
-         GetSupplementable()->View()->DisplayMode() !=
-             blink::mojom::DisplayMode::kBrowser &&
-         GetSupplementable()->IsInWebAppScope()))) {
+        (is_in_pwa_window && GetSupplementable()->IsInWebAppScope()))) {
     return false;
   }
 
diff --git a/third_party/blink/renderer/modules/presentation/presentation_connection.cc b/third_party/blink/renderer/modules/presentation/presentation_connection.cc
index 85905a3..de49ab2 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_connection.cc
+++ b/third_party/blink/renderer/modules/presentation/presentation_connection.cc
@@ -240,7 +240,7 @@
   DCHECK(request);
 
   auto* connection = MakeGarbageCollected<ControllerPresentationConnection>(
-      *controller->GetSupplementable(), controller, presentation_info.id,
+      *controller->GetFrame(), controller, presentation_info.id,
       presentation_info.url);
   controller->RegisterConnection(connection);
 
diff --git a/third_party/blink/renderer/modules/presentation/presentation_controller.cc b/third_party/blink/renderer/modules/presentation/presentation_controller.cc
index f2e7db76..5dd3bf68 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_controller.cc
+++ b/third_party/blink/renderer/modules/presentation/presentation_controller.cc
@@ -22,6 +22,7 @@
 
 PresentationController::PresentationController(LocalFrame& frame)
     : Supplement<LocalFrame>(frame),
+      ExecutionContextLifecycleObserver(frame.GetDocument()),
       presentation_controller_receiver_(this, frame.DomWindow()) {}
 
 PresentationController::~PresentationController() = default;
@@ -59,6 +60,7 @@
   visitor->Trace(connections_);
   visitor->Trace(availability_state_);
   Supplement<LocalFrame>::Trace(visitor);
+  ExecutionContextLifecycleObserver::Trace(visitor);
 }
 
 void PresentationController::SetPresentation(Presentation* presentation) {
@@ -151,10 +153,10 @@
 
 mojo::Remote<mojom::blink::PresentationService>&
 PresentationController::GetPresentationService() {
-  if (!presentation_service_remote_ && GetSupplementable()) {
+  if (!presentation_service_remote_ && GetFrame()) {
     scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-        GetSupplementable()->GetTaskRunner(TaskType::kPresentation);
-    GetSupplementable()->GetBrowserInterfaceBroker().GetInterface(
+        GetFrame()->GetTaskRunner(TaskType::kPresentation);
+    GetFrame()->GetBrowserInterfaceBroker().GetInterface(
         presentation_service_remote_.BindNewPipeAndPassReceiver(task_runner));
     presentation_service_remote_->SetController(
         presentation_controller_receiver_.BindNewPipeAndPassRemote(
diff --git a/third_party/blink/renderer/modules/presentation/presentation_controller.h b/third_party/blink/renderer/modules/presentation/presentation_controller.h
index a5789e6..ba9cfbb 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_controller.h
+++ b/third_party/blink/renderer/modules/presentation/presentation_controller.h
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/presentation/presentation.mojom-blink.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
@@ -30,6 +31,7 @@
 class MODULES_EXPORT PresentationController
     : public GarbageCollected<PresentationController>,
       public Supplement<LocalFrame>,
+      public ExecutionContextLifecycleObserver,
       public mojom::blink::PresentationController {
   USING_GARBAGE_COLLECTED_MIXIN(PresentationController);
 
@@ -77,6 +79,9 @@
   virtual void RemoveAvailabilityObserver(PresentationAvailabilityObserver*);
 
  private:
+  // Implementation of ExecutionContextLifecycleObserver.
+  void ContextDestroyed() override {}
+
   // mojom::blink::PresentationController implementation.
   void OnScreenAvailabilityUpdated(const KURL&,
                                    mojom::blink::ScreenAvailability) override;
diff --git a/third_party/blink/renderer/platform/fonts/mac/core_text_font_format_support.cc b/third_party/blink/renderer/platform/fonts/mac/core_text_font_format_support.cc
index df03c3500..e353199 100644
--- a/third_party/blink/renderer/platform/fonts/mac/core_text_font_format_support.cc
+++ b/third_party/blink/renderer/platform/fonts/mac/core_text_font_format_support.cc
@@ -11,12 +11,13 @@
 // Compare CoreText.h in an up to date SDK, redefining here since we don't seem
 // to have access to this value when building against the 10.10 SDK in our
 // standard Chrome build configuration.
-static const uint32_t kBlinkLocalCTVersionNumber10_12 = 0x00090000;
+// static const uint32_t kBlinkLocalCTVersionNumber10_12 = 0x00090000;
 static const uint32_t kBlinkLocalCTVersionNumber10_13 = 0x000A0000;
+static const uint32_t kBlinkLocalCTVersionNumber10_14 = 0x000B0000;
 
 bool CoreTextVersionSupportsVariations() {
   return &CTGetCoreTextVersion &&
-         CTGetCoreTextVersion() >= kBlinkLocalCTVersionNumber10_12;
+         CTGetCoreTextVersion() >= kBlinkLocalCTVersionNumber10_14;
 }
 
 // CoreText versions below 10.13 display COLR cpal as black/foreground-color
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc
index f800478..75ad3a1 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -322,11 +322,10 @@
 
 namespace {
 
-int AdjustedFocusRingOffset(int offset, int width) {
-  if (::features::IsFormControlsRefreshEnabled()) {
-    // For FormControlsRefresh just use the value of outline-offset.
-    return offset;
-  }
+int AdjustedFocusRingOffset(int offset) {
+  // For FormControlsRefresh we just use the value of outline-offset so we don't
+  // need to call this method.
+  DCHECK(!::features::IsFormControlsRefreshEnabled());
 
 #if defined(OS_MACOSX)
   return offset + 2;
@@ -344,10 +343,10 @@
   // only half of the width is outside of the offset.
   if (::features::IsFormControlsRefreshEnabled()) {
     // For FormControlsRefresh 2/3 of the width is outside of the offset.
-    return AdjustedFocusRingOffset(offset, width) + std::ceil(width / 3.f) * 2;
+    return offset + std::ceil(width / 3.f) * 2;
   }
 
-  return AdjustedFocusRingOffset(offset, width) + (width + 1) / 2;
+  return AdjustedFocusRingOffset(offset) + (width + 1) / 2;
 }
 
 void GraphicsContext::DrawFocusRingPath(const SkPath& path,
@@ -394,9 +393,8 @@
 
   SkRegion focus_ring_region;
   if (!::features::IsFormControlsRefreshEnabled()) {
-    // For FormControlsRefresh the offset is already adjusted by
-    // GraphicsContext::DrawFocusRing.
-    offset = AdjustedFocusRingOffset(offset, std::ceil(width));
+    // For FormControlsRefresh we don't need to adjust the offset.
+    offset = AdjustedFocusRingOffset(offset);
   }
   for (unsigned i = 0; i < rect_count; i++) {
     SkIRect r = rects[i];
@@ -430,7 +428,6 @@
     const float first_border_width = (width / 3) * 2;
     const float second_border_width = width - first_border_width;
 
-    offset = AdjustedFocusRingOffset(offset, std::ceil(width));
     // How much space the focus ring would like to take from the actual border.
     const float inside_border_width = 1;
     if (min_border_width >= inside_border_width) {
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.cc b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
index 22e4bd8f..365eb7c 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
@@ -180,11 +180,6 @@
 #endif
 }
 
-void GraphicsLayer::SetHasWillChangeTransformHint(
-    bool has_will_change_transform) {
-  CcLayer()->SetHasWillChangeTransformHint(has_will_change_transform);
-}
-
 void GraphicsLayer::SetParent(GraphicsLayer* layer) {
 #if DCHECK_IS_ON()
   DCHECK(!layer || !layer->HasAncestor(this));
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.h b/third_party/blink/renderer/platform/graphics/graphics_layer.h
index 710ce05..973734d 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.h
@@ -214,8 +214,6 @@
                                   const cc::Layer&,
                                   JSONObject&) const override;
 
-  void SetHasWillChangeTransformHint(bool);
-
   bool HasLayerState() const { return layer_state_.get(); }
   void SetLayerState(const PropertyTreeState&, const IntPoint& layer_offset);
   const PropertyTreeState& GetPropertyTreeState() const {
diff --git a/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc b/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc
index 386766e..edcd2bb6 100644
--- a/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc
@@ -52,7 +52,6 @@
     case Feature::kWebNfc:
     case Feature::kWebFileSystem:
     case Feature::kAppBanner:
-    case Feature::kPrinting:
       return true;
   }
 }
diff --git a/third_party/blink/renderer/platform/widget/frame_widget.h b/third_party/blink/renderer/platform/widget/frame_widget.h
index fffcf36..01e5f8b 100644
--- a/third_party/blink/renderer/platform/widget/frame_widget.h
+++ b/third_party/blink/renderer/platform/widget/frame_widget.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_FRAME_WIDGET_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_FRAME_WIDGET_H_
 
+#include "third_party/blink/public/mojom/manifest/display_mode.mojom-blink.h"
 #include "third_party/blink/public/web/web_swap_result.h"
 #include "third_party/blink/public/web/web_widget_client.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
@@ -74,6 +75,9 @@
                                           cc::EventListenerProperties) = 0;
   virtual cc::EventListenerProperties EventListenerProperties(
       cc::EventListenerClass) const = 0;
+
+  // Returns the DisplayMode in use for the widget.
+  virtual mojom::blink::DisplayMode DisplayMode() const = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/web_tests/FlagExpectations/composite-after-paint b/third_party/blink/web_tests/FlagExpectations/composite-after-paint
index ca3f44b5..5b82b7c 100644
--- a/third_party/blink/web_tests/FlagExpectations/composite-after-paint
+++ b/third_party/blink/web_tests/FlagExpectations/composite-after-paint
@@ -26,6 +26,11 @@
 virtual/controls-refresh-hc/* [ Skip ]
 virtual/web-components-v0-disabled/* [ Skip ]
 
+# They test Layer::ScrollsWithRespectTo() which is for pre-CompositeAfterPaint only.
+compositing/overflow/scrolls-with-respect-to-nested.html [ Skip ]
+compositing/overflow/scrolls-with-respect-to-transform.html [ Skip ]
+compositing/overflow/scrolls-with-respect-to.html [ Skip ]
+
 # Can't rebaseline because the file path is too long.
 virtual/compositor_threaded_scrollbar_scrolling/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky.html [ Skip ]
 
@@ -56,9 +61,6 @@
 
 compositing/masks/broken-mask.html [ Failure ]
 compositing/masks/mask-with-removed-filters.html [ Failure ]
-crbug.com/667946 compositing/overflow/scrolls-with-respect-to-nested.html [ Failure ]
-crbug.com/667946 compositing/overflow/scrolls-with-respect-to-transform.html [ Failure ]
-crbug.com/667946 compositing/overflow/scrolls-with-respect-to.html [ Failure ]
 external/wpt/css/css-transforms/transform3d-backface-visibility-006.html [ Failure ]
 external/wpt/portals/portals-rendering.html [ Failure ]
 fullscreen/compositor-touch-hit-rects-fullscreen-video-controls.html [ Failure ]
@@ -78,11 +80,7 @@
 # No composited scrolling for overflow:hidden (on marquee).
 compositing/overflow/do-not-repaint-if-scrolling-composited-layers.html [ Failure ]
 
-# Extra layers for non-fast scrolling areas.
-compositing/overflow/textarea-scroll-touch.html [ Failure ]
-
 # Subpixel or invisible color differences that look benign, but we can't rebaseline ref tests.
-compositing/overflow/ancestor-with-clip-path.html [ Failure ]
 fullscreen/rendering/backdrop-object.html [ Failure ]
 
 # Passes on bot, timeouts locally.
@@ -103,9 +101,6 @@
 # Outline paints incorrectly with columns.
 crbug.com/1047358 paint/pagination/composited-paginated-outlined-box.html [ Failure ]
 
-# Crash on weird clip hierarchy in multiple columns
-compositing/geometry/composited-in-columns.html [ Crash ]
-
 # Ad frame highlight size is incorrect
 crbug.com/1047359 http/tests/subresource_filter/ad-highlight-frame-resized.html [ Failure ]
 
@@ -121,7 +116,3 @@
 
 compositing/gestures/gesture-tapHighlight-composited-img.html [ Pass Failure ]
 http/tests/images/image-decode-in-frame.html [ Pass Failure ]
-
-# TODO(iopopesc) these need to be rebaselined for FormControlsRefresh focus ring.
-crbug.com/1035582 virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi.html [ Failure ]
-crbug.com/1035582 compositing/overflow/do-not-paint-outline-into-composited-scrolling-contents.html [ Failure ]
diff --git a/third_party/blink/web_tests/LeakExpectations b/third_party/blink/web_tests/LeakExpectations
index 7f0bb48..f54b726d 100644
--- a/third_party/blink/web_tests/LeakExpectations
+++ b/third_party/blink/web_tests/LeakExpectations
@@ -119,6 +119,7 @@
 crbug.com/1038388 [ Linux ] http/tests/devtools/tracing/timeline-time/timeline-time.js [ Pass Failure ]
 
 # Sheriff 2020-04-03
+crbug.com/1067650 [ Linux ] http/tests/devtools/tracing/timeline-misc/timeline-load-event.js [ Pass Failure ]
 crbug.com/1067650 [ Linux ] virtual/threaded/http/tests/devtools/tracing/timeline-misc/timeline-load-event.js [ Pass Failure ]
 
 # Sheriff 2020-04-06
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 57de3c3..b8896fbc 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -274,7 +274,7 @@
 
 # These appear to be actually incorrect at device_scale_factor 2.0:
 crbug.com/968791 crbug.com/1051044 virtual/scalefactor200/css3/filters/effect-reference-image-lazy-attach.html [ Failure ]
-crbug.com/968791 crbug.com/1051044 virtual/scalefactor200/css3/filters/effect-reference-image.html [ Failure ]
+crbug.com/968791 crbug.com/1051044 virtual/scalefactor200/external/wpt/css/filter-effects/effect-reference-feimage.html [ Failure ]
 crbug.com/968791 crbug.com/1051044 virtual/scalefactor200/css3/filters/effect-reference-image-hw.html [ Failure ]
 crbug.com/968791 virtual/scalefactor200/external/wpt/css/filter-effects/filters-test-brightness-003.html [ Failure ]
 
@@ -2342,7 +2342,7 @@
 
 crbug.com/377696 printing/setPrinting.html [ Failure ]
 
-crbug.com/1051044 css3/filters/effect-reference-image.html [ Pass Failure ]
+crbug.com/1051044 external/wpt/css/filter-effects/effect-reference-feimage.html [ Pass Failure ]
 crbug.com/1051044 css3/filters/effect-reference-image-lazy-attach.html [ Pass Failure ]
 
 crbug.com/524160 [ Debug ] http/tests/media/media-source/stream_memory_tests/mediasource-appendbuffer-quota-exceeded-default-buffers.html [ Timeout ]
@@ -5141,7 +5141,7 @@
 crbug.com/855816 [ Win ] virtual/stable/http/tests/navigation/rename-subframe-goback.html [ Pass Timeout ]
 
 # User Activation
-crbug.com/736415 external/wpt/html/user-activation/navigation-state-reset-crossorigin.sub.tentative.html [ Failure ]
+crbug.com/736415 external/wpt/html/user-activation/navigation-state-reset-crossorigin.sub.tentative.html [ Failure Timeout ]
 crbug.com/736415 external/wpt/html/user-activation/navigation-state-reset-sameorigin.tentative.html [ Failure ]
 
 # Sheriff 2018-07-05
@@ -6586,14 +6586,20 @@
 # Sheriff 2020-03-27
 crbug.com/1064839 fast/hidpi/image-srcset-svg-canvas.html [ Pass Failure ]
 crbug.com/1065335 [ Win ] fast/hidpi/image-srcset-relative-svg-canvas.html [ Pass Failure ]
+
 # Sheriff 2020-03-31
 crbug.com/1065335 [ Win ] fast/hidpi/image-srcset-relative-svg-canvas-2x.html [ Pass Failure ]
 # Sheriff 2020-04-01
 crbug.com/1066122 virtual/threaded-no-composited-antialiasing/animations/events/animation-iteration-event.html [ Pass Failure ]
 crbug.com/1066732 fast/events/platform-wheelevent-paging-x-in-scrolling-div.html [ Pass Failure ]
 crbug.com/1066732 fast/events/platform-wheelevent-paging-y-in-scrolling-div.html [ Pass Failure ]
+
+# These tests will pass once the --enable-features=TrustTokens flag is on by default.
+crbug.com/1061765 external/wpt/trust-tokens/end-to-end/* [ Skip ]
+
 # Sheriff 2020-04-02
 crbug.com/1067084 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-with-rtl.html [ Pass Failure ]
+
 # Sheriff 2020-04-04
 crbug.com/1067533 [ Mac10.10 ] external/wpt/shape-detection/idlharness.https.any.sharedworker.html [ Pass Failure ]
 
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 373b011..b732c40 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -738,5 +738,10 @@
     "prefix": "web-bluetooth-new-permissions-backend",
     "bases": ["bluetooth", "external/wpt/bluetooth"],
     "args": ["--enable-features=WebBluetoothNewPermissionsBackend"]
+  },
+  {
+    "prefix": "trust-tokens",
+    "bases": [ "external/wpt/trust-tokens/end-to-end" ],
+    "args": [ "--enable-features=TrustTokens" ]
   }
 ]
diff --git a/third_party/blink/web_tests/compositing/overflow/ancestor-with-clip-path-expected.html b/third_party/blink/web_tests/compositing/overflow/ancestor-with-clip-path-expected.html
index a6d1b20..e7d45dd6 100644
--- a/third_party/blink/web_tests/compositing/overflow/ancestor-with-clip-path-expected.html
+++ b/third_party/blink/web_tests/compositing/overflow/ancestor-with-clip-path-expected.html
@@ -1,6 +1,13 @@
 <!DOCTYPE HTML>
 <style>
+body {
+    margin: 0
+}
+
 #scroller {
+    position: relative;
+    top: -150px;
+    left: -50px;
     overflow: scroll;
     height: 300px;
     width: 300px;
@@ -8,10 +15,13 @@
 }
 
 #outer {
-    height: 800px;
+    position: absolute;
+    top: 150px;
+    left: 50px;
     width: 250px;
+    height: 300px;
+    overflow: hidden;
     background-color: blue;
-    clip-path: polygon(40px 550px,432px 302px,409px 237px,46px 156px);
 }
 
 #scrolled {
@@ -21,17 +31,17 @@
 }
 
 #fixed {
-    position: fixed;
+    position: absolute;
     height: 100px;
     width: 100px;
     background-color: green;
-    top: 400px;
-    left: 100px;
+    top: 250px;
+    left: 50px;
 }
 </style>
 <div id="outer">
     <div id="scroller">
         <div id="scrolled"></div>
-        <div id="fixed"></div>
     </div>
+    <div id="fixed"></div>
 </div>
diff --git a/third_party/blink/web_tests/compositing/overflow/ancestor-with-clip-path.html b/third_party/blink/web_tests/compositing/overflow/ancestor-with-clip-path.html
index 5f1995e..f6653ca 100644
--- a/third_party/blink/web_tests/compositing/overflow/ancestor-with-clip-path.html
+++ b/third_party/blink/web_tests/compositing/overflow/ancestor-with-clip-path.html
@@ -1,6 +1,10 @@
 <!DOCTYPE HTML>
 <script src="../../resources/run-after-layout-and-paint.js"></script>
 <style>
+body {
+    margin: 0;
+}
+
 #scroller {
     overflow: scroll;
     height: 300px;
@@ -9,8 +13,8 @@
 }
 
 #outer {
-    height: 800px;
-    width: 250px;
+    height: 550px;
+    width: 300px;
     background-color: blue;
 }
 
@@ -41,7 +45,7 @@
 
 onload = function() {
     runAfterLayoutAndPaint(function() {
-        outer.style.clipPath = "polygon(40px 550px,432px 302px,409px 237px,46px 156px)";
+        outer.style.clipPath = "polygon(50px 450px, 400px 450px, 400px 150px, 50px 150px)";
     }, true);
 };
 </script>
diff --git a/third_party/blink/web_tests/css3/filters/effect-reference-image-expected.html b/third_party/blink/web_tests/css3/filters/effect-reference-image-expected.html
deleted file mode 100644
index f8ddbf1..0000000
--- a/third_party/blink/web_tests/css3/filters/effect-reference-image-expected.html
+++ /dev/null
@@ -1 +0,0 @@
-<img src="resources/reference.png">
diff --git a/third_party/blink/web_tests/css3/filters/effect-reference-image.html b/third_party/blink/web_tests/css3/filters/effect-reference-image.html
deleted file mode 100644
index 2d76dbf..0000000
--- a/third_party/blink/web_tests/css3/filters/effect-reference-image.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<style>
-  #filtered {
-    width: 160px;
-    height: 90px;
-    filter: url(#imagereplace);
-  }
-</style>
-<div id="filtered"></div>
-<svg xmlns="http://www.w3.org/3000/svg" width="0" height="0" xmlns:xlink="http://www.w3.org/1999/xlink">
-  <filter id="imagereplace" x="0%" y="0%" width="100%" height="100%">
-     <feimage xlink:href="resources/reference.png"/>
-  </filter>
-</svg>
diff --git a/third_party/blink/web_tests/css3/filters/effect-reference-obb-dimensions.html b/third_party/blink/web_tests/css3/filters/effect-reference-obb-dimensions.html
deleted file mode 100644
index de0a07c..0000000
--- a/third_party/blink/web_tests/css3/filters/effect-reference-obb-dimensions.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<style>
-body {
-    margin: 0;
-    padding: 0;
-}
-#target {
-    position: absolute;
-    width: 100px;
-    height: 100px;
-    background-color: green;
-    filter: url('#bboxOffset');
-}
-</style>
-<div id="target"></div>
-<svg height="0">
-  <filter id="bboxOffset" primitiveUnits="objectBoundingBox" x="0" y="0" width="1.25" height="1.25">
-    <feOffset dx="0.2" dy="0.2"/>
-  </filter>
-</svg>
diff --git a/third_party/blink/web_tests/css3/filters/effect-reference-on-span-crash-expected.txt b/third_party/blink/web_tests/css3/filters/effect-reference-on-span-crash-expected.txt
deleted file mode 100644
index 524f271..0000000
--- a/third_party/blink/web_tests/css3/filters/effect-reference-on-span-crash-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
- This test passes by not crashing.
diff --git a/third_party/blink/web_tests/css3/filters/effect-reference-on-span-crash.html b/third_party/blink/web_tests/css3/filters/effect-reference-on-span-crash.html
deleted file mode 100644
index ad9302c..0000000
--- a/third_party/blink/web_tests/css3/filters/effect-reference-on-span-crash.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<script>
-if (window.testRunner) {
-  testRunner.dumpAsText();
-}
-</script>
-<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0" version="1.1">
-  <defs>
-    <filter id="colormatrix" color-interpolation-filters="sRGB">
-      <feColorMatrix type="hueRotate" values="180"/>
-    </filter>
-  </defs>
-</svg>
-<span style="background-color: green; filter: url(#colormatrix);">This test passes by not crashing.</span>
diff --git a/third_party/blink/web_tests/css3/filters/effect-reference-rename-2.html b/third_party/blink/web_tests/css3/filters/effect-reference-rename-2.html
deleted file mode 100644
index 62cc52e..0000000
--- a/third_party/blink/web_tests/css3/filters/effect-reference-rename-2.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE html>
-<script src="../../resources/run-after-layout-and-paint.js"></script>
-<svg height="0">
-  <filter id="NotMyFilter">
-    <feColorMatrix type="hueRotate" values="90"/>
-  </filter>
-</svg>
-<div style="width: 100px; height: 100px; background-color: red; filter: url(#MyFilter);"></div>
-<script>
-runAfterLayoutAndPaint(function() {
-  document.getElementById("NotMyFilter").id = "MyFilter";
-}, true);
-</script>
diff --git a/third_party/blink/web_tests/css3/filters/root-renderer-with-opacity-filter-expected.html b/third_party/blink/web_tests/css3/filters/root-renderer-with-opacity-filter-expected.html
deleted file mode 100644
index 77723d8..0000000
--- a/third_party/blink/web_tests/css3/filters/root-renderer-with-opacity-filter-expected.html
+++ /dev/null
@@ -1,4 +0,0 @@
-<!DOCTYPE html>
-<html>
-<body style="filter: opacity(0.501)">
-This test ensures we properly paint the root renderer when an opacity filter is applied.
diff --git a/third_party/blink/web_tests/css3/filters/root-renderer-with-opacity-filter.html b/third_party/blink/web_tests/css3/filters/root-renderer-with-opacity-filter.html
deleted file mode 100644
index f05e5f8..0000000
--- a/third_party/blink/web_tests/css3/filters/root-renderer-with-opacity-filter.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<!DOCTYPE html>
-<html style="filter: opacity(0.501)">
-This test ensures we properly paint the root renderer when an opacity filter is applied.
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
index 9d420c8e..822f820 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
@@ -108515,6 +108515,18 @@
      {}
     ]
    ],
+   "css/filter-effects/effect-reference-delete.html": [
+    [
+     "css/filter-effects/effect-reference-delete.html",
+     [
+      [
+       "/css/filter-effects/reference/effect-reference-delete-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/filter-effects/effect-reference-lighting-no-light.tentative.html": [
     [
      "css/filter-effects/effect-reference-lighting-no-light.tentative.html",
@@ -108527,6 +108539,18 @@
      {}
     ]
    ],
+   "css/filter-effects/effect-reference-merge-no-inputs.tentative.html": [
+    [
+     "css/filter-effects/effect-reference-merge-no-inputs.tentative.html",
+     [
+      [
+       "/css/filter-effects/reference/effect-reference-merge-no-inputs.tentative-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/filter-effects/effect-reference-rename-001.html": [
     [
      "css/filter-effects/effect-reference-rename-001.html",
@@ -108755,6 +108779,18 @@
      {}
     ]
    ],
+   "css/filter-effects/filter-region-negative-positioned-child-001.html": [
+    [
+     "css/filter-effects/filter-region-negative-positioned-child-001.html",
+     [
+      [
+       "/css/filter-effects/reference/filter-region-negative-positioned-child-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/filter-effects/filter-saturate-001-test.html": [
     [
      "css/filter-effects/filter-saturate-001-test.html",
@@ -134942,6 +134978,9 @@
    "common/reftest-wait.js.headers": [
     []
    ],
+   "common/rendering-utils.js": [
+    []
+   ],
    "common/sab.js": [
     []
    ],
@@ -159950,15 +159989,24 @@
    "css/filter-effects/reference/effect-reference-after-001-ref.html": [
     []
    ],
+   "css/filter-effects/reference/effect-reference-delete-ref.html": [
+    []
+   ],
    "css/filter-effects/reference/effect-reference-lighting-no-light.tentative-ref.html": [
     []
    ],
+   "css/filter-effects/reference/effect-reference-merge-no-inputs.tentative-ref.html": [
+    []
+   ],
    "css/filter-effects/reference/effect-reference-rename-001-ref.html": [
     []
    ],
    "css/filter-effects/reference/empty-element-with-filter-ref.html": [
     []
    ],
+   "css/filter-effects/reference/filter-region-negative-positioned-child-001-ref.html": [
+    []
+   ],
    "css/filter-effects/reference/filter-url-to-non-existent-filter-001-ref.html": [
     []
    ],
@@ -353104,6 +353152,10 @@
    "6805c323df5a975231648b830e33ce183c3cbbd3",
    "support"
   ],
+  "common/rendering-utils.js": [
+   "46283bd5d078a14922e24160053017b6e8cb072c",
+   "support"
+  ],
   "common/sab.js": [
    "c7fd1a742e64f66744b416584952effe29fda208",
    "support"
@@ -441037,7 +441089,7 @@
    "reftest"
   ],
   "css/filter-effects/background-image-blur-repaint.html": [
-   "7b3df263081ecceb6517e056e60cfaa35b9981a0",
+   "4a3c00c7ad334a67312346ce57aa81ac96d024d2",
    "reftest"
   ],
   "css/filter-effects/blur-clip-stacking-context-001.html": [
@@ -441240,10 +441292,18 @@
    "a6de2465027e99ff6239dd45524862465dde847f",
    "reftest"
   ],
+  "css/filter-effects/effect-reference-delete.html": [
+   "314c9a7d123bd65b04483956513337116f7e0382",
+   "reftest"
+  ],
   "css/filter-effects/effect-reference-lighting-no-light.tentative.html": [
    "beefd47a544d5c82b4b1d468ce99938e6d9924d9",
    "reftest"
   ],
+  "css/filter-effects/effect-reference-merge-no-inputs.tentative.html": [
+   "4fb67db643dd5aebdbff53a0773035747c18836c",
+   "reftest"
+  ],
   "css/filter-effects/effect-reference-rename-001.html": [
    "6c8374536f4cf748784b7a58fc158d230ea3557f",
    "reftest"
@@ -441364,6 +441424,10 @@
    "add64624b500d9688940834e76d63f4b0bb51489",
    "reftest"
   ],
+  "css/filter-effects/filter-region-negative-positioned-child-001.html": [
+   "8f302ab52abe2f1008ca60d46fbc77d8ce35c22a",
+   "reftest"
+  ],
   "css/filter-effects/filter-saturate-001-ref.html": [
    "4f654f9c554d1e2ab98960ec291419d44375c1ae",
    "support"
@@ -441612,10 +441676,18 @@
    "45192b13451fdfe2f00c17dbc84d30a770426e86",
    "support"
   ],
+  "css/filter-effects/reference/effect-reference-delete-ref.html": [
+   "918715265fa4c1b95ec46d04014ea82e73cbcd40",
+   "support"
+  ],
   "css/filter-effects/reference/effect-reference-lighting-no-light.tentative-ref.html": [
    "e863a6703b2acebbdb10a5eef342cbbd1b6b5bc9",
    "support"
   ],
+  "css/filter-effects/reference/effect-reference-merge-no-inputs.tentative-ref.html": [
+   "5743e0c3de96ba4fbdf814bf5ec997e0c93e65da",
+   "support"
+  ],
   "css/filter-effects/reference/effect-reference-rename-001-ref.html": [
    "fe3beae2d4997a7603153c5c885f01c7ca656bcd",
    "support"
@@ -441624,6 +441696,10 @@
    "cf2c997f6c0d60cac9896c0b0014189cea7790bc",
    "support"
   ],
+  "css/filter-effects/reference/filter-region-negative-positioned-child-001-ref.html": [
+   "c9da47b1b434303f0111e8d1f03d5518e8f573fc",
+   "support"
+  ],
   "css/filter-effects/reference/filter-url-to-non-existent-filter-001-ref.html": [
    "c1aeed8c441e6f72c6faa893d64ae30bdacbec06",
    "support"
@@ -484713,7 +484789,7 @@
    "support"
   ],
   "interfaces/css-typed-om.idl": [
-   "df9669e5637f18de89792facba8fabe234d658f8",
+   "00d3f49bfe653ce4782e13e6c0d46c65783874cc",
    "support"
   ],
   "interfaces/cssom-view.idl": [
@@ -484937,7 +485013,7 @@
    "support"
   ],
   "interfaces/permissions.idl": [
-   "9cde372a127188ef1081ad390d5301d2d8b198f8",
+   "d45c7f93cac7683a76e89d0e7f429f5f3cb695d9",
    "support"
   ],
   "interfaces/picture-in-picture.idl": [
@@ -485165,7 +485241,7 @@
    "support"
   ],
   "interfaces/worklets.idl": [
-   "16187664b3b4124758c84d054b02b8ddbf585508",
+   "f89eacd50bd58c02860c84b6e4c486ab978a5018",
    "support"
   ],
   "interfaces/xhr.idl": [
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/effect-reference-feimage.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/effect-reference-feimage.html
new file mode 100644
index 0000000..a1c3744
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/effect-reference-feimage.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>CSS Filters: feImage and CSS reference filters.</title>
+<link rel="help" href="https://drafts.fxtf.org/filter-effects-1/#feImageElement">
+<link rel="help" href="https://drafts.fxtf.org/filter-effects-1/#FilterProperty">
+<link rel="match" href="reference/effect-reference-feimage-ref.html">
+<meta name="assert" content="This test ensures that CSS reference filters supports feImage."/>
+<style>
+  #filtered {
+    width: 160px;
+    height: 90px;
+    filter: url(#imagereplace);
+  }
+</style>
+<div id="filtered"></div>
+<svg width="0" height="0">
+  <filter id="imagereplace" x="0%" y="0%" width="100%" height="100%">
+     <feimage xlink:href="support/color-palette.png"/>
+  </filter>
+</svg>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/effect-reference-obb-dimensions.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/effect-reference-obb-dimensions.html
new file mode 100644
index 0000000..2bb424d9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/effect-reference-obb-dimensions.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>CSS Filters: objectBoundingBox type of primitiveUnits in SVG filters</title>
+<link rel="author" title="Fredrik Söderquist" href="mailto:fs@opera.com">
+<link rel="help" href="https://drafts.fxtf.org/filter-effects/#InterfaceSVGFilterElement">
+<link rel="help" href="https://drafts.fxtf.org/filter-effects/#attr-valuedef-primitiveunits-objectboundingbox">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=532938">
+<link rel="match" href="reference/effect-reference-obb-dimensions-ref.html">
+<meta name="assert" content="Check that primitiveUnits of type objectBoundingBox get properly applied in SVG filter primitives."/>
+
+<style>
+body {
+  margin: 0;
+  padding: 0;
+}
+#target {
+  position: absolute;
+  width: 100px;
+  height: 100px;
+  background-color: green;
+  filter: url('#bboxOffset');
+}
+</style>
+<div id="target"></div>
+<svg height="0">
+  <filter id="bboxOffset" primitiveUnits="objectBoundingBox" x="0" y="0" width="1.25" height="1.25">
+    <feOffset dx="0.2" dy="0.2"/>
+  </filter>
+</svg>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/effect-reference-on-span.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/effect-reference-on-span.html
new file mode 100644
index 0000000..66bd8fa88
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/effect-reference-on-span.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<title>CSS Filters: filters on <span> elements</title>
+<link rel="author" title="Stephen White" href="mailto:senorblanco@chromium.org">
+<link rel="help" href="https://drafts.fxtf.org/filter-effects-1/#FilterProperty">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=513408">
+<link rel="match" href="reference/effect-reference-on-span-ref.html">
+<meta name="assert" content="Check that a CSS filter to a <span> element works as expected."/>
+
+<body>
+<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0" version="1.1">
+  <defs>
+    <filter id="colormatrix" color-interpolation-filters="sRGB">
+      <feColorMatrix values="0 0 0 0 0, 0 0 0 0 0, 1 0 0 0 0, 0 0 0 1 0"/>
+    </filter>
+  </defs>
+</svg><span style="background-color: red; filter: url(#colormatrix);">This text's background should be blue.</span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/effect-reference-rename-002.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/effect-reference-rename-002.html
new file mode 100644
index 0000000..8652881
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/effect-reference-rename-002.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>CSS Filters: reference to renamed SVG filter</title>
+<link rel="author" title="Fredrik Söderquist" href="mailto:fs@opera.com">
+<link rel="help" href="https://drafts.fxtf.org/filter-effects-1/#FilterProperty">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=453019">
+<link rel="match" href="reference/effect-reference-rename-002-ref.html">
+<meta name="assert" content="Check that a SVG filter, initially named differently than what an element expects, gets applied to such element once renamed, after rendering the first frame."/>
+
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/rendering-utils.js"></script>
+
+<body>
+<svg height="0">
+  <filter id="NotMyFilter">
+    <feColorMatrix type="hueRotate" values="90"/>
+  </filter>
+</svg>
+<div style="width: 100px; height: 100px; background-color: red; filter: url(#MyFilter);"></div>
+<script>
+waitForAtLeastOneFrame().then(function() {
+  document.getElementById("NotMyFilter").id = "MyFilter";
+  takeScreenshot();
+});
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/reference/effect-reference-feimage-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/reference/effect-reference-feimage-ref.html
new file mode 100644
index 0000000..9b982b3c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/reference/effect-reference-feimage-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<img src="../support/color-palette.png">
diff --git a/third_party/blink/web_tests/css3/filters/effect-reference-obb-dimensions-expected.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/reference/effect-reference-obb-dimensions-ref.html
similarity index 100%
rename from third_party/blink/web_tests/css3/filters/effect-reference-obb-dimensions-expected.html
rename to third_party/blink/web_tests/external/wpt/css/filter-effects/reference/effect-reference-obb-dimensions-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/reference/effect-reference-on-span-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/reference/effect-reference-on-span-ref.html
new file mode 100644
index 0000000..76a7953
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/reference/effect-reference-on-span-ref.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<span style="background-color: blue;">This text's background should be blue.</span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/css3/filters/effect-reference-rename-2-expected.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/reference/effect-reference-rename-002-ref.html
similarity index 100%
rename from third_party/blink/web_tests/css3/filters/effect-reference-rename-2-expected.html
rename to third_party/blink/web_tests/external/wpt/css/filter-effects/reference/effect-reference-rename-002-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/reference/root-element-with-opacity-filter-001-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/reference/root-element-with-opacity-filter-001-ref.html
new file mode 100644
index 0000000..cd16e54c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/reference/root-element-with-opacity-filter-001-ref.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<html>
+<body style="filter: opacity(0.501)">
+This test ensures we properly paint the root element when an opacity filter is applied.
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/root-element-with-opacity-filter-001.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/root-element-with-opacity-filter-001.html
new file mode 100644
index 0000000..577e2bd4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/root-element-with-opacity-filter-001.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<title>Opacity Filter: root element opacity filter</title>
+<link rel="help" href="https://drafts.fxtf.org/filter-effects/#FilterProperty">
+<link rel="help" href="https://drafts.fxtf.org/filter-effects/#funcdef-filter-opacity">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=349587">
+<link rel="match" href="reference/root-element-with-opacity-filter-001-ref.html">
+<meta name="assert" content="This test ensures that the root renderer has an opacity filter"/>
+<html style="filter: opacity(0.501)">
+This test ensures we properly paint the root element when an opacity filter is applied.
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/css-typed-om.idl b/third_party/blink/web_tests/external/wpt/interfaces/css-typed-om.idl
index df9669e..00d3f49 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/css-typed-om.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/css-typed-om.idl
@@ -41,9 +41,9 @@
     [SameObject] readonly attribute StylePropertyMap attributeStyleMap;
 };
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(sequence<CSSUnparsedSegment> members)]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSUnparsedValue : CSSStyleValue {
+    constructor(sequence<CSSUnparsedSegment> members);
     iterable<CSSUnparsedSegment>;
     readonly attribute unsigned long length;
     getter CSSUnparsedSegment (unsigned long index);
@@ -52,16 +52,16 @@
 
 typedef (USVString or CSSVariableReferenceValue) CSSUnparsedSegment;
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(USVString variable, optional CSSUnparsedValue? fallback = null)]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSVariableReferenceValue {
+    constructor(USVString variable, optional CSSUnparsedValue? fallback = null);
     attribute USVString variable;
     readonly attribute CSSUnparsedValue? fallback;
 };
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(USVString value)]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSKeywordValue : CSSStyleValue {
+    constructor(USVString value);
     attribute USVString value;
 };
 
@@ -108,9 +108,9 @@
     [Exposed=Window] static CSSNumericValue parse(USVString cssText);
 };
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(double value, USVString unit)]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSUnitValue : CSSNumericValue {
+    constructor(double value, USVString unit);
     attribute double value;
     readonly attribute USVString unit;
 };
@@ -120,45 +120,45 @@
     readonly attribute CSSMathOperator operator;
 };
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(CSSNumberish... args)]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSMathSum : CSSMathValue {
+    constructor(CSSNumberish... args);
     readonly attribute CSSNumericArray values;
 };
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(CSSNumberish... args)]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSMathProduct : CSSMathValue {
+    constructor(CSSNumberish... args);
     readonly attribute CSSNumericArray values;
 };
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(CSSNumberish arg)]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSMathNegate : CSSMathValue {
+    constructor(CSSNumberish arg);
     readonly attribute CSSNumericValue value;
 };
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(CSSNumberish arg)]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSMathInvert : CSSMathValue {
+    constructor(CSSNumberish arg);
     readonly attribute CSSNumericValue value;
 };
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(CSSNumberish... args)]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSMathMin : CSSMathValue {
+    constructor(CSSNumberish... args);
     readonly attribute CSSNumericArray values;
 };
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(CSSNumberish... args)]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSMathMax : CSSMathValue {
+    constructor(CSSNumberish... args);
     readonly attribute CSSNumericArray values;
 };
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(CSSNumberish min, CSSNumberish val, CSSNumberish max)]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSMathClamp : CSSMathValue {
+    constructor(CSSNumberish min, CSSNumberish val, CSSNumberish max);
     readonly attribute CSSNumericValue min;
     readonly attribute CSSNumericValue val;
     readonly attribute CSSNumericValue max;
@@ -230,9 +230,9 @@
     CSSUnitValue fr(double value);
 };
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(sequence<CSSTransformComponent> transforms)]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSTransformValue : CSSStyleValue {
+    constructor(sequence<CSSTransformComponent> transforms);
     iterable<CSSTransformComponent>;
     readonly attribute unsigned long length;
     getter CSSTransformComponent (unsigned long index);
@@ -249,63 +249,63 @@
     DOMMatrix toMatrix();
 };
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(CSSNumericValue x, CSSNumericValue y, optional CSSNumericValue z)]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSTranslate : CSSTransformComponent {
+    constructor(CSSNumericValue x, CSSNumericValue y, optional CSSNumericValue z);
     attribute CSSNumericValue x;
     attribute CSSNumericValue y;
     attribute CSSNumericValue z;
 };
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(CSSNumericValue angle),
- Constructor(CSSNumberish x, CSSNumberish y, CSSNumberish z, CSSNumericValue angle)]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSRotate : CSSTransformComponent {
+    constructor(CSSNumericValue angle);
+    constructor(CSSNumberish x, CSSNumberish y, CSSNumberish z, CSSNumericValue angle);
     attribute CSSNumberish x;
     attribute CSSNumberish y;
     attribute CSSNumberish z;
     attribute CSSNumericValue angle;
 };
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(CSSNumberish x, CSSNumberish y, optional CSSNumberish z)]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSScale : CSSTransformComponent {
+    constructor(CSSNumberish x, CSSNumberish y, optional CSSNumberish z);
     attribute CSSNumberish x;
     attribute CSSNumberish y;
     attribute CSSNumberish z;
 };
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(CSSNumericValue ax, CSSNumericValue ay)]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSSkew : CSSTransformComponent {
+    constructor(CSSNumericValue ax, CSSNumericValue ay);
     attribute CSSNumericValue ax;
     attribute CSSNumericValue ay;
 };
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(CSSNumericValue ax)]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSSkewX : CSSTransformComponent {
+    constructor(CSSNumericValue ax);
     attribute CSSNumericValue ax;
 };
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(CSSNumericValue ay)]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSSkewY : CSSTransformComponent {
+    constructor(CSSNumericValue ay);
     attribute CSSNumericValue ay;
 };
 
 /* Note that skew(x,y) is *not* the same as skewX(x) skewY(y),
    thus the separate interfaces for all three. */
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(CSSNumericValue length)]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSPerspective : CSSTransformComponent {
+    constructor(CSSNumericValue length);
     attribute CSSNumericValue length;
 };
 
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(DOMMatrixReadOnly matrix, optional CSSMatrixComponentOptions options = {})]
+[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
 interface CSSMatrixComponent : CSSTransformComponent {
+    constructor(DOMMatrixReadOnly matrix, optional CSSMatrixComponentOptions options = {});
     attribute DOMMatrix matrix;
 };
 
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/permissions.idl b/third_party/blink/web_tests/external/wpt/interfaces/permissions.idl
index 9cde372a..d45c7f9 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/permissions.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/permissions.idl
@@ -46,6 +46,10 @@
   DOMString deviceId;
 };
 
+dictionary CameraDevicePermissionDescriptor : DevicePermissionDescriptor {
+  boolean panTiltZoom = false;
+};
+
 dictionary PermissionSetParameters {
   required PermissionDescriptor descriptor;
   required PermissionState state;
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/worklets.idl b/third_party/blink/web_tests/external/wpt/interfaces/worklets.idl
index 1618766..f89eacd5 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/worklets.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/worklets.idl
@@ -9,7 +9,7 @@
 
 [Exposed=Window]
 interface Worklet {
-    [NewObject] Promise<void> addModule(USVString moduleURL, optional WorkletOptions options);
+    [NewObject] Promise<void> addModule(USVString moduleURL, optional WorkletOptions options = {});
 };
 
 dictionary WorkletOptions {
diff --git a/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/README.txt b/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/README.txt
new file mode 100644
index 0000000..a86468d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/README.txt
@@ -0,0 +1,4 @@
+These tests confirm that Trust Tokens protocol operations executed correctly end
+to end (in contrast to just checking, as the parent directory's
+trust-token-parameter-validation does, that a method's interface is present and
+correctly rejects invalid arguments).
diff --git a/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/has-trust-token-with-no-top-frame.tentative.https.html b/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/has-trust-token-with-no-top-frame.tentative.https.html
new file mode 100644
index 0000000..4788cd990
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/has-trust-token-with-no-top-frame.tentative.https.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Tests the Trust Token API's hasTrustToken behavior in documents with no top frame</title>
+<link rel="help" href="https://github.com/WICG/trust-token-api" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+  <script>
+    'use strict';
+
+    const frame = document.createElement('iframe');
+    document.body.appendChild(frame);
+    const cachedDocument = window[0].document;
+    frame.remove();
+
+    test(() => {
+      assert_equals(cachedDocument.hasTrustToken("https://issuer.example"), undefined,
+        "Can't construct a Promise in a destroyed execution context.");
+    }, 'hasTrustToken in a destroyed document.');
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/has-trust-token.tentative.https.html b/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/has-trust-token.tentative.https.html
new file mode 100644
index 0000000..17e037f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/has-trust-token.tentative.https.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Tests the Trust Token API's hasTrustToken function (tentative: the API is a prototype).</title>
+<link rel="help" href="https://github.com/WICG/trust-token-api#trust-token-redemption" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+  'use strict';
+
+  promise_test((t) => {
+      return promise_rejects_js(t, TypeError, document.hasTrustToken(
+        "http://not-a-secure-url.example"));
+    },
+    'hasTrustToken requires a secure URL as its issuer argument.');
+
+  promise_test((t) => {
+      return promise_rejects_js(t, TypeError, document.hasTrustToken(
+        "file:///"));
+    },
+    'hasTrustToken requires a HTTP(S) URL as its issuer argument.');
+
+  // These hasTrustToken calls all affect global state: each call in the form
+  // of hasTrustToken(issuer) will result in |issuer| becoming associated in
+  // persistent storage with the calling top frame's origin.
+  //
+  // TODO(davidvc, crbug.com/1061764): Add a way to reset the global state after
+  // the test concludes.
+  //
+  // TODO(davidvc, crbug.com/1063140): Once it's possible to write WPTs that
+  // result in a trust token being deposited in storage, this should be
+  // expanded to cover the case where the user _does_ have a token.
+  promise_test(async (t) => {
+      let result = await document.hasTrustToken("https://issuer.example/");
+      assert_false(result, "The client should not possess any trust tokens for " +
+        "https://issuer.example since it has not executed an issuance operation" +
+        " against that issuer.");
+
+      result = await document.hasTrustToken("https://issuer2.example/");
+      assert_false(result, "The client should not possess any trust tokens for" +
+        " https://issuer2.example since it has not executed an issuance " +
+        "operation against that issuer.");
+
+      await promise_rejects_dom(t, "OperationError", document.hasTrustToken(
+          "https://issuer3.example/"),
+        "The first two hasTrustToken operations associated this top-level" +
+        " origin with the maximum number of issuers (2), so an attempt to " +
+        " execute hasTrustToken against another issuer should fail.");
+
+      result = await document.hasTrustToken("https://issuer2.example/");
+      assert_false(result, "Since this top-level origin is already associated " +
+        "with https://issuer2.example, subsequent hasTrustToken operations should " +
+        "not error out even though the top-level origin is at its " +
+        "number-of-issuers limit.");
+    }, "When given a valid, secure origin, hasTrustToken should succeed " +
+    "unless associating that origin with the top-level domain would exceed " +
+    "the top-level origin's number-of-associated-issuers limit.");
+</script>
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/overflow/do-not-paint-outline-into-composited-scrolling-contents-expected.png b/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/overflow/do-not-paint-outline-into-composited-scrolling-contents-expected.png
new file mode 100644
index 0000000..f8c749d
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/overflow/do-not-paint-outline-into-composited-scrolling-contents-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/overflow/textarea-scroll-touch-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/overflow/textarea-scroll-touch-expected.txt
new file mode 100644
index 0000000..bb824aa
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/overflow/textarea-scroll-touch-expected.txt
@@ -0,0 +1,103 @@
+ 
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutTextControl TEXTAREA",
+      "position": [1, 1],
+      "bounds": [189, 124],
+      "drawsContent": false,
+      "transform": 1
+    },
+    {
+      "name": "Scroll corner of LayoutTextControl TEXTAREA",
+      "position": [193, 18],
+      "bounds": [261, 126]
+    },
+    {
+      "name": "LayoutTextControl TEXTAREA",
+      "position": [1, 1],
+      "bounds": [189, 328],
+      "backgroundColor": "#CCCCCC",
+      "transform": 2
+    },
+    {
+      "name": "VerticalScrollbar",
+      "position": [190, 1],
+      "bounds": [15, 109],
+      "transform": 1
+    },
+    {
+      "name": "LayoutTextControl TEXTAREA",
+      "position": [1, 1],
+      "bounds": [189, 124],
+      "drawsContent": false,
+      "transform": 3
+    },
+    {
+      "name": "Scroll corner of LayoutTextControl TEXTAREA",
+      "position": [175, 95],
+      "bounds": [30, 30],
+      "transform": 3
+    },
+    {
+      "name": "LayoutTextControl TEXTAREA",
+      "position": [1, 1],
+      "bounds": [189, 328],
+      "backgroundColor": "#CCCCCC",
+      "transform": 4
+    },
+    {
+      "name": "VerticalScrollbar",
+      "position": [190, 1],
+      "bounds": [15, 109],
+      "transform": 3
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 18, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, -50, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [248, 18, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "parent": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, -50, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/flag-specific/composite-after-paint/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
deleted file mode 100644
index a19bae81..0000000
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/trust-tokens/external/wpt/trust-tokens/end-to-end/README.txt b/third_party/blink/web_tests/virtual/trust-tokens/external/wpt/trust-tokens/end-to-end/README.txt
new file mode 100644
index 0000000..c981686
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/trust-tokens/external/wpt/trust-tokens/end-to-end/README.txt
@@ -0,0 +1,3 @@
+Enable the network service side of the Trust Token API
+(https://github.com/wicg/trust-token-api) with --enable-features=TrustTokens in
+order to test API calls end to end.
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 f517567..3d1692e 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
@@ -1768,6 +1768,7 @@
     method getSelection
     method hasFocus
     method hasStorageAccess
+    method hasTrustToken
     method importNode
     method open
     method prepend
diff --git a/tools/binary_size/find_large_commits.py b/tools/binary_size/find_large_commits.py
index 58d0b16..59438c8 100755
--- a/tools/binary_size/find_large_commits.py
+++ b/tools/binary_size/find_large_commits.py
@@ -34,15 +34,17 @@
 
 
 def _LookupCommitInfo(rev):
-  sha1 = subprocess.check_output(['git', 'crrev-parse', str(rev)]).strip()
-  desc = subprocess.check_output(['git', 'log', '-n1', sha1])
+  sha1 = subprocess.check_output(
+      ['git', 'crrev-parse', str(rev)], encoding="utf-8").strip()
+  desc = subprocess.check_output(['git', 'log', '-n1', sha1], encoding="utf-8")
   author = re.search(r'Author: .*?<(.*?)>', desc).group(1)
   day, year = re.search(r'Date:\s+\w+\s+(\w+ \d+)\s+.*?\s+(\d+)', desc).groups()
   date = '{} {}'.format(day, year)
   title = re.search(r'\n +(\S.*)', desc).group(1).replace('\t', ' ')
   milestone = None
   if 'Roll AFDO' not in title:
-    releases = subprocess.check_output(['git', 'find-releases', sha1])
+    releases = subprocess.check_output(['git', 'find-releases', sha1],
+                                       encoding="utf-8")
     version = re.search('initially in (\d\d)', releases)
     milestone = ''
     if version:
diff --git a/tools/binary_size/generate_milestone_reports.py b/tools/binary_size/generate_milestone_reports.py
index fd98e1f..61c81da 100755
--- a/tools/binary_size/generate_milestone_reports.py
+++ b/tools/binary_size/generate_milestone_reports.py
@@ -139,7 +139,6 @@
   temp_dir = tempfile.mkdtemp()
   try:
     subpaths = set(x.size_file_subpath for x in reports)
-    logging.warning('Downloading %d .size files', len(subpaths))
     arg_tuples = ((p, temp_dir, base_url) for p in subpaths)
     for _ in _Shard(_DownloadOneSizeFile, arg_tuples):
       pass
@@ -178,8 +177,6 @@
 def main():
   parser = argparse.ArgumentParser(description=__doc__)
   parser.add_argument(
-      'directory', help='Directory to save report files to (must not exist).')
-  parser.add_argument(
       '--size-file-bucket',
       required=True,
       help='GCS bucket to find size files in. (e.g. "gs://bucket/subdir")')
@@ -187,58 +184,36 @@
       '--sync',
       action='store_true',
       help='Sync data files to GCS (otherwise just prints out command to run).')
-  parser.add_argument(
-      '--skip-existing',
-      action='store_true',
-      help='Used to control skipping existing reports, now does nothing.')
-
   args = parser.parse_args()
 
   size_file_bucket = args.size_file_bucket.rstrip('/')
   if not size_file_bucket.startswith('gs://'):
     parser.error('Size file bucket must start with gs://')
 
-  _MakeDirectory(args.directory)
-  if os.listdir(args.directory):
-    parser.error('Directory must be empty')
-
   reports_to_make = set(_EnumerateReports())
 
+  logging.warning('Downloading %d size files.', len(reports_to_make))
   with _DownloadSizeFiles(args.size_file_bucket, reports_to_make) as sizes_dir:
-    logging.warning('Downloading %d size files.', len(reports_to_make))
 
-    for i, r in enumerate(reports_to_make):
-      _BuildOneReport(r, args.directory, sizes_dir)
-      sys.stdout.write('\rGenerated {} of {}'.format(i + 1,
-                                                     len(reports_to_make)))
-      sys.stdout.flush()
-    sys.stdout.write('\n')
+    staging_dir = os.path.join(sizes_dir, 'staging')
+    _MakeDirectory(staging_dir)
 
-  _WriteMilestonesJson(os.path.join(args.directory, 'milestones.json'))
+    for r in reports_to_make:
+      _BuildOneReport(r, staging_dir, sizes_dir)
 
-  logging.warning('Reports saved to %s', args.directory)
-  cmd = [
-      _GSUTIL,
-      '-m',
-      'rsync',
-      '-J',
-      '-a',
-      'public-read',
-      '-r',
-      args.directory,
-      _PUSH_URL,
-  ]
+    _WriteMilestonesJson(os.path.join(staging_dir, 'milestones.json'))
 
-  if args.sync:
-    subprocess.check_call(cmd)
-    subprocess.check_call([
-        _GSUTIL, 'setmeta', '-h', 'Cache-Control:no-cache',
-        _PUSH_URL + 'milestones.json'
-    ])
-  else:
-    print()
-    print('Sync files by running:')
-    print('   ', ' '.join(cmd))
+    if args.sync:
+      subprocess.check_call([
+          _GSUTIL, '-m', 'rsync', '-J', '-a', 'public-read', '-r', staging_dir,
+          _PUSH_URL
+      ])
+      subprocess.check_call([
+          _GSUTIL, 'setmeta', '-h', 'Cache-Control:no-cache',
+          _PUSH_URL + 'milestones.json'
+      ])
+    else:
+      logging.warning('Finished dry run. Run with --sync to upload.')
 
 
 if __name__ == '__main__':
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index a5c46ff..7d33689 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -20326,6 +20326,17 @@
   <int value="3" label="NON_ERROR_PAGE_URL_AND_NO_ERROR_INFO"/>
 </enum>
 
+<enum name="EulaScreenUserAction">
+  <summary>Actions which happen on the screen.</summary>
+  <int value="0" label="Accept button clicked"/>
+  <int value="1" label="Back button clicked"/>
+  <int value="2" label="Additional ToS shown"/>
+  <int value="3" label="Security settings shown"/>
+  <int value="4" label="Learn more about usage stats shown"/>
+  <int value="5" label="Stats usage unselected"/>
+  <int value="6" label="Stats usage selected"/>
+</enum>
+
 <enum name="EVCTCompliance">
   <obsolete>
     Deprecated 06/2017.
@@ -26913,6 +26924,7 @@
   <int value="3199" label="CrossOriginEmbedderPolicyRequireCorp"/>
   <int value="3200" label="CoopAndCoepIsolated"/>
   <int value="3201" label="WrongBaselineOfButtonElement"/>
+  <int value="3202" label="V8Document_HasTrustToken_Method"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -69268,7 +69280,6 @@
   <int value="40" label="OutstandingNetworkRequestFetch"/>
   <int value="41" label="OutstandingNetworkRequestXHR"/>
   <int value="42" label="AppBanner"/>
-  <int value="43" label="Printing"/>
 </enum>
 
 <enum name="WebShareMethod">
@@ -69298,6 +69309,8 @@
   <int value="8" label="Connection help opened"/>
   <int value="9" label="Site settings opened"/>
   <int value="10" label="Security details opened"/>
+  <int value="11" label="Third-party cookies allowed for site"/>
+  <int value="12" label="Third-party cookies blocked for site"/>
 </enum>
 
 <enum name="WebSiteSettingsAllSitesAction">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index c3220515..680d444 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -43063,6 +43063,10 @@
 
 <histogram name="Download.Start.ContentType.InsecureChain"
     enum="DownloadContentType" expires_after="2020-09-06">
+  <obsolete>
+    Removed in 04/2020. Superseded by mixed download metrics under
+    Download.InsecureBlocking.
+  </obsolete>
   <owner>cthomp@chromium.org</owner>
   <owner>estark@chromium.org</owner>
   <summary>
@@ -43086,6 +43090,10 @@
 
 <histogram name="Download.Start.ContentType.SecureChain"
     enum="DownloadContentType" expires_after="2020-05-03">
+  <obsolete>
+    Removed in 04/2020. Superseded by mixed download metrics under
+    Download.InsecureBlocking.
+  </obsolete>
   <owner>cthomp@chromium.org</owner>
   <owner>estark@chromium.org</owner>
   <summary>
@@ -79240,7 +79248,7 @@
     expires_after="2020-08-09">
 <!-- Name completed by a combination of the following two histogram_suffixes: -->
 
-<!-- histogram_suffixes name="GPU.PeakMemoryUsage" -->
+<!-- histogram_suffixes name="GPU.PeakMemoryAllocationSourceBase" -->
 
 <!-- and histogram_suffixes name="GPU.PeakMemoryAllocationSource" -->
 
@@ -108837,6 +108845,13 @@
   <summary>Time spent on error screens during update.</summary>
 </histogram>
 
+<histogram name="OOBE.EulaScreen.UserActions" enum="EulaScreenUserAction"
+    expires_after="2021-02-02">
+  <owner>raleksandrov@google.com</owner>
+  <owner>cros-oac@google.com</owner>
+  <summary>Counts all user actions on the EulaScreen.</summary>
+</histogram>
+
 <histogram name="OOBE.EULAToSignInTime" units="ms" expires_after="never">
 <!-- expires-never: Core metric for monitoring initial Gaia loading regressions.
 -->
@@ -135530,6 +135545,9 @@
 
 <histogram name="ResourceReporter.BrowserProcess.CpuUsage"
     enum="ResourceReporterCpuUsage" expires_after="M85">
+  <obsolete>
+    Removed 04/2020.
+  </obsolete>
   <owner>afakhry@chromium.org</owner>
   <summary>
     The CPU usage range reported for the browser process when the Chrome OS
@@ -135541,6 +135559,9 @@
 
 <histogram name="ResourceReporter.BrowserProcess.MemoryUsage"
     enum="ResourceReporterMemoryUsage" expires_after="M85">
+  <obsolete>
+    Removed 04/2020.
+  </obsolete>
   <owner>afakhry@chromium.org</owner>
   <summary>
     The system memory usage range reported for the browser process when the
@@ -135552,6 +135573,9 @@
 
 <histogram name="ResourceReporter.GpuProcess.CpuUsage"
     enum="ResourceReporterCpuUsage" expires_after="M85">
+  <obsolete>
+    Removed 04/2020.
+  </obsolete>
   <owner>afakhry@chromium.org</owner>
   <summary>
     The CPU usage range reported for the GPU process when the Chrome OS device
@@ -135563,6 +135587,9 @@
 
 <histogram name="ResourceReporter.GpuProcess.MemoryUsage"
     enum="ResourceReporterMemoryUsage" expires_after="M85">
+  <obsolete>
+    Removed 04/2020.
+  </obsolete>
   <owner>afakhry@chromium.org</owner>
   <summary>
     The system's RAM memory usage range reported for the GPU process when the
@@ -151706,7 +151733,10 @@
 </histogram>
 
 <histogram name="Signin.OAuthMultiloginResponseStatus"
-    enum="OAuthMultiloginResponseStatus" expires_after="2020-04-19">
+    enum="OAuthMultiloginResponseStatus" expires_after="never">
+<!-- expires-never: This reports the status received from gaia Multilogin and
+should be kept to monitor/debug signin Multilogin issues-->
+
   <owner>droger@chromium.org</owner>
   <owner>msarda@chromium.org</owner>
   <summary>Response status received from gaia Multilogin.</summary>
@@ -182347,8 +182377,9 @@
 </histogram>
 
 <histogram name="WebsiteSettings.Action" enum="WebsiteSettingsAction"
-    expires_after="M81">
+    expires_after="M87">
   <owner>estark@chromium.org</owner>
+  <owner>dullweber@chromium.org</owner>
   <summary>
     Tracks actions with the website setting (a.k.a. page info / origin info)
     bubble, such as opening it up or clicking on the Connection tab.
@@ -189939,13 +189970,20 @@
   <affected-histogram name="Memory.GPU.PeakMemoryAllocationSource.Scroll"/>
 </histogram_suffixes>
 
-<histogram_suffixes name="GPU.PeakMemoryUsage" separator=".">
+<histogram_suffixes name="GPU.PeakMemoryAllocationSourcesBase" separator=".">
   <suffix base="true" name="ChangeTab" label="Changing Tabs."/>
   <suffix base="true" name="PageLoad" label="Page Load."/>
   <suffix base="true" name="Scroll" label="Scroll."/>
   <affected-histogram name="Memory.GPU.PeakMemoryAllocationSource"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="GPU.PeakMemoryUsage" separator=".">
+  <suffix name="ChangeTab" label="Changing Tabs."/>
+  <suffix name="PageLoad" label="Page Load."/>
+  <suffix name="Scroll" label="Scroll."/>
+  <affected-histogram name="Memory.GPU.PeakMemoryUsage"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="GPU.ProtectedVideoType" separator=".">
   <suffix name="Clear" label="Clear"/>
   <suffix name="HardwareProtected" label="HardwareProtected"/>
diff --git a/tools/metrics/rappor/rappor.xml b/tools/metrics/rappor/rappor.xml
index c7b03f7..2f0e0127 100644
--- a/tools/metrics/rappor/rappor.xml
+++ b/tools/metrics/rappor/rappor.xml
@@ -161,6 +161,9 @@
 </rappor-metric>
 
 <rappor-metric name="ResourceReporter.Cpu" type="UMA_RAPPOR_TYPE">
+  <obsolete>
+    Removed 04/2020.
+  </obsolete>
   <owner>afakhry@chromium.org</owner>
   <summary>
     A Chrome task, its process priority, and its CPU usage range.
@@ -200,6 +203,9 @@
 </rappor-metric>
 
 <rappor-metric name="ResourceReporter.Memory" type="UMA_RAPPOR_TYPE">
+  <obsolete>
+    Removed 04/2020.
+  </obsolete>
   <owner>afakhry@chromium.org</owner>
   <summary>
     A Chrome task, and its memory usage range.
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index 2039dbe5..669a42a1 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -456,12 +456,26 @@
   ]
 }
 
+android_library("clipboard_java_test_support") {
+  testonly = true
+  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+
+  sources =
+      [ "javatests/src/org/chromium/ui/base/ClipboardAndroidTestSupport.java" ]
+
+  deps = [
+    "//base:base_java",
+    "//base:base_java_test_support",
+  ]
+}
+
 android_library("ui_javatests") {
   testonly = true
 
   sources = [ "javatests/src/org/chromium/ui/base/ClipboardAndroidTest.java" ]
 
   deps = [
+    ":clipboard_java_test_support",
     ":ui_java",
     ":ui_java_test_support",
     "//base:base_java",
@@ -470,12 +484,11 @@
     "//content/public/test/android:content_java_test_support",
     "//third_party/junit",
   ]
-
-  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
 }
 
 generate_jni("ui_javatest_jni_headers") {
   testonly = true
 
-  sources = [ "javatests/src/org/chromium/ui/base/ClipboardAndroidTest.java" ]
+  sources =
+      [ "javatests/src/org/chromium/ui/base/ClipboardAndroidTestSupport.java" ]
 }
diff --git a/ui/android/javatests/src/org/chromium/ui/base/ClipboardAndroidTest.java b/ui/android/javatests/src/org/chromium/ui/base/ClipboardAndroidTest.java
index 1567e248..e5450a5 100644
--- a/ui/android/javatests/src/org/chromium/ui/base/ClipboardAndroidTest.java
+++ b/ui/android/javatests/src/org/chromium/ui/base/ClipboardAndroidTest.java
@@ -14,10 +14,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.annotations.JNINamespace;
-import org.chromium.base.annotations.NativeMethods;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.content_public.browser.test.NativeLibraryTestRule;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
@@ -26,7 +23,6 @@
 /**
  * Clipboard tests for Android platform that depend on access to the ClipboardManager.
  */
-@JNINamespace("ui")
 @RunWith(BaseJUnit4ClassRunner.class)
 public class ClipboardAndroidTest extends DummyUiActivityTestCase {
     @Rule
@@ -40,7 +36,8 @@
 
     @Override
     public void tearDownTest() throws Exception {
-        ClipboardAndroidTestJni.get().cleanup();
+        ClipboardAndroidTestSupport.cleanup();
+        super.tearDownTest();
     }
 
     /**
@@ -49,7 +46,6 @@
      */
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1067719")
     public void internalClipboardInvalidation() {
         CriteriaHelper.pollUiThread(() -> getActivity().hasWindowFocus());
 
@@ -57,7 +53,7 @@
         final String originalText = "foo";
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             Assert.assertTrue("Original text was not written to the native clipboard.",
-                    ClipboardAndroidTestJni.get().nativeWriteHtml(originalText));
+                    ClipboardAndroidTestSupport.writeHtml(originalText));
         });
 
         // Assert that the ClipboardManager contains the original text. Then simulate another
@@ -76,14 +72,7 @@
         // Assert that the overwrite from another application is registered by the native clipboard.
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             Assert.assertTrue("Invalidating text not found in the native clipboard.",
-                    ClipboardAndroidTestJni.get().nativeClipboardContains(invalidatingText));
+                    ClipboardAndroidTestSupport.clipboardContains(invalidatingText));
         });
     }
-
-    @NativeMethods
-    interface Natives {
-        void cleanup();
-        boolean nativeWriteHtml(String htmlText);
-        boolean nativeClipboardContains(String text);
-    }
 }
diff --git a/ui/android/javatests/src/org/chromium/ui/base/ClipboardAndroidTestSupport.java b/ui/android/javatests/src/org/chromium/ui/base/ClipboardAndroidTestSupport.java
new file mode 100644
index 0000000..cd45a37
--- /dev/null
+++ b/ui/android/javatests/src/org/chromium/ui/base/ClipboardAndroidTestSupport.java
@@ -0,0 +1,44 @@
+// Copyright 2020 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.ui.base;
+
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+
+/**
+ * JNI methods for {@link ClipboardAndroidTest}.
+ */
+@JNINamespace("ui")
+public class ClipboardAndroidTestSupport {
+    /**
+     * Cleans up clipboard on native side.
+     */
+    public static void cleanup() {
+        ClipboardAndroidTestSupportJni.get().cleanup();
+    }
+
+    /**
+     * Writes HTML to the native side clipboard.
+     * @param htmlText the htmlText to write.
+     */
+    public static boolean writeHtml(String htmlText) {
+        return ClipboardAndroidTestSupportJni.get().nativeWriteHtml(htmlText);
+    }
+
+    /**
+     * Checks that the native side clipboard contains.
+     * @param text the expected text.
+     */
+    public static boolean clipboardContains(String text) {
+        return ClipboardAndroidTestSupportJni.get().nativeClipboardContains(text);
+    }
+
+    @NativeMethods
+    interface Natives {
+        void cleanup();
+        boolean nativeWriteHtml(String htmlText);
+        boolean nativeClipboardContains(String text);
+    }
+}
diff --git a/ui/base/clipboard/DEPS b/ui/base/clipboard/DEPS
index 952f025..e703203 100644
--- a/ui/base/clipboard/DEPS
+++ b/ui/base/clipboard/DEPS
@@ -1,7 +1,7 @@
 include_rules = [
   "+mojo/public/cpp/base",
   "+third_party/mozilla",
-  "+ui/android/ui_javatest_jni_headers/ClipboardAndroidTest_jni.h",
+  "+ui/android/ui_javatest_jni_headers/ClipboardAndroidTestSupport_jni.h",
   "+ui/base/ui_base_jni_headers",
   "-ui/ozone/*",
   "+ui/ozone/buildflags.h",
diff --git a/ui/base/clipboard/clipboard_android_test_support.cc b/ui/base/clipboard/clipboard_android_test_support.cc
index 3df5e46..c14b76b 100644
--- a/ui/base/clipboard/clipboard_android_test_support.cc
+++ b/ui/base/clipboard/clipboard_android_test_support.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 "ui/android/ui_javatest_jni_headers/ClipboardAndroidTest_jni.h"
+#include "ui/android/ui_javatest_jni_headers/ClipboardAndroidTestSupport_jni.h"
 
 #include <string>
 
@@ -15,11 +15,11 @@
 
 namespace ui {
 
-void JNI_ClipboardAndroidTest_Cleanup(JNIEnv* env) {
+void JNI_ClipboardAndroidTestSupport_Cleanup(JNIEnv* env) {
   Clipboard::DestroyClipboardForCurrentThread();
 }
 
-jboolean JNI_ClipboardAndroidTest_NativeWriteHtml(
+jboolean JNI_ClipboardAndroidTestSupport_NativeWriteHtml(
     JNIEnv* env,
     const base::android::JavaParamRef<jstring>& j_html_text) {
   {
@@ -39,7 +39,7 @@
                                       ClipboardBuffer::kCopyPaste);
 }
 
-jboolean JNI_ClipboardAndroidTest_NativeClipboardContains(
+jboolean JNI_ClipboardAndroidTestSupport_NativeClipboardContains(
     JNIEnv* env,
     const base::android::JavaParamRef<jstring>& j_text) {
   // The Java side of the test pretended to be another app using
diff --git a/ui/base/l10n/l10n_util.cc b/ui/base/l10n/l10n_util.cc
index 70cae720..f21fee3 100644
--- a/ui/base/l10n/l10n_util.cc
+++ b/ui/base/l10n/l10n_util.cc
@@ -888,6 +888,12 @@
   }
 }
 
+void GetAcceptLanguages(std::vector<std::string>* locale_codes) {
+  for (const char* accept_language : kAcceptLanguageList) {
+    locale_codes->push_back(accept_language);
+  }
+}
+
 bool IsLanguageAccepted(const std::string& display_locale,
                         const std::string& locale) {
   for (const char* accept_language : kAcceptLanguageList) {
diff --git a/ui/base/l10n/l10n_util.h b/ui/base/l10n/l10n_util.h
index a371dc42..21de1482 100644
--- a/ui/base/l10n/l10n_util.h
+++ b/ui/base/l10n/l10n_util.h
@@ -212,6 +212,9 @@
     const std::string& display_locale,
     std::vector<std::string>* locale_codes);
 
+// Returns a vector of untranslated locale codes usable for accept-languages.
+UI_BASE_EXPORT void GetAcceptLanguages(std::vector<std::string>* locale_codes);
+
 // Returns true if |locale| is in a predefined AcceptLanguageList and
 // a display name for the |locale| is available in the locale |display_locale|.
 UI_BASE_EXPORT bool IsLanguageAccepted(const std::string& display_locale,
diff --git a/ui/message_center/views/message_popup_view.cc b/ui/message_center/views/message_popup_view.cc
index a8ee041..bd6fac81 100644
--- a/ui/message_center/views/message_popup_view.cc
+++ b/ui/message_center/views/message_popup_view.cc
@@ -113,7 +113,7 @@
   views::Widget* widget = new views::Widget();
   popup_collection_->ConfigureWidgetInitParamsForContainer(widget, &params);
   widget->set_focus_on_creation(false);
-  widget->AddObserver(this);
+  observer_.Add(widget);
 
 #if defined(OS_WIN)
   // We want to ensure that this toast always goes to the native desktop,
@@ -204,6 +204,10 @@
   popup_collection_->Update();
 }
 
+void MessagePopupView::OnWidgetDestroyed(views::Widget* widget) {
+  observer_.Remove(widget);
+}
+
 bool MessagePopupView::IsWidgetValid() const {
   return GetWidget() && !GetWidget()->IsClosed();
 }
diff --git a/ui/message_center/views/message_popup_view.h b/ui/message_center/views/message_popup_view.h
index 550925a..9b5476c5d 100644
--- a/ui/message_center/views/message_popup_view.h
+++ b/ui/message_center/views/message_popup_view.h
@@ -5,7 +5,9 @@
 #ifndef UI_MESSAGE_CENTER_VIEWS_MESSAGE_POPUP_VIEW_H_
 #define UI_MESSAGE_CENTER_VIEWS_MESSAGE_POPUP_VIEW_H_
 
+#include "base/scoped_observer.h"
 #include "ui/message_center/message_center_export.h"
+#include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 #include "ui/views/widget/widget_observer.h"
 
@@ -59,6 +61,7 @@
 
   // views::WidgetObserver:
   void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
+  void OnWidgetDestroyed(views::Widget* widget) override;
 
   bool is_hovered() const { return is_hovered_; }
   bool is_active() const { return is_active_; }
@@ -83,6 +86,8 @@
   bool is_hovered_ = false;
   bool is_active_ = false;
 
+  ScopedObserver<views::Widget, views::WidgetObserver> observer_{this};
+
   DISALLOW_COPY_AND_ASSIGN(MessagePopupView);
 };
 
diff --git a/ui/platform_window/x11/x11_window.cc b/ui/platform_window/x11/x11_window.cc
index 98b442e..e42446d 100644
--- a/ui/platform_window/x11/x11_window.cc
+++ b/ui/platform_window/x11/x11_window.cc
@@ -61,7 +61,6 @@
       return WindowType::kPopup;
     case PlatformWindowType::kDrag:
       return WindowType::kDrag;
-      break;
     case PlatformWindowType::kBubble:
       return WindowType::kBubble;
   }
diff --git a/ui/views/accessibility/ax_virtual_view.cc b/ui/views/accessibility/ax_virtual_view.cc
index cf273bc..ea292416 100644
--- a/ui/views/accessibility/ax_virtual_view.cc
+++ b/ui/views/accessibility/ax_virtual_view.cc
@@ -11,6 +11,7 @@
 #include <utility>
 
 #include "base/callback.h"
+#include "base/containers/adapters.h"
 #include "base/no_destructor.h"
 #include "build/build_config.h"
 #include "ui/accessibility/ax_action_data.h"
@@ -236,6 +237,15 @@
 
   if (populate_data_callback_ && GetOwnerView())
     populate_data_callback_.Run(&node_data);
+
+  // According to the ARIA spec, the node should not be ignored if it is
+  // focusable. This is to ensure that the focusable node is both understandable
+  // and operable.
+  if (node_data.HasState(ax::mojom::State::kIgnored) &&
+      node_data.HasState(ax::mojom::State::kFocusable)) {
+    node_data.RemoveState(ax::mojom::State::kIgnored);
+  }
+
   return node_data;
 }
 
@@ -302,12 +312,24 @@
     const ui::AXCoordinateSystem coordinate_system,
     const ui::AXClippingBehavior clipping_behavior,
     ui::AXOffscreenResult* offscreen_result) const {
+  // We could optionally add clipping here if ever needed.
+  // TODO(nektar): Implement bounds that are relative to the parent.
+  gfx::Rect bounds = gfx::ToEnclosingRect(GetData().relative_bounds.bounds);
+  View* owner_view = GetOwnerView();
+  if (owner_view && owner_view->GetWidget())
+    View::ConvertRectToScreen(owner_view, &bounds);
   switch (coordinate_system) {
     case ui::AXCoordinateSystem::kScreenDIPs:
-      // We could optionally add clipping here if ever needed.
-      // TODO(nektar): Implement bounds that are relative to the parent.
-      return gfx::ToEnclosingRect(custom_data_.relative_bounds.bounds);
-    case ui::AXCoordinateSystem::kScreenPhysicalPixels:
+      return bounds;
+    case ui::AXCoordinateSystem::kScreenPhysicalPixels: {
+      float scale_factor = 1.0;
+      if (owner_view && owner_view->GetWidget()) {
+        gfx::NativeView native_view = owner_view->GetWidget()->GetNativeView();
+        if (native_view)
+          scale_factor = ui::GetScaleFactorForNativeView(native_view);
+      }
+      return gfx::ScaleToEnclosingRect(bounds, scale_factor);
+    }
     case ui::AXCoordinateSystem::kRootFrame:
     case ui::AXCoordinateSystem::kFrame:
       NOTIMPLEMENTED();
@@ -318,27 +340,39 @@
 gfx::NativeViewAccessible AXVirtualView::HitTestSync(
     int screen_physical_pixel_x,
     int screen_physical_pixel_y) const {
-  if (custom_data_.relative_bounds.bounds.Contains(
-          static_cast<float>(screen_physical_pixel_x),
-          static_cast<float>(screen_physical_pixel_y))) {
-    if (!IsIgnored())
-      return GetNativeObject();
-  }
+  const ui::AXNodeData& node_data = GetData();
+  if (node_data.HasState(ax::mojom::State::kInvisible))
+    return nullptr;
 
   // Check if the point is within any of the virtual children of this view.
   // AXVirtualView's HitTestSync is a recursive function that will return the
   // deepest child, since it does not support relative bounds.
-  for (const std::unique_ptr<AXVirtualView>& child : children_) {
+  // Search the greater indices first, since they're on top in the z-order.
+  for (const std::unique_ptr<AXVirtualView>& child :
+       base::Reversed(children_)) {
     gfx::NativeViewAccessible result =
         child->HitTestSync(screen_physical_pixel_x, screen_physical_pixel_y);
     if (result)
       return result;
   }
+
+  // If it's not inside any of our virtual children, and it's inside the bounds
+  // of this virtual view, then it's inside this virtual view.
+  gfx::Rect bounds_in_screen_physical_pixels =
+      GetBoundsRect(ui::AXCoordinateSystem::kScreenPhysicalPixels,
+                    ui::AXClippingBehavior::kUnclipped);
+  if (bounds_in_screen_physical_pixels.Contains(
+          static_cast<float>(screen_physical_pixel_x),
+          static_cast<float>(screen_physical_pixel_y)) &&
+      !node_data.IsIgnored()) {
+    return GetNativeObject();
+  }
+
   return nullptr;
 }
 
 gfx::NativeViewAccessible AXVirtualView::GetFocus() {
-  auto* owner_view = GetOwnerView();
+  View* owner_view = GetOwnerView();
   if (owner_view) {
     if (!(owner_view->HasFocus())) {
       return nullptr;
@@ -389,15 +423,7 @@
 }
 
 bool AXVirtualView::IsIgnored() const {
-  const ui::AXNodeData& node_data = GetData();
-
-  // According to the ARIA spec, the node should not be ignored if it is
-  // focusable. This is to ensure that the focusable node is both understandable
-  // and operable.
-  if (node_data.HasState(ax::mojom::State::kFocusable))
-    return false;
-
-  return node_data.IsIgnored();
+  return GetData().IsIgnored();
 }
 
 bool AXVirtualView::HandleAccessibleAction(
diff --git a/ui/views/accessibility/ax_virtual_view.h b/ui/views/accessibility/ax_virtual_view.h
index c74273c..105c961b 100644
--- a/ui/views/accessibility/ax_virtual_view.h
+++ b/ui/views/accessibility/ax_virtual_view.h
@@ -142,7 +142,7 @@
   gfx::Rect GetBoundsRect(
       const ui::AXCoordinateSystem coordinate_system,
       const ui::AXClippingBehavior clipping_behavior,
-      ui::AXOffscreenResult* offscreen_result) const override;
+      ui::AXOffscreenResult* offscreen_result = nullptr) const override;
   gfx::NativeViewAccessible HitTestSync(
       int screen_physical_pixel_x,
       int screen_physical_pixel_y) const override;
diff --git a/ui/views/accessibility/ax_virtual_view_unittest.cc b/ui/views/accessibility/ax_virtual_view_unittest.cc
index 3f1cb20..b48ada53 100644
--- a/ui/views/accessibility/ax_virtual_view_unittest.cc
+++ b/ui/views/accessibility/ax_virtual_view_unittest.cc
@@ -626,6 +626,51 @@
   EXPECT_EQ(0, virtual_child_4->GetIndexInParent());
 }
 
+TEST_F(AXVirtualViewTest, HitTesting) {
+  ASSERT_EQ(0, virtual_label_->GetChildCount());
+
+  const gfx::Vector2d offset_from_origin =
+      button_->GetBoundsInScreen().OffsetFromOrigin();
+
+  // Test that hit testing is recursive.
+  AXVirtualView* virtual_child_1 = new AXVirtualView;
+  virtual_child_1->GetCustomData().relative_bounds.bounds =
+      gfx::RectF(0, 0, 10, 10);
+  virtual_label_->AddChildView(base::WrapUnique(virtual_child_1));
+  AXVirtualView* virtual_child_2 = new AXVirtualView;
+  virtual_child_2->GetCustomData().relative_bounds.bounds =
+      gfx::RectF(5, 5, 5, 5);
+  virtual_child_1->AddChildView(base::WrapUnique(virtual_child_2));
+  gfx::Point point_1 = gfx::Point(2, 2) + offset_from_origin;
+  EXPECT_EQ(virtual_child_1->GetNativeObject(),
+            virtual_child_1->HitTestSync(point_1.x(), point_1.y()));
+  gfx::Point point_2 = gfx::Point(7, 7) + offset_from_origin;
+  EXPECT_EQ(virtual_child_2->GetNativeObject(),
+            virtual_label_->HitTestSync(point_2.x(), point_2.y()));
+
+  // Test that hit testing follows the z-order.
+  AXVirtualView* virtual_child_3 = new AXVirtualView;
+  virtual_child_3->GetCustomData().relative_bounds.bounds =
+      gfx::RectF(5, 5, 10, 10);
+  virtual_label_->AddChildView(base::WrapUnique(virtual_child_3));
+  AXVirtualView* virtual_child_4 = new AXVirtualView;
+  virtual_child_4->GetCustomData().relative_bounds.bounds =
+      gfx::RectF(10, 10, 10, 10);
+  virtual_child_3->AddChildView(base::WrapUnique(virtual_child_4));
+  EXPECT_EQ(virtual_child_3->GetNativeObject(),
+            virtual_label_->HitTestSync(point_2.x(), point_2.y()));
+  gfx::Point point_3 = gfx::Point(12, 12) + offset_from_origin;
+  EXPECT_EQ(virtual_child_4->GetNativeObject(),
+            virtual_label_->HitTestSync(point_3.x(), point_3.y()));
+
+  // Test that hit testing skips ignored nodes but not their descendants.
+  virtual_child_3->GetCustomData().AddState(ax::mojom::State::kIgnored);
+  EXPECT_EQ(virtual_child_2->GetNativeObject(),
+            virtual_label_->HitTestSync(point_2.x(), point_2.y()));
+  EXPECT_EQ(virtual_child_4->GetNativeObject(),
+            virtual_label_->HitTestSync(point_3.x(), point_3.y()));
+}
+
 // Test for GetTargetForNativeAccessibilityEvent().
 #if defined(OS_WIN)
 TEST_F(AXVirtualViewTest, GetTargetForEvents) {
diff --git a/ui/views/controls/table/table_view.cc b/ui/views/controls/table/table_view.cc
index 458e247..94eb7a0 100644
--- a/ui/views/controls/table/table_view.cc
+++ b/ui/views/controls/table/table_view.cc
@@ -1422,28 +1422,33 @@
 }
 
 gfx::Rect TableView::CalculateHeaderRowAccessibilityBounds() const {
-  return AdjustRectForAXRelativeBounds(header_->GetVisibleBounds());
+  gfx::Rect header_bounds = header_->GetVisibleBounds();
+  gfx::Point header_origin = header_bounds.origin();
+  ConvertPointToTarget(header_, this, &header_origin);
+  header_bounds.set_origin(header_origin);
+  return header_bounds;
 }
 
 gfx::Rect TableView::CalculateHeaderCellAccessibilityBounds(
     const int visible_column_index) const {
+  const gfx::Rect& header_bounds = CalculateHeaderRowAccessibilityBounds();
   const VisibleColumn& visible_column = visible_columns_[visible_column_index];
-  gfx::Rect header_cell_bounds(visible_column.x, header_->y(),
-                               visible_column.width, header_->height());
-  return AdjustRectForAXRelativeBounds(header_cell_bounds);
+  gfx::Rect header_cell_bounds(visible_column.x, header_bounds.y(),
+                               visible_column.width, header_bounds.height());
+  return header_cell_bounds;
 }
 
 gfx::Rect TableView::CalculateTableRowAccessibilityBounds(
     const int row_index) const {
   gfx::Rect row_bounds = GetRowBounds(row_index);
-  return AdjustRectForAXRelativeBounds(row_bounds);
+  return row_bounds;
 }
 
 gfx::Rect TableView::CalculateTableCellAccessibilityBounds(
     const int row_index,
     const int visible_column_index) const {
   gfx::Rect cell_bounds = GetCellBounds(row_index, visible_column_index);
-  return AdjustRectForAXRelativeBounds(cell_bounds);
+  return cell_bounds;
 }
 
 void TableView::UpdateAccessibilityFocus() {
@@ -1510,13 +1515,6 @@
   return i->get();
 }
 
-gfx::Rect TableView::AdjustRectForAXRelativeBounds(
-    const gfx::Rect& rect) const {
-  gfx::Rect converted_rect = rect;
-  View::ConvertRectToScreen(this, &converted_rect);
-  return converted_rect;
-}
-
 DEFINE_ENUM_CONVERTERS(TableTypes,
                        {TableTypes::TEXT_ONLY, base::ASCIIToUTF16("TEXT_ONLY")},
                        {TableTypes::ICON_AND_TEXT,
diff --git a/ui/views/controls/table/table_view.h b/ui/views/controls/table/table_view.h
index 6a5546c7..1a640fd 100644
--- a/ui/views/controls/table/table_view.h
+++ b/ui/views/controls/table/table_view.h
@@ -383,11 +383,6 @@
   // |visible_column_index| indexes into |visible_columns_|.
   AXVirtualView* GetVirtualAccessibilityCell(int row, int visible_column_index);
 
-  // Returns |rect|, adjusted for use in AXRelativeBounds by translating it into
-  // screen coordinates. The result must be converted to gfx::RectF when setting
-  // into AXRelativeBounds.
-  gfx::Rect AdjustRectForAXRelativeBounds(const gfx::Rect& rect) const;
-
   ui::TableModel* model_ = nullptr;
 
   std::vector<ui::TableColumn> columns_;
diff --git a/ui/views/controls/table/table_view_unittest.cc b/ui/views/controls/table/table_view_unittest.cc
index fb2a3ec..df538cc5 100644
--- a/ui/views/controls/table/table_view_unittest.cc
+++ b/ui/views/controls/table/table_view_unittest.cc
@@ -80,23 +80,33 @@
 
     // Generate the bounds for the header row and cells.
     auto header_row = std::vector<gfx::Rect>();
-    header_row.push_back(table_->CalculateHeaderRowAccessibilityBounds());
+    gfx::Rect header_row_bounds =
+        table_->CalculateHeaderRowAccessibilityBounds();
+    View::ConvertRectToScreen(table_, &header_row_bounds);
+    header_row.push_back(header_row_bounds);
     for (size_t column_index = 0; column_index < visible_col_count();
          column_index++) {
-      header_row.push_back(
-          table_->CalculateHeaderCellAccessibilityBounds(column_index));
+      gfx::Rect header_cell_bounds =
+          table_->CalculateHeaderCellAccessibilityBounds(column_index);
+      View::ConvertRectToScreen(table_, &header_cell_bounds);
+      header_row.push_back(header_cell_bounds);
     }
     expected_bounds.push_back(header_row);
 
     // Generate the bounds for the table rows and cells.
     for (int row_index = 0; row_index < table_->GetRowCount(); row_index++) {
       auto table_row = std::vector<gfx::Rect>();
-      table_row.push_back(
-          table_->CalculateTableRowAccessibilityBounds(row_index));
+      gfx::Rect table_row_bounds =
+          table_->CalculateTableRowAccessibilityBounds(row_index);
+      View::ConvertRectToScreen(table_, &table_row_bounds);
+      table_row.push_back(table_row_bounds);
       for (size_t column_index = 0; column_index < visible_col_count();
            column_index++) {
-        table_row.push_back(table_->CalculateTableCellAccessibilityBounds(
-            row_index, column_index));
+        gfx::Rect table_cell_bounds =
+            table_->CalculateTableCellAccessibilityBounds(row_index,
+                                                          column_index);
+        View::ConvertRectToScreen(table_, &table_cell_bounds);
+        table_row.push_back(table_cell_bounds);
       }
       expected_bounds.push_back(table_row);
     }
diff --git a/ui/views/controls/tree/tree_view.cc b/ui/views/controls/tree/tree_view.cc
index 7d26bdf..01da559 100644
--- a/ui/views/controls/tree/tree_view.cc
+++ b/ui/views/controls/tree/tree_view.cc
@@ -922,7 +922,6 @@
     data->AddAction(ax::mojom::Action::kFocus);
     data->AddAction(ax::mojom::Action::kScrollToMakeVisible);
     gfx::Rect node_bounds = GetBackgroundBoundsForNode(node);
-    View::ConvertRectToScreen(this, &node_bounds);
     data->relative_bounds.bounds = gfx::RectF(node_bounds);
   } else {
     data->AddState(ax::mojom::State::kInvisible);
diff --git a/ui/views/corewm/tooltip_aura.cc b/ui/views/corewm/tooltip_aura.cc
index a1e74af..c115ea2b 100644
--- a/ui/views/corewm/tooltip_aura.cc
+++ b/ui/views/corewm/tooltip_aura.cc
@@ -296,6 +296,8 @@
 
 void TooltipAura::OnWidgetDestroying(views::Widget* widget) {
   DCHECK_EQ(widget_, widget);
+  if (widget_)
+    widget_->RemoveObserver(this);
   widget_ = nullptr;
   tooltip_window_ = nullptr;
 }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java
index f6f5a02..557f316 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java
@@ -362,7 +362,7 @@
         // Create the NotificationChannel, but only on API 26+ because
         // the NotificationChannel class is new and not in the support library
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            int importance = NotificationManager.IMPORTANCE_DEFAULT;
+            int importance = NotificationManager.IMPORTANCE_LOW;
             NotificationChannel channel =
                     new NotificationChannel(CHANNEL_ID, "Downloads", importance);
             NotificationManager notificationManager =
diff --git a/weblayer/public/java/org/chromium/weblayer/BroadcastReceiver.java b/weblayer/public/java/org/chromium/weblayer/BroadcastReceiver.java
index f78ca697..4721e3fa 100644
--- a/weblayer/public/java/org/chromium/weblayer/BroadcastReceiver.java
+++ b/weblayer/public/java/org/chromium/weblayer/BroadcastReceiver.java
@@ -13,7 +13,7 @@
 /**
  * Listens to events from WebLayer-spawned notifications.
  */
-class BroadcastReceiver extends android.content.BroadcastReceiver {
+public class BroadcastReceiver extends android.content.BroadcastReceiver {
     @Override
     public void onReceive(Context context, Intent intent) {
         try {