diff --git a/DEPS b/DEPS
index c441185..2ee5d13 100644
--- a/DEPS
+++ b/DEPS
@@ -211,7 +211,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '29eeb610bd49e80a8109a6b42f2c95adcc619099',
+  'angle_revision': 'd2e76b73efc2c16d7c4a2cddbef4965deeb2f1c9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -246,7 +246,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling NaCl
   # and whatever else without interference from each other.
-  'nacl_revision': 'd28e6c52e05ea2f1b3231de473b6dbfce59bce40',
+  'nacl_revision': 'cb31feffa500401c95c6c44d88b7c358236bca36',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -266,7 +266,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'b2af2da3481cd7a14c8ff6c9edf98164d8fad009',
+  'catapult_revision': 'f46e9e72306f00405741ac4abd146cf904aabe3e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -274,7 +274,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '57654f96e343a4fe2bf29f28a770b5c4f391f759',
+  'devtools_frontend_revision': '3100aca993ac2fc227ed4a29c855c8302e0cff0d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -318,7 +318,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.
-  'quiche_revision': 'e84d83393be4536291ce719787f6a275db07b733',
+  'quiche_revision': '9aef115356ce48848790dd64a0f2d2ed9f55149b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -873,7 +873,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '628b7f61f813da6b7e44a31e34a6c51f041bcf68',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '5fcf0fa9382ef09d010f2ffdcb1ee90dce670efc',
       'condition': 'checkout_chromeos',
   },
 
@@ -893,7 +893,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '091f5ac0a64ce66b75c185eb0ee698ef4e837145',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '428143ee24c5f084c8dfb38cd17f07b4f7ba9bf7',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1391,7 +1391,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/r8',
-              'version': 'DR3nwJggFDcmTDz7P8fJQCtRLO1nxDt26czkOqhtZJ8C',
+              'version': 'F8cKQoSai0fZxFRJVRZnoWeS-oVyp53L7bpuVq9t44AC',
           },
       ],
       'condition': 'checkout_android',
@@ -1588,7 +1588,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'bx6pXjyY-3vkrBjpqnuCDbusE-Ui_g3xSzSffOHHktoC',
+        'version': 'BhbSyh4bjbYmwfA5WJTXzE9uMvDFyyviZuft_yjZ8ZgC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1599,7 +1599,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'DYU_0-Aok5UvqgV35z_dEgBIqBP98Ypx98hUZ8a8NcoC',
+        'version': 'xO6b6gm3QxUDRY3PGCD_ON0uPe2bjwP9ftauSv0ar3cC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 92d6310..cc832089 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -2059,7 +2059,8 @@
         }
 
         // If we are reloading the same url, then set transition type as reload.
-        if (params.getUrl() != null && params.getUrl().equals(mWebContents.getLastCommittedUrl())
+        if (params.getUrl() != null
+                && params.getUrl().equals(mWebContents.getLastCommittedUrl().getSpec())
                 && params.getTransitionType() == PageTransition.TYPED) {
             params.setTransitionType(PageTransition.RELOAD);
         }
@@ -2131,9 +2132,9 @@
      */
     public String getLastCommittedUrl() {
         if (isDestroyed(NO_WARN)) return null;
-        String url = mWebContents.getLastCommittedUrl();
-        if (url == null || url.trim().isEmpty()) return null;
-        return url;
+        GURL url = mWebContents.getLastCommittedUrl();
+        if (url == null || url.isEmpty()) return null;
+        return url.getSpec();
     }
 
     public void requestFocus() {
diff --git a/ash/touch/touch_hud_renderer.cc b/ash/touch/touch_hud_renderer.cc
index e8c3c85..c2390745 100644
--- a/ash/touch/touch_hud_renderer.cc
+++ b/ash/touch/touch_hud_renderer.cc
@@ -4,6 +4,7 @@
 
 #include "ash/touch/touch_hud_renderer.h"
 
+#include "base/scoped_observer.h"
 #include "base/time/time.h"
 #include "third_party/skia/include/effects/SkGradientShader.h"
 #include "ui/compositor/layer.h"
diff --git a/ash/wm/desks/desk_name_view.cc b/ash/wm/desks/desk_name_view.cc
index 5cf6e16..69b0ea09 100644
--- a/ash/wm/desks/desk_name_view.cc
+++ b/ash/wm/desks/desk_name_view.cc
@@ -117,7 +117,7 @@
 }
 
 void DeskNameView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
-  node_data->role = ax::mojom::Role::kTextField;
+  Textfield::GetAccessibleNodeData(node_data);
   // When Bento is enabled and the user creates a new desk, |full_text_| will be
   // empty but the accesssible name for |this| will be the default desk name.
   node_data->SetName(full_text_.empty() ? GetAccessibleName() : full_text_);
diff --git a/base/allocator/BUILD.gn b/base/allocator/BUILD.gn
index c01a56e..9348e2dc7 100644
--- a/base/allocator/BUILD.gn
+++ b/base/allocator/BUILD.gn
@@ -320,5 +320,9 @@
 
     # <unistd.h> functions
     "-Wl,-wrap,getcwd",
+
+    # <stdio.h> functions
+    "-Wl,-wrap,asprintf",
+    "-Wl,-wrap,vasprintf",
   ]
 }
diff --git a/base/allocator/allocator.gni b/base/allocator/allocator.gni
index e15c2216..d15ff97 100644
--- a/base/allocator/allocator.gni
+++ b/base/allocator/allocator.gni
@@ -8,6 +8,12 @@
 # Sanitizers replace the allocator, don't use our own.
 _is_using_sanitizers = is_asan || is_hwasan || is_lsan || is_tsan || is_msan
 
+# - Windows: shims don't work for component builds and debug CRT is not
+# compatible, see below.
+# - Android: symbol wrapping is not universal for component builds.
+_disable_partition_alloc = (is_win && (is_component_build || is_debug)) ||
+                           (is_android && is_component_build)
+
 # The debug CRT on Windows has some debug features that are incompatible with
 # the shim. NaCl in particular does seem to link some binaries statically
 # against the debug CRT with "is_nacl=false".
@@ -18,9 +24,8 @@
   _default_use_allocator_shim = false
 }
 
-# TODO(https://crbug.com/1166748): android/x86 is temporarily disabled.
-if (_default_use_allocator_shim &&
-    (is_win || (is_android && target_cpu != "x86"))) {
+if (_default_use_allocator_shim && (is_win || is_android) &&
+    !_disable_partition_alloc) {
   _default_allocator = "partition"
 } else if (is_android || is_apple || _is_using_sanitizers || is_win ||
            is_fuchsia || ((is_linux || is_chromeos) && target_cpu == "arm64") ||
diff --git a/base/allocator/allocator_shim_override_linker_wrapped_symbols.h b/base/allocator/allocator_shim_override_linker_wrapped_symbols.h
index e2c6a68..04103f9 100644
--- a/base/allocator/allocator_shim_override_linker_wrapped_symbols.h
+++ b/base/allocator/allocator_shim_override_linker_wrapped_symbols.h
@@ -110,4 +110,38 @@
   return __wrap_strdup(local_buffer);
 }
 
+// Override stdio.h
+
+// This is non-standard (_GNU_SOURCE only), but implemented by Bionic on
+// Android, and used by libc++.
+SHIM_ALWAYS_EXPORT int __wrap_vasprintf(char** strp,
+                                        const char* fmt,
+                                        va_list va_args) {
+  constexpr int kInitialSize = 128;
+  *strp = static_cast<char*>(
+      malloc(kInitialSize));  // Our malloc() doesn't return nullptr.
+
+  int actual_size = vsnprintf(*strp, kInitialSize, fmt, va_args);
+  *strp = static_cast<char*>(realloc(*strp, actual_size + 1));
+
+  // Now we know the size. This is not very efficient, but we cannot really do
+  // better without accessing internal libc functions, or reimplementing
+  // *printf().
+  //
+  // This is very lightly used in Chromium in practice, see crbug.com/116558 for
+  // details.
+  if (actual_size >= kInitialSize)
+    return vsnprintf(*strp, actual_size + 1, fmt, va_args);
+
+  return actual_size;
+}
+
+SHIM_ALWAYS_EXPORT int __wrap_asprintf(char** strp, const char* fmt, ...) {
+  va_list va_args;
+  va_start(va_args, fmt);
+  int retval = vasprintf(strp, fmt, va_args);
+  va_end(va_args);
+  return retval;
+}
+
 }  // extern "C"
diff --git a/base/allocator/allocator_shim_unittest.cc b/base/allocator/allocator_shim_unittest.cc
index 41d9568..9a8fb841 100644
--- a/base/allocator/allocator_shim_unittest.cc
+++ b/base/allocator/allocator_shim_unittest.cc
@@ -8,8 +8,10 @@
 #include <string.h>
 
 #include <atomic>
+#include <iomanip>
 #include <memory>
 #include <new>
+#include <sstream>
 #include <vector>
 
 #include "base/allocator/buildflags.h"
@@ -685,8 +687,29 @@
   counts_after = total_counts(allocs_intercepted_by_size);
   EXPECT_GT(counts_after, counts_before);
 
+  // Calls vasprintf() indirectly, see below.
+  counts_before = counts_after;
+  std::stringstream stream;
+  stream << std::setprecision(1) << std::showpoint << std::fixed << 1.e38;
+  EXPECT_GT(stream.str().size(), 30u);
+  counts_after = total_counts(allocs_intercepted_by_size);
+  EXPECT_GT(counts_after, counts_before);
+
   RemoveAllocatorDispatchForTesting(&g_mock_dispatch);
 }
+
+#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
+// Non-regression test for crbug.com/1166558.
+TEST_F(AllocatorShimTest, InterceptVasprintf) {
+  // Printing a float which expands to >=30 characters calls vasprintf() in
+  // libc, which we should intercept.
+  std::stringstream stream;
+  stream << std::setprecision(1) << std::showpoint << std::fixed << 1.e38;
+  EXPECT_GT(stream.str().size(), 30u);
+  // Should not crash.
+}
+#endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
+
 #endif  // defined(OS_ANDROID)
 
 }  // namespace
diff --git a/base/allocator/partition_allocator/partition_root.h b/base/allocator/partition_allocator/partition_root.h
index c594eb05..dd0f164 100644
--- a/base/allocator/partition_allocator/partition_root.h
+++ b/base/allocator/partition_allocator/partition_root.h
@@ -803,6 +803,24 @@
   if (UNLIKELY(!ptr))
     return;
 
+    // On Android, malloc() interception is more fragile than on other
+    // platforms, as we use wrapped symbols. However, the GigaCage allows us to
+    // quickly tell that a pointer was allocated with PartitionAlloc. GigaCage
+    // is unfortunately not used for the aligned partition when BackupRefPtr is
+    // enabled, yielding the set of conditions below.
+    //
+    // This is a crash to detect imperfect symbol interception. However, we can
+    // forward allocations we don't own to the system malloc() implementation in
+    // these rare cases, assuming that some remain.
+    //
+    // TODO(lizeb): Make this a PA_CHECK() at least temporarily to detect
+    // potential issues in the wild as well.
+#if defined(OS_ANDROID) && BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \
+    !ENABLE_REF_COUNT_FOR_BACKUP_REF_PTR
+  PA_DCHECK(IsManagedByPartitionAllocNormalBuckets(ptr) ||
+            IsManagedByPartitionAllocDirectMap(ptr));
+#endif
+
   // No check as the pointer hasn't been adjusted yet.
   SlotSpan* slot_span = SlotSpan::FromPointerNoAlignmentCheck(ptr);
   // TODO(palmer): See if we can afford to make this a CHECK.
diff --git a/base/i18n/time_formatting.cc b/base/i18n/time_formatting.cc
index c085f5e..0a74e4c7 100644
--- a/base/i18n/time_formatting.cc
+++ b/base/i18n/time_formatting.cc
@@ -154,11 +154,22 @@
   return TimeFormat(formatter.get(), time);
 }
 
+#if defined(OS_CHROMEOS)
+string16 TimeFormatMonthAndYear(const Time& time,
+                                const icu::TimeZone* time_zone) {
+  icu::SimpleDateFormat formatter =
+      CreateSimpleDateFormatter(DateFormatToString(DATE_FORMAT_YEAR_MONTH));
+  if (time_zone)
+    formatter.setTimeZone(*time_zone);
+  return TimeFormat(&formatter, time);
+}
+#else
 string16 TimeFormatMonthAndYear(const Time& time) {
   icu::SimpleDateFormat formatter =
       CreateSimpleDateFormatter(DateFormatToString(DATE_FORMAT_YEAR_MONTH));
   return TimeFormat(&formatter, time);
 }
+#endif  // defined(OS_CHROMEOS)
 
 string16 TimeFormatFriendlyDateAndTime(const Time& time) {
   std::unique_ptr<icu::DateFormat> formatter(
diff --git a/base/i18n/time_formatting.h b/base/i18n/time_formatting.h
index 64b09964..1063f6e6 100644
--- a/base/i18n/time_formatting.h
+++ b/base/i18n/time_formatting.h
@@ -11,6 +11,11 @@
 #include "base/compiler_specific.h"
 #include "base/i18n/base_i18n_export.h"
 #include "base/strings/string16.h"
+#include "build/build_config.h"
+
+#if defined(OS_CHROMEOS)
+#include "third_party/icu/source/i18n/unicode/timezone.h"
+#endif  // defined(OS_CHROMEOS)
 
 namespace base {
 
@@ -72,8 +77,17 @@
 // Returns a numeric date and time such as "12/13/52 2:44:30 PM".
 BASE_I18N_EXPORT string16 TimeFormatShortDateAndTime(const Time& time);
 
+#if defined(OS_CHROMEOS)
+// Returns a month and year, e.g. "November 2007"
+// Note: If `time_zone` is non-null, the time will be formatted in the provided
+// time zone. Otherwise, it will default to local time.
+BASE_I18N_EXPORT string16
+TimeFormatMonthAndYear(const Time& time,
+                       const icu::TimeZone* time_zone = nullptr);
+#else
 // Returns a month and year, e.g. "November 2007"
 BASE_I18N_EXPORT string16 TimeFormatMonthAndYear(const Time& time);
+#endif  // defined(OS_CHROMEOS)
 
 // Returns a numeric date and time with time zone such as
 // "12/13/52 2:44:30 PM PST".
diff --git a/base/i18n/time_formatting_unittest.cc b/base/i18n/time_formatting_unittest.cc
index 824b9b9..701370a 100644
--- a/base/i18n/time_formatting_unittest.cc
+++ b/base/i18n/time_formatting_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/icu_test_util.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/icu/source/common/unicode/uversion.h"
 #include "third_party/icu/source/i18n/unicode/calendar.h"
@@ -203,6 +204,36 @@
                                                  kDropAmPm));
 }
 
+#if defined(OS_CHROMEOS)
+TEST(TimeFormattingTest, TimeMonthYearInUTC) {
+  // See third_party/icu/source/data/locales/en.txt.
+  // The date patterns are "EEEE, MMMM d, y", "MMM d, y", and "M/d/yy".
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+  i18n::SetICUDefaultLocale("en_US");
+  test::ScopedRestoreDefaultTimezone la_time("America/Los_Angeles");
+
+  Time time;
+  EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &time));
+  EXPECT_EQ(
+      ASCIIToUTF16("April 2011"),
+      TimeFormatMonthAndYear(time, /*time_zone=*/icu::TimeZone::getGMT()));
+  EXPECT_EQ(ASCIIToUTF16("April 2011"),
+            TimeFormatMonthAndYear(time, /*time_zone=*/nullptr));
+
+  const Time::Exploded kDiffMonthsForDiffTzTime = {
+      2011, 4, 5, 1,  // Fri, Apr 1, 2011 UTC = Thurs, March 31, 2011 US PDT.
+      0,    0, 0, 0   // 00:00:00.000 UTC = 05:00:00 previous day US PDT.
+  };
+
+  EXPECT_TRUE(Time::FromUTCExploded(kDiffMonthsForDiffTzTime, &time));
+  EXPECT_EQ(
+      ASCIIToUTF16("April 2011"),
+      TimeFormatMonthAndYear(time, /*time_zone=*/icu::TimeZone::getGMT()));
+  EXPECT_EQ(ASCIIToUTF16("March 2011"),
+            TimeFormatMonthAndYear(time, /*time_zone=*/nullptr));
+}
+#endif  // defined(OS_CHROMEOS)
+
 TEST(TimeFormattingTest, TimeFormatDateUS) {
   // See third_party/icu/source/data/locales/en.txt.
   // The date patterns are "EEEE, MMMM d, y", "MMM d, y", and "M/d/yy".
diff --git a/base/power_monitor/power_monitor_device_source.h b/base/power_monitor/power_monitor_device_source.h
index c09a6a4..937f418 100644
--- a/base/power_monitor/power_monitor_device_source.h
+++ b/base/power_monitor/power_monitor_device_source.h
@@ -130,7 +130,7 @@
   PowerMessageWindow power_message_window_;
 #endif
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   PowerObserver::DeviceThermalState current_thermal_state_ =
       PowerObserver::DeviceThermalState::kUnknown;
 #endif
diff --git a/build/config/chromeos/rules.gni b/build/config/chromeos/rules.gni
index 9e61743..d8d5025 100644
--- a/build/config/chromeos/rules.gni
+++ b/build/config/chromeos/rules.gni
@@ -8,56 +8,90 @@
 import("//build/config/gclient_args.gni")
 import("//build/config/python.gni")
 
-assert(is_chromeos_ash)
+assert(is_chromeos_ash || is_chromeos_lacros)
 
 # Determine the real paths for various items in the SDK, which may be used
 # in the 'generate_runner_script' template below. We do so outside the template
 # to confine exec_script to a single invocation.
-cros_is_vm = false
 if (is_chromeos_device && cros_sdk_version != "") {
-  _cache_path_prefix =
-      "//build/cros_cache/chrome-sdk/symlinks/${cros_board}+${cros_sdk_version}"
+  # Ideally these should be maps, however, gn doesn't support map, so using a
+  # list of list to simulate a map:
+  # [key1, [value1, value2, ...]], [key2, [value1, value2, ...]], where
+  # the keys are boards and values are symlinks or symlink targets, and the
+  # mapping shouldn't be used for anything else.
+  #
+  # A sample usage is:
+  # foreach(m, _symlink_targets_map) {
+  #   if(m[0] == target_key) {
+  #     target_value = m[1]
+  #   }
+  # }
+  #
+  _symlink_map = []
+  _symlink_targets_map = []
+  _boards = []
 
-  foreach(b, string_split(cros_boards_with_qemu_images, ":")) {
-    if (cros_board == b) {
-      cros_is_vm = true
+  if (cros_boards != "") {
+    _boards += string_split(cros_boards, ":")
+  }
+  if (cros_boards_with_qemu_images != "") {
+    _boards += string_split(cros_boards_with_qemu_images, ":")
+  }
+
+  foreach(b, _boards) {
+    _cache_path_prefix =
+        "//build/cros_cache/chrome-sdk/symlinks/${b}+${cros_sdk_version}"
+
+    cros_is_vm = false
+    foreach(b1, string_split(cros_boards_with_qemu_images, ":")) {
+      if (b == b1) {
+        cros_is_vm = true
+      }
     }
+
+    _symlinks = []
+    _symlinks = [
+      # Tast harness & test data.
+      rebase_path("${_cache_path_prefix}+chromeos-base/tast-cmd"),
+      rebase_path("${_cache_path_prefix}+chromeos-base/tast-remote-tests-cros"),
+
+      # Binutils (and other toolchain tools) used to deploy Chrome to the device.
+      rebase_path(
+          "${_cache_path_prefix}+environment_chromeos-base_chromeos-chrome.tar.xz"),
+      rebase_path("${_cache_path_prefix}+target_toolchain"),
+    ]
+    if (cros_is_vm) {
+      # VM-related tools.
+      _symlinks += [
+        rebase_path("${_cache_path_prefix}+sys-firmware/seabios"),
+        rebase_path("${_cache_path_prefix}+chromiumos_qemu_image.tar.xz"),
+        rebase_path("${_cache_path_prefix}+app-emulation/qemu"),
+      ]
+    }
+    _symlink_map += [ [
+          b,
+          _symlinks,
+        ] ]
   }
 
-  _symlinks = [
-    # Tast harness & test data.
-    rebase_path("${_cache_path_prefix}+chromeos-base/tast-cmd"),
-    rebase_path("${_cache_path_prefix}+chromeos-base/tast-remote-tests-cros"),
-
-    # Binutils (and other toolchain tools) used to deploy Chrome to the device.
-    rebase_path(
-        "${_cache_path_prefix}+environment_chromeos-base_chromeos-chrome.tar.xz"),
-    rebase_path("${_cache_path_prefix}+target_toolchain"),
-  ]
-  if (cros_is_vm) {
-    # VM-related tools.
-    _symlinks += [
-      rebase_path("${_cache_path_prefix}+sys-firmware/seabios"),
-      rebase_path("${_cache_path_prefix}+chromiumos_qemu_image.tar.xz"),
-      rebase_path("${_cache_path_prefix}+app-emulation/qemu"),
-    ]
+  _all_symlinks = []
+  foreach(m, _symlink_map) {
+    _all_symlinks += m[1]
   }
-  _symlink_targets =
-      exec_script("//build/get_symlink_targets.py", _symlinks, "list lines")
-  tast_sdk_items = [
-    _symlink_targets[0],
-    _symlink_targets[1],
-  ]
-  toolchain_sdk_items = [
-    _symlink_targets[2],
-    _symlink_targets[3],
-  ]
-  if (cros_is_vm) {
-    vm_sdk_items = [
-      _symlink_targets[4],
-      _symlink_targets[5],
-      _symlink_targets[6],
-    ]
+  _all_symlink_targets =
+      exec_script("//build/get_symlink_targets.py", _all_symlinks, "list lines")
+  _index = 0
+  foreach(m, _symlink_map) {
+    _symlink_targets = []
+    foreach(_, m[1]) {
+      _symlink_targets += [ _all_symlink_targets[_index] ]
+      _index += 1
+    }
+
+    _symlink_targets_map += [ [
+          m[0],
+          _symlink_targets,
+        ] ]
   }
 }
 
@@ -76,28 +110,86 @@
 #       build dir to the VM after launching it.
 #   runtime_deps_file: Path to file listing runtime deps for the test. If set,
 #       all files listed will be copied to the VM before testing.
+#   override_board: "cros_board" variable was used to decide both the toolchain
+#       to build Chrome and the DUTs to test the binary, however, with the
+#       introduction of Lacros, this is not true anymore: a Lacros chrome can be
+#       built by an amd64-generic toolchain, but tested on eve devices, and to
+#       accommondate the use case, this variable is introduced to allow
+#       overriding the board to test the built binary.
+#   tast_vars: A list of "key=value" runtime variable pairs to pass to invoke
+#       the Tast tests. For more details, please see:
+#       https://chromium.googlesource.com/chromiumos/platform/tast/+/HEAD/docs/writing_tests.md#Runtime-variables
 template("generate_runner_script") {
   forward_variables_from(invoker,
                          [
+                           "override_board",
                            "deploy_chrome",
                            "generated_script",
                            "runtime_deps_file",
                            "tast_attr_expr",
                            "tast_tests",
+                           "tast_vars",
                            "testonly",
                            "test_exe",
                          ])
-  if (!defined(deploy_chrome)) {
-    deploy_chrome = false
+
+  if (defined(override_board)) {
+    cros_board = override_board
   }
 
-  assert(defined(generated_script),
-         "Must specify where to place generated test launcher script via " +
-             "'generated_script'")
-  is_tast = defined(tast_attr_expr) || defined(tast_tests)
-  assert(!(is_tast && defined(test_exe)),
-         "Tast tests are invoked from binaries shipped with the VM image. " +
-             "There should be no locally built binary needed.")
+  if (is_chromeos_device && cros_sdk_version != "") {
+    assert(defined(generated_script),
+           "Must specify where to place generated test launcher script via " +
+               "'generated_script'")
+    is_tast = defined(tast_attr_expr) || defined(tast_tests)
+    assert(!(is_tast && defined(test_exe)),
+           "Tast tests are invoked from binaries shipped with the VM image. " +
+               "There should be no locally built binary needed.")
+    assert(is_tast || !defined(tast_vars),
+           "tast_vars is only support for Tast tests")
+
+    # Determine the real paths for various items in the SDK, which may be used
+    # in the 'generate_runner_script' template below.
+    cros_is_vm = false
+    foreach(b, string_split(cros_boards_with_qemu_images, ":")) {
+      if (cros_board == b) {
+        cros_is_vm = true
+      }
+    }
+
+    if (!defined(deploy_chrome)) {
+      deploy_chrome = false
+    }
+
+    if (is_tast || cros_is_vm || deploy_chrome) {
+      foreach(m, _symlink_targets_map) {
+        if (cros_board == m[0]) {
+          _symlink_targets = []
+          _symlink_targets = m[1]
+        }
+      }
+
+      if (is_tast) {
+        _tast_sdk_items = [
+          _symlink_targets[0],
+          _symlink_targets[1],
+        ]
+      }
+      if (deploy_chrome) {
+        _toolchain_sdk_items = [
+          _symlink_targets[2],
+          _symlink_targets[3],
+        ]
+      }
+      if (cros_is_vm) {
+        _vm_sdk_items = [
+          _symlink_targets[4],
+          _symlink_targets[5],
+          _symlink_targets[6],
+        ]
+      }
+    }
+  }
 
   # TODO(crbug.com/1112471): Get this to run cleanly under Python 3.
   python2_action(target_name) {
@@ -171,21 +263,25 @@
       args += [ "--deploy-chrome" ]
     }
 
+    if (is_chromeos_lacros) {
+      args += [ "--deploy-lacros" ]
+    }
+
     # If we're in the cros chrome-sdk (and not the raw ebuild), the test will
     # need some additional runtime data located in the SDK cache.
     if (cros_sdk_version != "") {
       # Add the VM/QEMU-launching bits if needed.
       if (cros_is_vm) {
-        data += vm_sdk_items
+        data += _vm_sdk_items
       }
       if (is_tast) {
-        data += tast_sdk_items
+        data += _tast_sdk_items
       }
       if (deploy_chrome) {
         # To deploy chrome to the VM, it needs to be stripped down to fit into
         # the VM. This is done by using binutils in the toolchain. So add the
         # toolchain to the data.
-        data += toolchain_sdk_items
+        data += _toolchain_sdk_items
       }
     }
 
@@ -223,6 +319,14 @@
           ]
         }
       }
+      if (defined(tast_vars)) {
+        foreach(var, tast_vars) {
+          args += [
+            "--tast-var",
+            var,
+          ]
+        }
+      }
     }
   }
 }
@@ -278,6 +382,39 @@
       "//third_party/breakpad:minidump_dump",
       "//third_party/breakpad:minidump_stackwalk",
     ]
+
+    data = [ "//components/crash/content/tools/generate_breakpad_symbols.py" ]
+  }
+}
+
+template("lacros_tast_test") {
+  forward_variables_from(invoker,
+                         [
+                           "override_board",
+                           "tast_tests",
+                         ])
+  generate_runner_script(target_name) {
+    testonly = true
+    generated_script = "$root_build_dir/bin/run_${target_name}"
+    runtime_deps_file = "$root_out_dir/${target_name}.runtime_deps"
+
+    # By default, tast tests download a lacros-chrome from a gcs location and
+    # use it for testing. To support running lacros tast tests from Chromium CI,
+    # a Var is added to support pointing the tast tests to use a specified
+    # pre-deployed lacros-chrome. The location is decided by:
+    # https://source.chromium.org/chromium/chromium/src/+/master:third_party/chromite/scripts/deploy_chrome.py;l=80;drc=86f1234a4be8e9574442e076cdc835897f7bea61
+    tast_vars = [ "lacrosDeployedBinary=/usr/local/lacros-chrome" ]
+    data_deps = [
+      "//chrome",  # Builds the browser.
+
+      # Tools used to symbolize Chrome crash dumps.
+      # TODO(crbug.com/1156772): Remove these if/when all tests pick them up by
+      # default.
+      "//third_party/breakpad:dump_syms",
+      "//third_party/breakpad:minidump_dump",
+      "//third_party/breakpad:minidump_stackwalk",
+    ]
+
     data = [ "//components/crash/content/tools/generate_breakpad_symbols.py" ]
   }
 }
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 61486b9b..55b0b0b 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20210114.3.1
+0.20210115.0.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 61486b9b..55b0b0b 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20210114.3.1
+0.20210115.0.1
diff --git a/cc/OWNERS b/cc/OWNERS
index 3eec17e2..3b3a9427 100644
--- a/cc/OWNERS
+++ b/cc/OWNERS
@@ -36,7 +36,6 @@
 smcgruer@chromium.org
 
 # images
-khushalsagar@chromium.org
 vmpstr@chromium.org
 
 # surfaces
diff --git a/cc/mojo_embedder/async_layer_tree_frame_sink.cc b/cc/mojo_embedder/async_layer_tree_frame_sink.cc
index 452bb737..0a09747 100644
--- a/cc/mojo_embedder/async_layer_tree_frame_sink.cc
+++ b/cc/mojo_embedder/async_layer_tree_frame_sink.cc
@@ -275,9 +275,10 @@
   DCHECK(compositor_frame_sink_ptr_);
   if (needs_begin_frames_ != needs_begin_frames) {
     if (needs_begin_frames_) {
-      TRACE_EVENT_ASYNC_END0("cc,benchmark", "NeedsBeginFrames", this);
+      TRACE_EVENT_NESTABLE_ASYNC_END0("cc,benchmark", "NeedsBeginFrames", this);
     } else {
-      TRACE_EVENT_ASYNC_BEGIN0("cc,benchmark", "NeedsBeginFrames", this);
+      TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("cc,benchmark", "NeedsBeginFrames",
+                                        this);
     }
   }
   needs_begin_frames_ = needs_begin_frames;
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 47056c31..d70a3bf 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -3441,6 +3441,7 @@
     "java/src/org/chromium/chrome/browser/sharing/SharingServiceProxy.java",
     "java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandler.java",
     "java/src/org/chromium/chrome/browser/sharing/shared_clipboard/SharedClipboardMessageHandler.java",
+    "java/src/org/chromium/chrome/browser/signin/SigninBridge.java",
     "java/src/org/chromium/chrome/browser/signin/SigninManagerImpl.java",
     "java/src/org/chromium/chrome/browser/signin/SigninPromoUtil.java",
     "java/src/org/chromium/chrome/browser/signin/SigninUtils.java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index c537050..4029085 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -84,11 +84,8 @@
   "java/src/org/chromium/chrome/browser/app/tabmodel/AsyncTabParamsManagerSingleton.java",
   "java/src/org/chromium/chrome/browser/app/tabmodel/ChromeNextTabPolicySupplier.java",
   "java/src/org/chromium/chrome/browser/app/tabmodel/ChromeTabModelFilterFactory.java",
-  "java/src/org/chromium/chrome/browser/app/tabmodel/CustomTabsTabModelOrchestrator.java",
   "java/src/org/chromium/chrome/browser/app/tabmodel/DefaultTabModelSelectorFactory.java",
-  "java/src/org/chromium/chrome/browser/app/tabmodel/TabModelOrchestrator.java",
   "java/src/org/chromium/chrome/browser/app/tabmodel/TabWindowManagerSingleton.java",
-  "java/src/org/chromium/chrome/browser/app/tabmodel/TabbedModeTabModelOrchestrator.java",
   "java/src/org/chromium/chrome/browser/app/video_tutorials/ChromeLanguageInfoProvider.java",
   "java/src/org/chromium/chrome/browser/app/video_tutorials/NewTabPageVideoIPHManager.java",
   "java/src/org/chromium/chrome/browser/app/video_tutorials/VideoPlayerActivity.java",
@@ -1243,6 +1240,7 @@
   "java/src/org/chromium/chrome/browser/sharing/shared_clipboard/SharedClipboardShareActivity.java",
   "java/src/org/chromium/chrome/browser/signin/SigninActivity.java",
   "java/src/org/chromium/chrome/browser/signin/SigninActivityLauncherImpl.java",
+  "java/src/org/chromium/chrome/browser/signin/SigninBridge.java",
   "java/src/org/chromium/chrome/browser/signin/SigninFragment.java",
   "java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java",
   "java/src/org/chromium/chrome/browser/signin/SigninHelperProvider.java",
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/TestingAutofillAssistantModuleEntryProvider.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/TestingAutofillAssistantModuleEntryProvider.java
index 90a43500..a661385 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/TestingAutofillAssistantModuleEntryProvider.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/TestingAutofillAssistantModuleEntryProvider.java
@@ -110,12 +110,13 @@
     }
 
     @Override
-    public void getModuleEntry(Tab tab, Callback<AutofillAssistantModuleEntry> callback) {
+    public void getModuleEntry(
+            Tab tab, Callback<AutofillAssistantModuleEntry> callback, boolean showUi) {
         if (mCannotInstall) {
             callback.onResult(null);
             return;
         }
         mNotInstalled = false;
-        super.getModuleEntry(tab, callback);
+        super.getModuleEntry(tab, callback, showUi);
     }
 }
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectActionHandler.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectActionHandler.java
index 934a8da7..91c3e79 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectActionHandler.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectActionHandler.java
@@ -216,7 +216,7 @@
         mModuleEntryProvider.getModuleEntry(tab, (entry) -> {
             mDelegate = createDelegate(entry);
             callback.onResult(mDelegate);
-        });
+        }, /* showUi = */ true);
     }
 
     /** Creates a delegate from the given {@link AutofillAssistantModuleEntry}, if possible. */
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
index 754ec9f1..db5d89d9 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
@@ -133,36 +133,45 @@
                 }
 
                 if (AutofillAssistantModuleEntryProvider.INSTANCE.getModuleEntryIfInstalled()
-                        == null) {
-                    // Opt-out users who don't have DFM installed.
-                    AutofillAssistantMetrics.recordLiteScriptStarted(
-                            tab.getWebContents(), LiteScriptStarted.LITE_SCRIPT_DFM_UNAVAILABLE);
+                                == null
+                        && arguments.containsTriggerScript()
+                        && !ChromeFeatureList.isEnabled(
+                                ChromeFeatureList
+                                        .AUTOFILL_ASSISTANT_LOAD_DFM_FOR_TRIGGER_SCRIPTS)) {
                     return;
                 }
             }
 
-            AutofillAssistantModuleEntryProvider.INSTANCE.getModuleEntry(
-                    tab, (moduleEntry) -> {
-                        if (moduleEntry == null || activity.isActivityFinishingOrDestroyed()) {
-                            AutofillAssistantMetrics.recordDropOut(
-                                    DropOutReason.DFM_INSTALL_FAILED);
-                            return;
+            if (AutofillAssistantModuleEntryProvider.INSTANCE.getModuleEntryIfInstalled() == null) {
+                AutofillAssistantModuleEntryProvider.INSTANCE.getModuleEntry(tab, (moduleEntry) -> {
+                    if (moduleEntry == null || activity.isActivityFinishingOrDestroyed()) {
+                        AutofillAssistantMetrics.recordDropOut(DropOutReason.DFM_INSTALL_FAILED);
+                        if (arguments.containsTriggerScript()) {
+                            AutofillAssistantMetrics.recordLiteScriptFinished(tab.getWebContents(),
+                                    LiteScriptStarted.LITE_SCRIPT_DFM_UNAVAILABLE);
                         }
-
-                        moduleEntry.start(
-                                BottomSheetControllerProvider.from(activity.getWindowAndroid()),
-                                activity.getBrowserControlsManager(),
-                                activity.getCompositorViewHolder(), activity, tab.getWebContents(),
-                                activity.getWindowAndroid().getKeyboardDelegate(),
-                                activity.getWindowAndroid().getApplicationBottomInsetProvider(),
-                                activity.getActivityTabProvider(),
-                                activity instanceof CustomTabActivity, arguments.getInitialUrl(),
-                                arguments.getParameters(), arguments.getExperimentIds(),
-                                arguments.getCallerAccount(), arguments.getUserName());
-                    });
+                        return;
+                    }
+                    start(activity, arguments, moduleEntry);
+                }, /* showUi = */ !arguments.containsTriggerScript());
+            } else {
+                start(activity, arguments,
+                        AutofillAssistantModuleEntryProvider.INSTANCE.getModuleEntryIfInstalled());
+            }
         });
     }
 
+    private static void start(ChromeActivity activity, AutofillAssistantArguments arguments,
+            AutofillAssistantModuleEntry module) {
+        module.start(BottomSheetControllerProvider.from(activity.getWindowAndroid()),
+                activity.getBrowserControlsManager(), activity.getCompositorViewHolder(), activity,
+                activity.getCurrentWebContents(), activity.getWindowAndroid().getKeyboardDelegate(),
+                activity.getWindowAndroid().getApplicationBottomInsetProvider(),
+                activity.getActivityTabProvider(), activity instanceof CustomTabActivity,
+                arguments.getInitialUrl(), arguments.getParameters(), arguments.getExperimentIds(),
+                arguments.getCallerAccount(), arguments.getUserName());
+    }
+
     /**
      * Checks whether direct actions provided by Autofill Assistant should be available - assuming
      * that direct actions are available at all.
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryProvider.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryProvider.java
index d536aa8..29d84e0 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryProvider.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryProvider.java
@@ -42,7 +42,7 @@
 
     /** Gets the AA module entry, installing it if necessary. */
     /* package */
-    void getModuleEntry(Tab tab, Callback<AutofillAssistantModuleEntry> callback) {
+    void getModuleEntry(Tab tab, Callback<AutofillAssistantModuleEntry> callback, boolean showUi) {
         AutofillAssistantModuleEntry entry = getModuleEntryIfInstalled();
         if (entry != null) {
             AutofillAssistantMetrics.recordFeatureModuleInstallation(
@@ -50,7 +50,7 @@
             callback.onResult(entry);
             return;
         }
-        loadDynamicModuleWithUi(tab, callback);
+        loadDynamicModule(tab, callback, showUi);
     }
 
     /**
@@ -83,14 +83,14 @@
         AutofillAssistantModule.installDeferred();
     }
 
-    private static void loadDynamicModuleWithUi(
-            Tab tab, Callback<AutofillAssistantModuleEntry> callback) {
+    private static void loadDynamicModule(
+            Tab tab, Callback<AutofillAssistantModuleEntry> callback, boolean showUi) {
         ModuleInstallUi ui = new ModuleInstallUi(tab, R.string.autofill_assistant_module_title,
                 new ModuleInstallUi.FailureUiListener() {
                     @Override
                     public void onFailureUiResponse(boolean retry) {
                         if (retry) {
-                            loadDynamicModuleWithUi(tab, callback);
+                            loadDynamicModule(tab, callback, showUi);
                         } else {
                             AutofillAssistantMetrics.recordFeatureModuleInstallation(
                                     FeatureModuleInstallation.DFM_FOREGROUND_INSTALLATION_FAILED);
@@ -98,8 +98,11 @@
                         }
                     }
                 });
-        // Shows toast informing user about install start.
-        ui.showInstallStartUi();
+        if (showUi) {
+            // Shows toast informing user about install start.
+            ui.showInstallStartUi();
+        }
+
         AutofillAssistantModule.install((success) -> {
             if (success) {
                 // Don't show success UI from DFM, transition to Autofill Assistant UI directly.
@@ -107,9 +110,12 @@
                         FeatureModuleInstallation.DFM_FOREGROUND_INSTALLATION_SUCCEEDED);
                 callback.onResult(AutofillAssistantModule.getImpl());
                 return;
+            } else if (showUi) {
+                // Show inforbar to ask user if they want to retry or cancel.
+                ui.showInstallFailureUi();
+            } else {
+                callback.onResult(null);
             }
-            // Show inforbar to ask user if they want to retry or cancel.
-            ui.showInstallFailureUi();
         });
     }
 }
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
index d346ec3e..033b89e 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
@@ -57,7 +57,6 @@
 import android.os.Build;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.uiautomator.UiDevice;
-import android.text.TextUtils;
 import android.view.View;
 import android.widget.ImageView;
 
@@ -135,6 +134,7 @@
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
+import org.chromium.components.embedder_support.util.UrlUtilities;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.WebContentsUtils;
@@ -587,10 +587,9 @@
             }, "Overview not hidden yet");
             int delta;
             if (switchToAnotherTab
-                    && !TextUtils.equals(mActivityTestRule.getActivity()
-                                                 .getCurrentWebContents()
-                                                 .getLastCommittedUrl(),
-                            NTP_URL)) {
+                    && !UrlUtilities.isNTPUrl(mActivityTestRule.getActivity()
+                                                      .getCurrentWebContents()
+                                                      .getLastCommittedUrl())) {
                 // Capture the original tab.
                 delta = 1;
             } else {
@@ -2130,9 +2129,9 @@
 
         // Make sure the fading animation is done.
         int delta;
-        if (TextUtils.equals(
-                    mActivityTestRule.getActivity().getCurrentWebContents().getLastCommittedUrl(),
-                    NTP_URL)) {
+        if (UrlUtilities.isNTPUrl(mActivityTestRule.getActivity()
+                                          .getCurrentWebContents()
+                                          .getLastCommittedUrl())) {
             // NTP is not invalidated, so no new captures.
             delta = 0;
         } else {
@@ -2187,9 +2186,9 @@
 
     private void checkFinalCaptureCount(boolean switchToAnotherTab, int initCount) {
         int expected;
-        if (TextUtils.equals(
-                    mActivityTestRule.getActivity().getCurrentWebContents().getLastCommittedUrl(),
-                    NTP_URL)) {
+        if (UrlUtilities.isNTPUrl(mActivityTestRule.getActivity()
+                                          .getCurrentWebContents()
+                                          .getLastCommittedUrl())) {
             expected = 0;
         } else {
             expected = mRepeat;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index e668a43..7372a35 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -60,9 +60,7 @@
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.app.tabmodel.AsyncTabParamsManagerSingleton;
 import org.chromium.chrome.browser.app.tabmodel.ChromeNextTabPolicySupplier;
-import org.chromium.chrome.browser.app.tabmodel.TabModelOrchestrator;
 import org.chromium.chrome.browser.app.tabmodel.TabWindowManagerSingleton;
-import org.chromium.chrome.browser.app.tabmodel.TabbedModeTabModelOrchestrator;
 import org.chromium.chrome.browser.bookmarks.BookmarkUtils;
 import org.chromium.chrome.browser.compositor.CompositorViewHolder;
 import org.chromium.chrome.browser.compositor.bottombar.ephemeraltab.EphemeralTabCoordinator;
@@ -249,7 +247,6 @@
 
     private ToolbarControlContainer mControlContainer;
 
-    private TabbedModeTabModelOrchestrator mTabModelOrchestrator;
     private TabModelSelectorImpl mTabModelSelectorImpl;
     private TabModelSelectorTabObserver mTabModelSelectorTabObserver;
     private TabModelSelectorTabModelObserver mTabModelObserver;
@@ -1626,13 +1623,7 @@
     }
 
     @Override
-    protected TabModelOrchestrator createTabModelOrchestrator() {
-        mTabModelOrchestrator = new TabbedModeTabModelOrchestrator();
-        return mTabModelOrchestrator;
-    }
-
-    @Override
-    protected void createTabModels() {
+    protected TabModelSelector createTabModelSelector() {
         assert mTabModelSelectorImpl == null;
 
         Bundle savedInstanceState = getSavedInstanceState();
@@ -1643,15 +1634,17 @@
         int index = savedInstanceState != null ? savedInstanceState.getInt(WINDOW_INDEX, 0) : 0;
 
         mNextTabPolicySupplier = new ChromeNextTabPolicySupplier(mOverviewModeBehaviorSupplier);
-
-        boolean tabModelWasCreated =
-                mTabModelOrchestrator.createTabModels(this, this, mNextTabPolicySupplier, index);
-        if (!tabModelWasCreated) {
+        mTabModelSelectorImpl =
+                (TabModelSelectorImpl) TabWindowManagerSingleton.getInstance().requestSelector(
+                        this, this, mNextTabPolicySupplier, index);
+        if (mTabModelSelectorImpl == null) {
+            Toast.makeText(
+                         this, getString(R.string.unsupported_number_of_windows), Toast.LENGTH_LONG)
+                    .show();
             finish();
-            return;
+            return null;
         }
 
-        mTabModelSelectorImpl = mTabModelOrchestrator.getTabModelSelector();
         mTabModelSelectorImpl.addObserver(new EmptyTabModelSelectorObserver() {
             @Override
             public void onTabStateInitialized() {
@@ -1675,6 +1668,8 @@
         mAppIndexingUtil = new AppIndexingUtil(mTabModelSelectorImpl);
 
         if (startIncognito) mTabModelSelectorImpl.selectModel(true);
+
+        return mTabModelSelectorImpl;
     }
 
     @Override
@@ -2224,13 +2219,6 @@
     }
 
     @Override
-    protected void destroyTabModels() {
-        if (mTabModelOrchestrator != null) {
-            mTabModelOrchestrator.destroy();
-        }
-    }
-
-    @Override
     public void onTrimMemory(int level) {
         super.onTrimMemory(level);
         if (ChromeApplication.isSevereMemorySignal(level)) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index afa7f5d..706fc1f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -71,7 +71,6 @@
 import org.chromium.chrome.browser.app.tab_activity_glue.ReparentingDelegateFactory;
 import org.chromium.chrome.browser.app.tab_activity_glue.TabReparentingController;
 import org.chromium.chrome.browser.app.tabmodel.AsyncTabParamsManagerSingleton;
-import org.chromium.chrome.browser.app.tabmodel.TabModelOrchestrator;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
 import org.chromium.chrome.browser.bookmarks.BookmarkModel;
@@ -242,7 +241,7 @@
             new TabModelSelectorProfileSupplier(mTabModelSelectorSupplier);
     protected ObservableSupplierImpl<BookmarkBridge> mBookmarkBridgeSupplier =
             new ObservableSupplierImpl<>();
-    private TabModelOrchestrator mTabModelOrchestrator;
+    private TabModelSelector mTabModelSelector;
     private TabModelSelectorTabObserver mTabModelSelectorTabObserver;
     private TabCreator mRegularTabCreator;
     private TabCreator mIncognitoTabCreator;
@@ -264,6 +263,7 @@
     /** Whether or not {@link #postDeferredStartupIfNeeded()} has already successfully run. */
     private boolean mDeferredStartupPosted;
 
+    private boolean mTabModelsInitialized;
     private boolean mNativeInitialized;
     private boolean mRemoveWindowBackgroundDone;
     protected AccessibilityVisibilityHandler mAccessibilityVisibilityHandler;
@@ -358,9 +358,6 @@
         // onPreInflationStartup event.
         mComponent = createComponent();
 
-        // Create the orchestrator that manages Tab models and persistence
-        mTabModelOrchestrator = createTabModelOrchestrator();
-
         // There's no corresponding call to removeObserver() for this addObserver() because
         // mTabModelProfileSupplier has the same lifecycle as this activity.
         mTabModelProfileSupplier.addObserver((profile) -> {
@@ -491,10 +488,9 @@
                     mCompositorViewHolder.getCompositorView());
 
             initializeTabModels();
-            TabModelSelector tabModelSelector = mTabModelOrchestrator.getTabModelSelector();
             setTabContentManager(new TabContentManager(this, getContentOffsetProvider(),
                     !SysUtils.isLowEndDevice(),
-                    tabModelSelector != null ? tabModelSelector::getTabById : null));
+                    mTabModelSelector != null ? mTabModelSelector::getTabById : null));
 
             if (!isFinishing()) {
                 getBrowserControlsManager().initialize(
@@ -656,28 +652,28 @@
      * this activity.
      */
     public final void initializeTabModels() {
-        if (mTabModelOrchestrator.areTabModelsInitialized()) return;
+        if (mTabModelsInitialized) return;
 
-        createTabModels();
-        TabModelSelector tabModelSelector = mTabModelOrchestrator.getTabModelSelector();
+        mTabModelSelector = createTabModelSelector();
 
-        if (tabModelSelector == null) {
+        if (mTabModelSelector == null) {
             assert isFinishing();
+            mTabModelsInitialized = true;
             return;
         }
 
-        mTabModelSelectorSupplier.set(tabModelSelector);
-        mActivityTabProvider.setTabModelSelector(tabModelSelector);
-        getStatusBarColorController().setTabModelSelector(tabModelSelector);
+        mTabModelSelectorSupplier.set(mTabModelSelector);
+        mActivityTabProvider.setTabModelSelector(mTabModelSelector);
+        getStatusBarColorController().setTabModelSelector(mTabModelSelector);
 
         Pair<? extends TabCreator, ? extends TabCreator> tabCreators = createTabCreators();
         mRegularTabCreator = tabCreators.first;
         mIncognitoTabCreator = tabCreators.second;
 
-        OfflinePageUtils.observeTabModelSelector(this, tabModelSelector);
+        OfflinePageUtils.observeTabModelSelector(this, mTabModelSelector);
         if (mTabModelSelectorTabObserver != null) mTabModelSelectorTabObserver.destroy();
 
-        mTabModelSelectorTabObserver = new TabModelSelectorTabObserver(tabModelSelector) {
+        mTabModelSelectorTabObserver = new TabModelSelectorTabObserver(mTabModelSelector) {
             @Override
             public void onLoadStopped(Tab tab, boolean toDifferentDocument) {
                 postDeferredStartupIfNeeded();
@@ -694,6 +690,8 @@
                 postDeferredStartupIfNeeded();
             }
         };
+
+        mTabModelsInitialized = true;
     }
 
     /**
@@ -709,19 +707,9 @@
     }
 
     /**
-     * @return The {@link TabModelOrchestrator} owned by this {@link ChromeActivity}.
+     * @return The {@link TabModelSelector} owned by this {@link ChromeActivity}.
      */
-    protected abstract TabModelOrchestrator createTabModelOrchestrator();
-
-    /**
-     * Call the {@link TabModelOrchestrator} to initialize its members.
-     */
-    protected abstract void createTabModels();
-
-    /**
-     * Call the {@link TabModelOrchestrator} to destroy its members.
-     */
-    protected abstract void destroyTabModels();
+    protected abstract TabModelSelector createTabModelSelector();
 
     /**
      * @return The {@link TabCreator}s owned
@@ -860,8 +848,7 @@
         VrModuleProvider.getDelegate().onActivityHidden(this);
 
         Tab tab = getActivityTab();
-        TabModelSelector tabModelSelector = mTabModelOrchestrator.getTabModelSelector();
-        if (tabModelSelector != null && !tabModelSelector.isReparentingInProgress()
+        if (mTabModelSelector != null && !mTabModelSelector.isReparentingInProgress()
                 && tab != null) {
             tab.hide(TabHidingType.ACTIVITY_HIDDEN);
         }
@@ -1325,7 +1312,10 @@
             mActivityTabStartupMetricsTracker = null;
         }
 
-        destroyTabModels();
+        if (mTabModelsInitialized) {
+            TabModelSelector selector = getTabModelSelector();
+            if (selector != null) selector.destroy();
+        }
 
         if (mBookmarkBridgeSupplier != null) {
             BookmarkBridge bookmarkBridge = mBookmarkBridgeSupplier.get();
@@ -1601,7 +1591,7 @@
      * @return Whether the tab models have been fully initialized.
      */
     public boolean areTabModelsInitialized() {
-        return mTabModelOrchestrator.areTabModelsInitialized();
+        return mTabModelsInitialized;
     }
 
     /**
@@ -1610,11 +1600,11 @@
      * @return The {@link TabModelSelector}, possibly null.
      */
     public TabModelSelector getTabModelSelector() {
-        if (!mTabModelOrchestrator.areTabModelsInitialized()) {
+        if (!mTabModelsInitialized) {
             throw new IllegalStateException(
                     "Attempting to access TabModelSelector before initialization");
         }
-        return mTabModelOrchestrator.getTabModelSelector();
+        return mTabModelSelector;
     }
 
     /**
@@ -1635,7 +1625,7 @@
 
     @Override
     public TabCreator getTabCreator(boolean incognito) {
-        if (!mTabModelOrchestrator.areTabModelsInitialized()) {
+        if (!mTabModelsInitialized) {
             throw new IllegalStateException(
                     "Attempting to access TabCreator before initialization");
         }
@@ -1702,7 +1692,7 @@
      * null if the Tab does not exist or the system is not initialized.
      */
     public Tab getActivityTab() {
-        if (!mTabModelOrchestrator.areTabModelsInitialized()) {
+        if (!mTabModelsInitialized) {
             return null;
         }
         return TabModelUtils.getCurrentTab(getCurrentTabModel());
@@ -1713,7 +1703,7 @@
      *         WebContents.
      */
     public WebContents getCurrentWebContents() {
-        if (!mTabModelOrchestrator.areTabModelsInitialized()) {
+        if (!mTabModelsInitialized) {
             return null;
         }
         return TabModelUtils.getCurrentWebContents(getCurrentTabModel());
@@ -2037,7 +2027,7 @@
 
         if (id == R.id.help_id) {
             String url = currentTab != null ? currentTab.getUrlString() : "";
-            Profile profile = getTabModelSelector().isIncognitoSelected()
+            Profile profile = mTabModelSelector.isIncognitoSelected()
                     ? Profile.getLastUsedRegularProfile().getPrimaryOTRProfile()
                     : Profile.getLastUsedRegularProfile();
             startHelpAndFeedback(url, "MobileMenuFeedback", profile);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/CustomTabsTabModelOrchestrator.java b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/CustomTabsTabModelOrchestrator.java
deleted file mode 100644
index b319dbb..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/CustomTabsTabModelOrchestrator.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// 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.app.tabmodel;
-
-import androidx.annotation.Nullable;
-
-import org.chromium.base.supplier.Supplier;
-import org.chromium.chrome.browser.dependency_injection.ActivityScope;
-import org.chromium.chrome.browser.tabmodel.AsyncTabParamsManager;
-import org.chromium.chrome.browser.tabmodel.NextTabPolicy;
-import org.chromium.chrome.browser.tabmodel.NextTabPolicy.NextTabPolicySupplier;
-import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
-import org.chromium.chrome.browser.tabmodel.TabModelFilterFactory;
-import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
-import org.chromium.chrome.browser.tabmodel.TabPersistencePolicy;
-import org.chromium.chrome.browser.tabmodel.TabPersistentStore;
-import org.chromium.ui.base.WindowAndroid;
-
-import javax.inject.Inject;
-
-/**
- * Glue-level class that manages lifetime of root .tabmodel objects: {@link TabPersistentStore} and
- * {@link TabModelSelectorImpl} for custom tabs.
- */
-@ActivityScope
-public class CustomTabsTabModelOrchestrator extends TabModelOrchestrator {
-    @Inject
-    public CustomTabsTabModelOrchestrator() {}
-
-    /**
-     * Creates the TabModelSelector and the TabPersistentStore.
-     */
-    public void createTabModels(@Nullable Supplier<WindowAndroid> windowAndroidSupplier,
-            TabCreatorManager tabCreatorManager, TabModelFilterFactory tabModelFilterFactory,
-            TabPersistencePolicy persistencePolicy, AsyncTabParamsManager asyncTabParamsManager) {
-        // Instantiate TabModelSelectorImpl
-        NextTabPolicySupplier nextTabPolicySupplier = () -> NextTabPolicy.LOCATIONAL;
-        mTabModelSelector = new TabModelSelectorImpl(windowAndroidSupplier, tabCreatorManager,
-                tabModelFilterFactory, nextTabPolicySupplier, asyncTabParamsManager, false, false,
-                false);
-
-        // Instantiate TabPersistentStore
-        mTabPersistentStore =
-                new TabPersistentStore(persistencePolicy, mTabModelSelector, tabCreatorManager);
-
-        wireSelectorAndStore();
-        markTabModelsInitialized();
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/DefaultTabModelSelectorFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/DefaultTabModelSelectorFactory.java
index 41f14d8..9a4f7363 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/DefaultTabModelSelectorFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/DefaultTabModelSelectorFactory.java
@@ -5,15 +5,19 @@
 package org.chromium.chrome.browser.app.tabmodel;
 
 import android.app.Activity;
+import android.os.Build;
 
 import org.chromium.base.annotations.VerifiesOnN;
-import org.chromium.chrome.browser.tabmodel.AsyncTabParamsManager;
+import org.chromium.chrome.browser.multiwindow.MultiInstanceManager;
+import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
 import org.chromium.chrome.browser.tabmodel.NextTabPolicy.NextTabPolicySupplier;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
 import org.chromium.chrome.browser.tabmodel.TabModelFilterFactory;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorFactory;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
+import org.chromium.chrome.browser.tabmodel.TabPersistencePolicy;
+import org.chromium.chrome.browser.tabmodel.TabbedModeTabPersistencePolicy;
 
 /**
  * Default {@link TabModelSelectorFactory} for Chrome.
@@ -25,11 +29,32 @@
     @Override
     public TabModelSelector buildSelector(Activity activity, TabCreatorManager tabCreatorManager,
             NextTabPolicySupplier nextTabPolicySupplier, int selectorIndex) {
+        // Merge tabs if this TabModelSelector is for a ChromeTabbedActivity created in
+        // fullscreen mode and there are no TabModelSelector's currently alive. This indicates
+        // that it is a cold start or process restart in fullscreen mode.
+        boolean mergeTabs = Build.VERSION.SDK_INT > Build.VERSION_CODES.M
+                && MultiInstanceManager.isTabModelMergingEnabled()
+                && !activity.isInMultiWindowMode();
+        if (MultiInstanceManager.shouldMergeOnStartup(activity)) {
+            mergeTabs = mergeTabs
+                    && (!MultiWindowUtils.getInstance().isInMultiDisplayMode(activity)
+                            || TabWindowManagerSingleton.getInstance()
+                                            .getNumberOfAssignedTabModelSelectors()
+                                    == 0);
+        } else {
+            mergeTabs = mergeTabs
+                    && TabWindowManagerSingleton.getInstance()
+                                    .getNumberOfAssignedTabModelSelectors()
+                            == 0;
+        }
+        if (mergeTabs) {
+            MultiInstanceManager.mergedOnStartup();
+        }
+        TabPersistencePolicy persistencePolicy =
+                new TabbedModeTabPersistencePolicy(selectorIndex, mergeTabs);
         TabModelFilterFactory tabModelFilterFactory = new ChromeTabModelFilterFactory();
-        AsyncTabParamsManager asyncTabParamsManager = AsyncTabParamsManagerSingleton.getInstance();
-
-        return new TabModelSelectorImpl(/*windowAndroidSupplier=*/null, tabCreatorManager,
-                tabModelFilterFactory, nextTabPolicySupplier, asyncTabParamsManager, true, true,
-                false);
+        return new TabModelSelectorImpl(activity, /*windowAndroidSupplier=*/null, tabCreatorManager,
+                persistencePolicy, tabModelFilterFactory, nextTabPolicySupplier,
+                AsyncTabParamsManagerSingleton.getInstance(), true, true, false);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabModelOrchestrator.java b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabModelOrchestrator.java
deleted file mode 100644
index e91d8cc..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabModelOrchestrator.java
+++ /dev/null
@@ -1,80 +0,0 @@
-// 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.app.tabmodel;
-
-import org.chromium.base.supplier.ObservableSupplierImpl;
-import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
-import org.chromium.chrome.browser.tabmodel.TabPersistentStore;
-import org.chromium.chrome.browser.tabmodel.TabPersistentStore.TabPersistentStoreObserver;
-
-/**
- * Implementers are glue-level objects that manage lifetime of root .tabmodel objects: {@link
- * TabPersistentStore} and {@link TabModelSelectorImpl}.
- */
-public abstract class TabModelOrchestrator {
-    protected TabPersistentStore mTabPersistentStore;
-    protected TabModelSelectorImpl mTabModelSelector;
-    private boolean mTabModelsInitialized;
-
-    /**
-     * @return Whether the tab models have been fully initialized.
-     */
-    public boolean areTabModelsInitialized() {
-        return mTabModelsInitialized;
-    }
-
-    /**
-     * @return The {@link TabModelSelectorImpl} managed by this orchestrator.
-     */
-    public TabModelSelectorImpl getTabModelSelector() {
-        return mTabModelSelector;
-    }
-
-    /**
-     * Destroy the {@link TabPersistentStore} and {@link TabModelSelectorImpl} members.
-     */
-    public void destroy() {
-        if (!mTabModelsInitialized) {
-            return;
-        }
-
-        if (mTabPersistentStore != null) {
-            mTabPersistentStore.destroy();
-            mTabPersistentStore = null;
-        }
-
-        if (mTabModelSelector != null) {
-            mTabModelSelector.destroy();
-            mTabModelSelector = null;
-        }
-
-        mTabModelsInitialized = false;
-    }
-
-    protected void wireSelectorAndStore() {
-        // Supply TabModelSelectorImpl with TabPersistentStore.
-        //
-        // TODO(crbug.com/1138561): Remove this dependency by making TabModelSelectorImpl emit
-        // events and TabPersistentStore react to them as an observer.
-        ObservableSupplierImpl<TabPersistentStore> tabPersistentStoreSupplier =
-                new ObservableSupplierImpl<>();
-        tabPersistentStoreSupplier.set(mTabPersistentStore);
-        mTabModelSelector.setTabPersistentStoreSupplier(tabPersistentStoreSupplier);
-
-        // Notify TabModelSelectorImpl when TabPersistentStore initializes tab state
-        final TabPersistentStoreObserver persistentStoreObserver =
-                new TabPersistentStoreObserver() {
-                    @Override
-                    public void onStateLoaded() {
-                        mTabModelSelector.markTabStateInitialized();
-                    }
-                };
-        mTabPersistentStore.addObserver(persistentStoreObserver);
-    }
-
-    protected void markTabModelsInitialized() {
-        mTabModelsInitialized = true;
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabbedModeTabModelOrchestrator.java b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabbedModeTabModelOrchestrator.java
deleted file mode 100644
index 11fe7e67..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabbedModeTabModelOrchestrator.java
+++ /dev/null
@@ -1,90 +0,0 @@
-// 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.app.tabmodel;
-
-import android.app.Activity;
-import android.os.Build;
-import android.util.Pair;
-
-import org.chromium.chrome.browser.multiwindow.MultiInstanceManager;
-import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
-import org.chromium.chrome.browser.tabmodel.NextTabPolicy.NextTabPolicySupplier;
-import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
-import org.chromium.chrome.browser.tabmodel.TabModelSelector;
-import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
-import org.chromium.chrome.browser.tabmodel.TabPersistencePolicy;
-import org.chromium.chrome.browser.tabmodel.TabPersistentStore;
-import org.chromium.chrome.browser.tabmodel.TabbedModeTabPersistencePolicy;
-import org.chromium.ui.widget.Toast;
-
-/**
- * Glue-level class that manages lifetime of root .tabmodel objects: {@link TabPersistentStore} and
- * {@link TabModelSelectorImpl} for tabbed mode.
- */
-public class TabbedModeTabModelOrchestrator extends TabModelOrchestrator {
-    public TabbedModeTabModelOrchestrator() {}
-
-    /**
-     * Creates the TabModelSelector and the TabPersistentStore.
-     *
-     * @return Whether the creation was successful. It may fail is we reached the limit of number of
-     *         windows.
-     */
-    public boolean createTabModels(Activity activity, TabCreatorManager tabCreatorManager,
-            NextTabPolicySupplier nextTabPolicySupplier, int selectorIndex) {
-        boolean mergeTabs = shouldMergeTabs(activity);
-        if (mergeTabs) {
-            MultiInstanceManager.mergedOnStartup();
-        }
-
-        // Instantiate TabModelSelectorImpl
-        Pair<Integer, TabModelSelector> selectorAssignment =
-                TabWindowManagerSingleton.getInstance().requestSelector(
-                        activity, tabCreatorManager, nextTabPolicySupplier, selectorIndex);
-        int assignedIndex = selectorAssignment.first;
-        mTabModelSelector = (TabModelSelectorImpl) selectorAssignment.second;
-        if (mTabModelSelector == null) {
-            markTabModelsInitialized();
-            Toast.makeText(activity,
-                         activity.getString(
-                                 org.chromium.chrome.R.string.unsupported_number_of_windows),
-                         Toast.LENGTH_LONG)
-                    .show();
-            return false;
-        }
-
-        // Instantiate TabPersistentStore
-        TabPersistencePolicy tabPersistencePolicy =
-                new TabbedModeTabPersistencePolicy(assignedIndex, mergeTabs);
-        mTabPersistentStore =
-                new TabPersistentStore(tabPersistencePolicy, mTabModelSelector, tabCreatorManager);
-
-        wireSelectorAndStore();
-        markTabModelsInitialized();
-        return true;
-    }
-
-    private boolean shouldMergeTabs(Activity activity) {
-        // Merge tabs if this TabModelSelector is for a ChromeTabbedActivity created in
-        // fullscreen mode and there are no TabModelSelector's currently alive. This indicates
-        // that it is a cold start or process restart in fullscreen mode.
-        boolean mergeTabs = Build.VERSION.SDK_INT > Build.VERSION_CODES.M
-                && MultiInstanceManager.isTabModelMergingEnabled()
-                && !activity.isInMultiWindowMode();
-        if (MultiInstanceManager.shouldMergeOnStartup(activity)) {
-            mergeTabs = mergeTabs
-                    && (!MultiWindowUtils.getInstance().isInMultiDisplayMode(activity)
-                            || TabWindowManagerSingleton.getInstance()
-                                            .getNumberOfAssignedTabModelSelectors()
-                                    == 0);
-        } else {
-            mergeTabs = mergeTabs
-                    && TabWindowManagerSingleton.getInstance()
-                                    .getNumberOfAssignedTabModelSelectors()
-                            == 0;
-        }
-        return mergeTabs;
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index 8166ec2..2ad2546 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -11,11 +11,9 @@
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
-
 import org.chromium.base.Log;
 import org.chromium.base.ObserverList;
 import org.chromium.base.SysUtils;
@@ -70,9 +68,6 @@
 import org.chromium.ui.touch_selection.SelectionEventType;
 import org.chromium.url.GURL;
 
-import java.net.MalformedURLException;
-import java.net.URL;
-
 /**
  * Manages the Contextual Search feature. This class keeps track of the status of Contextual
  * Search and coordinates the control with the layout.
@@ -378,16 +373,12 @@
         return mSelectionController.getBaseWebContents();
     }
 
-    /** @return The Base Page's {@link URL}. */
+    /** @return The Base Page's {@link GURL}. */
     @Nullable
-    private URL getBasePageURL() {
+    private GURL getBasePageURL() {
         WebContents baseWebContents = mSelectionController.getBaseWebContents();
         if (baseWebContents == null) return null;
-        try {
-            return new URL(baseWebContents.getVisibleUrl().getSpec());
-        } catch (MalformedURLException e) {
-            return null;
-        }
+        return baseWebContents.getVisibleUrl();
     }
 
     /** Notifies that the base page has started loading a page. */
@@ -539,15 +530,10 @@
 
     @Override
     @Nullable
-    public URL getBasePageUrl() {
+    public GURL getBasePageUrl() {
         WebContents baseWebContents = getBaseWebContents();
         if (baseWebContents == null) return null;
-
-        try {
-            return new URL(baseWebContents.getLastCommittedUrl());
-        } catch (MalformedURLException e) {
-            return null;
-        }
+        return baseWebContents.getLastCommittedUrl();
     }
 
     /** Accessor for the {@code InfoBarContainer} currently attached to the {@code Tab}. */
@@ -1232,7 +1218,9 @@
         // this problem, we are ignoring tap gestures in the Search Bar if we don't know what
         // to search for.
         if (mSearchRequest != null && getSearchPanelWebContents() != null) {
-            String url = getContentViewUrl(getSearchPanelWebContents());
+            GURL gurl = getContentViewUrl(getSearchPanelWebContents());
+            // TODO(yfriedman): crbug/783819 - Finish ContextualSearch migration to gurl.
+            String url = gurl.getSpec();
 
             // If it's a search URL, format it so the SearchBox becomes visible.
             if (mSearchRequest.isContextualSearchUrl(url)) {
@@ -1253,11 +1241,11 @@
      * @param searchWebContents The given WebContents.
      * @return The current loaded URL.
      */
-    private String getContentViewUrl(WebContents searchWebContents) {
+    private GURL getContentViewUrl(WebContents searchWebContents) {
         // First, check the pending navigation entry, because there might be an navigation
         // not yet committed being processed. Otherwise, get the URL from the WebContents.
         NavigationEntry entry = searchWebContents.getNavigationController().getPendingEntry();
-        return entry != null ? entry.getUrl().getSpec() : searchWebContents.getLastCommittedUrl();
+        return entry != null ? entry.getUrl() : searchWebContents.getLastCommittedUrl();
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchNetworkCommunicator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchNetworkCommunicator.java
index 31d2ea6..9dd4977 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchNetworkCommunicator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchNetworkCommunicator.java
@@ -6,7 +6,7 @@
 
 import androidx.annotation.Nullable;
 
-import java.net.URL;
+import org.chromium.url.GURL;
 
 /**
  * An interface for network communication between the Contextual Search client and server.
@@ -50,5 +50,6 @@
      * This is needed to stub out for testing, but has nothing to do with networking.
      * @return The URL of the base page (needed for testing purposes).
      */
-    @Nullable URL getBasePageUrl();
+    @Nullable
+    GURL getBasePageUrl();
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java
index 07d93a9..0ae2a66 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java
@@ -28,8 +28,9 @@
 import org.chromium.chrome.browser.signin.services.UnifiedConsentServiceBridge;
 import org.chromium.chrome.browser.version.ChromeVersionInfo;
 import org.chromium.components.embedder_support.util.UrlConstants;
+import org.chromium.components.embedder_support.util.UrlUtilities;
+import org.chromium.url.GURL;
 
-import java.net.URL;
 import java.util.HashSet;
 import java.util.regex.Pattern;
 
@@ -349,7 +350,7 @@
         // and it's also possible that public pages, e.g. news, have more searches for multi-word
         // entities like people.
         if (!isUserUndecided()) {
-            URL url = mNetworkCommunicator.getBasePageUrl();
+            GURL url = mNetworkCommunicator.getBasePageUrl();
             ContextualSearchUma.logBasePageProtocol(isBasePageHTTP(url));
             boolean isSingleWord = !CONTAINS_WHITESPACE_PATTERN.matcher(searchTerm.trim()).find();
             ContextualSearchUma.logSearchTermResolvedWords(isSingleWord);
@@ -388,10 +389,9 @@
         if (!TemplateUrlServiceFactory.get().isDefaultSearchEngineGoogle()) return false;
 
         // Only allow HTTP or HTTPS URLs.
-        URL url = mNetworkCommunicator.getBasePageUrl();
-        String urlProtocol = url != null ? url.getProtocol() : "";
-        if (!(urlProtocol.equals(UrlConstants.HTTP_SCHEME)
-                    || urlProtocol.equals(UrlConstants.HTTPS_SCHEME))) {
+        GURL url = mNetworkCommunicator.getBasePageUrl();
+
+        if (url == null || !UrlUtilities.isHttpOrHttps(url)) {
             return false;
         }
 
@@ -523,8 +523,8 @@
      * @param url The URL of the base page.
      * @return Whether the given content view is for an HTTP page.
      */
-    boolean isBasePageHTTP(@Nullable URL url) {
-        return url != null && UrlConstants.HTTP_SCHEME.equals(url.getProtocol());
+    boolean isBasePageHTTP(@Nullable GURL url) {
+        return url != null && UrlConstants.HTTP_SCHEME.equals(url.getScheme());
     }
 
     // --------------------------------------------------------------------------------------------
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 cac448e..f7e9b23a 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
@@ -24,7 +24,6 @@
 import org.chromium.chrome.browser.ChromeApplication;
 import org.chromium.chrome.browser.KeyboardShortcuts;
 import org.chromium.chrome.browser.app.ChromeActivity;
-import org.chromium.chrome.browser.app.tabmodel.TabModelOrchestrator;
 import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.browserservices.ui.controller.Verifier;
 import org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityCoordinator;
@@ -46,6 +45,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabState;
 import org.chromium.chrome.browser.tabmodel.ChromeTabCreator;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
 import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
 import org.chromium.chrome.browser.ui.RootUiCoordinator;
@@ -293,20 +293,8 @@
     }
 
     @Override
-    protected TabModelOrchestrator createTabModelOrchestrator() {
-        return mTabFactory.createTabModelOrchestrator();
-    }
-
-    @Override
-    protected void destroyTabModels() {
-        if (mTabFactory != null) {
-            mTabFactory.destroyTabModelOrchestrator();
-        }
-    }
-
-    @Override
-    protected void createTabModels() {
-        mTabFactory.createTabModels();
+    protected TabModelSelector createTabModelSelector() {
+        return mTabFactory.createTabModelSelector();
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabFactory.java
index d2427843..26188ed 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabFactory.java
@@ -15,8 +15,6 @@
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.app.tabmodel.AsyncTabParamsManagerSingleton;
 import org.chromium.chrome.browser.app.tabmodel.ChromeTabModelFilterFactory;
-import org.chromium.chrome.browser.app.tabmodel.CustomTabsTabModelOrchestrator;
-import org.chromium.chrome.browser.app.tabmodel.TabModelOrchestrator;
 import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.customtabs.CustomTabDelegateFactory;
 import org.chromium.chrome.browser.customtabs.CustomTabTabPersistencePolicy;
@@ -28,6 +26,7 @@
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.AsyncTabParamsManager;
 import org.chromium.chrome.browser.tabmodel.ChromeTabCreator;
+import org.chromium.chrome.browser.tabmodel.NextTabPolicy;
 import org.chromium.chrome.browser.tabmodel.TabModelFilterFactory;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
@@ -57,7 +56,7 @@
     private final Lazy<AsyncTabParamsManager> mAsyncTabParamsManager;
 
     @Nullable
-    private CustomTabsTabModelOrchestrator mTabModelOrchestrator;
+    private TabModelSelectorImpl mTabModelSelector;
 
     @Inject
     public CustomTabActivityTabFactory(ChromeActivity<?> activity,
@@ -78,35 +77,21 @@
         mAsyncTabParamsManager = asyncTabParamsManager;
     }
 
-    /** Creates a {@link TabModelOrchestrator} for the custom tab. */
-    public TabModelOrchestrator createTabModelOrchestrator() {
-        mTabModelOrchestrator = new CustomTabsTabModelOrchestrator();
-        return mTabModelOrchestrator;
-    }
-
-    public void destroyTabModelOrchestrator() {
-        if (mTabModelOrchestrator != null) {
-            mTabModelOrchestrator.destroy();
-        }
-    }
-
-    /** Calls the {@link TabModelOrchestrator} to create TabModels and TabPersistentStore. */
-    public void createTabModels() {
-        mTabModelOrchestrator.createTabModels(mActivityWindowAndroid::get, mActivity,
-                mTabModelFilterFactory, mPersistencePolicy, mAsyncTabParamsManager.get());
+    /** Creates a {@link TabModelSelector} for the custom tab. */
+    public TabModelSelectorImpl createTabModelSelector() {
+        mTabModelSelector = new TabModelSelectorImpl(mActivity, mActivityWindowAndroid::get,
+                mActivity, mPersistencePolicy, mTabModelFilterFactory,
+                () -> NextTabPolicy.LOCATIONAL, mAsyncTabParamsManager.get(), false, false, false);
+        return mTabModelSelector;
     }
 
     /** Returns the previously created {@link TabModelSelector}. */
     public TabModelSelectorImpl getTabModelSelector() {
-        if (mTabModelOrchestrator == null) {
+        if (mTabModelSelector == null) {
             assert false;
-            createTabModelOrchestrator();
+            return createTabModelSelector();
         }
-        if (mTabModelOrchestrator.getTabModelSelector() == null) {
-            assert false;
-            createTabModels();
-        }
-        return mTabModelOrchestrator.getTabModelSelector();
+        return mTabModelSelector;
     }
 
     /** Creates a {@link ChromeTabCreator}s for the custom tab. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
index 521f9f8..968264e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
@@ -45,6 +45,7 @@
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.WebContentsObserver;
 import org.chromium.ui.util.ColorUtils;
+import org.chromium.url.GURL;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -423,8 +424,7 @@
         WebContents webContents = mTab.getWebContents();
         if (webContents == null) return;
 
-        String url = webContents.getLastCommittedUrl();
-        if (url == null) return;
+        GURL url = webContents.getLastCommittedUrl();
 
         onStartedReaderMode();
 
@@ -461,15 +461,14 @@
         WebContents webContents = mTab.getWebContents();
         if (webContents == null) return;
 
-        String url = webContents.getLastCommittedUrl();
-        if (url == null) return;
+        GURL url = webContents.getLastCommittedUrl();
 
         onStartedReaderMode();
 
         DomDistillerTabUtils.distillCurrentPage(webContents);
 
         String distillerUrl = DomDistillerUrlUtils.getDistillerViewUrlFromUrl(
-                DOM_DISTILLER_SCHEME, url, webContents.getTitle());
+                DOM_DISTILLER_SCHEME, url.getSpec(), webContents.getTitle());
 
         CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
         builder.setShowTitle(true);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordChangeLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordChangeLauncher.java
index 6e24417..f598c13 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordChangeLauncher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordChangeLauncher.java
@@ -10,6 +10,7 @@
 import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantArguments;
 import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantFacade;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.url.GURL;
 
 /** Class for starting a password change flow in Autofill Assistant. */
 public class PasswordChangeLauncher {
@@ -26,7 +27,7 @@
     private static final String INTENT = "PASSWORD_CHANGE";
 
     @CalledByNative
-    public static void start(WindowAndroid windowAndroid, String origin, String username) {
+    public static void start(WindowAndroid windowAndroid, GURL origin, String username) {
         ChromeActivity activity = (ChromeActivity) windowAndroid.getActivity().get();
         if (activity == null) {
             Log.v(TAG, "Failed to retrieve ChromeActivity.");
@@ -34,7 +35,7 @@
         }
         AutofillAssistantFacade.start(activity,
                 AutofillAssistantArguments.newBuilder()
-                        .withInitialUrl(origin)
+                        .withInitialUrl(origin.getSpec())
                         .addParameter(PASSWORD_CHANGE_USERNAME_PARAMETER, username)
                         .addParameter(INTENT_PARAMETER, INTENT)
                         .addParameter(AutofillAssistantArguments.PARAMETER_START_IMMEDIATELY, true)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java
index c123e2f4..6866be1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java
@@ -1270,7 +1270,7 @@
 
         final FaviconHelper faviconHelper = new FaviconHelper();
         faviconHelper.getLocalFaviconImageForURL(Profile.fromWebContents(mWebContents),
-                mWebContents.getLastCommittedUrl(),
+                mWebContents.getLastCommittedUrl().getSpec(),
                 activity.getResources().getDimensionPixelSize(R.dimen.payments_favicon_size),
                 (bitmap, iconUrl) -> {
                     if (bitmap == null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninBridge.java
new file mode 100644
index 0000000..a51d859
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninBridge.java
@@ -0,0 +1,33 @@
+// Copyright 2021 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.signin;
+
+import android.content.Context;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.chrome.browser.sync.settings.AccountManagementFragment;
+import org.chromium.components.signin.GAIAServiceType;
+import org.chromium.ui.base.WindowAndroid;
+
+/**
+ * The bridge regroups methods invoked by native code to interact with Android Signin UI.
+ */
+final class SigninBridge {
+    /**
+     * Opens account management screen.
+     */
+    @CalledByNative
+    private static void openAccountManagementScreen(
+            WindowAndroid windowAndroid, @GAIAServiceType int gaiaServiceType) {
+        ThreadUtils.assertOnUiThread();
+        final Context context = windowAndroid.getContext().get();
+        if (context != null) {
+            AccountManagementFragment.openAccountManagementScreen(context, gaiaServiceType);
+        }
+    }
+
+    private SigninBridge() {}
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninUtils.java
index d6da252b..aee18a9a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninUtils.java
@@ -10,7 +10,6 @@
 import android.os.Build;
 import android.provider.Settings;
 
-import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.IntentUtils;
@@ -25,13 +24,11 @@
 import org.chromium.chrome.browser.signin.services.SigninMetricsUtils;
 import org.chromium.chrome.browser.signin.services.WebSigninBridge;
 import org.chromium.chrome.browser.signin.ui.account_picker.AccountPickerBottomSheetCoordinator;
-import org.chromium.chrome.browser.sync.settings.AccountManagementFragment;
 import org.chromium.chrome.browser.tabmodel.TabCreator;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerProvider;
 import org.chromium.components.signin.AccountManagerFacadeProvider;
-import org.chromium.components.signin.GAIAServiceType;
 import org.chromium.components.signin.metrics.AccountConsistencyPromoAction;
 import org.chromium.ui.base.WindowAndroid;
 
@@ -69,13 +66,9 @@
         return IntentUtils.safeStartActivity(activity, new Intent(Settings.ACTION_SYNC_SETTINGS));
     }
 
-    @CalledByNative
-    private static void openAccountManagementScreen(WindowAndroid windowAndroid,
-            @GAIAServiceType int gaiaServiceType, @Nullable String email) {
-        ThreadUtils.assertOnUiThread();
-        AccountManagementFragment.openAccountManagementScreen(gaiaServiceType);
-    }
-
+    /**
+     * TODO(crbug/1165772): Move this method to SigninBridge.
+     */
     @CalledByNative
     @VisibleForTesting
     static void openAccountPickerBottomSheet(WindowAndroid windowAndroid, String continueUrl) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/AccountManagementFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/AccountManagementFragment.java
index 8d1b68f..66e9629 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/AccountManagementFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/AccountManagementFragment.java
@@ -21,7 +21,6 @@
 import androidx.preference.PreferenceScreen;
 
 import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.base.ContextUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.preferences.Pref;
@@ -449,11 +448,12 @@
      * Open the account management UI.
      * @param serviceType A signin::GAIAServiceType that triggered the dialog.
      */
-    public static void openAccountManagementScreen(@GAIAServiceType int serviceType) {
+    public static void openAccountManagementScreen(
+            Context context, @GAIAServiceType int serviceType) {
         Bundle arguments = new Bundle();
         arguments.putInt(SHOW_GAIA_SERVICE_TYPE_EXTRA, serviceType);
         SettingsLauncher settingsLauncher = new SettingsLauncherImpl();
         settingsLauncher.launchSettingsActivity(
-                ContextUtils.getApplicationContext(), AccountManagementFragment.class, arguments);
+                context, AccountManagementFragment.class, arguments);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabIdManager.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabIdManager.java
index 272f200..0362697 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabIdManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabIdManager.java
@@ -95,4 +95,9 @@
         mPreferences = SharedPreferencesManager.getInstance();
         mIdCounter.set(mPreferences.readInt(ChromePreferenceKeys.TAB_ID_MANAGER_NEXT_ID));
     }
+
+    @VisibleForTesting
+    static void resetInstanceForTesting() {
+        sInstance = null;
+    }
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
index b036ac1..82ad191 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.tabmodel;
 
+import android.app.Activity;
 import android.os.Handler;
 
 import androidx.annotation.Nullable;
@@ -19,6 +20,7 @@
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tab.TabSelectionType;
 import org.chromium.chrome.browser.tabmodel.NextTabPolicy.NextTabPolicySupplier;
+import org.chromium.chrome.browser.tabmodel.TabPersistentStore.TabPersistentStoreObserver;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.url.GURL;
 
@@ -35,6 +37,7 @@
     /** Flag set to false when the asynchronous loading of tabs is finished. */
     private final AtomicBoolean mSessionRestoreInProgress =
             new AtomicBoolean(true);
+    private final TabPersistentStore mTabSaver;
 
     private boolean mIsUndoSupported;
 
@@ -44,10 +47,6 @@
 
     private final TabModelOrderController mOrderController;
 
-    // TODO(crbug.com/1138561): Remove the dependency from TabModelSelectorImpl to
-    // TabPersistentStore.
-    private Supplier<TabPersistentStore> mTabSaver;
-
     private final AsyncTabParamsManager mAsyncTabParamsManager;
 
     private NextTabPolicySupplier mNextTabPolicySupplier;
@@ -62,41 +61,41 @@
 
     /**
      * Builds a {@link TabModelSelectorImpl} instance.
+     * @param activity An {@link Activity} instance.
      * @param windowAndroidSupplier A supplier of {@link WindowAndroid} instance which is passed
      *         down to {@link IncognitoTabModelImplCreator} for creating {@link IncognitoTabModel}.
      * @param tabCreatorManager A {@link TabCreatorManager} instance.
+     * @param persistencePolicy A {@link TabPersistencePolicy} instance.
      * @param tabModelFilterFactory
      * @param nextTabPolicySupplier
      * @param asyncTabParamsManager
      * @param supportUndo Whether a tab closure can be undone.
      */
-    public TabModelSelectorImpl(@Nullable Supplier<WindowAndroid> windowAndroidSupplier,
-            TabCreatorManager tabCreatorManager, TabModelFilterFactory tabModelFilterFactory,
+    public TabModelSelectorImpl(Activity activity,
+            @Nullable Supplier<WindowAndroid> windowAndroidSupplier,
+            TabCreatorManager tabCreatorManager, TabPersistencePolicy persistencePolicy,
+            TabModelFilterFactory tabModelFilterFactory,
             NextTabPolicySupplier nextTabPolicySupplier,
             AsyncTabParamsManager asyncTabParamsManager, boolean supportUndo,
             boolean isTabbedActivity, boolean startIncognito) {
         super(tabCreatorManager, tabModelFilterFactory, startIncognito);
         mWindowAndroidSupplier = windowAndroidSupplier;
+        final TabPersistentStoreObserver persistentStoreObserver =
+                new TabPersistentStoreObserver() {
+            @Override
+            public void onStateLoaded() {
+                markTabStateInitialized();
+            }
+        };
         mIsUndoSupported = supportUndo;
         mIsTabbedActivityForSync = isTabbedActivity;
+        mTabSaver = new TabPersistentStore(persistencePolicy, this, tabCreatorManager);
+        mTabSaver.addObserver(persistentStoreObserver);
         mOrderController = new TabModelOrderControllerImpl(this);
         mNextTabPolicySupplier = nextTabPolicySupplier;
         mAsyncTabParamsManager = asyncTabParamsManager;
     }
 
-    /**
-     * TODO(crbug.com/1138561): Do not add more parameters here. This is temporary while the
-     * dependency from TabModelSelectorImpl to TabPersistentStore is removed.
-     *
-     * This must be called after the constructor; NPEs are expected, otherwise.
-     * A Supplier that supplies null can be passed in tests.
-     */
-    public void setTabPersistentStoreSupplier(
-            Supplier<TabPersistentStore> tabPersistentStoreSupplier) {
-        assert mTabSaver == null;
-        mTabSaver = tabPersistentStoreSupplier;
-    }
-
     @Override
     public void markTabStateInitialized() {
         super.markTabStateInitialized();
@@ -114,9 +113,7 @@
     }
 
     private void handleOnPageLoadStopped(Tab tab) {
-        if (tab != null && mTabSaver.get() != null) {
-            mTabSaver.get().addTabToSaveQueue(tab);
-        }
+        if (tab != null) mTabSaver.addTabToSaveQueue(tab);
     }
 
     /**
@@ -139,13 +136,13 @@
                 (ChromeTabCreator) getTabCreatorManager().getTabCreator(true);
         TabModelImpl normalModel = new TabModelImpl(Profile.getLastUsedRegularProfile(),
                 mIsTabbedActivityForSync, regularTabCreator, incognitoTabCreator, mOrderController,
-                mTabContentManager, mTabSaver.get(), mNextTabPolicySupplier, mAsyncTabParamsManager,
-                this, mIsUndoSupported);
+                mTabContentManager, mTabSaver, mNextTabPolicySupplier, mAsyncTabParamsManager, this,
+                mIsUndoSupported);
         regularTabCreator.setTabModel(normalModel, mOrderController);
 
         IncognitoTabModel incognitoModel = new IncognitoTabModelImpl(
                 new IncognitoTabModelImplCreator(mWindowAndroidSupplier, regularTabCreator,
-                        incognitoTabCreator, mOrderController, mTabContentManager, mTabSaver.get(),
+                        incognitoTabCreator, mOrderController, mTabContentManager, mTabSaver,
                         mNextTabPolicySupplier, mAsyncTabParamsManager, this));
         incognitoTabCreator.setTabModel(incognitoModel, mOrderController);
         onNativeLibraryReadyInternal(tabContentProvider, normalModel, incognitoModel);
@@ -156,9 +153,7 @@
             IncognitoTabModel incognitoModel) {
         mTabContentManager = tabContentProvider;
         initialize(normalModel, incognitoModel);
-        if (mTabSaver.get() != null) {
-            mTabSaver.get().setTabContentManager(mTabContentManager);
-        }
+        mTabSaver.setTabContentManager(mTabContentManager);
 
         addObserver(new EmptyTabModelSelectorObserver() {
             @Override
@@ -168,9 +163,8 @@
                     mTabContentManager.invalidateIfChanged(tab.getId(), tab.getUrlString());
                 }
 
-                if (mTabSaver.get() != null
-                        && creationState == TabCreationState.FROZEN_FOR_LAZY_LOAD) {
-                    mTabSaver.get().addTabToSaveQueue(tab);
+                if (creationState == TabCreationState.FROZEN_FOR_LAZY_LOAD) {
+                    mTabSaver.addTabToSaveQueue(tab);
                 }
             }
         });
@@ -213,9 +207,7 @@
 
             @Override
             public void onNavigationEntriesDeleted(Tab tab) {
-                if (mTabSaver.get() != null) {
-                    mTabSaver.get().addTabToSaveQueue(tab);
-                }
+                mTabSaver.addTabToSaveQueue(tab);
             }
 
             @Override
@@ -232,9 +224,7 @@
 
             @Override
             public void onRootIdChanged(Tab tab, int newRootId) {
-                if (mTabSaver.get() != null) {
-                    mTabSaver.get().addTabToSaveQueue(tab);
-                }
+                mTabSaver.addTabToSaveQueue(tab);
             }
         };
     }
@@ -295,9 +285,7 @@
      */
     public void saveState() {
         commitAllTabClosures();
-        if (mTabSaver.get() != null) {
-            mTabSaver.get().saveState();
-        }
+        mTabSaver.saveState();
     }
 
     /**
@@ -306,16 +294,12 @@
      * @param ignoreIncognitoFiles Whether to skip loading incognito tabs.
      */
     public void loadState(boolean ignoreIncognitoFiles) {
-        if (mTabSaver.get() != null) {
-            mTabSaver.get().loadState(ignoreIncognitoFiles);
-        }
+        mTabSaver.loadState(ignoreIncognitoFiles);
     }
 
     @Override
     public void mergeState() {
-        if (mTabSaver.get() != null) {
-            mTabSaver.get().mergeState();
-        }
+        mTabSaver.mergeState();
     }
 
     /**
@@ -325,9 +309,7 @@
      *                     active tab.
      */
     public void restoreTabs(boolean setActiveTab) {
-        if (mTabSaver.get() != null) {
-            mTabSaver.get().restoreTabs(setActiveTab);
-        }
+        mTabSaver.restoreTabs(setActiveTab);
     }
 
     /**
@@ -336,9 +318,7 @@
      * there isn't a tab being restored with this url, or the tab has already been restored.
      */
     public void tryToRestoreTabStateForUrl(String url) {
-        if (mTabSaver.get() != null && isSessionRestoreInProgress()) {
-            mTabSaver.get().restoreTabStateForUrl(url);
-        }
+        if (isSessionRestoreInProgress()) mTabSaver.restoreTabStateForUrl(url);
     }
 
     /**
@@ -347,26 +327,24 @@
      * there isn't a tab being restored with this id, or the tab has already been restored.
      */
     public void tryToRestoreTabStateForId(int id) {
-        if (isSessionRestoreInProgress()) {
-            mTabSaver.get().restoreTabStateForId(id);
-        }
+        if (isSessionRestoreInProgress()) mTabSaver.restoreTabStateForId(id);
     }
 
     public void clearState() {
-        if (mTabSaver.get() != null) {
-            mTabSaver.get().clearState();
-        }
+        mTabSaver.clearState();
+    }
+
+    @Override
+    public void destroy() {
+        mTabSaver.destroy();
+        super.destroy();
     }
 
     /**
      * @return Number of restored tabs on cold startup.
      */
     public int getRestoredTabCount() {
-        if (mTabSaver.get() != null) {
-            return mTabSaver.get().getRestoredTabCount();
-        } else {
-            return 0;
-        }
+        return mTabSaver.getRestoredTabCount();
     }
 
     @Override
@@ -388,9 +366,7 @@
                     cacheTabBitmap(mVisibleTab);
                 }
                 mVisibleTab.hide(TabHidingType.CHANGED_TABS);
-                if (mTabSaver.get() != null) {
-                    mTabSaver.get().addTabToSaveQueue(mVisibleTab);
-                }
+                mTabSaver.addTabToSaveQueue(mVisibleTab);
             }
             mVisibleTab = null;
         }
@@ -439,6 +415,6 @@
 
     @VisibleForTesting
     public TabPersistentStore getTabPersistentStoreForTesting() {
-        return mTabSaver.get();
+        return mTabSaver;
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFakeServer.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFakeServer.java
index 70c5bed..7b944408 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFakeServer.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFakeServer.java
@@ -16,9 +16,8 @@
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelContentFactory;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.WebContentsObserver;
+import org.chromium.url.GURL;
 
-import java.net.MalformedURLException;
-import java.net.URL;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
@@ -558,17 +557,12 @@
 
     @Override
     @Nullable
-    public URL getBasePageUrl() {
-        URL baseUrl = mBaseManager.getBasePageUrl();
+    public GURL getBasePageUrl() {
+        GURL baseUrl = mBaseManager.getBasePageUrl();
         if (baseUrl != null) {
-            try {
-                // Return plain HTTP URLs so we can test that we don't give them our legacy privacy
-                // exceptions.
-                return new URL(baseUrl.toString().replace("https://", "http://"));
-            } catch (MalformedURLException e) {
-                // TODO(donnd): Replace Auto-generated catch block
-                e.printStackTrace();
-            }
+            // Return plain HTTP URLs so we can test that we don't give them our legacy privacy
+            // exceptions.
+            return new GURL(baseUrl.getSpec().replace("https://", "http://"));
         }
         return baseUrl;
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicyTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicyTest.java
index 6b29fda6..6858b8e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicyTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicyTest.java
@@ -27,8 +27,7 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-
-import java.net.URL;
+import org.chromium.url.GURL;
 
 /**
  * Tests for the ContextualSearchPolicy class.
@@ -58,7 +57,7 @@
         UnifiedConsentServiceBridge.setUrlKeyedAnonymizedDataCollectionEnabled(
                 Profile.getLastUsedRegularProfile(), true);
         try {
-            when(mMockServer.getBasePageUrl()).thenReturn(new URL("https://someUrl"));
+            when(mMockServer.getBasePageUrl()).thenReturn(new GURL("https://someUrl"));
         } catch (Exception e) {
             Assert.fail("Exception raised building a sample URL");
         }
@@ -114,7 +113,7 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             setupAllConditionsToSendUrl();
             try {
-                when(mMockServer.getBasePageUrl()).thenReturn(new URL("ftp://someSource"));
+                when(mMockServer.getBasePageUrl()).thenReturn(new GURL("ftp://someSource"));
             } catch (Exception e) {
                 Assert.fail("Exception building FTP Uri");
             }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java
index 6dbff08..ca857638 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java
@@ -34,11 +34,12 @@
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.app.tabmodel.AsyncTabParamsManagerSingleton;
 import org.chromium.chrome.browser.app.tabmodel.ChromeTabModelFilterFactory;
-import org.chromium.chrome.browser.app.tabmodel.CustomTabsTabModelOrchestrator;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
 import org.chromium.chrome.browser.tab.MockTab;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabStateFileManager;
+import org.chromium.chrome.browser.tabmodel.NextTabPolicy;
+import org.chromium.chrome.browser.tabmodel.NextTabPolicy.NextTabPolicySupplier;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
 import org.chromium.chrome.browser.tabmodel.TabPersistencePolicy;
@@ -455,11 +456,12 @@
         CustomTabActivity activity = new CustomTabActivity();
         ApplicationStatus.onStateChangeForTesting(activity, ActivityState.CREATED);
 
-        CustomTabsTabModelOrchestrator orchestrator = new CustomTabsTabModelOrchestrator();
-        orchestrator.createTabModels(activity::getWindowAndroid, activity,
-                new ChromeTabModelFilterFactory(), buildTestPersistencePolicy(),
-                AsyncTabParamsManagerSingleton.getInstance());
-        TabModelSelectorImpl selector = orchestrator.getTabModelSelector();
+        NextTabPolicySupplier nextTabPolicySupplier = () -> NextTabPolicy.LOCATIONAL;
+
+        TabModelSelectorImpl selector = new TabModelSelectorImpl(activity,
+                activity::getWindowAndroid, activity, buildTestPersistencePolicy(),
+                new ChromeTabModelFilterFactory(), nextTabPolicySupplier,
+                AsyncTabParamsManagerSingleton.getInstance(), false, false, false);
         selector.initializeForTesting(normalTabModel, incognitoTabModel);
         ApplicationStatus.onStateChangeForTesting(activity, ActivityState.DESTROYED);
         return selector;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionTestUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionTestUtils.java
index a92462c..2a8c791 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionTestUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/directactions/DirectActionTestUtils.java
@@ -71,7 +71,7 @@
     static void allowGoForward(ChromeActivityTestRule<?> rule) throws Exception {
         ChromeActivity activity = rule.getActivity();
         String initialUrl = TestThreadUtils.runOnUiThreadBlocking(
-                () -> activity.getCurrentWebContents().getLastCommittedUrl());
+                () -> activity.getCurrentWebContents().getLastCommittedUrl().getSpec());
 
         // Any built-in page that is not about:blank and is reasonably cheap to render will do,
         // here.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/jsdialog/JavascriptAppModalDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/jsdialog/JavascriptAppModalDialogTest.java
index 3ce506d..5d16f57 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/jsdialog/JavascriptAppModalDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/jsdialog/JavascriptAppModalDialogTest.java
@@ -85,7 +85,10 @@
         onView(withText(R.string.cancel)).perform(click());
 
         Assert.assertEquals(BEFORE_UNLOAD_URL,
-                mActivityTestRule.getActivity().getCurrentWebContents().getLastCommittedUrl());
+                mActivityTestRule.getActivity()
+                        .getCurrentWebContents()
+                        .getLastCommittedUrl()
+                        .getSpec());
         executeJavaScriptAndWaitForDialog("history.back();");
 
         jsDialog = getCurrentDialog();
@@ -98,7 +101,10 @@
         onView(withText(R.string.leave)).perform(click());
         onPageLoaded.waitForCallback(callCount);
         Assert.assertEquals(EMPTY_PAGE,
-                mActivityTestRule.getActivity().getCurrentWebContents().getLastCommittedUrl());
+                mActivityTestRule.getActivity()
+                        .getCurrentWebContents()
+                        .getLastCommittedUrl()
+                        .getSpec());
     }
 
     /**
@@ -165,7 +171,10 @@
         Assert.assertNotNull("No dialog showing.", jsDialog);
         onView(withText(R.string.cancel)).perform(click());
         Assert.assertEquals(BEFORE_UNLOAD_URL,
-                mActivityTestRule.getActivity().getCurrentWebContents().getLastCommittedUrl());
+                mActivityTestRule.getActivity()
+                        .getCurrentWebContents()
+                        .getLastCommittedUrl()
+                        .getSpec());
 
         // Show it again, it should have the option to suppress subsequent dialogs.
         OnEvaluateJavaScriptResultHelper resultHelper =
@@ -178,7 +187,10 @@
                 .check(matches(isChecked()));
         onView(withText(R.string.cancel)).perform(click());
         Assert.assertEquals(BEFORE_UNLOAD_URL,
-                mActivityTestRule.getActivity().getCurrentWebContents().getLastCommittedUrl());
+                mActivityTestRule.getActivity()
+                        .getCurrentWebContents()
+                        .getLastCommittedUrl()
+                        .getSpec());
 
         // Try showing a dialog again and verify it is not shown.
         resultHelper.evaluateJavaScriptForTests(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/RepostFormWarningTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/RepostFormWarningTest.java
index d279e96..2615084 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/RepostFormWarningTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/RepostFormWarningTest.java
@@ -10,13 +10,14 @@
 import androidx.test.filters.SmallTest;
 
 import org.hamcrest.Matchers;
-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.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
@@ -25,6 +26,7 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
@@ -40,11 +42,17 @@
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@Batch(Batch.PER_CLASS)
 public class RepostFormWarningTest {
     // Active tab.
 
+    @ClassRule
+    public static ChromeTabbedActivityTestRule sActivityTestRule =
+            new ChromeTabbedActivityTestRule();
+
     @Rule
-    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
+    public BlankCTATabInitialStateRule mInitialStateRule =
+            new BlankCTATabInitialStateRule(sActivityTestRule, false);
 
     private Tab mTab;
     // Callback helper that manages waiting for pageloads to finish.
@@ -54,16 +62,9 @@
 
     @Before
     public void setUp() throws Exception {
-        mActivityTestRule.startMainActivityOnBlankPage();
-
-        mTab = mActivityTestRule.getActivity().getActivityTab();
+        mTab = sActivityTestRule.getActivity().getActivityTab();
         mCallbackHelper = new TestCallbackHelperContainer(mTab.getWebContents());
-        mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
-    }
-
-    @After
-    public void tearDown() {
-        mTestServer.stopAndDestroyServer();
+        mTestServer = sActivityTestRule.getTestServer();
     }
 
     /** Verifies that the form resubmission warning is not displayed upon first POST navigation. */
@@ -148,7 +149,8 @@
         waitForRepostFormWarningDialog();
 
         TestThreadUtils.runOnUiThreadBlocking(
-                (Runnable) () -> mActivityTestRule.getActivity().getCurrentTabModel().closeTab(mTab));
+                (Runnable) ()
+                        -> sActivityTestRule.getActivity().getCurrentTabModel().closeTab(mTab));
 
         waitForNoReportFormWarningDialog();
     }
@@ -156,7 +158,7 @@
     private PropertyModel getCurrentModalDialog() {
         return TestThreadUtils.runOnUiThreadBlockingNoException(
                 ()
-                        -> mActivityTestRule.getActivity()
+                        -> sActivityTestRule.getActivity()
                                    .getModalDialogManager()
                                    .getCurrentDialogForTest());
     }
@@ -174,7 +176,7 @@
             Criteria.checkThat("No modal dialog shown", dialogModel, Matchers.notNullValue());
             Criteria.checkThat("Modal dialog is not a HTTP post dialog",
                     dialogModel.get(ModalDialogProperties.TITLE),
-                    Matchers.is(mActivityTestRule.getActivity().getString(
+                    Matchers.is(sActivityTestRule.getActivity().getString(
                             R.string.http_post_warning_title)));
         });
         return getCurrentModalDialog();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabIdManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabIdManagerTest.java
index 6352b8a..1cbc521c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabIdManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabIdManagerTest.java
@@ -16,18 +16,21 @@
 
 import org.chromium.base.test.UiThreadTest;
 import org.chromium.base.test.util.AdvancedMockContext;
+import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 
 /** Tests for the TabIdManager. */
 @RunWith(ChromeJUnit4ClassRunner.class)
+@Batch(Batch.UNIT_TESTS)
 public class TabIdManagerTest {
     Context mContext;
 
     @Before
     public void setUp() {
         mContext = new AdvancedMockContext(InstrumentationRegistry.getTargetContext());
+        TabIdManager.resetInstanceForTesting();
     }
 
     /** Tests that IDs are stored and generated properly. */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/ContextMenuLoadUrlParamsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/ContextMenuLoadUrlParamsTest.java
index c4be5334..817a66c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/ContextMenuLoadUrlParamsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/ContextMenuLoadUrlParamsTest.java
@@ -74,7 +74,8 @@
 
         public RecordingTabModelSelector(Activity activity, TabCreatorManager tabCreatorManager,
                 TabModelFilterFactory tabModelFilterFactory, int selectorIndex) {
-            super(null, tabCreatorManager, tabModelFilterFactory,
+            super(activity, null, tabCreatorManager,
+                    new TabbedModeTabPersistencePolicy(selectorIndex, false), tabModelFilterFactory,
                     ()
                             -> NextTabPolicy.HIERARCHICAL,
                     AsyncTabParamsManagerSingleton.getInstance(), false, false, false);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelMergingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelMergingTest.java
index 5111d77..45471592 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelMergingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelMergingTest.java
@@ -361,9 +361,7 @@
         MockTabPersistentStoreObserver mockObserver = new MockTabPersistentStoreObserver();
         TabModelSelectorImpl tabModelSelector =
                 (TabModelSelectorImpl) mActivity2.getTabModelSelector();
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            tabModelSelector.getTabPersistentStoreForTesting().addObserver(mockObserver);
-        });
+        tabModelSelector.getTabPersistentStoreForTesting().addObserver(mockObserver);
 
         // Merge tabs into ChromeTabbedActivity2. Wait for the merge to finish, ensuring the
         // tab metadata file for ChromeTabbedActivity gets deleted before attempting to merge
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
index 5b514bf..3f21e37 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
@@ -32,7 +32,6 @@
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.app.tabmodel.AsyncTabParamsManagerSingleton;
 import org.chromium.chrome.browser.app.tabmodel.ChromeTabModelFilterFactory;
-import org.chromium.chrome.browser.app.tabmodel.TabModelOrchestrator;
 import org.chromium.chrome.browser.app.tabmodel.TabWindowManagerSingleton;
 import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutHelper;
 import org.chromium.chrome.browser.flags.ActivityType;
@@ -247,17 +246,11 @@
                 }
 
                 @Override
-                protected TabModelOrchestrator createTabModelOrchestrator() {
+                protected TabModelSelector createTabModelSelector() {
                     return null;
                 }
 
                 @Override
-                protected void createTabModels() {}
-
-                @Override
-                protected void destroyTabModels() {}
-
-                @Override
                 protected BrowserControlsManager createBrowserControlsManager() {
                     return null;
                 }
@@ -728,9 +721,8 @@
                         // createAndRestoreRealTabModelImpls is called multiple times in one test).
                         sTabWindowManager.onActivityStateChange(
                                 mChromeActivity, ActivityState.DESTROYED);
-                        return (TestTabModelSelector) sTabWindowManager
-                                .requestSelector(mChromeActivity, mChromeActivity, null, 0)
-                                .second;
+                        return (TestTabModelSelector) sTabWindowManager.requestSelector(
+                                mChromeActivity, mChromeActivity, null, 0);
                     }
                 });
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabWindowManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabWindowManagerTest.java
index 5cc728a9..9864a32 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabWindowManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabWindowManagerTest.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.tabmodel;
 
 import android.app.Activity;
-import android.util.Pair;
 
 import androidx.test.filters.SmallTest;
 
@@ -72,10 +71,9 @@
         ApplicationStatus.onStateChangeForTesting(a, ActivityState.DESTROYED);
     }
 
-    private Pair<Integer, TabModelSelector> requestSelector(
-            ChromeActivity activity, int requestedIndex) {
+    private MockTabModelSelector requestSelector(ChromeActivity activity, int requestedIndex) {
         final TabWindowManager manager = TabWindowManagerSingleton.getInstance();
-        return manager.requestSelector(
+        return (MockTabModelSelector) manager.requestSelector(
                 activity, activity, () -> NextTabPolicy.HIERARCHICAL, requestedIndex);
     }
 
@@ -100,10 +98,8 @@
         final TabWindowManager manager = TabWindowManagerSingleton.getInstance();
 
         ChromeActivity activity0 = buildActivity();
-        Pair<Integer, TabModelSelector> assignment0 = requestSelector(activity0, 0);
+        TabModelSelector selector0 = requestSelector(activity0, 0);
 
-        Assert.assertEquals(0, assignment0.first.intValue());
-        TabModelSelector selector0 = assignment0.second;
         Assert.assertNotNull("Was not able to build the TabModelSelector", selector0);
         Assert.assertEquals("Unexpected model index", 0, manager.getIndexForWindow(activity0));
     }
@@ -121,13 +117,11 @@
 
         ChromeActivity activity0 = buildActivity();
         ChromeActivity activity1 = buildActivity();
-        Pair<Integer, TabModelSelector> assignment0 = requestSelector(activity0, 0);
-        Pair<Integer, TabModelSelector> assignment1 = requestSelector(activity1, 1);
+        TabModelSelector selector0 = requestSelector(activity0, 0);
+        TabModelSelector selector1 = requestSelector(activity1, 1);
 
-        Assert.assertEquals(0, assignment0.first.intValue());
-        Assert.assertEquals(1, assignment1.first.intValue());
-        Assert.assertNotNull("Was not able to build the TabModelSelector", assignment0.second);
-        Assert.assertNotNull("Was not able to build the TabModelSelector", assignment1.second);
+        Assert.assertNotNull("Was not able to build the TabModelSelector", selector0);
+        Assert.assertNotNull("Was not able to build the TabModelSelector", selector1);
         Assert.assertEquals("Unexpected model index", 0, manager.getIndexForWindow(activity0));
         Assert.assertEquals("Unexpected model index", 1, manager.getIndexForWindow(activity1));
     }
@@ -166,14 +160,12 @@
 
         ChromeActivity activity0 = buildActivity();
         ChromeActivity activity1 = buildActivity();
-        Pair<Integer, TabModelSelector> assignment0 = requestSelector(activity0, 0);
+        TabModelSelector selector0 = requestSelector(activity0, 0);
         // Request 0 again, but should get 1 instead.
-        Pair<Integer, TabModelSelector> assignment1 = requestSelector(activity1, 0);
+        TabModelSelector selector1 = requestSelector(activity1, 0);
 
-        Assert.assertEquals(0, assignment0.first.intValue());
-        Assert.assertEquals(1, assignment1.first.intValue());
-        Assert.assertNotNull("Was not able to build the TabModelSelector", assignment0.second);
-        Assert.assertNotNull("Was not able to build the TabModelSelector", assignment1.second);
+        Assert.assertNotNull("Was not able to build the TabModelSelector", selector0);
+        Assert.assertNotNull("Was not able to build the TabModelSelector", selector1);
         Assert.assertEquals("Unexpected model index", 0, manager.getIndexForWindow(activity0));
         Assert.assertEquals("Unexpected model index", 1, manager.getIndexForWindow(activity1));
     }
@@ -193,14 +185,12 @@
 
         ChromeActivity activity0 = buildActivity();
         ChromeActivity activity1 = buildActivity();
-        Pair<Integer, TabModelSelector> assignment0 = requestSelector(activity0, 2);
+        TabModelSelector selector0 = requestSelector(activity0, 2);
         // Request 2 again, but should get 0 instead.
-        Pair<Integer, TabModelSelector> assignment1 = requestSelector(activity1, 2);
+        TabModelSelector selector1 = requestSelector(activity1, 2);
 
-        Assert.assertEquals(2, assignment0.first.intValue());
-        Assert.assertEquals(0, assignment1.first.intValue());
-        Assert.assertNotNull("Was not able to build the TabModelSelector", assignment0.second);
-        Assert.assertNotNull("Was not able to build the TabModelSelector", assignment1.second);
+        Assert.assertNotNull("Was not able to build the TabModelSelector", selector0);
+        Assert.assertNotNull("Was not able to build the TabModelSelector", selector1);
         Assert.assertEquals("Unexpected model index", 2, manager.getIndexForWindow(activity0));
         Assert.assertEquals("Unexpected model index", 0, manager.getIndexForWindow(activity1));
     }
@@ -217,10 +207,9 @@
         final TabWindowManager manager = TabWindowManagerSingleton.getInstance();
 
         ChromeActivity activity0 = buildActivity();
-        Pair<Integer, TabModelSelector> assignment0 = requestSelector(activity0, 0);
+        TabModelSelector selector0 = requestSelector(activity0, 0);
 
-        Assert.assertEquals(0, assignment0.first.intValue());
-        Assert.assertNotNull("Was not able to build the TabModelSelector", assignment0.second);
+        Assert.assertNotNull("Was not able to build the TabModelSelector", selector0);
         Assert.assertEquals("Unexpected model index", 0, manager.getIndexForWindow(activity0));
 
         destroyActivity(activity0);
@@ -241,10 +230,9 @@
         final TabWindowManager manager = TabWindowManagerSingleton.getInstance();
 
         ChromeActivity activity0 = buildActivity();
-        Pair<Integer, TabModelSelector> assignment0 = requestSelector(activity0, 0);
+        TabModelSelector selector0 = requestSelector(activity0, 0);
 
-        Assert.assertEquals(0, assignment0.first.intValue());
-        Assert.assertNotNull("Was not able to build the TabModelSelector", assignment0.second);
+        Assert.assertNotNull("Was not able to build the TabModelSelector", selector0);
         Assert.assertEquals("Unexpected model index", 0, manager.getIndexForWindow(activity0));
 
         destroyActivity(activity0);
@@ -253,10 +241,9 @@
                 manager.getIndexForWindow(activity0));
 
         ChromeActivity activity1 = buildActivity();
-        Pair<Integer, TabModelSelector> assignment1 = requestSelector(activity1, 0);
+        TabModelSelector selector1 = requestSelector(activity1, 0);
 
-        Assert.assertEquals(0, assignment1.first.intValue());
-        Assert.assertNotNull("Was not able to build the TabModelSelector", assignment1.second);
+        Assert.assertNotNull("Was not able to build the TabModelSelector", selector1);
         Assert.assertEquals("Unexpected model index", 0, manager.getIndexForWindow(activity1));
     }
 
@@ -276,13 +263,11 @@
 
         ChromeActivity activity0 = buildActivity();
         ChromeActivity activity1 = buildActivity();
-        Pair<Integer, TabModelSelector> assignment0 = requestSelector(activity0, 0);
-        Pair<Integer, TabModelSelector> assignment1 = requestSelector(activity1, 1);
+        TabModelSelector selector0 = requestSelector(activity0, 0);
+        TabModelSelector selector1 = requestSelector(activity1, 1);
 
-        Assert.assertEquals(0, assignment0.first.intValue());
-        Assert.assertEquals(1, assignment1.first.intValue());
-        Assert.assertNotNull("Was not able to build the TabModelSelector", assignment0.second);
-        Assert.assertNotNull("Was not able to build the TabModelSelector", assignment1.second);
+        Assert.assertNotNull("Was not able to build the TabModelSelector", selector0);
+        Assert.assertNotNull("Was not able to build the TabModelSelector", selector1);
         Assert.assertEquals("Unexpected model index", 0, manager.getIndexForWindow(activity0));
         Assert.assertEquals("Unexpected model index", 1, manager.getIndexForWindow(activity1));
 
@@ -292,10 +277,9 @@
                 manager.getIndexForWindow(activity1));
 
         ChromeActivity activity2 = buildActivity();
-        Pair<Integer, TabModelSelector> assignment2 = requestSelector(activity2, 1);
+        TabModelSelector selector2 = requestSelector(activity2, 1);
 
-        Assert.assertEquals(1, assignment2.first.intValue());
-        Assert.assertNotNull("Was not able to build the TabModelSelector", assignment2.second);
+        Assert.assertNotNull("Was not able to build the TabModelSelector", selector2);
         Assert.assertEquals("Unexpected model index", 0, manager.getIndexForWindow(activity0));
         Assert.assertEquals("Unexpected model index", 1, manager.getIndexForWindow(activity2));
     }
@@ -312,10 +296,8 @@
 
         ChromeActivity activity0 = buildActivity();
         ChromeActivity activity1 = buildActivity();
-        Pair<Integer, TabModelSelector> assignment0 = requestSelector(activity0, 0);
-        Pair<Integer, TabModelSelector> assignment1 = requestSelector(activity1, 1);
-        MockTabModelSelector selector0 = (MockTabModelSelector) assignment0.second;
-        MockTabModelSelector selector1 = (MockTabModelSelector) assignment1.second;
+        MockTabModelSelector selector0 = requestSelector(activity0, 0);
+        MockTabModelSelector selector1 = requestSelector(activity1, 1);
         Tab tab1 = selector0.addMockTab();
         Tab tab2 = selector1.addMockIncognitoTab();
 
@@ -350,10 +332,8 @@
 
         ChromeActivity activity0 = buildActivity();
         ChromeActivity activity1 = buildActivity();
-        Pair<Integer, TabModelSelector> assignment0 = requestSelector(activity0, 0);
-        Pair<Integer, TabModelSelector> assignment1 = requestSelector(activity1, 1);
-        MockTabModelSelector selector0 = (MockTabModelSelector) assignment0.second;
-        MockTabModelSelector selector1 = (MockTabModelSelector) assignment1.second;
+        MockTabModelSelector selector0 = requestSelector(activity0, 0);
+        MockTabModelSelector selector1 = requestSelector(activity1, 1);
         Tab tab1 = selector0.addMockTab();
         Tab tab2 = selector1.addMockIncognitoTab();
 
@@ -388,10 +368,8 @@
 
         ChromeActivity activity0 = buildActivity();
         ChromeActivity activity1 = buildActivity();
-        Pair<Integer, TabModelSelector> assignment0 = requestSelector(activity0, 0);
-        Pair<Integer, TabModelSelector> assignment1 = requestSelector(activity1, 1);
-        MockTabModelSelector selector0 = (MockTabModelSelector) assignment0.second;
-        MockTabModelSelector selector1 = (MockTabModelSelector) assignment1.second;
+        MockTabModelSelector selector0 = requestSelector(activity0, 0);
+        MockTabModelSelector selector1 = requestSelector(activity1, 1);
         Tab tab1 = selector0.addMockTab();
         Tab tab2 = selector1.addMockTab();
         Tab tab3 = selector0.addMockIncognitoTab();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationFaviconTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationFaviconTest.java
index 9fbb186..f0405bf 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationFaviconTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationFaviconTest.java
@@ -29,6 +29,7 @@
 import org.chromium.components.browser_ui.media.MediaNotificationInfo;
 import org.chromium.components.favicon.IconType;
 import org.chromium.components.favicon.LargeIconBridge;
+import org.chromium.url.GURL;
 
 /**
  * Test of media notifications to ensure that the favicon is displayed on normal devices and
@@ -57,8 +58,8 @@
         }
 
         @Override
-        public boolean getLargeIconForStringUrl(
-                final String pageUrl, int desiredSizePx, final LargeIconCallback callback) {
+        public boolean getLargeIconForUrl(
+                final GURL pageUrl, int desiredSizePx, final LargeIconCallback callback) {
             mGetIconCalledAtLeastOnce = true;
             mCallback = callback;
             return true;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/payments/PaymentRequestParamsBuilder.java b/chrome/android/junit/src/org/chromium/chrome/browser/payments/PaymentRequestParamsBuilder.java
index a0a87f7..f607aaa 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/payments/PaymentRequestParamsBuilder.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/payments/PaymentRequestParamsBuilder.java
@@ -36,6 +36,8 @@
 import org.chromium.payments.mojom.PaymentRequest;
 import org.chromium.payments.mojom.PaymentRequestClient;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.url.GURL;
+import org.chromium.url.JUnitTestGURLs;
 import org.chromium.url.Origin;
 
 import java.lang.ref.WeakReference;
@@ -69,7 +71,9 @@
         mPaymentUiService = paymentUiService;
         mJourneyLogger = Mockito.mock(JourneyLogger.class);
         mWebContents = Mockito.mock(WebContents.class);
-        Mockito.doReturn("https://top.level.origin").when(mWebContents).getLastCommittedUrl();
+        Mockito.doReturn(JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_1))
+                .when(mWebContents)
+                .getLastCommittedUrl();
         mRenderFrameHost = Mockito.mock(RenderFrameHost.class);
         Mockito.doReturn("https://frame.origin").when(mRenderFrameHost).getLastCommittedURL();
         Origin origin = Mockito.mock(Origin.class);
@@ -144,7 +148,7 @@
     }
 
     @Override
-    public boolean isOriginSecure(String url) {
+    public boolean isOriginSecure(GURL url) {
         return true;
     }
 
@@ -164,7 +168,7 @@
     }
 
     @Override
-    public boolean isOriginAllowedToUseWebPaymentApis(String url) {
+    public boolean isOriginAllowedToUseWebPaymentApis(GURL url) {
         return true;
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImplTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImplTest.java
index c2d7144..da7027d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImplTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImplTest.java
@@ -8,6 +8,8 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
+import android.app.Activity;
+
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -15,6 +17,7 @@
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
@@ -35,6 +38,8 @@
 @Config(manifest = Config.NONE)
 public class TabModelSelectorImplTest {
     @Mock
+    TabPersistencePolicy mMockTabPersistencePolicy;
+    @Mock
     TabModelFilterFactory mMockTabModelFilterFactory;
     @Mock
     TabContentManager mMockTabContentManager;
@@ -45,22 +50,27 @@
 
     private TabModelSelectorImpl mTabModelSelector;
     private MockTabCreatorManager mTabCreatorManager;
+    private Activity mActivity;
 
     @Before
     public void setUp() {
+        mActivity = Robolectric.buildActivity(Activity.class).setup().get();
         MockitoAnnotations.initMocks(this);
 
+        doReturn(TabPersistentStore.SAVED_STATE_FILE_PREFIX)
+                .when(mMockTabPersistencePolicy)
+                .getStateFileName();
+
         doReturn(mock(TabModelFilter.class))
                 .when(mMockTabModelFilterFactory)
                 .createTabModelFilter(any());
         mTabCreatorManager = new MockTabCreatorManager();
         AsyncTabParamsManager realAsyncTabParamsManager =
                 AsyncTabParamsManagerFactory.createAsyncTabParamsManager();
-        mTabModelSelector = new TabModelSelectorImpl(null, mTabCreatorManager,
-                mMockTabModelFilterFactory, mNextTabPolicySupplier, realAsyncTabParamsManager,
-                /*supportUndo=*/false,
+        mTabModelSelector = new TabModelSelectorImpl(mActivity, null, mTabCreatorManager,
+                mMockTabPersistencePolicy, mMockTabModelFilterFactory, mNextTabPolicySupplier,
+                realAsyncTabParamsManager, /*supportUndo=*/false,
                 /*isTabbedActivity=*/false, /*startIncognito=*/false);
-        mTabModelSelector.setTabPersistentStoreSupplier(() -> null);
         mTabCreatorManager.initialize(mTabModelSelector);
         mTabModelSelector.onNativeLibraryReadyInternal(mMockTabContentManager,
                 new MockTabModel(false, null), new MockTabModel(true, null));
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index b755688a..68079aa3 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2888,13 +2888,13 @@
       "android/service_tab_launcher.h",
       "android/shortcut_helper.cc",
       "android/shortcut_helper.h",
+      "android/signin/signin_bridge.cc",
+      "android/signin/signin_bridge.h",
       "android/signin/signin_manager_android.cc",
       "android/signin/signin_manager_android.h",
       "android/signin/signin_metrics_utils.cc",
       "android/signin/signin_promo_util_android.cc",
       "android/signin/signin_promo_util_android.h",
-      "android/signin/signin_utils.cc",
-      "android/signin/signin_utils.h",
       "android/signin/unified_consent_service_bridge.cc",
       "android/signin/web_signin_bridge.cc",
       "android/signin/web_signin_bridge.h",
diff --git a/chrome/browser/android/signin/signin_utils.cc b/chrome/browser/android/signin/signin_bridge.cc
similarity index 65%
rename from chrome/browser/android/signin/signin_utils.cc
rename to chrome/browser/android/signin/signin_bridge.cc
index e1f6d9f..b5b1f5a 100644
--- a/chrome/browser/android/signin/signin_utils.cc
+++ b/chrome/browser/android/signin/signin_bridge.cc
@@ -2,30 +2,28 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/android/signin/signin_utils.h"
+#include "chrome/browser/android/signin/signin_bridge.h"
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
+#include "chrome/android/chrome_jni_headers/SigninBridge_jni.h"
 #include "chrome/android/chrome_jni_headers/SigninUtils_jni.h"
 #include "ui/android/window_android.h"
 
 using base::android::JavaParamRef;
 
 // static
-void SigninUtils::OpenAccountManagementScreen(
+void SigninBridge::OpenAccountManagementScreen(
     ui::WindowAndroid* window,
-    signin::GAIAServiceType service_type,
-    const std::string& email) {
+    signin::GAIAServiceType service_type) {
   DCHECK(window);
   JNIEnv* env = base::android::AttachCurrentThread();
-  Java_SigninUtils_openAccountManagementScreen(
-      env, window->GetJavaObject(), static_cast<int>(service_type),
-      email.empty() ? nullptr
-                    : base::android::ConvertUTF8ToJavaString(env, email));
+  Java_SigninBridge_openAccountManagementScreen(env, window->GetJavaObject(),
+                                                static_cast<int>(service_type));
 }
 
-void SigninUtils::OpenAccountPickerBottomSheet(
+void SigninBridge::OpenAccountPickerBottomSheet(
     ui::WindowAndroid* window,
     const std::string& continue_url) {
   DCHECK(window);
diff --git a/chrome/browser/android/signin/signin_utils.h b/chrome/browser/android/signin/signin_bridge.h
similarity index 62%
rename from chrome/browser/android/signin/signin_utils.h
rename to chrome/browser/android/signin/signin_bridge.h
index e18d35e..75fd461 100644
--- a/chrome/browser/android/signin/signin_utils.h
+++ b/chrome/browser/android/signin/signin_bridge.h
@@ -2,32 +2,29 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_ANDROID_SIGNIN_SIGNIN_UTILS_H_
-#define CHROME_BROWSER_ANDROID_SIGNIN_SIGNIN_UTILS_H_
+#ifndef CHROME_BROWSER_ANDROID_SIGNIN_SIGNIN_BRIDGE_H_
+#define CHROME_BROWSER_ANDROID_SIGNIN_SIGNIN_BRIDGE_H_
 
 #include <string>
 
-#include "base/macros.h"
 #include "components/signin/core/browser/signin_header_helper.h"
 
 namespace ui {
 class WindowAndroid;
 }
 
-// The glue for Java-side implementation of SigninUtils.
-class SigninUtils {
+// The glue for Java-side implementation of SigninBridge.
+class SigninBridge {
  public:
   // Opens the account management screen.
   static void OpenAccountManagementScreen(ui::WindowAndroid* profile,
-                                          signin::GAIAServiceType service_type,
-                                          const std::string& email);
+                                          signin::GAIAServiceType service_type);
 
   // Opens the account picker bottomsheet
   static void OpenAccountPickerBottomSheet(ui::WindowAndroid* window,
                                            const std::string& continue_url);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(SigninUtils);
+  SigninBridge() = delete;
 };
 
-#endif  // CHROME_BROWSER_ANDROID_SIGNIN_SIGNIN_UTILS_H_
+#endif  // CHROME_BROWSER_ANDROID_SIGNIN_SIGNIN_BRIDGE_H_
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index e2eb5c2..b60f2d38 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -1422,6 +1422,7 @@
 
   return profile_manager->GetProfile(
       GetStartupProfilePath(profile_manager->user_data_dir(),
+                            /*current_directory=*/base::FilePath(),
                             *base::CommandLine::ForCurrentProcess()));
 }
 
diff --git a/chrome/browser/apps/app_service/extension_apps_chromeos.cc b/chrome/browser/apps/app_service/extension_apps_chromeos.cc
index ab3648b6..ea440ea1 100644
--- a/chrome/browser/apps/app_service/extension_apps_chromeos.cc
+++ b/chrome/browser/apps/app_service/extension_apps_chromeos.cc
@@ -173,6 +173,10 @@
         policy::policy_prefs::kSystemFeaturesDisableList,
         base::BindRepeating(&ExtensionAppsBase::OnSystemFeaturesPrefChanged,
                             GetWeakPtr()));
+    local_state_pref_change_registrar_.Add(
+        policy::policy_prefs::kSystemFeaturesDisableMode,
+        base::BindRepeating(&ExtensionAppsBase::OnSystemFeaturesPrefChanged,
+                            GetWeakPtr()));
     OnSystemFeaturesPrefChanged();
   }
 }
@@ -504,13 +508,21 @@
     return;
   }
 
-  UpdateAppDisabledState(disabled_system_features_pref,
-                         policy::SystemFeature::kCamera,
-                         extension_misc::kCameraAppId);
+  const bool is_pref_disabled_mode_hidden =
+      local_state->GetString(
+          policy::policy_prefs::kSystemFeaturesDisableMode) ==
+      policy::kHiddenDisableMode;
+  const bool is_disabled_mode_changed =
+      (is_pref_disabled_mode_hidden != is_disabled_apps_mode_hidden_);
+  is_disabled_apps_mode_hidden_ = is_pref_disabled_mode_hidden;
+
+  UpdateAppDisabledState(
+      disabled_system_features_pref, policy::SystemFeature::kCamera,
+      extension_misc::kCameraAppId, is_disabled_mode_changed);
 
   UpdateAppDisabledState(disabled_system_features_pref,
                          policy::SystemFeature::kWebStore,
-                         extensions::kWebStoreAppId);
+                         extensions::kWebStoreAppId, is_disabled_mode_changed);
 }
 
 bool ExtensionAppsChromeOs::Accepts(const extensions::Extension* extension) {
@@ -529,10 +541,10 @@
 apps::mojom::AppPtr ExtensionAppsChromeOs::Convert(
     const extensions::Extension* extension,
     apps::mojom::Readiness readiness) {
-  apps::mojom::AppPtr app =
-      ConvertImpl(extension, base::Contains(disabled_apps_, extension->id())
-                                 ? apps::mojom::Readiness::kDisabledByPolicy
-                                 : readiness);
+  const bool is_app_disabled = base::Contains(disabled_apps_, extension->id());
+  apps::mojom::AppPtr app = ConvertImpl(
+      extension,
+      is_app_disabled ? apps::mojom::Readiness::kDisabledByPolicy : readiness);
   bool paused = paused_apps_.IsPaused(extension->id());
   app->icon_key =
       icon_key_factory().MakeIconKey(GetIconEffects(extension, paused));
@@ -543,6 +555,12 @@
   app->paused = paused ? apps::mojom::OptionalBool::kTrue
                        : apps::mojom::OptionalBool::kFalse;
 
+  if (is_app_disabled && is_disabled_apps_mode_hidden_) {
+    app->show_in_launcher = apps::mojom::OptionalBool::kFalse;
+    app->show_in_search = apps::mojom::OptionalBool::kFalse;
+    app->show_in_shelf = apps::mojom::OptionalBool::kFalse;
+  }
+
   return app;
 }
 
@@ -712,7 +730,8 @@
 void ExtensionAppsChromeOs::UpdateAppDisabledState(
     const base::ListValue* disabled_system_features_pref,
     int feature,
-    const std::string& app_id) {
+    const std::string& app_id,
+    bool is_disabled_mode_changed) {
   const bool is_disabled =
       base::Contains(*disabled_system_features_pref, base::Value(feature));
   // Sometimes the policy is updated before the app is installed, so this way
@@ -720,7 +739,8 @@
   // and the app will be published with the correct readiness upon its
   // installation.
   const bool should_publish =
-      (base::Contains(disabled_apps_, app_id) != is_disabled);
+      (base::Contains(disabled_apps_, app_id) != is_disabled) ||
+      is_disabled_mode_changed;
 
   if (is_disabled) {
     disabled_apps_.insert(app_id);
diff --git a/chrome/browser/apps/app_service/extension_apps_chromeos.h b/chrome/browser/apps/app_service/extension_apps_chromeos.h
index 371a45b..72f26d87 100644
--- a/chrome/browser/apps/app_service/extension_apps_chromeos.h
+++ b/chrome/browser/apps/app_service/extension_apps_chromeos.h
@@ -153,7 +153,8 @@
   void UpdateAppDisabledState(
       const base::ListValue* disabled_system_features_pref,
       int feature,
-      const std::string& app_id);
+      const std::string& app_id,
+      bool is_disabled_mode_changed);
 
   apps::InstanceRegistry* instance_registry_;
   ScopedObserver<extensions::AppWindowRegistry,
@@ -164,6 +165,11 @@
 
   std::set<std::string> disabled_apps_;
 
+  // Boolean signifying whether the preferred user experience mode of disabled
+  // apps is hidden (true) or blocked (false). The value comes from user pref
+  // and is set by updating SystemDisabledMode policy.
+  bool is_disabled_apps_mode_hidden_ = false;
+
   std::map<extensions::AppWindow*, aura::Window*> app_window_to_aura_window_;
 
   ArcAppListPrefs* arc_prefs_ = nullptr;
diff --git a/chrome/browser/autofill/autofill_server_browsertest.cc b/chrome/browser/autofill/autofill_server_browsertest.cc
index 9b6a0aa2..973c059f5 100644
--- a/chrome/browser/autofill/autofill_server_browsertest.cc
+++ b/chrome/browser/autofill/autofill_server_browsertest.cc
@@ -152,12 +152,6 @@
     WaitForPersonalDataManagerToBeLoaded(browser()->profile());
   }
 
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    // Enable finch experiment for sending field metadata.
-    command_line->AppendSwitchASCII(::switches::kForceFieldTrials,
-                                    "AutofillFieldMetadata/Enabled/");
-  }
-
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -194,10 +188,10 @@
   auto* query_form = query.add_forms();
   query_form->set_signature(15916856893790176210U);
 
-  test::FillQueryField(query_form->add_fields(), 2594484045U, "one", "text");
-  test::FillQueryField(query_form->add_fields(), 2750915947U, "two", "text");
-  test::FillQueryField(query_form->add_fields(), 3494787134U, "three", "text");
-  test::FillQueryField(query_form->add_fields(), 1236501728U, "four", "text");
+  query_form->add_fields()->set_signature(2594484045U);
+  query_form->add_fields()->set_signature(2750915947U);
+  query_form->add_fields()->set_signature(3494787134U);
+  query_form->add_fields()->set_signature(1236501728U);
 
   std::string expected_query_string;
   ASSERT_TRUE(query.SerializeToString(&expected_query_string));
@@ -226,21 +220,21 @@
   } else {
     upload->set_data_present("1f7e0003780000080004");
   }
-  upload->set_action_signature(15724779818122431245U);
-  upload->set_form_name("test_form");
   upload->set_passwords_revealed(false);
   upload->set_submission_event(
       AutofillUploadContents_SubmissionIndicatorEvent_HTML_FORM_SUBMISSION);
   upload->set_has_form_tag(true);
 
-  test::FillUploadField(upload->add_field(), 2594484045U, "one", "text",
+  // Enabling raw form data uploading (e.g., field name) is too complicated in
+  // this test. So, don't expect it in the upload.
+  test::FillUploadField(upload->add_field(), 2594484045U, nullptr, nullptr,
                         nullptr, 2U);
-  test::FillUploadField(upload->add_field(), 2750915947U, "two", "text", "off",
-                        2U);
-  test::FillUploadField(upload->add_field(), 3494787134U, "three", "text",
+  test::FillUploadField(upload->add_field(), 2750915947U, nullptr, nullptr,
                         nullptr, 2U);
-  test::FillUploadField(upload->add_field(), 1236501728U, "four", "text", "off",
-                        2U);
+  test::FillUploadField(upload->add_field(), 3494787134U, nullptr, nullptr,
+                        nullptr, 2U);
+  test::FillUploadField(upload->add_field(), 1236501728U, nullptr, nullptr,
+                        nullptr, 2U);
 
   std::string expected_upload_string;
   ASSERT_TRUE(request.SerializeToString(&expected_upload_string));
@@ -271,10 +265,9 @@
   auto* query_form = query.add_forms();
   query_form->set_signature(8900697631820480876U);
 
-  test::FillQueryField(query_form->add_fields(), 2594484045U, "one", "text");
-  test::FillQueryField(query_form->add_fields(), 2750915947U, "two", "text");
-  test::FillQueryField(query_form->add_fields(), 116843943U, "three",
-                       "password");
+  query_form->add_fields()->set_signature(2594484045U);
+  query_form->add_fields()->set_signature(2750915947U);
+  query_form->add_fields()->set_signature(116843943U);
 
   std::string expected_query_string;
   ASSERT_TRUE(query.SerializeToString(&expected_query_string));
diff --git a/chrome/browser/background_fetch/background_fetch_browsertest.cc b/chrome/browser/background_fetch/background_fetch_browsertest.cc
index 7d5ef476..e4b82d5 100644
--- a/chrome/browser/background_fetch/background_fetch_browsertest.cc
+++ b/chrome/browser/background_fetch/background_fetch_browsertest.cc
@@ -601,7 +601,7 @@
 
 IN_PROC_BROWSER_TEST_F(
     BackgroundFetchBrowserTest,
-    OfflineItemCollection_VerifyResourceDownloadedWhenDownloadTotalLargerThanActualSize) {
+    DISABLED_OfflineItemCollection_VerifyResourceDownloadedWhenDownloadTotalLargerThanActualSize) {
   // Starts a Background Fetch for a single to-be-downloaded file and waits for
   // the fetch to be registered with the offline items collection.
   std::vector<OfflineItem> items;
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index fee4d5f..48ac613 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -446,7 +446,7 @@
   base::FilePath user_data_dir =
       g_browser_process->profile_manager()->user_data_dir();
   base::FilePath startup_profile_dir =
-      GetStartupProfilePath(user_data_dir, command_line);
+      GetStartupProfilePath(user_data_dir, current_directory, command_line);
 
   StartupBrowserCreator::ProcessCommandLineAlreadyRunning(
       command_line, current_directory, startup_profile_dir);
diff --git a/chrome/browser/chromeos/eol_notification.cc b/chrome/browser/chromeos/eol_notification.cc
index b82489f..426e189 100644
--- a/chrome/browser/chromeos/eol_notification.cc
+++ b/chrome/browser/chromeos/eol_notification.cc
@@ -46,29 +46,6 @@
   return eol_date - base::TimeDelta::FromDays(kSecondWarningDaysInAdvance);
 }
 
-base::string16 FormatMonthAndYearWithOffset(base::Time eol_date) {
-  // TODO(crbug/998983): This is not the ideal way to correct months shifts.
-  // A follow up CL will modify base/i18n/time_formatting.h so that
-  // setting the time can be formatted based off UTC rather than only local.
-  //
-  // If the EOL date is on the first day of the month, then notifications with
-  // different month names may be shown to different users by
-  // base::TimeFormatMonthAndYear(), depending on their time zone.  There are
-  // devices in Goldeneye with EOL dates on the first and last day of the month.
-  // Since only the month is shown, the day is set to the 15th to prevent any
-  // forward or backward month shifts.
-  constexpr int kApproxMidPointDayInMonth = 15;
-
-  base::Time adjusted_date;
-  base::Time::Exploded exploded;
-  eol_date.UTCExplode(&exploded);
-  exploded.day_of_month = kApproxMidPointDayInMonth;
-  if (!base::Time::FromUTCExploded(exploded, &adjusted_date)) {
-    return base::TimeFormatMonthAndYear(eol_date);
-  }
-  return base::TimeFormatMonthAndYear(adjusted_date);
-}
-
 }  // namespace
 
 // static
@@ -150,8 +127,10 @@
     // Notifies user that updates will stop occurring at a month and year.
     notification = ash::CreateSystemNotification(
         message_center::NOTIFICATION_TYPE_SIMPLE, kEolNotificationId,
-        l10n_util::GetStringFUTF16(IDS_PENDING_EOL_NOTIFICATION_TITLE,
-                                   FormatMonthAndYearWithOffset(eol_date)),
+        l10n_util::GetStringFUTF16(
+            IDS_PENDING_EOL_NOTIFICATION_TITLE,
+            TimeFormatMonthAndYear(eol_date,
+                                   /*time_zone=*/icu::TimeZone::getGMT())),
         l10n_util::GetStringFUTF16(IDS_PENDING_EOL_NOTIFICATION_MESSAGE,
                                    ui::GetChromeOSDeviceName()),
         base::string16() /* display_source */, GURL(kEolNotificationId),
diff --git a/chrome/browser/chromeos/full_restore/app_launch_handler_browsertest.cc b/chrome/browser/chromeos/full_restore/app_launch_handler_browsertest.cc
deleted file mode 100644
index bc7a2e3..0000000
--- a/chrome/browser/chromeos/full_restore/app_launch_handler_browsertest.cc
+++ /dev/null
@@ -1,300 +0,0 @@
-// Copyright 2021 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/full_restore/app_launch_handler.h"
-
-#include "ash/public/cpp/ash_features.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/timer/timer.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
-#include "chrome/browser/web_applications/components/web_app_id.h"
-#include "chrome/browser/web_applications/components/web_application_info.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "components/full_restore/app_launch_info.h"
-#include "components/full_restore/full_restore_read_handler.h"
-#include "components/full_restore/full_restore_save_handler.h"
-#include "components/full_restore/full_restore_utils.h"
-#include "components/services/app_service/public/mojom/types.mojom.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/test_utils.h"
-#include "ui/aura/window.h"
-#include "ui/base/window_open_disposition.h"
-#include "ui/display/types/display_constants.h"
-
-namespace chromeos {
-namespace full_restore {
-
-namespace {
-
-const char kAppId[] = "mldnpnnoiloahfhddhobgjeophloidmo";
-const int32_t kId = 100;
-
-}  // namespace
-
-class AppLaunchHandlerBrowserTest : public InProcessBrowserTest {
- public:
-  AppLaunchHandlerBrowserTest() {
-    scoped_feature_list_.InitAndEnableFeature(ash::features::kFullRestore);
-  }
-  ~AppLaunchHandlerBrowserTest() override = default;
-
-  void WaitForAppLaunchInfoSaved() {
-    ::full_restore::FullRestoreSaveHandler* save_handler =
-        ::full_restore::FullRestoreSaveHandler::GetInstance();
-    base::OneShotTimer* timer = save_handler->GetTimerForTesting();
-    EXPECT_TRUE(timer->IsRunning());
-
-    // Simulate timeout, and the launch info is saved.
-    timer->FireNow();
-    content::RunAllTasksUntilIdle();
-  }
-
-  void CreateWebApp() {
-    auto web_application_info = std::make_unique<WebApplicationInfo>();
-    web_application_info->start_url = GURL("https://example.org");
-    web_app::AppId app_id =
-        web_app::InstallWebApp(profile(), std::move(web_application_info));
-
-    // Wait for app service to see the newly installed app.
-    auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile());
-    proxy->FlushMojoCallsForTesting();
-  }
-
-  bool FindWebAppWindow() {
-    for (auto* browser : *BrowserList::GetInstance()) {
-      aura::Window* window = browser->window()->GetNativeWindow();
-      if (window->GetProperty(::full_restore::kRestoreWindowIdKey) == kId)
-        return true;
-    }
-    return false;
-  }
-
-  Profile* profile() { return browser()->profile(); }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(AppLaunchHandlerBrowserTest, NotLaunchBrowser) {
-  // Add app launch info.
-  ::full_restore::SaveAppLaunchInfo(
-      profile()->GetPath(), std::make_unique<::full_restore::AppLaunchInfo>(
-                                extension_misc::kChromeAppId, kId));
-
-  WaitForAppLaunchInfoSaved();
-
-  size_t count = BrowserList::GetInstance()->size();
-
-  // Create AppLaunchHandler, and set should restore.
-  auto app_launch_handler = std::make_unique<AppLaunchHandler>(profile());
-  app_launch_handler->SetShouldRestore();
-
-  content::RunAllTasksUntilIdle();
-
-  // Verify there is no new browser launched.
-  EXPECT_EQ(count, BrowserList::GetInstance()->size());
-}
-
-IN_PROC_BROWSER_TEST_F(AppLaunchHandlerBrowserTest, RestoreAndAddApp) {
-  // Add app launch info.
-  ::full_restore::SaveAppLaunchInfo(
-      profile()->GetPath(),
-      std::make_unique<::full_restore::AppLaunchInfo>(
-          kAppId, kId, apps::mojom::LaunchContainer::kLaunchContainerWindow,
-          WindowOpenDisposition::NEW_WINDOW, display::kDefaultDisplayId,
-          std::vector<base::FilePath>{}, nullptr));
-
-  WaitForAppLaunchInfoSaved();
-
-  // Create AppLaunchHandler, and set should restore.
-  auto app_launch_handler = std::make_unique<AppLaunchHandler>(profile());
-  app_launch_handler->SetShouldRestore();
-
-  CreateWebApp();
-
-  content::RunAllTasksUntilIdle();
-
-  EXPECT_TRUE(FindWebAppWindow());
-}
-
-IN_PROC_BROWSER_TEST_F(AppLaunchHandlerBrowserTest, AddAppAndRestore) {
-  // Add app launch info.
-  ::full_restore::SaveAppLaunchInfo(
-      profile()->GetPath(),
-      std::make_unique<::full_restore::AppLaunchInfo>(
-          kAppId, kId, apps::mojom::LaunchContainer::kLaunchContainerWindow,
-          WindowOpenDisposition::NEW_WINDOW, display::kDefaultDisplayId,
-          std::vector<base::FilePath>{}, nullptr));
-
-  WaitForAppLaunchInfoSaved();
-
-  // Create AppLaunchHandler.
-  auto app_launch_handler = std::make_unique<AppLaunchHandler>(profile());
-
-  CreateWebApp();
-
-  // Set should restore
-  app_launch_handler->SetShouldRestore();
-
-  content::RunAllTasksUntilIdle();
-
-  EXPECT_TRUE(FindWebAppWindow());
-}
-
-IN_PROC_BROWSER_TEST_F(AppLaunchHandlerBrowserTest, NotRestore) {
-  // Add app launch infos.
-  ::full_restore::SaveAppLaunchInfo(
-      profile()->GetPath(), std::make_unique<::full_restore::AppLaunchInfo>(
-                                extension_misc::kChromeAppId, kId));
-  ::full_restore::SaveAppLaunchInfo(
-      profile()->GetPath(),
-      std::make_unique<::full_restore::AppLaunchInfo>(
-          kAppId, kId, apps::mojom::LaunchContainer::kLaunchContainerWindow,
-          WindowOpenDisposition::NEW_WINDOW, display::kDefaultDisplayId,
-          std::vector<base::FilePath>{}, nullptr));
-
-  WaitForAppLaunchInfoSaved();
-
-  size_t count = BrowserList::GetInstance()->size();
-
-  // Create AppLaunchHandler.
-  auto app_launch_handler = std::make_unique<AppLaunchHandler>(profile());
-  app_launch_handler->LauncherBrowserWhenReady();
-
-  CreateWebApp();
-
-  content::RunAllTasksUntilIdle();
-
-  // Verify there is no new browser launched.
-  EXPECT_EQ(count, BrowserList::GetInstance()->size());
-  EXPECT_FALSE(FindWebAppWindow());
-}
-
-IN_PROC_BROWSER_TEST_F(AppLaunchHandlerBrowserTest, RestoreAndLaunchBrowser) {
-  size_t count = BrowserList::GetInstance()->size();
-
-  // Add the chrome browser launch info.
-  ::full_restore::SaveAppLaunchInfo(
-      profile()->GetPath(), std::make_unique<::full_restore::AppLaunchInfo>(
-                                extension_misc::kChromeAppId, kId));
-
-  WaitForAppLaunchInfoSaved();
-
-  // Create AppLaunchHandler.
-  auto app_launch_handler = std::make_unique<AppLaunchHandler>(profile());
-
-  // Set should restore
-  app_launch_handler->SetShouldRestore();
-  content::RunAllTasksUntilIdle();
-
-  app_launch_handler->LauncherBrowserWhenReady();
-  content::RunAllTasksUntilIdle();
-
-  // Verify there is new browser launched.
-  EXPECT_EQ(count + 1, BrowserList::GetInstance()->size());
-}
-
-IN_PROC_BROWSER_TEST_F(AppLaunchHandlerBrowserTest, LaunchBrowserAndRestore) {
-  size_t count = BrowserList::GetInstance()->size();
-
-  // Add the chrome browser launch info.
-  ::full_restore::SaveAppLaunchInfo(
-      profile()->GetPath(), std::make_unique<::full_restore::AppLaunchInfo>(
-                                extension_misc::kChromeAppId, kId));
-
-  WaitForAppLaunchInfoSaved();
-
-  // Create AppLaunchHandler.
-  auto app_launch_handler = std::make_unique<AppLaunchHandler>(profile());
-
-  app_launch_handler->LauncherBrowserWhenReady();
-  content::RunAllTasksUntilIdle();
-
-  // Verify there is no new browser launched.
-  EXPECT_EQ(count, BrowserList::GetInstance()->size());
-
-  // Set should restore
-  app_launch_handler->SetShouldRestore();
-  content::RunAllTasksUntilIdle();
-
-  // Verify there is new browser launched.
-  EXPECT_EQ(count + 1, BrowserList::GetInstance()->size());
-}
-
-IN_PROC_BROWSER_TEST_F(AppLaunchHandlerBrowserTest,
-                       RestoreAndLaunchBrowserAndAddApp) {
-  size_t count = BrowserList::GetInstance()->size();
-
-  // Add app launch infos.
-  ::full_restore::SaveAppLaunchInfo(
-      profile()->GetPath(), std::make_unique<::full_restore::AppLaunchInfo>(
-                                extension_misc::kChromeAppId, kId));
-  ::full_restore::SaveAppLaunchInfo(
-      profile()->GetPath(),
-      std::make_unique<::full_restore::AppLaunchInfo>(
-          kAppId, kId, apps::mojom::LaunchContainer::kLaunchContainerWindow,
-          WindowOpenDisposition::NEW_WINDOW, display::kDefaultDisplayId,
-          std::vector<base::FilePath>{}, nullptr));
-
-  WaitForAppLaunchInfoSaved();
-
-  // Create AppLaunchHandler, and set should restore.
-  auto app_launch_handler = std::make_unique<AppLaunchHandler>(profile());
-  app_launch_handler->SetShouldRestore();
-  content::RunAllTasksUntilIdle();
-
-  app_launch_handler->LauncherBrowserWhenReady();
-  content::RunAllTasksUntilIdle();
-
-  CreateWebApp();
-  content::RunAllTasksUntilIdle();
-
-  // Verify there is new browser launched.
-  EXPECT_EQ(count + 2, BrowserList::GetInstance()->size());
-  EXPECT_TRUE(FindWebAppWindow());
-}
-
-IN_PROC_BROWSER_TEST_F(AppLaunchHandlerBrowserTest,
-                       LaunchBrowserAndAddAppAndRestore) {
-  size_t count = BrowserList::GetInstance()->size();
-
-  // Add app launch infos.
-  ::full_restore::SaveAppLaunchInfo(
-      profile()->GetPath(), std::make_unique<::full_restore::AppLaunchInfo>(
-                                extension_misc::kChromeAppId, kId));
-  ::full_restore::SaveAppLaunchInfo(
-      profile()->GetPath(),
-      std::make_unique<::full_restore::AppLaunchInfo>(
-          kAppId, kId, apps::mojom::LaunchContainer::kLaunchContainerWindow,
-          WindowOpenDisposition::NEW_WINDOW, display::kDefaultDisplayId,
-          std::vector<base::FilePath>{}, nullptr));
-
-  WaitForAppLaunchInfoSaved();
-
-  // Create AppLaunchHandler.
-  auto app_launch_handler = std::make_unique<AppLaunchHandler>(profile());
-
-  app_launch_handler->LauncherBrowserWhenReady();
-  content::RunAllTasksUntilIdle();
-
-  CreateWebApp();
-  content::RunAllTasksUntilIdle();
-
-  // Set should restore
-  app_launch_handler->SetShouldRestore();
-  content::RunAllTasksUntilIdle();
-
-  // Verify there is new browser launched.
-  EXPECT_EQ(count + 2, BrowserList::GetInstance()->size());
-  EXPECT_TRUE(FindWebAppWindow());
-}
-
-}  // namespace full_restore
-}  // namespace chromeos
diff --git a/chrome/browser/devtools/devtools_sanity_browsertest.cc b/chrome/browser/devtools/devtools_sanity_browsertest.cc
index 4b7891c..69ea9c7e 100644
--- a/chrome/browser/devtools/devtools_sanity_browsertest.cc
+++ b/chrome/browser/devtools/devtools_sanity_browsertest.cc
@@ -1863,7 +1863,13 @@
   std::unique_ptr<DevToolsWindowCreationObserver> observer_;
 };
 
-IN_PROC_BROWSER_TEST_F(DevToolsAutoOpenerTest, TestAutoOpenForTabs) {
+// TODO(https://crbug.com/1167158): Flaky on debug builds.
+#if defined(NDEBUG)
+#define MAYBE_TestAutoOpenForTabs DISABLED_TestAutoOpenForTabs
+#else
+#define MAYBE_TestAutoOpenForTabs TestAutoOpenForTabs
+#endif
+IN_PROC_BROWSER_TEST_F(DevToolsAutoOpenerTest, MAYBE_TestAutoOpenForTabs) {
   {
     DevToolsWindowCreationObserver observer;
     AddTabAtIndexToBrowser(browser(), 0, GURL("about:blank"),
diff --git a/chrome/browser/extensions/activity_log/activity_log_unittest.cc b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
index 954b28c..8baedd9 100644
--- a/chrome/browser/extensions/activity_log/activity_log_unittest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
@@ -85,6 +85,8 @@
                       bool is_lock_screen_context) override {}
   void SetSystemFont(const std::string& font_family,
                      const std::string& font_size) override {}
+  void SetWebViewPartitionID(const std::string& partition_id) override {}
+
   mojo::AssociatedReceiverSet<mojom::Renderer> receivers_;
 };
 
diff --git a/chrome/browser/extensions/api/automation/automation_apitest.cc b/chrome/browser/extensions/api/automation/automation_apitest.cc
index 2ec2412..c0cd4bdb 100644
--- a/chrome/browser/extensions/api/automation/automation_apitest.cc
+++ b/chrome/browser/extensions/api/automation/automation_apitest.cc
@@ -292,6 +292,12 @@
 }
 #endif
 
+IN_PROC_BROWSER_TEST_F(AutomationApiTest, IframeNav) {
+  StartEmbeddedTestServer();
+  ASSERT_TRUE(RunExtensionSubtest("automation/tests/desktop", "iframenav.html"))
+      << message_;
+}
+
 IN_PROC_BROWSER_TEST_F(AutomationApiTest, DesktopNotRequested) {
   ASSERT_TRUE(RunExtensionSubtest("automation/tests/tabs",
                                   "desktop_not_requested.html")) << message_;
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc
index 37c4ffb..9cf97e7 100644
--- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc
@@ -9,6 +9,7 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
 #include "base/threading/thread_restrictions.h"
+#include "build/build_config.h"
 #include "components/version_info/version_info.h"
 #include "content/public/test/browser_test.h"
 #include "extensions/common/features/feature_channel.h"
@@ -73,7 +74,13 @@
                          DeclarativeNetRequestLazyAPItest,
                          ::testing::Values(ContextType::kServiceWorker));
 
-IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestLazyAPItest, DynamicRules) {
+// Flaky on MSAN: https://crbug.com/1167168
+#if defined(MEMORY_SANITIZER)
+#define MAYBE_DynamicRules DISABLED_DynamicRules
+#else
+#define MAYBE_DynamicRules DynamicRules
+#endif
+IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestLazyAPItest, MAYBE_DynamicRules) {
   ASSERT_TRUE(RunTest("dynamic_rules")) << message_;
 }
 
diff --git a/chrome/browser/extensions/autoplay_browsertest.cc b/chrome/browser/extensions/autoplay_browsertest.cc
index ec3492ab..ec4f662e 100644
--- a/chrome/browser/extensions/autoplay_browsertest.cc
+++ b/chrome/browser/extensions/autoplay_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/strings/stringprintf.h"
+#include "build/build_config.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/ui/extensions/extension_action_test_helper.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -29,6 +30,13 @@
   ASSERT_TRUE(RunExtensionTest("autoplay")) << message_;
 }
 
+// TODO(crbug.com/1166927): AutoplayAllowedInIframe sporadically (~10%?) times
+// out on Linux.
+#if defined(OS_LINUX)
+#define MAYBE_AutoplayAllowedInIframe DISABLED_AutoplayAllowedInIframe
+#else
+#define MAYBE_AutoplayAllowedInIframe AutoplayAllowedInIframe
+#endif  // defined(OS_LINUX)
 IN_PROC_BROWSER_TEST_F(AutoplayExtensionBrowserTest, AutoplayAllowedInIframe) {
   ASSERT_TRUE(StartEmbeddedTestServer());
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 23c72e0..d0dda089 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -84,6 +84,7 @@
     &autofill_assistant::features::kAutofillAssistantChromeEntry,
     &autofill_assistant::features::kAutofillAssistantDirectActions,
     &autofill_assistant::features::kAutofillAssistantDisableOnboardingFlow,
+    &autofill_assistant::features::kAutofillAssistantLoadDFMForTriggerScripts,
     &autofill_assistant::features::kAutofillAssistantProactiveHelp,
     &autofill_assistant::features::
         kAutofillAssistantDisableProactiveHelpTiedToMSBB,
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index 523da9ad..0406c31 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -225,6 +225,8 @@
     public static final String AUTOFILL_ASSISTANT_DIRECT_ACTIONS = "AutofillAssistantDirectActions";
     public static final String AUTOFILL_ASSISTANT_DISABLE_ONBOARDING_FLOW =
             "AutofillAssistantDisableOnboardingFlow";
+    public static final String AUTOFILL_ASSISTANT_LOAD_DFM_FOR_TRIGGER_SCRIPTS =
+            "AutofillAssistantLoadDFMForTriggerScripts";
     public static final String AUTOFILL_ASSISTANT_PROACTIVE_HELP = "AutofillAssistantProactiveHelp";
     public static final String AUTOFILL_ASSISTANT_DISABLE_PROACTIVE_HELP_TIED_TO_MSBB =
             "AutofillAssistantDisableProactiveHelpTiedToMSBB";
diff --git a/chrome/browser/guest_view/mime_handler_view/chrome_mime_handler_view_browsertest.cc b/chrome/browser/guest_view/mime_handler_view/chrome_mime_handler_view_browsertest.cc
index 884a1c71..e7da47a 100644
--- a/chrome/browser/guest_view/mime_handler_view/chrome_mime_handler_view_browsertest.cc
+++ b/chrome/browser/guest_view/mime_handler_view/chrome_mime_handler_view_browsertest.cc
@@ -12,6 +12,7 @@
 #include "base/test/test_timeouts.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
+#include "build/buildflag.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/ui/browser.h"
@@ -46,6 +47,7 @@
 #include "net/dns/mock_host_resolver.h"
 #include "net/http/http_response_headers.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "printing/buildflags/buildflags.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/controls/webview/webview.h"
 #include "ui/views/view.h"
@@ -54,6 +56,9 @@
 #if defined(USE_AURA)
 #include "ui/aura/window.h"
 #endif
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
+#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
+#endif
 
 using extensions::ExtensionsAPIClient;
 using extensions::MimeHandlerViewGuest;
@@ -184,6 +189,42 @@
                                base::span<const uint8_t> message) override {}
 };
 
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
+class PrintPreviewDelegate : printing::PrintPreviewUI::TestDelegate {
+ public:
+  PrintPreviewDelegate() {
+    printing::PrintPreviewUI::SetDelegateForTesting(this);
+  }
+  PrintPreviewDelegate(const PrintPreviewDelegate&) = delete;
+  PrintPreviewDelegate& operator=(const PrintPreviewDelegate&) = delete;
+  ~PrintPreviewDelegate() override {
+    printing::PrintPreviewUI::SetDelegateForTesting(nullptr);
+  }
+
+  void WaitUntilPreviewIsReady() {
+    if (total_page_count_ > 0)
+      return;
+
+    base::RunLoop run_loop;
+    quit_callback_ = run_loop.QuitClosure();
+    run_loop.Run();
+  }
+
+ private:
+  // PrintPreviewUI::TestDelegate:
+  void DidGetPreviewPageCount(uint32_t page_count) override {
+    EXPECT_GE(page_count, 1u);
+    total_page_count_ = page_count;
+    if (quit_callback_)
+      std::move(quit_callback_).Run();
+  }
+  void DidRenderPreviewPage(content::WebContents* preview_dialog) override {}
+
+  uint32_t total_page_count_ = 0;
+  base::OnceClosure quit_callback_;
+};
+#endif
+
 }  // namespace
 
 // Flaky (https://crbug.com/1033009)
@@ -229,3 +270,23 @@
   ui_test_utils::NavigateToURL(browser(), data_url);
   ASSERT_TRUE(GetGuestViewManager()->WaitForSingleGuestCreated());
 }
+
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
+IN_PROC_BROWSER_TEST_F(ChromeMimeHandlerViewTest, EmbeddedThenPrint) {
+  PrintPreviewDelegate print_preview_delegate;
+  InitializeTestPage(embedded_test_server()->GetURL("/test_embedded.html"));
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+  auto* gv_manager = GetGuestViewManager();
+  gv_manager->WaitForAllGuestsDeleted();
+  EXPECT_EQ(1U, gv_manager->num_guests_created());
+
+  // Verify that print dialog comes up.
+  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+  auto* main_frame = web_contents->GetMainFrame();
+  // Use setTimeout() to prevent ExecuteScript() from blocking on the print
+  // dialog.
+  ASSERT_TRUE(content::ExecuteScript(
+      main_frame, "setTimeout(function() { window.print(); }, 0)"));
+  print_preview_delegate.WaitUntilPreviewIsReady();
+}
+#endif
diff --git a/chrome/browser/lookalikes/lookalike_url_service.cc b/chrome/browser/lookalikes/lookalike_url_service.cc
index c7bc57b..8e7f3c3 100644
--- a/chrome/browser/lookalikes/lookalike_url_service.cc
+++ b/chrome/browser/lookalikes/lookalike_url_service.cc
@@ -13,6 +13,7 @@
 #include "base/memory/singleton.h"
 #include "base/task/post_task.h"
 #include "base/time/default_clock.h"
+#include "base/trace_event/trace_event.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/engagement/site_engagement_service_factory.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
@@ -75,11 +76,15 @@
 std::vector<DomainInfo> UpdateEngagedSitesOnWorkerThread(
     base::Time now,
     scoped_refptr<HostContentSettingsMap> map) {
+  TRACE_EVENT0("navigation",
+               "LookalikeUrlService UpdateEngagedSitesOnWorkerThread");
   std::vector<DomainInfo> new_engaged_sites;
 
   auto details =
       site_engagement::SiteEngagementService::GetAllDetailsInBackground(now,
                                                                         map);
+  TRACE_EVENT1("navigation", "LookalikeUrlService SiteEngagementService",
+               "site_count", details.size());
   for (const site_engagement::mojom::SiteEngagementDetails& detail : details) {
     if (!detail.origin.SchemeIsHTTPOrHTTPS()) {
       continue;
@@ -121,6 +126,7 @@
 void LookalikeUrlService::ForceUpdateEngagedSites(
     EngagedSitesCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  TRACE_EVENT0("navigation", "LookalikeUrlService::ForceUpdateEngagedSites");
 
   // Queue an update on a worker thread if necessary.
   if (!update_in_progress_) {
@@ -156,7 +162,9 @@
 void LookalikeUrlService::OnUpdateEngagedSitesCompleted(
     std::vector<DomainInfo> new_engaged_sites) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
+  DCHECK(update_in_progress_);
+  TRACE_EVENT0("navigation",
+               "LookalikeUrlService::OnUpdateEngagedSitesCompleted");
   engaged_sites_.swap(new_engaged_sites);
   last_engagement_fetch_time_ = clock_->Now();
   update_in_progress_ = false;
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
index f6aa3f4..1e588e70 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
@@ -1188,8 +1188,8 @@
 
     if (auto first_contentful_paint =
             ad_frame_data.earliest_first_contentful_paint()) {
-      ADS_HISTOGRAM("AdPaintTiming.NavigationToFirstContentfulPaint2",
-                    PAGE_LOAD_HISTOGRAM, visibility,
+      ADS_HISTOGRAM("AdPaintTiming.NavigationToFirstContentfulPaint3",
+                    PAGE_LOAD_LONG_HISTOGRAM, visibility,
                     first_contentful_paint.value());
     }
   }
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc
index 7020d17c..9d725eb 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc
@@ -1000,15 +1000,15 @@
   ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
 
   histogram_tester.ExpectTotalCount(
-      "PageLoad.Clients.Ads.AdPaintTiming.NavigationToFirstContentfulPaint2",
+      "PageLoad.Clients.Ads.AdPaintTiming.NavigationToFirstContentfulPaint3",
       1);
   histogram_tester.ExpectTotalCount(
       "PageLoad.Clients.Ads.Visible.AdPaintTiming."
-      "NavigationToFirstContentfulPaint2",
+      "NavigationToFirstContentfulPaint3",
       1);
   histogram_tester.ExpectTotalCount(
       "PageLoad.Clients.Ads.NonVisible.AdPaintTiming."
-      "NavigationToFirstContentfulPaint2",
+      "NavigationToFirstContentfulPaint3",
       0);
 }
 
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
index 3d934b0..567a50b 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
@@ -2857,7 +2857,7 @@
   NavigateFrame(kNonAdUrl, main_frame);
 
   histogram_tester().ExpectTotalCount(
-      "AdPaintTiming.NavigationToFirstContentfulPaint2", 0);
+      "AdPaintTiming.NavigationToFirstContentfulPaint3", 0);
 }
 
 TEST_F(AdsPageLoadMetricsObserverTest, FirstContentfulPaint_Recorded) {
@@ -2875,7 +2875,7 @@
   NavigateFrame(kNonAdUrl, main_frame);
 
   histogram_tester().ExpectUniqueSample(
-      SuffixedHistogram("AdPaintTiming.NavigationToFirstContentfulPaint2"), 100,
+      SuffixedHistogram("AdPaintTiming.NavigationToFirstContentfulPaint3"), 100,
       1);
 
   auto entries = test_ukm_recorder().GetEntriesByName(
@@ -2908,7 +2908,7 @@
 
   // The histogram value should be that of the earliest FCP recorded.
   histogram_tester().ExpectUniqueSample(
-      SuffixedHistogram("AdPaintTiming.NavigationToFirstContentfulPaint2"), 90,
+      SuffixedHistogram("AdPaintTiming.NavigationToFirstContentfulPaint3"), 90,
       1);
 
   auto entries = test_ukm_recorder().GetEntriesByName(
@@ -2942,7 +2942,7 @@
 
   // The histogram value should be that of the earliest FCP recorded.
   histogram_tester().ExpectUniqueSample(
-      SuffixedHistogram("AdPaintTiming.NavigationToFirstContentfulPaint2"), 90,
+      SuffixedHistogram("AdPaintTiming.NavigationToFirstContentfulPaint3"), 90,
       1);
 
   auto entries = test_ukm_recorder().GetEntriesByName(
@@ -2971,7 +2971,7 @@
 
   // The histogram value should be that of the earliest FCP recorded.
   histogram_tester().ExpectUniqueSample(
-      SuffixedHistogram("AdPaintTiming.NavigationToFirstContentfulPaint2"), 90,
+      SuffixedHistogram("AdPaintTiming.NavigationToFirstContentfulPaint3"), 90,
       1);
 
   auto entries = test_ukm_recorder().GetEntriesByName(
diff --git a/chrome/browser/paint_preview/paint_preview_browsertest.cc b/chrome/browser/paint_preview/paint_preview_browsertest.cc
index 5ea42d8b..65f62ed 100644
--- a/chrome/browser/paint_preview/paint_preview_browsertest.cc
+++ b/chrome/browser/paint_preview/paint_preview_browsertest.cc
@@ -373,15 +373,7 @@
 // occurs *during* crash handling, leaving the frame in an invalid state and
 // leading to a crash when it subsequently unloaded.
 // This is fixed by deferring it to a PostTask.
-// Flaky on Mac. TODO(https://crbug.com/1160608): Enabled this test.
-#if defined(OS_MAC)
-#define MAYBE_DontReloadInRenderProcessExit \
-  DISABLED_DontReloadInRenderProcessExit
-#else
-#define MAYBE_DontReloadInRenderProcessExit DontReloadInRenderProcessExit
-#endif
-IN_PROC_BROWSER_TEST_P(PaintPreviewBrowserTest,
-                       MAYBE_DontReloadInRenderProcessExit) {
+IN_PROC_BROWSER_TEST_P(PaintPreviewBrowserTest, DontReloadInRenderProcessExit) {
   // In the FileSystem variant of this test, blocking needs to be permitted to
   // allow cleanup to work during the crash.
   base::ScopedAllowBlockingForTesting scope;
diff --git a/chrome/browser/password_manager/android/credential_leak_controller_android.cc b/chrome/browser/password_manager/android/credential_leak_controller_android.cc
index 6c2b562..f223c605 100644
--- a/chrome/browser/password_manager/android/credential_leak_controller_android.cc
+++ b/chrome/browser/password_manager/android/credential_leak_controller_android.cc
@@ -12,6 +12,7 @@
 #include "chrome/common/url_constants.h"
 #include "components/password_manager/core/browser/leak_detection_dialog_utils.h"
 #include "ui/android/window_android.h"
+#include "url/android/gurl_android.h"
 
 using password_manager::metrics_util::LeakDialogDismissalReason;
 using password_manager::metrics_util::LogLeakDialogTypeAndDismissalReason;
@@ -62,7 +63,7 @@
   } else if (ShouldShowChangePasswordButton()) {
     Java_PasswordChangeLauncher_start(
         env, window_android_->GetJavaObject(),
-        base::android::ConvertUTF8ToJavaString(env, origin_.spec()),
+        url::GURLAndroid::FromNativeGURL(env, origin_),
         base::android::ConvertUTF16ToJavaString(env, username_));
   }
 
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 382c7725..f4e57be4 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -1776,10 +1776,11 @@
   ~PDFExtensionLinkClickTest() override {}
 
  protected:
-  void LoadTestLinkPdfGetGuestContents() {
+  content::WebContents* LoadTestLinkPdfGetGuestContents() {
     GURL test_pdf_url(embedded_test_server()->GetURL("/pdf/test-link.pdf"));
     guest_contents_ = LoadPdfGetGuestContents(test_pdf_url);
-    ASSERT_TRUE(guest_contents_);
+    EXPECT_TRUE(guest_contents_);
+    return guest_contents_;
   }
 
   // The rectangle of the link in test-link.pdf is [72 706 164 719] in PDF user
@@ -1974,6 +1975,49 @@
   EXPECT_EQ("http://www.example.com/", url.spec());
 }
 
+namespace {
+// Fails the test if a navigation is started in the given WebContents.
+class FailOnNavigation : public content::WebContentsObserver {
+ public:
+  explicit FailOnNavigation(content::WebContents* contents)
+      : content::WebContentsObserver(contents) {}
+
+  // content::WebContentsObserver:
+  void DidStartNavigation(
+      content::NavigationHandle* navigation_handle) override {
+    ADD_FAILURE() << "Unexpected navigation";
+  }
+};
+}  // namespace
+
+// If the PDF viewer can't navigate the tab using a tab id, make sure it doesn't
+// try to navigate the mime handler extension's frame.
+// Regression test for https://crbug.com/1158381
+IN_PROC_BROWSER_TEST_P(PDFExtensionLinkClickTest, LinkClickInPdfInNonTab) {
+  // For ease of testing, we'll still load the PDF in a tab, but we clobber the
+  // tab id in the viewer to make it think it's not in a tab.
+  WebContents* guest_contents = LoadTestLinkPdfGetGuestContents();
+  ASSERT_TRUE(guest_contents);
+  ASSERT_TRUE(
+      content::ExecuteScript(guest_contents,
+                             "window.viewer.browserApi.getStreamInfo().tabId = "
+                             "    chrome.tabs.TAB_ID_NONE;"));
+
+  FailOnNavigation fail_if_mimehandler_navigates(guest_contents);
+  content::SimulateMouseClickAt(
+      GetWebContentsForInputRouting(), blink::WebInputEvent::kNoModifiers,
+      blink::WebMouseEvent::Button::kLeft, GetLinkPosition());
+
+  // Since the guest contents is for a mime handling extension (in this case,
+  // the PDF viewer extension), it must not navigate away from the extension. If
+  // |fail_if_mimehandler_navigates| doesn't see a navigation, we consider the
+  // test to have passed.
+  base::RunLoop run_loop;
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
+  run_loop.Run();
+}
+
 INSTANTIATE_TEST_SUITE_P(/* no prefix */,
                          PDFExtensionLinkClickTest,
                          testing::Bool());
diff --git a/chrome/browser/policy/system_features_policy_browsertest.cc b/chrome/browser/policy/system_features_policy_browsertest.cc
index 1773c147..9347bbc 100644
--- a/chrome/browser/policy/system_features_policy_browsertest.cc
+++ b/chrome/browser/policy/system_features_policy_browsertest.cc
@@ -18,6 +18,7 @@
 #include "chromeos/constants/chromeos_features.h"
 #include "components/policy/core/common/policy_pref_names.h"
 #include "components/policy/policy_constants.h"
+#include "components/services/app_service/public/mojom/types.mojom.h"
 #include "components/strings/grit/components_strings.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_navigation_observer.h"
@@ -55,18 +56,25 @@
   }
 
   // Disables specified system features or enables all if system_features is
-  // empty.
-  void UpdateSystemFeaturesDisableList(base::Value system_features) {
+  // empty. Updates disabled mode for disabled system features.
+  void UpdateSystemFeaturesDisableList(base::Value system_features,
+                                       const char* disabled_mode) {
     PolicyMap policies;
     policies.Set(key::kSystemFeaturesDisableList, POLICY_LEVEL_MANDATORY,
                  POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
                  std::move(system_features), nullptr);
+    if (disabled_mode) {
+      policies.Set(key::kSystemFeaturesDisableMode, POLICY_LEVEL_MANDATORY,
+                   POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+                   base::Value(disabled_mode), nullptr);
+    }
     UpdateProviderPolicy(policies);
   }
 
   void VerifyAppState(const char* app_id,
                       apps::mojom::Readiness expected_readiness,
-                      bool blocked_icon) {
+                      bool blocked_icon,
+                      apps::mojom::OptionalBool expected_visibility) {
     auto* profile = browser()->profile();
     extensions::ExtensionRegistry* registry =
         extensions::ExtensionRegistry::Get(profile);
@@ -77,8 +85,8 @@
     proxy->FlushMojoCallsForTesting();
 
     proxy->AppRegistryCache().ForOneApp(
-        app_id,
-        [&expected_readiness, &blocked_icon](const apps::AppUpdate& update) {
+        app_id, [&expected_readiness, &blocked_icon,
+                 &expected_visibility](const apps::AppUpdate& update) {
           EXPECT_EQ(expected_readiness, update.Readiness());
           if (blocked_icon) {
             EXPECT_TRUE(apps::IconEffects::kBlocked &
@@ -87,6 +95,9 @@
             EXPECT_FALSE(apps::IconEffects::kBlocked &
                          update.IconKey()->icon_effects);
           }
+          EXPECT_EQ(expected_visibility, update.ShowInLauncher());
+          EXPECT_EQ(expected_visibility, update.ShowInSearch());
+          EXPECT_EQ(expected_visibility, update.ShowInShelf());
         });
   }
 
@@ -97,55 +108,85 @@
 IN_PROC_BROWSER_TEST_F(SystemFeaturesPolicyTest, DisableCameraBeforeInstall) {
   base::Value system_features(base::Value::Type::LIST);
   system_features.Append(kCameraFeature);
-  UpdateSystemFeaturesDisableList(std::move(system_features));
+  UpdateSystemFeaturesDisableList(std::move(system_features), nullptr);
   EnableExtensions(false);
   VerifyAppState(extension_misc::kCameraAppId,
-                 apps::mojom::Readiness::kDisabledByPolicy, true);
+                 apps::mojom::Readiness::kDisabledByPolicy, true,
+                 apps::mojom::OptionalBool::kTrue);
 
-  UpdateSystemFeaturesDisableList(base::Value());
+  UpdateSystemFeaturesDisableList(base::Value(), nullptr);
   VerifyAppState(extension_misc::kCameraAppId, apps::mojom::Readiness::kReady,
-                 false);
+                 false, apps::mojom::OptionalBool::kTrue);
 }
 
 IN_PROC_BROWSER_TEST_F(SystemFeaturesPolicyTest, DisableCameraAfterInstall) {
   EnableExtensions(false);
   base::Value system_features(base::Value::Type::LIST);
   system_features.Append(kCameraFeature);
-  UpdateSystemFeaturesDisableList(std::move(system_features));
+  UpdateSystemFeaturesDisableList(std::move(system_features), nullptr);
 
   VerifyAppState(extension_misc::kCameraAppId,
-                 apps::mojom::Readiness::kDisabledByPolicy, true);
+                 apps::mojom::Readiness::kDisabledByPolicy, true,
+                 apps::mojom::OptionalBool::kTrue);
 
-  UpdateSystemFeaturesDisableList(base::Value());
+  UpdateSystemFeaturesDisableList(base::Value(), nullptr);
   VerifyAppState(extension_misc::kCameraAppId, apps::mojom::Readiness::kReady,
-                 false);
+                 false, apps::mojom::OptionalBool::kTrue);
 }
 
 IN_PROC_BROWSER_TEST_F(SystemFeaturesPolicyTest, DisableWebStoreBeforeInstall) {
   base::Value system_features(base::Value::Type::LIST);
   system_features.Append(kWebStoreFeature);
-  UpdateSystemFeaturesDisableList(std::move(system_features));
+  UpdateSystemFeaturesDisableList(std::move(system_features), nullptr);
   EnableExtensions(true);
   VerifyAppState(extensions::kWebStoreAppId,
-                 apps::mojom::Readiness::kDisabledByPolicy, true);
+                 apps::mojom::Readiness::kDisabledByPolicy, true,
+                 apps::mojom::OptionalBool::kTrue);
 
-  UpdateSystemFeaturesDisableList(base::Value());
+  UpdateSystemFeaturesDisableList(base::Value(), nullptr);
   VerifyAppState(extensions::kWebStoreAppId, apps::mojom::Readiness::kReady,
-                 false);
+                 false, apps::mojom::OptionalBool::kTrue);
 }
 
 IN_PROC_BROWSER_TEST_F(SystemFeaturesPolicyTest, DisableWebStoreAfterInstall) {
   EnableExtensions(false);
   base::Value system_features(base::Value::Type::LIST);
   system_features.Append(kWebStoreFeature);
-  UpdateSystemFeaturesDisableList(std::move(system_features));
+  UpdateSystemFeaturesDisableList(std::move(system_features), nullptr);
 
   VerifyAppState(extensions::kWebStoreAppId,
-                 apps::mojom::Readiness::kDisabledByPolicy, true);
+                 apps::mojom::Readiness::kDisabledByPolicy, true,
+                 apps::mojom::OptionalBool::kTrue);
 
-  UpdateSystemFeaturesDisableList(base::Value());
+  UpdateSystemFeaturesDisableList(base::Value(), nullptr);
   VerifyAppState(extensions::kWebStoreAppId, apps::mojom::Readiness::kReady,
-                 false);
+                 false, apps::mojom::OptionalBool::kTrue);
+}
+
+IN_PROC_BROWSER_TEST_F(SystemFeaturesPolicyTest,
+                       DisableWebStoreAfterInstallWithModes) {
+  EnableExtensions(false);
+  base::Value system_features(base::Value::Type::LIST);
+  system_features.Append(kWebStoreFeature);
+  // Disable app with default mode (blocked)..
+  UpdateSystemFeaturesDisableList(system_features.Clone(), nullptr);
+  VerifyAppState(extensions::kWebStoreAppId,
+                 apps::mojom::Readiness::kDisabledByPolicy, true,
+                 apps::mojom::OptionalBool::kTrue);
+  // Disable and hide app.
+  UpdateSystemFeaturesDisableList(system_features.Clone(), kHiddenDisableMode);
+  VerifyAppState(extensions::kWebStoreAppId,
+                 apps::mojom::Readiness::kDisabledByPolicy, true,
+                 apps::mojom::OptionalBool::kFalse);
+  // Disable and block app.
+  UpdateSystemFeaturesDisableList(system_features.Clone(), kBlockedDisableMode);
+  VerifyAppState(extensions::kWebStoreAppId,
+                 apps::mojom::Readiness::kDisabledByPolicy, true,
+                 apps::mojom::OptionalBool::kTrue);
+  // Enable app
+  UpdateSystemFeaturesDisableList(base::Value(), nullptr);
+  VerifyAppState(extensions::kWebStoreAppId, apps::mojom::Readiness::kReady,
+                 false, apps::mojom::OptionalBool::kTrue);
 }
 
 IN_PROC_BROWSER_TEST_F(SystemFeaturesPolicyTest,
@@ -154,29 +195,31 @@
   base::Value system_features(base::Value::Type::LIST);
   system_features.Append(kWebStoreFeature);
   system_features.Append(kCameraFeature);
-  UpdateSystemFeaturesDisableList(std::move(system_features));
+  UpdateSystemFeaturesDisableList(std::move(system_features), nullptr);
 
   VerifyAppState(extensions::kWebStoreAppId,
-                 apps::mojom::Readiness::kDisabledByPolicy, true);
+                 apps::mojom::Readiness::kDisabledByPolicy, true,
+                 apps::mojom::OptionalBool::kTrue);
   VerifyAppState(extension_misc::kCameraAppId,
-                 apps::mojom::Readiness::kDisabledByPolicy, true);
+                 apps::mojom::Readiness::kDisabledByPolicy, true,
+                 apps::mojom::OptionalBool::kTrue);
 
-  UpdateSystemFeaturesDisableList(base::Value());
+  UpdateSystemFeaturesDisableList(base::Value(), nullptr);
   VerifyAppState(extensions::kWebStoreAppId, apps::mojom::Readiness::kReady,
-                 false);
+                 false, apps::mojom::OptionalBool::kTrue);
 }
 
 IN_PROC_BROWSER_TEST_F(SystemFeaturesPolicyTest, RedirectChromeSettingsURL) {
   PolicyMap policies;
   base::Value system_features(base::Value::Type::LIST);
   system_features.Append(kBrowserSettingsFeature);
-  UpdateSystemFeaturesDisableList(std::move(system_features));
+  UpdateSystemFeaturesDisableList(std::move(system_features), nullptr);
 
   GURL settings_url = GURL(chrome::kChromeUISettingsURL);
   EXPECT_EQ(l10n_util::GetStringUTF16(IDS_CHROME_URLS_DISABLED_PAGE_HEADER),
             GetWebUITitle(settings_url));
 
-  UpdateSystemFeaturesDisableList(base::Value());
+  UpdateSystemFeaturesDisableList(base::Value(), nullptr);
   EXPECT_EQ(l10n_util::GetStringUTF16(IDS_SETTINGS_SETTINGS),
             GetWebUITitle(settings_url));
 }
diff --git a/chrome/browser/printing/print_preview_pdf_generated_browsertest.cc b/chrome/browser/printing/print_preview_pdf_generated_browsertest.cc
index 5d16987d..01881f3 100644
--- a/chrome/browser/printing/print_preview_pdf_generated_browsertest.cc
+++ b/chrome/browser/printing/print_preview_pdf_generated_browsertest.cc
@@ -64,7 +64,6 @@
 #endif
 
 using content::WebContents;
-using content::WebContentsObserver;
 
 namespace printing {
 
@@ -117,21 +116,25 @@
   bool source_is_pdf;
 };
 
-// Observes the print preview webpage. Once it observes the PreviewPageCount
-// message, will send a sequence of commands to the print preview dialog and
+// Implements PrintPreviewUI::TestDelegate. Once DidGetPreviewPageCount() is
+// called, will send a sequence of commands to the print preview dialog and
 // change the settings of the preview dialog.
-class PrintPreviewObserver : public WebContentsObserver {
+class PrintPreviewDelegate : printing::PrintPreviewUI::TestDelegate {
  public:
-  PrintPreviewObserver(Browser* browser,
+  PrintPreviewDelegate(Browser* browser,
                        WebContents* dialog,
                        const base::FilePath& pdf_file_save_path)
-      : WebContentsObserver(dialog),
-        browser_(browser),
+      : browser_(browser),
         state_(kWaitingToSendSaveAsPdf),
         failed_setting_("None"),
-        pdf_file_save_path_(pdf_file_save_path) {}
-
-  ~PrintPreviewObserver() override {}
+        pdf_file_save_path_(pdf_file_save_path) {
+    printing::PrintPreviewUI::SetDelegateForTesting(this);
+  }
+  PrintPreviewDelegate(const PrintPreviewDelegate&) = delete;
+  PrintPreviewDelegate& operator=(const PrintPreviewDelegate&) = delete;
+  ~PrintPreviewDelegate() override {
+    printing::PrintPreviewUI::SetDelegateForTesting(nullptr);
+  }
 
   // Sets closure for the observer so that it can end the loop.
   void set_quit_closure(base::OnceClosure closure) {
@@ -144,13 +147,6 @@
                                                   std::move(quit_closure_));
   }
 
-  bool OnMessageReceived(const IPC::Message& message) override {
-    IPC_BEGIN_MESSAGE_MAP(PrintPreviewObserver, message)
-      IPC_MESSAGE_HANDLER(PrintHostMsg_DidStartPreview, OnDidStartPreview)
-    IPC_END_MESSAGE_MAP()
-    return false;
-  }
-
   // Gets the web contents for the print preview dialog so that the UI and
   // other elements can be accessed.
   WebContents* GetDialog() {
@@ -206,7 +202,7 @@
       // Called by |GetUI()->handler_|, it is a callback function that call
       // |EndLoop| when an attempt to save the PDF has been made.
       GetUI()->SetPdfSavedClosureForTesting(base::BindOnce(
-          &PrintPreviewObserver::EndLoop, base::Unretained(this)));
+          &PrintPreviewDelegate::EndLoop, base::Unretained(this)));
       ASSERT_FALSE(pdf_file_save_path_.empty());
       GetUI()->SetSelectedFileForTesting(pdf_file_save_path_);
       return;
@@ -231,21 +227,21 @@
   // listens for 'UILoadedForTest' and 'UIFailedLoadingForTest.'
   class UIDoneLoadingMessageHandler : public content::WebUIMessageHandler {
    public:
-    explicit UIDoneLoadingMessageHandler(PrintPreviewObserver* observer)
-        : observer_(observer) {}
+    explicit UIDoneLoadingMessageHandler(PrintPreviewDelegate* delegate)
+        : delegate_(delegate) {}
 
     ~UIDoneLoadingMessageHandler() override {}
 
-    // When a setting has been set succesfully, this is called and the observer
+    // When a setting has been set successfully, this is called and the delegate
     // is told to send the next setting to be set.
     void HandleDone(const base::ListValue* /* args */) {
-      observer_->ManipulatePreviewSettings();
+      delegate_->ManipulatePreviewSettings();
     }
 
     // Ends the test because a setting was not set successfully. Called when
     // this class hears 'UIFailedLoadingForTest.'
     void HandleFailure(const base::ListValue* /* args */) {
-      FAIL() << "Failed to set: " << observer_->GetFailedSetting();
+      FAIL() << "Failed to set: " << delegate_->GetFailedSetting();
     }
 
     // Allows this class to listen for the 'UILoadedForTest' and
@@ -267,19 +263,11 @@
     }
 
    private:
-    PrintPreviewObserver* const observer_;
-
-    DISALLOW_COPY_AND_ASSIGN(UIDoneLoadingMessageHandler);
+    PrintPreviewDelegate* const delegate_;
   };
 
-  // Called when the observer gets the IPC message with the preview document's
-  // properties.
-  void OnDidStartPreview(const mojom::DidStartPreviewParams& params,
-                         const printing::mojom::PreviewIds& ids) {
-    WebContents* web_contents = GetDialog();
-    ASSERT_TRUE(web_contents);
-    Observe(web_contents);
-
+  // PrintPreviewUI::TestDelegate:
+  void DidGetPreviewPageCount(uint32_t page_count) override {
     PrintPreviewUI* ui = GetUI();
     ASSERT_TRUE(ui);
     ASSERT_TRUE(ui->web_ui());
@@ -288,24 +276,18 @@
         std::make_unique<UIDoneLoadingMessageHandler>(this));
     ui->SendEnableManipulateSettingsForTest();
   }
-
-  void DidCloneToNewWebContents(WebContents* old_web_contents,
-                                WebContents* new_web_contents) override {
-    Observe(new_web_contents);
-  }
+  void DidRenderPreviewPage(content::WebContents* preview_dialog) override {}
 
   Browser* browser_;
   base::OnceClosure quit_closure_;
   std::unique_ptr<PrintPreviewSettings> settings_;
 
-  // State of the observer. The state indicates what message to send
-  // next. The state advances whenever the message handler calls
-  // ManipulatePreviewSettings() on the observer.
+  // |state_| that indicates what message to send next. The state advances
+  // whenever the message handler calls ManipulatePreviewSettings() on the
+  // delegate.
   State state_;
   std::string failed_setting_;
   const base::FilePath pdf_file_save_path_;
-
-  DISALLOW_COPY_AND_ASSIGN(PrintPreviewObserver);
 };
 
 class PrintPreviewPdfGeneratedBrowserTest : public InProcessBrowserTest {
@@ -317,14 +299,14 @@
   // for all the settings to be set, then save the preview to PDF.
   void NavigateAndPrint(const base::FilePath::StringType& file_name,
                           const PrintPreviewSettings& settings) {
-    print_preview_observer_->SetPrintPreviewSettings(settings);
+    print_preview_delegate_->SetPrintPreviewSettings(settings);
     base::FilePath path(file_name);
     GURL gurl = net::FilePathToFileURL(base::MakeAbsoluteFilePath(path));
 
     ui_test_utils::NavigateToURL(browser(), gurl);
 
     base::RunLoop loop;
-    print_preview_observer_->set_quit_closure(loop.QuitClosure());
+    print_preview_delegate_->set_quit_closure(loop.QuitClosure());
     chrome::Print(browser());
     loop.Run();
 
@@ -461,31 +443,17 @@
     std::cerr.flush();
   }
 
-  // Duplicates the tab that was created when the browser opened. This is done
-  // so that the observer can listen to the duplicated tab as soon as possible
-  // and start listening for messages related to print preview.
-  void DuplicateTab() {
+  void CreatePreviewDelegate() {
     WebContents* tab =
         browser()->tab_strip_model()->GetActiveWebContents();
     ASSERT_TRUE(tab);
-
-    print_preview_observer_ = std::make_unique<PrintPreviewObserver>(
+    print_preview_delegate_ = std::make_unique<PrintPreviewDelegate>(
         browser(), tab, pdf_file_save_path_);
-    chrome::DuplicateTab(browser());
-
-    WebContents* initiator =
-        browser()->tab_strip_model()->GetActiveWebContents();
-    ASSERT_TRUE(initiator);
-    ASSERT_NE(tab, initiator);
   }
 
-  // Resets the test so that another web page can be printed. It also deletes
-  // the duplicated tab as it isn't needed anymore.
+  // Resets the test so that another web page can be printed.
   void Reset() {
     png_output_.clear();
-    ASSERT_EQ(2, browser()->tab_strip_model()->count());
-    chrome::CloseTab(browser());
-    ASSERT_EQ(1, browser()->tab_strip_model()->count());
   }
 
   // Creates a temporary directory to store a text file that will be used for
@@ -554,7 +522,7 @@
                                       &png_output_));
   }
 
-  std::unique_ptr<PrintPreviewObserver> print_preview_observer_;
+  std::unique_ptr<PrintPreviewDelegate> print_preview_delegate_;
   base::FilePath pdf_file_save_path_;
 
   // Vector for storing the PNG to be sent to the layout test framework.
@@ -615,7 +583,7 @@
     cmd = base::UTF8ToWide(input);
 #endif
 
-    DuplicateTab();
+    CreatePreviewDelegate();
     PrintPreviewSettings settings(
         true, "", false, false, mojom::MarginType::kDefaultMargins,
         cmd.find(file_extension) != base::FilePath::StringType::npos);
diff --git a/chrome/browser/privacy_sandbox/android/BUILD.gn b/chrome/browser/privacy_sandbox/android/BUILD.gn
index c2393f18..e9c25ca 100644
--- a/chrome/browser/privacy_sandbox/android/BUILD.gn
+++ b/chrome/browser/privacy_sandbox/android/BUILD.gn
@@ -21,6 +21,7 @@
     "//components/browser_ui/settings/android:java",
     "//third_party/android_deps:androidx_fragment_fragment_java",
     "//third_party/android_deps:androidx_preference_preference_java",
+    "//ui/android:ui_full_java",
   ]
   resources_package = "org.chromium.chrome.browser.privacy_sandbox"
 }
diff --git a/chrome/browser/privacy_sandbox/android/java/res/xml/privacy_sandbox_preferences.xml b/chrome/browser/privacy_sandbox/android/java/res/xml/privacy_sandbox_preferences.xml
index 16c1632..d650ca1 100644
--- a/chrome/browser/privacy_sandbox/android/java/res/xml/privacy_sandbox_preferences.xml
+++ b/chrome/browser/privacy_sandbox/android/java/res/xml/privacy_sandbox_preferences.xml
@@ -6,26 +6,25 @@
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
 
+    <org.chromium.components.browser_ui.settings.TextMessagePreference
+        android:summary="@string/privacy_sandbox_description"
+        app:allowDividerBelow="false" />
+
+    <PreferenceCategory
+        android:title="@string/privacy_sandbox_trial_title"/>
+
+    <org.chromium.components.browser_ui.settings.TextMessagePreference
+        android:key="privacy_sandbox_trial_description"
+        app:allowDividerBelow="false" />
+
     <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
         android:key="privacy_sandbox_toggle"
-        android:title="@string/privacy_sandbox_title"
+        android:title="@string/privacy_sandbox_toggle"
         android:persistent="false"
         app:allowDividerBelow="false" />
 
     <org.chromium.components.browser_ui.settings.TextMessagePreference
-        android:key="subtitle"
-        android:title="@string/privacy_sandbox_subtitle"
-        app:allowDividerBelow="false" />
-
-    <org.chromium.components.browser_ui.settings.TextMessagePreference
-        android:summary="@string/privacy_sandbox_bullet_one"
-        android:icon="@drawable/ic_people_24dp"
-        app:allowDividerBelow="false" />
-
-    <org.chromium.components.browser_ui.settings.TextMessagePreference
-        android:summary="@string/privacy_sandbox_bullet_two"
-        android:icon="@drawable/ic_data_viz_grey"
-        app:iconTint="@color/default_icon_color_tint_list"
+        android:summary="@string/privacy_sandbox_toggle_description"
         app:allowDividerBelow="false" />
 
 </PreferenceScreen>
diff --git a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSettingsFragment.java b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSettingsFragment.java
index facce0a..2653185 100644
--- a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSettingsFragment.java
+++ b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSettingsFragment.java
@@ -13,11 +13,16 @@
 import androidx.preference.PreferenceFragmentCompat;
 
 import org.chromium.components.browser_ui.settings.SettingsUtils;
+import org.chromium.ui.text.SpanApplier;
+import org.chromium.ui.text.SpanApplier.SpanInfo;
+import org.chromium.ui.widget.ChromeBulletSpan;
 
 /**
  * Settings fragment for privacy sandbox settings. This class represents a View in the MVC paradigm.
  */
 public class PrivacySandboxSettingsFragment extends PreferenceFragmentCompat {
+    public static final String TRIAL_DESCRIPTION_PREFERENCE = "privacy_sandbox_trial_description";
+
     /**
      * Initializes all the objects related to the preferences page.
      */
@@ -26,6 +31,12 @@
         // Add all preferences and set the title.
         getActivity().setTitle(R.string.prefs_privacy_sandbox);
         SettingsUtils.addPreferencesFromResource(this, R.xml.privacy_sandbox_preferences);
+        // Format the trial description, which has bullet points.
+        findPreference(TRIAL_DESCRIPTION_PREFERENCE)
+                .setSummary(SpanApplier.applySpans(
+                        getContext().getString(R.string.privacy_sandbox_trial_description),
+                        new SpanInfo("<li1>", "</li1>", new ChromeBulletSpan(getContext())),
+                        new SpanInfo("<li2>", "</li2>", new ChromeBulletSpan(getContext()))));
     }
 
     @Override
diff --git a/chrome/browser/profiles/profile_destroyer.cc b/chrome/browser/profiles/profile_destroyer.cc
index 1c6cdcc211f..0a8ba8c 100644
--- a/chrome/browser/profiles/profile_destroyer.cc
+++ b/chrome/browser/profiles/profile_destroyer.cc
@@ -151,15 +151,11 @@
   if (pending_destroyers_ == NULL)
     pending_destroyers_ = new DestroyerSet;
   pending_destroyers_->insert(this);
-  for (auto i = hosts->begin(); i != hosts->end(); ++i) {
-    (*i)->AddObserver(this);
-    // For each of the observations, we bump up our reference count.
-    // It will go back to 0 and free us when all hosts are terminated.
-    ++num_hosts_;
-  }
+  for (auto* host : *hosts)
+    observations_.AddObservation(host);
   // If we are going to wait for render process hosts, we don't want to do it
   // for longer than kTimerDelaySeconds.
-  if (num_hosts_) {
+  if (observations_.IsObservingAnySource()) {
     timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kTimerDelaySeconds),
                  base::BindOnce(&ProfileDestroyer::DestroyProfile,
                                 weak_ptr_factory_.GetWeakPtr()));
@@ -182,9 +178,9 @@
                             num_hosts_
                                 ? ProfileDestructionType::kDelayedAndCrashed
                                 : ProfileDestructionType::kDelayed);
-  CHECK_EQ(0U, num_hosts_) << "Some render process hosts were not "
-                           << "destroyed early enough!";
-  DCHECK(pending_destroyers_ != NULL);
+  CHECK(!observations_.IsObservingAnySource())
+      << "Some render process hosts were not destroyed early enough!";
+  DCHECK(pending_destroyers_);
   auto iter = pending_destroyers_->find(this);
   DCHECK(iter != pending_destroyers_->end());
   pending_destroyers_->erase(iter);
@@ -198,9 +194,8 @@
     content::RenderProcessHost* host) {
   TRACE_EVENT2("shutdown", "ProfileDestroyer::RenderProcessHostDestroyed",
                "profile", profile_, "render_process_host", host);
-  DCHECK_GT(num_hosts_, 0u);
-  --num_hosts_;
-  if (num_hosts_ == 0) {
+  observations_.RemoveObservation(host);
+  if (!observations_.IsObservingAnySource()) {
     // Delay the destruction one step further in case other observers need to
     // look at the profile attached to the host.
     base::ThreadTaskRunnerHandle::Get()->PostTask(
diff --git a/chrome/browser/profiles/profile_destroyer.h b/chrome/browser/profiles/profile_destroyer.h
index 30d8ee3..0c42525 100644
--- a/chrome/browser/profiles/profile_destroyer.h
+++ b/chrome/browser/profiles/profile_destroyer.h
@@ -10,16 +10,14 @@
 #include <set>
 
 #include "base/memory/ref_counted.h"
+#include "base/scoped_multi_source_observation.h"
 #include "base/timer/timer.h"
+#include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_process_host_observer.h"
 
 class Profile;
 class ProfileImpl;
 
-namespace content {
-class RenderProcessHost;
-}
-
 // We use this class to destroy the off the record profile so that we can make
 // sure it gets done asynchronously after all render process hosts are gone.
 class ProfileDestroyer : public content::RenderProcessHostObserver {
@@ -71,6 +69,10 @@
   // We don't want to wait forever, so we have a cancellation timer.
   base::OneShotTimer timer_;
 
+  base::ScopedMultiSourceObservation<content::RenderProcessHost,
+                                     content::RenderProcessHostObserver>
+      observations_{this};
+
   // Used to count down the number of render process host left.
   uint32_t num_hosts_;
 
diff --git a/chrome/browser/resources/chromeos/login/components/oobe_adaptive_dialog/oobe_adaptive_dialog.html b/chrome/browser/resources/chromeos/login/components/oobe_adaptive_dialog/oobe_adaptive_dialog.html
index b8f5a83..8202e9c 100644
--- a/chrome/browser/resources/chromeos/login/components/oobe_adaptive_dialog/oobe_adaptive_dialog.html
+++ b/chrome/browser/resources/chromeos/login/components/oobe_adaptive_dialog/oobe_adaptive_dialog.html
@@ -87,7 +87,9 @@
         --oobe-adaptive-dialog-content-direction: column;
         --oobe-adaptive-dialog-item-alignment: center;
         --oobe-text-alignment: center;
-        --oobe-adaptive-dialog-content-width: 100%;
+        --oobe-adaptive-dialog-content-width: calc(
+            var(--oobe-adaptive-dialog-width) -
+            2 * var(--oobe-adaptive-dialog-content-padding));
         /* Header takes 40% of the width remaining after applying padding */
         --oobe-adaptive-dialog-header-width: clamp(302px,
             calc(0.4 * (var(--oobe-adaptive-dialog-width) -
diff --git a/chrome/browser/resources/pdf/BUILD.gn b/chrome/browser/resources/pdf/BUILD.gn
index 6882668..020938c 100644
--- a/chrome/browser/resources/pdf/BUILD.gn
+++ b/chrome/browser/resources/pdf/BUILD.gn
@@ -245,6 +245,7 @@
 
 js_library("navigator") {
   deps = [
+    ":browser_api",
     ":open_pdf_params_parser",
     ":viewport",
   ]
diff --git a/chrome/browser/resources/pdf/browser_api.js b/chrome/browser/resources/pdf/browser_api.js
index 50f912a..75d21fd7 100644
--- a/chrome/browser/resources/pdf/browser_api.js
+++ b/chrome/browser/resources/pdf/browser_api.js
@@ -86,6 +86,27 @@
   }
 
   /**
+   * Navigates the current tab.
+   * @param {string} url The URL to navigate the tab to.
+   */
+  navigateInCurrentTab(url) {
+    const tabId = this.getStreamInfo().tabId;
+    // We need to use the tabs API to navigate because
+    // |window.location.href| cannot be used. This PDF extension is not loaded
+    // in the top level frame (it's embedded using MimeHandlerView). Using
+    // |window.location| would navigate the wrong frame, so we can't
+    // use it as a fallback. If it turns out that we do need a way to navigate
+    // in non-tab cases, we would need to create another mechanism to
+    // communicate with MimeHandler code in the browser (e.g. via
+    // mimeHandlerPrivate), which could then navigate the correct frame.
+    // Furthermore, navigations to local resources would be blocked with
+    // |window.location|.
+    if (chrome.tabs && tabId !== chrome.tabs.TAB_ID_NONE) {
+      chrome.tabs.update(tabId, {url: url});
+    }
+  }
+
+  /**
    * Sets the browser zoom.
    * @param {number} zoom The zoom factor to send to the browser.
    * @return {Promise} A promise that will be resolved when the browser zoom
diff --git a/chrome/browser/resources/pdf/navigator.js b/chrome/browser/resources/pdf/navigator.js
index 1e37b1e..51ffa74 100644
--- a/chrome/browser/resources/pdf/navigator.js
+++ b/chrome/browser/resources/pdf/navigator.js
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {BrowserApi} from './browser_api.js';
 import {OpenPdfParamsParser} from './open_pdf_params_parser.js';
 import {Viewport} from './viewport.js';
 
@@ -33,24 +34,15 @@
 // navigating.
 /** @implements {NavigatorDelegate} */
 export class NavigatorDelegateImpl {
-  /**
-   * @param {number} tabId The tab ID of the PDF viewer or -1 if the viewer is
-   *     not displayed in a tab.
-   */
-  constructor(tabId) {
-    /** @private {number} */
-    this.tabId_ = tabId;
+  /** @param {!BrowserApi} browserApi */
+  constructor(browserApi) {
+    /** @private {!BrowserApi} */
+    this.browserApi_ = browserApi;
   }
 
   /** @override */
   navigateInCurrentTab(url) {
-    // When the PDFviewer is inside a browser tab, prefer the tabs API because
-    // it can navigate from one file:// URL to another.
-    if (chrome.tabs && this.tabId_ !== -1) {
-      chrome.tabs.update(this.tabId_, {url: url});
-    } else {
-      window.location.href = url;
-    }
+    this.browserApi_.navigateInCurrentTab(url);
   }
 
   /** @override */
diff --git a/chrome/browser/resources/pdf/pdf_viewer.js b/chrome/browser/resources/pdf/pdf_viewer.js
index b7047dfa3..2ee0f63 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.js
+++ b/chrome/browser/resources/pdf/pdf_viewer.js
@@ -428,11 +428,10 @@
         'keydown',
         e => this.handleKeyEvent_(/** @type {!KeyboardEvent} */ (e)));
 
-    const tabId = this.browserApi.getStreamInfo().tabId;
     this.navigator_ = new PdfNavigator(
         this.originalUrl, this.viewport,
         /** @type {!OpenPdfParamsParser} */ (this.paramsParser),
-        new NavigatorDelegateImpl(tabId));
+        new NavigatorDelegateImpl(browserApi));
 
     // Listen for save commands from the browser.
     if (chrome.mimeHandlerPrivate && chrome.mimeHandlerPrivate.onSave) {
diff --git a/chrome/browser/resources/signin/profile_picker/profile_card.html b/chrome/browser/resources/signin/profile_picker/profile_card.html
index 0f3ec10..1f2dfd43 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_card.html
+++ b/chrome/browser/resources/signin/profile_picker/profile_card.html
@@ -9,9 +9,8 @@
   }
 
   cr-button {
-    background-color: var(--profile-card-background-color);
+    --hover-bg-color: none; /* Stick to the profile card hover color. */
     border: none;
-    border-radius: inherit;
     box-shadow: none;
     flex-direction: column;
     height: inherit;
@@ -19,10 +18,6 @@
     width: inherit;
   }
 
-  :host-context(.focus-outline-visible) cr-button:focus {
-    box-shadow: 0 0 0 2px rgba(var(--google-blue-600-rgb), .4);
-  }
-
   #avatarContainer {
     height: var(--avatar-icon-size);
     position: relative;
@@ -35,7 +30,8 @@
     --domain-icon-border-size: 2px;
     align-items: center;
     background-color: white;
-    border: var(--domain-icon-border-size) solid var(--profile-card-background-color);
+    border:
+      var(--domain-icon-border-size) solid var(--profile-card-hover-color);
     border-radius: 50%;
     box-shadow: 0 0 2px rgba(60, 64, 67, 0.12), 0 0 6px rgba(60, 64, 67, 0.15);
     display: flex;
@@ -123,7 +119,8 @@
 
 <div id="profileCardContainer">
   <cr-button on-click="onProfileClick_"
-      aria-label="[[i18n('profileCardButtonLabel', profileState.localProfileName)]]">
+      aria-label="[[i18n(
+          'profileCardButtonLabel', profileState.localProfileName)]]">
     <div id="avatarContainer">
       <img class="profile-avatar" alt="" src="[[profileState.avatarIcon]]">
       <div id="iconContainer" hidden="[[!profileState.isManaged]]">
diff --git a/chrome/browser/resources/signin/profile_picker/profile_card_menu.html b/chrome/browser/resources/signin/profile_picker/profile_card_menu.html
index d1ff0ec..2ba12a0e 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_card_menu.html
+++ b/chrome/browser/resources/signin/profile_picker/profile_card_menu.html
@@ -51,7 +51,7 @@
 
   #profileCardContainer {
     align-items: center;
-    background-color: var(--profile-card-background-color);
+    background-color: var(--profile-card-hover-color);
     border-radius: 12px 0 0 12px;
     display: flex;
     flex-direction: column;
diff --git a/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html b/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html
index e1061c0..2e2e7acd 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html
+++ b/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html
@@ -55,9 +55,10 @@
   }
 
   .profiles-container {
-    --grid-gutter: 16px;
+    --grid-gutter: 8px;
     align-items: center;
     display: grid;
+    /* Actual gap: --grid-gutter + 2 * --profile-item-margin = 16px */
     grid-column-gap: var(--grid-gutter);
     grid-row-gap: var(--grid-gutter);
     grid-template-columns: repeat(auto-fit, calc(var(--profile-item-width) +
@@ -74,7 +75,8 @@
 
   .profile-item {
     align-items: center;
-    border-radius: 12px;
+    border: 1px solid var(--google-grey-300);
+    border-radius: 8px;
     display: flex;
     flex-direction: column;
     height: var(--profile-item-height);
@@ -83,9 +85,15 @@
     width: var(--profile-item-width);
   }
 
-  #addProfile {
-    border: 1px dashed;
-    border-color: var(--google-grey-400);
+  .profile-item:focus-within,
+  .profile-item:hover {
+    background-color: var(--profile-card-hover-color);
+  }
+
+  #addProfile.profile-item {
+    --hover-bg-color: none;
+    border: 1px dashed var(--google-grey-300);
+    box-shadow: none;
     position: relative;
   }
 
@@ -94,13 +102,11 @@
     top: 0;
   }
 
-  cr-icon-button[iron-icon='profiles:add'] {
-    --cr-icon-button-icon-size: var(--avatar-icon-size);
-    --cr-icon-button-size: 84px;
-    --cr-icon-button-fill-color: var(--google-grey-refresh-100);
-    --cr-icon-button-margin-end: 0;
-    --cr-icon-button-margin-start: 0;
-    --cr-icon-button-stroke-color: var(--google-grey-refresh-700);
+  iron-icon[icon='profiles:add'] {
+    --iron-icon-height: var(--avatar-icon-size);
+    --iron-icon-width: var(--avatar-icon-size);
+    --iron-icon-fill-color: var(--google-grey-refresh-100);
+    --iron-icon-stroke-color: var(--google-grey-refresh-700);
   }
 
   #browseAsGuestButton {
@@ -130,15 +136,14 @@
       background: url(images/dark_mode_right_banner_image.svg);
     }
 
-    #addProfile {
+    .profile-item,
+    #addProfile.profile-item {
       border-color: var(--google-grey-refresh-700);
-      position: relative;
     }
 
-    cr-icon-button[iron-icon='profiles:add'] {
-      --cr-icon-button-fill-color: var(--google-grey-refresh-500);
-      --cr-icon-button-stroke-color: rgb(48, 48, 50);
-      color: var(--google-grey-refresh-500);
+    iron-icon[icon='profiles:add'] {
+      --iron-icon-fill-color: var(--google-grey-refresh-500);
+      --iron-icon-stroke-color: rgb(48, 48, 50);
     }
   }
 </style>
@@ -159,14 +164,13 @@
           class="profile-item" profile-state="[[item]]">
       </profile-card>
     </template>
-    <div id="addProfile" class="profile-item">
+    <cr-button id="addProfile" class="profile-item"
+        on-click="onAddProfileClick_" aria-labelledby="addProfileButtonLabel">
       <div id="addProfileButtonLabel" class="profile-card-info">
         $i18n{addSpaceButton}
       </div>
-      <cr-icon-button iron-icon="profiles:add"
-          on-click="onAddProfileClick_" aria-labelledby="addProfileButtonLabel">
-      </cr-icon-button>
-    </div>
+      <iron-icon icon="profiles:add"></iron-icon>
+    </cr-button>
   </div>
 </div>
 <div id="rightBanner" class="banner"></div>
diff --git a/chrome/browser/resources/signin/profile_picker/profile_picker_shared_css.html b/chrome/browser/resources/signin/profile_picker/profile_picker_shared_css.html
index 1ea85fcd..5a5097e 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_picker_shared_css.html
+++ b/chrome/browser/resources/signin/profile_picker/profile_picker_shared_css.html
@@ -5,13 +5,13 @@
       --scrollbar-width: 4px;
       --scrollbar-background: var(--google-grey-refresh-100);
       --footer-margin: 40px;
-      --profile-card-background-color: var(--md-background-color);
+      --profile-card-hover-color: var(--md-background-color);
     }
 
     @media (prefers-color-scheme: dark) {
       :host {
         --scrollbar-background: var(--google-grey-800);
-        --profile-card-background-color: var(--google-grey-800);
+        --profile-card-hover-color: var(--google-grey-800);
       }
     }
 
diff --git a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ChromeBasePreferenceTest.java b/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ChromeBasePreferenceTest.java
index 427b5e2..e9af1be2 100644
--- a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ChromeBasePreferenceTest.java
+++ b/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ChromeBasePreferenceTest.java
@@ -38,8 +38,7 @@
 /**
  * Tests of {@link ChromeBasePreference}.
  *
- * TODO(chouinard): Once SettingsLauncher and SettingsActivity have compontentized interfaces, these
- * test should be moved to //components/browser_ui/settings/.
+ * TODO(crbug.com/1166810): Move these tests to //components/browser_ui/settings/.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 public class ChromeBasePreferenceTest {
diff --git a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ChromeImageViewPreferenceTest.java b/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ChromeImageViewPreferenceTest.java
index 9ea9484..dc3438b27 100644
--- a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ChromeImageViewPreferenceTest.java
+++ b/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ChromeImageViewPreferenceTest.java
@@ -36,8 +36,7 @@
 /**
  * Tests of {@link ChromeImageViewPreference}.
  *
- * TODO(chouinard): Once SettingsLauncher and SettingsActivity have compontentized interfaces, these
- * tests should be moved to //components/browser_ui/settings/.
+ * TODO(crbug.com/1166810): Move these tests to //components/browser_ui/settings/.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 public class ChromeImageViewPreferenceTest {
diff --git a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ManagedPreferencesUtilsTest.java b/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ManagedPreferencesUtilsTest.java
index 4e898ee..9293dea 100644
--- a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ManagedPreferencesUtilsTest.java
+++ b/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ManagedPreferencesUtilsTest.java
@@ -37,9 +37,7 @@
 /**
  * Tests of {@link ManagedPreferencesUtils}.
  *
- * TODO(chouinard): Once SettingsLauncher and SettingsActivity have
- * compontentized interfaces, these test should be moved to
- * //components/browser_ui/settings/.
+ * TODO(crbug.com/1166810): Move these tests to //components/browser_ui/settings/.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 public class ManagedPreferencesUtilsTest {
diff --git a/chrome/browser/signin/chrome_signin_helper.cc b/chrome/browser/signin/chrome_signin_helper.cc
index 5bb714a9..5f54d36a 100644
--- a/chrome/browser/signin/chrome_signin_helper.cc
+++ b/chrome/browser/signin/chrome_signin_helper.cc
@@ -49,7 +49,7 @@
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
 
 #if defined(OS_ANDROID)
-#include "chrome/browser/android/signin/signin_utils.h"
+#include "chrome/browser/android/signin/signin_bridge.h"
 #include "ui/android/view_android.h"
 #else
 #include "chrome/browser/ui/browser_commands.h"
@@ -303,7 +303,7 @@
       // See https://crbug.com/1145031#c5 for details.
       return;
     }
-    SigninUtils::OpenAccountPickerBottomSheet(
+    SigninBridge::OpenAccountPickerBottomSheet(
         window, manage_accounts_params.continue_url.empty()
                     ? chrome::kChromeUINativeNewTabURL
                     : manage_accounts_params.continue_url);
@@ -322,8 +322,7 @@
     auto* window = web_contents->GetNativeView()->GetWindowAndroid();
     if (!window)
       return;
-    SigninUtils::OpenAccountManagementScreen(window, service_type,
-                                             manage_accounts_params.email);
+    SigninBridge::OpenAccountManagementScreen(window, service_type);
   }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH) || defined(OS_ANDROID)
diff --git a/chrome/browser/signin/dice_web_signin_interceptor.cc b/chrome/browser/signin/dice_web_signin_interceptor.cc
index ce9d9bc7f..92b9a1c 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor.cc
@@ -9,6 +9,7 @@
 #include "base/check.h"
 #include "base/hash/hash.h"
 #include "base/i18n/case_conversion.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/optional.h"
 #include "base/strings/utf_string_conversions.h"
@@ -48,6 +49,8 @@
 
 constexpr char kProfileCreationInterceptionDeclinedPref[] =
     "signin.ProfileCreationInterceptionDeclinedPref";
+constexpr char kProfileSwitchInterceptionDeclinedPref[] =
+    "signin.ProfileSwitchInterceptionDeclinedPref";
 
 void RecordSigninInterceptionHeuristicOutcome(
     SigninInterceptionHeuristicOutcome outcome) {
@@ -148,6 +151,7 @@
 void DiceWebSigninInterceptor::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
   registry->RegisterDictionaryPref(kProfileCreationInterceptionDeclinedPref);
+  registry->RegisterDictionaryPref(kProfileSwitchInterceptionDeclinedPref);
   registry->RegisterBooleanPref(prefs::kSigninInterceptionEnabled, true);
 }
 
@@ -176,6 +180,10 @@
       email,
       &g_browser_process->profile_manager()->GetProfileAttributesStorage());
   if (switch_to_entry) {
+    if (HasUserDeclinedProfileSwitch(email)) {
+      return SigninInterceptionHeuristicOutcome::
+          kAbortUserDeclinedProfileForAccount;
+    }
     if (entry)
       *entry = switch_to_entry;
     return SigninInterceptionHeuristicOutcome::kInterceptProfileSwitch;
@@ -275,7 +283,8 @@
       interception_bubble_handle_ = delegate_->ShowSigninInterceptionBubble(
           web_contents, bubble_parameters,
           base::BindOnce(&DiceWebSigninInterceptor::OnProfileSwitchChoice,
-                         base::Unretained(this), entry->GetPath()));
+                         base::Unretained(this), account_info->email,
+                         entry->GetPath()));
       was_interception_ui_displayed_ = true;
     } else {
       // Interception is aborted.
@@ -484,9 +493,12 @@
 }
 
 void DiceWebSigninInterceptor::OnProfileSwitchChoice(
+    const std::string& email,
     const base::FilePath& profile_path,
     SigninInterceptionResult switch_profile) {
   if (switch_profile != SigninInterceptionResult::kAccepted) {
+    if (switch_profile == SigninInterceptionResult::kDeclined)
+      RecordProfileSwitchDeclined(email);
     Reset();
     return;
   }
@@ -570,8 +582,7 @@
                               kProfileCreationInterceptionDeclinedPref);
   std::string key = GetPersistentEmailHash(email);
   base::Optional<int> declined_count = update->FindIntKey(key);
-  update->SetIntKey(
-      key, declined_count.has_value() ? declined_count.value() + 1 : 1);
+  update->SetIntKey(key, declined_count.value_or(0) + 1);
 }
 
 bool DiceWebSigninInterceptor::HasUserDeclinedProfileCreation(
@@ -585,3 +596,31 @@
   return declined_count &&
          declined_count.value() >= kMaxProfileCreationDeclinedCount;
 }
+
+void DiceWebSigninInterceptor::RecordProfileSwitchDeclined(
+    const std::string& email) {
+  DictionaryPrefUpdate update(profile_->GetPrefs(),
+                              kProfileSwitchInterceptionDeclinedPref);
+  std::string key = GetPersistentEmailHash(email);
+  base::Optional<int> declined_count = update->FindIntKey(key);
+  update->SetIntKey(key, declined_count.value_or(0) + 1);
+}
+
+bool DiceWebSigninInterceptor::HasUserDeclinedProfileSwitch(
+    const std::string& email) const {
+  const base::DictionaryValue* pref_data = profile_->GetPrefs()->GetDictionary(
+      kProfileSwitchInterceptionDeclinedPref);
+  base::Optional<int> declined_count =
+      pref_data->FindIntKey(GetPersistentEmailHash(email));
+
+  // The limit is controlled by an experiment. Zero value completely turns off
+  // the profile switch bubble. Negative values mean there is no limit. By
+  // default, there is no limit.
+  int max_profile_switch_declined_count =
+      base::GetFieldTrialParamByFeatureAsInt(
+          kDiceWebSigninInterceptionFeature,
+          "max_profile_switch_declined_count", -1);
+
+  return max_profile_switch_declined_count >= 0 &&
+         declined_count.value_or(0) >= max_profile_switch_declined_count;
+}
diff --git a/chrome/browser/signin/dice_web_signin_interceptor.h b/chrome/browser/signin/dice_web_signin_interceptor.h
index 5465468..97d19149 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor.h
+++ b/chrome/browser/signin/dice_web_signin_interceptor.h
@@ -271,7 +271,8 @@
                                SigninInterceptionResult create);
   // Called after the user chose whether the session should continue in a new
   // profile.
-  void OnProfileSwitchChoice(const base::FilePath& profile_path,
+  void OnProfileSwitchChoice(const std::string& email,
+                             const base::FilePath& profile_path,
                              SigninInterceptionResult switch_profile);
 
   // Called when the new profile is created or loaded from disk.
@@ -287,17 +288,23 @@
   // Returns a 8-bit hash of the email that can be persisted.
   static std::string GetPersistentEmailHash(const std::string& email);
 
-  // Should be called when the user declines profile creation, in order to
-  // remember their decision. This information is stored in prefs. Only a hash
-  // of the email is saved, as Chrome does not need to store the actual email,
-  // but only need to compare emails. The hash has low entropy to ensure it
-  // cannot be reversed.
+  // Should be called when the user declines profile creation or profile switch,
+  // in order to remember their decision. This information is stored in prefs.
+  // Only a hash of the email is saved, as Chrome does not need to store the
+  // actual email, but only need to compare emails. The hash has low entropy to
+  // ensure it cannot be reversed.
   void RecordProfileCreationDeclined(const std::string& email);
+  void RecordProfileSwitchDeclined(const std::string& email);
 
   // Checks if the user previously declined 2 times creating a new profile for
   // this account.
   bool HasUserDeclinedProfileCreation(const std::string& email) const;
 
+  // Checks if the user previously declined more than a threshold number of
+  // times switching to a new profile for this account. The limit is set up
+  // via an experiment parameter.
+  bool HasUserDeclinedProfileSwitch(const std::string& email) const;
+
   Profile* const profile_;
   signin::IdentityManager* const identity_manager_;
   std::unique_ptr<Delegate> delegate_;
diff --git a/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc b/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc
index c3ba70e..0c2ada8 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/callback.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
@@ -482,7 +483,7 @@
   MaybeIntercept(account_info.account_id);
 }
 
-TEST_F(DiceWebSigninInterceptorTest, DeclineRepeatedly) {
+TEST_F(DiceWebSigninInterceptorTest, DeclineCreationRepeatedly) {
   base::HistogramTester histogram_tester;
   AccountInfo primary_account_info =
       identity_test_env()->MakeUnconsentedPrimaryAccountAvailable(
@@ -523,15 +524,6 @@
       SigninInterceptionHeuristicOutcome::kAbortUserDeclinedProfileForAccount,
       1);
 
-  // Even with a slightly different email.
-  MaybeIntercept(account_info.account_id);
-  account_info.email = "al.ice@example.com";
-  EXPECT_EQ(interceptor()->is_interception_in_progress(), false);
-  histogram_tester.ExpectBucketCount(
-      "Signin.Intercept.HeuristicOutcome",
-      SigninInterceptionHeuristicOutcome::kAbortUserDeclinedProfileForAccount,
-      2);
-
   // Another account can still be intercepted.
   account_info.email = "oscar@example.com";
   identity_test_env()->UpdateAccountInfoForAccount(account_info);
@@ -550,6 +542,141 @@
   EXPECT_EQ(interceptor()->is_interception_in_progress(), true);
 }
 
+TEST_F(DiceWebSigninInterceptorTest, DeclineSwitchRepeatedly_NoLimit) {
+  base::HistogramTester histogram_tester;
+  // Setup for profile switch interception.
+  std::string email = "bob@example.com";
+  AccountInfo account_info = identity_test_env()->MakeAccountAvailable(email);
+  Profile* profile_2 = CreateTestingProfile("Profile 2");
+  ProfileAttributesEntry* entry = nullptr;
+  ASSERT_TRUE(profile_attributes_storage()->GetProfileAttributesWithPath(
+      profile_2->GetPath(), &entry));
+  entry->SetAuthInfo(account_info.gaia, base::UTF8ToUTF16(email),
+                     /*is_consented_primary_account=*/false);
+
+  // Test that the profile switch can be declined multiple times.
+  DiceWebSigninInterceptor::Delegate::BubbleParameters expected_parameters = {
+      DiceWebSigninInterceptor::SigninInterceptionType::kProfileSwitch,
+      account_info, AccountInfo(), SkColor()};
+  for (int i = 0; i < 10; ++i) {
+    EXPECT_CALL(*mock_delegate(),
+                ShowSigninInterceptionBubble(
+                    web_contents(), MatchBubbleParameters(expected_parameters),
+                    testing::_))
+        .WillOnce(testing::WithArg<2>(testing::Invoke(
+            [](base::OnceCallback<void(SigninInterceptionResult)> callback) {
+              std::move(callback).Run(SigninInterceptionResult::kDeclined);
+              return nullptr;
+            })));
+    MaybeIntercept(account_info.account_id);
+    EXPECT_EQ(interceptor()->is_interception_in_progress(), false);
+    histogram_tester.ExpectUniqueSample(
+        "Signin.Intercept.HeuristicOutcome",
+        SigninInterceptionHeuristicOutcome::kInterceptProfileSwitch, i + 1);
+  }
+}
+
+TEST_F(DiceWebSigninInterceptorTest,
+       DeclineSwitchRepeatedly_LimitedByExperiment) {
+  const int kMaxProfileSwitchDeclinedCount = 3;
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeatureWithParameters(
+      kDiceWebSigninInterceptionFeature,
+      {{"max_profile_switch_declined_count",
+        base::NumberToString(kMaxProfileSwitchDeclinedCount)}});
+
+  base::HistogramTester histogram_tester;
+  // Setup for profile switch interception.
+  std::string email = "bob@example.com";
+  AccountInfo account_info = identity_test_env()->MakeAccountAvailable(email);
+  Profile* profile_2 = CreateTestingProfile("Profile 2");
+  ProfileAttributesEntry* entry = nullptr;
+  ASSERT_TRUE(profile_attributes_storage()->GetProfileAttributesWithPath(
+      profile_2->GetPath(), &entry));
+  entry->SetAuthInfo(account_info.gaia, base::UTF8ToUTF16(email),
+                     /*is_consented_primary_account=*/false);
+
+  // Decline the interception kMaxProfileSwitchDeclinedCount times.
+  DiceWebSigninInterceptor::Delegate::BubbleParameters expected_parameters = {
+      DiceWebSigninInterceptor::SigninInterceptionType::kProfileSwitch,
+      account_info, AccountInfo(), SkColor()};
+  for (int i = 0; i < kMaxProfileSwitchDeclinedCount; ++i) {
+    EXPECT_CALL(*mock_delegate(),
+                ShowSigninInterceptionBubble(
+                    web_contents(), MatchBubbleParameters(expected_parameters),
+                    testing::_))
+        .WillOnce(testing::WithArg<2>(testing::Invoke(
+            [](base::OnceCallback<void(SigninInterceptionResult)> callback) {
+              std::move(callback).Run(SigninInterceptionResult::kDeclined);
+              return nullptr;
+            })));
+    MaybeIntercept(account_info.account_id);
+    EXPECT_EQ(interceptor()->is_interception_in_progress(), false);
+    histogram_tester.ExpectUniqueSample(
+        "Signin.Intercept.HeuristicOutcome",
+        SigninInterceptionHeuristicOutcome::kInterceptProfileSwitch, i + 1);
+  }
+
+  // Next time the interception is not shown again.
+  MaybeIntercept(account_info.account_id);
+  EXPECT_EQ(interceptor()->is_interception_in_progress(), false);
+  histogram_tester.ExpectBucketCount(
+      "Signin.Intercept.HeuristicOutcome",
+      SigninInterceptionHeuristicOutcome::kAbortUserDeclinedProfileForAccount,
+      1);
+
+  // Another account can still be intercepted.
+  account_info.email = "oscar@example.com";
+  identity_test_env()->UpdateAccountInfoForAccount(account_info);
+  Profile* profile_3 = CreateTestingProfile("Profile 3");
+  ProfileAttributesEntry* entry_2 = nullptr;
+  ASSERT_TRUE(profile_attributes_storage()->GetProfileAttributesWithPath(
+      profile_3->GetPath(), &entry_2));
+  entry_2->SetAuthInfo(account_info.gaia, base::UTF8ToUTF16(account_info.email),
+                       /*is_consented_primary_account=*/false);
+
+  expected_parameters = {
+      DiceWebSigninInterceptor::SigninInterceptionType::kProfileSwitch,
+      account_info, AccountInfo(), SkColor()};
+  EXPECT_CALL(*mock_delegate(),
+              ShowSigninInterceptionBubble(
+                  web_contents(), MatchBubbleParameters(expected_parameters),
+                  testing::_));
+  MaybeIntercept(account_info.account_id);
+  histogram_tester.ExpectBucketCount(
+      "Signin.Intercept.HeuristicOutcome",
+      SigninInterceptionHeuristicOutcome::kInterceptProfileSwitch,
+      kMaxProfileSwitchDeclinedCount + 1);
+  EXPECT_EQ(interceptor()->is_interception_in_progress(), true);
+}
+
+TEST_F(DiceWebSigninInterceptorTest,
+       DeclineSwitchRepeatedly_DisabledByExperiment) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeatureWithParameters(
+      kDiceWebSigninInterceptionFeature,
+      {{"max_profile_switch_declined_count", "0"}});
+
+  base::HistogramTester histogram_tester;
+  // Setup for profile switch interception.
+  std::string email = "bob@example.com";
+  AccountInfo account_info = identity_test_env()->MakeAccountAvailable(email);
+  Profile* profile_2 = CreateTestingProfile("Profile 2");
+  ProfileAttributesEntry* entry = nullptr;
+  ASSERT_TRUE(profile_attributes_storage()->GetProfileAttributesWithPath(
+      profile_2->GetPath(), &entry));
+  entry->SetAuthInfo(account_info.gaia, base::UTF8ToUTF16(email),
+                     /*is_consented_primary_account=*/false);
+
+  // The interception is not shown even at first attempt.
+  MaybeIntercept(account_info.account_id);
+  EXPECT_EQ(interceptor()->is_interception_in_progress(), false);
+  histogram_tester.ExpectBucketCount(
+      "Signin.Intercept.HeuristicOutcome",
+      SigninInterceptionHeuristicOutcome::kAbortUserDeclinedProfileForAccount,
+      1);
+}
+
 TEST_F(DiceWebSigninInterceptorTest, PersistentHash) {
   // The hash is persistent (the value should never change).
   EXPECT_EQ("email_174",
@@ -558,10 +685,13 @@
   EXPECT_NE(interceptor()->GetPersistentEmailHash("bob@gmail.com"),
             interceptor()->GetPersistentEmailHash("alice@example.com"));
   // Equivalent emails get the same hash.
-  EXPECT_EQ(interceptor()->GetPersistentEmailHash("bo.b@gmail.com"),
-            interceptor()->GetPersistentEmailHash("bob@gmail.com"));
   EXPECT_EQ(interceptor()->GetPersistentEmailHash("bob"),
             interceptor()->GetPersistentEmailHash("bob@gmail.com"));
+  EXPECT_EQ(interceptor()->GetPersistentEmailHash("bo.b@gmail.com"),
+            interceptor()->GetPersistentEmailHash("bob@gmail.com"));
+  // Dots are removed only for gmail accounts.
+  EXPECT_NE(interceptor()->GetPersistentEmailHash("alice@example.com"),
+            interceptor()->GetPersistentEmailHash("al.ice@example.com"));
 }
 
 // Interception other than the profile switch require at least 2 accounts.
diff --git a/chrome/browser/spellchecker/spellcheck_service_browsertest.cc b/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
index b4a0510..47173f566 100644
--- a/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
+++ b/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
@@ -479,17 +479,6 @@
   EXPECT_TRUE(GetEnableSpellcheckState());
 }
 
-// When the renderer notifies that it corrected a word, the render process
-// host should record UMA stats about the correction.
-IN_PROC_BROWSER_TEST_F(SpellcheckServiceHostBrowserTest, NotifyChecked) {
-  const char kMisspellRatio[] = "SpellCheck.MisspellRatio";
-
-  base::HistogramTester tester;
-  tester.ExpectTotalCount(kMisspellRatio, 0);
-  NotifyChecked();
-  tester.ExpectTotalCount(kMisspellRatio, 1);
-}
-
 #if BUILDFLAG(USE_RENDERER_SPELLCHECKER)
 // When the renderer requests the spelling service for correcting text, the
 // render process host should call the remote spelling service.
diff --git a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
index 926e2177..6f4d020 100644
--- a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/url_and_title.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/driver/profile_sync_service.h"
 #include "components/sync/engine_impl/bookmark_update_preprocessing.h"
 #include "components/sync/engine_impl/loopback_server/loopback_server_entity.h"
@@ -157,6 +158,15 @@
   }
 };
 
+class SingleClientBookmarksSyncTestWithEnabledClientTags : public SyncTest {
+ public:
+  SingleClientBookmarksSyncTestWithEnabledClientTags()
+      : SyncTest(SINGLE_CLIENT) {
+    feature_list_.InitAndEnableFeature(
+        switches::kSyncUseClientTagForBookmarkCommits);
+  }
+};
+
 IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTestWithVerifier, Sanity) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
 
@@ -1499,4 +1509,29 @@
                   .Wait());
 }
 
+IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTestWithEnabledClientTags,
+                       CommitLocalCreationWithClientTag) {
+  ASSERT_TRUE(SetupSync());
+
+  const std::string kTitle = "Title";
+  const BookmarkNode* folder = AddFolder(
+      kSingleProfileIndex, GetOtherNode(kSingleProfileIndex), 0, kTitle);
+
+  // Wait until the local bookmark gets committed.
+  ASSERT_TRUE(bookmarks_helper::ServerBookmarksEqualityChecker(
+                  GetSyncService(kSingleProfileIndex), GetFakeServer(),
+                  {{kTitle, /*url=*/GURL()}},
+                  /*cryptographer=*/nullptr)
+                  .Wait());
+
+  // Verify the client tag hash was committed to the server.
+  std::vector<sync_pb::SyncEntity> server_bookmarks =
+      GetFakeServer()->GetSyncEntitiesByModelType(syncer::BOOKMARKS);
+  ASSERT_EQ(1u, server_bookmarks.size());
+  EXPECT_EQ(server_bookmarks[0].client_defined_unique_tag(),
+            syncer::ClientTagHash::FromUnhashed(
+                syncer::BOOKMARKS, folder->guid().AsLowercaseString())
+                .value());
+}
+
 }  // namespace
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManager.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManager.java
index 0431494..93bf6b3 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManager.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManager.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.tabmodel;
 
 import android.app.Activity;
-import android.util.Pair;
 
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.NextTabPolicy.NextTabPolicySupplier;
@@ -35,13 +34,11 @@
      * @param nextTabPolicySupplier An instance of {@link NextTabPolicySupplier}.
      * @param index The index of the requested {@link TabModelSelector}. Not guaranteed to be the
      *              index of the {@link TabModelSelector} returned.
-     * @return {@link Pair} of the index and the {@link TabModelSelector} assigned to that index, or
-     *         {@code null} if there are too many
+     * @return A {@link TabModelSelector} index, or {@code null} if there are too many
      *         {@link TabModelSelector}s already built.
      */
-    Pair<Integer, TabModelSelector> requestSelector(Activity activity,
-            TabCreatorManager tabCreatorManager, NextTabPolicySupplier nextTabPolicySupplier,
-            int index);
+    TabModelSelector requestSelector(Activity activity, TabCreatorManager tabCreatorManager,
+            NextTabPolicySupplier nextTabPolicySupplier, int index);
 
     /**
      * An index that represents the invalid state (i.e. when the window wasn't found in the list).
diff --git a/chrome/browser/tabmodel/internal/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManagerImpl.java b/chrome/browser/tabmodel/internal/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManagerImpl.java
index c676af1..0e08abd 100644
--- a/chrome/browser/tabmodel/internal/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManagerImpl.java
+++ b/chrome/browser/tabmodel/internal/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManagerImpl.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.tabmodel;
 
 import android.app.Activity;
-import android.util.Pair;
 import android.util.SparseArray;
 
 import org.chromium.base.ActivityState;
@@ -44,18 +43,10 @@
     }
 
     @Override
-    public Pair<Integer, TabModelSelector> requestSelector(Activity activity,
-            TabCreatorManager tabCreatorManager, NextTabPolicySupplier nextTabPolicySupplier,
-            int index) {
+    public TabModelSelector requestSelector(Activity activity, TabCreatorManager tabCreatorManager,
+            NextTabPolicySupplier nextTabPolicySupplier, int index) {
         if (mAssignments.get(activity) != null) {
-            TabModelSelector assignedSelector = mAssignments.get(activity);
-            for (int i = 0; i < mSelectors.size(); i++) {
-                if (mSelectors.get(i) == assignedSelector) {
-                    return Pair.create(i, assignedSelector);
-                }
-            }
-            throw new IllegalStateException(
-                    "TabModelSelector is assigned to an Activity but has no index.");
+            return mAssignments.get(activity);
         }
 
         if (index < 0 || index >= mSelectors.size()) index = 0;
@@ -77,7 +68,7 @@
         mSelectors.set(index, selector);
         mAssignments.put(activity, selector);
 
-        return Pair.create(index, selector);
+        return selector;
     }
 
     @Override
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index afee70a..6907f9e 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2588,6 +2588,8 @@
       "webui/signin/inline_login_handler_chromeos.h",
       "webui/signin/inline_login_handler_modal_delegate.cc",
       "webui/signin/inline_login_handler_modal_delegate.h",
+      "webui/signin/signin_helper_chromeos.cc",
+      "webui/signin/signin_helper_chromeos.h",
       "webui/version/version_handler_chromeos.cc",
       "webui/version/version_handler_chromeos.h",
       "window_sizer/window_sizer_chromeos.cc",
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 0492b39b..9b1a925 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -778,21 +778,24 @@
         Access payment methods
       </message>
 
-      <!-- Privacy Sandbox Settings. Used by //chrome/browser/privacy/settings. -->
+      <!-- Privacy Sandbox Settings. Used by //chrome/browser/privacy_sandbox. -->
       <message name="IDS_PREFS_PRIVACY_SANDBOX" desc="Title for the 'Privacy sandbox' page.">
         Privacy sandbox
       </message>
-      <message name="IDS_PRIVACY_SANDBOX_TITLE" desc="Title for the Privacy Sandbox toggle.">
-        Web crowd and ad measurement
+      <message name="IDS_PRIVACY_SANDBOX_DESCRIPTION" desc="">
+        Privacy Sandbox is a set of new technologies that will eventually protect people from cross-site tracking mechanisms, like third-party cookies, while preserving web content and services that depend on those mechanisms today. Chrome is working with website developers, publishers, advertisers, and other browsers to develop this new technology.
       </message>
-      <message name="IDS_PRIVACY_SANDBOX_SUBTITLE" desc="Description for the privacy sandbox toggle.">
-        By joining a Web Crowd, you allow websites to make your web experience relevant without using personally identifiable information. Similar to a crowd in a concert, websites and advertisers can only know thousands of users share a similar interest.
+      <message name="IDS_PRIVACY_SANDBOX_TRIAL_TITLE" desc="">
+        Privacy Sandbox trial
       </message>
-      <message name="IDS_PRIVACY_SANDBOX_BULLET_ONE" desc="First bullet point of the Privacy Sandbox description.">
-        Allow websites and apps to give you a more relevant experience using a non-identifiable alias.
+      <message name="IDS_PRIVACY_SANDBOX_TRIAL_DESCRIPTION" desc="">
+        Privacy Sandbox is not widely released yet. During this trial period:\n<ph name="BEGIN_LIST_ITEM1">&lt;li1&gt;</ph>Website and app developers can try out the ability to create relevant experiences without accessing uniquely identifiable information about you<ph name="END_LIST_ITEM1">&lt;/li1&gt;</ph>\n<ph name="BEGIN_LIST_ITEM2">&lt;li2&gt;</ph>Users can opt out of the trial using the controls on this page.<ph name="END_LIST_ITEM2">&lt;/li2&gt;</ph>
       </message>
-      <message name="IDS_PRIVACY_SANDBOX_BULLET_TWO" desc="Second bullet point of the Privacy Sandbox description">
-        Allow advertisers to study ad campaigns using non-identifiable information.
+      <message name="IDS_PRIVACY_SANDBOX_TOGGLE" desc="Title for the Privacy Sandbox toggle.">
+        Crowdmasking
+      </message>
+      <message name="IDS_PRIVACY_SANDBOX_TOGGLE_DESCRIPTION" desc="">
+        When on, websites won’t be able to use information that personally identifies you to make their sites more relevant. Similar to a crowd in a concert, sites can only know that thousands of users share a similar interest.\n\nYour “crowd” is based on sites you’ve visited. Advertisers can only see what sites the whole crowd visits, not what sites you personally visit.
       </message>
       <message name="IDS_PRIVACY_SANDBOX_SNACKBAR_MESSAGE" desc="The text displayed in the snackbar, which gives the user an option to navigate to the Privacy Sandbox settings page. 'Privacy sandbox' has TC ID 5753235213964358658.">
         Explore the Privacy Sandbox
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_BULLET_ONE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_BULLET_ONE.png.sha1
deleted file mode 100644
index c2faed2..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_BULLET_ONE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-c7e4215bdaca99c5779f861088625bf12a23893f
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_BULLET_TWO.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_BULLET_TWO.png.sha1
deleted file mode 100644
index c2faed2..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_BULLET_TWO.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-c7e4215bdaca99c5779f861088625bf12a23893f
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_DESCRIPTION.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..e390b7c6
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+ad621d4b76db8fe8a958472c451b28a81cf65dfe
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_SUBTITLE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_SUBTITLE.png.sha1
deleted file mode 100644
index 9e11f48..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_SUBTITLE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-a9b460c04d8c8fc10c5bc27c649879387db23d77
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_TITLE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_TITLE.png.sha1
deleted file mode 100644
index 9e11f48..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_TITLE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-a9b460c04d8c8fc10c5bc27c649879387db23d77
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_TOGGLE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_TOGGLE.png.sha1
new file mode 100644
index 0000000..e390b7c6
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_TOGGLE.png.sha1
@@ -0,0 +1 @@
+ad621d4b76db8fe8a958472c451b28a81cf65dfe
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_TOGGLE_DESCRIPTION.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_TOGGLE_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..e390b7c6
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_TOGGLE_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+ad621d4b76db8fe8a958472c451b28a81cf65dfe
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_TRIAL_DESCRIPTION.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_TRIAL_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..e390b7c6
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_TRIAL_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+ad621d4b76db8fe8a958472c451b28a81cf65dfe
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_TRIAL_TITLE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_TRIAL_TITLE.png.sha1
new file mode 100644
index 0000000..e390b7c6
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PRIVACY_SANDBOX_TRIAL_TITLE.png.sha1
@@ -0,0 +1 @@
+ad621d4b76db8fe8a958472c451b28a81cf65dfe
\ No newline at end of file
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc
index 618dee3..e65394d 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -1229,6 +1229,7 @@
 }
 
 base::FilePath GetStartupProfilePath(const base::FilePath& user_data_dir,
+                                     const base::FilePath& cur_dir,
                                      const base::CommandLine& command_line) {
 // If the browser is launched due to activation on Windows native notification,
 // the profile id encoded in the notification launch id should be chosen over
@@ -1254,14 +1255,6 @@
         command_line.GetSwitchValuePath(switches::kProfileDirectory));
   }
 
-  return g_browser_process->profile_manager()->GetLastUsedProfileDir(
-      user_data_dir);
-}
-
-#if !BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OS_ANDROID)
-Profile* GetStartupProfile(const base::FilePath& user_data_dir,
-                           const base::FilePath& cur_dir,
-                           const base::CommandLine& command_line) {
   ProfileManager* profile_manager = g_browser_process->profile_manager();
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
   if (ShouldShowProfilePickerAtProcessLaunch(profile_manager, command_line)) {
@@ -1281,13 +1274,21 @@
       // profile picker lives) also exists (or is creatable).
       // TODO(crbug.com/1150326): Refactor this to indicate more directly that
       // profile picker should be shown (returning an enum, or so).
-      return guest_profile;
+      return ProfileManager::GetGuestProfilePath();
     }
   }
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
 
+  return profile_manager->GetLastUsedProfileDir(user_data_dir);
+}
+
+#if !BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OS_ANDROID)
+Profile* GetStartupProfile(const base::FilePath& user_data_dir,
+                           const base::FilePath& cur_dir,
+                           const base::CommandLine& command_line) {
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
   base::FilePath profile_path =
-      GetStartupProfilePath(user_data_dir, command_line);
+      GetStartupProfilePath(user_data_dir, cur_dir, command_line);
   Profile* profile = profile_manager->GetProfile(profile_path);
 
   // If there is no entry in profile attributes storage, the profile is deleted,
diff --git a/chrome/browser/ui/startup/startup_browser_creator.h b/chrome/browser/ui/startup/startup_browser_creator.h
index cb43506..7f4b56f9 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.h
+++ b/chrome/browser/ui/startup/startup_browser_creator.h
@@ -211,6 +211,7 @@
 // Returns the path that contains the profile that should be loaded on process
 // startup.
 base::FilePath GetStartupProfilePath(const base::FilePath& user_data_dir,
+                                     const base::FilePath& cur_dir,
                                      const base::CommandLine& command_line);
 
 #if !BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OS_ANDROID)
diff --git a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
index ee2b5f6..4d68e65 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
@@ -2235,4 +2235,37 @@
 INSTANTIATE_TEST_SUITE_P(All,
                          GuestStartupBrowserCreatorPickerTest,
                          /*ephemeral_guest_profile_enabled=*/testing::Bool());
+
+class StartupBrowserCreatorPickerNoParamsTest
+    : public StartupBrowserCreatorPickerTestBase {};
+
+// Create a secondary profile in a separate PRE run because the existence of
+// profiles is checked during startup in the actual test.
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorPickerNoParamsTest,
+                       PRE_ShowPickerWhenAlreadyLaunched) {
+  CreateMultipleProfiles();
+  // Need to close the browser window manually so that the real test does not
+  // treat it as session restore.
+  CloseAllBrowsers();
+}
+
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorPickerNoParamsTest,
+                       ShowPickerWhenAlreadyLaunched) {
+  // Preprequisite: The picker is shown on the first start-up
+  ASSERT_EQ(0u, chrome::GetTotalBrowserCount());
+
+  // Simulate a second start when the browser is already running.
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  base::FilePath user_data_dir = profile_manager->user_data_dir();
+  base::FilePath current_dir = base::FilePath();
+  base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+  StartupBrowserCreator::ProcessCommandLineAlreadyRunning(
+      command_line, current_dir,
+      GetStartupProfilePath(user_data_dir, current_dir, command_line));
+  base::RunLoop().RunUntilIdle();
+
+  // The picker is shown again if no profile was previously opened.
+  EXPECT_EQ(0u, chrome::GetTotalBrowserCount());
+}
+
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
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 af800f70..dc57b7bd 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
@@ -407,7 +407,7 @@
 #define MAYBE_InvokeUi_ManyPermissions InvokeUi_ManyPermissions
 #endif
 IN_PROC_BROWSER_TEST_F(ExtensionInstallDialogViewInteractiveBrowserTest,
-                       InvokeUi_ManyPermissions) {
+                       MAYBE_InvokeUi_ManyPermissions) {
   for (int i = 0; i < 20; i++)
     AddPermission("Example permission");
   ShowAndVerifyUi();
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc
index 7c4d2d4..59328940 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc
@@ -476,7 +476,7 @@
 #define MAYBE_EmitAccessibilityEventsOnButtonFocusHint EmitAccessibilityEventsOnButtonFocusHint
 #endif
 IN_PROC_BROWSER_TEST_F(OmniboxPopupContentsViewTest,
-                       EmitAccessibilityEventsOnButtonFocusHint) {
+                       MAYBE_EmitAccessibilityEventsOnButtonFocusHint) {
   TestAXEventObserver observer;
   CreatePopupForTestQuery();
   ACMatches matches;
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
index 4a64a6c..43fc9ed 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
@@ -33,7 +33,6 @@
 #include "chrome/browser/web_applications/test/web_app_install_observer.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -64,6 +63,12 @@
     "Win";
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+// Command-line switch that overrides test case input. Takes a comma
+// separated list of testing actions. This aids in development of tests
+// by allowing one to run a single test at a time, and avoid running every
+// test case in the suite.
+const char kWebAppIntegrationTestCase[] = "web-app-integration-test-case";
+
 std::string StripAllWhitespace(std::string line) {
   std::string output;
   output.reserve(line.size());
@@ -86,10 +91,24 @@
   return file_path.AppendASCII(file_name);
 }
 
+std::string GetCommandLineTestOverride() {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(kWebAppIntegrationTestCase)) {
+    return command_line->GetSwitchValueASCII(kWebAppIntegrationTestCase);
+  }
+  return "";
+}
+
 std::vector<std::string> ReadTestInputFile(const std::string& file_name) {
+  std::vector<std::string> test_cases;
+  std::string command_line_test_case = GetCommandLineTestOverride();
+  if (!command_line_test_case.empty()) {
+    test_cases.push_back(StripAllWhitespace(command_line_test_case));
+    return test_cases;
+  }
+
   base::FilePath file = GetTestFilePath(file_name);
   std::string contents;
-  std::vector<std::string> test_cases;
   if (!base::ReadFileToString(file, &contents)) {
     return test_cases;
   }
diff --git a/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc b/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc
index c0fcee0..35ebb98a 100644
--- a/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "build/build_config.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -214,8 +215,14 @@
   ExpectTabs(browser(), {start_url_});
 }
 
+// Flaky on lacros: https://crbug.com/1167176
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#define MAYBE_CaptureLinksNewClient DISABLED_CaptureLinksNewClient
+#else
+#define MAYBE_CaptureLinksNewClient CaptureLinksNewClient
+#endif
 IN_PROC_BROWSER_TEST_F(WebAppDeclarativeLinkCapturingBrowserTest,
-                       CaptureLinksNewClient) {
+                       MAYBE_CaptureLinksNewClient) {
   InstallTestApp("/web_apps/capture_links_new_client.html");
 
   Navigate(browser(), out_of_scope_);
diff --git a/chrome/browser/ui/webui/settings/about_handler.cc b/chrome/browser/ui/webui/settings/about_handler.cc
index c521456..900d4a0 100644
--- a/chrome/browser/ui/webui/settings/about_handler.cc
+++ b/chrome/browser/ui/webui/settings/about_handler.cc
@@ -622,7 +622,9 @@
     response.SetStringKey(
         "aboutPageEndOfLifeMessage",
         l10n_util::GetStringFUTF16(
-            eol_string_id, base::TimeFormatMonthAndYear(eol_info.eol_date),
+            eol_string_id,
+            base::TimeFormatMonthAndYear(eol_info.eol_date,
+                                         /*time_zone=*/icu::TimeZone::getGMT()),
             base::ASCIIToUTF16(has_eol_passed ? chrome::kEolNotificationURL
                                               : chrome::kAutoUpdatePolicyURL)));
   } else {
diff --git a/chrome/browser/ui/webui/signin/OWNERS b/chrome/browser/ui/webui/signin/OWNERS
index 5a70aec..f44c3be 100644
--- a/chrome/browser/ui/webui/signin/OWNERS
+++ b/chrome/browser/ui/webui/signin/OWNERS
@@ -7,4 +7,5 @@
 per-file inline_login_handler_impl*=file://chrome/credential_provider/OWNERS
 per-file inline_login_handler.*=file://chrome/credential_provider/OWNERS
 
-per-file inline_login_*_chromeos.*=file://chromeos/components/account_manager/OWNERS
+per-file inline_login_*_chromeos*=file://chromeos/components/account_manager/OWNERS
+per-file signin_helper_chromeos.*=file://chromeos/components/account_manager/OWNERS
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc b/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc
index 42d96a0a..ab46bdf 100644
--- a/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/ui/webui/chromeos/edu_coexistence_state_tracker.h"
 #include "chrome/browser/ui/webui/signin/inline_login_dialog_chromeos.h"
 #include "chrome/browser/ui/webui/signin/inline_login_handler.h"
+#include "chrome/browser/ui/webui/signin/signin_helper_chromeos.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/components/account_manager/account_manager_factory.h"
 #include "chromeos/constants/chromeos_features.h"
@@ -81,102 +82,6 @@
   return kCrosAddAccountEduFlow;
 }
 
-// A helper class for completing the inline login flow. Primarily, it is
-// responsible for exchanging the auth code, obtained after a successful user
-// sign in, for OAuth tokens and subsequently populating Chrome OS
-// AccountManager with these tokens.
-// This object is supposed to be used in a one-shot fashion and it deletes
-// itself after its work is complete.
-class SigninHelper : public GaiaAuthConsumer {
- public:
-  SigninHelper(
-      chromeos::AccountManager* account_manager,
-      const base::RepeatingClosure& close_dialog_closure,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const std::string& gaia_id,
-      const std::string& email,
-      const std::string& auth_code,
-      const std::string& signin_scoped_device_id)
-      : account_manager_(account_manager),
-        close_dialog_closure_(close_dialog_closure),
-        email_(email),
-        url_loader_factory_(url_loader_factory),
-        gaia_auth_fetcher_(this,
-                           gaia::GaiaSource::kChrome,
-                           url_loader_factory) {
-    account_key_ = ::account_manager::AccountKey{
-        gaia_id, account_manager::AccountType::kGaia};
-
-    DCHECK(!signin_scoped_device_id.empty());
-    gaia_auth_fetcher_.StartAuthCodeForOAuth2TokenExchangeWithDeviceId(
-        auth_code, signin_scoped_device_id);
-  }
-
-  ~SigninHelper() override = default;
-
- protected:
-  // GaiaAuthConsumer overrides.
-  void OnClientOAuthSuccess(const ClientOAuthResult& result) override {
-    // Flow of control after this call:
-    // |AccountManager::UpsertAccount| updates / inserts the account and calls
-    // its |Observer|s, one of which is
-    // |ProfileOAuth2TokenServiceDelegateChromeOS|.
-    // |ProfileOAuth2TokenServiceDelegateChromeOS::OnTokenUpserted| seeds the
-    // Gaia id and email id for this account in |AccountTrackerService| and
-    // invokes |FireRefreshTokenAvailable|. This causes the account to propagate
-    // throughout the Identity Service chain, including in
-    // |AccountFetcherService|. |AccountFetcherService::OnRefreshTokenAvailable|
-    // invokes |AccountTrackerService::StartTrackingAccount|, triggers a fetch
-    // for the account information from Gaia and updates this information into
-    // |AccountTrackerService|. At this point the account will be fully added to
-    // the system.
-    UpsertAccount(result.refresh_token);
-
-    CloseDialogAndExit();
-  }
-
-  void OnClientOAuthFailure(const GoogleServiceAuthError& error) override {
-    // TODO(sinhak): Display an error.
-    CloseDialogAndExit();
-  }
-
-  void UpsertAccount(const std::string& refresh_token) {
-    account_manager_->UpsertAccount(account_key_, email_, refresh_token);
-  }
-
-  void CloseDialogAndExit() {
-    close_dialog_closure_.Run();
-    Exit();
-  }
-
-  void Exit() {
-    base::SequencedTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
-  }
-
-  chromeos::AccountManager* GetAccountManager() { return account_manager_; }
-
-  const std::string GetEmail() { return email_; }
-
-  const scoped_refptr<network::SharedURLLoaderFactory> GetUrlLoaderFactory() {
-    return url_loader_factory_;
-  }
-
- private:
-  // A non-owning pointer to Chrome OS AccountManager.
-  chromeos::AccountManager* const account_manager_;
-  // A closure to close the hosting dialog window.
-  base::RepeatingClosure close_dialog_closure_;
-  // The user's AccountKey for which |this| object has been created.
-  ::account_manager::AccountKey account_key_;
-  // The user's email for which |this| object has been created.
-  const std::string email_;
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-  // Used for exchanging auth code for OAuth tokens.
-  GaiaAuthFetcher gaia_auth_fetcher_;
-
-  DISALLOW_COPY_AND_ASSIGN(SigninHelper);
-};
-
 // A version of SigninHelper for child users. After obtaining OAuth token it
 // logs the parental consent with provided parent id and rapt. After successful
 // consent logging populates Chrome OS AccountManager with the token.
diff --git a/chrome/browser/ui/webui/signin/signin_helper_chromeos.cc b/chrome/browser/ui/webui/signin/signin_helper_chromeos.cc
new file mode 100644
index 0000000..1ea11a85
--- /dev/null
+++ b/chrome/browser/ui/webui/signin/signin_helper_chromeos.cc
@@ -0,0 +1,83 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/signin/signin_helper_chromeos.h"
+
+#include "google_apis/gaia/gaia_auth_fetcher.h"
+
+namespace chromeos {
+
+SigninHelper::SigninHelper(
+    chromeos::AccountManager* account_manager,
+    const base::RepeatingClosure& close_dialog_closure,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    const std::string& gaia_id,
+    const std::string& email,
+    const std::string& auth_code,
+    const std::string& signin_scoped_device_id)
+    : account_manager_(account_manager),
+      close_dialog_closure_(close_dialog_closure),
+      email_(email),
+      url_loader_factory_(std::move(url_loader_factory)),
+      gaia_auth_fetcher_(this, gaia::GaiaSource::kChrome, url_loader_factory) {
+  account_key_ = ::account_manager::AccountKey{
+      gaia_id, ::account_manager::AccountType::kGaia};
+
+  DCHECK(!signin_scoped_device_id.empty());
+  gaia_auth_fetcher_.StartAuthCodeForOAuth2TokenExchangeWithDeviceId(
+      auth_code, signin_scoped_device_id);
+}
+
+SigninHelper::~SigninHelper() = default;
+
+void SigninHelper::OnClientOAuthSuccess(const ClientOAuthResult& result) {
+  // Flow of control after this call:
+  // |AccountManager::UpsertAccount| updates / inserts the account and calls
+  // its |Observer|s, one of which is
+  // |ProfileOAuth2TokenServiceDelegateChromeOS|.
+  // |ProfileOAuth2TokenServiceDelegateChromeOS::OnTokenUpserted| seeds the
+  // Gaia id and email id for this account in |AccountTrackerService| and
+  // invokes |FireRefreshTokenAvailable|. This causes the account to propagate
+  // throughout the Identity Service chain, including in
+  // |AccountFetcherService|. |AccountFetcherService::OnRefreshTokenAvailable|
+  // invokes |AccountTrackerService::StartTrackingAccount|, triggers a fetch
+  // for the account information from Gaia and updates this information into
+  // |AccountTrackerService|. At this point the account will be fully added to
+  // the system.
+  UpsertAccount(result.refresh_token);
+
+  CloseDialogAndExit();
+}
+
+void SigninHelper::OnClientOAuthFailure(const GoogleServiceAuthError& error) {
+  // TODO(sinhak): Display an error.
+  CloseDialogAndExit();
+}
+
+void SigninHelper::UpsertAccount(const std::string& refresh_token) {
+  account_manager_->UpsertAccount(account_key_, email_, refresh_token);
+}
+
+void SigninHelper::CloseDialogAndExit() {
+  close_dialog_closure_.Run();
+  Exit();
+}
+
+void SigninHelper::Exit() {
+  base::SequencedTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
+}
+
+chromeos::AccountManager* SigninHelper::GetAccountManager() {
+  return account_manager_;
+}
+
+std::string SigninHelper::GetEmail() {
+  return email_;
+}
+
+scoped_refptr<network::SharedURLLoaderFactory>
+SigninHelper::GetUrlLoaderFactory() {
+  return url_loader_factory_;
+}
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/signin/signin_helper_chromeos.h b/chrome/browser/ui/webui/signin/signin_helper_chromeos.h
new file mode 100644
index 0000000..1a4bcff
--- /dev/null
+++ b/chrome/browser/ui/webui/signin/signin_helper_chromeos.h
@@ -0,0 +1,72 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_HELPER_CHROMEOS_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_HELPER_CHROMEOS_H_
+
+#include "chromeos/components/account_manager/account_manager.h"
+#include "google_apis/gaia/gaia_auth_consumer.h"
+#include "google_apis/gaia/gaia_auth_fetcher.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+
+namespace chromeos {
+
+// A helper class for completing the inline login flow. Primarily, it is
+// responsible for exchanging the auth code, obtained after a successful user
+// sign in, for OAuth tokens and subsequently populating Chrome OS
+// AccountManager with these tokens.
+// This object is supposed to be used in a one-shot fashion and it deletes
+// itself after its work is complete.
+class SigninHelper : public GaiaAuthConsumer {
+ public:
+  SigninHelper(
+      chromeos::AccountManager* account_manager,
+      const base::RepeatingClosure& close_dialog_closure,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const std::string& gaia_id,
+      const std::string& email,
+      const std::string& auth_code,
+      const std::string& signin_scoped_device_id);
+
+  SigninHelper(const SigninHelper&) = delete;
+  SigninHelper& operator=(const SigninHelper&) = delete;
+  ~SigninHelper() override;
+
+ protected:
+  // GaiaAuthConsumer overrides.
+  void OnClientOAuthSuccess(const ClientOAuthResult& result) override;
+  void OnClientOAuthFailure(const GoogleServiceAuthError& error) override;
+
+  void UpsertAccount(const std::string& refresh_token);
+
+  // Closes the inline login dialog and calls `Exit`.
+  void CloseDialogAndExit();
+
+  // Deletes this object.
+  void Exit();
+
+  chromeos::AccountManager* GetAccountManager();
+
+  // Returns email address of the account being added.
+  std::string GetEmail();
+
+  scoped_refptr<network::SharedURLLoaderFactory> GetUrlLoaderFactory();
+
+ private:
+  // A non-owning pointer to Chrome OS AccountManager.
+  chromeos::AccountManager* const account_manager_;
+  // A closure to close the hosting dialog window.
+  base::RepeatingClosure close_dialog_closure_;
+  // The user's AccountKey for which |this| object has been created.
+  ::account_manager::AccountKey account_key_;
+  // The user's email for which |this| object has been created.
+  const std::string email_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+  // Used for exchanging auth code for OAuth tokens.
+  GaiaAuthFetcher gaia_auth_fetcher_;
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_HELPER_CHROMEOS_H_
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 4083a16..8395422 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1610690109-4d1151c59ad39afee419f9fccf0374e5470965d9.profdata
+chrome-linux-master-1610711423-18f006eac9d09987835ac3798b0f3a285cb6646c.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 2a303a21..fd959ae 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1610679078-fe2bb289a257049a216843cebd228815ff771bd5.profdata
+chrome-win32-master-1610711423-66d1757656af1c1220147c4ed72bfc529787f050.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 743bb94b..4dfa432 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1610679078-e6bf7486827a51823717a01d08f3ea1e32a813ef.profdata
+chrome-win64-master-1610711423-bdaee35d021c9a50df10abe9e0067f035c4386a3.profdata
diff --git a/chrome/installer/mac/signing/model.py b/chrome/installer/mac/signing/model.py
index 490df48..a5d3c40 100644
--- a/chrome/installer/mac/signing/model.py
+++ b/chrome/installer/mac/signing/model.py
@@ -178,7 +178,8 @@
                  creator_code=None,
                  channel_customize=False,
                  package_as_dmg=True,
-                 package_as_pkg=False):
+                 package_as_pkg=False,
+                 inflation_kilobytes=0):
         """Creates a new Distribution object. All arguments are optional.
 
         Args:
@@ -208,6 +209,8 @@
                 the product.
             package_as_pkg: If True, then a .pkg file will be created containing
                 the product.
+            inflation_kilobytes: If non-zero, a blob of this size will be
+                inserted into the DMG. Incompatible with package_as_pkg = True.
         """
         if channel_customize:
             # Side-by-side channels must have a distinct names and creator
@@ -226,6 +229,10 @@
         self.channel_customize = channel_customize
         self.package_as_dmg = package_as_dmg
         self.package_as_pkg = package_as_pkg
+        self.inflation_kilobytes = inflation_kilobytes
+
+        # inflation_kilobytes are only inserted into DMGs
+        assert not self.inflation_kilobytes or self.package_as_dmg
 
     def brandless_copy(self):
         """Derives and returns a copy of this Distribution object, identical
diff --git a/chrome/installer/mac/signing/pipeline.py b/chrome/installer/mac/signing/pipeline.py
index c5a005a..59f20d1 100644
--- a/chrome/installer/mac/signing/pipeline.py
+++ b/chrome/installer/mac/signing/pipeline.py
@@ -413,6 +413,12 @@
     ]
     # yapf: enable
 
+    if dist.inflation_kilobytes:
+        pkg_dmg += [
+            '--copy',
+            '{}/inflation.bin:/.background/inflation.bin'.format(packaging_dir)
+        ]
+
     if config.is_chrome_branded():
         # yapf: disable
         pkg_dmg += [
@@ -506,6 +512,8 @@
         customizations.append(dist.creator_code)
     if dist.branding_code and _include_branding_code_in_app(dist):
         customizations.append(dist.branding_code)
+    if dist.inflation_kilobytes:
+        customizations.append(str(dist.inflation_kilobytes))
 
     return '-'.join(customizations)
 
@@ -607,6 +615,14 @@
                         notary_paths.work,
                         _intermediate_work_dir_name(dist_config.distribution)))
 
+                if dist.inflation_kilobytes:
+                    inflation_path = os.path.join(
+                        paths.packaging_dir(config), 'inflation.bin')
+                    commands.run_command([
+                        'dd', 'if=/dev/urandom', 'of=' + inflation_path,
+                        'bs=1000', 'count={}'.format(dist.inflation_kilobytes)
+                    ])
+
                 if dist.package_as_dmg:
                     dmg_path = _package_and_sign_dmg(paths, dist_config)
 
diff --git a/chrome/installer/mac/signing/pipeline_test.py b/chrome/installer/mac/signing/pipeline_test.py
index 9ef55066..b204b338 100644
--- a/chrome/installer/mac/signing/pipeline_test.py
+++ b/chrome/installer/mac/signing/pipeline_test.py
@@ -823,6 +823,72 @@
             mock.call._package_installer_tools(mock.ANY, mock.ANY),
         ])
 
+    def test_sign_inflated_distribution_dmg(self, **kwargs):
+        manager = mock.Mock()
+        for attr in kwargs:
+            manager.attach_mock(kwargs[attr], attr)
+
+        app_uuid = 'f38ee49c-c55b-4a10-a4f5-aaaa17636b76'
+        dmg_uuid = '9f49067e-a13d-436a-8016-3a22a4f6ef92'
+        kwargs['submit'].side_effect = [app_uuid, dmg_uuid]
+        kwargs['wait_for_results'].side_effect = [
+            iter([app_uuid]), iter([dmg_uuid])
+        ]
+        kwargs[
+            '_package_and_sign_dmg'].return_value = '/$O/AppProduct-99.0.9999.99.dmg'
+
+        class Config(test_config.TestConfig):
+
+            @property
+            def distributions(self):
+                return [
+                    model.Distribution(
+                        inflation_kilobytes=5000,
+                        package_as_dmg=True,
+                        package_as_pkg=False),
+                ]
+
+        config = Config()
+
+        pipeline.sign_all(self.paths, config)
+
+        self.assertEqual(1, kwargs['_package_installer_tools'].call_count)
+
+        manager.assert_has_calls([
+            # First customize the distribution and sign it.
+            mock.call._customize_and_sign_chrome(mock.ANY, mock.ANY,
+                                                 '/$W_1/stable-5000', mock.ANY),
+
+            # Prepare the app for notarization.
+            mock.call.run_command([
+                'zip', '--recurse-paths', '--symlinks', '--quiet',
+                '/$W_1/AppProduct-99.0.9999.99.zip', 'App Product.app'
+            ],
+                                  cwd='/$W_1/stable-5000'),
+            mock.call.submit('/$W_1/AppProduct-99.0.9999.99.zip', mock.ANY),
+            mock.call.shutil.rmtree('/$W_2'),
+            mock.call.wait_for_results({app_uuid: None}.keys(), mock.ANY),
+            mock.call._staple_chrome(
+                self.paths.replace_work('/$W_1/stable-5000'), mock.ANY),
+            mock.call.run_command([
+                'dd', 'if=/dev/urandom',
+                'of=/$I/Product Packaging/inflation.bin', 'bs=1000',
+                'count=5000'
+            ]),
+
+            # Make the DMG.
+            mock.call._package_and_sign_dmg(mock.ANY, mock.ANY),
+
+            # Notarize the DMG.
+            mock.call.submit('/$O/AppProduct-99.0.9999.99.dmg', mock.ANY),
+            mock.call.wait_for_results({dmg_uuid: None}.keys(), mock.ANY),
+            mock.call.staple('/$O/AppProduct-99.0.9999.99.dmg'),
+            mock.call.shutil.rmtree('/$W_1'),
+
+            # Package the installer tools.
+            mock.call._package_installer_tools(mock.ANY, mock.ANY),
+        ])
+
     def test_sign_basic_distribution_pkg(self, **kwargs):
         manager = mock.Mock()
         for attr in kwargs:
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 1d98268..b466ee1 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2505,7 +2505,6 @@
         "../browser/chromeos/file_manager/video_player_browsertest.cc",
         "../browser/chromeos/file_manager/video_player_jstest.cc",
         "../browser/chromeos/first_run/drive_first_run_browsertest.cc",
-        "../browser/chromeos/full_restore/app_launch_handler_browsertest.cc",
         "../browser/chromeos/input_method/input_method_engine_browsertests.cc",
         "../browser/chromeos/input_method/native_input_method_engine_browsertest.cc",
         "../browser/chromeos/input_method/textinput_browsertest.cc",
diff --git a/chrome/test/data/extensions/api_test/automation/sites/iframenav/iframe-button.html b/chrome/test/data/extensions/api_test/automation/sites/iframenav/iframe-button.html
new file mode 100644
index 0000000..3008b51
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/automation/sites/iframenav/iframe-button.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div>
+  <button onClick="doNav();">
+    First Button
+  </button>
+</div>
+</body>
+<script>
+function doNav() {
+   window.parent.doNavFunc();
+}
+</script>
+</html>
diff --git a/chrome/test/data/extensions/api_test/automation/sites/iframenav/iframe-button2.html b/chrome/test/data/extensions/api_test/automation/sites/iframenav/iframe-button2.html
new file mode 100644
index 0000000..f04382e
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/automation/sites/iframenav/iframe-button2.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div>
+  <button>
+    Second Button
+  </button>
+</div>
+</body>
+</html>
diff --git a/chrome/test/data/extensions/api_test/automation/sites/iframenav/iframe-top.html b/chrome/test/data/extensions/api_test/automation/sites/iframenav/iframe-top.html
new file mode 100644
index 0000000..23490f7b
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/automation/sites/iframenav/iframe-top.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+
+<div>
+  <iframe id="frame1" src="iframe-button.html"></iframe>
+</div>
+
+<script>
+  function doNavFunc() {
+    let iframe = document.getElementById("frame1");
+    iframe.src="iframe-button2.html";
+  }
+</script>
+
+</html>
diff --git a/chrome/test/data/extensions/api_test/automation/tests/desktop/iframenav.html b/chrome/test/data/extensions/api_test/automation/tests/desktop/iframenav.html
new file mode 100644
index 0000000..664957b0
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/automation/tests/desktop/iframenav.html
@@ -0,0 +1,7 @@
+<!--
+ * Copyright 2021 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.
+-->
+<script src="common.js"></script>
+<script src="iframenav.js"></script>
diff --git a/chrome/test/data/extensions/api_test/automation/tests/desktop/iframenav.js b/chrome/test/data/extensions/api_test/automation/tests/desktop/iframenav.js
new file mode 100644
index 0000000..ef80ef33a
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/automation/tests/desktop/iframenav.js
@@ -0,0 +1,54 @@
+// Copyright 2021 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.
+
+var firstButton = undefined;
+var clicked = false;
+
+function checkNodes(rootNode) {
+  // Grab the first button and hold on to it.
+  if (!firstButton) {
+     firstButton = findAutomationNode(rootNode, function(n) {
+       return n.name == 'First Button'
+     });
+  }
+
+  // Search for second button.
+  const secondButton = findAutomationNode(rootNode, function(n) {
+     return n.name == 'Second Button'
+  });
+
+  // If we have the first but not the second, click the
+  // switch button to swap out the frame.
+  if (firstButton && !secondButton && !clicked) {
+    firstButton.doDefault();
+    clicked = true;
+  }
+
+  // Still waiting for expected state. If neither button
+  // was found yet, keep polling.
+  if (!firstButton || firstButton.role || !secondButton) {
+     setTimeout(checkNodes.bind(this, rootNode), 100);
+     return;
+  }
+
+  // Repetitive check with the above condition, but to make it clear to the
+  // reader what's being tested. If the first button's role is still valid,
+  // the test failed because its tree should have been destroyed.
+  chrome.test.assertTrue(!!firstButton && !!secondButton);
+  chrome.test.assertTrue(!firstButton.role && !!secondButton.role);
+  chrome.test.succeed();
+}
+
+var allTests = [
+  function treeDestroyedTest() {
+    chrome.test.getConfig(function(config) {
+      const url = 'http://a.com:' + config.testServer.port + '/iframenav/iframe-top.html';
+      chrome.tabs.create({url: url});
+
+      chrome.automation.getDesktop(checkNodes.bind(this));
+    });
+  },
+];
+
+chrome.test.runTests(allTests);
diff --git a/chrome/test/data/webui/signin/local_profile_customization_focus_test.js b/chrome/test/data/webui/signin/local_profile_customization_focus_test.js
index 23b649c..c50181d 100644
--- a/chrome/test/data/webui/signin/local_profile_customization_focus_test.js
+++ b/chrome/test/data/webui/signin/local_profile_customization_focus_test.js
@@ -48,7 +48,7 @@
   /** @param {!ProfilePickerMainViewElement} mainView */
   function navigateToProfileCreationFromMainView(mainView) {
     mainView.$$('#addProfile').focus();
-    mainView.$$('#addProfile').querySelectorAll('cr-icon-button')[0].click();
+    mainView.$$('#addProfile').click();
     flush();
   }
 
diff --git a/chrome/test/data/webui/signin/profile_picker_app_test.js b/chrome/test/data/webui/signin/profile_picker_app_test.js
index 4083880..7203d5e 100644
--- a/chrome/test/data/webui/signin/profile_picker_app_test.js
+++ b/chrome/test/data/webui/signin/profile_picker_app_test.js
@@ -82,8 +82,8 @@
         'profiles-list-changed', [browserProxy.profileSample]);
     flushTasks();
     assertEquals(
-        mainView.shadowRoot.querySelectorAll('profile-card').length, 1);
-    mainView.$$('#addProfile').querySelectorAll('cr-icon-button')[0].click();
+        mainView.$$('#wrapper').querySelectorAll('profile-card').length, 1);
+    mainView.$$('#addProfile').click();
     await waitForProfileCretionLoad();
     assertEquals(
         testElement.shadowRoot.querySelectorAll('[slot=view]').length, 2);
diff --git a/chromecast/browser/cast_network_contexts.cc b/chromecast/browser/cast_network_contexts.cc
index de42438..3f33336e 100644
--- a/chromecast/browser/cast_network_contexts.cc
+++ b/chromecast/browser/cast_network_contexts.cc
@@ -30,6 +30,10 @@
 namespace chromecast {
 namespace shell {
 
+namespace {
+constexpr char kCookieStoreFile[] = "Cookies";
+}  // namespace
+
 // SharedURLLoaderFactory backed by a CastNetworkContexts and its system
 // NetworkContext. Transparently handles crashes.
 class CastNetworkContexts::URLLoaderFactoryForSystem
@@ -193,6 +197,13 @@
   network_context_params->accept_language =
       CastHttpUserAgentSettings::AcceptLanguage();
 
+  auto* browser_context = CastBrowserProcess::GetInstance()->browser_context();
+  DCHECK(browser_context);
+  network_context_params->cookie_path =
+      browser_context->GetPath().Append(kCookieStoreFile);
+  network_context_params->restore_old_session_cookies = false;
+  network_context_params->persist_session_cookies = true;
+
   // Disable idle sockets close on memory pressure, if instructed by DCS. On
   // memory constrained devices:
   // 1. if idle sockets are closed when memory pressure happens, cast_shell will
diff --git a/chromeos/components/sync_wifi/local_network_collector_impl.cc b/chromeos/components/sync_wifi/local_network_collector_impl.cc
index f24a7271..7e0fe08e 100644
--- a/chromeos/components/sync_wifi/local_network_collector_impl.cc
+++ b/chromeos/components/sync_wifi/local_network_collector_impl.cc
@@ -209,14 +209,8 @@
       properties->type_properties->get_wifi()->auto_connect));
   proto.set_is_preferred(IsPreferredProtoFromMojo(properties->priority));
 
-  if (properties->metered) {
-    proto.set_metered(
-        properties->metered->active_value
-            ? sync_pb::
-                  WifiConfigurationSpecifics_MeteredOption_METERED_OPTION_YES
-            : sync_pb::
-                  WifiConfigurationSpecifics_MeteredOption_METERED_OPTION_NO);
-  }
+  // TODO(crbug/1128692): Restore support for the metered property when mojo
+  // networks track the "Automatic" state.
 
   bool is_proxy_modified =
       network_metadata_store_->GetIsFieldExternallyModified(
diff --git a/chromeos/components/sync_wifi/network_type_conversions.cc b/chromeos/components/sync_wifi/network_type_conversions.cc
index d3b30e7..a201ba3 100644
--- a/chromeos/components/sync_wifi/network_type_conversions.cc
+++ b/chromeos/components/sync_wifi/network_type_conversions.cc
@@ -276,17 +276,8 @@
           ? 1
           : 0);
 
-  if (specifics.has_metered() &&
-      specifics.metered() !=
-          sync_pb::
-              WifiConfigurationSpecifics_MeteredOption_METERED_OPTION_UNSPECIFIED &&
-      specifics.metered() !=
-          sync_pb::
-              WifiConfigurationSpecifics_MeteredOption_METERED_OPTION_AUTO) {
-    config->metered = network_config::mojom::MeteredConfig::New(
-        specifics.metered() ==
-        sync_pb::WifiConfigurationSpecifics_MeteredOption_METERED_OPTION_YES);
-  }
+  // TODO(crbug/1128692): Restore support for the metered property when mojo
+  // networks track the "Automatic" state.
 
   // For backwards compatibility, any available custom nameservers are still
   // applied when the dns_option is not set.
diff --git a/chromeos/constants/chromeos_switches.cc b/chromeos/constants/chromeos_switches.cc
index 8c308b92..09f86733 100644
--- a/chromeos/constants/chromeos_switches.cc
+++ b/chromeos/constants/chromeos_switches.cc
@@ -33,10 +33,6 @@
 
 const char kAggressiveCacheDiscardThreshold[] = "aggressive-cache-discard";
 
-const char kAggressiveTabDiscardThreshold[] = "aggressive-tab-discard";
-
-const char kAggressiveThreshold[] = "aggressive";
-
 // If this flag is passed, failed policy fetches will not cause profile
 // initialization to fail. This is useful for tests because it means that
 // tests don't have to mock out the policy infrastructure.
@@ -145,13 +141,6 @@
 // Sets ARC Terms Of Service hostname url for testing.
 const char kArcTosHostForTests[] = "arc-tos-host-for-tests";
 
-// If this flag is present then the device had ARC M available and gets ARC N
-// when updating.
-// TODO(pmarko): Remove this when we assess that it's not necessary anymore:
-// crbug.com/761348.
-const char kArcTransitionMigrationRequired[] =
-    "arc-transition-migration-required";
-
 // If this flag is set, it indicates that this device is a "Cellular First"
 // device. Cellular First devices use cellular telephone data networks as
 // their primary means of connecting to the internet.
@@ -169,8 +158,6 @@
 // non-user-writable JPEG file).
 const char kChildWallpaperSmall[] = "child-wallpaper-small";
 
-const char kConservativeThreshold[] = "conservative";
-
 // Forces CrOS region value.
 const char kCrosRegion[] = "cros-region";
 
@@ -235,9 +222,6 @@
 // Disables requests for an enterprise machine certificate during attestation.
 const char kDisableMachineCertRequest[] = "disable-machine-cert-request";
 
-// Disables the multiple display layout UI.
-const char kDisableMultiDisplayLayout[] = "disable-multi-display-layout";
-
 // Disables the ChromeVox hint timer in OOBE, which can lead to unexpected
 // behavior during tests.
 const char kDisableOOBEChromeVoxHintTimerForTesting[] =
@@ -363,11 +347,6 @@
 // "keyboard-bottom-left", keyboard-bottom-right", "keyboard-top-right".
 const char kFingerprintSensorLocation[] = "fingerprint-sensor-location";
 
-// Forces Chrome to use CertVerifyProcBuiltin for verification of server
-// certificates, ignoring the status of
-// net::features::kCertVerifierBuiltinFeature.
-const char kForceCertVerifierBuiltin[] = "force-cert-verifier-builtin";
-
 // Passed to Chrome the first time that it's run after the system boots.
 // Not passed on restart after sign out.
 const char kFirstExecAfterBoot[] = "first-exec-after-boot";
@@ -490,11 +469,6 @@
 // notes. If unset, a hardcoded list is used instead.
 const char kNoteTakingAppIds[] = "note-taking-app-ids";
 
-// Forces OOBE/login to force show a comma-separated list of screens from
-// chromeos::kScreenNames in oobe_screen.cc. Supported screens are:
-//   user-image
-const char kOobeForceShowScreen[] = "oobe-force-show-screen";
-
 // Allows the eula url to be overridden for tests.
 const char kOobeEulaUrlForTests[] = "oobe-eula-url-for-tests";
 
diff --git a/chromeos/constants/chromeos_switches.h b/chromeos/constants/chromeos_switches.h
index f979f91..307586aa 100644
--- a/chromeos/constants/chromeos_switches.h
+++ b/chromeos/constants/chromeos_switches.h
@@ -25,9 +25,6 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kAggressiveCacheDiscardThreshold[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
-extern const char kAggressiveTabDiscardThreshold[];
-COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kAggressiveThreshold[];
-COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kAllowFailedPolicyFetchForTest[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kAllowRAInDevMode[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kAppAutoLaunched[];
@@ -57,12 +54,9 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcScale[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcStartMode[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcTosHostForTests[];
-COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
-extern const char kArcTransitionMigrationRequired[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kCellularFirst[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kChildWallpaperLarge[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kChildWallpaperSmall[];
-COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kConservativeThreshold[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kCrosRegion[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kCrosRegionsMode[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kCrosRegionsModeHide[];
@@ -91,8 +85,6 @@
 extern const char kDisableLoginAnimations[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kDisableMachineCertRequest[];
-COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
-extern const char kDisableMultiDisplayLayout[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kDisableNewZIPUnpacker[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kDisableOOBEChromeVoxHintTimerForTesting[];
@@ -153,8 +145,6 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kFingerprintSensorLocation[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
-extern const char kForceCertVerifierBuiltin[];
-COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kForceDevToolsAvailable[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kForceFirstRunUI[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
@@ -190,7 +180,6 @@
 extern const char kNeedArcMigrationPolicyCheck[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kNoteTakingAppIds[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kOobeEulaUrlForTests[];
-COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kOobeForceShowScreen[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kOobeForceTabletFirstRun[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kOobeGuestSession[];
diff --git a/chromeos/lacros/BUILD.gn b/chromeos/lacros/BUILD.gn
index c70fef7c..403b557 100644
--- a/chromeos/lacros/BUILD.gn
+++ b/chromeos/lacros/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chromeos/rules.gni")
 import("//build/config/chromeos/ui_mode.gni")
 import("//build/config/linux/gtk/gtk.gni")
 import("//build/lacros/lacros_resource_sizes.gni")
@@ -30,3 +31,41 @@
 lacros_resource_sizes_test("resource_sizes_lacros_chrome") {
   data_deps = [ "//chrome:chrome" ]
 }
+
+# "cros_board" set in the GN args defines the board used to build lacros-chrome,
+# while the "override_board" here defines the board on which to test the built
+# binary, for more details, please refer to the "generate_runner_script"
+# template in //build/config/chromeos/rules.gni.
+template("lacros_tast_test_helper") {
+  forward_variables_from(invoker, [ "override_board" ])
+  assert(defined(override_board), "override_board is required")
+  lacros_tast_test(target_name) {
+    # TODO(crbug.com/1158590): use --attr-expr instead of hard-coding the tests.
+    tast_tests = [
+      "lacros.AudioPlay",
+      "lacros.AppLauncherLaunch",
+      "lacros.Basic",
+      "lacros.ShelfLaunch",
+    ]
+  }
+}
+
+# On Chromium CI, only a very limited number of boards are supported:
+# amd64-generic, eve, kevin etc, so just hard-coding them for simplicity.
+if (is_chromeos_device) {
+  _boards = string_split(cros_boards, ":") +
+            string_split(cros_boards_with_qemu_images, ":")
+  foreach(b, _boards) {
+    if (b == "amd64-generic") {
+      lacros_tast_test_helper("lacros_tast_tests_amd64_generic") {
+        override_board = "amd64-generic"
+      }
+    }
+
+    if (b == "eve") {
+      lacros_tast_test_helper("lacros_tast_tests_eve") {
+        override_board = "eve"
+      }
+    }
+  }
+}
diff --git a/chromeos/memory/pressure/pressure.cc b/chromeos/memory/pressure/pressure.cc
index af0954b..92c33a2 100644
--- a/chromeos/memory/pressure/pressure.cc
+++ b/chromeos/memory/pressure/pressure.cc
@@ -82,8 +82,8 @@
 }
 
 bool SupportsPSI() {
-  static bool supports_psi =
-      base::PathExists(base::FilePath("/proc/pressure/"));
+  // Checking path existence in procfs is fast.
+  static bool supports_psi = access("/proc/pressure/", F_OK) == 0;
   return supports_psi;
 }
 
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 39781322..2006fd7 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -225,6 +225,7 @@
       "//components/safe_browsing/ios:unit_tests",
       "//components/security_state/ios:unit_tests",
       "//components/signin/ios/browser:unit_tests",
+      "//components/signin/public/identity_manager/objc:unit_tests",
       "//components/translate/ios/browser:unit_tests",
       "//components/ukm/ios:unit_tests",
     ]
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index 293c7dd..4923996 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -396,6 +396,7 @@
     "enterprise/arc_data_snapshotd_bridge_unittest.cc",
     "enterprise/arc_data_snapshotd_manager_unittest.cc",
     "enterprise/snapshot_hours_policy_service_unittest.cc",
+    "enterprise/snapshot_reboot_controller_unittest.cc",
     "enterprise/snapshot_session_controller_unittest.cc",
     "ime/arc_ime_service_unittest.cc",
     "ime/key_event_result_receiver_unittest.cc",
diff --git a/components/arc/enterprise/BUILD.gn b/components/arc/enterprise/BUILD.gn
index 58e3c83..107fabd 100644
--- a/components/arc/enterprise/BUILD.gn
+++ b/components/arc/enterprise/BUILD.gn
@@ -13,6 +13,8 @@
     "arc_data_snapshotd_manager.h",
     "snapshot_hours_policy_service.cc",
     "snapshot_hours_policy_service.h",
+    "snapshot_reboot_controller.cc",
+    "snapshot_reboot_controller.h",
     "snapshot_session_controller.cc",
     "snapshot_session_controller.h",
   ]
@@ -23,6 +25,7 @@
     "//chromeos/cryptohome",
     "//chromeos/dbus:dbus",
     "//chromeos/dbus/arc:arc",
+    "//chromeos/dbus/power",
     "//chromeos/dbus/upstart:upstart",
     "//components/arc:prefs",
     "//components/prefs",
diff --git a/components/arc/enterprise/arc_data_snapshotd_manager.cc b/components/arc/enterprise/arc_data_snapshotd_manager.cc
index 1e105d6..9c02f71b 100644
--- a/components/arc/enterprise/arc_data_snapshotd_manager.cc
+++ b/components/arc/enterprise/arc_data_snapshotd_manager.cc
@@ -52,12 +52,33 @@
          !command_line->HasSwitch(chromeos::switches::kLoginManager);
 }
 
+// Returns true if it is the first Chrome start up after reboot.
+bool IsFirstExecAfterBoot() {
+  return user_manager::UserManager::Get() &&
+         user_manager::UserManager::Get()->IsFirstExecAfterBoot();
+}
+
 // Enables ozone platform headless via command line.
 void EnableHeadlessMode() {
   auto* command_line = base::CommandLine::ForCurrentProcess();
   command_line->AppendSwitchASCII(switches::kOzonePlatform, "headless");
 }
 
+// Returns non-empty account ID string if a MGS is active.
+// Otherwise returns an empty string.
+std::string GetMgsCryptohomeAccountId() {
+  // Take snapshots only for MGSs.
+  if (user_manager::UserManager::Get() &&
+      user_manager::UserManager::Get()->IsLoggedInAsPublicAccount() &&
+      user_manager::UserManager::Get()->GetActiveUser()) {
+    return cryptohome::Identification(user_manager::UserManager::Get()
+                                          ->GetActiveUser()
+                                          ->GetAccountId())
+        .id();
+  }
+  return std::string();
+}
+
 }  // namespace
 
 bool ArcDataSnapshotdManager::is_snapshot_enabled_for_testing_ = false;
@@ -287,7 +308,8 @@
   if (IsRestoredSession()) {
     state_ = State::kRestored;
   } else {
-    if (snapshot_.is_blocked_ui_mode() && IsSnapshotEnabled()) {
+    if (snapshot_.is_blocked_ui_mode() && IsSnapshotEnabled() &&
+        IsFirstExecAfterBoot()) {
       state_ = State::kBlockedUi;
       EnableHeadlessMode();
     }
@@ -334,7 +356,7 @@
     std::move(callback).Run();
     return;
   }
-  std::string account_id = GetCryptohomeAccountId();
+  std::string account_id = GetMgsCryptohomeAccountId();
   if (!account_id.empty() && IsSnapshotEnabled() &&
       (snapshot_.last() || snapshot_.previous())) {
     state_ = State::kLoading;
@@ -439,9 +461,11 @@
     case State::kMgsLaunched:
     case State::kMgsToLaunch:
       state_ = State::kStopping;
+      snapshot_.set_blocked_ui_mode(false);
       if (session_controller_)
         session_controller_->RemoveObserver(this);
       session_controller_.reset();
+      reboot_controller_.reset();
       break;
     // Otherwise, stop all flows, clear snapshots and do not restart browser.
     case State::kNone:
@@ -454,11 +478,48 @@
 }
 
 void ArcDataSnapshotdManager::OnSnapshotUpdateEndTimeChanged() {
-  if (policy_service_.snapshot_update_end_time().is_null())
+  if (policy_service_.snapshot_update_end_time().is_null()) {
+    // Process the end of the snapshot update interval.
+    if (reboot_controller_) {
+      // Stop the reboot process if already requested.
+      snapshot_.set_blocked_ui_mode(false);
+      snapshot_.Sync();
+    }
+    reboot_controller_.reset();
     return;
+  }
   if (!IsSnapshotEnabled())
     return;
-  // TODO(pbond): may be start a reboot process to update a snapshot.
+  // Snapshot can be updated if necessary. Inside the snapshot update interval.
+  // Do not request the reboot of device if already requested.
+  if (reboot_controller_)
+    return;
+  // Do not reboot if last and previous snapshots exist and should not be
+  // updated.
+  if (snapshot_.last() && !snapshot_.last()->updated() &&
+      snapshot_.previous() && !snapshot_.previous()->updated()) {
+    return;
+  }
+
+  switch (state_) {
+    case State::kNone:
+    case State::kLoading:
+    case State::kRestored:
+    case State::kRunning:
+      snapshot_.set_blocked_ui_mode(true);
+      snapshot_.Sync();
+
+      // Request  device to be reboot in a blocked UI mode.
+      reboot_controller_ = std::make_unique<SnapshotRebootController>();
+      return;
+    case State::kBlockedUi:
+    case State::kMgsToLaunch:
+    case State::kMgsLaunched:
+    case State::kStopping:
+      // Do not reboot the device if in blocked UI mode or in process of
+      // disabling the feature.
+      return;
+  }
 }
 
 bool ArcDataSnapshotdManager::IsSnapshotEnabled() {
@@ -663,7 +724,7 @@
     OnSnapshotTaken(false /* success */);
     return;
   }
-  std::string account_id = GetCryptohomeAccountId();
+  std::string account_id = GetMgsCryptohomeAccountId();
   if (account_id.empty() || state_ != State::kMgsLaunched) {
     LOG(ERROR) << "Cryptohome account ID is empty.";
     OnSnapshotTaken(false /* success */);
@@ -738,18 +799,5 @@
     LOG(ERROR) << "Failed to update UI progress bar.";
 }
 
-std::string ArcDataSnapshotdManager::GetCryptohomeAccountId() {
-  // Take snapshots only for MGSs.
-  if (user_manager::UserManager::Get() &&
-      user_manager::UserManager::Get()->IsLoggedInAsPublicAccount() &&
-      user_manager::UserManager::Get()->GetActiveUser()) {
-    return cryptohome::Identification(user_manager::UserManager::Get()
-                                          ->GetActiveUser()
-                                          ->GetAccountId())
-        .id();
-  }
-  return "";
-}
-
 }  // namespace data_snapshotd
 }  // namespace arc
diff --git a/components/arc/enterprise/arc_data_snapshotd_manager.h b/components/arc/enterprise/arc_data_snapshotd_manager.h
index f9c1512..a754587 100644
--- a/components/arc/enterprise/arc_data_snapshotd_manager.h
+++ b/components/arc/enterprise/arc_data_snapshotd_manager.h
@@ -12,6 +12,7 @@
 #include "base/memory/weak_ptr.h"
 #include "components/arc/enterprise/arc_apps_tracker.h"
 #include "components/arc/enterprise/snapshot_hours_policy_service.h"
+#include "components/arc/enterprise/snapshot_reboot_controller.h"
 #include "components/arc/enterprise/snapshot_session_controller.h"
 #include "components/session_manager/core/session_manager_observer.h"
 
@@ -107,6 +108,8 @@
 
     bool is_last() const { return is_last_; }
 
+    bool updated() const { return updated_; }
+
    private:
     SnapshotInfo(const std::string& os_version,
                  const std::string& creation_date,
@@ -255,6 +258,10 @@
     session_controller_ = std::move(session_controller);
   }
 
+  SnapshotRebootController* get_reboot_controller_for_testing() const {
+    return reboot_controller_.get();
+  }
+
  private:
   // Attempts to arc-data-snapshotd daemon regardless of state of the class.
   // Runs |callback| once finished.
@@ -307,10 +314,6 @@
   // Called once a progress bar is updated.
   void OnUiUpdated(bool success);
 
-  // Returns non-empty account ID string if a MGS is active.
-  // Otherwise returns an empty string.
-  std::string GetCryptohomeAccountId();
-
   static bool is_snapshot_enabled_for_testing_;
 
   SnapshotHoursPolicyService policy_service_;
@@ -333,6 +336,9 @@
   // events.
   std::unique_ptr<SnapshotSessionController> session_controller_;
 
+  // Initialized only when the device reboot is requested.
+  std::unique_ptr<SnapshotRebootController> reboot_controller_;
+
   // Used for cancelling previously posted tasks to daemon.
   base::WeakPtrFactory<ArcDataSnapshotdManager> daemon_weak_ptr_factory_{this};
   // WeakPtrFactory to use for callbacks.
diff --git a/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc b/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc
index bf26288..3a46b87 100644
--- a/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc
+++ b/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc
@@ -147,11 +147,16 @@
     upstart_client_ = std::make_unique<TestUpstartClient>();
 
     arc::prefs::RegisterLocalStatePrefs(local_state_.registry());
+
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        chromeos::switches::kFirstExecAfterBoot);
   }
 
   void SetUp() override { SetDBusClientAvailability(true /* is_available */); }
 
   void TearDown() override {
+    ArcDataSnapshotdManager::set_snapshot_enabled_for_testing(
+        false /* enabled */);
     manager_.reset();
     apps_tracker_ = nullptr;
     delegate_ = nullptr;
@@ -689,6 +694,69 @@
                  false /* expected_blocked_ui_mode */);
 }
 
+// Test that if the snapshot update interval is not started (end time is null),
+// the device is not rebooted.
+TEST_F(ArcDataSnapshotdManagerBasicTest, OnSnapshotUpdateEndTimeNullFailure) {
+  SetupLocalState(false /* blocked_ui_mode */);
+  ArcDataSnapshotdManager::set_snapshot_enabled_for_testing(true /* enabled */);
+  ExpectStopDaemon(false /* success */);
+  auto* manager = CreateManager(base::DoNothing());
+  manager->OnSnapshotUpdateEndTimeChanged();
+  EXPECT_FALSE(manager->get_reboot_controller_for_testing());
+}
+
+// Test that if snapshot feature is not enabled, the device is not rebooted.
+TEST_F(ArcDataSnapshotdManagerBasicTest,
+       OnSnapshotUpdateEndTimeDisabledFailure) {
+  SetupLocalState(false /* blocked_ui_mode */);
+  auto* manager = CreateManager(base::DoNothing());
+  manager->policy_service()->set_snapshot_update_end_time_for_testing(
+      base::Time::Now());
+  manager->OnSnapshotUpdateEndTimeChanged();
+  EXPECT_FALSE(manager->get_reboot_controller_for_testing());
+}
+
+// Test that if both snapshots exist and no need to update them, the device is
+// not rebooted.
+TEST_F(ArcDataSnapshotdManagerBasicTest, OnSnapshotUpdateEndTimeExistsFailure) {
+  SetupLocalState(false /* blocked_ui_mode */);
+  ArcDataSnapshotdManager::set_snapshot_enabled_for_testing(true /* enabled */);
+  ExpectStopDaemon(false /* success */);
+  auto* manager = CreateManager(base::DoNothing());
+  manager->policy_service()->set_snapshot_update_end_time_for_testing(
+      base::Time::Now());
+  manager->OnSnapshotUpdateEndTimeChanged();
+  EXPECT_FALSE(manager->get_reboot_controller_for_testing());
+}
+
+// Test the end time changed twice in a raw scenario.
+TEST_F(ArcDataSnapshotdManagerBasicTest, OnSnapshotUpdateEndTimeChanged) {
+  ArcDataSnapshotdManager::set_snapshot_enabled_for_testing(true /* enabled */);
+  ExpectStopDaemon(false /* success */);
+  auto* manager = CreateManager(base::DoNothing());
+  manager->policy_service()->set_snapshot_update_end_time_for_testing(
+      base::Time::Now());
+  // Request reboot in blocked UI mode.
+  manager->OnSnapshotUpdateEndTimeChanged();
+  EXPECT_TRUE(manager->get_reboot_controller_for_testing());
+  CheckSnapshots(0 /* expected_snapshots_number */,
+                 true /* expected_blocked_ui_mode */);
+
+  // The reboot is requested above.
+  manager->OnSnapshotUpdateEndTimeChanged();
+  EXPECT_TRUE(manager->get_reboot_controller_for_testing());
+  CheckSnapshots(0 /* expected_snapshots_number */,
+                 true /* expected_blocked_ui_mode */);
+
+  // Stop requesting a reboot if not inside the snapshot update interval.
+  manager->policy_service()->set_snapshot_update_end_time_for_testing(
+      base::Time());
+  manager->OnSnapshotUpdateEndTimeChanged();
+  EXPECT_FALSE(manager->get_reboot_controller_for_testing());
+  CheckSnapshots(0 /* expected_snapshots_number */,
+                 false /* expected_blocked_ui_mode */);
+}
+
 // Test that no one state should lead to any changes except when MGS is expected
 // to be launched.
 TEST_P(ArcDataSnapshotdManagerStateTest, OnSnapshotSessionStarted) {
@@ -774,6 +842,35 @@
                  false /* expected_blocked_ui_mode */);
 }
 
+TEST_P(ArcDataSnapshotdManagerStateTest, OnSnapshotUpdateEndTimeChanged) {
+  ArcDataSnapshotdManager::set_snapshot_enabled_for_testing(true /* enabled */);
+  ExpectStopDaemon(false /* success */);
+  auto* manager = CreateManager(base::DoNothing());
+  manager->policy_service()->set_snapshot_update_end_time_for_testing(
+      base::Time::Now());
+  manager->set_state_for_testing(expected_state());
+  manager->OnSnapshotUpdateEndTimeChanged();
+
+  switch (expected_state()) {
+    case ArcDataSnapshotdManager::State::kNone:
+    case ArcDataSnapshotdManager::State::kLoading:
+    case ArcDataSnapshotdManager::State::kRestored:
+    case ArcDataSnapshotdManager::State::kRunning:
+      EXPECT_TRUE(manager->get_reboot_controller_for_testing());
+      CheckSnapshots(0 /* expected_snapshots_number */,
+                     true /* expected_blocked_ui_mode */);
+      break;
+    case ArcDataSnapshotdManager::State::kBlockedUi:
+    case ArcDataSnapshotdManager::State::kMgsToLaunch:
+    case ArcDataSnapshotdManager::State::kMgsLaunched:
+    case ArcDataSnapshotdManager::State::kStopping:
+      EXPECT_FALSE(manager->get_reboot_controller_for_testing());
+      CheckSnapshots(0 /* expected_snapshots_number */,
+                     false /* expected_blocked_ui_mode */);
+      break;
+  }
+}
+
 INSTANTIATE_TEST_SUITE_P(
     ArcDataSnapshotdManagerTest,
     ArcDataSnapshotdManagerStateTest,
diff --git a/components/arc/enterprise/snapshot_hours_policy_service.h b/components/arc/enterprise/snapshot_hours_policy_service.h
index 640aa4e..3a8618f 100644
--- a/components/arc/enterprise/snapshot_hours_policy_service.h
+++ b/components/arc/enterprise/snapshot_hours_policy_service.h
@@ -69,6 +69,10 @@
   }
   const util::WallClockTimer* get_timer_for_testing() const { return &timer_; }
 
+  void set_snapshot_update_end_time_for_testing(base::Time time) {
+    snapshot_update_end_time_ = time;
+  }
+
  private:
   // Processes the policy update: either ArcEnabled and
   // DeviceArcDataSnapshotHours.
diff --git a/components/arc/enterprise/snapshot_reboot_controller.cc b/components/arc/enterprise/snapshot_reboot_controller.cc
new file mode 100644
index 0000000..fbd8959d
--- /dev/null
+++ b/components/arc/enterprise/snapshot_reboot_controller.cc
@@ -0,0 +1,91 @@
+// 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 "components/arc/enterprise/snapshot_reboot_controller.h"
+
+#include "base/logging.h"
+#include "chromeos/dbus/power/power_manager_client.h"
+#include "components/session_manager/core/session_manager.h"
+#include "components/user_manager/user_manager.h"
+
+namespace arc {
+namespace data_snapshotd {
+
+namespace {
+
+// Requests reboot. The reboot may not happen immediately.
+void RequestReboot() {
+  chromeos::PowerManagerClient::Get()->RequestRestart(
+      power_manager::REQUEST_RESTART_OTHER, "ARC data snapshot");
+}
+
+// Returns true if any user is logged in.
+bool IsUserLoggedIn() {
+  return user_manager::UserManager::Get() &&
+         user_manager::UserManager::Get()->IsUserLoggedIn();
+}
+
+}  // namespace
+
+const int kMaxRebootAttempts = 3;
+const base::TimeDelta kRebootAttemptDelay = base::TimeDelta::FromMinutes(5);
+
+SnapshotRebootController::SnapshotRebootController() {
+  session_manager::SessionManager::Get()->AddObserver(this);
+  if (IsUserLoggedIn()) {
+    // TODO (pbond): show notification.
+  } else {
+    // The next operation after reboot is blocking, ensure no one uses device
+    // during the next 5 mins.
+    StartRebootTimer();
+  }
+}
+
+SnapshotRebootController::~SnapshotRebootController() {
+  session_manager::SessionManager::Get()->RemoveObserver(this);
+}
+
+void SnapshotRebootController::OnSessionStateChanged() {
+  if (IsUserLoggedIn()) {
+    StopRebootTimer();
+    // TODO(pbond): show notification.
+  } else {
+    // The next operation after reboot is blocking, ensure no one uses device
+    // during the next 5 mins.
+    StartRebootTimer();
+  }
+}
+
+void SnapshotRebootController::StartRebootTimer() {
+  if (reboot_timer_.IsRunning())
+    return;
+  reboot_attempts_ = 0;
+  SetRebootTimer();
+}
+
+void SnapshotRebootController::SetRebootTimer() {
+  reboot_timer_.Start(FROM_HERE, kRebootAttemptDelay,
+                      base::BindOnce(&SnapshotRebootController::OnRebootTimer,
+                                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void SnapshotRebootController::StopRebootTimer() {
+  reboot_attempts_ = 0;
+  if (!reboot_timer_.IsRunning())
+    return;
+  reboot_timer_.Stop();
+}
+
+void SnapshotRebootController::OnRebootTimer() {
+  reboot_attempts_++;
+  RequestReboot();
+  if (reboot_attempts_ >= kMaxRebootAttempts) {
+    LOG(ERROR) << "The number of reboot attempts exceeded for ARC snapshots.";
+    return;
+  }
+  SetRebootTimer();
+}
+
+}  // namespace data_snapshotd
+}  // namespace arc
diff --git a/components/arc/enterprise/snapshot_reboot_controller.h b/components/arc/enterprise/snapshot_reboot_controller.h
new file mode 100644
index 0000000..e0ad5871
--- /dev/null
+++ b/components/arc/enterprise/snapshot_reboot_controller.h
@@ -0,0 +1,52 @@
+// 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 COMPONENTS_ARC_ENTERPRISE_SNAPSHOT_REBOOT_CONTROLLER_H_
+#define COMPONENTS_ARC_ENTERPRISE_SNAPSHOT_REBOOT_CONTROLLER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "components/session_manager/core/session_manager_observer.h"
+
+namespace arc {
+namespace data_snapshotd {
+
+// Maximum number of consequent reboot attempts.
+extern const int kMaxRebootAttempts;
+
+// A time delta between reboot attempts.
+extern const base::TimeDelta kRebootAttemptDelay;
+
+// This class observes the MGS state changes and requests a reboot as soon as
+// possible.
+class SnapshotRebootController
+    : public session_manager::SessionManagerObserver {
+ public:
+  SnapshotRebootController();
+  SnapshotRebootController(const SnapshotRebootController&) = delete;
+  ~SnapshotRebootController() override;
+
+  SnapshotRebootController& operator=(const SnapshotRebootController&) = delete;
+
+  // session_manager::SessionManagerObserver overrides:
+  void OnSessionStateChanged() override;
+
+  base::OneShotTimer* get_timer_for_testing() { return &reboot_timer_; }
+
+ private:
+  void StartRebootTimer();
+  void SetRebootTimer();
+  void StopRebootTimer();
+  void OnRebootTimer();
+
+  base::OneShotTimer reboot_timer_;
+  int reboot_attempts_ = 0;
+
+  base::WeakPtrFactory<SnapshotRebootController> weak_ptr_factory_{this};
+};
+
+}  // namespace data_snapshotd
+}  // namespace arc
+
+#endif  // COMPONENTS_ARC_ENTERPRISE_SNAPSHOT_REBOOT_CONTROLLER_H_
diff --git a/components/arc/enterprise/snapshot_reboot_controller_unittest.cc b/components/arc/enterprise/snapshot_reboot_controller_unittest.cc
new file mode 100644
index 0000000..52a7b5a1
--- /dev/null
+++ b/components/arc/enterprise/snapshot_reboot_controller_unittest.cc
@@ -0,0 +1,105 @@
+// 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 "components/arc/enterprise/snapshot_reboot_controller.h"
+#include "base/test/task_environment.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/power/fake_power_manager_client.h"
+#include "components/account_id/account_id.h"
+#include "components/session_manager/core/session_manager.h"
+#include "components/user_manager/fake_user_manager.h"
+#include "components/user_manager/scoped_user_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace arc {
+namespace data_snapshotd {
+
+namespace {
+
+constexpr char kPublicAccountEmail[] = "public@localhost";
+
+chromeos::FakePowerManagerClient* client() {
+  return chromeos::FakePowerManagerClient::Get();
+}
+
+}  // namespace
+
+class SnapshotRebootControllerTest : public testing::Test {
+ public:
+  void SetUp() override {
+    // Initialize fake D-Bus client.
+    chromeos::DBusThreadManager::Initialize();
+    EXPECT_TRUE(chromeos::DBusThreadManager::Get()->IsUsingFakes());
+
+    chromeos::PowerManagerClient::InitializeFake();
+
+    fake_user_manager_ = new user_manager::FakeUserManager();
+    scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
+        base::WrapUnique(fake_user_manager_));
+  }
+
+  void TearDown() override {
+    chromeos::PowerManagerClient::Shutdown();
+
+    chromeos::DBusThreadManager::Shutdown();
+    scoped_user_manager_.reset();
+    fake_user_manager_ = nullptr;
+  }
+
+  void LoginUserSession() {
+    auto account_id = AccountId::FromUserEmail(kPublicAccountEmail);
+    user_manager()->AddUser(account_id);
+
+    user_manager()->UserLoggedIn(
+        account_id, account_id.GetUserEmail() + "-hash", false, false);
+  }
+
+  void FastForwardAttempt() {
+    task_environment_.FastForwardBy(kRebootAttemptDelay);
+    task_environment_.RunUntilIdle();
+  }
+
+  user_manager::FakeUserManager* user_manager() { return fake_user_manager_; }
+
+ private:
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+
+  session_manager::SessionManager session_manager_;
+  user_manager::FakeUserManager* fake_user_manager_;
+  std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
+};
+
+TEST_F(SnapshotRebootControllerTest, UserLoggedIn) {
+  LoginUserSession();
+  EXPECT_TRUE(user_manager()->IsUserLoggedIn());
+  SnapshotRebootController controller;
+  EXPECT_FALSE(controller.get_timer_for_testing()->IsRunning());
+  EXPECT_EQ(0, client()->num_request_restart_calls());
+}
+
+TEST_F(SnapshotRebootControllerTest, BasicReboot) {
+  SnapshotRebootController controller;
+  EXPECT_EQ(0, client()->num_request_restart_calls());
+  for (int i = 0; i < kMaxRebootAttempts; i++) {
+    EXPECT_TRUE(controller.get_timer_for_testing()->IsRunning());
+    FastForwardAttempt();
+    EXPECT_EQ(i + 1, client()->num_request_restart_calls());
+  }
+  EXPECT_FALSE(controller.get_timer_for_testing()->IsRunning());
+}
+
+TEST_F(SnapshotRebootControllerTest, OnSessionStateChangedLogin) {
+  SnapshotRebootController controller;
+  EXPECT_TRUE(controller.get_timer_for_testing()->IsRunning());
+
+  LoginUserSession();
+  controller.OnSessionStateChanged();
+  EXPECT_FALSE(controller.get_timer_for_testing()->IsRunning());
+  EXPECT_EQ(0, client()->num_request_restart_calls());
+}
+
+}  // namespace data_snapshotd
+}  // namespace arc
diff --git a/components/arc/mojom/BUILD.gn b/components/arc/mojom/BUILD.gn
index 74128c4..5784d111 100644
--- a/components/arc/mojom/BUILD.gn
+++ b/components/arc/mojom/BUILD.gn
@@ -391,6 +391,7 @@
       ":mojom",
       "//ui/base/ime:text_input_types",
       "//ui/events",
+      "//ui/events/ozone/layout",
     ]
   }
 
diff --git a/components/arc/mojom/ime_mojom_traits.cc b/components/arc/mojom/ime_mojom_traits.cc
index ddf8023..870444b 100644
--- a/components/arc/mojom/ime_mojom_traits.cc
+++ b/components/arc/mojom/ime_mojom_traits.cc
@@ -7,6 +7,8 @@
 #include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/events/keycodes/dom/keycode_converter.h"
 #include "ui/events/keycodes/keyboard_code_conversion.h"
+#include "ui/events/ozone/layout/keyboard_layout_engine.h"
+#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
 
 namespace mojo {
 using KeyEventUniquePtr = std::unique_ptr<ui::KeyEvent>;
@@ -38,8 +40,10 @@
 
   ui::KeyboardCode key_code;
   ui::DomKey dom_key;
-  if (!DomCodeToUsLayoutDomKey(dom_code, flags, &dom_key, &key_code))
+  if (!ui::KeyboardLayoutEngineManager::GetKeyboardLayoutEngine()->Lookup(
+          dom_code, flags, &dom_key, &key_code)) {
     return false;
+  }
 
   *out = std::make_unique<ui::KeyEvent>(type, key_code, dom_code, flags,
                                         dom_key, base::TimeTicks::Now());
diff --git a/components/autofill/core/browser/autofill_download_manager.cc b/components/autofill/core/browser/autofill_download_manager.cc
index e356433a..097f5b9 100644
--- a/components/autofill/core/browser/autofill_download_manager.cc
+++ b/components/autofill/core/browser/autofill_download_manager.cc
@@ -590,10 +590,12 @@
 std::vector<variations::VariationID>*
     AutofillDownloadManager::active_experiments_ = nullptr;
 
-AutofillDownloadManager::AutofillDownloadManager(AutofillDriver* driver,
-                                                 Observer* observer,
-                                                 const std::string& api_key,
-                                                 LogManager* log_manager)
+AutofillDownloadManager::AutofillDownloadManager(
+    AutofillDriver* driver,
+    Observer* observer,
+    const std::string& api_key,
+    IsRawMetadataUploadingEnabled is_raw_metadata_uploading_enabled,
+    LogManager* log_manager)
     : driver_(driver),
       observer_(observer),
       api_key_(api_key),
@@ -601,7 +603,8 @@
       autofill_server_url_(GetAutofillServerURL()),
       throttle_reset_period_(GetThrottleResetPeriod()),
       max_form_cache_size_(kAutofillDownloadManagerMaxFormCacheSize),
-      loader_backoff_(&kAutofillBackoffPolicy) {
+      loader_backoff_(&kAutofillBackoffPolicy),
+      is_raw_metadata_uploading_enabled_(is_raw_metadata_uploading_enabled) {
   DCHECK(observer_);
 }
 
@@ -610,6 +613,7 @@
     : AutofillDownloadManager(driver,
                               observer,
                               kDefaultAPIKey,
+                              IsRawMetadataUploadingEnabled(false),
                               /*log_manager=*/nullptr) {}
 
 AutofillDownloadManager::~AutofillDownloadManager() = default;
@@ -706,7 +710,8 @@
   std::vector<FormSignature> form_signatures;
   if (!form.EncodeUploadRequest(available_field_types, form_was_autofilled,
                                 login_form_signature, observed_submission,
-                                &upload, &form_signatures)) {
+                                is_raw_metadata_uploading_enabled_, &upload,
+                                &form_signatures)) {
     return false;
   }
 
diff --git a/components/autofill/core/browser/autofill_download_manager.h b/components/autofill/core/browser/autofill_download_manager.h
index 8aaececf..afd5e4dc 100644
--- a/components/autofill/core/browser/autofill_download_manager.h
+++ b/components/autofill/core/browser/autofill_download_manager.h
@@ -19,6 +19,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string_piece.h"
 #include "base/time/time.h"
+#include "base/types/strong_alias.h"
 #include "components/autofill/core/browser/autofill_type.h"
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/common/signatures.h"
@@ -50,6 +51,8 @@
     REQUEST_QUERY,
     REQUEST_UPLOAD,
   };
+  using IsRawMetadataUploadingEnabled =
+      base::StrongAlias<class IsRawMetadataUploadingEnabledTag, bool>;
 
   // An interface used to notify clients of AutofillDownloadManager.
   class Observer {
@@ -79,16 +82,18 @@
 
   // |driver| must outlive this instance.
   // |observer| - observer to notify on successful completion or error.
-  // Uses an API callback function that gives an empty string.
-  AutofillDownloadManager(AutofillDriver* driver, Observer* observer);
-  // |driver| must outlive this instance.
-  // |observer| - observer to notify on successful completion or error.
   // |api_key| - API key to add to API request query parameters. Will only take
   //   effect if using API.
-  AutofillDownloadManager(AutofillDriver* driver,
-                          Observer* observer,
-                          const std::string& api_key,
-                          LogManager* log_manager);
+  AutofillDownloadManager(
+      AutofillDriver* driver,
+      Observer* observer,
+      const std::string& api_key,
+      IsRawMetadataUploadingEnabled is_raw_metadata_uploading_enabled,
+      LogManager* log_manager);
+  // |driver| must outlive this instance.
+  // |observer| - observer to notify on successful completion or error.
+  // Uses an API callback function that gives an empty string.
+  AutofillDownloadManager(AutofillDriver* driver, Observer* observer);
   virtual ~AutofillDownloadManager();
 
   // Starts a query request to Autofill servers. The observer is called with the
@@ -225,6 +230,10 @@
   // Used for exponential backoff of requests.
   net::BackoffEntry loader_backoff_;
 
+  // Whether form data (e.g. form and field names) can be uploaded in clear
+  // text.
+  bool is_raw_metadata_uploading_enabled_ = false;
+
   base::WeakPtrFactory<AutofillDownloadManager> weak_factory_{this};
 };
 
diff --git a/components/autofill/core/browser/autofill_download_manager_unittest.cc b/components/autofill/core/browser/autofill_download_manager_unittest.cc
index 47fde356..bf06e7d 100644
--- a/components/autofill/core/browser/autofill_download_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_download_manager_unittest.cc
@@ -158,10 +158,12 @@
                                                Observer* observer,
                                                const std::string& api_key,
                                                size_t length)
-      : AutofillDownloadManager(driver,
-                                observer,
-                                api_key,
-                                /*log_manager=*/nullptr),
+      : AutofillDownloadManager(
+            driver,
+            observer,
+            api_key,
+            AutofillDownloadManager::IsRawMetadataUploadingEnabled(false),
+            /*log_manager=*/nullptr),
         length_(length) {}
 
  protected:
@@ -343,8 +345,10 @@
   form_structures.push_back(std::make_unique<FormStructure>(form));
 
   // Make download manager.
-  AutofillDownloadManager download_manager(&driver_, this, "dummykey",
-                                           /*log_manager=*/nullptr);
+  AutofillDownloadManager download_manager(
+      &driver_, this, "dummykey",
+      AutofillDownloadManager::IsRawMetadataUploadingEnabled(false),
+      /*log_manager=*/nullptr);
 
   // Request with id 0.
   base::HistogramTester histogram;
@@ -533,8 +537,10 @@
   std::vector<std::unique_ptr<FormStructure>> form_structures;
   form_structures.push_back(std::make_unique<FormStructure>(form));
 
-  AutofillDownloadManager download_manager(&driver_, this, "dummykey",
-                                           /*log_manager=*/nullptr);
+  AutofillDownloadManager download_manager(
+      &driver_, this, "dummykey",
+      AutofillDownloadManager::IsRawMetadataUploadingEnabled(false),
+      /*log_manager=*/nullptr);
 
   // Start the query request and look if it is successful. No response was
   // received yet.
@@ -722,7 +728,7 @@
       // We don't want upload throttling for testing purpose.
       {features::kAutofillUploadThrottling});
 
-  // Build the form structures that we want to query.
+  // Build the form structures that we want to upload.
   FormData form;
   FormFieldData field;
 
@@ -739,8 +745,10 @@
   form_structure.set_submission_source(SubmissionSource::FORM_SUBMISSION);
 
   std::unique_ptr<PrefService> pref_service = test::PrefServiceForTesting();
-  AutofillDownloadManager download_manager(&driver_, this, "dummykey",
-                                           /*log_manager=*/nullptr);
+  AutofillDownloadManager download_manager(
+      &driver_, this, "dummykey",
+      AutofillDownloadManager::IsRawMetadataUploadingEnabled(false),
+      /*log_manager=*/nullptr);
   EXPECT_TRUE(download_manager.StartUploadRequest(form_structure, true,
                                                   ServerFieldTypeSet(), "",
                                                   true, pref_service.get()));
@@ -786,6 +794,80 @@
                               net::HTTP_OK, 1);
 }
 
+TEST_F(AutofillDownloadManagerTest, UploadWithRawMetadata) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      // Enabled
+      {},
+      // Disabled
+      // We don't want upload throttling for testing purpose.
+      {features::kAutofillUploadThrottling});
+
+  for (bool is_raw_metadata_uploading_enabled : {false, true}) {
+    SCOPED_TRACE(testing::Message() << "is_raw_metadata_uploading_enabled = "
+                                    << is_raw_metadata_uploading_enabled);
+    // Build the form structures that we want to upload.
+    FormData form;
+    form.name = UTF8ToUTF16("form1");
+    FormFieldData field;
+
+    field.name = UTF8ToUTF16("firstname");
+    field.form_control_type = "text";
+    form.fields.push_back(field);
+
+    field.name = UTF8ToUTF16("lastname");
+    field.form_control_type = "text";
+    form.fields.push_back(field);
+    FormStructure form_structure(form);
+    form_structure.set_submission_source(SubmissionSource::FORM_SUBMISSION);
+
+    std::unique_ptr<PrefService> pref_service = test::PrefServiceForTesting();
+    AutofillDownloadManager download_manager(
+        &driver_, this, "dummykey",
+        AutofillDownloadManager::IsRawMetadataUploadingEnabled(
+            is_raw_metadata_uploading_enabled),
+        /*log_manager=*/nullptr);
+    EXPECT_TRUE(download_manager.StartUploadRequest(form_structure, true,
+                                                    ServerFieldTypeSet(), "",
+                                                    true, pref_service.get()));
+
+    // Inspect the request that the test URL loader sent.
+    ASSERT_EQ(1, test_url_loader_factory_.NumPending());
+    network::TestURLLoaderFactory::PendingRequest* request =
+        test_url_loader_factory_.GetPendingRequest(0);
+
+    // Assert some of the fields within the uploaded proto to make sure it was
+    // filled with something else than default data.
+    AutofillUploadRequest autofill_upload_request;
+    EXPECT_TRUE(
+        GetUploadRequestProtoFromRequest(request, &autofill_upload_request));
+    AutofillUploadContents upload = autofill_upload_request.upload();
+    EXPECT_GT(upload.client_version().size(), 0U);
+    EXPECT_EQ(FormSignature(upload.form_signature()),
+              form_structure.form_signature());
+    // Only a few strings are tested, full testing happens in FormStructure's
+    // tests.
+    ASSERT_EQ(is_raw_metadata_uploading_enabled, upload.has_form_name());
+    ASSERT_EQ(is_raw_metadata_uploading_enabled, upload.field()[0].has_name());
+    ASSERT_EQ(is_raw_metadata_uploading_enabled, upload.field()[1].has_type());
+    if (is_raw_metadata_uploading_enabled) {
+      EXPECT_EQ(form.name, UTF8ToUTF16(upload.form_name()));
+      EXPECT_EQ(form.fields[0].name, UTF8ToUTF16(upload.field()[0].name()));
+      EXPECT_EQ(form.fields[1].form_control_type, upload.field()[1].type());
+    }
+
+    test_url_loader_factory_.SimulateResponseForPendingRequest(
+        request->request.url.spec(), "");
+    EXPECT_EQ(1U, responses_.size());
+    EXPECT_EQ(AutofillDownloadManagerTest::UPLOAD_SUCCESSFULL,
+              responses_.front().type_of_response);
+
+    ASSERT_EQ(0, test_url_loader_factory_.NumPending());
+    test_url_loader_factory_.ClearResponses();
+    responses_.clear();
+  }
+}
+
 TEST_F(AutofillDownloadManagerTest, BackoffLogic_Query) {
   FormData form;
   FormFieldData field;
diff --git a/components/autofill/core/browser/autofill_handler.cc b/components/autofill/core/browser/autofill_handler.cc
index e513358..d3036399 100644
--- a/components/autofill/core/browser/autofill_handler.cc
+++ b/components/autofill/core/browser/autofill_handler.cc
@@ -102,6 +102,13 @@
          channel != version_info::Channel::BETA;
 }
 
+// static
+bool AutofillHandler::IsRawMetadataUploadingEnabled(
+    version_info::Channel channel) {
+  return channel == version_info::Channel::CANARY ||
+         channel == version_info::Channel::DEV;
+}
+
 AutofillHandler::AutofillHandler(
     AutofillDriver* driver,
     LogManager* log_manager,
@@ -112,7 +119,10 @@
       is_rich_query_enabled_(IsRichQueryEnabled(channel)) {
   if (enable_download_manager == ENABLE_AUTOFILL_DOWNLOAD_MANAGER) {
     download_manager_ = std::make_unique<AutofillDownloadManager>(
-        driver, this, GetAPIKeyForUrl(channel), log_manager);
+        driver, this, GetAPIKeyForUrl(channel),
+        AutofillDownloadManager::IsRawMetadataUploadingEnabled(
+            IsRawMetadataUploadingEnabled(channel)),
+        log_manager);
   }
 }
 
diff --git a/components/autofill/core/browser/autofill_handler.h b/components/autofill/core/browser/autofill_handler.h
index 53aeb287..7ad7078d 100644
--- a/components/autofill/core/browser/autofill_handler.h
+++ b/components/autofill/core/browser/autofill_handler.h
@@ -55,6 +55,10 @@
   // neither on the STABLE nor BETA release channel.
   static bool IsRichQueryEnabled(version_info::Channel channel);
 
+  // Raw metadata uploading enabled iff this Chrome instance is on Canary or Dev
+  // channel.
+  static bool IsRawMetadataUploadingEnabled(version_info::Channel channel);
+
   // TODO(crbug.com/1151542): Move to anonymous namespace once
   // AutofillManager::OnLoadedServerPredictions() moves to AutofillHandler.
   static void LogAutofillTypePredictionsAvailable(
diff --git a/components/autofill/core/browser/autofill_regexes.cc b/components/autofill/core/browser/autofill_regexes.cc
index 4875a11..fc38787 100644
--- a/components/autofill/core/browser/autofill_regexes.cc
+++ b/components/autofill/core/browser/autofill_regexes.cc
@@ -4,8 +4,8 @@
 
 #include "components/autofill/core/browser/autofill_regexes.h"
 
+#include <map>
 #include <memory>
-#include <unordered_map>
 #include <utility>
 
 #include "base/check.h"
@@ -28,19 +28,20 @@
   AutofillRegexes() = default;
 
   // Returns the compiled regex matcher corresponding to |pattern|.
-  icu::RegexMatcher* GetMatcher(const base::string16& pattern);
+  icu::RegexMatcher* GetMatcher(const base::StringPiece16& pattern);
 
  private:
   ~AutofillRegexes() = default;
 
   // Maps patterns to their corresponding regex matchers.
-  std::unordered_map<base::string16, std::unique_ptr<icu::RegexMatcher>>
+  std::map<base::string16, std::unique_ptr<icu::RegexMatcher>, std::less<>>
       matchers_;
 
   DISALLOW_COPY_AND_ASSIGN(AutofillRegexes);
 };
 
-icu::RegexMatcher* AutofillRegexes::GetMatcher(const base::string16& pattern) {
+icu::RegexMatcher* AutofillRegexes::GetMatcher(
+    const base::StringPiece16& pattern) {
   auto it = matchers_.find(pattern);
   if (it == matchers_.end()) {
     const icu::UnicodeString icu_pattern(false, pattern.data(),
@@ -62,8 +63,8 @@
 
 namespace autofill {
 
-bool MatchesPattern(const base::string16& input,
-                    const base::string16& pattern,
+bool MatchesPattern(const base::StringPiece16& input,
+                    const base::StringPiece16& pattern,
                     base::string16* match,
                     int32_t group_to_be_captured) {
   static base::NoDestructor<AutofillRegexes> g_autofill_regexes;
diff --git a/components/autofill/core/browser/autofill_regexes.h b/components/autofill/core/browser/autofill_regexes.h
index 585e88e2..4ec3fce0 100644
--- a/components/autofill/core/browser/autofill_regexes.h
+++ b/components/autofill/core/browser/autofill_regexes.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_REGEXES_H_
 
 #include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
 
 // Parsing utilities.
 namespace autofill {
@@ -13,8 +14,8 @@
 // Case-insensitive regular expression matching.
 // Returns true if |pattern| is found in |input|.
 // The |group_to_be_captured| numbered group is captured into |match|.
-bool MatchesPattern(const base::string16& input,
-                    const base::string16& pattern,
+bool MatchesPattern(const base::StringPiece16& input,
+                    const base::StringPiece16& pattern,
                     base::string16* match = nullptr,
                     int32_t group_to_be_captured = 0);
 
diff --git a/components/autofill/core/browser/autofill_test_utils.cc b/components/autofill/core/browser/autofill_test_utils.cc
index d7379a38..71475e4 100644
--- a/components/autofill/core/browser/autofill_test_utils.cc
+++ b/components/autofill/core/browser/autofill_test_utils.cc
@@ -830,28 +830,6 @@
     type_validities->add_validity(validity_states[i]);
 }
 
-void FillQueryField(AutofillQueryContents::Form::Field* field,
-                    unsigned signature,
-                    const char* name,
-                    const char* control_type) {
-  field->set_signature(signature);
-  if (name)
-    field->set_name(name);
-  if (control_type)
-    field->set_type(control_type);
-}
-
-void FillQueryField(AutofillPageQueryRequest_Form_Field* field,
-                    unsigned signature,
-                    const char* name,
-                    const char* control_type) {
-  field->set_signature(signature);
-  if (name)
-    field->set_name(name);
-  if (control_type)
-    field->set_control_type(control_type);
-}
-
 void GenerateTestAutofillPopup(
     AutofillExternalDelegate* autofill_external_delegate) {
   int query_id = 1;
diff --git a/components/autofill/core/browser/autofill_test_utils.h b/components/autofill/core/browser/autofill_test_utils.h
index 2e79e04..0f5e2b7 100644
--- a/components/autofill/core/browser/autofill_test_utils.h
+++ b/components/autofill/core/browser/autofill_test_utils.h
@@ -302,18 +302,6 @@
                      unsigned autofill_type,
                      const std::vector<unsigned>& validity_states);
 
-// Fills the query form |field| with the information passed by parameter. If the
-// value of a const char* parameter is NULL, the corresponding attribute won't
-// be set at all, as opposed to being set to empty string.
-void FillQueryField(AutofillQueryContents::Form::Field* field,
-                    unsigned signature,
-                    const char* name,
-                    const char* control_type);
-void FillQueryField(AutofillPageQueryRequest_Form_Field* field,
-                    unsigned signature,
-                    const char* name,
-                    const char* control_type);
-
 // Creates the structure of signatures that would be encoded by
 // FormStructure::EncodeUploadRequest() and FormStructure::EncodeQueryRequest()
 // and consumed by FormStructure::ParseQueryResponse() and
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index c975f25..6e4fb42 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -747,6 +747,7 @@
     bool form_was_autofilled,
     const std::string& login_form_signature,
     bool observed_submission,
+    bool is_raw_metadata_uploading_enabled,
     AutofillUploadContents* upload,
     std::vector<FormSignature>* encoded_signatures) const {
   DCHECK(AllTypesCaptured(*this, available_field_types));
@@ -779,7 +780,7 @@
                                  upload);
   }
 
-  if (IsAutofillFieldMetadataEnabled()) {
+  if (is_raw_metadata_uploading_enabled) {
     upload->set_action_signature(StrToHash64Bit(target_url_.host()));
     if (!form_name().empty())
       upload->set_form_name(base::UTF16ToUTF8(form_name()));
@@ -799,7 +800,8 @@
   if (IsMalformed())
     return false;  // Malformed form, skip it.
 
-  EncodeFormForUpload(upload, encoded_signatures);
+  EncodeFormForUpload(is_raw_metadata_uploading_enabled, upload,
+                      encoded_signatures);
   return true;
 }
 
@@ -986,13 +988,6 @@
   return forms;
 }
 
-// static
-bool FormStructure::IsAutofillFieldMetadataEnabled() {
-  const std::string group_name =
-      base::FieldTrialList::FindFullName("AutofillFieldMetadata");
-  return base::StartsWith(group_name, "Enabled", base::CompareCase::SENSITIVE);
-}
-
 std::unique_ptr<FormStructure> FormStructure::CreateForPasswordManagerUpload(
     FormSignature form_signature,
     const std::vector<FieldSignature>& field_signatures) {
@@ -1996,17 +1991,11 @@
     if (is_rich_query_enabled_) {
       EncodeFieldMetadataForQuery(*field, added_field->mutable_metadata());
     }
-
-    if (IsAutofillFieldMetadataEnabled()) {
-      added_field->set_control_type(field->form_control_type);
-
-      if (!field->name.empty())
-        added_field->set_name(base::UTF16ToUTF8(field->name));
-    }
   }
 }
 
 void FormStructure::EncodeFormForUpload(
+    bool is_raw_metadata_uploading_enabled,
     AutofillUploadContents* upload,
     std::vector<FormSignature>* encoded_signatures) const {
   DCHECK(!IsMalformed());
@@ -2069,7 +2058,7 @@
           added_field->mutable_randomized_field_metadata());
     }
 
-    if (IsAutofillFieldMetadataEnabled()) {
+    if (is_raw_metadata_uploading_enabled) {
       added_field->set_type(field->form_control_type);
 
       if (!field->name.empty())
diff --git a/components/autofill/core/browser/form_structure.h b/components/autofill/core/browser/form_structure.h
index 927a33d..4b8cd4e 100644
--- a/components/autofill/core/browser/form_structure.h
+++ b/components/autofill/core/browser/form_structure.h
@@ -79,6 +79,7 @@
       bool form_was_autofilled,
       const std::string& login_form_signature,
       bool observed_submission,
+      bool is_raw_metadata_uploading_enabled,
       autofill::AutofillUploadContents* upload,
       std::vector<FormSignature>* encoded_signatures) const;
 
@@ -494,6 +495,7 @@
       std::vector<FormSignature>* queried_form_signatures) const;
 
   void EncodeFormForUpload(
+      bool is_raw_metadata_uploading_enabled,
       autofill::AutofillUploadContents* upload,
       std::vector<FormSignature>* encoded_signatures) const;
 
diff --git a/components/autofill/core/browser/form_structure_unittest.cc b/components/autofill/core/browser/form_structure_unittest.cc
index 69870f71..1679e31 100644
--- a/components/autofill/core/browser/form_structure_unittest.cc
+++ b/components/autofill/core/browser/form_structure_unittest.cc
@@ -11,7 +11,6 @@
 #include "base/base64.h"
 #include "base/command_line.h"
 #include "base/feature_list.h"
-#include "base/metrics/field_trial.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -73,21 +72,7 @@
     return base::NumberToString(StrToHash64Bit(str));
   }
 
-  void SetUp() override {
-    // By default this trial is enabled on tests.
-    EnableAutofillMetadataFieldTrial();
-  }
-
  protected:
-  void InitFeature(base::test::ScopedFeatureList* feature_list,
-                   const base::Feature& feature,
-                   bool is_enabled) {
-    if (is_enabled)
-      feature_list->InitAndEnableFeature(feature);
-    else
-      feature_list->InitAndDisableFeature(feature);
-  }
-
   bool FormShouldBeParsed(const FormData form) {
     return FormStructure(form).ShouldBeParsed();
   }
@@ -106,12 +91,6 @@
     return FormStructure(form).ShouldBeQueried();
   }
 
-  void DisableAutofillMetadataFieldTrial() {
-    field_trial_ = nullptr;
-    scoped_feature_list_.Reset();
-    scoped_feature_list_.Init();
-  }
-
   void SetUpForEncoder() {
     scoped_feature_list_.Reset();
     scoped_feature_list_.InitWithFeatures(
@@ -126,17 +105,8 @@
   }
 
  private:
-  void EnableAutofillMetadataFieldTrial() {
-    scoped_feature_list_.Reset();
-    scoped_feature_list_.Init();
-    field_trial_ = base::FieldTrialList::CreateFieldTrial(
-        "AutofillFieldMetadata", "Enabled");
-    field_trial_->group();
-  }
-
   uint32_t id_counter_ = 10;
   base::test::ScopedFeatureList scoped_feature_list_;
-  scoped_refptr<base::FieldTrial> field_trial_;
 };
 
 class ParameterizedFormStructureTest
@@ -2356,16 +2326,11 @@
   AutofillPageQueryRequest::Form* query_form = query.add_forms();
   query_form->set_signature(form_structure.form_signature().value());
 
-  test::FillQueryField(query_form->add_fields(), 412125936U, "name_on_card",
-                       "text");
-  test::FillQueryField(query_form->add_fields(), 1917667676U, "billing_address",
-                       "text");
-  test::FillQueryField(query_form->add_fields(), 2226358947U, "card_number",
-                       "text");
-  test::FillQueryField(query_form->add_fields(), 747221617U, "expiration_month",
-                       "text");
-  test::FillQueryField(query_form->add_fields(), 4108155786U, "expiration_year",
-                       "text");
+  query_form->add_fields()->set_signature(412125936U);
+  query_form->add_fields()->set_signature(1917667676U);
+  query_form->add_fields()->set_signature(2226358947U);
+  query_form->add_fields()->set_signature(747221617U);
+  query_form->add_fields()->set_signature(4108155786U);
 
   std::string expected_query_string;
   ASSERT_TRUE(query.SerializeToString(&expected_query_string));
@@ -2414,19 +2379,13 @@
   query_form = query.add_forms();
   query_form->set_signature(form_structure3.form_signature().value());
 
-  test::FillQueryField(query_form->add_fields(), 412125936U, "name_on_card",
-                       "text");
-  test::FillQueryField(query_form->add_fields(), 1917667676U, "billing_address",
-                       "text");
-  test::FillQueryField(query_form->add_fields(), 2226358947U, "card_number",
-                       "text");
-  test::FillQueryField(query_form->add_fields(), 747221617U, "expiration_month",
-                       "text");
-  test::FillQueryField(query_form->add_fields(), 4108155786U, "expiration_year",
-                       "text");
+  query_form->add_fields()->set_signature(412125936U);
+  query_form->add_fields()->set_signature(1917667676U);
+  query_form->add_fields()->set_signature(2226358947U);
+  query_form->add_fields()->set_signature(747221617U);
+  query_form->add_fields()->set_signature(4108155786U);
   for (int i = 0; i < 5; ++i) {
-    test::FillQueryField(query_form->add_fields(), 509334676U, "address",
-                         "text");
+    query_form->add_fields()->set_signature(509334676U);
   }
 
   ASSERT_TRUE(query.SerializeToString(&expected_query_string));
@@ -2701,7 +2660,7 @@
 
   AutofillUploadContents encoded_upload;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, false, std::string(), true, &encoded_upload,
+      available_field_types, false, std::string(), true, true, &encoded_upload,
       &signatures));
 
   std::string encoded_upload_string;
@@ -2714,7 +2673,7 @@
 
   AutofillUploadContents encoded_upload2;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, true, std::string(), true, &encoded_upload2,
+      available_field_types, true, std::string(), true, true, &encoded_upload2,
       &signatures));
 
   encoded_upload2.SerializeToString(&encoded_upload_string);
@@ -2769,7 +2728,7 @@
 
   AutofillUploadContents encoded_upload3;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, false, std::string(), true, &encoded_upload3,
+      available_field_types, false, std::string(), true, true, &encoded_upload3,
       &signatures));
 
   encoded_upload3.SerializeToString(&encoded_upload_string);
@@ -2905,7 +2864,7 @@
 
   AutofillUploadContents encoded_upload;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, false, std::string(), true, &encoded_upload,
+      available_field_types, false, std::string(), true, true, &encoded_upload,
       &signatures));
 
   std::string encoded_upload_string;
@@ -3046,7 +3005,7 @@
 
   AutofillUploadContents encoded_upload;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, false, std::string(), true, &encoded_upload,
+      available_field_types, false, std::string(), true, true, &encoded_upload,
       &signatures));
 
   std::string encoded_upload_string;
@@ -3181,7 +3140,7 @@
 
   AutofillUploadContents encoded_upload;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, false, std::string(), true, &encoded_upload,
+      available_field_types, false, std::string(), true, true, &encoded_upload,
       &signatures));
   EXPECT_EQ(signatures, expected_signatures);
 
@@ -3195,7 +3154,7 @@
 
   AutofillUploadContents encoded_upload2;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, true, std::string(), true, &encoded_upload2,
+      available_field_types, true, std::string(), true, true, &encoded_upload2,
       &signatures));
   EXPECT_EQ(signatures, expected_signatures);
 
@@ -3253,7 +3212,7 @@
 
   AutofillUploadContents encoded_upload3;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, false, std::string(), true, &encoded_upload3,
+      available_field_types, false, std::string(), true, true, &encoded_upload3,
       &signatures));
   EXPECT_EQ(signatures, expected_signatures);
 
@@ -3284,7 +3243,7 @@
 
   AutofillUploadContents encoded_upload4;
   EXPECT_FALSE(form_structure->EncodeUploadRequest(
-      available_field_types, false, std::string(), true, &encoded_upload4,
+      available_field_types, false, std::string(), true, true, &encoded_upload4,
       &signatures));
 }
 
@@ -3419,7 +3378,8 @@
 
   AutofillUploadContents encoded_upload;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, true, "42", true, &encoded_upload, &signatures));
+      available_field_types, true, "42", true, true, &encoded_upload,
+      &signatures));
 
   std::string encoded_upload_string;
   encoded_upload.SerializeToString(&encoded_upload_string);
@@ -3508,7 +3468,7 @@
   AutofillUploadContents encoded_upload;
   std::vector<FormSignature> signatures;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, true, std::string(), true, &encoded_upload,
+      available_field_types, true, std::string(), true, true, &encoded_upload,
       &signatures));
 
   std::string encoded_upload_string;
@@ -3517,8 +3477,6 @@
 }
 
 TEST_F(FormStructureTestImpl, EncodeUploadRequestWithPropertiesMask) {
-  DisableAutofillMetadataFieldTrial();
-
   std::unique_ptr<FormStructure> form_structure;
   std::vector<ServerFieldTypeSet> possible_field_types;
   std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
@@ -3618,7 +3576,8 @@
 
   AutofillUploadContents encoded_upload;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, true, std::string(), true, &encoded_upload,
+      available_field_types, true, std::string(), true,
+      /*is_raw_metadata_uploading_enabled=*/false, &encoded_upload,
       &signatures));
 
   std::string encoded_upload_string;
@@ -3709,7 +3668,7 @@
   AutofillUploadContents encoded_upload;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
       available_field_types, true, std::string(),
-      /* observed_submission= */ false, &encoded_upload, &signatures));
+      /* observed_submission= */ false, true, &encoded_upload, &signatures));
 
   std::string encoded_upload_string;
   encoded_upload.SerializeToString(&encoded_upload_string);
@@ -3791,7 +3750,7 @@
 
   AutofillUploadContents encoded_upload;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, true, std::string(), true, &encoded_upload,
+      available_field_types, true, std::string(), true, true, &encoded_upload,
       &signatures));
 
   std::string encoded_upload_string;
@@ -3879,7 +3838,7 @@
 
   AutofillUploadContents encoded_upload;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, true, std::string(), true, &encoded_upload,
+      available_field_types, true, std::string(), true, true, &encoded_upload,
       &signatures));
 
   std::string encoded_upload_string;
@@ -3964,7 +3923,7 @@
 
   AutofillUploadContents encoded_upload;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, true, std::string(), true, &encoded_upload,
+      available_field_types, true, std::string(), true, true, &encoded_upload,
       &signatures));
 
   std::string encoded_upload_string;
@@ -4054,7 +4013,7 @@
 
   AutofillUploadContents encoded_upload;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, true, std::string(), true, &encoded_upload,
+      available_field_types, true, std::string(), true, true, &encoded_upload,
       &signatures));
 
   std::string encoded_upload_string;
@@ -4063,9 +4022,8 @@
 }
 
 // Sending field metadata to the server is disabled.
-TEST_F(FormStructureTestImpl, EncodeUploadRequest_DisabledMetadataTrial) {
-  DisableAutofillMetadataFieldTrial();
-
+TEST_F(FormStructureTestImpl, EncodeUploadRequest_DisabledMetadata) {
+  // Metadata uploading is disabled by a parameter of |EncodeUploadRequest|.
   std::unique_ptr<FormStructure> form_structure;
   std::vector<ServerFieldTypeSet> possible_field_types;
   std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
@@ -4155,7 +4113,8 @@
 
   AutofillUploadContents encoded_upload;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, true, std::string(), true, &encoded_upload,
+      available_field_types, true, std::string(), true,
+      /*is_raw_metadata_uploading_enabled=*/false, &encoded_upload,
       &signatures));
 
   std::string encoded_upload_string;
@@ -4236,7 +4195,7 @@
 
   AutofillUploadContents encoded_upload;
   EXPECT_TRUE(form_structure.EncodeUploadRequest(available_field_types, false,
-                                                 std::string(), true,
+                                                 std::string(), true, true,
                                                  &encoded_upload, &signatures));
 
   std::string encoded_upload_string;
@@ -4267,7 +4226,7 @@
 
   AutofillUploadContents encoded_upload2;
   EXPECT_TRUE(form_structure.EncodeUploadRequest(
-      available_field_types, false, std::string(), true, &encoded_upload2,
+      available_field_types, false, std::string(), true, true, &encoded_upload2,
       &signatures));
 
   encoded_upload2.SerializeToString(&encoded_upload_string);
@@ -4321,7 +4280,7 @@
 
   AutofillUploadContents encoded_upload3;
   EXPECT_TRUE(form_structure.EncodeUploadRequest(
-      available_field_types, false, std::string(), true, &encoded_upload3,
+      available_field_types, false, std::string(), true, true, &encoded_upload3,
       &signatures));
 
   encoded_upload3.SerializeToString(&encoded_upload_string);
@@ -4353,7 +4312,7 @@
 
   AutofillUploadContents encoded_upload4;
   EXPECT_TRUE(form_structure.EncodeUploadRequest(
-      available_field_types, false, std::string(), true, &encoded_upload4,
+      available_field_types, false, std::string(), true, true, &encoded_upload4,
       &signatures));
 
   encoded_upload4.SerializeToString(&encoded_upload_string);
@@ -4421,7 +4380,7 @@
 
   AutofillUploadContents encoded_upload5;
   EXPECT_TRUE(form_structure.EncodeUploadRequest(
-      available_field_types, false, std::string(), true, &encoded_upload5,
+      available_field_types, false, std::string(), true, true, &encoded_upload5,
       &signatures));
 
   encoded_upload5.SerializeToString(&encoded_upload_string);
@@ -4531,7 +4490,7 @@
 
   AutofillUploadContents encoded_upload;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, false, std::string(), true, &encoded_upload,
+      available_field_types, false, std::string(), true, true, &encoded_upload,
       &signatures));
 
   std::string encoded_upload_string;
@@ -4555,7 +4514,7 @@
 
   AutofillUploadContents encoded_upload2;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, false, std::string(), true, &encoded_upload2,
+      available_field_types, false, std::string(), true, true, &encoded_upload2,
       &signatures));
 
   encoded_upload2.SerializeToString(&encoded_upload_string);
@@ -4573,7 +4532,7 @@
 
   AutofillUploadContents encoded_upload3;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, false, std::string(), true, &encoded_upload3,
+      available_field_types, false, std::string(), true, true, &encoded_upload3,
       &signatures));
 
   encoded_upload3.SerializeToString(&encoded_upload_string);
@@ -4599,7 +4558,7 @@
 
   AutofillUploadContents encoded_upload4;
   EXPECT_TRUE(form_structure->EncodeUploadRequest(
-      available_field_types, false, std::string(), true, &encoded_upload4,
+      available_field_types, false, std::string(), true, true, &encoded_upload4,
       &signatures));
 
   encoded_upload4.SerializeToString(&encoded_upload_string);
@@ -4634,7 +4593,7 @@
   EXPECT_TRUE(form_structure.EncodeUploadRequest(
       {{}} /* available_field_types */, false /* form_was_autofilled */,
       std::string() /* login_form_signature */, true /* observed_submission */,
-      &upload, &signatures));
+      true /* is_raw_metadata_uploading_enabled */, &upload, &signatures));
   EXPECT_EQ(true, upload.passwords_revealed());
 }
 
@@ -4658,7 +4617,8 @@
     EXPECT_TRUE(form_structure.EncodeUploadRequest(
         {{}} /* available_field_types */, false /* form_was_autofilled */,
         std::string() /* login_form_signature */,
-        true /* observed_submission */, &upload, &signatures));
+        true /* observed_submission */,
+        false /* is_raw_metadata_uploading_enabled */, &upload, &signatures));
     EXPECT_EQ(is_form_tag, upload.has_form_tag());
   }
 }
@@ -4715,7 +4675,7 @@
   ASSERT_TRUE(form_structure.EncodeUploadRequest(
       {{}} /* available_field_types */, false /* form_was_autofilled */,
       std::string() /* login_form_signature */, true /* observed_submission */,
-      &upload, &signatures));
+      false /* is_raw_metadata_uploading_enabled */, &upload, &signatures));
 
   const auto form_signature = form_structure.form_signature();
 
@@ -4858,7 +4818,7 @@
     form_structure.set_randomized_encoder(RandomizedEncoder::Create(&prefs));
     AutofillUploadContents upload = AutofillUploadContents();
     std::vector<FormSignature> signatures;
-    form_structure.EncodeUploadRequest({}, true, "", true, &upload,
+    form_structure.EncodeUploadRequest({}, true, "", true, true, &upload,
                                        &signatures);
 
     EXPECT_EQ(has_consent, upload.randomized_form_metadata().has_url());
@@ -5011,9 +4971,8 @@
   AutofillPageQueryRequest::Form* query_form = query.add_forms();
   query_form->set_signature(form_structure.form_signature().value());
 
-  test::FillQueryField(query_form->add_fields(), 239111655U, "username",
-                       "text");
-  test::FillQueryField(query_form->add_fields(), 420638584U, "email", "text");
+  query_form->add_fields()->set_signature(239111655U);
+  query_form->add_fields()->set_signature(420638584U);
 
   std::string expected_query_string;
   ASSERT_TRUE(query.SerializeToString(&expected_query_string));
@@ -5067,11 +5026,9 @@
   AutofillPageQueryRequest::Form* query_form = query.add_forms();
   query_form->set_signature(form_structure.form_signature().value());
 
-  test::FillQueryField(query_form->add_fields(), 239111655U, "username",
-                       "text");
-  test::FillQueryField(query_form->add_fields(), 420638584U, "email", "text");
-  test::FillQueryField(query_form->add_fields(), 2051817934U, "password",
-                       "password");
+  query_form->add_fields()->set_signature(239111655U);
+  query_form->add_fields()->set_signature(420638584U);
+  query_form->add_fields()->set_signature(2051817934U);
 
   std::string expected_query_string;
   ASSERT_TRUE(query.SerializeToString(&expected_query_string));
@@ -5126,11 +5083,9 @@
   AutofillPageQueryRequest::Form* query_form = query.add_forms();
   query_form->set_signature(form_structure.form_signature().value());
 
-  test::FillQueryField(query_form->add_fields(), 239111655U, "username",
-                       "text");
-  test::FillQueryField(query_form->add_fields(), 420638584U, "email", "text");
-  test::FillQueryField(query_form->add_fields(), 2051817934U, "password",
-                       "password");
+  query_form->add_fields()->set_signature(239111655U);
+  query_form->add_fields()->set_signature(420638584U);
+  query_form->add_fields()->set_signature(2051817934U);
 
   std::string expected_query_string;
   ASSERT_TRUE(query.SerializeToString(&expected_query_string));
@@ -5178,9 +5133,8 @@
   AutofillPageQueryRequest::Form* query_form = query.add_forms();
   query_form->set_signature(form_structure.form_signature().value());
 
-  test::FillQueryField(query_form->add_fields(), 239111655U, "username",
-                       "text");
-  test::FillQueryField(query_form->add_fields(), 1318412689U, nullptr, "text");
+  query_form->add_fields()->set_signature(239111655U);
+  query_form->add_fields()->set_signature(1318412689U);
 
   std::string expected_query_string;
   ASSERT_TRUE(query.SerializeToString(&expected_query_string));
@@ -5197,59 +5151,6 @@
   EXPECT_EQ(expected_query_string, encoded_query_string);
 }
 
-// Sending field metadata to the server is disabled.
-TEST_F(FormStructureTestImpl, EncodeQueryRequest_DisabledMetadataTrial) {
-  DisableAutofillMetadataFieldTrial();
-
-  FormData form;
-  // No name set for the form.
-  form.url = GURL("http://cool.com");
-  form.action = form.url.Resolve("/login");
-
-  FormFieldData field;
-  field.label = ASCIIToUTF16("username");
-  field.name = ASCIIToUTF16("username");
-  field.form_control_type = "text";
-  field.unique_renderer_id = MakeFieldRendererId();
-  form.fields.push_back(field);
-
-  field.label = base::string16();
-  field.name = ASCIIToUTF16("country");
-  field.form_control_type = "text";
-  field.check_status = FormFieldData::CheckStatus::kNotCheckable;
-  field.unique_renderer_id = MakeFieldRendererId();
-  form.fields.push_back(field);
-
-  FormStructure form_structure(form);
-  std::vector<FormStructure*> forms;
-  forms.push_back(&form_structure);
-  std::vector<FormSignature> encoded_signatures;
-  AutofillPageQueryRequest encoded_query;
-
-  // Create the expected query and serialize it to a string.
-  AutofillPageQueryRequest query;
-  query.set_client_version(GetProductNameAndVersionForUserAgent());
-  AutofillPageQueryRequest::Form* query_form = query.add_forms();
-  query_form->set_signature(form_structure.form_signature().value());
-
-  test::FillQueryField(query_form->add_fields(), 239111655U, nullptr, nullptr);
-  test::FillQueryField(query_form->add_fields(), 3654076265U, nullptr, nullptr);
-
-  std::string expected_query_string;
-  ASSERT_TRUE(query.SerializeToString(&expected_query_string));
-
-  const FormSignature kExpectedSignature(7635954436925888745UL);
-
-  ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_query,
-                                                &encoded_signatures));
-  ASSERT_EQ(1U, encoded_signatures.size());
-  EXPECT_EQ(kExpectedSignature, encoded_signatures.front());
-
-  std::string encoded_query_string;
-  encoded_query.SerializeToString(&encoded_query_string);
-  EXPECT_EQ(expected_query_string, encoded_query_string);
-}
-
 TEST_F(FormStructureTestImpl, PossibleValues) {
   FormData form_data;
   form_data.url = GURL("http://www.foo.com/");
@@ -7911,8 +7812,8 @@
   ASSERT_EQ(FieldSignature(100u), form->field(2)->GetFieldSignature());
   EXPECT_TRUE(form->EncodeUploadRequest(
       {} /* available_field_types */, false /* form_was_autofilled */,
-      "" /*login_form_signature*/, true /*observed_submission*/, &upload,
-      &signatures));
+      "" /*login_form_signature*/, true /*observed_submission*/,
+      true /* is_raw_metadata_uploading_enabled */, &upload, &signatures));
 }
 
 // Tests if a new logical form is started with the second appearance of a field
diff --git a/components/autofill_assistant/browser/autofill_assistant_onboarding_fetcher_unittest.cc b/components/autofill_assistant/browser/autofill_assistant_onboarding_fetcher_unittest.cc
index 0d51602..0ffb217a4 100644
--- a/components/autofill_assistant/browser/autofill_assistant_onboarding_fetcher_unittest.cc
+++ b/components/autofill_assistant/browser/autofill_assistant_onboarding_fetcher_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "components/autofill_assistant/browser/autofill_assistant_onboarding_fetcher.h"
 
-#include "base/android/locale_utils.h"
 #include "base/containers/flat_map.h"
 #include "base/test/task_environment.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -71,9 +70,8 @@
         EXPECT_EQ(map.at("onboarding_text"), "Text");
       });
 
-  fetcher()->FetchOnboardingDefinition("BUY_MOVIE_TICKETS",
-                                       base::android::GetDefaultLocaleString(),
-                                       300, std::move(callback));
+  fetcher()->FetchOnboardingDefinition("BUY_MOVIE_TICKETS", "en-US", 300,
+                                       std::move(callback));
   EXPECT_EQ(1, GetNumberOfPendingRequests());
   SimulateResponse();
   EXPECT_EQ(0, GetNumberOfPendingRequests());
diff --git a/components/autofill_assistant/browser/features.cc b/components/autofill_assistant/browser/features.cc
index f4440f9..229f62be 100644
--- a/components/autofill_assistant/browser/features.cc
+++ b/components/autofill_assistant/browser/features.cc
@@ -24,14 +24,6 @@
     "AutofillAssistantDisableOnboardingFlow",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kAutofillAssistantProactiveHelp{
-    "AutofillAssistantProactiveHelp", base::FEATURE_DISABLED_BY_DEFAULT};
-
-// Use Chrome's TabHelper system to deal with the life cycle of WebContent's
-// depending Autofill Assistant objects.
-const base::Feature kAutofillAssistantWithTabHelper{
-    "AutofillAssistantWithTabHelper", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // By default, proactive help is only offered if MSBB is turned on. This feature
 // flag allows disabling the link. Proactive help can still be offered to users
 // so long as no communication to a remote backend is required. Specifically,
@@ -40,5 +32,20 @@
     "AutofillAssistantDisableProactiveHelpTiedToMSBB",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Whether autofill assistant should load the DFM for trigger scripts when
+// necessary. Without this feature, trigger scripts will exit if the DFM is not
+// available.
+const base::Feature kAutofillAssistantLoadDFMForTriggerScripts{
+    "AutofillAssistantLoadDFMForTriggerScripts",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kAutofillAssistantProactiveHelp{
+    "AutofillAssistantProactiveHelp", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Use Chrome's TabHelper system to deal with the life cycle of WebContent's
+// depending Autofill Assistant objects.
+const base::Feature kAutofillAssistantWithTabHelper{
+    "AutofillAssistantWithTabHelper", base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace features
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/features.h b/components/autofill_assistant/browser/features.h
index 8821243..7b1efcd 100644
--- a/components/autofill_assistant/browser/features.h
+++ b/components/autofill_assistant/browser/features.h
@@ -17,9 +17,10 @@
 extern const base::Feature kAutofillAssistantChromeEntry;
 extern const base::Feature kAutofillAssistantDirectActions;
 extern const base::Feature kAutofillAssistantDisableOnboardingFlow;
+extern const base::Feature kAutofillAssistantDisableProactiveHelpTiedToMSBB;
+extern const base::Feature kAutofillAssistantLoadDFMForTriggerScripts;
 extern const base::Feature kAutofillAssistantProactiveHelp;
 extern const base::Feature kAutofillAssistantWithTabHelper;
-extern const base::Feature kAutofillAssistantDisableProactiveHelpTiedToMSBB;
 
 }  // namespace features
 }  // namespace autofill_assistant
diff --git a/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionHelper.java b/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionHelper.java
index fc68d6ff..c45f747 100644
--- a/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionHelper.java
+++ b/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionHelper.java
@@ -31,6 +31,7 @@
 import org.chromium.services.media_session.MediaMetadata;
 import org.chromium.services.media_session.MediaPosition;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.url.GURL;
 
 import java.util.Collections;
 import java.util.List;
@@ -440,7 +441,7 @@
         // Don't waste time trying to find it.
         if (!mMaybeHasFavicon) return false;
 
-        String pageUrl = mWebContents.getLastCommittedUrl();
+        GURL pageUrl = mWebContents.getLastCommittedUrl();
         int size = MediaNotificationImageUtils.MINIMAL_MEDIA_IMAGE_SIZE_PX;
         if (mLargeIconBridge == null) {
             mLargeIconBridge = new LargeIconBridge(mDelegate.getBrowserContextHandle());
@@ -453,7 +454,7 @@
             }
         };
 
-        return mLargeIconBridge.getLargeIconForStringUrl(pageUrl, size, callback);
+        return mLargeIconBridge.getLargeIconForUrl(pageUrl, size, callback);
     }
 
     /**
diff --git a/components/browser_ui/styles/android/BUILD.gn b/components/browser_ui/styles/android/BUILD.gn
index 763967d..b6b8880 100644
--- a/components/browser_ui/styles/android/BUILD.gn
+++ b/components/browser_ui/styles/android/BUILD.gn
@@ -154,7 +154,6 @@
     "java/res/drawable/ic_music_note_24dp.xml",
     "java/res/drawable/ic_offline_pin_24dp_on_dark_bg.xml",
     "java/res/drawable/ic_offline_pin_24dp_on_light_bg.xml",
-    "java/res/drawable/ic_people_24dp.xml",
     "java/res/drawable/ic_permission_location_filled.xml",
     "java/res/drawable/ic_play_circle_filled_24dp_on_dark_bg.xml",
     "java/res/drawable/ic_play_circle_filled_24dp_on_light_bg.xml",
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_people_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_people_24dp.xml
deleted file mode 100644
index 0c667784..0000000
--- a/components/browser_ui/styles/android/java/res/drawable/ic_people_24dp.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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. -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:fillColor="@color/default_icon_color"
-        android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zM8,11c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zM8,13c-2.33,0 -7,1.17 -7,3.5L1,19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zM16,13c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45L17,19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z"/>
-</vector>
diff --git a/components/domain_reliability/quic_error_mapping.cc b/components/domain_reliability/quic_error_mapping.cc
index 57ff62e..c34c6dfc 100644
--- a/components/domain_reliability/quic_error_mapping.cc
+++ b/components/domain_reliability/quic_error_mapping.cc
@@ -436,6 +436,18 @@
     {quic::QUIC_INVALID_PRIORITY_UPDATE, "quic::quic_invalid_priority_update"},
     {quic::QUIC_PEER_PORT_CHANGE_HANDSHAKE_UNCONFIRMED,
      "quic.peer_port_change_handshake_unconfirmed"},
+
+    {quic::QUIC_TLS_BAD_CERTIFICATE, "quic::quic_tls_bad_certificate"},
+    {quic::QUIC_TLS_UNSUPPORTED_CERTIFICATE,
+     "quic::quic_tls_unsupported_certificate"},
+    {quic::QUIC_TLS_CERTIFICATE_REVOKED, "quic::quic_tls_certificate_revoked"},
+    {quic::QUIC_TLS_CERTIFICATE_EXPIRED, "quic::quic_tls_certificate_expired"},
+    {quic::QUIC_TLS_CERTIFICATE_UNKNOWN, "quic::quic_tls_certificate_unknown"},
+    {quic::QUIC_TLS_INTERNAL_ERROR, "quic::quic_tls_internal_error"},
+    {quic::QUIC_TLS_UNRECOGNIZED_NAME, "quic::quic_tls_unrecognized_name"},
+    {quic::QUIC_TLS_CERTIFICATE_REQUIRED,
+     "quic::quic_tls_certificate_required"},
+
     // No error. Used as bound while iterating.
     {quic::QUIC_LAST_ERROR, "quic.last_error"}};
 
diff --git a/components/external_intents/android/BUILD.gn b/components/external_intents/android/BUILD.gn
index 2eccc0ef..5dc05eb5 100644
--- a/components/external_intents/android/BUILD.gn
+++ b/components/external_intents/android/BUILD.gn
@@ -86,6 +86,8 @@
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/junit",
     "//ui/android:ui_java",
+    "//url:gurl_java",
+    "//url:gurl_junit_test_support",
     "//url:origin_java",
   ]
 }
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
index 568bca91..d00e56b8 100644
--- a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
+++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
@@ -57,6 +57,7 @@
 import org.chromium.ui.UiUtils;
 import org.chromium.ui.base.PageTransition;
 import org.chromium.ui.base.PermissionCallback;
+import org.chromium.url.GURL;
 import org.chromium.url.URI;
 
 import java.lang.annotation.Retention;
@@ -938,9 +939,9 @@
 
         // TODO(https://crbug.com/1009539): Replace this host parsing with a UrlUtilities or GURL
         //   function call.
-        String lastCommittedUrl = getLastCommittedUrl();
+        GURL lastCommittedUrl = getLastCommittedUrl();
         String previousUriString =
-                lastCommittedUrl != null ? lastCommittedUrl : params.getReferrerUrl();
+                lastCommittedUrl != null ? lastCommittedUrl.getSpec() : params.getReferrerUrl();
         if (previousUriString == null || (!isLink && !isFormSubmit)) return false;
 
         URI currentUri;
@@ -1793,7 +1794,7 @@
      * @return The last committed URL from the WebContents.
      */
     @VisibleForTesting
-    protected String getLastCommittedUrl() {
+    protected GURL getLastCommittedUrl() {
         if (mDelegate.getWebContents() == null) return null;
         return mDelegate.getWebContents().getLastCommittedUrl();
     }
diff --git a/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java b/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
index 65b43d8..97e2ce2 100644
--- a/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
+++ b/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
@@ -42,6 +42,7 @@
 import org.chromium.content_public.browser.test.NativeLibraryTestUtils;
 import org.chromium.ui.base.PageTransition;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.url.GURL;
 import org.chromium.url.Origin;
 
 import java.net.URISyntaxException;
@@ -1376,8 +1377,8 @@
                 .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
                         START_OTHER_ACTIVITY);
 
-        mUrlHandler.mLastCommittedUrl = "https://refertest.com";
-        checkUrl("http://refertest.com")
+        mUrlHandler.mLastCommittedUrl = new GURL("https://refertest.com");
+        checkUrl("https://refertest.com")
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
     }
 
@@ -1980,7 +1981,7 @@
 
     private static class ExternalNavigationHandlerForTesting extends ExternalNavigationHandler {
         public String defaultSmsPackageName;
-        public String mLastCommittedUrl;
+        public GURL mLastCommittedUrl;
         public boolean mIsSerpReferrer;
         public boolean mShouldRequestFileAccess;
         public String mNewUrlAfterClobbering;
@@ -2018,7 +2019,7 @@
         }
 
         @Override
-        protected String getLastCommittedUrl() {
+        protected GURL getLastCommittedUrl() {
             return mLastCommittedUrl;
         }
 
diff --git a/components/invalidation/impl/invalidation_service_test_template.cc b/components/invalidation/impl/invalidation_service_test_template.cc
index d7f3e34..9e5f934d 100644
--- a/components/invalidation/impl/invalidation_service_test_template.cc
+++ b/components/invalidation/impl/invalidation_service_test_template.cc
@@ -4,24 +4,23 @@
 
 #include "components/invalidation/impl/invalidation_service_test_template.h"
 
-namespace internal {
+namespace invalidation {
 
 BoundFakeInvalidationHandler::BoundFakeInvalidationHandler(
-    const invalidation::InvalidationService& invalidator)
+    const InvalidationService& invalidator)
     : invalidator_(invalidator),
-      last_retrieved_state_(invalidation::DEFAULT_INVALIDATION_ERROR) {}
+      last_retrieved_state_(DEFAULT_INVALIDATION_ERROR) {}
 
 BoundFakeInvalidationHandler::~BoundFakeInvalidationHandler() = default;
 
-invalidation::InvalidatorState
-BoundFakeInvalidationHandler::GetLastRetrievedState() const {
+InvalidatorState BoundFakeInvalidationHandler::GetLastRetrievedState() const {
   return last_retrieved_state_;
 }
 
 void BoundFakeInvalidationHandler::OnInvalidatorStateChange(
-    invalidation::InvalidatorState state) {
+    InvalidatorState state) {
   FakeInvalidationHandler::OnInvalidatorStateChange(state);
   last_retrieved_state_ = invalidator_.GetInvalidatorState();
 }
 
-}  // namespace internal
+}  // namespace invalidation
diff --git a/components/invalidation/impl/invalidation_service_test_template.h b/components/invalidation/impl/invalidation_service_test_template.h
index 572c15f..e559972 100644
--- a/components/invalidation/impl/invalidation_service_test_template.h
+++ b/components/invalidation/impl/invalidation_service_test_template.h
@@ -82,28 +82,24 @@
 #include "components/invalidation/public/topic_invalidation_map.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-// TODO(crbug.com/1031125): consider moving this file content under invalidation
-// namespace, instead of specifying it everywhere (invalidation::internal if
-// polluting invalidation namespace is a concern, though it already pollutes
-// global namespace).
+namespace invalidation {
 
 template <typename InvalidatorTestDelegate>
 class InvalidationServiceTest : public testing::Test {
  protected:
   InvalidationServiceTest() = default;
 
-  invalidation::InvalidationService*
-  CreateAndInitializeInvalidationService() {
+  InvalidationService* CreateAndInitializeInvalidationService() {
     this->delegate_.CreateInvalidationService();
     return this->delegate_.GetInvalidationService();
   }
 
   InvalidatorTestDelegate delegate_;
 
-  const invalidation::Topic topic1 = "BOOKMARK";
-  const invalidation::Topic topic2 = "PREFERENCE";
-  const invalidation::Topic topic3 = "AUTOFILL";
-  const invalidation::Topic topic4 = "PUSH_MESSAGE";
+  const Topic topic1 = "BOOKMARK";
+  const Topic topic2 = "PREFERENCE";
+  const Topic topic3 = "AUTOFILL";
+  const Topic topic4 = "PUSH_MESSAGE";
 };
 
 TYPED_TEST_SUITE_P(InvalidationServiceTest);
@@ -113,39 +109,33 @@
 // between.  The handler should only see invalidations when its registered and
 // its IDs are registered.
 TYPED_TEST_P(InvalidationServiceTest, Basic) {
-  invalidation::InvalidationService* const invalidator =
+  InvalidationService* const invalidator =
       this->CreateAndInitializeInvalidationService();
 
-  invalidation::FakeInvalidationHandler handler;
+  FakeInvalidationHandler handler;
 
   invalidator->RegisterInvalidationHandler(&handler);
 
-  invalidation::TopicInvalidationMap invalidation_map;
-  invalidation_map.Insert(
-      invalidation::Invalidation::Init(this->topic1, 1, "1"));
-  invalidation_map.Insert(
-      invalidation::Invalidation::Init(this->topic2, 2, "2"));
-  invalidation_map.Insert(
-      invalidation::Invalidation::Init(this->topic3, 3, "3"));
+  TopicInvalidationMap invalidation_map;
+  invalidation_map.Insert(Invalidation::Init(this->topic1, 1, "1"));
+  invalidation_map.Insert(Invalidation::Init(this->topic2, 2, "2"));
+  invalidation_map.Insert(Invalidation::Init(this->topic3, 3, "3"));
 
   // Should be ignored since no IDs are registered to |handler|.
   this->delegate_.TriggerOnIncomingInvalidation(invalidation_map);
   EXPECT_EQ(0, handler.GetInvalidationCount());
 
-  invalidation::TopicSet topics;
+  TopicSet topics;
   topics.insert(this->topic1);
   topics.insert(this->topic2);
   EXPECT_TRUE(invalidator->UpdateInterestedTopics(&handler, topics));
 
-  this->delegate_.TriggerOnInvalidatorStateChange(
-      invalidation::INVALIDATIONS_ENABLED);
-  EXPECT_EQ(invalidation::INVALIDATIONS_ENABLED, handler.GetInvalidatorState());
+  this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED);
+  EXPECT_EQ(INVALIDATIONS_ENABLED, handler.GetInvalidatorState());
 
-  invalidation::TopicInvalidationMap expected_invalidations;
-  expected_invalidations.Insert(
-      invalidation::Invalidation::Init(this->topic1, 1, "1"));
-  expected_invalidations.Insert(
-      invalidation::Invalidation::Init(this->topic2, 2, "2"));
+  TopicInvalidationMap expected_invalidations;
+  expected_invalidations.Insert(Invalidation::Init(this->topic1, 1, "1"));
+  expected_invalidations.Insert(Invalidation::Init(this->topic2, 2, "2"));
 
   this->delegate_.TriggerOnIncomingInvalidation(invalidation_map);
   EXPECT_EQ(1, handler.GetInvalidationCount());
@@ -155,25 +145,20 @@
   topics.insert(this->topic3);
   EXPECT_TRUE(invalidator->UpdateInterestedTopics(&handler, topics));
 
-  expected_invalidations = invalidation::TopicInvalidationMap();
-  expected_invalidations.Insert(
-      invalidation::Invalidation::Init(this->topic2, 2, "2"));
-  expected_invalidations.Insert(
-      invalidation::Invalidation::Init(this->topic3, 3, "3"));
+  expected_invalidations = TopicInvalidationMap();
+  expected_invalidations.Insert(Invalidation::Init(this->topic2, 2, "2"));
+  expected_invalidations.Insert(Invalidation::Init(this->topic3, 3, "3"));
 
   // Removed Topics should not be notified, newly-added ones should.
   this->delegate_.TriggerOnIncomingInvalidation(invalidation_map);
   EXPECT_EQ(2, handler.GetInvalidationCount());
   EXPECT_THAT(expected_invalidations, Eq(handler.GetLastInvalidationMap()));
 
-  this->delegate_.TriggerOnInvalidatorStateChange(
-      invalidation::TRANSIENT_INVALIDATION_ERROR);
-  EXPECT_EQ(invalidation::TRANSIENT_INVALIDATION_ERROR,
-            handler.GetInvalidatorState());
+  this->delegate_.TriggerOnInvalidatorStateChange(TRANSIENT_INVALIDATION_ERROR);
+  EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler.GetInvalidatorState());
 
-  this->delegate_.TriggerOnInvalidatorStateChange(
-      invalidation::INVALIDATIONS_ENABLED);
-  EXPECT_EQ(invalidation::INVALIDATIONS_ENABLED, handler.GetInvalidatorState());
+  this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED);
+  EXPECT_EQ(INVALIDATIONS_ENABLED, handler.GetInvalidatorState());
 
   invalidator->UnregisterInvalidationHandler(&handler);
 
@@ -188,13 +173,13 @@
 // registered should get invalidations, and the ones that have registered
 // topics should receive invalidations for those topics.
 TYPED_TEST_P(InvalidationServiceTest, MultipleHandlers) {
-  invalidation::InvalidationService* const invalidator =
+  InvalidationService* const invalidator =
       this->CreateAndInitializeInvalidationService();
 
-  invalidation::FakeInvalidationHandler handler1;
-  invalidation::FakeInvalidationHandler handler2;
-  invalidation::FakeInvalidationHandler handler3;
-  invalidation::FakeInvalidationHandler handler4;
+  FakeInvalidationHandler handler1;
+  FakeInvalidationHandler handler2;
+  FakeInvalidationHandler handler3;
+  FakeInvalidationHandler handler4;
 
   invalidator->RegisterInvalidationHandler(&handler1);
   invalidator->RegisterInvalidationHandler(&handler2);
@@ -202,14 +187,14 @@
   invalidator->RegisterInvalidationHandler(&handler4);
 
   {
-    invalidation::TopicSet topics;
+    TopicSet topics;
     topics.insert(this->topic1);
     topics.insert(this->topic2);
     EXPECT_TRUE(invalidator->UpdateInterestedTopics(&handler1, topics));
   }
 
   {
-    invalidation::TopicSet topics;
+    TopicSet topics;
     topics.insert(this->topic3);
     EXPECT_TRUE(invalidator->UpdateInterestedTopics(&handler2, topics));
   }
@@ -217,48 +202,36 @@
   // Don't register any topics for handler3.
 
   {
-    invalidation::TopicSet topics;
+    TopicSet topics;
     topics.insert(this->topic4);
     EXPECT_TRUE(invalidator->UpdateInterestedTopics(&handler4, topics));
   }
 
   invalidator->UnregisterInvalidationHandler(&handler4);
 
-  this->delegate_.TriggerOnInvalidatorStateChange(
-      invalidation::INVALIDATIONS_ENABLED);
-  EXPECT_EQ(invalidation::INVALIDATIONS_ENABLED,
-            handler1.GetInvalidatorState());
-  EXPECT_EQ(invalidation::INVALIDATIONS_ENABLED,
-            handler2.GetInvalidatorState());
-  EXPECT_EQ(invalidation::INVALIDATIONS_ENABLED,
-            handler3.GetInvalidatorState());
-  EXPECT_EQ(invalidation::TRANSIENT_INVALIDATION_ERROR,
-            handler4.GetInvalidatorState());
+  this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED);
+  EXPECT_EQ(INVALIDATIONS_ENABLED, handler1.GetInvalidatorState());
+  EXPECT_EQ(INVALIDATIONS_ENABLED, handler2.GetInvalidatorState());
+  EXPECT_EQ(INVALIDATIONS_ENABLED, handler3.GetInvalidatorState());
+  EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler4.GetInvalidatorState());
 
   {
-    invalidation::TopicInvalidationMap invalidation_map;
-    invalidation_map.Insert(
-        invalidation::Invalidation::Init(this->topic1, 1, "1"));
-    invalidation_map.Insert(
-        invalidation::Invalidation::Init(this->topic2, 2, "2"));
-    invalidation_map.Insert(
-        invalidation::Invalidation::Init(this->topic3, 3, "3"));
-    invalidation_map.Insert(
-        invalidation::Invalidation::Init(this->topic4, 4, "4"));
+    TopicInvalidationMap invalidation_map;
+    invalidation_map.Insert(Invalidation::Init(this->topic1, 1, "1"));
+    invalidation_map.Insert(Invalidation::Init(this->topic2, 2, "2"));
+    invalidation_map.Insert(Invalidation::Init(this->topic3, 3, "3"));
+    invalidation_map.Insert(Invalidation::Init(this->topic4, 4, "4"));
     this->delegate_.TriggerOnIncomingInvalidation(invalidation_map);
 
-    invalidation::TopicInvalidationMap expected_invalidations;
-    expected_invalidations.Insert(
-        invalidation::Invalidation::Init(this->topic1, 1, "1"));
-    expected_invalidations.Insert(
-        invalidation::Invalidation::Init(this->topic2, 2, "2"));
+    TopicInvalidationMap expected_invalidations;
+    expected_invalidations.Insert(Invalidation::Init(this->topic1, 1, "1"));
+    expected_invalidations.Insert(Invalidation::Init(this->topic2, 2, "2"));
 
     EXPECT_EQ(1, handler1.GetInvalidationCount());
     EXPECT_THAT(expected_invalidations, Eq(handler1.GetLastInvalidationMap()));
 
-    expected_invalidations = invalidation::TopicInvalidationMap();
-    expected_invalidations.Insert(
-        invalidation::Invalidation::Init(this->topic3, 3, "3"));
+    expected_invalidations = TopicInvalidationMap();
+    expected_invalidations.Insert(Invalidation::Init(this->topic3, 3, "3"));
 
     EXPECT_EQ(1, handler2.GetInvalidationCount());
     EXPECT_THAT(expected_invalidations, Eq(handler2.GetLastInvalidationMap()));
@@ -267,16 +240,11 @@
     EXPECT_EQ(0, handler4.GetInvalidationCount());
   }
 
-  this->delegate_.TriggerOnInvalidatorStateChange(
-      invalidation::TRANSIENT_INVALIDATION_ERROR);
-  EXPECT_EQ(invalidation::TRANSIENT_INVALIDATION_ERROR,
-            handler1.GetInvalidatorState());
-  EXPECT_EQ(invalidation::TRANSIENT_INVALIDATION_ERROR,
-            handler2.GetInvalidatorState());
-  EXPECT_EQ(invalidation::TRANSIENT_INVALIDATION_ERROR,
-            handler3.GetInvalidatorState());
-  EXPECT_EQ(invalidation::TRANSIENT_INVALIDATION_ERROR,
-            handler4.GetInvalidatorState());
+  this->delegate_.TriggerOnInvalidatorStateChange(TRANSIENT_INVALIDATION_ERROR);
+  EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler1.GetInvalidatorState());
+  EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler2.GetInvalidatorState());
+  EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler3.GetInvalidatorState());
+  EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler4.GetInvalidatorState());
 
   invalidator->UnregisterInvalidationHandler(&handler3);
   invalidator->UnregisterInvalidationHandler(&handler2);
@@ -286,18 +254,18 @@
 // Multiple registrations by different handlers on the same Topic should return
 // false.
 TYPED_TEST_P(InvalidationServiceTest, MultipleRegistrations) {
-  invalidation::InvalidationService* const invalidator =
+  InvalidationService* const invalidator =
       this->CreateAndInitializeInvalidationService();
 
-  invalidation::FakeInvalidationHandler handler1;
-  invalidation::FakeInvalidationHandler handler2;
+  FakeInvalidationHandler handler1;
+  FakeInvalidationHandler handler2;
 
   invalidator->RegisterInvalidationHandler(&handler1);
   invalidator->RegisterInvalidationHandler(&handler2);
 
   // Registering both handlers for the same topic. First call should succeed,
   // second should fail.
-  invalidation::TopicSet topics;
+  TopicSet topics;
   topics.insert(this->topic1);
   EXPECT_TRUE(invalidator->UpdateInterestedTopics(&handler1, topics));
   EXPECT_FALSE(invalidator->UpdateInterestedTopics(&handler2, topics));
@@ -309,114 +277,93 @@
 // Make sure that passing an empty set to UpdateInterestedTopics clears
 // the corresponding entries for the handler.
 TYPED_TEST_P(InvalidationServiceTest, EmptySetUnregisters) {
-  invalidation::InvalidationService* const invalidator =
+  InvalidationService* const invalidator =
       this->CreateAndInitializeInvalidationService();
 
-  invalidation::FakeInvalidationHandler handler1;
+  FakeInvalidationHandler handler1;
 
   // Control observer.
-  invalidation::FakeInvalidationHandler handler2;
+  FakeInvalidationHandler handler2;
 
   invalidator->RegisterInvalidationHandler(&handler1);
   invalidator->RegisterInvalidationHandler(&handler2);
 
   {
-    invalidation::TopicSet topics;
+    TopicSet topics;
     topics.insert(this->topic1);
     topics.insert(this->topic2);
     EXPECT_TRUE(invalidator->UpdateInterestedTopics(&handler1, topics));
   }
 
   {
-    invalidation::TopicSet topics;
+    TopicSet topics;
     topics.insert(this->topic3);
     EXPECT_TRUE(invalidator->UpdateInterestedTopics(&handler2, topics));
   }
 
   // Unregister the topics for the first observer. It should not receive any
   // further invalidations.
-  EXPECT_TRUE(
-      invalidator->UpdateInterestedTopics(&handler1, invalidation::TopicSet()));
+  EXPECT_TRUE(invalidator->UpdateInterestedTopics(&handler1, TopicSet()));
 
-  this->delegate_.TriggerOnInvalidatorStateChange(
-      invalidation::INVALIDATIONS_ENABLED);
-  EXPECT_EQ(invalidation::INVALIDATIONS_ENABLED,
-            handler1.GetInvalidatorState());
-  EXPECT_EQ(invalidation::INVALIDATIONS_ENABLED,
-            handler2.GetInvalidatorState());
+  this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED);
+  EXPECT_EQ(INVALIDATIONS_ENABLED, handler1.GetInvalidatorState());
+  EXPECT_EQ(INVALIDATIONS_ENABLED, handler2.GetInvalidatorState());
 
   {
-    invalidation::TopicInvalidationMap invalidation_map;
-    invalidation_map.Insert(
-        invalidation::Invalidation::Init(this->topic1, 1, "1"));
-    invalidation_map.Insert(
-        invalidation::Invalidation::Init(this->topic2, 2, "2"));
-    invalidation_map.Insert(
-        invalidation::Invalidation::Init(this->topic3, 3, "3"));
+    TopicInvalidationMap invalidation_map;
+    invalidation_map.Insert(Invalidation::Init(this->topic1, 1, "1"));
+    invalidation_map.Insert(Invalidation::Init(this->topic2, 2, "2"));
+    invalidation_map.Insert(Invalidation::Init(this->topic3, 3, "3"));
     this->delegate_.TriggerOnIncomingInvalidation(invalidation_map);
     EXPECT_EQ(0, handler1.GetInvalidationCount());
     EXPECT_EQ(1, handler2.GetInvalidationCount());
   }
 
-  this->delegate_.TriggerOnInvalidatorStateChange(
-      invalidation::TRANSIENT_INVALIDATION_ERROR);
-  EXPECT_EQ(invalidation::TRANSIENT_INVALIDATION_ERROR,
-            handler1.GetInvalidatorState());
-  EXPECT_EQ(invalidation::TRANSIENT_INVALIDATION_ERROR,
-            handler2.GetInvalidatorState());
+  this->delegate_.TriggerOnInvalidatorStateChange(TRANSIENT_INVALIDATION_ERROR);
+  EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler1.GetInvalidatorState());
+  EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler2.GetInvalidatorState());
 
   invalidator->UnregisterInvalidationHandler(&handler2);
   invalidator->UnregisterInvalidationHandler(&handler1);
 }
 
-namespace internal {
-
 // A FakeInvalidationHandler that is "bound" to a specific
 // InvalidationService.  This is for cross-referencing state information with
 // the bound InvalidationService.
-class BoundFakeInvalidationHandler
-    : public invalidation::FakeInvalidationHandler {
+class BoundFakeInvalidationHandler : public FakeInvalidationHandler {
  public:
-  explicit BoundFakeInvalidationHandler(
-      const invalidation::InvalidationService& invalidator);
+  explicit BoundFakeInvalidationHandler(const InvalidationService& invalidator);
   ~BoundFakeInvalidationHandler() override;
 
   // Returns the last return value of GetInvalidatorState() on the
   // bound invalidator from the last time the invalidator state
   // changed.
-  invalidation::InvalidatorState GetLastRetrievedState() const;
+  InvalidatorState GetLastRetrievedState() const;
 
   // InvalidationHandler implementation.
-  void OnInvalidatorStateChange(invalidation::InvalidatorState state) override;
+  void OnInvalidatorStateChange(InvalidatorState state) override;
 
  private:
-  const invalidation::InvalidationService& invalidator_;
-  invalidation::InvalidatorState last_retrieved_state_;
+  const InvalidationService& invalidator_;
+  InvalidatorState last_retrieved_state_;
 
   DISALLOW_COPY_AND_ASSIGN(BoundFakeInvalidationHandler);
 };
 
-}  // namespace internal
-
 TYPED_TEST_P(InvalidationServiceTest, GetInvalidatorStateAlwaysCurrent) {
-  invalidation::InvalidationService* const invalidator =
+  InvalidationService* const invalidator =
       this->CreateAndInitializeInvalidationService();
 
-  internal::BoundFakeInvalidationHandler handler(*invalidator);
+  BoundFakeInvalidationHandler handler(*invalidator);
   invalidator->RegisterInvalidationHandler(&handler);
 
-  this->delegate_.TriggerOnInvalidatorStateChange(
-      invalidation::INVALIDATIONS_ENABLED);
-  EXPECT_EQ(invalidation::INVALIDATIONS_ENABLED, handler.GetInvalidatorState());
-  EXPECT_EQ(invalidation::INVALIDATIONS_ENABLED,
-            handler.GetLastRetrievedState());
+  this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED);
+  EXPECT_EQ(INVALIDATIONS_ENABLED, handler.GetInvalidatorState());
+  EXPECT_EQ(INVALIDATIONS_ENABLED, handler.GetLastRetrievedState());
 
-  this->delegate_.TriggerOnInvalidatorStateChange(
-      invalidation::TRANSIENT_INVALIDATION_ERROR);
-  EXPECT_EQ(invalidation::TRANSIENT_INVALIDATION_ERROR,
-            handler.GetInvalidatorState());
-  EXPECT_EQ(invalidation::TRANSIENT_INVALIDATION_ERROR,
-            handler.GetLastRetrievedState());
+  this->delegate_.TriggerOnInvalidatorStateChange(TRANSIENT_INVALIDATION_ERROR);
+  EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler.GetInvalidatorState());
+  EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler.GetLastRetrievedState());
 
   invalidator->UnregisterInvalidationHandler(&handler);
 }
@@ -428,4 +375,6 @@
                             EmptySetUnregisters,
                             GetInvalidatorStateAlwaysCurrent);
 
+}  // namespace invalidation
+
 #endif  // COMPONENTS_INVALIDATION_IMPL_INVALIDATION_SERVICE_TEST_TEMPLATE_H_
diff --git a/components/js_injection/browser/js_to_browser_messaging.cc b/components/js_injection/browser/js_to_browser_messaging.cc
index e2a4e18..68aa371 100644
--- a/components/js_injection/browser/js_to_browser_messaging.cc
+++ b/components/js_injection/browser/js_to_browser_messaging.cc
@@ -72,6 +72,10 @@
     std::vector<blink::MessagePortDescriptor> ports) {
   DCHECK(render_frame_host_);
 
+  // This generally shouldn't happen, and may indicate a compromised renderer.
+  if (render_frame_host_->IsInactiveAndDisallowReactivation())
+    return;
+
   content::WebContents* web_contents =
       content::WebContents::FromRenderFrameHost(render_frame_host_);
 
@@ -119,6 +123,10 @@
 void JsToBrowserMessaging::SetBrowserToJsMessaging(
     mojo::PendingAssociatedRemote<mojom::BrowserToJsMessaging>
         java_to_js_messaging) {
+  // This generally shouldn't happen, and may indicate a compromised renderer.
+  if (render_frame_host_->IsInactiveAndDisallowReactivation())
+    return;
+
   // A RenderFrame may inject JsToBrowserMessaging in the JavaScript context
   // more than once because of reusing of RenderFrame.
   host_.reset();
diff --git a/components/lookalikes/core/lookalike_url_util.cc b/components/lookalikes/core/lookalike_url_util.cc
index 6b0a225..c65ba8d6 100644
--- a/components/lookalikes/core/lookalike_url_util.cc
+++ b/components/lookalikes/core/lookalike_url_util.cc
@@ -23,6 +23,7 @@
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
 #include "base/time/default_clock.h"
+#include "base/trace_event/trace_event.h"
 #include "base/values.h"
 #include "components/lookalikes/core/features.h"
 #include "components/security_interstitials/core/pref_names.h"
@@ -381,6 +382,7 @@
 DomainInfo::DomainInfo(const DomainInfo&) = default;
 
 DomainInfo GetDomainInfo(const std::string& hostname) {
+  TRACE_EVENT0("navigation", "GetDomainInfo");
   if (net::HostStringIsLocalhost(hostname) ||
       net::IsHostnameNonUnique(hostname)) {
     return DomainInfo(std::string(), std::string(), std::string(),
diff --git a/components/no_state_prefetch/browser/prerender_manager.cc b/components/no_state_prefetch/browser/prerender_manager.cc
index ae248992..863327f3 100644
--- a/components/no_state_prefetch/browser/prerender_manager.cc
+++ b/components/no_state_prefetch/browser/prerender_manager.cc
@@ -977,6 +977,7 @@
     content::RenderProcessHost* host) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   size_t erased = prerender_process_hosts_.erase(host);
+  host->RemoveObserver(this);
   DCHECK_EQ(1u, erased);
 }
 
diff --git a/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java b/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java
index ce9a755..9051f6a5 100644
--- a/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java
+++ b/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java
@@ -147,26 +147,26 @@
 
     @Test
     @MediumTest
-    public void nestedLinkClickTest() throws Exception {
+    @DisableIf.Build(message = "Test is failing on Android P, see crbug.com/1110939.",
+            sdk_is_greater_than = VERSION_CODES.O_MR1, sdk_is_less_than = VERSION_CODES.Q)
+    public void
+    nestedLinkClickTest() throws Exception {
         initPlayerManager(true);
         final View playerHostView = mPlayerManager.getView();
         assertLinkUrl(playerHostView, 220, 220, TEST_IN_VIEWPORT_LINK_URL);
         assertLinkUrl(playerHostView, 300, 270, TEST_IN_VIEWPORT_LINK_URL);
 
-        // Temporarily commenting out as this is flaky on P.
+        UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        int deviceHeight = device.getDisplayHeight();
+        int statusBarHeight = statusBarHeight();
+        int navigationBarHeight = navigationBarHeight();
+        int padding = 20;
+        int fromY = deviceHeight - navigationBarHeight - padding;
+        int toY = statusBarHeight + padding;
+        mLinkClickHandler.mUrl = null;
+        device.swipe(300, fromY, 300, toY, 10);
 
-        // UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-        // int deviceHeight = device.getDisplayHeight();
-        // int statusBarHeight = statusBarHeight();
-        // int navigationBarHeight = navigationBarHeight();
-        // int padding = 20;
-        // int fromY = deviceHeight - navigationBarHeight - padding;
-        // int toY = statusBarHeight + padding;
-        // mLinkClickHandler.mUrl = null;
-        // device.swipe(300, fromY, 300, toY, 10);
-
-        // Manually click as assertLinkUrl() doesn't handle subframe scrolls well.
-        // assertLinkUrl(playerHostView, 200, 1500, TEST_OUT_OF_VIEWPORT_LINK_URL);
+        assertLinkUrl(playerHostView, 200, 1500, TEST_OUT_OF_VIEWPORT_LINK_URL);
     }
 
     @Test
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index 0271d4fe..65e4549e 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -1054,9 +1054,17 @@
     }
   }
 
+  // TODO(crbug.com/959776): This currently only considers a possible username
+  // valid if a credential with the same username already exists for the same
+  // site. This is too conservative, and we should allow any possible username
+  // that matches a credential on any site in the user's password store.
+  std::vector<base::string16> usernames;
+  usernames.reserve(GetBestMatches().size());
+  base::ranges::transform(GetBestMatches(), std::back_inserter(usernames),
+                          &PasswordForm::username_value);
+
   bool is_possible_username_valid = IsPossibleUsernameValid(
-      *possible_username, parsed_submitted_form_->signon_realm,
-      base::Time::Now());
+      *possible_username, parsed_submitted_form_->signon_realm, usernames);
   LogUsingPossibleUsername(client_, /*is_used*/ is_possible_username_valid,
                            "Local heuristics");
   return is_possible_username_valid;
diff --git a/components/password_manager/core/browser/password_form_manager_unittest.cc b/components/password_manager/core/browser/password_form_manager_unittest.cc
index 11a093f4..c2ac2ff 100644
--- a/components/password_manager/core/browser/password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_form_manager_unittest.cc
@@ -2098,8 +2098,8 @@
   feature_list.InitAndEnableFeature(features::kUsernameFirstFlow);
 
   CreateFormManager(observed_form_only_password_fields_);
-  fetcher_->NotifyFetchCompleted();
-  const base::string16 possible_username = ASCIIToUTF16("possible_username");
+  SetNonFederatedAndNotifyFetchCompleted({&saved_match_});
+  const base::string16 possible_username = ASCIIToUTF16("test@example.com");
   PossibleUsernameData possible_username_data(
       saved_match_.signon_realm, autofill::FieldRendererId(1),
       possible_username, base::Time::Now(), 0 /* driver_id */);
@@ -2117,7 +2117,8 @@
 #else
   // Local heuristics on Android for username first flow are not supported, so
   // the username should not be taken from the username form.
-  EXPECT_TRUE(form_manager_->GetPendingCredentials().username_value.empty());
+  EXPECT_EQ(saved_match_.username_value,
+            form_manager_->GetPendingCredentials().username_value);
 #endif  // !defined(OS_ANDROID)
 }
 
@@ -2808,8 +2809,8 @@
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(features::kUsernameFirstFlow);
   CreateFormManager(observed_form_only_password_fields_);
-  fetcher_->NotifyFetchCompleted();
-  const base::string16 possible_username = ASCIIToUTF16("possible_username");
+  SetNonFederatedAndNotifyFetchCompleted({&saved_match_});
+  const base::string16 possible_username = ASCIIToUTF16("test@example.org");
   PossibleUsernameData possible_username_data(
       saved_match_.signon_realm, autofill::FieldRendererId(1u),
       possible_username, base::Time::Now(), 0 /* driver_id */);
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc
index 511dd4ac..a94b333 100644
--- a/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -74,8 +74,10 @@
 using base::Feature;
 using base::TestMockTimeTaskRunner;
 using testing::_;
+using testing::AllOf;
 using testing::AnyNumber;
 using testing::ByMove;
+using testing::Field;
 using testing::Invoke;
 using testing::IsNull;
 using testing::Mock;
@@ -3410,11 +3412,12 @@
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(features::kUsernameFirstFlow);
   EXPECT_CALL(*store_, GetLogins(_, _))
-      .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
+      .WillRepeatedly(
+          WithArg<1>(InvokeConsumer(store_.get(), MakeSavedForm())));
 
   PasswordForm form(MakeSimpleFormWithOnlyPasswordField());
   // Simulate the user typed a username in username form.
-  const base::string16 username = ASCIIToUTF16("username1");
+  const base::string16 username = ASCIIToUTF16("googleuser@gmail.com");
   EXPECT_CALL(driver_, GetLastCommittedURL()).WillOnce(ReturnRef(form.url));
   manager()->OnUserModifiedNonPasswordField(&driver_, FieldRendererId(1001),
                                             username /* value */);
@@ -3438,21 +3441,24 @@
   // Simulates successful submission.
   manager()->OnPasswordFormsRendered(&driver_, {} /* observed */, true);
 
-  // Simulate saving the form, as if the info bar was accepted.
-  PasswordForm saved_form;
-  EXPECT_CALL(*store_, AddLogin(_)).WillOnce(SaveArg<0>(&saved_form));
   ASSERT_TRUE(form_manager_to_save);
-  form_manager_to_save->Save();
-
-#if defined(OS_ANDROID)
-  // Local heuristics on Android for username first flow are not supported, so
-  // the username should not be taken from the username form.
-  EXPECT_TRUE(saved_form.username_value.empty());
+#if !defined(OS_ANDROID)
+  EXPECT_CALL(*store_,
+              AddLogin(AllOf(Field(&PasswordForm::username_value, username),
+                             Field(&PasswordForm::password_value, password))));
 #else
-  EXPECT_EQ(username, saved_form.username_value);
+  // Local heuristics on Android for username first flow are not supported, so
+  // the username should not be taken from the username form. Furthermore, since
+  // there is no change to the already saved username, this should trigger an
+  // update flow, rather than a save flow.
+  EXPECT_CALL(
+      *store_,
+      UpdateLogin(AllOf(
+          Field(&PasswordForm::username_value, MakeSavedForm().username_value),
+          Field(&PasswordForm::password_value, password))));
 #endif  // defined(OS_ANDROID)
-
-  EXPECT_EQ(password, saved_form.password_value);
+  // Simulate saving the form, as if the info bar was accepted.
+  form_manager_to_save->Save();
 }
 
 #if !defined(OS_IOS)
diff --git a/components/password_manager/core/browser/possible_username_data.cc b/components/password_manager/core/browser/possible_username_data.cc
index 4bdfc27e..fb1c26a 100644
--- a/components/password_manager/core/browser/possible_username_data.cc
+++ b/components/password_manager/core/browser/possible_username_data.cc
@@ -4,7 +4,12 @@
 
 #include "components/password_manager/core/browser/possible_username_data.h"
 
-#include "base/strings/string_util.h"
+#include <vector>
+
+#include "base/containers/contains.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "components/password_manager/core/browser/leak_detection/encryption_utils.h"
 
 using base::char16;
 using base::TimeDelta;
@@ -26,29 +31,25 @@
     default;
 PossibleUsernameData::~PossibleUsernameData() = default;
 
-bool IsPossibleUsernameValid(const PossibleUsernameData& possible_username,
-                             const std::string& submitted_signon_realm,
-                             const base::Time now) {
+bool IsPossibleUsernameValid(
+    const PossibleUsernameData& possible_username,
+    const std::string& submitted_signon_realm,
+    const std::vector<base::string16>& possible_usernames) {
   if (submitted_signon_realm != possible_username.signon_realm)
     return false;
+
   // The goal is to avoid false positives in considering which strings might be
   // username. In the initial version of the username first flow it is better to
-  // be conservative in that.
-  // TODO(https://crbug.com/959776): Reconsider allowing non-ascii symbols in
-  // username for the username first flow.
-  if (!base::IsStringASCII(possible_username.value))
-    return false;
-  for (char16 c : possible_username.value) {
-    if (base::IsUnicodeWhitespace(c))
-      return false;
-  }
-
-  if (now - possible_username.last_change >
-      kMaxDelayBetweenTypingUsernameAndSubmission) {
+  // be conservative in that. This check only allows usernames that match
+  // existing usernames after canonicalization.
+  base::string16 (*Canonicalize)(base::StringPiece16) = &CanonicalizeUsername;
+  if (!base::Contains(possible_usernames, Canonicalize(possible_username.value),
+                      Canonicalize)) {
     return false;
   }
 
-  return true;
+  return base::Time::Now() - possible_username.last_change <=
+         kMaxDelayBetweenTypingUsernameAndSubmission;
 }
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/possible_username_data.h b/components/password_manager/core/browser/possible_username_data.h
index e905789ab..2c47e526 100644
--- a/components/password_manager/core/browser/possible_username_data.h
+++ b/components/password_manager/core/browser/possible_username_data.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_POSSIBLE_USERNAME_DATA_H_
 
 #include <string>
+#include <vector>
 
 #include "base/optional.h"
 #include "base/strings/string16.h"
@@ -18,8 +19,8 @@
 // The maximum time between the user typed in a text field and subsequent
 // submission of the password form, such that the typed value is considered to
 // be possible to be username.
-constexpr base::TimeDelta kMaxDelayBetweenTypingUsernameAndSubmission =
-    base::TimeDelta::FromSeconds(60);
+constexpr auto kMaxDelayBetweenTypingUsernameAndSubmission =
+    base::TimeDelta::FromMinutes(1);
 
 // Contains information that the user typed in a text field. It might be the
 // username during username first flow.
@@ -47,12 +48,14 @@
 
 // Checks that |possible_username| might represent an username:
 // 1.|possible_username.signon_realm| == |submitted_signon_realm|
-// 2.|possible_username.value| does not have whitespaces and non-ASCII symbols.
-// 3.|possible_username.value.last_change| was not more than 60 seconds before
-// now.
-bool IsPossibleUsernameValid(const PossibleUsernameData& possible_username,
-                             const std::string& submitted_signon_realm,
-                             const base::Time now);
+// 2.|possible_username.value| is contained in |possible_usernames| after
+//   canonicalization.
+// 3.|possible_username.value.last_change| was not more than
+//   |kMaxDelayBetweenTypingUsernameAndSubmission| ago.
+bool IsPossibleUsernameValid(
+    const PossibleUsernameData& possible_username,
+    const std::string& submitted_signon_realm,
+    const std::vector<base::string16>& possible_usernames);
 
 }  // namespace password_manager
 
diff --git a/components/password_manager/core/browser/possible_username_data_unittest.cc b/components/password_manager/core/browser/possible_username_data_unittest.cc
index 0fd9d39..08b2000 100644
--- a/components/password_manager/core/browser/possible_username_data_unittest.cc
+++ b/components/password_manager/core/browser/possible_username_data_unittest.cc
@@ -5,10 +5,10 @@
 #include "components/password_manager/core/browser/possible_username_data.h"
 
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/task_environment.h"
 #include "components/autofill/core/common/renderer_id.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using base::ASCIIToUTF16;
 using base::Time;
 using base::TimeDelta;
 
@@ -16,71 +16,65 @@
 
 namespace {
 
-class IsPossibleUsernameValidTest : public testing::Test {
- public:
-  IsPossibleUsernameValidTest()
-      : possible_username_data_(
-            "https://example.com/" /* submitted_signon_realm */,
-            autofill::FieldRendererId(1u),
-            ASCIIToUTF16("username") /* value */,
-            base::Time::Now() /* last_change */,
-            10 /* driver_id */) {}
+constexpr base::char16 kUser[] = STRING16_LITERAL("user");
 
+class IsPossibleUsernameValidTest : public testing::Test {
  protected:
-  PossibleUsernameData possible_username_data_;
+  base::test::SingleThreadTaskEnvironment task_environment_{
+      base::test::SingleThreadTaskEnvironment::TimeSource::MOCK_TIME};
+  PossibleUsernameData possible_username_data_{
+      "https://example.com/" /* submitted_signon_realm */,
+      autofill::FieldRendererId(1u), kUser /* value */,
+      base::Time::Now() /* last_change */, 10 /* driver_id */};
 };
 
 TEST_F(IsPossibleUsernameValidTest, Valid) {
-  EXPECT_TRUE(IsPossibleUsernameValid(possible_username_data_,
-                                      possible_username_data_.signon_realm,
-                                      possible_username_data_.last_change));
+  EXPECT_TRUE(IsPossibleUsernameValid(
+      possible_username_data_, possible_username_data_.signon_realm, {kUser}));
 }
 
 // Check that if time delta between last change and submission is more than 60
 // seconds, than data is invalid.
 TEST_F(IsPossibleUsernameValidTest, TimeDeltaBeforeLastChangeAndSubmission) {
-  Time valid_submission_time = possible_username_data_.last_change +
-                               kMaxDelayBetweenTypingUsernameAndSubmission;
-  Time invalid_submission_time =
-      valid_submission_time + TimeDelta::FromSeconds(1);
-  EXPECT_TRUE(IsPossibleUsernameValid(possible_username_data_,
-                                      possible_username_data_.signon_realm,
-                                      valid_submission_time));
-  EXPECT_FALSE(IsPossibleUsernameValid(possible_username_data_,
-                                       possible_username_data_.signon_realm,
-                                       invalid_submission_time));
+  task_environment_.FastForwardBy(kMaxDelayBetweenTypingUsernameAndSubmission);
+  EXPECT_TRUE(IsPossibleUsernameValid(
+      possible_username_data_, possible_username_data_.signon_realm, {kUser}));
+  task_environment_.FastForwardBy(TimeDelta::FromSeconds(1));
+  EXPECT_FALSE(IsPossibleUsernameValid(
+      possible_username_data_, possible_username_data_.signon_realm, {kUser}));
 }
 
 TEST_F(IsPossibleUsernameValidTest, SignonRealm) {
   EXPECT_FALSE(IsPossibleUsernameValid(possible_username_data_,
-                                       "https://m.example.com/",
-                                       possible_username_data_.last_change));
+                                       "https://m.example.com/", {kUser}));
 
   EXPECT_FALSE(IsPossibleUsernameValid(possible_username_data_,
-                                       "https://google.com/",
-                                       possible_username_data_.last_change));
+                                       "https://google.com/", {kUser}));
 }
 
 TEST_F(IsPossibleUsernameValidTest, PossibleUsernameValue) {
-  PossibleUsernameData possible_username_data = possible_username_data_;
+  // Different capitalization is okay.
+  EXPECT_TRUE(IsPossibleUsernameValid(possible_username_data_,
+                                      possible_username_data_.signon_realm,
+                                      {STRING16_LITERAL("USER")}));
+  // Different email hosts are okay.
+  EXPECT_TRUE(IsPossibleUsernameValid(possible_username_data_,
+                                      possible_username_data_.signon_realm,
+                                      {STRING16_LITERAL("user@gmail.com")}));
 
-  // White spaces are not allowed in username.
-  possible_username_data.value = ASCIIToUTF16("user name");
-  EXPECT_FALSE(IsPossibleUsernameValid(possible_username_data,
-                                       possible_username_data.signon_realm,
-                                       possible_username_data.last_change));
+  // Other usernames are okay.
+  EXPECT_TRUE(IsPossibleUsernameValid(possible_username_data_,
+                                      possible_username_data_.signon_realm,
+                                      {kUser, STRING16_LITERAL("alice")}));
 
-  // New lines are not allowed in username.
-  possible_username_data.value = ASCIIToUTF16("user\nname");
-  EXPECT_FALSE(IsPossibleUsernameValid(possible_username_data,
-                                       possible_username_data.signon_realm,
-                                       possible_username_data.last_change));
+  // No usernames are not okay.
+  EXPECT_FALSE(IsPossibleUsernameValid(
+      possible_username_data_, possible_username_data_.signon_realm, {}));
 
-  // Digits and special characters are ok.
-  possible_username_data.value = ASCIIToUTF16("User_name1234!+&%#\'\"@");
-  EXPECT_TRUE(IsPossibleUsernameValid(possible_username_data,
-                                      possible_username_data.signon_realm,
-                                      possible_username_data.last_change));
+  // Completely different usernames are not okay.
+  EXPECT_FALSE(IsPossibleUsernameValid(
+      possible_username_data_, possible_username_data_.signon_realm,
+      {STRING16_LITERAL("alice"), STRING16_LITERAL("bob")}));
 }
 
 }  // namespace
diff --git a/components/payments/content/android/BUILD.gn b/components/payments/content/android/BUILD.gn
index b977170..eb60baa 100644
--- a/components/payments/content/android/BUILD.gn
+++ b/components/payments/content/android/BUILD.gn
@@ -288,6 +288,7 @@
     "//third_party/junit",
     "//third_party/mockito:mockito_java",
     "//url:gurl_java",
+    "//url:gurl_junit_test_support",
     "//url:origin_java",
   ]
 }
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/OriginSecurityChecker.java b/components/payments/content/android/java/src/org/chromium/components/payments/OriginSecurityChecker.java
index ea0f9bf..4dcfc70 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/OriginSecurityChecker.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/OriginSecurityChecker.java
@@ -6,6 +6,7 @@
 
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
+import org.chromium.url.GURL;
 
 /** Helper for origin security. */
 @JNINamespace("payments")
@@ -17,7 +18,7 @@
      * @param url The URL to check.
      * @return Whether the origin of the URL is secure.
      */
-    public static boolean isOriginSecure(String url) {
+    public static boolean isOriginSecure(GURL url) {
         return OriginSecurityCheckerJni.get().isOriginSecure(url);
     }
 
@@ -27,7 +28,7 @@
      * @param url The URL to check.
      * @return Whether the scheme of the URL is cryptographic.
      */
-    public static boolean isSchemeCryptographic(String url) {
+    public static boolean isSchemeCryptographic(GURL url) {
         return OriginSecurityCheckerJni.get().isSchemeCryptographic(url);
     }
 
@@ -35,7 +36,7 @@
 
     @NativeMethods
     interface Natives {
-        boolean isOriginSecure(String url);
-        boolean isSchemeCryptographic(String url);
+        boolean isOriginSecure(GURL url);
+        boolean isSchemeCryptographic(GURL url);
     }
 }
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestService.java b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestService.java
index f846965..72ef34a 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestService.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestService.java
@@ -226,7 +226,7 @@
          * @param url The URL to check.
          * @return Whether the origin of the URL is secure.
          */
-        default boolean isOriginSecure(String url) {
+        default boolean isOriginSecure(GURL url) {
             return OriginSecurityChecker.isOriginSecure(url);
         }
 
@@ -263,7 +263,7 @@
          * @param url The URL to check.
          * @return Whether the page is allowed to use web payment APIs.
          */
-        default boolean isOriginAllowedToUseWebPaymentApis(String url) {
+        default boolean isOriginAllowedToUseWebPaymentApis(GURL url) {
             return UrlUtil.isOriginAllowedToUseWebPaymentApis(url);
         }
 
@@ -429,7 +429,8 @@
             return false;
         }
         // TODO(crbug.com/992593): replace UrlFormatter with GURL operations.
-        mTopLevelOrigin = mDelegate.formatUrlForSecurityDisplay(mWebContents.getLastCommittedUrl());
+        mTopLevelOrigin =
+                mDelegate.formatUrlForSecurityDisplay(mWebContents.getLastCommittedUrl().getSpec());
 
         mMerchantName = mWebContents.getTitle();
         mCertificateChain = mDelegate.getCertificateChain(mWebContents);
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/UrlUtil.java b/components/payments/content/android/java/src/org/chromium/components/payments/UrlUtil.java
index 5a4d206..4bbada577 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/UrlUtil.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/UrlUtil.java
@@ -6,6 +6,7 @@
 
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
+import org.chromium.url.GURL;
 
 /** URL validity checker for web payment APIs. */
 @JNINamespace("payments::android")
@@ -15,7 +16,7 @@
      * @param url The URL to check.
      * @return Whether the page is allowed to use web payment APIs.
      */
-    public static boolean isOriginAllowedToUseWebPaymentApis(String url) {
+    public static boolean isOriginAllowedToUseWebPaymentApis(GURL url) {
         return UrlUtilJni.get().isOriginAllowedToUseWebPaymentApis(url);
     }
 
@@ -25,14 +26,14 @@
      * @param url The URL to check.
      * @return Whether this is a local development URL.
      */
-    public static boolean isLocalDevelopmentUrl(String url) {
+    public static boolean isLocalDevelopmentUrl(GURL url) {
         return UrlUtilJni.get().isLocalDevelopmentUrl(url);
     }
 
     /** The interface implemented by the automatically generated JNI bindings class UrlUtilJni. */
     @NativeMethods
     /* package */ interface Natives {
-        boolean isOriginAllowedToUseWebPaymentApis(String url);
-        boolean isLocalDevelopmentUrl(String url);
+        boolean isOriginAllowedToUseWebPaymentApis(GURL url);
+        boolean isLocalDevelopmentUrl(GURL url);
     }
 }
diff --git a/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentRequestServiceBuilder.java b/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentRequestServiceBuilder.java
index 5da36d2..a8ba8ee 100644
--- a/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentRequestServiceBuilder.java
+++ b/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentRequestServiceBuilder.java
@@ -17,6 +17,8 @@
 import org.chromium.payments.mojom.PaymentMethodData;
 import org.chromium.payments.mojom.PaymentOptions;
 import org.chromium.payments.mojom.PaymentRequestClient;
+import org.chromium.url.GURL;
+import org.chromium.url.JUnitTestGURLs;
 import org.chromium.url.Origin;
 
 import java.util.Collection;
@@ -60,7 +62,7 @@
             JourneyLogger journeyLogger) {
         mDelegate = this;
         mWebContents = Mockito.mock(WebContents.class);
-        setTopLevelOrigin("https://top.level.origin");
+        setTopLevelOrigin(JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_1));
         mRenderFrameHost = Mockito.mock(RenderFrameHost.class);
         setFrameOrigin("https://frame.origin");
         Origin origin = Mockito.mock(Origin.class);
@@ -122,7 +124,7 @@
     }
 
     @Override
-    public boolean isOriginSecure(String url) {
+    public boolean isOriginSecure(GURL url) {
         return mIsOriginSecure;
     }
 
@@ -142,7 +144,7 @@
     }
 
     @Override
-    public boolean isOriginAllowedToUseWebPaymentApis(String lastCommittedUrl) {
+    public boolean isOriginAllowedToUseWebPaymentApis(GURL lastCommittedUrl) {
         return mIsOriginAllowedToUseWebPaymentApis;
     }
 
@@ -230,7 +232,7 @@
         return this;
     }
 
-    /* package */ PaymentRequestServiceBuilder setTopLevelOrigin(String topLevelOrigin) {
+    /* package */ PaymentRequestServiceBuilder setTopLevelOrigin(GURL topLevelOrigin) {
         Mockito.doReturn(topLevelOrigin).when(mWebContents).getLastCommittedUrl();
         return this;
     }
diff --git a/components/payments/content/android/origin_security_checker_android.cc b/components/payments/content/android/origin_security_checker_android.cc
index d5c535e..7228fb4 100644
--- a/components/payments/content/android/origin_security_checker_android.cc
+++ b/components/payments/content/android/origin_security_checker_android.cc
@@ -6,6 +6,7 @@
 #include "base/android/scoped_java_ref.h"
 #include "components/payments/content/android/jni_headers/OriginSecurityChecker_jni.h"
 #include "services/network/public/cpp/is_potentially_trustworthy.h"
+#include "url/android/gurl_android.h"
 #include "url/gurl.h"
 
 namespace payments {
@@ -19,17 +20,17 @@
 // static
 jboolean JNI_OriginSecurityChecker_IsOriginSecure(
     JNIEnv* env,
-    const JavaParamRef<jstring>& jurl) {
-  GURL url(ConvertJavaStringToUTF8(env, jurl));
-  return url.is_valid() && network::IsUrlPotentiallyTrustworthy(url);
+    const JavaParamRef<jobject>& j_url) {
+  std::unique_ptr<GURL> url = url::GURLAndroid::ToNativeGURL(env, j_url);
+  return url->is_valid() && network::IsUrlPotentiallyTrustworthy(*url);
 }
 
 // static
 jboolean JNI_OriginSecurityChecker_IsSchemeCryptographic(
     JNIEnv* env,
-    const JavaParamRef<jstring>& jurl) {
-  GURL url(ConvertJavaStringToUTF8(env, jurl));
-  return url.is_valid() && url.SchemeIsCryptographic();
+    const JavaParamRef<jobject>& j_url) {
+  std::unique_ptr<GURL> url = url::GURLAndroid::ToNativeGURL(env, j_url);
+  return url->is_valid() && url->SchemeIsCryptographic();
 }
 
 }  // namespace payments
diff --git a/components/payments/content/android/url_util.cc b/components/payments/content/android/url_util.cc
index f0f56d99..66dc734 100644
--- a/components/payments/content/android/url_util.cc
+++ b/components/payments/content/android/url_util.cc
@@ -6,6 +6,7 @@
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
 #include "components/payments/content/android/jni_headers/UrlUtil_jni.h"
+#include "url/android/gurl_android.h"
 #include "url/gurl.h"
 
 namespace payments {
@@ -14,17 +15,17 @@
 // static
 jboolean JNI_UrlUtil_IsOriginAllowedToUseWebPaymentApis(
     JNIEnv* env,
-    const base::android::JavaParamRef<jstring>& jurl) {
-  return UrlUtil::IsOriginAllowedToUseWebPaymentApis(
-      GURL(base::android::ConvertJavaStringToUTF8(env, jurl)));
+    const base::android::JavaParamRef<jobject>& j_url) {
+  std::unique_ptr<GURL> url = url::GURLAndroid::ToNativeGURL(env, j_url);
+  return UrlUtil::IsOriginAllowedToUseWebPaymentApis(*url);
 }
 
 // static
 jboolean JNI_UrlUtil_IsLocalDevelopmentUrl(
     JNIEnv* env,
-    const base::android::JavaParamRef<jstring>& jurl) {
-  return UrlUtil::IsLocalDevelopmentUrl(
-      GURL(base::android::ConvertJavaStringToUTF8(env, jurl)));
+    const base::android::JavaParamRef<jobject>& j_url) {
+  std::unique_ptr<GURL> url = url::GURLAndroid::ToNativeGURL(env, j_url);
+  return UrlUtil::IsLocalDevelopmentUrl(*url);
 }
 
 }  // namespace android
diff --git a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
index 03523610..d63300cd 100644
--- a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
+++ b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
@@ -165,8 +165,6 @@
 // A mock class to allow us to set expectations on upload callbacks.
 class MockStatusCallbackObserver {
  public:
-  MockStatusCallbackObserver() = default;
-
   MOCK_METHOD1(OnCallbackComplete, void(bool));
 };
 
@@ -174,8 +172,6 @@
 // callbacks.
 class MockRemoteCommandsObserver {
  public:
-  MockRemoteCommandsObserver() = default;
-
   MOCK_METHOD3(OnRemoteCommandsFetched,
                void(DeviceManagementStatus,
                     const std::vector<em::RemoteCommand>&,
@@ -184,16 +180,12 @@
 
 class MockDeviceDMTokenCallbackObserver {
  public:
-  MockDeviceDMTokenCallbackObserver() = default;
-
   MOCK_METHOD1(OnDeviceDMTokenRequested,
                std::string(const std::vector<std::string>&));
 };
 
 class MockRobotAuthCodeCallbackObserver {
  public:
-  MockRobotAuthCodeCallbackObserver() = default;
-
   MOCK_METHOD2(OnRobotAuthCodeFetched,
                void(DeviceManagementStatus, const std::string&));
 };
@@ -205,6 +197,20 @@
   MOCK_METHOD1(OnResponseReceived, void(base::Optional<base::Value>));
 };
 
+std::string CreatePolicyData(const std::string& policy_value) {
+  em::PolicyData policy_data;
+  policy_data.set_policy_type(dm_protocol::kChromeUserPolicyType);
+  policy_data.set_policy_value(policy_value);
+  return policy_data.SerializeAsString();
+}
+
+em::DeviceManagementResponse GetPolicyResponse() {
+  em::DeviceManagementResponse policy_response;
+  policy_response.mutable_policy_response()->add_responses()->set_policy_data(
+      CreatePolicyData("fake-policy-data"));
+  return policy_response;
+}
+
 }  // namespace
 
 class CloudPolicyClientTest : public testing::Test {
@@ -249,7 +255,7 @@
 
     em::CertificateBasedDeviceRegistrationData data;
     data.set_certificate_type(em::CertificateBasedDeviceRegistrationData::
-        ENTERPRISE_ENROLLMENT_CERTIFICATE);
+                                  ENTERPRISE_ENROLLMENT_CERTIFICATE);
     data.set_device_certificate(kEnrollmentCertificate);
 
     em::DeviceRegisterRequest* request = data.mutable_device_register_request();
@@ -268,8 +274,9 @@
 
     em::CertificateBasedDeviceRegisterRequest* cert_based_register_request =
         cert_based_registration_request_
-        .mutable_certificate_based_register_request();
-    fake_signing_service_.SignDataSynchronously(data.SerializeAsString(),
+            .mutable_certificate_based_register_request();
+    fake_signing_service_.SignDataSynchronously(
+        data.SerializeAsString(),
         cert_based_register_request->mutable_signed_request());
 
     em::PolicyFetchRequest* policy_fetch_request =
@@ -278,12 +285,9 @@
     policy_fetch_request->set_signature_type(em::PolicyFetchRequest::SHA1_RSA);
     policy_fetch_request->set_verification_key_hash(kPolicyVerificationKeyHash);
     policy_fetch_request->set_device_dm_token(kDeviceDMToken);
-    policy_response_.mutable_policy_response()
-        ->add_responses()
-        ->set_policy_data(CreatePolicyData("fake-policy-data"));
 
-    registration_response_.mutable_register_response()->
-        set_device_management_token(kDMToken);
+    registration_response_.mutable_register_response()
+        ->set_device_management_token(kDMToken);
 
     failed_reregistration_response_.mutable_register_response()
         ->set_device_management_token(kDMToken2);
@@ -338,19 +342,19 @@
     remote_command_request_.mutable_remote_command_request()
         ->set_send_secure_commands(true);
 
-    attribute_update_permission_request_.
-        mutable_device_attribute_update_permission_request();
-    attribute_update_permission_response_.
-        mutable_device_attribute_update_permission_response()->
-        set_result(
+    attribute_update_permission_request_
+        .mutable_device_attribute_update_permission_request();
+    attribute_update_permission_response_
+        .mutable_device_attribute_update_permission_response()
+        ->set_result(
             em::DeviceAttributeUpdatePermissionResponse_ResultType_ATTRIBUTE_UPDATE_ALLOWED);
 
-    attribute_update_request_.mutable_device_attribute_update_request()->
-        set_asset_id(kAssetId);
-    attribute_update_request_.mutable_device_attribute_update_request()->
-        set_location(kLocation);
-    attribute_update_response_.mutable_device_attribute_update_response()->
-        set_result(
+    attribute_update_request_.mutable_device_attribute_update_request()
+        ->set_asset_id(kAssetId);
+    attribute_update_request_.mutable_device_attribute_update_request()
+        ->set_location(kLocation);
+    attribute_update_response_.mutable_device_attribute_update_response()
+        ->set_result(
             em::DeviceAttributeUpdateResponse_ResultType_ATTRIBUTE_UPDATE_SUCCESS);
 
     gcm_id_update_request_.mutable_gcm_id_update_request()->set_gcm_id(kGcmID);
@@ -387,13 +391,9 @@
 #endif
   }
 
-  void SetUp() override {
-    CreateClient();
-  }
+  void SetUp() override { CreateClient(); }
 
-  void TearDown() override {
-    client_->RemoveObserver(&observer_);
-  }
+  void TearDown() override { client_->RemoveObserver(&observer_); }
 
   void RegisterClient(const std::string& device_dm_token) {
     EXPECT_CALL(observer_, OnRegistrationStateChanged(_));
@@ -474,17 +474,11 @@
                         service_.StartJobAsync(net_error, response_code)));
   }
 
-  void CheckPolicyResponse() {
+  void CheckPolicyResponse(
+      const em::DeviceManagementResponse& policy_response) {
     ASSERT_TRUE(client_->GetPolicyFor(policy_type_, std::string()));
     EXPECT_THAT(*client_->GetPolicyFor(policy_type_, std::string()),
-                MatchProto(policy_response_.policy_response().responses(0)));
-  }
-
-  std::string CreatePolicyData(const std::string& policy_value) {
-    em::PolicyData policy_data;
-    policy_data.set_policy_type(dm_protocol::kChromeUserPolicyType);
-    policy_data.set_policy_value(policy_value);
-    return policy_data.SerializeAsString();
+                MatchProto(policy_response.policy_response().responses(0)));
   }
 
   void AttemptUploadEncryptedWaitUntilIdle(
@@ -522,7 +516,6 @@
   // Protobufs used in successful responses.
   em::DeviceManagementResponse registration_response_;
   em::DeviceManagementResponse failed_reregistration_response_;
-  em::DeviceManagementResponse policy_response_;
   em::DeviceManagementResponse unregistration_response_;
   em::DeviceManagementResponse upload_certificate_response_;
   em::DeviceManagementResponse upload_status_response_;
@@ -542,7 +535,7 @@
   std::string job_payload_;
   std::string client_id_;
   std::string policy_type_;
-  MockDeviceManagementService service_;
+  StrictMock<MockDeviceManagementService> service_;
   StrictMock<MockCloudPolicyClientObserver> observer_;
   StrictMock<MockStatusCallbackObserver> callback_observer_;
   StrictMock<MockDeviceDMTokenCallbackObserver>
@@ -560,23 +553,24 @@
 };
 
 TEST_F(CloudPolicyClientTest, Init) {
-  EXPECT_CALL(service_, StartJob(_)).Times(0);
   EXPECT_FALSE(client_->is_registered());
   EXPECT_FALSE(client_->GetPolicyFor(policy_type_, std::string()));
   EXPECT_EQ(0, client_->fetched_invalidation_version());
 }
 
 TEST_F(CloudPolicyClientTest, SetupRegistrationAndPolicyFetch) {
-  EXPECT_CALL(service_, StartJob(_)).Times(0);
-  EXPECT_CALL(observer_, OnRegistrationStateChanged(_));
-  EXPECT_CALL(device_dmtoken_callback_observer_, OnDeviceDMTokenRequested(_))
+  const em::DeviceManagementResponse policy_response = GetPolicyResponse();
+
+  EXPECT_CALL(observer_, OnRegistrationStateChanged);
+  EXPECT_CALL(device_dmtoken_callback_observer_,
+              OnDeviceDMTokenRequested(std::vector<std::string>()))
       .WillOnce(Return(kDeviceDMToken));
   client_->SetupRegistration(kDMToken, client_id_, std::vector<std::string>());
   EXPECT_TRUE(client_->is_registered());
   EXPECT_FALSE(client_->GetPolicyFor(policy_type_, std::string()));
 
-  ExpectAndCaptureJob(/*response=*/policy_response_);
-  EXPECT_CALL(observer_, OnPolicyFetched(_));
+  ExpectAndCaptureJob(/*response=*/policy_response);
+  EXPECT_CALL(observer_, OnPolicyFetched);
   client_->FetchPolicy();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(DeviceManagementService::JobConfiguration::TYPE_POLICY_FETCH,
@@ -585,11 +579,12 @@
   EXPECT_EQ(job_request_.SerializePartialAsString(),
             policy_request_.SerializePartialAsString());
   EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
-  CheckPolicyResponse();
+  CheckPolicyResponse(policy_response);
 }
 
 TEST_F(CloudPolicyClientTest, SetupRegistrationAndPolicyFetchWithOAuthToken) {
-  EXPECT_CALL(service_, StartJob(_)).Times(0);
+  const em::DeviceManagementResponse policy_response = GetPolicyResponse();
+
   EXPECT_CALL(observer_, OnRegistrationStateChanged(_));
   EXPECT_CALL(device_dmtoken_callback_observer_, OnDeviceDMTokenRequested(_))
       .WillOnce(Return(kDeviceDMToken));
@@ -598,7 +593,7 @@
   EXPECT_TRUE(client_->is_registered());
   EXPECT_FALSE(client_->GetPolicyFor(policy_type_, std::string()));
 
-  ExpectAndCaptureJob(/*response=*/policy_response_);
+  ExpectAndCaptureJob(/*response=*/policy_response);
   EXPECT_CALL(observer_, OnPolicyFetched(_));
   client_->FetchPolicy();
   base::RunLoop().RunUntilIdle();
@@ -610,12 +605,14 @@
   EXPECT_EQ(job_request_.SerializePartialAsString(),
             policy_request_.SerializePartialAsString());
   EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
-  CheckPolicyResponse();
+  CheckPolicyResponse(policy_response);
 }
 
 #if defined(OS_WIN) || defined(OS_APPLE) || \
     (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
 TEST_F(CloudPolicyClientTest, RegistrationWithTokenAndPolicyFetch) {
+  const em::DeviceManagementResponse policy_response = GetPolicyResponse();
+
   ExpectAndCaptureJob(/*response=*/registration_response_);
   EXPECT_CALL(observer_, OnRegistrationStateChanged(_));
   EXPECT_CALL(device_dmtoken_callback_observer_, OnDeviceDMTokenRequested(_))
@@ -630,7 +627,7 @@
   EXPECT_FALSE(client_->GetPolicyFor(policy_type_, std::string()));
   EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
 
-  ExpectAndCaptureJob(/*response=*/policy_response_);
+  ExpectAndCaptureJob(/*response=*/policy_response);
   EXPECT_CALL(observer_, OnPolicyFetched(_));
   client_->FetchPolicy();
   base::RunLoop().RunUntilIdle();
@@ -640,11 +637,13 @@
   EXPECT_EQ(job_request_.SerializePartialAsString(),
             policy_request_.SerializePartialAsString());
   EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
-  CheckPolicyResponse();
+  CheckPolicyResponse(policy_response);
 }
 #endif
 
 TEST_F(CloudPolicyClientTest, RegistrationAndPolicyFetch) {
+  const em::DeviceManagementResponse policy_response = GetPolicyResponse();
+
   ExpectAndCaptureJob(/*response=*/registration_response_);
   EXPECT_CALL(observer_, OnRegistrationStateChanged(_));
   EXPECT_CALL(device_dmtoken_callback_observer_, OnDeviceDMTokenRequested(_))
@@ -666,7 +665,7 @@
   EXPECT_FALSE(client_->GetPolicyFor(policy_type_, std::string()));
   EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
 
-  ExpectAndCaptureJob(/*response=*/policy_response_);
+  ExpectAndCaptureJob(/*response=*/policy_response);
   EXPECT_CALL(observer_, OnPolicyFetched(_));
   client_->FetchPolicy();
   base::RunLoop().RunUntilIdle();
@@ -676,10 +675,12 @@
   EXPECT_EQ(job_request_.SerializePartialAsString(),
             policy_request_.SerializePartialAsString());
   EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
-  CheckPolicyResponse();
+  CheckPolicyResponse(policy_response);
 }
 
 TEST_F(CloudPolicyClientTest, RegistrationAndPolicyFetchWithOAuthToken) {
+  const em::DeviceManagementResponse policy_response = GetPolicyResponse();
+
   ExpectAndCaptureJob(/*response=*/registration_response_);
   EXPECT_CALL(observer_, OnRegistrationStateChanged(_));
   EXPECT_CALL(device_dmtoken_callback_observer_, OnDeviceDMTokenRequested(_))
@@ -702,7 +703,7 @@
   EXPECT_FALSE(client_->GetPolicyFor(policy_type_, std::string()));
   EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
 
-  ExpectAndCaptureJob(/*response=*/policy_response_);
+  ExpectAndCaptureJob(/*response=*/policy_response);
   EXPECT_CALL(observer_, OnPolicyFetched(_));
   client_->FetchPolicy();
   base::RunLoop().RunUntilIdle();
@@ -714,10 +715,12 @@
   EXPECT_EQ(job_request_.SerializePartialAsString(),
             policy_request_.SerializePartialAsString());
   EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
-  CheckPolicyResponse();
+  CheckPolicyResponse(policy_response);
 }
 
 TEST_F(CloudPolicyClientTest, RegistrationWithCertificateAndPolicyFetch) {
+  const em::DeviceManagementResponse policy_response = GetPolicyResponse();
+
   EXPECT_CALL(device_dmtoken_callback_observer_, OnDeviceDMTokenRequested(_))
       .WillOnce(Return(kDeviceDMToken));
   ExpectAndCaptureJob(/*response=*/registration_response_);
@@ -739,7 +742,7 @@
   EXPECT_FALSE(client_->GetPolicyFor(policy_type_, std::string()));
   EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
 
-  ExpectAndCaptureJob(/*response=*/policy_response_);
+  ExpectAndCaptureJob(/*response=*/policy_response);
   EXPECT_CALL(observer_, OnPolicyFetched(_));
   client_->FetchPolicy();
   base::RunLoop().RunUntilIdle();
@@ -749,7 +752,7 @@
   EXPECT_EQ(job_request_.SerializePartialAsString(),
             policy_request_.SerializePartialAsString());
   EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
-  CheckPolicyResponse();
+  CheckPolicyResponse(policy_response);
 }
 
 TEST_F(CloudPolicyClientTest, RegistrationWithCertificateFailToSignRequest) {
@@ -798,8 +801,8 @@
 }
 
 TEST_F(CloudPolicyClientTest, RegistrationNoToken) {
-  registration_response_.mutable_register_response()->
-      clear_device_management_token();
+  registration_response_.mutable_register_response()
+      ->clear_device_management_token();
   ExpectAndCaptureJob(/*response=*/registration_response_);
   EXPECT_CALL(observer_, OnClientError(_));
   CloudPolicyClient::RegistrationParameters register_user(
@@ -889,38 +892,47 @@
 TEST_F(CloudPolicyClientTest, PolicyUpdate) {
   RegisterClient();
 
-  ExpectAndCaptureJob(/*response=*/policy_response_);
-  EXPECT_CALL(observer_, OnPolicyFetched(_));
-  client_->FetchPolicy();
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(DeviceManagementService::JobConfiguration::TYPE_POLICY_FETCH,
-            job_type_);
-  EXPECT_EQ(auth_data_, DMAuth::FromDMToken(kDMToken));
-  EXPECT_EQ(job_request_.SerializePartialAsString(),
-            policy_request_.SerializePartialAsString());
-  CheckPolicyResponse();
+  {
+    const em::DeviceManagementResponse policy_response = GetPolicyResponse();
 
-  policy_response_.mutable_policy_response()->clear_responses();
-  policy_response_.mutable_policy_response()->add_responses()->set_policy_data(
-      CreatePolicyData("updated-fake-policy-data"));
-  ExpectAndCaptureJob(/*response=*/policy_response_);
-  EXPECT_CALL(observer_, OnPolicyFetched(_));
-  client_->FetchPolicy();
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(DeviceManagementService::JobConfiguration::TYPE_POLICY_FETCH,
-            job_type_);
-  EXPECT_EQ(auth_data_, DMAuth::FromDMToken(kDMToken));
-  EXPECT_EQ(job_request_.SerializePartialAsString(),
-            policy_request_.SerializePartialAsString());
-  EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
-  CheckPolicyResponse();
+    ExpectAndCaptureJob(/*response=*/policy_response);
+    EXPECT_CALL(observer_, OnPolicyFetched(_));
+    client_->FetchPolicy();
+    base::RunLoop().RunUntilIdle();
+    EXPECT_EQ(DeviceManagementService::JobConfiguration::TYPE_POLICY_FETCH,
+              job_type_);
+    EXPECT_EQ(auth_data_, DMAuth::FromDMToken(kDMToken));
+    EXPECT_EQ(job_request_.SerializePartialAsString(),
+              policy_request_.SerializePartialAsString());
+    CheckPolicyResponse(policy_response);
+  }
+
+  {
+    em::DeviceManagementResponse policy_response;
+    policy_response.mutable_policy_response()->add_responses()->set_policy_data(
+        CreatePolicyData("updated-fake-policy-data"));
+
+    ExpectAndCaptureJob(/*response=*/policy_response);
+    EXPECT_CALL(observer_, OnPolicyFetched(_));
+    client_->FetchPolicy();
+    base::RunLoop().RunUntilIdle();
+    EXPECT_EQ(DeviceManagementService::JobConfiguration::TYPE_POLICY_FETCH,
+              job_type_);
+    EXPECT_EQ(auth_data_, DMAuth::FromDMToken(kDMToken));
+    EXPECT_EQ(job_request_.SerializePartialAsString(),
+              policy_request_.SerializePartialAsString());
+    EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
+    CheckPolicyResponse(policy_response);
+  }
 }
 
 TEST_F(CloudPolicyClientTest, PolicyFetchWithMetaData) {
+  em::DeviceManagementResponse policy_response = GetPolicyResponse();
+
   RegisterClient();
 
-  const base::Time timestamp(
-      base::Time::UnixEpoch() + base::TimeDelta::FromDays(20));
+  const base::Time timestamp(base::Time::UnixEpoch() +
+                             base::TimeDelta::FromDays(20));
   client_->set_last_policy_timestamp(timestamp);
   client_->set_public_key_version(42);
   em::PolicyFetchRequest* policy_fetch_request =
@@ -928,7 +940,7 @@
   policy_fetch_request->set_timestamp(timestamp.ToJavaTime());
   policy_fetch_request->set_public_key_version(42);
 
-  ExpectAndCaptureJob(/*response=*/policy_response_);
+  ExpectAndCaptureJob(/*response=*/policy_response);
   EXPECT_CALL(observer_, OnPolicyFetched(_));
   client_->FetchPolicy();
   base::RunLoop().RunUntilIdle();
@@ -937,10 +949,12 @@
   EXPECT_EQ(auth_data_, DMAuth::FromDMToken(kDMToken));
   EXPECT_EQ(job_request_.SerializePartialAsString(),
             policy_request_.SerializePartialAsString());
-  CheckPolicyResponse();
+  CheckPolicyResponse(policy_response);
 }
 
 TEST_F(CloudPolicyClientTest, PolicyFetchWithInvalidation) {
+  em::DeviceManagementResponse policy_response = GetPolicyResponse();
+
   RegisterClient();
 
   int64_t previous_version = client_->fetched_invalidation_version();
@@ -951,7 +965,7 @@
   policy_fetch_request->set_invalidation_version(12345);
   policy_fetch_request->set_invalidation_payload("12345");
 
-  ExpectAndCaptureJob(/*response=*/policy_response_);
+  ExpectAndCaptureJob(/*response=*/policy_response);
   EXPECT_CALL(observer_, OnPolicyFetched(_));
   client_->FetchPolicy();
   base::RunLoop().RunUntilIdle();
@@ -960,18 +974,20 @@
   EXPECT_EQ(auth_data_, DMAuth::FromDMToken(kDMToken));
   EXPECT_EQ(job_request_.SerializePartialAsString(),
             policy_request_.SerializePartialAsString());
-  CheckPolicyResponse();
+  CheckPolicyResponse(policy_response);
   EXPECT_EQ(12345, client_->fetched_invalidation_version());
 }
 
 TEST_F(CloudPolicyClientTest, PolicyFetchWithInvalidationNoPayload) {
+  const em::DeviceManagementResponse policy_response = GetPolicyResponse();
+
   RegisterClient();
 
   int64_t previous_version = client_->fetched_invalidation_version();
   client_->SetInvalidationInfo(-12345, std::string());
   EXPECT_EQ(previous_version, client_->fetched_invalidation_version());
 
-  ExpectAndCaptureJob(/*response=*/policy_response_);
+  ExpectAndCaptureJob(/*response=*/policy_response);
   EXPECT_CALL(observer_, OnPolicyFetched(_));
   client_->FetchPolicy();
   base::RunLoop().RunUntilIdle();
@@ -980,16 +996,18 @@
   EXPECT_EQ(auth_data_, DMAuth::FromDMToken(kDMToken));
   EXPECT_EQ(job_request_.SerializePartialAsString(),
             policy_request_.SerializePartialAsString());
-  CheckPolicyResponse();
+  CheckPolicyResponse(policy_response);
   EXPECT_EQ(-12345, client_->fetched_invalidation_version());
 }
 
 // Tests that previous OAuth token is no longer sent in policy fetch after its
 // value was cleared.
 TEST_F(CloudPolicyClientTest, PolicyFetchClearOAuthToken) {
+  const em::DeviceManagementResponse policy_response = GetPolicyResponse();
+
   RegisterClient();
 
-  ExpectAndCaptureJob(/*response=*/policy_response_);
+  ExpectAndCaptureJob(/*response=*/policy_response);
   EXPECT_CALL(observer_, OnPolicyFetched(_));
   client_->SetOAuthTokenAsAdditionalAuth(kOAuthToken);
   client_->FetchPolicy();
@@ -1001,9 +1019,9 @@
               Contains(Pair(dm_protocol::kParamOAuthToken, kOAuthToken)));
   EXPECT_EQ(job_request_.SerializePartialAsString(),
             policy_request_.SerializePartialAsString());
-  CheckPolicyResponse();
+  CheckPolicyResponse(policy_response);
 
-  ExpectAndCaptureJob(/*response=*/policy_response_);
+  ExpectAndCaptureJob(/*response=*/policy_response);
   EXPECT_CALL(observer_, OnPolicyFetched(_));
   client_->SetOAuthTokenAsAdditionalAuth("");
   client_->FetchPolicy();
@@ -1013,14 +1031,15 @@
   EXPECT_EQ(auth_data_, DMAuth::FromDMToken(kDMToken));
   EXPECT_EQ(job_request_.SerializePartialAsString(),
             policy_request_.SerializePartialAsString());
-  CheckPolicyResponse();
+  CheckPolicyResponse(policy_response);
 }
 
 TEST_F(CloudPolicyClientTest, BadPolicyResponse) {
+  em::DeviceManagementResponse policy_response;
+
   RegisterClient();
 
-  policy_response_.clear_policy_response();
-  ExpectAndCaptureJob(/*response=*/policy_response_);
+  ExpectAndCaptureJob(/*response=*/policy_response);
   EXPECT_CALL(observer_, OnClientError(_));
   client_->FetchPolicy();
   base::RunLoop().RunUntilIdle();
@@ -1032,11 +1051,11 @@
   EXPECT_FALSE(client_->GetPolicyFor(policy_type_, std::string()));
   EXPECT_EQ(DM_STATUS_RESPONSE_DECODING_ERROR, client_->status());
 
-  policy_response_.mutable_policy_response()->add_responses()->set_policy_data(
+  policy_response.mutable_policy_response()->add_responses()->set_policy_data(
       CreatePolicyData("fake-policy-data"));
-  policy_response_.mutable_policy_response()->add_responses()->set_policy_data(
+  policy_response.mutable_policy_response()->add_responses()->set_policy_data(
       CreatePolicyData("excess-fake-policy-data"));
-  ExpectAndCaptureJob(/*response=*/policy_response_);
+  ExpectAndCaptureJob(/*response=*/policy_response);
   EXPECT_CALL(observer_, OnPolicyFetched(_));
   client_->FetchPolicy();
   base::RunLoop().RunUntilIdle();
@@ -1046,7 +1065,7 @@
   EXPECT_EQ(job_request_.SerializePartialAsString(),
             policy_request_.SerializePartialAsString());
   EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
-  CheckPolicyResponse();
+  CheckPolicyResponse(policy_response);
 }
 
 TEST_F(CloudPolicyClientTest, PolicyRequestFailure) {
@@ -1119,13 +1138,15 @@
 }
 
 TEST_F(CloudPolicyClientTest, PolicyFetchWithExtensionPolicy) {
+  em::DeviceManagementResponse policy_response = GetPolicyResponse();
+
   RegisterClient();
 
-  // Set up the |expected_responses| and |policy_response_|.
+  // Set up the |expected_responses| and |policy_response|.
   static const char* kExtensions[] = {
-    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
-    "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
-    "cccccccccccccccccccccccccccccccc",
+      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+      "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+      "cccccccccccccccccccccccccccccccc",
   };
   typedef std::map<std::pair<std::string, std::string>, em::PolicyFetchResponse>
       ResponseMap;
@@ -1135,7 +1156,7 @@
                                           std::string());
   // Copy the user policy fetch request.
   expected_responses[key].CopyFrom(
-      policy_response_.policy_response().responses(0));
+      policy_response.policy_response().responses(0));
   expected_namespaces.insert(key);
   key.first = dm_protocol::kChromeExtensionPolicyType;
   expected_namespaces.insert(key);
@@ -1145,7 +1166,7 @@
     policy_data.set_policy_type(key.first);
     policy_data.set_settings_entity_id(key.second);
     expected_responses[key].set_policy_data(policy_data.SerializeAsString());
-    policy_response_.mutable_policy_response()->add_responses()->CopyFrom(
+    policy_response.mutable_policy_response()->add_responses()->CopyFrom(
         expected_responses[key]);
   }
 
@@ -1156,7 +1177,7 @@
           service_.CaptureJobType(&job_type),
           service_.CaptureRequest(&policy_request_),
           service_.StartJobAsync(net::OK, DeviceManagementService::kSuccess,
-                                 policy_response_)));
+                                 policy_response)));
   EXPECT_CALL(observer_, OnPolicyFetched(_));
   client_->AddPolicyTypeToFetch(dm_protocol::kChromeExtensionPolicyType,
                                 std::string());
@@ -1396,7 +1417,9 @@
             job_type);
 
   // Now initiate a policy fetch - this should not cancel the upload job.
-  ExpectAndCaptureJob(/*response=*/policy_response_);
+  const em::DeviceManagementResponse policy_response = GetPolicyResponse();
+
+  ExpectAndCaptureJob(/*response=*/policy_response);
   EXPECT_CALL(observer_, OnPolicyFetched(_));
   client_->FetchPolicy();
   base::RunLoop().RunUntilIdle();
@@ -1405,7 +1428,7 @@
   EXPECT_EQ(auth_data_, DMAuth::FromDMToken(kDMToken));
   EXPECT_EQ(job_request_.SerializePartialAsString(),
             policy_request_.SerializePartialAsString());
-  CheckPolicyResponse();
+  CheckPolicyResponse(policy_response);
 
   // upload_status_job->SendResponse(DM_STATUS_SUCCESS,
   // upload_status_response_);
@@ -2226,7 +2249,7 @@
                      base::Unretained(&robot_auth_code_callback_observer_)));
   base::RunLoop().RunUntilIdle();
 
-  ExpectAndCaptureJob(/*response=*/policy_response_);
+  ExpectAndCaptureJob(/*response=*/GetPolicyResponse());
   EXPECT_CALL(observer_, OnPolicyFetched(_));
 
   client_->FetchPolicy();
diff --git a/components/signin/public/identity_manager/objc/BUILD.gn b/components/signin/public/identity_manager/objc/BUILD.gn
index 950157c..ceea196 100644
--- a/components/signin/public/identity_manager/objc/BUILD.gn
+++ b/components/signin/public/identity_manager/objc/BUILD.gn
@@ -11,3 +11,23 @@
 
   public_deps = [ "//components/signin/public/identity_manager" ]
 }
+
+source_set("unit_tests") {
+  testonly = true
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  sources = [ "identity_manager_observer_bridge_unittest.mm" ]
+
+  deps = [
+    ":objc",
+    "//base/test:test_support",
+    "//components/signin/public/identity_manager:test_support",
+    "//services/network:test_support",
+    "//testing/gtest",
+  ]
+
+  if (is_ios) {
+    deps += [ "//components/signin/public/identity_manager/ios:test_support" ]
+  }
+}
diff --git a/components/signin/public/identity_manager/objc/identity_manager_observer_bridge_unittest.mm b/components/signin/public/identity_manager/objc/identity_manager_observer_bridge_unittest.mm
new file mode 100644
index 0000000..13b4e20
--- /dev/null
+++ b/components/signin/public/identity_manager/objc/identity_manager_observer_bridge_unittest.mm
@@ -0,0 +1,200 @@
+// 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 "components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h"
+
+#import "base/test/task_environment.h"
+#import "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h"
+#import "components/signin/public/identity_manager/identity_test_environment.h"
+#import "components/signin/public/identity_manager/primary_account_change_event.h"
+#import "services/network/test/test_url_loader_factory.h"
+#import "testing/gtest/include/gtest/gtest.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface ObserverBridgeDelegateFake
+    : NSObject <IdentityManagerObserverBridgeDelegate>
+
+@property(nonatomic, assign) NSInteger onPrimaryAccountSetCount;
+@property(nonatomic, assign) NSInteger onPrimaryAccountClearedCount;
+@property(nonatomic, assign) NSInteger onRefreshTokenUpdatedForAccountCount;
+@property(nonatomic, assign) NSInteger onRefreshTokenRemovedForAccountCount;
+@property(nonatomic, assign) NSInteger onRefreshTokensLoadedCount;
+@property(nonatomic, assign) NSInteger onAccountsInCookieUpdatedCount;
+@property(nonatomic, assign)
+    NSInteger onEndBatchOfRefreshTokenStateChangesCount;
+
+@property(nonatomic, assign) CoreAccountInfo receivedPrimaryAccountInfo;
+@property(nonatomic, assign) CoreAccountId receivedAccountId;
+@property(nonatomic, assign)
+    signin::AccountsInCookieJarInfo receivedccountsInCookieJarInfo;
+@property(nonatomic, assign) GoogleServiceAuthError receivedError;
+
+@end
+
+@implementation ObserverBridgeDelegateFake
+
+- (void)onPrimaryAccountSet:(const CoreAccountInfo&)primaryAccountInfo {
+  ++self.onPrimaryAccountSetCount;
+  self.receivedPrimaryAccountInfo = primaryAccountInfo;
+}
+
+- (void)onPrimaryAccountCleared:
+    (const CoreAccountInfo&)previousPrimaryAccountInfo {
+  ++self.onPrimaryAccountClearedCount;
+  self.receivedPrimaryAccountInfo = previousPrimaryAccountInfo;
+}
+
+- (void)onRefreshTokenUpdatedForAccount:(const CoreAccountInfo&)accountInfo {
+  ++self.onRefreshTokenUpdatedForAccountCount;
+  self.receivedPrimaryAccountInfo = accountInfo;
+}
+
+- (void)onRefreshTokenRemovedForAccount:(const CoreAccountId&)accountId {
+  ++self.onRefreshTokenRemovedForAccountCount;
+  self.receivedAccountId = accountId;
+}
+
+- (void)onRefreshTokensLoaded {
+  ++self.onRefreshTokensLoadedCount;
+}
+
+- (void)onAccountsInCookieUpdated:
+            (const signin::AccountsInCookieJarInfo&)accountsInCookieJarInfo
+                            error:(const GoogleServiceAuthError&)error {
+  ++self.onAccountsInCookieUpdatedCount;
+  self.receivedccountsInCookieJarInfo = accountsInCookieJarInfo;
+  self.receivedError = error;
+}
+
+- (void)onEndBatchOfRefreshTokenStateChanges {
+  ++self.onEndBatchOfRefreshTokenStateChangesCount;
+}
+
+@end
+
+namespace signin {
+
+class IdentityManagerObserverBridgeTest : public testing::Test {
+ protected:
+  IdentityManagerObserverBridgeTest()
+      : identity_test_env_(&test_url_loader_factory_) {
+    observer_bridge_delegate_ = [[ObserverBridgeDelegateFake alloc] init];
+    signin::IdentityManager* identity_manager =
+        identity_test_env_.identity_manager();
+    observer_bridge_ = std::make_unique<signin::IdentityManagerObserverBridge>(
+        identity_manager, observer_bridge_delegate_);
+    account_id_ = CoreAccountId("accountid");
+    account_info_.account_id = account_id_;
+    account_info_.gaia = "joegaia";
+    account_info_.email = "joe@example.com";
+
+    const std::string gaia_id = signin::GetTestGaiaIdForEmail("1@mail.com");
+    gaia::ListedAccount one;
+    one.id = CoreAccountId(gaia_id);
+    just_one_.push_back(one);
+  }
+  ~IdentityManagerObserverBridgeTest() override {}
+
+ public:
+  IdentityManagerObserverBridgeTest(const IdentityManagerObserverBridgeTest&) =
+      delete;
+  IdentityManagerObserverBridgeTest& operator=(
+      const IdentityManagerObserverBridgeTest&) = delete;
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  signin::IdentityTestEnvironment identity_test_env_;
+  std::unique_ptr<signin::IdentityManagerObserverBridge> observer_bridge_;
+  ObserverBridgeDelegateFake* observer_bridge_delegate_;
+  CoreAccountId account_id_;
+  CoreAccountInfo account_info_;
+  const std::vector<gaia::ListedAccount> no_account_;
+  std::vector<gaia::ListedAccount> just_one_;
+};
+
+// Tests IdentityManagerObserverBridge::OnPrimaryAccountChanged(), with set
+// event.
+TEST_F(IdentityManagerObserverBridgeTest, TestOnPrimaryAccountSet) {
+  PrimaryAccountChangeEvent::State previous_state;
+  PrimaryAccountChangeEvent::State current_state(account_info_,
+                                                 signin::ConsentLevel::kSync);
+  PrimaryAccountChangeEvent event_details(previous_state, current_state);
+  observer_bridge_.get()->OnPrimaryAccountChanged(event_details);
+  EXPECT_EQ(1, observer_bridge_delegate_.onPrimaryAccountSetCount);
+  EXPECT_EQ(account_info_,
+            observer_bridge_delegate_.receivedPrimaryAccountInfo);
+}
+
+// Tests IdentityManagerObserverBridge::OnPrimaryAccountChanged(), with clear
+// event.
+TEST_F(IdentityManagerObserverBridgeTest, TestOnPrimaryAccountCleared) {
+  PrimaryAccountChangeEvent::State previous_state(account_info_,
+                                                  signin::ConsentLevel::kSync);
+  PrimaryAccountChangeEvent::State current_state;
+  PrimaryAccountChangeEvent event_details(previous_state, current_state);
+  observer_bridge_.get()->OnPrimaryAccountChanged(event_details);
+  EXPECT_EQ(1, observer_bridge_delegate_.onPrimaryAccountClearedCount);
+  EXPECT_EQ(account_info_,
+            observer_bridge_delegate_.receivedPrimaryAccountInfo);
+}
+
+// Tests IdentityManagerObserverBridge::OnRefreshTokenUpdatedForAccount()
+TEST_F(IdentityManagerObserverBridgeTest, TestOnRefreshTokenUpdatedForAccount) {
+  observer_bridge_.get()->OnRefreshTokenUpdatedForAccount(account_info_);
+  EXPECT_EQ(1, observer_bridge_delegate_.onRefreshTokenUpdatedForAccountCount);
+  EXPECT_EQ(account_info_,
+            observer_bridge_delegate_.receivedPrimaryAccountInfo);
+}
+
+// Tests IdentityManagerObserverBridge::OnRefreshTokenRemovedForAccount()
+TEST_F(IdentityManagerObserverBridgeTest, OnRefreshTokenRemovedForAccount) {
+  CoreAccountId account_id;
+  observer_bridge_.get()->OnRefreshTokenRemovedForAccount(account_id);
+  EXPECT_EQ(1, observer_bridge_delegate_.onRefreshTokenRemovedForAccountCount);
+}
+
+// Tests IdentityManagerObserverBridge::OnRefreshTokensLoaded()
+TEST_F(IdentityManagerObserverBridgeTest, OnRefreshTokensLoaded) {
+  observer_bridge_.get()->OnRefreshTokensLoaded();
+  EXPECT_EQ(1, observer_bridge_delegate_.onRefreshTokensLoadedCount);
+}
+
+// Tests IdentityManagerObserverBridge::OnAccountsInCookieUpdated() with no
+// error.
+TEST_F(IdentityManagerObserverBridgeTest,
+       OnAccountsInCookieUpdatedWithNoError) {
+  signin::AccountsInCookieJarInfo accounts_in_cookie_jar_info = {
+      true, just_one_, no_account_};
+  GoogleServiceAuthError noError(GoogleServiceAuthError::State::NONE);
+  observer_bridge_.get()->OnAccountsInCookieUpdated(accounts_in_cookie_jar_info,
+                                                    noError);
+  EXPECT_EQ(1, observer_bridge_delegate_.onAccountsInCookieUpdatedCount);
+  EXPECT_EQ(noError, observer_bridge_delegate_.receivedError);
+}
+
+// Tests IdentityManagerObserverBridge::OnAccountsInCookieUpdated() with error.
+TEST_F(IdentityManagerObserverBridgeTest, OnAccountsInCookieUpdatedWithError) {
+  signin::AccountsInCookieJarInfo accounts_in_cookie_jar_info = {
+      false, no_account_, just_one_};
+  GoogleServiceAuthError error(
+      GoogleServiceAuthError::State::CONNECTION_FAILED);
+  observer_bridge_.get()->OnAccountsInCookieUpdated(accounts_in_cookie_jar_info,
+                                                    error);
+  EXPECT_EQ(1, observer_bridge_delegate_.onAccountsInCookieUpdatedCount);
+  EXPECT_EQ(error, observer_bridge_delegate_.receivedError);
+}
+
+// Tests IdentityManagerObserverBridge::OnEndBatchOfRefreshTokenStateChanges().
+TEST_F(IdentityManagerObserverBridgeTest,
+       OnEndBatchOfRefreshTokenStateChanges) {
+  observer_bridge_.get()->OnEndBatchOfRefreshTokenStateChanges();
+  EXPECT_EQ(
+      1, observer_bridge_delegate_.onEndBatchOfRefreshTokenStateChangesCount);
+}
+
+}
diff --git a/components/spellcheck/browser/spellcheck_host_metrics.cc b/components/spellcheck/browser/spellcheck_host_metrics.cc
index 4e658ee..c9bee576 100644
--- a/components/spellcheck/browser/spellcheck_host_metrics.cc
+++ b/components/spellcheck/browser/spellcheck_host_metrics.cc
@@ -60,9 +60,6 @@
       RecordReplacedWordStats(0);
   }
 
-  int percentage = (100 * misspelled_word_count_) / spellchecked_word_count_;
-  UMA_HISTOGRAM_PERCENTAGE("SpellCheck.MisspellRatio", percentage);
-
   // Collects actual number of checked words, excluding duplication.
   base::MD5Digest digest;
   base::MD5Sum(reinterpret_cast<const unsigned char*>(word.c_str()),
@@ -101,14 +98,6 @@
 void SpellCheckHostMetrics::RecordReplacedWordStats(int delta) {
   replaced_word_count_ += delta;
 
-  if (misspelled_word_count_) {
-    // zero |misspelled_word_count_| is possible when an extension
-    // gives the misspelling, which is not recorded as a part of this
-    // metrics.
-    int percentage = (100 * replaced_word_count_) / misspelled_word_count_;
-    UMA_HISTOGRAM_PERCENTAGE("SpellCheck.ReplaceRatio", percentage);
-  }
-
   if (suggestion_show_count_) {
     int percentage = (100 * replaced_word_count_) / suggestion_show_count_;
     UMA_HISTOGRAM_PERCENTAGE("SpellCheck.SuggestionHitRatio", percentage);
diff --git a/components/sync/engine_impl/commit_contribution_impl.cc b/components/sync/engine_impl/commit_contribution_impl.cc
index 67ebb67..b73c9b2 100644
--- a/components/sync/engine_impl/commit_contribution_impl.cc
+++ b/components/sync/engine_impl/commit_contribution_impl.cc
@@ -197,7 +197,12 @@
   commit_proto->set_id_string(entity_data.id);
   // Populate client_defined_unique_tag only for non-bookmark and non-Nigori
   // data types.
-  if (type != BOOKMARKS && type != NIGORI) {
+  if (type == NIGORI) {
+    // Client tags are irrelevant for NIGORI (it uses the root node).
+  } else if (type != BOOKMARKS ||
+             !entity_data.client_tag_hash.value().empty()) {
+    // The client tag is mandatory for all datatypes except bookmarks, and
+    // experimental for bookmarks (behind feature toggle).
     commit_proto->set_client_defined_unique_tag(
         entity_data.client_tag_hash.value());
   }
diff --git a/components/sync/protocol/bookmark_model_metadata.proto b/components/sync/protocol/bookmark_model_metadata.proto
index d07ee5c..6b52c8ea 100644
--- a/components/sync/protocol/bookmark_model_metadata.proto
+++ b/components/sync/protocol/bookmark_model_metadata.proto
@@ -46,4 +46,12 @@
   // is required to populate the client tag (and be considered invalid
   // otherwise).
   optional int64 last_sync_time = 4;
+
+  // Represents whether bookmark commits sent to the server (most importantly
+  // creations) populate client tags. This is a layer on top of the usual
+  // FeatureList to avoid risky transitions during startup, to guard against
+  // in-flight commits.
+  // TODO(crbug.com/1032052): remove this code when the logic is enabled by
+  // default and enforced to true upon startup.
+  optional bool bookmark_client_tags_in_protocol_enabled = 5;
 }
diff --git a/components/sync/trusted_vault/standalone_trusted_vault_client.cc b/components/sync/trusted_vault/standalone_trusted_vault_client.cc
index 98a8eda..4b78da2 100644
--- a/components/sync/trusted_vault/standalone_trusted_vault_client.cc
+++ b/components/sync/trusted_vault/standalone_trusted_vault_client.cc
@@ -147,7 +147,8 @@
   std::unique_ptr<TrustedVaultConnection> connection;
   GURL trusted_vault_service_gurl =
       ExtractTrustedVaultServiceURLFromCommandLine();
-  if (trusted_vault_service_gurl.is_valid()) {
+  if (base::FeatureList::IsEnabled(switches::kFollowTrustedVaultKeyRotation) &&
+      trusted_vault_service_gurl.is_valid()) {
     connection = std::make_unique<TrustedVaultConnectionImpl>(
         trusted_vault_service_gurl, url_loader_factory->Clone(),
         std::make_unique<TrustedVaultAccessTokenFetcherImpl>(
diff --git a/components/sync_bookmarks/bookmark_local_changes_builder.cc b/components/sync_bookmarks/bookmark_local_changes_builder.cc
index 849ae46..6c5b57b 100644
--- a/components/sync_bookmarks/bookmark_local_changes_builder.cc
+++ b/components/sync_bookmarks/bookmark_local_changes_builder.cc
@@ -47,6 +47,19 @@
     data->creation_time = syncer::ProtoTimeToTime(metadata->creation_time());
     data->modification_time =
         syncer::ProtoTimeToTime(metadata->modification_time());
+
+    if (entity->has_final_guid() &&
+        bookmark_tracker_->bookmark_client_tags_in_protocol_enabled()) {
+      DCHECK(!metadata->client_tag_hash().empty());
+      data->client_tag_hash =
+          syncer::ClientTagHash::FromHashed(metadata->client_tag_hash());
+      DCHECK(metadata->is_deleted() ||
+             data->client_tag_hash ==
+                 syncer::ClientTagHash::FromUnhashed(
+                     syncer::BOOKMARKS,
+                     entity->bookmark_node()->guid().AsLowercaseString()));
+    }
+
     if (!metadata->is_deleted()) {
       const bookmarks::BookmarkNode* node = entity->bookmark_node();
       // Skip current entity if its favicon is not loaded yet. It will be
diff --git a/components/sync_bookmarks/bookmark_model_merger.cc b/components/sync_bookmarks/bookmark_model_merger.cc
index b5b5719..a13f8307 100644
--- a/components/sync_bookmarks/bookmark_model_merger.cc
+++ b/components/sync_bookmarks/bookmark_model_merger.cc
@@ -331,6 +331,7 @@
     return false;
   }
   if (!HasExpectedBookmarkGuid(update_entity.specifics.bookmark(),
+                               update_entity.client_tag_hash,
                                update_entity.originator_cache_guid,
                                update_entity.originator_client_item_id)) {
     // Ignore updates with an unexpected GUID.
diff --git a/components/sync_bookmarks/bookmark_remote_updates_handler.cc b/components/sync_bookmarks/bookmark_remote_updates_handler.cc
index 55a0c90..d5ee58f4 100644
--- a/components/sync_bookmarks/bookmark_remote_updates_handler.cc
+++ b/components/sync_bookmarks/bookmark_remote_updates_handler.cc
@@ -240,6 +240,7 @@
         continue;
       }
       if (!HasExpectedBookmarkGuid(update_entity.specifics.bookmark(),
+                                   update_entity.client_tag_hash,
                                    update_entity.originator_cache_guid,
                                    update_entity.originator_client_item_id)) {
         // Ignore updates with an unexpected GUID.
@@ -292,13 +293,25 @@
     // assume that it was never committed. The server will track the client that
     // sent up the original commit and return this in a get updates response. We
     // need to check if we have an entry that didn't get its server id updated
-    // correctly. The server sends down a |originator_cache_guid| and an
-    // |original_client_item_id|. If we have a entry by that description, we
-    // should update the |sync_id| in |bookmark_tracker_|. The rest of code will
-    // handle this a conflict and adjust the model if needed.
+    // correctly. The server sends down |original_client_item_id| (regular case)
+    // or |client_provided_unique_tag| (experimental). If the tracker contains
+    // a matching entry, it should be treated as update.
     const SyncedBookmarkTracker::Entity* old_tracked_entity =
         bookmark_tracker_->GetEntityForSyncId(
             update_entity.originator_client_item_id);
+    if (!old_tracked_entity && !update_entity.client_tag_hash.value().empty()) {
+      // There's currently no way to perform a lookup by client tag hash. As an
+      // approximation, the bookmark node's GUID can be used, which is the same
+      // as the temporary sync ID assigned upon local creation (just like
+      // originator client ID). This doesn't work for remote deletions, which
+      // don't include a GUID in specifics, but the existing UMA data for
+      // DuplicateBookmarkEntityOnRemoteUpdateCondition::kServerIdTombstone
+      // indicates that users don't in practice run into this.
+      // TODO(crbug.com/1143246): Adopt proper lookups by client tag hash once
+      // the tracker supports this.
+      old_tracked_entity = bookmark_tracker_->GetEntityForSyncId(
+          remote_guid.AsLowercaseString());
+    }
     if (old_tracked_entity) {
       if (tracked_entity) {
         DCHECK_NE(tracked_entity, old_tracked_entity);
diff --git a/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc b/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc
index c02f124..a8932be 100644
--- a/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc
+++ b/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc
@@ -1004,6 +1004,8 @@
   bookmark_specifics->set_legacy_canonicalized_title(kTitle);
   bookmark_specifics->set_url(kUrl.spec());
   data.is_folder = false;
+  data.originator_client_item_id = bookmark_specifics->guid();
+
   syncer::UpdateResponseData response_data;
   response_data.entity = std::move(data);
   // Similar to what's done in the loopback_server.
@@ -1055,6 +1057,8 @@
   bookmark_specifics->set_icon_url(kIconUrl.spec());
   bookmark_specifics->set_favicon("PNG");
   data.is_folder = false;
+  data.originator_client_item_id = bookmark_specifics->guid();
+
   syncer::UpdateResponseData response_data;
   response_data.entity = std::move(data);
   // Similar to what's done in the loopback_server.
@@ -1093,6 +1097,8 @@
   bookmark_specifics->set_legacy_canonicalized_title(kTitle);
   bookmark_specifics->set_url(kUrl.spec());
   data.is_folder = false;
+  data.originator_client_item_id = bookmark_specifics->guid();
+
   syncer::UpdateResponseData response_data;
   response_data.entity = std::move(data);
   // Similar to what's done in the loopback_server.
@@ -1111,7 +1117,7 @@
 // server but the commit respone isn't received for some reason. Further updates
 // to that entity should update the sync id in the tracker.
 TEST_F(BookmarkRemoteUpdatesHandlerWithInitialMergeTest,
-       ShouldUpdateSyncIdWhenRecevingAnUpdateForNewlyCreatedLocalNode) {
+       ShouldUpdateSyncIdWhenRecevingUpdateForNewlyCreatedLocalNode) {
   const std::string kCacheGuid = "generated_id";
   const std::string kOriginatorClientItemId =
       base::GUID::GenerateRandomV4().AsLowercaseString();
@@ -1121,7 +1127,6 @@
                                      base::TimeDelta::FromSeconds(1));
 
   sync_pb::ModelTypeState model_type_state;
-  model_type_state.set_cache_guid(kCacheGuid);
   model_type_state.set_initial_sync_done(true);
 
   const sync_pb::UniquePosition unique_position;
@@ -1147,7 +1152,6 @@
   syncer::UpdateResponseDataList updates;
   syncer::EntityData data;
   data.id = kSyncId;
-  data.originator_cache_guid = kCacheGuid;
   data.originator_client_item_id = kOriginatorClientItemId;
   // Set the other required fields.
   data.unique_position = syncer::UniquePosition::InitialPosition(
@@ -1173,6 +1177,71 @@
   EXPECT_THAT(entity->bookmark_node(), Eq(&node));
 }
 
+// Same as above for bookmarks created with client tags.
+TEST_F(
+    BookmarkRemoteUpdatesHandlerWithInitialMergeTest,
+    ShouldUpdateSyncIdWhenRecevingUpdateForNewlyCreatedLocalNodeWithClientTag) {
+  base::test::ScopedFeatureList override_features;
+  override_features.InitAndEnableFeature(
+      switches::kSyncUseClientTagForBookmarkCommits);
+
+  const std::string kBookmarkGuid =
+      base::GUID::GenerateRandomV4().AsLowercaseString();
+  const std::string kSyncId = "server_id";
+  const int64_t kServerVersion = 1000;
+  const base::Time kModificationTime(base::Time::Now() -
+                                     base::TimeDelta::FromSeconds(1));
+
+  sync_pb::ModelTypeState model_type_state;
+  model_type_state.set_initial_sync_done(true);
+
+  const sync_pb::UniquePosition unique_position;
+  sync_pb::EntitySpecifics specifics;
+  sync_pb::BookmarkSpecifics* bookmark_specifics = specifics.mutable_bookmark();
+  bookmark_specifics->set_guid(kBookmarkGuid);
+  bookmark_specifics->set_legacy_canonicalized_title("Title");
+  bookmarks::BookmarkNode node(
+      /*id=*/1, base::GUID::ParseLowercase(kBookmarkGuid), GURL());
+  // Track a sync entity (similar to what happens after a local creation). The
+  // |originator_client_item_id| is used a temp sync id and mark the entity that
+  // it needs to be committed..
+  const SyncedBookmarkTracker::Entity* entity =
+      tracker()->Add(&node, /*sync_id=*/kBookmarkGuid, kServerVersion,
+                     kModificationTime, unique_position, specifics);
+  tracker()->IncrementSequenceNumber(entity);
+
+  ASSERT_THAT(tracker()->GetEntityForSyncId(kBookmarkGuid), Eq(entity));
+
+  // Now receive an update with the actual server id.
+  syncer::UpdateResponseDataList updates;
+  syncer::EntityData data;
+  data.id = kSyncId;
+  data.client_tag_hash =
+      syncer::ClientTagHash::FromUnhashed(syncer::BOOKMARKS, kBookmarkGuid);
+  // Set the other required fields.
+  data.unique_position = syncer::UniquePosition::InitialPosition(
+                             syncer::UniquePosition::RandomSuffix())
+                             .ToProto();
+  data.specifics = specifics;
+  data.specifics.mutable_bookmark()->set_guid(kBookmarkGuid);
+  data.is_folder = true;
+
+  syncer::UpdateResponseData response_data;
+  response_data.entity = std::move(data);
+  // Similar to what's done in the loopback_server.
+  response_data.response_version = 0;
+  updates.push_back(std::move(response_data));
+
+  updates_handler()->Process(updates,
+                             /*got_new_encryption_requirements=*/false);
+
+  // The sync id in the tracker should have been updated.
+  EXPECT_THAT(tracker()->GetEntityForSyncId(kBookmarkGuid), IsNull());
+  EXPECT_THAT(tracker()->GetEntityForSyncId(kSyncId), Eq(entity));
+  EXPECT_THAT(entity->metadata()->server_id(), Eq(kSyncId));
+  EXPECT_THAT(entity->bookmark_node(), Eq(&node));
+}
+
 TEST_F(BookmarkRemoteUpdatesHandlerWithInitialMergeTest,
        ShouldRecommitWhenEncryptionIsOutOfDate) {
   sync_pb::ModelTypeState model_type_state;
diff --git a/components/sync_bookmarks/bookmark_specifics_conversions.cc b/components/sync_bookmarks/bookmark_specifics_conversions.cc
index 1857e0c..17b96c1 100644
--- a/components/sync_bookmarks/bookmark_specifics_conversions.cc
+++ b/components/sync_bookmarks/bookmark_specifics_conversions.cc
@@ -394,14 +394,14 @@
 }
 
 bool HasExpectedBookmarkGuid(const sync_pb::BookmarkSpecifics& specifics,
+                             const syncer::ClientTagHash& client_tag_hash,
                              const std::string& originator_cache_guid,
                              const std::string& originator_client_item_id) {
   DCHECK(base::GUID::ParseLowercase(specifics.guid()).is_valid());
 
-  if (originator_client_item_id.empty()) {
-    // This could be a future bookmark with a client tag instead of an
-    // originator client item ID.
-    NOTIMPLEMENTED();
+  // If the client tag hash matches, that should already be good enough.
+  if (syncer::ClientTagHash::FromUnhashed(
+          syncer::BOOKMARKS, specifics.guid()) == client_tag_hash) {
     return true;
   }
 
diff --git a/components/sync_bookmarks/bookmark_specifics_conversions.h b/components/sync_bookmarks/bookmark_specifics_conversions.h
index 18ac8ef..c5a3d49 100644
--- a/components/sync_bookmarks/bookmark_specifics_conversions.h
+++ b/components/sync_bookmarks/bookmark_specifics_conversions.h
@@ -24,6 +24,7 @@
 }  // namespace sync_pb
 
 namespace syncer {
+class ClientTagHash;
 struct EntityData;
 }  // namespace syncer
 
@@ -89,9 +90,8 @@
 // Checks if bookmark specifics contain a GUID that matches the value that would
 // be inferred from other redundant fields. |specifics| must be valid as per
 // IsValidBookmarkSpecifics().
-// TODO(crbug.com/1032052): Replace this with an analogous function that
-// verifies that the bookmark's client tag hash matches the GUID.
 bool HasExpectedBookmarkGuid(const sync_pb::BookmarkSpecifics& specifics,
+                             const syncer::ClientTagHash& client_tag_hash,
                              const std::string& originator_cache_guid,
                              const std::string& originator_client_item_id);
 
diff --git a/components/sync_bookmarks/switches.cc b/components/sync_bookmarks/switches.cc
index 16812cc..8fb804f 100644
--- a/components/sync_bookmarks/switches.cc
+++ b/components/sync_bookmarks/switches.cc
@@ -19,4 +19,7 @@
 const base::Feature kSyncIgnoreChangesInTouchIcons{
     "SyncIgnoreChangesInTouchIcons", base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kSyncUseClientTagForBookmarkCommits{
+    "SyncUseClientTagForBookmarkCommits", base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace switches
diff --git a/components/sync_bookmarks/switches.h b/components/sync_bookmarks/switches.h
index 2f6b220c..37b0de4 100644
--- a/components/sync_bookmarks/switches.h
+++ b/components/sync_bookmarks/switches.h
@@ -17,6 +17,7 @@
 extern const base::Feature kSyncDeduplicateAllBookmarksWithSameGUID;
 // TODO(crbug.com/1075709): remove after launch.
 extern const base::Feature kSyncIgnoreChangesInTouchIcons;
+extern const base::Feature kSyncUseClientTagForBookmarkCommits;
 
 }  // namespace switches
 
diff --git a/components/sync_bookmarks/synced_bookmark_tracker.cc b/components/sync_bookmarks/synced_bookmark_tracker.cc
index 04d12fd..ec5aa83 100644
--- a/components/sync_bookmarks/synced_bookmark_tracker.cc
+++ b/components/sync_bookmarks/synced_bookmark_tracker.cc
@@ -222,6 +222,9 @@
   auto tracker = base::WrapUnique(new SyncedBookmarkTracker(
       std::move(model_type_state), /*bookmarks_full_title_reuploaded=*/false,
       /*last_sync_time=*/base::Time::Now()));
+  tracker->bookmark_client_tags_in_protocol_enabled_ =
+      base::FeatureList::IsEnabled(
+          switches::kSyncUseClientTagForBookmarkCommits);
   return tracker;
 }
 
@@ -236,23 +239,43 @@
     return nullptr;
   }
 
-  auto tracker =
-      CreateEmpty(std::move(*model_metadata.mutable_model_type_state()));
-
   // When the reupload feature is enabled and disabled again, there may occur
   // new entities which weren't reuploaded.
   const bool bookmarks_full_title_reuploaded =
       model_metadata.bookmarks_full_title_reuploaded() &&
       base::FeatureList::IsEnabled(switches::kSyncReuploadBookmarkFullTitles);
-  if (bookmarks_full_title_reuploaded) {
-    tracker->SetBookmarksFullTitleReuploaded();
-  }
 
   // If the field is not present, |last_sync_time| will be initialized with the
   // Unix epoch.
-  tracker->last_sync_time_ =
+  const base::Time last_sync_time =
       syncer::ProtoTimeToTime(model_metadata.last_sync_time());
 
+  // base::WrapUnique() used because the constructor is private.
+  auto tracker = base::WrapUnique(new SyncedBookmarkTracker(
+      model_metadata.model_type_state(), bookmarks_full_title_reuploaded,
+      last_sync_time));
+
+  // Read |bookmark_client_tags_in_protocol_enabled_| while honoring the
+  // corresponding feature toggle too.
+  if (model_metadata.bookmark_client_tags_in_protocol_enabled()) {
+    // If the feature used to be enabled, it can continue to do so as long as
+    // the feature toggle is still enabled. If it becomes disabled, the boolean
+    // transitions to false immediately (independently of in-flight local
+    // changes) to guarantee that there is an effective kill switch.
+    tracker->bookmark_client_tags_in_protocol_enabled_ =
+        base::FeatureList::IsEnabled(
+            switches::kSyncUseClientTagForBookmarkCommits);
+  } else {
+    // If the feature used to be disabled, transitioning to true requires *NOT*
+    // having pending local changes (in-flight creations, strictly speaking, but
+    // that's too complex to implement), to avoid creating duplicates on the
+    // server (the same bookmark with and without a client tag).
+    tracker->bookmark_client_tags_in_protocol_enabled_ =
+        !tracker->HasLocalChanges() &&
+        base::FeatureList::IsEnabled(
+            switches::kSyncUseClientTagForBookmarkCommits);
+  }
+
   const CorruptionReason corruption_reason =
       tracker->InitEntitiesFromModelAndMetadata(model,
                                                 std::move(model_metadata));
@@ -447,6 +470,9 @@
   model_metadata.set_bookmarks_full_title_reuploaded(
       bookmarks_full_title_reuploaded_);
   model_metadata.set_last_sync_time(syncer::TimeToProtoTime(last_sync_time_));
+  model_metadata.set_bookmark_client_tags_in_protocol_enabled(
+      bookmark_client_tags_in_protocol_enabled_);
+
   for (const std::pair<const std::string, std::unique_ptr<Entity>>& pair :
        sync_id_to_entities_map_) {
     DCHECK(pair.second) << " for ID " << pair.first;
@@ -758,6 +784,10 @@
   return true;
 }
 
+bool SyncedBookmarkTracker::bookmark_client_tags_in_protocol_enabled() const {
+  return bookmark_client_tags_in_protocol_enabled_;
+}
+
 void SyncedBookmarkTracker::TraverseAndAppend(
     const bookmarks::BookmarkNode* node,
     std::vector<const SyncedBookmarkTracker::Entity*>* ordered_entities) const {
diff --git a/components/sync_bookmarks/synced_bookmark_tracker.h b/components/sync_bookmarks/synced_bookmark_tracker.h
index 4217ec7..7310a303 100644
--- a/components/sync_bookmarks/synced_bookmark_tracker.h
+++ b/components/sync_bookmarks/synced_bookmark_tracker.h
@@ -301,6 +301,10 @@
   // reuploaded.
   bool ReuploadBookmarksOnLoadIfNeeded();
 
+  // Returns whether bookmark commits sent to the server (most importantly
+  // creations) should populate client tags.
+  bool bookmark_client_tags_in_protocol_enabled() const;
+
  private:
   // Enumeration of possible reasons why persisted metadata are considered
   // corrupted and don't match the bookmark model. Used in UMA metrics. Do not
@@ -384,6 +388,12 @@
   // required to populate the client tag (and be considered invalid otherwise).
   base::Time last_sync_time_;
 
+  // Represents whether bookmark commits sent to the server (most importantly
+  // creations) populate client tags.
+  // TODO(crbug.com/1032052): remove this code when the logic is enabled by
+  // default and enforced to true upon startup.
+  bool bookmark_client_tags_in_protocol_enabled_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(SyncedBookmarkTracker);
 };
 
diff --git a/components/viz/OWNERS b/components/viz/OWNERS
index 13c6cfe..52f6f18db 100644
--- a/components/viz/OWNERS
+++ b/components/viz/OWNERS
@@ -44,7 +44,7 @@
 # overlays
 ccameron@chromium.org
 dcastagna@chromium.org
-khushalsagar@chromium.org
+vasilyt@chromium.org
 magchen@chromium.org
 
 # scheduling / begin frames
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm
index 3fb1c59..b594031 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -1980,7 +1980,7 @@
     cocoa_role = NSAccessibilityGroupRole;
   } else if ((_owner->IsPlainTextField() &&
               _owner->HasState(ax::mojom::State::kMultiline)) ||
-             _owner->IsRichTextField()) {
+             (_owner->IsRichTextField() && !ui::IsComboBox(role))) {
     cocoa_role = NSAccessibilityTextAreaRole;
   } else if (role == ax::mojom::Role::kImage &&
              _owner->HasExplicitlyEmptyName()) {
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 1a49164..6a90ae07 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -1802,6 +1802,10 @@
   RunHtmlTest(FILE_PATH_LITERAL("img.html"));
 }
 
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityImgBroken) {
+  RunHtmlTest(FILE_PATH_LITERAL("img-broken.html"));
+}
+
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityImgEmptyAlt) {
   RunHtmlTest(FILE_PATH_LITERAL("img-empty-alt.html"));
 }
@@ -2098,6 +2102,11 @@
   RunHtmlTest(FILE_PATH_LITERAL("list.html"));
 }
 
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
+                       AccessibilityListTextRemoval) {
+  RunHtmlTest(FILE_PATH_LITERAL("list-text-removal.html"));
+}
+
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityListItemLevel) {
   RunHtmlTest(FILE_PATH_LITERAL("list-item-level.html"));
 }
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 0d0d4d2..115f5e7 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -171,7 +171,6 @@
     command_line->AppendSwitchASCII(
         switches::kAutoplayPolicy,
         switches::autoplay::kNoUserGestureRequiredPolicy);
-    command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures, "WakeLock");
     ContentBrowserTest::SetUpCommandLine(command_line);
   }
 
diff --git a/content/browser/renderer_host/OWNERS b/content/browser/renderer_host/OWNERS
index 7395c24..c232bf6 100644
--- a/content/browser/renderer_host/OWNERS
+++ b/content/browser/renderer_host/OWNERS
@@ -22,7 +22,7 @@
 per-file *mac*=ccameron@chromium.org
 
 # Android platform code.
-per-file *android*=khushalsagar@chromium.org
+per-file *android*=boliu@chromium.org
 
 # WebSQL.
 per-file web_database_*=jsbell@chromium.org
diff --git a/content/browser/renderer_host/navigation_controller_impl_unittest.cc b/content/browser/renderer_host/navigation_controller_impl_unittest.cc
index 860eb907..21e9957 100644
--- a/content/browser/renderer_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/renderer_host/navigation_controller_impl_unittest.cc
@@ -3443,8 +3443,8 @@
   EXPECT_EQ(url2b, other_controller.GetPendingEntry()->GetURL());
 
   // Let the pending entry commit.
-  other_contents->TestDidNavigate(other_contents->GetMainFrame(), false, url2b,
-                                  ui::PAGE_TRANSITION_LINK);
+  other_contents->GetMainFrame()->SendNavigateWithTransition(
+      0, false, url2b, ui::PAGE_TRANSITION_LINK);
 }
 
 // Test CopyStateFromAndPrune with 2 urls, a back navigation pending in the
diff --git a/content/browser/renderer_host/pepper/pepper_renderer_connection.cc b/content/browser/renderer_host/pepper/pepper_renderer_connection.cc
index 75930be..7bba16a3 100644
--- a/content/browser/renderer_host/pepper/pepper_renderer_connection.cc
+++ b/content/browser/renderer_host/pepper/pepper_renderer_connection.cc
@@ -11,15 +11,20 @@
 #include "base/bind.h"
 #include "base/memory/ref_counted.h"
 #include "base/stl_util.h"
+#include "content/browser/bad_message.h"
 #include "content/browser/browser_child_process_host_impl.h"
+#include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/plugin_service_impl.h"
 #include "content/browser/ppapi_plugin_process_host.h"
 #include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h"
 #include "content/browser/renderer_host/pepper/pepper_file_ref_host.h"
 #include "content/browser/renderer_host/pepper/pepper_file_system_browser_host.h"
 #include "content/common/frame_messages.h"
 #include "content/common/pepper_renderer_instance_data.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/common/content_client.h"
 #include "ipc/ipc_message_macros.h"
 #include "ppapi/host/resource_host.h"
@@ -94,11 +99,49 @@
 
 }  // namespace
 
-PepperRendererConnection::PepperRendererConnection(int render_process_id)
+class PepperRendererConnection::OpenChannelToPpapiPluginCallback
+    : public PpapiPluginProcessHost::PluginClient {
+ public:
+  OpenChannelToPpapiPluginCallback(PepperRendererConnection* filter,
+                                   OpenChannelToPepperPluginCallback callback)
+      : callback_(std::move(callback)), filter_(filter) {}
+
+  void GetPpapiChannelInfo(base::ProcessHandle* renderer_handle,
+                           int* renderer_id) override {
+    // base::kNullProcessHandle indicates that the channel will be used by the
+    // browser itself. Make sure we never output that value here.
+    CHECK_NE(base::kNullProcessHandle, filter_->PeerHandle());
+    *renderer_handle = filter_->PeerHandle();
+    *renderer_id = filter_->render_process_id_;
+  }
+
+  void OnPpapiChannelOpened(const IPC::ChannelHandle& channel_handle,
+                            base::ProcessId plugin_pid,
+                            int plugin_child_id) override {
+    std::move(callback_).Run(mojo::MakeScopedHandle(channel_handle.mojo_handle),
+                             plugin_pid, plugin_child_id);
+    delete this;
+  }
+
+  bool Incognito() override { return filter_->incognito_; }
+
+ private:
+  OpenChannelToPepperPluginCallback callback_;
+  scoped_refptr<PepperRendererConnection> filter_;
+};
+
+PepperRendererConnection::PepperRendererConnection(
+    int render_process_id,
+    PluginServiceImpl* plugin_service,
+    BrowserContext* browser_context,
+    StoragePartition* storage_partition)
     : BrowserMessageFilter(kPepperFilteredMessageClasses,
                            base::size(kPepperFilteredMessageClasses)),
       BrowserAssociatedInterface<mojom::PepperIOHost>(this),
-      render_process_id_(render_process_id) {
+      render_process_id_(render_process_id),
+      incognito_(browser_context->IsOffTheRecord()),
+      plugin_service_(plugin_service),
+      profile_data_directory_(storage_partition->GetPath()) {
   // Only give the renderer permission for stable APIs.
   in_process_host_.reset(new BrowserPpapiHostImpl(this,
                                                   ppapi::PpapiPermissions(),
@@ -245,4 +288,73 @@
   in_process_host_->DeleteInstance(instance);
 }
 
+void PepperRendererConnection::DidCreateOutOfProcessPepperInstance(
+    int32_t plugin_child_id,
+    int32_t pp_instance,
+    bool is_external,
+    int32_t render_frame_id,
+    const GURL& document_url,
+    const GURL& plugin_url,
+    bool is_privileged_context,
+    DidCreateOutOfProcessPepperInstanceCallback callback) {
+  // It's important that we supply the render process ID ourselves based on the
+  // channel the message arrived on. We use the
+  //   PP_Instance -> (process id, frame id)
+  // mapping to decide how to handle messages received from the (untrusted)
+  // plugin. An exploited renderer must not be able to insert fake mappings
+  // that may allow it access to other render processes.
+  PepperRendererInstanceData instance_data{render_process_id_, render_frame_id,
+                                           document_url, plugin_url,
+                                           is_privileged_context};
+  if (is_external) {
+    // We provide the BrowserPpapiHost to the embedder, so it's safe to cast.
+    BrowserPpapiHostImpl* host = static_cast<BrowserPpapiHostImpl*>(
+        GetContentClient()->browser()->GetExternalBrowserPpapiHost(
+            plugin_child_id));
+    if (host)
+      host->AddInstance(pp_instance, instance_data);
+  } else {
+    PpapiPluginProcessHost::DidCreateOutOfProcessInstance(
+        plugin_child_id, pp_instance, instance_data);
+  }
+  std::move(callback).Run();
+}
+
+void PepperRendererConnection::DidDeleteOutOfProcessPepperInstance(
+    int32_t plugin_child_id,
+    int32_t pp_instance,
+    bool is_external) {
+  if (is_external) {
+    // We provide the BrowserPpapiHost to the embedder, so it's safe to cast.
+    BrowserPpapiHostImpl* host = static_cast<BrowserPpapiHostImpl*>(
+        GetContentClient()->browser()->GetExternalBrowserPpapiHost(
+            plugin_child_id));
+    if (host)
+      host->DeleteInstance(pp_instance);
+  } else {
+    PpapiPluginProcessHost::DidDeleteOutOfProcessInstance(plugin_child_id,
+                                                          pp_instance);
+  }
+}
+
+void PepperRendererConnection::OpenChannelToPepperPlugin(
+    const url::Origin& embedder_origin,
+    const base::FilePath& path,
+    const base::Optional<url::Origin>& origin_lock,
+    OpenChannelToPepperPluginCallback callback) {
+  // Enforce that the sender of the IPC (i.e. |render_process_id_|) is actually
+  // able/allowed to host a frame with |embedder_origin|.
+  auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
+  if (!policy->CanAccessDataForOrigin(render_process_id_, embedder_origin)) {
+    bad_message::ReceivedBadMessage(
+        this, bad_message::RFMF_INVALID_PLUGIN_EMBEDDER_ORIGIN);
+    return;
+  }
+
+  plugin_service_->OpenChannelToPpapiPlugin(
+      render_process_id_, embedder_origin, path, profile_data_directory_,
+      origin_lock,
+      new OpenChannelToPpapiPluginCallback(this, std::move(callback)));
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/pepper/pepper_renderer_connection.h b/content/browser/renderer_host/pepper/pepper_renderer_connection.h
index b066c6fc..bc5c986 100644
--- a/content/browser/renderer_host/pepper/pepper_renderer_connection.h
+++ b/content/browser/renderer_host/pepper/pepper_renderer_connection.h
@@ -24,7 +24,10 @@
 
 namespace content {
 
+class BrowserContext;
 class BrowserPpapiHostImpl;
+class PluginServiceImpl;
+class StoragePartition;
 
 // This class represents a connection from the browser to the renderer for
 // sending/receiving pepper ResourceHost related messages. When the browser
@@ -34,7 +37,10 @@
     : public BrowserMessageFilter,
       public BrowserAssociatedInterface<mojom::PepperIOHost> {
  public:
-  explicit PepperRendererConnection(int render_process_id);
+  PepperRendererConnection(int render_process_id,
+                           PluginServiceImpl* plugin_service,
+                           BrowserContext* browser_context,
+                           StoragePartition* storage_partition);
 
   // BrowserMessageFilter overrides.
   bool OnMessageReceived(const IPC::Message& msg) override;
@@ -42,6 +48,7 @@
  private:
   ~PepperRendererConnection() override;
 
+  class OpenChannelToPpapiPluginCallback;
   // Returns the host for the child process for the given |child_process_id|.
   // If |child_process_id| is 0, returns the host owned by this
   // PepperRendererConnection, which serves as the host for in-process plugins.
@@ -60,8 +67,26 @@
                                   const GURL& document_url,
                                   const GURL& plugin_url) override;
   void DidDeleteInProcessInstance(int32_t instance) override;
+  void DidCreateOutOfProcessPepperInstance(
+      int32_t plugin_child_id,
+      int32_t pp_instance,
+      bool is_external,
+      int32_t render_frame_id,
+      const GURL& document_url,
+      const GURL& plugin_url,
+      bool is_priviledged_context,
+      DidCreateOutOfProcessPepperInstanceCallback callback) override;
+  void DidDeleteOutOfProcessPepperInstance(int32_t plugin_child_id,
+                                           int32_t pp_instance,
+                                           bool is_external) override;
+  void OpenChannelToPepperPlugin(
+      const url::Origin& embedder_origin,
+      const base::FilePath& path,
+      const base::Optional<url::Origin>& origin_lock,
+      OpenChannelToPepperPluginCallback callback) override;
 
-  int render_process_id_;
+  const int render_process_id_;
+  const bool incognito_;
 
   // We have a single BrowserPpapiHost per-renderer for all in-process plugins
   // running. This is just a work-around allowing new style resources to work
@@ -69,6 +94,9 @@
   // information (like the plugin name) won't be available.
   std::unique_ptr<BrowserPpapiHostImpl> in_process_host_;
 
+  PluginServiceImpl* const plugin_service_;
+  const base::FilePath profile_data_directory_;
+
   DISALLOW_COPY_AND_ASSIGN(PepperRendererConnection);
 };
 
diff --git a/content/browser/renderer_host/render_frame_message_filter.cc b/content/browser/renderer_host/render_frame_message_filter.cc
index 9cc3d07..17247f44 100644
--- a/content/browser/renderer_host/render_frame_message_filter.cc
+++ b/content/browser/renderer_host/render_frame_message_filter.cc
@@ -70,34 +70,6 @@
 
 }  // namespace
 
-class RenderFrameMessageFilter::OpenChannelToPpapiPluginCallback
-    : public RenderMessageCompletionCallback,
-      public PpapiPluginProcessHost::PluginClient {
- public:
-  OpenChannelToPpapiPluginCallback(RenderFrameMessageFilter* filter,
-                                   IPC::Message* reply_msg)
-      : RenderMessageCompletionCallback(filter, reply_msg) {}
-
-  void GetPpapiChannelInfo(base::ProcessHandle* renderer_handle,
-                           int* renderer_id) override {
-    // base::kNullProcessHandle indicates that the channel will be used by the
-    // browser itself. Make sure we never output that value here.
-    CHECK_NE(base::kNullProcessHandle, filter()->PeerHandle());
-    *renderer_handle = filter()->PeerHandle();
-    *renderer_id = filter()->render_process_id_;
-  }
-
-  void OnPpapiChannelOpened(const IPC::ChannelHandle& channel_handle,
-                            base::ProcessId plugin_pid,
-                            int plugin_child_id) override {
-    FrameHostMsg_OpenChannelToPepperPlugin::WriteReplyParams(
-        reply_msg(), channel_handle, plugin_pid, plugin_child_id);
-    SendReplyAndDeleteThis();
-  }
-
-  bool Incognito() override { return filter()->incognito_; }
-};
-
 RenderFrameMessageFilter::RenderFrameMessageFilter(
     int render_process_id,
     PluginServiceImpl* plugin_service,
@@ -105,8 +77,6 @@
     StoragePartition* storage_partition)
     : BrowserMessageFilter(FrameMsgStart),
       plugin_service_(plugin_service),
-      profile_data_directory_(storage_partition->GetPath()),
-      incognito_(browser_context->IsOffTheRecord()),
       render_process_id_(render_process_id) {}
 
 RenderFrameMessageFilter::~RenderFrameMessageFilter() {
@@ -118,12 +88,6 @@
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(RenderFrameMessageFilter, message)
     IPC_MESSAGE_HANDLER(FrameHostMsg_GetPluginInfo, OnGetPluginInfo)
-    IPC_MESSAGE_HANDLER_DELAY_REPLY(FrameHostMsg_OpenChannelToPepperPlugin,
-                                    OnOpenChannelToPepperPlugin)
-    IPC_MESSAGE_HANDLER(FrameHostMsg_DidCreateOutOfProcessPepperInstance,
-                        OnDidCreateOutOfProcessPepperInstance)
-    IPC_MESSAGE_HANDLER(FrameHostMsg_DidDeleteOutOfProcessPepperInstance,
-                        OnDidDeleteOutOfProcessPepperInstance)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
 
@@ -155,67 +119,4 @@
       allow_wildcard, nullptr, info, actual_mime_type);
 }
 
-void RenderFrameMessageFilter::OnOpenChannelToPepperPlugin(
-    const url::Origin& embedder_origin,
-    const base::FilePath& path,
-    const base::Optional<url::Origin>& origin_lock,
-    IPC::Message* reply_msg) {
-  // Enforce that the sender of the IPC (i.e. |render_process_id_|) is actually
-  // able/allowed to host a frame with |embedder_origin|.
-  auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
-  if (!policy->CanAccessDataForOrigin(render_process_id_, embedder_origin)) {
-    NOTREACHED() << embedder_origin;
-    bad_message::ReceivedBadMessage(
-        this, bad_message::RFMF_INVALID_PLUGIN_EMBEDDER_ORIGIN);
-    return;
-  }
-
-  plugin_service_->OpenChannelToPpapiPlugin(
-      render_process_id_, embedder_origin, path, profile_data_directory_,
-      origin_lock, new OpenChannelToPpapiPluginCallback(this, reply_msg));
-}
-
-void RenderFrameMessageFilter::OnDidCreateOutOfProcessPepperInstance(
-    int plugin_child_id,
-    int32_t pp_instance,
-    PepperRendererInstanceData instance_data,
-    bool is_external) {
-  // It's important that we supply the render process ID ourselves based on the
-  // channel the message arrived on. We use the
-  //   PP_Instance -> (process id, frame id)
-  // mapping to decide how to handle messages received from the (untrusted)
-  // plugin, so an exploited renderer must not be able to insert fake mappings
-  // that may allow it access to other render processes.
-  DCHECK_EQ(0, instance_data.render_process_id);
-  instance_data.render_process_id = render_process_id_;
-  if (is_external) {
-    // We provide the BrowserPpapiHost to the embedder, so it's safe to cast.
-    BrowserPpapiHostImpl* host = static_cast<BrowserPpapiHostImpl*>(
-        GetContentClient()->browser()->GetExternalBrowserPpapiHost(
-            plugin_child_id));
-    if (host)
-      host->AddInstance(pp_instance, instance_data);
-  } else {
-    PpapiPluginProcessHost::DidCreateOutOfProcessInstance(
-        plugin_child_id, pp_instance, instance_data);
-  }
-}
-
-void RenderFrameMessageFilter::OnDidDeleteOutOfProcessPepperInstance(
-    int plugin_child_id,
-    int32_t pp_instance,
-    bool is_external) {
-  if (is_external) {
-    // We provide the BrowserPpapiHost to the embedder, so it's safe to cast.
-    BrowserPpapiHostImpl* host = static_cast<BrowserPpapiHostImpl*>(
-        GetContentClient()->browser()->GetExternalBrowserPpapiHost(
-            plugin_child_id));
-    if (host)
-      host->DeleteInstance(pp_instance);
-  } else {
-    PpapiPluginProcessHost::DidDeleteOutOfProcessInstance(plugin_child_id,
-                                                          pp_instance);
-  }
-}
-
 }  // namespace content
diff --git a/content/browser/renderer_host/render_frame_message_filter.h b/content/browser/renderer_host/render_frame_message_filter.h
index 100c3936..74c9535 100644
--- a/content/browser/renderer_host/render_frame_message_filter.h
+++ b/content/browser/renderer_host/render_frame_message_filter.h
@@ -60,8 +60,6 @@
   friend class BrowserThread;
   friend class base::DeleteHelper<RenderFrameMessageFilter>;
 
-  class OpenChannelToPpapiPluginCallback;
-
   ~RenderFrameMessageFilter() override;
 
   void OnGetPluginInfo(int render_frame_id,
@@ -71,29 +69,8 @@
                        bool* found,
                        WebPluginInfo* info,
                        std::string* actual_mime_type);
-  void OnOpenChannelToPepperPlugin(
-      const url::Origin& embedder_origin,
-      const base::FilePath& path,
-      const base::Optional<url::Origin>& origin_lock,
-      IPC::Message* reply_msg);
-  void OnDidCreateOutOfProcessPepperInstance(
-      int plugin_child_id,
-      int32_t pp_instance,
-      PepperRendererInstanceData instance_data,
-      bool is_external);
-  void OnDidDeleteOutOfProcessPepperInstance(int plugin_child_id,
-                                             int32_t pp_instance,
-                                             bool is_external);
-  void OnOpenChannelToPpapiBroker(int routing_id, const base::FilePath& path);
 
   PluginServiceImpl* plugin_service_;
-  base::FilePath profile_data_directory_;
-
-  // Initialized to 0, accessed on FILE thread only.
-  base::TimeTicks last_plugin_refresh_time_;
-
-  // Whether this process is used for incognito contents.
-  const bool incognito_;
 
   const int render_process_id_;
 };
diff --git a/content/browser/renderer_host/render_process_host_browsertest.cc b/content/browser/renderer_host/render_process_host_browsertest.cc
index 8863f4e..44a6012f 100644
--- a/content/browser/renderer_host/render_process_host_browsertest.cc
+++ b/content/browser/renderer_host/render_process_host_browsertest.cc
@@ -172,8 +172,7 @@
 class RenderProcessHostTest : public ContentBrowserTest,
                               public RenderProcessHostObserver {
  public:
-  RenderProcessHostTest()
-      : process_exits_(0), host_destructions_(0), use_frame_priority_(false) {}
+  RenderProcessHostTest() = default;
 
   void SetUp() override {
     if (use_frame_priority_) {
@@ -201,11 +200,19 @@
     RenderProcessHostImpl* impl = static_cast<RenderProcessHostImpl*>(process);
     impl->visible_clients_ = visible_clients;
   }
+
  protected:
-  void set_process_exit_callback(base::OnceClosure callback) {
+  void SetProcessExitCallback(RenderProcessHost* rph,
+                              base::OnceClosure callback) {
+    Observe(rph);
     process_exit_callback_ = std::move(callback);
   }
 
+  void Observe(RenderProcessHost* rph) {
+    DCHECK(!observation_.IsObserving());
+    observation_.Observe(rph);
+  }
+
   // RenderProcessHostObserver:
   void RenderProcessExited(RenderProcessHost* host,
                            const ChildProcessTerminationInfo& info) override {
@@ -215,16 +222,19 @@
   }
   void RenderProcessHostDestroyed(RenderProcessHost* host) override {
     ++host_destructions_;
+    observation_.Reset();
   }
   void WaitUntilProcessExits(int target) {
     while (process_exits_ < target)
       base::RunLoop().RunUntilIdle();
   }
 
-  int process_exits_;
-  int host_destructions_;
+  base::ScopedObservation<RenderProcessHost, RenderProcessHostObserver>
+      observation_{this};
+  int process_exits_ = 0;
+  int host_destructions_ = 0;
   base::OnceClosure process_exit_callback_;
-  bool use_frame_priority_;
+  bool use_frame_priority_ = false;
   base::test::ScopedFeatureList feature_list_;
 };
 
@@ -363,8 +373,7 @@
   spare_renderer->BindReceiver(service.BindNewPipeAndPassReceiver());
 
   base::RunLoop run_loop;
-  set_process_exit_callback(run_loop.QuitClosure());
-  spare_renderer->AddObserver(this);  // For process_exit_callback.
+  SetProcessExitCallback(spare_renderer, run_loop.QuitClosure());
 
   // Should reply with a bad message and cause process death.
   {
@@ -752,8 +761,12 @@
 
   // Ensure that the ShellCloser observer is first, so that it will have first
   // dibs on the ProcessExited callback.
-  rph->AddObserver(&shell_closer);
-  rph->AddObserver(&observer_logger);
+  base::ScopedObservation<RenderProcessHost, RenderProcessHostObserver>
+      observation_1(&shell_closer);
+  base::ScopedObservation<RenderProcessHost, RenderProcessHostObserver>
+      observation_2(&observer_logger);
+  observation_1.Observe(rph);
+  observation_2.Observe(rph);
 
   // This will crash the render process, and start all the callbacks.
   // We can't use NavigateToURL here since it accesses the shell() after
@@ -768,13 +781,6 @@
             "ObserverLogger::RenderProcessExited "
             "ShellCloser::RenderProcessHostDestroyed "
             "ObserverLogger::RenderProcessHostDestroyed ", logging_string);
-
-  // If the test fails, and somehow the RPH is still alive somehow, at least
-  // deregister the observers so that the test fails and doesn't also crash.
-  if (!observer_logger.host_destroyed()) {
-    rph->RemoveObserver(&shell_closer);
-    rph->RemoveObserver(&observer_logger);
-  }
 }
 
 IN_PROC_BROWSER_TEST_F(RenderProcessHostTest, KillProcessOnBadMojoMessage) {
@@ -787,13 +793,12 @@
 
   host_destructions_ = 0;
   process_exits_ = 0;
-  rph->AddObserver(this);
 
   mojo::Remote<mojom::TestService> service;
   rph->BindReceiver(service.BindNewPipeAndPassReceiver());
 
   base::RunLoop run_loop;
-  set_process_exit_callback(run_loop.QuitClosure());
+  SetProcessExitCallback(rph, run_loop.QuitClosure());
 
   // Should reply with a bad message and cause process death.
   {
@@ -804,8 +809,6 @@
 
   EXPECT_EQ(1, process_exits_);
   EXPECT_EQ(0, host_destructions_);
-  if (!host_destructions_)
-    rph->RemoveObserver(this);
 }
 
 // Observes a WebContents and a specific frame within it, and waits until they
@@ -894,7 +897,6 @@
 
   host_destructions_ = 0;
   process_exits_ = 0;
-  rph->AddObserver(this);
 
   mojo::Remote<mojom::TestService> service;
   rph->BindReceiver(service.BindNewPipeAndPassReceiver());
@@ -906,7 +908,7 @@
     // must run after these notifications have been delivered.
     ScopedAllowRendererCrashes scoped_allow_renderer_crashes(rph);
     base::RunLoop run_loop;
-    set_process_exit_callback(media::BindToCurrentLoop(run_loop.QuitClosure()));
+    SetProcessExitCallback(rph, run_loop.QuitClosure());
     service->DoSomething(base::DoNothing());
     run_loop.Run();
   }
@@ -924,8 +926,6 @@
   EXPECT_EQ(0, rph->get_media_stream_count_for_testing());
   EXPECT_EQ(1, process_exits_);
   EXPECT_EQ(0, host_destructions_);
-  if (!host_destructions_)
-    rph->RemoveObserver(this);
 }
 
 // Test class instance to run specific setup steps for capture streams.
@@ -996,7 +996,6 @@
 
   host_destructions_ = 0;
   process_exits_ = 0;
-  rph->AddObserver(this);
 
   mojo::Remote<mojom::TestService> service;
   rph->BindReceiver(service.BindNewPipeAndPassReceiver());
@@ -1005,7 +1004,7 @@
     // Force a bad message event to occur which will terminate the renderer.
     ScopedAllowRendererCrashes scoped_allow_renderer_crashes(rph);
     base::RunLoop run_loop;
-    set_process_exit_callback(media::BindToCurrentLoop(run_loop.QuitClosure()));
+    SetProcessExitCallback(rph, run_loop.QuitClosure());
     service->DoSomething(base::DoNothing());
     run_loop.Run();
   }
@@ -1022,8 +1021,6 @@
   EXPECT_EQ(0, rph->get_media_stream_count_for_testing());
   EXPECT_EQ(1, process_exits_);
   EXPECT_EQ(0, host_destructions_);
-  if (!host_destructions_)
-    rph->RemoveObserver(this);
 }
 
 // Tests that media stream count increments when getUserMedia() is
@@ -1062,7 +1059,6 @@
 
   host_destructions_ = 0;
   process_exits_ = 0;
-  rph->AddObserver(this);
 
   mojo::Remote<mojom::TestService> service;
   rph->BindReceiver(service.BindNewPipeAndPassReceiver());
@@ -1071,7 +1067,7 @@
     // Force a bad message event to occur which will terminate the renderer.
     ScopedAllowRendererCrashes scoped_allow_renderer_crashes(rph);
     base::RunLoop run_loop;
-    set_process_exit_callback(media::BindToCurrentLoop(run_loop.QuitClosure()));
+    SetProcessExitCallback(rph, run_loop.QuitClosure());
     service->DoSomething(base::DoNothing());
     run_loop.Run();
   }
@@ -1088,8 +1084,6 @@
   EXPECT_EQ(0, rph->get_media_stream_count_for_testing());
   EXPECT_EQ(1, process_exits_);
   EXPECT_EQ(0, host_destructions_);
-  if (!host_destructions_)
-    rph->RemoveObserver(this);
 }
 
 IN_PROC_BROWSER_TEST_F(RenderProcessHostTest, KeepAliveRendererProcess) {
@@ -1119,7 +1113,7 @@
 
   host_destructions_ = 0;
   process_exits_ = 0;
-  rph->AddObserver(this);
+  Observe(rph);
   rfh->SetKeepAliveTimeoutForTesting(base::TimeDelta::FromSeconds(30));
 
   // Navigate to a site that will be in a different process.
@@ -1130,8 +1124,6 @@
   WaitUntilProcessExits(1);
 
   EXPECT_LT(base::TimeTicks::Now() - start, base::TimeDelta::FromSeconds(30));
-  if (!host_destructions_)
-    rph->RemoveObserver(this);
 }
 
 IN_PROC_BROWSER_TEST_F(RenderProcessHostTest,
@@ -1186,7 +1178,7 @@
 
   host_destructions_ = 0;
   process_exits_ = 0;
-  rph->AddObserver(this);
+  Observe(rph);
   rfh->SetKeepAliveTimeoutForTesting(base::TimeDelta::FromSeconds(1));
 
   base::TimeTicks start = base::TimeTicks::Now();
@@ -1195,8 +1187,6 @@
   WaitUntilProcessExits(1);
 
   EXPECT_GE(base::TimeTicks::Now() - start, base::TimeDelta::FromSeconds(1));
-  if (!host_destructions_)
-    rph->RemoveObserver(this);
 }
 
 // Test is flaky on Android builders: https://crbug.com/875179
@@ -1223,7 +1213,7 @@
 
   host_destructions_ = 0;
   process_exits_ = 0;
-  rph->AddObserver(this);
+  Observe(rph);
   rfh->SetKeepAliveTimeoutForTesting(base::TimeDelta::FromSeconds(1));
 
   base::TimeTicks start = base::TimeTicks::Now();
@@ -1232,8 +1222,6 @@
   WaitUntilProcessExits(1);
 
   EXPECT_GE(base::TimeTicks::Now() - start, base::TimeDelta::FromSeconds(1));
-  if (!host_destructions_)
-    rph->RemoveObserver(this);
 }
 
 IN_PROC_BROWSER_TEST_F(RenderProcessHostTest, ManyKeepaliveRequests) {
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 7e36c27d..f478602 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1930,7 +1930,9 @@
       GetID(), PluginServiceImpl::GetInstance(), GetBrowserContext(),
       storage_partition_impl_));
 
-  AddFilter(new PepperRendererConnection(GetID()));
+  AddFilter(new PepperRendererConnection(
+      GetID(), PluginServiceImpl::GetInstance(), GetBrowserContext(),
+      storage_partition_impl_));
 #endif
 
   p2p_socket_dispatcher_host_ =
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index cb236a1..4948053 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -1055,7 +1055,7 @@
   std::set<mojo::ReceiverId> dom_storage_receiver_ids_;
 
   // The observers watching our lifetime.
-  base::ObserverList<RenderProcessHostObserver>::Unchecked observers_;
+  base::ObserverList<RenderProcessHostObserver> observers_;
 
   // True if the process can be shut down suddenly.  If this is true, then we're
   // sure that all the RenderViews in the process can be shutdown suddenly.  If
diff --git a/content/browser/web_contents/web_contents_android.cc b/content/browser/web_contents/web_contents_android.cc
index 1370a92d..6680dab8 100644
--- a/content/browser/web_contents/web_contents_android.cc
+++ b/content/browser/web_contents/web_contents_android.cc
@@ -422,11 +422,11 @@
   return *rwhva->GetCachedBackgroundColor();
 }
 
-ScopedJavaLocalRef<jstring> WebContentsAndroid::GetLastCommittedURL(
+ScopedJavaLocalRef<jobject> WebContentsAndroid::GetLastCommittedURL(
     JNIEnv* env,
     const JavaParamRef<jobject>&) const {
-  return ConvertUTF8ToJavaString(env,
-                                 web_contents_->GetLastCommittedURL().spec());
+  return url::GURLAndroid::FromNativeGURL(env,
+                                          web_contents_->GetLastCommittedURL());
 }
 
 jboolean WebContentsAndroid::IsIncognito(JNIEnv* env,
diff --git a/content/browser/web_contents/web_contents_android.h b/content/browser/web_contents/web_contents_android.h
index fc05cd6..e6375b4 100644
--- a/content/browser/web_contents/web_contents_android.h
+++ b/content/browser/web_contents/web_contents_android.h
@@ -97,7 +97,7 @@
                          const base::android::JavaParamRef<jobject>& obj);
   jint GetBackgroundColor(JNIEnv* env,
                           const base::android::JavaParamRef<jobject>& obj);
-  base::android::ScopedJavaLocalRef<jstring> GetLastCommittedURL(
+  base::android::ScopedJavaLocalRef<jobject> GetLastCommittedURL(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>&) const;
   jboolean IsIncognito(JNIEnv* env,
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index f9b7b17..3d6de99 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -771,9 +771,8 @@
 
   EXPECT_TRUE(controller().NeedsReload());
   controller().LoadIfNecessary();
-  orig_rfh->PrepareForCommit();
-  contents()->TestDidNavigate(orig_rfh, false, native_url,
-                              ui::PAGE_TRANSITION_RELOAD);
+  orig_rfh->SendNavigateWithTransition(0, false, native_url,
+                                       ui::PAGE_TRANSITION_RELOAD);
   EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
   EXPECT_EQ(GURL(), contents()->GetSiteInstance()->GetSiteURL());
   EXPECT_FALSE(orig_instance->HasSite());
@@ -814,8 +813,8 @@
   EXPECT_TRUE(controller().NeedsReload());
   controller().LoadIfNecessary();
   orig_rfh->PrepareForCommit();
-  contents()->TestDidNavigate(orig_rfh, false, regular_url,
-                              ui::PAGE_TRANSITION_RELOAD);
+  orig_rfh->SendNavigateWithTransition(0, false, regular_url,
+                                       ui::PAGE_TRANSITION_RELOAD);
   EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
   EXPECT_TRUE(orig_instance->HasSite());
   EXPECT_EQ(AreDefaultSiteInstancesEnabled(),
@@ -1244,8 +1243,8 @@
   EXPECT_EQ(entry1, controller().GetLastCommittedEntry());
 
   // When the second back commits, it should be ignored.
-  contents()->TestDidNavigate(google_rfh, false, url2,
-                              ui::PAGE_TRANSITION_TYPED);
+  google_rfh->SendNavigateWithTransition(0, false, url2,
+                                         ui::PAGE_TRANSITION_TYPED);
   EXPECT_EQ(entry1, controller().GetLastCommittedEntry());
 
   // The newly created process for url1 should be locked to chrome://gpu.
diff --git a/content/browser/webrtc/webrtc_audio_browsertest.cc b/content/browser/webrtc/webrtc_audio_browsertest.cc
index 77f7efe..3ad8720 100644
--- a/content/browser/webrtc/webrtc_audio_browsertest.cc
+++ b/content/browser/webrtc/webrtc_audio_browsertest.cc
@@ -25,18 +25,11 @@
 
 namespace content {
 
-#if defined(OS_ANDROID)
-// Test fails on WebRTC Android FYI: https://crbug.com/1166107
-#define MAYBE_WebRtcAudioBrowserTest DISABLED_WebRtcAudioBrowserTest
-#else
-#define MAYBE_WebRtcAudioBrowserTest WebRtcAudioBrowserTest
-#endif
-
 // This class tests the scenario when permission to access mic or camera is
 // granted.
-class MAYBE_WebRtcAudioBrowserTest : public WebRtcContentBrowserTestBase {
+class WebRtcAudioBrowserTest : public WebRtcContentBrowserTestBase {
  public:
-  ~MAYBE_WebRtcAudioBrowserTest() override {}
+  ~WebRtcAudioBrowserTest() override {}
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     WebRtcContentBrowserTestBase::SetUpCommandLine(command_line);
@@ -125,7 +118,7 @@
 
 #endif  // defined(OS_MAC)
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebRtcAudioBrowserTest,
                        MAYBE_CanMakeVideoCallAndThenRenegotiateToAudio) {
   std::string constraints =
       BuildConstraints(kAudioConstraints, kVideoConstraints);
@@ -135,7 +128,7 @@
                                        audio_only_constraints + ");");
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebRtcAudioBrowserTest,
                        MAYBE_EstablishAudioVideoCallAndEnsureAudioIsPlaying) {
   std::string constraints =
       BuildConstraints(kAudioConstraints, kVideoConstraints);
@@ -143,7 +136,7 @@
                                        constraints + ");");
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebRtcAudioBrowserTest,
                        MAYBE_EstablishAudioOnlyCallAndEnsureAudioIsPlaying) {
   std::string constraints =
       BuildConstraints(kAudioConstraints, kVideoConstraints);
@@ -151,7 +144,7 @@
                                        constraints + ");");
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebRtcAudioBrowserTest,
                        MAYBE_EstablishIsac16KCallAndEnsureAudioIsPlaying) {
   std::string constraints =
       BuildConstraints(kAudioConstraints, kVideoConstraints);
@@ -159,7 +152,7 @@
       "callWithIsac16KAndEnsureAudioIsPlaying(" + constraints + ");");
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebRtcAudioBrowserTest,
                        EstablishAudioVideoCallAndVerifyRemoteMutingWorks) {
   std::string constraints =
       BuildConstraints(kAudioConstraints, kVideoConstraints);
@@ -167,7 +160,7 @@
       "callAndEnsureRemoteAudioTrackMutingWorks(" + constraints + ");");
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebRtcAudioBrowserTest,
                        EstablishAudioVideoCallAndVerifyLocalMutingWorks) {
   std::string constraints =
       BuildConstraints(kAudioConstraints, kVideoConstraints);
@@ -175,7 +168,7 @@
       "callAndEnsureLocalAudioTrackMutingWorks(" + constraints + ");");
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebRtcAudioBrowserTest,
                        MAYBE_EnsureLocalVideoMuteDoesntMuteAudio) {
   std::string constraints =
       BuildConstraints(kAudioConstraints, kVideoConstraints);
@@ -183,7 +176,7 @@
       "callAndEnsureLocalVideoMutingDoesntMuteAudio(" + constraints + ");");
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebRtcAudioBrowserTest,
                        MAYBE_EnsureRemoteVideoMuteDoesntMuteAudio) {
   std::string constraints =
       BuildConstraints(kAudioConstraints, kVideoConstraints);
@@ -191,7 +184,7 @@
       "callAndEnsureRemoteVideoMutingDoesntMuteAudio(" + constraints + ");");
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebRtcAudioBrowserTest,
                        MAYBE_EstablishAudioVideoCallAndVerifyUnmutingWorks) {
   std::string constraints =
       BuildConstraints(kAudioConstraints, kVideoConstraints);
@@ -202,7 +195,7 @@
 // TODO(crbug.com/988432): This test is a temporary replacement for:
 // external/wpt/webrtc/RTCRtpReceiver-getSynchronizationSources.https.html
 IN_PROC_BROWSER_TEST_F(
-    MAYBE_WebRtcAudioBrowserTest,
+    WebRtcAudioBrowserTest,
     MAYBE_EstablishAudioOnlyCallAndVerifyGetSynchronizationSourcesWorks) {
   MakeAudioDetectingPeerConnectionCall(
       "testEstablishAudioOnlyCallAndVerifyGetSynchronizationSourcesWorks();");
diff --git a/content/browser/webrtc/webrtc_browsertest.cc b/content/browser/webrtc/webrtc_browsertest.cc
index 1b9f385..a182211 100644
--- a/content/browser/webrtc/webrtc_browsertest.cc
+++ b/content/browser/webrtc/webrtc_browsertest.cc
@@ -23,11 +23,8 @@
 
 namespace content {
 
-#if defined(OS_ANDROID)
-// Renderer crashes under Android ASAN (ADDRESS_SANITIZER):
-// https://crbug.com/408496.
-// Test fails on WebRTC Android FYI (even without ASAN):
-// https://crbug.com/1166107
+#if defined(OS_ANDROID) && defined(ADDRESS_SANITIZER)
+// Renderer crashes under Android ASAN: https://crbug.com/408496.
 #define MAYBE_WebRtcBrowserTest DISABLED_WebRtcBrowserTest
 #else
 #define MAYBE_WebRtcBrowserTest WebRtcBrowserTest
diff --git a/content/browser/webrtc/webrtc_data_browsertest.cc b/content/browser/webrtc/webrtc_data_browsertest.cc
index e35c7638..849914c 100644
--- a/content/browser/webrtc/webrtc_data_browsertest.cc
+++ b/content/browser/webrtc/webrtc_data_browsertest.cc
@@ -20,11 +20,8 @@
 
 namespace content {
 
-#if defined(OS_ANDROID)
-// Renderer crashes under Android ASAN (ADDRESS_SANITIZER):
-// https://crbug.com/408496.
-// Test fails on WebRTC Android FYI (even without ASAN):
-// https://crbug.com/1166107
+#if defined(OS_ANDROID) && defined(ADDRESS_SANITIZER)
+// Renderer crashes under Android ASAN: https://crbug.com/408496.
 #define MAYBE_WebRtcDataBrowserTest DISABLED_WebRtcDataBrowserTest
 #else
 #define MAYBE_WebRtcDataBrowserTest WebRtcDataBrowserTest
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 4bb0498..04eeff9 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -218,16 +218,6 @@
   IPC_STRUCT_TRAITS_MEMBER(source)
 IPC_STRUCT_TRAITS_END()
 
-#if BUILDFLAG(ENABLE_PLUGINS)
-IPC_STRUCT_TRAITS_BEGIN(content::PepperRendererInstanceData)
-  IPC_STRUCT_TRAITS_MEMBER(render_process_id)
-  IPC_STRUCT_TRAITS_MEMBER(render_frame_id)
-  IPC_STRUCT_TRAITS_MEMBER(document_url)
-  IPC_STRUCT_TRAITS_MEMBER(plugin_url)
-  IPC_STRUCT_TRAITS_MEMBER(is_potentially_secure_plugin_context)
-IPC_STRUCT_TRAITS_END()
-#endif
-
 // -----------------------------------------------------------------------------
 // Messages sent from the browser to the renderer.
 
@@ -258,59 +248,6 @@
                             content::WebPluginInfo /* plugin info */,
                             std::string /* actual_mime_type */)
 
-// A renderer sends this to the browser process when it wants to create a ppapi
-// plugin.  The browser will create the plugin process if necessary, and will
-// return a handle to the channel on success.
-//
-// The plugin_child_id is the ChildProcessHost ID assigned in the browser
-// process. This ID is valid only in the context of the browser process and is
-// used to identify the proper process when the renderer notifies it that the
-// plugin is hung.
-//
-// |embedder_origin| provides the origin of the frame that embeds the plugin
-// (i.e. the origin of the document that contains the <embed> html tag).
-// |embedder_origin| needs to be included in the message payload, because the
-// message is received and handled on the IO thread in the browser process
-// (where it is not possible to consult
-// RenderFrameHostImpl::GetLastCommittedOrigin).
-//
-// On error an empty string and null handles are returned.
-IPC_SYNC_MESSAGE_CONTROL3_3(FrameHostMsg_OpenChannelToPepperPlugin,
-                            url::Origin /* embedder_origin */,
-                            base::FilePath /* path */,
-                            base::Optional<url::Origin>, /* origin_lock */
-                            IPC::ChannelHandle /* handle to channel */,
-                            base::ProcessId /* plugin_pid */,
-                            int /* plugin_child_id */)
-
-// Notification that a plugin has created a new plugin instance. The parameters
-// indicate:
-//  - The plugin process ID that we're creating the instance for.
-//  - The instance ID of the instance being created.
-//  - A PepperRendererInstanceData struct which contains properties from the
-//    renderer which are associated with the plugin instance. This includes the
-//    routing ID of the associated RenderFrame and the URL of plugin.
-//  - Whether the plugin we're creating an instance for is external or internal.
-//
-// This message must be sync even though it returns no parameters to avoid
-// a race condition with the plugin process. The plugin process sends messages
-// to the browser that assume the browser knows about the instance. We need to
-// make sure that the browser actually knows about the instance before we tell
-// the plugin to run.
-IPC_SYNC_MESSAGE_CONTROL4_0(
-    FrameHostMsg_DidCreateOutOfProcessPepperInstance,
-    int /* plugin_child_id */,
-    int32_t /* pp_instance */,
-    content::PepperRendererInstanceData /* creation_data */,
-    bool /* is_external */)
-
-// Notification that a plugin has destroyed an instance. This is the opposite of
-// the "DidCreate" message above.
-IPC_MESSAGE_CONTROL3(FrameHostMsg_DidDeleteOutOfProcessPepperInstance,
-                     int /* plugin_child_id */,
-                     int32_t /* pp_instance */,
-                     bool /* is_external */)
-
 #endif  // BUILDFLAG(ENABLE_PLUGINS)
 
 // Used to tell the parent that the user right clicked on an area of the
diff --git a/content/common/pepper_plugin.mojom b/content/common/pepper_plugin.mojom
index 2a13a4e..8627c0a 100644
--- a/content/common/pepper_plugin.mojom
+++ b/content/common/pepper_plugin.mojom
@@ -6,6 +6,7 @@
 
 import "mojo/public/mojom/base/file_path.mojom";
 import "mojo/public/mojom/base/process_id.mojom";
+import "url/mojom/origin.mojom";
 import "url/mojom/url.mojom";
 
 // Generic Pepper messages. Implemented by the browser.
@@ -32,6 +33,58 @@
 
   // Notification that an in-process instance has been destroyed.
   DidDeleteInProcessInstance(int32 instance_id);
+
+  // Notification that a plugin has created a new plugin instance. The
+  // parameters indicate:
+  //  - The plugin process ID that we're creating the instance for.
+  //  - The instance ID of the instance being created.
+  //  - A PepperRendererInstanceData struct which contains properties from the
+  //    renderer which are associated with the plugin instance. This includes
+  //    the routing ID of the associated RenderFrame and the URL of plugin.
+  //  - Whether the plugin we're creating an instance for is external or
+  //    internal.
+  //
+  // This message must be sync even though it returns no parameters to avoid
+  // a race condition with the plugin process. The plugin process sends messages
+  // to the browser that assume the browser knows about the instance. We need to
+  // make sure that the browser actually knows about the instance before we tell
+  // the plugin to run.
+  [Sync] DidCreateOutOfProcessPepperInstance(int32 plugin_child_id,
+                                             int32 pp_instance,
+                                             bool is_external,
+                                             int32 frame_routing_id,
+                                             url.mojom.Url document_url,
+                                             url.mojom.Url plugin_url,
+                                             bool is_privileged_context) => ();
+
+  // Notification that a plugin has destroyed an instance.
+  DidDeleteOutOfProcessPepperInstance(int32 plugin_child_id,
+                                      int32 pp_instance,
+                                      bool is_external);
+
+  // A renderer sends this to the browser process when it wants to create a
+  // ppapi plugin.  The browser will create the plugin process if necessary,
+  // and will return a handle to the channel on success.
+  //
+  // The plugin_child_id is the ChildProcessHost ID assigned in the browser
+  // process. This ID is valid only in the context of the browser process and is
+  // used to identify the proper process when the renderer notifies it that the
+  // plugin is hung.
+  //
+  // |embedder_origin| provides the origin of the frame that embeds the plugin
+  // (i.e. the origin of the document that contains the <embed> html tag).
+  // |embedder_origin| needs to be included in the message payload, because the
+  // message is received and handled on the IO thread in the browser process
+  // (where it is not possible to consult
+  // RenderFrameHostImpl::GetLastCommittedOrigin).
+  //
+  // On error null handles are returned.
+  [Sync] OpenChannelToPepperPlugin(url.mojom.Origin embedder_origin,
+                                   mojo_base.mojom.FilePath path,
+                                   url.mojom.Origin? origin_lock) =>
+                                   (handle<message_pipe>? handle_to_channel,
+                                    mojo_base.mojom.ProcessId plugin_pid,
+                                    int32 plugin_child_id);
 };
 
 // This interface is used on the renderer IO thread and is received on the
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
index 8bf6a35..6a12a878 100644
--- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
@@ -626,7 +626,7 @@
     }
 
     @Override
-    public String getLastCommittedUrl() {
+    public GURL getLastCommittedUrl() {
         checkNotDestroyed();
         return WebContentsImplJni.get().getLastCommittedURL(
                 mNativeWebContentsAndroid, WebContentsImpl.this);
@@ -1110,7 +1110,7 @@
         void selectWordAroundCaret(long nativeWebContentsAndroid, WebContentsImpl caller);
         void adjustSelectionByCharacterOffset(long nativeWebContentsAndroid, WebContentsImpl caller,
                 int startAdjust, int endAdjust, boolean showSelectionMenu);
-        String getLastCommittedURL(long nativeWebContentsAndroid, WebContentsImpl caller);
+        GURL getLastCommittedURL(long nativeWebContentsAndroid, WebContentsImpl caller);
         boolean isIncognito(long nativeWebContentsAndroid, WebContentsImpl caller);
         void resumeLoadingCreatedWebContents(long nativeWebContentsAndroid, WebContentsImpl caller);
         void evaluateJavaScript(long nativeWebContentsAndroid, WebContentsImpl caller,
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java b/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java
index b8484cc..cdd90bb5 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java
@@ -292,7 +292,7 @@
      *
      * @return The last committed URL.
      */
-    String getLastCommittedUrl();
+    GURL getLastCommittedUrl();
 
     /**
      * Get the InCognito state of WebContents.
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
index 7e29bee8..480c47a5 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
@@ -690,7 +690,7 @@
 
     private void reloadPage() throws Exception {
         // Reload the page, then focus will be lost and keyboard should be hidden.
-        mRule.fullyLoadUrl(mRule.getWebContents().getLastCommittedUrl());
+        mRule.fullyLoadUrl(mRule.getWebContents().getLastCommittedUrl().getSpec());
     }
 
     @Test
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index 332c015..a8b4cc9 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -286,6 +286,7 @@
     "render_process_host.h",
     "render_process_host_creation_observer.h",
     "render_process_host_factory.h",
+    "render_process_host_observer.cc",
     "render_process_host_observer.h",
     "render_view_host.h",
     "render_widget_host.h",
diff --git a/content/public/browser/render_process_host_observer.cc b/content/public/browser/render_process_host_observer.cc
new file mode 100644
index 0000000..44606995
--- /dev/null
+++ b/content/public/browser/render_process_host_observer.cc
@@ -0,0 +1,16 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/public/browser/render_process_host_observer.h"
+#include "base/check.h"
+
+namespace content {
+
+RenderProcessHostObserver::~RenderProcessHostObserver() {
+  // TODO(https://crbug.com/1153961): Instrumentation. When fixed, decide if
+  // this CHECK should be removed or turned into a DCHECK.
+  CHECK(!IsInObserverList());
+}
+
+}  // namespace content
diff --git a/content/public/browser/render_process_host_observer.h b/content/public/browser/render_process_host_observer.h
index 17e9ef2..c2400b5 100644
--- a/content/public/browser/render_process_host_observer.h
+++ b/content/public/browser/render_process_host_observer.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_PUBLIC_BROWSER_RENDER_PROCESS_HOST_OBSERVER_H_
 #define CONTENT_PUBLIC_BROWSER_RENDER_PROCESS_HOST_OBSERVER_H_
 
+#include "base/observer_list_types.h"
 #include "base/process/kill.h"
 #include "base/process/process_handle.h"
 #include "content/common/content_export.h"
@@ -18,7 +19,7 @@
 // in RenderProcessHost lifecycle events. Note that this does not allow
 // observing the creation of a RenderProcessHost. There is a separate observer
 // for that: RenderProcessHostCreationObserver.
-class CONTENT_EXPORT RenderProcessHostObserver {
+class CONTENT_EXPORT RenderProcessHostObserver : public base::CheckedObserver {
  public:
   // This method is invoked when the process was launched and the channel was
   // connected. This is the earliest time it is safe to call Shutdown on the
@@ -48,7 +49,7 @@
   virtual void RenderProcessHostDestroyed(RenderProcessHost* host) {}
 
  protected:
-  virtual ~RenderProcessHostObserver() {}
+  ~RenderProcessHostObserver() override;
 };
 
 }  // namespace content
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/mock/MockWebContents.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/mock/MockWebContents.java
index 10eecca..a0905f2 100644
--- a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/mock/MockWebContents.java
+++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/mock/MockWebContents.java
@@ -184,7 +184,7 @@
             int startAdjust, int endAdjust, boolean showSelectionMenu) {}
 
     @Override
-    public String getLastCommittedUrl() {
+    public GURL getLastCommittedUrl() {
         return null;
     }
 
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/HistoryUtils.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/HistoryUtils.java
index 42ca1f82..8fe4da0 100644
--- a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/HistoryUtils.java
+++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/HistoryUtils.java
@@ -109,7 +109,7 @@
                 instrumentation, new Callable<String>() {
                     @Override
                     public String call() {
-                        return webContents.getLastCommittedUrl();
+                        return webContents.getLastCommittedUrl().getSpec();
                     }
                 });
     }
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index b6b9a277..c4692d2 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -2299,33 +2299,19 @@
 RenderProcessHostWatcher::RenderProcessHostWatcher(
     RenderProcessHost* render_process_host,
     WatchType type)
-    : render_process_host_(render_process_host),
-      type_(type),
+    : type_(type),
       did_exit_normally_(true),
       allow_renderer_crashes_(
           std::make_unique<ScopedAllowRendererCrashes>(render_process_host)),
       quit_closure_(run_loop_.QuitClosure()) {
-  render_process_host_->AddObserver(this);
+  observation_.Observe(render_process_host);
 }
 
 RenderProcessHostWatcher::RenderProcessHostWatcher(WebContents* web_contents,
                                                    WatchType type)
     : RenderProcessHostWatcher(web_contents->GetMainFrame()->GetProcess(),
                                type) {}
-
-RenderProcessHostWatcher::~RenderProcessHostWatcher() {
-  ClearProcessHost();
-}
-
-void RenderProcessHostWatcher::ClearProcessHost() {
-  // Although we would like to make it so that from here on, renderer crashes
-  // cause test failures, resetting ScopedAllowRendererCrashes inside the RPH
-  // observers is too soon. The current crash is notified to the testing
-  // framework *after* the observers run and we need this one to be ignored.
-  if (render_process_host_)
-    render_process_host_->RemoveObserver(this);
-  render_process_host_ = nullptr;
-}
+RenderProcessHostWatcher::~RenderProcessHostWatcher() = default;
 
 void RenderProcessHostWatcher::Wait() {
   run_loop_.Run();
@@ -2334,12 +2320,12 @@
       << "RenderProcessHostWatcher::Wait() may only be called once";
   allow_renderer_crashes_.reset();
   // Call this here just in case something else quits the RunLoop.
-  ClearProcessHost();
+  observation_.Reset();
 }
 
 void RenderProcessHostWatcher::QuitRunLoop() {
   std::move(quit_closure_).Run();
-  ClearProcessHost();
+  observation_.Reset();
 }
 
 void RenderProcessHostWatcher::RenderProcessReady(RenderProcessHost* host) {
@@ -2358,7 +2344,6 @@
 
 void RenderProcessHostWatcher::RenderProcessHostDestroyed(
     RenderProcessHost* host) {
-  render_process_host_ = nullptr;
   if (type_ == WATCH_FOR_HOST_DESTRUCTION)
     QuitRunLoop();
 }
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index 4baf625..a338f88b 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -22,6 +22,7 @@
 #include "base/optional.h"
 #include "base/process/process.h"
 #include "base/run_loop.h"
+#include "base/scoped_observation.h"
 #include "base/strings/string16.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "build/build_config.h"
@@ -31,6 +32,7 @@
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/render_frame_metadata_provider.h"
+#include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_process_host_observer.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/web_contents_delegate.h"
@@ -1096,8 +1098,6 @@
   bool did_exit_normally() { return did_exit_normally_; }
 
  private:
-  // Stop observing and drop the reference to the RenderProcessHost.
-  void ClearProcessHost();
   // Quit the run loop and clean up.
   void QuitRunLoop();
 
@@ -1107,7 +1107,8 @@
                            const ChildProcessTerminationInfo& info) override;
   void RenderProcessHostDestroyed(RenderProcessHost* host) override;
 
-  RenderProcessHost* render_process_host_;
+  base::ScopedObservation<RenderProcessHost, RenderProcessHostObserver>
+      observation_{this};
   WatchType type_;
   bool did_exit_normally_;
 
diff --git a/content/public/test/mock_render_process_host.h b/content/public/test/mock_render_process_host.h
index 4630d2a..b0d4e74 100644
--- a/content/public/test/mock_render_process_host.h
+++ b/content/public/test/mock_render_process_host.h
@@ -260,7 +260,7 @@
   int id_;
   bool has_connection_;
   BrowserContext* browser_context_;
-  base::ObserverList<RenderProcessHostObserver>::Unchecked observers_;
+  base::ObserverList<RenderProcessHostObserver> observers_;
 
   base::flat_set<PriorityClient*> priority_clients_;
   int prev_routing_id_;
diff --git a/content/public/test/web_contents_tester.h b/content/public/test/web_contents_tester.h
index b886b41..e1c890bf 100644
--- a/content/public/test/web_contents_tester.h
+++ b/content/public/test/web_contents_tester.h
@@ -26,7 +26,6 @@
 namespace content {
 
 class BrowserContext;
-class RenderFrameHost;
 
 // This interface allows embedders of content/ to write tests that depend on a
 // test version of WebContents.  This interface can be retrieved from any
@@ -93,17 +92,6 @@
   // Sets the loading state to the given value.
   virtual void TestSetIsLoading(bool value) = 0;
 
-  // Simulates a navigation with the given information.
-  //
-  // Guidance for calling these:
-  // - did_create_new_entry should be true if simulating a navigation that
-  //   created a new navigation entry; false for history navigations, reloads,
-  //   and other navigations that don't affect the history list.
-  virtual void TestDidNavigate(RenderFrameHost* render_frame_host,
-                               bool did_create_new_entry,
-                               const GURL& url,
-                               ui::PageTransition transition) = 0;
-
   // Simulate this WebContents' main frame having an opener that points to the
   // main frame of |opener|.
   virtual void SetOpener(WebContents* opener) = 0;
diff --git a/content/renderer/pepper/host_dispatcher_wrapper.cc b/content/renderer/pepper/host_dispatcher_wrapper.cc
index df6b1e85..4c48a5b 100644
--- a/content/renderer/pepper/host_dispatcher_wrapper.cc
+++ b/content/renderer/pepper/host_dispatcher_wrapper.cc
@@ -6,6 +6,7 @@
 
 #include "build/build_config.h"
 #include "content/common/frame_messages.h"
+#include "content/renderer/pepper/pepper_browser_connection.h"
 #include "content/renderer/pepper/pepper_hung_plugin_filter.h"
 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
 #include "content/renderer/pepper/pepper_proxy_channel_delegate_impl.h"
@@ -87,13 +88,11 @@
     bool is_privileged_context =
         plugin_instance->GetContainer()->GetDocument().IsSecureContext() &&
         network::IsUrlPotentiallyTrustworthy(plugin_instance->GetPluginURL());
-    render_frame->Send(new FrameHostMsg_DidCreateOutOfProcessPepperInstance(
-        plugin_child_id_, instance,
-        PepperRendererInstanceData(
-            0,  // The render process id will be supplied in the browser.
+    PepperBrowserConnection::Get(render_frame)
+        ->DidCreateOutOfProcessPepperInstance(
+            plugin_child_id_, instance, is_external_,
             render_frame->GetRoutingID(), host->GetDocumentURL(instance),
-            plugin_instance->GetPluginURL(), is_privileged_context),
-        is_external_));
+            plugin_instance->GetPluginURL(), is_privileged_context);
   }
 }
 
@@ -106,8 +105,9 @@
   if (host) {
     RenderFrame* render_frame = host->GetRenderFrameForInstance(instance);
     if (render_frame) {
-      render_frame->Send(new FrameHostMsg_DidDeleteOutOfProcessPepperInstance(
-          plugin_child_id_, instance, is_external_));
+      PepperBrowserConnection::Get(render_frame)
+          ->DidDeleteOutOfProcessPepperInstance(plugin_child_id_, instance,
+                                                is_external_);
     }
   }
 }
diff --git a/content/renderer/pepper/pepper_browser_connection.cc b/content/renderer/pepper/pepper_browser_connection.cc
index 218e089..719adff 100644
--- a/content/renderer/pepper/pepper_browser_connection.cc
+++ b/content/renderer/pepper/pepper_browser_connection.cc
@@ -43,15 +43,41 @@
     int render_frame_id,
     const GURL& document_url,
     const GURL& plugin_url) {
-  if (auto* io_host = GetIOHost()) {
-    io_host->DidCreateInProcessInstance(instance, render_frame_id, document_url,
-                                        plugin_url);
-  }
+  if (!GetIOHost())
+    return;
+  GetIOHost()->DidCreateInProcessInstance(instance, render_frame_id,
+                                          document_url, plugin_url);
 }
 
 void PepperBrowserConnection::DidDeleteInProcessInstance(PP_Instance instance) {
-  if (auto* io_host = GetIOHost())
-    io_host->DidDeleteInProcessInstance(instance);
+  if (!GetIOHost())
+    return;
+  GetIOHost()->DidDeleteInProcessInstance(instance);
+}
+
+void PepperBrowserConnection::DidCreateOutOfProcessPepperInstance(
+    int32_t plugin_child_id,
+    int32_t pp_instance,
+    bool is_external,
+    int32_t render_frame_id,
+    const GURL& document_url,
+    const GURL& plugin_url,
+    bool is_priviledged_context) {
+  if (!GetIOHost())
+    return;
+  GetIOHost()->DidCreateOutOfProcessPepperInstance(
+      plugin_child_id, pp_instance, is_external, render_frame_id, document_url,
+      plugin_url, is_priviledged_context);
+}
+
+void PepperBrowserConnection::DidDeleteOutOfProcessPepperInstance(
+    int32_t plugin_child_id,
+    int32_t pp_instance,
+    bool is_external) {
+  if (!GetIOHost())
+    return;
+  GetIOHost()->DidDeleteOutOfProcessPepperInstance(plugin_child_id, pp_instance,
+                                                   is_external);
 }
 
 void PepperBrowserConnection::SendBrowserCreate(
diff --git a/content/renderer/pepper/pepper_browser_connection.h b/content/renderer/pepper/pepper_browser_connection.h
index 4add11a..d93e166 100644
--- a/content/renderer/pepper/pepper_browser_connection.h
+++ b/content/renderer/pepper/pepper_browser_connection.h
@@ -61,6 +61,23 @@
   // Called when the renderer deletes an in-process instance.
   void DidDeleteInProcessInstance(PP_Instance instance);
 
+  // Called when the renderer creates an out of process instance.
+  void DidCreateOutOfProcessPepperInstance(int32_t plugin_child_id,
+                                           int32_t pp_instance,
+                                           bool is_external,
+                                           int32_t render_frame_id,
+                                           const GURL& document_url,
+                                           const GURL& plugin_url,
+                                           bool is_priviledged_context);
+
+  // Called when the renderer deletes an out of process instance.
+  void DidDeleteOutOfProcessPepperInstance(int32_t plugin_child_id,
+                                           int32_t pp_instance,
+                                           bool is_external);
+
+  // Return a bound PepperIOHost. This may return null in unittests.
+  mojom::PepperIOHost* GetIOHost();
+
  private:
   // RenderFrameObserver implementation.
   void OnDestruct() override;
@@ -70,9 +87,6 @@
       int32_t sequence_number,
       const std::vector<int>& pending_resource_host_ids);
 
-  // Return a bound PepperIOHost. This may return null in unittests.
-  mojom::PepperIOHost* GetIOHost();
-
   // Return the next sequence number.
   int32_t GetNextSequence();
 
diff --git a/content/renderer/pepper/plugin_module.cc b/content/renderer/pepper/plugin_module.cc
index cb83ca23..6fd669e7 100644
--- a/content/renderer/pepper/plugin_module.cc
+++ b/content/renderer/pepper/plugin_module.cc
@@ -24,6 +24,7 @@
 #include "content/public/renderer/content_renderer_client.h"
 #include "content/renderer/pepper/host_dispatcher_wrapper.h"
 #include "content/renderer/pepper/host_globals.h"
+#include "content/renderer/pepper/pepper_browser_connection.h"
 #include "content/renderer/pepper/pepper_hung_plugin_filter.h"
 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
 #include "content/renderer/pepper/pepper_plugin_registry.h"
@@ -697,13 +698,19 @@
   }
 
   // Out of process: have the browser start the plugin process for us.
-  IPC::ChannelHandle channel_handle;
+  mojo::ScopedMessagePipeHandle channel_handle;
   base::ProcessId peer_pid = 0;
   int plugin_child_id = 0;
-  render_frame->Send(new FrameHostMsg_OpenChannelToPepperPlugin(
+  mojom::PepperIOHost* io_host =
+      PepperBrowserConnection::Get(render_frame)->GetIOHost();
+  if (!io_host) {
+    // Couldn't be initialized.
+    return scoped_refptr<PluginModule>();
+  }
+  io_host->OpenChannelToPepperPlugin(
       render_frame->GetWebFrame()->GetSecurityOrigin(), path, origin_lock,
-      &channel_handle, &peer_pid, &plugin_child_id));
-  if (!channel_handle.is_mojo_channel_handle()) {
+      &channel_handle, &peer_pid, &plugin_child_id);
+  if (!channel_handle.is_valid()) {
     // Couldn't be initialized.
     return scoped_refptr<PluginModule>();
   }
@@ -717,7 +724,7 @@
                                                      module.get());
 
   if (!module->CreateOutOfProcessModule(render_frame, path, permissions,
-                                        channel_handle, peer_pid,
+                                        channel_handle.release(), peer_pid,
                                         plugin_child_id, false,
                                         task_runner))  // is_external = false
     return scoped_refptr<PluginModule>();
diff --git a/content/shell/android/BUILD.gn b/content/shell/android/BUILD.gn
index 1e64a0554..2ab1995 100644
--- a/content/shell/android/BUILD.gn
+++ b/content/shell/android/BUILD.gn
@@ -146,6 +146,7 @@
     "//net/android:net_java",
     "//third_party/android_deps:com_google_code_findbugs_jsr305_java",
     "//ui/android:ui_java",
+    "//url:gurl_java",
   ]
 
   sources = [
diff --git a/content/shell/android/java/src/org/chromium/content_shell/Shell.java b/content/shell/android/java/src/org/chromium/content_shell/Shell.java
index 9cd02fa..0123f70 100644
--- a/content/shell/android/java/src/org/chromium/content_shell/Shell.java
+++ b/content/shell/android/java/src/org/chromium/content_shell/Shell.java
@@ -196,7 +196,7 @@
     public void loadUrl(String url) {
         if (url == null) return;
 
-        if (TextUtils.equals(url, mWebContents.getLastCommittedUrl())) {
+        if (TextUtils.equals(url, mWebContents.getLastCommittedUrl().getSpec())) {
             mNavigationController.reload(true);
         } else {
             mNavigationController.loadUrl(new LoadUrlParams(sanitizeUrl(url)));
diff --git a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java
index 2700ceec..331aa11d7 100644
--- a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java
+++ b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java
@@ -110,7 +110,7 @@
         ContentShellActivity activity = launchContentShellWithUrl(isolatedTestFileUrl);
         Assert.assertNotNull(getActivity());
         waitForActiveShellToBeDoneLoading();
-        Assert.assertEquals(isolatedTestFileUrl, getWebContents().getLastCommittedUrl());
+        Assert.assertEquals(isolatedTestFileUrl, getWebContents().getLastCommittedUrl().getSpec());
         return activity;
     }
 
@@ -250,8 +250,7 @@
             Criteria.checkThat("Shell is null.", shell, Matchers.notNullValue());
             Criteria.checkThat("Shell is still loading.", shell.isLoading(), Matchers.is(false));
             Criteria.checkThat("Shell's URL is empty or null.",
-                    shell.getWebContents().getLastCommittedUrl(),
-                    Matchers.not(Matchers.isEmptyOrNullString()));
+                    shell.getWebContents().getLastCommittedUrl().isEmpty(), Matchers.is(false));
         }, WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
     }
 
diff --git a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java
index edf239c7..1a6b8ae 100644
--- a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java
+++ b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java
@@ -122,7 +122,8 @@
         super.onSaveInstanceState(outState);
         WebContents webContents = getActiveWebContents();
         if (webContents != null) {
-            outState.putString(ACTIVE_SHELL_URL_KEY, webContents.getLastCommittedUrl());
+            // TODO(yfriedman): crbug/783819 - This should use GURL serialize/deserialize.
+            outState.putString(ACTIVE_SHELL_URL_KEY, webContents.getLastCommittedUrl().getSpec());
         }
 
         mWindowAndroid.saveInstanceState(outState);
diff --git a/content/test/data/accessibility/aria/aria-combobox-uneditable-expected-mac.txt b/content/test/data/accessibility/aria/aria-combobox-uneditable-expected-mac.txt
index f30f15e3..a3fe1ad 100644
--- a/content/test/data/accessibility/aria/aria-combobox-uneditable-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-combobox-uneditable-expected-mac.txt
@@ -1,7 +1,8 @@
 AXWebArea AXFocused=1
 ++AXGroup
 ++++AXStaticText AXValue='Choose a fruit, with text content'
-++AXPopUpButton AXLinkedUIElements=[:6] AXTitle='Choose a fruit, with text content' AXValue='Apple'
+++AXComboBox AXLinkedUIElements=[:6] AXTitle='Choose a fruit, with text content' AXValue='Apple'
+
 ++++AXStaticText AXValue='Apple'
 ++AXList
 ++++AXStaticText AXValue='Apple'
diff --git a/content/test/data/accessibility/aria/aria-haspopup-expected-mac.txt b/content/test/data/accessibility/aria/aria-haspopup-expected-mac.txt
index 11d0f74..a3e1617 100644
--- a/content/test/data/accessibility/aria/aria-haspopup-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-haspopup-expected-mac.txt
@@ -1,10 +1,10 @@
 AXWebArea
-++AXPopUpButton AXHasPopup=1 AXPopupValue='menu'
-++AXPopUpButton
-++AXPopUpButton AXHasPopup=1 AXPopupValue='menu'
-++AXPopUpButton AXHasPopup=1 AXPopupValue='listbox'
-++AXPopUpButton AXHasPopup=1 AXPopupValue='grid'
-++AXPopUpButton AXHasPopup=1 AXPopupValue='dialog'
-++AXPopUpButton AXHasPopup=1 AXPopupValue='listbox'
-++AXPopUpButton AXHasPopup=1 AXPopupValue='listbox'
-++AXPopUpButton AXHasPopup=1 AXPopupValue='listbox'
+++AXComboBox AXHasPopup=1 AXPopupValue='menu'
+++AXComboBox
+++AXComboBox AXHasPopup=1 AXPopupValue='menu'
+++AXComboBox AXHasPopup=1 AXPopupValue='listbox'
+++AXComboBox AXHasPopup=1 AXPopupValue='grid'
+++AXComboBox AXHasPopup=1 AXPopupValue='dialog'
+++AXComboBox AXHasPopup=1 AXPopupValue='listbox'
+++AXComboBox AXHasPopup=1 AXPopupValue='listbox'
+++AXComboBox AXHasPopup=1 AXPopupValue='listbox'
diff --git a/content/test/data/accessibility/aria/aria-orientation-expected-mac.txt b/content/test/data/accessibility/aria/aria-orientation-expected-mac.txt
index 4c605836..c04259a 100644
--- a/content/test/data/accessibility/aria/aria-orientation-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-orientation-expected-mac.txt
@@ -1,7 +1,7 @@
 AXWebArea
-++AXGroup
-++AXGroup AXOrientation='AXHorizontalOrientation'
-++AXGroup AXOrientation='AXVerticalOrientation'
+++AXComboBox
+++AXComboBox AXOrientation='AXHorizontalOrientation'
+++AXComboBox AXOrientation='AXVerticalOrientation'
 ++AXList AXOrientation='AXVerticalOrientation'
 ++AXList AXOrientation='AXHorizontalOrientation'
 ++AXList AXOrientation='AXVerticalOrientation'
diff --git a/content/test/data/accessibility/aria/aria-owns-crash-expected-blink.txt b/content/test/data/accessibility/aria/aria-owns-crash-expected-blink.txt
index 9f5363fec..7e7aa07 100644
--- a/content/test/data/accessibility/aria/aria-owns-crash-expected-blink.txt
+++ b/content/test/data/accessibility/aria/aria-owns-crash-expected-blink.txt
@@ -5,7 +5,10 @@
 ++++++++genericContainer ignored invisible name='cats'
 ++++++++++comboBoxGrouping ignored invisible
 ++++++++++++textField ignored invisible
+++++++++++++++genericContainer ignored invisible
 ++++++++++++listBox ignored invisible
 ++++++++++++++listItem ignored invisible
 ++++++++++++++++listMarker ignored invisible name='• '
+++++++++++++++++++staticText ignored name='• '
 ++++++++genericContainer ignored invisible
+
diff --git a/content/test/data/accessibility/aria/aria1.1-combobox-expected-mac.txt b/content/test/data/accessibility/aria/aria1.1-combobox-expected-mac.txt
index b942711..96f8201 100644
--- a/content/test/data/accessibility/aria/aria1.1-combobox-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria1.1-combobox-expected-mac.txt
@@ -1,12 +1,12 @@
 AXWebArea
 ++AXGroup
 ++++AXStaticText AXValue='State'
-++AXGroup AXTitle='State'
+++AXComboBox AXTitle='State'
 ++++AXTextField AXLinkedUIElements=[:6]
 ++AXList
 ++++AXStaticText AXValue='Alabama'
 ++++AXStaticText AXFocused=1 AXValue='Alaska'
-++AXGroup AXTitle='State'
+++AXComboBox AXTitle='State'
 ++++AXTextField AXLinkedUIElements=[:11]
 ++AXList
 ++++AXStaticText AXValue='Alabama'
diff --git a/content/test/data/accessibility/event/aria-hidden-descendants-already-ignored-expected-auralinux.txt b/content/test/data/accessibility/event/aria-hidden-descendants-already-ignored-expected-auralinux.txt
index e91acb5b..19466cf 100644
--- a/content/test/data/accessibility/event/aria-hidden-descendants-already-ignored-expected-auralinux.txt
+++ b/content/test/data/accessibility/event/aria-hidden-descendants-already-ignored-expected-auralinux.txt
@@ -1,3 +1 @@
 CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_HEADING) role=ROLE_TOOL_BAR ENABLED,HORIZONTAL,SENSITIVE,SHOWING,VISIBLE
-CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_SECTION) role=ROLE_SECTION ENABLED,SENSITIVE,SHOWING,VISIBLE
-CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_SECTION) role=ROLE_TOOL_BAR ENABLED,HORIZONTAL,SENSITIVE,SHOWING,VISIBLE
\ No newline at end of file
diff --git a/content/test/data/accessibility/event/children-changed-only-on-ancestor-expected-auralinux.txt b/content/test/data/accessibility/event/children-changed-only-on-ancestor-expected-auralinux.txt
new file mode 100644
index 0000000..16f2577c
--- /dev/null
+++ b/content/test/data/accessibility/event/children-changed-only-on-ancestor-expected-auralinux.txt
@@ -0,0 +1,13 @@
+CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_ARTICLE) role=ROLE_TREE ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE
+CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_LINK) role=ROLE_TREE_ITEM ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE
+CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_LINK) role=ROLE_TREE_ITEM ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE
+CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_LINK) role=ROLE_TREE_ITEM ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE
+CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_PANEL) role=ROLE_ARTICLE ENABLED,SENSITIVE,SHOWING,VISIBLE
+CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_SECTION) role=ROLE_LINK ENABLED,SENSITIVE,SHOWING,VISIBLE
+CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_SECTION) role=ROLE_LINK ENABLED,SENSITIVE,SHOWING,VISIBLE
+CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_SECTION) role=ROLE_LINK ENABLED,SENSITIVE,SHOWING,VISIBLE
+CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_STATIC) role=ROLE_SECTION ENABLED,SENSITIVE,SHOWING,VISIBLE
+CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_STATIC) role=ROLE_SECTION ENABLED,SENSITIVE,SHOWING,VISIBLE
+CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_STATIC) role=ROLE_SECTION ENABLED,SENSITIVE,SHOWING,VISIBLE
+=== Start Continuation ===
+CHILDREN-CHANGED:ADD index:0 CHILD:(role=ROLE_ARTICLE) role=ROLE_TREE ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE
diff --git a/content/test/data/accessibility/event/children-changed-only-on-ancestor-expected-win.txt b/content/test/data/accessibility/event/children-changed-only-on-ancestor-expected-win.txt
index 9ef6c48..39fbfa9d 100644
--- a/content/test/data/accessibility/event/children-changed-only-on-ancestor-expected-win.txt
+++ b/content/test/data/accessibility/event/children-changed-only-on-ancestor-expected-win.txt
@@ -5,7 +5,4 @@
 EVENT_OBJECT_REORDER on <div> role=ROLE_SYSTEM_OUTLINEITEM name="grandchild3" INVISIBLE,FOCUSABLE level=2
 === Start Continuation ===
 EVENT_OBJECT_REORDER on <div#tree> role=ROLE_SYSTEM_OUTLINE IA2_STATE_VERTICAL
-EVENT_OBJECT_REORDER on <div> role=ROLE_SYSTEM_OUTLINEITEM name="grandchild1" FOCUSABLE level=2 PosInSet=1 SetSize=3
-EVENT_OBJECT_REORDER on <div> role=ROLE_SYSTEM_OUTLINEITEM name="grandchild2" FOCUSABLE level=2 PosInSet=2 SetSize=3
-EVENT_OBJECT_REORDER on <div> role=ROLE_SYSTEM_OUTLINEITEM name="grandchild3" FOCUSABLE level=2 PosInSet=3 SetSize=3
-EVENT_OBJECT_SHOW on <div#article> role=ROLE_SYSTEM_DOCUMENT
+EVENT_OBJECT_SHOW on <div#article> role=ROLE_SYSTEM_DOCUMENT
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/img-broken-expected-blink.txt b/content/test/data/accessibility/html/img-broken-expected-blink.txt
new file mode 100644
index 0000000..b726a242
--- /dev/null
+++ b/content/test/data/accessibility/html/img-broken-expected-blink.txt
@@ -0,0 +1,6 @@
+rootWebArea name='done'
+++genericContainer ignored
+++++genericContainer
+++++++image name='pipe'
+++++++image name='control'
+++++++image name='broken-control'
diff --git a/content/test/data/accessibility/html/img-broken.html b/content/test/data/accessibility/html/img-broken.html
new file mode 100644
index 0000000..f45eaa95
--- /dev/null
+++ b/content/test/data/accessibility/html/img-broken.html
@@ -0,0 +1,23 @@
+<!--
+@MAC-ALLOW:AXRoleDescription='image'
+@WIN-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:xml-roles:*
+@ANDROID-ALLOW:has_image
+@WAIT-FOR:done
+-->
+<!-- When an image changes its broken state, it can get an extra user agent
+shadow DOM content that visibly displays the alt text. This should not accidentally
+cause extra ax children of the image to be created, since an image must be a leaf.
+The test toggles the broken state back and forth several times in order to ensure
+no illegal states in Blink are triggered (DCHECKs) -->
+<img src="pipe.jpg" alt="pipe"><img src="pipe.jpg" alt="control"><img src="pipe-broken.jpg" alt="broken-control">
+<script>
+  let counter = 0;
+  function changeImageName() {
+    const first_img = document.querySelector('img');
+    first_img.src = first_img.src == 'pipe.jpg' ? 'pipe-broken.jpg' : 'pipe.jpg';
+    if (++counter == 4)
+      document.title = 'done';
+  }
+  setInterval(changeImageName, 15);
+</script>
diff --git a/content/test/data/accessibility/html/input-radio-checkbox-label-expected-blink.txt b/content/test/data/accessibility/html/input-radio-checkbox-label-expected-blink.txt
index dcc2735..8ee8042 100644
--- a/content/test/data/accessibility/html/input-radio-checkbox-label-expected-blink.txt
+++ b/content/test/data/accessibility/html/input-radio-checkbox-label-expected-blink.txt
@@ -3,11 +3,9 @@
 ++++genericContainer
 ++++++labelText ignored
 ++++++++staticText ignored name='label ignored for radio button '
-++++++++++inlineTextBox ignored name='label ignored for radio button '
 ++++++++radioButton name='label ignored for radio button' checkedState=false
 ++++++labelText ignored
 ++++++++staticText ignored name='label ignored for checkbox '
-++++++++++inlineTextBox ignored name='label ignored for checkbox '
 ++++++++checkBox name='label ignored for checkbox' checkedState=false
 ++++++labelText name='label exposed for radio button '
 ++++++++staticText name='label exposed for radio button '
diff --git a/content/test/data/accessibility/html/list-text-removal-expected-blink.txt b/content/test/data/accessibility/html/list-text-removal-expected-blink.txt
new file mode 100644
index 0000000..ff968967
--- /dev/null
+++ b/content/test/data/accessibility/html/list-text-removal-expected-blink.txt
@@ -0,0 +1,7 @@
+rootWebArea name='done'
+++genericContainer ignored
+++++genericContainer ignored
+++++++list
+++++++++listItem hierarchicalLevel=1
+++++++++++staticText name='item'
+++++++++++++inlineTextBox name='item'
diff --git a/content/test/data/accessibility/html/list-text-removal.html b/content/test/data/accessibility/html/list-text-removal.html
new file mode 100644
index 0000000..b2f254ac
--- /dev/null
+++ b/content/test/data/accessibility/html/list-text-removal.html
@@ -0,0 +1,33 @@
+<!--
+@BLINK-ALLOW:hierarchicalLevel*
+@MAC-ALLOW:AXSubrole
+@WAIT-FOR:done
+-->
+<html>
+  <style type="text/css">
+    .inlineList li {
+      display: inline;
+    }
+  </style>
+  <body>
+    <ul class="inlineList">
+      <li>item</li>
+    </ul>$
+  </body>
+<script>
+function convertDollarToWhitespace(node) {
+  let child = node.firstChild;
+  while (child) {
+    if (child.nodeType == Node.ELEMENT_NODE)
+      convertDollarToWhitespace(child);
+    else if (child.nodeType == Node.TEXT_NODE && child.data.indexOf('$') >= 0)
+      child.data = '  \n ';
+    child = child.nextSibling;
+  }
+}
+setTimeout(() => {
+  convertDollarToWhitespace(document.body);
+  document.title = 'done';
+}, 500);
+</script>
+</html>
diff --git a/content/test/data/accessibility/html/object-image-error.html b/content/test/data/accessibility/html/object-image-error.html
index 6744bb3..931d3e8 100644
--- a/content/test/data/accessibility/html/object-image-error.html
+++ b/content/test/data/accessibility/html/object-image-error.html
@@ -3,8 +3,7 @@
 @NO-LOAD-EXPECTED:broken.jpg
 -->
 <div>
-    <object data="./broken.jpg"
-            onerror="document.getElementById('done').innerText='complete'; ">
+    <object data="./broken.jpg">
         Fallback
     </object>
 </div>
diff --git a/content/test/test_web_contents.cc b/content/test/test_web_contents.cc
index 3572ec5..6eff45e2 100644
--- a/content/test/test_web_contents.cc
+++ b/content/test/test_web_contents.cc
@@ -129,65 +129,6 @@
   return WebContentsImpl::GetTitle();
 }
 
-void TestWebContents::TestDidNavigate(RenderFrameHost* render_frame_host,
-                                      bool did_create_new_entry,
-                                      const GURL& url,
-                                      ui::PageTransition transition) {
-  TestDidNavigateWithSequenceNumber(render_frame_host, did_create_new_entry,
-                                    url, Referrer(), transition, false, -1, -1);
-}
-
-void TestWebContents::TestDidNavigateWithSequenceNumber(
-    RenderFrameHost* render_frame_host,
-    bool did_create_new_entry,
-    const GURL& url,
-    const Referrer& referrer,
-    ui::PageTransition transition,
-    bool was_within_same_document,
-    int item_sequence_number,
-    int document_sequence_number) {
-  TestRenderFrameHost* rfh =
-      static_cast<TestRenderFrameHost*>(render_frame_host);
-  rfh->InitializeRenderFrameIfNeeded();
-
-  if (!rfh->is_loading())
-    rfh->SimulateNavigationStart(url);
-
-  auto params = mojom::DidCommitProvisionalLoadParams::New();
-  params->item_sequence_number = item_sequence_number;
-  params->document_sequence_number = document_sequence_number;
-  params->url = url;
-  params->base_url = GURL();
-  params->referrer = blink::mojom::Referrer::From(referrer);
-  params->transition = transition;
-  params->redirects = std::vector<GURL>();
-  params->should_update_history = true;
-  params->contents_mime_type = std::string("text/html");
-  params->intended_as_new_entry = did_create_new_entry;
-  params->did_create_new_entry = did_create_new_entry;
-  params->should_replace_current_entry = false;
-  params->gesture = NavigationGestureUser;
-  params->method = "GET";
-  params->post_id = 0;
-  params->http_status_code = 200;
-  params->url_is_unreachable = false;
-  if (item_sequence_number != -1 && document_sequence_number != -1) {
-    params->page_state = blink::PageState::CreateForTestingWithSequenceNumbers(
-        url, item_sequence_number, document_sequence_number);
-  } else {
-    params->page_state = blink::PageState::CreateFromURL(url);
-  }
-  params->original_request_url = GURL();
-  params->is_overriding_user_agent = false;
-  params->history_list_was_cleared = false;
-  params->origin = url::Origin::Create(url);
-  params->insecure_request_policy =
-      blink::mojom::InsecureRequestPolicy::kLeaveInsecureRequestsAlone;
-  params->has_potentially_trustworthy_unique_origin = false;
-
-  rfh->SendNavigateWithParams(std::move(params), was_within_same_document);
-}
-
 const std::string& TestWebContents::GetSaveFrameHeaders() {
   return save_frame_headers_;
 }
diff --git a/content/test/test_web_contents.h b/content/test/test_web_contents.h
index 542419f..ec85a99a 100644
--- a/content/test/test_web_contents.h
+++ b/content/test/test_web_contents.h
@@ -74,18 +74,6 @@
 
   void NavigateAndFail(const GURL& url, int error_code) override;
   void TestSetIsLoading(bool value) override;
-  void TestDidNavigate(RenderFrameHost* render_frame_host,
-                       bool did_create_new_entry,
-                       const GURL& url,
-                       ui::PageTransition transition) override;
-  void TestDidNavigateWithSequenceNumber(RenderFrameHost* render_frame_host,
-                                         bool did_create_new_entry,
-                                         const GURL& url,
-                                         const Referrer& referrer,
-                                         ui::PageTransition transition,
-                                         bool was_within_same_document,
-                                         int item_sequence_number,
-                                         int document_sequence_number);
   void SetOpener(WebContents* opener) override;
   const std::string& GetSaveFrameHeaders() override;
   const base::string16& GetSuggestedFileName() override;
diff --git a/docs/speed_metrics/webperf_okrs.md b/docs/speed_metrics/webperf_okrs.md
index 7c9b631..28aa641 100644
--- a/docs/speed_metrics/webperf_okrs.md
+++ b/docs/speed_metrics/webperf_okrs.md
@@ -29,6 +29,9 @@
   ([bug](https://bugs.chromium.org/p/chromium/issues/detail?id=1091754)).
     * Present proposal to security team, and begin socializing the proposal externally.
 * **A/B testing**: organize workshop on client-side A/B testing.
+* **JS Sampling Profiler**:
+  * Complete the GC integration work.
+  * Ship the API.
 
 ## 2020 Q4 Progress
 
@@ -38,6 +41,10 @@
   [Intent to Ship](https://groups.google.com/a/chromium.org/g/blink-dev/c/RExJ9a3SmQw).
 * **Page abandonment**: made some data available publicly and socialized it in a
   [blogpost](https://calendar.perfplanet.com/2020/abandonment/).
+* **JS Sampling Profiler**:
+  * Implemented the API so it requires COOP/COEP and gated it behind Document Policy.
+  * Finished a prototype of GC integration for the V8 sampling profiler (which will help reduce profiler startup time).
+  * Landed some initial support for code object refcounting.
 * **Smoothness**: published a proposal around dropped frames and presented it at TPAC.
 * **Back-forward cache**: determined that it is backwards compatible to expose a PerformanceNavigationTiming
   entry for back-forward navigations.
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn
index 6616ad16..f6b7859 100644
--- a/extensions/BUILD.gn
+++ b/extensions/BUILD.gn
@@ -305,7 +305,6 @@
     "//components/dom_distiller/core:test_support",
     "//components/guest_view/browser:test_support",
     "//components/javascript_dialogs",
-    "//components/printing/common",
     "//components/resources",
     "//components/strings",
     "//components/sync",
diff --git a/extensions/DEPS b/extensions/DEPS
index 4d8fbad..dbcdddc2 100644
--- a/extensions/DEPS
+++ b/extensions/DEPS
@@ -41,9 +41,6 @@
   ".*(test|test_util)\.(cc|h)$": [
     "+content/public/test",
   ],
-  "mime_handler_view_browsertest.cc": [
-    "+components/printing/common",
-  ],
   "mime_handler_view_interactive_uitest.cc": [
     "+chrome/browser/ui/exclusive_access/exclusive_access_test.h",
     "+chrome/test/base/interactive_test_utils.h",
diff --git a/extensions/browser/api/automation_internal/automation_internal_api.cc b/extensions/browser/api/automation_internal/automation_internal_api.cc
index c028d84..9bed7277 100644
--- a/extensions/browser/api/automation_internal/automation_internal_api.cc
+++ b/extensions/browser/api/automation_internal/automation_internal_api.cc
@@ -20,6 +20,7 @@
 #include "content/public/browser/browser_plugin_guest_manager.h"
 #include "content/public/browser/media_player_id.h"
 #include "content/public/browser/media_session.h"
+#include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_widget_host.h"
@@ -132,6 +133,10 @@
 
 }  // namespace
 
+using OldAXTreeIdMap = std::map<content::NavigationHandle*, ui::AXTreeID>;
+base::LazyInstance<OldAXTreeIdMap>::DestructorAtExit g_old_ax_tree =
+    LAZY_INSTANCE_INITIALIZER;
+
 // Helper class that receives accessibility data from |WebContents|.
 class AutomationWebContentsObserver
     : public content::WebContentsObserver,
@@ -154,6 +159,29 @@
     router->DispatchAccessibilityEvents(extension_event_bundle);
   }
 
+  void DidStartNavigation(content::NavigationHandle* navigation) override {
+    content::RenderFrameHost* previous_rfh = content::RenderFrameHost::FromID(
+        navigation->GetPreviousRenderFrameHostId());
+    DCHECK(previous_rfh);
+    g_old_ax_tree.Get()[navigation] = previous_rfh->GetAXTreeID();
+  }
+
+  void DidFinishNavigation(content::NavigationHandle* navigation) override {
+    ui::AXTreeID old_ax_tree = g_old_ax_tree.Get()[navigation];
+    g_old_ax_tree.Get().erase(navigation);
+
+    if (old_ax_tree == ui::AXTreeIDUnknown())
+      return;
+
+    ui::AXTreeID new_ax_tree = navigation->GetRenderFrameHost()->GetAXTreeID();
+
+    if (old_ax_tree == new_ax_tree)
+      return;
+
+    AutomationEventRouter::GetInstance()->DispatchTreeDestroyedEvent(
+        old_ax_tree, browser_context_);
+  }
+
   void AccessibilityLocationChangesReceived(
       const std::vector<content::AXLocationChangeNotificationDetails>& details)
       override {
@@ -177,19 +205,6 @@
         tree_id, browser_context_);
   }
 
-  void RenderFrameHostChanged(content::RenderFrameHost* old_host,
-                              content::RenderFrameHost* new_host) override {
-    if (!old_host)
-      return;
-
-    ui::AXTreeID tree_id = old_host->GetAXTreeID();
-    if (tree_id == ui::AXTreeIDUnknown())
-      return;
-
-    AutomationEventRouter::GetInstance()->DispatchTreeDestroyedEvent(
-        tree_id, browser_context_);
-  }
-
   void MediaStartedPlaying(const MediaPlayerInfo& video_type,
                            const content::MediaPlayerId& id) override {
     content::AXEventNotificationDetails content_event_bundle;
diff --git a/extensions/browser/api/storage/storage_frontend.cc b/extensions/browser/api/storage/storage_frontend.cc
index e988e2e..402ede1 100644
--- a/extensions/browser/api/storage/storage_frontend.cc
+++ b/extensions/browser/api/storage/storage_frontend.cc
@@ -130,7 +130,7 @@
 }
 
 void StorageFrontend::Init(scoped_refptr<ValueStoreFactory> factory) {
-  TRACE_EVENT0("browser,startup", "StorageFrontend::Init")
+  TRACE_EVENT0("browser,startup", "StorageFrontend::Init");
 
   observers_ = new SettingsObserverList();
   browser_context_observer_.reset(new DefaultObserver(browser_context_));
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc
index d86ce6e..b281f98 100644
--- a/extensions/browser/extension_prefs.cc
+++ b/extensions/browser/extension_prefs.cc
@@ -1993,7 +1993,7 @@
 }
 
 void ExtensionPrefs::InitPrefStore() {
-  TRACE_EVENT0("browser,startup", "ExtensionPrefs::InitPrefStore")
+  TRACE_EVENT0("browser,startup", "ExtensionPrefs::InitPrefStore");
 
   // When this is called, the PrefService is initialized and provides access
   // to the user preferences stored in a JSON file.
@@ -2476,7 +2476,7 @@
 void ExtensionPrefs::InitExtensionControlledPrefs(
     const ExtensionsInfo& extensions_info) {
   TRACE_EVENT0("browser,startup",
-               "ExtensionPrefs::InitExtensionControlledPrefs")
+               "ExtensionPrefs::InitExtensionControlledPrefs");
 
   for (const auto& info : extensions_info) {
     const ExtensionId& extension_id = info->extension_id;
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc
index b8548ad3..3c86e74 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc
@@ -21,8 +21,6 @@
 #include "components/guest_view/browser/test_guest_view_manager.h"
 #include "components/javascript_dialogs/app_modal_dialog_controller.h"
 #include "components/javascript_dialogs/app_modal_dialog_view.h"
-#include "components/printing/common/print.mojom.h"
-#include "components/printing/common/print_messages.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -182,60 +180,6 @@
   EXPECT_EQ(1U, gv_manager->num_guests_created());
 }
 
-#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
-class PrintPreviewWaiter : public content::BrowserMessageFilter {
- public:
-  PrintPreviewWaiter() : BrowserMessageFilter(PrintMsgStart) {}
-
-  bool OnMessageReceived(const IPC::Message& message) override {
-    IPC_BEGIN_MESSAGE_MAP(PrintPreviewWaiter, message)
-      IPC_MESSAGE_HANDLER(PrintHostMsg_DidStartPreview, OnDidStartPreview)
-    IPC_END_MESSAGE_MAP()
-    return false;
-  }
-
-  void OnDidStartPreview(const printing::mojom::DidStartPreviewParams& params,
-                         const printing::mojom::PreviewIds& ids) {
-    // Expect that there is at least one page.
-    did_load_ = true;
-    run_loop_.Quit();
-
-    EXPECT_TRUE(params.page_count >= 1);
-  }
-
-  void Wait() {
-    if (!did_load_)
-      run_loop_.Run();
-  }
-
- private:
-  ~PrintPreviewWaiter() override = default;
-
-  bool did_load_ = false;
-  base::RunLoop run_loop_;
-};
-
-IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, EmbeddedThenPrint) {
-  RunTest("test_embedded.html");
-  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
-  auto* gv_manager = GetGuestViewManager();
-  gv_manager->WaitForAllGuestsDeleted();
-  EXPECT_EQ(1U, gv_manager->num_guests_created());
-
-  // Verify that print dialog comes up.
-  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
-  auto* main_frame = web_contents->GetMainFrame();
-  auto print_preview_waiter = base::MakeRefCounted<PrintPreviewWaiter>();
-  web_contents->GetMainFrame()->GetProcess()->AddFilter(
-      print_preview_waiter.get());
-  // Use setTimeout() to prevent ExecuteScript() from blocking on the print
-  // dialog.
-  ASSERT_TRUE(content::ExecuteScript(
-      main_frame, "setTimeout(function() { window.print(); }, 0)"));
-  print_preview_waiter->Wait();
-}
-#endif  // BUILDFLAG(ENABLE_PRINT_PREVIEW)
-
 // This test start with an <object> that has a content frame. Then the content
 // frame (plugin frame) is navigated to a cross-origin target page. After the
 // navigation is completed, the <object> is set to render MimeHandlerView by
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
index 998d6d0..dd77a8b9 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
@@ -217,6 +217,7 @@
 }
 
 void MimeHandlerViewGuest::DidAttachToEmbedder() {
+  DCHECK(stream_->handler_url().SchemeIs(extensions::kExtensionScheme));
   web_contents()->GetController().LoadURL(
       stream_->handler_url(), content::Referrer(),
       ui::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
@@ -443,6 +444,18 @@
       stream_->TakeTransferrableURLLoader());
 }
 
+void MimeHandlerViewGuest::DidFinishNavigation(
+    content::NavigationHandle* navigation_handle) {
+  if (navigation_handle->IsInMainFrame()) {
+    // We should not navigate the guest away from the handling extension.
+    const url::Origin handler_origin =
+        url::Origin::Create(stream_->handler_url());
+    const url::Origin new_origin =
+        url::Origin::Create(navigation_handle->GetURL());
+    CHECK(new_origin.IsSameOriginWith(handler_origin));
+  }
+}
+
 void MimeHandlerViewGuest::FuseBeforeUnloadControl(
     mojo::PendingReceiver<mime_handler::BeforeUnloadControl> receiver) {
   if (!pending_before_unload_control_)
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h
index a7f0b19a8..d9e22ce 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h
@@ -175,6 +175,7 @@
   void DocumentOnLoadCompletedInMainFrame() final;
   void ReadyToCommitNavigation(
       content::NavigationHandle* navigation_handle) final;
+  void DidFinishNavigation(content::NavigationHandle* navigation_handle) final;
 
   std::unique_ptr<MimeHandlerViewGuestDelegate> delegate_;
   std::unique_ptr<StreamContainer> stream_;
diff --git a/extensions/browser/renderer_startup_helper.cc b/extensions/browser/renderer_startup_helper.cc
index 7f9fce0..8b5eb01 100644
--- a/extensions/browser/renderer_startup_helper.cc
+++ b/extensions/browser/renderer_startup_helper.cc
@@ -138,8 +138,7 @@
   // partition ID to it.
   std::string webview_partition_id = WebViewGuest::GetPartitionID(process);
   if (!webview_partition_id.empty()) {
-    process->Send(new ExtensionMsg_SetWebViewPartitionID(
-        WebViewGuest::GetPartitionID(process)));
+    renderer->SetWebViewPartitionID(webview_partition_id);
   }
 
   BrowserContext* renderer_context = process->GetBrowserContext();
diff --git a/extensions/browser/renderer_startup_helper_unittest.cc b/extensions/browser/renderer_startup_helper_unittest.cc
index 9be3c1a..9ef927c 100644
--- a/extensions/browser/renderer_startup_helper_unittest.cc
+++ b/extensions/browser/renderer_startup_helper_unittest.cc
@@ -57,6 +57,8 @@
   void SetSystemFont(const std::string& font_family,
                      const std::string& font_size) override {}
 
+  void SetWebViewPartitionID(const std::string& partition_id) override {}
+
   std::vector<std::string> activated_extensions_;
   std::vector<std::string> unloaded_extensions_;
   mojo::AssociatedReceiverSet<mojom::Renderer> receivers_;
diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h
index 45f92c7..17bb559 100644
--- a/extensions/common/extension_messages.h
+++ b/extensions/common/extension_messages.h
@@ -720,10 +720,6 @@
 IPC_MESSAGE_CONTROL1(ExtensionMsg_TransferBlobs,
                      std::vector<std::string> /* blob_uuids */)
 
-// Report the WebView partition ID to the WebView guest renderer process.
-IPC_MESSAGE_CONTROL1(ExtensionMsg_SetWebViewPartitionID,
-                     std::string /* webview_partition_id */)
-
 // Enable or disable spatial navigation.
 IPC_MESSAGE_ROUTED1(ExtensionMsg_SetSpatialNavigationEnabled,
                     bool /* spatial_nav_enabled */)
diff --git a/extensions/common/mojom/renderer.mojom b/extensions/common/mojom/renderer.mojom
index 7b09a73..273d828 100644
--- a/extensions/common/mojom/renderer.mojom
+++ b/extensions/common/mojom/renderer.mojom
@@ -33,4 +33,7 @@
 
   // Tells the renderer process the platform's system font.
   SetSystemFont(string font_family, string font_size);
+
+  // Reports the WebView partition ID to the WebView guest renderer process.
+  SetWebViewPartitionID(string partition_id);
 };
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index 9dd2d9e..2ad828ee 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -876,8 +876,6 @@
   IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchEvent, OnDispatchEvent)
   IPC_MESSAGE_HANDLER(ExtensionMsg_SetScriptingAllowlist,
                       OnSetScriptingAllowlist)
-  IPC_MESSAGE_HANDLER(ExtensionMsg_SetWebViewPartitionID,
-                      OnSetWebViewPartitionID)
   IPC_MESSAGE_HANDLER(ExtensionMsg_ShouldSuspend, OnShouldSuspend)
   IPC_MESSAGE_HANDLER(ExtensionMsg_Suspend, OnSuspend)
   IPC_MESSAGE_HANDLER(ExtensionMsg_TransferBlobs, OnTransferBlobs)
@@ -1014,6 +1012,12 @@
   system_font_size_ = font_size;
 }
 
+void Dispatcher::SetWebViewPartitionID(const std::string& partition_id) {
+  // |webview_partition_id_| cannot be changed once set.
+  CHECK(webview_partition_id_.empty() || webview_partition_id_ == partition_id);
+  webview_partition_id_ = partition_id;
+}
+
 void Dispatcher::OnCancelSuspend(const std::string& extension_id) {
   DispatchEvent(extension_id, kOnSuspendCanceledEvent, base::ListValue(),
                 nullptr);
@@ -1185,12 +1189,6 @@
   ExtensionsClient::Get()->SetScriptingAllowlist(extension_ids);
 }
 
-void Dispatcher::OnSetWebViewPartitionID(const std::string& partition_id) {
-  // |webview_partition_id_| cannot be changed once set.
-  CHECK(webview_partition_id_.empty() || webview_partition_id_ == partition_id);
-  webview_partition_id_ = partition_id;
-}
-
 void Dispatcher::OnShouldSuspend(const std::string& extension_id,
                                  uint64_t sequence_id) {
   RenderThread::Get()->Send(
diff --git a/extensions/renderer/dispatcher.h b/extensions/renderer/dispatcher.h
index 517422d..a784c3f 100644
--- a/extensions/renderer/dispatcher.h
+++ b/extensions/renderer/dispatcher.h
@@ -222,6 +222,7 @@
                       bool lock_screen_context) override;
   void SetSystemFont(const std::string& font_family,
                      const std::string& font_size) override;
+  void SetWebViewPartitionID(const std::string& partition_id) override;
   void OnRendererAssociatedRequest(
       mojo::PendingAssociatedReceiver<mojom::Renderer> receiver);
   void OnCancelSuspend(const std::string& extension_id);
@@ -246,7 +247,6 @@
                        const base::ListValue& event_args);
   void OnSetScriptingAllowlist(
       const ExtensionsClient::ScriptingAllowlist& extension_ids);
-  void OnSetWebViewPartitionID(const std::string& partition_id);
   void OnShouldSuspend(const std::string& extension_id, uint64_t sequence_id);
   void OnSuspend(const std::string& extension_id);
   void OnTransferBlobs(const std::vector<std::string>& blob_uuids);
diff --git a/gpu/OWNERS b/gpu/OWNERS
index 1a2f997..6aeb7ec 100644
--- a/gpu/OWNERS
+++ b/gpu/OWNERS
@@ -1,6 +1,5 @@
 backer@chromium.org
 kbr@chromium.org
-khushalsagar@chromium.org
 vasilyt@chromium.org
 vmiura@chromium.org
 zmo@chromium.org
diff --git a/ios/chrome/app/application_delegate/app_state.mm b/ios/chrome/app/application_delegate/app_state.mm
index 2adfdc7..320f71b 100644
--- a/ios/chrome/app/application_delegate/app_state.mm
+++ b/ios/chrome/app/application_delegate/app_state.mm
@@ -435,6 +435,8 @@
   }
   _appIsTerminating = YES;
 
+  [_appCommandDispatcher prepareForShutdown];
+
   // Cancel any in-flight distribution notifications.
   CHECK(ios::GetChromeBrowserProvider());
   ios::GetChromeBrowserProvider()
@@ -468,6 +470,19 @@
     API_AVAILABLE(ios(13)) {
   NSMutableArray<NSString*>* sessionIDs =
       [NSMutableArray arrayWithCapacity:sceneSessions.count];
+  // This method is invoked by iOS to inform the application that the sessions
+  // for "closed windows" is garbage collected and that any data associated with
+  // them by the application needs to be deleted.
+  //
+  // Usually Chrome uses -[SceneState sceneSessionID] as identifier to properly
+  // support devices that do not support multi-window (and which use a constant
+  // identifier). For devices that do not support multi-window the session is
+  // saved at a constant path, so it is harmnless to delete files at a path
+  // derived from -persistentIdentifier (since there won't be files deleted).
+  // For devices that do support multi-window, there is data to delete once the
+  // session is garbage collected.
+  //
+  // Thus it is always correct to use -persistentIdentifier here.
   for (UISceneSession* session in sceneSessions) {
     [sessionIDs addObject:session.persistentIdentifier];
   }
diff --git a/ios/chrome/browser/ui/commands/command_dispatcher.h b/ios/chrome/browser/ui/commands/command_dispatcher.h
index f9241719..e977b4d 100644
--- a/ios/chrome/browser/ui/commands/command_dispatcher.h
+++ b/ios/chrome/browser/ui/commands/command_dispatcher.h
@@ -65,6 +65,13 @@
 // otherwise.
 - (CommandDispatcher*)strictCallableForProtocol:(Protocol*)protocol;
 
+// After this method is called, -stopDispatching methods will stop dispatching,
+// but this object will continue to respond to registered selectors by silently
+// failing. This method should be called on -applicationWillTerminate. It helps
+// avoid untangling the dispatcher chains in the correct order, which sometimes
+// can be very hard.
+- (void)prepareForShutdown;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_COMMANDS_COMMAND_DISPATCHER_H_
diff --git a/ios/chrome/browser/ui/commands/command_dispatcher.mm b/ios/chrome/browser/ui/commands/command_dispatcher.mm
index 5e569fca..df679270 100644
--- a/ios/chrome/browser/ui/commands/command_dispatcher.mm
+++ b/ios/chrome/browser/ui/commands/command_dispatcher.mm
@@ -15,13 +15,48 @@
 #error "This file requires ARC support."
 #endif
 
+#pragma mark - SilentlyFailingObject
+
+// Object that responds to any selector and does nothing when its called.
+// Used as a "nice OCMock" equivalent for CommandDispatcher that's preparing for
+// shutdown.
+@interface SilentlyFailingObject : NSProxy
+@end
+@implementation SilentlyFailingObject
+
+- (instancetype)init {
+  return self;
+}
+
+- (void)forwardInvocation:(NSInvocation*)invocation {
+}
+
+- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector {
+  // Return some method signature to silence errors.
+  // Here it's (void)(self, _cmd).
+  return [NSMethodSignature signatureWithObjCTypes:"v@:"];
+}
+
+@end
+
+#pragma mark - CommandDispatcher
+
 @implementation CommandDispatcher {
   // Stores which target to forward to for a given selector.
   std::unordered_map<SEL, __weak id> _forwardingTargets;
+
+  // Stores remembered targets while preparing for shutdown.
+  std::unordered_map<SEL, __weak id> _silentlyFailingTargets;
+
+  // Tracks if preparing for shutdown has been requested.
+  // This is an ivar, not a property, to avoid having synthesized getter/setter
+  // methods.
+  BOOL _preparingForShutdown;
 }
 
 - (void)startDispatchingToTarget:(id)target forSelector:(SEL)selector {
   DCHECK(![self targetForSelector:selector]);
+  DCHECK(![self shouldFailSilentlyForSelector:selector]);
 
   _forwardingTargets[selector] = target;
 }
@@ -40,6 +75,10 @@
 }
 
 - (void)stopDispatchingForSelector:(SEL)selector {
+  if (_preparingForShutdown) {
+    id target = _forwardingTargets[selector];
+    _silentlyFailingTargets[selector] = target;
+  }
   _forwardingTargets.erase(selector);
 }
 
@@ -85,7 +124,9 @@
   BOOL conforming = YES;
   for (unsigned int i = 0; i < methodCount; i++) {
     SEL selector = requiredInstanceMethods[i].name;
-    if (_forwardingTargets.find(selector) == _forwardingTargets.end()) {
+    BOOL targetFound =
+        _forwardingTargets.find(selector) != _forwardingTargets.end();
+    if (!targetFound && ![self shouldFailSilentlyForSelector:selector]) {
       conforming = NO;
       break;
     }
@@ -114,6 +155,10 @@
   return self;
 }
 
+- (void)prepareForShutdown {
+  _preparingForShutdown = YES;
+}
+
 #pragma mark - NSObject
 
 // Overridden to forward messages to registered handlers.
@@ -149,9 +194,18 @@
 // Returns the target registered to receive messeages for |selector|.
 - (id)targetForSelector:(SEL)selector {
   auto target = _forwardingTargets.find(selector);
-  if (target == _forwardingTargets.end())
+  if (target == _forwardingTargets.end()) {
+    if ([self shouldFailSilentlyForSelector:selector]) {
+      return [[SilentlyFailingObject alloc] init];
+    }
     return nil;
+  }
   return target->second;
 }
 
+- (BOOL)shouldFailSilentlyForSelector:(SEL)selector {
+  return _preparingForShutdown && _silentlyFailingTargets.find(selector) !=
+                                      _silentlyFailingTargets.end();
+}
+
 @end
diff --git a/ios/chrome/browser/ui/commands/command_dispatcher_unittest.mm b/ios/chrome/browser/ui/commands/command_dispatcher_unittest.mm
index cee63cd..79d615f 100644
--- a/ios/chrome/browser/ui/commands/command_dispatcher_unittest.mm
+++ b/ios/chrome/browser/ui/commands/command_dispatcher_unittest.mm
@@ -355,6 +355,70 @@
   EXPECT_TRUE(exception_caught);
 }
 
+// Tests that an exception is not thrown when prepareForShutdown was called
+// before the target was removed.
+TEST_F(CommandDispatcherTest,
+       PrepareForShutdownLetsStopDispatchingForSelectorFailSilently) {
+  id dispatcher = [[CommandDispatcher alloc] init];
+  CommandDispatcherTestSimpleTarget* target =
+      [[CommandDispatcherTestSimpleTarget alloc] init];
+
+  // Check stopDispatchingForSelector:
+  [dispatcher startDispatchingToTarget:target forSelector:@selector(show)];
+  [dispatcher prepareForShutdown];
+  [dispatcher stopDispatchingForSelector:@selector(show)];
+  bool exception_caught = false;
+  @try {
+    [dispatcher show];
+  } @catch (NSException* exception) {
+    exception_caught = true;
+  }
+
+  EXPECT_FALSE(exception_caught);
+}
+
+TEST_F(CommandDispatcherTest,
+       PrepareForShutdownLetsStopDispatchingForProtocolFailSilently) {
+  id dispatcher = [[CommandDispatcher alloc] init];
+  CommandDispatcherTestSimpleTarget* target =
+      [[CommandDispatcherTestSimpleTarget alloc] init];
+
+  // Check stopDispatchingForProtocol:
+  [dispatcher startDispatchingToTarget:target
+                           forProtocol:@protocol(HideProtocol)];
+  [dispatcher prepareForShutdown];
+  [dispatcher stopDispatchingForProtocol:@protocol(HideProtocol)];
+  bool exception_caught = false;
+  @try {
+    [dispatcher hide];
+  } @catch (NSException* exception) {
+    exception_caught = true;
+  }
+
+  EXPECT_FALSE(exception_caught);
+}
+
+TEST_F(CommandDispatcherTest,
+       PrepareForShutdownLetsStopDispatchingToTargetFailSilently) {
+  id dispatcher = [[CommandDispatcher alloc] init];
+  CommandDispatcherTestSimpleTarget* target =
+      [[CommandDispatcherTestSimpleTarget alloc] init];
+
+  // Check stopDispatchingToTarget:
+  [dispatcher startDispatchingToTarget:target
+                           forProtocol:@protocol(HideProtocol)];
+  [dispatcher prepareForShutdown];
+  [dispatcher stopDispatchingToTarget:target];
+  bool exception_caught = false;
+  @try {
+    [dispatcher hide];
+  } @catch (NSException* exception) {
+    exception_caught = true;
+  }
+
+  EXPECT_FALSE(exception_caught);
+}
+
 // Tests that -respondsToSelector returns YES for methods once they are
 // dispatched for.
 // Tests handler methods with no arguments.
diff --git a/ios/chrome/browser/ui/main/browser_view_wrangler.mm b/ios/chrome/browser/ui/main/browser_view_wrangler.mm
index 9f47e2e..6c57b5f9 100644
--- a/ios/chrome/browser/ui/main/browser_view_wrangler.mm
+++ b/ios/chrome/browser/ui/main/browser_view_wrangler.mm
@@ -105,9 +105,6 @@
   std::unique_ptr<Browser> _otrBrowser;
 }
 
-// Opaque session ID from _sceneState, nil when multi-window isn't enabled.
-@property(nonatomic, readonly) NSString* sessionID;
-
 @property(nonatomic, strong, readwrite) WrangledBrowser* mainInterface;
 @property(nonatomic, strong, readwrite) WrangledBrowser* incognitoInterface;
 
@@ -171,16 +168,6 @@
 
 #pragma mark - BrowserViewInformation property implementations
 
-- (NSString*)sessionID {
-  NSString* sessionID = nil;
-  if (IsMultiwindowSupported()) {
-    if (@available(iOS 13, *)) {
-      sessionID = _sceneState.scene.session.persistentIdentifier;
-    }
-  }
-  return sessionID;
-}
-
 - (void)setCurrentInterface:(WrangledBrowser*)interface {
   DCHECK(interface);
   // |interface| must be one of the interfaces this class already owns.
@@ -325,6 +312,11 @@
   DCHECK(!_isShutdown);
   _isShutdown = YES;
 
+  [self.mainBrowser->GetCommandDispatcher() prepareForShutdown];
+  if ([self hasIncognitoInterface]) {
+    [self.otrBrowser->GetCommandDispatcher() prepareForShutdown];
+  }
+
   // At this stage, new BrowserCoordinators shouldn't be lazily constructed by
   // calling their property getters.
   [_mainBrowserCoordinator stop];
@@ -415,7 +407,7 @@
 - (void)setSessionIDForBrowser:(Browser*)browser
                 restoreSession:(BOOL)restoreSession {
   SnapshotBrowserAgent::FromBrowser(browser)->SetSessionID(
-      base::SysNSStringToUTF8(self.sessionID));
+      base::SysNSStringToUTF8(_sceneState.sceneSessionID));
 
   SessionRestorationBrowserAgent* restorationAgent =
       SessionRestorationBrowserAgent::FromBrowser(browser);
@@ -443,12 +435,13 @@
     NSString* previousSessionID =
         _sceneState.appState.previousSingleWindowSessionID;
     if (previousSessionID &&
-        ![self.sessionID isEqualToString:previousSessionID]) {
+        ![_sceneState.sceneSessionID isEqualToString:previousSessionID]) {
       restorationAgent->SetSessionID(
           base::SysNSStringToUTF8(previousSessionID));
       restorationAgent->RestoreSession();
 
-      restorationAgent->SetSessionID(base::SysNSStringToUTF8(self.sessionID));
+      restorationAgent->SetSessionID(
+          base::SysNSStringToUTF8(_sceneState.sceneSessionID));
       restorationAgent->SaveSession(true);
 
       // Fallback to the normal codepath. It will set the session identifier
@@ -458,7 +451,8 @@
     }
   }
 
-  restorationAgent->SetSessionID(base::SysNSStringToUTF8(self.sessionID));
+  restorationAgent->SetSessionID(
+      base::SysNSStringToUTF8(_sceneState.sceneSessionID));
   if (restoreSession)
     restorationAgent->RestoreSession();
 }
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index ea407ba..5f3c18a 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -348,7 +348,7 @@
         // Add the scene to the list of connected scene, to restore in case of
         // crashes.
         [[PreviousSessionInfo sharedInstance]
-            addSceneSessionID:sceneState.scene.session.persistentIdentifier];
+            addSceneSessionID:sceneState.sceneSessionID];
       }
     }
   }
@@ -396,8 +396,7 @@
           // If Multiple scenes are not supported, the session shouldn't be
           // removed as it can be used for normal restoration.
           [[PreviousSessionInfo sharedInstance]
-              removeSceneSessionID:sceneState.scene.session
-                                       .persistentIdentifier];
+              removeSceneSessionID:sceneState.sceneSessionID];
         }
       }
     }
diff --git a/ios/chrome/browser/ui/main/scene_state.h b/ios/chrome/browser/ui/main/scene_state.h
index 2dce073f..b9ece91 100644
--- a/ios/chrome/browser/ui/main/scene_state.h
+++ b/ios/chrome/browser/ui/main/scene_state.h
@@ -76,7 +76,8 @@
 @property(nonatomic, strong, readonly) id<BrowserInterfaceProvider>
     interfaceProvider;
 
-// The persistent identifier for the scene session.
+// The persistent identifier for the scene session. This should be used instead
+// of -[UISceneSession persistentIdentifier].
 @property(nonatomic, readonly) NSString* sceneSessionID;
 
 // True if First Run UI (terms of service & sync sign-in) is being presented
diff --git a/ios/chrome/browser/ui/main/scene_state.mm b/ios/chrome/browser/ui/main/scene_state.mm
index 7af9df66..86b0a12 100644
--- a/ios/chrome/browser/ui/main/scene_state.mm
+++ b/ios/chrome/browser/ui/main/scene_state.mm
@@ -130,7 +130,9 @@
 - (NSString*)sceneSessionID {
   NSString* sessionID = nil;
   if (@available(ios 13, *)) {
-    sessionID = _scene.session.persistentIdentifier;
+    if (IsMultiwindowSupported()) {
+      sessionID = _scene.session.persistentIdentifier;
+    }
   }
   return sessionID;
 }
diff --git a/media/capture/BUILD.gn b/media/capture/BUILD.gn
index 153627e..8d0147a 100644
--- a/media/capture/BUILD.gn
+++ b/media/capture/BUILD.gn
@@ -190,6 +190,8 @@
       "video/mac/video_capture_device_factory_mac.mm",
       "video/mac/video_capture_device_mac.h",
       "video/mac/video_capture_device_mac.mm",
+      "video/mac/video_capture_metrics_mac.h",
+      "video/mac/video_capture_metrics_mac.mm",
     ]
     deps += [
       "//services/video_capture/public/uma",
@@ -493,6 +495,7 @@
       "video/mac/video_capture_device_avfoundation_mac_unittest.mm",
       "video/mac/video_capture_device_factory_mac_unittest.mm",
       "video/mac/video_capture_device_mac_unittest.mm",
+      "video/mac/video_capture_metrics_mac_unittest.mm",
     ]
     frameworks = [
       "AVFoundation.framework",
@@ -500,6 +503,7 @@
       "CoreVideo.framework",
       "IOSurface.framework",
     ]
+    deps += [ "//third_party/ocmock" ]
   }
 
   if (is_win) {
diff --git a/media/capture/video/mac/DEPS b/media/capture/video/mac/DEPS
index 577e795b..b17486db 100644
--- a/media/capture/video/mac/DEPS
+++ b/media/capture/video/mac/DEPS
@@ -2,3 +2,9 @@
   "+third_party/decklink",
   "+services/video_capture/public/uma",
 ]
+
+specific_include_rules = {
+"video_capture_metrics_mac_unittest.mm": [
+  "+third_party/ocmock"
+]
+}
diff --git a/media/capture/video/mac/video_capture_device_avfoundation_mac.h b/media/capture/video/mac/video_capture_device_avfoundation_mac.h
index 120f8c6..4e8f82c6 100644
--- a/media/capture/video/mac/video_capture_device_avfoundation_mac.h
+++ b/media/capture/video/mac/video_capture_device_avfoundation_mac.h
@@ -53,6 +53,7 @@
   base::Lock _lock;
   media::VideoCaptureDeviceAVFoundationFrameReceiver* _frameReceiver
       GUARDED_BY(_lock);  // weak.
+  bool _capturedFirstFrame GUARDED_BY(_lock);
 
   base::scoped_nsobject<AVCaptureSession> _captureSession;
 
diff --git a/media/capture/video/mac/video_capture_device_avfoundation_mac.mm b/media/capture/video/mac/video_capture_device_avfoundation_mac.mm
index 1ea68ad..f7574a1 100644
--- a/media/capture/video/mac/video_capture_device_avfoundation_mac.mm
+++ b/media/capture/video/mac/video_capture_device_avfoundation_mac.mm
@@ -23,6 +23,7 @@
 #import "media/capture/video/mac/video_capture_device_avfoundation_utils_mac.h"
 #include "media/capture/video/mac/video_capture_device_factory_mac.h"
 #include "media/capture/video/mac/video_capture_device_mac.h"
+#import "media/capture/video/mac/video_capture_metrics_mac.h"
 #include "media/capture/video_capture_types.h"
 #include "services/video_capture/public/uma/video_capture_service_event.h"
 #include "ui/gfx/geometry/size.h"
@@ -162,6 +163,7 @@
                               DISPATCH_QUEUE_SERIAL),
         base::scoped_policy::ASSUME);
     DCHECK(frameReceiver);
+    _capturedFirstFrame = false;
     _weakPtrFactoryForTakePhoto =
         std::make_unique<base::WeakPtrFactory<VideoCaptureDeviceAVFoundation>>(
             self);
@@ -350,6 +352,8 @@
     }
   }
 
+  base::AutoLock lock(_lock);
+  _capturedFirstFrame = false;
   return YES;
 }
 
@@ -692,6 +696,10 @@
     return;
 
   const base::TimeDelta timestamp = GetCMSampleBufferTimestamp(sampleBuffer);
+  bool logUma = !std::exchange(_capturedFirstFrame, true);
+  if (logUma) {
+    media::LogFirstCapturedVideoFrame(_bestCaptureFormat, sampleBuffer);
+  }
 
   // The SampleBufferTransformer CHECK-crashes if the sample buffer is not MJPEG
   // and does not have a pixel buffer (https://crbug.com/1160647) so we fall
diff --git a/media/capture/video/mac/video_capture_metrics_mac.h b/media/capture/video/mac/video_capture_metrics_mac.h
new file mode 100644
index 0000000..c56789a
--- /dev/null
+++ b/media/capture/video/mac/video_capture_metrics_mac.h
@@ -0,0 +1,23 @@
+// Copyright 2021 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 MEDIA_CAPTURE_VIDEO_MAC_VIDEO_CAPTURE_METRICS_MAC_H_
+#define MEDIA_CAPTURE_VIDEO_MAC_VIDEO_CAPTURE_METRICS_MAC_H_
+
+#import <AVFoundation/AVFoundation.h>
+#include <CoreMedia/CoreMedia.h>
+#import <Foundation/Foundation.h>
+
+#include "media/capture/capture_export.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace media {
+
+CAPTURE_EXPORT
+void LogFirstCapturedVideoFrame(const AVCaptureDeviceFormat* bestCaptureFormat,
+                                const CMSampleBufferRef buffer);
+
+}  // namespace media
+
+#endif  // MEDIA_CAPTURE_VIDEO_MAC_VIDEO_CAPTURE_METRICS_MAC_H_
\ No newline at end of file
diff --git a/media/capture/video/mac/video_capture_metrics_mac.mm b/media/capture/video/mac/video_capture_metrics_mac.mm
new file mode 100644
index 0000000..9c6a64d
--- /dev/null
+++ b/media/capture/video/mac/video_capture_metrics_mac.mm
@@ -0,0 +1,88 @@
+// Copyright 2021 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 "media/capture/video/mac/video_capture_metrics_mac.h"
+
+#include "base/metrics/histogram_functions.h"
+#import "media/capture/video/mac/video_capture_device_avfoundation_mac.h"
+#include "media/capture/video/video_capture_device_info.h"
+
+namespace media {
+
+namespace {
+
+enum class ResolutionComparison {
+  kWidthGtHeightEq = 0,
+  kWidthLtHeightEq = 1,
+  kWidthEqHeightGt = 2,
+  kWidthEqHeightLt = 3,
+  kEq = 4,
+  kWidthGtHeightGt = 5,
+  kWidthLtHeightGt = 6,
+  kWidthGtHeightLt = 7,
+  kWidthLtHeightLt = 8,
+  kMaxValue = kWidthLtHeightLt,
+};
+
+ResolutionComparison CompareDimensions(const CMVideoDimensions& requested,
+                                       const CMVideoDimensions& captured) {
+  if (requested.width > captured.width) {
+    if (requested.height > captured.height)
+      return ResolutionComparison::kWidthGtHeightGt;
+    if (requested.height < captured.height)
+      return ResolutionComparison::kWidthGtHeightLt;
+    return ResolutionComparison::kWidthGtHeightEq;
+  } else if (requested.width < captured.width) {
+    if (requested.height > captured.height)
+      return ResolutionComparison::kWidthLtHeightGt;
+    if (requested.height < captured.height)
+      return ResolutionComparison::kWidthLtHeightLt;
+    return ResolutionComparison::kWidthLtHeightEq;
+  } else {
+    if (requested.height > captured.height)
+      return ResolutionComparison::kWidthEqHeightGt;
+    if (requested.height < captured.height)
+      return ResolutionComparison::kWidthEqHeightLt;
+    return ResolutionComparison::kEq;
+  }
+}
+
+}  // namespace
+
+void LogFirstCapturedVideoFrame(const AVCaptureDeviceFormat* bestCaptureFormat,
+                                const CMSampleBufferRef buffer) {
+  if (bestCaptureFormat) {
+    const CMFormatDescriptionRef requestedFormat =
+        [bestCaptureFormat formatDescription];
+    base::UmaHistogramEnumeration(
+        "Media.VideoCapture.Mac.Device.RequestedPixelFormat",
+        [VideoCaptureDeviceAVFoundation
+            FourCCToChromiumPixelFormat:CMFormatDescriptionGetMediaSubType(
+                                            requestedFormat)],
+        media::VideoPixelFormat::PIXEL_FORMAT_MAX);
+
+    if (buffer) {
+      const CMFormatDescriptionRef capturedFormat =
+          CMSampleBufferGetFormatDescription(buffer);
+      base::UmaHistogramBoolean(
+          "Media.VideoCapture.Mac.Device.CapturedWithRequestedPixelFormat",
+          CMFormatDescriptionGetMediaSubType(capturedFormat) ==
+              CMFormatDescriptionGetMediaSubType(requestedFormat));
+      base::UmaHistogramEnumeration(
+          "Media.VideoCapture.Mac.Device.CapturedWithRequestedResolution",
+          CompareDimensions(
+              CMVideoFormatDescriptionGetDimensions(requestedFormat),
+              CMVideoFormatDescriptionGetDimensions(capturedFormat)));
+
+      const CVPixelBufferRef pixelBufferRef =
+          CMSampleBufferGetImageBuffer(buffer);
+      bool is_io_sufrace =
+          pixelBufferRef && CVPixelBufferGetIOSurface(pixelBufferRef);
+      base::UmaHistogramBoolean(
+          "Media.VideoCapture.Mac.Device.CapturedIOSurface", is_io_sufrace);
+    }
+  }
+}
+
+}  // namespace media
\ No newline at end of file
diff --git a/media/capture/video/mac/video_capture_metrics_mac_unittest.mm b/media/capture/video/mac/video_capture_metrics_mac_unittest.mm
new file mode 100644
index 0000000..205b20e
--- /dev/null
+++ b/media/capture/video/mac/video_capture_metrics_mac_unittest.mm
@@ -0,0 +1,87 @@
+// Copyright 2021 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 "media/capture/video/mac/video_capture_metrics_mac.h"
+
+#import <AVFoundation/AVFoundation.h>
+#include <CoreMedia/CoreMedia.h>
+#import <Foundation/Foundation.h>
+
+#include "base/mac/scoped_cftyperef.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "media/base/video_types.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#include "third_party/ocmock/gtest_support.h"
+
+namespace media {
+
+namespace {}  // namespace
+
+TEST(VideoCaptureMetricsMacTest, NoMetricsLoggedIfNullRequestedCaptureFormat) {
+  base::HistogramTester histogram_tester;
+  LogFirstCapturedVideoFrame(nullptr, nullptr);
+  EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix("Media."),
+              testing::IsEmpty());
+}
+
+TEST(VideoCaptureMetricsMacTest, LogRequestedPixelFormat) {
+  base::HistogramTester histogram_tester;
+
+  base::ScopedCFTypeRef<CMFormatDescriptionRef> requested_format;
+  OSStatus status = CMVideoFormatDescriptionCreate(
+      kCFAllocatorDefault,
+      kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange /*NV12*/, 320, 180,
+      nullptr, requested_format.InitializeInto());
+  ASSERT_EQ(0, status);
+  id capture_format = OCMClassMock([AVCaptureDeviceFormat class]);
+  OCMStub([capture_format formatDescription]).andReturn(requested_format.get());
+
+  LogFirstCapturedVideoFrame(capture_format, nullptr);
+  EXPECT_THAT(histogram_tester.GetAllSamples(
+                  "Media.VideoCapture.Mac.Device.RequestedPixelFormat"),
+              testing::UnorderedElementsAre(
+                  base::Bucket(VideoPixelFormat::PIXEL_FORMAT_NV12, 1)));
+}
+
+TEST(VideoCaptureMetricsMacTest, LogFirstFrameWhenAsRequested) {
+  base::HistogramTester histogram_tester;
+
+  base::ScopedCFTypeRef<CMFormatDescriptionRef> requested_format;
+  OSStatus status = CMVideoFormatDescriptionCreate(
+      kCFAllocatorDefault,
+      kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange /*NV12*/, 320, 180,
+      nullptr, requested_format.InitializeInto());
+  ASSERT_EQ(0, status);
+  id capture_format = OCMClassMock([AVCaptureDeviceFormat class]);
+  OCMStub([capture_format formatDescription]).andReturn(requested_format.get());
+
+  // First frame equal.
+  base::ScopedCFTypeRef<CMSampleBufferRef> first_frame;
+  status = CMSampleBufferCreate(kCFAllocatorDefault, nullptr, false, nullptr,
+                                nullptr, requested_format, 0, 0, nullptr, 0,
+                                nullptr, first_frame.InitializeInto());
+  ASSERT_EQ(0, status);
+
+  LogFirstCapturedVideoFrame(capture_format, first_frame);
+
+  EXPECT_THAT(histogram_tester.GetAllSamples(
+                  "Media.VideoCapture.Mac.Device.RequestedPixelFormat"),
+              testing::UnorderedElementsAre(
+                  base::Bucket(VideoPixelFormat::PIXEL_FORMAT_NV12, 1)));
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples(
+          "Media.VideoCapture.Mac.Device.CapturedWithRequestedPixelFormat"),
+      testing::UnorderedElementsAre(base::Bucket(1, 1)));
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples(
+          "Media.VideoCapture.Mac.Device.CapturedWithRequestedResolution"),
+      testing::UnorderedElementsAre(base::Bucket(4, 1)));
+  EXPECT_THAT(histogram_tester.GetAllSamples(
+                  "Media.VideoCapture.Mac.Device.CapturedIOSurface"),
+              testing::UnorderedElementsAre(base::Bucket(0, 1)));
+}
+
+}  // namespace media
diff --git a/net/dns/dns_config_service.cc b/net/dns/dns_config_service.cc
index 88ba349a..3ae325c 100644
--- a/net/dns/dns_config_service.cc
+++ b/net/dns/dns_config_service.cc
@@ -70,8 +70,8 @@
   NOTREACHED();
 }
 
-DnsConfigService::Watcher::Watcher(DnsConfigService* service)
-    : service_(service) {}
+DnsConfigService::Watcher::Watcher(DnsConfigService& service)
+    : service_(&service) {}
 
 DnsConfigService::Watcher::~Watcher() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -92,31 +92,41 @@
 }
 
 DnsConfigService::HostsReader::HostsReader(
-    DnsConfigService* service,
-    base::FilePath::StringPieceType hosts_file_path)
-    : service_(service), hosts_file_path_(hosts_file_path) {}
+    base::FilePath::StringPieceType hosts_file_path,
+    DnsConfigService& service)
+    : service_(&service), hosts_file_path_(hosts_file_path) {}
 
 DnsConfigService::HostsReader::~HostsReader() = default;
 
-bool DnsConfigService::HostsReader::ReadHosts(DnsHosts* out_dns_hosts) {
+base::Optional<DnsHosts> DnsConfigService::HostsReader::ReadHosts() {
   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                 base::BlockingType::MAY_BLOCK);
   DCHECK(!hosts_file_path_.empty());
-  return ParseHostsFile(hosts_file_path_, out_dns_hosts);
+  DnsHosts dns_hosts;
+  if (!ParseHostsFile(hosts_file_path_, &dns_hosts))
+    return base::nullopt;
+
+  return dns_hosts;
 }
 
-bool DnsConfigService::HostsReader::AddAdditionalHostsTo(DnsHosts& dns_hosts) {
+bool DnsConfigService::HostsReader::AddAdditionalHostsTo(
+    DnsHosts& in_out_dns_hosts) {
   // Nothing to add in base implementation.
   return true;
 }
 
 void DnsConfigService::HostsReader::DoWork() {
-  success_ = ReadHosts(&hosts_) && AddAdditionalHostsTo(hosts_);
+  hosts_ = ReadHosts();
+  if (!hosts_.has_value())
+    return;
+
+  if (!AddAdditionalHostsTo(hosts_.value()))
+    hosts_.reset();
 }
 
 void DnsConfigService::HostsReader::OnWorkFinished() {
-  if (success_) {
-    service_->OnHostsRead(hosts_);
+  if (hosts_.has_value()) {
+    service_->OnHostsRead(std::move(hosts_).value());
   } else {
     LOG(WARNING) << "Failed to read DnsHosts.";
   }
@@ -128,7 +138,7 @@
   if (!hosts_reader_) {
     DCHECK(!hosts_file_path_.empty());
     hosts_reader_ =
-        base::MakeRefCounted<HostsReader>(this, hosts_file_path_.value());
+        base::MakeRefCounted<HostsReader>(hosts_file_path_.value(), *this);
   }
   hosts_reader_->WorkNow();
 }
@@ -149,7 +159,7 @@
   StartTimer();
 }
 
-void DnsConfigService::OnConfigRead(const DnsConfig& config) {
+void DnsConfigService::OnConfigRead(DnsConfig config) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(config.IsValid());
 
@@ -163,11 +173,11 @@
     OnCompleteConfig();
 }
 
-void DnsConfigService::OnHostsRead(const DnsHosts& hosts) {
+void DnsConfigService::OnHostsRead(DnsHosts hosts) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (hosts != dns_config_.hosts) {
-    dns_config_.hosts = hosts;
+    dns_config_.hosts = std::move(hosts);
     need_update_ = true;
   }
 
diff --git a/net/dns/dns_config_service.h b/net/dns/dns_config_service.h
index 3e953c8..d674225 100644
--- a/net/dns/dns_config_service.h
+++ b/net/dns/dns_config_service.h
@@ -80,7 +80,7 @@
    public:
     // `service` is expected to own the created Watcher and thus stay valid for
     // the lifetime of the created Watcher.
-    explicit Watcher(DnsConfigService* service);
+    explicit Watcher(DnsConfigService& service);
     virtual ~Watcher();
 
     Watcher(const Watcher&) = delete;
@@ -108,11 +108,13 @@
 
   // Reader of HOSTS files. In this base implementation, uses standard logic
   // appropriate to most platforms to read the HOSTS file located at
-  // `service->GetHostsFilePath()`.
+  // `hosts_file_path`.
   class HostsReader : public SerialWorker {
    public:
-    HostsReader(DnsConfigService* service,
-                base::FilePath::StringPieceType hosts_file_path);
+    // `service` is expected to own the created reader and thus stay valid for
+    // the lifetime of the created reader.
+    HostsReader(base::FilePath::StringPieceType hosts_file_path,
+                DnsConfigService& service);
 
     HostsReader(const HostsReader&) = delete;
     HostsReader& operator=(const HostsReader&) = delete;
@@ -120,19 +122,19 @@
    protected:
     ~HostsReader() override;
 
-    // Reads the HOSTS file and parses to a `DnsHosts`. Returns false on
+    // Reads the HOSTS file and parses to a `DnsHosts`. Returns nullopt on
     // failure. Will be called on a separate blockable ThreadPool thread.
     //
     // Override if needed to implement platform-specific behavior, e.g. for a
     // platform-specific HOSTS format.
-    virtual bool ReadHosts(DnsHosts* out_dns_hosts);
+    virtual base::Optional<DnsHosts> ReadHosts();
 
     // Adds any necessary additional entries to the given `DnsHosts`. Returns
     // false on failure. Will be called on a separate blockable ThreadPool
     // thread.
     //
     // Override if needed to implement platform-specific behavior.
-    virtual bool AddAdditionalHostsTo(DnsHosts& dns_hosts);
+    virtual bool AddAdditionalHostsTo(DnsHosts& in_out_dns_hosts);
 
     // SerialWorker:
     void DoWork() final;
@@ -144,8 +146,7 @@
     // running on worker thread.
     DnsConfigService* const service_;
     // Written in DoWork, read in OnWorkFinished, no locking necessary.
-    DnsHosts hosts_;
-    bool success_ = false;
+    base::Optional<DnsHosts> hosts_;
 
     const base::FilePath hosts_file_path_;
   };
@@ -162,9 +163,9 @@
   void InvalidateHosts();
 
   // Called with new config. |config|.hosts is ignored.
-  void OnConfigRead(const DnsConfig& config);
+  void OnConfigRead(DnsConfig config);
   // Called with new hosts. Rest of the config is assumed unchanged.
-  void OnHostsRead(const DnsHosts& hosts);
+  void OnHostsRead(DnsHosts hosts);
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/net/dns/dns_config_service_posix.cc b/net/dns/dns_config_service_posix.cc
index b50f468..3ab9e10 100644
--- a/net/dns/dns_config_service_posix.cc
+++ b/net/dns/dns_config_service_posix.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <string>
 #include <type_traits>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/files/file.h"
@@ -15,6 +16,7 @@
 #include "base/lazy_instance.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/optional.h"
 #include "base/sequence_checker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/scoped_blocking_call.h"
@@ -143,24 +145,24 @@
 }
 #endif  // defined(OS_ANDROID)
 
-bool ReadDnsConfig(DnsConfig* dns_config) {
+base::Optional<DnsConfig> ReadDnsConfig() {
   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                 base::BlockingType::MAY_BLOCK);
-  dns_config->unhandled_options = false;
+
 #if !defined(OS_ANDROID)
-  bool success = false;
+  base::Optional<DnsConfig> dns_config;
 // TODO(fuchsia): Use res_ninit() when it's implemented on Fuchsia.
 #if defined(OS_OPENBSD) || defined(OS_FUCHSIA)
   // Note: res_ninit in glibc always returns 0 and sets RES_INIT.
   // res_init behaves the same way.
   memset(&_res, 0, sizeof(_res));
   if (res_init() == 0)
-    success = ConvertResStateToDnsConfig(_res, dns_config);
+    dns_config = ConvertResStateToDnsConfig(_res);
 #else  // all other OS_POSIX
   struct __res_state res;
   memset(&res, 0, sizeof(res));
   if (res_ninit(&res) == 0)
-    success = ConvertResStateToDnsConfig(res, dns_config);
+    dns_config = ConvertResStateToDnsConfig(res);
     // Prefer res_ndestroy where available.
 #if defined(OS_APPLE) || defined(OS_FREEBSD)
   res_ndestroy(&res);
@@ -169,26 +171,34 @@
 #endif  // defined(OS_APPLE) || defined(OS_FREEBSD)
 #endif  // defined(OS_OPENBSD)
 
+  if (!dns_config.has_value())
+    return dns_config;
+
 #if defined(OS_MAC)
-  if (!DnsConfigWatcher::CheckDnsConfig(&dns_config->unhandled_options))
-    return false;
+  if (!DnsConfigWatcher::CheckDnsConfig(
+          dns_config->unhandled_options /* out_unhandled_options */)) {
+    return base::nullopt;
+  }
 #endif  // defined(OS_MAC)
   // Override |fallback_period| value to match default setting on Windows.
   dns_config->fallback_period = kDnsDefaultFallbackPeriod;
-  return success;
+  return dns_config;
 #else  // defined(OS_ANDROID)
-  dns_config->nameservers.clear();
+  DnsConfig dns_config;
 
   if (base::android::BuildInfo::GetInstance()->sdk_int() >=
       base::android::SDK_VERSION_MARSHMALLOW) {
-    return net::android::GetDnsServers(&dns_config->nameservers,
-                                       &dns_config->dns_over_tls_active,
-                                       &dns_config->dns_over_tls_hostname);
+    if (net::android::GetDnsServers(&dns_config.nameservers,
+                                    &dns_config.dns_over_tls_active,
+                                    &dns_config.dns_over_tls_hostname)) {
+      return dns_config;
+    }
+    return base::nullopt;
   }
 
   if (IsVpnPresent()) {
-    dns_config->unhandled_options = true;
-    return true;
+    dns_config.unhandled_options = true;
+    return dns_config;
   }
 
   // NOTE(pauljensen): __system_property_get and the net.dns1/2 properties are
@@ -200,25 +210,25 @@
   __system_property_get("net.dns2", property_value);
   std::string dns2_string = property_value;
   if (dns1_string.empty() && dns2_string.empty())
-    return false;
+    return base::nullopt;
 
   IPAddress dns1_address;
   IPAddress dns2_address;
   bool parsed1 = dns1_address.AssignFromIPLiteral(dns1_string);
   bool parsed2 = dns2_address.AssignFromIPLiteral(dns2_string);
   if (!parsed1 && !parsed2)
-    return false;
+    return base::nullopt;
 
   if (parsed1) {
     IPEndPoint dns1(dns1_address, dns_protocol::kDefaultPort);
-    dns_config->nameservers.push_back(dns1);
+    dns_config.nameservers.push_back(dns1);
   }
   if (parsed2) {
     IPEndPoint dns2(dns2_address, dns_protocol::kDefaultPort);
-    dns_config->nameservers.push_back(dns2);
+    dns_config.nameservers.push_back(dns2);
   }
 
-  return true;
+  return dns_config;
 #endif  // !defined(OS_ANDROID)
 }
 
@@ -226,7 +236,7 @@
 
 class DnsConfigServicePosix::Watcher : public DnsConfigService::Watcher {
  public:
-  explicit Watcher(DnsConfigServicePosix* service)
+  explicit Watcher(DnsConfigServicePosix& service)
       : DnsConfigService::Watcher(service) {}
   ~Watcher() override = default;
 
@@ -274,19 +284,18 @@
 // net.dns1 and net.dns2; see #if around ReadDnsConfig above.)
 class DnsConfigServicePosix::ConfigReader : public SerialWorker {
  public:
-  explicit ConfigReader(DnsConfigServicePosix* service)
-      : service_(service), success_(false) {
+  explicit ConfigReader(DnsConfigServicePosix& service) : service_(&service) {
     // Allow execution on another thread; nothing thread-specific about
     // constructor.
     DETACH_FROM_SEQUENCE(sequence_checker_);
   }
 
-  void DoWork() override { success_ = ReadDnsConfig(&dns_config_); }
+  void DoWork() override { dns_config_ = ReadDnsConfig(); }
 
   void OnWorkFinished() override {
     DCHECK(!IsCancelled());
-    if (success_) {
-      service_->OnConfigRead(dns_config_);
+    if (dns_config_.has_value()) {
+      service_->OnConfigRead(std::move(dns_config_).value());
     } else {
       LOG(WARNING) << "Failed to read DnsConfig.";
     }
@@ -300,8 +309,7 @@
   // on worker thread.
   DnsConfigServicePosix* const service_;
   // Written in DoWork, read in OnWorkFinished, no locking necessary.
-  DnsConfig dns_config_;
-  bool success_;
+  base::Optional<DnsConfig> dns_config_;
 
   DISALLOW_COPY_AND_ASSIGN(ConfigReader);
 };
@@ -330,25 +338,24 @@
 bool DnsConfigServicePosix::StartWatching() {
   CreateReader();
   // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139
-  watcher_.reset(new Watcher(this));
+  watcher_ = std::make_unique<Watcher>(*this);
   return watcher_->Watch();
 }
 
 void DnsConfigServicePosix::CreateReader() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!config_reader_);
-  config_reader_ = base::MakeRefCounted<ConfigReader>(this);
+  config_reader_ = base::MakeRefCounted<ConfigReader>(*this);
 }
 
 #if !defined(OS_ANDROID)
-bool ConvertResStateToDnsConfig(const struct __res_state& res,
-                                DnsConfig* dns_config) {
-  DCHECK(dns_config);
+base::Optional<DnsConfig> ConvertResStateToDnsConfig(
+    const struct __res_state& res) {
+  DnsConfig dns_config;
+  dns_config.unhandled_options = false;
 
   if (!(res.options & RES_INIT))
-    return false;
-
-  dns_config->nameservers.clear();
+    return base::nullopt;
 
 #if defined(OS_APPLE) || defined(OS_FREEBSD)
   union res_sockaddr_union addresses[MAXNS];
@@ -360,9 +367,9 @@
     if (!ipe.FromSockAddr(
             reinterpret_cast<const struct sockaddr*>(&addresses[i]),
             sizeof addresses[i])) {
-      return false;
+      return base::nullopt;
     }
-    dns_config->nameservers.push_back(ipe);
+    dns_config.nameservers.push_back(ipe);
   }
 #elif defined(OS_LINUX) || defined(OS_CHROMEOS)
   static_assert(std::extent<decltype(res.nsaddr_list)>() >= MAXNS &&
@@ -383,11 +390,11 @@
       addr = reinterpret_cast<const struct sockaddr*>(res._u._ext.nsaddrs[i]);
       addr_len = sizeof *res._u._ext.nsaddrs[i];
     } else {
-      return false;
+      return base::nullopt;
     }
     if (!ipe.FromSockAddr(addr, addr_len))
-      return false;
-    dns_config->nameservers.push_back(ipe);
+      return base::nullopt;
+    dns_config.nameservers.push_back(ipe);
   }
 #else   // !(defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_APPLE) ||
         // defined(OS_FREEBSD))
@@ -397,22 +404,22 @@
     if (!ipe.FromSockAddr(
             reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]),
             sizeof res.nsaddr_list[i])) {
-      return false;
+      return base::nullopt;
     }
-    dns_config->nameservers.push_back(ipe);
+    dns_config.nameservers.push_back(ipe);
   }
 #endif  // defined(OS_APPLE) || defined(OS_FREEBSD)
 
-  dns_config->search.clear();
+  dns_config.search.clear();
   for (int i = 0; (i < MAXDNSRCH) && res.dnsrch[i]; ++i) {
-    dns_config->search.push_back(std::string(res.dnsrch[i]));
+    dns_config.search.emplace_back(res.dnsrch[i]);
   }
 
-  dns_config->ndots = res.ndots;
-  dns_config->fallback_period = base::TimeDelta::FromSeconds(res.retrans);
-  dns_config->attempts = res.retry;
+  dns_config.ndots = res.ndots;
+  dns_config.fallback_period = base::TimeDelta::FromSeconds(res.retrans);
+  dns_config.attempts = res.retry;
 #if defined(RES_ROTATE)
-  dns_config->rotate = res.options & RES_ROTATE;
+  dns_config.rotate = res.options & RES_ROTATE;
 #endif
 #if !defined(RES_USE_DNSSEC)
   // Some versions of libresolv don't have support for the DO bit. In this
@@ -424,26 +431,26 @@
   // cannot be overwritten by /etc/resolv.conf
   const unsigned kRequiredOptions = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
   if ((res.options & kRequiredOptions) != kRequiredOptions) {
-    dns_config->unhandled_options = true;
-    return true;
+    dns_config.unhandled_options = true;
+    return dns_config;
   }
 
   const unsigned kUnhandledOptions = RES_USEVC | RES_IGNTC | RES_USE_DNSSEC;
   if (res.options & kUnhandledOptions) {
-    dns_config->unhandled_options = true;
-    return true;
+    dns_config.unhandled_options = true;
+    return dns_config;
   }
 
-  if (dns_config->nameservers.empty())
-    return false;
+  if (dns_config.nameservers.empty())
+    return base::nullopt;
 
   // If any name server is 0.0.0.0, assume the configuration is invalid.
   // TODO(szym): Measure how often this happens. http://crbug.com/125599
-  for (unsigned i = 0; i < dns_config->nameservers.size(); ++i) {
-    if (dns_config->nameservers[i].address().IsZero())
-      return false;
+  for (const IPEndPoint& nameserver : dns_config.nameservers) {
+    if (nameserver.address().IsZero())
+      return base::nullopt;
   }
-  return true;
+  return dns_config;
 }
 
 #endif  // !defined(OS_ANDROID)
diff --git a/net/dns/dns_config_service_posix.h b/net/dns/dns_config_service_posix.h
index 3f31069a..16e83422 100644
--- a/net/dns/dns_config_service_posix.h
+++ b/net/dns/dns_config_service_posix.h
@@ -59,11 +59,9 @@
 };
 
 #if !defined(OS_ANDROID)
-// Fills in |dns_config| from |res|. Returns false iff a valid config could not
-// be determined.
-bool NET_EXPORT_PRIVATE
-ConvertResStateToDnsConfig(const struct __res_state& res,
-                           DnsConfig* dns_config);
+// Returns nullopt iff a valid config could not be determined.
+base::Optional<DnsConfig> NET_EXPORT_PRIVATE
+ConvertResStateToDnsConfig(const struct __res_state& res);
 #endif
 
 }  // namespace internal
diff --git a/net/dns/dns_config_service_posix_unittest.cc b/net/dns/dns_config_service_posix_unittest.cc
index da0c68e..af6b58c 100644
--- a/net/dns/dns_config_service_posix_unittest.cc
+++ b/net/dns/dns_config_service_posix_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/cancelable_callback.h"
 #include "base/files/file_util.h"
+#include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/sequenced_task_runner.h"
 #include "base/stl_util.h"
@@ -146,17 +147,16 @@
 
 TEST(DnsConfigServicePosixTest, ConvertResStateToDnsConfig) {
   struct __res_state res;
-  DnsConfig config;
-  EXPECT_FALSE(config.IsValid());
   InitializeResState(&res);
-  ASSERT_TRUE(internal::ConvertResStateToDnsConfig(res, &config));
+  base::Optional<DnsConfig> config = internal::ConvertResStateToDnsConfig(res);
   CloseResState(&res);
-  EXPECT_TRUE(config.IsValid());
+  ASSERT_TRUE(config.has_value());
+  EXPECT_TRUE(config->IsValid());
 
   DnsConfig expected_config;
-  EXPECT_FALSE(expected_config.EqualsIgnoreHosts(config));
+  EXPECT_FALSE(expected_config.EqualsIgnoreHosts(config.value()));
   InitializeExpectedConfig(&expected_config);
-  EXPECT_TRUE(expected_config.EqualsIgnoreHosts(config));
+  EXPECT_TRUE(expected_config.EqualsIgnoreHosts(config.value()));
 }
 
 TEST(DnsConfigServicePosixTest, RejectEmptyNameserver) {
@@ -175,12 +175,11 @@
   res.nsaddr_list[1] = sa;
   res.nscount = 2;
 
-  DnsConfig config;
-  EXPECT_FALSE(internal::ConvertResStateToDnsConfig(res, &config));
+  EXPECT_FALSE(internal::ConvertResStateToDnsConfig(res));
 
   sa.sin_addr.s_addr = 0xDEADBEEF;
   res.nsaddr_list[0] = sa;
-  EXPECT_TRUE(internal::ConvertResStateToDnsConfig(res, &config));
+  EXPECT_TRUE(internal::ConvertResStateToDnsConfig(res));
 }
 
 TEST(DnsConfigServicePosixTest, DestroyWhileJobsWorking) {
diff --git a/net/dns/dns_config_service_win.cc b/net/dns/dns_config_service_win.cc
index e06fb4c..f073bd1 100644
--- a/net/dns/dns_config_service_win.cc
+++ b/net/dns/dns_config_service_win.cc
@@ -74,42 +74,53 @@
 // Convenience for reading values using RegKey.
 class RegistryReader {
  public:
-  explicit RegistryReader(const wchar_t* key) {
+  explicit RegistryReader(const wchar_t key[]) {
     // Ignoring the result. |key_.Valid()| will catch failures.
     key_.Open(HKEY_LOCAL_MACHINE, key, KEY_QUERY_VALUE);
   }
 
   ~RegistryReader() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
 
-  bool ReadString(const wchar_t* name,
-                  DnsSystemSettings::RegString* out) const {
+  base::Optional<DnsSystemSettings::RegString> ReadString(
+      const wchar_t name[]) const {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    out->set = false;
+    DnsSystemSettings::RegString reg_string;
+    reg_string.set = false;
     if (!key_.Valid()) {
       // Assume that if the |key_| is invalid then the key is missing.
-      return true;
+      return reg_string;
     }
-    LONG result = key_.ReadValue(name, &out->value);
+    LONG result = key_.ReadValue(name, &reg_string.value);
     if (result == ERROR_SUCCESS) {
-      out->set = true;
-      return true;
+      reg_string.set = true;
+      return reg_string;
     }
-    return (result == ERROR_FILE_NOT_FOUND);
+
+    if (result == ERROR_FILE_NOT_FOUND)
+      return reg_string;
+
+    return base::nullopt;
   }
 
-  bool ReadDword(const wchar_t* name, DnsSystemSettings::RegDword* out) const {
+  base::Optional<DnsSystemSettings::RegDword> ReadDword(
+      const wchar_t name[]) const {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    out->set = false;
+    DnsSystemSettings::RegDword reg_dword;
+    reg_dword.set = false;
     if (!key_.Valid()) {
       // Assume that if the |key_| is invalid then the key is missing.
-      return true;
+      return reg_dword;
     }
-    LONG result = key_.ReadValueDW(name, &out->value);
+    LONG result = key_.ReadValueDW(name, &reg_dword.value);
     if (result == ERROR_SUCCESS) {
-      out->set = true;
-      return true;
+      reg_dword.set = true;
+      return reg_dword;
     }
-    return (result == ERROR_FILE_NOT_FOUND);
+
+    if (result == ERROR_FILE_NOT_FOUND)
+      return reg_dword;
+
+    return base::nullopt;
   }
 
  private:
@@ -141,22 +152,35 @@
   return out;
 }
 
-bool ReadDevolutionSetting(const RegistryReader& reader,
-                           DnsSystemSettings::DevolutionSetting* setting) {
-  return reader.ReadDword(L"UseDomainNameDevolution", &setting->enabled) &&
-         reader.ReadDword(L"DomainNameDevolutionLevel", &setting->level);
+base::Optional<DnsSystemSettings::DevolutionSetting> ReadDevolutionSetting(
+    const RegistryReader& reader) {
+  DnsSystemSettings::DevolutionSetting setting;
+
+  base::Optional<DnsSystemSettings::RegDword> reg_value =
+      reader.ReadDword(L"UseDomainNameDevolution");
+  if (!reg_value)
+    return base::nullopt;
+  setting.enabled = reg_value.value();
+
+  reg_value = reader.ReadDword(L"DomainNameDevolutionLevel");
+  if (!reg_value)
+    return base::nullopt;
+  setting.level = reg_value.value();
+
+  return setting;
 }
 
 // Reads DnsSystemSettings from IpHelper and registry.
-bool ReadSystemSettings(DnsSystemSettings* settings) {
+base::Optional<DnsSystemSettings> ReadSystemSettings() {
   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                 base::BlockingType::MAY_BLOCK);
-  settings->addresses = ReadIpHelper(GAA_FLAG_SKIP_ANYCAST |
-                                     GAA_FLAG_SKIP_UNICAST |
-                                     GAA_FLAG_SKIP_MULTICAST |
-                                     GAA_FLAG_SKIP_FRIENDLY_NAME);
-  if (!settings->addresses.get())
-    return false;
+  DnsSystemSettings settings;
+
+  settings.addresses =
+      ReadIpHelper(GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_UNICAST |
+                   GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_FRIENDLY_NAME);
+  if (!settings.addresses.get())
+    return base::nullopt;
 
   RegistryReader tcpip_reader(kTcpipPath);
   RegistryReader tcpip6_reader(kTcpip6Path);
@@ -164,76 +188,91 @@
   RegistryReader policy_reader(kPolicyPath);
   RegistryReader primary_dns_suffix_reader(kPrimaryDnsSuffixPath);
 
-  if (!policy_reader.ReadString(L"SearchList", &settings->policy_search_list)) {
-    return false;
+  base::Optional<DnsSystemSettings::RegString> reg_string =
+      policy_reader.ReadString(L"SearchList");
+  if (!reg_string)
+    return base::nullopt;
+  settings.policy_search_list = std::move(reg_string).value();
+
+  reg_string = tcpip_reader.ReadString(L"SearchList");
+  if (!reg_string)
+    return base::nullopt;
+  settings.tcpip_search_list = std::move(reg_string).value();
+
+  reg_string = tcpip_reader.ReadString(L"Domain");
+  if (!reg_string)
+    return base::nullopt;
+  settings.tcpip_domain = std::move(reg_string).value();
+
+  base::Optional<DnsSystemSettings::DevolutionSetting> devolution_setting =
+      ReadDevolutionSetting(policy_reader);
+  if (!devolution_setting)
+    return base::nullopt;
+  settings.policy_devolution = devolution_setting.value();
+
+  devolution_setting = ReadDevolutionSetting(dnscache_reader);
+  if (!devolution_setting)
+    return base::nullopt;
+  settings.dnscache_devolution = devolution_setting.value();
+
+  devolution_setting = ReadDevolutionSetting(tcpip_reader);
+  if (!devolution_setting)
+    return base::nullopt;
+  settings.tcpip_devolution = devolution_setting.value();
+
+  base::Optional<DnsSystemSettings::RegDword> reg_dword =
+      policy_reader.ReadDword(L"AppendToMultiLabelName");
+  if (!reg_dword)
+    return base::nullopt;
+  settings.append_to_multi_label_name = reg_dword.value();
+
+  reg_string = primary_dns_suffix_reader.ReadString(L"PrimaryDnsSuffix");
+  if (!reg_string) {
+    return base::nullopt;
   }
-
-  if (!tcpip_reader.ReadString(L"SearchList", &settings->tcpip_search_list))
-    return false;
-
-  if (!tcpip_reader.ReadString(L"Domain", &settings->tcpip_domain))
-    return false;
-
-  if (!ReadDevolutionSetting(policy_reader, &settings->policy_devolution))
-    return false;
-
-  if (!ReadDevolutionSetting(dnscache_reader, &settings->dnscache_devolution))
-    return false;
-
-  if (!ReadDevolutionSetting(tcpip_reader, &settings->tcpip_devolution))
-    return false;
-
-  if (!policy_reader.ReadDword(L"AppendToMultiLabelName",
-                               &settings->append_to_multi_label_name)) {
-    return false;
-  }
-
-  if (!primary_dns_suffix_reader.ReadString(L"PrimaryDnsSuffix",
-                                            &settings->primary_dns_suffix)) {
-    return false;
-  }
+  settings.primary_dns_suffix = std::move(reg_string).value();
 
   base::win::RegistryKeyIterator nrpt_rules(HKEY_LOCAL_MACHINE, kNrptPath);
   base::win::RegistryKeyIterator cs_nrpt_rules(HKEY_LOCAL_MACHINE,
                                                kControlSetNrptPath);
-  settings->have_name_resolution_policy =
+  settings.have_name_resolution_policy =
       (nrpt_rules.SubkeyCount() > 0 || cs_nrpt_rules.SubkeyCount() > 0);
 
   base::win::RegistryKeyIterator dns_connections(HKEY_LOCAL_MACHINE,
                                                  kDnsConnectionsPath);
   base::win::RegistryKeyIterator dns_connections_proxies(
       HKEY_LOCAL_MACHINE, kDnsConnectionsProxies);
-  settings->have_proxy = (dns_connections.SubkeyCount() > 0 ||
-                          dns_connections_proxies.SubkeyCount() > 0);
+  settings.have_proxy = (dns_connections.SubkeyCount() > 0 ||
+                         dns_connections_proxies.SubkeyCount() > 0);
 
-  return true;
+  return settings;
 }
 
 // Default address of "localhost" and local computer name can be overridden
 // by the HOSTS file, but if it's not there, then we need to fill it in.
-bool AddLocalhostEntries(DnsHosts* hosts) {
+bool AddLocalhostEntriesTo(DnsHosts& in_out_hosts) {
   IPAddress loopback_ipv4 = IPAddress::IPv4Localhost();
   IPAddress loopback_ipv6 = IPAddress::IPv6Localhost();
 
   // This does not override any pre-existing entries from the HOSTS file.
-  hosts->insert(std::make_pair(DnsHostsKey("localhost", ADDRESS_FAMILY_IPV4),
-                               loopback_ipv4));
-  hosts->insert(std::make_pair(DnsHostsKey("localhost", ADDRESS_FAMILY_IPV6),
-                               loopback_ipv6));
+  in_out_hosts.insert(std::make_pair(
+      DnsHostsKey("localhost", ADDRESS_FAMILY_IPV4), loopback_ipv4));
+  in_out_hosts.insert(std::make_pair(
+      DnsHostsKey("localhost", ADDRESS_FAMILY_IPV6), loopback_ipv6));
 
   wchar_t buffer[MAX_PATH];
   DWORD size = MAX_PATH;
-  std::string localname;
-  if (!GetComputerNameExW(ComputerNameDnsHostname, buffer, &size) ||
-      !ParseDomainASCII(buffer, &localname)) {
+  if (!GetComputerNameExW(ComputerNameDnsHostname, buffer, &size))
     return false;
-  }
+  std::string localname = ParseDomainASCII(buffer);
+  if (localname.empty())
+    return false;
   localname = base::ToLowerASCII(localname);
 
   bool have_ipv4 =
-      hosts->count(DnsHostsKey(localname, ADDRESS_FAMILY_IPV4)) > 0;
+      in_out_hosts.count(DnsHostsKey(localname, ADDRESS_FAMILY_IPV4)) > 0;
   bool have_ipv6 =
-      hosts->count(DnsHostsKey(localname, ADDRESS_FAMILY_IPV6)) > 0;
+      in_out_hosts.count(DnsHostsKey(localname, ADDRESS_FAMILY_IPV6)) > 0;
 
   if (have_ipv4 && have_ipv6)
     return true;
@@ -264,10 +303,12 @@
       }
       if (!have_ipv4 && (ipe.GetFamily() == ADDRESS_FAMILY_IPV4)) {
         have_ipv4 = true;
-        (*hosts)[DnsHostsKey(localname, ADDRESS_FAMILY_IPV4)] = ipe.address();
+        in_out_hosts[DnsHostsKey(localname, ADDRESS_FAMILY_IPV4)] =
+            ipe.address();
       } else if (!have_ipv6 && (ipe.GetFamily() == ADDRESS_FAMILY_IPV6)) {
         have_ipv6 = true;
-        (*hosts)[DnsHostsKey(localname, ADDRESS_FAMILY_IPV6)] = ipe.address();
+        in_out_hosts[DnsHostsKey(localname, ADDRESS_FAMILY_IPV6)] =
+            ipe.address();
       }
     }
   }
@@ -282,7 +323,7 @@
 
   ~RegistryWatcher() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
 
-  bool Watch(const wchar_t* key, const CallbackType& callback) {
+  bool Watch(const wchar_t key[], const CallbackType& callback) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     DCHECK(!callback.is_null());
     DCHECK(callback_.is_null());
@@ -336,19 +377,21 @@
 }
 
 void ConfigureSuffixSearch(const DnsSystemSettings& settings,
-                           DnsConfig* config) {
+                           DnsConfig& in_out_config) {
   // SearchList takes precedence, so check it first.
   if (settings.policy_search_list.set) {
-    std::vector<std::string> search;
-    if (ParseSearchList(settings.policy_search_list.value, &search)) {
-      config->search.swap(search);
+    std::vector<std::string> search =
+        ParseSearchList(settings.policy_search_list.value);
+    if (!search.empty()) {
+      in_out_config.search = std::move(search);
       return;
     }
     // Even if invalid, the policy disables the user-specified setting below.
   } else if (settings.tcpip_search_list.set) {
-    std::vector<std::string> search;
-    if (ParseSearchList(settings.tcpip_search_list.value, &search)) {
-      config->search.swap(search);
+    std::vector<std::string> search =
+        ParseSearchList(settings.tcpip_search_list.value);
+    if (!search.empty()) {
+      in_out_config.search = std::move(search);
       return;
     }
   }
@@ -365,15 +408,14 @@
   // The user setting (tcpip_domain) can be configurred at Computer Name in
   // System Settings
   std::string primary_suffix;
-  if ((settings.primary_dns_suffix.set &&
-       ParseDomainASCII(settings.primary_dns_suffix.value, &primary_suffix)) ||
-      (settings.tcpip_domain.set &&
-       ParseDomainASCII(settings.tcpip_domain.value, &primary_suffix))) {
-    // Primary suffix goes in front.
-    config->search.insert(config->search.begin(), primary_suffix);
-  } else {
+  if (settings.primary_dns_suffix.set)
+    primary_suffix = ParseDomainASCII(settings.primary_dns_suffix.value);
+  if (primary_suffix.empty() && settings.tcpip_domain.set)
+    primary_suffix = ParseDomainASCII(settings.tcpip_domain.value);
+  if (primary_suffix.empty())
     return;  // No primary suffix, hence no devolution.
-  }
+  // Primary suffix goes in front.
+  in_out_config.search.insert(in_out_config.search.begin(), primary_suffix);
 
   // Devolution is determined by precedence: policy > dnscache > tcpip.
   // |enabled|: UseDomainNameDevolution and |level|: DomainNameDevolutionLevel
@@ -415,7 +457,7 @@
 
   for (size_t offset = 0; num_dots >= devolution.level.value; --num_dots) {
     offset = primary_suffix.find('.', offset + 1);
-    config->search.push_back(primary_suffix.substr(offset + 1));
+    in_out_config.search.push_back(primary_suffix.substr(offset + 1));
   }
 }
 
@@ -448,39 +490,40 @@
 DnsSystemSettings::~DnsSystemSettings() {
 }
 
-bool ParseDomainASCII(base::WStringPiece widestr, std::string* domain) {
-  DCHECK(domain);
+DnsSystemSettings::DnsSystemSettings(DnsSystemSettings&&) = default;
+DnsSystemSettings& DnsSystemSettings::operator=(DnsSystemSettings&&) = default;
+
+std::string ParseDomainASCII(base::WStringPiece widestr) {
   if (widestr.empty())
-    return false;
+    return "";
 
   // Check if already ASCII.
   if (base::IsStringASCII(base::AsStringPiece16(widestr))) {
-    domain->assign(widestr.begin(), widestr.end());
-    return true;
+    return std::string(widestr.begin(), widestr.end());
   }
 
   // Otherwise try to convert it from IDN to punycode.
   const int kInitialBufferSize = 256;
   url::RawCanonOutputT<base::char16, kInitialBufferSize> punycode;
   if (!url::IDNToASCII(base::as_u16cstr(widestr), widestr.length(), &punycode))
-    return false;
+    return "";
 
   // |punycode_output| should now be ASCII; convert it to a std::string.
   // (We could use UTF16ToASCII() instead, but that requires an extra string
   // copy. Since ASCII is a subset of UTF8 the following is equivalent).
-  bool success = base::UTF16ToUTF8(punycode.data(), punycode.length(), domain);
+  std::string converted;
+  bool success =
+      base::UTF16ToUTF8(punycode.data(), punycode.length(), &converted);
   DCHECK(success);
-  DCHECK(base::IsStringASCII(*domain));
-  return success && !domain->empty();
+  DCHECK(base::IsStringASCII(converted));
+  return converted;
 }
 
-bool ParseSearchList(const std::wstring& value,
-                     std::vector<std::string>* output) {
-  DCHECK(output);
+std::vector<std::string> ParseSearchList(base::WStringPiece value) {
   if (value.empty())
-    return false;
+    return {};
 
-  output->clear();
+  std::vector<std::string> output;
 
   // If the list includes an empty hostname (",," or ", ,"), it is terminated.
   // Although nslookup and network connection property tab ignore such
@@ -490,18 +533,19 @@
            value, L",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
     // Convert non-ASCII to punycode, although getaddrinfo does not properly
     // handle such suffixes.
-    std::string parsed;
-    if (!ParseDomainASCII(t, &parsed))
+    std::string parsed = ParseDomainASCII(t);
+    if (parsed.empty())
       break;
-    output->push_back(parsed);
+    output.push_back(std::move(parsed));
   }
-  return !output->empty();
+  return output;
 }
 
-bool ConvertSettingsToDnsConfig(const DnsSystemSettings& settings,
-                                DnsConfig* config) {
+base::Optional<DnsConfig> ConvertSettingsToDnsConfig(
+    const DnsSystemSettings& settings) {
   bool uses_vpn = false;
-  *config = DnsConfig();
+
+  DnsConfig dns_config;
 
   // Use GetAdapterAddresses to get effective DNS server order and
   // connection-specific DNS suffix. Ignore disconnected and loopback adapters.
@@ -519,7 +563,7 @@
     // previously found, skip processing another adapter.
     if (adapter->OperStatus != IfOperStatusUp ||
         adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK ||
-        !config->nameservers.empty())
+        !dns_config.nameservers.empty())
       continue;
 
     for (const IP_ADAPTER_DNS_SERVER_ADDRESS* address =
@@ -533,9 +577,9 @@
         // Override unset port.
         if (!ipe.port())
           ipe = IPEndPoint(ipe.address(), dns_protocol::kDefaultPort);
-        config->nameservers.push_back(ipe);
+        dns_config.nameservers.push_back(ipe);
       } else {
-        return false;
+        return base::nullopt;
       }
     }
 
@@ -544,34 +588,34 @@
     // |DnsSuffix| stores the effective connection-specific suffix, which is
     // obtained via DHCP (regkey: Tcpip\Parameters\Interfaces\{XXX}\DhcpDomain)
     // or specified by the user (regkey: Tcpip\Parameters\Domain).
-    std::string dns_suffix;
-    if (ParseDomainASCII(adapter->DnsSuffix, &dns_suffix))
-      config->search.push_back(dns_suffix);
+    std::string dns_suffix = ParseDomainASCII(adapter->DnsSuffix);
+    if (!dns_suffix.empty())
+      dns_config.search.push_back(std::move(dns_suffix));
   }
 
-  if (config->nameservers.empty())
-    return false;  // No point continuing.
+  if (dns_config.nameservers.empty())
+    return base::nullopt;  // No point continuing.
 
   // Windows always tries a multi-label name "as is" before using suffixes.
-  config->ndots = 1;
+  dns_config.ndots = 1;
 
   if (!settings.append_to_multi_label_name.set) {
-    config->append_to_multi_label_name = false;
+    dns_config.append_to_multi_label_name = false;
   } else {
-    config->append_to_multi_label_name =
+    dns_config.append_to_multi_label_name =
         (settings.append_to_multi_label_name.value != 0);
   }
 
   if (settings.have_name_resolution_policy) {
     // TODO(szym): only set this to true if NRPT has DirectAccess rules.
-    config->use_local_ipv6 = true;
+    dns_config.use_local_ipv6 = true;
   }
 
   if (settings.have_name_resolution_policy || settings.have_proxy || uses_vpn)
-    config->unhandled_options = true;
+    dns_config.unhandled_options = true;
 
-  ConfigureSuffixSearch(settings, config);
-  return true;
+  ConfigureSuffixSearch(settings, dns_config);
+  return dns_config;
 }
 
 // Watches registry and HOSTS file for changes. Must live on a sequence which
@@ -580,7 +624,7 @@
     : public NetworkChangeNotifier::IPAddressObserver,
       public DnsConfigService::Watcher {
  public:
-  explicit Watcher(DnsConfigServiceWin* service)
+  explicit Watcher(DnsConfigServiceWin& service)
       : DnsConfigService::Watcher(service) {}
   ~Watcher() override { NetworkChangeNotifier::RemoveIPAddressObserver(this); }
 
@@ -648,25 +692,22 @@
 // Reads config from registry and IpHelper. All work performed in ThreadPool.
 class DnsConfigServiceWin::ConfigReader : public SerialWorker {
  public:
-  explicit ConfigReader(DnsConfigServiceWin* service)
-      : service_(service),
-        success_(false) {}
+  explicit ConfigReader(DnsConfigServiceWin& service) : service_(&service) {}
 
  private:
   ~ConfigReader() override {}
 
   void DoWork() override {
-    DnsSystemSettings settings = {};
-    success_ = false;
-    if (ReadSystemSettings(&settings))
-      success_ = ConvertSettingsToDnsConfig(settings, &dns_config_);
+    base::Optional<DnsSystemSettings> settings = ReadSystemSettings();
+    if (settings.has_value())
+      dns_config_ = ConvertSettingsToDnsConfig(settings.value());
   }
 
   void OnWorkFinished() override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     DCHECK(!IsCancelled());
-    if (success_) {
-      service_->OnConfigRead(dns_config_);
+    if (dns_config_.has_value()) {
+      service_->OnConfigRead(std::move(dns_config_).value());
     } else {
       LOG(WARNING) << "Failed to read DnsConfig.";
       // Try again in a while in case DnsConfigWatcher missed the signal.
@@ -678,25 +719,24 @@
 
   DnsConfigServiceWin* service_;
   // Written in DoWork(), read in OnWorkFinished(). No locking required.
-  DnsConfig dns_config_;
-  bool success_;
+  base::Optional<DnsConfig> dns_config_;
 };
 
 // Extension of DnsConfigService::HostsReader that fills in localhost and local
 // computer name if necessary.
 class DnsConfigServiceWin::HostsReader : public DnsConfigService::HostsReader {
  public:
-  explicit HostsReader(DnsConfigServiceWin* service)
-      : DnsConfigService::HostsReader(service, GetHostsPath().value()) {}
+  explicit HostsReader(DnsConfigServiceWin& service)
+      : DnsConfigService::HostsReader(GetHostsPath().value(), service) {}
 
   HostsReader(const HostsReader&) = delete;
   HostsReader& operator=(const HostsReader&) = delete;
 
  protected:
-  bool AddAdditionalHostsTo(DnsHosts& dns_hosts) override {
+  bool AddAdditionalHostsTo(DnsHosts& in_out_dns_hosts) override {
     base::ScopedBlockingCall scoped_blocking_call(
         FROM_HERE, base::BlockingType::MAY_BLOCK);
-    return AddLocalhostEntries(&dns_hosts);
+    return AddLocalhostEntriesTo(in_out_dns_hosts);
   }
 
  private:
@@ -727,11 +767,11 @@
 
 bool DnsConfigServiceWin::StartWatching() {
   if (!config_reader_)
-    config_reader_ = base::MakeRefCounted<ConfigReader>(this);
+    config_reader_ = base::MakeRefCounted<ConfigReader>(*this);
   if (!hosts_reader_)
-    hosts_reader_ = base::MakeRefCounted<HostsReader>(this);
+    hosts_reader_ = base::MakeRefCounted<HostsReader>(*this);
   // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139
-  watcher_.reset(new Watcher(this));
+  watcher_.reset(new Watcher(*this));
   return watcher_->Watch();
 }
 
diff --git a/net/dns/dns_config_service_win.h b/net/dns/dns_config_service_win.h
index 97dcfb6..59967bf 100644
--- a/net/dns/dns_config_service_win.h
+++ b/net/dns/dns_config_service_win.h
@@ -39,17 +39,15 @@
 namespace internal {
 
 // Converts a UTF-16 domain name to ASCII, possibly using punycode.
-// Returns true if the conversion succeeds and output is not empty. In case of
-// failure, |domain| might become dirty.
-bool NET_EXPORT_PRIVATE ParseDomainASCII(base::WStringPiece widestr,
-                                         std::string* domain);
+// Returns empty string on failure.
+std::string NET_EXPORT_PRIVATE ParseDomainASCII(base::WStringPiece widestr);
 
 // Parses |value| as search list (comma-delimited list of domain names) from
-// a registry key and stores it in |out|. Returns true on success. Empty
+// a registry key and stores it in |out|. Returns empty vector on failure. Empty
 // entries (e.g., "chromium.org,,org") terminate the list. Non-ascii hostnames
 // are converted to punycode.
-bool NET_EXPORT_PRIVATE ParseSearchList(const std::wstring& value,
-                                        std::vector<std::string>* out);
+std::vector<std::string> NET_EXPORT_PRIVATE
+ParseSearchList(base::WStringPiece value);
 
 // All relevant settings read from registry and IP Helper. This isolates our
 // logic from system calls and is exposed for unit tests. Keep it an aggregate
@@ -76,6 +74,9 @@
   DnsSystemSettings();
   ~DnsSystemSettings();
 
+  DnsSystemSettings(DnsSystemSettings&&);
+  DnsSystemSettings& operator=(DnsSystemSettings&&);
+
   // Filled in by GetAdapterAddresses. Note that the alternative
   // GetNetworkParams does not include IPv6 addresses.
   std::unique_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> addresses;
@@ -114,11 +115,10 @@
   bool have_proxy = false;
 };
 
-// Fills in |dns_config| from |settings|. Exposed for tests. Returns false if a
-// valid config could not be determined.
-bool NET_EXPORT_PRIVATE
-ConvertSettingsToDnsConfig(const DnsSystemSettings& settings,
-                           DnsConfig* dns_config);
+// Fills in |dns_config| from |settings|. Exposed for tests. Returns nullopt if
+// a valid config could not be determined.
+base::Optional<DnsConfig> NET_EXPORT_PRIVATE
+ConvertSettingsToDnsConfig(const DnsSystemSettings& settings);
 
 // Service for reading and watching Windows system DNS settings. This object is
 // not thread-safe and methods may perform blocking I/O so methods must be
diff --git a/net/dns/dns_config_service_win_unittest.cc b/net/dns/dns_config_service_win_unittest.cc
index 4ab74766..7b99086 100644
--- a/net/dns/dns_config_service_win_unittest.cc
+++ b/net/dns/dns_config_service_win_unittest.cc
@@ -4,11 +4,16 @@
 
 #include "net/dns/dns_config_service_win.h"
 
+#include <string>
+#include <vector>
+
 #include "base/check.h"
 #include "base/memory/free_deleter.h"
+#include "base/optional.h"
 #include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
 #include "net/dns/public/dns_protocol.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace net {
@@ -18,33 +23,22 @@
 TEST(DnsConfigServiceWinTest, ParseSearchList) {
   const struct TestCase {
     const wchar_t* input;
-    const char* output[4];  // NULL-terminated, empty if expected false
+    std::vector<std::string> expected;
   } cases[] = {
-      {L"chromium.org", {"chromium.org", nullptr}},
-      {L"chromium.org,org", {"chromium.org", "org", nullptr}},
+      {L"chromium.org", {"chromium.org"}},
+      {L"chromium.org,org", {"chromium.org", "org"}},
       // Empty suffixes terminate the list
-      {L"crbug.com,com,,org", {"crbug.com", "com", nullptr}},
+      {L"crbug.com,com,,org", {"crbug.com", "com"}},
       // IDN are converted to punycode
       {L"\u017c\xf3\u0142ta.pi\u0119\u015b\u0107.pl,pl",
-       {"xn--ta-4ja03asj.xn--pi-wla5e0q.pl", "pl", nullptr}},
+       {"xn--ta-4ja03asj.xn--pi-wla5e0q.pl", "pl"}},
       // Empty search list is invalid
-      {L"", {nullptr}},
-      {L",,", {nullptr}},
+      {L"", {}},
+      {L",,", {}},
   };
 
   for (const auto& t : cases) {
-    std::vector<std::string> actual_output, expected_output;
-    actual_output.push_back("UNSET");
-    for (const char* const* output = t.output; *output; ++output) {
-      expected_output.push_back(*output);
-    }
-    bool result = internal::ParseSearchList(t.input, &actual_output);
-    if (!expected_output.empty()) {
-      EXPECT_TRUE(result);
-      EXPECT_EQ(expected_output, actual_output);
-    } else {
-      EXPECT_FALSE(result) << "Unexpected parse success on " << t.input;
-    }
+    EXPECT_EQ(internal::ParseSearchList(t.input), t.expected);
   }
 }
 
@@ -183,14 +177,13 @@
       expected_nameservers.push_back(IPEndPoint(ip, port));
     }
 
-    DnsConfig config;
-    bool success = internal::ConvertSettingsToDnsConfig(settings, &config);
+    base::Optional<DnsConfig> config =
+        internal::ConvertSettingsToDnsConfig(settings);
     bool expected_success = !expected_nameservers.empty();
-    EXPECT_EQ(expected_success, success);
-    EXPECT_EQ(expected_nameservers, config.nameservers);
-    if (success) {
-      ASSERT_EQ(1u, config.search.size());
-      EXPECT_EQ(t.expected_suffix, config.search[0]);
+    EXPECT_EQ(expected_success, config.has_value());
+    if (config.has_value()) {
+      EXPECT_EQ(expected_nameservers, config->nameservers);
+      EXPECT_THAT(config->search, testing::ElementsAre(t.expected_suffix));
     }
   }
 }
@@ -211,7 +204,7 @@
       internal::DnsSystemSettings::DevolutionSetting dnscache_devolution;
       internal::DnsSystemSettings::DevolutionSetting tcpip_devolution;
     } input_settings;
-    std::string expected_search[5];
+    std::vector<std::string> expected_search;
   } cases[] = {
       {
           // Policy SearchList override.
@@ -389,13 +382,10 @@
     settings.dnscache_devolution = t.input_settings.dnscache_devolution;
     settings.tcpip_devolution = t.input_settings.tcpip_devolution;
 
-    DnsConfig config;
-    EXPECT_TRUE(internal::ConvertSettingsToDnsConfig(settings, &config));
-    std::vector<std::string> expected_search;
-    for (size_t j = 0; !t.expected_search[j].empty(); ++j) {
-      expected_search.push_back(t.expected_search[j]);
-    }
-    EXPECT_EQ(expected_search, config.search);
+    EXPECT_THAT(
+        internal::ConvertSettingsToDnsConfig(settings),
+        testing::Optional(testing::Field(
+            &DnsConfig::search, testing::ElementsAreArray(t.expected_search))));
   }
 }
 
@@ -416,9 +406,10 @@
     internal::DnsSystemSettings settings;
     settings.addresses = CreateAdapterAddresses(infos);
     settings.append_to_multi_label_name = t.input;
-    DnsConfig config;
-    EXPECT_TRUE(internal::ConvertSettingsToDnsConfig(settings, &config));
-    EXPECT_EQ(t.expected_output, config.append_to_multi_label_name);
+    EXPECT_THAT(
+        internal::ConvertSettingsToDnsConfig(settings),
+        testing::Optional(testing::Field(&DnsConfig::append_to_multi_label_name,
+                                         testing::Eq(t.expected_output))));
   }
 }
 
@@ -441,10 +432,11 @@
     internal::DnsSystemSettings settings;
     settings.addresses = CreateAdapterAddresses(infos);
     settings.have_name_resolution_policy = t.have_nrpt;
-    DnsConfig config;
-    EXPECT_TRUE(internal::ConvertSettingsToDnsConfig(settings, &config));
-    EXPECT_EQ(t.unhandled_options, config.unhandled_options);
-    EXPECT_EQ(t.have_nrpt, config.use_local_ipv6);
+    base::Optional<DnsConfig> config =
+        internal::ConvertSettingsToDnsConfig(settings);
+    ASSERT_TRUE(config.has_value());
+    EXPECT_EQ(t.unhandled_options, config->unhandled_options);
+    EXPECT_EQ(t.have_nrpt, config->use_local_ipv6);
   }
 }
 
@@ -467,9 +459,10 @@
     internal::DnsSystemSettings settings;
     settings.addresses = CreateAdapterAddresses(infos);
     settings.have_proxy = t.have_proxy;
-    DnsConfig config;
-    EXPECT_TRUE(internal::ConvertSettingsToDnsConfig(settings, &config));
-    EXPECT_EQ(t.unhandled_options, config.unhandled_options);
+    EXPECT_THAT(
+        internal::ConvertSettingsToDnsConfig(settings),
+        testing::Optional(testing::Field(&DnsConfig::unhandled_options,
+                                         testing::Eq(t.unhandled_options))));
   }
 }
 
@@ -483,9 +476,9 @@
 
   internal::DnsSystemSettings settings;
   settings.addresses = CreateAdapterAddresses(infos);
-  DnsConfig config;
-  EXPECT_TRUE(internal::ConvertSettingsToDnsConfig(settings, &config));
-  EXPECT_TRUE(config.unhandled_options);
+  EXPECT_THAT(internal::ConvertSettingsToDnsConfig(settings),
+              testing::Optional(testing::Field(&DnsConfig::unhandled_options,
+                                               testing::IsTrue())));
 }
 
 }  // namespace
diff --git a/net/dns/dns_config_watcher_mac.cc b/net/dns/dns_config_watcher_mac.cc
index bb175d7..f48dc70 100644
--- a/net/dns/dns_config_watcher_mac.cc
+++ b/net/dns/dns_config_watcher_mac.cc
@@ -78,9 +78,7 @@
 }
 
 // static
-bool DnsConfigWatcher::CheckDnsConfig(bool* out_unhandled_options) {
-  DCHECK(out_unhandled_options);
-
+bool DnsConfigWatcher::CheckDnsConfig(bool& out_unhandled_options) {
   if (!GetDnsInfoApi().dns_configuration_copy)
     return false;
   std::unique_ptr<dns_config_t, DnsConfigTDeleter> dns_config(
@@ -100,7 +98,7 @@
     ++num_resolvers;
   }
 
-  *out_unhandled_options = num_resolvers > 1;
+  out_unhandled_options = num_resolvers > 1;
   return true;
 }
 
diff --git a/net/dns/dns_config_watcher_mac.h b/net/dns/dns_config_watcher_mac.h
index 9151a52..86dbe5a 100644
--- a/net/dns/dns_config_watcher_mac.h
+++ b/net/dns/dns_config_watcher_mac.h
@@ -17,7 +17,7 @@
   bool Watch(const base::RepeatingCallback<void(bool succeeded)>& callback);
 
   // Returns false iff a valid config could not be determined.
-  static bool CheckDnsConfig(bool* out_unhandled_options);
+  static bool CheckDnsConfig(bool& out_unhandled_options);
 
  private:
   NotifyWatcherMac watcher_;
diff --git a/net/dns/dns_parse_domain_ascii_win_fuzzer.cc b/net/dns/dns_parse_domain_ascii_win_fuzzer.cc
index bc675dc..aa89afb 100644
--- a/net/dns/dns_parse_domain_ascii_win_fuzzer.cc
+++ b/net/dns/dns_parse_domain_ascii_win_fuzzer.cc
@@ -18,9 +18,9 @@
 
   base::StringPiece16 widestr(
       reinterpret_cast<const base::StringPiece16::value_type*>(data), size / 2);
-  std::string result;
+  std::string result = net::internal::ParseDomainASCII(widestr);
 
-  if (net::internal::ParseDomainASCII(widestr, &result))
+  if (!result.empty())
     // Call base::ToLowerASCII to get some additional code coverage signal.
     result = base::ToLowerASCII(result);
 
diff --git a/net/quic/quic_test_packet_maker.cc b/net/quic/quic_test_packet_maker.cc
index b5072c9..fe9e6a2 100644
--- a/net/quic/quic_test_packet_maker.cc
+++ b/net/quic/quic_test_packet_maker.cc
@@ -1194,7 +1194,8 @@
     const std::string& quic_error_details,
     uint64_t frame_type) {
   auto* close_frame = new quic::QuicConnectionCloseFrame(
-      version_.transport_version, quic_error, quic_error_details, frame_type);
+      version_.transport_version, quic_error, quic::NO_IETF_QUIC_ERROR,
+      quic_error_details, frame_type);
   frames_.push_back(quic::QuicFrame(close_frame));
   DVLOG(1) << "Adding frame: " << frames_.back();
 }
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 94d0675..e1bd17dd 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1774,25 +1774,6 @@
             ]
         }
     ],
-    "CodeCacheDeletionWithoutFilter": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "CodeCacheDeletionWithoutFilter"
-                    ]
-                }
-            ]
-        }
-    ],
     "CompositeCrossOriginIframes": [
         {
             "platforms": [
@@ -4172,24 +4153,6 @@
             ]
         }
     ],
-    "MediaFeeds": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "MediaFeeds"
-                    ]
-                }
-            ]
-        }
-    ],
     "MediaFoundationAsyncH264Encoding": [
         {
             "platforms": [
@@ -4220,25 +4183,6 @@
             ]
         }
     ],
-    "MediaHistory": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "UseMediaHistoryStore"
-                    ]
-                }
-            ]
-        }
-    ],
     "MediaLearningSmoothnessExperiment": [
         {
             "platforms": [
@@ -7179,21 +7123,6 @@
             ]
         }
     ],
-    "UrgentDiscardingFromPerformanceManager": [
-        {
-            "platforms": [
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_20200129",
-                    "enable_features": [
-                        "UrgentDiscardingFromPerformanceManager"
-                    ]
-                }
-            ]
-        }
-    ],
     "UseFluentLanguageModel": [
         {
             "platforms": [
@@ -7284,23 +7213,6 @@
             ]
         }
     ],
-    "UserDataSnapshot": [
-        {
-            "platforms": [
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "UserDataSnapshot",
-                    "enable_features": [
-                        "UserDataSnapshot"
-                    ]
-                }
-            ]
-        }
-    ],
     "UsernameFirstFlow": [
         {
             "platforms": [
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 a1dda79..73dad54 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -3092,6 +3092,7 @@
   kAddressSpaceUnknownSecureContextNavigatedToPrivate = 3771,
   kAddressSpaceUnknownNonSecureContextNavigatedToPrivate = 3772,
   kRTCPeerConnectionSdpSemanticsPlanB = 3773,
+  kFetchRespondWithNoResponseWithUsedRequestBody = 3774,
 
   // 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/platform/OWNERS b/third_party/blink/public/platform/OWNERS
index a3af200..50f174c 100644
--- a/third_party/blink/public/platform/OWNERS
+++ b/third_party/blink/public/platform/OWNERS
@@ -8,7 +8,6 @@
 
 per-file web_application_cache_*=pwnall@chromium.org
 per-file web_graphics_context_3d_provider.h=kbr@chromium.org
-per-file web_graphics_context_3d_provider.h=khushalsagar@chromium.org
 per-file web_rtc_*=hbos@chromium.org
 per-file web_media_player*=mlamouri@chromium.org
 
diff --git a/third_party/blink/renderer/core/accessibility/ax_object_cache_base.h b/third_party/blink/renderer/core/accessibility/ax_object_cache_base.h
index d3290f1..1ea08910 100644
--- a/third_party/blink/renderer/core/accessibility/ax_object_cache_base.h
+++ b/third_party/blink/renderer/core/accessibility/ax_object_cache_base.h
@@ -28,7 +28,8 @@
   ~AXObjectCacheBase() override = default;
 
   virtual AXObject* Get(const Node*) = 0;
-  virtual AXObject* GetOrCreate(LayoutObject*) = 0;
+  virtual AXObject* GetOrCreate(LayoutObject*,
+                                AXObject* parent_if_known = nullptr) = 0;
 
  protected:
   AXObjectCacheBase() = default;
diff --git a/third_party/blink/renderer/core/css/container_query_test.cc b/third_party/blink/renderer/core/css/container_query_test.cc
index ac1add2..168602c7 100644
--- a/third_party/blink/renderer/core/css/container_query_test.cc
+++ b/third_party/blink/renderer/core/css/container_query_test.cc
@@ -114,25 +114,45 @@
 }
 
 TEST_F(ContainerQueryTest, ContainerQueryEvaluation) {
-  // TODO(crbug.com/1145970): This test relies on the temporary fixed container
-  // size in ElementRuleCollector::CollectMatchingRulesForList.
   SetBodyInnerHTML(R"HTML(
     <style>
+      #container {
+        contain: size layout;
+        width: 500px;
+        height: 500px;
+      }
+      #container.adjust {
+        width: 600px;
+      }
+
       div { z-index:1; }
       /* Should apply: */
       @container (min-width: 500px) {
         div { z-index:2; }
       }
-      /* Should not apply: */
+      /* Should initially not apply: */
       @container (min-width: 600px) {
         div { z-index:3; }
       }
     </style>
-    <div id=div></div>
+    <div id=container>
+      <div id=div></div>
+    </div>
   )HTML");
   Element* div = GetDocument().getElementById("div");
   ASSERT_TRUE(div);
   EXPECT_EQ(2, div->ComputedStyleRef().ZIndex());
+
+  // Check that dependent elements are responsive to changes:
+  Element* container = GetDocument().getElementById("container");
+  ASSERT_TRUE(container);
+  container->setAttribute(html_names::kClassAttr, "adjust");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_EQ(3, div->ComputedStyleRef().ZIndex());
+
+  container->setAttribute(html_names::kClassAttr, "");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_EQ(2, div->ComputedStyleRef().ZIndex());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_selector_test.cc b/third_party/blink/renderer/core/css/css_selector_test.cc
index 4ae3dca..777fabc 100644
--- a/third_party/blink/renderer/core/css/css_selector_test.cc
+++ b/third_party/blink/renderer/core/css/css_selector_test.cc
@@ -165,4 +165,25 @@
   EXPECT_TRUE(HasLinkOrVisited(":host-context(:link)"));
 }
 
+TEST(CSSSelector, CueDefaultNamespace) {
+  css_test_helpers::TestStyleSheet sheet;
+
+  sheet.AddCSSRules(R"HTML(
+    @namespace "http://www.w3.org/1999/xhtml";
+    video::cue(b) {}
+  )HTML");
+
+  const CSSSelector& cue_selector =
+      (*sheet.GetRuleSet().CuePseudoRules())[0]->Selector();
+  EXPECT_EQ(cue_selector.GetPseudoType(), CSSSelector::kPseudoCue);
+
+  const CSSSelectorList* cue_arguments = cue_selector.SelectorList();
+  ASSERT_TRUE(cue_arguments);
+  const CSSSelector* vtt_type_selector = cue_arguments->First();
+  ASSERT_TRUE(vtt_type_selector);
+  EXPECT_EQ(vtt_type_selector->TagQName().LocalName(), "b");
+  // Default namespace should not affect VTT node type selector.
+  EXPECT_EQ(vtt_type_selector->TagQName().NamespaceURI(), g_star_atom);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/element_rule_collector.cc b/third_party/blink/renderer/core/css/element_rule_collector.cc
index fe63272..cf4c597 100644
--- a/third_party/blink/renderer/core/css/element_rule_collector.cc
+++ b/third_party/blink/renderer/core/css/element_rule_collector.cc
@@ -206,12 +206,9 @@
     if (auto* container_query = rule_data->GetContainerQuery()) {
       result_.SetDependsOnContainerQueries();
 
-      // TODO(crbug.com/1145970): Propagate actual ContainerQueryEvaluator
-      // instance from the container.
-      // For now a fixed container size of 500x500 is used.
-      auto* eval = MakeGarbageCollected<ContainerQueryEvaluator>(500.0, 500.0);
+      auto* evaluator = style_recalc_context_.cq_evaluator;
 
-      if (!eval->Eval(*container_query)) {
+      if (!evaluator || !evaluator->Eval(*container_query)) {
         rejected++;
         continue;
       }
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
index c9163dc..c385aef 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
@@ -34,7 +34,7 @@
   return range.MakeSubRange(&first, &range.Peek());
 }
 
-bool AtEndIgnoringWhitepace(CSSParserTokenRange range) {
+bool AtEndIgnoringWhitespace(CSSParserTokenRange range) {
   range.ConsumeWhitespace();
   return range.AtEnd();
 }
@@ -427,8 +427,9 @@
   // [1] https://drafts.csswg.org/selectors/#matches
   // [2] https://drafts.csswg.org/selectors/#selector-subject
   base::AutoReset<bool> ignore_namespace(
-      &ignore_default_namespace_, resist_default_namespace_ && !has_q_name &&
-                                      AtEndIgnoringWhitepace(range));
+      &ignore_default_namespace_,
+      ignore_default_namespace_ || (resist_default_namespace_ && !has_q_name &&
+                                    AtEndIgnoringWhitespace(range)));
 
   if (!compound_selector) {
     AtomicString namespace_uri = DetermineNamespace(namespace_prefix);
@@ -699,6 +700,10 @@
     case CSSSelector::kPseudoCue: {
       DisallowPseudoElementsScope scope(this);
       base::AutoReset<bool> inside_compound(&inside_compound_pseudo_, true);
+      base::AutoReset<bool> ignore_namespace(
+          &ignore_default_namespace_,
+          ignore_default_namespace_ ||
+              selector->GetPseudoType() == CSSSelector::kPseudoCue);
 
       std::unique_ptr<CSSSelectorList> selector_list =
           std::make_unique<CSSSelectorList>();
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index 9845ba0..edfed5ff 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -861,9 +861,6 @@
     MatchAllRules(state, collector,
                   matching_behavior != kMatchAllRulesExcludingSMIL);
 
-    if (collector.MatchedResult().DependsOnContainerQueries())
-      state.Style()->SetDependsOnContainerQueries(true);
-
     if (tracker_)
       AddMatchedRulesToTracker(collector);
 
@@ -883,6 +880,9 @@
 
     CascadeAndApplyMatchedProperties(state, cascade);
 
+    if (collector.MatchedResult().DependsOnContainerQueries())
+      state.Style()->SetDependsOnContainerQueries(true);
+
     ApplyCallbackSelectors(state);
 
     // Cache our original display.
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
index a84798e..805e627 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
@@ -1122,4 +1122,41 @@
   EXPECT_FALSE(e->ComputedStyleRef().DependsOnContainerQueries());
 }
 
+// Verify that the ComputedStyle::DependsOnContainerQuery flag does
+// not end up in the MatchedPropertiesCache (MPC).
+TEST_F(StyleResolverTest, DependsOnContainerQueriesMPC) {
+  ScopedCSSContainerQueriesForTest scoped_feature(true);
+
+  GetDocument().documentElement()->setInnerHTML(R"HTML(
+    <style>
+      @container (min-width: 9999999px) {
+        #a { color: green; }
+      }
+    </style>
+    <div id=a></div>
+    <div id=b></div>
+  )HTML");
+
+  // In the above example, both <div id=a> and <div id=b> match the same
+  // rules (i.e. whatever is provided by UA style). The selector inside
+  // the @container rule does ultimately _not_ match <div id=a> (because the
+  // container query evaluates to 'false'), however, it _does_ cause the
+  // ComputedStyle::DependsOnContainerQuery flag to be set on #a.
+  //
+  // We must ensure that we don't add the DependsOnContainerQuery-flagged
+  // style to the MPC, otherwise the subsequent cache hit for #b would result
+  // in the flag being (incorrectly) set for that element.
+
+  UpdateAllLifecyclePhasesForTest();
+
+  auto* a = GetDocument().getElementById("a");
+  auto* b = GetDocument().getElementById("b");
+
+  ASSERT_TRUE(a);
+  ASSERT_TRUE(b);
+
+  EXPECT_TRUE(a->ComputedStyleRef().DependsOnContainerQueries());
+  EXPECT_FALSE(b->ComputedStyleRef().DependsOnContainerQueries());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index c1777f6..a5d55b3c 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -32,6 +32,7 @@
 #include "third_party/blink/public/mojom/frame/color_scheme.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_theme_engine.h"
+#include "third_party/blink/renderer/core/css/container_query_evaluator.h"
 #include "third_party/blink/renderer/core/css/counter_style_map.h"
 #include "third_party/blink/renderer/core/css/css_default_style_sheets.h"
 #include "third_party/blink/renderer/core/css/css_font_family_value.h"
@@ -76,6 +77,8 @@
 #include "third_party/blink/renderer/core/html/html_slot_element.h"
 #include "third_party/blink/renderer/core/html/imports/html_imports_controller.h"
 #include "third_party/blink/renderer/core/html_names.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_size.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/layout/layout_theme.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
@@ -1971,17 +1974,23 @@
   return initial_data_;
 }
 
-void StyleEngine::UpdateStyleAndLayoutTreeForContainer(Element& container) {
+void StyleEngine::UpdateStyleAndLayoutTreeForContainer(
+    Element& container,
+    const LogicalSize& logical_size) {
   DCHECK(!style_recalc_root_.GetRootNode());
   DCHECK(!container.NeedsStyleRecalc());
   DCHECK(!in_container_query_style_recalc_);
 
   base::AutoReset<bool> cq_recalc(&in_container_query_style_recalc_, true);
 
-  // TODO(crbug.com/1145970): Populate this context with a
-  // ContainerQueryEvaluator.
   StyleRecalcContext style_recalc_context;
 
+  PhysicalSize physical_size = ToPhysicalSize(
+      logical_size, container.ComputedStyleRef().GetWritingMode());
+  style_recalc_context.cq_evaluator =
+      MakeGarbageCollected<ContainerQueryEvaluator>(physical_size.width,
+                                                    physical_size.height);
+
   style_recalc_root_.Update(nullptr, &container);
   RecalcStyle({StyleRecalcChange::kRecalcContainerQueryDependent},
               style_recalc_context);
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h
index 8dc67432..7c0ee0d 100644
--- a/third_party/blink/renderer/core/css/style_engine.h
+++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -77,6 +77,7 @@
 class StyleSheetContents;
 class StyleInitialData;
 class ViewportStyleResolver;
+struct LogicalSize;
 
 enum InvalidationScope { kInvalidateCurrentScope, kInvalidateAllScopes };
 
@@ -400,7 +401,8 @@
   void UpdateViewportStyle();
   void UpdateStyleAndLayoutTree();
   // To be called from layout when container queries change for the container.
-  void UpdateStyleAndLayoutTreeForContainer(Element& container);
+  void UpdateStyleAndLayoutTreeForContainer(Element& container,
+                                            const LogicalSize&);
   void RecalcStyle() { RecalcStyle({}, StyleRecalcContext()); }
 
   void ClearEnsuredDescendantStyles(Element& element);
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc
index 26d2267..1801dc36 100644
--- a/third_party/blink/renderer/core/css/style_engine_test.cc
+++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -3792,13 +3792,15 @@
   SetDependsOnContainerQueries(*affected);
 
   unsigned start_count = GetStyleEngine().StyleForElementCount();
-  GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(*container1);
+  GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(*container1,
+                                                        LogicalSize());
 
   // The first span.affected child and #container2
   EXPECT_EQ(2u, GetStyleEngine().StyleForElementCount() - start_count);
 
   start_count = GetStyleEngine().StyleForElementCount();
-  GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(*container2);
+  GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(*container2,
+                                                        LogicalSize());
 
   // Three direct span.affected children, and the two display:none elements.
   EXPECT_EQ(6u, GetStyleEngine().StyleForElementCount() - start_count);
@@ -3845,7 +3847,8 @@
   SetDependsOnContainerQueries(*affected);
 
   unsigned start_count = GetStyleEngine().StyleForElementCount();
-  GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(*container);
+  GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(*container,
+                                                        LogicalSize());
 
   // span.affected is updated because containment does not apply to the display
   // types on the element styled with containment. All marked as affected are
@@ -3881,7 +3884,8 @@
   SetDependsOnContainerQueries(*before);
 
   unsigned start_count = GetStyleEngine().StyleForElementCount();
-  GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(*container);
+  GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(*container,
+                                                        LogicalSize());
 
   EXPECT_EQ(2u, GetStyleEngine().StyleForElementCount() - start_count);
 }
@@ -3910,7 +3914,8 @@
   EXPECT_TRUE(old_inner_style);
 
   unsigned start_count = GetStyleEngine().StyleForElementCount();
-  GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(*container);
+  GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(*container,
+                                                        LogicalSize());
 
   // Input elements mark their InnerEditorElement() style-dirty when they are
   // recalculated. That means the UpdateStyleAndLayoutTreeForContainer() call
diff --git a/third_party/blink/renderer/core/css/style_recalc.h b/third_party/blink/renderer/core/css/style_recalc.h
index 5e1810e..c88f44e8 100644
--- a/third_party/blink/renderer/core/css/style_recalc.h
+++ b/third_party/blink/renderer/core/css/style_recalc.h
@@ -9,6 +9,7 @@
 
 namespace blink {
 
+class ContainerQueryEvaluator;
 class Element;
 class Node;
 class PseudoElement;
@@ -107,6 +108,9 @@
 // TODO(crbug.com/1145970): Populate this class.
 class StyleRecalcContext {
   STACK_ALLOCATED();
+
+ public:
+  ContainerQueryEvaluator* cq_evaluator = nullptr;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc
index 5b8ac42..c3ccabd 100644
--- a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc
@@ -53,6 +53,13 @@
       recorder_(recorder),
       clock_(base::DefaultTickClock::GetInstance()),
       event_name_("Blink.UpdateTime") {
+  // All of these are assumed to have one entry per sub-metric.
+  DCHECK_EQ(base::size(absolute_metric_records_), metrics_data().size());
+  DCHECK_EQ(base::size(current_sample_.sub_metrics_durations),
+            metrics_data().size());
+  DCHECK_EQ(base::size(current_sample_.sub_main_frame_durations),
+            metrics_data().size());
+
   // Record average and worst case for the primary metric.
   primary_metric_.reset();
 
@@ -72,13 +79,13 @@
   const char* const uma_pre_fcp_aggregated_postscript = ".AggregatedPreFCP";
 
   // Populate all the sub-metrics.
-  absolute_metric_records_.ReserveInitialCapacity(kCount);
+  size_t metric_index = 0;
   for (const MetricInitializationData& metric_data : metrics_data()) {
     // Absolute records report the absolute time for each metric per frame.
     // They also aggregate the time spent in each stage between navigation
     // (LocalFrameView resets) and First Contentful Paint.
     // They have an associated UMA too that we own and allocate here.
-    auto& absolute_record = absolute_metric_records_.emplace_back();
+    auto& absolute_record = absolute_metric_records_[metric_index];
     absolute_record.reset();
     absolute_record.pre_fcp_aggregate = base::TimeDelta();
     if (metric_data.has_uma) {
@@ -102,12 +109,9 @@
       absolute_record.uma_aggregate_counter.reset(new CustomCountHistogram(
           aggregated_uma_name.ToString().Utf8().c_str(), 0, 10000000, 50));
     }
-  }
 
-  // Make space in the current sample.
-  current_sample_.sub_metrics_durations.Grow(static_cast<wtf_size_t>(kCount));
-  current_sample_.sub_main_frame_durations.Grow(
-      static_cast<wtf_size_t>(kCount));
+    metric_index++;
+  }
 }
 
 LocalFrameUkmAggregator::~LocalFrameUkmAggregator() {
@@ -198,7 +202,7 @@
   bool is_pre_fcp = (fcp_state_ != kHavePassedFCP);
 
   // Accumulate for UKM and record the UMA
-  DCHECK_LT(metric_index, absolute_metric_records_.size());
+  DCHECK_LT(metric_index, base::size(absolute_metric_records_));
   auto& record = absolute_metric_records_[metric_index];
   record.interval_duration += duration;
   if (in_main_frame_update_)
@@ -421,7 +425,7 @@
 void LocalFrameUkmAggregator::UpdateSample(
     cc::ActiveFrameSequenceTrackers trackers) {
   current_sample_.primary_metric_duration = primary_metric_.interval_duration;
-  for (unsigned i = 0; i < static_cast<unsigned>(kCount); ++i) {
+  for (size_t i = 0; i < metrics_data().size(); ++i) {
     current_sample_.sub_metrics_durations[i] =
         absolute_metric_records_[i].interval_duration;
     current_sample_.sub_main_frame_durations[i] =
@@ -440,7 +444,7 @@
   builder.SetMainFrame(primary_metric_.pre_fcp_aggregate.InMicroseconds());
   primary_metric_.uma_aggregate_counter->CountMicroseconds(
       primary_metric_.pre_fcp_aggregate);
-  for (unsigned i = 0; i < static_cast<unsigned>(kCount); ++i) {
+  for (size_t i = 0; i < metrics_data().size(); ++i) {
     auto& absolute_record = absolute_metric_records_[i];
     if (absolute_record.uma_aggregate_counter) {
       absolute_record.uma_aggregate_counter->CountMicroseconds(
@@ -502,7 +506,7 @@
       current_sample_.primary_metric_duration.InMicroseconds());
   builder.SetMainFrameIsBeforeFCP(fcp_state_ != kHavePassedFCP);
   builder.SetMainFrameReasons(current_sample_.trackers);
-  for (unsigned i = 0; i < static_cast<unsigned>(kCount); ++i) {
+  for (size_t i = 0; i < metrics_data().size(); ++i) {
     switch (static_cast<MetricId>(i)) {
       CASE_FOR_ID(CompositingAssignments, i);
       CASE_FOR_ID(CompositingCommit, i);
@@ -549,13 +553,13 @@
 }
 
 bool LocalFrameUkmAggregator::AllMetricsAreZero() {
-  if (primary_metric_.interval_duration.InMicroseconds())
+  if (!primary_metric_.interval_duration.is_zero())
     return false;
   for (auto& record : absolute_metric_records_) {
-    if (record.interval_duration.InMicroseconds()) {
+    if (!record.interval_duration.is_zero()) {
       return false;
     }
-    if (record.main_frame_duration.InMicroseconds()) {
+    if (!record.main_frame_duration.is_zero()) {
       return false;
     }
   }
diff --git a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
index 7332abcd..13019c9 100644
--- a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
+++ b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
@@ -299,8 +299,8 @@
 
   struct SampleToRecord {
     base::TimeDelta primary_metric_duration;
-    Vector<base::TimeDelta> sub_metrics_durations;
-    Vector<base::TimeDelta> sub_main_frame_durations;
+    std::array<base::TimeDelta, kCount> sub_metrics_durations;
+    std::array<base::TimeDelta, kCount> sub_main_frame_durations;
     cc::ActiveFrameSequenceTrackers trackers;
   };
 
@@ -338,7 +338,7 @@
   // Event and metric data
   const char* const event_name_;
   AbsoluteMetricRecord primary_metric_;
-  Vector<AbsoluteMetricRecord> absolute_metric_records_;
+  std::array<AbsoluteMetricRecord, kCount> absolute_metric_records_;
 
   // The current sample to report. When RecordEvent() is called we
   // check for uniform_random[0,1) < 1 / n where n is the number of frames
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index 7df196b..cc5d3ffc 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -1262,10 +1262,10 @@
   // SVGGraphicsElement::getBBox().
   // NOTE: Markers are not specifically ignored here by SVG 1.1 spec, but we
   // ignore them since stroke-width is ignored (and marker size can depend on
-  // stroke-width). objectBoundingBox is returned local coordinates.
+  // stroke-width). objectBoundingBox is returned in local coordinates and
+  // always unzoomed.
   // The name objectBoundingBox is taken from the SVG 1.1 spec.
   virtual FloatRect ObjectBoundingBox() const;
-  virtual FloatRect StrokeBoundingBox() const;
 
   // Returns the smallest rectangle enclosing all of the painted content
   // respecting clipping, masking, filters, opacity, stroke-width and markers.
@@ -1275,6 +1275,11 @@
   // coordinate space is the viewport space.
   virtual FloatRect VisualRectInLocalSVGCoordinates() const;
 
+  // Like VisualRectInLocalSVGCoordinates() but does not include visual overflow
+  // (name is misleading). May be zoomed (currently only for <foreignObject>,
+  // which represents this via its LocalToSVGParentTransform()).
+  virtual FloatRect StrokeBoundingBox() const;
+
   // This returns the transform applying to the local SVG coordinate space,
   // which combines the CSS transform properties and animation motion transform.
   // See SVGElement::calculateTransform().
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index 7ebc8511..01823b8 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -438,6 +438,17 @@
         CalculateInitialFragmentGeometry(constraint_space, *this);
   }
 
+  if (RuntimeEnabledFeatures::CSSContainerQueriesEnabled() &&
+      IsContainerForContainerQueries()) {
+    if (auto* element = DynamicTo<Element>(GetDOMNode())) {
+      LogicalSize available_size = CalculateChildAvailableSize(
+          constraint_space, *this, fragment_geometry->border_box_size,
+          fragment_geometry->border + fragment_geometry->padding);
+      GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
+          *element, available_size);
+    }
+  }
+
   TextAutosizer::NGLayoutScope text_autosizer_layout_scope(
       box_, fragment_geometry->border_box_size.inline_size);
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h b/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h
index f6f8d0e..9d1dfed 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h
@@ -237,6 +237,10 @@
     return box_->ShouldApplySizeContainment();
   }
 
+  bool IsContainerForContainerQueries() const {
+    return box_->IsContainerForContainerQueries();
+  }
+
   // CSS defines certain cases to synthesize inline block baselines from box.
   // See comments in UseLogicalBottomMarginEdgeForInlineBlockBaseline().
   bool UseBlockEndMarginEdgeForInlineBlockBaseline() const {
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.h b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.h
index 2cddf75..e35a3367 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.h
@@ -72,7 +72,7 @@
   }
   FloatRect StrokeBoundingBox() const override {
     NOT_DESTROYED();
-    return ObjectBoundingBox();
+    return VisualRectInLocalSVGCoordinates();
   }
   FloatRect VisualRectInLocalSVGCoordinates() const override {
     NOT_DESTROYED();
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc
index ecbe99c32..40ddbbf 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc
@@ -387,4 +387,25 @@
   EXPECT_EQ(PhysicalOffset(450, 450), result.PointInInnerNodeFrame());
 }
 
+TEST_F(LayoutSVGForeignObjectTest, BBoxPropagationZoomed) {
+  GetFrame().SetPageZoomFactor(2);
+  SetBodyInnerHTML(R"HTML(
+    <svg>
+      <g>
+        <foreignObject x="6" y="5" width="100" height="50" id="target"/>
+      </g>
+    </svg>
+  )HTML");
+  UpdateAllLifecyclePhasesForTest();
+
+  const auto& target = *GetLayoutObjectByElementId("target");
+  ASSERT_EQ(target.StyleRef().EffectiveZoom(), 2);
+
+  EXPECT_EQ(target.ObjectBoundingBox(), FloatRect(6, 5, 100, 50));
+  EXPECT_EQ(target.StrokeBoundingBox(), FloatRect(12, 10, 200, 100));
+  const auto& parent_g = *target.Parent();
+  EXPECT_EQ(parent_g.ObjectBoundingBox(), FloatRect(6, 5, 100, 50));
+  EXPECT_EQ(parent_g.StrokeBoundingBox(), FloatRect(6, 5, 100, 50));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/svg/svg_content_container.cc b/third_party/blink/renderer/core/layout/svg/svg_content_container.cc
index 37814c8..9decb97f 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_content_container.cc
+++ b/third_party/blink/renderer/core/layout/svg/svg_content_container.cc
@@ -143,6 +143,16 @@
   return false;
 }
 
+static FloatRect ObjectBoundsForPropagation(const LayoutObject& object) {
+  FloatRect bounds = object.ObjectBoundingBox();
+  // The local-to-parent transform for <foreignObject> contains a zoom inverse,
+  // so we need to apply zoom to the bounding box that we use for propagation to
+  // be in the correct coordinate space.
+  if (IsA<LayoutSVGForeignObject>(object))
+    bounds.Scale(object.StyleRef().EffectiveZoom());
+  return bounds;
+}
+
 bool SVGContentContainer::UpdateBoundingBoxes(bool& object_bounding_box_valid) {
   object_bounding_box_valid = false;
 
@@ -154,8 +164,9 @@
     if (!HasValidBoundingBoxForContainer(*current))
       continue;
     const AffineTransform& transform = current->LocalToSVGParentTransform();
-    UpdateObjectBoundingBox(object_bounding_box, object_bounding_box_valid,
-                            transform.MapRect(current->ObjectBoundingBox()));
+    UpdateObjectBoundingBox(
+        object_bounding_box, object_bounding_box_valid,
+        transform.MapRect(ObjectBoundsForPropagation(*current)));
     stroke_bounding_box.Unite(transform.MapRect(current->StrokeBoundingBox()));
   }
 
diff --git a/third_party/blink/renderer/core/loader/address_space_feature.cc b/third_party/blink/renderer/core/loader/address_space_feature.cc
index 3df2c4a..8848c33 100644
--- a/third_party/blink/renderer/core/loader/address_space_feature.cc
+++ b/third_party/blink/renderer/core/loader/address_space_feature.cc
@@ -30,6 +30,8 @@
 
 #include "third_party/blink/renderer/core/loader/address_space_feature.h"
 
+#include <tuple>
+
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-forward.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
@@ -41,130 +43,104 @@
 using AddressSpace = network::mojom::blink::IPAddressSpace;
 using Feature = mojom::blink::WebFeature;
 
-// Returns the kAddressSpaceLocal* WebFeature enum value corresponding to the
-// given client loading a resource from the local address space, if any.
-base::Optional<Feature> AddressSpaceLocalFeatureForSubresource(
-    AddressSpace client_address_space,
-    bool client_is_secure_context) {
-  switch (client_address_space) {
-    case AddressSpace::kUnknown:
-      return client_is_secure_context
-                 ? Feature::kAddressSpaceUnknownSecureContextEmbeddedLocal
-                 : Feature::kAddressSpaceUnknownNonSecureContextEmbeddedLocal;
-    case AddressSpace::kPublic:
-      return client_is_secure_context
-                 ? Feature::kAddressSpacePublicSecureContextEmbeddedLocal
-                 : Feature::kAddressSpacePublicNonSecureContextEmbeddedLocal;
-    case AddressSpace::kPrivate:
-      return client_is_secure_context
-                 ? Feature::kAddressSpacePrivateSecureContextEmbeddedLocal
-                 : Feature::kAddressSpacePrivateNonSecureContextEmbeddedLocal;
-    case AddressSpace::kLocal:
-      return base::nullopt;  // Local to local is fine, we do not track it.
-  }
+// A key in |kFeatureMap|.
+//
+// Mirrors the arguments to |AddressSpaceFeature()| except for |fetch_type|.
+struct FeatureKey {
+  AddressSpace client_address_space;
+  bool client_is_secure_context;
+  AddressSpace response_address_space;
+};
+
+// FeatureKey instances are comparable for equality.
+bool operator==(const FeatureKey& lhs, const FeatureKey& rhs) {
+  return std::tie(lhs.client_address_space, lhs.client_is_secure_context,
+                  lhs.response_address_space) ==
+         std::tie(rhs.client_address_space, rhs.client_is_secure_context,
+                  rhs.response_address_space);
 }
 
-// Returns the kAddressSpacePrivate* WebFeature enum value corresponding to the
-// given client loading a resource from the private address space, if any.
-base::Optional<Feature> AddressSpacePrivateFeatureForSubresource(
-    AddressSpace client_address_space,
-    bool client_is_secure_context) {
-  switch (client_address_space) {
-    case AddressSpace::kUnknown:
-      return client_is_secure_context
-                 ? Feature::kAddressSpaceUnknownSecureContextEmbeddedPrivate
-                 : Feature::kAddressSpaceUnknownNonSecureContextEmbeddedPrivate;
-    case AddressSpace::kPublic:
-      return client_is_secure_context
-                 ? Feature::kAddressSpacePublicSecureContextEmbeddedPrivate
-                 : Feature::kAddressSpacePublicNonSecureContextEmbeddedPrivate;
-    case AddressSpace::kPrivate:
-    case AddressSpace::kLocal:
-      // Private or local to local is fine, we do not track it.
-      return base::nullopt;
-  }
-}
+// An entry in |kFeatureMap|.
+//
+// A single key maps to features for all |fetch_type| values. We could instead
+// have two maps, one for subresources and one for navigations, but they would
+// have the exact same set of keys. Hence it is simpler to have a single map.
+struct FeatureEntry {
+  // The key to this entry.
+  FeatureKey key;
 
-base::Optional<Feature> AddressSpaceFeatureForSubresource(
-    AddressSpace client_address_space,
-    bool client_is_secure_context,
-    AddressSpace resource_address_space) {
-  switch (resource_address_space) {
-    case AddressSpace::kUnknown:
-    case AddressSpace::kPublic:
-      return base::nullopt;
-    case AddressSpace::kPrivate:
-      return AddressSpacePrivateFeatureForSubresource(client_address_space,
-                                                      client_is_secure_context);
-    case AddressSpace::kLocal:
-      return AddressSpaceLocalFeatureForSubresource(client_address_space,
-                                                    client_is_secure_context);
-  }
-}
+  // The corresponding feature for |kSubresource| fetch types.
+  Feature subresource_feature;
 
-// Returns the kAddressSpaceLocal* WebFeature enum value corresponding to the
-// given client loading a resource from the local address space, if any.
-base::Optional<Feature> AddressSpaceLocalFeatureForNavigation(
-    AddressSpace client_address_space,
-    bool is_secure_context) {
-  switch (client_address_space) {
-    case AddressSpace::kUnknown:
-      return is_secure_context
-                 ? Feature::kAddressSpaceUnknownSecureContextNavigatedToLocal
-                 : Feature::
-                       kAddressSpaceUnknownNonSecureContextNavigatedToLocal;
-    case AddressSpace::kPublic:
-      return is_secure_context
-                 ? Feature::kAddressSpacePublicSecureContextNavigatedToLocal
-                 : Feature::kAddressSpacePublicNonSecureContextNavigatedToLocal;
-    case AddressSpace::kPrivate:
-      return is_secure_context
-                 ? Feature::kAddressSpacePrivateSecureContextNavigatedToLocal
-                 : Feature::
-                       kAddressSpacePrivateNonSecureContextNavigatedToLocal;
-    case AddressSpace::kLocal:
-      return base::nullopt;  // Local to local is fine, we do not track it.
-  }
-}
+  // The corresponding feature for |kNavigation| fetch types.
+  Feature navigation_feature;
+};
 
-// Returns the kAddressSpacePrivate* WebFeature enum value corresponding to the
-// given client loading a resource from the private address space, if any.
-base::Optional<Feature> AddressSpacePrivateFeatureForNavigation(
-    AddressSpace client_address_space,
-    bool is_secure_context) {
-  switch (client_address_space) {
-    case AddressSpace::kUnknown:
-      return is_secure_context
-                 ? Feature::kAddressSpaceUnknownSecureContextNavigatedToPrivate
-                 : Feature::
-                       kAddressSpaceUnknownNonSecureContextNavigatedToPrivate;
-    case AddressSpace::kPublic:
-      return is_secure_context
-                 ? Feature::kAddressSpacePublicSecureContextNavigatedToPrivate
-                 : Feature::
-                       kAddressSpacePublicNonSecureContextNavigatedToPrivate;
-    case AddressSpace::kPrivate:
-    case AddressSpace::kLocal:
-      // Private or local to local is fine, we do not track it.
-      return base::nullopt;
-  }
-}
+constexpr bool kNonSecureContext = false;
+constexpr bool kSecureContext = true;
 
-base::Optional<Feature> AddressSpaceFeatureForNavigation(
-    AddressSpace client_address_space,
-    bool is_secure_context,
-    AddressSpace response_address_space) {
-  switch (response_address_space) {
-    case AddressSpace::kUnknown:
-    case AddressSpace::kPublic:
-      return base::nullopt;
-    case AddressSpace::kPrivate:
-      return AddressSpacePrivateFeatureForNavigation(client_address_space,
-                                                     is_secure_context);
-    case AddressSpace::kLocal:
-      return AddressSpaceLocalFeatureForNavigation(client_address_space,
-                                                   is_secure_context);
+constexpr struct FeatureEntry kFeatureMap[]{
+    {
+        {AddressSpace::kPrivate, kNonSecureContext, AddressSpace::kLocal},
+        Feature::kAddressSpacePrivateNonSecureContextEmbeddedLocal,
+        Feature::kAddressSpacePrivateNonSecureContextNavigatedToLocal,
+    },
+    {
+        {AddressSpace::kPrivate, kSecureContext, AddressSpace::kLocal},
+        Feature::kAddressSpacePrivateSecureContextEmbeddedLocal,
+        Feature::kAddressSpacePrivateSecureContextNavigatedToLocal,
+    },
+    {
+        {AddressSpace::kPublic, kNonSecureContext, AddressSpace::kLocal},
+        Feature::kAddressSpacePublicNonSecureContextEmbeddedLocal,
+        Feature::kAddressSpacePublicNonSecureContextNavigatedToLocal,
+    },
+    {
+        {AddressSpace::kPublic, kSecureContext, AddressSpace::kLocal},
+        Feature::kAddressSpacePublicSecureContextEmbeddedLocal,
+        Feature::kAddressSpacePublicSecureContextNavigatedToLocal,
+    },
+    {
+        {AddressSpace::kPublic, kNonSecureContext, AddressSpace::kPrivate},
+        Feature::kAddressSpacePublicNonSecureContextEmbeddedPrivate,
+        Feature::kAddressSpacePublicNonSecureContextNavigatedToPrivate,
+    },
+    {
+        {AddressSpace::kPublic, kSecureContext, AddressSpace::kPrivate},
+        Feature::kAddressSpacePublicSecureContextEmbeddedPrivate,
+        Feature::kAddressSpacePublicSecureContextNavigatedToPrivate,
+    },
+    {
+        {AddressSpace::kUnknown, kNonSecureContext, AddressSpace::kLocal},
+        Feature::kAddressSpaceUnknownNonSecureContextEmbeddedLocal,
+        Feature::kAddressSpaceUnknownNonSecureContextNavigatedToLocal,
+    },
+    {
+        {AddressSpace::kUnknown, kSecureContext, AddressSpace::kLocal},
+        Feature::kAddressSpaceUnknownSecureContextEmbeddedLocal,
+        Feature::kAddressSpaceUnknownSecureContextNavigatedToLocal,
+    },
+    {
+        {AddressSpace::kUnknown, kNonSecureContext, AddressSpace::kPrivate},
+        Feature::kAddressSpaceUnknownNonSecureContextEmbeddedPrivate,
+        Feature::kAddressSpaceUnknownNonSecureContextNavigatedToPrivate,
+    },
+    {
+        {AddressSpace::kUnknown, kSecureContext, AddressSpace::kPrivate},
+        Feature::kAddressSpaceUnknownSecureContextEmbeddedPrivate,
+        Feature::kAddressSpaceUnknownSecureContextNavigatedToPrivate,
+    },
+};
+
+// Attempts to find an entry matching |key| in |kFeatureMap|.
+// Returns a pointer to the entry if successful, nullptr otherwise.
+const FeatureEntry* FindFeatureEntry(const FeatureKey& key) {
+  for (const FeatureEntry& entry : kFeatureMap) {
+    if (key == entry.key) {
+      return &entry;
+    }
   }
+  return nullptr;
 }
 
 base::Optional<Feature> AddressSpaceFeature(
@@ -172,15 +148,21 @@
     AddressSpace client_address_space,
     bool client_is_secure_context,
     AddressSpace response_address_space) {
+  FeatureKey key;
+  key.client_address_space = client_address_space;
+  key.client_is_secure_context = client_is_secure_context;
+  key.response_address_space = response_address_space;
+
+  const FeatureEntry* entry = FindFeatureEntry(key);
+  if (!entry) {
+    return base::nullopt;
+  }
+
   switch (fetch_type) {
     case FetchType::kSubresource:
-      return AddressSpaceFeatureForSubresource(client_address_space,
-                                               client_is_secure_context,
-                                               response_address_space);
+      return entry->subresource_feature;
     case FetchType::kNavigation:
-      return AddressSpaceFeatureForNavigation(client_address_space,
-                                              client_is_secure_context,
-                                              response_address_space);
+      return entry->navigation_feature;
   }
 }
 
diff --git a/third_party/blink/renderer/core/loader/address_space_feature.h b/third_party/blink/renderer/core/loader/address_space_feature.h
index 35d5d384..d7fd01a 100644
--- a/third_party/blink/renderer/core/loader/address_space_feature.h
+++ b/third_party/blink/renderer/core/loader/address_space_feature.h
@@ -66,7 +66,7 @@
     FetchType fetch_type,
     network::mojom::blink::IPAddressSpace client_address_space,
     bool client_is_secure_context,
-    network::mojom::blink::IPAddressSpace resource_address_space);
+    network::mojom::blink::IPAddressSpace response_address_space);
 
 // Increments the correct kAddressSpace* WebFeature UseCounter corresponding to
 // the given |client_frame| performing a fetch of type |fetch_type| and
diff --git a/third_party/blink/renderer/modules/accessibility/ax_image_map_link.cc b/third_party/blink/renderer/modules/accessibility/ax_image_map_link.cc
index dfdd18c..3b38ea9 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_image_map_link.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_image_map_link.cc
@@ -50,15 +50,14 @@
   return Traversal<HTMLMapElement>::FirstAncestor(*area);
 }
 
-AXObject* AXImageMapLink::ComputeParent() const {
-  DCHECK(!IsDetached());
-  if (parent_)
-    return parent_;
-
-  if (!MapElement())
-    return nullptr;
-
-  return AXObjectCache().GetOrCreate(MapElement()->GetLayoutObject());
+AXObject* AXImageMapLink::ComputeParentImpl() const {
+  if (MapElement()) {
+    AXObject* ax_parent =
+        AXObjectCache().GetOrCreate(MapElement()->GetLayoutObject());
+    if (ax_parent)
+      return ax_parent;
+  }
+  return AXNodeObject::ComputeParentImpl();
 }
 
 ax::mojom::blink::Role AXImageMapLink::DetermineAccessibilityRole() {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_image_map_link.h b/third_party/blink/renderer/modules/accessibility/ax_image_map_link.h
index e1c75b3c..e7bd0f3 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_image_map_link.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_image_map_link.h
@@ -57,7 +57,7 @@
   Element* ActionElement() const override;
   KURL Url() const override;
   bool IsLinked() const override { return true; }
-  AXObject* ComputeParent() const override;
+  AXObject* ComputeParentImpl() const override;
   void GetRelativeBounds(AXObject** out_container,
                          FloatRect& out_bounds_in_container,
                          SkMatrix44& out_container_transform,
diff --git a/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc b/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc
index 8e05e56..572d4f0 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc
@@ -163,17 +163,6 @@
   return inline_text_box_->GetText();
 }
 
-AXObject* AXInlineTextBox::ComputeParent() const {
-  DCHECK(!IsDetached());
-  if (!inline_text_box_ || !ax_object_cache_)
-    return nullptr;
-  LineLayoutText line_layout_text = inline_text_box_->GetLineLayoutItem();
-  if (!line_layout_text)
-    return nullptr;
-  return ax_object_cache_->GetOrCreate(
-      LineLayoutAPIShim::LayoutObjectFrom(line_layout_text));
-}
-
 // In addition to LTR and RTL direction, edit fields also support
 // top to bottom and bottom to top via the CSS writing-mode property.
 ax::mojom::blink::WritingDirection AXInlineTextBox::GetTextDirection() const {
@@ -211,7 +200,7 @@
   scoped_refptr<AbstractInlineTextBox> next_on_line =
       inline_text_box_->NextOnLine();
   if (next_on_line)
-    return ax_object_cache_->GetOrCreate(next_on_line.get());
+    return AXObjectCache().GetOrCreate(next_on_line.get(), parent_);
 
   return nullptr;
 }
@@ -226,7 +215,7 @@
   scoped_refptr<AbstractInlineTextBox> previous_on_line =
       inline_text_box_->PreviousOnLine();
   if (previous_on_line)
-    return ax_object_cache_->GetOrCreate(previous_on_line.get());
+    return AXObjectCache().GetOrCreate(previous_on_line.get(), parent_);
 
   return nullptr;
 }
@@ -234,6 +223,15 @@
 void AXInlineTextBox::GetDocumentMarkers(
     Vector<DocumentMarker::MarkerType>* marker_types,
     Vector<AXRange>* marker_ranges) const {
+  // TODO(nektar) Address 20% performance degredation and restore code.
+  // It may be necessary to add document markers as part of tree data instead
+  // of computing for every node. To measure current performance, create a
+  // release build without DCHECKs, and then run command similar to:
+  // tools/perf/run_benchmark blink_perf.accessibility   --browser=exact \
+  //   --browser-executable=path/to/chrome --story-filter="accessibility.*"
+  //   --results-label="[my-branch-name]"
+  // Pay attention only to rows with  ProcessDeferredAccessibilityEvents
+  // and RenderAccessibilityImpl::SendPendingAccessibilityEvents.
   if (!RuntimeEnabledFeatures::
           AccessibilityUseAXPositionForDocumentMarkersEnabled())
     return;
@@ -272,6 +270,14 @@
   if (dom_range_start.IsNull() || dom_range_end.IsNull())
     return;
 
+  // TODO(nektar) Figure out why the start > end sometimes.
+  // To see error, comment out below early return and run command similar to:
+  // run_web_tests.py --driver-logging -t linux-debug
+  //   --additional-driver-flag=--force-renderer-accessibility
+  //   external/wpt/css/css-ui/text-overflow-006.html
+  if (dom_range_start > dom_range_end)
+    return;  // Temporary until above TODO is resolved.
+  DCHECK_LE(dom_range_start, dom_range_end);
   const EphemeralRangeInFlatTree dom_range(
       ToPositionInFlatTree(dom_range_start),
       ToPositionInFlatTree(dom_range_end));
@@ -321,8 +327,11 @@
   }
 }
 
-void AXInlineTextBox::Init() {
+void AXInlineTextBox::Init(AXObject* parent) {
   role_ = ax::mojom::blink::Role::kInlineTextBox;
+  DCHECK(parent);
+  SetParent(parent);
+  UpdateCachedAttributeValuesIfNeeded(false);
 }
 
 void AXInlineTextBox::Detach() {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.h b/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.h
index cce5d63..60ad0c8e 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.h
@@ -57,7 +57,6 @@
                          FloatRect& out_bounds_in_container,
                          SkMatrix44& out_container_transform,
                          bool* clips_children = nullptr) const override;
-  AXObject* ComputeParent() const override;
   ax::mojom::blink::WritingDirection GetTextDirection() const override;
   Node* GetNode() const override;
   AXObject* NextOnLine() const override;
@@ -66,7 +65,7 @@
                           Vector<AXRange>* marker_ranges) const override;
 
  protected:
-  void Init() override;
+  void Init(AXObject* parent) override;
   void Detach() override;
   bool IsDetached() const override;
   bool IsAXInlineTextBox() const override;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
index 931603f..ffac50da 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -223,8 +223,10 @@
 }
 
 ax::mojom::blink::Role AXLayoutObject::DetermineAccessibilityRole() {
-  if (!layout_object_)
+  if (!layout_object_) {
+    NOTREACHED();
     return ax::mojom::blink::Role::kUnknown;
+  }
   if (GetCSSAltText(GetNode())) {
     const ComputedStyle* style = GetNode()->GetComputedStyle();
     ContentData* content_data = style->GetContentData();
@@ -282,10 +284,6 @@
   return GetNode();
 }
 
-void AXLayoutObject::Init() {
-  AXNodeObject::Init();
-}
-
 void AXLayoutObject::Detach() {
   AXNodeObject::Detach();
 
@@ -1415,7 +1413,7 @@
         // No visible rendered text -- must be whitespace.
         // Either it is useful whitespace for separating words or not.
         if (layout_text->IsAllCollapsibleWhitespace()) {
-          if (cached_is_ignored_)
+          if (LastKnownIsIgnoredValue())
             return "";
           // If no textboxes, this was whitespace at the line's end.
           text_alternative = " ";
@@ -1527,179 +1525,6 @@
   return result;
 }
 
-//
-// Low-level accessibility tree exploration, only for use within the
-// accessibility module.
-//
-// LAYOUT TREE WALKING ALGORITHM
-//
-// The fundamental types of elements in the Blink layout tree are block
-// elements and inline elements. It can get a little confusing when
-// an inline element has both inline and block children, for example:
-//
-//   <a href="#">
-//     Before Block
-//     <div>
-//       In Block
-//     </div>
-//     Outside Block
-//   </a>
-//
-// Blink wants to maintain the invariant that all of the children of a node
-// are either all block or all inline, so it creates three anonymous blocks:
-//
-//      #1 LayoutBlockFlow (anonymous)
-//        #2 LayoutInline A continuation=#4
-//          #3 LayoutText "Before Block"
-//      #4 LayoutBlockFlow (anonymous) continuation=#8
-//        #5 LayoutBlockFlow DIV
-//          #6 LayoutText "In Block"
-//      #7 LayoutBlockFlow (anonymous)
-//        #8 LayoutInline A is_continuation
-//          #9 LayoutText "Outside Block"
-//
-// For a good explanation of why this is done, see this blog entry. It's
-// describing WebKit in 2007, but the fundamentals haven't changed much.
-//
-// https://webkit.org/blog/115/webcore-rendering-ii-blocks-and-inlines/
-//
-// Now, it's important to understand that we couldn't just use the layout
-// tree as the accessibility tree as-is, because the div is no longer
-// inside the link! In fact, the link has been split into two different
-// nodes, #2 and #8. Luckily, the layout tree contains continuations to
-// help us untangle situations like this.
-//
-// Here's the algorithm we use to walk the layout tree in order to build
-// the accessibility tree:
-//
-// 1. When computing the first child or next sibling of a node, skip over any
-//    LayoutObjects that are continuations.
-//
-// 2. When computing the next sibling of a node and there are no more siblings
-//    in the layout tree, see if the parent node has a continuation, and if
-//    so follow it and make that the next sibling.
-//
-// 3. When computing the first child of a node that has a continuation but
-//    no children in the layout tree, the continuation is the first child.
-//
-// The end result is this tree, which we use as the basis for the
-// accessibility tree.
-//
-//      #1 LayoutBlockFlow (anonymous)
-//        #2 LayoutInline A
-//          #3 LayoutText "Before Block"
-//          #4 LayoutBlockFlow (anonymous)
-//            #5 LayoutBlockFlow DIV
-//              #6 LayoutText "In Block"
-//            #8 LayoutInline A is_continuation
-//              #9 LayoutText "Outside Block"
-//      #7 LayoutBlockFlow (anonymous)
-//
-// This algorithm results in an accessibility tree that preserves containment
-// (i.e. the contents of the link in the example above are descendants of the
-// link node) while including all of the rich layout detail from the layout
-// tree.
-//
-// There are just a couple of other corner cases to walking the layout tree:
-//
-// * Walk tables in table order (thead, tbody, tfoot), which may not match
-//   layout order.
-// * Skip CSS first-letter nodes.
-//
-
-// Given a layout object, return the start of the continuation chain.
-static inline LayoutInline* StartOfContinuations(LayoutObject* layout_object) {
-  // See LAYOUT TREE WALKING ALGORITHM, above, for more context as to why
-  // we need to do this.
-
-  // For inline elements, if it's a continuation, the start of the chain
-  // is always the primary layout object associated with the node.
-  if (layout_object->IsInlineElementContinuation())
-    return To<LayoutInline>(layout_object->GetNode()->GetLayoutObject());
-
-  // Blocks with a previous continuation always have a next continuation,
-  // so we can get the next continuation and do the same trick to get
-  // the primary layout object associated with the node.
-  auto* layout_block_flow = DynamicTo<LayoutBlockFlow>(layout_object);
-  if (layout_block_flow && layout_block_flow->InlineElementContinuation()) {
-    auto* result =
-        To<LayoutInline>(layout_block_flow->InlineElementContinuation()
-                             ->GetNode()
-                             ->GetLayoutObject());
-    DCHECK_NE(result, layout_object);
-    return result;
-  }
-
-  return nullptr;
-}
-
-// See LAYOUT TREE WALKING ALGORITHM, above, for details.
-static inline LayoutObject* ParentLayoutObject(LayoutObject* layout_object) {
-  if (!layout_object)
-    return nullptr;
-
-  // If the node is a continuation, the parent is the start of the continuation
-  // chain.  See LAYOUT TREE WALKING ALGORITHM, above, for more context as to
-  // why we need to do this.
-  LayoutObject* start_of_conts = StartOfContinuations(layout_object);
-  if (start_of_conts)
-    return start_of_conts;
-
-  // Otherwise just return the parent in the layout tree.
-  return layout_object->Parent();
-}
-
-//
-// High-level accessibility tree access.
-//
-
-AXObject* AXLayoutObject::ComputeParent() const {
-  DCHECK(!IsDetached());
-  if (!layout_object_)
-    return nullptr;
-
-  if (AriaRoleAttribute() == ax::mojom::blink::Role::kMenuBar)
-    return AXObjectCache().GetOrCreate(layout_object_->Parent());
-
-  if (GetNode())
-    return AXNodeObject::ComputeParent();
-
-  LayoutObject* parent_layout_obj = ParentLayoutObject(layout_object_);
-  if (parent_layout_obj)
-    return AXObjectCache().GetOrCreate(parent_layout_obj);
-
-  // A WebArea's parent should be the page popup owner, if any, otherwise null.
-  if (IsWebArea()) {
-    LocalFrame* frame = layout_object_->GetFrame();
-    return AXObjectCache().GetOrCreate(frame->PagePopupOwner());
-  }
-
-  return nullptr;
-}
-
-AXObject* AXLayoutObject::ComputeParentIfExists() const {
-  if (!layout_object_)
-    return nullptr;
-
-  if (AriaRoleAttribute() == ax::mojom::blink::Role::kMenuBar)
-    return AXObjectCache().Get(layout_object_->Parent());
-
-  if (GetNode())
-    return AXNodeObject::ComputeParentIfExists();
-
-  LayoutObject* parent_layout_obj = ParentLayoutObject(layout_object_);
-  if (parent_layout_obj)
-    return AXObjectCache().Get(parent_layout_obj);
-
-  // A WebArea's parent should be the page popup owner, if any, otherwise null.
-  if (IsWebArea()) {
-    LocalFrame* frame = layout_object_->GetFrame();
-    return AXObjectCache().Get(frame->PagePopupOwner());
-  }
-
-  return nullptr;
-}
-
 bool AXLayoutObject::CanHaveChildren() const {
   if (!layout_object_)
     return false;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.h b/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
index aefa84c..5770989 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
@@ -76,7 +76,6 @@
   // Overridden from AXObject.
   //
 
-  void Init() override;
   void Detach() override;
   bool IsDetached() const override;
   bool IsAXLayoutObject() const final;
@@ -136,11 +135,6 @@
   // Hit testing.
   AXObject* AccessibilityHitTest(const IntPoint&) const override;
 
-  // High-level accessibility tree access. Other modules should only use these
-  // functions.
-  AXObject* ComputeParent() const override;
-  AXObject* ComputeParentIfExists() const override;
-
   bool CanHaveChildren() const override;
 
   // Notifications that this object may have changed.
diff --git a/third_party/blink/renderer/modules/accessibility/ax_menu_list.cc b/third_party/blink/renderer/modules/accessibility/ax_menu_list.cc
index 52fa5ad..4a6df3e 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_menu_list.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_menu_list.cc
@@ -64,19 +64,31 @@
 
   // There's no reason to clear our AXMenuListPopup child. If we get a
   // call to clearChildren, it's because the options might have changed,
-  // so call it on our popup.
-  DCHECK_EQ(ChildCountIncludingIgnored(), 1);
+  // so call it on our popup. Clearing the AXMenuListPopup child would cause
+  // additional thrashing and events that the AT would need to process,
+  // potentially causing the AT to believe that the popup had closed and a
+  // new popup and reopened.
+  DCHECK_EQ(children_.size(), 1U);
   children_[0]->ClearChildren();
 }
 
 void AXMenuList::AddChildren() {
+#if DCHECK_IS_ON()
   DCHECK(!IsDetached());
+  DCHECK(!is_adding_children_) << " Reentering method on " << GetNode();
+  base::AutoReset<bool> reentrancy_protector(&is_adding_children_, true);
+  DCHECK_EQ(children_.size(), 0U)
+      << "Parent still has " << children_.size() << " children before adding:"
+      << "\nParent is " << ToString(true, true) << "\nFirst child is "
+      << children_[0]->ToString(true, true);
+#endif
   have_children_ = true;
 
   AXObjectCacheImpl& cache = AXObjectCache();
-  AXObject* popup = cache.GetOrCreate(ax::mojom::Role::kMenuListPopup);
+  AXObject* popup =
+      cache.CreateAndInit(ax::mojom::blink::Role::kMenuListPopup, this);
   DCHECK(popup);
-  To<AXMockObject>(popup)->SetParent(this);
+  DCHECK(!popup->IsDetached());
   children_.push_back(popup);
   popup->AddChildren();
 }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_menu_list_option.cc b/third_party/blink/renderer/modules/accessibility/ax_menu_list_option.cc
index decf019e..57e1be17 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_menu_list_option.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_menu_list_option.cc
@@ -57,7 +57,7 @@
   return element_;
 }
 
-AXObject* AXMenuListOption::ComputeParent() const {
+AXObject* AXMenuListOption::ComputeParentImpl() const {
   Node* node = GetNode();
   if (!node)
     return nullptr;
@@ -73,17 +73,20 @@
   if (!menu_list)
     return select_ax_object;
 
-  if (menu_list->HasChildren()) {
-    const auto& child_objects = menu_list->ChildrenIncludingIgnored();
-    if (child_objects.IsEmpty())
-      return nullptr;
-    DCHECK_EQ(child_objects.size(), 1UL);
-    DCHECK(IsA<AXMenuListPopup>(child_objects[0].Get()));
-    To<AXMenuListPopup>(child_objects[0].Get())->UpdateChildrenIfNecessary();
-  } else {
+  if (!menu_list->HasChildren()) {
     menu_list->UpdateChildrenIfNecessary();
   }
-  return parent_.Get();
+
+  const auto& child_objects = menu_list->ChildrenIncludingIgnored();
+  if (child_objects.IsEmpty())
+    return nullptr;
+  DCHECK_EQ(child_objects.size(), 1UL)
+      << "A menulist must have a single popup child";
+  DCHECK(IsA<AXMenuListPopup>(child_objects[0].Get()));
+  To<AXMenuListPopup>(child_objects[0].Get())->UpdateChildrenIfNecessary();
+
+  // Return the popup child, which is the parent of this AXMenuListOption.
+  return child_objects[0];
 }
 
 bool AXMenuListOption::IsVisible() const {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_menu_list_option.h b/third_party/blink/renderer/modules/accessibility/ax_menu_list_option.h
index 1caea39..93aff6c 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_menu_list_option.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_menu_list_option.h
@@ -38,6 +38,7 @@
  public:
   AXMenuListOption(HTMLOptionElement*, AXObjectCacheImpl&);
   ~AXMenuListOption() override;
+  bool IsDetached() const override { return !element_; }
 
  private:
   void Trace(Visitor*) const override;
@@ -46,10 +47,9 @@
 
   Node* GetNode() const override { return element_; }
   void Detach() override;
-  bool IsDetached() const override { return !element_; }
   LocalFrameView* DocumentFrameView() const override;
   bool CanHaveChildren() const override { return false; }
-  AXObject* ComputeParent() const override;
+  AXObject* ComputeParentImpl() const override;
 
   Element* ActionElement() const override;
   bool IsVisible() const override;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_menu_list_popup.cc b/third_party/blink/renderer/modules/accessibility/ax_menu_list_popup.cc
index b4823a35..595053f 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_menu_list_popup.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_menu_list_popup.cc
@@ -61,17 +61,14 @@
 }
 
 AXMenuListOption* AXMenuListPopup::MenuListOptionAXObject(
-    HTMLElement* element) const {
+    HTMLElement* element) {
   DCHECK(element);
   if (!IsA<HTMLOptionElement>(*element))
     return nullptr;
 
-  auto* ax_object =
-      DynamicTo<AXMenuListOption>(AXObjectCache().GetOrCreate(element));
-  if (!ax_object)
-    return nullptr;
+  AXObject* ax_object = AXObjectCache().GetOrCreate(element, this);
 
-  return ax_object;
+  return DynamicTo<AXMenuListOption>(ax_object);
 }
 
 int AXMenuListPopup::GetSelectedIndex() const {
@@ -93,7 +90,16 @@
 }
 
 void AXMenuListPopup::AddChildren() {
+#if DCHECK_IS_ON()
   DCHECK(!IsDetached());
+  DCHECK(!is_adding_children_) << " Reentering method on " << GetNode();
+  base::AutoReset<bool> reentrancy_protector(&is_adding_children_, true);
+  DCHECK_EQ(children_.size(), 0U)
+      << "Parent still has " << children_.size() << " children before adding:"
+      << "\nParent is " << ToString(true, true) << "\nFirst child is "
+      << children_[0]->ToString(true, true);
+#endif
+
   if (!parent_)
     return;
 
@@ -109,8 +115,8 @@
   for (auto* const option_element : html_select_element->GetOptionList()) {
     AXMenuListOption* option = MenuListOptionAXObject(option_element);
     if (option) {
+      DCHECK(!option->IsDetached());
       children_.push_back(option);
-      option->SetParent(this);
     }
   }
 }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_menu_list_popup.h b/third_party/blink/renderer/modules/accessibility/ax_menu_list_popup.h
index baa1149..fc180737 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_menu_list_popup.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_menu_list_popup.h
@@ -60,7 +60,7 @@
   void AddChildren() override;
   bool ComputeAccessibilityIsIgnored(IgnoredReasons* = nullptr) const override;
 
-  AXMenuListOption* MenuListOptionAXObject(HTMLElement*) const;
+  AXMenuListOption* MenuListOptionAXObject(HTMLElement*);
   int GetSelectedIndex() const;
 
   // Note that this may be -1 if nothing is selected.
diff --git a/third_party/blink/renderer/modules/accessibility/ax_mock_object.h b/third_party/blink/renderer/modules/accessibility/ax_mock_object.h
index 5e409c07..5c87a4c 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_mock_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_mock_object.h
@@ -42,7 +42,6 @@
   ~AXMockObject() override;
 
   // AXObject overrides.
-  AXObject* ComputeParent() const override { return parent_; }
   AXRestriction Restriction() const override { return kRestrictionNone; }
   bool IsMockObject() const final { return true; }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index a8a8c32..1067d1cd 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -89,6 +89,7 @@
 #include "third_party/blink/renderer/core/layout/layout_block_flow.h"
 #include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
 #include "third_party/blink/renderer/core/layout/layout_image.h"
+#include "third_party/blink/renderer/core/layout/layout_inline.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/layout/layout_table.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
@@ -126,6 +127,31 @@
          role == ax::mojom::blink::Role::kRowGroup;
 }
 
+// Within a table, provide the accessible, semantic parent of |node|,
+// by traversing the DOM tree, ignoring elements that are neutral in a table.
+// Return the AXObject for the ancestor.
+blink::AXObject* GetDOMTableAXAncestor(blink::Node* node,
+                                       blink::AXObjectCacheImpl& cache) {
+  // Used by code to determine roles of elements inside of an HTML table,
+  // Use DOM to get parent since parent_ is not initialized yet when role is
+  // being computed, and because HTML table structure should not take into
+  // account aria-owns.
+  if (!node)
+    return nullptr;
+
+  while (true) {
+    node = blink::NodeTraversal::Parent(*node);
+    if (!node)
+      return nullptr;
+
+    blink::AXObject* ax_object = cache.GetOrCreate(node);
+    if (ax_object && !IsNeutralWithinTable(ax_object))
+      return ax_object;
+  }
+
+  return nullptr;
+}
+
 enum class AXAction {
   kActionIncrement = 0,
   kActionDecrement,
@@ -682,7 +708,7 @@
   if (!GetElement())
     return ax::mojom::blink::Role::kUnknown;
 
-  AXObject* parent = ParentObject();
+  AXObject* parent = GetDOMTableAXAncestor(GetNode(), AXObjectCache());
   if (!parent || !parent->IsTableLikeRole())
     return ax::mojom::blink::Role::kGenericContainer;
 
@@ -693,9 +719,7 @@
 }
 
 ax::mojom::blink::Role AXNodeObject::DetermineTableRowRole() const {
-  AXObject* parent = ParentObject();
-  while (IsNeutralWithinTable(parent))
-    parent = parent->ParentObject();
+  AXObject* parent = GetDOMTableAXAncestor(GetNode(), AXObjectCache());
 
   if (!parent || !parent->IsTableLikeRole())
     return ax::mojom::blink::Role::kGenericContainer;
@@ -710,14 +734,13 @@
 }
 
 ax::mojom::blink::Role AXNodeObject::DetermineTableCellRole() const {
-  AXObject* parent = ParentObject();
+  AXObject* parent = GetDOMTableAXAncestor(GetNode(), AXObjectCache());
   if (!parent || !parent->IsTableRowLikeRole())
     return ax::mojom::blink::Role::kGenericContainer;
 
   // Ensure table container.
-  AXObject* grandparent = parent->ParentObject();
-  while (IsNeutralWithinTable(grandparent))
-    grandparent = grandparent->ParentObject();
+  AXObject* grandparent =
+      GetDOMTableAXAncestor(parent->GetNode(), AXObjectCache());
   if (!grandparent || !grandparent->IsTableLikeRole())
     return ax::mojom::blink::Role::kGenericContainer;
 
@@ -1018,8 +1041,10 @@
 }
 
 ax::mojom::blink::Role AXNodeObject::DetermineAccessibilityRole() {
-  if (!GetNode())
+  if (!GetNode()) {
+    NOTREACHED();
     return ax::mojom::blink::Role::kUnknown;
+  }
 
   native_role_ = NativeRoleIgnoringAria();
 
@@ -1158,12 +1183,12 @@
   return nullptr;
 }
 
-void AXNodeObject::Init() {
+void AXNodeObject::Init(AXObject* parent_if_known) {
 #if DCHECK_IS_ON()
   DCHECK(!initialized_);
   initialized_ = true;
 #endif
-  AXObject::Init();
+  AXObject::Init(parent_if_known);
 }
 
 void AXNodeObject::Detach() {
@@ -1636,6 +1661,9 @@
   return String();
 }
 
+// TODO(nektar): Consider removing this method in favor of
+// AXInlineTextBox::GetDocumentMarkers, or add document markers to the tree data
+// instead of nodes objects.
 void AXNodeObject::GetDocumentMarkers(
     Vector<DocumentMarker::MarkerType>* marker_types,
     Vector<AXRange>* marker_ranges) const {
@@ -2751,6 +2779,8 @@
   HeapVector<Member<AXObject>> owned_children;
   AXObjectCache().GetAriaOwnedChildren(this, owned_children);
 
+  // TODO(aleventhal) Why isn't this just using cached children?
+  AXNodeObject* parent = const_cast<AXNodeObject*>(this);
   for (Node* child = LayoutTreeBuilderTraversal::FirstChild(*node_); child;
        child = LayoutTreeBuilderTraversal::NextSibling(*child)) {
     auto* child_text_node = DynamicTo<Text>(child);
@@ -2759,7 +2789,7 @@
       // skip over empty text nodes
       continue;
     }
-    AXObject* child_obj = AXObjectCache().GetOrCreate(child);
+    AXObject* child_obj = AXObjectCache().GetOrCreate(child, parent);
     if (child_obj && !AXObjectCache().IsAriaOwned(child_obj))
       children.push_back(child_obj);
   }
@@ -2942,28 +2972,6 @@
   }
 }
 
-static Node* GetParentNodeForComputeParent(Node* node) {
-  if (!node)
-    return nullptr;
-
-  return LayoutTreeBuilderTraversal::Parent(*node);
-}
-
-AXObject* AXNodeObject::ComputeParent() const {
-  DCHECK(!IsDetached());
-  if (Node* parent_node = GetParentNodeForComputeParent(GetNode()))
-    return AXObjectCache().GetOrCreate(parent_node);
-
-  return nullptr;
-}
-
-AXObject* AXNodeObject::ComputeParentIfExists() const {
-  if (Node* parent_node = GetParentNodeForComputeParent(GetNode()))
-    return AXObjectCache().Get(parent_node);
-
-  return nullptr;
-}
-
 bool AXNodeObject::IsHtmlTable() const {
   return IsTableLikeRole() && GetLayoutObject() &&
          GetLayoutObject()->IsTable() && IsA<HTMLTableElement>(GetNode());
@@ -3092,6 +3100,7 @@
   if (GetLayoutObject()->IsText()) {
     ClearChildren();
     AddInlineTextBoxChildren(true);
+    have_children_ = true;  // Avoid adding these children twice.
     return;
   }
 
@@ -3121,20 +3130,22 @@
     return;
   }
 
+  if (LastKnownIsIgnoredValue()) {
+    // Inline textboxes are included if and only if the parent is unignored.
+    // If the parent is ignored but included in tree, the inline textbox is
+    // still withheld.
+    return;
+  }
+
   auto* layout_text = To<LayoutText>(GetLayoutObject());
   for (scoped_refptr<AbstractInlineTextBox> box =
            layout_text->FirstAbstractInlineTextBox();
        box.get(); box = box->NextInlineTextBox()) {
-    AXObject* ax_box = AXObjectCache().GetOrCreate(box.get());
-    if (!ax_box || !ax_box->AccessibilityIsIncludedInTree())
+    AXObject* ax_box = AXObjectCache().GetOrCreate(box.get(), this);
+    if (!ax_box)
       continue;
 
     children_.push_back(ax_box);
-    // If |force| is set to true, it means that we are forcing the children to
-    // be added without going through the normal tree building mechanism, which
-    // would have also set the parent of each child to |this|.
-    if (force)
-      ax_box->SetParent(this);
   }
 }
 
@@ -3155,7 +3166,7 @@
   for (HTMLAreaElement& area :
        Traversal<HTMLAreaElement>::DescendantsOf(*map)) {
     // add an <area> element for this child if it has a link
-    AddChild(AXObjectCache().GetOrCreate(&area));
+    AddChild(AXObjectCache().GetOrCreate(&area, this));
   }
 }
 
@@ -3177,7 +3188,7 @@
   DCHECK(GetLayoutObject());
   LayoutObject* child = GetLayoutObject()->SlowFirstChild();
   while (child) {
-    AddChild(AXObjectCache().GetOrCreate(child));
+    AddChild(AXObjectCache().GetOrCreate(child, this));
     child = child->NextSibling();
   }
 }
@@ -3188,7 +3199,8 @@
 
   for (Node* child = LayoutTreeBuilderTraversal::FirstChild(*node_); child;
        child = LayoutTreeBuilderTraversal::NextSibling(*child)) {
-    AXObject* child_obj = AXObjectCache().GetOrCreate(child);
+    AXObject* child_obj = AXObjectCache().GetOrCreate(child, this);
+
     if (RuntimeEnabledFeatures::AccessibilityExposeIgnoredNodesEnabled() &&
         child_obj &&
         child_obj->RoleValue() == ax::mojom::blink::Role::kStaticText &&
@@ -3203,9 +3215,28 @@
   }
 }
 
+void AXNodeObject::AddOwnedChildren() {
+  AXObjectVector owned_children;
+  AXObjectCache().GetAriaOwnedChildren(this, owned_children);
+
+  for (const auto& owned_child : owned_children) {
+    owned_child->SetParent(this);
+    AddChild(owned_child, true);
+  }
+}
+
 void AXNodeObject::AddChildren() {
-  if (IsDetached())
-    return;
+#if DCHECK_IS_ON()
+  DCHECK(!IsDetached());
+  DCHECK(!is_adding_children_) << " Reentering method on " << GetNode();
+  base::AutoReset<bool> reentrancy_protector(&is_adding_children_, true);
+  DCHECK_EQ(children_.size(), 0U)
+      << "\nParent still has " << children_.size() << " children before adding:"
+      << "\nParent is " << ToString(true, true) << "\nFirst child is "
+      << children_[0]->ToString(true, true);
+#endif
+#define CHECK_ATTACHED() \
+  DCHECK(!IsDetached()) << "Detached adding children: " << ToString(true, true)
 
   // If the need to add more children in addition to existing children arises,
   // childrenChanged should have been called, leaving the object with no
@@ -3213,40 +3244,47 @@
   DCHECK(!have_children_);
   have_children_ = true;
 
-  AXObjectVector owned_children;
-  AXObjectCache().GetAriaOwnedChildren(this, owned_children);
-
   if (IsHtmlTable())
     AddTableChildren();
   else if (ShouldUseLayoutObjectTraversalForChildren())
     AddLayoutChildren();
   else
     AddNodeChildren();
-
-  // TODO(crbug.com/1158511) This shouldn't be needed!
-  if (IsDetached())
-    return;
+  CHECK_ATTACHED();
 
   AddPopupChildren();
+  CHECK_ATTACHED();
+
   AddImageMapChildren();
-  AddInlineTextBoxChildren(false);
+  CHECK_ATTACHED();
+
+  AddInlineTextBoxChildren();
+  CHECK_ATTACHED();
+
   AddValidationMessageChild();
+  CHECK_ATTACHED();
+
   AddAccessibleNodeChildren();
+  CHECK_ATTACHED();
 
-  for (const auto& owned_child : owned_children)
-    AddChild(owned_child, true);
+  AddOwnedChildren();
+  CHECK_ATTACHED();
 
+#if DCHECK_IS_ON()
+  // All added children must be attached.
   for (const auto& child : children_) {
-    if (!child->CachedParentObject())
-      child->SetParent(this);
+    DCHECK(!child->IsDetached())
+        << "A brand new child was detached: " << child->ToString(true, true)
+        << "\n ... of parent " << ToString(true, true);
   }
+#endif
 }
 
 void AXNodeObject::AddNodeChild(Node* node) {
   if (!node)
     return;
 
-  AddChild(AXObjectCache().GetOrCreate(node));
+  AddChild(AXObjectCache().GetOrCreate(node, this));
 }
 
 void AXNodeObject::AddChild(AXObject* child, bool is_from_aria_owns) {
@@ -3263,6 +3301,8 @@
   if (!child || !CanHaveChildren())
     return;
 
+  DCHECK(!child->IsDetached())
+      << "Cannot add a detached child: " << child->ToString(true, true);
   if (is_from_aria_owns) {
     DCHECK(AXObjectCache().IsAriaOwned(child));
   } else {
@@ -3272,11 +3312,29 @@
       return;
   }
 
-  if (!child->AccessibilityIsIncludedInTree()) {
-    DCHECK(!is_from_aria_owns) << "Owned elements smust be in tree";
+  // Set the parent:
+  // - For a new object it will have already been set.
+  // - For a reused, older object, it may need to be changed to a new parent.
+  child->SetParent(this);
+
+#if DCHECK_IS_ON()
+  child->EnsureCorrectParentComputation();
+#endif
+
+  // Update cached values preemptively, but don't allow children changed to be
+  // called if ignored change, we are already recomputing children and don't
+  // want to recurse.
+  child->UpdateCachedAttributeValuesIfNeeded(false);
+
+  if (!child->LastKnownIsIncludedInTreeValue()) {
+    DCHECK(!is_from_aria_owns)
+        << "Owned elements must be in tree: " << child->ToString(true, true)
+        << "\nRecompute included in tree: "
+        << child->ComputeAccessibilityIsIgnoredButIncludedInTree();
     // Child is ignored and not in the tree.
     // Recompute the child's children now as we skip over the ignored object.
     child->SetNeedsToUpdateChildren();
+
     // Get the ignored child's children and add to children of ancestor
     // included in tree. This will recurse if necessary, skipping levels of
     // unignored descendants as it goes.
@@ -3287,11 +3345,16 @@
       // If the child was owned, it will be added elsewhere as a direct
       // child of the object owning it, and not as an indirect child under
       // an object not included in the tree.
-      if (!AXObjectCache().IsAriaOwned(children[i]))
+      if (!AXObjectCache().IsAriaOwned(children[i])) {
+        DCHECK(!children[i]->IsDetached()) << "Cannot add a detached child: "
+                                           << children[i]->ToString(true, true);
         children_.insert(new_index++, children[i]);
+      }
     }
   } else if (!child->IsMenuListOption()) {
     // MenuListOptions must only be added in AXMenuListPopup::AddChildren.
+    DCHECK(!child->IsDetached())
+        << "Cannot add a detached child: " << child->ToString(true, true);
     children_.insert(index, child);
   }
 }
@@ -3655,6 +3718,21 @@
   if (!GetNode() && !GetLayoutObject())
     return;
 
+  // When children changed on a <map> that means we need to forward the
+  // children changed to the <img> that parents the <area> elements.
+  // TODO(accessibility) Consider treating <img usemap> as aria-owns so that
+  // we get implementation "for free" vai relation cache, etc.
+  if (HTMLMapElement* map_element = DynamicTo<HTMLMapElement>(GetNode())) {
+    HTMLImageElement* image_element = map_element->ImageElement();
+    if (image_element) {
+      AXObject* ax_image = AXObjectCache().Get(image_element);
+      if (ax_image) {
+        ax_image->ChildrenChanged();
+        return;
+      }
+    }
+  }
+
   // Always update current object, in case it wasn't included in the tree but
   // now is. In that case, the LastKnownIsIncludedInTreeValue() won't have been
   // updated yet, so we can't use that. Unfortunately, this is not a safe time
@@ -3672,7 +3750,6 @@
       node_to_update->SetNeedsToUpdateChildren();
   }
 
-
   // If this node's children are not part of the accessibility tree then
   // skip notification and walking up the ancestors.
   // Cases where this happens:
@@ -3680,7 +3757,7 @@
   // - this or an ancestor is a leaf node
   // Uses |cached_is_descendant_of_leaf_node_| to avoid updating cached
   // attributes for eachc change via | UpdateCachedAttributeValuesIfNeeded()|.
-  if (!CanHaveChildren() || cached_is_descendant_of_leaf_node_)
+  if (!CanHaveChildren() || LastKnownIsDescendantOfLeafNode())
     return;
 
   // Calling CanHaveChildren(), above, can occasionally detach |this|.
@@ -3692,10 +3769,70 @@
 }
 
 void AXNodeObject::UpdateChildrenIfNecessary() {
-  if (NeedsToUpdateChildren())
+#if DCHECK_IS_ON()
+  DCHECK(GetDocument());
+  DCHECK(GetDocument()->IsActive());
+  DCHECK(!GetDocument()->IsDetached());
+  DCHECK(GetDocument()->GetPage());
+  DCHECK(GetDocument()->View());
+  DCHECK(!AXObjectCache().HasBeenDisposed());
+
+  // Store previous children for DCHECK to ensure no lost children, left
+  // without parents.
+  HashSet<AXID> prev_children_axids;
+  for (const auto& child : children_) {
+    if (!child->IsDetached()) {
+      AXID prev_child_axid = child->AXObjectID();
+      prev_children_axids.insert(prev_child_axid);
+      DCHECK(AXObjectCache().ObjectFromAXID(prev_child_axid));
+    }
+  }
+#endif
+
+  // Clear current children and get new children.
+  // TODO(accessibility) is it necessary to have a separate
+  // NeedsToUpdateChildren() from HasChildren()?
+  if (NeedsToUpdateChildren()) {
     ClearChildren();
+    if (!IsMenuList()) {  // AXMenuList is special and keeps its popup child.
+      // Ensure children have been correctly cleared.
+      DCHECK(!HasChildren())
+          << GetNode() << "  with " << children_.size() << " children";
+      DCHECK_EQ(children_.size(), 0U);
+    }
+  } else if (HasChildren()) {
+    return;  // Keep existing children.
+  } else {
+    // Will update children even though NeedsToUpdateChildren() was not true.
+    // Cannot have children otherwise will end up adding to them.
+    DCHECK_EQ(children_.size(), 0U);
+  }
 
   AXObject::UpdateChildrenIfNecessary();
+
+#if DCHECK_IS_ON()
+  // Remove current children's AXIDs from set so that we can see what children
+  // have been lost.
+  for (const auto& child : children_)
+    prev_children_axids.erase(child->AXObjectID());
+
+  // Report lost children. These are children that had |this| as a parent at
+  // the start of this method, but no longer do, yet the cache still considers
+  // them alive.
+  bool has_last_children = false;
+  for (AXID prev_child_axid : prev_children_axids) {
+    AXObject* prev_child = nullptr;
+    if (prev_child_axid)
+      AXObjectCache().ObjectFromAXID(prev_child_axid);
+    if (prev_child) {
+      has_last_children = true;
+      LOG(ERROR) << "Lost child: " << prev_child->ToString(true, true)
+                 << "\nThis: " << ToString(true, true);
+    }
+  }
+
+  DCHECK(!has_last_children);
+#endif
 }
 
 void AXNodeObject::SelectedOptions(AXObjectVector& options) const {
@@ -4276,7 +4413,8 @@
       }
 
       Element* title_element = document->TitleElement();
-      AXObject* title_ax_object = AXObjectCache().GetOrCreate(title_element);
+      AXObject* title_ax_object = AXObjectCache().GetOrCreate(
+          title_element, AXObjectCache().Get(document));
       if (title_ax_object) {
         if (related_objects) {
           local_related_objects.push_back(
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.h b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
index 5416390..bf122550 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
@@ -85,7 +85,7 @@
   // Overridden from AXObject.
   //
 
-  void Init() override;
+  void Init(AXObject* parent_if_known) override;
   void Detach() override;
   bool IsDetached() const override;
   bool IsAXNodeObject() const final;
@@ -208,10 +208,6 @@
                          SkMatrix44& out_container_transform,
                          bool* clips_children = nullptr) const override;
 
-  // High-level accessibility tree access.
-  AXObject* ComputeParent() const override;
-  AXObject* ComputeParentIfExists() const override;
-
   void AddChildren() override;
 
   bool CanHaveChildren() const override;
@@ -306,12 +302,14 @@
 
   void AddNodeChildren();
   void AddLayoutChildren();
-  void AddInlineTextBoxChildren(bool force);
+  void AddInlineTextBoxChildren(bool force = false);
   void AddImageMapChildren();
   void AddPopupChildren();
   bool IsHtmlTable() const;
   void AddTableChildren();
   void AddValidationMessageChild();
+  void AddOwnedChildren();
+
   ax::mojom::blink::Dropeffect ParseDropeffect(String& dropeffect) const;
 
   DISALLOW_COPY_AND_ASSIGN(AXNodeObject);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 0e8ad53..400d6b1a 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -85,6 +85,20 @@
 
 namespace {
 
+Node* GetParentNodeForComputeParent(Node* node) {
+  if (!node)
+    return nullptr;
+
+  // Prefer LayoutTreeBuilderTraversal::Parent(), which handles pseudo content.
+  Node* parent = LayoutTreeBuilderTraversal::Parent(*node);
+  if (parent)
+    return parent;
+
+  // Unfortunately, LayoutTreeBuilderTraversal::Parent() can return nullptr for
+  // a text node, such as inside a text area. Fall back on DOM parentNode().
+  return node->parentNode();
+}
+
 struct RoleHashTraits : HashTraits<ax::mojom::blink::Role> {
   static const bool kEmptyValueIsZero = true;
   static ax::mojom::blink::Role EmptyValue() {
@@ -566,8 +580,6 @@
       have_children_(false),
       role_(ax::mojom::blink::Role::kUnknown),
       aria_role_(ax::mojom::blink::Role::kUnknown),
-      last_known_is_ignored_value_(kDefaultBehavior),
-      last_known_is_ignored_but_included_in_tree_value_(kDefaultBehavior),
       explicit_container_id_(0),
       parent_(nullptr),
       last_modification_count_(-1),
@@ -590,22 +602,45 @@
   --number_of_live_ax_objects_;
 }
 
-void AXObject::Init() {
+void AXObject::Init(AXObject* parent_if_known) {
+#if DCHECK_IS_ON()
+  DCHECK(!parent_);
+  DCHECK(!is_initializing_);
+  base::AutoReset<bool> reentrancy_protector(&is_initializing_, true);
+#endif
+
+  // The role must be determined immedediately.
+  // Note: in order to avoid reentrancy, the role computation cannot use the
+  // ParentObject(), although it can use the DOM parent.
   role_ = DetermineAccessibilityRole();
   DCHECK(role_ != ax::mojom::blink::Role::kUnknown)
-      << "Illegal Role::kUnknown for " << GetNode();
+      << "Illegal Role::kUnknown for " << GetNode() << " " << GetLayoutObject();
+
+  // Determine the parent as soon as possible.
+  // Every AXObject must have a parent unless it's the root.
+  SetParent(parent_if_known ? parent_if_known : ComputeParent());
+  DCHECK(parent_ || IsA<Document>(GetNode()))
+      << "The following node should have a parent: " << GetNode();
+
+  // Initialize all other cached values.
+  UpdateCachedAttributeValuesIfNeeded(false);
 }
 
 void AXObject::Detach() {
+#if DCHECK_IS_ON()
+  DCHECK(!is_adding_children_) << ToString(true, true);
   DCHECK(ax_object_cache_);
   DCHECK(!ax_object_cache_->IsFrozen())
       << "Do not detach children while the tree is frozen, in order to avoid "
          "an object detaching itself in the middle of computing its own "
          "accessibility properties.";
+#endif
+
   // Clear any children and call detachFromParent on them so that
   // no children are left with dangling pointers to their parent.
   ClearChildren();
 
+  parent_ = nullptr;
   ax_object_cache_ = nullptr;
 }
 
@@ -614,9 +649,125 @@
 }
 
 void AXObject::SetParent(AXObject* parent) {
+  DCHECK(parent || IsA<Document>(GetNode()))
+      << "Parent cannot be null, except at the root, was null at " << GetNode()
+      << " " << GetLayoutObject();
   parent_ = parent;
 }
 
+// In many cases, ComputeParent() is not called, because the parent adding
+// the child will pass itself into AXObject::Init() via parent_if_known.
+// ComputeParent() is still necessary because some parts of the code,
+// especially web tests, result in AXObjects being created in the middle of
+// the tree before their parents are created.
+// TODO(accessibility) Consider forcing all ax objects to be created from
+// the top down, eliminating the need for ComputeParent().
+AXObject* AXObject::ComputeParent() const {
+  DCHECK(!IsDetached());
+
+  DCHECK(!parent_ || parent_->IsDetached())
+      << "Should use cached parent unless it's detached, and should not "
+         "attempt to recompute it, occurred on "
+      << GetNode();
+
+  DCHECK(GetNode() || GetLayoutObject())
+      << "Can't compute parent on AXObjects that don't have a backing Node or "
+         "LayoutObject. Objects without those must set the parent in Init().";
+
+  return ComputeParentImpl();
+}
+
+AXObject* AXObject::ComputeParentImpl() const {
+  if (AXObjectCache().IsAriaOwned(this))
+    return AXObjectCache().GetAriaOwnedParent(this);
+
+  Node* current_node = GetNode();
+
+  // A WebArea's parent should be the page popup owner, if any, otherwise null.
+  if (IsA<Document>(current_node)) {
+    LocalFrame* frame = GetLayoutObject()->GetFrame();
+    return AXObjectCache().GetOrCreate(frame->PagePopupOwner());
+  }
+
+  // If no node, or a pseudo element, use the layout parent.
+  if (!current_node) {
+    LayoutObject* current_layout_obj = GetLayoutObject();
+    DCHECK(current_layout_obj);
+    // If no DOM node and no parent, this must be an anonymous layout object.
+    DCHECK(current_layout_obj->IsAnonymous());
+    LayoutObject* parent_layout_obj = current_layout_obj->Parent();
+    if (!parent_layout_obj)
+      return nullptr;
+    if (AXObject* ax_parent = AXObjectCache().GetOrCreate(parent_layout_obj)) {
+      DCHECK(!ax_parent->IsDetached());
+      return ax_parent;
+    }
+    // Switch to using DOM nodes. The only cases that should occur do not have
+    // chains of multiple parents without DOM nodes.
+    Node* parent_node = parent_layout_obj->GetNode();
+    DCHECK(parent_node) << "Computing an accessible parent from the layout "
+                           "parent did not yield an accessible object nor a "
+                           "DOM node to walk up from, current_layout_obj = "
+                        << current_layout_obj;
+    if (!parent_node)
+      return nullptr;
+  }
+
+  while (true) {
+    current_node = GetParentNodeForComputeParent(current_node);
+    if (!current_node)
+      break;
+    AXObject* ax_parent = AXObjectCache().GetOrCreate(current_node);
+    if (ax_parent) {
+      DCHECK(!ax_parent->IsDetached());
+      return ax_parent;
+    }
+  }
+
+  return nullptr;
+}
+
+#if DCHECK_IS_ON()
+void AXObject::EnsureCorrectParentComputation() {
+  if (!parent_)
+    return;
+
+  DCHECK(!parent_->IsDetached());
+
+  // Don't check the computed parent if the cached parent is a mock object.
+  // It is expected that a computed parent could never be a mock object,
+  // which has no backing DOM node or layout object, and therefore cannot be
+  // found by traversing DOM/layout ancestors.
+  if (parent_->IsMockObject())
+    return;
+
+  // Cannot compute a parent for an object that has no backing node or layout
+  // object to start from.
+  if (!GetNode() || !GetLayoutObject())
+    return;
+
+  // Don't check the computed parent if the cached parent is an image map:
+  // <area> children's location in the DOM and HTML hierarchy does not match.
+  // TODO(aleventhal) Try to remove this rule, it may be unnecessary now.
+  if (parent_->RoleValue() == ax::mojom::blink::Role::kImageMap)
+    return;
+
+  // TODO(aleventhal) Different in test fast/css/first-letter-removed-added.html
+  // when run with --force-renderer-accessibility.
+  if (GetNode() && GetNode()->IsPseudoElement())
+    return;
+
+  // Verify that the algorithm in ComputeParentImpl() provides same results as
+  // parents that init their children with themselvs as the parent_if_known.
+  // Inconsistency indicates a problem could potentially exist where a child's
+  // parent does not include the child in its children.
+  DCHECK_EQ(ComputeParentImpl(), parent_)
+      << "\n**** ComputeParent should have provided the same result as "
+         "the known parent.\n**** Child was "
+      << this;
+}
+#endif
+
 const AtomicString& AXObject::GetAOMPropertyOrARIAAttribute(
     AOMStringProperty property) const {
   Element* element = this->GetElement();
@@ -1532,7 +1683,8 @@
   return !AccessibilityIsIgnored() || AccessibilityIsIgnoredButIncludedInTree();
 }
 
-void AXObject::UpdateCachedAttributeValuesIfNeeded() const {
+void AXObject::UpdateCachedAttributeValuesIfNeeded(
+    bool notify_parent_of_ignored_changes) const {
   if (IsDetached())
     return;
 
@@ -1541,79 +1693,94 @@
   if (cache.ModificationCount() == last_modification_count_)
     return;
 
+  last_modification_count_ = cache.ModificationCount();
+
 #if DCHECK_IS_ON()  // Required in order to get Lifecycle().ToString()
+  DCHECK(!is_updating_cached_values_)
+      << "Reentering UpdateCachedAttributeValuesIfNeeded() on same node: "
+      << GetNode();
+
+  base::AutoReset<bool> reentrancy_protector(&is_updating_cached_values_, true);
+
   DCHECK(!GetDocument() || GetDocument()->Lifecycle().GetState() >=
                                DocumentLifecycle::kAfterPerformLayout)
       << "Unclean document at lifecycle "
       << GetDocument()->Lifecycle().ToString();
 #endif
 
-  last_modification_count_ = cache.ModificationCount();
+  // TODO(accessibility) Every AXObject must have a parent except the root.
+  // Sometimes the parent is detached and a new parent isn't yet reattached.
+  if (!parent_)
+    parent_ = ComputeParent();
 
-  cached_background_color_ = ComputeBackgroundColor();
   cached_is_hidden_via_style = ComputeIsHiddenViaStyle();
   cached_is_inert_or_aria_hidden_ = ComputeIsInertOrAriaHidden();
+  cached_background_color_ = ComputeBackgroundColor();
   cached_is_descendant_of_leaf_node_ = !!LeafNodeAncestor();
   cached_is_descendant_of_disabled_node_ = !!DisabledAncestor();
   cached_has_inherited_presentational_role_ =
       !!InheritsPresentationalRoleFrom();
-  cached_is_ignored_ = ComputeAccessibilityIsIgnored();
-  cached_is_ignored_but_included_in_tree_ =
-      cached_is_ignored_ && ComputeAccessibilityIsIgnoredButIncludedInTree();
+  bool is_ignored = ComputeAccessibilityIsIgnored();
+  bool is_ignored_but_included_in_tree =
+      is_ignored && ComputeAccessibilityIsIgnoredButIncludedInTree();
 #if DCHECK_IS_ON()
   // Ensure that display-locked text is pruned from the tree. This means that
   // they will be missed in the virtual buffer; therefore, it may be a rule
   // subject to change. Note that changing the rule would potentially cause a
   // lot of display-locked whitespace to be exposed.
-  if (cached_is_ignored_ &&
-      RoleValue() == ax::mojom::blink::Role::kStaticText && GetNode() &&
+  if (is_ignored && RoleValue() == ax::mojom::blink::Role::kStaticText &&
+      GetNode() &&
       DisplayLockUtilities::NearestLockedExclusiveAncestor(*GetNode())) {
     DCHECK(!cached_is_ignored_but_included_in_tree_)
         << "Display locked text should not be included in the tree (subject to "
            "future rule change)";
   }
 #endif
+  bool included_in_tree_changed = false;
+
+  // If the child's "included in tree" state changes, we will be notifying the
+  // parent to recompute it's children.
+  // Exceptions:
+  // - Caller passes in |notify_parent_of_ignored_changes = false| -- this
+  //   occurs when this is a new child, or when a parent is in the middle of
+  //   adding this child, and doing this would be redundant.
+  // - Inline text boxes: their "included in tree" state is entirely dependent
+  //   on their static text parent.
+  if (notify_parent_of_ignored_changes &&
+      RoleValue() != ax::mojom::blink::Role::kInlineTextBox) {
+    bool is_included_in_tree = !is_ignored || is_ignored_but_included_in_tree;
+    if (is_included_in_tree != LastKnownIsIncludedInTreeValue())
+      included_in_tree_changed = true;
+  }
+
+  cached_is_ignored_ = is_ignored;
+  cached_is_ignored_but_included_in_tree_ = is_ignored_but_included_in_tree;
   cached_is_editable_root_ = ComputeIsEditableRoot();
   // Compute live region root, which can be from any ARIA live value, including
   // "off", or from an automatic ARIA live value, e.g. from role="status".
   // TODO(dmazzoni): remove this const_cast.
   AtomicString aria_live;
   if (GetNode() && IsA<Document>(GetNode())) {
-    // The document is never a live region root. This extra bit of logic also
-    // protects against going up the parent chain from a popup document, into
-    // a parent document with unclean layout.
+    // The document root is never a live region root.
     cached_live_region_root_ = nullptr;
   } else if (RoleValue() == ax::mojom::blink::Role::kInlineTextBox) {
     // Inline text boxes do not need live region properties.
     cached_live_region_root_ = nullptr;
   } else {
-    cached_live_region_root_ =
-        IsLiveRegionRoot()
-            ? const_cast<AXObject*>(this)
-            : (ParentObjectIfExists() ? ParentObjectIfExists()->LiveRegionRoot()
-                                      : nullptr);
+    // Is a live region root if this or an ancestor is a live region.
+    DCHECK(parent_);
+    cached_live_region_root_ = IsLiveRegionRoot() ? const_cast<AXObject*>(this)
+                                                  : parent_->LiveRegionRoot();
   }
   cached_aria_column_index_ = ComputeAriaColumnIndex();
   cached_aria_row_index_ = ComputeAriaRowIndex();
 
-  bool ignored_states_changed = false;
-  if (cached_is_ignored_ != LastKnownIsIgnoredValue()) {
-    last_known_is_ignored_value_ =
-        cached_is_ignored_ ? kIgnoreObject : kIncludeObject;
-    ignored_states_changed = true;
-  }
-
-  if (cached_is_ignored_but_included_in_tree_ !=
-      LastKnownIsIgnoredButIncludedInTreeValue()) {
-    last_known_is_ignored_but_included_in_tree_value_ =
-        cached_is_ignored_but_included_in_tree_ ? kIncludeObject
-                                                : kIgnoreObject;
-    ignored_states_changed = true;
-  }
-
-  if (ignored_states_changed) {
-    if (AXObject* parent = ParentObjectIfExists())
-      parent->ChildrenChanged();
+  if (included_in_tree_changed) {
+    if (AXObject* parent = CachedParentObject()) {
+      // Defer this ChildrenChanged(), otherwise it can cause reentry into
+      // UpdateCachedAttributeValuesIfNeeded() on |this|.
+      AXObjectCache().ChildrenChanged(parent);
+    }
   }
 
   if (GetLayoutObject() && GetLayoutObject()->IsText()) {
@@ -1998,32 +2165,11 @@
 }
 
 bool AXObject::LastKnownIsIgnoredValue() const {
-  if (last_known_is_ignored_value_ == kDefaultBehavior) {
-    last_known_is_ignored_value_ =
-        AccessibilityIsIgnored() ? kIgnoreObject : kIncludeObject;
-  }
-
-  return last_known_is_ignored_value_ == kIgnoreObject;
-}
-
-void AXObject::SetLastKnownIsIgnoredValue(bool is_ignored) {
-  last_known_is_ignored_value_ = is_ignored ? kIgnoreObject : kIncludeObject;
+  return cached_is_ignored_;
 }
 
 bool AXObject::LastKnownIsIgnoredButIncludedInTreeValue() const {
-  if (last_known_is_ignored_but_included_in_tree_value_ == kDefaultBehavior) {
-    last_known_is_ignored_but_included_in_tree_value_ =
-        AccessibilityIsIgnoredButIncludedInTree() ? kIncludeObject
-                                                  : kIgnoreObject;
-  }
-
-  return last_known_is_ignored_but_included_in_tree_value_ == kIncludeObject;
-}
-
-void AXObject::SetLastKnownIsIgnoredButIncludedInTreeValue(
-    bool is_ignored_but_included_in_tree) {
-  last_known_is_ignored_but_included_in_tree_value_ =
-      is_ignored_but_included_in_tree ? kIncludeObject : kIgnoreObject;
+  return cached_is_ignored_but_included_in_tree_;
 }
 
 bool AXObject::LastKnownIsIncludedInTreeValue() const {
@@ -3123,14 +3269,14 @@
 }
 
 const AXObject::AXObjectVector AXObject::UnignoredChildren() {
+  UpdateChildrenIfNecessary();
+
   if (!AccessibilityIsIncludedInTree()) {
     NOTREACHED() << "We don't support finding the unignored children of "
                     "objects excluded from the accessibility tree.";
     return {};
   }
 
-  UpdateChildrenIfNecessary();
-
   // Capture only descendants that are not accessibility ignored, and that are
   // one level deeper than the current object after flattening any accessibility
   // ignored descendants.
@@ -3428,23 +3574,17 @@
   if (IsDetached())
     return nullptr;
 
-  if (parent_)
-    return parent_;
+  // This can happen when an object in the middle of the tree is suddenly
+  // detached, but the children still exist. One example of this is when
+  // a <select size="1"> changes to <select size="2">, where the
+  // Role::kMenuListPopup is detached.
+  if (!parent_) {
+    parent_ = ComputeParent();
+    DCHECK(parent_ || IsA<Document>(GetNode()))
+        << "The following node should have a parent: " << GetNode();
+  }
 
-  if (AXObjectCache().IsAriaOwned(this))
-    return AXObjectCache().GetAriaOwnedParent(this);
-
-  return ComputeParent();
-}
-
-AXObject* AXObject::ParentObjectIfExists() const {
-  if (IsDetached())
-    return nullptr;
-
-  if (parent_)
-    return parent_;
-
-  return ComputeParentIfExists();
+  return parent_;
 }
 
 AXObject* AXObject::ParentObjectUnignored() const {
@@ -3502,10 +3642,28 @@
 }
 
 void AXObject::ClearChildren() {
-  // Detach all weak pointers from objects to their parents.
+  // Detach all weak pointers from immediate children to their parents.
+  // First check to make sure the child's parent wasn't already reassigned.
+  // In addition, the immediate children are different from children_, and are
+  // the objects where the parent_ points to this. For example:
+  // Parent (this)
+  //   Child not included in tree  (immediate child)
+  //     Child included in tree (an item in |children_|)
+  // Therefore, for each child in |children_|, we may need to go up one or
+  // more ancestors to find the actual immediate child. This is accomplished via
+  // an additional inner loop.
+  // TODO(accessibility) This ugly extra inner loop can be removed if we remove
+  // "not included in tree" holes from the tree, and there will no longer be
+  // any difference between children_ and immediate children.
   for (const auto& child : children_) {
-    if (child->parent_ == this)
-      child->DetachFromParent();
+    AXObject* immediate_child = child;
+    while (immediate_child) {
+      if (immediate_child->parent_ == this) {
+        immediate_child->parent_ = nullptr;
+        break;
+      }
+      immediate_child = immediate_child->parent_;
+    }
   }
 
   children_.clear();
@@ -3522,7 +3680,7 @@
     return;
 
   for (const auto& child : accessible_node->GetChildren())
-    children_.push_back(AXObjectCache().GetOrCreate(child));
+    children_.push_back(AXObjectCache().GetOrCreate(child, this));
 }
 
 Element* AXObject::GetElement() const {
@@ -4305,7 +4463,7 @@
 }
 
 void AXObject::SelectionChanged() {
-  if (AXObject* parent = ParentObjectIfExists())
+  if (AXObject* parent = CachedParentObject())
     parent->SelectionChanged();
 }
 
@@ -4635,7 +4793,7 @@
     }
 
     case ax::mojom::blink::Role::kUnknown:
-      NOTREACHED() << "Illegal Role::kUnknown for " << GetNode();
+      NOTREACHED() << "Illegal Role::kUnknown for " << ToString(true, true);
       break;
   }
 
@@ -4729,7 +4887,7 @@
   return common_ancestor;
 }
 
-String AXObject::ToString(bool verbose) const {
+String AXObject::ToString(bool verbose, bool cached_values_only) const {
   // Build a friendly name for debugging the object.
   // If verbose, build a longer name name in the form of:
   // CheckBox axid#28 <input.someClass#cbox1> name="checkbox"
@@ -4737,12 +4895,16 @@
       AXObject::InternalRoleName(RoleValue()).GetString().EncodeForDebugging();
 
   if (verbose) {
+    if (IsDetached())
+      string_builder = string_builder + " (detached)";
     string_builder = string_builder + " axid#" + String::Number(AXObjectID());
     // Add useful HTML element info, like <div.myClass#myId>.
     if (GetElement()) {
       string_builder =
           string_builder + " <" + GetElement()->tagName().LowerASCII();
-      if (GetElement()->FastHasAttribute(html_names::kClassAttr)) {
+      // Cannot safely get @class from SVG elements.
+      if (!GetElement()->IsSVGElement() &&
+          GetElement()->FastHasAttribute(html_names::kClassAttr)) {
         string_builder = string_builder + "." +
                          GetElement()->FastGetAttribute(html_names::kClassAttr);
       }
@@ -4754,17 +4916,25 @@
     }
 
     // Add properties of interest that often contribute to errors:
-    if (HasARIAOwns(GetElement()))
-      string_builder = string_builder + " @aria-owns";
-    if (GetAOMPropertyOrARIAAttribute(AOMRelationProperty::kActiveDescendant))
-      string_builder = string_builder + " @aria-activedescendant";
+    if (HasARIAOwns(GetElement())) {
+      string_builder =
+          string_builder + " aria-owns=" +
+          GetElement()->FastGetAttribute(html_names::kAriaOwnsAttr);
+    }
+    if (GetAOMPropertyOrARIAAttribute(AOMRelationProperty::kActiveDescendant)) {
+      string_builder =
+          string_builder + " aria-activedescendant=" +
+          GetElement()->FastGetAttribute(html_names::kAriaOwnsAttr);
+    }
     if (IsFocused())
       string_builder = string_builder + " focused";
-    if (AXObjectCache().IsAriaOwned(this))
+    if (!IsDetached() && AXObjectCache().IsAriaOwned(this))
       string_builder = string_builder + " isAriaOwned";
-    if (AccessibilityIsIgnored()) {
+    if (cached_values_only ? LastKnownIsIgnoredValue()
+                           : AccessibilityIsIgnored()) {
       string_builder = string_builder + " isIgnored";
-      if (!AccessibilityIsIncludedInTree())
+      if (cached_values_only ? !LastKnownIsIncludedInTreeValue()
+                             : !AccessibilityIsIncludedInTree())
         string_builder = string_builder + " isRemovedFromTree";
     }
     if (GetNode() &&
@@ -4778,6 +4948,10 @@
       string_builder = string_builder + " isHiddenViaCSS";
     if (GetNode() && GetNode()->IsInert())
       string_builder = string_builder + " isInert";
+    if (NeedsToUpdateChildren())
+      string_builder = string_builder + " needsToUpdateChildren";
+    if (IsAXLayoutObject() && !GetLayoutObject())
+      string_builder = string_builder + " missingLayout";
 
     string_builder = string_builder + " name=";
   } else {
@@ -4785,7 +4959,10 @@
   }
 
   // Append name last, in case it is long.
-  return string_builder + ComputedName().EncodeForDebugging();
+  if (!cached_values_only)
+    string_builder = string_builder + ComputedName().EncodeForDebugging();
+
+  return string_builder;
 }
 
 bool operator==(const AXObject& first, const AXObject& second) {
@@ -4838,6 +5015,13 @@
   return first == second || first > second;
 }
 
+std::ostream& operator<<(std::ostream& stream, const AXObject* obj) {
+  if (obj)
+    return stream << obj->ToString(true).Utf8();
+  else
+    return stream << "<AXObject nullptr>";
+}
+
 std::ostream& operator<<(std::ostream& stream, const AXObject& obj) {
   return stream << obj.ToString(true).Utf8();
 }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h
index 12bf7d25..3c40ba6 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -336,6 +336,12 @@
  protected:
   explicit AXObject(AXObjectCacheImpl&);
 
+#if DCHECK_IS_ON()
+  bool is_initializing_ = false;
+  mutable bool is_updating_cached_values_ = false;
+  bool is_adding_children_ = false;
+#endif
+
  public:
   virtual ~AXObject();
   virtual void Trace(Visitor*) const;
@@ -346,16 +352,21 @@
   // unique ID, then added to AXObjectCacheImpl, and finally init() must
   // be called last.
   void SetAXObjectID(AXID ax_object_id) { id_ = ax_object_id; }
-  virtual void Init();
+  virtual void Init(AXObject* parent_if_known);
 
   // When the corresponding WebCore object that this AXObject
   // wraps is deleted, it must be detached.
   virtual void Detach();
   virtual bool IsDetached() const;
 
-  // Sets the parent AXObject directly. If the parent of this object is known,
-  // this can be faster than using computeParent().
-  virtual void SetParent(AXObject* parent);
+  // Updates the cached attribute values. This may be recursive, so to prevent
+  // deadlocks, functions called here may only search up the tree (ancestors),
+  // not down.
+  // Fires children change on the parent if the node's ignored or included in
+  // tree status changes. Use |notify_parent_of_ignored_changes = false| to
+  // prevent this.
+  void UpdateCachedAttributeValuesIfNeeded(
+      bool notify_parent_of_ignored_changes = true) const;
 
   // The AXObjectCacheImpl that owns this object, and its unique ID within this
   // cache.
@@ -519,6 +530,9 @@
   bool ComputeIsInertOrAriaHidden(IgnoredReasons* = nullptr) const;
   bool IsBlockedByAriaModalDialog(IgnoredReasons* = nullptr) const;
   bool IsDescendantOfLeafNode() const;
+  bool LastKnownIsDescendantOfLeafNode() const {
+    return cached_is_descendant_of_leaf_node_;
+  }
   AXObject* LeafNodeAncestor() const;
   bool IsDescendantOfDisabledNode() const;
   bool ComputeAccessibilityIsIgnoredButIncludedInTree() const;
@@ -527,10 +541,8 @@
   const AXObject* DatetimeAncestor(int max_levels_to_check = 3) const;
   const AXObject* DisabledAncestor() const;
   bool LastKnownIsIgnoredValue() const;
-  void SetLastKnownIsIgnoredValue(bool);
   bool LastKnownIsIgnoredButIncludedInTreeValue() const;
   bool LastKnownIsIncludedInTreeValue() const;
-  void SetLastKnownIsIgnoredButIncludedInTreeValue(bool);
   bool HasInheritedPresentationalRole() const;
   bool IsPresentationalChild() const;
   bool CanBeActiveDescendant() const;
@@ -913,6 +925,8 @@
   //
   // Can be called on all nodes that are included in the accessibility tree,
   // including those that are accessibility ignored.
+  // TODO(accessibility) This actually returns ignored children when they are
+  // included in the tree. A better name would be ChildrenIncludedInTree().
   const AXObjectVector UnignoredChildren() const;
   const AXObjectVector UnignoredChildren();
 
@@ -1032,15 +1046,29 @@
   AXObject* ParentObject() const;
 
   // Get the parent of this object if it has already been created.
-  //
   // Works for all nodes, and may return nodes that are accessibility ignored,
   // including nodes that might not be in the tree.
-  AXObject* ParentObjectIfExists() const;
-
-  virtual AXObject* ComputeParent() const = 0;
-  virtual AXObject* ComputeParentIfExists() const { return nullptr; }
   AXObject* CachedParentObject() const { return parent_; }
 
+  // Sets the parent AXObject directly. If the parent of this object is known,
+  // this can be faster than using ComputeParent().
+  void SetParent(AXObject* parent);
+
+  // If parent was not initialized during AddChildren() it can be computed by
+  // walking the DOM (or layout for nodeless aka anonymous layout object).
+  // ComputeParent() adds DCHECKs to ensure that it is not being called when
+  // an attached parent_ is already cached, and that it is possible to compute
+  // the parent. It calls ComputeParentImpl() for the actual work.
+  AXObject* ComputeParent() const;
+  // Subclasses override ComputeParentImpl() to change parent computation.
+  virtual AXObject* ComputeParentImpl() const;
+
+#if DCHECK_IS_ON()
+  // When the parent on children during AddChildren(), take the opportunity to
+  // check out ComputeParent() implementation. It should match.
+  void EnsureCorrectParentComputation();
+#endif
+
   // Get or create the first ancestor that's not accessibility ignored.
   // Works for all nodes.
   AXObject* ParentObjectUnignored() const;
@@ -1215,16 +1243,17 @@
   bool IsHiddenForTextAlternativeCalculation() const;
 
   // Returns a string representation of this object.
-  String ToString(bool verbose = false) const;
+  // |cached_values_only| avoids recomputing cached values, and thus can be
+  // used during UpdateCachedValuesIfNecessary() without causing recursion.
+  String ToString(bool verbose = false, bool cached_values_only = false) const;
 
  protected:
   AXID id_;
+  // Only children that are included in tree, maybe rename to children_in_tree_.
   AXObjectVector children_;
   mutable bool have_children_;
   ax::mojom::blink::Role role_;
   ax::mojom::blink::Role aria_role_;
-  mutable AXObjectInclusion last_known_is_ignored_value_;
-  mutable AXObjectInclusion last_known_is_ignored_but_included_in_tree_value_;
   LayoutRect explicit_element_rect_;
   AXID explicit_container_id_;
 
@@ -1281,16 +1310,25 @@
   const AXObject* TableRowParent() const;
   const AXObject* TableParent() const;
 
+  // Any parent, regardless of whether it's ignored or not included in the tree.
   mutable Member<AXObject> parent_;
 
-  // The following cached attribute values (the ones starting with m_cached*)
-  // are only valid if m_lastModificationCount matches
-  // AXObjectCacheImpl::modificationCount().
+  // Helpers for serialization.
+  void SerializeStyleAttributes(ui::AXNodeData* node_data);
+  void SerializeSparseAttributes(ui::AXNodeData* node_data);
+  void SerializeTableAttributes(ui::AXNodeData* node_data);
+  void SerializeListAttributes(ui::AXNodeData* node_data);
+  void SerializeScrollAttributes(ui::AXNodeData* node_data);
+
+ private:
   mutable int last_modification_count_;
+
+  // The following cached attribute values (the ones starting with m_cached*)
+  // are only valid if last_modification_count_ matches
+  // AXObjectCacheImpl::ModificationCount().
   mutable RGBA32 cached_background_color_;
   mutable bool cached_is_ignored_ : 1;
   mutable bool cached_is_ignored_but_included_in_tree_ : 1;
-
   mutable bool cached_is_inert_or_aria_hidden_ : 1;
   mutable bool cached_is_hidden_via_style : 1;
   mutable bool cached_is_descendant_of_leaf_node_ : 1;
@@ -1304,18 +1342,6 @@
 
   Member<AXObjectCacheImpl> ax_object_cache_;
 
-  // Updates the cached attribute values. This may be recursive, so to prevent
-  // deadlocks,
-  // functions called here may only search up the tree (ancestors), not down.
-  void UpdateCachedAttributeValuesIfNeeded() const;
-
-  // Helpers for serialization.
-  void SerializeStyleAttributes(ui::AXNodeData* node_data);
-  void SerializeSparseAttributes(ui::AXNodeData* node_data);
-  void SerializeTableAttributes(ui::AXNodeData* node_data);
-  void SerializeListAttributes(ui::AXNodeData* node_data);
-  void SerializeScrollAttributes(ui::AXNodeData* node_data);
-
  private:
   void UpdateDistributionForFlatTreeTraversal() const;
   bool IsARIAControlledByTextboxWithActiveDescendant() const;
@@ -1362,6 +1388,7 @@
 MODULES_EXPORT bool operator>(const AXObject& first, const AXObject& second);
 MODULES_EXPORT bool operator>=(const AXObject& first, const AXObject& second);
 MODULES_EXPORT std::ostream& operator<<(std::ostream&, const AXObject&);
+MODULES_EXPORT std::ostream& operator<<(std::ostream&, const AXObject*);
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 5201d0d..2ab6f28 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -470,7 +470,8 @@
   return MakeGarbageCollected<AXInlineTextBox>(inline_text_box, *this);
 }
 
-AXObject* AXObjectCacheImpl::GetOrCreate(AccessibleNode* accessible_node) {
+AXObject* AXObjectCacheImpl::GetOrCreate(AccessibleNode* accessible_node,
+                                         AXObject* parent_if_known) {
   if (AXObject* obj = Get(accessible_node))
     return obj;
 
@@ -479,25 +480,37 @@
   const AXID ax_id = AssociateAXID(new_obj);
   accessible_node_mapping_.Set(accessible_node, ax_id);
 
-  new_obj->Init();
+  new_obj->Init(parent_if_known);
   return new_obj;
 }
 
 AXObject* AXObjectCacheImpl::GetOrCreate(const Node* node) {
-  return GetOrCreate(const_cast<Node*>(node));
+  return GetOrCreate(node, nullptr);
 }
 
 AXObject* AXObjectCacheImpl::GetOrCreate(Node* node) {
+  return GetOrCreate(node, nullptr);
+}
+
+AXObject* AXObjectCacheImpl::GetOrCreate(const Node* node,
+                                         AXObject* parent_if_known) {
+  return GetOrCreate(const_cast<Node*>(node), parent_if_known);
+}
+
+AXObject* AXObjectCacheImpl::GetOrCreate(Node* node,
+                                         AXObject* parent_if_known) {
   if (!IsNodeRelevantForAccessibility(node))
     return nullptr;
 
   if (AXObject* obj = Get(node))
     return obj;
 
-  return CreateAndInit(node);
+  return CreateAndInit(node, parent_if_known);
 }
 
-AXObject* AXObjectCacheImpl::CreateAndInit(Node* node, AXID use_axid) {
+AXObject* AXObjectCacheImpl::CreateAndInit(Node* node,
+                                           AXObject* parent_if_known,
+                                           AXID use_axid) {
 #if DCHECK_IS_ON()
   DCHECK(node);
   DCHECK(node->isConnected());
@@ -514,7 +527,21 @@
   // a locked subtree, which are created based on its node.
   if (node->GetLayoutObject() && IsLayoutObjectRelevantForAccessibility(node) &&
       !DisplayLockUtilities::NearestLockedExclusiveAncestor(*node)) {
-    return CreateAndInit(node->GetLayoutObject(), use_axid);
+    return CreateAndInit(node->GetLayoutObject(), parent_if_known, use_axid);
+  }
+
+  // Return null if inside a shadow tree of something that can't have children,
+  // for example, an <img> has a user agent shadow root containing a <span> for
+  // the alt text. Do not create an accessible for that as it would be unable
+  // to have a parent that has it as a child.
+  // TODO(aleventhal) There may be similar cases for content inside a <head>,
+  // <style> or <script> -- for style/script the inner text might change.
+  // In those cases the nodes are most likely display:none, but if they are
+  // display locked we may not know that they are display:none.
+  if (node->IsInShadowTree()) {
+    AXObject* shadow_host = Get(node->OwnerShadowHost());
+    if (shadow_host && !shadow_host->CanHaveChildren())
+      return nullptr;
   }
 
   AXObject* new_obj = CreateFromNode(node);
@@ -526,26 +553,29 @@
   const AXID ax_id = AssociateAXID(new_obj, use_axid);
   DCHECK(!HashTraits<AXID>::IsDeletedValue(ax_id));
   node_object_mapping_.Set(node, ax_id);
-  new_obj->Init();
-  new_obj->SetLastKnownIsIgnoredValue(new_obj->AccessibilityIsIgnored());
-  new_obj->SetLastKnownIsIgnoredButIncludedInTreeValue(
-      new_obj->AccessibilityIsIgnoredButIncludedInTree());
-  MaybeNewRelationTarget(node, new_obj);
+  new_obj->Init(parent_if_known);
+  MaybeNewRelationTarget(*node, new_obj);
 
   return new_obj;
 }
 
 AXObject* AXObjectCacheImpl::GetOrCreate(LayoutObject* layout_object) {
+  return GetOrCreate(layout_object, nullptr);
+}
+
+AXObject* AXObjectCacheImpl::GetOrCreate(LayoutObject* layout_object,
+                                         AXObject* parent_if_known) {
   if (!layout_object)
     return nullptr;
 
   if (AXObject* obj = Get(layout_object))
     return obj;
 
-  return CreateAndInit(layout_object);
+  return CreateAndInit(layout_object, parent_if_known);
 }
 
 AXObject* AXObjectCacheImpl::CreateAndInit(LayoutObject* layout_object,
+                                           AXObject* parent_if_known,
                                            AXID use_axid) {
 #if DCHECK_IS_ON()
   DCHECK(layout_object);
@@ -560,6 +590,16 @@
   DCHECK(!node || IsLayoutObjectRelevantForAccessibility(node))
       << "Shouldn't get here if the layout object is not relevant for a11y";
 
+  // Return null if inside a shadow tree of something that can't have children,
+  // for example, an <img> has a user agent shadow root containing a <span> for
+  // the alt text. Do not create an accessible for that as it would be unable
+  // to have a parent that has it as a child.
+  if (node && node->IsInShadowTree()) {
+    AXObject* shadow_host = Get(node->OwnerShadowHost());
+    if (shadow_host && !shadow_host->CanHaveChildren())
+      return nullptr;
+  }
+
   // Prefer creating AXNodeObjects over AXLayoutObjects in locked subtrees
   // (e.g. content-visibility: auto), even if a LayoutObject is available,
   // because the LayoutObject is not guaranteed to be up-to-date (it might come
@@ -579,7 +619,7 @@
       // when there isn't. The locked subtree should not have AXLayoutObjects.
       return nullptr;
     }
-    return CreateAndInit(node, use_axid);
+    return CreateAndInit(node, parent_if_known, use_axid);
   }
 
   AXObject* new_obj = CreateFromRenderer(layout_object);
@@ -590,23 +630,29 @@
 
   const AXID axid = AssociateAXID(new_obj, use_axid);
   layout_object_mapping_.Set(layout_object, axid);
-  new_obj->Init();
-  new_obj->SetLastKnownIsIgnoredValue(new_obj->AccessibilityIsIgnored());
-  new_obj->SetLastKnownIsIgnoredButIncludedInTreeValue(
-      new_obj->AccessibilityIsIgnoredButIncludedInTree());
-  if (node)
-    MaybeNewRelationTarget(node, new_obj);
+  new_obj->Init(parent_if_known);
+  if (node)  // There may not be a node, e.g. for an anonymous block.
+    MaybeNewRelationTarget(*node, new_obj);
 
   return new_obj;
 }
 
-AXObject* AXObjectCacheImpl::GetOrCreate(
-    AbstractInlineTextBox* inline_text_box) {
+AXObject* AXObjectCacheImpl::GetOrCreate(AbstractInlineTextBox* inline_text_box,
+                                         AXObject* parent) {
   if (!inline_text_box)
     return nullptr;
 
-  if (AXObject* obj = Get(inline_text_box))
+  DCHECK(parent);
+  if (AXObject* obj = Get(inline_text_box)) {
+    if (obj->CachedParentObject()) {
+      // It is possible that the parent AXObject has changed, but the related
+      // text node will be the same.
+      // TODO(alventhal) When is the cached parent different?
+      DCHECK_EQ(obj->CachedParentObject()->GetNode(), parent->GetNode());
+    }
+    obj->SetParent(parent);
     return obj;
+  }
 
   AXObject* new_obj = CreateFromInlineTextBox(inline_text_box);
 
@@ -616,18 +662,17 @@
   const AXID axid = AssociateAXID(new_obj);
 
   inline_text_box_object_mapping_.Set(inline_text_box, axid);
-  new_obj->Init();
-  new_obj->SetLastKnownIsIgnoredValue(new_obj->AccessibilityIsIgnored());
-  new_obj->SetLastKnownIsIgnoredButIncludedInTreeValue(
-      new_obj->AccessibilityIsIgnoredButIncludedInTree());
+  new_obj->Init(parent);
   return new_obj;
 }
 
-AXObject* AXObjectCacheImpl::GetOrCreate(ax::mojom::blink::Role role) {
+AXObject* AXObjectCacheImpl::CreateAndInit(ax::mojom::blink::Role role,
+                                           AXObject* parent) {
+  DCHECK(parent);
   AXObject* obj = nullptr;
 
   switch (role) {
-    case ax::mojom::Role::kMenuListPopup:
+    case ax::mojom::blink::Role::kMenuListPopup:
       DCHECK(use_ax_menu_list_);
       obj = MakeGarbageCollected<AXMenuListPopup>(*this);
       break;
@@ -640,7 +685,7 @@
 
   AssociateAXID(obj);
 
-  obj->Init();
+  obj->Init(parent);
   return obj;
 }
 
@@ -869,12 +914,18 @@
     return;
   }
 
+#if DCHECK_IS_ON()
   DCHECK(!tree_update_document->GetPage()->Animator().IsServicingAnimations() ||
          (tree_update_document->Lifecycle().GetState() <
               DocumentLifecycle::kInAccessibility ||
           tree_update_document->Lifecycle().StateAllowsDetach()))
       << "DeferTreeUpdateInternal should only be outside of the lifecycle or "
-         "before the accessibility state.";
+         "before the accessibility state:"
+      << "\n* IsServicingAnimations: "
+      << tree_update_document->GetPage()->Animator().IsServicingAnimations()
+      << "\n* Lifecycle: " << tree_update_document->Lifecycle().ToString();
+#endif
+
   tree_update_callback_queue_.push_back(MakeGarbageCollected<TreeUpdateParams>(
       obj->GetNode(), obj->AXObjectID(), ComputeEventFrom(),
       ActiveEventIntents(), std::move(callback)));
@@ -905,12 +956,18 @@
     return;
   }
 
+#if DCHECK_IS_ON()
   DCHECK(!tree_update_document.GetPage()->Animator().IsServicingAnimations() ||
          (tree_update_document.Lifecycle().GetState() <
               DocumentLifecycle::kInAccessibility ||
           tree_update_document.Lifecycle().StateAllowsDetach()))
       << "DeferTreeUpdateInternal should only be outside of the lifecycle or "
-         "before the accessibility state.";
+         "before the accessibility state:"
+      << "\n* IsServicingAnimations: "
+      << tree_update_document.GetPage()->Animator().IsServicingAnimations()
+      << "\n* Lifecycle: " << tree_update_document.Lifecycle().ToString();
+#endif
+
   tree_update_callback_queue_.push_back(MakeGarbageCollected<TreeUpdateParams>(
       node, 0, ComputeEventFrom(), ActiveEventIntents(), std::move(callback)));
 
@@ -1095,7 +1152,7 @@
   }
 
   if (optional_node_for_relation_update)
-    relation_cache_->UpdateRelatedTree(optional_node_for_relation_update);
+    relation_cache_->UpdateRelatedTree(optional_node_for_relation_update, obj);
 }
 
 void AXObjectCacheImpl::TextChangedWithCleanLayout(Node* node) {
@@ -1167,7 +1224,7 @@
   if (AXObject::HasARIAOwns(element))
     HandleAttributeChangedWithCleanLayout(html_names::kAriaOwnsAttr, element);
 
-  MaybeNewRelationTarget(node, Get(node));
+  MaybeNewRelationTarget(*node, Get(node));
 
   // Even if the node or parent are ignored, an ancestor may need to include
   // descendants of the attached node, thus ChildrenChangedWithCleanLayout()
@@ -1190,6 +1247,11 @@
   }
 }
 
+void AXObjectCacheImpl::ChildrenChanged(AXObject* obj) {
+  DeferTreeUpdate(&AXObjectCacheImpl::ChildrenChangedWithCleanLayout,
+                  obj->GetNode(), obj);
+}
+
 void AXObjectCacheImpl::ChildrenChanged(Node* node) {
   if (!node)
     return;
@@ -1302,7 +1364,7 @@
 
   if (optional_node) {
     ContainingTableRowsOrColsMaybeChanged(optional_node);
-    relation_cache_->UpdateRelatedTree(optional_node);
+    relation_cache_->UpdateRelatedTree(optional_node, obj);
   }
 }
 
@@ -1362,19 +1424,27 @@
   // Returns the new object.
   auto refresh = [this](AXObject* current) {
     Node* node = current->GetNode();
+    AXObject* cached_parent = current->CachedParentObject();
     DCHECK(node) << "Refresh() is currently only supported for objects "
                     "with a backing node";
     AXID retained_axid = current->AXObjectID();
     // Remove from relevant maps, but not from relation cache, as the relations
     // between AXIDs will still the same.
     node_object_mapping_.erase(node);
-    if (current->GetLayoutObject())
+    if (current->GetLayoutObject()) {
       layout_object_mapping_.erase(current->GetLayoutObject());
+    } else if (node->GetLayoutObject()) {
+      DCHECK(!layout_object_mapping_.at(node->GetLayoutObject()))
+          << node << " " << node->GetLayoutObject();
+    }
     current->Detach();
     // TODO(accessibility) We don't use the return value, can we use .erase()
     // and it will still make sure that the object is cleaned up?
     objects_.Take(retained_axid);
-    return CreateAndInit(node, retained_axid);
+    AXObject* new_object = CreateAndInit(node, cached_parent, retained_axid);
+    if (!new_object)
+      RemoveAXID(current);  // Failed to create, so remove object completely.
+    return new_object;
   };
 
   while (!invalidated_ids_.IsEmpty()) {
@@ -1389,25 +1459,50 @@
       if (object->GetDocument() != &document) {
         // Wrong document -- this AXObjectCache processes the current popup
         // document too. Keep the ID around until its document is processed.
+        DCHECK(!HashTraits<AXID>::IsDeletedValue(ax_id));
         wrong_document_invalidated_ids.insert(ax_id);
         continue;
       }
 
       bool did_use_layout_object_traversal =
           object->ShouldUseLayoutObjectTraversalForChildren();
-      AXObject* parent = object->ParentObjectIncludedInTree();
+
+      // Invalidate children on the first available non-detached parent that is
+      // included in the tree. Sometimes a cached parent is detached because
+      // an object was detached in the middle of the tree, and cached parents
+      // are not corrected until the call to UpdateChildrenIfNecessary() below.
+      AXObject* parent = object;
+      while (true) {
+        AXObject* candidate_parent = parent->CachedParentObject();
+        if (!candidate_parent || candidate_parent->IsDetached()) {
+          // The cached parent pointed to a detached AXObject. Compute a new
+          // candidate parent and repair the cached parent now, so that
+          // refreshing and initializing the new object can occur (a parent is
+          // required).
+          candidate_parent = parent->ComputeParent();
+          parent->SetParent(candidate_parent);
+        }
+
+        if (!candidate_parent)
+          break;  // No higher candidate parent found, will invalidate |parent|.
+
+        parent = candidate_parent;
+        // Queue up a ChildrenChanged() call for this parent.
+        pending_children_changed_ids.insert(parent->AXObjectID());
+        if (parent->LastKnownIsIncludedInTreeValue())
+          break;  // Stop here (otherwise continue to higher ancestor).
+      }
+
       AXObject* new_object = refresh(object);
 
       // Children might change because child traversal style changed.
-      if (new_object->ShouldUseLayoutObjectTraversalForChildren() !=
-          did_use_layout_object_traversal) {
-        // TODO(accessibility) Need test for this, e.g. for continuations.
+      if (new_object &&
+          new_object->ShouldUseLayoutObjectTraversalForChildren() !=
+              did_use_layout_object_traversal) {
+        // TODO(accessibility) Need test for this.
+        DCHECK(!HashTraits<AXID>::IsDeletedValue(ax_id));
         pending_children_changed_ids.insert(ax_id);
       }
-
-      // Queue up a ChildrenChanged() call for this parent.
-      if (parent && parent != object)
-        pending_children_changed_ids.insert(parent->AXObjectID());
     }
     // Update parents' children.
     for (AXID parent_id : pending_children_changed_ids) {
@@ -1606,13 +1701,12 @@
   if (event_type == ax::mojom::blink::Event::kChildrenChanged &&
       obj->CachedParentObject()) {
     const bool was_ignored = obj->LastKnownIsIgnoredValue();
-    const bool was_ignored_but_included_in_tree =
-        obj->LastKnownIsIgnoredButIncludedInTreeValue();
-    bool is_ignored_changed =
-        was_ignored != obj->AccessibilityIsIgnored() ||
-        was_ignored_but_included_in_tree !=
-            obj->AccessibilityIsIgnoredButIncludedInTree();
-    if (is_ignored_changed)
+    const bool was_in_tree = obj->LastKnownIsIncludedInTreeValue();
+    obj->UpdateCachedAttributeValuesIfNeeded(false);
+    const bool is_ignored = obj->LastKnownIsIgnoredValue();
+    const bool is_in_tree = obj->LastKnownIsIncludedInTreeValue();
+
+    if (is_ignored != was_ignored || was_in_tree != is_in_tree)
       ChildrenChangedWithCleanLayout(nullptr, obj->CachedParentObject());
   }
 }
@@ -1769,23 +1863,28 @@
 }
 
 // This might be the new target of a relation. Handle all possible cases.
-void AXObjectCacheImpl::MaybeNewRelationTarget(Node* node, AXObject* obj) {
+void AXObjectCacheImpl::MaybeNewRelationTarget(Node& node, AXObject* obj) {
   // Track reverse relations
-  relation_cache_->UpdateRelatedTree(node);
+  relation_cache_->UpdateRelatedTree(&node, obj);
 
   if (!obj)
     return;
 
-  // Check whether aria-activedescendant on a focused object points to |obj|.
-  // If so, fire activedescendantchanged event now.
-  // This is only for ARIA active descendants, not in a native control like a
-  // listbox, which has its own initial active descendant handling.
+  DCHECK_EQ(obj->GetNode(), &node);
+
+  // Check whether aria-activedescendant on the focused object points to
+  // |obj|. If so, fire activedescendantchanged event now. This is only for
+  // ARIA active descendants, not in a native control like a listbox, which
+  // has its own initial active descendant handling.
   Node* focused_node = document_->FocusedElement();
   if (focused_node) {
     AXObject* focus = Get(focused_node);
-    if (focus && focus->ActiveDescendant() == obj &&
-        obj->CanBeActiveDescendant())
+    if (focus &&
+        focus->GetAOMPropertyOrARIAAttribute(
+            AOMRelationProperty::kActiveDescendant) == &node &&
+        obj->CanBeActiveDescendant()) {
       focus->HandleActiveDescendantChanged();
+    }
   }
 }
 
@@ -1861,7 +1960,7 @@
              IsA<HTMLLabelElement>(*element)) {
     LabelChangedWithCleanLayout(element);
   } else if (attr_name == html_names::kIdAttr) {
-    MaybeNewRelationTarget(element, Get(element));
+    MaybeNewRelationTarget(*element, Get(element));
   } else if (attr_name == html_names::kTabindexAttr) {
     FocusableChangedWithCleanLayout(element);
   } else if (attr_name == html_names::kDisabledAttr ||
@@ -1924,7 +2023,7 @@
     DCHECK(message_ax_object);
     // Cache the validation message container for reuse.
     validation_message_axid_ = AssociateAXID(message_ax_object);
-    message_ax_object->Init();
+    message_ax_object->Init(Root());
     // Validation message alert object is a child of the document, as not all
     // form controls can have a child. Also, there are form controls such as
     // listbox that technically can have children, but they are probably not
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
index 8b1fe88..71a2867 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
@@ -93,6 +93,7 @@
   void SelectionChanged(Node*) override;
   void UpdateReverseRelations(const AXObject* relation_source,
                               const Vector<String>& target_ids);
+  void ChildrenChanged(AXObject*);
   void ChildrenChanged(Node*) override;
   void ChildrenChanged(const LayoutObject*) override;
   void ChildrenChanged(AccessibleNode*) override;
@@ -184,14 +185,17 @@
   AXObject* ObjectFromAXID(AXID id) const { return objects_.at(id); }
   AXObject* Root();
 
-  // Used for objects without backing elements.
-  AXObject* GetOrCreate(ax::mojom::blink::Role);
+  // Used for objects without backing DOM nodes, layout objects, etc.
+  AXObject* CreateAndInit(ax::mojom::blink::Role, AXObject* parent);
 
-  AXObject* GetOrCreate(AccessibleNode*);
-  AXObject* GetOrCreate(LayoutObject*) override;
-  AXObject* GetOrCreate(const Node*);
+  AXObject* GetOrCreate(AccessibleNode*, AXObject* parent_if_known);
+  AXObject* GetOrCreate(LayoutObject*, AXObject* parent_if_known) override;
+  AXObject* GetOrCreate(LayoutObject* layout_object);
+  AXObject* GetOrCreate(const Node*, AXObject* parent_if_known);
+  AXObject* GetOrCreate(Node*, AXObject* parent_if_known);
   AXObject* GetOrCreate(Node*);
-  AXObject* GetOrCreate(AbstractInlineTextBox*);
+  AXObject* GetOrCreate(const Node*);
+  AXObject* GetOrCreate(AbstractInlineTextBox*, AXObject* parent);
 
   AXID GetAXID(Node*) override;
   Element* GetElementFromAXID(AXID) override;
@@ -207,7 +211,9 @@
   void ChildrenChangedWithCleanLayout(Node* optional_node_for_relation_update,
                                       AXObject*);
 
-  void MaybeNewRelationTarget(Node* node, AXObject* obj);
+  // When an object is created or its id changes, this must be called so that
+  // the relation cache is updated.
+  void MaybeNewRelationTarget(Node& node, AXObject* obj);
 
   void HandleActiveDescendantChangedWithCleanLayout(Node*);
   void HandleRoleChangeWithCleanLayout(Node*);
@@ -292,6 +298,10 @@
 
   static bool UseAXMenuList() { return use_ax_menu_list_; }
 
+#if DCHECK_IS_ON()
+  bool HasBeenDisposed() { return has_been_disposed_; }
+#endif
+
   // Retrieves a vector of all AXObjects whose bounding boxes may have changed
   // since the last query. Clears the vector so that the next time it's
   // called, it will only retrieve objects that have changed since now.
@@ -313,8 +323,10 @@
 
   // Create an AXObject, and do not check if a previous one exists.
   // Also, initialize the object and add it to maps for later retrieval.
-  AXObject* CreateAndInit(Node*, AXID use_axid = 0);
-  AXObject* CreateAndInit(LayoutObject*, AXID use_axid = 0);
+  AXObject* CreateAndInit(Node*, AXObject* parent_if_known, AXID use_axid = 0);
+  AXObject* CreateAndInit(LayoutObject*,
+                          AXObject* parent_if_known,
+                          AXID use_axid = 0);
 
   // Mark object as invalid and needing to be refreshed when layout is clean.
   // Will result in a new object with the same AXID, and will also call
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_test.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_test.cc
index c6a8edc8..33dd355 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_test.cc
@@ -64,7 +64,7 @@
   static unsigned num_children_changed_calls_;
 
   void ChildrenChanged() final { num_children_changed_calls_++; }
-  AXObject* ComputeParent() const final { return nullptr; }
+  AXObject* ComputeParentImpl() const final { return nullptr; }
   Document* GetDocument() const final { return &AXObjectCache().GetDocument(); }
 };
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
index 01bb417f..9d74433 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
@@ -33,7 +33,7 @@
         << "Unclean document at lifecycle " << document.Lifecycle().ToString();
 #endif
     if (element.FastHasAttribute(html_names::kAriaOwnsAttr)) {
-      if (AXObject* owner = GetOrCreate(&element)) {
+      if (AXObject* owner = GetOrCreate(&element, nullptr)) {
         owner_ids_to_update_.insert(owner->AXObjectID());
       }
     }
@@ -141,8 +141,8 @@
       // back to its real parent in the tree by detaching it from its current
       // parent and calling childrenChanged on its real parent.
       removed_child->DetachFromParent();
-      AXObject* real_parent = removed_child->ParentObjectIncludedInTree();
-
+      // Recompute the real parent and cache it.
+      AXObject* real_parent = removed_child->ParentObject();
       ChildrenChanged(real_parent);
     }
   }
@@ -160,6 +160,7 @@
     // on the original parent so that it can recompute its list of children.
     AXObject* original_parent = added_child->ParentObject();
     added_child->DetachFromParent();
+    added_child->SetParent(const_cast<AXObject*>(owner));
     ChildrenChanged(original_parent);
   }
 }
@@ -174,14 +175,14 @@
 
   Vector<String> owned_id_vector;
   for (const auto& element : attr_associated_elements) {
-    AXObject* child = GetOrCreate(element);
+    AXObject* child = GetOrCreate(element, owner);
 
     // TODO(meredithl): Determine how to update reverse relations for elements
     // without an id.
     if (element->GetIdAttribute())
       owned_id_vector.push_back(element->GetIdAttribute());
     if (IsValidOwnsRelation(const_cast<AXObject*>(owner), child))
-      validated_owned_children_result.push_back(GetOrCreate(element));
+      validated_owned_children_result.push_back(GetOrCreate(element, owner));
   }
 
   // Track reverse relations for future tree updates.
@@ -243,7 +244,7 @@
     Vector<AXID> validated_owned_child_axids;
     for (const String& id_name : owned_id_vector) {
       Element* child_element = scope.getElementById(AtomicString(id_name));
-      AXObject* child = GetOrCreate(child_element);
+      AXObject* child = GetOrCreate(child_element, owner);
       if (IsValidOwnsRelation(const_cast<AXObject*>(owner), child))
         owned_children.push_back(child);
     }
@@ -274,18 +275,22 @@
   UnmapOwnedChildren(owner, current_child_axids);
   MapOwnedChildren(owner, validated_owned_child_axids);
 
+#if DCHECK_IS_ON()
+  // Owned children must be in tree to avoid serialization issues.
+  for (AXObject* child : validated_owned_children_result) {
+    DCHECK(child->AccessibilityIsIncludedInTree())
+        << "Owned child not in tree: " << child->ToString(true, false)
+        << "\nRecompute included in tree: "
+        << child->ComputeAccessibilityIsIgnoredButIncludedInTree();
+  }
+#endif
+
   // Finally, update the mapping from the owner to the list of child IDs.
   aria_owner_to_children_mapping_.Set(owner->AXObjectID(),
                                       validated_owned_child_axids);
 
   ChildrenChanged(owner);
-
-#if DCHECK_IS_ON()
-  // Owned children must be in tree to avoid serialization issues.
-  for (AXObject* child : validated_owned_children_result) {
-    DCHECK(child->AccessibilityIsIncludedInTree());
-  }
-#endif
+  owner->UpdateChildrenIfNecessary();
 }
 
 bool AXRelationCache::MayHaveHTMLLabelViaForAttribute(
@@ -318,9 +323,11 @@
   }
 }
 
-void AXRelationCache::UpdateRelatedTree(Node* node) {
+void AXRelationCache::UpdateRelatedTree(Node* node, AXObject* obj) {
   HeapVector<Member<AXObject>> related_sources;
-  AXObject* related_target = Get(node);
+  DCHECK(node);
+  DCHECK(!obj || Get(node) == obj);
+  AXObject* related_target = obj ? obj : Get(node);
   // If it's already owned, schedule an update on the owner.
   if (related_target && IsAriaOwned(related_target)) {
     AXObject* owned_parent = GetAriaOwnedParent(related_target);
@@ -407,8 +414,8 @@
   return object_cache_->Get(node);
 }
 
-AXObject* AXRelationCache::GetOrCreate(Node* node) {
-  return object_cache_->GetOrCreate(node);
+AXObject* AXRelationCache::GetOrCreate(Node* node, const AXObject* owner) {
+  return object_cache_->GetOrCreate(node, const_cast<AXObject*>(owner));
 }
 
 void AXRelationCache::ChildrenChanged(AXObject* object) {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h
index a7619b7..6fce88df 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h
@@ -45,7 +45,9 @@
   // just changed, check to see if another object wants to be its parent due to
   // aria-owns. If so, add it to a queue of ids to process later during
   // ProcessUpdatesWithCleanLayout.
-  void UpdateRelatedTree(Node*);
+  // |node| is not optional.
+  // |obj| is optional. If provided, it must match the AXObject for |node|.
+  void UpdateRelatedTree(Node* node, AXObject* obj);
 
   // Remove given AXID from cache.
   void RemoveAXID(AXID);
@@ -146,7 +148,7 @@
 
   // Helpers that call back into object cache
   AXObject* ObjectFromAXID(AXID) const;
-  AXObject* GetOrCreate(Node*);
+  AXObject* GetOrCreate(Node*, const AXObject* owner);
   AXObject* Get(Node*);
   void ChildrenChanged(AXObject*);
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_validation_message.cc b/third_party/blink/renderer/modules/accessibility/ax_validation_message.cc
index 9458d0b..15eed4a 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_validation_message.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_validation_message.cc
@@ -18,10 +18,6 @@
 
 AXValidationMessage::~AXValidationMessage() {}
 
-AXObject* AXValidationMessage::ComputeParent() const {
-  return AXObjectCache().Root();
-}
-
 bool AXValidationMessage::ComputeAccessibilityIsIgnored(
     IgnoredReasons* ignored_reasons) const {
   return false;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_validation_message.h b/third_party/blink/renderer/modules/accessibility/ax_validation_message.h
index a589c4e6..fd174fdb 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_validation_message.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_validation_message.h
@@ -26,7 +26,6 @@
   // AXObject:
   bool CanHaveChildren() const override { return false; }
   bool ComputeAccessibilityIsIgnored(IgnoredReasons* = nullptr) const override;
-  AXObject* ComputeParent() const override;
   void GetRelativeBounds(AXObject** out_container,
                          FloatRect& out_bounds_in_container,
                          SkMatrix44& out_container_transform,
diff --git a/third_party/blink/renderer/modules/accessibility/ax_virtual_object.cc b/third_party/blink/renderer/modules/accessibility/ax_virtual_object.cc
index 977db453..b31a2e63 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_virtual_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_virtual_object.cc
@@ -27,6 +27,15 @@
 }
 
 void AXVirtualObject::AddChildren() {
+#if DCHECK_IS_ON()
+  DCHECK(!IsDetached());
+  DCHECK(!is_adding_children_) << " Reentering method on " << GetNode();
+  base::AutoReset<bool> reentrancy_protector(&is_adding_children_, true);
+  DCHECK_EQ(children_.size(), 0U)
+      << "Parent still has " << children_.size() << " children before adding:"
+      << "\nParent is " << ToString(true, true) << "\nFirst child is "
+      << children_[0]->ToString(true, true);
+#endif
   if (!accessible_node_)
     return;
 
@@ -34,12 +43,13 @@
   have_children_ = true;
 
   for (const auto& child : accessible_node_->GetChildren()) {
-    AXObject* ax_child = AXObjectCache().GetOrCreate(child);
+    AXObject* ax_child = AXObjectCache().GetOrCreate(child, this);
     if (!ax_child)
       continue;
+    DCHECK(!ax_child->IsDetached());
+    DCHECK(ax_child->AccessibilityIsIncludedInTree());
 
     children_.push_back(ax_child);
-    ax_child->SetParent(this);
   }
 }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_virtual_object.h b/third_party/blink/renderer/modules/accessibility/ax_virtual_object.h
index 4049d30..e03b75fe 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_virtual_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_virtual_object.h
@@ -21,7 +21,6 @@
 
   // AXObject overrides.
   void Detach() override;
-  AXObject* ComputeParent() const override { return parent_; }
   bool IsVirtualObject() const override { return true; }
   void AddChildren() override;
   void ChildrenChanged() override;
diff --git a/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc b/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc
index 071f5584..9861aa6b 100644
--- a/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc
+++ b/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc
@@ -24,6 +24,7 @@
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/fetch/body_stream_buffer.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
+#include "third_party/blink/renderer/core/streams/readable_stream.h"
 #include "third_party/blink/renderer/modules/service_worker/cross_origin_resource_policy_checker.h"
 #include "third_party/blink/renderer/modules/service_worker/fetch_event.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h"
@@ -375,6 +376,11 @@
 
 void FetchRespondWithObserver::OnNoResponse() {
   DCHECK(GetExecutionContext());
+  if (request_body_stream_ && (request_body_stream_->IsLocked() ||
+                               request_body_stream_->IsDisturbed())) {
+    GetExecutionContext()->CountUse(
+        WebFeature::kFetchRespondWithNoResponseWithUsedRequestBody);
+  }
   ServiceWorkerGlobalScope* service_worker_global_scope =
       To<ServiceWorkerGlobalScope>(GetExecutionContext());
   service_worker_global_scope->RespondToFetchEventWithNoResponse(
@@ -382,6 +388,17 @@
   event_->ResolveHandledPromise();
 }
 
+void FetchRespondWithObserver::SetEvent(FetchEvent* event) {
+  DCHECK(!event_);
+  DCHECK(!request_body_stream_);
+  event_ = event;
+  // We don't use Body::body() in order to avoid accidental CountUse calls.
+  BodyStreamBuffer* body_buffer = event_->request()->BodyBuffer();
+  if (body_buffer) {
+    request_body_stream_ = body_buffer->Stream();
+  }
+}
+
 FetchRespondWithObserver::FetchRespondWithObserver(
     ExecutionContext* context,
     int fetch_event_id,
@@ -399,6 +416,7 @@
 
 void FetchRespondWithObserver::Trace(Visitor* visitor) const {
   visitor->Trace(event_);
+  visitor->Trace(request_body_stream_);
   RespondWithObserver::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.h b/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.h
index 2de7884..0ba6bf6 100644
--- a/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.h
+++ b/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.h
@@ -18,6 +18,7 @@
 class CrossOriginResourcePolicyChecker;
 class ExecutionContext;
 class FetchEvent;
+class ReadableStream;
 class ScriptValue;
 class WaitUntilObserver;
 
@@ -47,10 +48,7 @@
                            const char* property_name) override;
   void OnNoResponse() override;
 
-  void SetEvent(FetchEvent* event) {
-    DCHECK(!event_);
-    event_ = event;
-  }
+  void SetEvent(FetchEvent* event);
 
   void Trace(Visitor*) const override;
 
@@ -61,6 +59,7 @@
   const mojom::RequestContextFrameType frame_type_;
   const network::mojom::RequestDestination request_destination_;
   Member<FetchEvent> event_;
+  Member<ReadableStream> request_body_stream_;
   base::WeakPtr<CrossOriginResourcePolicyChecker> corp_checker_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 };
diff --git a/third_party/blink/renderer/modules/webaudio/realtime_audio_worklet_thread.cc b/third_party/blink/renderer/modules/webaudio/realtime_audio_worklet_thread.cc
index d4f8ece8..8b7afe0 100644
--- a/third_party/blink/renderer/modules/webaudio/realtime_audio_worklet_thread.cc
+++ b/third_party/blink/renderer/modules/webaudio/realtime_audio_worklet_thread.cc
@@ -32,8 +32,12 @@
     // TODO(crbug.com/1022888): The worklet thread priority is always NORMAL on
     // Linux and Chrome OS regardless of this thread priority setting.
     params.thread_priority = base::ThreadPriority::REALTIME_AUDIO;
+    TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("audio-worklet"),
+                 "RealtimeAudioWorkletThread() - REALTIME_AUDIO");
   } else {
     params.thread_priority = base::ThreadPriority::NORMAL;
+    TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("audio-worklet"),
+                 "RealtimeAudioWorkletThread() - NORMAL");
   }
 
   if (++s_ref_count_ == 1)
diff --git a/third_party/blink/renderer/platform/graphics/OWNERS b/third_party/blink/renderer/platform/graphics/OWNERS
index 8f1c634..457035c 100644
--- a/third_party/blink/renderer/platform/graphics/OWNERS
+++ b/third_party/blink/renderer/platform/graphics/OWNERS
@@ -18,14 +18,10 @@
 # lowLatency canvas
 mcasas@chromium.org
 
-# canvas resources and images
-khushalsagar@chromium.org
-
-
 # Video SurfaceLayer functionality.
 per-file video_frame*=file://media/OWNERS
 per-file video_frame*=mlamouri@chromium.org
 per-file video_frame*=lethalantidote@chromium.org
 per-file surface_layer_bridge*=file://media/OWNERS
 per-file surface_layer_bridge*=mlamouri@chromium.org
-per-file surface_layer_bridge*=lethalantidote@chromium.org
\ No newline at end of file
+per-file surface_layer_bridge*=lethalantidote@chromium.org
diff --git a/third_party/blink/renderer/platform/graphics/gpu/OWNERS b/third_party/blink/renderer/platform/graphics/gpu/OWNERS
index 10e108e..1bac549 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/OWNERS
+++ b/third_party/blink/renderer/platform/graphics/gpu/OWNERS
@@ -12,4 +12,4 @@
 klausw@chromium.org
 
 # SharedGpuContext, ImageLayerBridge, etc.
-khushalsagar@chromium.org
\ No newline at end of file
+sunnyps@chromium.org
diff --git a/third_party/blink/renderer/platform/heap/impl/persistent.h b/third_party/blink/renderer/platform/heap/impl/persistent.h
index 0c12b71..40388bd 100644
--- a/third_party/blink/renderer/platform/heap/impl/persistent.h
+++ b/third_party/blink/renderer/platform/heap/impl/persistent.h
@@ -39,19 +39,11 @@
   base::Location location_;
 };
 
-#if !BUILDFLAG(FROM_HERE_USES_LOCATION_BUILTINS) && \
-    BUILDFLAG(RAW_HEAP_SNAPSHOTS)
-#if !BUILDFLAG(ENABLE_LOCATION_SOURCE)
-#define PERSISTENT_FROM_HERE \
-  PersistentLocation(::base::Location::CreateFromHere(__FILE__))
-#else
-#define PERSISTENT_FROM_HERE \
-  PersistentLocation(        \
-      ::base::Location::CreateFromHere(__func__, __FILE__, __LINE__))
-#endif
-#else
+#if BUILDFLAG(RAW_HEAP_SNAPSHOTS)
+#define PERSISTENT_FROM_HERE PersistentLocation(base::Location::Current())
+#else  // !RAW_HEAP_SNAPSHOTS
 #define PERSISTENT_FROM_HERE PersistentLocation()
-#endif  // BUILDFLAG(RAW_HEAP_SNAPSHOTS)
+#endif  // !RAW_HEAP_SNAPSHOTS
 
 template <typename T,
           WeaknessPersistentConfiguration weaknessConfiguration,
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 3f7f5e5..5dbe6093 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -922,9 +922,7 @@
     // For simulating Android's overlay fullscreen video in web tests on Linux.
     {
       name: "ForcedColors",
-      // No status because this blink runtime feature doesn't work by itself.
-      // It's controlled by the corresponding Chromium feature which needs to
-      // be enabled to make the whole feature work.
+      status: "stable",
     },
     {
       name: "ForceDeferScriptIntervention",
@@ -1068,7 +1066,7 @@
       // provides a convenient way for testing legacy layout code path in blink
       // unit tests.
       name: "LayoutNG",
-      implied_by: ["LayoutNGGrid", "BidiCaretAffinity"],
+      implied_by: ["LayoutNGGrid", "BidiCaretAffinity", "CSSContainerQueries"],
       status: "stable",
     },
     {
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin.cc b/third_party/blink/renderer/platform/weborigin/security_origin.cc
index 6153335..0fd156a1 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_origin.cc
@@ -438,48 +438,9 @@
   // //services/network/public/cpp/is_potentially_trustworthy.h).
 
   DCHECK_NE(protocol_, "data");
-
-  // The code below is based on the specification at
-  // https://w3c.github.io/webappsec-secure-contexts/#potentially-trustworthy-origin
-
-  // 1. If origin is an opaque origin, return "Not Trustworthy".
   if (IsOpaque())
     return is_opaque_origin_potentially_trustworthy_;
-
-  // 2. Assert: origin is a tuple origin.
-  DCHECK(!IsOpaque());
-
-  // 3. If origin’s scheme is either "https" or "wss", return "Potentially
-  //    Trustworthy".
-  // This is handled by the url::GetSecureSchemes() call below.
-
-  // 4. If origin’s host component matches one of the CIDR notations 127.0.0.0/8
-  //    or ::1/128 [RFC4632], return "Potentially Trustworthy".
-  // 5. If origin’s host component is "localhost" or falls within ".localhost",
-  //    and the user agent conforms to the name resolution rules in
-  //    [let-localhost-be-localhost], return "Potentially Trustworthy".
-  if (IsLocalhost())
-    return true;
-
-  // 6. If origin’s scheme component is file, return "Potentially Trustworthy".
-  // This is handled by the IsLocal() call below.
-
-  // 7. If origin’s scheme component is one which the user agent considers to be
-  //    authenticated, return "Potentially Trustworthy".
-  //    Note: See §7.1 Packaged Applications for detail here.
-  //
-  if (base::Contains(url::GetSecureSchemes(), protocol_.Ascii()) || IsLocal())
-    return true;
-
-  // 8. If origin has been configured as a trustworthy origin, return
-  //    "Potentially Trustworthy".
-  //    Note: See §7.2 Development Environments for detail here.
-  if (network::SecureOriginAllowlist::GetInstance().IsOriginAllowlisted(
-          ToUrlOrigin()))
-    return true;
-
-  // 9. Return "Not Trustworthy".
-  return false;
+  return network::IsOriginPotentiallyTrustworthy(ToUrlOrigin());
 }
 
 // static
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index 438881c..8265e425 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -670,7 +670,6 @@
 crbug.com/1035582 paint/invalidation/invalidate-caret-before-text-node-update.html [ Failure ]
 crbug.com/1035582 paint/invalidation/outline/focus-continuations.html [ Failure ]
 crbug.com/1035582 paint/invalidation/outline/focus-enable-continuations.html [ Failure ]
-crbug.com/1035582 paint/invalidation/outline/focus-ring-on-child-move.html [ Failure ]
 crbug.com/1035582 paint/invalidation/outline/focus-ring-on-continuation-move.html [ Failure ]
 crbug.com/1035582 paint/invalidation/outline/focus-ring-on-inline-continuation-move.html [ Failure ]
 crbug.com/1035582 paint/invalidation/outline/outline-become-affected-by-descendant.html [ Failure ]
diff --git a/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility b/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility
index 130e8a3..77a402f 100644
--- a/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility
+++ b/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility
@@ -40,17 +40,6 @@
 fast/css-generated-content/after-with-first-letter-float-crash.html [ Crash ]
 fast/css-generated-content/table-row-after-no-crash.html [ Crash ]
 fast/css-generated-content/positioned-div-with-floating-after-content-crash.html [ Crash ]
-fast/css/text-overflow-ellipsis-text-align-right.html [ Crash ]
-fast/css/text-overflow-ellipsis-strict.html [ Crash ]
-fast/css/text-overflow-ellipsis-block-with-border-and-padding.html [ Crash ]
-fast/css/vertical-text-overflow-ellipsis-text-align-justify.html [ Crash ]
-fast/css/vertical-text-overflow-ellipsis-text-align-center.html [ Crash ]
-fast/css/text-overflow-ellipsis.html [ Crash ]
-fast/css/text-overflow-ellipsis-text-align-left.html [ Crash ]
-fast/css/vertical-text-overflow-ellipsis-text-align-right.html [ Crash ]
-fast/css/text-overflow-ellipsis-text-align-center.html [ Crash ]
-fast/css/vertical-text-overflow-ellipsis-text-align-left.html [ Crash ]
-fast/css/text-overflow-ellipsis-text-align-justify.html [ Crash ]
 paint/markers/inline-spelling-markers-hidpi-composited.html [ Crash ]
 paint/markers/inline_spelling_markers.html [ Crash ]
 paint/markers/composition-marker-split.html [ Crash ]
@@ -63,10 +52,6 @@
 external/wpt/css/css-text/text-transform/text-transform-upperlower-044.html [ Crash ]
 external/wpt/css/css-text/text-transform/text-transform-upperlower-041.html [ Crash ]
 external/wpt/css/css-text/text-transform/text-transform-upperlower-043.html [ Crash ]
-external/wpt/css/css-ui/text-overflow-006.html [ Crash ]
-external/wpt/css/css-ui/text-overflow-013.html [ Crash ]
-external/wpt/css/css-ui/text-overflow-028.html [ Crash ]
-external/wpt/css/css-ui/text-overflow-027.html [ Crash ]
 external/wpt/css/css-overflow/webkit-line-clamp-031.html [ Crash ]
 external/wpt/css/css-overflow/webkit-line-clamp-034.html [ Crash ]
 external/wpt/css/css-overflow/webkit-line-clamp-025.html [ Crash ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index a7e05259..cf7d92fe 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -5879,3 +5879,6 @@
 crbug.com/1147859 external/wpt/css/css-pseudo/highlight-painting-order.html [ Failure ]
 
 crbug.com/1060837 virtual/plz-dedicated-worker/external/wpt/html/cross-origin-embedder-policy/dedicated-worker.https.html [ Timeout ]
+
+# Sheriff 2021-01-15
+crbug.com/1167210 [ Mac ] virtual/forced-high-contrast-colors/fast/css/forced-colors-mode/forced-colors-mode-15.html [ Pass Failure ]
\ No newline at end of file
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 2dab837..45ec3916 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -567,11 +567,6 @@
              "--disable-features=CookiesWithoutSameSiteMustBeSecure"]
   },
   {
-    "prefix": "forced-colors",
-    "bases": ["external/wpt/css/css-forced-color-adjust"],
-    "args": ["--enable-blink-features=ForcedColors"]
-  },
-  {
     "prefix": "forced-high-contrast-colors",
     "bases": ["external/wpt/forced-colors-mode",
               "html/details_summary/color-scheme-validation"],
diff --git a/third_party/blink/web_tests/accessibility/canvas-fallback-content.html b/third_party/blink/web_tests/accessibility/canvas-fallback-content.html
index 6b3c61d..aad37166 100644
--- a/third_party/blink/web_tests/accessibility/canvas-fallback-content.html
+++ b/third_party/blink/web_tests/accessibility/canvas-fallback-content.html
@@ -1,4 +1,4 @@
-<!DOCTYPE HTML>
+pc<!DOCTYPE HTML>
 <html>
 <body>
 <script src="../resources/testharness.js"></script>
@@ -39,7 +39,10 @@
 <div id="console"></div>
 <script>
   function check(id, expectedRole) {
+      console.log('Checking for ' + expectedRole + ' at #' + id);
       var axElement = accessibilityController.accessibleElementById(id);
+      assert_true(Boolean(axElement));
+      assert_equals(axElement.role, expectedRole, id);
       var element = document.getElementById(id);
       // TODO(crbug.com/930327): focus() triggers a lifecycle update without updating observers.
       // Make sure to get the axElement before calling focus(), to trigger a full update cycle.
@@ -77,10 +80,12 @@
 
     // Check that the role is updated when the element changes.
     document.getElementById('focusable1').setAttribute('role', 'button');
-    check("focusable1", "AXRole: AXButton", t);
-
     document.getElementById('focusable2').setAttribute('role', 'button');
-    check("focusable2", "AXRole: AXButton", t, true);
+    // Role changes do not take effect immediately.
+    requestAnimationFrame(() => {
+      check("focusable1", "AXRole: AXButton", t);
+      check("focusable2", "AXRole: AXButton", t,);
+    });
 }, "This test makes sure that focusable elements in canvas fallback content are accessible.");
 
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-forced-color-adjust/inheritance-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-forced-color-adjust/inheritance-expected.txt
deleted file mode 100644
index 2711c74..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-forced-color-adjust/inheritance-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL Property forced-color-adjust has initial value auto assert_true: forced-color-adjust doesn't seem to be supported in the computed style expected true got false
-FAIL Property forced-color-adjust inherits assert_true: forced-color-adjust doesn't seem to be supported in the computed style expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-forced-color-adjust/parsing/forced-color-adjust-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-forced-color-adjust/parsing/forced-color-adjust-computed-expected.txt
deleted file mode 100644
index 109a375145f6..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-forced-color-adjust/parsing/forced-color-adjust-computed-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL Property forced-color-adjust value 'auto' assert_true: forced-color-adjust doesn't seem to be supported in the computed style expected true got false
-FAIL Property forced-color-adjust value 'none' assert_true: forced-color-adjust doesn't seem to be supported in the computed style expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-forced-color-adjust/parsing/forced-color-adjust-valid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-forced-color-adjust/parsing/forced-color-adjust-valid-expected.txt
deleted file mode 100644
index d488b40..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-forced-color-adjust/parsing/forced-color-adjust-valid-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL e.style['forced-color-adjust'] = "none" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['forced-color-adjust'] = "auto" should set the property value assert_not_equals: property should be set got disallowed value ""
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/mediaqueries/forced-colors-expected.txt b/third_party/blink/web_tests/external/wpt/css/mediaqueries/forced-colors-expected.txt
deleted file mode 100644
index 8f3a9b998..0000000
--- a/third_party/blink/web_tests/external/wpt/css/mediaqueries/forced-colors-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-This is a testharness.js-based test.
-FAIL Should be parseable in a CSS stylesheet: '(forced-colors)' assert_true: expected true got false
-FAIL Should be parseable in a CSS stylesheet: '(forced-colors: none)' assert_true: expected true got false
-FAIL Should be parseable in a CSS stylesheet: '(forced-colors: active)' assert_true: expected true got false
-PASS Should not be parseable in a CSS stylesheet: '(forced-colors: 0)'
-PASS Should not be parseable in a CSS stylesheet: '(forced-colors: no-preference)'
-PASS Should not be parseable in a CSS stylesheet: '(forced-colors: 10px)'
-PASS Should not be parseable in a CSS stylesheet: '(forced-colors: active 0)'
-PASS Should not be parseable in a CSS stylesheet: '(forced-colors: none active)'
-PASS Should not be parseable in a CSS stylesheet: '(forced-colors: active/none)'
-FAIL Should be parseable in JS: '(forced-colors)' assert_true: expected true got false
-FAIL Should be parseable in JS: '(forced-colors: none)' assert_true: expected true got false
-FAIL Should be parseable in JS: '(forced-colors: active)' assert_true: expected true got false
-PASS Should not be parseable in JS: '(forced-colors: 0)'
-PASS Should not be parseable in JS: '(forced-colors: no-preference)'
-PASS Should not be parseable in JS: '(forced-colors: 10px)'
-PASS Should not be parseable in JS: '(forced-colors: active 0)'
-PASS Should not be parseable in JS: '(forced-colors: none active)'
-PASS Should not be parseable in JS: '(forced-colors: active/none)'
-FAIL Check that none evaluates to false in the boolean context assert_equals: expected true but got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/clients-matchall-blob-url-worker.https-expected.txt b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/clients-matchall-blob-url-worker.https-expected.txt
new file mode 100644
index 0000000..0065aa4a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/clients-matchall-blob-url-worker.https-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL Test Clients.matchAll() with a blob URL worker client. assert_equals: expected 1 but got 0
+FAIL Test Clients.matchAll() with an uncontrolled blob URL worker client. assert_equals: expected 1 but got 0
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/clients-matchall-blob-url-worker.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/clients-matchall-blob-url-worker.https.html
new file mode 100644
index 0000000..c29bac8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/clients-matchall-blob-url-worker.https.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<title>Service Worker: Clients.matchAll with a blob URL worker client</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<script>
+const SCRIPT = 'resources/clients-matchall-worker.js';
+
+promise_test(async (t) => {
+  const scope = 'resources/clients-matchall-blob-url-worker.html';
+
+  const reg = await service_worker_unregister_and_register(t, SCRIPT, scope);
+  t.add_cleanup(_ => reg.unregister());
+  await wait_for_state(t, reg.installing, 'activated');
+
+  const frame = await with_iframe(scope);
+  t.add_cleanup(_ => frame.remove());
+
+  {
+    const message = await frame.contentWindow.waitForWorker();
+    assert_equals(message.data, 'Worker is ready.',
+                  'Worker should reply to the message.');
+  }
+
+  const channel = new MessageChannel();
+  const message = await new Promise(resolve => {
+    channel.port1.onmessage = resolve;
+    frame.contentWindow.navigator.serviceWorker.controller.postMessage(
+      {port: channel.port2, options: {type: 'worker'}}, [channel.port2]);
+  });
+
+  checkMessageEvent(message);
+
+}, 'Test Clients.matchAll() with a blob URL worker client.');
+
+promise_test(async (t) => {
+  const scope = 'resources/blank.html';
+
+  const reg = await service_worker_unregister_and_register(t, SCRIPT, scope);
+  t.add_cleanup(_ => reg.unregister());
+  await wait_for_state(t, reg.installing, 'activated');
+
+  const workerScript = `
+    self.onmessage = (e) => {
+      self.postMessage("Worker is ready.");
+    };
+  `;
+  const blob = new Blob([workerScript], { type: 'text/javascript' });
+  const blobUrl = URL.createObjectURL(blob);
+  const worker = new Worker(blobUrl);
+
+  {
+    const message = await new Promise(resolve => {
+      worker.onmessage = resolve;
+      worker.postMessage("Ping to worker.");
+    });
+    assert_equals(message.data, 'Worker is ready.',
+                  'Worker should reply to the message.');
+  }
+
+  const channel = new MessageChannel();
+  const message = await new Promise(resolve => {
+    channel.port1.onmessage = resolve;
+    reg.active.postMessage(
+      {port: channel.port2,
+       options: {includeUncontrolled: true, type: 'worker'}},
+      [channel.port2]
+    );
+  });
+
+  checkMessageEvent(message);
+
+}, 'Test Clients.matchAll() with an uncontrolled blob URL worker client.');
+
+function checkMessageEvent(e) {
+  assert_equals(e.data.length, 1);
+
+  const workerClient = e.data[0];
+  assert_equals(workerClient[0], undefined); // visibilityState
+  assert_equals(workerClient[1], undefined); // focused
+  assert_true(workerClient[2].includes('blob:')); // url
+  assert_equals(workerClient[3], 'worker'); // type
+  assert_equals(workerClient[4], 'none'); // frameType
+}
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/clients-matchall-blob-url-worker.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/clients-matchall-blob-url-worker.html
new file mode 100644
index 0000000..ee89a0d8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/clients-matchall-blob-url-worker.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<script>
+const workerScript = `
+  self.onmessage = (e) => {
+    self.postMessage("Worker is ready.");
+  };
+`;
+const blob = new Blob([workerScript], { type: 'text/javascript' });
+const blobUrl = URL.createObjectURL(blob);
+const worker = new Worker(blobUrl);
+
+function waitForWorker() {
+  return new Promise(resolve => {
+    worker.onmessage = resolve;
+    worker.postMessage("Ping to worker.");
+  });
+}
+</script>
+</html>
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/outline/focus-ring-on-child-move-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/outline/focus-ring-on-child-move-expected.txt
deleted file mode 100644
index 2777e49..0000000
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/outline/focus-ring-on-child-move-expected.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutBlockFlow (positioned) DIV",
-          "rect": [99, 49, 302, 302],
-          "reason": "outline"
-        },
-        {
-          "object": "LayoutBlockFlow (positioned) DIV id='child'",
-          "rect": [300, 50, 20, 300],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutBlockFlow (positioned) DIV id='child'",
-          "rect": [150, 50, 20, 300],
-          "reason": "geometry"
-        }
-      ]
-    }
-  ]
-}
-
diff --git a/third_party/blink/web_tests/paint/invalidation/outline/focus-ring-on-child-move-expected.txt b/third_party/blink/web_tests/paint/invalidation/outline/focus-ring-on-child-move-expected.txt
index 4959aa2..c9d5757 100644
--- a/third_party/blink/web_tests/paint/invalidation/outline/focus-ring-on-child-move-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/outline/focus-ring-on-child-move-expected.txt
@@ -6,8 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [300, 50, 20, 300],
-        [150, 50, 20, 300]
+        [98, 48, 304, 304]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/outline/focus-ring-on-child-move-expected.txt b/third_party/blink/web_tests/platform/linux/paint/invalidation/outline/focus-ring-on-child-move-expected.txt
deleted file mode 100644
index c9d5757..0000000
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/outline/focus-ring-on-child-move-expected.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "invalidations": [
-        [98, 48, 304, 304]
-      ]
-    }
-  ]
-}
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/paint/invalidation/outline/focus-ring-on-child-move-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.14/paint/invalidation/outline/focus-ring-on-child-move-expected.txt
deleted file mode 100644
index c9d5757..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.14/paint/invalidation/outline/focus-ring-on-child-move-expected.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "invalidations": [
-        [98, 48, 304, 304]
-      ]
-    }
-  ]
-}
-
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/outline/focus-ring-on-child-move-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/outline/focus-ring-on-child-move-expected.txt
deleted file mode 100644
index c9d5757..0000000
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/outline/focus-ring-on-child-move-expected.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "invalidations": [
-        [98, 48, 304, 304]
-      ]
-    }
-  ]
-}
-
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/outline/focus-ring-on-child-move-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/outline/focus-ring-on-child-move-expected.txt
deleted file mode 100644
index c9d5757..0000000
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/outline/focus-ring-on-child-move-expected.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "invalidations": [
-        [98, 48, 304, 304]
-      ]
-    }
-  ]
-}
-
diff --git a/third_party/blink/web_tests/virtual/forced-colors/README.md b/third_party/blink/web_tests/virtual/forced-colors/README.md
deleted file mode 100644
index de185c88..0000000
--- a/third_party/blink/web_tests/virtual/forced-colors/README.md
+++ /dev/null
@@ -1 +0,0 @@
-This directory is for testing Forced Colors mode implementation.
diff --git a/third_party/blink/web_tests/virtual/forced-colors/external/wpt/css/css-forced-color-adjust/README.txt b/third_party/blink/web_tests/virtual/forced-colors/external/wpt/css/css-forced-color-adjust/README.txt
deleted file mode 100644
index de185c88..0000000
--- a/third_party/blink/web_tests/virtual/forced-colors/external/wpt/css/css-forced-color-adjust/README.txt
+++ /dev/null
@@ -1 +0,0 @@
-This directory is for testing Forced Colors mode implementation.
diff --git a/third_party/blink/web_tests/virtual/forced-colors/external/wpt/css/css-forced-color-adjust/inheritance-expected.txt b/third_party/blink/web_tests/virtual/forced-colors/external/wpt/css/css-forced-color-adjust/inheritance-expected.txt
deleted file mode 100644
index 80e63c66..0000000
--- a/third_party/blink/web_tests/virtual/forced-colors/external/wpt/css/css-forced-color-adjust/inheritance-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-PASS Property forced-color-adjust has initial value auto
-PASS Property forced-color-adjust inherits
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/forced-colors/external/wpt/css/css-forced-color-adjust/parsing/forced-color-adjust-computed-expected.txt b/third_party/blink/web_tests/virtual/forced-colors/external/wpt/css/css-forced-color-adjust/parsing/forced-color-adjust-computed-expected.txt
deleted file mode 100644
index d988ed8..0000000
--- a/third_party/blink/web_tests/virtual/forced-colors/external/wpt/css/css-forced-color-adjust/parsing/forced-color-adjust-computed-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-PASS Property forced-color-adjust value 'auto'
-PASS Property forced-color-adjust value 'none'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/forced-colors/external/wpt/css/css-forced-color-adjust/parsing/forced-color-adjust-valid-expected.txt b/third_party/blink/web_tests/virtual/forced-colors/external/wpt/css/css-forced-color-adjust/parsing/forced-color-adjust-valid-expected.txt
deleted file mode 100644
index 8a0eba0..0000000
--- a/third_party/blink/web_tests/virtual/forced-colors/external/wpt/css/css-forced-color-adjust/parsing/forced-color-adjust-valid-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-PASS e.style['forced-color-adjust'] = "none" should set the property value
-PASS e.style['forced-color-adjust'] = "auto" should set the property value
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/plz-dedicated-worker/external/wpt/service-workers/service-worker/clients-matchall-blob-url-worker.https-expected.txt b/third_party/blink/web_tests/virtual/plz-dedicated-worker/external/wpt/service-workers/service-worker/clients-matchall-blob-url-worker.https-expected.txt
new file mode 100644
index 0000000..4b89cc4
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/plz-dedicated-worker/external/wpt/service-workers/service-worker/clients-matchall-blob-url-worker.https-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+PASS Test Clients.matchAll() with a blob URL worker client.
+PASS Test Clients.matchAll() with an uncontrolled blob URL worker client.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
index 6c271cb..f5edaf826 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
@@ -173,6 +173,7 @@
 fontVariantNumeric
 fontVariationSettings
 fontWeight
+forcedColorAdjust
 gap
 getPropertyPriority
 getPropertyValue
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt
index 6e2de31..288cc68c 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt
@@ -190,6 +190,7 @@
     font-variant-numeric
     font-variation-settings
     font-weight
+    forced-color-adjust
     grid-auto-columns
     grid-auto-flow
     grid-auto-rows
diff --git a/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt b/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
index 280292379..8c40cef 100644
--- a/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
+++ b/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
@@ -180,6 +180,7 @@
 fontVariantNumeric
 fontVariationSettings
 fontWeight
+forcedColorAdjust
 gap
 getPropertyPriority
 getPropertyValue
diff --git a/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
index 9cce4c6..fd1e2f5 100644
--- a/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
@@ -192,6 +192,7 @@
     font-variant-numeric
     font-variation-settings
     font-weight
+    forced-color-adjust
     grid-auto-columns
     grid-auto-flow
     grid-auto-rows
diff --git a/third_party/r8/README.chromium b/third_party/r8/README.chromium
index 5b2d55d4..2cc564e 100644
--- a/third_party/r8/README.chromium
+++ b/third_party/r8/README.chromium
@@ -1,7 +1,7 @@
 Name: R8
 URL: https://r8.googlesource.com/r8
-Revision: 1d0cad3ec27fc88a1ca4ffc4d3d4d5d0b3f80f5b
-Version: Unknown
+Revision: 120900065d05300341e1dbd89feee4354f525468
+Version: 3.0.12-dev
 License: BSD 3-Clause
 License File: NOT_SHIPPED
 Security Critical: no
diff --git a/third_party/r8/patches/0001-Desugaring-Make-all-lambdas-stateless-fix-naming-sch.patch b/third_party/r8/patches/0001-Desugaring-Make-all-lambdas-stateless-fix-naming-sch.patch
index b6b1dd2..55440801b 100644
--- a/third_party/r8/patches/0001-Desugaring-Make-all-lambdas-stateless-fix-naming-sch.patch
+++ b/third_party/r8/patches/0001-Desugaring-Make-all-lambdas-stateless-fix-naming-sch.patch
@@ -1,4 +1,4 @@
-From 43c8250f2420c41640305d68b9d4512d69b850b0 Mon Sep 17 00:00:00 2001
+From 5a248a7f422c11f6e41e1c8684d207f3c2953b3b Mon Sep 17 00:00:00 2001
 From: Sam Maier <smaier@chromium.org>
 Date: Tue, 19 May 2020 15:55:44 -0400
 Subject: [PATCH 1/3] Desugaring: Make all lambdas stateless & fix naming
@@ -12,10 +12,10 @@
  1 file changed, 3 insertions(+), 2 deletions(-)
 
 diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
-index c9e47c259..b5b72c0ab 100644
+index a50581ee0..0d166d275 100644
 --- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
 +++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
-@@ -130,7 +130,8 @@ public final class LambdaClass {
+@@ -134,7 +134,8 @@ public final class LambdaClass {
      // If the lambda class should match 1:1 the class it is accessed from, we
      // just add the name of this type to make lambda class name unique.
      // It also helps link the class lambda originated from in some cases.
@@ -25,7 +25,7 @@
        lambdaClassDescriptor.append(accessedFrom.getHolderType().getName()).append('$');
      }
  
-@@ -209,7 +210,7 @@ public final class LambdaClass {
+@@ -213,7 +214,7 @@ public final class LambdaClass {
    }
  
    public final boolean isStateless() {
@@ -35,5 +35,5 @@
  
    void addSynthesizedFrom(DexProgramClass clazz) {
 -- 
-2.29.2.299.gdc1121823c-goog
+2.30.0.284.gd98b1dd5eaa7-goog
 
diff --git a/third_party/r8/patches/0002-Command-line-flags-for-class-merging-and-outlining.patch b/third_party/r8/patches/0002-Command-line-flags-for-class-merging-and-outlining.patch
index 3be15d19..9d6ac91 100644
--- a/third_party/r8/patches/0002-Command-line-flags-for-class-merging-and-outlining.patch
+++ b/third_party/r8/patches/0002-Command-line-flags-for-class-merging-and-outlining.patch
@@ -1,4 +1,4 @@
-From 81210f660e8b86116e7b512ae25c44c44cb1e1e7 Mon Sep 17 00:00:00 2001
+From bfb105aec6d8e20aa91f23652b148cda5d800211 Mon Sep 17 00:00:00 2001
 From: Andrew Grieve <agrieve@chromium.org>
 Date: Mon, 28 Sep 2020 15:51:32 -0400
 Subject: [PATCH 2/3] Command-line flags for class merging and outlining
@@ -8,25 +8,21 @@
  1 file changed, 4 insertions(+), 4 deletions(-)
 
 diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
-index 47c084e22..a7b1fc0eb 100644
+index 985090a37..64daa52a6 100644
 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
 +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
-@@ -236,11 +236,11 @@ public class InternalOptions implements GlobalKeepInfoConfiguration {
+@@ -240,8 +240,8 @@ public class InternalOptions implements GlobalKeepInfoConfiguration {
    public boolean enableFieldAssignmentTracker = true;
    public boolean enableFieldBitAccessAnalysis =
        System.getProperty("com.android.tools.r8.fieldBitAccessAnalysis") != null;
 -  public boolean enableStaticClassMerging = true;
--  public boolean enableHorizontalClassMerging = true;
-+  public boolean enableStaticClassMerging = System.getProperty("com.android.tools.r8.staticClassMerging") != null;
-+  public boolean enableHorizontalClassMerging = System.getProperty("com.android.tools.r8.horizontalClassMerging") != null;
-   public int horizontalClassMergingMaxGroupSize = 30;
-   public boolean enableHorizontalClassMergingOfKotlinLambdas = true;
 -  public boolean enableVerticalClassMerging = true;
++  public boolean enableStaticClassMerging = System.getProperty("com.android.tools.r8.staticClassMerging") != null;
 +  public boolean enableVerticalClassMerging = System.getProperty("com.android.tools.r8.verticalClassMerging") != null;
    public boolean enableArgumentRemoval = true;
    public boolean enableUnusedInterfaceRemoval = true;
    public boolean enableDevirtualization = true;
-@@ -1167,7 +1167,7 @@ public class InternalOptions implements GlobalKeepInfoConfiguration {
+@@ -1202,7 +1202,7 @@ public class InternalOptions implements GlobalKeepInfoConfiguration {
      public static final String CLASS_NAME = "com.android.tools.r8.GeneratedOutlineSupport";
      public static final String METHOD_PREFIX = "outline";
  
@@ -35,6 +31,15 @@
      public int minSize = 3;
      public int maxSize = 99;
      public int threshold = 20;
+@@ -1273,7 +1273,7 @@ public class InternalOptions implements GlobalKeepInfoConfiguration {
+ 
+   public static class HorizontalClassMergerOptions {
+ 
+-    public boolean enable = true;
++    public boolean enable = System.getProperty("com.android.tools.r8.horizontalClassMerging") != null;
+     public boolean enableConstructorMerging = true;
+     public boolean enableJavaLambdaMerging = false;
+     public boolean enableKotlinLambdaMerging = true;
 -- 
-2.29.2.299.gdc1121823c-goog
+2.30.0.284.gd98b1dd5eaa7-goog
 
diff --git a/third_party/r8/patches/0003-Allow-access-modification-everywhere.patch b/third_party/r8/patches/0003-Allow-access-modification-everywhere.patch
index 86c7279f..b6effe5 100644
--- a/third_party/r8/patches/0003-Allow-access-modification-everywhere.patch
+++ b/third_party/r8/patches/0003-Allow-access-modification-everywhere.patch
@@ -1,4 +1,4 @@
-From eaf26704d9d5143d3cd5a8aa96ebff225bdf6747 Mon Sep 17 00:00:00 2001
+From 9d6a4562f1bdde941dcef77c311bcb9beb47313c Mon Sep 17 00:00:00 2001
 From: Andrew Grieve <agrieve@chromium.org>
 Date: Wed, 21 Oct 2020 10:59:42 -0400
 Subject: [PATCH 3/3] Allow access modification everywhere
@@ -11,10 +11,10 @@
  1 file changed, 1 insertion(+), 1 deletion(-)
 
 diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
-index bdda13be7..7553cbfa6 100644
+index b374e5843..999fd8307 100644
 --- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
 +++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
-@@ -881,7 +881,7 @@ public class AppInfoWithLiveness extends AppInfoWithClassHierarchy
+@@ -900,7 +900,7 @@ public class AppInfoWithLiveness extends AppInfoWithClassHierarchy
  
    public boolean isAccessModificationAllowed(DexReference reference) {
      assert options().getProguardConfiguration().isAccessModificationAllowed();
@@ -24,5 +24,5 @@
  
    public boolean isRepackagingAllowed(DexProgramClass clazz) {
 -- 
-2.29.2.299.gdc1121823c-goog
+2.30.0.284.gd98b1dd5eaa7-goog
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 34126a5..a2b804a 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -30998,6 +30998,7 @@
   <int value="3772"
       label="AddressSpaceUnknownNonSecureContextNavigatedToPrivate"/>
   <int value="3773" label="RTCPeerConnectionSdpSemanticsPlanB"/>
+  <int value="3774" label="FetchRespondWithNoResponseWithUsedRequestBody"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -65103,6 +65104,20 @@
   <int value="5" label="RESOLVE_SPECULATIVE_ABORT"/>
 </enum>
 
+<enum name="ResolutionComparison">
+  <int value="0" label="Requested width larger than capture"/>
+  <int value="1" label="Requested width smaller than capture"/>
+  <int value="2" label="Requested height larger than capture"/>
+  <int value="3" label="Requested height smaller than capture"/>
+  <int value="4" label="Requested resolution equals capture"/>
+  <int value="5" label="Requested width and height larger than capture"/>
+  <int value="6"
+      label="Requested width smaller, and height larger than capture"/>
+  <int value="7"
+      label="Requested width larger and height smaller than capture"/>
+  <int value="8" label="Requested width and height smaller than capture"/>
+</enum>
+
 <enum name="ResolutionUnspecWasteCategory">
   <int value="0" label="AF_WASTE_IPV4_ONLY">
     Running in a IPv4-only configuration. No waste.
diff --git a/tools/metrics/histograms/expand_owners.py b/tools/metrics/histograms/expand_owners.py
index 566f8d56..f3de3e9 100644
--- a/tools/metrics/histograms/expand_owners.py
+++ b/tools/metrics/histograms/expand_owners.py
@@ -17,8 +17,8 @@
 # module's directory, histograms, and the directory above tools, which may or
 # may not be src depending on the machine running the code, is up three
 # directory levels from the histograms directory.
-_DIR_ABOVE_TOOLS = [os.path.dirname(__file__), '..', '..', '..']
-_SRC = 'src/'
+DIR_ABOVE_TOOLS = [os.path.dirname(__file__), '..', '..', '..']
+SRC = 'src/'
 
 
 class Error(Exception):
@@ -91,7 +91,7 @@
   Args:
     path: The path to an OWNERS file, e.g. 'src/gin/OWNERS'.
   """
-  return path.startswith(_SRC) and path.endswith(_OWNERS)
+  return path.startswith(SRC) and path.endswith(_OWNERS)
 
 
 def _GetHigherLevelOwnersFilePath(path):
@@ -111,7 +111,7 @@
   # The highest directory that is searched for component information is one
   # directory lower than the directory above tools. Depending on the machine
   # running this code, the directory above tools may or may not be src.
-  path_to_limiting_dir = os.path.abspath(os.path.join(*_DIR_ABOVE_TOOLS))
+  path_to_limiting_dir = os.path.abspath(os.path.join(*DIR_ABOVE_TOOLS))
   limiting_dir = path_to_limiting_dir.split(os.sep)[-1]
   owners_file_limit = (os.sep).join([limiting_dir, _OWNERS])
   if path.endswith(owners_file_limit):
@@ -138,10 +138,10 @@
   if _IsWellFormattedFilePath(path):
     # _SRC is removed because the file system on the machine running the code
     # may not have a(n) src directory.
-    path_without_src = path[len(_SRC):]
+    path_without_src = path[len(SRC):]
 
     return os.path.abspath(
-        os.path.join(*(_DIR_ABOVE_TOOLS + path_without_src.split(os.sep))))
+        os.path.join(*(DIR_ABOVE_TOOLS + path_without_src.split(os.sep))))
 
   raise Error(
       'The given path {} is not well-formatted. Well-formatted paths begin '
@@ -184,7 +184,7 @@
 
       elif first_word.startswith(directive):
         next_path = _GetOwnersFilePath(
-          os.path.join(_SRC, first_word[len(directive):]))
+            os.path.join(SRC, first_word[len(directive):]))
 
         if os.path.exists(next_path) and os.path.isfile(next_path):
           extracted_emails.extend(
@@ -241,7 +241,7 @@
   """
   # Verify that the paths are absolute and the root is a parent of the
   # passed in path.
-  root_path = os.path.abspath(os.path.join(*_DIR_ABOVE_TOOLS))
+  root_path = os.path.abspath(os.path.join(*DIR_ABOVE_TOOLS))
   path = os.path.abspath(path)
   if not path.startswith(root_path):
     raise Error('Path {} is not a subpath of the root path {}.'.format(
@@ -250,7 +250,7 @@
   dirmd_exe = 'dirmd'
   if sys.platform == 'win32':
     dirmd_exe = 'dirmd.bat'
-  dirmd_path = os.path.join(*(_DIR_ABOVE_TOOLS +
+  dirmd_path = os.path.join(*(DIR_ABOVE_TOOLS +
                               ['third_party', 'depot_tools', dirmd_exe]))
   dirmd = subprocess.Popen([dirmd_path, 'compute', '--root', root_path, path],
                            stdout=subprocess.PIPE)
diff --git a/tools/metrics/histograms/expand_owners_unittest.py b/tools/metrics/histograms/expand_owners_unittest.py
index 5beb49e7..76203b35 100644
--- a/tools/metrics/histograms/expand_owners_unittest.py
+++ b/tools/metrics/histograms/expand_owners_unittest.py
@@ -5,6 +5,7 @@
 import unittest
 
 import expand_owners
+import mock
 import os
 import shutil
 import tempfile
@@ -13,43 +14,38 @@
 _DEFAULT_COMPONENT = '# COMPONENT: Default>Component'
 
 
-def _DirnameN(path, n):
-  """Calls os.path.dirname() on the argument n times."""
-  path = os.path.abspath(path)
-  for _ in range(n):
-    path = os.path.dirname(path)
-  return path
+def _GetToolsParentDir():
+  """Returns an absolute path to the the tools directory's parent directory.
 
-
-assert __file__.endswith('tools/metrics/histograms/expand_owners_unittest.py')
-
-_PATH_TO_CHROMIUM_DIR = _DirnameN(__file__, 5)
+  Example: 'C:\a\n\ff\' or '/opt/n/ff/'.
+  """
+  return os.path.abspath(os.path.join(*expand_owners.DIR_ABOVE_TOOLS))
 
 
 def _GetFileDirective(path):
   """Returns a file directive line.
 
   Args:
-    path: An absolute path, e.g. '/some/directory/chromium/src/tools/OWNERS'.
+    path: An absolute path, e.g. '/some/directory/subdirectory/tools/OWNERS'.
 
   Returns:
     A file directive that can be used in an OWNERS file, e.g.
     file://tools/OWNERS.
   """
-  return ''.join(['file://', path.split('src/')[1]])
+  return ''.join(['file://', path[len(_GetToolsParentDir()) + 1:]])
 
 
 def _GetSrcRelativePath(path):
   """Returns a(n) src-relative path for the given file path.
 
   Args:
-    path: An absolute path, e.g. '/some/directory/chromium/src/tools/OWNERS'.
+    path: An absolute path, e.g. '/some/directory/subdirectory/tools/OWNERS'.
 
   Returns:
     A src-relative path, e.g.'src/tools/OWNERS'.
   """
-  assert path.startswith(_PATH_TO_CHROMIUM_DIR)
-  return path[len(_PATH_TO_CHROMIUM_DIR) + 1:]
+  assert path.startswith(_GetToolsParentDir())
+  return expand_owners.SRC + path[len(_GetToolsParentDir()) + 1:]
 
 
 def _MakeOwnersFile(filename, directory):
@@ -75,10 +71,17 @@
     self.temp_dir = tempfile.mkdtemp(
         dir=os.path.abspath(os.path.join(os.path.dirname(__file__))))
 
+    # The below construction is used rather than __file__.endswith() because
+    # the file extension could be .py or .pyc.
+    assert os.sep.join(
+        ['tools', 'metrics', 'histograms',
+         'expand_owners_unittest.py']) in __file__
+
   def tearDown(self):
     super(ExpandOwnersTest, self).tearDown()
     shutil.rmtree(self.temp_dir)
 
+  @unittest.skip("http://crbug.com/1164985")
   def testExpandOwnersUsesMetadataOverOwners(self):
     """Checks that DIR_METADATA is used if available"""
     with open(os.path.join(self.temp_dir, 'DIR_METADATA'), "w+") as md:
@@ -134,8 +137,10 @@
     expand_owners.ExpandHistogramsOWNERS(histograms)
     self.assertMultiLineEqual(histograms.toxml(), expected_histograms.toxml())
 
-  def testExpandOwnersWithSimpleOWNERSFilePath(self):
+  @mock.patch('expand_owners._ExtractComponentViaDirmd')
+  def testExpandOwnersWithSimpleOWNERSFilePath(self, mock_dirmd_extract):
     """Checks that OWNERS files are expanded."""
+    mock_dirmd_extract.return_value = None
     absolute_path = _MakeOwnersFile('simple_OWNERS', self.temp_dir)
     src_relative_path = _GetSrcRelativePath(absolute_path)
 
@@ -188,11 +193,15 @@
     expand_owners.ExpandHistogramsOWNERS(histograms)
     self.assertMultiLineEqual(histograms.toxml(), expected_histograms.toxml())
 
-  def testExpandOwnersWithLongFilePath(self):
+  @mock.patch('expand_owners._ExtractComponentViaDirmd')
+  def testExpandOwnersWithLongFilePath(self, mock_dirmd_extract):
+    """Checks that long OWNERS file paths are supported.
+
+    Most OWNERS file paths appear between owners tags on the same line, e.g.
+    <owner>src/chrome/browser</owner>. However, especially long paths may appear
+    on their own line between the tags.
     """
-    Check that long file path which forces <owner> tags to separate lines is
-    supported.
-    """
+    mock_dirmd_extract.return_value = None
     absolute_path = _MakeOwnersFile('simple_OWNERS', self.temp_dir)
     src_relative_path = _GetSrcRelativePath(absolute_path)
 
@@ -229,8 +238,10 @@
     expand_owners.ExpandHistogramsOWNERS(histograms)
     self.assertMultiLineEqual(histograms.toxml(), expected_histograms.toxml())
 
-  def testExpandOwnersWithDuplicateOwners(self):
+  @mock.patch('expand_owners._ExtractComponentViaDirmd')
+  def testExpandOwnersWithDuplicateOwners(self, mock_dirmd_extract):
     """Checks that owners are unique."""
+    mock_dirmd_extract.return_value = None
     absolute_path = _MakeOwnersFile('simple_OWNERS', self.temp_dir)
     src_relative_path = _GetSrcRelativePath(absolute_path)
 
@@ -266,8 +277,10 @@
     expand_owners.ExpandHistogramsOWNERS(histograms)
     self.assertMultiLineEqual(histograms.toxml(), expected_histograms.toxml())
 
-  def testExpandOwnersWithFileDirectiveOWNERSFilePath(self):
+  @mock.patch('expand_owners._ExtractComponentViaDirmd')
+  def testExpandOwnersWithFileDirectiveOWNERSFilePath(self, mock_dirmd_extract):
     """Checks that OWNERS files with file directives are expanded."""
+    mock_dirmd_extract.return_value = None
     simple_absolute_path = _MakeOwnersFile('simple_OWNERS', self.temp_dir)
 
     with open(simple_absolute_path, 'w') as owners_file:
@@ -314,8 +327,11 @@
     expand_owners.ExpandHistogramsOWNERS(histograms)
     self.assertEqual(histograms.toxml(), expected_histograms.toxml())
 
-  def testExpandOwnersForOWNERSFileWithDuplicateComponents(self):
+  @mock.patch('expand_owners._ExtractComponentViaDirmd')
+  def testExpandOwnersForOWNERSFileWithDuplicateComponents(
+      self, mock_dirmd_extract):
     """Checks that only one component tag is added if there are duplicates."""
+    mock_dirmd_extract.return_value = None
     absolute_path = _MakeOwnersFile('OWNERS', self.temp_dir)
     src_relative_path = _GetSrcRelativePath(absolute_path)
 
@@ -463,8 +479,7 @@
 """)
 
     with self.assertRaisesRegexp(
-        expand_owners.Error,
-        r'The file at .*src/medium/medium/roast/OWNERS does not exist\.'):
+        expand_owners.Error, r'The file at .*medium.*OWNERS does not exist\.'):
       expand_owners.ExpandHistogramsOWNERS(histograms_with_fake_file_path)
 
   def testExpandOwnersWithoutOwnersFromFile(self):
@@ -595,28 +610,21 @@
       expand_owners._ExtractEmailAddressesFromOWNERS(
           file_directive_absolute_path)
 
+  def testGetHigherLevelPath(self):
+    """Checks that higher directories are recursively checked for OWNERS.
 
-class GetHigherLevelOwnersFilePathTest(unittest.TestCase):
-
-  def testGetHigherLevelPathDerivedPathInSrcDirectory(self):
-    """Checks that higher directories are recursively checked for OWNERS."""
-    path = expand_owners._GetOwnersFilePath('src/banana/chocolate/OWNERS')
-    self.assertRegexpMatches(
-        expand_owners._GetHigherLevelOwnersFilePath(path), r'.*src/OWNERS')
-
-  def testGetHigherLevelPathGivenPathInSrcDirectory(self):
-    """Checks that '' is returned when the last directory is reached.
-
-    If the directory above the tools directory is src, then receiving
-    'src/OWNERS' is the point at which recursion stops. However, this directory
-    may not always be src.
+    Also, checks that there isn't a recursive loop.
     """
-    path_to_chromium_directory = [
-        os.path.dirname(__file__), '..', '..', '..', '..'
-    ]
-    path = os.path.abspath(
-        os.path.join(*(path_to_chromium_directory + ['src/OWNERS'])))
-    self.assertEqual(expand_owners._GetHigherLevelOwnersFilePath(path), '')
+    path = expand_owners._GetOwnersFilePath('src/banana/chocolate/OWNERS')
+    result = expand_owners._GetHigherLevelOwnersFilePath(path)
+
+    # The condition is true when the tools directory's parent directory is src,
+    # which is generally the case locally. However, the parent directory is not
+    # always src, e.g. on various testing bots.
+    if os.path.basename(_GetToolsParentDir()) == 'src':
+      self.assertRegexpMatches(result, r'.*OWNERS')
+    else:
+      self.assertEqual(result, '')
 
 
 if __name__ == '__main__':
diff --git a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
index 9353e2d5..3757232 100644
--- a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
@@ -599,6 +599,8 @@
       name="PageLoad.AdPaintTiming.NavigationToFirstContentfulPaint"/>
   <affected-histogram
       name="PageLoad.AdPaintTiming.NavigationToFirstContentfulPaint2"/>
+  <affected-histogram
+      name="PageLoad.AdPaintTiming.NavigationToFirstContentfulPaint3"/>
   <affected-histogram name="PageLoad.Bytes"/>
   <affected-histogram name="PageLoad.Cpu"/>
   <affected-histogram
@@ -16603,8 +16605,8 @@
 <histogram_suffixes name="SendTabToSelfEntryPoint" separator="."
     ordering="prefix">
   <owner>jeffreycohen@chromium.org</owner>
-  <owner>sebsg@chromium.org</owner>
   <owner>tgupta@chromium.org</owner>
+  <owner>chrome-sharing-core@google.com</owner>
   <suffix name="ContentMenu" label="Option shown in the content context menu"/>
   <suffix name="LinkMenu" label="Option shown in the link context menu"/>
   <suffix name="OmniboxIcon" label="Icon shown in the omnibox"/>
@@ -16619,9 +16621,6 @@
   <obsolete>
     The affected histogram is obsolete (5/20).
   </obsolete>
-  <owner>jeffreycohen@chromium.org</owner>
-  <owner>sebsg@chromium.org</owner>
-  <owner>tgupta@chromium.org</owner>
   <suffix name="ContentMenu"
       label="The count of devices shown in the content submenu"/>
   <suffix name="LinkMenu"
diff --git a/tools/metrics/histograms/histograms_xml/media/histograms.xml b/tools/metrics/histograms/histograms_xml/media/histograms.xml
index 10a2ebc..edbb8fe 100644
--- a/tools/metrics/histograms/histograms_xml/media/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/media/histograms.xml
@@ -3877,6 +3877,51 @@
   </summary>
 </histogram>
 
+<histogram name="Media.VideoCapture.Mac.Device.CapturedIOSurface"
+    enum="Boolean" expires_after="M92">
+  <owner>eshr@google.com</owner>
+  <owner>handellm@google.com</owner>
+  <summary>
+    A count of how often the capture device delivers an IOSurface to the capture
+    pipeline. This is recorded once per opening of the camera, when the first
+    video frame is captured.
+  </summary>
+</histogram>
+
+<histogram
+    name="Media.VideoCapture.Mac.Device.CapturedWithRequestedPixelFormat"
+    enum="Boolean" expires_after="M92">
+  <owner>eshr@google.com</owner>
+  <owner>handellm@google.com</owner>
+  <summary>
+    A boolean count of how often the requested pixel format is the one that was
+    actually captured. This is recorded once per opening of the camera, when the
+    first video frame is captured.
+  </summary>
+</histogram>
+
+<histogram name="Media.VideoCapture.Mac.Device.CapturedWithRequestedResolution"
+    enum="ResolutionComparison" expires_after="M92">
+  <owner>eshr@google.com</owner>
+  <owner>handellm@google.com</owner>
+  <summary>
+    An enumeration count of whether or not the requested resolution equals the
+    captured resolution, detailing which dimensions differ in the enum. This is
+    recorded once per opening of the camera, when the first video frame is
+    captured.
+  </summary>
+</histogram>
+
+<histogram name="Media.VideoCapture.Mac.Device.RequestedPixelFormat"
+    enum="VideoResolutionDesignation" expires_after="M92">
+  <owner>eshr@google.com</owner>
+  <owner>handellm@google.com</owner>
+  <summary>
+    Counts the pixel formats requested by the VideoCaptureDevice on Mac. This is
+    recorded when the first video frame is captured.
+  </summary>
+</histogram>
+
 <histogram name="Media.VideoCapture.MacBook.AttemptCountWhenNoCamera"
     units="attempts" expires_after="2020-03-15">
   <owner>chfremer@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml
index a856efe8..390c69ae 100644
--- a/tools/metrics/histograms/histograms_xml/others/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -13587,17 +13587,18 @@
 </histogram>
 
 <histogram name="SendTabToSelf.AndroidShareSheet.ClickResult"
-    enum="SendTabToSelfClickResult" expires_after="M88">
+    enum="SendTabToSelfClickResult" expires_after="M97">
   <owner>jeffreycohen@chromium.org</owner>
   <owner>tgupta@chromium.org</owner>
+  <owner>chrome-sharing-core@google.com</owner>
   <summary>Tracks the user flow for sending a tab for SendTabToSelf.</summary>
 </histogram>
 
 <histogram base="true" name="SendTabToSelf.ClickResult"
-    enum="SendTabToSelfClickResult" expires_after="M88">
+    enum="SendTabToSelfClickResult" expires_after="M97">
   <owner>jeffreycohen@chromium.org</owner>
-  <owner>sebsg@chromium.org</owner>
   <owner>tgupta@chromium.org</owner>
+  <owner>chrome-sharing-core@google.com</owner>
   <summary>
     Record whether the user has clicked the item when it is shown.
   </summary>
@@ -14666,12 +14667,18 @@
 </histogram>
 
 <histogram name="SpellCheck.MisspellRatio" units="%" expires_after="M90">
+  <obsolete>
+    Removed 2021/01: the relevant code path isn't used anymore.
+  </obsolete>
   <owner>gujen@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>The percentage of misspelled words within checked words.</summary>
 </histogram>
 
 <histogram name="SpellCheck.ReplaceRatio" units="%" expires_after="M90">
+  <obsolete>
+    Removed 2021/01: the relevant code path isn't used anymore.
+  </obsolete>
   <owner>gujen@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>The percentage of replaced words within misspelled words.</summary>
diff --git a/tools/metrics/histograms/histograms_xml/page/histograms.xml b/tools/metrics/histograms/histograms_xml/page/histograms.xml
index 936450c..df73252e 100644
--- a/tools/metrics/histograms/histograms_xml/page/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/page/histograms.xml
@@ -34,6 +34,26 @@
 <histogram base="true"
     name="PageLoad.AdPaintTiming.NavigationToFirstContentfulPaint2" units="ms"
     expires_after="2021-07-31">
+  <obsolete>
+    Replaced by PageLoad.PaintTiming.NavigationToLargestContentfulPaint3 in Jan
+    2021 to increase the bucket range.
+  </obsolete>
+  <owner>jkarlin@chromium.org</owner>
+  <owner>johnidel@chromium.org</owner>
+  <summary>
+    Records the time from frame navigation start to FirstContentfulPaint of each
+    ad frame that receives a FirstContentfulPaint. The time could be quite
+    large, as some ads don't paint until they're scrolled into view. But the
+    metric is still useful in aggregate.
+
+    Recorded for all ad frames with non-zero bytes or cpu usage that receive a
+    FirstContentfulPaint. Recorded when the ad frame or page is destroyed.
+  </summary>
+</histogram>
+
+<histogram base="true"
+    name="PageLoad.AdPaintTiming.NavigationToFirstContentfulPaint3" units="ms"
+    expires_after="2021-12-31">
   <owner>jkarlin@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/signin/histograms.xml b/tools/metrics/histograms/histograms_xml/signin/histograms.xml
index 07250917..b067699 100644
--- a/tools/metrics/histograms/histograms_xml/signin/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/signin/histograms.xml
@@ -521,12 +521,13 @@
 </histogram>
 
 <histogram name="Signin.InvestigatedScenario" enum="SigninInvestigatedScenario"
-    expires_after="M90">
+    expires_after="M95">
   <owner>treib@chromium.org</owner>
   <owner>mmoskvitin@google.com</owner>
   <summary>
-    Records the general type of signin that is occuring in relation to previous
-    signin and local data.
+    Recorded when Sync is turned on in a profile. Records whether this is the
+    first time Sync is turned on in this profile or, if not, whether Sync was
+    previously turned on for the same or for a different account.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/metrics_python_tests.py b/tools/metrics/metrics_python_tests.py
index 5c9cafa..5e31885 100755
--- a/tools/metrics/metrics_python_tests.py
+++ b/tools/metrics/metrics_python_tests.py
@@ -23,8 +23,7 @@
 sys.exit(
     typ.main(tests=resolve(
         'actions/extract_actions_test.py',
-        # TODO(crbug/1126653): Turn back on once tests can pass again.
-        # 'histograms/expand_owners_unittest.py',
+        'histograms/expand_owners_unittest.py',
         'histograms/extract_histograms_test.py',
         'histograms/generate_expired_histograms_array_unittest.py',
         'histograms/pretty_print_test.py',
diff --git a/tools/perf/benchmarks/v8_browsing.py b/tools/perf/benchmarks/v8_browsing.py
index 1b17073..1b4a116ec 100644
--- a/tools/perf/benchmarks/v8_browsing.py
+++ b/tools/perf/benchmarks/v8_browsing.py
@@ -11,19 +11,6 @@
 from telemetry.web_perf import timeline_based_measurement
 import page_sets
 
-V8_BROWSING_BENCHMARK_UMA = [
-    'V8.WasmCompileModuleMicroSeconds.wasm',
-    'V8.WasmCompileModuleAsyncMicroSeconds',
-    'V8.WasmCompileModuleStreamingMicroSeconds',
-    'V8.WasmFinishModuleStreamingMicroSeconds',
-    'V8.WasmTierUpModuleMicroSeconds',
-    'V8.WasmCompileFunctionMicroSeconds.wasm',
-    'V8.WasmInstantiateModuleMicroSeconds.wasm',
-    'V8.WasmModuleCodeSizeTopTierMiB',
-    'V8.WasmCompileFunctionPeakMemoryBytes',
-    'V8.WasmModuleCodeSizeMiB',
-]
-
 
 def AugmentOptionsForV8BrowsingMetrics(options, enable_runtime_call_stats=True):
   categories = [
@@ -57,9 +44,6 @@
 
   options.config.chrome_trace_config.SetTraceBufferSizeInKb(400 * 1024)
 
-  options.config.chrome_trace_config.EnableUMAHistograms(
-      *V8_BROWSING_BENCHMARK_UMA)
-
   metrics = [
       'blinkGcMetric',
       'consoleErrorMetric',
@@ -68,7 +52,6 @@
       'memoryMetric',
       'pcscanMetric',
       'reportedByPageMetric',
-      'umaMetric',
       'wasmMetric',
   ]
   options.ExtendTimelineBasedMetric(metrics)
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 8d5cb13f..fcffa17 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/win/94ca9a9578a7eeb3df93a820955458fb9aff28fd/trace_processor_shell.exe"
         },
         "mac": {
-            "hash": "19571150217264641baf978653dd92211ef6b268",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/94ca9a9578a7eeb3df93a820955458fb9aff28fd/trace_processor_shell"
+            "hash": "cef9f6facb62a38a26057262c029aad1fa51bca4",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/2d79f95e908dcbae188e1fedd12e8424cbd14392/trace_processor_shell"
         },
         "linux": {
             "hash": "240f01ceac4408177109ca356554518088cf0498",
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 5427e8eb..35b14dd 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -254,7 +254,6 @@
 crbug.com/1160628 [ android-pixel-2 ] rendering.mobile/idle_power_request_animation_frame [ Skip ]
 crbug.com/1160628 [ android-pixel-2 ] rendering.mobile/idle_power_set_timeout_long [ Skip ]
 crbug.com/1160628 [ android-pixel-2 ] rendering.mobile/idle_power_set_timetout [ Skip ]
-crbug.com/1166558 [ android-pixel-2 ] rendering.mobile/microgame_fps [ Skip ]
 
 # Benchmark: rasterize_and_record_micro.top_25
 crbug.com/764543 rasterize_and_record_micro.top_25/file://static_top_25/wikipedia.html [ Skip ]
diff --git a/ui/accessibility/ax_role_properties.cc b/ui/accessibility/ax_role_properties.cc
index c09f6364..8b966e2c 100644
--- a/ui/accessibility/ax_role_properties.cc
+++ b/ui/accessibility/ax_role_properties.cc
@@ -723,6 +723,17 @@
   }
 }
 
+bool IsComboBox(const ax::mojom::Role role) {
+  switch (role) {
+    case ax::mojom::Role::kComboBoxMenuButton:
+    case ax::mojom::Role::kComboBoxGrouping:
+    case ax::mojom::Role::kTextFieldWithComboBox:
+      return true;
+    default:
+      return false;
+  }
+}
+
 bool ShouldHaveReadonlyStateByDefault(const ax::mojom::Role role) {
   switch (role) {
     case ax::mojom::Role::kArticle:
diff --git a/ui/accessibility/ax_role_properties.h b/ui/accessibility/ax_role_properties.h
index 1178560..0280412 100644
--- a/ui/accessibility/ax_role_properties.h
+++ b/ui/accessibility/ax_role_properties.h
@@ -179,6 +179,9 @@
 // break, or inline text box.
 AX_BASE_EXPORT bool IsText(ax::mojom::Role role);
 
+// Returns true if the provided role is any of the combobox-related roles.
+AX_BASE_EXPORT bool IsComboBox(ax::mojom::Role role);
+
 // Returns true if the node should be read only by default
 AX_BASE_EXPORT bool ShouldHaveReadonlyStateByDefault(
     const ax::mojom::Role role);
diff --git a/ui/accessibility/platform/ax_platform_node_mac.mm b/ui/accessibility/platform/ax_platform_node_mac.mm
index ce50a0a..93a3618 100644
--- a/ui/accessibility/platform/ax_platform_node_mac.mm
+++ b/ui/accessibility/platform/ax_platform_node_mac.mm
@@ -57,8 +57,8 @@
       {ax::mojom::Role::kColorWell, NSAccessibilityColorWellRole},
       {ax::mojom::Role::kColumn, NSAccessibilityColumnRole},
       {ax::mojom::Role::kColumnHeader, @"AXCell"},
-      {ax::mojom::Role::kComboBoxGrouping, NSAccessibilityGroupRole},
-      {ax::mojom::Role::kComboBoxMenuButton, NSAccessibilityPopUpButtonRole},
+      {ax::mojom::Role::kComboBoxGrouping, NSAccessibilityComboBoxRole},
+      {ax::mojom::Role::kComboBoxMenuButton, NSAccessibilityComboBoxRole},
       {ax::mojom::Role::kComment, NSAccessibilityGroupRole},
       {ax::mojom::Role::kComplementary, NSAccessibilityGroupRole},
       {ax::mojom::Role::kContentDeletion, NSAccessibilityGroupRole},
diff --git a/ui/android/OWNERS b/ui/android/OWNERS
index 3dce3d6e..2d96ba4 100644
--- a/ui/android/OWNERS
+++ b/ui/android/OWNERS
@@ -8,6 +8,3 @@
 # for display/window/view_android
 boliu@chromium.org
 jinsukkim@chromium.org
-
-# for CC and Viz integration
-khushalsagar@chromium.org
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc
index 0193353..5f8e78b 100644
--- a/ui/base/ui_base_features.cc
+++ b/ui/base/ui_base_features.cc
@@ -164,7 +164,7 @@
 
 // Enables forced colors mode for web content.
 const base::Feature kForcedColors{"ForcedColors",
-                                  base::FEATURE_DISABLED_BY_DEFAULT};
+                                  base::FEATURE_ENABLED_BY_DEFAULT};
 
 bool IsForcedColorsEnabled() {
   static const bool forced_colors_enabled =
diff --git a/ui/gfx/OWNERS b/ui/gfx/OWNERS
index e34675cb..4bbf63b 100644
--- a/ui/gfx/OWNERS
+++ b/ui/gfx/OWNERS
@@ -25,7 +25,6 @@
 
 # Overlay transforms.
 per-file overlay*=alexst@chromium.org
-per-file overlay*=khushalsagar@chromium.org
 per-file overlay*=spang@chromium.org
 
 # Transform, interpolated transform and transform util.
diff --git a/ui/gl/OWNERS b/ui/gl/OWNERS
index 85860991..1f6eeb2 100644
--- a/ui/gl/OWNERS
+++ b/ui/gl/OWNERS
@@ -6,7 +6,7 @@
 per-file *_ozone*=alexst@chromium.org
 per-file *_ozone*=dnicoara@chromium.org
 per-file *_ozone*=spang@chromium.org
-per-file *surface_control*=khushalsagar@chromium.org
+per-file *surface_control*=vasilyt@chromium.org
 
 # For Windows Direct Composition and Swap Chain
 magchen@chromium.org
diff --git a/ui/views/accessibility/accessibility_alert_window.cc b/ui/views/accessibility/accessibility_alert_window.cc
index 9afdb52..c1b5b43 100644
--- a/ui/views/accessibility/accessibility_alert_window.cc
+++ b/ui/views/accessibility/accessibility_alert_window.cc
@@ -23,7 +23,7 @@
   alert_window_->Init(ui::LayerType::LAYER_NOT_DRAWN);
   alert_window_->SetProperty(ui::kAXRoleOverride, ax::mojom::Role::kAlert);
   parent->AddChild(alert_window_.get());
-  observer_.Add(aura::Env::GetInstance());
+  observation_.Observe(aura::Env::GetInstance());
 }
 
 AccessibilityAlertWindow::~AccessibilityAlertWindow() = default;
@@ -38,7 +38,7 @@
 }
 
 void AccessibilityAlertWindow::OnWillDestroyEnv() {
-  observer_.RemoveAll();
+  observation_.Reset();
   alert_window_.reset();
 }
 
diff --git a/ui/views/accessibility/accessibility_alert_window.h b/ui/views/accessibility/accessibility_alert_window.h
index 5895a71..d64082df 100644
--- a/ui/views/accessibility/accessibility_alert_window.h
+++ b/ui/views/accessibility/accessibility_alert_window.h
@@ -9,7 +9,7 @@
 #include <string>
 
 #include "base/gtest_prod_util.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "ui/aura/env.h"
 #include "ui/aura/env_observer.h"
 #include "ui/views/views_export.h"
@@ -48,7 +48,7 @@
   // The accessibility cache associated with |alert_window_|.
   views::AXAuraObjCache* cache_;
 
-  ScopedObserver<aura::Env, aura::EnvObserver> observer_{this};
+  base::ScopedObservation<aura::Env, aura::EnvObserver> observation_{this};
 };
 
 }  // namespace views
diff --git a/ui/views/accessibility/accessibility_alert_window_unittest.cc b/ui/views/accessibility/accessibility_alert_window_unittest.cc
index 60af94e..fe3c5e3 100644
--- a/ui/views/accessibility/accessibility_alert_window_unittest.cc
+++ b/ui/views/accessibility/accessibility_alert_window_unittest.cc
@@ -76,7 +76,7 @@
   AccessibilityAlertWindow window(parent_.get(), &cache);
   window.OnWillDestroyEnv();
 
-  EXPECT_FALSE(window.observer_.IsObservingSources());
+  EXPECT_FALSE(window.observation_.IsObserving());
   EXPECT_FALSE(window.alert_window_);
 }
 
diff --git a/ui/views/accessibility/ax_view_obj_wrapper.cc b/ui/views/accessibility/ax_view_obj_wrapper.cc
index 5384534..b75370f 100644
--- a/ui/views/accessibility/ax_view_obj_wrapper.cc
+++ b/ui/views/accessibility/ax_view_obj_wrapper.cc
@@ -4,6 +4,7 @@
 
 #include "ui/views/accessibility/ax_view_obj_wrapper.h"
 
+#include <string>
 #include <vector>
 
 #include "ui/accessibility/ax_action_data.h"
@@ -20,7 +21,7 @@
     : AXAuraObjWrapper(aura_obj_cache), view_(view) {
   if (view->GetWidget())
     aura_obj_cache_->GetOrCreate(view->GetWidget());
-  observer_.Add(view);
+  observation_.Observe(view);
 }
 
 AXViewObjWrapper::~AXViewObjWrapper() = default;
@@ -97,7 +98,7 @@
 }
 
 void AXViewObjWrapper::OnViewIsDeleting(View* observed_view) {
-  observer_.RemoveAll();
+  observation_.Reset();
   view_ = nullptr;
 }
 
diff --git a/ui/views/accessibility/ax_view_obj_wrapper.h b/ui/views/accessibility/ax_view_obj_wrapper.h
index 758381f..c4ebc6c 100644
--- a/ui/views/accessibility/ax_view_obj_wrapper.h
+++ b/ui/views/accessibility/ax_view_obj_wrapper.h
@@ -7,9 +7,10 @@
 
 #include <stdint.h>
 
+#include <string>
 #include <vector>
 
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "ui/views/accessibility/ax_aura_obj_wrapper.h"
 #include "ui/views/view.h"
 #include "ui/views/view_observer.h"
@@ -43,7 +44,7 @@
  private:
   View* view_;
 
-  ScopedObserver<View, ViewObserver> observer_{this};
+  base::ScopedObservation<View, ViewObserver> observation_{this};
 };
 
 }  // namespace views
diff --git a/ui/views/accessibility/ax_widget_obj_wrapper.cc b/ui/views/accessibility/ax_widget_obj_wrapper.cc
index c58811c..a81b7a5 100644
--- a/ui/views/accessibility/ax_widget_obj_wrapper.cc
+++ b/ui/views/accessibility/ax_widget_obj_wrapper.cc
@@ -18,7 +18,7 @@
 AXWidgetObjWrapper::AXWidgetObjWrapper(AXAuraObjCache* aura_obj_cache,
                                        Widget* widget)
     : AXAuraObjWrapper(aura_obj_cache), widget_(widget) {
-  widget_observer_.Add(widget);
+  widget_observation_.Observe(widget);
   widget->AddRemovalsObserver(this);
 }
 
diff --git a/ui/views/accessibility/ax_widget_obj_wrapper.h b/ui/views/accessibility/ax_widget_obj_wrapper.h
index 9d768b8..14c735c 100644
--- a/ui/views/accessibility/ax_widget_obj_wrapper.h
+++ b/ui/views/accessibility/ax_widget_obj_wrapper.h
@@ -7,9 +7,10 @@
 
 #include <stdint.h>
 
+#include <string>
 #include <vector>
 
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "ui/accessibility/platform/ax_unique_id.h"
 #include "ui/views/accessibility/ax_aura_obj_wrapper.h"
 #include "ui/views/widget/widget.h"
@@ -52,7 +53,7 @@
 
   const ui::AXUniqueId unique_id_;
 
-  ScopedObserver<Widget, WidgetObserver> widget_observer_{this};
+  base::ScopedObservation<Widget, WidgetObserver> widget_observation_{this};
 };
 
 }  // namespace views
diff --git a/ui/views/accessibility/ax_window_obj_wrapper.cc b/ui/views/accessibility/ax_window_obj_wrapper.cc
index bdbe0ec..6576335 100644
--- a/ui/views/accessibility/ax_window_obj_wrapper.cc
+++ b/ui/views/accessibility/ax_window_obj_wrapper.cc
@@ -81,7 +81,7 @@
     : AXAuraObjWrapper(aura_obj_cache),
       window_(window),
       is_root_window_(window->IsRootWindow()) {
-  observer_.Add(window);
+  observation_.Observe(window);
 
   if (is_root_window_)
     aura_obj_cache_->OnRootWindowObjCreated(window);
diff --git a/ui/views/accessibility/ax_window_obj_wrapper.h b/ui/views/accessibility/ax_window_obj_wrapper.h
index 7c5514b..221e5b0 100644
--- a/ui/views/accessibility/ax_window_obj_wrapper.h
+++ b/ui/views/accessibility/ax_window_obj_wrapper.h
@@ -7,9 +7,10 @@
 
 #include <stdint.h>
 
+#include <string>
 #include <vector>
 
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "ui/accessibility/ax_enums.mojom-forward.h"
 #include "ui/accessibility/platform/ax_unique_id.h"
 #include "ui/aura/window.h"
@@ -69,7 +70,8 @@
   // pointer could be left in |aura_obj_cache_|. See https://crbug.com/1091545
   bool window_destroying_ = false;
 
-  ScopedObserver<aura::Window, aura::WindowObserver> observer_{this};
+  base::ScopedObservation<aura::Window, aura::WindowObserver> observation_{
+      this};
 };
 
 }  // namespace views
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc b/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc
index e644376..3b7c1df 100644
--- a/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc
+++ b/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc
@@ -10,7 +10,7 @@
 
 #include "base/containers/contains.h"
 #include "base/memory/singleton.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_multi_source_observation.h"
 #include "ui/accessibility/ax_action_data.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_node_data.h"
@@ -87,11 +87,11 @@
       return;
 
     widgets_.push_back(widget);
-    widget_observer_.Add(widget);
+    widget_observations_.AddObservation(widget);
 
     aura::Window* window = widget->GetNativeWindow();
     if (window)
-      window_observer_.Add(window);
+      window_observations_.AddObservation(window);
   }
 
   gfx::NativeViewAccessible GetNativeViewAccessible() override {
@@ -103,11 +103,11 @@
   // WidgetObserver:
 
   void OnWidgetDestroying(Widget* widget) override {
-    widget_observer_.Remove(widget);
+    widget_observations_.RemoveObservation(widget);
 
     aura::Window* window = widget->GetNativeWindow();
-    if (window && window_observer_.IsObserving(window))
-      window_observer_.Remove(window);
+    if (window && window_observations_.IsObservingSource(window))
+      window_observations_.RemoveObservation(window);
 
     auto iter = std::find(widgets_.begin(), widgets_.end(), widget);
     if (iter != widgets_.end())
@@ -167,8 +167,10 @@
   ui::AXNodeData data_;
   ui::AXUniqueId unique_id_;
   std::vector<Widget*> widgets_;
-  ScopedObserver<views::Widget, views::WidgetObserver> widget_observer_{this};
-  ScopedObserver<aura::Window, aura::WindowObserver> window_observer_{this};
+  base::ScopedMultiSourceObservation<views::Widget, views::WidgetObserver>
+      widget_observations_{this};
+  base::ScopedMultiSourceObservation<aura::Window, aura::WindowObserver>
+      window_observations_{this};
 };
 
 }  // namespace
diff --git a/ui/views/animation/animation_delegate_views.cc b/ui/views/animation/animation_delegate_views.cc
index f379e561..f114d727 100644
--- a/ui/views/animation/animation_delegate_views.cc
+++ b/ui/views/animation/animation_delegate_views.cc
@@ -14,7 +14,7 @@
 
 AnimationDelegateViews::AnimationDelegateViews(View* view) : view_(view) {
   if (view)
-    scoped_observer_.Add(view);
+    scoped_observation_.Observe(view);
 }
 
 AnimationDelegateViews::~AnimationDelegateViews() {
@@ -46,7 +46,8 @@
 }
 
 void AnimationDelegateViews::OnViewIsDeleting(View* observed_view) {
-  scoped_observer_.Remove(view_);
+  DCHECK(scoped_observation_.IsObservingSource(view_));
+  scoped_observation_.Reset();
   view_ = nullptr;
   UpdateAnimationRunner();
 }
diff --git a/ui/views/animation/animation_delegate_views.h b/ui/views/animation/animation_delegate_views.h
index c8aed39..7edc84d 100644
--- a/ui/views/animation/animation_delegate_views.h
+++ b/ui/views/animation/animation_delegate_views.h
@@ -7,7 +7,7 @@
 
 #include <memory>
 
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "ui/gfx/animation/animation_container_observer.h"
 #include "ui/gfx/animation/animation_delegate.h"
 #include "ui/views/view.h"
@@ -61,7 +61,7 @@
   // The animation runner that |container_| uses.
   CompositorAnimationRunner* compositor_animation_runner_ = nullptr;
 
-  ScopedObserver<View, ViewObserver> scoped_observer_{this};
+  base::ScopedObservation<View, ViewObserver> scoped_observation_{this};
 };
 
 }  // namespace views
diff --git a/ui/views/animation/compositor_animation_runner.h b/ui/views/animation/compositor_animation_runner.h
index 2f26f8d2..b2e543e 100644
--- a/ui/views/animation/compositor_animation_runner.h
+++ b/ui/views/animation/compositor_animation_runner.h
@@ -7,7 +7,6 @@
 
 #include <memory>
 
-#include "base/scoped_observer.h"
 #include "base/time/time.h"
 #include "ui/compositor/compositor.h"
 #include "ui/compositor/compositor_animation_observer.h"
diff --git a/ui/views/animation/ink_drop_event_handler.cc b/ui/views/animation/ink_drop_event_handler.cc
index 1247ade..d0771c2 100644
--- a/ui/views/animation/ink_drop_event_handler.cc
+++ b/ui/views/animation/ink_drop_event_handler.cc
@@ -18,7 +18,7 @@
           std::make_unique<ui::ScopedTargetHandler>(host_view, this)),
       host_view_(host_view),
       delegate_(delegate) {
-  observer_.Add(host_view_);
+  observation_.Observe(host_view_);
 }
 
 InkDropEventHandler::~InkDropEventHandler() = default;
diff --git a/ui/views/animation/ink_drop_event_handler.h b/ui/views/animation/ink_drop_event_handler.h
index 93f1996..ebc95790 100644
--- a/ui/views/animation/ink_drop_event_handler.h
+++ b/ui/views/animation/ink_drop_event_handler.h
@@ -7,7 +7,7 @@
 
 #include <memory>
 
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "base/strings/string_piece.h"
 #include "ui/events/event_handler.h"
 #include "ui/views/view.h"
@@ -75,7 +75,7 @@
   // The last user Event to trigger an InkDrop-ripple animation.
   std::unique_ptr<ui::LocatedEvent> last_ripple_triggering_event_;
 
-  ScopedObserver<View, ViewObserver> observer_{this};
+  base::ScopedObservation<View, ViewObserver> observation_{this};
 
   DISALLOW_COPY_AND_ASSIGN(InkDropEventHandler);
 };
diff --git a/ui/views/background.cc b/ui/views/background.cc
index 7ab9015..3cf74b72 100644
--- a/ui/views/background.cc
+++ b/ui/views/background.cc
@@ -8,7 +8,7 @@
 
 #include "base/check.h"
 #include "base/macros.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "build/build_config.h"
 #include "cc/paint/paint_flags.h"
 #include "ui/gfx/canvas.h"
@@ -67,15 +67,18 @@
  public:
   explicit ThemedVectorIconBackground(View* view,
                                       const ui::ThemedVectorIcon& icon)
-      : icon_(icon), observer_(this) {
+      : icon_(icon) {
     DCHECK(!icon_.empty());
-    observer_.Add(view);
+    observation_.Observe(view);
     OnViewThemeChanged(view);
   }
 
   // ViewObserver:
   void OnViewThemeChanged(View* view) override { view->SchedulePaint(); }
-  void OnViewIsDeleting(View* view) override { observer_.Remove(view); }
+  void OnViewIsDeleting(View* view) override {
+    DCHECK(observation_.IsObservingSource(view));
+    observation_.Reset();
+  }
 
   void Paint(gfx::Canvas* canvas, View* view) const override {
     canvas->DrawImageInt(icon_.GetImageSkia(view->GetNativeTheme()), 0, 0);
@@ -83,7 +86,7 @@
 
  private:
   const ui::ThemedVectorIcon icon_;
-  ScopedObserver<View, ViewObserver> observer_;
+  base::ScopedObservation<View, ViewObserver> observation_{this};
 
   DISALLOW_COPY_AND_ASSIGN(ThemedVectorIconBackground);
 };
@@ -94,9 +97,8 @@
  public:
   explicit ThemedSolidBackground(View* view, ui::NativeTheme::ColorId color_id)
       : SolidBackground(gfx::kPlaceholderColor),
-        observer_(this),
         color_id_(color_id) {
-    observer_.Add(view);
+    observation_.Observe(view);
     OnViewThemeChanged(view);
   }
   ~ThemedSolidBackground() override = default;
@@ -106,10 +108,13 @@
     SetNativeControlColor(view->GetNativeTheme()->GetSystemColor(color_id_));
     view->SchedulePaint();
   }
-  void OnViewIsDeleting(View* view) override { observer_.Remove(view); }
+  void OnViewIsDeleting(View* view) override {
+    DCHECK(observation_.IsObservingSource(view));
+    observation_.Reset();
+  }
 
  private:
-  ScopedObserver<View, ViewObserver> observer_;
+  base::ScopedObservation<View, ViewObserver> observation_{this};
   ui::NativeTheme::ColorId color_id_;
 
   DISALLOW_COPY_AND_ASSIGN(ThemedSolidBackground);
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.cc b/ui/views/bubble/bubble_dialog_delegate_view.cc
index 7c19157..6e0f3355 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view.cc
@@ -195,9 +195,9 @@
  public:
   AnchorWidgetObserver(BubbleDialogDelegate* owner, Widget* widget)
       : owner_(owner) {
-    widget_observer_.Add(widget);
+    widget_observation_.Observe(widget);
 #if !defined(OS_APPLE)
-    window_observer_.Add(widget->GetNativeWindow());
+    window_observation_.Observe(widget->GetNativeWindow());
 #endif
   }
   ~AnchorWidgetObserver() override = default;
@@ -205,9 +205,11 @@
   // WidgetObserver:
   void OnWidgetDestroying(Widget* widget) override {
 #if !defined(OS_APPLE)
-    window_observer_.Remove(widget->GetNativeWindow());
+    DCHECK(window_observation_.IsObservingSource(widget->GetNativeWindow()));
+    window_observation_.Reset();
 #endif
-    widget_observer_.Remove(widget);
+    DCHECK(widget_observation_.IsObservingSource(widget));
+    widget_observation_.Reset();
     owner_->OnAnchorWidgetDestroying();
     // |this| may be destroyed here!
   }
@@ -238,9 +240,11 @@
 
  private:
   BubbleDialogDelegate* owner_;
-  ScopedObserver<views::Widget, views::WidgetObserver> widget_observer_{this};
+  base::ScopedObservation<views::Widget, views::WidgetObserver>
+      widget_observation_{this};
 #if !defined(OS_APPLE)
-  ScopedObserver<aura::Window, aura::WindowObserver> window_observer_{this};
+  base::ScopedObservation<aura::Window, aura::WindowObserver>
+      window_observation_{this};
 #endif
 };
 
@@ -250,7 +254,7 @@
  public:
   BubbleWidgetObserver(BubbleDialogDelegate* owner, Widget* widget)
       : owner_(owner) {
-    observer_.Add(widget);
+    observation_.Observe(widget);
   }
   ~BubbleWidgetObserver() override = default;
 
@@ -264,7 +268,8 @@
   }
 
   void OnWidgetDestroyed(Widget* widget) override {
-    observer_.Remove(widget);
+    DCHECK(observation_.IsObservingSource(widget));
+    observation_.Reset();
     owner_->OnWidgetDestroyed(widget);
   }
 
@@ -295,7 +300,8 @@
 
  private:
   BubbleDialogDelegate* owner_;
-  ScopedObserver<views::Widget, views::WidgetObserver> observer_{this};
+  base::ScopedObservation<views::Widget, views::WidgetObserver> observation_{
+      this};
 };
 
 BubbleDialogDelegate::BubbleDialogDelegate() = default;
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.h b/ui/views/bubble/bubble_dialog_delegate_view.h
index 2f2b186..d335aa4 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.h
+++ b/ui/views/bubble/bubble_dialog_delegate_view.h
@@ -9,7 +9,6 @@
 
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
-#include "base/scoped_observer.h"
 #include "build/build_config.h"
 #include "ui/accessibility/ax_enums.mojom-forward.h"
 #include "ui/base/class_property.h"
diff --git a/ui/views/bubble/tooltip_icon.cc b/ui/views/bubble/tooltip_icon.cc
index fa42d321..4a24284a 100644
--- a/ui/views/bubble/tooltip_icon.cc
+++ b/ui/views/bubble/tooltip_icon.cc
@@ -98,7 +98,7 @@
   bubble_->SetCanActivate(!mouse_inside_);
 
   bubble_->Show();
-  observer_.Add(bubble_->GetWidget());
+  observation_.Observe(bubble_->GetWidget());
 
   if (mouse_inside_) {
     View* frame = bubble_->GetWidget()->non_client_view()->frame_view();
@@ -117,7 +117,8 @@
 }
 
 void TooltipIcon::OnWidgetDestroyed(Widget* widget) {
-  observer_.Remove(widget);
+  DCHECK(observation_.IsObservingSource(widget));
+  observation_.Reset();
 
   SetDrawAsHovered(false);
   mouse_watcher_.reset();
diff --git a/ui/views/bubble/tooltip_icon.h b/ui/views/bubble/tooltip_icon.h
index 80436bd8..7fd90f1 100644
--- a/ui/views/bubble/tooltip_icon.h
+++ b/ui/views/bubble/tooltip_icon.h
@@ -8,7 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "base/strings/string16.h"
 #include "base/timer/timer.h"
 #include "ui/views/bubble/bubble_border.h"
@@ -102,7 +102,7 @@
   // A watcher that keeps |bubble_| open if the user's mouse enters it.
   std::unique_ptr<MouseWatcher> mouse_watcher_;
 
-  ScopedObserver<Widget, WidgetObserver> observer_{this};
+  base::ScopedObservation<Widget, WidgetObserver> observation_{this};
 
   base::ObserverList<Observer, /*check_empty=*/true> observers_;
 
diff --git a/ui/views/controls/combobox/combobox.cc b/ui/views/controls/combobox/combobox.cc
index f911fa3..8a1bd561 100644
--- a/ui/views/controls/combobox/combobox.cc
+++ b/ui/views/controls/combobox/combobox.cc
@@ -309,14 +309,16 @@
 void Combobox::SetModel(ui::ComboboxModel* model) {
   DCHECK(model) << "After construction, the model must not be null.";
 
-  if (model_)
-    observer_.Remove(model_);
+  if (model_) {
+    DCHECK(observation_.IsObservingSource(model_));
+    observation_.Reset();
+  }
 
   model_ = model;
 
   if (model_) {
     menu_model_ = std::make_unique<ComboboxMenuModel>(this, model_);
-    observer_.Add(model_);
+    observation_.Observe(model_);
     SetSelectedIndex(model_->GetDefaultIndex());
     OnComboboxModelChanged(model_);
   }
diff --git a/ui/views/controls/combobox/combobox.h b/ui/views/controls/combobox/combobox.h
index 812810c..fa78168 100644
--- a/ui/views/controls/combobox/combobox.h
+++ b/ui/views/controls/combobox/combobox.h
@@ -9,7 +9,7 @@
 #include <utility>
 
 #include "base/macros.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
 #include "ui/base/models/combobox_model.h"
@@ -224,7 +224,8 @@
   // The focus ring for this Combobox.
   FocusRing* focus_ring_ = nullptr;
 
-  ScopedObserver<ui::ComboboxModel, ui::ComboboxModelObserver> observer_{this};
+  base::ScopedObservation<ui::ComboboxModel, ui::ComboboxModelObserver>
+      observation_{this};
 
   DISALLOW_COPY_AND_ASSIGN(Combobox);
 };
diff --git a/ui/views/controls/editable_combobox/editable_combobox.cc b/ui/views/controls/editable_combobox/editable_combobox.cc
index b9203707..f535357 100644
--- a/ui/views/controls/editable_combobox/editable_combobox.cc
+++ b/ui/views/controls/editable_combobox/editable_combobox.cc
@@ -136,7 +136,7 @@
         filter_on_edit_(filter_on_edit),
         show_on_empty_(show_on_empty) {
     UpdateItemsShown();
-    observer_.Add(combobox_model_);
+    observation_.Observe(combobox_model_);
   }
 
   ~EditableComboboxMenuModel() override = default;
@@ -252,7 +252,8 @@
   // When false, UpdateItemsShown doesn't do anything.
   bool update_items_shown_enabled_ = true;
 
-  ScopedObserver<ui::ComboboxModel, ui::ComboboxModelObserver> observer_{this};
+  base::ScopedObservation<ui::ComboboxModel, ui::ComboboxModelObserver>
+      observation_{this};
 
   DISALLOW_COPY_AND_ASSIGN(EditableComboboxMenuModel);
 };
@@ -322,7 +323,7 @@
       show_on_empty_(show_on_empty),
       showing_password_text_(type != Type::kPassword) {
   SetModel(std::move(combobox_model));
-  observer_.Add(textfield_);
+  observation_.Observe(textfield_);
   textfield_->set_controller(this);
   textfield_->SetFontList(GetFontList());
   textfield_->SetTextInputType((type == Type::kPassword)
diff --git a/ui/views/controls/editable_combobox/editable_combobox.h b/ui/views/controls/editable_combobox/editable_combobox.h
index a87863d..551433e 100644
--- a/ui/views/controls/editable_combobox/editable_combobox.h
+++ b/ui/views/controls/editable_combobox/editable_combobox.h
@@ -10,7 +10,7 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "base/strings/string16.h"
 #include "build/build_config.h"
 #include "ui/base/ui_base_types.h"
@@ -187,7 +187,7 @@
 
   bool dropdown_blocked_for_animation_ = false;
 
-  ScopedObserver<View, ViewObserver> observer_{this};
+  base::ScopedObservation<View, ViewObserver> observation_{this};
 
   DISALLOW_COPY_AND_ASSIGN(EditableCombobox);
 };
diff --git a/ui/views/controls/focus_ring.cc b/ui/views/controls/focus_ring.cc
index e347131..95d55d5 100644
--- a/ui/views/controls/focus_ring.cc
+++ b/ui/views/controls/focus_ring.cc
@@ -113,13 +113,13 @@
 
   if (details.is_add) {
     // Need to start observing the parent.
-    view_observer_.Add(details.parent);
+    view_observation_.Observe(details.parent);
     RefreshLayer();
-  } else if (view_observer_.IsObserving(details.parent)) {
+  } else if (view_observation_.IsObservingSource(details.parent)) {
     // This view is being removed from its parent. It needs to remove itself
     // from its parent's observer list in the case where the FocusView is
     // removed from its parent but not deleted.
-    view_observer_.Remove(details.parent);
+    view_observation_.Reset();
   }
 }
 
diff --git a/ui/views/controls/focus_ring.h b/ui/views/controls/focus_ring.h
index c3e42b6a4..7882505 100644
--- a/ui/views/controls/focus_ring.h
+++ b/ui/views/controls/focus_ring.h
@@ -7,7 +7,7 @@
 
 #include <memory>
 
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/views/controls/focusable_border.h"
 #include "ui/views/view.h"
@@ -96,7 +96,7 @@
   // The predicate used to determine whether the parent has focus.
   base::Optional<ViewPredicate> has_focus_predicate_;
 
-  ScopedObserver<View, ViewObserver> view_observer_{this};
+  base::ScopedObservation<View, ViewObserver> view_observation_{this};
 
   DISALLOW_COPY_AND_ASSIGN(FocusRing);
 };
diff --git a/ui/views/event_monitor_aura.cc b/ui/views/event_monitor_aura.cc
index 69edc44..698f21e 100644
--- a/ui/views/event_monitor_aura.cc
+++ b/ui/views/event_monitor_aura.cc
@@ -7,7 +7,7 @@
 #include <memory>
 
 #include "base/check_op.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/events/event_observer.h"
@@ -25,21 +25,23 @@
                     const std::set<ui::EventType>& types)
       : EventMonitorAura(event_observer, target_window, types),
         target_window_(target_window) {
-    window_observer_.Add(target_window);
+    window_observation_.Observe(target_window);
   }
   ~WindowMonitorAura() override = default;
 
   // aura::WindowObserver:
   void OnWindowDestroying(aura::Window* window) override {
     DCHECK_EQ(window, target_window_);
-    window_observer_.Remove(target_window_);
+    DCHECK(window_observation_.IsObservingSource(target_window_));
+    window_observation_.Reset();
     target_window_ = nullptr;
     TearDown();
   }
 
  private:
   aura::Window* target_window_;
-  ScopedObserver<aura::Window, aura::WindowObserver> window_observer_{this};
+  base::ScopedObservation<aura::Window, aura::WindowObserver>
+      window_observation_{this};
 
   DISALLOW_COPY_AND_ASSIGN(WindowMonitorAura);
 };
diff --git a/ui/views/examples/examples_views_delegate_chromeos.cc b/ui/views/examples/examples_views_delegate_chromeos.cc
index cc3365f..2a47b1d 100644
--- a/ui/views/examples/examples_views_delegate_chromeos.cc
+++ b/ui/views/examples/examples_views_delegate_chromeos.cc
@@ -14,8 +14,7 @@
 constexpr gfx::Size kDefaultSize(1024, 768);
 }  // namespace
 
-ExamplesViewsDelegateChromeOS::ExamplesViewsDelegateChromeOS()
-    : observer_(this) {}
+ExamplesViewsDelegateChromeOS::ExamplesViewsDelegateChromeOS() = default;
 
 ExamplesViewsDelegateChromeOS::~ExamplesViewsDelegateChromeOS() = default;
 
@@ -28,7 +27,7 @@
 
     wm_helper_ = std::make_unique<wm::WMTestHelper>(kDefaultSize);
     wm_helper_->host()->Show();
-    observer_.Add(wm_helper_->host());
+    observation_.Observe(wm_helper_->host());
     params->context = wm_helper_->host()->window();
   }
 }
@@ -37,7 +36,8 @@
     aura::WindowTreeHost* host) {
   Widget* widget = GetExamplesWidget();
   if (widget) {
-    observer_.Remove(host);
+    DCHECK(observation_.IsObservingSource(host));
+    observation_.Reset();
     widget->Close();
   }
 }
diff --git a/ui/views/examples/examples_views_delegate_chromeos.h b/ui/views/examples/examples_views_delegate_chromeos.h
index 1bba04f..3933cd5 100644
--- a/ui/views/examples/examples_views_delegate_chromeos.h
+++ b/ui/views/examples/examples_views_delegate_chromeos.h
@@ -7,7 +7,7 @@
 
 #include <memory>
 
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/aura/window_tree_host_observer.h"
 #include "ui/views/test/desktop_test_views_delegate.h"
@@ -33,7 +33,8 @@
   // aura::WindowTreeHostObserver:
   void OnHostCloseRequested(aura::WindowTreeHost* host) override;
 
-  ScopedObserver<aura::WindowTreeHost, aura::WindowTreeHostObserver> observer_;
+  base::ScopedObservation<aura::WindowTreeHost, aura::WindowTreeHostObserver>
+      observation_{this};
   std::unique_ptr<wm::WMTestHelper> wm_helper_;
 };
 
diff --git a/ui/views/layout/animating_layout_manager.cc b/ui/views/layout/animating_layout_manager.cc
index 3e6a87fa..cc67d166 100644
--- a/ui/views/layout/animating_layout_manager.cc
+++ b/ui/views/layout/animating_layout_manager.cc
@@ -146,8 +146,7 @@
     }
 
     void OnViewIsDeleting(View* observed_view) override {
-      if (animation_delegate_->scoped_observer_.IsObserving(observed_view))
-        animation_delegate_->scoped_observer_.Remove(observed_view);
+      animation_delegate_->scoped_observation_.Reset();
     }
 
    private:
@@ -165,7 +164,8 @@
   AnimatingLayoutManager* const target_layout_manager_;
   std::unique_ptr<gfx::SlideAnimation> animation_;
   ViewWidgetObserver view_widget_observer_{this};
-  ScopedObserver<View, ViewObserver> scoped_observer_{&view_widget_observer_};
+  base::ScopedObservation<View, ViewObserver> scoped_observation_{
+      &view_widget_observer_};
 };
 
 AnimatingLayoutManager::AnimationDelegate::AnimationDelegate(
@@ -179,7 +179,7 @@
   if (host_view->GetWidget())
     MakeReadyForAnimation();
   else
-    scoped_observer_.Add(host_view);
+    scoped_observation_.Observe(host_view);
   UpdateAnimationParameters();
 }
 
@@ -205,8 +205,7 @@
   if (!ready_to_animate_) {
     target_layout_manager_->ResetLayout();
     ready_to_animate_ = true;
-    if (scoped_observer_.IsObserving(target_layout_manager_->host_view()))
-      scoped_observer_.Remove(target_layout_manager_->host_view());
+    scoped_observation_.Reset();
   }
 }
 
diff --git a/ui/views/layout/animating_layout_manager_unittest.cc b/ui/views/layout/animating_layout_manager_unittest.cc
index d50d50d1..02f66e60 100644
--- a/ui/views/layout/animating_layout_manager_unittest.cc
+++ b/ui/views/layout/animating_layout_manager_unittest.cc
@@ -8,7 +8,7 @@
 #include <utility>
 #include <vector>
 
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -108,7 +108,7 @@
   ~AnimationEventLogger() override = default;
 
   explicit AnimationEventLogger(AnimatingLayoutManager* layout) {
-    scoped_observer_.Add(layout);
+    scoped_observation_.Observe(layout);
   }
 
   void OnLayoutIsAnimatingChanged(AnimatingLayoutManager* source,
@@ -120,7 +120,8 @@
 
  private:
   std::vector<bool> events_;
-  ScopedObserver<AnimatingLayoutManager, Observer> scoped_observer_{this};
+  base::ScopedObservation<AnimatingLayoutManager, Observer> scoped_observation_{
+      this};
 };
 
 }  // anonymous namespace
@@ -2879,7 +2880,7 @@
  public:
   explicit AnimationWatcher(AnimatingLayoutManager* layout_manager)
       : layout_manager_(layout_manager) {
-    observer_.Add(layout_manager);
+    observation_.Observe(layout_manager);
   }
 
   void OnLayoutIsAnimatingChanged(AnimatingLayoutManager*,
@@ -2901,8 +2902,9 @@
 
  private:
   AnimatingLayoutManager* const layout_manager_;
-  ScopedObserver<AnimatingLayoutManager, AnimatingLayoutManager::Observer>
-      observer_{this};
+  base::ScopedObservation<AnimatingLayoutManager,
+                          AnimatingLayoutManager::Observer>
+      observation_{this};
   std::unique_ptr<base::RunLoop> run_loop_;
   bool waiting_ = false;
 };
diff --git a/ui/views/test/ax_event_counter.cc b/ui/views/test/ax_event_counter.cc
index b1c68ee3..592d9fd0 100644
--- a/ui/views/test/ax_event_counter.cc
+++ b/ui/views/test/ax_event_counter.cc
@@ -7,9 +7,8 @@
 namespace views {
 namespace test {
 
-AXEventCounter::AXEventCounter(views::AXEventManager* event_manager)
-    : tree_observer_(this) {
-  tree_observer_.Add(event_manager);
+AXEventCounter::AXEventCounter(views::AXEventManager* event_manager) {
+  tree_observation_.Observe(event_manager);
 }
 
 AXEventCounter::~AXEventCounter() = default;
diff --git a/ui/views/test/ax_event_counter.h b/ui/views/test/ax_event_counter.h
index be647c1..5cab9fa 100644
--- a/ui/views/test/ax_event_counter.h
+++ b/ui/views/test/ax_event_counter.h
@@ -7,7 +7,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/run_loop.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/views/accessibility/ax_event_manager.h"
 #include "ui/views/accessibility/ax_event_observer.h"
@@ -39,8 +39,8 @@
   base::flat_map<ax::mojom::Event, int> event_counts_;
   ax::mojom::Event wait_for_event_type_ = ax::mojom::Event::kNone;
   base::RunLoop* run_loop_ = nullptr;
-  ScopedObserver<views::AXEventManager, views::AXEventObserver> tree_observer_{
-      this};
+  base::ScopedObservation<views::AXEventManager, views::AXEventObserver>
+      tree_observation_{this};
 };
 
 }  // namespace test
diff --git a/ui/views/test/widget_test.cc b/ui/views/test/widget_test.cc
index e6c48cf..1c42693 100644
--- a/ui/views/test/widget_test.cc
+++ b/ui/views/test/widget_test.cc
@@ -261,7 +261,7 @@
 
 void WidgetVisibleWaiter::Wait() {
   if (!widget_->IsVisible()) {
-    widget_observer_.Add(widget_);
+    widget_observation_.Observe(widget_);
     run_loop_.Run();
   }
 }
@@ -270,7 +270,8 @@
                                                     bool visible) {
   DCHECK_EQ(widget_, widget);
   if (visible) {
-    widget_observer_.Remove(widget);
+    DCHECK(widget_observation_.IsObservingSource(widget));
+    widget_observation_.Reset();
     run_loop_.Quit();
   }
 }
@@ -280,7 +281,8 @@
   ADD_FAILURE() << "Widget destroying before it became visible!";
   // Even though the test failed, be polite and remove the observer so we
   // don't crash with a UAF in the destructor.
-  widget_observer_.Remove(widget);
+  DCHECK(widget_observation_.IsObservingSource(widget));
+  widget_observation_.Reset();
 }
 
 }  // namespace test
diff --git a/ui/views/test/widget_test.h b/ui/views/test/widget_test.h
index 02565096..9743c69 100644
--- a/ui/views/test/widget_test.h
+++ b/ui/views/test/widget_test.h
@@ -12,7 +12,7 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "base/test/bind.h"
 #include "build/build_config.h"
 #include "ui/gfx/native_widget_types.h"
@@ -307,7 +307,7 @@
 
   Widget* const widget_;
   base::RunLoop run_loop_;
-  ScopedObserver<Widget, WidgetObserver> widget_observer_{this};
+  base::ScopedObservation<Widget, WidgetObserver> widget_observation_{this};
 };
 
 }  // namespace test
diff --git a/ui/views/view.cc b/ui/views/view.cc
index b7a1edbc..ecf38ee 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -17,7 +17,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/notreached.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
@@ -160,7 +160,7 @@
   // views::ViewObserver:
   void OnViewBoundsChanged(View* observed_view) override;
 
-  ScopedObserver<View, ViewObserver> observed_view_{this};
+  base::ScopedObservation<View, ViewObserver> observed_view_{this};
 
   SkPath path_;
   ui::Layer layer_;
@@ -171,7 +171,7 @@
   layer_.set_delegate(this);
   layer_.SetFillsBoundsOpaquely(false);
   layer_.SetName("ViewMaskLayer");
-  observed_view_.Add(observed_view);
+  observed_view_.Observe(observed_view);
   OnViewBoundsChanged(observed_view);
 }
 
diff --git a/ui/views/view_tracker.cc b/ui/views/view_tracker.cc
index 7b54e2032..eaa66da 100644
--- a/ui/views/view_tracker.cc
+++ b/ui/views/view_tracker.cc
@@ -16,10 +16,10 @@
   if (view == view_)
     return;
 
-  observer_.RemoveAll();
+  observation_.Reset();
   view_ = view;
   if (view_)
-    observer_.Add(view_);
+    observation_.Observe(view_);
 }
 
 void ViewTracker::OnViewIsDeleting(View* observed_view) {
diff --git a/ui/views/view_tracker.h b/ui/views/view_tracker.h
index fead2fa1..74886d9 100644
--- a/ui/views/view_tracker.h
+++ b/ui/views/view_tracker.h
@@ -5,7 +5,7 @@
 #ifndef UI_VIEWS_VIEW_TRACKER_H_
 #define UI_VIEWS_VIEW_TRACKER_H_
 
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "ui/views/view.h"
 #include "ui/views/view_observer.h"
 #include "ui/views/views_export.h"
@@ -28,7 +28,7 @@
  private:
   View* view_ = nullptr;
 
-  ScopedObserver<View, ViewObserver> observer_{this};
+  base::ScopedObservation<View, ViewObserver> observation_{this};
 };
 
 }  // namespace views
diff --git a/ui/views/widget/unique_widget_ptr.cc b/ui/views/widget/unique_widget_ptr.cc
index fe1d260..6e21997 100644
--- a/ui/views/widget/unique_widget_ptr.cc
+++ b/ui/views/widget/unique_widget_ptr.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
@@ -32,7 +32,7 @@
   // deliberately implicit.
   UniqueWidgetPtrImpl(std::unique_ptr<Widget> widget)  // NOLINT
       : widget_closer_(widget.release()) {
-    widget_observer_.Add(widget_closer_.get());
+    widget_observation_.Observe(widget_closer_.get());
   }
 
   UniqueWidgetPtrImpl(const UniqueWidgetPtrImpl&) = delete;
@@ -46,19 +46,19 @@
   void Reset() {
     if (!widget_closer_)
       return;
-    widget_observer_.RemoveAll();
+    widget_observation_.Reset();
     widget_closer_.reset();
   }
 
   // WidgetObserver overrides.
   void OnWidgetDestroying(Widget* widget) override {
     DCHECK_EQ(widget, widget_closer_.get());
-    widget_observer_.RemoveAll();
+    widget_observation_.Reset();
     widget_closer_.release();
   }
 
  private:
-  ScopedObserver<Widget, WidgetObserver> widget_observer_{this};
+  base::ScopedObservation<Widget, WidgetObserver> widget_observation_{this};
   WidgetAutoClosePtr widget_closer_;
 };
 
diff --git a/ui/views/widget/unique_widget_ptr_unittest.cc b/ui/views/widget/unique_widget_ptr_unittest.cc
index df8804ab..3e333ba3 100644
--- a/ui/views/widget/unique_widget_ptr_unittest.cc
+++ b/ui/views/widget/unique_widget_ptr_unittest.cc
@@ -5,8 +5,9 @@
 #include "ui/views/widget/unique_widget_ptr.h"
 
 #include <memory>
+#include <utility>
 
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "ui/views/test/views_test_base.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
@@ -29,7 +30,7 @@
   std::unique_ptr<Widget> AllocateTestWidget() override {
     auto widget = ViewsTestBase::AllocateTestWidget();
     widget->Init(CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS));
-    widget_observer_.Add(widget.get());
+    widget_observation_.Observe(widget.get());
     return widget;
   }
 
@@ -46,13 +47,14 @@
   void OnWidgetDestroying(Widget* widget) override {
     ASSERT_NE(widget_, nullptr);
     ASSERT_EQ(widget_, widget);
-    widget_observer_.Remove(widget_);
+    ASSERT_TRUE(widget_observation_.IsObservingSource(widget_));
+    widget_observation_.Reset();
     widget_ = nullptr;
   }
 
  private:
   Widget* widget_ = nullptr;
-  ScopedObserver<Widget, WidgetObserver> widget_observer_{this};
+  base::ScopedObservation<Widget, WidgetObserver> widget_observation_{this};
 };
 
 // Make sure explicitly resetting the |unique_widget_ptr| variable properly
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
index 52c1d1e..e8d1afc 100644
--- a/ui/views/widget/widget.cc
+++ b/ui/views/widget/widget.cc
@@ -396,7 +396,7 @@
     SetInitialBoundsForFramelessWindow(bounds);
   }
 
-  observer_manager_.Add(GetNativeTheme());
+  observation_.Observe(GetNativeTheme());
   native_widget_initialized_ = true;
   native_widget_->OnWidgetInitDone();
 
@@ -1502,16 +1502,16 @@
 void Widget::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) {
   TRACE_EVENT0("ui", "Widget::OnNativeThemeUpdated");
 
-  DCHECK(observer_manager_.IsObserving(observed_theme));
+  DCHECK(observation_.IsObservingSource(observed_theme));
 
 #if defined(OS_APPLE) || defined(OS_WIN)
   ui::NativeTheme* current_native_theme = observed_theme;
 #else
   ui::NativeTheme* current_native_theme = GetNativeTheme();
 #endif
-  if (!observer_manager_.IsObserving(current_native_theme)) {
-    observer_manager_.RemoveAll();
-    observer_manager_.Add(current_native_theme);
+  if (!observation_.IsObservingSource(current_native_theme)) {
+    observation_.Reset();
+    observation_.Observe(current_native_theme);
   }
 
   PropagateNativeThemeChanged();
diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h
index 819ea9a..d85ff8d5 100644
--- a/ui/views/widget/widget.h
+++ b/ui/views/widget/widget.h
@@ -14,7 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/optional.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "build/build_config.h"
 #include "ui/base/dragdrop/mojom/drag_drop_types.mojom-forward.h"
 #include "ui/base/ui_base_types.h"
@@ -1174,8 +1174,8 @@
   // Block the widget from closing.
   bool block_close_ = false;
 
-  ScopedObserver<ui::NativeTheme, ui::NativeThemeObserver> observer_manager_{
-      this};
+  base::ScopedObservation<ui::NativeTheme, ui::NativeThemeObserver>
+      observation_{this};
 
   base::WeakPtrFactory<Widget> weak_ptr_factory_{this};
 
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc
index c32b7760..113aa30 100644
--- a/ui/views/widget/widget_unittest.cc
+++ b/ui/views/widget/widget_unittest.cc
@@ -1493,7 +1493,7 @@
  public:
   explicit DesktopAuraTestValidPaintWidget(Widget::InitParams init_params)
       : Widget(std::move(init_params)) {
-    observer_.Add(this);
+    observation_.Observe(this);
   }
   ~DesktopAuraTestValidPaintWidget() override = default;
 
@@ -1536,7 +1536,7 @@
   bool expect_paint_ = true;
   bool received_paint_while_hidden_ = false;
   base::OnceClosure quit_closure_;
-  ScopedObserver<Widget, WidgetObserver> observer_{this};
+  base::ScopedObservation<Widget, WidgetObserver> observation_{this};
 
   DISALLOW_COPY_AND_ASSIGN(DesktopAuraTestValidPaintWidget);
 };
diff --git a/ui/views/widget/widget_utils.cc b/ui/views/widget/widget_utils.cc
index 81c67ad1..0675d724 100644
--- a/ui/views/widget/widget_utils.cc
+++ b/ui/views/widget/widget_utils.cc
@@ -21,16 +21,16 @@
 
 void WidgetOpenTimer::OnWidgetDestroying(Widget* widget) {
   DCHECK(open_timer_.has_value());
-  DCHECK(observed_widget_.IsObserving(widget));
+  DCHECK(observed_widget_.IsObservingSource(widget));
   callback_.Run(open_timer_->Elapsed());
   open_timer_.reset();
-  observed_widget_.Remove(widget);
+  observed_widget_.Reset();
 }
 
 void WidgetOpenTimer::Reset(Widget* widget) {
   DCHECK(!open_timer_.has_value());
-  DCHECK(!observed_widget_.IsObserving(widget));
-  observed_widget_.Add(widget);
+  DCHECK(!observed_widget_.IsObservingSource(widget));
+  observed_widget_.Observe(widget);
   open_timer_ = base::ElapsedTimer();
 }
 
diff --git a/ui/views/widget/widget_utils.h b/ui/views/widget/widget_utils.h
index cb092fe..ee84051b 100644
--- a/ui/views/widget/widget_utils.h
+++ b/ui/views/widget/widget_utils.h
@@ -6,7 +6,7 @@
 #define UI_VIEWS_WIDGET_WIDGET_UTILS_H_
 
 #include "base/callback.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "base/time/time.h"
 #include "base/timer/elapsed_timer.h"
 #include "ui/gfx/native_widget_types.h"
@@ -39,7 +39,7 @@
   // Time the bubble has been open. Used for UMA metrics collection.
   base::Optional<base::ElapsedTimer> open_timer_;
 
-  ScopedObserver<Widget, WidgetObserver> observed_widget_{this};
+  base::ScopedObservation<Widget, WidgetObserver> observed_widget_{this};
 };
 
 // Returns the root window for |widget|.  On non-Aura, this is equivalent to
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index 4c96541..c347d16 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -456,7 +456,7 @@
       hwnd(), ui::WindowEventTarget::kWin32InputEventTarget,
       static_cast<ui::WindowEventTarget*>(this));
   DCHECK(delegate_->GetHWNDMessageDelegateInputMethod());
-  observer_.Add(delegate_->GetHWNDMessageDelegateInputMethod());
+  observation_.Observe(delegate_->GetHWNDMessageDelegateInputMethod());
 
   // The usual way for UI Automation to obtain a fragment root is through
   // WM_GETOBJECT. However, if there's a relation such as "Controller For"
diff --git a/ui/views/win/hwnd_message_handler.h b/ui/views/win/hwnd_message_handler.h
index 3d498de..042392a 100644
--- a/ui/views/win/hwnd_message_handler.h
+++ b/ui/views/win/hwnd_message_handler.h
@@ -18,7 +18,7 @@
 #include "base/lazy_instance.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "base/strings/string16.h"
 #include "base/win/scoped_gdi_object.h"
 #include "base/win/win_util.h"
@@ -819,7 +819,8 @@
   // Populated if the cursor position is being mocked for testing purposes.
   base::Optional<gfx::Point> mock_cursor_position_;
 
-  ScopedObserver<ui::InputMethod, ui::InputMethodObserver> observer_{this};
+  base::ScopedObservation<ui::InputMethod, ui::InputMethodObserver>
+      observation_{this};
 
   // The WeakPtrFactories below (one inside the
   // CR_MSG_MAP_CLASS_DECLARATIONS macro and autohide_factory_) must
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/payments/WebLayerPaymentRequestFactory.java b/weblayer/browser/java/org/chromium/weblayer_private/payments/WebLayerPaymentRequestFactory.java
index 6210114..c78d931 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/payments/WebLayerPaymentRequestFactory.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/payments/WebLayerPaymentRequestFactory.java
@@ -25,6 +25,7 @@
 import org.chromium.mojo.system.MojoResult;
 import org.chromium.payments.mojom.PaymentRequest;
 import org.chromium.services.service_manager.InterfaceFactory;
+import org.chromium.url.GURL;
 import org.chromium.weblayer_private.ProfileImpl;
 import org.chromium.weblayer_private.TabImpl;
 
@@ -63,7 +64,7 @@
                     PaymentRequestServiceUtil.getLiveWebContents(mRenderFrameHost);
             if (webContents == null || webContents.isDestroyed()) return null;
 
-            String url = webContents.getLastCommittedUrl();
+            GURL url = webContents.getLastCommittedUrl();
             if (url == null || !OriginSecurityChecker.isSchemeCryptographic(url)) {
                 return null;
             }